567 lines
22 KiB
PHP
567 lines
22 KiB
PHP
<?php
|
||
|
||
namespace app\api\logic\sv;
|
||
|
||
use think\facade\Db;
|
||
|
||
use app\common\model\sv\SvPublishSetting;
|
||
use app\common\model\sv\SvPublishSettingAccount;
|
||
use app\common\model\sv\SvAccount;
|
||
use app\common\model\sv\SvDevice;
|
||
use app\common\model\sv\SvVideoSetting;
|
||
use app\common\model\sv\SvVideoTask;
|
||
use app\common\model\sv\SvPublishSettingDetail;
|
||
use app\common\service\FileService;
|
||
|
||
/**
|
||
* PublishLogic
|
||
* @desc 机器人
|
||
* @author Qasim
|
||
*/
|
||
class PublishLogic extends SvBaseLogic
|
||
{
|
||
|
||
protected static $interval = 3600; //视频发布间隔时间(秒)
|
||
/**
|
||
* @desc 添加机器人
|
||
* @param array $params
|
||
* @return bool
|
||
*/
|
||
public static function add(array $params)
|
||
{
|
||
|
||
// 启动事务
|
||
Db::startTrans();
|
||
try {
|
||
$params['user_id'] = self::$uid;
|
||
|
||
if (is_array($params['accounts'])) {
|
||
$params['accounts'] = implode(',', $params['accounts']);
|
||
}
|
||
if (is_array($params['time_config'])) {
|
||
$params['time_config'] = json_encode($params['time_config'], JSON_UNESCAPED_UNICODE);
|
||
}
|
||
// 添加
|
||
$publish = SvPublishSetting::create($params);
|
||
if (!$publish->isEmpty()) {
|
||
self::batchPushlishAccount($publish, $params);
|
||
}
|
||
// 提交事务
|
||
Db::commit();
|
||
self::$returnData = $publish->toArray();
|
||
return true;
|
||
} catch (\Exception $e) {
|
||
// 回滚事务
|
||
Db::rollback();
|
||
// clogger($e);
|
||
self::setError($e->getMessage());
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @desc 更新机器人
|
||
* @param array $params
|
||
* @return bool
|
||
*/
|
||
public static function update(array $params)
|
||
{
|
||
|
||
Db::startTrans();
|
||
try {
|
||
// 检查机器人是否存在
|
||
$publish = SvPublishSetting::where('id', $params['id'])->where('user_id', self::$uid)->findOrEmpty();
|
||
if ($publish->isEmpty()) {
|
||
self::setError('任务不存在');
|
||
return false;
|
||
}
|
||
|
||
//查询任务明细是否存在
|
||
$publishDetial = SvPublishSettingDetail::where('publish_id', $params['id'])->where('user_id', self::$uid)->findOrEmpty();
|
||
if (!$publishDetial->isEmpty()) {
|
||
self::setError('任务正在执行中,不能修改');
|
||
return false;
|
||
}
|
||
|
||
if (is_array($params['accounts'])) {
|
||
$params['accounts'] = implode(',', $params['accounts']);
|
||
}
|
||
if (is_array($params['time_config'])) {
|
||
$params['time_config'] = json_encode($params['time_config'], JSON_UNESCAPED_UNICODE);
|
||
}
|
||
|
||
// 更新
|
||
SvPublishSetting::where('id', $publish->id)->update($params);
|
||
SvPublishSettingAccount::where('publish_id', $publish->id)->delete();
|
||
self::batchPushlishAccount($publish, $params);
|
||
|
||
Db::commit();
|
||
self::$returnData = $publish->refresh()->toArray();
|
||
return true;
|
||
} catch (\Exception $e) {
|
||
Db::rollback();
|
||
// clogger($e);
|
||
self::setError($e->getMessage());
|
||
return false;
|
||
}
|
||
}
|
||
|
||
private static function batchPushlishAccount($publish, $params)
|
||
{
|
||
$insertData = [];
|
||
$accounts = explode(',', $params['accounts']);
|
||
foreach ($accounts as $key => $account) {
|
||
$account = SvAccount::where('account', $account)->limit(1)->find();
|
||
$videoSetting = SvVideoSetting::where('id', $params['video_setting_id'])->limit(1)->find();
|
||
array_push($insertData, [
|
||
'publish_id' => $publish->id,
|
||
'user_id' => self::$uid,
|
||
'name' => $params['name'],
|
||
'account' => $account['account'],
|
||
'account_type' => $account['type'],
|
||
'device_code' => $account['device_code'],
|
||
'video_setting_id' => $params['video_setting_id'],
|
||
'publish_start' => $params['publish_start'],
|
||
'publish_end' => $params['publish_end'],
|
||
'next_publish_time' => self::_getPublishTime($account, $videoSetting['video_count'], 0), //视频发布时间
|
||
'count' => $videoSetting['video_count'],
|
||
'published_count' => 0,
|
||
'status' => 1,
|
||
'created_time' => time(),
|
||
]);
|
||
}
|
||
$model = new SvPublishSettingAccount();
|
||
$model->saveAll($insertData);
|
||
}
|
||
|
||
public static function change(array $params)
|
||
{
|
||
$find = SvPublishSettingAccount::where('id', $params['id'])->findOrEmpty();
|
||
if ($find->isEmpty()) {
|
||
self::setError('任务不存在');
|
||
return false;
|
||
}
|
||
$find->status = $params['status'];
|
||
$find->updated_time = time();
|
||
$find->save();
|
||
self::$returnData = $find->refresh()->toArray();
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* @desc 获取机器人详情
|
||
* @param array $params
|
||
* @return bool
|
||
*/
|
||
public static function detail(array $params)
|
||
{
|
||
try {
|
||
// 检查机器人是否存在
|
||
$publish = SvPublishSetting::field('*')
|
||
->where('id', $params['id'])
|
||
->where('user_id', self::$uid)
|
||
->findOrEmpty();
|
||
if ($publish->isEmpty()) {
|
||
self::setError('任务不存在');
|
||
return false;
|
||
}
|
||
$publish['accounts'] = explode(',', $publish['accounts']);
|
||
$publish['time_config'] = json_decode($publish['time_config'], true);
|
||
|
||
|
||
self::$returnData = $publish->toArray();
|
||
return true;
|
||
} catch (\Exception $e) {
|
||
self::setError($e->getMessage());
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @desc 删除机器人
|
||
* @param array $params
|
||
* @return bool
|
||
*/
|
||
public static function delete(array $params)
|
||
{
|
||
Db::startTrans();
|
||
try {
|
||
// 检查机器人是否存在
|
||
$publish = SvPublishSettingAccount::where('id', $params['id'])->where('user_id', self::$uid)->findOrEmpty();
|
||
if ($publish->isEmpty()) {
|
||
self::setError('任务不存在');
|
||
return false;
|
||
}
|
||
|
||
//查询任务明细是否存在
|
||
// $publishDetial = SvPublishSettingDetail::where('publish_id', $params['id'])->where('user_id', self::$uid)->findOrEmpty();
|
||
// if (!$publishDetial->isEmpty()) {
|
||
// self::setError('任务正在执行中,不能删除');
|
||
// return false;
|
||
// }
|
||
$publish->delete();
|
||
//SvPublishSettingAccount::where('publish_id', $publish->id)->delete();
|
||
|
||
Db::commit();
|
||
return true;
|
||
} catch (\Exception $e) {
|
||
Db::rollback();
|
||
// clogger($e);
|
||
self::setError($e->getMessage());
|
||
return false;
|
||
}
|
||
}
|
||
|
||
|
||
public static function recordDetail(array $params)
|
||
{
|
||
try {
|
||
// 检查机器人是否存在
|
||
$record = SvPublishSettingDetail::field('*')
|
||
->where('id', $params['id'])
|
||
->where('user_id', self::$uid)
|
||
->findOrEmpty();
|
||
if ($record->isEmpty()) {
|
||
self::setError('任务记录不存在');
|
||
return false;
|
||
}
|
||
|
||
|
||
self::$returnData = $record->toArray();
|
||
return true;
|
||
} catch (\Exception $e) {
|
||
self::setError($e->getMessage());
|
||
return false;
|
||
}
|
||
}
|
||
|
||
|
||
public static function recordDelete(array $params)
|
||
{
|
||
$record = SvPublishSettingDetail::field('*')
|
||
->where('id', $params['id'])
|
||
->where('user_id', self::$uid)
|
||
->findOrEmpty();
|
||
if ($record->isEmpty()) {
|
||
self::setError('任务记录不存在');
|
||
return false;
|
||
}
|
||
$record->delete();
|
||
|
||
return true;
|
||
}
|
||
public static function recordRetry(array $params)
|
||
{
|
||
try {
|
||
if (time() > strtotime($params['retry_time'])) {
|
||
self::setError('重试时间不能小于当前时间');
|
||
return false;
|
||
}
|
||
// 检查机器人是否存在
|
||
$record = SvPublishSettingDetail::field('*')
|
||
->where('id', $params['id'])
|
||
->where('user_id', self::$uid)
|
||
->findOrEmpty();
|
||
if ($record->isEmpty()) {
|
||
self::setError('任务记录不存在');
|
||
return false;
|
||
}
|
||
|
||
$setting = SvPublishSetting::where('id', $record['publish_id'])->limit(1)->find();
|
||
if (empty($setting)) {
|
||
self::setError('任务配置不存在');
|
||
return false;
|
||
}
|
||
$time_config = json_decode($setting['time_config'], true);
|
||
if (empty($time_config)) {
|
||
$time_config = [
|
||
[
|
||
'start_time' => date('H:i', time() + 600), // 开始时间
|
||
'end_time' => '23:59' // 结束时间
|
||
]
|
||
];
|
||
}
|
||
$periods = array_map(function ($item) use ($setting) {
|
||
return [
|
||
'start' => strtotime("{$setting['publish_start']} {$item['start_time']}:00"),
|
||
'end' => strtotime("{$setting['publish_end']} {$item['end_time']}:00")
|
||
];
|
||
}, $time_config);
|
||
//print_r($periods);die;
|
||
if (strtotime($params['retry_time']) > $periods[0]['end']) {
|
||
self::setError('重试时间不在任务时间段内');
|
||
return false;
|
||
}
|
||
|
||
$record->status = 0;
|
||
$record->publish_time = $params['retry_time'];
|
||
$record->save();
|
||
|
||
self::$returnData = $record->toArray();
|
||
return true;
|
||
} catch (\Exception $e) {
|
||
self::setError($e->getMessage());
|
||
return false;
|
||
}
|
||
}
|
||
|
||
public static function testAdd(array $params)
|
||
{
|
||
Db::startTrans();
|
||
try {
|
||
$device = SvDevice::where('status', 1)->where('user_id', self::$uid)->order('id asc')->limit(1)->findOrEmpty();
|
||
if ($device->isEmpty()) {
|
||
self::setError('没有在线设备');
|
||
return false;
|
||
}
|
||
|
||
if(mb_strlen($params['title'], 'utf-8') > 150){
|
||
self::setError('标题不能超过150个字');
|
||
return false;
|
||
}
|
||
|
||
if(mb_strlen($params['subtitle'], 'utf-8') > 150){
|
||
self::setError('正文不能超过150个字');
|
||
return false;
|
||
}
|
||
|
||
|
||
$publish = SvPublishSetting::create([
|
||
'user_id' => self::$uid,
|
||
'name' => empty($params['title']) ? '模拟发布' : $params['title'],
|
||
'accounts' => implode(',', $params['accounts']),
|
||
'video_setting_id' => 0,
|
||
'type' => 3,
|
||
'publish_start' => date('Y-m-d', time()),
|
||
'publish_end' => date('Y-m-d', time()),
|
||
'time_config' => '[]',
|
||
'data_type' => 1,
|
||
'create_time' => time(),
|
||
'update_time' => time()
|
||
]);
|
||
|
||
$url = $params['url'] ?? config('app.app_host') . '/uploads/video/20250517/7b300711-d826-4b46-8b1a-c6eaaa58cbce.mp4';
|
||
$insertData = array();
|
||
$count = count($params['accounts']);
|
||
foreach ($params['accounts'] as $key => $account) {
|
||
$account = SvAccount::where('account', $account)->where('user_id', self::$uid)->limit(1)->findOrEmpty();
|
||
if($account->isEmpty()){
|
||
self::setError("{$account}该账号信息不存在");
|
||
return false;
|
||
}
|
||
$publishAccount = SvPublishSettingAccount::create([
|
||
'publish_id' => $publish->id,
|
||
'user_id' => self::$uid,
|
||
'name' => empty($params['title']) ? '模拟发布' : $params['title'],
|
||
'account' => $account['account'],
|
||
'account_type' => $account['type'],
|
||
'device_code' => $account['device_code'],
|
||
'video_setting_id' => 0,
|
||
'publish_start' => date('Y-m-d', time()),
|
||
'publish_end' => date('Y-m-d', time()),
|
||
'next_publish_time' => date('Y-m-d H:i:s', time()), //视频发布时间
|
||
'count' => $count,
|
||
'published_count' => 0,
|
||
'status' => 1,
|
||
'data_type' => 1,
|
||
'created_time' => time(),
|
||
]);
|
||
|
||
array_push($insertData, [
|
||
'publish_id' => $publish->id,
|
||
'publish_account_id' => $publishAccount->id,
|
||
'video_task_id' => 0, //视频任务id,关联sv_video_tas
|
||
'user_id' => self::$uid,
|
||
'account' => $account['account'],
|
||
'account_type' => $account['type'],
|
||
'device_code' => $account['device_code'],
|
||
'material_id' => 0,
|
||
'material_type' => $params['material_type'],
|
||
'material_url' => $url,
|
||
'material_title' => empty($params['title']) ? ' ' : $params['title'],
|
||
'material_tag' => $params['topic'],
|
||
'poi' => $params['poi'],
|
||
'material_subtitle' => empty($params['subtitle']) ? ' ' : $params['subtitle'],
|
||
'task_id' => generate_unique_task_id(),
|
||
'platform' => $account['type'],
|
||
'status' => 0,
|
||
'publish_time' => date('Y-m-d H:i:s', time()), //视频发布时间
|
||
'create_time' => time(),
|
||
'data_type' => 1
|
||
]);
|
||
}
|
||
//print_r($insertData);die;
|
||
if (!empty($insertData)) {
|
||
$model = new SvPublishSettingDetail();
|
||
$model->saveAll($insertData);
|
||
}
|
||
Db::commit();
|
||
self::$returnData = [];
|
||
return true;
|
||
} catch (\Exception $e) {
|
||
Db::rollback();
|
||
self::setError($e->getMessage());
|
||
return false;
|
||
}
|
||
}
|
||
|
||
|
||
public static function setPublishDetail()
|
||
{
|
||
print_r('执行发布记录拉取任务');
|
||
try {
|
||
$accounts = SvPublishSettingAccount::alias('pa')
|
||
->field('pa.*, ps.publish_start, ps.publish_end, ps.time_config, a.device_code as devicecode')
|
||
->join('sv_video_setting vs', 'vs.id = pa.video_setting_id and vs.user_id = pa.user_id')
|
||
->join('sv_publish_setting ps', 'ps.id = pa.publish_id and ps.user_id = pa.user_id')
|
||
->join('sv_account a', 'a.account = pa.account and a.user_id = pa.user_id')
|
||
//->join('sv_device d', 'd.device_code = pa.device_code and d.user_id = pa.user_id')
|
||
->where('pa.status', 1)
|
||
->where('vs.status', 'in', [3, 5])
|
||
//->order('ps.id desc')
|
||
->select()->toArray();
|
||
|
||
print_r(Db::getLastSql());
|
||
print_r("count: " . count($accounts));
|
||
$insertData = [];
|
||
$videoIds = [];
|
||
foreach ($accounts as $key => $account) {
|
||
$videos = SvVideoTask::alias('vs')
|
||
->field('vs.*')
|
||
->where('vs.video_setting_id', $account['video_setting_id'])
|
||
->where('vs.user_id', $account['user_id'])
|
||
->where('vs.status', 6)
|
||
// ->where('vs.id', 'not in', function($query) use($account){
|
||
// $query->name('sv_publish_setting_detail')->field('video_task_id')->where('user_id', $account['user_id'])->select();
|
||
// })
|
||
//->fetchSql(true)
|
||
->select()
|
||
->toArray();
|
||
//print_r($videos);die;
|
||
$videoCount = count($videos);
|
||
foreach ($videos as $key => $video) {
|
||
$detail = SvPublishSettingDetail::where('publish_id', $account['publish_id'])
|
||
->where('publish_account_id', $account['id'])
|
||
->where('video_task_id', $video['id'])
|
||
->where('user_id', $account['user_id'])
|
||
->where('account', $account['account'])
|
||
->find();
|
||
if (empty($detail)) {
|
||
array_push($insertData, [
|
||
'publish_id' => $account['publish_id'],
|
||
'publish_account_id' => $account['id'],
|
||
'video_task_id' => $video['id'], //视频任务id,关联sv_video_tas
|
||
'user_id' => $account['user_id'],
|
||
'account' => $account['account'],
|
||
'account_type' => $account['account_type'],
|
||
'device_code' => $account['devicecode'],
|
||
'material_id' => $video['id'],
|
||
'material_type' => 1,
|
||
'material_url' => FileService::getFileUrl($video['video_result_url']),
|
||
'material_title' => $video['name'],
|
||
'material_tag' => $video['topic'],
|
||
'poi' => $video['poi'],
|
||
'material_subtitle' => $video['name'],
|
||
'task_id' => generate_unique_task_id(),
|
||
'platform' => $account['account_type'],
|
||
'status' => 0,
|
||
'publish_time' => self::_getPublishTime($account, $videoCount, $key),
|
||
'create_time' => time()
|
||
]);
|
||
array_push($videoIds, $video['id']);
|
||
}
|
||
}
|
||
//print_r($insertData);die;
|
||
}
|
||
//print_r($insertData);die;
|
||
if (!empty($insertData)) {
|
||
$model = new SvPublishSettingDetail();
|
||
$model->saveAll($insertData);
|
||
}
|
||
|
||
self::$returnData = $insertData;
|
||
return true;
|
||
} catch (\Exception $e) {
|
||
print_r($e);
|
||
die;
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 计算发布时间
|
||
* @param array $account 机器人账号信息
|
||
* @param int $videoCount 视频数量
|
||
* @param int $num 视频序号
|
||
* @param int $type 视频发布时间分配方式 1:循环分配 2:平均分配
|
||
* @return string 发布时间
|
||
*/
|
||
private static function _getPublishTime($account, int $videoCount, int $num, int $type = 1)
|
||
{
|
||
$account['time_config'] = json_decode($account['time_config'], true);
|
||
try {
|
||
if (empty($account['time_config'])) {
|
||
$account['time_config'] = [
|
||
[
|
||
'start_time' => date('H:i', time() + 600), // 开始时间
|
||
'end_time' => '23:59' // 结束时间
|
||
]
|
||
];
|
||
}
|
||
|
||
|
||
$timeConfig = $account['time_config'];
|
||
// 时间配置解析
|
||
$periods = array_map(function ($item) use ($account) {
|
||
return [
|
||
'start' => strtotime("{$account['publish_start']} {$item['start_time']}:00"),
|
||
'end' => strtotime("{$account['publish_end']} {$item['end_time']}:00")
|
||
];
|
||
}, $timeConfig);
|
||
if ($type == 1) {
|
||
$periodCount = count($periods);
|
||
$currentPeriod = $num % $periodCount; // 当前视频所属时段索引
|
||
|
||
// 计算当前时段内的视频序号(从0开始)
|
||
$periodVideoNum = (int)($num / $periodCount);
|
||
|
||
// 每个时段的总视频数(向上取整)
|
||
$videosPerPeriod = ceil($videoCount / $periodCount);
|
||
|
||
// 计算时间间隔(+1保证首尾留空)
|
||
$interval = ($periods[$currentPeriod]['end'] - $periods[$currentPeriod]['start']) / ($videosPerPeriod + 1);
|
||
|
||
// 生成精确时间戳(秒级精度)
|
||
$timestamp = $periods[$currentPeriod]['start'] + ($periodVideoNum + 1) * $interval;
|
||
|
||
// 确保时间不超过当前时段
|
||
$timestamp = min($timestamp, $periods[$currentPeriod]['end'] - 1);
|
||
|
||
return date('Y-m-d H:i:s', $timestamp);
|
||
} else if ($type == 2) {
|
||
// 计算各时间段分配数量
|
||
$periodCount = count($periods);
|
||
$baseCount = floor($videoCount / $periodCount);
|
||
$extra = $videoCount % $periodCount;
|
||
|
||
// 生成时间点
|
||
$timestamps = [];
|
||
foreach ($periods as $index => $period) {
|
||
$count = $baseCount + ($index < $extra ? 1 : 0);
|
||
$duration = $period['end'] - $period['start'];
|
||
$interval = $duration / ($count + 1);
|
||
|
||
for ($i = 1; $i <= $count; $i++) {
|
||
$timestamps[] = $period['start'] + $interval * $i;
|
||
}
|
||
}
|
||
// 获取当前视频序号对应的时间
|
||
return isset($timestamps[$num]) ? date('Y-m-d H:i:s', $timestamps[$num]) : date('Y-m-d H:i:s', $periods[0]['start'] + 60); // 默认值
|
||
}
|
||
} catch (\Exception $e) {
|
||
print_r($e);
|
||
die;
|
||
}
|
||
}
|
||
}
|