在 Yii 2.0 中,编辑与更新租户的任务类型与步骤设置(多条记录、树形结构)的实现 (二)

1、编辑与更新租户的任务类型与步骤设置的需求有所变更,需要让用户可自主添加任务类型,然后从 18 个步骤中选择其中一些加入至新添加的任务类型中。上一版本的实现:http://www.shuijingwanwq.com/2019/11/06/3583/ ,在上一版本中,仅允许用户从现有的任务类型与步骤中删除或者取消勾选,但是不能够新增加任务类型。

2、在全局的任务步骤配置表中,再添加一个 手动任务 下的步骤(上传素材),后续每个租户第 1 次进入编辑页面时,默认包含 5 个任务类型,每个任务类型下的任务步骤全部为勾选状态。此时,用户可自主添加任务类型,然后从 18 个步骤中选择其中一些加入至新添加的任务类型中。任务类型可(添加、删除、重命名其名称、排序),不可(勾选、不勾选)。任务步骤可从 18 个任务步骤列表中选择(添加、删除、勾选、不勾选、排序)。

3、新建数据库迁移文件(/console/migrations/m191108_073439_config_task_steps.php),新建步骤配置表,初始化步骤配置表的记录,删除租户的任务配置表、删除租户的任务步骤配置表

<?php
use yii/db/Migration;
/**
* Class m191108_073439_config_task_steps
*/
class m191108_073439_config_task_steps extends Migration
{
/**
* {@inheritdoc}
*/
public function safeUp()
{
$tableOptions = null;
if ($this->db->driverName === 'mysql') {
$tableOptions = 'CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE=InnoDB COMMENT="步骤配置"';
}
$this->createTable('{{%config_step}}', [
'id' => $this->primaryKey(),
'group_id' => $this->string(32)->notNull()->comment('租户ID'),
'code' => $this->string(32)->notNull()->comment('代码'),
'name' => $this->string(32)->notNull()->comment('名称'),
'category' => $this->smallInteger(1)->notNull()->defaultValue(1)->comment('类型,1:系统内置;2:用户定义'),
'status' => $this->smallInteger()->notNull()->defaultValue(1)->comment('状态,0:禁用;1:启用'),
'is_not_isolated' => $this->smallInteger()->notNull()->defaultValue(0)->comment('是否跨租户(不隔离),0:否;1:是'),
'is_deleted' => $this->smallInteger()->notNull()->defaultValue(0)->comment('是否被删除,0:否;1:是'),
'created_at' => $this->integer()->notNull()->defaultValue(0)->comment('创建时间'),
'updated_at' => $this->integer()->notNull()->defaultValue(0)->comment('更新时间'),
'deleted_at' => $this->integer()->notNull()->defaultValue(0)->comment('删除时间'),
], $tableOptions);
$this->createIndex('idx_code', '{{%config_step}}', ['code'], $unique = false);
$this->insert('{{%config_step}}', [
'id' => 1,
'group_id' => '',
'code' => 'retrieval',
'name' => '取稿',
'category' => 1,
'status' => 1,
'is_not_isolated' => 0,
'is_deleted' => 0,
'created_at' => time(),
'updated_at' => time(),
'deleted_at' => 0,
]);
$this->insert('{{%config_step}}', [
'id' => 2,
'group_id' => '',
'code' => 'begin_shot',
'name' => '上传素材',
'category' => 1,
'status' => 1,
'is_not_isolated' => 0,
'is_deleted' => 0,
'created_at' => time(),
'updated_at' => time(),
'deleted_at' => 0,
]);
$this->insert('{{%config_step}}', [
'id' => 3,
'group_id' => '',
'code' => 'material_review',
'name' => '素材审核',
'category' => 1,
'status' => 1,
'is_not_isolated' => 0,
'is_deleted' => 0,
'created_at' => time(),
'updated_at' => time(),
'deleted_at' => 0,
]);
$this->insert('{{%config_step}}', [
'id' => 4,
'group_id' => '',
'code' => 'writing',
'name' => '撰写稿件',
'category' => 1,
'status' => 1,
'is_not_isolated' => 0,
'is_deleted' => 0,
'created_at' => time(),
'updated_at' => time(),
'deleted_at' => 0,
]);
$this->insert('{{%config_step}}', [
'id' => 5,
'group_id' => '',
'code' => 'end_shot',
'name' => '拍摄完成',
'category' => 1,
'status' => 1,
'is_not_isolated' => 0,
'is_deleted' => 1,
'created_at' => time(),
'updated_at' => time(),
'deleted_at' => time(),
]);
$this->insert('{{%config_step}}', [
'id' => 6,
'group_id' => '',
'code' => 'commit_doc',
'name' => '提交稿件',
'category' => 1,
'status' => 1,
'is_not_isolated' => 0,
'is_deleted' => 1,
'created_at' => time(),
'updated_at' => time(),
'deleted_at' => time(),
]);
$this->insert('{{%config_step}}', [
'id' => 7,
'group_id' => '',
'code' => 'manuscript_review',
'name' => '稿件审核',
'category' => 1,
'status' => 1,
'is_not_isolated' => 0,
'is_deleted' => 0,
'created_at' => time(),
'updated_at' => time(),
'deleted_at' => 0,
]);
$this->insert('{{%config_step}}', [
'id' => 8,
'group_id' => '',
'code' => 'writing_draft',
'name' => '撰写通稿',
'category' => 1,
'status' => 1,
'is_not_isolated' => 0,
'is_deleted' => 0,
'created_at' => time(),
'updated_at' => time(),
'deleted_at' => 0,
]);
$this->insert('{{%config_step}}', [
'id' => 9,
'group_id' => '',
'code' => 'newspaper_issued',
'name' => '报纸签发',
'category' => 1,
'status' => 1,
'is_not_isolated' => 0,
'is_deleted' => 0,
'created_at' => time(),
'updated_at' => time(),
'deleted_at' => 0,
]);
$this->insert('{{%config_step}}', [
'id' => 10,
'group_id' => '',
'code' => 'video_editing',
'name' => '视频编辑',
'category' => 1,
'status' => 1,
'is_not_isolated' => 0,
'is_deleted' => 0,
'created_at' => time(),
'updated_at' => time(),
'deleted_at' => 0,
]);
$this->insert('{{%config_step}}', [
'id' => 11,
'group_id' => '',
'code' => 'tandem_list',
'name' => '串联单',
'category' => 1,
'status' => 1,
'is_not_isolated' => 0,
'is_deleted' => 0,
'created_at' => time(),
'updated_at' => time(),
'deleted_at' => 0,
]);
$this->insert('{{%config_step}}', [
'id' => 12,
'group_id' => '',
'code' => 'wechat_group_reference',
'name' => '微信组稿引用',
'category' => 1,
'status' => 1,
'is_not_isolated' => 0,
'is_deleted' => 0,
'created_at' => time(),
'updated_at' => time(),
'deleted_at' => 0,
]);
$this->insert('{{%config_step}}', [
'id' => 13,
'group_id' => '',
'code' => 'finish_interview',
'name' => '结束采访',
'category' => 1,
'status' => 1,
'is_not_isolated' => 0,
'is_deleted' => 0,
'created_at' => time(),
'updated_at' => time(),
'deleted_at' => 0,
]);
$this->insert('{{%config_step}}', [
'id' => 14,
'group_id' => '',
'code' => 'program_render',
'name' => '节目上传',
'category' => 1,
'status' => 1,
'is_not_isolated' => 0,
'is_deleted' => 0,
'created_at' => time(),
'updated_at' => time(),
'deleted_at' => 0,
]);
$this->insert('{{%config_step}}', [
'id' => 15,
'group_id' => '',
'code' => 'website_release',
'name' => '网站发布',
'category' => 1,
'status' => 1,
'is_not_isolated' => 0,
'is_deleted' => 0,
'created_at' => time(),
'updated_at' => time(),
'deleted_at' => 0,
]);
$this->insert('{{%config_step}}', [
'id' => 16,
'group_id' => '',
'code' => 'program_authen',
'name' => '节目审查',
'category' => 1,
'status' => 1,
'is_not_isolated' => 0,
'is_deleted' => 0,
'created_at' => time(),
'updated_at' => time(),
'deleted_at' => 0,
]);
$this->insert('{{%config_step}}', [
'id' => 17,
'group_id' => '',
'code' => 'app_release',
'name' => 'APP发布',
'category' => 1,
'status' => 1,
'is_not_isolated' => 0,
'is_deleted' => 0,
'created_at' => time(),
'updated_at' => time(),
'deleted_at' => 0,
]);
$this->insert('{{%config_step}}', [
'id' => 18,
'group_id' => '',
'code' => 'wechat_release',
'name' => '微信发布',
'category' => 1,
'status' => 1,
'is_not_isolated' => 0,
'is_deleted' => 0,
'created_at' => time(),
'updated_at' => time(),
'deleted_at' => 0,
]);
$this->insert('{{%config_step}}', [
'id' => 19,
'group_id' => '',
'code' => 'blog_release',
'name' => '微博发布',
'category' => 1,
'status' => 1,
'is_not_isolated' => 0,
'is_deleted' => 0,
'created_at' => time(),
'updated_at' => time(),
'deleted_at' => 0,
]);
$this->insert('{{%config_step}}', [
'id' => 20,
'group_id' => '',
'code' => 'qq_release',
'name' => '企鹅号发布',
'category' => 1,
'status' => 1,
'is_not_isolated' => 0,
'is_deleted' => 0,
'created_at' => time(),
'updated_at' => time(),
'deleted_at' => 0,
]);
$this->alterColumn('{{%config_task_step}}', 'config_task_id', $this->integer()->notNull()->comment('任务配置ID'));
$this->addColumn('{{%config_task_step}}', 'config_task_code', $this->string(32)->notNull()->comment('任务配置代码')->after('config_task_id'));
$this->addColumn('{{%config_task_step}}', 'config_step_id', $this->integer()->notNull()->comment('步骤配置ID')->after('config_task_code'));
$this->update('{{%config_task_step}}', ['config_task_code' => 'tv_broadcast'], ['config_task_id' => 1]);
$this->update('{{%config_task_step}}', ['config_task_code' => 'inter_view'], ['config_task_id' => 2]);
$this->update('{{%config_task_step}}', ['config_task_code' => 'rich_doc'], ['config_task_id' => 3]);
$this->update('{{%config_task_step}}', ['config_step_id' => 1], ['step_code' => 'retrieval']);
$this->update('{{%config_task_step}}', ['config_step_id' => 2], ['step_code' => 'begin_shot']);
$this->update('{{%config_task_step}}', ['config_step_id' => 3], ['step_code' => 'material_review']);
$this->update('{{%config_task_step}}', ['config_step_id' => 4], ['step_code' => 'writing']);
$this->update('{{%config_task_step}}', ['config_step_id' => 5, 'status' => 1], ['step_code' => 'end_shot']);
$this->update('{{%config_task_step}}', ['config_step_id' => 6, 'is_default' => 1, 'is_deleted' => 1, 'deleted_at' => time()], ['step_code' => 'commit_doc']);
$this->update('{{%config_task_step}}', ['config_step_id' => 7], ['step_code' => 'manuscript_review']);
$this->update('{{%config_task_step}}', ['config_step_id' => 8], ['step_code' => 'writing_draft']);
$this->update('{{%config_task_step}}', ['config_step_id' => 9], ['step_code' => 'newspaper_issued']);
$this->update('{{%config_task_step}}', ['config_step_id' => 10], ['step_code' => 'video_editing']);
$this->update('{{%config_task_step}}', ['config_step_id' => 11], ['step_code' => 'tandem_list']);
$this->update('{{%config_task_step}}', ['config_step_id' => 12], ['step_code' => 'wechat_group_reference']);
$this->update('{{%config_task_step}}', ['config_step_id' => 13], ['step_code' => 'finish_interview']);
$this->update('{{%config_task_step}}', ['config_step_id' => 14], ['step_code' => 'program_render']);
$this->update('{{%config_task_step}}', ['config_step_id' => 15], ['step_code' => 'website_release']);
$this->update('{{%config_task_step}}', ['config_step_id' => 16], ['step_code' => 'program_authen']);
$this->update('{{%config_task_step}}', ['config_step_id' => 17], ['step_code' => 'app_release']);
$this->update('{{%config_task_step}}', ['config_step_id' => 18], ['step_code' => 'wechat_release']);
$this->update('{{%config_task_step}}', ['config_step_id' => 19], ['step_code' => 'blog_release']);
$this->update('{{%config_task_step}}', ['config_step_id' => 20], ['step_code' => 'qq_release']);
$this->dropTable('{{%config_group_task}}');
$this->dropTable('{{%config_group_task_step}}');
$this->insert('{{%config_task_step}}', [
'id' => 27,
'group_id' => '',
'config_task_id' => 4,
'config_task_code' => 'other',
'config_step_id' => 2,
'step_code' => 'begin_shot',
'step_name' => '上传素材',
'sort_order' => 1,
'is_default' => 1,
'up_status_type' => 2,
'status' => 1,
'created_at' => time(),
'updated_at' => time()
]);
$this->dropIndex('uc_group_id_config_task_id_step_code_is_deleted_deleted_at', '{{%config_task_step}}');
$this->createIndex('uc_group_id_config_task_code_step_code_is_deleted_deleted_at', '{{%config_task_step}}', ['group_id', 'config_task_code', 'step_code', 'is_deleted', 'deleted_at'], $unique = true);
}
/**
* {@inheritdoc}
*/
public function safeDown()
{
echo "m191108_073439_config_task_steps cannot be reverted./n";
return false;
}
/*
// Use up()/down() to run migration code without a transaction.
public function up()
{
}
public function down()
{
echo "m191108_073439_config_task_steps cannot be reverted./n";
return false;
}
*/
}

