429 lines
17 KiB
PHP
429 lines
17 KiB
PHP
<?php
|
|
|
|
namespace app\service;
|
|
|
|
use think\facade\Db;
|
|
use think\facade\Log;
|
|
|
|
class MssqlQuery
|
|
{
|
|
private static $healthCache = [
|
|
'available' => null,
|
|
'checked_at' => 0
|
|
];
|
|
private static $healthCheckInterval = 60;
|
|
|
|
private static function isMssqlAvailable()
|
|
{
|
|
$now = time();
|
|
if ($now - self::$healthCache['checked_at'] < self::$healthCheckInterval) {
|
|
return self::$healthCache['available'];
|
|
}
|
|
|
|
try {
|
|
Db::connect('mssql')->query('SELECT 1');
|
|
self::$healthCache['available'] = true;
|
|
self::$healthCache['checked_at'] = $now;
|
|
Log::info('SQL Server 连接恢复正常');
|
|
} catch (\Exception $e) {
|
|
self::$healthCache['available'] = false;
|
|
self::$healthCache['checked_at'] = $now;
|
|
Log::warning("SQL Server 不可用(下次探测 " . self::$healthCheckInterval . "s 后): " . $e->getMessage());
|
|
}
|
|
|
|
return self::$healthCache['available'];
|
|
}
|
|
|
|
public static function queryChuku($startDate, $endDate, $page, $limit, $isolationSql, $isolationParams)
|
|
{
|
|
if (!self::isMssqlAvailable()) {
|
|
throw new \Exception('SQL Server 连接不可用');
|
|
}
|
|
|
|
return self::queryChukuReal($startDate, $endDate, $page, $limit, $isolationSql, $isolationParams);
|
|
}
|
|
|
|
private static function queryChukuReal($startDate, $endDate, $page, $limit, $isolationSql, $isolationParams)
|
|
{
|
|
$dateCondition = '';
|
|
$dateParams = [];
|
|
|
|
if ($startDate) {
|
|
$dateCondition .= " AND x.[日期] >= :start_date";
|
|
$dateParams['start_date'] = $startDate;
|
|
}
|
|
if ($endDate) {
|
|
$dateCondition .= " AND x.[日期] <= :end_date";
|
|
$dateParams['end_date'] = $endDate;
|
|
}
|
|
|
|
$startRow = ($page - 1) * $limit + 1;
|
|
$endRow = $page * $limit;
|
|
$countParams = array_merge($dateParams, $isolationParams);
|
|
$dataParams = array_merge($countParams, [
|
|
'start_row' => $startRow,
|
|
'end_row' => $endRow
|
|
]);
|
|
|
|
$sqlResult = self::buildLogSql(self::getChukuBaseSql(), $dateCondition, $isolationSql, $dataParams);
|
|
$dataSql = $sqlResult['dataSql'];
|
|
$logSql = $sqlResult['logSql'];
|
|
|
|
$countSql = self::getChukuCountSql();
|
|
$countSql = str_replace(['{date_condition}', '{isolation_condition}'], [$dateCondition, $isolationSql], $countSql);
|
|
|
|
Log::debug("出库查询 - 统计SQL: " . $countSql);
|
|
Log::debug("出库查询 - 统计参数: " . json_encode($countParams));
|
|
Log::debug("出库查询 - 统计参数数量: " . count($countParams));
|
|
|
|
try {
|
|
$total = Db::connect('mssql')->query($countSql, $countParams);
|
|
$total = isset($total[0]['total']) ? (int)$total[0]['total'] : 0;
|
|
Log::debug("出库查询 - 统计成功,总数: " . $total);
|
|
} catch (\Exception $e) {
|
|
Log::error("出库查询 - 统计失败: " . $e->getMessage());
|
|
Log::error("出库查询 - 统计SQL(原始): " . $countSql);
|
|
Log::error("出库查询 - 统计参数(原始): " . json_encode($countParams));
|
|
throw $e;
|
|
}
|
|
|
|
Log::debug("出库查询 - 数据SQL: " . $dataSql);
|
|
Log::debug("出库查询 - 数据参数: " . json_encode($dataParams));
|
|
Log::debug("出库查询 - 数据参数数量: " . count($dataParams));
|
|
|
|
try {
|
|
$rows = Db::connect('mssql')->query($dataSql, $dataParams);
|
|
Log::debug("出库查询 - 数据查询成功,行数: " . count($rows));
|
|
} catch (\Exception $e) {
|
|
Log::error("出库查询 - 数据查询失败: " . $e->getMessage());
|
|
Log::error("出库查询 - 数据SQL(原始): " . $dataSql);
|
|
Log::error("出库查询 - 数据参数(原始): " . json_encode($dataParams));
|
|
throw $e;
|
|
}
|
|
|
|
$records = [];
|
|
foreach ($rows as $row) {
|
|
$records[] = self::convertChukuRow($row);
|
|
}
|
|
|
|
return [
|
|
'records' => $records,
|
|
'total' => $total,
|
|
'sql' => $logSql
|
|
];
|
|
}
|
|
|
|
public static function queryRuku($startDate, $endDate, $page, $limit, $isolationSql, $isolationParams)
|
|
{
|
|
if (!self::isMssqlAvailable()) {
|
|
throw new \Exception('SQL Server 连接不可用');
|
|
}
|
|
|
|
return self::queryRukuReal($startDate, $endDate, $page, $limit, $isolationSql, $isolationParams);
|
|
}
|
|
|
|
private static function queryRukuReal($startDate, $endDate, $page, $limit, $isolationSql, $isolationParams)
|
|
{
|
|
$dateCondition = '';
|
|
$dateParams = [];
|
|
|
|
if ($startDate) {
|
|
$dateCondition .= " AND x.[日期] >= :start_date";
|
|
$dateParams['start_date'] = $startDate;
|
|
}
|
|
if ($endDate) {
|
|
$dateCondition .= " AND x.[日期] <= :end_date";
|
|
$dateParams['end_date'] = $endDate;
|
|
}
|
|
|
|
$startRow = ($page - 1) * $limit + 1;
|
|
$endRow = $page * $limit;
|
|
$countParams = array_merge($dateParams, $isolationParams);
|
|
$dataParams = array_merge($countParams, [
|
|
'start_row' => $startRow,
|
|
'end_row' => $endRow
|
|
]);
|
|
|
|
$sqlResult = self::buildLogSql(self::getRukuBaseSql(), $dateCondition, $isolationSql, $dataParams);
|
|
$dataSql = $sqlResult['dataSql'];
|
|
$logSql = $sqlResult['logSql'];
|
|
|
|
$countSql = self::getRukuCountSql();
|
|
$countSql = str_replace(['{date_condition}', '{isolation_condition}'], [$dateCondition, $isolationSql], $countSql);
|
|
|
|
Log::debug("入库查询 - 统计SQL: " . $countSql);
|
|
Log::debug("入库查询 - 统计参数: " . json_encode($countParams));
|
|
|
|
$total = Db::connect('mssql')->query($countSql, $countParams);
|
|
$total = isset($total[0]['total']) ? (int)$total[0]['total'] : 0;
|
|
|
|
Log::debug("入库查询 - 数据SQL: " . $dataSql);
|
|
Log::debug("入库查询 - 数据参数: " . json_encode($dataParams));
|
|
|
|
$rows = Db::connect('mssql')->query($dataSql, $dataParams);
|
|
|
|
$records = [];
|
|
foreach ($rows as $row) {
|
|
$records[] = self::convertRukuRow($row);
|
|
}
|
|
|
|
return [
|
|
'records' => $records,
|
|
'total' => $total,
|
|
'sql' => $logSql
|
|
];
|
|
}
|
|
|
|
private static function buildLogSql($sqlTemplate, $dateCondition, $isolationSql, $allParams)
|
|
{
|
|
$dataSql = str_replace(['{date_condition}', '{isolation_condition}'], [$dateCondition, $isolationSql], $sqlTemplate);
|
|
$logSql = $dataSql;
|
|
|
|
foreach ($allParams as $key => $value) {
|
|
$placeholder = ':' . $key;
|
|
if (is_string($value)) {
|
|
$logSql = str_replace($placeholder, "'" . $value . "'", $logSql);
|
|
} else {
|
|
$logSql = str_replace($placeholder, (string)$value, $logSql);
|
|
}
|
|
}
|
|
|
|
return ['dataSql' => $dataSql, 'logSql' => $logSql];
|
|
}
|
|
|
|
private static function convertChukuRow($row)
|
|
{
|
|
return [
|
|
'chuku_id' => $row['chuku_id'] ?? '',
|
|
'ruku_id' => $row['ruku_id'] ?? '',
|
|
'ticket_no' => $row['ticket_no'] ?? '',
|
|
'date' => self::convertDate($row['date'] ?? null),
|
|
'warehouse' => $row['warehouse'] ?? '',
|
|
'batch_no' => $row['batch_no'] ?? '',
|
|
'expire_date' => self::convertDate($row['expire_date'] ?? null),
|
|
'quantity' => self::convertFloat($row['quantity'] ?? null),
|
|
'unit_price' => self::convertFloat($row['unit_price'] ?? null),
|
|
'amount' => self::convertFloat($row['amount'] ?? null),
|
|
'purchase_price' => self::convertFloat($row['purchase_price'] ?? null),
|
|
'profit' => self::convertFloat($row['profit'] ?? null),
|
|
'operator' => $row['operator'] ?? '',
|
|
'salesman' => $row['salesman'] ?? '',
|
|
'receiver' => $row['receiver'] ?? '',
|
|
'summary' => $row['summary'] ?? '',
|
|
'supplier_id' => $row['supplier_id'] ?? '',
|
|
'supplier_name' => $row['supplier_name'] ?? '',
|
|
'supplier_short_name' => $row['supplier_short_name'] ?? '',
|
|
'customer_id' => $row['customer_id'] ?? '',
|
|
'customer_name' => $row['customer_name'] ?? '',
|
|
'customer_short_name' => $row['customer_short_name'] ?? '',
|
|
'customer_region' => $row['customer_region'] ?? '',
|
|
'product_id' => $row['product_id'] ?? '',
|
|
'product_name' => $row['product_name'] ?? '',
|
|
'product_spec' => $row['product_spec'] ?? '',
|
|
'product_origin' => $row['product_origin'] ?? '',
|
|
'product_unit' => $row['product_unit'] ?? '',
|
|
'manufacturer' => $row['manufacturer'] ?? '',
|
|
];
|
|
}
|
|
|
|
private static function convertRukuRow($row)
|
|
{
|
|
return [
|
|
'ruku_id' => $row['ruku_id'] ?? '',
|
|
'ticket_no' => $row['ticket_no'] ?? '',
|
|
'date' => self::convertDate($row['date'] ?? null),
|
|
'warehouse' => $row['warehouse'] ?? '',
|
|
'batch_no' => $row['batch_no'] ?? '',
|
|
'expire_date' => self::convertDate($row['expire_date'] ?? null),
|
|
'quantity' => self::convertFloat($row['quantity'] ?? null),
|
|
'unit_price' => self::convertFloat($row['unit_price'] ?? null),
|
|
'amount' => self::convertFloat($row['amount'] ?? null),
|
|
'purchase_price' => self::convertFloat($row['purchase_price'] ?? null),
|
|
'warehouse_qty' => self::convertFloat($row['warehouse_qty'] ?? null),
|
|
'operator' => $row['operator'] ?? '',
|
|
'purchaser' => $row['purchaser'] ?? '',
|
|
'responsible_person' => $row['responsible_person'] ?? '',
|
|
'arrival_no' => $row['arrival_no'] ?? '',
|
|
'invoice_no' => $row['invoice_no'] ?? '',
|
|
'summary' => $row['summary'] ?? '',
|
|
'supplier_id' => $row['supplier_id'] ?? '',
|
|
'supplier_name' => $row['supplier_name'] ?? '',
|
|
'supplier_short_name' => $row['supplier_short_name'] ?? '',
|
|
'product_id' => $row['product_id'] ?? '',
|
|
'product_name' => $row['product_name'] ?? '',
|
|
'product_spec' => $row['product_spec'] ?? '',
|
|
'product_origin' => $row['product_origin'] ?? '',
|
|
'product_unit' => $row['product_unit'] ?? '',
|
|
'manufacturer' => $row['manufacturer'] ?? '',
|
|
];
|
|
}
|
|
|
|
private static function convertDate($value)
|
|
{
|
|
if ($value === null) {
|
|
return null;
|
|
}
|
|
|
|
if (is_numeric($value)) {
|
|
try {
|
|
$base = new \DateTime('1899-12-30');
|
|
$base->add(new \DateInterval('P' . (int)$value . 'D'));
|
|
return $base->format('Y-m-d');
|
|
} catch (\Exception $e) {
|
|
return (string)$value;
|
|
}
|
|
}
|
|
|
|
if ($value instanceof \DateTime) {
|
|
return $value->format('Y-m-d');
|
|
}
|
|
|
|
return (string)$value;
|
|
}
|
|
|
|
private static function convertFloat($value)
|
|
{
|
|
if ($value === null) {
|
|
return null;
|
|
}
|
|
|
|
return is_numeric($value) ? (float)$value : null;
|
|
}
|
|
|
|
private static function getChukuBaseSql()
|
|
{
|
|
return "
|
|
WITH PageData AS (
|
|
SELECT
|
|
ROW_NUMBER() OVER (ORDER BY x.[日期] DESC) AS RowID,
|
|
x.[出库序号] AS chuku_id,
|
|
x.[入库序号] AS ruku_id,
|
|
x.[票号] AS ticket_no,
|
|
x.[日期] AS date,
|
|
x.[库房] AS warehouse,
|
|
x.[批号] AS batch_no,
|
|
x.[效期] AS expire_date,
|
|
x.[数量] AS quantity,
|
|
x.[单价] AS unit_price,
|
|
x.[金额] AS amount,
|
|
x.[进价] AS purchase_price,
|
|
x.[毛利] AS profit,
|
|
x.[操作员] AS operator,
|
|
x.[业务员] AS salesman,
|
|
x.[提货人] AS receiver,
|
|
x.[摘要] AS summary,
|
|
x.[供方序号] AS supplier_id,
|
|
k.[供方全称] AS supplier_name,
|
|
k.[简称] AS supplier_short_name,
|
|
x.[销方序号] AS customer_id,
|
|
f.[销方全称] AS customer_name,
|
|
f.[简称] AS customer_short_name,
|
|
f.[地区] AS customer_region,
|
|
x.[产品序号] AS product_id,
|
|
p.[产品名称] AS product_name,
|
|
p.[规格] AS product_spec,
|
|
p.[产地] AS product_origin,
|
|
p.[单位] AS product_unit,
|
|
p.[生产厂家] AS manufacturer
|
|
FROM [ywsj].[dbo].[vckmx] x
|
|
JOIN [ywsj].[dbo].[bgfkh] k ON x.[供方序号] = k.[供方序号]
|
|
JOIN [ywsj].[dbo].[bcp] p ON x.[产品序号] = p.[产品序号]
|
|
JOIN [ywsj].[dbo].[bxfkh] f ON x.[销方序号] = f.[销方序号]
|
|
WHERE 1=1
|
|
{date_condition}
|
|
{isolation_condition}
|
|
)
|
|
SELECT
|
|
chuku_id, ruku_id, ticket_no, date, warehouse, batch_no, expire_date,
|
|
quantity, unit_price, amount, purchase_price, profit,
|
|
operator, salesman, receiver, summary,
|
|
supplier_id, supplier_name, supplier_short_name,
|
|
customer_id, customer_name, customer_short_name, customer_region,
|
|
product_id, product_name, product_spec, product_origin,
|
|
product_unit, manufacturer
|
|
FROM PageData
|
|
WHERE RowID BETWEEN :start_row AND :end_row
|
|
ORDER BY RowID
|
|
";
|
|
}
|
|
|
|
private static function getChukuCountSql()
|
|
{
|
|
return "
|
|
SELECT COUNT(*) AS total
|
|
FROM [ywsj].[dbo].[vckmx] x
|
|
JOIN [ywsj].[dbo].[bgfkh] k ON x.[供方序号] = k.[供方序号]
|
|
JOIN [ywsj].[dbo].[bcp] p ON x.[产品序号] = p.[产品序号]
|
|
JOIN [ywsj].[dbo].[bxfkh] f ON x.[销方序号] = f.[销方序号]
|
|
WHERE 1=1
|
|
{date_condition}
|
|
{isolation_condition}
|
|
";
|
|
}
|
|
|
|
private static function getRukuBaseSql()
|
|
{
|
|
return "
|
|
WITH PageData AS (
|
|
SELECT
|
|
ROW_NUMBER() OVER (ORDER BY x.[日期] DESC) AS RowID,
|
|
x.[入库序号] AS ruku_id,
|
|
x.[票号] AS ticket_no,
|
|
x.[日期] AS date,
|
|
x.[库房] AS warehouse,
|
|
x.[批号] AS batch_no,
|
|
x.[效期] AS expire_date,
|
|
x.[数量] AS quantity,
|
|
x.[单价] AS unit_price,
|
|
x.[金额] AS amount,
|
|
x.[进价] AS purchase_price,
|
|
x.[库房数量] AS warehouse_qty,
|
|
x.[操作员] AS operator,
|
|
x.[采购员] AS purchaser,
|
|
x.[负责人] AS responsible_person,
|
|
x.[来货单号] AS arrival_no,
|
|
x.[发票单号] AS invoice_no,
|
|
x.[摘要] AS summary,
|
|
x.[供方序号] AS supplier_id,
|
|
k.[供方全称] AS supplier_name,
|
|
k.[简称] AS supplier_short_name,
|
|
x.[产品序号] AS product_id,
|
|
p.[产品名称] AS product_name,
|
|
p.[规格] AS product_spec,
|
|
p.[产地] AS product_origin,
|
|
p.[单位] AS product_unit,
|
|
p.[生产厂家] AS manufacturer
|
|
FROM [ywsj].[dbo].[vrkmx] x
|
|
JOIN [ywsj].[dbo].[bgfkh] k ON x.[供方序号] = k.[供方序号]
|
|
JOIN [ywsj].[dbo].[bcp] p ON x.[产品序号] = p.[产品序号]
|
|
WHERE 1=1
|
|
{date_condition}
|
|
{isolation_condition}
|
|
)
|
|
SELECT
|
|
ruku_id, ticket_no, date, warehouse, batch_no, expire_date,
|
|
quantity, unit_price, amount, purchase_price, warehouse_qty,
|
|
operator, purchaser, responsible_person, arrival_no, invoice_no,
|
|
summary, supplier_id, supplier_name, supplier_short_name,
|
|
product_id, product_name, product_spec, product_origin,
|
|
product_unit, manufacturer
|
|
FROM PageData
|
|
WHERE RowID BETWEEN :start_row AND :end_row
|
|
ORDER BY RowID
|
|
";
|
|
}
|
|
|
|
private static function getRukuCountSql()
|
|
{
|
|
return "
|
|
SELECT COUNT(*) AS total
|
|
FROM [ywsj].[dbo].[vrkmx] x
|
|
JOIN [ywsj].[dbo].[bgfkh] k ON x.[供方序号] = k.[供方序号]
|
|
JOIN [ywsj].[dbo].[bcp] p ON x.[产品序号] = p.[产品序号]
|
|
WHERE 1=1
|
|
{date_condition}
|
|
{isolation_condition}
|
|
";
|
|
}
|
|
}
|