😎增加操作和访问日志页面日历组件显示每天日志数量

This commit is contained in:
zuohuaijun 2024-12-25 14:52:55 +08:00
parent 5d48eebc2c
commit 04d09b6b7f
24 changed files with 1039 additions and 92 deletions

View File

@ -20,7 +20,7 @@ public class JobMonitor : IJobMonitor
var serviceScope = scopeFactory.CreateScope();
_sysConfigService = serviceScope.ServiceProvider.GetRequiredService<SysConfigService>();
_eventPublisher = serviceScope.ServiceProvider.GetRequiredService<IEventPublisher>();
_logger = serviceScope.ServiceProvider.GetRequiredService<ILogger<JobMonitor>>(); ;
_logger = serviceScope.ServiceProvider.GetRequiredService<ILogger<JobMonitor>>();
}
public Task OnExecutingAsync(JobExecutingContext context, CancellationToken stoppingToken)
@ -30,19 +30,18 @@ public class JobMonitor : IJobMonitor
public async Task OnExecutedAsync(JobExecutedContext context, CancellationToken stoppingToken)
{
if (context.Exception != null)
if (context.Exception == null) return;
var errorInfo = $"【{context.Trigger.Description}】定时任务错误:{context.Exception}";
if (await _sysConfigService.GetConfigValueByCode<bool>(ConfigConst.SysErrorMail))
{
var errorInfo = $"【{context.Trigger.Description}】定时任务错误:{context.Exception}";
if (await _sysConfigService.GetConfigValueByCode<bool>(ConfigConst.SysErrorMail))
{
// 将异常作业发送到邮件
await _eventPublisher.PublishAsync(CommonConst.SendErrorMail, errorInfo, stoppingToken);
}
else
{
//将异常信息存储本地记录
_logger.LogError(errorInfo);
}
// 将异常作业发送到邮件
await _eventPublisher.PublishAsync(CommonConst.SendErrorMail, errorInfo, stoppingToken);
}
else
{
// 将异常信息存储本地记录
_logger.LogError(errorInfo);
}
}
}

View File

@ -0,0 +1,20 @@
// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
namespace Admin.NET.Core.Service;
public class StatLogOutput
{
/// <summary>
/// 日期
/// </summary>
public string Date { get; set; }
/// <summary>
/// 数量
/// </summary>
public int Count { get; set; }
}

View File

@ -81,4 +81,34 @@ public class SysLogOpService : IDynamicApiController, ITransient
var res = await excelExporter.ExportAsByteArray(logOpList);
return new FileStreamResult(new MemoryStream(res), "application/octet-stream") { FileDownloadName = DateTime.Now.ToString("yyyyMMddHHmm") + "操作日志.xlsx" };
}
/// <summary>
/// 按天数统计操作日志 🔖
/// </summary>
/// <returns></returns>
[DisplayName("按天数统计操作日志")]
public async Task<List<StatLogOutput>> GetYearDayStats()
{
var _db = _sysLogOpRep.AsSugarClient();
var now = DateTime.Now;
var days = (now - now.AddYears(-1)).Days + 1;
var day365 = Enumerable.Range(0, days).Select(u => now.AddDays(-u)).ToList();
var queryableLeft = _db.Reportable(day365).ToQueryable<DateTime>();
var queryableRight = _db.Queryable<SysLogOp>(); //.SplitTable(tab => tab);
var list = await _db.Queryable(queryableLeft, queryableRight, JoinType.Left,
(x1, x2) => x1.ColumnName.Date == x2.CreateTime.Date)
.GroupBy((x1, x2) => x1.ColumnName)
.Select((x1, x2) => new StatLogOutput
{
Count = SqlFunc.AggregateSum(SqlFunc.IIF(x2.Id > 0, 1, 0)),
Date = x1.ColumnName.ToString("yyyy-MM-dd")
})
.MergeTable()
.OrderBy(x => x.Date)
.ToListAsync();
return list;
}
}

View File

@ -69,4 +69,34 @@ public class SysLogVisService : IDynamicApiController, ITransient
LogDateTime = u.LogDateTime
}).ToListAsync();
}
/// <summary>
/// 按天数统计操作日志 🔖
/// </summary>
/// <returns></returns>
[DisplayName("按天数统计操作日志")]
public async Task<List<StatLogOutput>> GetYearDayStats()
{
var _db = _sysLogVisRep.AsSugarClient();
var now = DateTime.Now;
var days = (now - now.AddYears(-1)).Days + 1;
var day365 = Enumerable.Range(0, days).Select(u => now.AddDays(-u)).ToList();
var queryableLeft = _db.Reportable(day365).ToQueryable<DateTime>();
var queryableRight = _db.Queryable<SysLogVis>(); //.SplitTable(tab => tab);
var list = await _db.Queryable(queryableLeft, queryableRight, JoinType.Left,
(x1, x2) => x1.ColumnName.Date == x2.CreateTime.Date)
.GroupBy((x1, x2) => x1.ColumnName)
.Select((x1, x2) => new StatLogOutput
{
Count = SqlFunc.AggregateSum(SqlFunc.IIF(x2.Id > 0, 1, 0)),
Date = x1.ColumnName.ToString("yyyy-MM-dd")
})
.MergeTable()
.OrderBy(x => x.Date)
.ToListAsync();
return list;
}
}

