协程版仓库后端项目
Some checks failed
Build Docker / build (push) Has been cancelled

This commit is contained in:
2025-07-08 14:59:47 +08:00
commit 0b2299c427
134 changed files with 19277 additions and 0 deletions

432
app/Dao/AbstractDao.php Normal file
View File

@ -0,0 +1,432 @@
<?php
/**
* Author: ykxiao
* Date: 2025/1/6
* Time: 下午4:07
* Description:
*
* (c) ykxiao <yk_9001@hotmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
declare(strict_types=1);
namespace App\Dao;
use App\Model\Model;
use App\Service\Trait\ColumnConfigTrait;
use Exception;
use Hyperf\Collection\Collection;
use Hyperf\Database\Model\Builder;
use Hyperf\Di\Annotation\Inject;
use Hyperf\HttpServer\Contract\RequestInterface;
/**
* Author: ykxiao
* Date: 2025/2/28
* Time: 上午8:32
* Description: AbstractDao.
*
* (c) ykxiao <yk_9001@hotmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
abstract class AbstractDao
{
use ColumnConfigTrait;
// 支持的表达式 - 白名单操作符
protected static array $OPERATORS_THAT_SUPPORT_VALUES = [
// 比较运算符
'=', '<', '<=', '>', '>=', '<>', '!=',
// 逻辑运算符
'between', 'not between', 'in', 'not in', 'like', 'is null', 'is not null',
];
#[Inject]
protected RequestInterface $request;
private int|string $keyId;
private array $queryResult;
/**
* @return Model
*/
abstract protected function getModel(): string;
/**
* 数据操作.
*/
public function builder(): Builder
{
return $this->getModel()::query();
}
/**
* 实例化.
*/
public function make(): AbstractDao
{
return new $this();
}
/**
* 记录查询.
* @throws Exception
*/
public function qInfo(array $params = []): Builder
{
$query = $this->builder();
$params = $params ?: $this->request->all();
if (!empty($params['ids']) && is_array($params['ids'])) {
$query->whereIn('id', $params['ids']);
} else {
$query->where('id', intval($params['id']));
}
if (!$query->exists()) {
throw new Exception('记录不存在');
}
return $query;
}
/**
* 通用创建&更新.
* @param array $data 需要新增更新的数据
* @param array $updateCondition 更新条件
* @return $this
* @throws Exception
*/
public function commonCreate(array $data = [], array $updateCondition = []): AbstractDao
{
$ignoreUpdateFields = ['id', 'company_id', 'creator_id', 'creator_name']; // 不允许更新的字段
// 1根据条件更新或创建
if (!empty($updateCondition)) {
$updateData = array_diff_key($data, array_flip($ignoreUpdateFields));
$this->builder()->updateOrCreate($updateCondition, $updateData);
// 为保证查询条件一致,重新构造查询
$query = $this->builder()->where($updateCondition);
$records = $query->get();
$this->queryResult = $records->count() > 1
? $records->toArray()
: $records->first()?->toArray();
return $this;
}
// 2根据主键 ID 更新
if (!empty($data['id'])) {
$this->keyId = $data['id'];
$query = $this->qInfo($data);
$query->lockForUpdate()->first(); // 获取锁
$updateData = array_diff_key($data, array_flip($ignoreUpdateFields));
$res = $query->update($updateData);
if (!$res) {
throw new Exception('数据更新失败');
}
// 更新后重新查询,避免条件变化导致查询不到
$this->queryResult = $this->builder()->where('id', $this->keyId)->first()?->toArray();
return $this;
}
// 3创建新记录
$res = $this->builder()->create($data);
if (!$res) {
throw new Exception('数据新增失败');
}
$this->keyId = $res->getKey();
$this->queryResult = $res->toArray();
return $this;
}
/**
* 返回更新&新增主键.
*/
public function getKey(): int
{
return $this->keyId;
}
/**
* 返回数据库操作结果.
*/
public function result(): array
{
return $this->queryResult;
}
/**
* 数据验证
*/
public function verifyData(Builder $builder, array $params = []): Builder
{
$map = [];
$condition = [];
if (!empty($params['id']) && is_numeric($params['id'])) {
$condition = ['id' => $params['id']];
}
foreach ($condition as $k => $v) {
$map[] = [$k, '!=', $v];
}
return clone $builder->where($map);
}
/**
* 更新状态
* @throws Exception
*/
public function updateStatus(): null|object
{
$params = $this->request->all();
$query = $this->qInfo()->lockForUpdate();
$info = $query->first();
if ($info['active_status'] == $params['status']) {
throw new Exception('状态不对,请确认');
}
$query->update(['active_status' => $params['status']]);
return $info;
}
/***************************************************************************************
* 构建查询数据条件参数.
* $params = [
* 'mail_sync_id' => 123,
* 'sn' => ['operator' => '!=', 'value' => 'ABC123'],
* 'aliid' => ['operator' => 'like', 'value' => '%example%'],
* 'serial_number' => ['operator' => '>', 'value' => 100],
* 'ali_model' => 'ModelX',
* 'model' => 'XYZ',
* 'date' => ['operator' => 'between', 'value' => ['2023-01-01', '2023-01-31']],
* 'id' => [
* ['operator' => '>=', 'value' => 10],
* ['operator' => '<=', 'value' => 100]
* ],
* 'status' => ['operator' => 'in', 'value' => ['active', 'pending']],
* ];
*************************************************************************************
* @throws Exception
*/
public static function daoBuildWhere(Builder $builder, array $params, array $fields): Builder
{
if (empty($params)) return $builder; // 如果没有参数,直接返回
// 用于存储所有 like 条件
$likeConditions = [];
foreach ($fields as $field) {
// 跳过空参数
if (!isset($params[$field]) || $params[$field] === '') {
continue;
}
$param = $params[$field];
// 如果参数是数组
if (is_array($param)) {
// 处理同一个字段的多个条件
if (isset($param[0]) && is_array($param[0])) {
self::addMultipleConditions($builder, $field, $param);
} else {
// 处理单个条件
self::handleSingleCondition($builder, $likeConditions, $field, $param);
}
} else {
// 处理字符串条件,字符串条件,强制使用绑定参数
$builder->where($field, '=', $param);
}
}
// 应用所有 like 条件
self::applyLikeConditions($builder, $likeConditions);
// 添加排序功能
if (!empty($params['sort_orders'])) {
self::applySortOrders($builder, $params['sort_orders'], $fields);
}
return $builder;
}
private static function addMultipleConditions(Builder $builder, string $field, array $conditions): void
{
// 处理同一个字段的多个条件
$builder->where(/**
* @throws Exception
*/ function ($query) use ($field, $conditions) {
foreach ($conditions as $condition) {
// 应用每个条件
self::applyCondition($query, $field, $condition);
}
});
}
private static function handleSingleCondition(Builder $builder, array &$likeConditions, string $field, array $param): void
{
// 收集 like 条件或处理其他单个条件
if (isset($param['operator']) && strtolower($param['operator']) === 'like') {
// 如果是 like 条件,将其添加到 likeConditions 数组中
$likeConditions[] = ['field' => $field, 'condition' => $param];
} else {
// 否则,直接应用条件
$builder->where(
/**
* @throws Exception
*/ function ($query) use ($field, $param) {
self::applyCondition($query, $field, $param);
});
}
}
private static function applyLikeConditions(Builder $builder, array $likeConditions): void
{
// 应用所有 like 条件
if (!empty($likeConditions)) {
$builder->where(function ($query) use ($likeConditions) {
foreach ($likeConditions as $likeCondition) {
if (empty($likeCondition['condition']['value'])) continue;
// 使用 orWhere 添加每个 like 条件
$query->orWhere($likeCondition['field'], 'like', $likeCondition['condition']['value']);
}
});
}
}
/**
* 应用排序功能.
* @param Builder $builder
* @param array $sortOrders
* @param array $fields
* @return void
* @throws Exception
*/
private static function applySortOrders(Builder $builder, array $sortOrders, array $fields): void
{
foreach ($sortOrders as $field => $sortOrder) {
if (!in_array($sortOrder, ['asc', 'desc'])) {
throw new Exception('排序方式不正确');
}
if (!in_array($field, $fields)) {
throw new Exception('排序字段不正确');
}
$builder->orderBy($field, $sortOrder);
}
}
/**
* 应用单个字段的查询条件.
* 调用方式
* builder,'name','='|['operate'=>'bett'].
* @throws Exception
*/
private static function applyCondition(Builder &$builder, string $field, mixed $condition): void
{
if (is_array($condition) && isset($condition['operator']) && isset($condition['value'])) {
if (empty($condition['value'])) {
return;
}
$operator = strtolower($condition['operator']);
if (!in_array($operator, self::$OPERATORS_THAT_SUPPORT_VALUES)) {
throw new Exception('不支持的查询表达式: ' . $condition['operator']);
}
$builder = match ($operator) {
'between' => $builder->whereBetween($field, $condition['value']),
'not between' => $builder->whereNotBetween($field, $condition['value']),
'in' => $builder->whereIn($field, $condition['value']),
'not in' => $builder->whereNotIn($field, $condition['value']),
'is null' => $builder->whereNull($field),
'is not null' => $builder->whereNotNull($field),
'like' => $builder->orWhere($field, 'like', $condition['value']),
default => $builder->where($field, $operator, $condition['value']),
};
} else {
$builder->where($field, '=', $condition);
}
}
/**
* 获取字段列表(公共).
* @param Builder $builder
* @param array $params
* @param array $fields
* @param string|array $addSelect
* @return array
* @throws Exception
*/
public function selectFields(Builder $builder, array $params, array $fields = [], string|array $addSelect = ''): array
{
self::daoBuildWhere($builder, $params, $fields);
$field = $params['field'] ?? '';
if (!in_array($field, $fields)) {
throw new Exception('字段参数错误');
}
$data = $builder->select(['id', $field]);
if (!empty($addSelect)) {
$data = $data->addSelect($addSelect);
}
return $data->get()->filter(function ($item) use ($field) {
// 过滤空值但不过滤布尔值
return !empty($item[$field]) || is_bool($item[$field]);
})->unique($field)->values()->toArray();
}
/**
* 分页处理函数.
*
* 本函数用于对给定的Builder或Collection对象进行分页处理并返回分页后的数据及其它相关信息。
*
* @param Builder|Collection $builder 可以是Eloquent Builder对象或Collection对象
* @param array $params 包含分页参数的数组,比如页码和每页数量
* @return array 返回一个包含分页数据、字段配置、过滤条件、表格设置等信息的数组
* @throws Exception
*/
public function paginate(Builder|Collection $builder, array $params = []): array
{
try {
// 从参数中获取页码和每页数量,未指定则默认值
$page = empty($params['page']) ? 1 : (int)$params['page'];
$page_size = empty($params['pageSize']) ? 100 : (int)$params['pageSize'];
// 计算跳过的记录数
$skip = ($page - 1) * $page_size;
// 获取总记录数
$total = $builder->count();
// 计算总页数若记录数为0则页数为0
$pageSize = $total == 0 ? 0 : (int)ceil($total / $page_size);
// 根据Builder类型获取分页后的数据
if ($builder instanceof Collection) {
// 对Collection对象进行分页
$rows = $builder->values()->slice($skip, $page_size)->values();
} else {
// 对Builder对象进行分页查询
$rows = $builder->skip($skip)->take($page_size)->orderBy('id', 'desc')->get();
}
// 获取列配置信息
$columnConfig = $this->getColumnConfig();
$tableConfig = $this->getTableConfig();
return compact(
'rows',
'page',
'total',
'pageSize',
'columnConfig',
'tableConfig'
);
} catch (Exception $e) {
throw new Exception($e->getMessage());
}
}
}