Flow Tester AI 프롬프트 템플릿 개선

- config.apiKey 필드를 JSON에서 제거 (서버 자동 주입)
- config.baseUrl을 빈 문자열로 설정 (서버 기본값 사용)
- 프롬프트 템플릿에 더 명확한 규칙 추가
- 로그인 스텝 포함한 완전한 예시 제공
- 예시 프롬프트 간소화
This commit is contained in:
2025-12-05 14:19:59 +09:00
parent 5c892c1ed9
commit 858ce6194d
6 changed files with 150 additions and 75 deletions

View File

@@ -168,33 +168,44 @@ public function validateJson(Request $request)
]);
}
// meta 정보 추출
// meta 정보 추출 (최상위 또는 meta 객체에서)
$meta = $data['meta'] ?? [];
$tags = $meta['tags'] ?? [];
// 카테고리 추론: tags[0] 또는 첫 번째 endpoint에서 추출
$category = $tags[0] ?? null;
// 카테고리 추론: 최상위 category > tags[0] > endpoint에서 추출 (login 제외)
$category = $data['category'] ?? $tags[0] ?? null;
if (! $category && ! empty($data['steps'])) {
$firstEndpoint = $data['steps'][0]['endpoint'] ?? '';
// /item-master/pages → item-master
if (preg_match('#^/([^/]+)#', $firstEndpoint, $matches)) {
$category = $matches[1];
// login, auth, refresh, logout 등 인증 관련 엔드포인트는 건너뛰고 추출
$authEndpoints = ['/login', '/logout', '/refresh', '/auth'];
foreach ($data['steps'] as $step) {
$endpoint = $step['endpoint'] ?? '';
// 인증 엔드포인트가 아닌 첫 번째 스텝에서 카테고리 추출
if (! in_array($endpoint, $authEndpoints) && preg_match('#^/([^/]+)#', $endpoint, $matches)) {
$category = $matches[1];
break;
}
}
}
// 이름 추론: meta.name 또는 description 첫 부분
$name = $meta['name'] ?? null;
if (! $name && ! empty($meta['description'])) {
// 설명의 첫 번째 줄 또는 50자까지
$name = mb_substr(strtok($meta['description'], "\n"), 0, 50);
// 이름 추론: 최상위 name > meta.name > description 첫 부분
$name = $data['name'] ?? $meta['name'] ?? null;
if (! $name) {
$description = $data['description'] ?? $meta['description'] ?? null;
if ($description) {
// 설명의 첫 번째 줄 또는 50자까지
$name = mb_substr(strtok($description, "\n"), 0, 50);
}
}
// 설명 추론: 최상위 description > meta.description
$description = $data['description'] ?? $meta['description'] ?? null;
return response()->json([
'valid' => true,
'stepCount' => count($data['steps'] ?? []),
'extracted' => [
'name' => $name,
'description' => $meta['description'] ?? null,
'description' => $description,
'category' => $category,
'author' => $meta['author'] ?? null,
'tags' => $tags,

View File

@@ -4,6 +4,7 @@
use App\Models\Department;
use App\Models\Role;
use App\Models\Tenants\Tenant;
use App\Services\UserService;
use Illuminate\Http\Request;
use Illuminate\View\View;
@@ -33,7 +34,14 @@ public function create(): View
$roles = $tenantId ? Role::where('tenant_id', $tenantId)->orderBy('name')->get() : collect();
$departments = $tenantId ? Department::where('tenant_id', $tenantId)->where('is_active', true)->orderBy('name')->get() : collect();
return view('users.create', compact('roles', 'departments'));
// 본사 테넌트 여부 확인 (본사: 이메일 인증, 그 외: 비밀번호 직접 입력)
$isHQ = false;
if ($tenantId) {
$tenant = Tenant::find($tenantId);
$isHQ = $tenant?->tenant_type === 'HQ';
}
return view('users.create', compact('roles', 'departments', 'isHQ'));
}
/**

View File

@@ -2,6 +2,7 @@
namespace App\Http\Requests;
use App\Models\Tenants\Tenant;
use Illuminate\Foundation\Http\FormRequest;
class StoreUserRequest extends FormRequest
@@ -14,6 +15,21 @@ public function authorize(): bool
return true;
}
/**
* 현재 선택된 테넌트가 본사(HQ)인지 확인
*/
protected function isHQTenant(): bool
{
$tenantId = session('selected_tenant_id');
if (! $tenantId) {
return false;
}
$tenant = Tenant::find($tenantId);
return $tenant?->tenant_type === 'HQ';
}
/**
* Prepare the data for validation.
* 일반관리자가 슈퍼관리자를 생성하려는 경우 is_super_admin 필드 제거
@@ -35,12 +51,11 @@ protected function prepareForValidation(): void
*/
public function rules(): array
{
return [
$rules = [
'user_id' => 'nullable|string|max:50|unique:users,user_id',
'name' => 'required|string|max:100',
'email' => 'required|email|max:255|unique:users,email',
'phone' => 'nullable|string|max:20',
// password는 시스템이 자동 생성하므로 입력 받지 않음
'role' => 'nullable|string|max:50',
'is_active' => 'nullable|boolean',
'is_super_admin' => 'nullable|boolean',
@@ -49,6 +64,13 @@ public function rules(): array
'department_ids' => 'nullable|array',
'department_ids.*' => 'integer|exists:departments,id',
];
// 비본사 테넌트: 비밀번호 직접 입력 필수
if (! $this->isHQTenant()) {
$rules['password'] = 'required|string|min:8|max:100|confirmed';
}
return $rules;
}
/**

View File

@@ -82,15 +82,28 @@ public function getUserById(int $id): ?User
}
/**
* 사용자 생성 (관리자용: 임의 비밀번호 생성 + 메일 발송)
* 사용자 생성
* - 본사(HQ): 임의 비밀번호 생성 + 메일 발송
* - 비본사: 입력된 비밀번호 사용 (메일 발송 안 함)
*/
public function createUser(array $data): User
{
$tenantId = session('selected_tenant_id');
// 임의 비밀번호 생성 (8자리 영문+숫자)
$plainPassword = $this->generateRandomPassword();
$data['password'] = Hash::make($plainPassword);
// 비밀번호 처리: 입력된 비밀번호가 있으면 사용, 없으면 자동 생성
$passwordProvided = ! empty($data['password']);
if ($passwordProvided) {
// 비본사: 입력된 비밀번호 사용
$data['password'] = Hash::make($data['password']);
$plainPassword = null; // 메일 발송하지 않음
} else {
// 본사: 임의 비밀번호 생성 (8자리 영문+숫자)
$plainPassword = $this->generateRandomPassword();
$data['password'] = Hash::make($plainPassword);
}
// password_confirmation은 User 모델의 fillable이 아니므로 제거
unset($data['password_confirmation']);
// is_active 처리
$data['is_active'] = isset($data['is_active']) && $data['is_active'] == '1';
@@ -120,8 +133,10 @@ public function createUser(array $data): User
$this->syncDepartments($user, $tenantId, $departmentIds);
}
// 비밀번호 안내 메일 발송
$this->sendPasswordMail($user, $plainPassword, true);
// 본사만 비밀번호 안내 메일 발송 (비본사는 관리자가 직접 알려줌)
if ($plainPassword !== null) {
$this->sendPasswordMail($user, $plainPassword, true);
}
return $user;
}