View File

@ -80,11 +80,11 @@ public class Startup : AppStartup
{
options.AddPersistence<DbJobPersistence>(); // 添加作业持久化器
options.AddMonitor<JobMonitor>(); // 添加作业执行监视器
// 定义未捕获的异常,通常是 Task 异常
// 定义未捕获的异常
options.UnobservedTaskExceptionHandler = (obj, args) =>
{
if (args.Exception?.Message != null)
Log.Error($"JobSchedule 有未处理异常 {args.Exception?.Message} ", args.Exception);
Log.Error($"JobSchedule 有未处理异常{args.Exception?.Message}", args.Exception);
};
}
});

View File

@ -21,7 +21,9 @@ import { AdminResultIActionResult } from '../models';
import { AdminResultListApiOutput } from '../models';
import { AdminResultListString } from '../models';
import { AdminResultSmKeyPairOutput } from '../models';
import { AdminResultStressTestHarnessResult } from '../models';
import { AdminResultString } from '../models';
import { StressTestInput } from '../models';
/**
* SysCommonApi - axios parameter creator
* @export
@ -406,6 +408,54 @@ export const SysCommonApiAxiosParamCreator = function (configuration?: Configura
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
return {
url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
options: localVarRequestOptions,
};
},
/**
*
* @summary 🔖
* @param {StressTestInput} [body]
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
apiSysCommonStressTestPost: async (body?: StressTestInput, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
const localVarPath = `/api/sysCommon/stressTest`;
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, 'https://example.com');
let baseOptions;
if (configuration) {
baseOptions = configuration.baseOptions;
}
const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options};
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;
// authentication Bearer required
// http bearer authentication required
if (configuration && configuration.accessToken) {
const accessToken = typeof configuration.accessToken === 'function'
? await configuration.accessToken()
: await configuration.accessToken;
localVarHeaderParameter["Authorization"] = "Bearer " + accessToken;
}
localVarHeaderParameter['Content-Type'] = 'application/json-patch+json';
const query = new URLSearchParams(localVarUrlObj.search);
for (const key in localVarQueryParameter) {
query.set(key, localVarQueryParameter[key]);
}
for (const key in options.params) {
query.set(key, options.params[key]);
}
localVarUrlObj.search = (new URLSearchParams(query)).toString();
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
const needsSerialization = (typeof body !== "string") || localVarRequestOptions.headers['Content-Type'] === 'application/json';
localVarRequestOptions.data = needsSerialization ? JSON.stringify(body !== undefined ? body : {}) : (body || "");
return {
url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
options: localVarRequestOptions,
@ -531,6 +581,20 @@ export const SysCommonApiFp = function(configuration?: Configuration) {
return axios.request(axiosRequestArgs);
};
},
/**
*
* @summary 🔖
* @param {StressTestInput} [body]
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async apiSysCommonStressTestPost(body?: StressTestInput, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultStressTestHarnessResult>>> {
const localVarAxiosArgs = await SysCommonApiAxiosParamCreator(configuration).apiSysCommonStressTestPost(body, options);
return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
return axios.request(axiosRequestArgs);
};
},
}
};
@ -619,6 +683,16 @@ export const SysCommonApiFactory = function (configuration?: Configuration, base
async apiSysCommonSmKeyPairGet(options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultSmKeyPairOutput>> {
return SysCommonApiFp(configuration).apiSysCommonSmKeyPairGet(options).then((request) => request(axios, basePath));
},
/**
*
* @summary 🔖
* @param {StressTestInput} [body]
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async apiSysCommonStressTestPost(body?: StressTestInput, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultStressTestHarnessResult>> {
return SysCommonApiFp(configuration).apiSysCommonStressTestPost(body, options).then((request) => request(axios, basePath));
},
};
};
@ -716,4 +790,15 @@ export class SysCommonApi extends BaseAPI {
public async apiSysCommonSmKeyPairGet(options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultSmKeyPairOutput>> {
return SysCommonApiFp(this.configuration).apiSysCommonSmKeyPairGet(options).then((request) => request(this.axios, this.basePath));
}
/**
*
* @summary 🔖
* @param {StressTestInput} [body]
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof SysCommonApi
*/
public async apiSysCommonStressTestPost(body?: StressTestInput, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultStressTestHarnessResult>> {
return SysCommonApiFp(this.configuration).apiSysCommonStressTestPost(body, options).then((request) => request(this.axios, this.basePath));
}
}

View File

