返回 AiToEarn
account.ts
根目录 / project / aitoearn-web / src / store / account.ts
1 import type { AccountGroupItem, SocialAccount } from '@/api/accounts/account.types'
2 import type { PluginAccountStatusMap } from '@/store/plugin/account.utils'
3 import lodash from 'lodash'
4 import { create } from 'zustand'
5 import { combine } from 'zustand/middleware'
6 import { getAccountGroupApi, getAccountListApi } from '@/api/accounts/account.api'
7 import { directTrans } from '@/app/i18n/client'
8 import { usePluginStore } from '@/store/plugin'
9 import { mergePluginAccountStatus } from '@/store/plugin/account.utils'
10
11 export interface AccountGroup extends AccountGroupItem {
12 children: SocialAccount[]
13 }
14
15 export interface IAccountStore {
16 accountList: SocialAccount[]
17 accountMap: Map<string, SocialAccount>
18 accountGroupList: AccountGroup[]
19 accountGroupMap: Map<string, AccountGroup>
20 accountLoading: boolean
21 accountListInitialized: boolean
22 accountPluginAuthLoading: boolean
23 accountPluginAuthInitialized: boolean
24 // 当前选择的账户
25 accountActive?: SocialAccount
26 // 当前选择的空间ID
27 activeSpaceId?: string
28 // 余额不足弹框状态
29 lowBalanceAlertOpen: boolean
30 }
31
32 const store: IAccountStore = {
33 // 不分组的账户数据
34 accountList: [],
35 // 分组的账户数据
36 accountGroupList: [],
37 accountGroupMap: new Map([]),
38 accountMap: new Map([]),
39 accountLoading: false,
40 accountListInitialized: false,
41 accountPluginAuthLoading: false,
42 accountPluginAuthInitialized: false,
43 accountActive: undefined,
44 activeSpaceId: undefined,
45 lowBalanceAlertOpen: false,
46 }
47
48 let pluginAuthStatusPromise: Promise<void> | null = null
49
50 function getStore() {
51 return lodash.cloneDeep(store)
52 }
53
54 function normalizeAccountListData(data: SocialAccount[] | { list: SocialAccount[] } | undefined) {
55 if (Array.isArray(data))
56 return data
57 return data?.list ?? []
58 }
59
60 function createAccountListState(accountList: SocialAccount[]) {
61 const accountMap = new Map<string, SocialAccount>()
62 accountList.forEach((account) => {
63 accountMap.set(account.id, account)
64 })
65
66 return {
67 accountList,
68 accountMap,
69 }
70 }
71
72 function mergeAccountStateWithPluginStatus(pluginAccountStatus: PluginAccountStatusMap) {
73 const accountList = useAccountStore.getState().accountList
74 if (accountList.length === 0)
75 return
76
77 const mergedAccountState = mergePluginAccountStatus(accountList, pluginAccountStatus)
78 const hasChange = mergedAccountState.accountList.some((account, index) => account !== accountList[index])
79
80 if (hasChange) {
81 useAccountStore.setState(mergedAccountState)
82 }
83 }
84
85 function mergeAccountStateWithCurrentPluginStatus() {
86 mergeAccountStateWithPluginStatus(usePluginStore.getState().platformAccounts)
87 }
88
89 // 视频发布所有组件的共享状态和方法
90 export const useAccountStore = create(
91 combine(
92 {
93 ...getStore(),
94 },
95 (set, get, storeApi) => {
96 const methods = {
97 setLowBalanceAlertOpen(lowBalanceAlertOpen: boolean) {
98 set({
99 lowBalanceAlertOpen,
100 })
101 },
102
103 clear() {
104 set({
105 ...getStore(),
106 })
107 },
108 // 设置选择账户ID
109 setAccountActive(accountActive?: SocialAccount) {
110 set({
111 accountActive,
112 })
113 },
114
115 // 设置选择的空间ID
116 setActiveSpaceId(activeSpaceId?: string) {
117 set({
118 activeSpaceId,
119 })
120 },
121
122 setAccountGroupList(accountGroupList: AccountGroup[]) {
123 set({ accountGroupList })
124 },
125
126 /**
127 * 异步获取账户列表(含稳健的loading与错误处理),避免阻塞UI
128 */
129 async getAccountList(isBackground = false) {
130 if (get().accountLoading)
131 return
132 set({ accountLoading: true })
133
134 try {
135 // 防卡死:给请求增加超时兜底(例如 15s),即使后端迟迟不返回也不会一直占用loading
136 const timeoutMs = 10000
137 const timeoutPromise = new Promise<Awaited<ReturnType<typeof getAccountListApi>>>((resolve) => {
138 setTimeout(() => resolve({ code: -1, data: { total: 0, list: [] }, message: '', url: '' }), timeoutMs)
139 })
140 const result = await Promise.race([getAccountListApi(), timeoutPromise])
141
142 if (result?.code !== 0)
143 return
144
145 const accountList = normalizeAccountListData(result.data)
146 set(createAccountListState(accountList))
147
148 methods.refreshPluginAuthStatusInBackground(isBackground)
149
150 // 后续分组数据获取不阻塞调用方
151 methods.getAccountGroup()
152 }
153 finally {
154 set({ accountLoading: false, accountListInitialized: true })
155 }
156 },
157
158 /**
159 * 后台异步启动账户列表加载(不等待,不阻塞首屏/刷新渲染)
160 */
161 async getAccountListInBackground() {
162 await methods.getAccountList(true)
163 },
164
165 refreshPluginAuthStatusInBackground(isBackground = false) {
166 if (get().accountPluginAuthInitialized) {
167 mergeAccountStateWithCurrentPluginStatus()
168 return
169 }
170
171 if (pluginAuthStatusPromise)
172 return
173
174 set({ accountPluginAuthLoading: true })
175
176 pluginAuthStatusPromise = usePluginStore
177 .getState()
178 .getAccountStatusSnapshot(isBackground, { waitForPluginApi: true })
179 .then(mergeAccountStateWithPluginStatus)
180 .catch(() => {})
181 .finally(() => {
182 set({ accountPluginAuthLoading: false, accountPluginAuthInitialized: true })
183 pluginAuthStatusPromise = null
184 })
185 },
186
187 // 获取用户组的数据并且将用户放到对应组下
188 async getAccountGroup() {
189 const res = await getAccountGroupApi()
190 const groupList = res?.data
191
192 if (!groupList)
193 return
194 if (groupList.length === 0)
195 return
196 const normalizedGroupList = groupList.map(v => ({
197 ...v,
198 name: v.isDefault ? directTrans('account', 'defaultSpace') : v.name,
199 }))
200
201 const accountGroupList: AccountGroup[] = []
202 // key=组ID,val=账户ID
203 const accountGroupMap = new Map<string, AccountGroup>()
204
205 const defaultGroup = normalizedGroupList.find(v => v.isDefault) ?? normalizedGroupList[0]
206
207 normalizedGroupList.map((v) => {
208 const accountGroupItem = {
209 ...v,
210 children: [],
211 }
212 accountGroupList.push(accountGroupItem)
213 accountGroupMap.set(v.id, accountGroupItem)
214 })
215 get().accountList.map((v) => {
216 if (accountGroupMap.get(v.groupId!)) {
217 accountGroupMap.get(v.groupId!)!.children?.push(v)
218 }
219 else if (defaultGroup) {
220 accountGroupMap.get(defaultGroup.id)!.children?.push(v)
221 v.groupId = defaultGroup.id
222 }
223 })
224
225 accountGroupList.sort((a, b) => {
226 return (a.rank ?? 0) - (b.rank ?? 0)
227 })
228
229 set({
230 accountGroupList,
231 accountGroupMap,
232 })
233 },
234
235 async accountInit() {
236 if (get().accountList.length > 0)
237 return
238 // 改为后台异步拉取,避免初始化时阻塞界面
239 methods.getAccountListInBackground()
240 },
241 }
242 return methods
243 },
244 ),
245 )
246
246 lines TYPESCRIPT