全局配置服务功能提交

This commit is contained in:
wong
2026-01-15 14:24:25 +08:00
parent cec64b889b
commit 430b8eee91
11 changed files with 443 additions and 13 deletions

View File

@@ -13,6 +13,7 @@ Route::group('v1/ai', function () {
//豆包ai
Route::group('doubao', function () {
Route::post('text', 'app\ai\controller\DouBaoAI@text');
Route::post('text', 'app\ai\controller\DouBaoAI@text'); // 文本生成
Route::post('image', 'app\ai\controller\DouBaoAI@image'); // 图片生成
});
})->middleware(['jwt']);

View File

@@ -49,7 +49,7 @@ class DouBaoAI extends Controller
],
];
}
$result = requestCurl($this->apiUrl, $params, 'POST', $this->headers, 'json');
$result = requestCurl($this->apiUrl.'/api/v3/chat/completions', $params, 'POST', $this->headers, 'json');
$result = json_decode($result, true);
if(isset($result['error'])){
$error = $result['error'];
@@ -64,5 +64,211 @@ class DouBaoAI extends Controller
}
/**
* 图片生成功能(基于火山方舟 Seedream 4.0-4.5 API
* 参考文档https://www.volcengine.com/docs/82379/1541523?lang=zh
*
* @param array $params 请求参数,如果为空则从请求中获取
* @return string JSON格式的响应
*/
public function image($params = [])
{
try {
// 如果参数为空,从请求中获取
if (empty($params)){
$content = $this->request->param('content', '');
$model = $this->request->param('model', 'doubao-seedream-4-5-251128');
$size = $this->request->param('size', '16:9'); // 支持档位(1K/2K/4K)、比例(16:9/9:16等)、像素(1280x720等)
$responseFormat = $this->request->param('response_format', 'url'); // url 或 b64_json
$sequentialImageGeneration = $this->request->param('sequential_image_generation', 'disabled'); // enabled 或 disabled
$watermark = $this->request->param('watermark', true); // true 或 false
// 参数验证
if(empty($content)){
return json_encode(['code' => 500, 'msg' => '提示词prompt不能为空']);
}
// 验证和规范化尺寸参数
$size = $this->validateAndNormalizeSize($size);
if(!in_array($responseFormat, ['url', 'b64_json'])){
$responseFormat = 'url';
}
if(!in_array($sequentialImageGeneration, ['enabled', 'disabled'])){
$sequentialImageGeneration = 'disabled';
}
// 构建请求参数(根据火山方舟文档)
$params = [
'model' => $model,
'prompt' => $content,
'sequential_image_generation' => $sequentialImageGeneration,
'response_format' => $responseFormat,
'size' => $size,
'stream' => false,
'watermark' => true
];
}
// 确保API URL正确图片生成API的endpoint
$imageApiUrl = $this->apiUrl. '/api/v3/images/generations';
// 发送请求
$result = requestCurl($imageApiUrl, $params, 'POST', $this->headers, 'json');
$result = json_decode($result, true);
// 错误处理
if(isset($result['error'])){
$error = $result['error'];
$errorMsg = isset($error['message']) ? $error['message'] : '图片生成失败';
$errorCode = isset($error['code']) ? $error['code'] : 'unknown';
\think\facade\Log::error('火山方舟图片生成失败', [
'error' => $error,
'params' => $params
]);
return json_encode([
'code' => 500,
'msg' => $errorMsg,
'error_code' => $errorCode
]);
}
// 成功响应处理(根据火山方舟文档的响应格式)
if(isset($result['data']) && is_array($result['data']) && !empty($result['data'])){
$imageData = $result['data'][0];
// 根据 response_format 获取图片数据
$imageUrl = null;
$imageB64 = null;
if(isset($imageData['url'])){
$imageUrl = $imageData['url'];
}
if(isset($imageData['b64_json'])){
$imageB64 = $imageData['b64_json'];
}
// 计算token如果有usage信息
$token = 0;
if(isset($result['usage']['total_tokens'])){
$token = intval($result['usage']['total_tokens']) * 20;
}
// 构建返回数据
$responseData = [
'token' => $token,
'image_url' => $imageUrl,
'image_b64' => $imageB64,
'model' => $params['model'] ?? '',
'size' => $params['size'] ?? '2K',
'created' => isset($result['created']) ? $result['created'] : time()
];
// 根据请求的response_format返回对应的数据
if($params['response_format'] == 'url' && $imageUrl){
$responseData['content'] = $imageUrl;
} elseif($params['response_format'] == 'b64_json' && $imageB64){
$responseData['content'] = $imageB64;
}
return json_encode([
'code' => 200,
'msg' => '图片生成成功',
'data' => $responseData
]);
} else {
// 响应格式不符合预期
\think\facade\Log::warning('火山方舟图片生成响应格式异常', [
'result' => $result,
'params' => $params
]);
return json_encode([
'code' => 500,
'msg' => '图片生成响应格式异常',
'raw_response' => $result
]);
}
} catch (\Exception $e) {
\think\facade\Log::error('火山方舟图片生成异常', [
'message' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
return json_encode([
'code' => 500,
'msg' => '图片生成异常:' . $e->getMessage()
]);
}
}
/**
* 验证和规范化尺寸参数
* 支持三种格式:
* 1. 档位形式1K, 2K, 4K不区分大小写
* 2. 比例形式16:9, 9:16, 1:1, 4:3, 3:4 等
* 3. 像素形式1280x720, 2048x2048 等宽度1280-4096高度720-4096宽高比0.0625-16
*
* @param string $size 尺寸参数
* @return string 规范化后的尺寸值
*/
private function validateAndNormalizeSize($size)
{
if (empty($size)) {
return '2K';
}
$size = trim($size);
// 1. 检查是否为档位形式1K, 2K, 4K
$sizeUpper = strtoupper($size);
if (in_array($sizeUpper, ['1K', '2K', '4K'])) {
return $sizeUpper;
}
// 2. 检查是否为比例形式(如 16:9, 9:16, 1:1
if (preg_match('/^(\d+):(\d+)$/', $size, $matches)) {
$width = intval($matches[1]);
$height = intval($matches[2]);
if ($width > 0 && $height > 0) {
$ratio = $width / $height;
// 验证宽高比范围0.0625 ~ 16
if ($ratio >= 0.0625 && $ratio <= 16) {
return $size; // 返回比例形式,如 "16:9"
}
}
}
// 3. 检查是否为像素形式(如 1280x720, 2048x2048
if (preg_match('/^(\d+)x(\d+)$/i', $size, $matches)) {
$width = intval($matches[1]);
$height = intval($matches[2]);
// 验证宽度范围1280 ~ 4096
if ($width < 1280 || $width > 4096) {
return '2K'; // 默认返回 2K
}
// 验证高度范围720 ~ 4096
if ($height < 720 || $height > 4096) {
return '2K'; // 默认返回 2K
}
// 验证宽高比范围0.0625 ~ 16
$ratio = $width / $height;
if ($ratio < 0.0625 || $ratio > 16) {
return '2K'; // 默认返回 2K
}
return $size; // 返回像素形式,如 "1280x720"
}
// 如果都不匹配,返回默认值
return '2K';
}
}

View File

@@ -7,6 +7,7 @@ use library\ResponseHelper;
use app\api\model\WechatFriendModel;
use app\api\model\WechatMessageModel;
use app\api\controller\MessageController;
use think\Db;
class DataProcessing extends BaseController
@@ -60,6 +61,7 @@ class DataProcessing extends BaseController
$friend->conRemark = $newRemark;
$friend->updateTime = time();
$friend->save();
$msg = '修改备成功';
break;
case 'CmdModifyFriendLabel': //修改好友标签
@@ -73,6 +75,7 @@ class DataProcessing extends BaseController
$friend->labels = json_encode($labels,256);
$friend->updateTime = time();
$friend->save();
$msg = '修标签成功';
break;
case 'CmdAllotFriend': //迁移好友
@@ -199,6 +202,7 @@ class DataProcessing extends BaseController
$data->updateTime = time();
$data->isTop = $isTop;
$data->save();
break;
}
return ResponseHelper::success('',$msg,$codee);