@ -17,6 +17,7 @@ import { Configuration } from '../configuration';
// Some imports not used depending on template conditions
// @ts-ignore
import { BASE_PATH, COLLECTION_FORMATS, RequestArgs, BaseAPI, RequiredError } from '../base';
import { AdminResultListStatLogOutput } from '../models';
import { AdminResultSqlSugarPagedListSysLogOp } from '../models';
import { AdminResultSysLogOp } from '../models';
import { LogInput } from '../models';
@ -210,6 +211,49 @@ export const SysLogOpApiAxiosParamCreator = function (configuration?: Configurat
const needsSerialization = (typeof body !== "string") || localVarRequestOptions.headers['Content-Type'] === 'application/json';
localVarRequestOptions.data = needsSerialization ? JSON.stringify(body !== undefined ? body : {}) : (body || "");
return {
url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
options: localVarRequestOptions,
};
},
/**
*
* @summary 🔖
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
apiSysLogOpYearDayStatsGet: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
const localVarPath = `/api/sysLogOp/yearDayStats`;
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, 'https://example.com');
let baseOptions;
if (configuration) {
baseOptions = configuration.baseOptions;
}
const localVarRequestOptions :AxiosRequestConfig = { method: 'GET', ...baseOptions, ...options};
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;
// authentication Bearer required
// http bearer authentication required
if (configuration && configuration.accessToken) {
const accessToken = typeof configuration.accessToken === 'function'
? await configuration.accessToken()
: await configuration.accessToken;
localVarHeaderParameter["Authorization"] = "Bearer " + accessToken;
}
const query = new URLSearchParams(localVarUrlObj.search);
for (const key in localVarQueryParameter) {
query.set(key, localVarQueryParameter[key]);
}
for (const key in options.params) {
query.set(key, options.params[key]);
}
localVarUrlObj.search = (new URLSearchParams(query)).toString();
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
return {
url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
options: localVarRequestOptions,
@ -279,6 +323,19 @@ export const SysLogOpApiFp = function(configuration?: Configuration) {
return axios.request(axiosRequestArgs);
};
},
/**
*
* @summary 🔖
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async apiSysLogOpYearDayStatsGet(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultListStatLogOutput>>> {
const localVarAxiosArgs = await SysLogOpApiAxiosParamCreator(configuration).apiSysLogOpYearDayStatsGet(options);
return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
return axios.request(axiosRequestArgs);
};
},
}
};
@ -327,6 +384,15 @@ export const SysLogOpApiFactory = function (configuration?: Configuration, baseP
async apiSysLogOpPagePost(body?: PageOpLogInput, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultSqlSugarPagedListSysLogOp>> {
return SysLogOpApiFp(configuration).apiSysLogOpPagePost(body, options).then((request) => request(axios, basePath));
},
/**
*
* @summary 🔖
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async apiSysLogOpYearDayStatsGet(options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultListStatLogOutput>> {
return SysLogOpApiFp(configuration).apiSysLogOpYearDayStatsGet(options).then((request) => request(axios, basePath));
},
};
};
@ -380,4 +446,14 @@ export class SysLogOpApi extends BaseAPI {
public async apiSysLogOpPagePost(body?: PageOpLogInput, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultSqlSugarPagedListSysLogOp>> {
return SysLogOpApiFp(this.configuration).apiSysLogOpPagePost(body, options).then((request) => request(this.axios, this.basePath));
}
/**
*
* @summary 🔖
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof SysLogOpApi
*/
public async apiSysLogOpYearDayStatsGet(options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultListStatLogOutput>> {
return SysLogOpApiFp(this.configuration).apiSysLogOpYearDayStatsGet(options).then((request) => request(this.axios, this.basePath));
}
}

View File

