169 lines
5.6 KiB
PHP
169 lines
5.6 KiB
PHP
<?php
|
|
|
|
namespace app\service;
|
|
|
|
use app\model\Permission;
|
|
use think\facade\Log;
|
|
|
|
/**
|
|
* 数据隔离服务
|
|
*/
|
|
class DataIsolation
|
|
{
|
|
/**
|
|
* 构建数据隔离WHERE子句
|
|
*
|
|
* @param Permission $permission 权限记录
|
|
* @param string $endpoint 接口标识 chuku/ruku
|
|
* @param string|null $customerName 客户名称(全量数据权限时可选)
|
|
* @return array ['sql' => string, 'params' => array]
|
|
* @throws \Exception
|
|
*/
|
|
public static function buildIsolationClause($permission, $endpoint, $customerName = null)
|
|
{
|
|
// 1. 检查接口权限
|
|
if ($endpoint === 'chuku' && !$permission->canAccess('chuku')) {
|
|
throw new \Exception('该 API Key 未被授权访问销售数据接口');
|
|
}
|
|
if ($endpoint === 'ruku' && !$permission->canAccess('ruku')) {
|
|
throw new \Exception('该 API Key 未被授权访问入库数据接口');
|
|
}
|
|
|
|
// 2. 全量数据权限:检查是否传入供方名称过滤
|
|
if ($permission->isFullData()) {
|
|
Log::debug("API Key {$permission->api_key_id} 拥有全量数据权限");
|
|
|
|
if (!empty($customerName)) {
|
|
$filterField = 'k.[供方全称]';
|
|
$sqlFragment = " AND {$filterField} LIKE :supplier_name";
|
|
$params = ['supplier_name' => '%' . $customerName . '%'];
|
|
Log::debug("全量数据模式下按供方名称过滤: {$filterField} LIKE %{$customerName}%");
|
|
return ['sql' => $sqlFragment, 'params' => $params];
|
|
}
|
|
|
|
return ['sql' => '', 'params' => []];
|
|
}
|
|
|
|
// 3. 根据数据模式确定过滤方式
|
|
$dataMode = $permission->data_mode;
|
|
|
|
if ($dataMode === Permission::MODE_FULL) {
|
|
return ['sql' => '', 'params' => []];
|
|
}
|
|
|
|
// 4. 使用数据库中已配置的过滤规则
|
|
if (!empty($permission->filter_field) && !empty($permission->filter_values)) {
|
|
return self::buildCustomFilter($permission, $endpoint);
|
|
}
|
|
|
|
// 5. 自动过滤:根据接口类型自动选择过滤字段和值
|
|
$apiKey = $permission->apiKey;
|
|
$client = $apiKey->client;
|
|
|
|
if (!$client) {
|
|
throw new \Exception('API Key 未关联客户');
|
|
}
|
|
|
|
return self::buildAutoFilter($permission, $endpoint, $client);
|
|
}
|
|
|
|
/**
|
|
* 构建自定义过滤规则
|
|
*/
|
|
private static function buildCustomFilter($permission, $endpoint)
|
|
{
|
|
$filterField = $permission->filter_field;
|
|
$filterValues = $permission->filter_values;
|
|
|
|
if (empty($filterField) || empty($filterValues)) {
|
|
Log::warning("API Key {$permission->api_key_id} 使用自定义模式,但未配置过滤规则,拒绝访问");
|
|
throw new \Exception('权限配置不完整:需要手动配置过滤字段和过滤值');
|
|
}
|
|
|
|
if (!is_array($filterValues)) {
|
|
$filterValues = json_decode($filterValues, true);
|
|
}
|
|
|
|
if ($filterValues === null || !is_array($filterValues) || empty($filterValues)) {
|
|
Log::warning("API Key {$permission->api_key_id} 过滤值为空或无效");
|
|
throw new \Exception('过滤值列表为空或无效,请联系管理员');
|
|
}
|
|
|
|
if (!self::validateFilterField($filterField, $endpoint)) {
|
|
throw new \Exception('非法的过滤字段');
|
|
}
|
|
|
|
$prefixedField = self::prefixFilterField($filterField, $endpoint);
|
|
|
|
$placeholders = [];
|
|
$params = [];
|
|
foreach ($filterValues as $index => $value) {
|
|
$placeholder = ':iso_' . $index;
|
|
$placeholders[] = $placeholder;
|
|
$params['iso_' . $index] = $value;
|
|
}
|
|
|
|
$sqlFragment = " AND {$prefixedField} IN (" . implode(',', $placeholders) . ')';
|
|
Log::debug("数据隔离: {$prefixedField} IN " . json_encode($filterValues));
|
|
|
|
return ['sql' => $sqlFragment, 'params' => $params];
|
|
}
|
|
|
|
/**
|
|
* 为过滤字段添加表别名前缀
|
|
*/
|
|
private static function prefixFilterField($field, $endpoint)
|
|
{
|
|
if (strpos($field, '[') === 0) {
|
|
$field = substr($field, 1, -1);
|
|
}
|
|
|
|
$prefixMap = [
|
|
'供方全称' => 'k.[供方全称]',
|
|
'供方序号' => 'k.[供方序号]',
|
|
'产品名称' => 'p.[产品名称]',
|
|
'产品序号' => 'p.[产品序号]',
|
|
];
|
|
|
|
if ($endpoint === 'chuku') {
|
|
$prefixMap['销方全称'] = 'f.[销方全称]';
|
|
$prefixMap['销方序号'] = 'f.[销方序号]';
|
|
}
|
|
|
|
return $prefixMap[$field] ?? "[{$field}]";
|
|
}
|
|
|
|
/**
|
|
* 构建自动过滤规则
|
|
*/
|
|
private static function buildAutoFilter($permission, $endpoint, $client)
|
|
{
|
|
$filterField = 'k.[供方全称]';
|
|
|
|
$filterValue = $client->name;
|
|
$sqlFragment = " AND {$filterField} = :iso_0";
|
|
$params = ['iso_0' => $filterValue];
|
|
|
|
Log::debug("数据隔离: {$filterField} = {$filterValue}");
|
|
|
|
return ['sql' => $sqlFragment, 'params' => $params];
|
|
}
|
|
|
|
/**
|
|
* 验证过滤字段是否在白名单内(防注入)
|
|
*/
|
|
private static function validateFilterField($field, $endpoint)
|
|
{
|
|
$allowedFields = [
|
|
'供方全称', '供方序号',
|
|
'产品名称', '产品序号',
|
|
'customer_name', 'supplier_name', 'customer_code', 'supplier_code',
|
|
];
|
|
|
|
if ($endpoint === 'chuku') {
|
|
$allowedFields = array_merge($allowedFields, ['销方全称', '销方序号']);
|
|
}
|
|
|
|
return in_array($field, $allowedFields);
|
|
}
|
|
} |