2626
2727namespace Seatplus \Eveapi \Jobs \Character ;
2828
29+ use Illuminate \Support \Carbon ;
2930use Illuminate \Support \Collection ;
3031use Illuminate \Support \Facades \Cache ;
31- use Illuminate \ Support \ Facades \ Redis ;
32+ use Seatplus \ EsiClient \ DataTransferObjects \ EsiResponse ;
3233use Seatplus \EsiClient \Exceptions \RequestFailedException ;
3334use Seatplus \Eveapi \Esi \HasRequestBodyInterface ;
3435use Seatplus \Eveapi \Jobs \Alliances \AllianceInfoJob ;
3536use Seatplus \Eveapi \Jobs \Corporation \CorporationInfoJob ;
3637use Seatplus \Eveapi \Jobs \EsiBase ;
3738use Seatplus \Eveapi \Models \Character \CharacterAffiliation ;
38- use Seatplus \Eveapi \Services \Jobs \CharacterAffiliationService ;
3939use Seatplus \Eveapi \Traits \HasRequestBody ;
4040
4141class CharacterAffiliationJob extends EsiBase implements HasRequestBodyInterface
@@ -44,7 +44,12 @@ class CharacterAffiliationJob extends EsiBase implements HasRequestBodyInterface
4444
4545 private array $ manual_ids = [];
4646
47- public function __construct (int |array |null $ character_ids = null )
47+ private Collection $ character_affiliations ;
48+
49+ /**
50+ * @throws \Throwable
51+ */
52+ public function __construct (int |array $ character_ids )
4853 {
4954 parent ::__construct (
5055 method: 'post ' ,
@@ -53,6 +58,11 @@ public function __construct(int|array|null $character_ids = null)
5358 );
5459
5560 $ this ->setManualIds ($ character_ids );
61+
62+ // throw error if manual_ids is not larger than 1000
63+ throw_unless (count ($ this ->manual_ids ) <= 1000 , new \Exception ('Character ids must not exceed 1000 ' ));
64+
65+ $ this ->character_affiliations = collect ();
5666 }
5767
5868 public function tags (): array
@@ -63,125 +73,61 @@ public function tags(): array
6373 ];
6474 }
6575
66- /**
67- * Get the middleware the job should pass through.
68- */
69- public function middleware (): array
70- {
71- return [
72- ...parent ::middleware (),
73- ];
74- }
75-
7676 /**
7777 * Execute the job.
7878 *
7979 * @throws \Exception
8080 */
8181 public function executeJob (): void
8282 {
83- if ($ this ->manual_ids ) {
84- $ this ->updateOrCreateCharacterAffiliations ($ this ->manual_ids );
85- }
8683
87- if (! $ this ->manual_ids ) {
88- Redis::throttle ('character_affiliations ' )
89- // allow one job to process every 5 minutes
90- ->block (0 )->allow (1 )->every (5 * 60 )
91- ->then (function () {
92- collect ()
93- ->merge ($ this ->getIdsToUpdateFromCache ())
94- ->merge ($ this ->getIdsToUpdateFromDatabase ())
95- ->chunk (1000 )
96- ->each (fn (Collection $ chunk ) => $ this ->updateOrCreateCharacterAffiliations ($ chunk ->toArray ()));
97- }, fn () => $ this ->delete ());
98- }
99- }
84+ $ this ->updateOrCreateCharacterAffiliations ($ this ->getManualIds ());
10085
101- private function getIdsToUpdateFromCache (): Collection
102- {
103- return CharacterAffiliationService::make ()->retrieve ()->unique ();
86+ CharacterAffiliation::query ()->upsert (
87+ $ this ->character_affiliations ->toArray (),
88+ ['character_id ' ],
89+ ['corporation_id ' , 'alliance_id ' , 'faction_id ' , 'last_pulled ' ]
90+ );
91+
92+ $ this ->followUp ();
10493 }
10594
106- private function getIdsToUpdateFromDatabase ( ): Collection
95+ public function processResponse ( EsiResponse $ response , Carbon $ timestamp ): void
10796 {
108- return CharacterAffiliation::query ()
109- // only those who were not pulled within the last hour
110- ->where ('last_pulled ' , '<= ' , now ()->subHour ()->toDateTimeString ())
111- // and don't try doomheimed characters
112- ->where ('corporation_id ' , '<> ' , 1_000_001 )
113- ->pluck ('character_id ' );
97+ collect ($ response )
98+ ->each (fn (object $ result ) => $ this ->character_affiliations ->push (
99+ [
100+ 'character_id ' => $ result ->character_id ,
101+ 'corporation_id ' => $ result ->corporation_id ,
102+ 'alliance_id ' => data_get ($ result , 'alliance_id ' ),
103+ 'faction_id ' => data_get ($ result , 'faction_id ' ),
104+ 'last_pulled ' => $ timestamp ,
105+ ]
106+ ));
114107 }
115108
116109 private function updateOrCreateCharacterAffiliations (array $ character_ids ): void
117110 {
118111 $ this ->setRequestBody ($ character_ids );
119-
120112 $ timestamp = now ();
121113
122- $ character_affiliations = collect ();
123-
124114 // try to get the character affiliations from the esi endpoint
125115 try {
126116 $ response = $ this ->retrieve ();
127117
128- collect ($ response )
129- ->each (fn (object $ result ) => $ character_affiliations ->push (
130- [
131- 'character_id ' => $ result ->character_id ,
132- 'corporation_id ' => $ result ->corporation_id ,
133- 'alliance_id ' => data_get ($ result , 'alliance_id ' ),
134- 'faction_id ' => data_get ($ result , 'faction_id ' ),
135- 'last_pulled ' => $ timestamp ,
136- ]
137- ));
118+ $ this ->processResponse ($ response , $ timestamp );
138119 } catch (RequestFailedException $ exception ) {
139- // if the request fails, we perform a binary search to find the character ids that are not valid
140- // if the request fails and the character ids are less than 2, we can assume that the character id is invalid
141- if (count ($ character_ids ) === 1 ) {
142- // dispatch a new character_info job to update the character info if it is member of doomheim
143- CharacterInfoJob::dispatch ($ character_ids [0 ])->onQueue ('low ' );
144-
145- // cache the invalid character id for 1 day
146- // first get the cached invalid character ids
147- $ invalid_character_ids = Cache::get ('invalid_character_ids ' , []);
148- // add the invalid character id to the array
149- $ invalid_character_ids [] = $ character_ids [0 ];
150- // cache the array
151- Cache::put ('invalid_character_ids ' , $ invalid_character_ids , 60 * 24 );
152-
153- return ;
154- }
155-
156- // if the request fails and the character ids are more than 2, we perform a binary search to find the invalid character ids
157- $ half = (int ) ceil (count ($ character_ids ) / 2 );
158- $ first_half = array_slice ($ character_ids , 0 , $ half );
159- $ second_half = array_slice ($ character_ids , $ half );
160-
161- $ this ->updateOrCreateCharacterAffiliations ($ first_half );
162- $ this ->updateOrCreateCharacterAffiliations ($ second_half );
120+ $ this ->handleFailedRequest ($ character_ids );
163121 }
164-
165- CharacterAffiliation::upsert (
166- $ character_affiliations ->toArray (),
167- ['character_id ' ],
168- ['corporation_id ' , 'alliance_id ' , 'faction_id ' , 'last_pulled ' ]
169- );
170-
171- $ this ->followUp ();
172122 }
173123
174124 public function getManualIds (): array
175125 {
176126 return $ this ->manual_ids ;
177127 }
178128
179- public function setManualIds (int |array | null $ manual_ids ): void
129+ public function setManualIds (int |array $ manual_ids ): void
180130 {
181- if (is_null ($ manual_ids )) {
182- return ;
183- }
184-
185131 $ manual_ids = is_array ($ manual_ids ) ? $ manual_ids : [$ manual_ids ];
186132
187133 $ this ->manual_ids = $ manual_ids ;
@@ -213,4 +159,44 @@ private function getMissingAlliances(): void
213159 ->unique ()
214160 ->each (fn (int $ alliance_id ) => AllianceInfoJob::dispatch ($ alliance_id )->onQueue ('high ' ));
215161 }
162+
163+ private function handleFailedRequest (array $ character_ids ): void
164+ {
165+ // if the request fails and the character ids are less than 2, we can assume that the character id is invalid
166+ if (count ($ character_ids ) === 1 ) {
167+ $ this ->handleSingleIdException ($ character_ids [0 ]);
168+
169+ return ;
170+ }
171+
172+ // if the request fails, we perform a binary search to find the character ids that are not valid
173+ $ this ->handleMultipleIdsException ($ character_ids );
174+
175+ }
176+
177+ private function handleSingleIdException (int $ character_id ): void
178+ {
179+
180+ // dispatch a new character_info job to update the character info if it is member of doomheim
181+ CharacterInfoJob::dispatch ($ character_id )->onQueue ('low ' );
182+
183+ // cache the invalid character id for 1 day
184+ // first get the cached invalid character ids
185+ $ invalid_character_ids = Cache::get ('invalid_character_ids ' , []);
186+ // add the invalid character id to the array
187+ $ invalid_character_ids [] = $ character_id ;
188+ // cache the array
189+ Cache::put ('invalid_character_ids ' , $ invalid_character_ids , 60 * 24 );
190+ }
191+
192+ private function handleMultipleIdsException (array $ character_ids ): void
193+ {
194+ // if the request fails and the character ids are more than 2, we perform a binary search to find the invalid character ids
195+ $ half = (int ) ceil (count ($ character_ids ) / 2 );
196+ $ first_half = array_slice ($ character_ids , 0 , $ half );
197+ $ second_half = array_slice ($ character_ids , $ half );
198+
199+ $ this ->updateOrCreateCharacterAffiliations ($ first_half );
200+ $ this ->updateOrCreateCharacterAffiliations ($ second_half );
201+ }
216202}
0 commit comments