@ -18,6 +18,7 @@ import { Configuration } from '../configuration';
// @ts-ignore
import { BASE_PATH, COLLECTION_FORMATS, RequestArgs, BaseAPI, RequiredError } from '../base';
import { AdminResultListLogVisOutput } from '../models';
import { AdminResultListStatLogOutput } from '../models';
import { AdminResultSqlSugarPagedListSysLogVis } from '../models';
import { PageVisLogInput } from '../models';
/**
@ -155,6 +156,49 @@ export const SysLogVisApiAxiosParamCreator = function (configuration?: Configura
const needsSerialization = (typeof body !== "string") || localVarRequestOptions.headers['Content-Type'] === 'application/json';
localVarRequestOptions.data = needsSerialization ? JSON.stringify(body !== undefined ? body : {}) : (body || "");
return {
url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
options: localVarRequestOptions,
};
},
/**
*
* @summary 🔖
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
apiSysLogVisYearDayStatsGet: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
const localVarPath = `/api/sysLogVis/yearDayStats`;
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, 'https://example.com');
let baseOptions;
if (configuration) {
baseOptions = configuration.baseOptions;
}
const localVarRequestOptions :AxiosRequestConfig = { method: 'GET', ...baseOptions, ...options};
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;
// authentication Bearer required
// http bearer authentication required
if (configuration && configuration.accessToken) {
const accessToken = typeof configuration.accessToken === 'function'
? await configuration.accessToken()
: await configuration.accessToken;
localVarHeaderParameter["Authorization"] = "Bearer " + accessToken;
}
const query = new URLSearchParams(localVarUrlObj.search);
for (const key in localVarQueryParameter) {
query.set(key, localVarQueryParameter[key]);
}
for (const key in options.params) {
query.set(key, options.params[key]);
}
localVarUrlObj.search = (new URLSearchParams(query)).toString();
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
return {
url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
options: localVarRequestOptions,
@ -209,6 +253,19 @@ export const SysLogVisApiFp = function(configuration?: Configuration) {
return axios.request(axiosRequestArgs);
};
},
/**
*
* @summary 🔖
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async apiSysLogVisYearDayStatsGet(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultListStatLogOutput>>> {
const localVarAxiosArgs = await SysLogVisApiAxiosParamCreator(configuration).apiSysLogVisYearDayStatsGet(options);
return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
return axios.request(axiosRequestArgs);
};
},
}
};
@ -246,6 +303,15 @@ export const SysLogVisApiFactory = function (configuration?: Configuration, base
async apiSysLogVisPagePost(body?: PageVisLogInput, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultSqlSugarPagedListSysLogVis>> {
return SysLogVisApiFp(configuration).apiSysLogVisPagePost(body, options).then((request) => request(axios, basePath));
},
/**
*
* @summary 🔖
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async apiSysLogVisYearDayStatsGet(options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultListStatLogOutput>> {
return SysLogVisApiFp(configuration).apiSysLogVisYearDayStatsGet(options).then((request) => request(axios, basePath));
},
};
};
@ -287,4 +353,14 @@ export class SysLogVisApi extends BaseAPI {
public async apiSysLogVisPagePost(body?: PageVisLogInput, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultSqlSugarPagedListSysLogVis>> {
return SysLogVisApiFp(this.configuration).apiSysLogVisPagePost(body, options).then((request) => request(this.axios, this.basePath));
}
/**
*
* @summary 🔖
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof SysLogVisApi
*/
public async apiSysLogVisYearDayStatsGet(options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultListStatLogOutput>> {
return SysLogVisApiFp(this.configuration).apiSysLogVisYearDayStatsGet(options).then((request) => request(this.axios, this.basePath));
}
}

View File

@ -0,0 +1,71 @@
/* tslint:disable */
/* eslint-disable */
/**
* Admin.NET
* .NET <br/><u><b><font color='FF0000'> 👮</font></b></u>
*
* OpenAPI spec version: 1.0.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
import { StatLogOutput } from './stat-log-output';
/**
*
*
* @export
* @interface AdminResultListStatLogOutput
*/
export interface AdminResultListStatLogOutput {
/**
*
*
* @type {number}
* @memberof AdminResultListStatLogOutput
*/
code?: number;
/**
* successwarningerror
*
* @type {string}
* @memberof AdminResultListStatLogOutput
*/
type?: string | null;
/**
*
*
* @type {string}
* @memberof AdminResultListStatLogOutput
*/
message?: string | null;
/**
*
*
* @type {Array<StatLogOutput>}
* @memberof AdminResultListStatLogOutput
*/
result?: Array<StatLogOutput> | null;
/**
*
*
* @type {any}
* @memberof AdminResultListStatLogOutput
*/
extras?: any | null;
/**
*
*
* @type {Date}
* @memberof AdminResultListStatLogOutput
*/
time?: Date;
}

View File

@ -0,0 +1,69 @@
/* tslint:disable */
/* eslint-disable */
/**
* Admin.NET
* .NET <br/><u><b><font color='FF0000'> 👮</font></b></u>
*
* OpenAPI spec version: 1.0.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
import { StressTestHarnessResult } from './stress-test-harness-result';
/**
*
*
* @export
* @interface AdminResultStressTestHarnessResult
*/
export interface AdminResultStressTestHarnessResult {
/**
*
*
* @type {number}
* @memberof AdminResultStressTestHarnessResult
*/
code?: number;
/**
* successwarningerror
*
* @type {string}
* @memberof AdminResultStressTestHarnessResult
*/
type?: string | null;
/**
*
*
* @type {string}
* @memberof AdminResultStressTestHarnessResult
*/
message?: string | null;
/**
* @type {StressTestHarnessResult}
* @memberof AdminResultStressTestHarnessResult
*/
result?: StressTestHarnessResult;
/**
*
*
* @type {any}
* @memberof AdminResultStressTestHarnessResult
*/
extras?: any | null;
/**
*
*
* @type {Date}
* @memberof AdminResultStressTestHarnessResult
*/
time?: Date;
}

View File

