mirror of
https://gitee.com/ThingsGateway/ThingsGateway.git
synced 2025-10-22 11:33:07 +08:00
Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
926eced724 | ||
![]() |
f7f8802272 | ||
![]() |
c6910dff02 | ||
![]() |
ad299d0dbb | ||
![]() |
8b124d1050 | ||
![]() |
ff41080dbd |
@@ -28,7 +28,7 @@ public class UserIdProvider : IUserIdProvider
|
||||
|
||||
if (UserId > 0)
|
||||
{
|
||||
return $"{UserId}{SysHub.Separate}{YitIdHelper.NextId()}";//返回用户ID
|
||||
return $"{UserId}{SysHub.Separate}{CommonUtils.GetSingleId()}";//返回用户ID
|
||||
}
|
||||
|
||||
return connection.ConnectionId;
|
||||
|
@@ -117,6 +117,25 @@ public class SugarAopService : ISugarAopService
|
||||
db.Aop.DataExecuted = (value, entity) =>
|
||||
{
|
||||
};
|
||||
|
||||
|
||||
db.Aop.OnLogExecuted = (sql, pars) =>
|
||||
{
|
||||
//执行时间超过1秒
|
||||
if (db.Ado.SqlExecutionTime.TotalSeconds > 1)
|
||||
{
|
||||
//代码CS文件名
|
||||
var fileName = db.Ado.SqlStackTrace.FirstFileName;
|
||||
//代码行数
|
||||
var fileLine = db.Ado.SqlStackTrace.FirstLine;
|
||||
//方法名
|
||||
var FirstMethodName = db.Ado.SqlStackTrace.FirstMethodName;
|
||||
|
||||
DbContext.WriteLog($"{fileName}-{FirstMethodName}-{fileLine} 执行时间超过1秒");
|
||||
DbContext.WriteLogWithSql(UtilMethods.GetNativeSql(sql, pars));
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -37,7 +37,7 @@ public sealed class SqlSugarOption : ConnectionConfig
|
||||
/// <summary>
|
||||
/// 是否控制台显示Sql语句
|
||||
/// </summary>
|
||||
public bool IsShowSql { get; set; }
|
||||
public bool? IsShowSql { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 更新数据
|
||||
|
@@ -98,6 +98,21 @@ public class Startup : AppStartup
|
||||
CodeFirstUtils.CodeFirst(fullName!);//CodeFirst
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
using var db = DbContext.GetDB<SysOperateLog>();
|
||||
if (db.CurrentConnectionConfig.DbType == SqlSugar.DbType.Sqlite)
|
||||
{
|
||||
if (!db.DbMaintenance.IsAnyIndex("idx_operatelog_optime_date"))
|
||||
{
|
||||
var indexsql = "CREATE INDEX idx_operatelog_optime_date ON sys_operatelog(strftime('%Y-%m-%d', OpTime));";
|
||||
db.Ado.ExecuteCommand(indexsql);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
|
||||
//删除在线用户统计
|
||||
var verificatInfoService = App.RootServices.GetService<IVerificatInfoService>();
|
||||
verificatInfoService.RemoveAllClientId();
|
||||
|
@@ -25,7 +25,7 @@ public static class ILoggerExtensions
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="properties">建议使用 ConcurrentDictionary 类型</param>
|
||||
/// <returns></returns>
|
||||
public static IDisposable ScopeContext(this ILogger logger, IDictionary<object, object> properties)
|
||||
public static IDisposable ScopeContext(this ILogger logger, IDictionary<string, object> properties)
|
||||
{
|
||||
if (logger == null) throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
|
@@ -26,11 +26,11 @@ public static class LogContextExtensions
|
||||
/// <param name="key">键</param>
|
||||
/// <param name="value">值</param>
|
||||
/// <returns></returns>
|
||||
public static LogContext Set(this LogContext logContext, object key, object value)
|
||||
public static LogContext Set(this LogContext logContext, string key, object value)
|
||||
{
|
||||
if (logContext == null || key == null) return logContext;
|
||||
|
||||
logContext.Properties ??= new Dictionary<object, object>();
|
||||
logContext.Properties ??= new Dictionary<string, object>();
|
||||
|
||||
logContext.Properties.Remove(key);
|
||||
logContext.Properties.Add(key, value);
|
||||
@@ -43,7 +43,7 @@ public static class LogContextExtensions
|
||||
/// <param name="logContext"></param>
|
||||
/// <param name="properties"></param>
|
||||
/// <returns></returns>
|
||||
public static LogContext SetRange(this LogContext logContext, IDictionary<object, object> properties)
|
||||
public static LogContext SetRange(this LogContext logContext, IDictionary<string, object> properties)
|
||||
{
|
||||
if (logContext == null
|
||||
|| properties == null
|
||||
@@ -63,7 +63,7 @@ public static class LogContextExtensions
|
||||
/// <param name="logContext"></param>
|
||||
/// <param name="key">键</param>
|
||||
/// <returns></returns>
|
||||
public static object Get(this LogContext logContext, object key)
|
||||
public static object Get(this LogContext logContext, string key)
|
||||
{
|
||||
if (logContext == null
|
||||
|| key == null
|
||||
@@ -80,7 +80,7 @@ public static class LogContextExtensions
|
||||
/// <param name="logContext"></param>
|
||||
/// <param name="key">键</param>
|
||||
/// <returns></returns>
|
||||
public static T Get<T>(this LogContext logContext, object key)
|
||||
public static T Get<T>(this LogContext logContext, string key)
|
||||
{
|
||||
var value = logContext.Get(key);
|
||||
return value.ChangeType<T>();
|
||||
|
@@ -84,7 +84,7 @@ public static class StringLoggingExtensions
|
||||
/// <param name="message"></param>
|
||||
/// <param name="properties">建议使用 ConcurrentDictionary 类型</param>
|
||||
/// <returns></returns>
|
||||
public static StringLoggingPart ScopeContext(this string message, IDictionary<object, object> properties)
|
||||
public static StringLoggingPart ScopeContext(this string message, IDictionary<string, object> properties)
|
||||
{
|
||||
return StringLoggingPart.Default().SetMessage(message).ScopeContext(properties);
|
||||
}
|
||||
|
@@ -20,7 +20,7 @@ public sealed class LogContext : IDisposable
|
||||
/// <summary>
|
||||
/// 日志上下文数据
|
||||
/// </summary>
|
||||
public IDictionary<object, object> Properties { get; set; }
|
||||
public IDictionary<string, object> Properties { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 原生日志上下文数据
|
||||
|
@@ -96,7 +96,7 @@ public sealed partial class StringLoggingPart
|
||||
/// </summary>
|
||||
/// <param name="properties">建议使用 ConcurrentDictionary 类型</param>
|
||||
/// <returns></returns>
|
||||
public StringLoggingPart ScopeContext(IDictionary<object, object> properties)
|
||||
public StringLoggingPart ScopeContext(IDictionary<string, object> properties)
|
||||
{
|
||||
if (properties == null) return this;
|
||||
LogContext = new LogContext { Properties = properties };
|
||||
|
@@ -59,7 +59,7 @@ public static class Log
|
||||
/// </summary>
|
||||
/// <param name="properties">建议使用 ConcurrentDictionary 类型</param>
|
||||
/// <returns></returns>
|
||||
public static (ILogger logger, IDisposable scope) ScopeContext(IDictionary<object, object> properties)
|
||||
public static (ILogger logger, IDisposable scope) ScopeContext(IDictionary<string, object> properties)
|
||||
{
|
||||
return GetLogger(StringLoggingPart.Default().ScopeContext(properties));
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
//下载文件
|
||||
export function blazor_downloadFile(url, fileName, dtoObject) {
|
||||
export async function blazor_downloadFile(url, fileName, dtoObject) {
|
||||
const params = new URLSearchParams();
|
||||
|
||||
// 将 dtoObject 的属性添加到 URLSearchParams 中
|
||||
@@ -12,97 +12,92 @@ export function blazor_downloadFile(url, fileName, dtoObject) {
|
||||
// 构建完整的 URL
|
||||
const fullUrl = `${url}?${params.toString()}`;
|
||||
|
||||
// 发起 fetch 请求
|
||||
fetch(fullUrl)
|
||||
.then(response => {
|
||||
// 获取响应头中的 content-disposition
|
||||
const dispositionHeader = response.headers.get('content-disposition');
|
||||
let resolvedFileName = fileName;
|
||||
try {
|
||||
// 发起 fetch 请求
|
||||
const response = await fetch(fullUrl);
|
||||
|
||||
if (dispositionHeader) {
|
||||
// 解析出文件名
|
||||
const match = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/.exec(dispositionHeader);
|
||||
const serverFileName = match && match[1] ? match[1].replace(/['"]/g, '') : null;
|
||||
if (serverFileName) {
|
||||
resolvedFileName = serverFileName;
|
||||
}
|
||||
// 获取响应头中的 content-disposition
|
||||
const dispositionHeader = response.headers.get('content-disposition');
|
||||
let resolvedFileName = fileName;
|
||||
|
||||
if (dispositionHeader) {
|
||||
// 解析出文件名
|
||||
const match = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/.exec(dispositionHeader);
|
||||
const serverFileName = match && match[1] ? match[1].replace(/['"]/g, '') : null;
|
||||
if (serverFileName) {
|
||||
resolvedFileName = serverFileName;
|
||||
}
|
||||
}
|
||||
|
||||
// 将响应转换为 blob 对象
|
||||
return response.blob().then(blob => {
|
||||
// 创建临时的文件 URL
|
||||
const fileUrl = window.URL.createObjectURL(blob);
|
||||
// 将响应转换为 blob 对象
|
||||
const blob = await response.blob();
|
||||
|
||||
// 创建一个 <a> 元素并设置下载链接和文件名
|
||||
const anchorElement = document.createElement('a');
|
||||
anchorElement.href = fileUrl;
|
||||
anchorElement.download = resolvedFileName;
|
||||
anchorElement.style.display = 'none';
|
||||
// 创建临时的文件 URL
|
||||
const fileUrl = window.URL.createObjectURL(blob);
|
||||
|
||||
// 将 <a> 元素添加到 body 中并触发下载
|
||||
document.body.appendChild(anchorElement);
|
||||
anchorElement.click();
|
||||
document.body.removeChild(anchorElement);
|
||||
// 创建一个 <a> 元素并设置下载链接和文件名
|
||||
const anchorElement = document.createElement('a');
|
||||
anchorElement.href = fileUrl;
|
||||
anchorElement.download = resolvedFileName;
|
||||
anchorElement.style.display = 'none';
|
||||
|
||||
// 撤销临时的文件 URL
|
||||
window.URL.revokeObjectURL(fileUrl);
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('DownFile error ', error);
|
||||
});
|
||||
// 将 <a> 元素添加到 body 中并触发下载
|
||||
document.body.appendChild(anchorElement);
|
||||
anchorElement.click();
|
||||
document.body.removeChild(anchorElement);
|
||||
|
||||
// 撤销临时的文件 URL
|
||||
window.URL.revokeObjectURL(fileUrl);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('DownFile error ', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//下载文件
|
||||
export function postJson_downloadFile(url, fileName, jsonBody) {
|
||||
const params = new URLSearchParams();
|
||||
export async function postJson_downloadFile(url, fileName, jsonBody) {
|
||||
|
||||
|
||||
// 发起 fetch 请求
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: jsonBody
|
||||
})
|
||||
.then(response => {
|
||||
// 获取响应头中的 content-disposition
|
||||
const dispositionHeader = response.headers.get('content-disposition');
|
||||
let resolvedFileName = fileName;
|
||||
|
||||
if (dispositionHeader) {
|
||||
// 解析出文件名
|
||||
const match = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/.exec(dispositionHeader);
|
||||
const serverFileName = match && match[1] ? match[1].replace(/['"]/g, '') : null;
|
||||
if (serverFileName) {
|
||||
resolvedFileName = serverFileName;
|
||||
}
|
||||
}
|
||||
|
||||
// 将响应转换为 blob 对象
|
||||
return response.blob().then(blob => {
|
||||
// 创建临时的文件 URL
|
||||
const fileUrl = window.URL.createObjectURL(blob);
|
||||
|
||||
// 创建一个 <a> 元素并设置下载链接和文件名
|
||||
const anchorElement = document.createElement('a');
|
||||
anchorElement.href = fileUrl;
|
||||
anchorElement.download = resolvedFileName;
|
||||
anchorElement.style.display = 'none';
|
||||
|
||||
// 将 <a> 元素添加到 body 中并触发下载
|
||||
document.body.appendChild(anchorElement);
|
||||
anchorElement.click();
|
||||
document.body.removeChild(anchorElement);
|
||||
|
||||
// 撤销临时的文件 URL
|
||||
window.URL.revokeObjectURL(fileUrl);
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('downfile error ', error);
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: jsonBody
|
||||
});
|
||||
|
||||
const dispositionHeader = response.headers.get('content-disposition');
|
||||
let resolvedFileName = fileName;
|
||||
|
||||
if (dispositionHeader) {
|
||||
const match = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/.exec(dispositionHeader);
|
||||
const serverFileName = match && match[1] ? match[1].replace(/['"]/g, '') : null;
|
||||
if (serverFileName) {
|
||||
resolvedFileName = serverFileName;
|
||||
}
|
||||
}
|
||||
|
||||
const blob = await response.blob();
|
||||
const fileUrl = window.URL.createObjectURL(blob);
|
||||
|
||||
const anchorElement = document.createElement('a');
|
||||
anchorElement.href = fileUrl;
|
||||
anchorElement.download = resolvedFileName;
|
||||
anchorElement.style.display = 'none';
|
||||
|
||||
document.body.appendChild(anchorElement);
|
||||
anchorElement.click();
|
||||
document.body.removeChild(anchorElement);
|
||||
|
||||
window.URL.revokeObjectURL(fileUrl);
|
||||
|
||||
return true; // 唯一新增的返回值
|
||||
} catch (error) {
|
||||
console.error('downfile error ', error);
|
||||
}
|
||||
}
|
||||
|
@@ -1,9 +1,9 @@
|
||||
<Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<PluginVersion>10.6.23</PluginVersion>
|
||||
<ProPluginVersion>10.6.23</ProPluginVersion>
|
||||
<AuthenticationVersion>2.1.7</AuthenticationVersion>
|
||||
<PluginVersion>10.6.28</PluginVersion>
|
||||
<ProPluginVersion>10.6.28</ProPluginVersion>
|
||||
<AuthenticationVersion>2.1.8</AuthenticationVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
@@ -26,28 +26,53 @@ internal sealed class GatewayExportService : IGatewayExportService
|
||||
|
||||
private IJSRuntime JSRuntime { get; set; }
|
||||
|
||||
public async Task OnChannelExport(ExportFilter exportFilter)
|
||||
public async Task<bool> OnChannelExport(ExportFilter exportFilter)
|
||||
{
|
||||
await using var ajaxJS = await JSRuntime.InvokeAsync<IJSObjectReference>("import", $"/_content/ThingsGateway.Razor/js/downloadFile.js");
|
||||
string url = "api/gatewayExport/channel";
|
||||
string fileName = $"{DateTime.Now.ToFileDateTimeFormat()}.xlsx";
|
||||
await ajaxJS.InvokeVoidAsync("postJson_downloadFile", url, fileName, exportFilter.ToJsonString());
|
||||
try
|
||||
{
|
||||
|
||||
await using var ajaxJS = await JSRuntime.InvokeAsync<IJSObjectReference>("import", $"/_content/ThingsGateway.Razor/js/downloadFile.js");
|
||||
string url = "api/gatewayExport/channel";
|
||||
string fileName = $"{DateTime.Now.ToFileDateTimeFormat()}.xlsx";
|
||||
return await ajaxJS.InvokeAsync<bool>("postJson_downloadFile", url, fileName, exportFilter.ToJsonString());
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task OnDeviceExport(ExportFilter exportFilter)
|
||||
public async Task<bool> OnDeviceExport(ExportFilter exportFilter)
|
||||
{
|
||||
await using var ajaxJS = await JSRuntime.InvokeAsync<IJSObjectReference>("import", $"/_content/ThingsGateway.Razor/js/downloadFile.js");
|
||||
string url = "api/gatewayExport/device";
|
||||
string fileName = $"{DateTime.Now.ToFileDateTimeFormat()}.xlsx";
|
||||
await ajaxJS.InvokeVoidAsync("postJson_downloadFile", url, fileName, exportFilter.ToJsonString());
|
||||
try
|
||||
{
|
||||
|
||||
|
||||
await using var ajaxJS = await JSRuntime.InvokeAsync<IJSObjectReference>("import", $"/_content/ThingsGateway.Razor/js/downloadFile.js");
|
||||
string url = "api/gatewayExport/device";
|
||||
string fileName = $"{DateTime.Now.ToFileDateTimeFormat()}.xlsx";
|
||||
return await ajaxJS.InvokeAsync<bool>("postJson_downloadFile", url, fileName, exportFilter.ToJsonString());
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task OnVariableExport(ExportFilter exportFilter)
|
||||
public async Task<bool> OnVariableExport(ExportFilter exportFilter)
|
||||
{
|
||||
await using var ajaxJS = await JSRuntime.InvokeAsync<IJSObjectReference>("import", $"/_content/ThingsGateway.Razor/js/downloadFile.js");
|
||||
string url = "api/gatewayExport/variable";
|
||||
string fileName = $"{DateTime.Now.ToFileDateTimeFormat()}.xlsx";
|
||||
await ajaxJS.InvokeVoidAsync("postJson_downloadFile", url, fileName, exportFilter.ToJsonString());
|
||||
try
|
||||
{
|
||||
await using var ajaxJS = await JSRuntime.InvokeAsync<IJSObjectReference>("import", $"/_content/ThingsGateway.Razor/js/downloadFile.js");
|
||||
string url = "api/gatewayExport/variable";
|
||||
string fileName = $"{DateTime.Now.ToFileDateTimeFormat()}.xlsx";
|
||||
return await ajaxJS.InvokeAsync<bool>("postJson_downloadFile", url, fileName, exportFilter.ToJsonString());
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -35,18 +35,30 @@ public sealed class HybridGatewayExportService : IGatewayExportService
|
||||
|
||||
}
|
||||
|
||||
public async Task OnChannelExport(ExportFilter exportFilter)
|
||||
public async Task<bool> OnChannelExport(ExportFilter exportFilter)
|
||||
{
|
||||
exportFilter.QueryPageOptions.IsPage = false;
|
||||
exportFilter.QueryPageOptions.IsVirtualScroll = false;
|
||||
try
|
||||
{
|
||||
|
||||
var sheets = await _channelService.ExportChannelAsync(exportFilter).ConfigureAwait(false);
|
||||
var path = await _importExportService.CreateFileAsync<Device>(sheets, "Channel", false).ConfigureAwait(false);
|
||||
|
||||
Open(path);
|
||||
exportFilter.QueryPageOptions.IsPage = false;
|
||||
exportFilter.QueryPageOptions.IsVirtualScroll = false;
|
||||
|
||||
var sheets = await _channelService.ExportChannelAsync(exportFilter).ConfigureAwait(false);
|
||||
var path = await _importExportService.CreateFileAsync<Device>(sheets, "Channel", false).ConfigureAwait(false);
|
||||
|
||||
Open(path);
|
||||
return true;
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static void Open(string path)
|
||||
private static bool Open(string path)
|
||||
{
|
||||
path = System.IO.Path.GetDirectoryName(path); // Ensure the path is absolute
|
||||
|
||||
@@ -63,25 +75,47 @@ public sealed class HybridGatewayExportService : IGatewayExportService
|
||||
{
|
||||
System.Diagnostics.Process.Start("open", path);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task OnDeviceExport(ExportFilter exportFilter)
|
||||
public async Task<bool> OnDeviceExport(ExportFilter exportFilter)
|
||||
{
|
||||
exportFilter.QueryPageOptions.IsPage = false;
|
||||
exportFilter.QueryPageOptions.IsVirtualScroll = false;
|
||||
var sheets = await _deviceService.ExportDeviceAsync(exportFilter).ConfigureAwait(false);
|
||||
var path = await _importExportService.CreateFileAsync<Device>(sheets, "Device", false).ConfigureAwait(false);
|
||||
Open(path);
|
||||
try
|
||||
{
|
||||
|
||||
exportFilter.QueryPageOptions.IsPage = false;
|
||||
exportFilter.QueryPageOptions.IsVirtualScroll = false;
|
||||
var sheets = await _deviceService.ExportDeviceAsync(exportFilter).ConfigureAwait(false);
|
||||
var path = await _importExportService.CreateFileAsync<Device>(sheets, "Device", false).ConfigureAwait(false);
|
||||
Open(path);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task OnVariableExport(ExportFilter exportFilter)
|
||||
public async Task<bool> OnVariableExport(ExportFilter exportFilter)
|
||||
{
|
||||
exportFilter.QueryPageOptions.IsPage = false;
|
||||
exportFilter.QueryPageOptions.IsVirtualScroll = false;
|
||||
var sheets = await _variableService.ExportVariableAsync(exportFilter).ConfigureAwait(false);
|
||||
var path = await _importExportService.CreateFileAsync<Variable>(sheets, "Variable", false).ConfigureAwait(false);
|
||||
Open(path);
|
||||
try
|
||||
{
|
||||
|
||||
exportFilter.QueryPageOptions.IsPage = false;
|
||||
exportFilter.QueryPageOptions.IsVirtualScroll = false;
|
||||
var sheets = await _variableService.ExportVariableAsync(exportFilter).ConfigureAwait(false);
|
||||
var path = await _importExportService.CreateFileAsync<Variable>(sheets, "Variable", false).ConfigureAwait(false);
|
||||
Open(path);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -12,7 +12,7 @@ namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
public interface IGatewayExportService
|
||||
{
|
||||
Task OnChannelExport(ExportFilter exportFilter);
|
||||
Task OnDeviceExport(ExportFilter exportFilter);
|
||||
Task OnVariableExport(ExportFilter exportFilter);
|
||||
Task<bool> OnChannelExport(ExportFilter exportFilter);
|
||||
Task<bool> OnDeviceExport(ExportFilter exportFilter);
|
||||
Task<bool> OnVariableExport(ExportFilter exportFilter);
|
||||
}
|
||||
|
@@ -518,7 +518,7 @@ internal sealed class PluginService : IPluginService
|
||||
{
|
||||
var fileInfo = new FileInfo(path);
|
||||
if (fileInfo.Exists)
|
||||
fileInfo.MoveTo($"{path}{YitIdHelper.NextId()}{DelEx}", true);
|
||||
fileInfo.MoveTo($"{path}{CommonUtils.GetSingleId()}{DelEx}", true);
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
|
@@ -18,6 +18,8 @@ using SqlSugar;
|
||||
|
||||
using System.Reflection;
|
||||
|
||||
using ThingsGateway.Authentication;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
[AppStartup(-100)]
|
||||
@@ -25,6 +27,9 @@ public class Startup : AppStartup
|
||||
{
|
||||
public void Configure(IServiceCollection services)
|
||||
{
|
||||
|
||||
ProAuthentication.TryGetAuthorizeInfo(out var authorizeInfo);
|
||||
|
||||
services.AddConfigurableOptions<ChannelThreadOptions>();
|
||||
services.AddConfigurableOptions<GatewayLogOptions>();
|
||||
services.AddConfigurableOptions<RpcLogOptions>();
|
||||
@@ -125,8 +130,35 @@ public class Startup : AppStartup
|
||||
}
|
||||
catch { }
|
||||
|
||||
try
|
||||
{
|
||||
using var db = DbContext.GetDB<BackendLog>();
|
||||
if (db.CurrentConnectionConfig.DbType == SqlSugar.DbType.Sqlite)
|
||||
{
|
||||
|
||||
if (!db.DbMaintenance.IsAnyIndex("idx_backendlog_logtime_date"))
|
||||
{
|
||||
var indexsql = "CREATE INDEX idx_backendlog_logtime_date ON backend_log(strftime('%Y-%m-%d', LogTime));";
|
||||
db.Ado.ExecuteCommand(indexsql);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
try
|
||||
{
|
||||
using var db = DbContext.GetDB<RpcLog>();
|
||||
if (db.CurrentConnectionConfig.DbType == SqlSugar.DbType.Sqlite)
|
||||
{
|
||||
|
||||
if (!db.DbMaintenance.IsAnyIndex("idx_rpclog_logtime_date"))
|
||||
{
|
||||
var indexsql = "CREATE INDEX idx_rpclog_logtime_date ON rpc_log(strftime('%Y-%m-%d', LogTime));";
|
||||
db.Ado.ExecuteCommand(indexsql);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
serviceProvider.GetService<IHostApplicationLifetime>().ApplicationStarted.Register(() =>
|
||||
{
|
||||
|
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<Import Project="$(SolutionDir)Version.props" />
|
||||
<Import Project="$(SolutionDir)PackNuget.props" />
|
||||
|
@@ -10,6 +10,7 @@
|
||||
|
||||
using Mapster;
|
||||
|
||||
using ThingsGateway.Admin.Application;
|
||||
using ThingsGateway.Gateway.Application;
|
||||
|
||||
using Yitter.IdGenerator;
|
||||
@@ -54,14 +55,14 @@ public partial class ChannelCopyComponent
|
||||
for (int i = 0; i < CopyCount; i++)
|
||||
{
|
||||
Channel channel = Model.Adapt<Channel>();
|
||||
channel.Id = YitIdHelper.NextId();
|
||||
channel.Id = CommonUtils.GetSingleId();
|
||||
channel.Name = $"{CopyChannelNamePrefix}{CopyChannelNameSuffixNumber + i}";
|
||||
|
||||
int index = 0;
|
||||
foreach (var item in Devices)
|
||||
{
|
||||
Device device = item.Key.Adapt<Device>();
|
||||
device.Id = YitIdHelper.NextId();
|
||||
device.Id = CommonUtils.GetSingleId();
|
||||
device.Name = $"{channel.Name}_{CopyDeviceNamePrefix}{CopyDeviceNameSuffixNumber + (index++)}";
|
||||
device.ChannelId = channel.Id;
|
||||
List<Variable> variables = new();
|
||||
@@ -69,7 +70,7 @@ public partial class ChannelCopyComponent
|
||||
foreach (var variable in item.Value)
|
||||
{
|
||||
Variable v = variable.Adapt<Variable>();
|
||||
v.Id = YitIdHelper.NextId();
|
||||
v.Id = CommonUtils.GetSingleId();
|
||||
v.DeviceId = device.Id;
|
||||
variables.Add(v);
|
||||
}
|
||||
|
@@ -220,9 +220,10 @@ public partial class ChannelTable : IDisposable
|
||||
|
||||
private async Task ExcelExportAsync(ITableExportContext<ChannelRuntime> tableExportContext, bool all = false)
|
||||
{
|
||||
bool ret;
|
||||
if (all)
|
||||
{
|
||||
await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new() });
|
||||
ret = await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new() });
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -230,16 +231,16 @@ public partial class ChannelTable : IDisposable
|
||||
{
|
||||
|
||||
case ChannelDevicePluginTypeEnum.PluginName:
|
||||
await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new(), PluginName = SelectModel.PluginName });
|
||||
ret = await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new(), PluginName = SelectModel.PluginName });
|
||||
break;
|
||||
case ChannelDevicePluginTypeEnum.Channel:
|
||||
await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new(), ChannelId = SelectModel.ChannelRuntime.Id });
|
||||
ret = await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new(), ChannelId = SelectModel.ChannelRuntime.Id });
|
||||
break;
|
||||
case ChannelDevicePluginTypeEnum.Device:
|
||||
await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new(), DeviceId = SelectModel.DeviceRuntime.Id, PluginType = SelectModel.DeviceRuntime.PluginType });
|
||||
ret = await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new(), DeviceId = SelectModel.DeviceRuntime.Id, PluginType = SelectModel.DeviceRuntime.PluginType });
|
||||
break;
|
||||
default:
|
||||
await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new() });
|
||||
ret = await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new() });
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -247,7 +248,8 @@ public partial class ChannelTable : IDisposable
|
||||
}
|
||||
|
||||
// 返回 true 时自动弹出提示框
|
||||
await ToastService.Default();
|
||||
if (ret)
|
||||
await ToastService.Default();
|
||||
}
|
||||
|
||||
async Task ExcelChannelAsync(ITableExportContext<ChannelRuntime> tableExportContext)
|
||||
|
@@ -530,20 +530,21 @@ EventCallback.Factory.Create<MouseEventArgs>(this, async e =>
|
||||
}
|
||||
async Task ExportCurrentChannel(ContextMenuItem item, object value)
|
||||
{
|
||||
bool ret;
|
||||
if (value is not ChannelDeviceTreeItem channelDeviceTreeItem) return;
|
||||
|
||||
if (channelDeviceTreeItem.TryGetChannelRuntime(out var channelRuntime))
|
||||
{
|
||||
await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new(), DeviceId = channelRuntime.Id });
|
||||
ret = await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new(), DeviceId = channelRuntime.Id });
|
||||
}
|
||||
else if (channelDeviceTreeItem.TryGetPluginName(out var pluginName))
|
||||
{
|
||||
//插件名称
|
||||
await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new(), PluginName = pluginName });
|
||||
ret = await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new(), PluginName = pluginName });
|
||||
}
|
||||
else if (channelDeviceTreeItem.TryGetPluginType(out var pluginType))
|
||||
{
|
||||
await GatewayExportService.OnChannelExport(new ExportFilter() { QueryPageOptions = new(), PluginType = pluginType });
|
||||
ret = await GatewayExportService.OnChannelExport(new ExportFilter() { QueryPageOptions = new(), PluginType = pluginType });
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -551,14 +552,17 @@ EventCallback.Factory.Create<MouseEventArgs>(this, async e =>
|
||||
}
|
||||
|
||||
// 返回 true 时自动弹出提示框
|
||||
await ToastService.Default();
|
||||
if (ret)
|
||||
await ToastService.Default();
|
||||
}
|
||||
async Task ExportAllChannel(ContextMenuItem item, object value)
|
||||
{
|
||||
await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new() });
|
||||
bool ret;
|
||||
ret = await GatewayExportService.OnChannelExport(new() { QueryPageOptions = new() });
|
||||
|
||||
// 返回 true 时自动弹出提示框
|
||||
await ToastService.Default();
|
||||
if (ret)
|
||||
await ToastService.Default();
|
||||
}
|
||||
|
||||
|
||||
@@ -1065,25 +1069,26 @@ EventCallback.Factory.Create<MouseEventArgs>(this, async e =>
|
||||
|
||||
async Task ExportCurrentDevice(ContextMenuItem item, object value)
|
||||
{
|
||||
bool ret;
|
||||
if (value is not ChannelDeviceTreeItem channelDeviceTreeItem) return;
|
||||
|
||||
if (channelDeviceTreeItem.TryGetDeviceRuntime(out var deviceRuntime))
|
||||
{
|
||||
await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new(), DeviceId = deviceRuntime.Id });
|
||||
ret = await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new(), DeviceId = deviceRuntime.Id });
|
||||
}
|
||||
else if (channelDeviceTreeItem.TryGetChannelRuntime(out var channelRuntime))
|
||||
{
|
||||
await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new(), ChannelId = channelRuntime.Id });
|
||||
ret = await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new(), ChannelId = channelRuntime.Id });
|
||||
}
|
||||
else if (channelDeviceTreeItem.TryGetPluginName(out var pluginName))
|
||||
{
|
||||
//插件名称
|
||||
await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new(), PluginName = pluginName });
|
||||
ret = await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new(), PluginName = pluginName });
|
||||
}
|
||||
else if (channelDeviceTreeItem.TryGetPluginType(out var pluginType))
|
||||
{
|
||||
//采集
|
||||
await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new(), PluginType = pluginType });
|
||||
ret = await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new(), PluginType = pluginType });
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1091,14 +1096,17 @@ EventCallback.Factory.Create<MouseEventArgs>(this, async e =>
|
||||
}
|
||||
|
||||
// 返回 true 时自动弹出提示框
|
||||
await ToastService.Default();
|
||||
if (ret)
|
||||
await ToastService.Default();
|
||||
}
|
||||
async Task ExportAllDevice(ContextMenuItem item, object value)
|
||||
{
|
||||
await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new() });
|
||||
bool ret;
|
||||
ret = await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new() });
|
||||
|
||||
// 返回 true 时自动弹出提示框
|
||||
await ToastService.Default();
|
||||
if (ret)
|
||||
await ToastService.Default();
|
||||
}
|
||||
|
||||
async Task ImportDevice(ContextMenuItem item, object value)
|
||||
|
@@ -10,6 +10,7 @@
|
||||
|
||||
using Mapster;
|
||||
|
||||
using ThingsGateway.Admin.Application;
|
||||
using ThingsGateway.Gateway.Application;
|
||||
|
||||
using Yitter.IdGenerator;
|
||||
@@ -50,14 +51,14 @@ public partial class DeviceCopyComponent
|
||||
for (int i = 0; i < CopyCount; i++)
|
||||
{
|
||||
Device device = Model.Adapt<Device>();
|
||||
device.Id = YitIdHelper.NextId();
|
||||
device.Id = CommonUtils.GetSingleId();
|
||||
device.Name = $"{CopyDeviceNamePrefix}{CopyDeviceNameSuffixNumber + i}";
|
||||
List<Variable> variables = new();
|
||||
|
||||
foreach (var item in Variables)
|
||||
{
|
||||
Variable v = item.Adapt<Variable>();
|
||||
v.Id = YitIdHelper.NextId();
|
||||
v.Id = CommonUtils.GetSingleId();
|
||||
v.DeviceId = device.Id;
|
||||
variables.Add(v);
|
||||
}
|
||||
|
@@ -221,9 +221,10 @@ public partial class DeviceTable : IDisposable
|
||||
|
||||
private async Task ExcelExportAsync(ITableExportContext<DeviceRuntime> tableExportContext, bool all = false)
|
||||
{
|
||||
bool ret;
|
||||
if (all)
|
||||
{
|
||||
await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new() });
|
||||
ret = await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new() });
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -231,16 +232,16 @@ public partial class DeviceTable : IDisposable
|
||||
{
|
||||
|
||||
case ChannelDevicePluginTypeEnum.PluginName:
|
||||
await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new(), PluginName = SelectModel.PluginName });
|
||||
ret = await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new(), PluginName = SelectModel.PluginName });
|
||||
break;
|
||||
case ChannelDevicePluginTypeEnum.Channel:
|
||||
await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new(), ChannelId = SelectModel.ChannelRuntime.Id });
|
||||
ret = await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new(), ChannelId = SelectModel.ChannelRuntime.Id });
|
||||
break;
|
||||
case ChannelDevicePluginTypeEnum.Device:
|
||||
await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new(), DeviceId = SelectModel.DeviceRuntime.Id, PluginType = SelectModel.DeviceRuntime.PluginType });
|
||||
ret = await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new(), DeviceId = SelectModel.DeviceRuntime.Id, PluginType = SelectModel.DeviceRuntime.PluginType });
|
||||
break;
|
||||
default:
|
||||
await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new() });
|
||||
ret = await GatewayExportService.OnDeviceExport(new() { QueryPageOptions = new() });
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -248,7 +249,8 @@ public partial class DeviceTable : IDisposable
|
||||
}
|
||||
|
||||
// 返回 true 时自动弹出提示框
|
||||
await ToastService.Default();
|
||||
if (ret)
|
||||
await ToastService.Default();
|
||||
}
|
||||
|
||||
async Task ExcelDeviceAsync(ITableExportContext<DeviceRuntime> tableExportContext)
|
||||
|
@@ -10,6 +10,7 @@
|
||||
|
||||
using Mapster;
|
||||
|
||||
using ThingsGateway.Admin.Application;
|
||||
using ThingsGateway.Gateway.Application;
|
||||
|
||||
using Yitter.IdGenerator;
|
||||
@@ -47,7 +48,7 @@ public partial class VariableCopyComponent
|
||||
var variable = Model.Adapt<List<Variable>>();
|
||||
foreach (var item in variable)
|
||||
{
|
||||
item.Id = YitIdHelper.NextId();
|
||||
item.Id = CommonUtils.GetSingleId();
|
||||
item.Name = $"{CopyVariableNamePrefix}{CopyVariableNameSuffixNumber + i}";
|
||||
variables.Add(item);
|
||||
}
|
||||
|
@@ -268,9 +268,10 @@ public partial class VariableRuntimeInfo : IDisposable
|
||||
|
||||
private async Task ExcelExportAsync(ITableExportContext<VariableRuntime> tableExportContext, bool all = false)
|
||||
{
|
||||
bool ret;
|
||||
if (all)
|
||||
{
|
||||
await GatewayExportService.OnVariableExport(new() { QueryPageOptions = new() });
|
||||
ret = await GatewayExportService.OnVariableExport(new() { QueryPageOptions = new() });
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -278,16 +279,16 @@ public partial class VariableRuntimeInfo : IDisposable
|
||||
{
|
||||
|
||||
case ChannelDevicePluginTypeEnum.PluginName:
|
||||
await GatewayExportService.OnVariableExport(new() { QueryPageOptions = new(), PluginName = SelectModel.PluginName });
|
||||
ret = await GatewayExportService.OnVariableExport(new() { QueryPageOptions = new(), PluginName = SelectModel.PluginName });
|
||||
break;
|
||||
case ChannelDevicePluginTypeEnum.Channel:
|
||||
await GatewayExportService.OnVariableExport(new() { QueryPageOptions = new(), ChannelId = SelectModel.ChannelRuntime.Id });
|
||||
ret = await GatewayExportService.OnVariableExport(new() { QueryPageOptions = new(), ChannelId = SelectModel.ChannelRuntime.Id });
|
||||
break;
|
||||
case ChannelDevicePluginTypeEnum.Device:
|
||||
await GatewayExportService.OnVariableExport(new() { QueryPageOptions = new(), DeviceId = SelectModel.DeviceRuntime.Id, PluginType = SelectModel.DeviceRuntime.PluginType });
|
||||
ret = await GatewayExportService.OnVariableExport(new() { QueryPageOptions = new(), DeviceId = SelectModel.DeviceRuntime.Id, PluginType = SelectModel.DeviceRuntime.PluginType });
|
||||
break;
|
||||
default:
|
||||
await GatewayExportService.OnVariableExport(new() { QueryPageOptions = new() });
|
||||
ret = await GatewayExportService.OnVariableExport(new() { QueryPageOptions = new() });
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -295,7 +296,8 @@ public partial class VariableRuntimeInfo : IDisposable
|
||||
}
|
||||
|
||||
// 返回 true 时自动弹出提示框
|
||||
await ToastService.Default();
|
||||
if (ret)
|
||||
await ToastService.Default();
|
||||
}
|
||||
|
||||
async Task ExcelVariableAsync(ITableExportContext<VariableRuntime> tableExportContext)
|
||||
|
@@ -12,6 +12,7 @@ using Mapster;
|
||||
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
using ThingsGateway.Admin.Application;
|
||||
using ThingsGateway.Extension.Generic;
|
||||
using ThingsGateway.Foundation;
|
||||
|
||||
@@ -21,6 +22,8 @@ using TouchSocket.Dmtp.Rpc;
|
||||
using TouchSocket.Rpc;
|
||||
using TouchSocket.Sockets;
|
||||
|
||||
using Yitter.IdGenerator;
|
||||
|
||||
namespace ThingsGateway.Plugin.Synchronization;
|
||||
|
||||
|
||||
@@ -258,10 +261,7 @@ public partial class Synchronization : BusinessBase, IRpcDriver
|
||||
var data = await _tcpDmtpClient.GetDmtpRpcActor().InvokeTAsync<List<DataWithDatabase>>(
|
||||
nameof(ReverseCallbackServer.GetData), waitInvoke).ConfigureAwait(false);
|
||||
|
||||
data.ForEach(a => a.Channel.Enable = false);
|
||||
await GlobalData.ChannelRuntimeService.CopyAsync(data.Select(a => a.Channel).ToList(), data.SelectMany(a => a.DeviceVariables).ToDictionary(a => a.Device, a => a.Variables), true, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
LogMessage?.LogTrace($"ForcedSync data success");
|
||||
await Add(data, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -277,10 +277,10 @@ public partial class Synchronization : BusinessBase, IRpcDriver
|
||||
foreach (var item in _tcpDmtpService.Clients)
|
||||
{
|
||||
var data = await item.GetDmtpRpcActor().InvokeTAsync<List<DataWithDatabase>>(nameof(ReverseCallbackServer.GetData), waitInvoke).ConfigureAwait(false);
|
||||
data.ForEach(a => a.Channel.Enable = false);
|
||||
|
||||
await GlobalData.ChannelRuntimeService.CopyAsync(data.Select(a => a.Channel).ToList(), data.SelectMany(a => a.DeviceVariables).ToDictionary(a => a.Device, a => a.Variables), true, cancellationToken).ConfigureAwait(false);
|
||||
LogMessage?.LogTrace($"{item.GetIPPort()}: ForcedSync data success");
|
||||
|
||||
await Add(data, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -300,6 +300,25 @@ public partial class Synchronization : BusinessBase, IRpcDriver
|
||||
}
|
||||
|
||||
}
|
||||
private async Task Add(List<DataWithDatabase> data, CancellationToken cancellationToken)
|
||||
{
|
||||
data.ForEach(a =>
|
||||
{
|
||||
a.Channel.Enable = false;
|
||||
a.Channel.Id = CommonUtils.GetSingleId();
|
||||
a.DeviceVariables.ForEach(b =>
|
||||
{
|
||||
b.Device.Id = 0;
|
||||
b.Variables.ForEach(c =>
|
||||
{
|
||||
c.Id = 0;
|
||||
});
|
||||
});
|
||||
});
|
||||
await GlobalData.ChannelRuntimeService.CopyAsync(data.Select(a => a.Channel).ToList(), data.SelectMany(a => a.DeviceVariables).ToDictionary(a => a.Device, a => a.Variables), true, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
LogMessage?.LogTrace($"ForcedSync data success");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步写入方法
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<Version>10.6.23</Version>
|
||||
<Version>10.6.28</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
Reference in New Issue
Block a user