4、打开表:任务配置,如图1

打开表:任务配置

图1

5、打开表:步骤配置,如图2

打开表:步骤配置

图2

6、打开表:任务步骤配置,如图3

打开表:任务步骤配置

图3

7、编辑路由配置文件,/api/config/urlManager.php

// 基础设置 - 步骤配置
[
'class' => 'yii/rest/UrlRule',
'controller' => ['v1/config-step'],
'only' => ['index'],
],
// 基础设置 - 租户设置 - 租户的任务类型与步骤设置
[
'class' => 'yii/rest/UrlRule',
'controller' => ['v1/config-task-step'],
'only' => ['view', 'edit', 'update'],
'tokens' => ['{id}' => '<id://w[//w,:;-]*>'],
'extraPatterns' => [
'GET edit/{id}' => 'edit',
],
],
// 移动端 - 获取租户的任务类型与步骤设置详情
[
'class' => 'yii/rest/UrlRule',
'controller' => ['v1/mobile/config-task-step'],
'only' => ['view'],
'tokens' => ['{id}' => '<id://w[//w,:;-]*>'],
'extraPatterns' => [
],
],

8、编辑控制器文件,/api/controllers/ConfigTaskStepController.php

<?php
/**
* Created by PhpStorm.
* User: Qiang Wang
* Date: 2018/04/04
* Time: 15:35
*/
namespace api/controllers;
use yii/rest/ActiveController;
/**
* Class ConfigTaskStepController
* @package api/controllers
*
* @author Qiang Wang <shuijingwanwq@163.com>
* @since 1.0
*/
class ConfigTaskStepController extends ActiveController
{
/**
* @inheritdoc
*/
public function actions()
{
$actions = parent::actions();
// 禁用"index"、"create"、"delete"、"options"动作
unset($actions['index'], $actions['create'], $actions['delete'], $actions['options']);
$actions['view']['class'] = 'api/rests/config_task_step/ViewAction';
$actions['update']['class'] = 'api/rests/config_task_step/UpdateAction';
$actions['edit'] = [
'class' => 'api/rests/config_task_step/EditAction',
'modelClass' => $this->modelClass,
'checkAccess' => [$this, 'checkAccess'],
];
return $actions;
}
}

9、新建编辑租户的任务类型与步骤设置的方法文件(/api/rests/config_task_step/EditAction.php)

<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace api/rests/config_task_step;
use Yii;
use api/models/redis/cmc_console/User as RedisCmcConsoleUser;
use api/services/ConfigTaskStepService;
use yii/web/UnprocessableEntityHttpException;
/**
* 编辑租户的任务类型与步骤设置:/config-task-steps/edit/my-group-id(config-task-step/edit)
*
* For more details and usage information on ViewAction, see the [guide article on rest controllers](guide:rest-controllers).
*
* @author Qiang Wang <shuijingwanwq@163.com>
* @since 1.0
*/
class EditAction extends Action
{
/**
* Displays a model.
* @param string $id the primary key of the model.
* @return array
* @throws UnprocessableEntityHttpException
*/
public function run($id)
{
// 当前用户的身份实例,未认证用户则为 Null
/* @var $identity RedisCmcConsoleUser */
$identity = Yii::$app->user->identity;
// 比对:group_id,检查其值是否等于:my-group-id
if ($id != 'my-group-id') {
throw new UnprocessableEntityHttpException(Yii::t('error', '226053'), 226053);
}
// 基于租户ID返回数据模型(任务步骤配置)列表
$configTaskStepItems = ConfigTaskStepService::findModelsByGroupId($identity->group_id, false);
return ['code' => 10000, 'message' => Yii::t('success', '126038'), 'data' => array_values($configTaskStepItems)];
}
}

10、编辑租户的任务类型的服务文件(/common/services/ConfigTaskService.php)

<?php
/**
* Created by PhpStorm.
* User: Qiang Wang
* Date: 2019/11/01
* Time: 10:19
*/
namespace common/services;
use Yii;
use common/logics/ConfigTask;
class ConfigTaskService extends Service
{
/**
* 基于租户ID返回数据模型列表
* @param string $groupId 租户ID
* @return array 一个 ActiveRecord 实例数组
* 格式如下:
* [
*     [ // object
*         'id' => 1, // ID
*         'group_id' => '', // 租户ID
*         'code' => 'tv_broadcast', // 任务代码
*         'name' => '电视播出', // 任务名称
*         'sort_order' => 1, // 顺序,顺序排列
*         'category' => 1, // 任务类型,1:系统内置;2:用户定义
*         'parameter' => '', // 自定义参数,序列化存储
*         'status' => 1, // 状态,0:禁用;1:启用
*         'is_deleted' => 0, // 是否被删除,0:否;1:是
*         'created_at' => 1573439809, // 创建时间
*         'updated_at' => 1573439809, // 更新时间
*         'deleted_at' => 0, // 删除时间
*     ],
*     [
*         'id' => 1,
*         'group_id' => '',
*         'code' => 'inter_view',
*         'name' => '采访任务',
*         'sort_order' => 2,
*         'category' => 1,
*         'parameter' => '',
*         'status' => 1,
*         'is_deleted' => 0,
*         'created_at' => 1573439809,
*         'updated_at' => 1573439809,
*         'deleted_at' => 0,
*     ],
*     ...
* ]
*/
public static function findModelsByGroupId($groupId)
{
// 基于租户ID查找资源(任务配置)列表
$configTaskItems = ConfigTask::find()->where(['group_id' => $groupId])->all();
if (empty($configTaskItems)) {
$groupId = '';
}
// 基于租户ID查找资源(任务配置、是否被删除:否)列表
return ConfigTask::findAllByGroupId($groupId);
}
}

11、编辑租户的任务类型与步骤设置的服务文件(/common/services/ConfigTaskStepService.php)

