


<pre class="wp-block-syntaxhighlighter-code">
<?php
/**
* Created by PhpStorm.
* User: Qiang Wang
* Date: 2020/03/16
* Time: 14:47
*/
namespace api\controllers;
use Yii;
use yii\base\InvalidConfigException;
use yii\db\Exception;
use yii\rest\ActiveController;
/**
* Class TaskGroupController
* @package api\controllers
*
* @author Qiang Wang <shuijingwanwq@163.com>
* @since 1.0
*/
class TaskGroupController extends ActiveController
{
public $serializer = [
'class' => 'api\rests\task_group\Serializer',
'collectionEnvelope' => 'items',
];
/**
* @inheritdoc
*/
public function actions()
{
$actions = parent::actions();
// 禁用"view"、"update"、"delete"、"options"动作
unset($actions['view'], $actions['update'], $actions['delete'], $actions['options']);
$actions['index']['class'] = 'api\rests\task_group\IndexAction';
$actions['create']['class'] = 'api\rests\task_group\CreateAction';
return $actions;
}
/**
* {@inheritdoc}
* @throws InvalidConfigException if a registered parser does not implement the [[RequestParserInterface]].
* @throws Exception execution failed
*/
public function afterAction($action, $result)
{
if ($action->id == 'create' && $result['code'] === 10000) {
// 返回所有请求参数
$request = Yii::$app->request;
$requestParams = $request->getBodyParams();
// 添加请求参数
$requestParams['task_group']['id'] = $result['data']['id'];
$requestParams['task_group']['uuid'] = $result['data']['uuid'];
$request->setBodyParams($requestParams);
$requestParams = $request->getBodyParams();
// 运行操作ID:index
$this->runAction('index');
}
return parent::afterAction($action, $result);
}
}
</pre>

{
"name": "Method Not Allowed",
"message": "Method Not Allowed. This URL can only handle the following request methods: GET, HEAD.",
"code": 0,
"status": 405,
"type": "yii\\web\\MethodNotAllowedHttpException"
}

