生成WSL报表

dev
嵇文龙 1 month ago
parent d15cf21d9b
commit c1126ffe74

@ -76,4 +76,33 @@ public static class HangfireModuleInstall
options.Queues = ["fee"];
});
}
public static void AddHangfireOPInstall(this IServiceCollection services)
{
if (services == null) throw new ArgumentNullException(nameof(services));
services.AddHangfire(configuration => configuration
.SetDataCompatibilityLevel(CompatibilityLevel.Version_170) //设置Hangfire 存储的数据兼容性级别为 Version_170
.UseSimpleAssemblyNameTypeSerializer()//使用简单的程序集名称类型序列化器。这是一种用于序列化和反序列化 Hangfire 任务数据的方法
.UseRecommendedSerializerSettings()//使用推荐的序列化器设置。这将使用推荐的序列化器选项进行配置
.UseStorage(new MySqlStorage(
AppSetting.app(new string[] { "HangfireSettings", "DbString" }),
new MySqlStorageOptions
{
TransactionIsolationLevel = IsolationLevel.ReadCommitted,// 事务隔离级别。默认是读取已提交。
QueuePollInterval = TimeSpan.FromSeconds(15), //- 作业队列轮询间隔。默认值为15秒。
JobExpirationCheckInterval = TimeSpan.FromHours(1), //- 作业到期检查间隔管理过期记录。默认值为1小时。
CountersAggregateInterval = TimeSpan.FromMinutes(5), //- 聚合计数器的间隔。默认为5分钟。
PrepareSchemaIfNecessary = true, //- 如果设置为true则创建数据库表。默认是true。
DashboardJobListLimit = 50000,//- 仪表板作业列表限制。默认值为50000。
TransactionTimeout = TimeSpan.FromMinutes(1), //- 交易超时。默认为1分钟。
TablesPrefix = "Hangfire"
})).UseHangfireHttpJob());
services.AddHangfireServer(options =>
{
//options.WorkerCount = AppSetting.app(new string[] { "HangfireSettings", "WorkerCount" }).ToInt();
//options.ServerName = AppSetting.app(new string[] { "HangfireSettings", "ServerName" });
options.Queues = ["op"];
});
}
}

@ -254,7 +254,7 @@ namespace DS.WMS.Core.Fee.Method
{
if (fe.FeeStatus != FeeStatus.Entering && fe.FeeStatus != FeeStatus.RejectSubmission)
{
sb.AppendFormat(MultiLanguageConst.FeeRecordStatus, fe.FeeName);
sb.AppendFormat(MultiLanguageConst.GetDescription(MultiLanguageConst.FeeRecordStatus), fe.FeeName);
continue;
}
}

@ -0,0 +1,32 @@
namespace DS.WMS.Core.HangfireJob.Dtos
{
public class WSLModel
{
public string Date { get; set; }
public string Month { get; set; }
public IEnumerable<WSLItem>? List { get; set; }
}
public class WSLItem
{
public long CustomerId { get; set; }
public string? CustomerName { get; set; }
public int? YesterdayTeu { get; set; }
public int? TodayTeu { get; set; }
public int? TodayTeuCTNPickup { get; set; }
public int? IncreasedTeu => TodayTeu - YesterdayTeu;
public string? Date { get; set; }
public int? NextMonthTEU { get; set; }
public int? LastMonthTEU { get; set; }
}
}

@ -0,0 +1,17 @@
using Hangfire;
namespace DS.WMS.Core.HangfireJob.Interface
{
/// <summary>
/// WSL报表发送
/// </summary>
public interface IWSLReportJobService
{
/// <summary>
/// 生成报表
/// </summary>
/// <returns></returns>
[Queue("op")]
Task GeneratReportAsync();
}
}

@ -1,4 +1,4 @@
using DS.WMS.Core.HangfireJob.Interface;
using System.Linq.Expressions;
using Hangfire;
using Hangfire.Dashboard.BasicAuthorization;
using Microsoft.AspNetCore.Builder;
@ -21,7 +21,7 @@ namespace DS.WMS.Core.HangfireJob.Method
// 将 Hangfire 仪表板添加到应用程序的请求处理管道中
app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
Authorization = new[] {new BasicAuthAuthorizationFilter(new BasicAuthAuthorizationFilterOptions
Authorization = [new BasicAuthAuthorizationFilter(new BasicAuthAuthorizationFilterOptions
{
RequireSsl = false,
SslRedirect = false,
@ -34,13 +34,22 @@ namespace DS.WMS.Core.HangfireJob.Method
PasswordClear = "ds2024"
}
}
})}
})]
});
RecurringJob.AddOrUpdate<IFeeCustTemplateJobService>(nameof(IFeeCustTemplateJobService),
s => s.GenerateFeesAsync(), Cron.Daily(23, 30));
return app;
}
/// <summary>
/// 注册定时任务
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="methodCall"></param>
/// <param name="cron">CRON表达式</param>
/// <param name="jobId">任务ID</param>
public static void RegisterJob<T>(Expression<Action<T>> methodCall, string cron, string? jobId = null)
{
RecurringJob.AddOrUpdate(jobId ?? typeof(T).FullName, methodCall, cron);
}
}
}