<?php
/**
* Created by PhpStorm.
* User: terryhong
* Date: 2018/12/20
* Time: 3:10 PM
*/
namespace common/services;
use Yii;
use common/logics/ConfigTask;
use common/logics/ConfigTaskStep;
use yii/web/NotFoundHttpException;
use yii/web/UnprocessableEntityHttpException;
class ConfigTaskStepService extends Service
{
/**
* 基于租户ID返回数据模型(任务步骤配置)列表
* @param string $groupId 租户ID
* @param bool $isDefaultYes 是否添加条件,默认步骤,1:是
* @return array 一个 ActiveRecord 实例数组
* 格式如下:
* [
*     1 = > [
*         'id' => 1, // ID
*         'code' => 'tv_broadcast', // 代码
*         'name' => '电视播出', // 名称
*         'sort_order' => 1, // 顺序,顺序排列
*         'steps' => [ // 步骤列表
*             [
*                 'id' => 2, // ID
*                 'code' => 'begin_shot', // 代码
*                 'name' => '上传素材', // 名称
*                 'sort_order' => 1, // 顺序,顺序排列
*                 'is_default' => 1, // 是否默认步骤,1:是;0:否
*             ],
*             [
*                 'id' => 4, // ID
*                 'code' => 'writing', // 代码
*                 'name' => '撰写稿件', // 名称
*                 'sort_order' => 2, // 顺序,顺序排列
*                 'is_default' => 1, // 是否默认步骤,1:是;0:否
*             ],
*             ...
*         ],
*     ],
*     ...
* ]
*/
public static function findModelsByGroupId($groupId, $isDefaultYes = true)
{
// 基于租户ID查找资源(任务步骤配置)列表
$configTaskStepItems = ConfigTaskStep::find()->where(['group_id' => $groupId])->all();
if (empty($configTaskStepItems)) {
$groupId = '';
}
// 基于租户ID查找资源(任务步骤配置、是否被删除:否)列表
if ($isDefaultYes) {
$configTaskStepIsDeletedNoItems = ConfigTaskStep::findAllIsDefaultYesByGroupId($groupId);
} else {
$configTaskStepIsDeletedNoItems = ConfigTaskStep::findAllByGroupId($groupId);
}
// 基于 任务配置ID 分组
$configTaskStepGroupItems = [];
foreach ($configTaskStepIsDeletedNoItems as $configTaskStepIsDeletedNoItem) {
$configTaskStepGroupItems[$configTaskStepIsDeletedNoItem->config_task_id][] = [
'id' => $configTaskStepIsDeletedNoItem->config_step_id,
'code' => $configTaskStepIsDeletedNoItem->step_code,
'name' => $configTaskStepIsDeletedNoItem->step_name,
'sort_order' => $configTaskStepIsDeletedNoItem->sort_order,
'is_default' => $configTaskStepIsDeletedNoItem->is_default,
];
}
// 基于租户ID返回数据模型(任务配置)列表
$configTaskItems = ConfigTaskService::findModelsByGroupId($groupId);
$data = [];
foreach ($configTaskItems as $configTaskItem) {
$data[$configTaskItem->id] = [
'id' => $configTaskItem->id,
'code' => $configTaskItem->code,
'name' => $configTaskItem->name,
'sort_order' => $configTaskItem->sort_order,
'steps' => isset($configTaskStepGroupItems[$configTaskItem->id]) ? $configTaskStepGroupItems[$configTaskItem->id] : [],
];
}
return $data;
}

12、在 Postman 中 GET:http://api.pcs-api.localhost/v1/config-task-steps/edit/my-group-id ,当租户未自定义时,即 任务配置表、任务步骤配置表 中无当前租户ID的记录时,默认获取 任务配置表、任务步骤配置表 中的租户ID为空的记录,响应数据与执行的 SQL 如下

{
"code": 10000,
"message": "编辑租户的任务类型与步骤设置成功",
"data": {
"items": [
{
"id": 1,
"code": "tv_broadcast",
"name": "电视播出",
"sort_order": 1,
"steps": [
{
"id": 2,
"code": "begin_shot",
"name": "上传素材",
"sort_order": 1,
"is_default": 1
},
{
"id": 4,
"code": "writing",
"name": "撰写稿件",
"sort_order": 2,
"is_default": 1
},
{
"id": 7,
"code": "manuscript_review",
"name": "稿件审核",
"sort_order": 4,
"is_default": 1
},
{
"id": 10,
"code": "video_editing",
"name": "视频编辑",
"sort_order": 5,
"is_default": 1
},
{
"id": 11,
"code": "tandem_list",
"name": "串联单",
"sort_order": 6,
"is_default": 1
},
{
"id": 14,
"code": "program_render",
"name": "节目上传",
"sort_order": 7,
"is_default": 1
},
{
"id": 16,
"code": "program_authen",
"name": "节目审查",
"sort_order": 8,
"is_default": 1
}
]
},
{
"id": 2,
"code": "inter_view",
"name": "采访任务",
"sort_order": 2,
"steps": [
{
"id": 2,
"code": "begin_shot",
"name": "上传素材",
"sort_order": 1,
"is_default": 1
},
{
"id": 3,
"code": "material_review",
"name": "素材审核",
"sort_order": 2,
"is_default": 1
},
{
"id": 4,
"code": "writing",
"name": "撰写稿件",
"sort_order": 4,
"is_default": 1
},
{
"id": 8,
"code": "writing_draft",
"name": "撰写通稿",
"sort_order": 5,
"is_default": 1
},
{
"id": 7,
"code": "manuscript_review",
"name": "稿件审核",
"sort_order": 6,
"is_default": 1
},
{
"id": 13,
"code": "finish_interview",
"name": "结束采访",
"sort_order": 7,
"is_default": 1
}
]
},
{
"id": 3,
"code": "rich_doc",
"name": "互联网发布",
"sort_order": 3,
"steps": [
{
"id": 1,
"code": "retrieval",
"name": "取稿",
"sort_order": 1,
"is_default": 1
},
{
"id": 4,
"code": "writing",
"name": "撰写稿件",
"sort_order": 2,
"is_default": 1
},
{
"id": 7,
"code": "manuscript_review",
"name": "稿件审核",
"sort_order": 4,
"is_default": 1
},
{
"id": 9,
"code": "newspaper_issued",
"name": "报纸签发",
"sort_order": 5,
"is_default": 1
},
{
"id": 12,
"code": "wechat_group_reference",
"name": "微信组稿引用",
"sort_order": 6,
"is_default": 1
},
{
"id": 15,
"code": "website_release",
"name": "网站发布",
"sort_order": 7,
"is_default": 1
},
{
"id": 17,
"code": "app_release",
"name": "APP发布",
"sort_order": 8,
"is_default": 1
},
{
"id": 18,
"code": "wechat_release",
"name": "微信发布",
"sort_order": 9,
"is_default": 1
},
{
"id": 19,
"code": "blog_release",
"name": "微博发布",
"sort_order": 10,
"is_default": 1
},
{
"id": 20,
"code": "qq_release",
"name": "企鹅号发布",
"sort_order": 11,
"is_default": 1
}
]
},
{
"id": 4,
"code": "other",
"name": "手动任务",
"sort_order": 4,
"steps": [
{
"id": 2,
"code": "begin_shot",
"name": "上传素材",
"sort_order": 1,
"is_default": 1
}
]
}
]
}
}
SELECT * FROM `pa_config_task_step` WHERE `group_id`='015ce30b116ce86058fa6ab4fea4ac63'
SELECT * FROM `pa_config_task_step` WHERE (`group_id`='') AND (`is_deleted`=0) ORDER BY `sort_order`, `id` DESC
SELECT * FROM `pa_config_task` WHERE `group_id`=''
SELECT * FROM `pa_config_task` WHERE (`group_id`='') AND (`is_deleted`=0) ORDER BY `sort_order`, `id` DESC

13、新建获取步骤配置列表的方法文件,/api/rests/config_step/IndexAction.php

<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace api/rests/config_step;
use Yii;
use api/models/ConfigStep;
use yii/base/InvalidConfigException;
use yii/data/ActiveDataProvider;
use yii/web/UnprocessableEntityHttpException;
/**
* 获取步骤配置列表:/config-steps(config-step/index)
*
* For more details and usage information on IndexAction, see the [guide article on rest controllers](guide:rest-controllers).
*
* @author Qiang Wang <shuijingwanwq@163.com>
* @since 1.0
*/
class IndexAction extends /yii/rest/IndexAction
{
/**
* Prepares the data provider that should return the requested collection of the models.
* @return mixed|object|ActiveDataProvider
* @throws InvalidConfigException if the configuration is invalid.
* @throws UnprocessableEntityHttpException
*/
protected function prepareDataProvider()
{
$requestParams = Yii::$app->getRequest()->getBodyParams();
if (empty($requestParams)) {
$requestParams = Yii::$app->getRequest()->getQueryParams();
}
$filter = null;
if ($this->dataFilter !== null) {
$this->dataFilter = Yii::createObject($this->dataFilter);
if ($this->dataFilter->load($requestParams)) {
$filter = $this->dataFilter->build();
if ($filter === false) {
$firstError = '';
foreach ($this->dataFilter->getFirstErrors() as $message) {
$firstError = $message;
break;
}
throw new UnprocessableEntityHttpException(Yii::t('error', Yii::t('error', Yii::t('error', '224003'), ['first_error' => $firstError])), 224003);
}
}
}
if ($this->prepareDataProvider !== null) {
return call_user_func($this->prepareDataProvider, $this, $filter);
}
/* @var $modelClass ConfigStep */
$modelClass = $this->modelClass;
// 查询步骤配置
$query = $modelClass::find()->isDeletedNo();
// 设置每页资源数量为资源总数
$count = $query->count();
$requestParams['per-page'] = $count;
return Yii::createObject([
'class' => ActiveDataProvider::className(),
'query' => $query,
'pagination' => [
'params' => $requestParams,
'pageSizeLimit' => [1, $count],
],
'sort' => [
'params' => $requestParams,
],
]);
}
}

14、新建获取步骤配置列表的序列化文件,/api/rests/config_step/Serializer.php

<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace api/rests/config_step;
use Yii;
use yii/data/DataProviderInterface;
/**
* Serializer converts resource objects and collections into array representation.
*
* Serializer is mainly used by REST controllers to convert different objects into array representation
* so that they can be further turned into different formats, such as JSON, XML, by response formatters.
*
* The default implementation handles resources as [[Model]] objects and collections as objects
* implementing [[DataProviderInterface]]. You may override [[serialize()]] to handle more types.
*
* @author Qiang Wang <shuijingwanwq@163.com>
* @since 1.0
*/
class Serializer extends /yii/rest/Serializer
{
/**
* Serializes a data provider.
* @param DataProviderInterface $dataProvider
* @return array the array representation of the data provider.
*/
protected function serializeDataProvider($dataProvider)
{
if ($this->preserveKeys) {
$models = $dataProvider->getModels();
} else {
$models = array_values($dataProvider->getModels());
}
$models = $this->serializeModels($models);
if (($pagination = $dataProvider->getPagination()) !== false) {
$this->addPaginationHeaders($pagination);
}
if ($this->request->getIsHead()) {
return null;
} elseif ($this->collectionEnvelope === null) {
return $models;
}
$result = [
$this->collectionEnvelope => $models,
];
if ($pagination !== false) {
return ['code' => 10000, 'message' => Yii::t('success', '126040'), 'data' => array_merge($result, $this->serializePagination($pagination))];
}
return ['code' => 10000, 'message' => Yii::t('success', '126040'), 'data' => $result];
}
}

15、在 Postman 中 GET:http://api.pcs-api.localhost/v1/config-steps ,获取步骤配置列表,以便于用户选择步骤添加至对应的类型中,响应数据与执行的 SQL 如下

{
"code": 10000,
"message": "获取步骤配置列表成功",
"data": {
"items": [
{
"id": 1,
"group_id": "",
"code": "retrieval",
"name": "取稿",
"category": 1,
"status": 1,
"is_not_isolated": 0,
"is_deleted": 0,
"created_at": 1573439819,
"updated_at": 1573439819,
"deleted_at": 0
},
{
"id": 2,
"group_id": "",
"code": "begin_shot",
"name": "上传素材",
"category": 1,
"status": 1,
"is_not_isolated": 0,
"is_deleted": 0,
"created_at": 1573439819,
"updated_at": 1573439819,
"deleted_at": 0
},
{
"id": 3,
"group_id": "",
"code": "material_review",
"name": "素材审核",
"category": 1,
"status": 1,
"is_not_isolated": 0,
"is_deleted": 0,
"created_at": 1573439819,
"updated_at": 1573439819,
"deleted_at": 0
},
{
"id": 4,
"group_id": "",
"code": "writing",
"name": "撰写稿件",
"category": 1,
"status": 1,
"is_not_isolated": 0,
"is_deleted": 0,
"created_at": 1573439819,
"updated_at": 1573439819,
"deleted_at": 0
},
{
"id": 7,
"group_id": "",
"code": "manuscript_review",
"name": "稿件审核",
"category": 1,
"status": 1,
"is_not_isolated": 0,
"is_deleted": 0,
"created_at": 1573439819,
"updated_at": 1573439819,
"deleted_at": 0
},
{
"id": 8,
"group_id": "",
"code": "writing_draft",
"name": "撰写通稿",
"category": 1,
"status": 1,
"is_not_isolated": 0,
"is_deleted": 0,
"created_at": 1573439819,
"updated_at": 1573439819,
"deleted_at": 0
},
{
"id": 9,
"group_id": "",
"code": "newspaper_issued",
"name": "报纸签发",
"category": 1,
"status": 1,
"is_not_isolated": 0,
"is_deleted": 0,
"created_at": 1573439819,
"updated_at": 1573439819,
"deleted_at": 0
},
{
"id": 10,
"group_id": "",
"code": "video_editing",
"name": "视频编辑",
"category": 1,
"status": 1,
"is_not_isolated": 0,
"is_deleted": 0,
"created_at": 1573439819,
"updated_at": 1573439819,
"deleted_at": 0
},
{
"id": 11,
"group_id": "",
"code": "tandem_list",
"name": "串联单",
"category": 1,
"status": 1,
"is_not_isolated": 0,
"is_deleted": 0,
"created_at": 1573439819,
"updated_at": 1573439819,
"deleted_at": 0
},
{
"id": 12,
"group_id": "",
"code": "wechat_group_reference",
"name": "微信组稿引用",
"category": 1,
"status": 1,
"is_not_isolated": 0,
"is_deleted": 0,
"created_at": 1573439819,
"updated_at": 1573439819,
"deleted_at": 0
},
{
"id": 13,
"group_id": "",
"code": "finish_interview",
"name": "结束采访",
"category": 1,
"status": 1,
"is_not_isolated": 0,
"is_deleted": 0,
"created_at": 1573439819,
"updated_at": 1573439819,
"deleted_at": 0
},
{
"id": 14,
"group_id": "",
"code": "program_render",
"name": "节目上传",
"category": 1,
"status": 1,
"is_not_isolated": 0,
"is_deleted": 0,
"created_at": 1573439819,
"updated_at": 1573439819,
"deleted_at": 0
},
{
"id": 15,
"group_id": "",
"code": "website_release",
"name": "网站发布",
"category": 1,
"status": 1,
"is_not_isolated": 0,
"is_deleted": 0,
"created_at": 1573439819,
"updated_at": 1573439819,
"deleted_at": 0
},
{
"id": 16,
"group_id": "",
"code": "program_authen",
"name": "节目审查",
"category": 1,
"status": 1,
"is_not_isolated": 0,
"is_deleted": 0,
"created_at": 1573439819,
"updated_at": 1573439819,
"deleted_at": 0
},
{
"id": 17,
"group_id": "",
"code": "app_release",
"name": "APP发布",
"category": 1,
"status": 1,
"is_not_isolated": 0,
"is_deleted": 0,
"created_at": 1573439819,
"updated_at": 1573439819,
"deleted_at": 0
},
{
"id": 18,
"group_id": "",
"code": "wechat_release",
"name": "微信发布",
"category": 1,
"status": 1,
"is_not_isolated": 0,
"is_deleted": 0,
"created_at": 1573439819,
"updated_at": 1573439819,
"deleted_at": 0
},
{
"id": 19,
"group_id": "",
"code": "blog_release",
"name": "微博发布",
"category": 1,
"status": 1,
"is_not_isolated": 0,
"is_deleted": 0,
"created_at": 1573439819,
"updated_at": 1573439819,
"deleted_at": 0
},
{
"id": 20,
"group_id": "",
"code": "qq_release",
"name": "企鹅号发布",
"category": 1,
"status": 1,
"is_not_isolated": 0,
"is_deleted": 0,
"created_at": 1573439819,
"updated_at": 1573439819,
"deleted_at": 0
}
],
"_links": {
"self": {
"href": "http://api.pcs-api.localhost/v1/config-steps?login_id=2e368664c41b8bf511bcc9c65d86dbc3&login_tid=549e02e6b704dbb437618029a711f7c8&per-page=18&page=1"
}
},
"_meta": {
"totalCount": 18,
"pageCount": 1,
"currentPage": 1,
"perPage": 18
}
}
}
SELECT COUNT(*) FROM `pa_config_step` WHERE `is_deleted`=0
SELECT COUNT(*) FROM `pa_config_step` WHERE `is_deleted`=0
SELECT * FROM `pa_config_step` WHERE `is_deleted`=0 LIMIT 18

16、新建更新任务类型与步骤设置的方法文件(/api/rests/config_task_step/UpdateAction.php)

<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace api/rests/config_task_step;
use Yii;
use api/models/ConfigTask;
use api/models/ConfigTaskStep;
use api/models/redis/cmc_console/User as RedisCmcConsoleUser;
use api/services/ConfigTaskStepService;
use yii/base/Model;
use yii/base/InvalidConfigException;
use yii/helpers/ArrayHelper;
use yii/web/UnprocessableEntityHttpException;
use yii/web/ServerErrorHttpException;
/**
* 更新租户的任务类型与步骤设置:/config-task-steps/{group_id}(config-task-step/update)
*
* For more details and usage information on CreateAction, see the [guide article on rest controllers](guide:rest-controllers).
*
* @author Qiang Wang <shuijingwanwq@163.com>
* @since 1.0
*/
class UpdateAction extends Action
{
/**
* @var string the scenario to be assigned to the model before it is validated and updated.
* $id {config_column_id}
*/
public $scenario = Model::SCENARIO_DEFAULT;
/**
* Updates an existing model.
* @param string $id the primary key of the model.
* @return array the model being updated
* @throws InvalidConfigException if a registered parser does not implement the [[RequestParserInterface]].
* @throws ServerErrorHttpException if there is any error when updating the model
* @throws /Throwable
*/
public function run($id)
{
// 当前用户的身份实例,未认证用户则为 Null
/* @var $identity RedisCmcConsoleUser */
$identity = Yii::$app->user->identity;
// 比对:group_id,检查其值是否等于:my-group-id
if ($id != 'my-group-id') {
throw new UnprocessableEntityHttpException(Yii::t('error', '226053'), 226053);
}
/* @var $modelClass ConfigTaskStep */
$modelClass = $this->modelClass;
$requestParams = Yii::$app->getRequest()->getBodyParams();
/* @var $configGroupTaskItem ConfigTask */
// 基于租户ID查找资源(任务配置、是否被删除:否)列表
$configTaskIsDeletedNoItems = ConfigTask::find()->where(['group_id' => $identity->group_id])->isDeletedNo()->indexBy(['code'])->all();
// 基于租户ID查找资源(任务配置)列表
$configTaskItems = ConfigTask::find()->where(['group_id' => $identity->group_id])->indexBy(['code'])->all();
/* @var $configGroupTaskStepItem ConfigTaskStep */
// 基于租户ID查找资源(任务步骤配置、是否被删除:否)列表
$configTaskStepIsDeletedNoItems = $modelClass::find()->where(['group_id' => $identity->group_id])->isDeletedNo()->indexBy(function ($row) { return $row['config_task_code'] . ':' . $row['step_code']; })->all();
// 基于租户ID查找资源(任务步骤配置)列表
$configTaskStepItems = $modelClass::find()->where(['group_id' => $identity->group_id])->indexBy(function ($row) { return $row['config_task_code'] . ':' . $row['step_code']; })->all();
if (!is_array($requestParams)) {
return ['code' => 226010, 'message' => Yii::t('error', '226010')];
}
$configTasks = [];
$configTaskSteps = [];
foreach ($requestParams as $configTask) {
// 检查 任务配置的(代码、名称、顺序) 是否存在
if (!ArrayHelper::keyExists('code', $configTask) || !ArrayHelper::keyExists('name', $configTask) || !ArrayHelper::keyExists('sort_order', $configTask)) {
throw new UnprocessableEntityHttpException(Yii::t('error', '226080'), 226080);
}
$configTasks[$configTask['code']] = [
'group_id' => $identity->group_id,
'code' => $configTask['code'],
'name' => $configTask['name'],
'sort_order' => $configTask['sort_order'],
];
// 检查 步骤配置列表 是否存在
if (ArrayHelper::keyExists('steps', $configTask)) {
foreach ($configTask['steps'] as $configTaskStep) {
// 检查 步骤代码、顺序、是否默认步骤 是否存在
if (!ArrayHelper::keyExists('code', $configTaskStep) || !ArrayHelper::keyExists('sort_order', $configTaskStep) || !ArrayHelper::keyExists('is_default', $configTaskStep)) {
throw new UnprocessableEntityHttpException(Yii::t('error', '226080'), 226080);
}
$configTaskSteps[$configTask['code'] . ':' . $configTaskStep['code']] = [
'group_id' => $identity->group_id,
'config_task_code' => $configTask['code'],
'step_code' => $configTaskStep['code'],
'sort_order' => $configTaskStep['sort_order'],
'is_default' => $configTaskStep['is_default'],
];
}
}
}
/* 任务配置 */
// 使用键名比较计算数组的差集,如果不为空,则删除 (软删除) 出现在 任务配置模型(MySQL) 中但是未出现在 任务配置 中的记录
$configTaskDiffItems = array_diff_key($configTaskIsDeletedNoItems, $configTasks);
$configTasks = array_values($configTasks);
$configTaskModels = [];
foreach ($configTasks as $configTask) {
if (isset($configTaskItems[$configTask['code']])) {
$configTaskItems[$configTask['code']]->scenario = ConfigTask::SCENARIO_UPDATE;
$configTaskModels[] = $configTaskItems[$configTask['code']];
} else {
$configTaskModels[] = new ConfigTask([
'scenario' => ConfigTask::SCENARIO_UPDATE,
]);
}
}
// 批量填充模型属性
if (!empty($configTaskModels) && !Model::loadMultiple($configTaskModels, $configTasks, '')) {
return ['code' => 226010, 'message' => Yii::t('error', '226010')];
}
// 批量验证模型
if (!empty($configTaskModels) && !Model::validateMultiple($configTaskModels)) {
$configTaskModelsResult = self::handleValidateMultipleError($configTaskModels);
if ($configTaskModelsResult['status'] === false) {
return ['code' => $configTaskModelsResult['code'], 'message' => $configTaskModelsResult['message']];
}
}
/* 任务步骤配置 */
// 使用键名比较计算数组的差集,如果不为空,则删除 (软删除) 出现在 任务步骤配置模型(MySQL) 中但是未出现在 任务步骤配置 中的记录
$configTaskStepDiffItems = array_diff_key($configTaskStepIsDeletedNoItems, $configTaskSteps);
$configTaskSteps = array_values($configTaskSteps);
$configTaskStepModels = [];
foreach ($configTaskSteps as $configTaskStep) {
$index = $configTaskStep['config_task_code'] . ':' . $configTaskStep['step_code'];
if (isset($configTaskStepItems[$index])) {
$configTaskStepItems[$index]->scenario = ConfigTask::SCENARIO_UPDATE;
$configTaskStepModels[] = $configTaskStepItems[$index];
} else {
$configTaskStepModels[] = new $modelClass([
'scenario' => $modelClass::SCENARIO_UPDATE,
]);
}
}
// 批量填充模型属性
if (!empty($configTaskStepModels) && !Model::loadMultiple($configTaskStepModels, $configTaskSteps, '')) {
return ['code' => 226010, 'message' => Yii::t('error', '226010')];
}
// 批量验证模型
if (!empty($configTaskStepModels) && !Model::validateMultiple($configTaskStepModels)) {
$configTaskStepModelsResult = self::handleValidateMultipleError($configTaskStepModels);
if ($configTaskStepModelsResult['status'] === false) {
return ['code' => $configTaskStepModelsResult['code'], 'message' => $configTaskStepModelsResult['message']];
}
}
/* 操作数据(事务) */
$configTaskStepService = new ConfigTaskStepService();
$result = $configTaskStepService->update($configTaskDiffItems, $configTaskModels, $configTaskStepDiffItems, $configTaskStepModels, $identity);
if ($result['status'] === false) {
throw new ServerErrorHttpException($result['message'], $result['code']);
}
return ['code' => 10000, 'message' => Yii::t('success', '126039')];
}
}

17、新建更新任务类型与步骤设置的服务文件(/api/services/ConfigTaskService.php)

<?php
/**
* Created by PhpStorm.
* User: Qiang Wang
* Date: 2019/11/01
* Time: 10:17
*/
namespace api/services;
use Yii;
use api/models/ConfigTask;
use yii/web/ServerErrorHttpException;
class ConfigTaskService extends /common/services/ConfigTaskService
{
/**
* 创建任务配置
* @param object $model 对象
*
* @param bool $runValidation 保存记录之前是否执行验证 (调用 [[validate()]]),默认为 true
*
* @return array
* 格式如下:
*
* [
*     'status' => true, // 成功
*     'data' => [ // object
*     ]
* ]
*
* [
*     'status' => false, // 失败
*     'code' => 226081, // 返回码
*     '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' => 226081, 'message' => Yii::t('error', Yii::t('error', Yii::t('error', '226081'), ['first_error' => $firstError]))];
} elseif (!$model->hasErrors()) {
throw new ServerErrorHttpException('Failed to create the object (task configuration) for unknown reason.');
}
}
/**
* 更新任务配置
* @param object $model 对象
*
* @param bool $runValidation 保存记录之前是否执行验证 (调用 [[validate()]]),默认为 true
*
* @return array
* 格式如下:
*
* [
*     'status' => true, // 成功
*     'data' => [ // object
*     ]
* ]
*
* [
*     'status' => false, // 失败
*     'code' => 226082, // 返回码
*     'message' => '', // 说明
* ]
*
* @throws ServerErrorHttpException
*/
public function update($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' => 226082, 'message' => Yii::t('error', Yii::t('error', Yii::t('error', '226082'), ['first_error' => $firstError]))];
} elseif (!$model->hasErrors()) {
throw new ServerErrorHttpException('Failed to update the object (task configuration) for unknown reason.');
}
}
/**
* 批量删除(删除任务配置)
*
* @param $models
*
* @return bool the saved model or false if saving fails
* @throws /Throwable
*/
public function deleteMultiple($models)
{
$transaction = Yii::$app->db->beginTransaction();
try {
foreach ($models as $model) {
/* @var $model ConfigTask */
if($model->softDelete() === false) {
throw new ServerErrorHttpException(Yii::t('error', '226083'), 226083);
}
}
$transaction->commit();
return true;
} catch (/Throwable $e) {
$transaction->rollBack();
throw $e;
}
}
}

