You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

549 lines
21 KiB

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using ClosedXML.Excel;
using LinqToDB.Data;
using LinqToDB.EntityFrameworkCore;
using Magicodes.ExporterAndImporter.Core;
using Magicodes.ExporterAndImporter.Core.Extension;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Extensions;
using RestSharp.Extensions;
using SettleAccount.Job.SignalR;
using SqlSugar;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.BlobStoring;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Users;
using Volo.Abp.Validation;
using Win.Sfs.BaseData.ImportExcelCommon;
using Win.Sfs.SettleAccount.Entities.BQ.Dtos;
using Win.Sfs.SettleAccount.Entities.BQ.Vmi;
using Win.Sfs.Shared.Filter;
using Win.Sfs.Shared.RepositoryBase;
namespace Win.Sfs.SettleAccount.Entities.BQ;
[AllowAnonymous]
[Route("api/settleaccount/[controller]/[action]")]
public class VmiAppService : ApplicationService, IJobService, ITransientDependency
{
public static SemaphoreSlim backupLock = new SemaphoreSlim(1);
private readonly IConfiguration _cfg;
private readonly IServiceProvider _serviceProvider;
private readonly INormalEfCoreRepository<VmiBalance, Guid> _balanceRepository;
private readonly INormalEfCoreRepository<VmiLog, Guid> _logRepository;
private readonly INormalEfCoreRepository<VmiSnapshot, Guid> _snapshotRepository;
private readonly IBlobContainer<MyFileContainer> _fileContainer;
private readonly IHubContext<PageHub> _hubContext;
private readonly ICurrentUser _currentUser;
public VmiAppService(IConfiguration cfg,
IServiceProvider serviceProvider,
INormalEfCoreRepository<VmiBalance, Guid> balanceRepository,
INormalEfCoreRepository<VmiLog, Guid> logRepository,
INormalEfCoreRepository<VmiSnapshot, Guid> snapshotRepository,
IBlobContainer<MyFileContainer> fileContainer,
IHubContext<PageHub> hubContext,
ICurrentUser currentUser)
{
this._cfg = cfg;
this._serviceProvider = serviceProvider;
this._balanceRepository = balanceRepository;
this._logRepository = logRepository;
this._snapshotRepository = snapshotRepository;
this._fileContainer = fileContainer;
this._hubContext = hubContext;
this._currentUser = currentUser;
LinqToDBForEFTools.Initialize();
}
[HttpPost]
public async Task Test(List<VmiLog> logs, int size = 1000)
{
using var scope = this._serviceProvider.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<SettleAccountDbContext>();
using var dc = db.CreateLinqToDBConnection();
dc.BeginTransaction();
try
{
//var skip = 0;
//var count = logs.Count;
//while (count > 0)
//{
// var batchSize = count > size ? size : count;
// var tempLogs = logs.Skip(skip).Take(batchSize);
// var tempMessages = tempLogs.Select(o => new VmiMessage { Message = JsonSerializer.Serialize(o) });
// await dc.BulkCopyAsync(new BulkCopyOptions { TableName = "Set_VmiLog" }, tempLogs).ConfigureAwait(false);
// await dc.BulkCopyAsync(new BulkCopyOptions { TableName = "Set_VmiMessage" }, tempMessages).ConfigureAwait(false);
// count -= batchSize;
// skip += batchSize;
//}
await dc.BulkCopyAsync(new BulkCopyOptions { TableName = "Set_VmiMessage", MaxBatchSize = 1 }, new List<VmiMessage> {
new VmiMessage(Guid.NewGuid()) { Message="1" },
new VmiMessage(Guid.NewGuid()) { Message="2" }
}).ConfigureAwait(false);
dc.Transaction.Commit();
}
catch
{
dc.Transaction.Rollback();
throw;
}
}
/// <summary>
/// Excel 转 JSON
/// </summary>
[HttpPost, Consumes("multipart/form-data")]
public async Task<List<PUB_ADJ_DETAIL_IMP_DTO>> ExcelToJSONAsync(List<IFormFile> files)
{
using var ms = new MemoryStream();
await files.FirstOrDefault().OpenReadStream().CopyToAsync(ms).ConfigureAwait(false);
return this.ImportInternal<PUB_ADJ_DETAIL_IMP_DTO>(ms.ToArray());
}
/// <summary>
/// 定时备份:0 0 8 26 * ?
/// </summary>
[NonAction]
[DisableValidation]
public virtual Task Invoke(IServiceProvider serviceProvider)
{
using var scope = serviceProvider.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<SettleAccountDbContext>();
using var transaction = db.Database.BeginTransaction();
try
{
var now = DateTime.Now;
var table = $"Set_VmiBalance_{now.ToString("yyyy_MM_dd_HH_mm_ss")}";
var snapshot = db.Set<VmiSnapshot>().Where(o => o.Name == table).FirstOrDefault();
if (snapshot == null)
{
snapshot = new VmiSnapshot { Name = now.ToString("yyyy-MM-dd HH:mm:ss"), Start = now, Description = table };
db.Set<VmiSnapshot>().Add(snapshot);
db.SaveChanges();
db.Database.ExecuteSqlRaw($"select * into {table} from Set_VmiBalance;");
db.Database.ExecuteSqlRaw($"create clustered index IX_{table} on {table} (BillTime)");
snapshot.End = DateTime.Now;
transaction.Commit();
return Task.CompletedTask;
}
throw new Exception("备份程序正在运行");
}
catch
{
transaction.Rollback();
throw;
}
}
/// <summary>
/// 定时备份:0 0 8 26 * ?
/// </summary>
[HttpPost("invoke")]
[DisableValidation]
public virtual async Task<IActionResult> VmiBackup()
{
if (backupLock.CurrentCount == 1)
{
return new JsonResult(new { Code = 500, Message = "备份程序正在运行" });
}
await backupLock.WaitAsync().ConfigureAwait(false);
try
{
await Invoke(_serviceProvider).ConfigureAwait(false);
return new JsonResult(new { Code = 200, Message = "备份成功" });
}
catch (Exception ex)
{
return new JsonResult(new { Code = 500, Message = ex.Message, Data = ex.ToString() });
}
finally
{
backupLock.Release();
}
}
/// <summary>
/// 1.库存余额查询
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost]
public async Task<PagedResultDto<VmiBalance>> Balance(RequestDto input)
{
var entities = await _balanceRepository.GetListByFilterAsync(input.Filters, input.Sorting, input.MaxResultCount, input.SkipCount, true).ConfigureAwait(false);
var totalCount = await _balanceRepository.GetCountByFilterAsync(input.Filters).ConfigureAwait(false);
return new PagedResultDto<VmiBalance>(totalCount, entities);
}
/// <summary>
/// 库存余额导出
/// </summary>
[HttpPost]
public async Task<string> BalanceExport(RequestDto input)
{
var entities = await _balanceRepository.GetListByFilterAsync(input.Filters).ConfigureAwait(false);
var fileName = $"库存余额_{DateTime.Now.ToString("yyyy-MM-dd_HH:mm:ss")}.xlsx";
var content = this.GetContent(entities, "库存备份");
await _fileContainer.SaveAsync(fileName, content, true).ConfigureAwait(false);
return fileName;
}
/// <summary>
/// 2.库存事务查询
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost]
public async Task<PagedResultDto<VmiLog>> Log(LogRequestDto input)
{
//按季度计算查询需要 union 的表名
var start = DateTime.Parse(input.Filters.FirstOrDefault(o => o.Column == "changedTime" && o.Action == EnumFilterAction.BiggerThanOrEqual).Value);
var end = DateTime.Parse(input.Filters.FirstOrDefault(o => o.Column == "changedTime" && o.Action == EnumFilterAction.SmallThan).Value);
var tables = new List<string>();
for (var time = start; time <= end; time = time.AddMonths(1))
{
var tableName = $"Set_VmiLog_{time.Year}_{(time.Month - 1) / 3 + 1}";
if (!tables.Contains(tableName))
{
tables.Add(tableName);
}
}
//不存在则创建table
tables.ForEach(tableName =>
{
using var scope = this._serviceProvider.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<SettleAccountDbContext>();
using var connection = context.Database.GetDbConnection();
connection.Open();
var cmd = connection.CreateCommand();
cmd.CommandText = $"select OBJECT_ID('{tableName}', 'U')";
var result = cmd.ExecuteScalar().ToString();
if (result == string.Empty)
{
cmd.CommandText = $"select * into {tableName} from Set_VmiLog;create clustered index IX_{tableName} on {tableName} (BillTime);";
cmd.ExecuteNonQuery();
}
});
//生成 union all 的 SQL
var sql = $"select * from {tables.First()}";
tables.Skip(1).ForEach(o => sql += $" union all select * from ${o}");
//使用 FromSqlRaw 查询
using var scope = this._serviceProvider.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<SettleAccountDbContext>();
var query = db.Set<VmiLog>().FromSqlRaw(sql);
var filters = input.Filters.ToLambda<VmiLog>();
if (input.Filters.Count > 0)
{
query = query.Where(input.Filters.ToLambda<VmiLog>());
}
var totalCount = query.Count();
query = string.IsNullOrEmpty(input.Sorting) ? query : DynamicQueryableExtensions.OrderBy(query, input.Sorting);
var entities = await query.PageBy(input.SkipCount, input.MaxResultCount).ToListAsync().ConfigureAwait(false);
return new PagedResultDto<VmiLog>(totalCount, entities);
}
/// <summary>
/// 库存事务导出
/// </summary>
[HttpPost]
public async Task<string> LogExport(RequestDto input)
{
var entities = await _logRepository.GetListByFilterAsync(input.Filters).ConfigureAwait(false);
var fileName = $"库存事务_{DateTime.Now.ToString("yyyy-MM-dd_HH:mm:ss")}.xlsx";
var content = this.GetContent(entities, "库存事务_");
await _fileContainer.SaveAsync(fileName, content, true).ConfigureAwait(false);
return fileName;
}
/// <summary>
/// 补货数据导出
/// </summary>
[HttpPost]
public async Task<string> ReplenishedExportAsync(RequestDto input)
{
var entities = await _logRepository.GetListByFilterAsync(input.Filters).ConfigureAwait(false);
//IQueryable<VmiLog> query = _logRepository.WhereIf(input.Filters?.Count != 0, input.Filters.ToLambda<VmiLog>());
var fileName = $"补货数据_{DateTime.Now.ToString("yyyy-MM-dd_HH:mm:ss")}.xlsx";
var content = this.GetContent(entities, "补货数据_");
await _fileContainer.SaveAsync(fileName, content, true).ConfigureAwait(false);
return fileName;
}
/// <summary>
/// 4.寄售库存调整
/// </summary>
/// <param name="log"></param>
/// <returns></returns>
[HttpPost]
public async Task EditBalance(VmiLog log)
{
log.SetId(GuidGenerator.Create());
if (log.ChangedQty >= decimal.Zero)
{
log.Qty = log.ChangedQty;
log.LogType = VmiLogType.Type500;
log.ChangedType = VmiType.In;
}
else
{
log.Qty = -log.Qty;
log.LogType = VmiLogType.Type600;
log.ChangedType = VmiType.Out;
}
log.ChangedBy = this._currentUser.UserName;
}
/// <summary>
/// 库存调整导入
/// </summary>
/// <param name="files"></param>
[HttpPost]
public async Task Import(List<IFormFile> files)
{
using var ms = new MemoryStream();
await files.FirstOrDefault().OpenReadStream().CopyToAsync(ms).ConfigureAwait(false);
var list = this.ImportInternal<VmiLog>(ms.ToArray());
foreach (var file in list)
{
await EditBalance(file).ConfigureAwait(false);
}
}
/// <summary>
/// 快照列表
/// </summary>
[HttpPost]
public async Task<ListResultDto<VmiSnapshot>> Snapshot()
{
var list = await _snapshotRepository.GetListAsync().ConfigureAwait(false);
return new ListResultDto<VmiSnapshot>(list);
}
/// <summary>
/// 3.时点库存查询
/// </summary>
[HttpPost]
public async Task<PagedResultDto<VmiBalance>> Backup(BackupListRequest input)
{
using var scope = this._serviceProvider.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<SettleAccountDbContext>();
var name = input.Name;
var sql = $"select * from {name}";
var query = db.Set<VmiBalance>().FromSqlRaw(sql);
var filters = input.Filters.ToLambda<VmiBalance>();
if (input.Filters.Count > 0)
{
query = query.Where(input.Filters.ToLambda<VmiBalance>());
}
var totalCount = query.Count();
query = string.IsNullOrEmpty(input.Sorting) ? query : DynamicQueryableExtensions.OrderBy(query, input.Sorting);
var entities = await query.PageBy(input.SkipCount, input.MaxResultCount).ToListAsync().ConfigureAwait(false);
return new PagedResultDto<VmiBalance>(totalCount, entities);
}
/// <summary>
/// 时点库存导出
/// </summary>
[HttpPost]
public async Task<string> BackupExport(BackupListRequest input)
{
using var scope = this._serviceProvider.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<SettleAccountDbContext>();
var name = input.Name;
var sql = $"select * from {name}";
var query = db.Set<VmiBalance>().FromSqlRaw(sql);
var filters = input.Filters.ToLambda<VmiBalance>();
if (input.Filters.Count > 0)
{
query = query.Where(input.Filters.ToLambda<VmiBalance>());
}
var totalCount = query.Count();
query = string.IsNullOrEmpty(input.Sorting) ? query : DynamicQueryableExtensions.OrderBy(query, input.Sorting);
var entities = await query.ToListAsync().ConfigureAwait(false);
var fileName = $"库存备份_{input.Name}.xlsx";
var content = this.GetContent(entities, "库存备份");
await _fileContainer.SaveAsync(fileName, content, true).ConfigureAwait(false);
return fileName;
}
private List<T> ImportInternal<T>(byte[] data)
{
var list = new List<T>();
using var workbook = new XLWorkbook(new MemoryStream(data));
var properties = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty)
.Where(o => o.GetAttributes<ImporterHeaderAttribute>().Any())
.ToDictionary(o => o.GetAttribute<ImporterHeaderAttribute>().Name, o => o);
var ws = workbook.Worksheets.FirstOrDefault();
for (int rowIndex = 2; rowIndex <= ws.RowsUsed().Count(); rowIndex++)
{
var row = ws.Row(rowIndex);
var model = Activator.CreateInstance<T>();
list.Add(model);
for (var columnIndex = 1; columnIndex < ws.ColumnsUsed().Count(); columnIndex++)
{
var cell = row.Cell(columnIndex);
var headerName = ws.Cell(1, columnIndex).Value.ToString().Trim();
if (properties.TryGetValue(headerName, out var property))
{
var value = cell.Value.ToString();
if (!string.IsNullOrEmpty(value))
{
property.SetValue(model, Convert.ChangeType(value, property.PropertyType));
}
}
}
}
return list;
}
private byte[] GetContent<TExport>(List<TExport> entities, string name = "sheet1")
{
using var workbook = new XLWorkbook();
var ws = workbook.Worksheets.Add(name);
var type = typeof(TExport);
var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty)
.Where(o => o.GetAttributes<DisplayAttribute>().Any())
.ToList();
var rowIndex = 1;
for (var i = 0; i < properties.Count; i++)
{
var property = properties[i];
var columnIndex = i + 1;
var cell = ws.Cell(1, columnIndex);
cell.Value = property.GetAttributes<DisplayAttribute>().Any() ? property.GetAttribute<DisplayAttribute>().Name : property.Name;
}
foreach (var item in entities)
{
rowIndex++;
for (var i = 0; i < properties.Count; i++)
{
var property = properties[i];
var columnIndex = i + 1;
var cell = ws.Cell(rowIndex, columnIndex);
SetCell(item, cell, property);
}
}
ws.RangeUsed().Style.Border.TopBorder =
ws.RangeUsed().Style.Border.RightBorder =
ws.RangeUsed().Style.Border.BottomBorder =
ws.RangeUsed().Style.Border.LeftBorder = XLBorderStyleValues.Thin;
ws.RangeUsed().Style.Border.TopBorderColor =
ws.RangeUsed().Style.Border.RightBorderColor =
ws.RangeUsed().Style.Border.BottomBorderColor =
ws.RangeUsed().Style.Border.LeftBorderColor = XLColor.Black;
ws.RangeUsed().SetAutoFilter();
ws.ColumnsUsed().AdjustToContents();
ws.RowsUsed().AdjustToContents();
using var stream = new MemoryStream();
workbook.SaveAs(stream);
stream.Seek(0, SeekOrigin.Begin);
return stream.ToArray();
}
private static void SetCell<TExportModel>(TExportModel? model, IXLCell cell, PropertyInfo property)
{
var propertyType = property.PropertyType;
var value = property.GetValue(model)?.ToString()?.Trim();
if (string.IsNullOrEmpty(value))
{
return;
}
if (propertyType == typeof(bool))
{
cell.Value = (bool)property.GetValue(model)! ? "是" : "否";
}
else if (propertyType.IsEnum)
{
cell.Value = (Enum.Parse(propertyType, value) as Enum)?.GetDisplayName();
}
else if (propertyType == typeof(DateTime))
{
cell.Value = ((DateTime)property.GetValue(model)!).ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
}
else
{
cell.Value = value;
}
}
internal Task Out(VmiLogType type200, string v, VmiLog vmiLog)
{
throw new NotImplementedException();
}
//private void SetPropertyValue(PropertyInfo property, VmiLog entity, string value)
//{
// try
// {
// object propertyValue = null;
// if (!string.IsNullOrEmpty(value))
// {
// propertyValue = Convert.ChangeType(value, property.PropertyType);
// }
// property.SetValue(entity, propertyValue, null);
// }
// catch (Exception)
// {
// throw;
// }
//}
//private string GetOperator(EnumFilterAction action)
//{
// var dictonary = new Dictionary<EnumFilterAction, string>() {
// {EnumFilterAction.Equal,"={0}"},
// {EnumFilterAction.NotEqual,"!={0}"},
// {EnumFilterAction.SmallThanOrEqual,"<={0}"},
// {EnumFilterAction.Like,"~/{0}/"},
// {EnumFilterAction.NotLike,"!~/{0}/"},
// {EnumFilterAction.BiggerThan,">{0}"},
// {EnumFilterAction.BiggerThanOrEqual,">={0}"},
// {EnumFilterAction.SmallThan,"<{0}"},
// };
// return dictonary[action];
//}
//private object GetValue(PropertyInfo property, string value)
//{
// if (property.PropertyType == typeof(int) ||
// property.PropertyType == typeof(long) ||
// property.PropertyType == typeof(float) ||
// property.PropertyType == typeof(double) ||
// property.PropertyType == typeof(decimal) ||
// property.PropertyType == typeof(bool))
// {
// return value;
// }
// return $"'{value}'";
//}
}
public class BackupListRequest : RequestDto
{
[Required]
public string Name { get; set; }
}
public class LogRequestDto : RequestDto
{
public List<VmiLogType> LogTypes { get; set; } = new List<VmiLogType>();
}