@ -1,8 +1,10 @@
using DS.Module.Core;
using DS.WMS.Core.Sys.Entity;
using DS.WMS.Core.HangfireJob.Dtos;
using DS.WMS.Core.HangfireJob.Interface;
using DS.WMS.Core.Op.Entity;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using MiniExcelLibs;
using SqlSugar;
namespace DS.WMS.Core.HangfireJob.Method
@ -10,11 +12,11 @@ namespace DS.WMS.Core.HangfireJob.Method
/// <summary>
/// WSL报表服务
/// </summary>
public class WSLReportJobService
public class WSLReportJobService : IWSLReportJobService
{
static readonly ApiFox api;
ISqlSugarClient? db;
IConfiguration configuration;
Microsoft.Extensions.Configuration.IConfiguration config;
IWebHostEnvironment hostEnvironment;
static WSLReportJobService()
@ -29,7 +31,7 @@ namespace DS.WMS.Core.HangfireJob.Method
public WSLReportJobService(IServiceProvider serviceProvider)
{
db = serviceProvider.GetRequiredService<ISqlSugarClient>();
configuration = serviceProvider.GetRequiredService<IConfiguration>();
config = serviceProvider.GetRequiredService<Microsoft.Extensions.Configuration.IConfiguration>();
hostEnvironment = serviceProvider.GetRequiredService<IWebHostEnvironment>();
}
@ -37,36 +39,106 @@ namespace DS.WMS.Core.HangfireJob.Method
/// 生成报表
/// </summary>
/// <returns></returns>
public async Task GeneratReportAsync()
public async Task GeneratReportAsync()
{
string path = Path.Combine(hostEnvironment.WebRootPath, "templates", "WSL.xlsx");
FileInfo templateFile = new FileInfo(path);
if (!templateFile.Exists)
throw new ApplicationException("未能在下列路径找到模板文件:" + path);
db.QueryFilter.Clear();
var dbLinks = await db.Queryable<Module.SqlSugar.SysTenantLink>().ToListAsync();
SqlSugarClient? tenantDb = null;
var today = DateTime.Now;
var today0 = new DateTime(today.Year, today.Month, today.Day);
var yesterday0 = today0.AddDays(-1).Date;
var yesterday = new DateTime(yesterday0.Year, yesterday0.Month, yesterday0.Day, 23, 59, 59);
var lastMonth = today.AddMonths(-1);
var nextMonth = today.AddMonths(1);
var startDate = new DateTime(lastMonth.Year, lastMonth.Month, lastMonth.Day);
var endDate = new DateTime(nextMonth.Year, nextMonth.Month, nextMonth.Day, 23, 59, 59);
try
{
foreach (var dbLink in dbLinks)
{
var adminUser = await db.Queryable<SysUser>()
.Where(x => x.TenantId == dbLink.TenantId && x.Status == 0 && x.UserType == 1)
.OrderByDescending(x => x.CreateTime)
if (config["TaskMail:DefaultSetting:Tenant"] != dbLink.TenantId.ToString())
continue;
tenantDb = new SqlSugarClient(new ConnectionConfig
{
ConfigId = dbLink.Id,
ConnectionString = dbLink.Connection,
DbType = dbLink.DbType,
IsAutoCloseConnection = true
});
WSLModel model = new()
{
Date = today.ToString("yyyy.MM.dd"),
Month = today.ToString("yyyy.MM")
};
var list = await tenantDb.Queryable<SeaExport>().Where(x => x.SourceCode == "FOB-WSL" && SqlFunc.Between(x.ETD, startDate, endDate))
.Select(x => new
{
x.Id,
x.UserName,
x.Password,
x.DefaultOrgId,
x.DefaultOrgName,
x.TenantId,
x.TenantName
}).FirstAsync();
if (adminUser == null)
x.CustomerId,
x.CustomerName,
x.ETD,
x.TEU
}).ToListAsync();
var ids = list.Select(x => x.Id.ToString());
var ctnList = await tenantDb.Queryable<OpCtn>().Where(x => ids.Contains(x.BSNO) &&
!SqlFunc.IsNullOrEmpty(x.CntrNo) && !SqlFunc.IsNullOrEmpty(x.SealNo))
.Select(x => new
{
x.BSNO,
x.TEU
}).ToListAsync();
model.List = list.GroupBy(x => new { x.CustomerId, x.CustomerName }).Select(x => new WSLItem
{
Console.WriteLine($"未能获取租户系统管理员租户ID{dbLink.TenantId}");
continue;
CustomerId = x.Key.CustomerId,
CustomerName = x.Key.CustomerName,
Date = model.Date,
YesterdayTeu = x.Where(x => x.ETD >= yesterday0 && x.ETD <= yesterday).Sum(x => x.TEU),
TodayTeu = x.Where(x => x.ETD >= today0 && x.ETD <= today).Sum(x => x.TEU),
NextMonthTEU = x.Where(x => x.ETD.Value.Year == nextMonth.Year && x.ETD.Value.Month == nextMonth.Month).Sum(x => x.TEU),
LastMonthTEU = x.Where(x => x.ETD.Value.Year == lastMonth.Year && x.ETD.Value.Month == lastMonth.Month).Sum(x => x.TEU),
}).ToList();
foreach (var item in model.List)
{
var ids2 = list.Where(x => x.CustomerId == item.CustomerId).Select(x => x.Id.ToString());
item.TodayTeuCTNPickup = ctnList.Where(x => ids2.Contains(x.BSNO)).Sum(x => x.TEU);
}
MemoryStream ms = new MemoryStream();
await MiniExcel.SaveAsByTemplateAsync(ms, path, model);
string base64Str = Convert.ToBase64String(ms.ToArray());
var attaches = new List<Attachment>
{
new() { AttachName = "WSL Volume Daily Increase Report.xlsx", AttachContent = base64Str}
};
dynamic[] mailParams = [new
{
SendTo = config["TaskMail:DefaultSetting:Receivers"],
Title = "WSL Volume Daily Increase Report",
Body = "Dear WSL Team" + Environment.NewLine + "Pls kindly check the daily report for your member's nomination booking:",
//ShowName = "",
Account = config["TaskMail:DefaultSetting:Account"],
Password = config["TaskMail:DefaultSetting:Password"],
Server = config["TaskMail:DefaultSetting:Host"],
Port = config["TaskMail:DefaultSetting:Port"],
UseSSL = config["TaskMail:DefaultSetting:UseSSL"],
Attaches = attaches
}];
var mailResult = await api.SendRequestAsync(HttpMethod.Post, config["TaskMail:MailApiUrl"], mailParams);
if (!mailResult.IsSuccessStatusCode)
throw new ApplicationException("发送邮件失败");
}
}
finally
@ -74,5 +146,29 @@ namespace DS.WMS.Core.HangfireJob.Method
tenantDb?.Dispose();
}
}
class DefaultSetting
{
public long Tenant { get; set; }
public string? Account { get; set; }
public string? Password { get; set; }
public string? Host { get; set; }
public int? Port { get; set; }
public bool UseSSL { get; set; }
public string? Receivers { get; set; }
}
class Attachment
{
public string? AttachName { get; set; }
public string? AttachContent { get; set; }
}
}
}

