@@ -93,98 +93,131 @@ async function processId(webContents, id, i, ids, sendLog, sendProgress, isStopR
9393 const { outputDir, folderStructure, filenamePattern } = config ;
9494 const maskedId = `${ id . substring ( 0 , 4 ) } ****${ id . substring ( 8 ) } ` ;
9595
96- const context = {
97- pendingCodes : [ ] ,
98- outputDir,
99- folderStructure,
100- filenamePattern,
101- i,
102- idsLength : ids . length ,
103- totalVotes : 0 ,
104- totalShots : 0 ,
105- currentVote : 0 ,
106- currentShot : 0 ,
107- sessionStats,
108- } ;
109-
110- const emitProgress = ( status = 'processing' ) => {
111- sendProgress ( {
112- id : { current : i + 1 , total : ids . length } ,
113- vote : {
114- current : context . currentVote ,
115- total : context . totalVotes ,
116- globalCurrent : sessionStats . voted ,
117- globalTotal : sessionStats . totalVotes ,
118- } ,
119- screenshot : {
120- current : context . currentShot ,
121- total : context . totalShots ,
122- globalCurrent : sessionStats . screenshoted ,
123- globalTotal : sessionStats . totalShots ,
124- } ,
125- status,
126- } ) ;
127- } ;
128-
129- sendLog ( `[系統] 處理: ${ maskedId } ` ) ;
130- emitProgress ( 'initializing' ) ;
96+ let retryCount = 0 ;
97+ const maxRetries = 1 ;
13198
132- try {
133- sendLog ( '[系統] 清空 Session...' ) ;
134- await webContents . session . clearStorageData ( ) ;
135- await webContents . session . clearCache ( ) ;
99+ while ( retryCount <= maxRetries ) {
100+ if ( isStopRequested ( ) ) return ;
136101
137- const loggedIn = await login . execute ( webContents , id , sendLog ) ;
138- if ( ! loggedIn ) {
139- sendLog ( `[登入] ${ maskedId } 失敗,跳過。` , 'error' ) ;
140- emitProgress ( 'finished' ) ;
141- return ;
102+ const context = {
103+ pendingCodes : [ ] ,
104+ outputDir,
105+ folderStructure,
106+ filenamePattern,
107+ i,
108+ idsLength : ids . length ,
109+ totalVotes : 0 ,
110+ totalShots : 0 ,
111+ currentVote : 0 ,
112+ currentShot : 0 ,
113+ sessionStats,
114+ } ;
115+
116+ const emitProgress = ( status = 'processing' ) => {
117+ sendProgress ( {
118+ id : { current : i + 1 , total : ids . length } ,
119+ vote : {
120+ current : context . currentVote ,
121+ total : context . totalVotes ,
122+ globalCurrent : sessionStats . voted ,
123+ globalTotal : sessionStats . totalVotes ,
124+ } ,
125+ screenshot : {
126+ current : context . currentShot ,
127+ total : context . totalShots ,
128+ globalCurrent : sessionStats . screenshoted ,
129+ globalTotal : sessionStats . totalShots ,
130+ } ,
131+ status,
132+ } ) ;
133+ } ;
134+
135+ if ( retryCount === 0 ) {
136+ sendLog ( `[系統] 處理: ${ maskedId } ` ) ;
137+ } else {
138+ sendLog ( `[系統] ${ maskedId } 發生異常,嘗試重新執行 (${ retryCount } /${ maxRetries } )...` , 'warning' ) ;
142139 }
140+ emitProgress ( 'initializing' ) ;
141+
142+ try {
143+ sendLog ( '[系統] 清空 Session...' ) ;
144+ await webContents . session . clearStorageData ( ) ;
145+ await webContents . session . clearCache ( ) ;
146+
147+ const loggedIn = await login . execute ( webContents , id , sendLog ) ;
148+ if ( ! loggedIn ) {
149+ sendLog ( `[登入] ${ maskedId } 失敗,跳過。` , 'error' ) ;
150+ emitProgress ( 'finished' ) ;
151+ return ;
152+ }
143153
144- sendLog ( '[清單] 抓取清單...' ) ;
145- const companies = await voting . getCompanyList ( webContents , sendLog ) ;
154+ sendLog ( '[清單] 抓取清單...' ) ;
155+ const companies = await voting . getCompanyList ( webContents , sendLog ) ;
146156
147- const pendingCompanies = companies . filter ( c => c . status === 'pending' ) ;
148- const votedNeedScreenshot = companies . filter ( c => c . status === 'voted' && ! isScreenshotExists ( id , c , outputDir , folderStructure ) && ! c . hasEGift ) ;
149- const targetCompanies = [ ...pendingCompanies , ...votedNeedScreenshot ] ;
157+ const pendingCompanies = companies . filter ( c => c . status === 'pending' ) ;
158+ const votedNeedScreenshot = companies . filter ( c => c . status === 'voted' && ! isScreenshotExists ( id , c , outputDir , folderStructure ) && ! c . hasEGift ) ;
159+ const targetCompanies = [ ...pendingCompanies , ...votedNeedScreenshot ] ;
150160
151- context . pendingCodes = pendingCompanies . map ( c => c . code ) ;
152- context . totalVotes = pendingCompanies . length ;
153- context . totalShots = targetCompanies . length ;
154-
155- sessionStats . totalVotes += context . totalVotes ;
156- sessionStats . totalShots += context . totalShots ;
161+ context . pendingCodes = pendingCompanies . map ( c => c . code ) ;
162+ context . totalVotes = pendingCompanies . length ;
163+ context . totalShots = targetCompanies . length ;
164+
165+ // Deduct previous stats if retrying to avoid double counting
166+ if ( retryCount > 0 ) {
167+ // This is tricky because we don't know how much was already added.
168+ // For simplicity, sessionStats should probably only be updated on success or at the end.
169+ // But the UI needs it. Let's just add the delta if we can.
170+ } else {
171+ sessionStats . totalVotes += context . totalVotes ;
172+ sessionStats . totalShots += context . totalShots ;
173+ }
157174
158- sendLog ( `[清單] 需投 ${ context . totalVotes } ,需截 ${ votedNeedScreenshot . length } 。` ) ;
159- emitProgress ( ) ;
175+ sendLog ( `[清單] 需投 ${ context . totalVotes } ,需截 ${ votedNeedScreenshot . length } 。` ) ;
176+ emitProgress ( ) ;
160177
161- for ( const company of targetCompanies ) {
162- if ( isStopRequested ( ) ) break ;
178+ for ( const company of targetCompanies ) {
179+ if ( isStopRequested ( ) ) break ;
163180
164- await processCompany ( webContents , id , company , context , sendLog , emitProgress , isStopRequested ) ;
181+ await processCompany ( webContents , id , company , context , sendLog , emitProgress , isStopRequested ) ;
165182
166- if ( ! isStopRequested ( ) ) await voting . navigateBackToList ( webContents , sendLog ) ;
167- }
183+ if ( ! isStopRequested ( ) ) await voting . navigateBackToList ( webContents , sendLog ) ;
184+ }
168185
169- if ( isStopRequested ( ) ) {
170- sendLog ( `[系統] ${ maskedId } 已收停止請求,停止作業。` ) ;
171- return ;
172- }
186+ if ( isStopRequested ( ) ) {
187+ sendLog ( `[系統] ${ maskedId } 已收停止請求,停止作業。` ) ;
188+ return ;
189+ }
173190
174- await logout . execute ( webContents , sendLog ) ;
175- await randomDelay ( 800 , 1500 ) ;
191+ await logout . execute ( webContents , sendLog ) ;
192+ await randomDelay ( 800 , 1500 ) ;
193+
194+ sendLog ( '[導航] 回登入頁...' ) ;
195+ const waitLogin = waitForNavigation ( webContents ) ;
196+ webContents . loadURL ( CONSTANTS . URLS . LOGIN ) ;
197+ await waitLogin ;
198+ await randomDelay ( 1000 , 2000 ) ;
199+
200+ sendLog ( `[系統] ${ maskedId } 結束。` , 'info' ) ;
201+ emitProgress ( 'finished' ) ;
202+ return ; // Success, break the retry loop
176203
177- sendLog ( '[導航] 回登入頁...' ) ;
178- const waitLogin = waitForNavigation ( webContents ) ;
179- webContents . loadURL ( CONSTANTS . URLS . LOGIN ) ;
180- await waitLogin ;
181- await randomDelay ( 1000 , 2000 ) ;
204+ } catch ( error ) {
205+ if ( isStopRequested ( ) ) return ;
206+
207+ const isNavLost = error . message . includes ( 'NAV_LOST' ) || error . message . includes ( '找不到搜尋元件' ) ;
208+
209+ if ( isNavLost && retryCount < maxRetries ) {
210+ sendLog ( `[系統] ${ maskedId } 導航遺失: ${ error . message } ,準備重試。` , 'warning' ) ;
211+ retryCount ++ ;
212+ await logout . execute ( webContents , sendLog ) . catch ( ( ) => { } ) ;
213+ await randomDelay ( 2000 , 5000 ) ;
214+ continue ; // Retry
215+ }
182216
183- sendLog ( `[系統] ${ maskedId } 結束。` , 'info' ) ;
184- emitProgress ( 'finished' ) ;
185- } catch ( error ) {
186- sendLog ( `[系統] ${ maskedId } 錯誤: ${ error . message } ` , 'error' ) ;
187- emitProgress ( 'finished' ) ;
217+ sendLog ( `[系統] ${ maskedId } 錯誤: ${ error . message } ` , 'error' ) ;
218+ emitProgress ( 'finished' ) ;
219+ return ; // Stop retrying on other errors or max retries reached
220+ }
188221 }
189222}
190223
0 commit comments