18、新建更新任务类型与步骤设置的服务文件(/api/services/ConfigTaskStepService.php)

<?php
/**
* Created by PhpStorm.
* User: terryhong
* Date: 2018/12/20
* Time: 3:09 PM
*/
namespace api/services;
use Yii;
use api/models/ConfigTask;
use api/models/ConfigStep;
use api/models/ConfigTaskStep;
use yii/web/ServerErrorHttpException;
class ConfigTaskStepService extends /common/services/ConfigTaskStepService
{
/**
* 创建任务步骤配置
* @param object $model 对象
*
* @param bool $runValidation 保存记录之前是否执行验证 (调用 [[validate()]]),默认为 true
*
* @return array
* 格式如下:
*
* [
*     'status' => true, // 成功
*     'data' => [ // object
*     ]
* ]
*
* [
*     'status' => false, // 失败
*     'code' => 226084, // 返回码
*     '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' => 226084, 'message' => Yii::t('error', Yii::t('error', Yii::t('error', '226084'), ['first_error' => $firstError]))];
} elseif (!$model->hasErrors()) {
throw new ServerErrorHttpException('Failed to create the object (task step configuration) for unknown reason.');
}
}
/**
* 添加|还原|更新|删除任务配置、添加|还原|更新|删除任务步骤配置
*
* @param array $deletedConfigTasks 需要删除的任务配置
* 格式如下:
* [
*     [ // object
*         'id' => 32, // ID
*         'group_id' => '015ce30b116ce86058fa6ab4fea4ac63', // 租户ID
*         'code' => 'other', // 代码
*         'name' => '手动任务', // 名称
*         'sort_order' => 1, // 顺序
*         'category' => 2, // 任务类型,1:系统内置;2:用户定义
*         'parameter' => 's:0:"";', // 自定义参数,序列化存储
*         'status' => 1, // 状态,0:禁用;1:启用
*         'is_deleted' => 0, // 是否被删除,0:否;1:是
*         'created_at' => 1573632826, // 创建时间
*         'updated_at' => 1573632826, // 更新时间
*         'deleted_at' => 0, // 删除时间
*     ],
*     ...
* ]
*
* @param array $configTasks 任务配置
* 格式如下:
* [
*     [ // object
*         'group_id' => '015ce30b116ce86058fa6ab4fea4ac63', // 租户ID
*         'code' => 'tv_broadcast', // 代码
*         'name' => '电视播出', // 名称
*         'sort_order' => 1, // 顺序
*     ],
*     [ // object
*         'id' => 29, // ID
*         'group_id' => '015ce30b116ce86058fa6ab4fea4ac63', // 租户ID
*         'code' => 'tv_broadcast', // 代码
*         'name' => '电视播出', // 名称
*         'sort_order' => 1, // 顺序
*         'category' => 2, // 任务类型,1:系统内置;2:用户定义
*         'parameter' => 's:0:"";', // 自定义参数,序列化存储
*         'status' => 1, // 状态,0:禁用;1:启用
*         'is_deleted' => 0, // 是否被删除,0:否;1:是
*         'created_at' => 1573632826, // 创建时间
*         'updated_at' => 1573632826, // 更新时间
*         'deleted_at' => 0, // 删除时间
*     ],
*     ...
* ]
*
* @param array $deletedConfigTaskSteps 需要删除的任务步骤配置
* 格式如下:
* [
*     [ // object
*         'id' => 145, // ID
*         'group_id' => '015ce30b116ce86058fa6ab4fea4ac63', // 租户ID
*         'config_task_id' => 32, // 任务配置ID
*         'config_task_code' => 'other', // 任务配置代码
*         'config_step_id' => 2, // 步骤配置ID
*         'step_code' => 'begin_shot', // 步骤配置代码
*         'step_name' => '上传素材', // 步骤配置名称
*         'sort_order' => 1, // 步骤顺序,顺序排列
*         'is_default' => 1, // 是否默认步骤,1:是;0:否
*         'up_status_type' => 2, // 状态更新方式,1:接口;2:人工
*         'status' => 1, // 状态,0:禁用;1:启用
*         'is_deleted' => 0, // 是否被删除,0:否;1:是
*         'created_at' => 1573632826, // 创建时间
*         'updated_at' => 1573632826, // 更新时间
*         'deleted_at' => 0, // 删除时间
*     ],
*     ...
* ]
*
*
* @param array $configTaskSteps 任务步骤配置
* 格式如下:
* [
*     [ // object
*         'group_id' => '015ce30b116ce86058fa6ab4fea4ac63', // 租户ID
*         'config_task_code' => 'tv_broadcast', // 任务配置代码
*         'step_code' => 'begin_shot', // 步骤代码
*         'sort_order' => 1, // 顺序
*         'is_default' => 1, // 是否默认步骤,1:是;0:否
*     ],
*     [ // object
*         'id' => 122, // ID
*         'group_id' => '015ce30b116ce86058fa6ab4fea4ac63', // 租户ID
*         'config_task_id' => 29, // 任务配置ID
*         'config_task_code' => 'tv_broadcast', // 任务配置代码
*         'config_step_id' => 2, // 步骤配置ID
*         'step_code' => 'begin_shot', // 步骤配置代码
*         'step_name' => '上传素材', // 步骤配置名称
*         'sort_order' => 1, // 步骤顺序,顺序排列
*         'is_default' => 1, // 是否默认步骤,1:是;0:否
*         'up_status_type' => 2, // 状态更新方式,1:接口;2:人工
*         'status' => 1, // 状态,0:禁用;1:启用
*         'is_deleted' => 0, // 是否被删除,0:否;1:是
*         'created_at' => 1573632826, // 创建时间
*         'updated_at' => 1573632826, // 更新时间
*         'deleted_at' => 0, // 删除时间
*     ],
*     ...
* ]
*
* @param object $identity 当前用户的身份实例
*
* @return array
* 格式如下:
*
* [
*     'status' => true, // 成功
* ]
*
* @throws /Throwable
*/
public function update($deletedConfigTasks, $configTasks, $deletedConfigTaskSteps, $configTaskSteps, $identity)
{
/* 操作数据(事务) */
$transaction = Yii::$app->db->beginTransaction();
try {
$configTaskService = new ConfigTaskService();
// 使用键名比较计算数组的差集,如果不为空,则删除 (软删除) 出现在 任务配置模型(MySQL) 中但是未出现在 任务配置列表 中的记录
$configTaskService->deleteMultiple($deletedConfigTasks);
// 遍历模型数组,判断在 任务配置 中是否存在,如果不存在则插入,如果存在则更新(已删除则先还原)
foreach ($configTasks as $configTask) {
$configTaskItem = ConfigTask::find()->where(['group_id' => $identity->group_id, 'code' => $configTask->code])->one();
/* @var $model ConfigTask */
if (!isset($configTaskItem)) {
$configTask->category = $configTask::CATEGORY_CUSTOMIZE;
$configTask->parameter = serialize('');
$configTask->status = $configTask::STATUS_ENABLED;
$configTask->is_deleted = $configTask::IS_DELETED_NO;
$configTask->deleted_at = $configTask::DELETED_AT_DEFAULT;
$configTaskServiceCreateResult = $configTaskService->create($configTask, false);
if ($configTaskServiceCreateResult['status'] === false) {
throw new ServerErrorHttpException($configTaskServiceCreateResult['message'], $configTaskServiceCreateResult['code']);
}
} else if (isset($configTaskItem)) {
if ($configTaskItem->is_deleted == ConfigTask::IS_DELETED_YES && $configTaskItem->restore() === false) {
throw new ServerErrorHttpException('Failed to restore the object (task configuration) for unknown reason.');
}
$configTaskItem->name = $configTask->name;
$configTaskItem->sort_order = $configTask->sort_order;
$configTaskItemServiceUpdateResult = $configTaskService->update($configTaskItem, false);
if ($configTaskItemServiceUpdateResult['status'] === false) {
throw new ServerErrorHttpException($configTaskItemServiceUpdateResult['message'], $configTaskItemServiceUpdateResult['code']);
}
}
}
// 使用键名比较计算数组的差集,如果不为空,则删除 (软删除) 出现在 任务步骤配置模型(MySQL) 中但是未出现在 任务步骤配置列表 中的记录
$this->deleteMultiple($deletedConfigTaskSteps);
$configTaskItems = ConfigTask::find()->where(['group_id' => $identity->group_id])->indexBy(['code'])->all();
$configStepItems = ConfigStep::find()->isDeletedNo()->indexBy(['code'])->all();
// 遍历模型数组,判断在 任务步骤配置 中是否存在,如果不存在则插入,如果存在则更新(已删除则先还原)
foreach ($configTaskSteps as $configTaskStep) {
$configTaskStepItem = ConfigTaskStep::find()->where(['group_id' => $identity->group_id, 'config_task_code' => $configTaskStep->config_task_code, 'step_code' => $configTaskStep->step_code])->one();
/* @var $model ConfigTaskStep */
if (!isset($configTaskStepItem)) {
$configTaskStep->config_task_id = $configTaskItems[$configTaskStep->config_task_code]->id;
$configTaskStep->config_step_id = $configStepItems[$configTaskStep->step_code]->id;
$configTaskStep->step_name = $configStepItems[$configTaskStep->step_code]->name;
$configTaskStep->status = $configTaskStep::STATUS_ENABLED;
$configTaskStep->is_deleted = $configTaskStep::IS_DELETED_NO;
$configTaskStep->deleted_at = $configTaskStep::DELETED_AT_DEFAULT;
$thisCreateResult = $this->create($configTaskStep, false);
if ($thisCreateResult['status'] === false) {
throw new ServerErrorHttpException($thisCreateResult['message'], $thisCreateResult['code']);
}
} else if (isset($configTaskStepItem)) {
if ($configTaskStepItem->is_deleted == ConfigTaskStep::IS_DELETED_YES && $configTaskStepItem->restore() === false) {
throw new ServerErrorHttpException('Failed to restore the object (task step configuration) for unknown reason.');
}
$configTaskStepItem->sort_order = $configTaskStep->sort_order;
$configTaskStepItem->is_default = $configTaskStep->is_default;
if ($configTaskStepItem->save(false)) {
// return ['status' => true, 'data' => $configTaskStepItem];
} elseif ($configTaskStepItem->hasErrors()) {
$firstError = '';
foreach ($configTaskStepItem->getFirstErrors() as $message) {
$firstError = $message;
break;
}
throw new ServerErrorHttpException(Yii::t('error', Yii::t('error', Yii::t('error', '226085'), ['first_error' => $firstError])), 226085);
} elseif (!$configTaskStepItem->hasErrors()) {
throw new ServerErrorHttpException('Failed to update the object (task step configuration) for unknown reason.');
}
}
}
$transaction->commit();
} catch(/Throwable $e) {
$transaction->rollBack();
throw $e;
}
return ['status' => true];
}
/**
* 批量删除(删除任务步骤配置)
*
* @param $models
*
* @return bool the saved model or false if saving fails
* @throws /Throwable
*/
public function deleteMultiple($models)
{
$transaction = Yii::$app->db->beginTransaction();
try {
foreach ($models as $model) {
if($model->softDelete() === false) {
throw new ServerErrorHttpException(Yii::t('error', '226086'), 226086);
}
}
$transaction->commit();
return true;
} catch (/Throwable $e) {
$transaction->rollBack();
throw $e;
}
}
}