@ -1,4 +1,5 @@
using System.ComponentModel;
using Masuit.Tools.Systems;
using SqlSugar;
namespace DS.WMS.Core.Info.Entity
@ -21,6 +22,12 @@ namespace DS.WMS.Core.Info.Entity
[SugarColumn(ColumnDescription = "客户ID", IsNullable = false)]
public long ClientId { get; set; }
/// <summary>
/// 客户简称
/// </summary>
[SugarColumn(IsIgnore = true)]
public string? ClientShortName { get; set; }
/// <summary>
/// 干系人ID
/// </summary>
@ -51,6 +58,12 @@ namespace DS.WMS.Core.Info.Entity
[SugarColumn(ColumnDescription = "干系人状态", IsNullable = false)]
public StakeholderStatus Status { get; set; }
/// <summary>
/// 干系人状态文本
/// </summary>
[SugarColumn(IsIgnore = true)]
public string StatusText => Status.GetDescription();
/// <summary>
/// 备注
/// </summary>

@ -43,12 +43,16 @@ namespace DS.WMS.Core.Info.Method
/// <returns></returns>
public async Task<DataResult> SubmitAuditAsync(IdModel idModel)
{
var list = await TenantDb.Queryable<InfoClientStakeholder>().Where(x => idModel.Ids.Contains(x.Id)).Select(x => new
{
x.Id,
x.Status,
x.CreateByName,
}).ToListAsync();
var list = await TenantDb.Queryable<InfoClientStakeholder>()
.InnerJoin<InfoClient>((x, y) => x.ClientId == y.Id)
.Where((x, y) => idModel.Ids.Contains(x.Id)).Select((x, y) => new
{
x.Id,
x.Status,
x.CreateBy,
x.CreateByName,
CustShortName = y.ShortName
}).ToListAsync();
if (list.Exists(x => x.Status == StakeholderStatus.Pending))
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.ItemsAreAuditing));
@ -59,7 +63,7 @@ namespace DS.WMS.Core.Info.Method
{
BusinessId = x.Id,
TaskTypeName = CLIENT_STAKEHOLDER_TASK.ToString(),
TaskTitle = $"【{CLIENT_STAKEHOLDER_TASK.GetDescription()}】{x.CreateByName}"
TaskTitle = $"【{CLIENT_STAKEHOLDER_TASK.GetDescription()}】{x.CustShortName} {x.CreateByName}"
});
await TenantDb.Ado.BeginTranAsync();
@ -244,6 +248,22 @@ namespace DS.WMS.Core.Info.Method
.Where(whereList)
.ToQueryPageAsync(request.PageCondition);
if (result.Data?.Count > 0)
{
var ids = result.Data.Select(x => x.ClientId).Distinct();
var clients = await TenantDb.Queryable<InfoClient>().Where(x => ids.Contains(x.Id))
.Select(x => new
{
x.Id,
x.ShortName
}).ToListAsync();
foreach (var item in result.Data)
{
item.ClientShortName = clients.Find(x => x.Id == item.ClientId)?.ShortName;
}
}
return result;
}