<pre class="wp-block-syntaxhighlighter-code">
<?php
/**
* Created by PhpStorm.
* User: Qiang Wang
* Date: 2020/03/16
* Time: 14:47
*/
namespace api\controllers;
use api\filters\TaskGroupFilter;
use yii\rest\ActiveController;
/**
* Class TaskGroupController
* @package api\controllers
*
* @author Qiang Wang <shuijingwanwq@163.com>
* @since 1.0
*/
class TaskGroupController extends ActiveController
{
public $serializer = [
'class' => 'api\rests\task_group\Serializer',
'collectionEnvelope' => 'items',
];
/**
* {@inheritdoc}
*/
public function behaviors()
{
$behaviors = parent::behaviors();
$behaviors['taskGroupFilter'] = [
'class' => TaskGroupFilter::className(),
'only' => ['create'],
];
return $behaviors;
}
/**
* @inheritdoc
*/
public function actions()
{
$actions = parent::actions();
// 禁用"view"、"update"、"delete"、"options"动作
unset($actions['view'], $actions['update'], $actions['delete'], $actions['options']);
$actions['index']['class'] = 'api\rests\task_group\IndexAction';
$actions['create']['class'] = 'api\rests\task_group\CreateAction';
return $actions;
}
}
</pre>
<pre class="wp-block-syntaxhighlighter-code">
<?php
/**
* Created by PhpStorm.
* User: Qiang Wang
* Date: 2020/03/18
* Time: 16:27
*/
namespace api\filters;
use Yii;
use api\services\TaskGroupService;
use yii\base\ActionFilter;
use yii\base\InvalidConfigException;
use yii\httpclient\Exception;
/**
* 任务组(即一键多渠道发布)过滤器
* @package api\filters
*
* @author Qiang Wang <shuijingwanwq@163.com>
* @since 1.0
*/
class TaskGroupFilter extends ActionFilter
{
/**
* {@inheritdoc}
*
* @throws Exception
* @throws InvalidConfigException
*/
public function afterAction($action, $result)
{
if ($result['code'] === 10000) {
// 返回所有请求参数
$request = Yii::$app->request;
$requestParams = $request->getBodyParams();
// 添加请求参数
$requestParams['task_group']['id'] = $result['data']['id'];
$requestParams['task_group']['uuid'] = $result['data']['uuid'];
/* 操作数据(一键发布至多渠道)(同步) */
TaskGroupService::createMultipleSync($requestParams);
}
return parent::afterAction($action, $result);
}
}
</pre>
<pre class="wp-block-syntaxhighlighter-code">
<?php
/**
* Created by PhpStorm.
* User: Qiang Wang
* Date: 2020/03/18
* Time: 10:23
*/
namespace common\services;
use common\logics\ArticleType;
use common\logics\Task;
use common\logics\WxArticle;
use common\logics\WxArticleNews;
use Yii;
use common\logics\Channel;
use common\logics\QqArticleNormal;
use common\logics\http\channel_pub_api\Article as HttpChannelPubApiArticle;
use yii\base\InvalidConfigException;
use yii\httpclient\Exception;
use yii\web\ServerErrorHttpException;
/**
* Class TaskGroupService
* @package common\services
*
* @author Qiang Wang <shuijingwanwq@163.com>
* @since 1.0
*/
class TaskGroupService extends Service
{
/**
* 创建任务组
* @param object $model 对象
*
* @param bool $runValidation 保存记录之前是否执行验证 (调用 [[validate()]]),默认为 true
*
* @return array
* 格式如下:
*
* [
* 'status' => true, // 成功
* 'data' => [ // object
* ]
* ]
*
* [
* 'status' => false, // 失败
* 'code' => 202184, // 返回码
* 'message' => '', // 说明
* ]
*
* @throws ServerErrorHttpException
*/
public function create($model, $runValidation = true)
{
if ($model->save($runValidation)) {
return ['status' => true, 'data' => $model];
} elseif ($model->hasErrors()) {
$firstError = '';
foreach ($model->getFirstErrors() as $message) {
$firstError = $message;
break;
}
return ['status' => false, 'code' => 202184, 'message' => Yii::t('error', Yii::t('error', Yii::t('error', '202184'), ['first_error' => $firstError]))];
} elseif (!$model->hasErrors()) {
throw new ServerErrorHttpException('Failed to create the object (task group) for unknown reason.');
}
}
/**
* 一键发布至多渠道(同步)
*
* @param array $requestParams 请求参数
*
* @throws Exception
* @throws InvalidConfigException
*/
public static function createMultipleSync($requestParams)
{
// 请求数据
$requestDatas = [];
// 判断渠道:企鹅号是否存在,准备对应渠道的数据
if (isset($requestParams[Channel::CODE_QQ])) {
foreach ($requestParams[Channel::CODE_QQ] as $key => $value) {
$value['source'] = $requestParams['task_group']['source'];
$value['source_uuid'] = $requestParams['task_group']['source_uuid'];
$value['source_pub_user_id'] = $requestParams['task_group']['source_pub_user_id'];
$value['source_callback_url'] = $requestParams['task_group']['source_callback_url'];
$value['task_group_id'] = $requestParams['task_group']['id'];
$value['task_group_uuid'] = $requestParams['task_group']['uuid'];
$value['cover_pics'] = $value['covers'] ?? [];
$value['apply'] = $value['original'] ?? QqArticleNormal::APPLY_DEFAULT;
unset($value['covers'], $value['original']);
$requestDatas[] = [
'url' => 'qq/v1/articles/standard',
'data' => $value,
];
}
}
// 判断渠道:微信公众帐号是否存在,准备对应渠道的数据
if (isset($requestParams[Channel::CODE_WX])) {
foreach ($requestParams[Channel::CODE_WX] as $key => $value) {
$value['app_ids'] = $value['channel_app_source_uuids'] ?? [];
$value['source'] = $requestParams['task_group']['source'];
$value['source_uuid'] = $requestParams['task_group']['source_uuid'];
$value['source_pub_user_id'] = $requestParams['task_group']['source_pub_user_id'];
$value['source_callback_url'] = $requestParams['task_group']['source_callback_url'];
$value['task_group_id'] = $requestParams['task_group']['id'];
$value['task_group_uuid'] = $requestParams['task_group']['uuid'];
$value['thumb'] = $value['covers'][0] ?? '';
$value['show_cover_pic'] = $value['cover_show'] ?? WxArticleNews::SHOW_COVER_PIC_DEFAULT;
$value['url'] = $value['source_url'] ?? '';
unset($value['covers'], $value['cover_show'], $value['source_url']);
$value['code'] = ArticleType::CODE_STANDARD;
$value['type'] = Task::TYPE_PUB;
$value['wx_send_type'] = WxArticle::WX_SEND_TYPE_MASS;
$requestDatas[] = [
'url' => 'wx/v1/wx-articles/article',
'data' => $value,
];
}
}
// 批量发布文章类型:标准(普通、图文)的文章
$httpChannelPubApiArticle = new HttpChannelPubApiArticle();
$httpChannelPubApiArticle->batchPostArticlesStandard(Yii::$app->params['groupId'], $requestDatas);
}
}
</pre>
<pre class="wp-block-syntaxhighlighter-code">
<?php
/**
* Created by PhpStorm.
* User: Qiang Wang
* Date: 2020/03/20
* Time: 13:56
*/
namespace common\logics\http\channel_pub_api;
use Yii;
use yii\httpclient\Client;
use yii\httpclient\CurlTransport;
use yii\web\ServerErrorHttpException;
/**
* 渠道发布接口的基础类
*
* @author Qiang Wang <shuijingwanwq@163.com>
* @since 1.0
*/
class Model extends \yii\base\Model
{
private $_httpClient;
/*
* 创建 HTTP 客户端对象
*
* @return object the created object
* @throws InvalidConfigException if a registered parser does not implement the [[RequestParserInterface]].
*/
public function getHttpClient()
{
if (!is_object($this->_httpClient)) {
$this->_httpClient = Yii::createObject([
'class' => Client::className(),
'baseUrl' => Yii::$app->params['channelPubApi']['hostInfo'] . Yii::$app->params['channelPubApi']['baseUrl'],
'requestConfig' => [
'format' => Client::FORMAT_JSON
],
'transport' => CurlTransport::className(),
]);
}
return $this->_httpClient;
}
/*
* 响应对象的处理
*
* @param object $response 响应对象
*
* @return array|bool
*
* 格式如下:
*
* 渠道发布接口的响应信息
* [
* 'message' => '', //说明
* 'data' => [], //数据
* ]
*
* 失败(将错误保存在 [[yii\base\Model::errors]] 属性中)
* false
*
* @throws ServerErrorHttpException 如果响应状态码不等于20x
*/
public function responseHandler($response)
{
// 检查响应状态码是否等于20x
$responseCode = $response->data['code'] ?? 0; // 返回码
if ($response->isOk) {
// 检查业务逻辑是否成功
if ($responseCode === 10000) {
return ['message' => $response->data['message'], 'data' => $response->data['data']];
} else {
$this->addError('id', Yii::t('error', Yii::t('error', Yii::t('error', '202186'), ['code' => $responseCode, 'message' => $response->data['message']])));
return !$this->hasErrors();
}
} else {
$responseMessage = $response->data['message'] ?? ''; // 说明
throw new ServerErrorHttpException(Yii::t('error', Yii::t('error', Yii::t('error', '202185'), ['status_code' => $response->statusCode, 'code' => $responseCode, 'message' => $responseMessage])), 202185);
}
}
}
</pre>
<pre class="wp-block-syntaxhighlighter-code">
<?php
/**
* Created by PhpStorm.
* User: Qiang Wang
* Date: 2020/03/20
* Time: 14:19
*/
namespace common\logics\http\channel_pub_api;
use Yii;
use yii\base\InvalidConfigException;
use yii\httpclient\Client;
use yii\httpclient\Exception;
/**
* 渠道发布接口的文章
*
* @author Qiang Wang <shuijingwanwq@163.com>
* @since 1.0
*/
class Article extends Model
{
/**
* 批量发布文章类型:标准(普通、图文)的文章
*
* @param string $groupId 租户ID
* @param array $requestDatas 请求数据
*
* @throws Exception
* @throws InvalidConfigException
*/
public function batchPostArticlesStandard($groupId, $requestDatas)
{
$client = new Client();
$requests = [];
foreach ($requestDatas as $requestData) {
$requests[] = $this->httpClient->createRequest()
->setMethod('post')
->setUrl($requestData['url'] . '?group_id=' . $groupId)
->setData($requestData['data']);
}
$client->batchSend($requests);
}
}
</pre>