19、编辑更新任务设置的模型文件(/api/models/ConfigTask.php)

<?php
namespace api/models;
use yii/helpers/ArrayHelper;
class ConfigTask extends /common/logics/ConfigTask
{
const SCENARIO_UPDATE = 'update';
public function scenarios()
{
$scenarios = parent::scenarios();
$scenarios[self::SCENARIO_UPDATE] = ['group_id', 'code', 'name', 'sort_order', 'is_deleted', 'deleted_at'];
return $scenarios;
}
/**
* @inheritdoc
*/
public function rules()
{
$rules = [
];
$parentRules = parent::rules();
return ArrayHelper::merge($rules, $parentRules);
}
/**
* {@inheritdoc}
* @return ConfigTaskQuery the active query used by this AR class.
*/
public static function find()
{
return new ConfigTaskQuery(get_called_class());
}
}

20、编辑更新任务步骤设置的模型文件(/api/models/ConfigTaskStep.php)

<?php
namespace api/models;
use yii/helpers/ArrayHelper;
class ConfigTaskStep extends /common/logics/ConfigTaskStep
{
const SCENARIO_UPDATE = 'update';
public function scenarios()
{
$scenarios = parent::scenarios();
$scenarios[self::SCENARIO_UPDATE] = ['group_id', 'config_task_code', 'step_code', 'sort_order', 'is_default', 'is_deleted', 'deleted_at'];
return $scenarios;
}
/**
* @inheritdoc
*/
public function rules()
{
$rules = [
/* 添加、更新任务步骤配置 */
['step_code', 'exist', 'targetClass' => '/api/models/ConfigStep', 'targetAttribute' => 'code', 'filter' => ['is_deleted' => ConfigStep::IS_DELETED_NO], 'on' => self::SCENARIO_UPDATE],
[['is_default'], 'in', 'range' => [static::IS_DEFAULT_NO, static::IS_DEFAULT_YES], 'on' => self::SCENARIO_UPDATE],
];
$parentRules = parent::rules();
return ArrayHelper::merge($rules, $parentRules);
}
/**
* {@inheritdoc}
* @return ConfigTaskStepQuery the active query used by this AR class.
*/
public static function find()
{
return new ConfigTaskStepQuery(get_called_class());
}
}

21、在 Postman 中 PUT:http://api.pcs-api.localhost/v1/config-task-steps/my-group-id ,请求数据(null)与执行的 SQL (未插入数据) 如下,如图4

在 Postman 中 PUT:http://api.pcs-api.localhost/v1/config-task-steps/my-group-id ,请求数据(null)与执行的 SQL (未插入数据) 如下

图4

SELECT * FROM `pa_config_task` WHERE (`group_id`='015ce30b116ce86058fa6ab4fea4ac63') AND (`is_deleted`=0)
SELECT * FROM `pa_config_task` WHERE (`group_id`='015ce30b116ce86058fa6ab4fea4ac63')
SELECT * FROM `pa_config_task_step` WHERE (`group_id`='015ce30b116ce86058fa6ab4fea4ac63') AND (`is_deleted`=0)
SELECT * FROM `pa_config_task_step` WHERE (`group_id`='015ce30b116ce86058fa6ab4fea4ac63')
Begin transaction
SELECT * FROM `pa_config_task` WHERE `group_id`='015ce30b116ce86058fa6ab4fea4ac63'
Commit transaction

22、在 Postman 中 PUT:http://api.pcs-api.localhost/v1/config-task-steps/my-group-id ,请求数据([])与执行的 SQL (未插入数据) 如下,如图5

在 Postman 中 PUT:http://api.pcs-api.localhost/v1/config-task-steps/my-group-id ,请求数据([])与执行的 SQL (未插入数据) 如下

图5

SELECT * FROM `pa_config_task` WHERE (`group_id`='015ce30b116ce86058fa6ab4fea4ac63') AND (`is_deleted`=0)
SELECT * FROM `pa_config_task` WHERE (`group_id`='015ce30b116ce86058fa6ab4fea4ac63')
SELECT * FROM `pa_config_task_step` WHERE (`group_id`='015ce30b116ce86058fa6ab4fea4ac63') AND (`is_deleted`=0)
SELECT * FROM `pa_config_task_step` WHERE (`group_id`='015ce30b116ce86058fa6ab4fea4ac63')
Begin transaction
SELECT * FROM `pa_config_task` WHERE `group_id`='015ce30b116ce86058fa6ab4fea4ac63'
Commit transaction

23、在 Postman 中 PUT:http://api.pcs-api.localhost/v1/config-task-steps/my-group-id ,请求数据(未自定义时,编辑接口的响应数据)与执行的 SQL (分别插入 4 、24 条记录) 如下,打开表,如图6

在 Postman 中 PUT:http://api.pcs-api.localhost/v1/config-task-steps/my-group-id ,请求数据(未自定义时,编辑接口的响应数据)与执行的 SQL (分别插入 4 、24 条记录) 如下,打开表

图6

[
{
"code": "tv_broadcast",
"name": "电视播出",
"sort_order": 1,
"steps": [
{
"code": "begin_shot",
"sort_order": 1,
"is_default": 1
},
{
"code": "writing",
"sort_order": 2,
"is_default": 1
},
{
"code": "manuscript_review",
"sort_order": 4,
"is_default": 1
},
{
"code": "video_editing",
"sort_order": 5,
"is_default": 1
},
{
"code": "tandem_list",
"sort_order": 6,
"is_default": 1
},
{
"code": "program_render",
"sort_order": 7,
"is_default": 1
},
{
"code": "program_authen",
"sort_order": 8,
"is_default": 1
}
]
},
{
"code": "inter_view",
"name": "采访任务",
"sort_order": 2,
"steps": [
{
"code": "begin_shot",
"sort_order": 1,
"is_default": 1
},
{
"code": "material_review",
"sort_order": 2,
"is_default": 1
},
{
"code": "writing",
"sort_order": 4,
"is_default": 1
},
{
"code": "writing_draft",
"sort_order": 5,
"is_default": 1
},
{
"code": "manuscript_review",
"sort_order": 6,
"is_default": 1
},
{
"code": "finish_interview",
"sort_order": 7,
"is_default": 1
}
]
},
{
"code": "rich_doc",
"name": "互联网发布",
"sort_order": 3,
"steps": [
{
"code": "retrieval",
"sort_order": 1,
"is_default": 1
},
{
"code": "writing",
"sort_order": 2,
"is_default": 1
},
{
"code": "manuscript_review",
"sort_order": 4,
"is_default": 1
},
{
"code": "newspaper_issued",
"sort_order": 5,
"is_default": 1
},
{
"code": "wechat_group_reference",
"sort_order": 6,
"is_default": 1
},
{
"code": "website_release",
"sort_order": 7,
"is_default": 1
},
{
"code": "app_release",
"sort_order": 8,
"is_default": 1
},
{
"code": "wechat_release",
"sort_order": 9,
"is_default": 1
},
{
"code": "blog_release",
"sort_order": 10,
"is_default": 1
},
{
"code": "qq_release",
"sort_order": 11,
"is_default": 1
}
]
},
{
"code": "other",
"name": "手动任务",
"sort_order": 4,
"steps": [
{
"code": "begin_shot",
"sort_order": 1,
"is_default": 1
}
]
}
]
SELECT * FROM `pa_config_task` WHERE (`group_id`='015ce30b116ce86058fa6ab4fea4ac63') AND (`is_deleted`=0)
SELECT * FROM `pa_config_task` WHERE `group_id`='015ce30b116ce86058fa6ab4fea4ac63'
SELECT * FROM `pa_config_task_step` WHERE (`group_id`='015ce30b116ce86058fa6ab4fea4ac63') AND (`is_deleted`=0)
SELECT * FROM `pa_config_task_step` WHERE `group_id`='015ce30b116ce86058fa6ab4fea4ac63'
SELECT EXISTS(SELECT * FROM `pa_config_task` WHERE (`pa_config_task`.`group_id`='015ce30b116ce86058fa6ab4fea4ac63') AND (`pa_config_task`.`code`='tv_broadcast') AND (`pa_config_task`.`is_deleted`=0) AND (`pa_config_task`.`deleted_at`=0)) // 总计 4 次执行
SELECT EXISTS(SELECT * FROM `pa_config_task` WHERE (`pa_config_task`.`group_id`='015ce30b116ce86058fa6ab4fea4ac63') AND (`pa_config_task`.`code`='inter_view') AND (`pa_config_task`.`is_deleted`=0) AND (`pa_config_task`.`deleted_at`=0)) // 总计 4 次执行
SELECT EXISTS(SELECT * FROM `pa_config_task` WHERE (`pa_config_task`.`group_id`='015ce30b116ce86058fa6ab4fea4ac63') AND (`pa_config_task`.`code`='rich_doc') AND (`pa_config_task`.`is_deleted`=0) AND (`pa_config_task`.`deleted_at`=0)) // 总计 4 次执行
SELECT EXISTS(SELECT * FROM `pa_config_task` WHERE (`pa_config_task`.`group_id`='015ce30b116ce86058fa6ab4fea4ac63') AND (`pa_config_task`.`code`='other') AND (`pa_config_task`.`is_deleted`=0) AND (`pa_config_task`.`deleted_at`=0)) // 总计 4 次执行
SELECT EXISTS(SELECT * FROM `pa_config_step` WHERE (`pa_config_step`.`code`='begin_shot') AND (`is_deleted`=0)) // 总计 24 次执行【SELECT EXISTS(SELECT * FROM `pa_config_step` WHERE (`pa_config_step`.`code`=】
SELECT EXISTS(SELECT * FROM `pa_config_task_step` WHERE (`pa_config_task_step`.`group_id`='015ce30b116ce86058fa6ab4fea4ac63') AND (`pa_config_task_step`.`config_task_code`='tv_broadcast') AND (`pa_config_task_step`.`step_code`='begin_shot') AND (`pa_config_task_step`.`is_deleted`=0) AND (`pa_config_task_step`.`deleted_at`=0)) // 总计 120 次执行【SELECT EXISTS(SELECT * FROM `pa_config_task_step` WHERE (`pa_config_task_step`.`group_id`】
Begin transaction
SELECT * FROM `pa_config_task` WHERE (`group_id`='015ce30b116ce86058fa6ab4fea4ac63') AND (`code`='tv_broadcast') // 总计 4 次执行【SELECT * FROM `pa_config_task` WHERE (`group_id`='015ce30b116ce86058fa6ab4fea4ac63') AND (`code`=】
INSERT INTO `pa_config_task` (`group_id`, `code`, `name`, `sort_order`, `is_deleted`, `deleted_at`, `category`, `parameter`, `status`, `created_at`, `updated_at`) VALUES ('015ce30b116ce86058fa6ab4fea4ac63', 'tv_broadcast', '电视播出', 1, 0, 0, 2, 's:0:/"/";', 1, 1573626589, 1573626589) // 总计 4 次执行【SELECT * FROM `pa_config_task` WHERE (INSERT INTO `pa_config_task` (`group_id`, `code`, `name`, `sort_order`, `is_deleted`, `deleted_at`, `category`, `parameter`, `status`, `created_at`, `updated_at`) VALUES ('015ce30b116ce86058fa6ab4fea4ac63'】
SELECT * FROM `pa_config_task` WHERE `group_id`='015ce30b116ce86058fa6ab4fea4ac63'
SELECT * FROM `pa_config_step` WHERE `is_deleted`=0
SELECT * FROM `pa_config_task_step` WHERE (`group_id`='015ce30b116ce86058fa6ab4fea4ac63') AND (`config_task_code`='tv_broadcast') AND (`step_code`='begin_shot') // 总计 24 次执行【SELECT * FROM `pa_config_task_step` WHERE (`group_id`='015ce30b116ce86058fa6ab4fea4ac63') AND (`config_task_code`=】
INSERT INTO `pa_config_task_step` (`group_id`, `config_task_code`, `step_code`, `sort_order`, `is_default`, `is_deleted`, `deleted_at`, `config_task_id`, `config_step_id`, `step_name`, `status`, `created_at`, `updated_at`) VALUES ('015ce30b116ce86058fa6ab4fea4ac63', 'tv_broadcast', 'begin_shot', 1, 1, 0, 0, 17, 2, '上传素材', 1, 1573626589, 1573626589) // 总计 24 次执行【INSERT INTO `pa_config_task_step` (`group_id`, `config_task_code`, `step_code`, `sort_order`, `is_default`, `is_deleted`, `deleted_at`, `config_task_id`, `config_step_id`, `step_name`, `status`, `created_at`, `updated_at`) VALUES ('015ce30b116ce86058fa6ab4fea4ac63'】
Commit transaction

24、在 Postman 中 GET:http://api.pcs-api.localhost/v1/config-task-steps/edit/my-group-id ,当租户已自定义时,即 任务配置表、任务步骤配置表 中有当前租户的记录时,默认获取 任务配置表、任务步骤配置表 中的当前租户ID的记录,响应数据与执行的 SQL 如下

{
"code": 10000,
"message": "编辑租户的任务类型与步骤设置成功",
"data": {
"items": [
{
"id": 17,
"code": "tv_broadcast",
"name": "电视播出",
"sort_order": 1,
"steps": [
{
"id": 2,
"code": "begin_shot",
"name": "上传素材",
"sort_order": 1,
"is_default": 1
},
{
"id": 4,
"code": "writing",
"name": "撰写稿件",
"sort_order": 2,
"is_default": 1
},
{
"id": 7,
"code": "manuscript_review",
"name": "稿件审核",
"sort_order": 4,
"is_default": 1
},
{
"id": 10,
"code": "video_editing",
"name": "视频编辑",
"sort_order": 5,
"is_default": 1
},
{
"id": 11,
"code": "tandem_list",
"name": "串联单",
"sort_order": 6,
"is_default": 1
},
{
"id": 14,
"code": "program_render",
"name": "节目上传",
"sort_order": 7,
"is_default": 1
},
{
"id": 16,
"code": "program_authen",
"name": "节目审查",
"sort_order": 8,
"is_default": 1
}
]
},
{
"id": 18,
"code": "inter_view",
"name": "采访任务",
"sort_order": 2,
"steps": [
{
"id": 2,
"code": "begin_shot",
"name": "上传素材",
"sort_order": 1,
"is_default": 1
},
{
"id": 3,
"code": "material_review",
"name": "素材审核",
"sort_order": 2,
"is_default": 1
},
{
"id": 4,
"code": "writing",
"name": "撰写稿件",
"sort_order": 4,
"is_default": 1
},
{
"id": 8,
"code": "writing_draft",
"name": "撰写通稿",
"sort_order": 5,
"is_default": 1
},
{
"id": 7,
"code": "manuscript_review",
"name": "稿件审核",
"sort_order": 6,
"is_default": 1
},
{
"id": 13,
"code": "finish_interview",
"name": "结束采访",
"sort_order": 7,
"is_default": 1
}
]
},
{
"id": 19,
"code": "rich_doc",
"name": "互联网发布",
"sort_order": 3,
"steps": [
{
"id": 1,
"code": "retrieval",
"name": "取稿",
"sort_order": 1,
"is_default": 1
},
{
"id": 4,
"code": "writing",
"name": "撰写稿件",
"sort_order": 2,
"is_default": 1
},
{
"id": 7,
"code": "manuscript_review",
"name": "稿件审核",
"sort_order": 4,
"is_default": 1
},
{
"id": 9,
"code": "newspaper_issued",
"name": "报纸签发",
"sort_order": 5,
"is_default": 1
},
{
"id": 12,
"code": "wechat_group_reference",
"name": "微信组稿引用",
"sort_order": 6,
"is_default": 1
},
{
"id": 15,
"code": "website_release",
"name": "网站发布",
"sort_order": 7,
"is_default": 1
},
{
"id": 17,
"code": "app_release",
"name": "APP发布",
"sort_order": 8,
"is_default": 1
},
{
"id": 18,
"code": "wechat_release",
"name": "微信发布",
"sort_order": 9,
"is_default": 1
},
{
"id": 19,
"code": "blog_release",
"name": "微博发布",
"sort_order": 10,
"is_default": 1
},
{
"id": 20,
"code": "qq_release",
"name": "企鹅号发布",
"sort_order": 11,
"is_default": 1
}
]
},
{
"id": 20,
"code": "other",
"name": "手动任务",
"sort_order": 4,
"steps": [
{
"id": 2,
"code": "begin_shot",
"name": "上传素材",
"sort_order": 1,
"is_default": 1
}
]
}
]
}
}
SELECT * FROM `pa_config_task_step` WHERE `group_id`='015ce30b116ce86058fa6ab4fea4ac63'
SELECT * FROM `pa_config_task_step` WHERE (`group_id`='015ce30b116ce86058fa6ab4fea4ac63') AND (`is_deleted`=0) ORDER BY `sort_order`, `id` DESC
SELECT * FROM `pa_config_task` WHERE `group_id`='015ce30b116ce86058fa6ab4fea4ac63'
SELECT * FROM `pa_config_task` WHERE (`group_id`='015ce30b116ce86058fa6ab4fea4ac63') AND (`is_deleted`=0) ORDER BY `sort_order`, `id` DESC