View File

@@ -179,17 +179,14 @@ class FriendTransferService
'alive' => 1
])
->column('id');
if (empty($accountIds)) {
Log::warning("批量迁移失败没有可用的在线账号账号ID={$currentAccountId}");
return ['transferred' => 0, 'failed' => count($friends)];
}
// 排除当前账号,选择其他账号
$availableAccountIds = array_filter($accountIds, function($id) use ($currentAccountId) {
return $id != $currentAccountId;
});
if (empty($availableAccountIds)) {
Log::warning("批量迁移失败没有其他可用的在线账号账号ID={$currentAccountId}");
return ['transferred' => 0, 'failed' => count($friends)];
@@ -440,7 +437,6 @@ class FriendTransferService
}
$friendsByAccount[$accountId][] = $friend;
}
// 按账号分组批量处理
foreach ($friendsByAccount as $accountId => $accountFriends) {
$batchResult = $this->transferFriendsBatch(

View File

@@ -49,7 +49,12 @@ class GetAddResultedV1Controller extends BaseController
$deviceIds = $this->getAllDevicesIdWithInCompany($companyId) ?: [0];
// 从 s2_device 导入数据。
$this->getNewDeviceFromS2_device($deviceIds, $companyId);
$newDeviceIds = $this->getNewDeviceFromS2_device($deviceIds, $companyId);
// 如果有新设备,自动加入到全局配置中
if (!empty($newDeviceIds)) {
$this->addDevicesToGlobalConfigs($newDeviceIds, $companyId);
}
}
/**
@@ -57,12 +62,23 @@ class GetAddResultedV1Controller extends BaseController
*
* @param array $ids
* @param int $companyId
* @return void
* @return array 返回新添加的设备ID数组
*/
protected function getNewDeviceFromS2_device(array $ids, int $companyId): void
protected function getNewDeviceFromS2_device(array $ids, int $companyId): array
{
$ids = implode(',', $ids);
// 先查询要插入的新设备ID
$newDeviceIds = Db::query("SELECT d.id
FROM s2_device d
JOIN s2_company_account a ON d.currentAccountId = a.id
WHERE isDeleted = 0 AND deletedAndStop = 0 AND d.id NOT IN ({$ids}) AND a.departmentId = {$companyId}");
$newDeviceIds = array_column($newDeviceIds, 'id');
if (empty($newDeviceIds)) {
return [];
}
$sql = "INSERT INTO ck_device(`id`, `imei`, `model`, phone, operatingSystem, memo, alive, brand, rooted, xPosed, softwareVersion, extra, createTime, updateTime, deleteTime, companyId)
SELECT
d.id, d.imei, d.model, d.phone, d.operatingSystem, d.memo, d.alive, d.brand, d.rooted, d.xPosed, d.softwareVersion, d.extra, d.createTime, d.lastUpdateTime, d.deleteTime, a.departmentId AS companyId
@@ -86,6 +102,8 @@ class GetAddResultedV1Controller extends BaseController
companyId = VALUES(companyId)";
Db::query($sql);
return $newDeviceIds;
}
/**
@@ -162,4 +180,144 @@ class GetAddResultedV1Controller extends BaseController
]
);
}
/**
* 将新设备自动加入到全局配置中planType=0的计划和工作台
*
* @param array $newDeviceIds 新添加的设备ID数组
* @param int $companyId 公司ID
* @return void
*/
protected function addDevicesToGlobalConfigs(array $newDeviceIds, int $companyId): void
{
try {
// 1. 更新全局计划(场景获客)的设备组
$this->addDevicesToGlobalPlans($newDeviceIds, $companyId);
// 2. 更新全局工作台的设备组
$this->addDevicesToGlobalWorkbenches($newDeviceIds, $companyId);
} catch (\Exception $e) {
// 记录错误但不影响设备添加流程
\think\facade\Log::error('自动添加设备到全局配置失败:' . $e->getMessage(), [
'newDeviceIds' => $newDeviceIds,
'companyId' => $companyId
]);
}
}
/**
* 将新设备加入到全局计划planType=0的设备组
*
* @param array $newDeviceIds 新添加的设备ID数组
* @param int $companyId 公司ID
* @return void
*/
protected function addDevicesToGlobalPlans(array $newDeviceIds, int $companyId): void
{
// 查询所有全局计划planType=0
$plans = Db::name('customer_acquisition_task')
->where('companyId', $companyId)
->where('planType', 0) // 全局计划
->where('deleteTime', 0)
->field('id,reqConf')
->select();
foreach ($plans as $plan) {
$reqConf = json_decode($plan['reqConf'], true) ?: [];
$deviceGroups = isset($reqConf['device']) ? $reqConf['device'] : [];
if (!is_array($deviceGroups)) {
$deviceGroups = [];
}
// 合并新设备ID去重
$deviceGroups = array_unique(array_merge($deviceGroups, $newDeviceIds));
$reqConf['device'] = array_values($deviceGroups); // 重新索引数组
// 更新数据库
Db::name('customer_acquisition_task')
->where('id', $plan['id'])
->update([
'reqConf' => json_encode($reqConf, JSON_UNESCAPED_UNICODE),
'updateTime' => time()
]);
}
}
/**
* 将新设备加入到全局工作台planType=0的设备组
*
* @param array $newDeviceIds 新添加的设备ID数组
* @param int $companyId 公司ID
* @return void
*/
protected function addDevicesToGlobalWorkbenches(array $newDeviceIds, int $companyId): void
{
// 查询所有全局工作台planType=0
$workbenches = Db::name('workbench')
->where('companyId', $companyId)
->where('planType', 0) // 全局工作台
->where('isDel', 0)
->field('id,type')
->select();
foreach ($workbenches as $workbench) {
// 根据工作台类型更新对应的配置表
$this->updateWorkbenchDevices($workbench['id'], $workbench['type'], $newDeviceIds);
}
}
/**
* 更新工作台的设备组
*
* @param int $workbenchId 工作台ID
* @param int $type 工作台类型
* @param array $newDeviceIds 新设备ID数组
* @return void
*/
protected function updateWorkbenchDevices(int $workbenchId, int $type, array $newDeviceIds): void
{
$configTableMap = [
1 => 'workbench_auto_like', // 自动点赞
2 => 'workbench_moments_sync', // 朋友圈同步
3 => 'workbench_group_push', // 群消息推送
4 => 'workbench_group_create', // 自动建群
5 => 'workbench_traffic_config', // 流量分发
6 => 'workbench_import_contact', // 通讯录导入
7 => 'workbench_group_welcome', // 入群欢迎语
];
$tableName = $configTableMap[$type] ?? null;
if (empty($tableName)) {
return;
}
// 查询配置
$config = Db::name($tableName)
->where('workbenchId', $workbenchId)
->field('id,devices')
->find();
if (empty($config)) {
return;
}
// 解析设备组
$deviceGroups = json_decode($config['devices'], true) ?: [];
if (!is_array($deviceGroups)) {
$deviceGroups = [];
}
// 合并新设备ID去重
$deviceGroups = array_unique(array_merge($deviceGroups, $newDeviceIds));
$deviceGroups = array_values($deviceGroups); // 重新索引数组
// 更新数据库
Db::name($tableName)
->where('id', $config['id'])
->update([
'devices' => json_encode($deviceGroups, JSON_UNESCAPED_UNICODE),
'updateTime' => time()
]);
}
}

View File

@@ -287,6 +287,13 @@ class GetAddFriendPlanDetailV1Controller extends Controller
$newData['messagePlans'] = $msgConf;
$newData = array_merge($newData, $sceneConf, $reqConf, $tagConf, $plan);
// 确保 planType 有默认值0=全局1=独立默认1
if (!isset($newData['planType'])) {
$newData['planType'] = 1;
} else {
$newData['planType'] = intval($newData['planType']);
}
// 移除不需要的字段
unset(
$newData['sceneConf'],

View File

@@ -61,6 +61,13 @@ class PlanSceneV1Controller extends BaseController
$val['reqConf'] = json_decode($val['reqConf'],true) ?: [];
$val['msgConf'] = json_decode($val['msgConf'],true) ?: [];
$val['tagConf'] = json_decode($val['tagConf'],true) ?: [];
// 确保 planType 有默认值0=全局1=独立默认1
if (!isset($val['planType'])) {
$val['planType'] = 1;
} else {
$val['planType'] = intval($val['planType']);
}
$stats = $statsMap[$val['id']] ?? [
'acquiredCount' => 0,

View File

@@ -150,6 +150,8 @@ class PostCreateAddFriendPlanV1Controller extends BaseController
'userId' => $this->getUserInfo('id'),
'companyId' => $this->getUserInfo('companyId'),
'status' => !empty($params['status']) ? 1 : 0,
// 计划类型0=全局1=独立(默认)
'planType' => isset($params['planType']) ? intval($params['planType']) : 1,
// 拉群配置
'groupInviteEnabled' => $groupInviteEnabled,
'groupName' => $params['groupName'] ?? '',

View File

@@ -125,6 +125,8 @@ class PostUpdateAddFriendPlanV1Controller extends BaseController
'msgConf' => json_encode($msgConf, JSON_UNESCAPED_UNICODE),
'tagConf' => json_encode($tagConf, JSON_UNESCAPED_UNICODE),
'status' => !empty($params['status']) ? 1 : 0,
// 计划类型0=全局1=独立(默认)
'planType' => isset($params['planType']) ? intval($params['planType']) : 1,
// 拉群配置
'groupInviteEnabled' => $groupInviteEnabled,
'groupName' => $params['groupName'] ?? '',

View File

@@ -79,6 +79,8 @@ class WorkbenchController extends Controller
$workbench->type = $param['type'];
$workbench->status = !empty($param['status']) ? 1 : 0;
$workbench->autoStart = !empty($param['autoStart']) ? 1 : 0;
// 计划类型0=全局1=独立默认1
$workbench->planType = isset($param['planType']) ? intval($param['planType']) : 1;
$workbench->userId = $userInfo['id'];
$workbench->companyId = $userInfo['companyId'];
$workbench->createTime = time();
@@ -133,7 +135,6 @@ class WorkbenchController extends Controller
case self::TYPE_GROUP_CREATE: // 自动建群
$config = new WorkbenchGroupCreate;
$config->workbenchId = $workbench->id;
$config->planType = !empty($param['planType']) ? $param['planType'] : 0;
$config->executorId = !empty($param['executorId']) ? $param['executorId'] : 0;
$config->devices = json_encode($param['deviceGroups'] ?? [], JSON_UNESCAPED_UNICODE);
@@ -295,7 +296,7 @@ class WorkbenchController extends Controller
$list = Workbench::where($where)
->with($with)
->field('id,companyId,name,type,status,autoStart,userId,createTime,updateTime')
->field('id,companyId,name,type,status,autoStart,planType,userId,createTime,updateTime')
->order('id', 'desc')
->page($page, $limit)
->select()
@@ -474,7 +475,7 @@ class WorkbenchController extends Controller
break;
case self::TYPE_IMPORT_CONTACT:
if (!empty($item->importContact)) {
if (!empty($item->importContact)) {
$item->config = $item->importContact;
$item->config->devices = json_decode($item->config->devices, true);
$item->config->poolGroups = json_decode($item->config->pools, true);
@@ -568,7 +569,7 @@ class WorkbenchController extends Controller
$workbench = Workbench::where($where)
->field('id,name,type,status,autoStart,createTime,updateTime,companyId')
->field('id,name,type,status,autoStart,planType,createTime,updateTime,companyId')
->with($with)
->find();
@@ -1187,6 +1188,12 @@ class WorkbenchController extends Controller
$workbench->name = $param['name'];
$workbench->status = !empty($param['status']) ? 1 : 0;
$workbench->autoStart = !empty($param['autoStart']) ? 1 : 0;
// 更新计划类型0=全局1=独立默认保留原值或1
if (isset($param['planType'])) {
$workbench->planType = intval($param['planType']);
} elseif (!isset($workbench->planType) || $workbench->planType === null) {
$workbench->planType = 1;
}
$workbench->updateTime = time();
$workbench->save();
@@ -1457,6 +1464,12 @@ class WorkbenchController extends Controller
$newWorkbench->type = $workbench->type;
$newWorkbench->status = 1; // 新拷贝的默认启用
$newWorkbench->autoStart = $workbench->autoStart;
// 复制计划类型0=全局1=独立默认1
if (isset($workbench->planType)) {
$newWorkbench->planType = intval($workbench->planType);
} else {
$newWorkbench->planType = 1;
}
$newWorkbench->userId = $this->request->userInfo['id'];
$newWorkbench->companyId = $this->request->userInfo['companyId'];
$newWorkbench->save();