204 lines
6.5 KiB
PHP
204 lines
6.5 KiB
PHP
<?php
|
||
|
||
namespace app\common\exception;
|
||
|
||
use DateTime;
|
||
use think\facade\Request;
|
||
use think\facade\Log;
|
||
use think\helper\Arr;
|
||
use think\facade\Db;
|
||
use app\common\arw\adjfut\src\Exception\ErrorMsg;
|
||
|
||
/**
|
||
* 排序类(调用此类的方法需要进行事务与锁表操作)
|
||
*/
|
||
class Sort
|
||
{
|
||
/**
|
||
* 模型层实例
|
||
* @var \think\model
|
||
*/
|
||
private $model;
|
||
|
||
/**
|
||
* 主键字段名
|
||
* @var string
|
||
*/
|
||
private $pk;
|
||
|
||
/**
|
||
* 排序字段名
|
||
* @var string
|
||
*/
|
||
private $sort_field;
|
||
|
||
/**
|
||
* 表名
|
||
* @var string
|
||
*/
|
||
private $table_name;
|
||
|
||
/**
|
||
* 构造器
|
||
*
|
||
* @param string $model 模型命名空间
|
||
*/
|
||
public function __construct(string $model)
|
||
{
|
||
//模型实例化
|
||
$this->model = new $model();
|
||
//主键字段名初始化
|
||
$this->pk = $this->model->db()->getPk();
|
||
//表名初始化
|
||
$this->table_name = $this->model->db()->getTable();
|
||
//排序字段名初始化
|
||
$this->initSortField();
|
||
}
|
||
|
||
/**
|
||
* 获取排序字段名
|
||
*/
|
||
public function getSortField(): string
|
||
{
|
||
return $this->sort_field;
|
||
}
|
||
|
||
/**
|
||
* 排序号新增处理
|
||
*
|
||
* @param array $extra_wheres 关联类型 例:["xxx_type" => 1]
|
||
* @return int 返回新增所需的最新排序号
|
||
*/
|
||
public function add(array $extra_wheres = []): int
|
||
{
|
||
$sort_field = $this->sort_field;
|
||
$max_sort = $this->model->where($extra_wheres)->order($sort_field, 'desc')->value($sort_field) ?? 0;
|
||
return ++$max_sort;
|
||
}
|
||
|
||
/**
|
||
* 排序号互换处理(数据A <=> 数据B)
|
||
*
|
||
* @param string $guid 主键值
|
||
* @param int $sort 新排序号
|
||
* @param array $extra_wheres 关联类型 例:["xxx_type" => 1]
|
||
*/
|
||
public function swap(string $guid, int $sort, array $extra_wheres = []): void
|
||
{
|
||
$model = $this->model;
|
||
$table_name = $this->table_name;
|
||
$pk = $this->pk;
|
||
$sort_field = $this->sort_field;
|
||
|
||
//数据A
|
||
$data_a = $model->where($pk, $guid)->find();
|
||
if (!$data_a) {
|
||
throwErrorMsg('Tool::sortEditProc() : 找不到该数据原排序号', 444);
|
||
}
|
||
|
||
//数据B
|
||
//查找当前数据所想更换的新排序号是否已有数据占用(排除自己),这个占用数据则视为数据B
|
||
$data_b = $model->where($sort_field, $sort)->where($pk, '<>', $guid)->where($extra_wheres)->find();
|
||
|
||
//数据B存在,开始进行互换处理
|
||
if ($data_b) {
|
||
//【互换内容初始化】
|
||
$update_data = [];
|
||
//检查模型是否有父祖级主键,有则也加入互换
|
||
if ($this->isExistFathersAndAncestors()) {
|
||
$update_data[$model->parent_guid_field] = $data_a[$model->parent_guid_field];
|
||
$update_data[$model->ancestors_guid_field] = $data_a[$model->ancestors_guid_field];
|
||
}
|
||
//原字段排序加入互换
|
||
$update_data = [$sort_field => $data_a[$sort_field]];
|
||
//关联类型也加入互换
|
||
foreach ($extra_wheres as $key => $value) {
|
||
$update_data[$key] = $data_a[$key];
|
||
};
|
||
|
||
//【数据逻辑处理】
|
||
//互换数据A与B的关联类型不相同时候,则代表为数据互换跨类型情况
|
||
if (self::isAssociationTypeDataSame($data_a, $data_b, $extra_wheres)) {
|
||
//数据互换不同类型情况-正常互换操作
|
||
Db::name($table_name)->where($sort_field, $sort)->where($extra_wheres)->update($update_data);
|
||
} else {
|
||
//数据互换跨类型情况-腾位操作
|
||
$this->vacate($sort, $extra_wheres);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 排序号腾位处理
|
||
*
|
||
* @param int|null $sort 当前排序号
|
||
* @param array $wheres 当前指定数据的额外查询条件(tp批量查询数组) 例:["xxx_type" => 1,...]
|
||
*/
|
||
public function vacate($sort, array $wheres = []): void
|
||
{
|
||
$sort_field = $this->sort_field;
|
||
$table_name = $this->table_name;
|
||
//新增数据的所属排序号已有数据占用,则腾位处理(已占用的数据及其后面所有数据一起排序号腾位+1)
|
||
if ($this->model->where($sort_field, $sort)->where($wheres)->value($sort_field) !== null) {
|
||
Db::name($table_name)->where($wheres)->where($sort_field, '>=', $sort)->inc($sort_field)->update();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 排序号退位处理
|
||
*
|
||
* @param string $guid 当前主键值
|
||
* @param array $correl_fields 关联类型字段 例:["xxx_guid",...] 代表删除排序号时会根据这些字段来关联(例:所属xxx类型、所属xxx系列)来进行排序处理
|
||
*/
|
||
public function back(string $guid, array $correl_fields): void
|
||
{
|
||
$model = $this->model;
|
||
$sort_field = $this->sort_field;
|
||
//在所删除的数据之后的数据所属排序号将进行减位处理减位-1
|
||
$deleted_find = $model->where($this->pk, $guid)->find();
|
||
if ($deleted_find) {
|
||
$con = [];
|
||
foreach ($correl_fields as $correl_field) {
|
||
$con[$correl_field] = $deleted_find[$correl_field];
|
||
}
|
||
$model->where($sort_field, '>', $deleted_find[$sort_field])->where($con)->dec($sort_field)->save();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 对比数据关联类型是否相同【排序号互换处理】
|
||
*
|
||
* @param object $data_a 互换数据A
|
||
* @param object $data_b 互换数据B
|
||
* @param array $extra_wheres 关联类型数组 例:["xxx_type" => 1]
|
||
*/
|
||
private static function isAssociationTypeDataSame(object $data_a, object $data_b, array $extra_wheres): bool
|
||
{
|
||
foreach ($extra_wheres as $key => $val) {
|
||
if ($data_a[$key] != $data_b[$key]) {
|
||
return false;
|
||
}
|
||
};
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 检查模型实例父祖级主键字段是否存在【排序号互换处理】
|
||
*/
|
||
private function isExistFathersAndAncestors(): bool
|
||
{
|
||
return isset($this->model->parent_guid_field) && isset($this->model->ancestors_guid_field);
|
||
}
|
||
|
||
/**
|
||
*初始化排序字段名
|
||
*/
|
||
private function initSortField(): void
|
||
{
|
||
if (!isset($this->model->order_field)) {
|
||
throwErrorMsg('排序处理失败!模型层未定义排序字段$order_field');
|
||
}
|
||
$this->sort_field = $this->model->order_field;
|
||
}
|
||
}
|