Yii2 AI 配置助手开发全流程教程:从结构设计到接口实现
2025-04-07
这是一篇基于实际协作开发过程沉淀下来的教程,记录了从零构建一个可管理、可校验、可调用的企业级 AI 配置助手模块的完整流程。教程以 Yii2 框架为基础,前后端通过 UUID 管理配置记录,支持查询与更新,默认数据结构清晰,验证机制严密,整体风格清晰明了,可开箱即用。
✅ 一、设计目标
由我(作者)提出的核心目标如下:
以企业为单位存储和管理 AI 能力配置信息(如调用频率、Token 限额、记忆能力等);
配置结构采用 JSON 存储,字段有默认值且必须完整,要求接收前端传入数组;
提供查询、更新两个接口,基于 UUID 定位唯一记录;
保证配置的读取为结构化数组,存储前自动 encode,保存时自动验证字段完整性与格式;
所有逻辑都聚焦在模型
HeadOfficeCapacityModel
内完成,接口仅做调度。
✅ 二、Mock 数据结构设计(AI 配置字段)
我们约定 ai_config
字段为 JSON,结构如下:
{ "enabled": true, "request_interval_sec": 60, "token_limit": 100000, "token_used": 4300, "access_start_time": "2025-03-01 00:00:00", "access_end_time": "2025-12-31 23:59:59", "memory": { "enabled": true, "depth": 1, "cross_session": false }, "sensitive_fuzzy_match": true, "suggested_keywords_enabled": true, "max_active_users": 100, "same_question_answer": true, "pre_defined_answer": true, "ai_answer_timeout": 86400 }
我们将这个结构转换为 PHP 注释,方便开发参考:
/** * @var array $ai_config 示例结构: * [ * 'enabled' => bool, * 'request_interval_sec' => int, * 'token_limit' => int, * 'token_used' => int, * 'access_start_time' => string (Y-m-d H:i:s), * 'access_end_time' => string (Y-m-d H:i:s), * 'memory' => [ * 'enabled' => bool, * 'depth' => int, * 'cross_session' => bool * ], * 'sensitive_fuzzy_match' => bool, * 'suggested_keywords_enabled' => bool, * 'max_active_users' => int, * 'same_question_answer' => bool, * 'pre_defined_answer' => bool, * 'ai_answer_timeout' => int * ] */
✅ 三、模型内部处理逻辑(HeadOfficeCapacityModel)
1. 自动转换(在 afterFind
与 beforeSave
中处理)
public function afterFind() { if (is_string($this->ai_config)) { $this->ai_config = Json::decode($this->ai_config); } parent::afterFind(); } public function beforeSave($insert) { if (is_array($this->ai_config)) { $this->validateAiConfig($this->ai_config); $this->ai_config = Json::encode($this->ai_config); } return parent::beforeSave($insert); }
2. 配置校验逻辑(结构完整性 + 类型检查)
protected function validateAiConfig($config) { if (!is_array($config)) { throw new InvalidArgumentException("AI配置必须是数组结构。"); } $requiredKeys = [ 'enabled' => 'boolean', 'request_interval_sec' => 'integer', 'token_limit' => 'integer', 'token_used' => 'integer', 'access_start_time' => 'datetime', 'access_end_time' => 'datetime', 'memory' => 'array', 'sensitive_fuzzy_match' => 'boolean', 'suggested_keywords_enabled' => 'boolean', 'max_active_users' => 'integer', 'same_question_answer' => 'boolean', 'pre_defined_answer' => 'boolean', 'ai_answer_timeout' => 'integer' ]; foreach ($requiredKeys as $key => $type) { if (!array_key_exists($key, $config)) { throw new InvalidArgumentException("缺少字段:{$key}"); } $value = $config[$key]; switch ($type) { case 'boolean': if (!is_bool($value)) throw new InvalidArgumentException("{$key} 应为布尔值。"); break; case 'integer': if (!is_int($value)) throw new InvalidArgumentException("{$key} 应为整数。"); break; case 'datetime': if (!strtotime($value)) throw new InvalidArgumentException("{$key} 应为合法时间字符串。"); break; case 'array': if (!is_array($value)) throw new InvalidArgumentException("{$key} 应为数组。"); break; } } foreach (['enabled' => 'boolean', 'depth' => 'integer', 'cross_session' => 'boolean'] as $key => $type) { if (!array_key_exists($key, $config['memory'])) { throw new InvalidArgumentException("memory 缺少字段:{$key}"); } $value = $config['memory'][$key]; switch ($type) { case 'boolean': if (!is_bool($value)) throw new InvalidArgumentException("memory.{$key} 应为布尔值。"); break; case 'integer': if (!is_int($value)) throw new InvalidArgumentException("memory.{$key} 应为整数。"); break; } } return true; }
3. 查 / 改方法(基于 UUID)
public static function getAiConfigByUuid(string $uuid): ?array { $model = static::findOne(['uuid' => $uuid]); return $model ? $model->ai_config : null; } public static function updateAiConfigByUuid(string $uuid, array $newConfig): bool { $model = static::findOne(['uuid' => $uuid]); if (!$model) return false; $model->ai_config = $newConfig; return $model->save(false); }
✅ 四、接口控制器实现(查询 + 更新)
我们采用 POST 方式,仅实现两个接口:
public function behaviors() { $behaviors = parent::behaviors(); $behaviors['verbs'] = [ 'class' => \yii\filters\VerbFilter::class, 'actions' => [ 'ai-config-view' => ['POST'], 'ai-config-update' => ['POST'], ], ]; return $behaviors; }
查询接口
/** * @route v1/ai-chat-manage/ai-config-view * @name 查询 AI 配置 * @method POST */ public function actionAiConfigView() { $uuid = Yii::$app->request->post('uuid'); if (empty($uuid)) { return $this->errorParam->paramValidity('UUID不能为空', 0); } $config = HeadOfficeCapacityModel::getAiConfigByUuid($uuid); return $config ? $this->errorParam->paramValidity($config, 1) : $this->errorParam->paramValidity('找不到对应记录', 0); }
更新接口(含异常捕获)
/** * @route v1/ai-chat-manage/ai-config-update * @name 更新 AI 配置 * @method POST */ public function actionAiConfigUpdate() { $uuid = Yii::$app->request->post('uuid'); $aiConfig = Yii::$app->request->post('ai_config'); if (empty($uuid)) { return $this->errorParam->paramValidity('UUID不能为空', 0); } if (!is_array($aiConfig)) { return $this->errorParam->paramValidity('ai_config 必须为数组', 0); } try { $success = HeadOfficeCapacityModel::updateAiConfigByUuid($uuid, $aiConfig); return $success ? $this->errorParam->paramValidity('更新成功', 1) : $this->errorParam->paramValidity('更新失败,可能UUID无效或数据未变更', 0); } catch (\Throwable $e) { Yii::error("AI配置更新异常:{$e->getMessage()}", __METHOD__); return $this->errorParam->paramValidity("更新失败:{$e->getMessage()}", 0); } }
✅ 五、总结:一套安全、标准、结构清晰的配置方案
这个 AI 配置助手模块具备以下优势:
JSON 配置统一集中管理,前端传数组、后端结构清晰;
自动 encode/decode,无需业务方操心格式转换;
严格字段验证,防止脏数据写入数据库;
所有操作基于 UUID,支持 SaaS 多企业场景;
接口统一、逻辑清晰、易于维护。
你只需将这套代码复制进你的项目中,即可直接运行并扩展。未来如需增设默认提示词、模型选择器、角色管理、权限开关等模块,只需在 ai_config
中扩展字段结构,并在验证器中同步添加规则即可。
欢迎在此基础上拓展属于你自己的 AI 模块治理体系。
by 楠哥 红尘炼心,知行一体。
发表评论: