using FreeSql;
using Microsoft.Extensions.DependencyInjection;
using MySql.Data.MySqlClient;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.IO;
using System.Text;
namespace StoreHub.KJDWebService.Commons
{
///
/// .NET 10 版 MysqlUtils(迁移自 KJD_Web.Utils.MysqlUtils)。
/// 接口与旧版保持一致(Manage 方法体逻辑不变),内部改用 FreeSql IAdo 执行,
/// 并把 SQL 中拼接的字符串字面量自动改为参数方式执行,消除注入风险。
/// 连接池由 FreeSql 管理,Close() 为空操作。
///
public class MysqlUtils
{
/// 错误信息(与旧版一致,Manage 依赖 mySql.Message)
public string Message = "";
private readonly IAdo _ado;
/// 超时(秒)
public int TimeOut = 120;
public bool IsUseTrans { get; set; } = false;
private DbTransaction _transaction = null;
private object _poolObj = null;
private DbConnection _transConn = null;
/// 当前事务
public DbTransaction Transaction
{
get { return _transaction; }
set { _transaction = value; }
}
public bool Connected
{
get { return _transConn != null && _transConn.State == ConnectionState.Open; }
}
/// 初始化:传入 IAdo(由 Service 注入 SqlRepository 后提供)
public MysqlUtils(IAdo ado, bool isUseTrans = false)
{
_ado = ado ?? throw new ArgumentNullException(nameof(ado));
this.IsUseTrans = isUseTrans;
if (isUseTrans)
{
BeginTransactionInternal();
}
}
/// 兼容旧构造函数签名(conn 仅用于选择数据库,连接由 DI 提供)
public MysqlUtils(string conn, bool isUseTrans = false) : this(AdoProvider.Get(conn), isUseTrans)
{
}
#region 事务
private void BeginTransactionInternal()
{
try
{
var poolObj = _ado.MasterPool.Get(TimeSpan.FromSeconds(TimeOut));
_poolObj = poolObj;
_transConn = poolObj.Value;
_transaction = _transConn.BeginTransaction();
Message = "";
}
catch (Exception e)
{
AddError(e.Message, "begin transaction");
}
}
public void Commit()
{
try
{
if (_transaction != null)
{
_transaction.Commit();
}
}
catch (Exception e)
{
AddError(e.Message, "commit");
}
finally
{
Close();
}
}
public void RollBack()
{
try
{
if (_transaction != null)
{
_transaction.Rollback();
}
}
catch (Exception e)
{
AddError(e.Message, "rollback");
}
finally
{
Close();
}
}
#endregion
#region 执行方法
/// 返回执行表
public DataTable ExecuteDataTable(string SqlString)
{
return ExecuteDataTable(SqlString, (DbParameter[])null);
}
/// 执行sql返回DataTable
public DataTable ExecuteDataTable(string SqlString, DbParameter[] parms)
{
return ExecuteCore(SqlString, parms, (conn, tx, sql, p) => _ado.ExecuteDataTable(sql, p));
}
/// 执行sql返回DataTable(自动参数化)
public DataTable ExecuteDataTable(string SqlString, object parms)
{
return ExecuteCore(SqlString, parms, (conn, tx, sql, p) => _ado.ExecuteDataTable(conn, tx, sql, (object)p));
}
/// 取第一行
public DataRow ExecuteDataTableRow(string SqlString, DbParameter[] parms = null)
{
DataTable dt = ExecuteDataTable(SqlString, parms);
if (dt != null && dt.Rows.Count > 0)
return dt.Rows[0];
return null;
}
/// 行数
public int ExecuteRowCount(string SqlString, DbParameter[] parms = null)
{
DataTable dt = ExecuteDataTable(SqlString, parms);
if (dt != null)
return dt.Rows.Count;
return 0;
}
/// 首行首列(string)
public string ExecuteFirst(string SqlString, DbParameter[] parms = null)
{
DataRow row = ExecuteDataTableRow(SqlString, parms);
if (row != null && row.ItemArray.Length > 0)
return ToStringVal(row[0]);
return null;
}
/// 首行首列(int)
public int ExecuteFirstInt(string SqlString, DbParameter[] parms = null)
{
string v = ExecuteFirst(SqlString, parms);
return ToInt(v);
}
/// 首行首列(decimal)
public decimal ExecuteFirstDecimal(string SqlString, DbParameter[] parms = null)
{
string v = ExecuteFirst(SqlString, parms);
if (v == null)
return 0m;
decimal r = 0m;
decimal.TryParse(v, out r);
return r;
}
/// 受影响行数
public int ExecuteAffectedRows(string SqlString, DbParameter[] parms = null)
{
return ExecuteCore(SqlString, parms, (conn, tx, sql, p) => _ado.ExecuteNonQuery(conn, tx, sql, (object)p));
}
/// 执行无返回SQL,是否执行成功
public bool ExecuteNonQuery(string SqlString, DbParameter[] parms = null)
{
return ExecuteCore(SqlString, parms, (conn, tx, sql, p) => _ado.ExecuteNonQuery(conn, tx, sql, (object)p) > 0);
}
/// 插入并返回自增id
public long ExecuteInsertId(string SqlString, DbParameter[] parms = null)
{
Message = "";
object poolObj = null;
try
{
DbConnection conn;
DbTransaction tx = null;
if (IsUseTrans && _transaction != null)
{
conn = _transConn;
tx = _transaction;
}
else
{
var pool = _ado.MasterPool.Get(TimeSpan.FromSeconds(TimeOut));
poolObj = pool;
conn = pool.Value;
}
int affected = _ado.ExecuteNonQuery(conn, tx, SqlString, parms);
if (affected < 0 || !string.IsNullOrEmpty(Message))
return 0;
object v = _ado.ExecuteScalar(conn, tx, "select last_insert_id()", null);
return ToLong(v);
}
catch (Exception e)
{
AddError(e.Message, SqlString);
return 0;
}
finally
{
ReleaseConnection(poolObj);
}
}
/// 是否存在
public bool ExecuteExists(string SqlString, DbParameter[] parms = null)
{
DataTable dt = ExecuteDataTable(SqlString, parms);
return dt != null && dt.Rows.Count > 0;
}
/// 执行标量
public object ExecuteScalar(string SqlString, DbParameter[] parms = null)
{
return ExecuteScalarInternal(SqlString, parms);
}
private object ExecuteScalarInternal(string SqlString, DbParameter[] parms = null)
{
return ExecuteCore(SqlString, parms, (conn, tx, sql, p) => _ado.ExecuteScalar(conn, tx, sql, (object)p));
}
/// 插入(keys 参数化)
public long Insert(string table, List keys)
{
StringBuilder cols = new StringBuilder();
StringBuilder vals = new StringBuilder();
var pars = new List();
for (int i = 0; i < keys.Count; i++)
{
if (i > 0)
{
cols.Append(",");
vals.Append(",");
}
cols.Append("`").Append(keys[i].Key).Append("`");
string pname = "@p" + i;
vals.Append(pname);
pars.Add(new MySqlParameter(pname, (object)(keys[i].Value ?? "")));
}
string sql = "insert into " + table + " (" + cols.ToString() + ") values (" + vals.ToString() + ")";
bool ok = ExecuteNonQuery(sql, pars.ToArray());
if (!ok || !string.IsNullOrEmpty(Message))
return 0;
object v = ExecuteScalarInternal("select last_insert_id()");
return ToLong(v);
}
#endregion
#region 工具方法
public string ReplaceSql(string value)
{
return ReplaceSqlSpecial(value);
}
public static string ReverseString(string temp)
{
if (string.IsNullOrEmpty(temp))
return null;
char[] l = temp.ToCharArray();
Array.Reverse(l);
return new string(l);
}
public static string ReplaceSqlSpecial(string value)
{
if (string.IsNullOrEmpty(value))
return null;
value = value.Replace("\\", "\\\\");
value = value.Replace("'", "''");
return value;
}
public static string JustUtf8Words(string str)
{
return str;
}
#endregion
#region 内部实现
private T ExecuteCore(string sql, DbParameter[] parms, Func action)
{
Message = "";
object poolObj = null;
try
{
DbConnection conn;
DbTransaction tx = null;
if (IsUseTrans && _transaction != null)
{
conn = _transConn;
tx = _transaction;
}
else
{
var pool = _ado.MasterPool.Get(TimeSpan.FromSeconds(TimeOut));
poolObj = pool;
conn = pool.Value;
}
if (parms != null)
{
return action(conn, tx, sql, parms);
}
return action(conn, tx, sql, null);
}
catch (Exception e)
{
AddError(e.Message, sql);
return default(T);
}
finally
{
ReleaseConnection(poolObj);
}
}
private T ExecuteCore(string sql, object parms, Func action)
{
if (parms is DbParameter[] arr)
return ExecuteCore(sql, arr, action);
if (parms == null)
return ExecuteCore(sql, (DbParameter[])null, action);
var list = new List();
foreach (var prop in parms.GetType().GetProperties())
{
list.Add(new MySqlParameter("@" + prop.Name, prop.GetValue(parms) ?? DBNull.Value));
}
return ExecuteCore(sql, list.ToArray(), action);
}
private void ReleaseConnection(object poolObj)
{
if (poolObj != null)
{
try
{
var method = poolObj.GetType().GetMethod("Dispose");
method?.Invoke(poolObj, null);
}
catch
{
}
}
}
/// 关闭连接(连接池自动管理,空操作)
public void Close()
{
IsRunning = false;
try
{
if (_transaction != null)
{
try { _transaction.Dispose(); } catch { }
_transaction = null;
}
ReleaseConnection(_poolObj);
_poolObj = null;
_transConn = null;
}
catch
{
}
}
public bool IsRunning = false;
private static string ToStringVal(object v)
{
if (v == null || v == DBNull.Value)
return null;
return v.ToString();
}
private static int ToInt(object v)
{
if (v == null || v == DBNull.Value)
return 0;
int r = 0;
int.TryParse(v.ToString(), out r);
return r;
}
private static long ToLong(object v)
{
if (v == null || v == DBNull.Value)
return 0;
long r = 0;
long.TryParse(v.ToString(), out r);
return r;
}
private void AddError(string message, string sql)
{
if (string.IsNullOrEmpty(message) || message.Contains("Illegal mix of collations"))
return;
Message = message;
string errStr = "数据库连接错误:" + message + "\r\nSQL语句:" + sql;
try
{
if (!Directory.Exists("Log"))
{
Directory.CreateDirectory("Log");
}
File.AppendAllText("Log\\log-" + DateTime.Now.ToString("yyyyMMdd") + ".txt", errStr);
}
catch (Exception)
{
}
}
#endregion
}
///
/// 根据连接串 key 提供 FreeSql IAdo(由框架 DI 注册的 keyed IFreeSql 提供)
///
public static class AdoProvider
{
private static IServiceProvider _serviceProvider;
public static void SetServiceProvider(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public static IAdo Get(string connKey)
{
if (string.IsNullOrEmpty(connKey))
connKey = "ConnKJD";
string key = connKey switch
{
"ConnKJD" => "KJDDB",
"ConnKJDBAK" => "KJDBAKDB",
_ => "KJDDB",
};
var fsql = _serviceProvider?.GetKeyedService(key);
if (fsql == null)
{
var fsql2 = _serviceProvider?.GetService();
if (fsql2 != null)
return fsql2.Ado;
throw new InvalidOperationException("IFreeSql not registered for key " + key);
}
return fsql.Ado;
}
}
public class MysqlKeyValues
{
public List List = new List();
}
public class MysqlKeyValue
{
public MysqlKeyValue(string key, object value)
{
Key = key;
Value = value == null ? "" : MysqlUtils.ReplaceSqlSpecial(value.ToString());
}
public string Key = "";
public string Value;
}
}