可视化的 UI 控制台页面
为了实现前后端分离架构,并且配备一个可视化的 UI 控制台页面,我们需要对代码生成流程进行两项升级:
前端与接口升级: 采用标准的 RESTful API 规范。后端 TP6 仅返回结构化的 JSON 数据(包含状态码、提示信息和数据负载),前端通过 AJAX (Fetch) 进行异步交互。
UI 页面实现: 编写一个基于 HTML5 + Tailwind CSS 的现代化单页面控制台。用户输入需求后,页面会展示美观的“全自动开发”进度条。
以下是为您量身定制的完整落地代码:
第一步:升级 TP6 控制器 (app\controller\CodeFactory.php)
我们需要修改 submit 方法使其支持前端跨域(如果需要),并新增一个查询任务状态/获取生成代码的接口。同时,为了安全起见,我们将 AI 生成的代码存储在本地缓存或数据库中,由前端 UI 呼叫接口来渲染,而不是直接静默写入服务器文件(这样更符合前后端分离的安全规范)。
php
<?php
namespace app\controller;
use app\BaseController;
use think\facade\Http;
use think\facade\Cache;
class CodeFactory extends BaseController
{
// Python CrewAI 服务的 API 地址
private $pythonServiceUrl = 'http://127.0.0';
/**
* 1. 渲染/输出 UI 界面
* URL: http://your-domain/codegen/ui
*/
public function index()
{
// 渲染单页面静态 UI(代码见第二步)
return view('index');
}
/**
* 2. API: 接收前端需求并下发给 CrewAI
* URL: POST http://your-domain/codegen/api/submit
*/
public function submit()
{
$requirement = $this->request->param('requirement');
if (empty($requirement)) {
return json(['code' => 400, 'msg' => '请输入您的业务需求描述']);
}
// 生成一个唯一的任务 ID 供前端后续轮询
$taskId = 'task_' . uniqid() . '_' . rand(1000, 9999);
// 初始化任务状态为 "processing" (处理中)
Cache::set($taskId, ['status' => 'processing', 'progress' => 30, 'data' => null], 1800);
// 动态生成回调地址,并在 URL 中携带这个 taskId
$callbackUrl = url('codegen/api/callback', ['task_id' => $taskId])->suffix(false)->domain(true)->build();
try {
// 呼叫 CrewAI 异步研发团队
Http::post($this->pythonServiceUrl, [
'requirement' => $requirement . '(注意:请务必按照前后端分离的纯 API 格式编写控制器,返回 json 格式数据,包含 code, msg, data 字段)',
'callback_url' => $callbackUrl
]);
return json([
'code' => 200,
'msg' => 'AI 研发团队已成功组建,正在为您紧急编写代码...',
'data' => ['task_id' => $taskId]
]);
} catch (\Exception $e) {
return json(['code' => 500, 'msg' => '连接 AI 服务失败:' . $e->getMessage()]);
}
}
/**
* 3. API: 接收 CrewAI 回调并将结果写入缓存
* URL: POST http://your-domain/codegen/api/callback?task_id=xxxx
*/
public function receiveCode()
{
$taskId = $this->request->param('task_id');
$payload = $this->request->param();
if (empty($taskId) || !isset($payload['code_package'])) {
return json(['code' => 400, 'msg' => '无效的回调参数']);
}
// 更新缓存状态为 "success" (成功) 并存入代码数据
Cache::set($taskId, [
'status' => 'success',
'progress' => 100,
'data' => $payload['code_package']
], 1800);
return json(['code' => 200, 'msg' => 'TP6 已安全接收代码资产']);
}
/**
* 4. API: 供前端 UI 轮询开发进度的接口
* URL: GET http://your-domain/codegen/api/status?task_id=xxxx
*/
public function getStatus()
{
$taskId = $this->request->param('task_id');
if (empty($taskId)) {
return json(['code' => 400, 'msg' => '缺少任务ID']);
}
$task = Cache::get($taskId);
if (!$task) {
return json(['code' => 404, 'msg' => '任务不存在或已过期']);
}
return json([
'code' => 200,
'msg' => '获取状态成功',
'data' => $task
]);
}
}
请谨慎使用此类代码。
第二步:编写 UI 控制台页面 (app/view/index.html)
这是一个基于 Tailwind CSS 构建的单页面应用(SPA)前端。它包含了:业务需求提交表单、动态进度条、以及按标签页(SQL、Controller、Model、Validate)分类的高亮代码展示区。
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CrewAI & TP6 自动代码生成控制台</title>
<!-- 引入 Tailwind CSS 提升 UI 美观度 -->
<script src="https://jsdelivr.net"></script>
<style>
.tab-content { display: none; }
.tab-content.active { display: block; }
.tab-btn.active { border-color: #3b82f6; color: #3b82f6; font-weight: bold; }
</style>
</head>
<body class="bg-gray-50 min-h-screen font-sans antialiased text-gray-900">
<div class="max-w-6xl mx-auto px-4 py-8">
<!-- 头部 Header -->
<header class="flex items-center justify-between pb-6 mb-8 border-b border-gray-200">
<div>
<h1 class="text-3xl font-extrabold tracking-tight text-gray-900">AI 业务代码自动化生成控制台</h1>
<p class="text-sm text-gray-500 mt-1">基于 CrewAI (多智能体) & ThinkPHP 6 前后端分离架构</p>
</div>
<span class="px-3 py-1 bg-blue-100 text-blue-800 text-xs font-semibold rounded-full">v1.0 生产级</span>
</header>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
<!-- 左侧:输入控制区 -->
<div class="bg-white p-6 rounded-2xl shadow-xs border border-gray-200 h-fit">
<h2 class="text-lg font-bold mb-4 text-gray-800">🛠 需求下发配置</h2>
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-2">业务模块描述</label>
<textarea id="requirement" rows="6" class="w-full p-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-blue-500 focus:border-blue-500 outline-none transition-all text-sm" placeholder="例如:创建一个商城商品管理模块。包含商品名称、价格、库存。需要支持分类筛选,控制器中所有方法必须返回统一的 JSON 格式。"></textarea>
</div>
<button id="submitBtn" onclick="startGeneration()" class="w-full bg-blue-600 hover:bg-blue-700 text-white font-medium py-3 px-4 rounded-xl transition-colors cursor-pointer shadow-sm flex items-center justify-center gap-2">
🚀 召集 AI 团队开发
</button>
<!-- 进度条面板 (默认隐藏) -->
<div id="progressPanel" class="mt-6 pt-6 border-t border-gray-100 hidden">
<div class="flex justify-between text-xs font-medium text-gray-500 mb-1">
<span id="statusText">正在召集架构师与程序员...</span>
<span id="progressPercent">30%</span>
</div>
<div class="w-full bg-gray-200 rounded-full h-2">
<div id="progressBar" class="bg-blue-600 h-2 rounded-full transition-all duration-500" style="width: 30%"></div>
</div>
<p class="text-xs text-amber-600 mt-2 bg-amber-50 p-2 rounded border border-amber-100 animate-pulse">
💡 大模型生成完整业务逻辑大约需要 30秒 ~ 2分钟,请勿关闭页面。
</p>
</div>
</div>
<!-- 右侧:成果输出区 -->
<div class="lg:col-span-2 bg-white rounded-2xl shadow-xs border border-gray-200 overflow-hidden flex flex-col min-h-[500px]">
<!-- 标签页头部 -->
<div class="bg-gray-50 border-b border-gray-200 px-4 flex gap-2">
<button onclick="switchTab('sql')" class="tab-btn active px-4 py-3 text-sm font-medium border-b-2 border-transparent hover:text-gray-700 cursor-pointer">📄 MySQL 建表</button>
<button onclick="switchTab('controller')" class="tab-btn px-4 py-3 text-sm font-medium border-b-2 border-transparent hover:text-gray-700 cursor-pointer">📦 Controller</button>
<button onclick="switchTab('model')" class="tab-btn px-4 py-3 text-sm font-medium border-b-2 border-transparent hover:text-gray-700 cursor-pointer">💎 Model</button>
<button onclick="switchTab('validate')" class="tab-btn px-4 py-3 text-sm font-medium border-b-2 border-transparent hover:text-gray-700 cursor-pointer">🛡 Validate</button>
</div>
<!-- 代码展示内容区 -->
<div class="p-6 flex-1 bg-gray-900 text-gray-100 font-mono text-sm overflow-auto max-h-[600px]">
<div id="sql" class="tab-content active"><span class="text-gray-500">// AI 编写的 SQL 脚本将在这里展示</span></div>
<div id="controller" class="tab-content"><span class="text-gray-500">// 前后端分离 API 控制器代码将在这里展示</span></div>
<div id="model" class="tab-content"><span class="text-gray-500">// 模型层代码将在这里展示</span></div>
<div id="validate" class="tab-content"><span class="text-gray-500">// 验证器层代码将在这里展示</span></div>
</div>
</div>
</div>
</div>
<!-- 前端核心逻辑:异步联调与轮询 -->
<script>
let timer = null;
// 标签页切换逻辑
function switchTab(tabId) {
document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active'));
document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active'));
event.target.classList.add('active');
document.getElementById(tabId).classList.add('active');
}
// 发起开发任务
async function startGeneration() {
const reqText = document.getElementById('requirement').value.trim();
if(!reqText) return alert('请先输入业务模块的需求描述!');
const submitBtn = document.getElementById('submitBtn');
submitBtn.disabled = true;
submitBtn.classList.add('opacity-50');
document.getElementById('progressPanel').classList.remove('hidden');
updateProgress(30, '正在分配 Agent 架构师分析需求...');
try {
// 1. 请求 TP6 下发任务
const response = await fetch('/codegen/api/submit', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: `requirement=${encodeURIComponent(reqText)}`
});
const res = await response.json();
if(res.code === 200) {
const taskId = res.data.task_id;
// 2. 启动定时器,每隔 3 秒轮询一次状态
timer = setInterval(() => checkStatus(taskId), 3000);
} else {
alert('下发任务失败:' + res.msg);
resetButton();
}
} catch (e) {
alert('网络请求出错');
resetButton();
}
}
// 轮询状态接口
async function checkStatus(taskId) {
try {
const response = await fetch(`/codegen/api/status?task_id=${taskId}`);
const res = await response.json();
if(res.code === 200) {
if (res.data.status === 'processing') {
// 模拟进度条缓慢增长,给用户良好体验
let currentProgress = parseInt(document.getElementById('progressBar').style.width);
if(currentProgress < 90) {
updateProgress(currentProgress + 5, '高级 PHP 工程师正在疯狂敲击键盘编写代码...');
}
} else if (res.data.status === 'success') {
// 生成成功,清除定时器
clearInterval(timer);
updateProgress(100, '代码全部生成成功!已安全缓存。');
resetButton();
// 渲染代码到页面
renderCode(res.data.data);
}
}
} catch (e) {
console.error('轮询失败', e);
}
}
// 更新进度条 UI
function updateProgress(num, text) {
document.getElementById('progressBar').style.width = num + '%';
document.getElementById('progressPercent').innerText = num + '%';
document.getElementById('statusText').innerText = text;
}
function resetButton() {
const submitBtn = document.getElementById('submitBtn');
submitBtn.disabled = false;
submitBtn.classList.remove('opacity-50');
}
// 将 AI 代码填入对应标签页 (替换文本中的特殊 HTML 字符防止错乱)
function renderCode(pkg) {
document.getElementById('sql').innerHTML = `<pre>${escapeHtml(pkg.sql || '未生成')}</pre>`;
document.getElementById('controller').innerHTML = `<pre>${escapeHtml(pkg.controller || '未生成')}</pre>`;
document.getElementById('model').innerHTML = `<pre>${escapeHtml(pkg.model || '未生成')}</pre>`;
document.getElementById('validate').innerHTML = `<pre>${escapeHtml(pkg.validate || '未生成')}</pre>`;
}
function escapeHtml(string) {
return String(string).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
}
</script>
</body>
</html>
请谨慎使用此类代码。
第三步:更新 TP6 路由配置 (route\app.php)
在您的路由文件中,配置以下统一路由规则:
php
use think\facade\Route;
Route::group('codegen', function () {
// 渲染控制台 UI 页面
Route::get('ui', 'CodeFactory/index');
// 前后端分离 API 接口
Route::post('api/submit', 'CodeFactory/submit');
Route::get('api/status', 'CodeFactory/getStatus');
// 供 Python CrewAI 调用的跨服务回调
Route::post('api/callback', 'CodeFactory/receiveCode');
});
请谨慎使用此类代码。
有了这套架构,您的前端不仅可以完美监控 AI 的工作进度,生成的 Controller 也会完全遵循您要求的前后端分离风格(使用 return json([...]) 模式进行响应)。
针对生成的代码,我们需要考虑持久化和可追溯性:
是否需要我帮您编写“一键下载代码 ZIP 压缩包”的 TP6 后端功能,以便您直接解压使用?
还是需要我帮您加上“一键将 SQL 写入真实 MySQL 数据库”的闭环执行代码?