Files
wh-api/app/Dao/AbstractDao.php
ykxiao 8a3ab17b25
Some checks failed
Build Docker / build (push) Has been cancelled
增加基础配置
2025-07-12 11:59:33 +08:00

450 lines
14 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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;
/**
* 获取query selector 字段
* @return array
*/
abstract public function getFields(): array;
/**
* 构建查询条件.
* @throws Exception
*/
public function buildWhere(Builder $builder, array $params = []): Builder
{
// 根据字段查询数据库params存在的条件进行查询
$fields = $this->getFields();
return self::daoBuildWhere($builder, $params, $fields);
}
/**
* 数据操作.
*/
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());
}
}
}