25、在 Postman 中 PUT:http://api.pcs-api.localhost/v1/config-task-steps/my-group-id ,请求数据(删除任务配置(采访任务)中的 6 项步骤配置;删除 1 项任务配置(手动任务)及其中的 1 项步骤配置;在 互联网发布 后添加 1 项任务配置(自定义一)及在其中添加 0 项步骤配置;在 自定义一 后添加 1 项任务配置(自定义二)及在其中添加 2 项步骤配置;更新 1 项任务配置(互联网发布)及其中的 1 项步骤配置)与执行的 SQL ( 任务配置表:2 条插入语句、2 条更新语句;任务步骤配置表:2 条插入语句、8 条更新语句) 如下,打开表,如图7

在 Postman 中 PUT:http://api.pcs-api.localhost/v1/config-task-steps/my-group-id ,请求数据(删除任务配置(采访任务)中的 6 项步骤配置;删除 1 项任务配置(手动任务)及其中的 1 项步骤配置;在 互联网发布 后添加 1 项任务配置(自定义一)及在其中添加 0 项步骤配置;在 自定义一 后添加 1 项任务配置(自定义二)及在其中添加 2 项步骤配置;更新 1 项任务配置(互联网发布)及其中的 1 项步骤配置)与执行的 SQL ( 任务配置表:2 条插入语句、2 条更新语句;任务步骤配置表:2 条插入语句、8 条更新语句) 如下,打开表

图7

[
{
"code": "tv_broadcast",
"name": "电视播出",
"sort_order": 1,
"steps": [
{
"code": "begin_shot",
"sort_order": 1,
"is_default": 1
},
{
"code": "writing",
"sort_order": 2,
"is_default": 1
},
{
"code": "manuscript_review",
"sort_order": 4,
"is_default": 1
},
{
"code": "video_editing",
"sort_order": 5,
"is_default": 1
},
{
"code": "tandem_list",
"sort_order": 6,
"is_default": 1
},
{
"code": "program_render",
"sort_order": 7,
"is_default": 1
},
{
"code": "program_authen",
"sort_order": 8,
"is_default": 1
}
]
},
{
"code": "inter_view",
"name": "采访任务",
"sort_order": 2,
"steps": []
},
{
"code": "rich_doc",
"name": "互联网发布更新",
"sort_order": 4,
"steps": [
{
"code": "retrieval",
"sort_order": 1,
"is_default": 1
},
{
"code": "writing",
"sort_order": 2,
"is_default": 1
},
{
"code": "manuscript_review",
"sort_order": 3,
"is_default": 1
},
{
"code": "newspaper_issued",
"sort_order": 5,
"is_default": 1
},
{
"code": "wechat_group_reference",
"sort_order": 6,
"is_default": 1
},
{
"code": "website_release",
"sort_order": 7,
"is_default": 1
},
{
"code": "app_release",
"sort_order": 8,
"is_default": 1
},
{
"code": "wechat_release",
"sort_order": 9,
"is_default": 1
},
{
"code": "blog_release",
"sort_order": 10,
"is_default": 1
},
{
"code": "qq_release",
"sort_order": 11,
"is_default": 1
}
]
},
{
"code": "zdy1",
"name": "自定义一",
"sort_order": 5,
"steps": []
},
{
"code": "zdy2",
"name": "自定义二",
"sort_order": 6,
"steps": [
{
"code": "begin_shot",
"sort_order": 1,
"is_default": 1
},
{
"code": "program_render",
"sort_order": 7,
"is_default": 1
}
]
}
]
SELECT * FROM `pa_config_task` WHERE (`group_id`='015ce30b116ce86058fa6ab4fea4ac63') AND (`is_deleted`=0)
SELECT * FROM `pa_config_task` WHERE `group_id`='015ce30b116ce86058fa6ab4fea4ac63'
SELECT * FROM `pa_config_task_step` WHERE (`group_id`='015ce30b116ce86058fa6ab4fea4ac63') AND (`is_deleted`=0)
SELECT * FROM `pa_config_task_step` WHERE `group_id`='015ce30b116ce86058fa6ab4fea4ac63'
SELECT `pa_config_task`.`id` FROM `pa_config_task` WHERE (`pa_config_task`.`group_id`='015ce30b116ce86058fa6ab4fea4ac63') AND (`pa_config_task`.`code`='tv_broadcast') AND (`pa_config_task`.`is_deleted`=0) AND (`pa_config_task`.`deleted_at`=0) LIMIT 2 // 总计 12 次执行【SELECT `pa_config_task`.`id` FROM `pa_config_task` WHERE (`pa_config_task`.`group_id`=】
SELECT EXISTS(SELECT * FROM `pa_config_task` WHERE (`pa_config_task`.`group_id`='015ce30b116ce86058fa6ab4fea4ac63') AND (`pa_config_task`.`code`='zdy1') AND (`pa_config_task`.`is_deleted`=0) AND (`pa_config_task`.`deleted_at`=0)) // 总计 8 次执行【SELECT EXISTS(SELECT * FROM `pa_config_task` WHERE (`pa_config_task`.`group_id`=】
SELECT EXISTS(SELECT * FROM `pa_config_step` WHERE (`pa_config_step`.`code`='begin_shot') AND (`is_deleted`=0)) // 总计 19 次执行【SELECT EXISTS(SELECT * FROM `pa_config_step` WHERE (`pa_config_step`.`code`=】
SELECT `pa_config_task_step`.`id` FROM `pa_config_task_step` WHERE (`pa_config_task_step`.`group_id`='015ce30b116ce86058fa6ab4fea4ac63') AND (`pa_config_task_step`.`config_task_code`='tv_broadcast') AND (`pa_config_task_step`.`step_code`='begin_shot') AND (`pa_config_task_step`.`is_deleted`=0) AND (`pa_config_task_step`.`deleted_at`=0) LIMIT 2 // 总计 85 次执行【SELECT `pa_config_task_step`.`id` FROM `pa_config_task_step` WHERE (`pa_config_task_step`.`group_id`=】
SELECT EXISTS(SELECT * FROM `pa_config_step` WHERE (`pa_config_step`.`code`='writing') AND (`is_deleted`=0)) // 总计 19 次执行【SELECT EXISTS(SELECT * FROM `pa_config_step` WHERE (`pa_config_step`.`code`=】
Begin transaction
UPDATE `pa_config_task` SET `is_deleted`=1, `deleted_at`=1573630064 WHERE `id`=20
SELECT * FROM `pa_config_task` WHERE (`group_id`='015ce30b116ce86058fa6ab4fea4ac63') AND (`code`='tv_broadcast') // 总计 5 次执行【SELECT * FROM `pa_config_task` WHERE (`group_id`='015ce30b116ce86058fa6ab4fea4ac63') AND (`code`=】
UPDATE `pa_config_task` SET `name`='互联网发布更新', `sort_order`=4, `updated_at`=1573630064 WHERE `id`=19
INSERT INTO `pa_config_task` (`group_id`, `code`, `name`, `sort_order`, `is_deleted`, `deleted_at`, `category`, `parameter`, `status`, `created_at`, `updated_at`) VALUES ('015ce30b116ce86058fa6ab4fea4ac63', 'zdy1', '自定义一', 5, 0, 0, 2, 's:0:/"/";', 1, 1573630064, 1573630064)
INSERT INTO `pa_config_task` (`group_id`, `code`, `name`, `sort_order`, `is_deleted`, `deleted_at`, `category`, `parameter`, `status`, `created_at`, `updated_at`) VALUES ('015ce30b116ce86058fa6ab4fea4ac63', 'zdy2', '自定义二', 6, 0, 0, 2, 's:0:/"/";', 1, 1573630064, 1573630064)
UPDATE `pa_config_task_step` SET `is_deleted`=1, `deleted_at`=1573630064 WHERE `id`=83 // 总计 7 次执行【UPDATE `pa_config_task_step` SET `is_deleted`=1】
SELECT * FROM `pa_config_task` WHERE `group_id`='015ce30b116ce86058fa6ab4fea4ac63'
SELECT * FROM `pa_config_step` WHERE `is_deleted`=0
SELECT * FROM `pa_config_task_step` WHERE (`group_id`='015ce30b116ce86058fa6ab4fea4ac63') AND (`config_task_code`='tv_broadcast') AND (`step_code`='begin_shot') // 总计 19 次执行【SELECT * FROM `pa_config_task_step` WHERE (`group_id`='015ce30b116ce86058fa6ab4fea4ac63') AND (`config_task_code`=】
UPDATE `pa_config_task_step` SET `sort_order`=3, `updated_at`=1573630064 WHERE `id`=91
INSERT INTO `pa_config_task_step` (`group_id`, `config_task_code`, `step_code`, `sort_order`, `is_default`, `is_deleted`, `deleted_at`, `config_task_id`, `config_step_id`, `step_name`, `status`, `created_at`, `updated_at`) VALUES ('015ce30b116ce86058fa6ab4fea4ac63', 'zdy2', 'begin_shot', 1, 1, 0, 0, 22, 2, '上传素材', 1, 1573630064, 1573630064)
INSERT INTO `pa_config_task_step` (`group_id`, `config_task_code`, `step_code`, `sort_order`, `is_default`, `is_deleted`, `deleted_at`, `config_task_id`, `config_step_id`, `step_name`, `status`, `created_at`, `updated_at`) VALUES ('015ce30b116ce86058fa6ab4fea4ac63', 'zdy2', 'program_render', 7, 1, 0, 0, 22, 14, '节目上传', 1, 1573630064, 1573630064)
Commit transaction

26、在 Postman 中 GET:http://api.pcs-api.localhost/v1/config-task-steps/edit/my-group-id ,当租户已自定义时,即 任务配置表、任务步骤配置表 中有当前租户的记录时,默认获取 任务配置表、任务步骤配置表 中的当前租户ID的记录,响应数据与执行的 SQL 如下

