installation du module RH
This commit is contained in:
78
app/Http/Controllers/Admin/JobPositionAiHelperController.php
Normal file
78
app/Http/Controllers/Admin/JobPositionAiHelperController.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class JobPositionAiHelperController extends Controller
|
||||
{
|
||||
public function generate(Request $request)
|
||||
{
|
||||
if (!auth()->user()->isAdmin()) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$request->validate([
|
||||
'title' => 'required|string',
|
||||
'description' => 'required|string',
|
||||
]);
|
||||
|
||||
$prompt = "Tu es un expert en ingénierie logicielle spécialisé dans la Fonction Publique Territoriale (FPT) et le Code Général de la Fonction Publique (CGFP).
|
||||
Mon application permet de créer des offres d'emploi. Je n'ai pas encore de backend RH métier. Ton rôle est de transformer une saisie utilisateur (souvent incomplète) en un objet JSON structuré respectant strictement les obligations réglementaires françaises (DVE, publicité légale).
|
||||
|
||||
### INPUT UTILISATEUR :
|
||||
Titre : {$request->title}
|
||||
Description : {$request->description}
|
||||
|
||||
### TES MISSIONS :
|
||||
1. ANALYSE STATUTAIRE : Identifie automatiquement la catégorie (A, B, C), le cadre d'emplois et les grades possibles selon l'intitulé du poste.
|
||||
2. COMPLÉTION RÉGLEMENTAIRE : Génère les mentions obligatoires manquantes (fondements juridiques pour les contractuels, références au CGFP).
|
||||
3. STRUCTURATION DES DONNÉES : Retourne uniquement un objet JSON contenant :
|
||||
- \"infos_poste\" : {intitule, categorie, cadre_emplois, grade_mini, grade_maxi}
|
||||
- \"conformite\" : {fondement_juridique_recrutement, mentions_legales_obligatoires: []}
|
||||
- \"publication\" : {support_obligatoire: \"Choisir le service public / Emploi Territorial\", delai_affichage_minimal: \"30 jours\"}
|
||||
- \"fiche_synthese\" : Un texte complet de l'annonce, richement formaté en Markdown (avec des titres ###, des puces -, et du texte en **gras** pour optimiser l'attractivité et le SEO), tout en restant conforme au droit.
|
||||
|
||||
### CONTRAINTES :
|
||||
- Ne propose que des cadres d'emplois existants dans la FPT (ex: Adjoint technique, Rédacteur, Attaché).
|
||||
- Si le poste semble ouvert aux contractuels, précise l'article L332-8 ou L332-14 du CGFP approprié.
|
||||
- L'ensemble de ta réponse doit être un objet JSON brut. Ne mets PAS de blocs de code markdown (comme ```json) autour de la réponse, retourne juste le JSON. Le contenu de la clé `fiche_synthese` DOIT cependant contenir du formatage Markdown interne.";
|
||||
|
||||
$apiKey = env('GEMINI_API_KEY');
|
||||
if (!$apiKey) {
|
||||
return response()->json(['error' => 'API Key non configurée'], 500);
|
||||
}
|
||||
|
||||
try {
|
||||
$response = Http::timeout(60)->post("https://generativelanguage.googleapis.com/v1beta/models/gemini-3.1-flash-lite-preview:generateContent?key={$apiKey}", [
|
||||
'generationConfig' => [
|
||||
'temperature' => 0.2,
|
||||
'responseMimeType' => 'application/json'
|
||||
],
|
||||
'contents' => [
|
||||
['role' => 'user', 'parts' => [['text' => $prompt]]]
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->successful()) {
|
||||
$text = $response->json('candidates.0.content.parts.0.text');
|
||||
// Extract JSON if it contains markdown formatting
|
||||
preg_match('/\{.*\}/s', $text, $matches);
|
||||
$json = $matches[0] ?? $text;
|
||||
|
||||
return response()->json(json_decode($json, true));
|
||||
}
|
||||
|
||||
Log::error("Gemini API Error: " . $response->body());
|
||||
return response()->json(['error' => 'Erreur de génération IA'], 500);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error("Gemini Connection Failed: " . $e->getMessage());
|
||||
return response()->json(['error' => 'Erreur de connexion à l\'IA'], 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,7 @@ class JobPositionController extends Controller
|
||||
'requirements' => 'nullable|array',
|
||||
'ai_prompt' => 'nullable|string',
|
||||
'ai_bypass_base_prompt' => 'boolean',
|
||||
'fpt_metadata' => 'nullable|array',
|
||||
'tenant_id' => 'nullable|exists:tenants,id',
|
||||
'quiz_ids' => 'nullable|array',
|
||||
'quiz_ids.*' => 'exists:quizzes,id',
|
||||
@@ -40,6 +41,7 @@ class JobPositionController extends Controller
|
||||
'requirements' => $request->requirements,
|
||||
'ai_prompt' => $request->ai_prompt,
|
||||
'ai_bypass_base_prompt' => $request->boolean('ai_bypass_base_prompt'),
|
||||
'fpt_metadata' => $request->fpt_metadata,
|
||||
'tenant_id' => auth()->user()->isSuperAdmin() ? $request->tenant_id : auth()->user()->tenant_id,
|
||||
]);
|
||||
|
||||
@@ -58,6 +60,7 @@ class JobPositionController extends Controller
|
||||
'requirements' => 'nullable|array',
|
||||
'ai_prompt' => 'nullable|string',
|
||||
'ai_bypass_base_prompt' => 'boolean',
|
||||
'fpt_metadata' => 'nullable|array',
|
||||
'tenant_id' => 'nullable|exists:tenants,id',
|
||||
'quiz_ids' => 'nullable|array',
|
||||
'quiz_ids.*' => 'exists:quizzes,id',
|
||||
@@ -69,6 +72,7 @@ class JobPositionController extends Controller
|
||||
'requirements' => $request->requirements,
|
||||
'ai_prompt' => $request->ai_prompt,
|
||||
'ai_bypass_base_prompt' => $request->boolean('ai_bypass_base_prompt'),
|
||||
'fpt_metadata' => $request->fpt_metadata,
|
||||
'tenant_id' => auth()->user()->isSuperAdmin() ? $request->tenant_id : auth()->user()->tenant_id,
|
||||
]);
|
||||
|
||||
|
||||
@@ -16,7 +16,11 @@ class PublicJobApplicationController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$jobs = JobPosition::with('tenant')->orderBy('created_at', 'desc')->get();
|
||||
$jobs = JobPosition::with('tenant')->orderBy('created_at', 'desc')->get()->map(function($job) {
|
||||
$job->description = strip_tags(\Illuminate\Support\Str::markdown($job->description));
|
||||
return $job;
|
||||
});
|
||||
|
||||
return Inertia::render('Public/Jobs/Index', [
|
||||
'jobs' => $jobs
|
||||
]);
|
||||
@@ -24,8 +28,11 @@ class PublicJobApplicationController extends Controller
|
||||
|
||||
public function show(JobPosition $jobPosition)
|
||||
{
|
||||
$data = $jobPosition->toArray();
|
||||
$data['description_html'] = \Illuminate\Support\Str::markdown($jobPosition->description);
|
||||
|
||||
return Inertia::render('Public/Jobs/Show', [
|
||||
'jobPosition' => $jobPosition
|
||||
'jobPosition' => $data
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
||||
use App\Traits\BelongsToTenant;
|
||||
|
||||
#[Fillable(['title', 'description', 'requirements', 'ai_prompt', 'ai_bypass_base_prompt', 'gemini_cache_id', 'gemini_cache_expires_at', 'tenant_id'])]
|
||||
#[Fillable(['title', 'description', 'requirements', 'ai_prompt', 'ai_bypass_base_prompt', 'gemini_cache_id', 'gemini_cache_expires_at', 'tenant_id', 'fpt_metadata'])]
|
||||
class JobPosition extends Model
|
||||
{
|
||||
use HasFactory, BelongsToTenant;
|
||||
@@ -18,6 +18,7 @@ class JobPosition extends Model
|
||||
'requirements' => 'array',
|
||||
'ai_bypass_base_prompt' => 'boolean',
|
||||
'gemini_cache_expires_at' => 'datetime',
|
||||
'fpt_metadata' => 'array',
|
||||
];
|
||||
|
||||
public function candidates(): HasMany
|
||||
|
||||
Reference in New Issue
Block a user