In Yii 2.0, one-key multi-channel publishing (that is, in one interface request, batch call multiple interface requests) is implemented
1. In the previous release interface, only one channel is supported for a release. Therefore, the release of the three channels needs to call the interface (APP, Netease account, Weibo) 3 times respectively, as shown in Figure 1
2. On the current release interface, it is ready to publish at one time and can be published on multiple channels, as shown in Figure 2
3. In the channel publishing interface, the task (article) release of each channel is isolated, and it is not supported in one release, and is published to multiple channels, as shown in Figure 3
4. At this stage, it needs to be supported in one release and released to multiple channels. Channel publishing provides a one-click multi-channel publishing interface. There are 2 scenarios, the first one: the publishing interface of other channels is called through HTTP within the interface. Scenario 2: Call other controller actions in the controller action inside the interface. Because, the entry of an interface is essentially the controller action. It was finally decided to adopt the second plan.
5. Edit /api/controllers/taskGroupController.php, overwrite the afterAction() method. Run other operation IDs in it.
* @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);
}
}
6. Request: IndexAction, error: method not allowed, method not allowed. This url can only handle the following request methods: GET, HEAD. From this point of view, other controller actions are called in the controller action inside the interface. is limited by the request type. Based on safety considerations, it is temporarily not allowed to release corresponding restrictions. as shown in Figure 4
{
"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"
}
7. Moreover, the publishing interface of each channel is distributed in independent applications such as QQ and WX. In the API application, the operation ID of the QQ, WX and other applications cannot be run. Therefore, it is decided to adopt the first option: the publishing interface of other channels is called through HTTP inside the interface. as shown in Figure 5
8. Edit /api/controllers/taskGroupController.php, add a filter in the behaviors() method: TaskGroupFilter
* @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;
}
}
9. Task group (that is, one-click multi-channel release) filter, create a new /api/filters/taskGroupFilter.php
* @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);
}
}
10. Realize the one-click distribution of the task group service class to the multi-channel (sync) method. For the time being, only Penguin and WeChat are realized. CreateMultipIPSync($requestParams), edit /common/services/TaskGroupService.php
* @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);
}
}
11. Implement the HTTP model class, and the channel publishes the basic class of the interface. New /common/logics/http/channel_pub_api/model.php
* @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);
}
}
}
12. Realize the HTTP model class, and the channel publishes the articles of the interface. Types of articles published in batches: standard (normal, graphic) articles. New /common/logics/http/channel_pub_api/article.php
* @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);
}
}
13. POST request:http://api.channel-pub-api.localhost/v1/task-groups?group_id=015ce30b116ce86058fa6ab4fea4ac63. Create a task group (that is, one-click multi-channel publishing). Successful response. as shown in Figure 6
{
"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
}
}
14. Check the log messages of the API application, and request 2 times Penguin and 1 WeChat respectively. as shown in Figure 7
15. Check the log messages of the QQ application, and request the Penguin number 2 times respectively. as shown in Figure 8
16. Check the log messages of the WX application, and request WeChat 1 time respectively. Figure 9
17. View the data in the mysql table, and write 2 times Penguin and 1 WeChat respectively. in line with expectations. All other channels can be handled in the same way according to similar rules. As shown in Figure 10