@ -53,6 +53,7 @@ export * from './admin-result-list-menu-output';
export * from './admin-result-list-pos-output';
export * from './admin-result-list-role-output';
export * from './admin-result-list-role-table-output';
export * from './admin-result-list-stat-log-output';
export * from './admin-result-list-string';
export * from './admin-result-list-sys-code-gen-template';
export * from './admin-result-list-sys-config';
@ -104,6 +105,7 @@ export * from './admin-result-sql-sugar-paged-list-sys-upgrade';
export * from './admin-result-sql-sugar-paged-list-sys-wechat-pay';
export * from './admin-result-sql-sugar-paged-list-tenant-output';
export * from './admin-result-sql-sugar-paged-list-user-output';
export * from './admin-result-stress-test-harness-result';
export * from './admin-result-string';
export * from './admin-result-sys-code-gen';
export * from './admin-result-sys-code-gen-config';
@ -239,6 +241,7 @@ export * from './job-create-type-enum';
export * from './job-detail-input';
export * from './job-detail-output';
export * from './job-trigger-input';
export * from './key-value-pair-string-string';
export * from './layout-kind';
export * from './list-schedule-input';
export * from './log-input';
@ -355,7 +358,10 @@ export * from './sql-sugar-paged-list-sys-upgrade';
export * from './sql-sugar-paged-list-sys-wechat-pay';
export * from './sql-sugar-paged-list-tenant-output';
export * from './sql-sugar-paged-list-user-output';
export * from './stat-log-output';
export * from './status-enum';
export * from './stress-test-harness-result';
export * from './stress-test-input';
export * from './struct-layout-attribute';
export * from './swagger-submit-url-body';
export * from './sync-sys-ldap-input';

View File

@ -0,0 +1,34 @@
/* tslint:disable */
/* eslint-disable */
/**
* Admin.NET
* .NET <br/><u><b><font color='FF0000'> 👮</font></b></u>
*
* OpenAPI spec version: 1.0.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
/**
*
*
* @export
* @interface KeyValuePairStringString
*/
export interface KeyValuePairStringString {
/**
* @type {string}
* @memberof KeyValuePairStringString
*/
key?: string | null;
/**
* @type {string}
* @memberof KeyValuePairStringString
*/
value?: string | null;
}

View File

@ -30,6 +30,12 @@ export interface MemberInfo {
*/
memberType?: MemberTypes;
/**
* @type {string}
* @memberof MemberInfo
*/
name?: string | null;
/**
* @type {Type}
* @memberof MemberInfo
@ -42,12 +48,6 @@ export interface MemberInfo {
*/
reflectedType?: Type;
/**
* @type {string}
* @memberof MemberInfo
*/
name?: string | null;
/**
* @type {Module}
* @memberof MemberInfo

View File

@ -82,30 +82,6 @@ export interface PageCodeGenInput {
*/
descStr?: string | null;
/**
*
*
* @type {string}
* @memberof PageCodeGenInput
*/
tableName?: string | null;
/**
*
*
* @type {string}
* @memberof PageCodeGenInput
*/
busName?: string | null;
/**
*
*
* @type {string}
* @memberof PageCodeGenInput
*/
nameSpace?: string | null;
/**
*
*
@ -114,30 +90,6 @@ export interface PageCodeGenInput {
*/
authorName?: string | null;
/**
*
*
* @type {string}
* @memberof PageCodeGenInput
*/
generateType?: string | null;
/**
*
*
* @type {boolean}
* @memberof PageCodeGenInput
*/
generateMenu?: boolean;
/**
* 使 Api Service
*
* @type {boolean}
* @memberof PageCodeGenInput
*/
isApiService?: boolean;
/**
*
*
@ -186,6 +138,38 @@ export interface PageCodeGenInput {
*/
connectionString?: string | null;
/**
*
*
* @type {string}
* @memberof PageCodeGenInput
*/
generateType?: string | null;
/**
*
*
* @type {string}
* @memberof PageCodeGenInput
*/
tableName?: string | null;
/**
*
*
* @type {string}
* @memberof PageCodeGenInput
*/
nameSpace?: string | null;
/**
*
*
* @type {string}
* @memberof PageCodeGenInput
*/
busName?: string | null;
/**
*
*
@ -202,6 +186,14 @@ export interface PageCodeGenInput {
*/
menuApplication?: string | null;
/**
*
*
* @type {boolean}
* @memberof PageCodeGenInput
*/
generateMenu?: boolean;
/**
*
*
@ -241,4 +233,12 @@ export interface PageCodeGenInput {
* @memberof PageCodeGenInput
*/
printName?: string | null;
/**
* 使 Api Service
*
* @type {boolean}
* @memberof PageCodeGenInput
*/
isApiService?: boolean;
}

View File

@ -0,0 +1,38 @@
/* tslint:disable */
/* eslint-disable */
/**
* Admin.NET
* .NET <br/><u><b><font color='FF0000'> 👮</font></b></u>
*
* OpenAPI spec version: 1.0.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
/**
*
*
* @export
* @interface StatLogOutput
*/
export interface StatLogOutput {
/**
*
*
* @type {string}
* @memberof StatLogOutput
*/
date?: string | null;
/**
*
*
* @type {number}
* @memberof StatLogOutput
*/
count?: number;
}

View File