{
"code": 10000,
"message": "编辑租户的任务类型与步骤设置成功",
"data": {
"items": [
{
"id": 17,
"code": "tv_broadcast",
"name": "电视播出",
"sort_order": 1,
"steps": [
{
"id": 2,
"code": "begin_shot",
"name": "上传素材",
"sort_order": 1,
"is_default": 1
},
{
"id": 4,
"code": "writing",
"name": "撰写稿件",
"sort_order": 2,
"is_default": 1
},
{
"id": 7,
"code": "manuscript_review",
"name": "稿件审核",
"sort_order": 4,
"is_default": 1
},
{
"id": 10,
"code": "video_editing",
"name": "视频编辑",
"sort_order": 5,
"is_default": 1
},
{
"id": 11,
"code": "tandem_list",
"name": "串联单",
"sort_order": 6,
"is_default": 1
},
{
"id": 14,
"code": "program_render",
"name": "节目上传",
"sort_order": 7,
"is_default": 1
},
{
"id": 16,
"code": "program_authen",
"name": "节目审查",
"sort_order": 8,
"is_default": 1
}
]
},
{
"id": 18,
"code": "inter_view",
"name": "采访任务",
"sort_order": 2,
"steps": []
},
{
"id": 19,
"code": "rich_doc",
"name": "互联网发布更新",
"sort_order": 4,
"steps": [
{
"id": 1,
"code": "retrieval",
"name": "取稿",
"sort_order": 1,
"is_default": 1
},
{
"id": 4,
"code": "writing",
"name": "撰写稿件",
"sort_order": 2,
"is_default": 1
},
{
"id": 7,
"code": "manuscript_review",
"name": "稿件审核",
"sort_order": 3,
"is_default": 1
},
{
"id": 9,
"code": "newspaper_issued",
"name": "报纸签发",
"sort_order": 5,
"is_default": 1
},
{
"id": 12,
"code": "wechat_group_reference",
"name": "微信组稿引用",
"sort_order": 6,
"is_default": 1
},
{
"id": 15,
"code": "website_release",
"name": "网站发布",
"sort_order": 7,
"is_default": 1
},
{
"id": 17,
"code": "app_release",
"name": "APP发布",
"sort_order": 8,
"is_default": 1
},
{
"id": 18,
"code": "wechat_release",
"name": "微信发布",
"sort_order": 9,
"is_default": 1
},
{
"id": 19,
"code": "blog_release",
"name": "微博发布",
"sort_order": 10,
"is_default": 1
},
{
"id": 20,
"code": "qq_release",
"name": "企鹅号发布",
"sort_order": 11,
"is_default": 1
}
]
},
{
"id": 21,
"code": "zdy1",
"name": "自定义一",
"sort_order": 5,
"steps": []
},
{
"id": 22,
"code": "zdy2",
"name": "自定义二",
"sort_order": 6,
"steps": [
{
"id": 2,
"code": "begin_shot",
"name": "上传素材",
"sort_order": 1,
"is_default": 1
},
{
"id": 14,
"code": "program_render",
"name": "节目上传",
"sort_order": 7,
"is_default": 1
}
]
}
]
}
}
SELECT * FROM `pa_config_task_step` WHERE `group_id`='015ce30b116ce86058fa6ab4fea4ac63'
SELECT * FROM `pa_config_task_step` WHERE (`group_id`='015ce30b116ce86058fa6ab4fea4ac63') AND (`is_deleted`=0) ORDER BY `sort_order`, `id` DESC
SELECT * FROM `pa_config_task` WHERE `group_id`='015ce30b116ce86058fa6ab4fea4ac63'
SELECT * FROM `pa_config_task` WHERE (`group_id`='015ce30b116ce86058fa6ab4fea4ac63') AND (`is_deleted`=0) ORDER BY `sort_order`, `id` DESC

27、在 Postman 中 PUT:http://api.pcs-api.localhost/v1/config-task-steps/my-group-id ,请求数据(在 采访任务 后添加之前删除的 1 项任务配置(手动任务,调整其顺序值为 3)及其中的 1 项步骤配置)与执行的更新 SQL ( 任务配置表:2 条更新语句(先还原再更新);任务步骤配置表:1 条更新语句) 如下,打开表,如图8

在 Postman 中 PUT:http://api.pcs-api.localhost/v1/config-task-steps/my-group-id ,请求数据(在 采访任务 后添加之前删除的 1 项任务配置(手动任务,调整其顺序值为 3)及其中的 1 项步骤配置)与执行的更新 SQL ( 任务配置表:2 条更新语句(先还原再更新);任务步骤配置表:1 条更新语句) 如下,打开表

图8

[
{
"code": "tv_broadcast",
"name": "电视播出",
"sort_order": 1,
"steps": [
{
"code": "begin_shot",
"sort_order": 1,
"is_default": 1
},
{
"code": "writing",
"sort_order": 2,
"is_default": 1
},
{
"code": "manuscript_review",
"sort_order": 4,
"is_default": 1
},
{
"code": "video_editing",
"sort_order": 5,
"is_default": 1
},
{
"code": "tandem_list",
"sort_order": 6,
"is_default": 1
},
{
"code": "program_render",
"sort_order": 7,
"is_default": 1
},
{
"code": "program_authen",
"sort_order": 8,
"is_default": 1
}
]
},
{
"code": "inter_view",
"name": "采访任务",
"sort_order": 2,
"steps": []
},
{
"code": "other",
"name": "手动任务",
"sort_order": 3,
"steps": [
{
"code": "begin_shot",
"sort_order": 1,
"is_default": 1
}
]
},
{
"code": "rich_doc",
"name": "互联网发布更新",
"sort_order": 4,
"steps": [
{
"code": "retrieval",
"sort_order": 1,
"is_default": 1
},
{
"code": "writing",
"sort_order": 2,
"is_default": 1
},
{
"code": "manuscript_review",
"sort_order": 3,
"is_default": 1
},
{
"code": "newspaper_issued",
"sort_order": 5,
"is_default": 1
},
{
"code": "wechat_group_reference",
"sort_order": 6,
"is_default": 1
},
{
"code": "website_release",
"sort_order": 7,
"is_default": 1
},
{
"code": "app_release",
"sort_order": 8,
"is_default": 1
},
{
"code": "wechat_release",
"sort_order": 9,
"is_default": 1
},
{
"code": "blog_release",
"sort_order": 10,
"is_default": 1
},
{
"code": "qq_release",
"sort_order": 11,
"is_default": 1
}
]
},
{
"code": "zdy1",
"name": "自定义一",
"sort_order": 5,
"steps": []
},
{
"code": "zdy2",
"name": "自定义二",
"sort_order": 6,
"steps": [
{
"code": "begin_shot",
"sort_order": 1,
"is_default": 1
},
{
"code": "program_render",
"sort_order": 7,
"is_default": 1
}
]
}
]
Begin transaction
UPDATE `pa_config_task` SET `is_deleted`=0, `deleted_at`=0 WHERE `id`=20
UPDATE `pa_config_task` SET `sort_order`=3, `updated_at`=1573632089 WHERE `id`=20
UPDATE `pa_config_task_step` SET `is_deleted`=0, `deleted_at`=0 WHERE `id`=99
Commit transaction

28、已经有 3 个租户自定义任务类型与步骤设置,2 张表的总行数(20、97),基于 Explain 分析 SQL 如下(type:const|ref,rows 至多为表中总行数的 1/4),索引是合理的,如图9
注:
(1)type:显示的是访问类型,是较为重要的一个指标,结果值从好到坏依次是:
system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL
(2)rows:这个数表示 MySQL 要遍历多少数据才能找到,至多为表中总行数的 1/4 ,就是比较理想的情况

已经有 3 个租户自定义任务类型与步骤设置,2 张表的总行数(20、97),基于 Explain 分析 SQL 如下(type:const|ref,rows 至多为表中总行数的 1/4),索引是合理的

图9

EXPLAIN SELECT * FROM `pa_config_task` WHERE `group_id`=''
id	select_type	table	partitions	type	possible_keys	key	key_len	ref	rows	filtered	Extra
1	SIMPLE	pa_config_task		ref	uc_group_id_code_is_deleted_deleted_at	uc_group_id_code_is_deleted_deleted_at	130	const	4	100.00	
EXPLAIN SELECT * FROM `pa_config_task` WHERE `group_id`='015ce30b116ce86058fa6ab4fea4ac63'
id	select_type	table	partitions	type	possible_keys	key	key_len	ref	rows	filtered	Extra
1	SIMPLE	pa_config_task		ref	uc_group_id_code_is_deleted_deleted_at	uc_group_id_code_is_deleted_deleted_at	130	const	6	100.00	
EXPLAIN SELECT * FROM `pa_config_task` WHERE (`group_id`='015ce30b116ce86058fa6ab4fea4ac63') AND (`is_deleted`=0)
id	select_type	table	partitions	type	possible_keys	key	key_len	ref	rows	filtered	Extra
1	SIMPLE	pa_config_task		ref	uc_group_id_code_is_deleted_deleted_at	uc_group_id_code_is_deleted_deleted_at	130	const	6	10.00	Using index condition
EXPLAIN SELECT * FROM `pa_config_task` WHERE (`group_id`='') AND (`is_deleted`=0) ORDER BY `sort_order`, `id` DESC
id	select_type	table	partitions	type	possible_keys	key	key_len	ref	rows	filtered	Extra
1	SIMPLE	pa_config_task		ref	uc_group_id_code_is_deleted_deleted_at	uc_group_id_code_is_deleted_deleted_at	130	const	4	10.00	Using index condition; Using filesort
EXPLAIN SELECT * FROM `pa_config_task_step` WHERE `group_id`='015ce30b116ce86058fa6ab4fea4ac63'
id	select_type	table	partitions	type	possible_keys	key	key_len	ref	rows	filtered	Extra
1	SIMPLE	pa_config_task_step		ref	uc_group_id_config_task_code_step_code_is_deleted_deleted_at	uc_group_id_config_task_code_step_code_is_deleted_deleted_at	130	const	26	100.00		
EXPLAIN SELECT * FROM `pa_config_task_step` WHERE (`group_id`='') AND (`is_deleted`=0) ORDER BY `sort_order`, `id` DESC
id	select_type	table	partitions	type	possible_keys	key	key_len	ref	rows	filtered	Extra
1	SIMPLE	pa_config_task_step		ref	uc_group_id_config_task_code_step_code_is_deleted_deleted_at	uc_group_id_config_task_code_step_code_is_deleted_deleted_at	130	const	27	10.00	Using index condition; Using filesort
EXPLAIN SELECT * FROM `pa_config_task_step` WHERE (`group_id`='015ce30b116ce86058fa6ab4fea4ac63') AND (`is_deleted`=0) ORDER BY `sort_order`, `id` DESC
id	select_type	table	partitions	type	possible_keys	key	key_len	ref	rows	filtered	Extra
1	SIMPLE	pa_config_task_step		ref	uc_group_id_config_task_code_step_code_is_deleted_deleted_at	uc_group_id_config_task_code_step_code_is_deleted_deleted_at	130	const	26	10.00	Using index condition; Using filesort
EXPLAIN SELECT `pa_config_task`.`id` FROM `pa_config_task` WHERE (`pa_config_task`.`group_id`='015ce30b116ce86058fa6ab4fea4ac63') AND (`pa_config_task`.`code`='tv_broadcast') AND (`pa_config_task`.`is_deleted`=0) AND (`pa_config_task`.`deleted_at`=0) LIMIT 2
id	select_type	table	partitions	type	possible_keys	key	key_len	ref	rows	filtered	Extra
1	SIMPLE	pa_config_task		const	uc_group_id_code_is_deleted_deleted_at	uc_group_id_code_is_deleted_deleted_at	266	const,const,const,const	1	100.00	Using index
EXPLAIN SELECT EXISTS(SELECT * FROM `pa_config_task` WHERE (`pa_config_task`.`group_id`='015ce30b116ce86058fa6ab4fea4ac63') AND (`pa_config_task`.`code`='zdy1') AND (`pa_config_task`.`is_deleted`=0) AND (`pa_config_task`.`deleted_at`=0))
id	select_type	table	partitions	type	possible_keys	key	key_len	ref	rows	filtered	Extra
1	PRIMARY										No tables used
2	SUBQUERY	pa_config_task		const	uc_group_id_code_is_deleted_deleted_at	uc_group_id_code_is_deleted_deleted_at	266	const,const,const,const	1	100.00	Using index
EXPLAIN SELECT EXISTS(SELECT * FROM `pa_config_step` WHERE (`pa_config_step`.`code`='writing') AND (`is_deleted`=0))
id	select_type	table	partitions	type	possible_keys	key	key_len	ref	rows	filtered	Extra
1	PRIMARY										No tables used
2	SUBQUERY	pa_config_step		ref	idx_code	idx_code	130	const	1	10.00	Using where
EXPLAIN SELECT * FROM `pa_config_task_step` WHERE (`group_id`='015ce30b116ce86058fa6ab4fea4ac63') AND (`config_task_code`='tv_broadcast') AND (`step_code`='begin_shot')
id	select_type	table	partitions	type	possible_keys	key	key_len	ref	rows	filtered	Extra
1	SIMPLE	pa_config_task_step		ref	uc_group_id_config_task_code_step_code_is_deleted_deleted_at	uc_group_id_config_task_code_step_code_is_deleted_deleted_at	390	const,const,const	1	100.00	

 

原创文章,作者:carmelaweatherly,如若转载,请注明出处:https://blog.ytso.com/181414.html

(0)
上一篇 2021年10月31日
下一篇 2021年10月31日

相关推荐

发表回复

登录后才能评论