{
"source": {
"channel_app_source_uuids": [
"55f62c8c67fc11ea809554ee75d2ebc1",
"36cf694a67fc11eaa79d54ee75d2ebc1",
"29473624681311eaab8e54ee75d2ebc1"
],
"title": "北京小汤山医院启用,设千张床位",
"content": "3月16日晚拍摄的北京小汤山医院局部(无人机照片)。记者从北京市疫情防控领导小组获悉,为做好境外输入人员疫情防控工作,3月16日起,启用北京小汤山医院。医院设床位1000余张,将主要用于境外来(返)京人员中需筛查人员、疑似病例及轻型、普通型确诊患者的筛查和治疗。",
"source": "spider",
"source_uuid": "825e6d5e36468cc4bf536799ce3565cf",
"source_pub_user_id": 1,
"source_callback_url": "http://scms.wjdev.chinamcloud.cn/api/thirdPush/callBack",
"task_group_source_article_id": 1,
"baijia_source_article_id": 2,
"baijia_author": "鞠焕宗",
"baijia_covers": [
"http://pic-bucket.ws.126.net/photo/0001/2020-03-17/F7TNMMRG00AN0001NOS.jpg",
"http://pic-bucket.ws.126.net/photo/0001/2020-03-17/F7TNMMRH00AN0001NOS.jpg",
"http://pic-bucket.ws.126.net/photo/0001/2020-03-17/F7TNMMRI00AN0001NOS.jpg"
],
"baijia_original": 0,
"baijia_original_url": "http://news.163.com/photoview/00AN0001/2307424.html",
"netease_source_article_id": 3,
"netease_author": "鞠焕宗",
"netease_article_category_id": 417,
"netease_covers": [
"http://pic-bucket.ws.126.net/photo/0001/2020-03-17/F7TNMMRG00AN0001NOS.jpg",
"http://pic-bucket.ws.126.net/photo/0001/2020-03-17/F7TNMMRH00AN0001NOS.jpg",
"http://pic-bucket.ws.126.net/photo/0001/2020-03-17/F7TNMMRI00AN0001NOS.jpg"
],
"netease_cover_type": "threeImg",
"netease_original": 0,
"qq_source_article_id": 4,
"qq_author": "鞠焕宗",
"qq_article_category_id": 24,
"qq_tag": "北京,疫情,防控,境外,输入,小汤山,医院",
"qq_covers": [
"http://pic-bucket.ws.126.net/photo/0001/2020-03-17/F7TNMMRG00AN0001NOS.jpg",
"http://pic-bucket.ws.126.net/photo/0001/2020-03-17/F7TNMMRH00AN0001NOS.jpg",
"http://pic-bucket.ws.126.net/photo/0001/2020-03-17/F7TNMMRI00AN0001NOS.jpg"
],
"qq_cover_type": 3,
"qq_original": 0,
"qq_original_platform": 0,
"qq_original_url": "",
"qq_original_author": "",
"weibo_source_article_id": 5,
"weibo_summary": "记者从北京市疫情防控领导小组获悉,为做好境外输入人员疫情防控工作,3月16日起,启用北京小汤山医院。医院设床位1000余张,将主要用于境外来(返)京人员中需筛查人员、疑似病例及轻型、普通型确诊患者的筛查和治疗。",
"weibo_link_content": "记者从北京市疫情防控领导小组获悉,为做好境外输入人员疫情防控工作,3月16日起,启用北京小汤山医院。医院设床位1000余张,将主要用于境外来(返)京人员中需筛查人员、疑似病例及轻型、普通型确诊患者的筛查和治疗。",
"weibo_covers": [
"http://pic-bucket.ws.126.net/photo/0001/2020-03-17/F7TNMMRG00AN0001NOS.jpg"
],
"wx_source_article_id": 6,
"wx_author": "鞠焕宗",
"wx_covers": [
"http://pic-bucket.ws.126.net/photo/0001/2020-03-17/F7TNMMRG00AN0001NOS.jpg"
],
"wx_cover_show": 1,
"wx_description": "记者从北京市疫情防控领导小组获悉,为做好境外输入人员疫情防控工作,3月16日起,启用北京小汤山医院。医院设床位1000余张,将主要用于境外来(返)京人员中需筛查人员、疑似病例及轻型、普通型确诊患者的筛查和治疗。",
"wx_source_url": "http://news.163.com/photoview/00AN0001/2307424.html",
"toutiao_source_article_id": 7,
"toutiao_source_url": "http://news.163.com/photoview/00AN0001/2307424.html"
},
"task_group": {
"source": "spider",
"source_uuid": "825e6d5e36468cc4bf536799ce3565cf",
"source_pub_user_id": 1,
"source_callback_url": "http://scms.wjdev.chinamcloud.cn/api/thirdPush/callBack",
"source_article_id": 1
},
"baijia": [
{
"channel_app_source_uuids": [
"29473624681311eaab8e54ee75d2ebc1"
],
"title": "北京小汤山医院启用,设千张床位",
"content": "3月16日晚拍摄的北京小汤山医院局部(无人机照片)。记者从北京市疫情防控领导小组获悉,为做好境外输入人员疫情防控工作,3月16日起,启用北京小汤山医院。医院设床位1000余张,将主要用于境外来(返)京人员中需筛查人员、疑似病例及轻型、普通型确诊患者的筛查和治疗。",
"source_article_id": 2,
"author": "鞠焕宗",
"covers": [
"http://pic-bucket.ws.126.net/photo/0001/2020-03-17/F7TNMMRG00AN0001NOS.jpg",
"http://pic-bucket.ws.126.net/photo/0001/2020-03-17/F7TNMMRH00AN0001NOS.jpg",
"http://pic-bucket.ws.126.net/photo/0001/2020-03-17/F7TNMMRI00AN0001NOS.jpg"
],
"original": 0,
"original_url": "http://news.163.com/photoview/00AN0001/2307424.html"
}
],
"netease": [
{
"channel_app_source_uuids": [
"29473624681311eaab8e54ee75d2ebc1"
],
"title": "北京小汤山医院启用,设千张床位",
"content": "3月16日晚拍摄的北京小汤山医院局部(无人机照片)。记者从北京市疫情防控领导小组获悉,为做好境外输入人员疫情防控工作,3月16日起,启用北京小汤山医院。医院设床位1000余张,将主要用于境外来(返)京人员中需筛查人员、疑似病例及轻型、普通型确诊患者的筛查和治疗。",
"source_article_id": 3,
"author": "鞠焕宗",
"article_category_id": 417,
"covers": [
"http://pic-bucket.ws.126.net/photo/0001/2020-03-17/F7TNMMRG00AN0001NOS.jpg",
"http://pic-bucket.ws.126.net/photo/0001/2020-03-17/F7TNMMRH00AN0001NOS.jpg",
"http://pic-bucket.ws.126.net/photo/0001/2020-03-17/F7TNMMRI00AN0001NOS.jpg"
],
"cover_type": "threeImg",
"original": 0
}
],
"qq": [
{
"channel_app_source_uuids": [
"55f62c8c67fc11ea809554ee75d2ebc1"
],
"title": "北京小汤山医院启用,设千张床位",
"content": "3月16日晚拍摄的北京小汤山医院局部(无人机照片)。记者从北京市疫情防控领导小组获悉,为做好境外输入人员疫情防控工作,3月16日起,启用北京小汤山医院。医院设床位1000余张,将主要用于境外来(返)京人员中需筛查人员、疑似病例及轻型、普通型确诊患者的筛查和治疗。",
"source_article_id": 4,
"author": "鞠焕宗",
"article_category_id": 24,
"tag": "北京,疫情,防控,境外,输入,小汤山,医院",
"covers": [
"http://pic-bucket.ws.126.net/photo/0001/2020-03-17/F7TNMMRG00AN0001NOS.jpg",
"http://pic-bucket.ws.126.net/photo/0001/2020-03-17/F7TNMMRH00AN0001NOS.jpg",
"http://pic-bucket.ws.126.net/photo/0001/2020-03-17/F7TNMMRI00AN0001NOS.jpg"
],
"cover_type": 3,
"original": 0,
"original_platform": 0,
"original_url": "",
"original_author": ""
},
{
"channel_app_source_uuids": [
"36cf694a67fc11eaa79d54ee75d2ebc1"
],
"title": "北京小汤山医院启用,设千张床位",
"content": "3月16日晚拍摄的北京小汤山医院局部(无人机照片)。记者从北京市疫情防控领导小组获悉,为做好境外输入人员疫情防控工作,3月16日起,启用北京小汤山医院。医院设床位1000余张,将主要用于境外来(返)京人员中需筛查人员、疑似病例及轻型、普通型确诊患者的筛查和治疗。",
"source_article_id": 4,
"author": "鞠焕宗",
"article_category_id": 24,
"tag": "北京,疫情,防控,境外,输入,小汤山,医院",
"covers": [
"http://pic-bucket.ws.126.net/photo/0001/2020-03-17/F7TNMMRJ00AN0001NOS.jpg",
"http://pic-bucket.ws.126.net/photo/0001/2020-03-17/F7TNMMRH00AN0001NOS.jpg",
"http://pic-bucket.ws.126.net/photo/0001/2020-03-17/F7TNMMRI00AN0001NOS.jpg"
],
"cover_type": 3,
"original": 0,
"original_platform": 0,
"original_url": "",
"original_author": ""
}
],
"weibo": [
{
"channel_app_source_uuids": [
"渠道的应用的来源ID(UUID)"
],
"title": "北京小汤山医院启用,设千张床位",
"content": "内容",
"source_article_id": "来源文章ID",
"summary": "导语",
"link_content": "链接内容",
"covers": [
"封面图"
]
}
],
"wx": [
{
"channel_app_source_uuids": [
"渠道的应用的来源ID(UUID)"
],
"title": "北京小汤山医院启用,设千张床位",
"content": "内容",
"source_article_id": "来源文章ID",
"author": "作者",
"covers": [
"封面图"
],
"cover_show": "封面图是否显示",
"description": "描述",
"source_url": "来源链接"
}
],
"toutiao": [
{
"channel_app_source_uuids": [
"渠道的应用的来源ID(UUID)"
],
"title": "北京小汤山医院启用,设千张床位",
"content": "内容",
"source_article_id": "来源文章ID",
"source_url": "来源链接"
}
]
}
{
"code": 10000,
"message": "已发布文章至渠道发布,待发布至多渠道,请稍候",
"data": {
"source_article_id": 1,
"is_deleted": 0,
"deleted_at": 0,
"status": 1,
"created_at": 1584758252,
"updated_at": 1584758252,
"uuid": "eba334aa6b1c11ea8d7154ee75d2ebc1",
"id": 63
}
}




PHP / Laravel / Yii2 老项目维护与长期技术支持
如果你的 PHP / Laravel / Yii2 项目已经上线,但遇到原开发离职、Bug 长期无人修复、接口不稳定、性能下降、代码难以接手等问题,可以联系我做一次远程技术排查。
适合以下情况:
✅ 老旧 PHP 系统无人维护
✅ Laravel / Yii2 项目 Bug 修复
✅ 后台管理系统小功能迭代
✅ RESTful API 接口排查
✅ MySQL / Redis / Nginx 性能问题
✅ 长期远程兼职维护
可先从一次小问题开始:
✅ 线上报错排查
✅ 接口异常分析
✅ 慢查询与性能瓶颈定位
✅ 代码结构初步评估
✅ 部署环境与日志检查
如需咨询,请联系我,并注明:PHP 维护咨询。
联系方式:
Telegram:@shuijingwan
微信:13980074657
邮箱:shuijingwanwq@gmail.com

发表回复