@ -0,0 +1,112 @@
/* tslint:disable */
/* eslint-disable */
/**
* Admin.NET
* .NET <br/><u><b><font color='FF0000'> 👮</font></b></u>
*
* OpenAPI spec version: 1.0.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
/**
*
*
* @export
* @interface StressTestHarnessResult
*/
export interface StressTestHarnessResult {
/**
* @type {number}
* @memberof StressTestHarnessResult
*/
totalRequests?: number;
/**
* @type {number}
* @memberof StressTestHarnessResult
*/
totalTimeInSeconds?: number;
/**
* @type {number}
* @memberof StressTestHarnessResult
*/
successfulRequests?: number;
/**
* @type {number}
* @memberof StressTestHarnessResult
*/
failedRequests?: number;
/**
* @type {number}
* @memberof StressTestHarnessResult
*/
queriesPerSecond?: number;
/**
* @type {number}
* @memberof StressTestHarnessResult
*/
minResponseTime?: number;
/**
* @type {number}
* @memberof StressTestHarnessResult
*/
maxResponseTime?: number;
/**
* @type {number}
* @memberof StressTestHarnessResult
*/
averageResponseTime?: number;
/**
* @type {number}
* @memberof StressTestHarnessResult
*/
percentile10ResponseTime?: number;
/**
* @type {number}
* @memberof StressTestHarnessResult
*/
percentile25ResponseTime?: number;
/**
* @type {number}
* @memberof StressTestHarnessResult
*/
percentile50ResponseTime?: number;
/**
* @type {number}
* @memberof StressTestHarnessResult
*/
percentile75ResponseTime?: number;
/**
* @type {number}
* @memberof StressTestHarnessResult
*/
percentile90ResponseTime?: number;
/**
* @type {number}
* @memberof StressTestHarnessResult
*/
percentile99ResponseTime?: number;
/**
* @type {number}
* @memberof StressTestHarnessResult
*/
percentile9999ResponseTime?: number;
}

View File

@ -0,0 +1,91 @@
/* tslint:disable */
/* eslint-disable */
/**
* Admin.NET
* .NET <br/><u><b><font color='FF0000'> 👮</font></b></u>
*
* OpenAPI spec version: 1.0.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
import { KeyValuePairStringString } from './key-value-pair-string-string';
/**
*
*
* @export
* @interface StressTestInput
*/
export interface StressTestInput {
/**
*
*
* @type {string}
* @memberof StressTestInput
* @example https://gitee.com/zuohuaijun/Admin.NET
*/
requestUri: string;
/**
*
*
* @type {number}
* @memberof StressTestInput
* @example 100
*/
numberOfRequests: number;
/**
*
*
* @type {number}
* @memberof StressTestInput
* @example 1
*/
numberOfRounds: number;
/**
*
*
* @type {number}
* @memberof StressTestInput
* @example 500
*/
maxDegreeOfParallelism?: number;
/**
*
*
* @type {{ [key: string]: string; }}
* @memberof StressTestInput
*/
headers?: { [key: string]: string; } | null;
/**
* JSON内容
*
* @type {Array<KeyValuePairStringString>}
* @memberof StressTestInput
*/
jsonContent?: Array<KeyValuePairStringString> | null;
/**
*
*
* @type {{ [key: string]: string; }}
* @memberof StressTestInput
*/
pathParameters?: { [key: string]: string; } | null;
/**
* URL参数
*
* @type {{ [key: string]: string; }}
* @memberof StressTestInput
*/
_queryParameters?: { [key: string]: string; } | null;
}

View File

