Browse Source

feat(ai): 添加AI命令下拉数据接口及OpenAI客户端支持

- 新增 pureIndex 方法用于获取指定类型的AI命令数据
- 添加OpenAI PHP客户端库支持及配置文件
- 实现OpenAiClient工具类封装聊天接口调用
- 配置Redis队列插件及相关日志记录
- 在创作任务服务中集成Redis消息队列发送功能
- 更新路由配置添加AI命令下拉数据访问权限控制
- 引入ApiException异常处理完善错误提示机制
master
zhangf@suq.cn 7 days ago
parent
commit
bc9efd7985
  1. 13
      app/controller/AiCommandController.php
  2. 2
      app/route/route.php
  3. 11
      app/service/AiCommandService.php
  4. 3
      app/service/CreationTaskService.php
  5. 11
      composer.json
  6. 1079
      composer.lock
  7. 19
      config/openai.php
  8. 4
      config/plugin/webman/redis-queue/app.php
  9. 7
      config/plugin/webman/redis-queue/command.php
  10. 32
      config/plugin/webman/redis-queue/log.php
  11. 11
      config/plugin/webman/redis-queue/process.php
  12. 21
      config/plugin/webman/redis-queue/redis.php
  13. 78
      plugin/piadmin/app/utils/openai/OpenAiClient.php

13
app/controller/AiCommandController.php

@ -3,6 +3,7 @@ namespace app\controller;
use app\service\AiCommandService;
use app\validate\AiCommandValidate;
use plugin\piadmin\app\exception\ApiException;
use plugin\piadmin\app\utils\ArrayUtils;
use support\Response;
@ -54,5 +55,15 @@ class AiCommandController
return success($service->deleteData($id));
}
public function pureIndex(AiCommandService $service)
{
$type = input('type');
if (!$type) {
throw new ApiException('请选择类型');
}
if (!in_array($type, [1,2,3])) {
throw new ApiException('请选择正确的类型');
}
return success($service->selectData($type));
}
}

2
app/route/route.php

@ -92,6 +92,8 @@ Route::group('/service/v1', function () {
Route::get('/read', [AiCommandController::class, 'read'])->setParams(['perm' => ['aiCommandRead']]);
//删除
Route::post('/delete', [AiCommandController::class, 'delete'])->setParams(['perm' => ['aiCommandDelete']]);
//下拉数据
Route::get('/pure/index', [AiCommandController::class, 'pureIndex'])->setParams(['perm' => 'aiCommandPureIndex']);
});
//AI创作投喂内容

11
app/service/AiCommandService.php

@ -114,4 +114,15 @@ class AiCommandService extends BaseService
return ['id' => $id];
}
public function selectData($type)
{
$query = [
'delete_time' => 0
];
if (isNotBlank($type)) {
$query[] = ['type', '=', $type];
}
$list = $this->dao->getList($query);
return $list;
}
}

3
app/service/CreationTaskService.php

@ -8,6 +8,7 @@ use app\dao\DistillationWordDao;
use plugin\piadmin\app\base\BaseService;
use plugin\piadmin\app\exception\ApiException;
use support\think\Db;
use Webman\RedisQueue\Redis;
class CreationTaskService extends BaseService
{
@ -42,6 +43,8 @@ class CreationTaskService extends BaseService
}
$data = $this->dao->save($params);
Db::commit();
//投递消息
Redis::send('creation-task', ['task_id' => $data['id']]);
} catch (\Exception $exception) {
Db::rollback();
throw new ApiException($exception->getMessage());

11
composer.json

@ -35,8 +35,10 @@
"x2nx/webman-migrate": "^0.0.8",
"tinywan/storage": "^1.1",
"aliyuncs/oss-sdk-php": "^2.7",
"openai-php/client": "^0.9.2",
"ext-simplexml": "*",
"ext-dom": "*"
"ext-dom": "*",
"webman/redis-queue": "^2.1"
},
"suggest": {
"ext-event": "For better performance. "
@ -61,5 +63,10 @@
]
},
"minimum-stability": "dev",
"prefer-stable": true
"prefer-stable": true,
"config": {
"allow-plugins": {
"php-http/discovery": true
}
}
}

1079
composer.lock
File diff suppressed because it is too large
View File

19
config/openai.php