@ -15,6 +15,7 @@ using NLog.Web;
using DS.WMS.Core.HangfireJob.Method;
using DS.Module.HangfireModule;
using Hangfire;
using DS.WMS.Core.HangfireJob.Interface;
var builder = WebApplication.CreateBuilder(args);
var environment = builder.Environment.EnvironmentName;
@ -57,5 +58,6 @@ app.UsePublicMiddlewares();
app.UseHangfireServer();
app.UseJobMiddlewares();
JobMiddleware.RegisterJob<IFeeCustTemplateJobService>(j => j.GenerateFeesAsync(), Cron.Daily(23, 30));
app.Run();

@ -15,6 +15,8 @@ using DS.Module.SqlSugar;
using DS.Module.Swagger;
using DS.Module.UserModule;
using DS.WMS.Core;
using DS.WMS.Core.HangfireJob.Interface;
using DS.WMS.Core.HangfireJob.Method;
using Hangfire;
using NLog.Web;
using Swashbuckle.AspNetCore.SwaggerUI;
@ -48,7 +50,8 @@ builder.Services.AddMultiLanguageInstall();//
builder.Services.AddDjyModuleInstall();//Djy·þÎñ
builder.Services.AddRuleEngineModuleInstall();//Djy¹æÔòÒýÇæУÑé·þÎñ
builder.Services.AddHangfireModuleInstall();//Hangfire·þÎñ
//builder.Services.AddHangfireModuleInstall();//Hangfire·þÎñ
builder.Services.AddHangfireOPInstall();//Hangfire·þÎñ
builder.Services.AddMQModuleInstall();//MQ
// builder.Services.AddEndpointsApiExplorer();
@ -105,4 +108,7 @@ app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
//await ServiceLocator.Instance.GetService<ISysCommonCacheService>().LoadCommonCache();
app.UseHangfireServer();
JobMiddleware.RegisterJob<IWSLReportJobService>(j => j.GeneratReportAsync(), Cron.Daily(23, 59));
app.Run();

@ -111,7 +111,16 @@
"JsonPrint": "/printApi/OpenPrint/GetOpenJsonPrintInfoAsync",
"JsonPrintByCode": "/printApi/OpenPrint/GetOpenJsonPrintInfoByTemplateCode",
"SQLPrint": "/printApi/OpenPrint/GetOpenSqlPrintInfo",
"MailApiUrl": "http://47.104.73.97:8801/mail/send"
"MailApiUrl": "http://47.104.73.97:8801/mail/send",
"DefaultSetting": {
"Tenant": 1750335377144680448,
"Account": "lyle@yyts.fun",
"Password": "15275215387jZ",
"Host": "smtp.mxhichina.com",
"Port": 465,
"UseSSL": true,
"Receivers": "daisusu@dongshengsoft.com"
}
},
"FeeService": {
"BaseUrl": "http://118.190.144.189:3008",

Loading…
Cancel
Save