@ -29,14 +29,6 @@ export interface SysRegion {
*/
id?: number;
/**
*
*
* @type {string}
* @memberof SysRegion
*/
name: string;
/**
* Id
*
@ -45,6 +37,14 @@ export interface SysRegion {
*/
pid?: number;
/**
*
*
* @type {string}
* @memberof SysRegion
*/
name: string;
/**
*
*

View File

@ -93,14 +93,6 @@ export interface SysSchedule {
*/
tenantId?: number | null;
/**
*
*
* @type {string}
* @memberof SysSchedule
*/
content: string;
/**
* Id
*
@ -133,6 +125,14 @@ export interface SysSchedule {
*/
endTime?: string | null;
/**
*
*
* @type {string}
* @memberof SysSchedule
*/
content: string;
/**
* @type {FinishStatusEnum}
* @memberof SysSchedule

View File

@ -211,6 +211,18 @@ export interface TypeInfo {
*/
isByRefLike?: boolean;
/**
* @type {boolean}
* @memberof TypeInfo
*/
isFunctionPointer?: boolean;
/**
* @type {boolean}
* @memberof TypeInfo
*/
isUnmanagedFunctionPointer?: boolean;
/**
* @type {boolean}
* @memberof TypeInfo

View File

@ -205,6 +205,18 @@ export interface Type {
*/
isByRefLike?: boolean;
/**
* @type {boolean}
* @memberof Type
*/
isFunctionPointer?: boolean;
/**
* @type {boolean}
* @memberof Type
*/
isUnmanagedFunctionPointer?: boolean;
/**
* @type {boolean}
* @memberof Type

View File

@ -63,6 +63,11 @@ export default {
let myChart = echarts.init(this.$refs.scEcharts, 'T');
myChart.setOption(this.myOptions);
this.myChart = myChart;
myChart.on('click', (evt) => {
this.$emit('clickData', evt.value);
});
window.addEventListener('resize', () => myChart.resize());
},
},

View File

@ -1,5 +1,9 @@
<template>
<div class="syslogop-container">
<el-card shadow="hover" :body-style="{ padding: '5px 5px 0 5px' }">
<scEcharts v-if="echartsOption.series.data" height="200px" :option="echartsOption" @clickData="clickData"></scEcharts>
</el-card>
<el-card shadow="hover" :body-style="{ padding: '5px 5px 0 5px', display: 'flex', width: '100%', height: '100%', alignItems: 'start' }">
<el-form :model="state.queryParams" ref="queryForm" :show-message="false" :inlineMessage="true" label-width="auto" style="flex: 1 1 0%">
<el-row :gutter="10">
@ -109,7 +113,7 @@
</template>
<script lang="ts" setup name="sysLogOp">
import { onMounted, reactive, ref } from 'vue';
import { defineAsyncComponent, onMounted, reactive, ref } from 'vue';
import { ElMessage } from 'element-plus';
import { useDateTimeShortCust } from '/@/hooks/dateTimeShortCust';
import { VxeGridInstance, VxeGridListeners, VxeGridPropTypes } from 'vxe-table';
@ -119,17 +123,20 @@ import { downloadByData, getFileName } from '/@/utils/download';
import VueJsonPretty from 'vue-json-pretty';
import 'vue-json-pretty/lib/styles.css';
import { StringToObj } from '/@/utils/json-utils';
import { formatDate } from '/@/utils/formatTime';
import { getAPI } from '/@/utils/axios-utils';
import { SysLogOpApi } from '/@/api-services/api';
import { SysLogOp, PageLogInput } from '/@/api-services/models';
const scEcharts = defineAsyncComponent(() => import('/@/components/scEcharts/index.vue'));
const shortcuts = useDateTimeShortCust();
const xGrid = ref<VxeGridInstance>();
const state = reactive({
queryParams: {
startTime: undefined,
endTime: undefined,
startTime: undefined as any,
endTime: undefined as any,
controllerName: undefined,
actionName: undefined,
account: undefined,
@ -148,6 +155,64 @@ const state = reactive({
exception: undefined,
},
activeTab: 'message',
logMaxValue: 1,
});
const echartsOption = ref({
title: {
top: 30,
left: 'center',
text: '日志统计',
show: false,
},
tooltip: {
formatter: function (p: any) {
return p.data[1] + ' 数据量:' + p.data[0];
},
},
visualMap: {
show: true,
// inRange: {
// color: ['#fbeee2', '#f2cac9', '#efafad', '#f19790', '#f1908c', '#f17666', '#f05a46', '#ed3b2f', '#ec2b24', '#de2a18'],
// },
min: 0,
max: 1000,
maxOpen: {
type: 'piecewise',
},
type: 'piecewise',
orient: 'horizontal',
left: 'right',
},
calendar: {
top: 30,
left: 30,
right: 30,
bottom: 30,
cellSize: ['auto', 20],
range: ['', ''],
splitLine: true,
dayLabel: {
firstDay: 1,
nameMap: 'ZH',
},
itemStyle: {
color: '#ccc',
borderWidth: 3,
borderColor: '#fff',
},
monthLabel: {
nameMap: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
},
yearLabel: {
show: false,
},
},
series: {
type: 'heatmap',
coordinateSystem: 'calendar',
data: [],
},
});
//
@ -199,10 +264,36 @@ const options = useVxeTable<SysLogOp>(
);
//
onMounted(() => {
onMounted(async () => {
state.localPageParam = Local.get(localPageParamKey) || state.localPageParam;
await getYearDayStatsData();
});
//
const getYearDayStatsData = async () => {
let data = [] as any;
var res = await getAPI(SysLogOpApi).apiSysLogOpYearDayStatsGet();
res.data.result?.forEach((item: any) => {
data.push([item.date, item.count]);
if (item.count > state.logMaxValue) state.logMaxValue = item.count; //
});
echartsOption.value.visualMap.max = state.logMaxValue;
echartsOption.value.series.data = data;
echartsOption.value.calendar.range = [data[0][0], data[data.length - 1][0]];
};
//
const clickData = (e: any) => {
if (e[1] < 1) return ElMessage.warning('没有日志数据');
state.queryParams.startTime = e[0];
var today = new Date(state.queryParams.startTime);
let endTime = today.setDate(today.getDate() + 1);
state.queryParams.endTime = formatDate(new Date(endTime), 'YYYY-mm-dd');
xGrid.value?.commitProxy('query');
};
// api
const handleQueryApi = async (page: VxeGridPropTypes.ProxyAjaxQueryPageParams, sort: VxeGridPropTypes.ProxyAjaxQuerySortCheckedParams) => {
const params = Object.assign(state.queryParams, { page: page.currentPage, pageSize: page.pageSize, field: sort.field, order: sort.order, descStr: 'desc' }) as PageLogInput;

View File

@ -1,5 +1,9 @@
<template>
<div class="syslogvis-container">
<el-card shadow="hover" :body-style="{ padding: '5px 5px 0 5px' }">
<scEcharts v-if="echartsOption.series.data" height="200px" :option="echartsOption" @clickData="clickData"></scEcharts>
</el-card>
<el-card shadow="hover" :body-style="{ padding: '5px 5px 0 5px', display: 'flex', width: '100%', height: '100%', alignItems: 'start' }">
<el-form :model="state.queryParams" ref="queryForm" :show-message="false" :inlineMessage="true" label-width="auto" style="flex: 1 1 0%">
<el-row :gutter="10">
@ -84,20 +88,22 @@ import { useDateTimeShortCust } from '/@/hooks/dateTimeShortCust';
import { VxeGridInstance, VxeGridListeners, VxeGridPropTypes } from 'vxe-table';
import { useVxeTable } from '/@/hooks/useVxeTableOptionsHook';
import { Local } from '/@/utils/storage';
import { formatDate } from '/@/utils/formatTime';
import { getAPI } from '/@/utils/axios-utils';
import { SysLogVisApi } from '/@/api-services';
import { SysLogVis, PageLogInput } from '/@/api-services/models';
const VisMap = defineAsyncComponent(() => import('./component/visMap.vue'));
const scEcharts = defineAsyncComponent(() => import('/@/components/scEcharts/index.vue'));
const xGrid = ref<VxeGridInstance>();
const mapRef = ref<InstanceType<typeof VisMap>>();
const shortcuts = useDateTimeShortCust();
const state = reactive({
queryParams: {
startTime: undefined,
endTime: undefined,
startTime: undefined as any,
endTime: undefined as any,
status: undefined,
actionName: undefined,
account: undefined,
@ -109,6 +115,64 @@ const state = reactive({
defaultSort: { field: 'id', order: 'desc', descStr: 'desc' },
},
title: '',
logMaxValue: 1,
});
const echartsOption = ref({
title: {
top: 30,
left: 'center',
text: '日志统计',
show: false,
},
tooltip: {
formatter: function (p: any) {
return p.data[1] + ' 数据量:' + p.data[0];
},
},
visualMap: {
show: true,
// inRange: {
// color: ['#fbeee2', '#f2cac9', '#efafad', '#f19790', '#f1908c', '#f17666', '#f05a46', '#ed3b2f', '#ec2b24', '#de2a18'],
// },
min: 0,
max: 1000,
maxOpen: {
type: 'piecewise',
},
type: 'piecewise',
orient: 'horizontal',
left: 'right',
},
calendar: {
top: 30,
left: 30,
right: 30,
bottom: 30,
cellSize: ['auto', 20],
range: ['', ''],
splitLine: true,
dayLabel: {
firstDay: 1,
nameMap: 'ZH',
},
itemStyle: {
color: '#ccc',
borderWidth: 3,
borderColor: '#fff',
},
monthLabel: {
nameMap: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
},
yearLabel: {
show: false,
},
},
series: {
type: 'heatmap',
coordinateSystem: 'calendar',
data: [],
},
});
//
@ -150,10 +214,36 @@ const options = useVxeTable<SysLogVis>(
);
//
onMounted(() => {
onMounted(async () => {
state.localPageParam = Local.get(localPageParamKey) || state.localPageParam;
await getYearDayStatsData();
});
//
const getYearDayStatsData = async () => {
let data = [] as any;
var res = await getAPI(SysLogVisApi).apiSysLogVisYearDayStatsGet();
res.data.result?.forEach((item: any) => {
data.push([item.date, item.count]);
if (item.count > state.logMaxValue) state.logMaxValue = item.count; //
});
echartsOption.value.visualMap.max = state.logMaxValue;
echartsOption.value.series.data = data;
echartsOption.value.calendar.range = [data[0][0], data[data.length - 1][0]];
};
//
const clickData = (e: any) => {
if (e[1] < 1) return ElMessage.warning('没有日志数据');
state.queryParams.startTime = e[0];
var today = new Date(state.queryParams.startTime);
let endTime = today.setDate(today.getDate() + 1);
state.queryParams.endTime = formatDate(new Date(endTime), 'YYYY-mm-dd');
xGrid.value?.commitProxy('query');
};
// api
const handleQueryApi = async (page: VxeGridPropTypes.ProxyAjaxQueryPageParams, sort: VxeGridPropTypes.ProxyAjaxQuerySortCheckedParams) => {
const params = Object.assign(state.queryParams, { page: page.currentPage, pageSize: page.pageSize, field: sort.field, order: sort.order, descStr: 'desc' }) as PageLogInput;