View Javadoc
1   /*
2    * Copyright (c) 2002-2025, City of Paris
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met:
8    *
9    *  1. Redistributions of source code must retain the above copyright notice
10   *     and the following disclaimer.
11   *
12   *  2. Redistributions in binary form must reproduce the above copyright notice
13   *     and the following disclaimer in the documentation and/or other materials
14   *     provided with the distribution.
15   *
16   *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
17   *     contributors may be used to endorse or promote products derived from
18   *     this software without specific prior written permission.
19   *
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30   * POSSIBILITY OF SUCH DAMAGE.
31   *
32   * License 1.0
33   */
34  package fr.paris.lutece.plugins.workflow.modules.ticketing.service.daemon;
35  
36  import java.util.ArrayList;
37  import java.util.HashMap;
38  import java.util.List;
39  import java.util.Map;
40  import java.util.Map.Entry;
41  import java.util.StringJoiner;
42  import java.util.stream.Collectors;
43  
44  import fr.paris.lutece.plugins.genericattributes.business.Response;
45  import fr.paris.lutece.plugins.ticketing.business.ticket.Ticket;
46  import fr.paris.lutece.plugins.ticketing.business.ticket.TicketHome;
47  import fr.paris.lutece.plugins.ticketing.business.ticketpj.TicketPj;
48  import fr.paris.lutece.plugins.ticketing.business.ticketpj.TicketPjHome;
49  import fr.paris.lutece.plugins.ticketing.service.TicketTransfertPjService;
50  import fr.paris.lutece.plugins.ticketing.service.util.PluginConfigurationService;
51  import fr.paris.lutece.plugins.workflow.modules.ticketing.business.anonymisation.IAnonymisationDAO;
52  import fr.paris.lutece.plugins.workflow.modules.ticketing.business.information.ITaskInformationDAO;
53  import fr.paris.lutece.plugins.workflow.modules.ticketing.business.resourcehistory.IResourceHistoryDAO;
54  import fr.paris.lutece.portal.business.file.File;
55  import fr.paris.lutece.portal.business.file.FileHome;
56  import fr.paris.lutece.portal.service.daemon.Daemon;
57  import fr.paris.lutece.portal.service.plugin.Plugin;
58  import fr.paris.lutece.portal.service.plugin.PluginService;
59  import fr.paris.lutece.portal.service.spring.SpringContextService;
60  import fr.paris.lutece.portal.service.util.AppLogService;
61  import fr.paris.lutece.util.sql.TransactionManager;
62  
63  /**
64   * Daemon used to Pj Migration Tickets
65   */
66  public class TicketPjMigrationDaemon extends Daemon
67  {
68      private static final int MAX_TICKETS_PAR_LOT_MIGRATION_PJ = PluginConfigurationService.getInt( PluginConfigurationService.PROPERTY_MIGRATION_TICKET_MAX_PJ, 200 );
69      private static final int           STATUS_TRANSFERT_OK              = 1;
70      private static Plugin              _plugin                          = PluginService.getPlugin( "ticketing" );
71  
72      private static IResourceHistoryDAO _daoResourceHist                 = SpringContextService.getBean( IResourceHistoryDAO.BEAN_SERVICE );
73      private static ITaskInformationDAO _daoTaskInfo                     = SpringContextService.getBean( ITaskInformationDAO.BEAN_SERVICE );
74      private static IAnonymisationDAO   _daoAnonymisation                = SpringContextService.getBean( IAnonymisationDAO.BEAN_SERVICE );
75  
76      /**
77       * Constructor
78       */
79      public TicketPjMigrationDaemon( )
80      {
81          super( );
82      }
83  
84      /**
85       * {@inheritDoc}
86       */
87      @Override
88      public void run( )
89      {
90          StringJoiner sb = new StringJoiner( "\n\r" );
91  
92          sb.add( "Début de la migration" );
93          migratePj( sb );
94          sb.add( "Fin de la migration" );
95          setLastRunLogs( sb.toString( ) );
96      }
97  
98  
99      /**
100      * Migrate file informations to centralize info link to a ticket in ticketing_ticket_pj
101      *
102      * @param sb
103      *            the logs
104      */
105     private void migratePj( StringJoiner sb )
106     {
107         List<Integer> ticketsList = TicketPjHome.getIdTicketListForPjMigration( MAX_TICKETS_PAR_LOT_MIGRATION_PJ );
108 
109         if ( !ticketsList.isEmpty( ) )
110         {
111             sb.add( "nombre de tickets dont les PJ sont à transférer : " + ticketsList.size( ) );
112             for ( Integer idTicket : ticketsList )
113             {
114                 Ticket ticket = TicketHome.findByPrimaryKeyWithoutFiles( idTicket );
115                 if ( !TicketPjHome.isTicketExistInTicketPj( idTicket ) )
116                 {
117                     try
118                     {
119                         TransactionManager.beginTransaction( _plugin );
120 
121                         // recuperation des PJ usager
122                         managePjforS3ForUsager( ticket );
123 
124                         // recuperation des PJ agent par id_history
125                         List<Integer> idResponseTotal = new ArrayList<>( );
126                         List<Integer> idCoreUploadFinal = new ArrayList<>( );
127 
128                         List<Integer> idHistoryList = _daoResourceHist.getIdHistoryListByResource( ticket.getId( ), _plugin );
129                         for ( int idHistory : idHistoryList )
130                         {
131                             // tables workflow_task_ticketing_information et workflow_task_notify_gru_history
132                             idResponseTotal = searchPjFromTaskInfoAndNotifyHistory( idHistory, idResponseTotal );
133 
134                             // table workflow_task_upload_files
135                             idCoreUploadFinal = searchPjFromTaskUploadFiles( idHistory, idCoreUploadFinal );
136                         }
137 
138                         if ( !idResponseTotal.isEmpty( ) )
139                         {
140                             managePjforS3WithIdResponse( idResponseTotal, ticket );
141                         }
142                         if ( !idCoreUploadFinal.isEmpty( ) )
143                         {
144                             // usager false
145                             managePjforS3ForAgent( idCoreUploadFinal, ticket, false );
146                         }
147 
148                         TicketPjHome.updateTransfertStatus( STATUS_TRANSFERT_OK, ticket.getId( ) );
149 
150                     } catch ( Exception e )
151                     {
152                         TransactionManager.rollBack( _plugin );
153                         sb.add( "Annulation de la migration pour le ticket id : " + idTicket );
154                         AppLogService.error( e );
155                     }
156                     TransactionManager.commitTransaction( _plugin );
157                 }
158                 TicketPjHome.updateTransfertStatus( STATUS_TRANSFERT_OK, ticket.getId( ) );
159             }
160         }
161         else
162         {
163             sb.add( "La liste est vide, il n'y a plus de pj à transférer" );
164         }
165     }
166 
167     /**
168      * Search ditinct id_core from workflow_task_upload_files
169      *
170      * @param idHistory
171      *            the id history
172      * @param idCoreUploadFinal
173      *            the list of id file to complete
174      * @return the list of distinct id_file
175      */
176     private List<Integer> searchPjFromTaskUploadFiles( int idHistory, List<Integer> idCoreUploadFinal )
177     {
178         List<Integer> idCoreUploadList = findIsFileFromTaskUploadFiles( idHistory, idCoreUploadFinal );
179         if ( !idCoreUploadList.isEmpty( ) )
180         {
181             idCoreUploadFinal.addAll( idCoreUploadList );
182         }
183         return idCoreUploadFinal.stream( ).distinct( ).collect( Collectors.toList( ) );
184     }
185 
186     /**
187      * Search list of distinct id_response
188      *
189      * @param idHistory
190      *            the id history
191      * @param idResponseTotal
192      *            the list of id response to complete
193      * @return the list of distinct id_response
194      */
195     private List<Integer> searchPjFromTaskInfoAndNotifyHistory( int idHistory, List<Integer> idResponseTotal )
196     {
197         List<Integer> idResponseTaskInfoList = findIdResponseFromTaskInfo( idHistory, idResponseTotal );
198         List<Integer> idResponseNotifyHistoryList = findIdResponseFromNotifyHistory( idHistory, idResponseTotal );
199 
200         if ( !idResponseTaskInfoList.isEmpty( ) )
201         {
202             idResponseTotal.addAll( idResponseTaskInfoList );
203         }
204         if ( !idResponseNotifyHistoryList.isEmpty( ) )
205         {
206             idResponseTotal.addAll( idResponseNotifyHistoryList );
207         }
208         return idResponseTotal.stream( ).distinct( ).collect( Collectors.toList( ) );
209     }
210 
211     /**
212      * Manage pj for usager
213      *
214      * @param ticket
215      *            the ticket
216      */
217     private void managePjforS3ForUsager( Ticket ticket )
218     {
219         List<Integer> usagerAttachment = TicketTransfertPjService.findUsagerAttachment( ticket );
220 
221         if ( ( null != usagerAttachment ) && !usagerAttachment.isEmpty( ) )
222         {
223             usagerAttachment = cleanIdCoreList( usagerAttachment );
224             // usager true
225             insertTicketPjAndCleanIdCoreList( usagerAttachment, ticket, true );
226         }
227     }
228 
229     /**
230      * Keep in list only the id_file still existinf in core_file
231      *
232      * @param idFileList
233      *            the list of id file
234      * @return the clean list of id file
235      */
236     private List<Integer> cleanIdCoreList( List<Integer> idFileList )
237     {
238         List<Integer> cleanidList = new ArrayList<>( );
239         for ( Integer idFile : idFileList )
240         {
241             if ( TicketPjHome.isFileExistInCoreFile( idFile ) )
242             {
243                 File file = FileHome.findByPrimaryKey( idFile );
244                 if ( TicketPjHome.isFileExistInCorePhysicalFile( file.getPhysicalFile( ).getIdPhysicalFile( ) ) )
245                 {
246                     cleanidList.add( idFile );
247                 }
248             }
249         }
250         return cleanidList;
251     }
252 
253     /**
254      * Manage pj for agent
255      *
256      * @param idCoreUploadFinal
257      *            the list of id file to complete
258      * @param ticket
259      *            the ticket
260      * @param isUsagerPj
261      *            true if the pj is from usager otherwise false
262      */
263     private void managePjforS3ForAgent( List<Integer> idCoreUploadFinal, Ticket ticket, boolean isUsagerPj )
264     {
265         idCoreUploadFinal = cleanIdCoreList( idCoreUploadFinal );
266         insertTicketPjAndCleanIdCoreList( idCoreUploadFinal, ticket, isUsagerPj );
267     }
268 
269     /**
270      * Manage Pj fron id_response list
271      *
272      * @param idResponseTotal
273      *            the list of id response to complete
274      * @param ticket
275      *            the ticket
276      */
277     private void managePjforS3WithIdResponse( List<Integer> idResponseTotal, Ticket ticket )
278     {
279         Map<Integer, Integer> coreIdFileAgentFromIdResponseListClean = new HashMap<Integer, Integer>( );
280         // map id_response id_file
281         Map<Integer, Integer> coreIdFileAgentFromIdResponseList = TicketHome.selectCoreFileForAgentPjMap( idResponseTotal );
282         if ( !coreIdFileAgentFromIdResponseList.isEmpty( ) )
283         {
284             for ( Entry<Integer, Integer> entry : coreIdFileAgentFromIdResponseList.entrySet( ) )
285             {
286                 if ( TicketPjHome.isFileExistInCoreFile( entry.getValue( ) ) )
287                 {
288                     coreIdFileAgentFromIdResponseListClean.put( entry.getKey( ), entry.getValue( ) );
289                 }
290             }
291             // usager false
292             insertTicketPjFromMap( coreIdFileAgentFromIdResponseListClean, ticket, false );
293         }
294     }
295 
296     /**
297      * Find the final list of id core from workflow_task_upload_files
298      *
299      * @param idHistory
300      *            the id history
301      * @param idCoreUploadFinal
302      *            the list of id file to complete
303      * @return the final list of id core from workflow_task_upload_files
304      */
305     private List<Integer> findIsFileFromTaskUploadFiles( int idHistory, List<Integer> idCoreUploadFinal )
306     {
307         List<Integer> idCoreUploadFound = TicketTransfertPjService.findUploadFiles( idHistory );
308         idCoreUploadFinal.addAll( idCoreUploadFound );
309         return idCoreUploadFinal;
310     }
311 
312     /**
313      * Find the final list of id core from workflow_task_ticketing_information
314      *
315      * @param idHistory
316      *            the id history
317      * @param idResponseTotal
318      *            the list of id response to complete
319      * @return the final list of id core from workflow_task_ticketing_information
320      */
321     private List<Integer> findIdResponseFromTaskInfo( int idHistory, List<Integer> idResponseTotal )
322     {
323         List<Integer> idResponseFromTaskInfo = ticketTaskInfoService( idHistory );
324         idResponseTotal.addAll( idResponseFromTaskInfo );
325         return idResponseTotal;
326     }
327 
328     /**
329      * Find the final list of id core from workflow_task_notify_gru_history
330      *
331      * @param idHistory
332      *            the id history
333      * @param idResponseTotal
334      *            the list of id response to complete
335      * @return the final list of id core from workflow_task_notify_gru_history
336      */
337     private List<Integer> findIdResponseFromNotifyHistory( int idHistory, List<Integer> idResponseTotal )
338     {
339         List<Integer> idResponseFromNotifyHistory = ticketNotifyHsitoryService( idHistory );
340         idResponseTotal.addAll( idResponseFromNotifyHistory );
341         return idResponseTotal;
342     }
343 
344     /**
345      * Insert pj in ticketing_ticket_pj and update file name in core_file with a id File list
346      *
347      * @param idFileList
348      *            the id pj list
349      * @param ticket
350      *            the ticket
351      * @param isUsagerPj
352      *            true if the pj is from usager otherwise false
353      */
354     private void insertTicketPjAndCleanIdCoreList( List<Integer> idFileList, Ticket ticket, boolean isUsagerPj )
355     {
356         idFileList = cleanIdCoreList( idFileList );
357         insertTicketPj( idFileList, ticket, isUsagerPj );
358     }
359 
360     /**
361      * Insert pj in ticketing_ticket_pj from id file list
362      *
363      * @param idFileList
364      *            the id file list
365      * @param ticket
366      *            the ticket
367      * @param isUsagerPj
368      *            true if the pj is from usager otherwise false
369      */
370     private void insertTicketPj( List<Integer> idFileList, Ticket ticket, boolean isUsagerPj )
371     {
372         if ( ( null != idFileList ) && !idFileList.isEmpty( ) )
373         {
374             for ( Integer idFile : idFileList )
375             {
376                 TicketPj pj = new TicketPj( );
377                 pj.setIdTicket( ticket.getId( ) );
378                 pj.setIdFile( idFile );
379                 for ( Response response : ticket.getListResponse( ) )
380                 {
381                     if ( ( null != response.getFile( ) ) && ( response.getFile( ).getIdFile( ) == idFile ) )
382                     {
383                         pj.setIdResponse( response.getIdResponse( ) );
384                         break;
385                     }
386                 }
387                 pj.setUrlTicketing( "" );
388                 pj.setStockageTicketing( 0 );
389                 pj.setUsager( isUsagerPj );
390                 TicketPjHome.create( pj );
391             }
392         }
393     }
394 
395     /**
396      * Insert pj in ticketing_ticket_pj from id file and id response map
397      *
398      * @param idsMaps
399      *            the id file id response map
400      * @param ticket
401      *            the ticket
402      * @param isUsagerPj
403      *            true if the pj is from usager otherwise false
404      */
405     private void insertTicketPjFromMap( Map<Integer, Integer> idsMaps, Ticket ticket, boolean isUsagerPj )
406     {
407         if ( ( null != idsMaps ) && !idsMaps.isEmpty( ) )
408         {
409             for ( Entry<Integer, Integer> entry : idsMaps.entrySet( ) )
410             {
411                 TicketPj pj = new TicketPj( );
412                 pj.setIdTicket( ticket.getId( ) );
413                 pj.setIdFile( entry.getValue( ) );
414                 pj.setIdResponse( entry.getKey( ) );
415                 pj.setUrlTicketing( "" );
416                 pj.setStockageTicketing( 0 );
417                 pj.setUsager( isUsagerPj );
418                 TicketPjHome.create( pj );
419             }
420         }
421     }
422 
423     /**
424      * find historic data From task info table
425      *
426      * @param ticket
427      *            the ticket to anonymizes
428      */
429     private List<Integer> ticketTaskInfoService( int idHistory )
430     {
431         String valueInfo = _daoTaskInfo.getInfoHistoryValueByIdHistory( idHistory, _plugin );
432         List<Integer> idResponseTotal = new ArrayList<>( );
433         if ( !valueInfo.isEmpty( ) )
434         {
435             String valueInfoUpdated = _daoTaskInfo.getInfoHistoryValueByIdHistory( idHistory, _plugin );
436             if ( valueInfoUpdated.contains( "a href=" ) )
437             {
438                 List<Integer> idResponseListForAgent = TicketTransfertPjService.extractIdResponse( valueInfoUpdated );
439                 idResponseTotal.addAll( idResponseListForAgent );
440             }
441         }
442         return idResponseTotal;
443     }
444 
445     /**
446      * find historic data From task info table
447      *
448      * @param ticket
449      *            the ticket to anonymizes
450      */
451     private List<Integer> ticketNotifyHsitoryService( int idHistory )
452     {
453         Map<String, String> valueNotifyMessages = _daoAnonymisation.loadMessageNotifyHIstoryTotal( idHistory, _plugin );
454         List<Integer> idResponseTotal = new ArrayList<>( );
455         if ( ( null != valueNotifyMessages ) && !valueNotifyMessages.isEmpty( ) )
456         {
457             for ( Map.Entry<String, String> mapEntry : valueNotifyMessages.entrySet( ) )
458             {
459                 if ( ( null != mapEntry.getValue( ) ) && mapEntry.getValue( ).contains( "a href=" ) )
460                 {
461                     List<Integer> idResponseListForAgent = TicketTransfertPjService.extractIdResponse( mapEntry.getValue( ) );
462                     idResponseTotal.addAll( idResponseListForAgent );
463                 }
464             }
465         }
466         return idResponseTotal;
467     }
468 }