@ -0,0 +1,19 @@
<?php
return [
// 默认ai平台
'default' => env('AI_PLATFORM'),
// ai平台配置
'platforms' => [
'kimi' => [
'base_url' => 'https://api.moonshot.cn/v1',
'api_key' => env('KIMI_API_KEY'),
'model' => env('KIMI_MODEL', 'moonshot-v1-8k')
],
'deepseek' => [
'base_url' => 'https://api.deepseek.com',
'api_key' => env('DEEPSEEK_API_KEY'),
'model' => env('DEEPSEEK_MODEL', 'deepseek-chat')
]
],
];

4
config/plugin/webman/redis-queue/app.php

@ -0,0 +1,4 @@
<?php
return [
'enable' => true,
];

7
config/plugin/webman/redis-queue/command.php

@ -0,0 +1,7 @@
<?php
use Webman\RedisQueue\Command\MakeConsumerCommand;
return [
MakeConsumerCommand::class
];

32
config/plugin/webman/redis-queue/log.php

@ -0,0 +1,32 @@
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
return [
'default' => [
'handlers' => [
[
'class' => Monolog\Handler\RotatingFileHandler::class,
'constructor' => [
runtime_path() . '/logs/redis-queue/queue.log',
7, //$maxFiles
Monolog\Logger::DEBUG,
],
'formatter' => [
'class' => Monolog\Formatter\LineFormatter::class,
'constructor' => [null, 'Y-m-d H:i:s', true],
],
]
],
]
];

11
config/plugin/webman/redis-queue/process.php

@ -0,0 +1,11 @@
<?php
return [
'consumer' => [
'handler' => Webman\RedisQueue\Process\Consumer::class,
'count' => 8, // 可以设置多进程同时消费
'constructor' => [
// 消费者类目录
'consumer_dir' => app_path() . '/queue/redis'
]
]
];

21
config/plugin/webman/redis-queue/redis.php

@ -0,0 +1,21 @@
<?php
return [
'default' => [
'host' => 'redis://' . env('REDIS_HOST') . ':' . env('REDIS_PORT'),
'options' => [
'auth' => env('REDIS_PASSWORD'),
'db' => env('REDIS_DB', 0),
'prefix' => env('REDIS_PREFIX', ''),
'max_attempts' => 5,
'retry_seconds' => 5,
],
// Connection pool, supports only Swoole or Swow drivers.
'pool' => [
'max_connections' => 5,
'min_connections' => 1,
'wait_timeout' => 3,
'idle_timeout' => 60,
'heartbeat_interval' => 50,
]
],
];

78
plugin/piadmin/app/utils/openai/OpenAiClient.php

@ -0,0 +1,78 @@
<?php
namespace plugin\piadmin\app\utils\openai;
use OpenAI;
use plugin\piadmin\app\exception\ApiException;
/**
* openAi 客户端
*/
class OpenAiClient
{
public static function getCurrentConfig(): array
{
$defaultPlatform = config('openai.default');
$platform = config('openai.platforms.' . $defaultPlatform, []);
if (empty($platform)) {
throw new ApiException("默认的AI平台: {$defaultPlatform} 未配置", '', '', []);
}
if (empty($platform['base_url'])) {
throw new ApiException("默认的AI平台: {$defaultPlatform} 未配置base_url", '', '', []);
}
if (empty($platform['api_key'])) {
throw new ApiException("默认的AI平台: {$defaultPlatform} 未配置api_key", '', '', []);
}
if (empty($platform['model'])) {
throw new ApiException("默认的AI平台: {$defaultPlatform} 未配置model", '', '', []);
}
return $platform;
}
public static function chat(string $prompt, bool $jsonResult = true): array
{
$platformConfig = self::getCurrentConfig();
$client = self::getClient();
$promptContent = $prompt;
$chatData = [
'model' => $platformConfig['model'],
'messages' => [
[
'role' => 'user',
'content' => $promptContent
]
]
];
if ($jsonResult) {
$chatData['response_format'] = [
'type' => 'json_object'
];
}
$openaiResult = $client->chat()->create($chatData);
$resultContent = $openaiResult->choices[0]->message->content;
$jsonResultContent = json_decode($resultContent, true);
$result = [];
if (!empty($jsonResultContent)) {
$result['content'] = $jsonResultContent;
} else {
$result['content'] = $resultContent;
}
$result['usage'] = $openaiResult->usage;
$result['prompt'] = $prompt;
$result['platform'] = $platformConfig;
return $result;
}
public static function getClient(): OpenAI\Client
{
$platformConfig = self::getCurrentConfig();
return OpenAI::factory()
->withApiKey($platformConfig['api_key'])
->withBaseUri($platformConfig['base_url'])
->withHttpClient(new \GuzzleHttp\Client([
'verify' => false
]))
->make();
}
}
Loading…
Cancel
Save