| | | 1 | | using AutoMapper; |
| | | 2 | | using SkiaSharp; |
| | | 3 | | using ExcelDataReader; |
| | | 4 | | using Microsoft.AspNetCore.Authorization; |
| | | 5 | | using Microsoft.AspNetCore.Http; |
| | | 6 | | using Microsoft.AspNetCore.Mvc; |
| | | 7 | | using Microsoft.Extensions.Logging; |
| | | 8 | | using Microsoft.Extensions.Options; |
| | | 9 | | using SVETA.Api.Data.Domain; |
| | | 10 | | using SVETA.Api.Data.DTO; |
| | | 11 | | using SVETA.Api.Data.DTO.Goods; |
| | | 12 | | using SVETA.Api.Data.DTO.Prices.PriceTrend; |
| | | 13 | | using SVETA.Api.Services.Interfaces; |
| | | 14 | | using Swashbuckle.AspNetCore.Annotations; |
| | | 15 | | using System; |
| | | 16 | | using System.Collections.Generic; |
| | | 17 | | using System.Data; |
| | | 18 | | using System.IO; |
| | | 19 | | using System.Linq; |
| | | 20 | | using System.Text; |
| | | 21 | | using System.Threading.Tasks; |
| | | 22 | | using WinSolutions.Sveta.Common; |
| | | 23 | | using WinSolutions.Sveta.Common.Extensions; |
| | | 24 | | using WinSolutions.Sveta.Server.Data.DataLoading; |
| | | 25 | | using WinSolutions.Sveta.Server.Data.DataModel.Entities; |
| | | 26 | | using WinSolutions.Sveta.Server.Services.Interfaces; |
| | | 27 | | using DocumentFormat.OpenXml.Office2010.Excel; |
| | | 28 | | using SVETA.Api.Services.Interfaces.ImportingExporting; |
| | | 29 | | |
| | | 30 | | namespace SVETA.Api.Controllers |
| | | 31 | | { |
| | | 32 | | /// <summary> |
| | | 33 | | /// Загрузка данных из файлов |
| | | 34 | | /// </summary> |
| | | 35 | | /// <remarks>author: tochilin.o</remarks> |
| | | 36 | | [Route("api/v1/Upload")] |
| | | 37 | | [ApiController] |
| | | 38 | | [RequestSizeLimit(2_000_000_000)] |
| | | 39 | | [RequestFormLimits(MultipartBodyLengthLimit = 2_000_000_000)] |
| | | 40 | | public partial class UploadController : SvetaController |
| | | 41 | | { |
| | | 42 | | const string _routeUrl = "api/v1/Upload"; |
| | | 43 | | |
| | | 44 | | private readonly IUploadService service; |
| | | 45 | | private readonly ILogger<GoodsController> logger; |
| | | 46 | | private readonly IOptions<ImagesSettings> imagesSettings; |
| | | 47 | | private readonly IPriceWorker _priceWorker; |
| | | 48 | | private readonly IUploadWorker _uploadWorker; |
| | | 49 | | private readonly ImagesSettings _imageSettings; |
| | | 50 | | readonly IDepartmentService _departmentService; |
| | | 51 | | private readonly IAuthenticationService _authService; |
| | | 52 | | IDiskStorageService _diskStorage; |
| | | 53 | | CommonSettings _commonSettings; |
| | | 54 | | IImportService _importService; |
| | | 55 | | |
| | | 56 | | public UploadController(IUploadService service,ILogger<GoodsController> logger,IOptions<ImagesSettings> imagesSe |
| | | 57 | | IAuthenticationService authService, IDepartmentService departmentService, IDiskStorageService diskStorage, |
| | | 58 | | IOptions<CommonSettings> commonSettings, IUploadWorker uploadWorker, IImportService importService) |
| | 0 | 59 | | : base(logger) |
| | 0 | 60 | | { |
| | 0 | 61 | | this.service = service; |
| | 0 | 62 | | this.logger = logger; |
| | 0 | 63 | | this.imagesSettings = imagesSettings; |
| | 0 | 64 | | _uploadWorker = uploadWorker; |
| | 0 | 65 | | _priceWorker = priceWorker; |
| | 0 | 66 | | _imageSettings = options.Value; |
| | 0 | 67 | | _authService = authService; |
| | 0 | 68 | | _departmentService = departmentService; |
| | 0 | 69 | | _diskStorage = diskStorage; |
| | 0 | 70 | | _commonSettings = commonSettings.Value; |
| | 0 | 71 | | _importService = importService; |
| | 0 | 72 | | } |
| | | 73 | | |
| | | 74 | | |
| | | 75 | | void LogLoadBegins(string methodName, string fileName) |
| | 0 | 76 | | { |
| | 0 | 77 | | logger.LogInformation($"{methodName}: пользователь {_authService.UserId} начал загружать файл '{fileName}'") |
| | 0 | 78 | | } |
| | | 79 | | |
| | | 80 | | void LogLoadEnds(string methodName, string fileName, LoadingResult res) |
| | 0 | 81 | | { |
| | 0 | 82 | | logger.LogInformation($"{methodName}: пользователь {_authService.UserId} загрузил файл '{fileName}', сохране |
| | 0 | 83 | | } |
| | | 84 | | |
| | | 85 | | void LogLoadEnds(string methodName, string fileName, bool hasErrors) |
| | 0 | 86 | | { |
| | 0 | 87 | | logger.LogInformation($"{methodName}: пользователь {_authService.UserId} загрузил файл '{fileName}', " + |
| | 0 | 88 | | $", {(hasErrors ? "есть ошибки" : "ошибок нет")}"); |
| | 0 | 89 | | } |
| | | 90 | | |
| | | 91 | | /// <summary> |
| | | 92 | | /// Возвращает товары из загрузки по ее id |
| | | 93 | | /// </summary> |
| | | 94 | | /// <param name="id">id загрузки</param> |
| | | 95 | | /// <param name="page">пейджинг: номер страницы (Любое значение ниже нуля изменится на 1)</param> |
| | | 96 | | /// <param name="limit">пейджинг: размер страницы (Любое значение ниже нуля изменится на 10)</param> |
| | | 97 | | /// <param name="filter">фильтр по значимым полям (Name, Barcode)</param> |
| | | 98 | | /// <param name="sort">сортировка по полям name,name|desc, brandName,brandName|desc, По умолчанию по name</param |
| | | 99 | | /// <remarks>author: oboligatov</remarks> |
| | | 100 | | [HttpGet("{id}/Goods")] |
| | | 101 | | [SwaggerResponse(200, "Успешно", typeof(BaseResponseDTO<GoodCatalogDTO>))] |
| | | 102 | | [SwaggerResponse(500, "Ошибка на стороне сервера", typeof(ErrorDTO))] |
| | | 103 | | [Authorize(Roles = Role.SystemAdmin + "," + Role.SystemOperator)] |
| | | 104 | | public async Task<IActionResult> GetGoodsFromUpload(long id, int page = 1, int limit = 10, string filter = null, |
| | 0 | 105 | | { |
| | 0 | 106 | | var upload = await service.GetUpload(id); |
| | 0 | 107 | | if(upload == null) |
| | 0 | 108 | | { |
| | 0 | 109 | | throw new KeyNotFoundException($"Загрузка {id} не найдена"); |
| | | 110 | | } |
| | | 111 | | |
| | 0 | 112 | | page = page < 1 ? 1 : page; |
| | 0 | 113 | | limit = limit < 1 ? 10 : limit; |
| | | 114 | | |
| | 0 | 115 | | var goods = await service.GetGoodsFromUpload(upload, page - 1, limit, filter, sort); |
| | 0 | 116 | | var totalCount = await service.GetGoodsFromUploadCount(upload, null); |
| | 0 | 117 | | var totalFiltredCount = await service.GetGoodsFromUploadCount(upload, filter); |
| | 0 | 118 | | var response = new BaseResponseDTO<GoodCatalogDTO>(_routeUrl, page, limit, totalFiltredCount, totalCount, so |
| | 0 | 119 | | { |
| | 0 | 120 | | Data = ToCatalogGoodDtoMapper(_imageSettings).Map<List<GoodCatalogDTO>>(goods) |
| | 0 | 121 | | }; |
| | | 122 | | |
| | 0 | 123 | | return Ok(response); |
| | 0 | 124 | | } |
| | | 125 | | |
| | | 126 | | /// <summary> |
| | | 127 | | /// Загрузка файла товаров |
| | | 128 | | /// </summary> |
| | | 129 | | /// <param name="file">Файл</param> |
| | | 130 | | /// <returns>succeed, errorCount, reportUrl, uploadId</returns> |
| | | 131 | | [HttpPost("UploadGoods")] |
| | | 132 | | [SwaggerResponse(200, "Успешно", typeof(UploadResultDTO))] |
| | | 133 | | [SwaggerResponse(500, "Ошибка на стороне сервера", typeof(ErrorDTO))] |
| | | 134 | | [Authorize(Roles = Role.SystemAdmin)] |
| | | 135 | | public async Task<IActionResult> UploadGoods(IFormFile file) |
| | 0 | 136 | | { |
| | 0 | 137 | | LoadingResult res = null; |
| | 0 | 138 | | using (var stream = _diskStorage.SaveUpload(file, out string fileName)) |
| | 0 | 139 | | { |
| | 0 | 140 | | LogLoadBegins("UploadGoods", fileName); |
| | | 141 | | |
| | 0 | 142 | | var savePath = imagesSettings.Value.ImageSavePath; |
| | | 143 | | |
| | 0 | 144 | | res = await service.UploadGoods(stream, r => |
| | 0 | 145 | | { |
| | 0 | 146 | | if(r is Photo p) |
| | 0 | 147 | | { |
| | 0 | 148 | | var full = p.FullSizeUrl; |
| | 0 | 149 | | var prev = p.PreviewUrl; |
| | 0 | 150 | | |
| | 0 | 151 | | if(!string.IsNullOrEmpty(full)) |
| | 0 | 152 | | { |
| | 0 | 153 | | p.FullSizePath = Path.Combine(savePath, full); |
| | 0 | 154 | | } |
| | 0 | 155 | | |
| | 0 | 156 | | if (!string.IsNullOrEmpty(prev)) |
| | 0 | 157 | | { |
| | 0 | 158 | | p.PreviewPath = Path.Combine(savePath, prev); |
| | 0 | 159 | | } |
| | 0 | 160 | | } |
| | 0 | 161 | | }, |
| | 0 | 162 | | getPhotoSize => |
| | 0 | 163 | | { |
| | 0 | 164 | | var errors = new List<string>(); |
| | 0 | 165 | | |
| | 0 | 166 | | int width = 0; |
| | 0 | 167 | | int height = 0; |
| | 0 | 168 | | |
| | 0 | 169 | | if (!string.IsNullOrEmpty(getPhotoSize.FullSizeUrl)) |
| | 0 | 170 | | { |
| | 0 | 171 | | if (_diskStorage.GetPictureSize(getPhotoSize.FullSizeUrl, out width, out height)) |
| | 0 | 172 | | { |
| | 0 | 173 | | getPhotoSize.FullSizeWidth = width; |
| | 0 | 174 | | getPhotoSize.FullSizeHeight = height; |
| | 0 | 175 | | } |
| | 0 | 176 | | else |
| | 0 | 177 | | { |
| | 0 | 178 | | errors.Add(getPhotoSize.FullSizeUrl); |
| | 0 | 179 | | getPhotoSize.FullSizeUrl = null; |
| | 0 | 180 | | } |
| | 0 | 181 | | } |
| | 0 | 182 | | |
| | 0 | 183 | | if (!string.IsNullOrEmpty(getPhotoSize.PreviewUrl)) |
| | 0 | 184 | | { |
| | 0 | 185 | | if (_diskStorage.GetPictureSize(getPhotoSize.PreviewUrl, out width, out height)) |
| | 0 | 186 | | { |
| | 0 | 187 | | getPhotoSize.PreviewWidth = width; |
| | 0 | 188 | | getPhotoSize.PreviewHeight = height; |
| | 0 | 189 | | } |
| | 0 | 190 | | else |
| | 0 | 191 | | { |
| | 0 | 192 | | if (!errors.Contains(getPhotoSize.PreviewUrl)) |
| | 0 | 193 | | { |
| | 0 | 194 | | errors.Add(getPhotoSize.PreviewUrl); |
| | 0 | 195 | | } |
| | 0 | 196 | | getPhotoSize.PreviewUrl = null; |
| | 0 | 197 | | } |
| | 0 | 198 | | } |
| | 0 | 199 | | |
| | 0 | 200 | | if (errors.Any()) |
| | 0 | 201 | | { |
| | 0 | 202 | | throw new Exception($"Файлы картинок не найдены: {string.Join(", ", errors.ToArray())}"); |
| | 0 | 203 | | } |
| | 0 | 204 | | }, DetectFileType(file)); |
| | | 205 | | |
| | 0 | 206 | | LogLoadEnds("UploadGoods", fileName, res); |
| | 0 | 207 | | } |
| | | 208 | | |
| | 0 | 209 | | return ToResult(res); |
| | 0 | 210 | | } |
| | | 211 | | |
| | | 212 | | |
| | | 213 | | /// <summary> |
| | | 214 | | /// Загрузка ZIP файла с фотографиями |
| | | 215 | | /// </summary> |
| | | 216 | | /// <param name="file">Файл</param> |
| | | 217 | | /// <returns></returns> |
| | | 218 | | [HttpPost("UploadPhotos")] |
| | | 219 | | [SwaggerResponse(200, "Успешно", typeof(UploadResultDTO))] |
| | | 220 | | [SwaggerResponse(500, "Ошибка на стороне сервера", typeof(ErrorDTO))] |
| | | 221 | | [Authorize(Roles = Role.SystemAdmin)] |
| | | 222 | | [Consumes("multipart/form-data")] |
| | | 223 | | public async Task<IActionResult> UploadPhotos(IFormFile file) |
| | 0 | 224 | | { |
| | 0 | 225 | | LoadingResult res = null; |
| | 0 | 226 | | using (var stream = _diskStorage.SaveUpload(file, out string fileName)) |
| | 0 | 227 | | { |
| | 0 | 228 | | LogLoadBegins("UploadPhotos", fileName); |
| | | 229 | | |
| | 0 | 230 | | var savePath = imagesSettings.Value.ImageSavePath; |
| | | 231 | | |
| | 0 | 232 | | res = await service.UploadPhotos(stream, r => |
| | 0 | 233 | | { |
| | 0 | 234 | | if(r is Photo p) |
| | 0 | 235 | | { |
| | 0 | 236 | | var full = p.FullSizeUrl; |
| | 0 | 237 | | var prev = p.PreviewUrl; |
| | 0 | 238 | | prev = prev.Replace(Path.GetExtension(prev), "_prev.jpg"); //для превью всегда jpg |
| | 0 | 239 | | |
| | 0 | 240 | | p.FullSizeUrl = full; |
| | 0 | 241 | | p.PreviewUrl = prev; |
| | 0 | 242 | | p.PreviewHeight = imagesSettings.Value.ImagePreveiwHeight; //передаем максимальную высоту для пр |
| | 0 | 243 | | p.PreviewWidth = imagesSettings.Value.ImagePreveiwWidth;//передаем максимальную ширину для превь |
| | 0 | 244 | | p.FullSizePath = Path.Combine(savePath, full); |
| | 0 | 245 | | p.PreviewPath = Path.Combine(savePath, prev); |
| | 0 | 246 | | } |
| | 0 | 247 | | }); |
| | | 248 | | |
| | 0 | 249 | | LogLoadEnds("UploadPhotos", fileName, res); |
| | 0 | 250 | | } |
| | | 251 | | |
| | 0 | 252 | | return ToResult(res); |
| | 0 | 253 | | } |
| | | 254 | | |
| | | 255 | | |
| | | 256 | | /// <summary> |
| | | 257 | | /// Загрузка файла остатков |
| | | 258 | | /// </summary> |
| | | 259 | | /// <param name="departmentId">id департмента</param> |
| | | 260 | | [HttpPost("UploadRests/{departmentId}")] |
| | | 261 | | [SwaggerResponse(200, "Успешно", typeof(UploadResultDTO))] |
| | | 262 | | [SwaggerResponse(500, "Ошибка на стороне сервера", typeof(ErrorDTO))] |
| | | 263 | | [Authorize(Roles = Role.SystemAdmin + "," + Role.SupplierOwner + "," + Role.SupplierOperator)] |
| | | 264 | | public async Task<IActionResult> UploadRests(long departmentId, IFormFile file) |
| | 0 | 265 | | { |
| | 0 | 266 | | await CheckDepartment(departmentId); |
| | 0 | 267 | | var res = await _importService.ImportRests(departmentId, file); |
| | 0 | 268 | | return ToResult(res); |
| | 0 | 269 | | } |
| | | 270 | | |
| | | 271 | | /// <summary> |
| | | 272 | | /// Загрузка сеттингов товара из файла |
| | | 273 | | /// </summary> |
| | | 274 | | /// <param name="departmentId">id департмента</param> |
| | | 275 | | [HttpPost("GoodSettings/{departmentId}")] |
| | | 276 | | [SwaggerResponse(200, "Успешно", typeof(UploadResultDTO))] |
| | | 277 | | [SwaggerResponse(500, "Ошибка на стороне сервера", typeof(ErrorDTO))] |
| | | 278 | | [Authorize(Roles = Role.SystemAdmin + "," + Role.SupplierOwner + "," + Role.SupplierOperator)] |
| | | 279 | | public async Task<IActionResult> UploadGoodSettings(long departmentId, IFormFile file) |
| | 0 | 280 | | { |
| | 0 | 281 | | await CheckDepartment(departmentId); |
| | 0 | 282 | | var res = await _importService.ImportGoodSettings(departmentId, file); |
| | 0 | 283 | | return ToResult(res); |
| | 0 | 284 | | } |
| | | 285 | | |
| | | 286 | | string DetectFileType(IFormFile file) |
| | 0 | 287 | | { |
| | 0 | 288 | | switch(file.ContentType) |
| | | 289 | | { |
| | 0 | 290 | | case "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": return "excel"; |
| | | 291 | | case "application/vnd.ms-excel": |
| | 0 | 292 | | case "text/csv": return "csv"; |
| | 0 | 293 | | default: throw new ArgumentException($"File type {file.Name} not supported"); |
| | | 294 | | } |
| | 0 | 295 | | } |
| | | 296 | | |
| | | 297 | | FileContentResult ReturnFile(MemoryStream stream, string fileName, string fileType = "excel") |
| | 0 | 298 | | { |
| | 0 | 299 | | if (fileType.ToLower() == "csv") |
| | 0 | 300 | | { |
| | 0 | 301 | | return File(stream.ToArray(), "text/csv", fileName); |
| | | 302 | | } |
| | | 303 | | else |
| | 0 | 304 | | { |
| | 0 | 305 | | return File(stream.ToArray(), |
| | 0 | 306 | | "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", |
| | 0 | 307 | | fileName); |
| | | 308 | | } |
| | 0 | 309 | | } |
| | | 310 | | |
| | | 311 | | /// <summary> |
| | | 312 | | /// Получение файла шаблона для загрузки сеттингов |
| | | 313 | | /// </summary> |
| | | 314 | | /// <param name="fileType">формат выходного файла (excel, csv), по умолчанию excel</param> |
| | | 315 | | /// <returns>file</returns> |
| | | 316 | | [HttpGet("GoodSettingsTemplate")] |
| | | 317 | | [Authorize(Roles = Role.SystemAdmin + "," + Role.SystemOperator + "," + Role.SupplierOwner + "," + Role.Supplier |
| | | 318 | | public async Task<IActionResult> GetGoodSettingsTemplate(string fileType = "excel") |
| | 0 | 319 | | { |
| | 0 | 320 | | var stream = await _importService.GetGoodSettingsTemplate(); |
| | 0 | 321 | | return ReturnFile(stream, "GoodSettings-template.xlsx"); |
| | 0 | 322 | | } |
| | | 323 | | |
| | | 324 | | /// <summary> |
| | | 325 | | /// Получение файла шаблона для загрузки остатков |
| | | 326 | | /// </summary> |
| | | 327 | | /// <param name="fileType">формат выходного файла (excel, csv), по умолчанию excel</param> |
| | | 328 | | /// <returns>file</returns> |
| | | 329 | | [HttpGet("GetRestsTemplate")] |
| | | 330 | | [Authorize(Roles = Role.SystemAdmin + "," + Role.SystemOperator + "," + Role.SupplierOwner + "," + Role.Supplier |
| | | 331 | | public async Task<IActionResult> GetRestsTemplate(string fileType = "excel") |
| | 0 | 332 | | { |
| | 0 | 333 | | var stream = await _importService.GetRestsTemplate(); |
| | 0 | 334 | | return ReturnFile(stream, "Rests-template.xlsx"); |
| | 0 | 335 | | } |
| | | 336 | | |
| | | 337 | | /// <summary> |
| | | 338 | | /// Получение файла шаблона для загрузки товаров |
| | | 339 | | /// </summary> |
| | | 340 | | /// <param name="fileType">формат выходного файла (excel, csv), по умолчанию excel</param> |
| | | 341 | | /// <returns>file</returns> |
| | | 342 | | [HttpGet("GetGoodsTemplate")] |
| | | 343 | | [Authorize(Roles = Role.SystemAdmin + "," + Role.SystemOperator)] |
| | | 344 | | public async Task<IActionResult> GetGoodsTemplate(string fileType = "excel") |
| | 0 | 345 | | { |
| | 0 | 346 | | fileType = fileType.ToLower(); |
| | 0 | 347 | | var resStream = new MemoryStream(); |
| | | 348 | | |
| | 0 | 349 | | await service.GetGoodsTemplate(resStream, fileType); |
| | 0 | 350 | | resStream.Position = 0; |
| | | 351 | | |
| | 0 | 352 | | if (fileType == "csv") |
| | 0 | 353 | | { |
| | 0 | 354 | | Response.Headers.Add("Content-Disposition", $"attachment;filename=goods-template.csv"); |
| | 0 | 355 | | return File(resStream, "text/csv"); |
| | | 356 | | } |
| | | 357 | | else |
| | 0 | 358 | | { |
| | 0 | 359 | | return File(resStream.ToArray(), |
| | 0 | 360 | | "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", |
| | 0 | 361 | | "goods-template.xlsx"); |
| | | 362 | | } |
| | 0 | 363 | | } |
| | | 364 | | |
| | | 365 | | /// <summary> |
| | | 366 | | /// Отмена загрузки товаров |
| | | 367 | | /// </summary> |
| | | 368 | | /// <param name="id">Id загрузки</param> |
| | | 369 | | /// <returns>succeed, errorCount, reportUrl, uploadId</returns> |
| | | 370 | | [HttpPost("RollbackGoods")] |
| | | 371 | | [Authorize(Roles = Role.SystemAdmin)] |
| | | 372 | | public async Task<IActionResult> RollbackGoods(long id) |
| | 0 | 373 | | { |
| | 0 | 374 | | await service.RollbackGoods(id); |
| | | 375 | | |
| | 0 | 376 | | return Ok(); |
| | 0 | 377 | | } |
| | | 378 | | |
| | | 379 | | |
| | | 380 | | /// <summary> |
| | | 381 | | /// Подтверждение товаров |
| | | 382 | | /// </summary> |
| | | 383 | | /// <param name="id">Id загрузки</param> |
| | | 384 | | /// <returns>succeed, errorCount, reportUrl, uploadId</returns> |
| | | 385 | | [HttpPost("CommitGoods")] |
| | | 386 | | [Authorize(Roles = Role.SystemAdmin)] |
| | | 387 | | public async Task<IActionResult> CommitGoods(long id) |
| | 0 | 388 | | { |
| | 0 | 389 | | await service.CommitGoods(id); |
| | | 390 | | |
| | 0 | 391 | | return Ok(); |
| | 0 | 392 | | } |
| | | 393 | | |
| | | 394 | | |
| | | 395 | | /// <summary> |
| | | 396 | | /// Получение отчета по загрузке |
| | | 397 | | /// </summary> |
| | | 398 | | /// <param name="id">Id загрузки</param> |
| | | 399 | | /// <returns>Файл отчета</returns> |
| | | 400 | | [HttpGet("GetReport")] |
| | | 401 | | [Authorize(Roles = Role.SystemAdmin + "," + Role.SystemOperator + "," + Role.SupplierOwner + "," + Role.Supplier |
| | | 402 | | public IActionResult GetReport(long id) |
| | 0 | 403 | | { |
| | 0 | 404 | | var fileName = $"upload-report{id}.xlsx"; |
| | 0 | 405 | | var stream = _diskStorage.ReadDownload(fileName); |
| | | 406 | | |
| | 0 | 407 | | var ms = new MemoryStream(); |
| | 0 | 408 | | stream.CopyTo(ms); |
| | | 409 | | |
| | 0 | 410 | | return File(ms.ToArray(), |
| | 0 | 411 | | "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", |
| | 0 | 412 | | fileName); |
| | 0 | 413 | | } |
| | | 414 | | |
| | | 415 | | /// <summary> |
| | | 416 | | /// Получение отчета по загрузке переоценок |
| | | 417 | | /// </summary> |
| | | 418 | | /// <param name="id">Id загрузки</param> |
| | | 419 | | /// <returns>Файл отчета</returns> |
| | | 420 | | [HttpGet("GetReportPriceTrend")] |
| | | 421 | | [Authorize(Roles = Role.SystemAdmin + "," + Role.SystemOperator + "," + Role.SupplierOwner + "," + Role.Supplier |
| | | 422 | | public IActionResult GetReportPriceTrend(long id) |
| | 0 | 423 | | { |
| | 0 | 424 | | var fileName = $"price_trend_errors{id}.xlsx"; |
| | 0 | 425 | | var stream = _diskStorage.ReadDownload(fileName); |
| | | 426 | | |
| | 0 | 427 | | var ms = new MemoryStream(); |
| | 0 | 428 | | stream.CopyTo(ms); |
| | | 429 | | |
| | 0 | 430 | | return File(ms.ToArray(), |
| | 0 | 431 | | "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", |
| | 0 | 432 | | fileName); |
| | 0 | 433 | | } |
| | | 434 | | |
| | | 435 | | private IActionResult ToResult(LoadingResult res) |
| | 0 | 436 | | { |
| | 0 | 437 | | var fileName = $"upload-report{res.UploadId}.xlsx"; |
| | 0 | 438 | | var stream = new MemoryStream(); |
| | 0 | 439 | | service.GetReport(res.UploadId, stream); |
| | 0 | 440 | | _diskStorage.SaveDownload(fileName, stream, false); |
| | | 441 | | |
| | 0 | 442 | | return Ok(new UploadResultDTO(res, _commonSettings.BaseUrl)); |
| | 0 | 443 | | } |
| | | 444 | | |
| | | 445 | | private IActionResult ToResult(IImportResult res) |
| | 0 | 446 | | { |
| | 0 | 447 | | return Ok(new UploadResultDTO |
| | 0 | 448 | | { |
| | 0 | 449 | | errorCount = res.errorCount, |
| | 0 | 450 | | reportUrl = res.reportUrl, |
| | 0 | 451 | | succeed = res.succeed, |
| | 0 | 452 | | uploadId = res.uploadId |
| | 0 | 453 | | }); |
| | 0 | 454 | | } |
| | | 455 | | |
| | | 456 | | /// <summary> |
| | | 457 | | /// Загружает изменение цен из файла. |
| | | 458 | | /// Файл должен содержать колонки "Название товара", "VendorCode товара", "Новая цена", "Старая цена" |
| | | 459 | | /// </summary> |
| | | 460 | | /// <param name="id">ключ к уже созданному элементу “переоценка”</param> |
| | | 461 | | /// <remarks>author: oboligatov</remarks> |
| | | 462 | | [HttpPost("UploadPriceTrends/{id}")] |
| | | 463 | | [SwaggerResponse(200, "Успешно", typeof(UploadResultDTO))] |
| | | 464 | | [SwaggerResponse(400, "Некорректная структура файла", typeof(ErrorDTO))] |
| | | 465 | | [SwaggerResponse(500, "Ошибка на стороне сервера", typeof(ErrorDTO))] |
| | | 466 | | [Authorize(Roles = Role.SystemAdmin + "," + Role.SupplierOwner + "," + Role.SupplierOperator)] |
| | | 467 | | public async Task<IActionResult> UploadPriceTrends(long id, IFormFile file) |
| | 0 | 468 | | { |
| | | 469 | | DataTable dataTable; |
| | | 470 | | string fileName; |
| | | 471 | | |
| | 0 | 472 | | using (var stream = _diskStorage.SaveUpload(file, out fileName)) |
| | 0 | 473 | | { |
| | 0 | 474 | | LogLoadBegins($"UploadPriceTrends/{id}", fileName); |
| | | 475 | | |
| | 0 | 476 | | System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance); |
| | 0 | 477 | | var readerConfig = new ExcelReaderConfiguration() |
| | 0 | 478 | | { |
| | 0 | 479 | | AutodetectSeparators = new char[] { ',', ';', '\t', '|', '#' }, |
| | 0 | 480 | | AnalyzeInitialCsvRows = 0 |
| | 0 | 481 | | }; |
| | 0 | 482 | | using (var reader = file.ContentType switch |
| | 0 | 483 | | { |
| | 0 | 484 | | "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" => ExcelReaderFactory.CreateRead |
| | 0 | 485 | | "application/vnd.ms-excel" => ExcelReaderFactory.CreateCsvReader(stream, readerConfig), |
| | 0 | 486 | | "text/csv" => ExcelReaderFactory.CreateCsvReader(stream, readerConfig), |
| | 0 | 487 | | _ => throw new ArgumentException($"File type {file.Name} not supported"), |
| | 0 | 488 | | }) |
| | 0 | 489 | | { |
| | 0 | 490 | | var conf = new ExcelDataSetConfiguration |
| | 0 | 491 | | { |
| | 0 | 492 | | ConfigureDataTable = _ => new ExcelDataTableConfiguration |
| | 0 | 493 | | { |
| | 0 | 494 | | UseHeaderRow = true, |
| | 0 | 495 | | ReadHeaderRow = (rowReader) => |
| | 0 | 496 | | { |
| | 0 | 497 | | rowReader.Read(); // пропускаем описание шаблона |
| | 0 | 498 | | rowReader.Read(); // пропускаем русские колонки |
| | 0 | 499 | | } |
| | 0 | 500 | | } |
| | 0 | 501 | | }; |
| | 0 | 502 | | dataTable = reader.AsDataSet(conf).Tables[0]; |
| | 0 | 503 | | } |
| | 0 | 504 | | } |
| | | 505 | | |
| | 0 | 506 | | if (!dataTable.Columns.Contains("UniqueCode")) |
| | 0 | 507 | | { |
| | 0 | 508 | | throw new ArgumentException("Поле 'UniqueCode' не найдено"); |
| | | 509 | | } |
| | 0 | 510 | | if (!dataTable.Columns.Contains("VendorCode")) |
| | 0 | 511 | | { |
| | 0 | 512 | | throw new ArgumentException("Поле 'VendorCode' не найдено"); |
| | | 513 | | } |
| | 0 | 514 | | if (!dataTable.Columns.Contains("BarCode")) |
| | 0 | 515 | | { |
| | 0 | 516 | | throw new ArgumentException("Поле 'BarCode' не найдено"); |
| | | 517 | | } |
| | 0 | 518 | | if (!dataTable.Columns.Contains("GoodName")) |
| | 0 | 519 | | { |
| | 0 | 520 | | throw new ArgumentException("Поле 'GoodName' не найдено"); |
| | | 521 | | } |
| | 0 | 522 | | if (!dataTable.Columns.Contains("Price")) |
| | 0 | 523 | | { |
| | 0 | 524 | | throw new ArgumentException("Поле 'Price' не найдено"); |
| | | 525 | | } |
| | 0 | 526 | | if (!dataTable.Columns.Contains("OldPrice")) |
| | 0 | 527 | | { |
| | 0 | 528 | | throw new ArgumentException("Поле 'OldPrice' не найдено"); |
| | | 529 | | } |
| | | 530 | | |
| | 0 | 531 | | var list = new List<PriceTrendFromFileDTO>(); |
| | 0 | 532 | | foreach (DataRow row in dataTable.Rows) |
| | 0 | 533 | | { |
| | 0 | 534 | | list.Add(new PriceTrendFromFileDTO |
| | 0 | 535 | | { |
| | 0 | 536 | | UniqueCode = row["UniqueCode"].ToString().NormalizeName(), |
| | 0 | 537 | | VendorCode = row["VendorCode"].ToString().NormalizeName(), |
| | 0 | 538 | | BarCode = row["BarCode"].ToString().NormalizeName(), |
| | 0 | 539 | | GoodName = row["GoodName"].ToString().NormalizeName(), |
| | 0 | 540 | | Price = row["Price"].ToString().NormalizeName(), |
| | 0 | 541 | | OldPrice = row["OldPrice"].ToString().NormalizeName() |
| | 0 | 542 | | }); |
| | 0 | 543 | | } |
| | 0 | 544 | | var errors = await _priceWorker.Load(id, list); |
| | | 545 | | |
| | 0 | 546 | | var result = new UploadResultDTO() { succeed = true }; |
| | 0 | 547 | | if (errors.Any()) |
| | 0 | 548 | | { |
| | 0 | 549 | | result.succeed = false; |
| | | 550 | | |
| | 0 | 551 | | var rows = new List<string[]>(); |
| | 0 | 552 | | rows.Add(new string[] { "Шаблон загрузки цен (* - поле обязательно для заполнения, ** - одно из полей об |
| | 0 | 553 | | rows.Add(new string[] { "**Уникальный код товара", "**Артикул", "**Штрихкод", "**Название товара", "*Акц |
| | 0 | 554 | | rows.Add(new string[] { "UniqueCode", "VendorCode", "BarCode", "GoodName", "Price", "OldPrice", "Error" |
| | 0 | 555 | | errors.ForEach(x => |
| | 0 | 556 | | { |
| | 0 | 557 | | rows.Add(new string[] |
| | 0 | 558 | | { |
| | 0 | 559 | | x.UniqueCode, |
| | 0 | 560 | | x.VendorCode, |
| | 0 | 561 | | x.BarCode, |
| | 0 | 562 | | x.GoodName, |
| | 0 | 563 | | x.Price, |
| | 0 | 564 | | x.OldPrice, |
| | 0 | 565 | | x.Message |
| | 0 | 566 | | }); |
| | 0 | 567 | | }); |
| | | 568 | | |
| | 0 | 569 | | var excel = CsvUtil.ToExcelStream(rows); |
| | 0 | 570 | | var resultFileName = $"price_trend_errors{id}.xlsx"; |
| | 0 | 571 | | _diskStorage.SaveDownload(resultFileName, excel, false); |
| | | 572 | | |
| | 0 | 573 | | result.errorCount = errors.Count; |
| | 0 | 574 | | result.reportUrl = $"{_commonSettings.BaseUrl.TrimEnd('/')}/api/v1/Upload/GetReportPriceTrend?id={id}"; |
| | 0 | 575 | | } |
| | | 576 | | |
| | 0 | 577 | | LogLoadEnds($"UploadPriceTrends/{id}", fileName, !result.succeed); |
| | | 578 | | |
| | 0 | 579 | | return Ok(result); |
| | 0 | 580 | | } |
| | | 581 | | |
| | | 582 | | /// <summary> |
| | | 583 | | /// Получение файла шаблона для загрузки переоценки |
| | | 584 | | /// </summary> |
| | | 585 | | /// <param name="fileType">формат выходного файла (excel, csv), по умолчанию excel</param> |
| | | 586 | | /// <returns>file</returns> |
| | | 587 | | [HttpGet("GetPriceTrendsTemplate")] |
| | | 588 | | [SwaggerResponse(200, "Успешно", typeof(FileStreamResult))] |
| | | 589 | | [SwaggerResponse(500, "Ошибка на стороне сервера", typeof(ErrorDTO))] |
| | | 590 | | //[Authorize(Roles = Role.SystemAdmin + "," + Role.SystemOperator + "," + Role.SupplierOwner + "," + Role.Suppli |
| | | 591 | | public IActionResult GetPriceTrendsTemplate(string fileType = "excel") |
| | 0 | 592 | | { |
| | 0 | 593 | | var rows = new List<string[]>(); |
| | 0 | 594 | | rows.Add(new string[] { "Шаблон загрузки цен (* - поле обязательно для заполнения, ** - одно из полей обязат |
| | 0 | 595 | | rows.Add(new string[] { "**Уникальный код товара", "**Артикул", "**Штрихкод", "**Название товара", "*Акционн |
| | 0 | 596 | | rows.Add(new string[] { "UniqueCode", "VendorCode", "BarCode", "GoodName", "Price", "OldPrice" }); |
| | 0 | 597 | | rows.Add(new string[] { "", "", "", "Печенье \"Шоколадное\", 150 г", "123,55", "" }); |
| | 0 | 598 | | rows.Add(new string[] { "", "", "", "влажный корм для кошек WHISKAS рагу с говядиной и ягненком 85г", "123,5 |
| | | 599 | | |
| | 0 | 600 | | var stream = new MemoryStream(); |
| | 0 | 601 | | if (fileType == "csv") |
| | 0 | 602 | | { |
| | 0 | 603 | | var csv = CsvUtil.ToCsv(rows); |
| | 0 | 604 | | using (var writer = new StreamWriter(stream, encoding: Encoding.UTF8, leaveOpen: true)) |
| | 0 | 605 | | { |
| | 0 | 606 | | writer.Write(csv); |
| | 0 | 607 | | } |
| | 0 | 608 | | stream.Position = 0; |
| | | 609 | | |
| | 0 | 610 | | Response.Headers.Add("Content-Disposition", $"attachment;filename=Price-template.csv"); |
| | 0 | 611 | | return File(stream, "text/csv"); |
| | | 612 | | } |
| | | 613 | | else |
| | 0 | 614 | | { |
| | 0 | 615 | | using (var book = CsvUtil.ToExcel(rows)) |
| | 0 | 616 | | { |
| | 0 | 617 | | book.SaveAs(stream); |
| | | 618 | | |
| | 0 | 619 | | return File(stream.ToArray(), |
| | 0 | 620 | | "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", |
| | 0 | 621 | | "Price-template.xlsx"); |
| | | 622 | | } |
| | | 623 | | } |
| | 0 | 624 | | } |
| | | 625 | | |
| | | 626 | | /// <summary> |
| | | 627 | | /// Загрузка файла расписания работы платформы |
| | | 628 | | /// </summary> |
| | | 629 | | /// <remarks>author i.rebenok</remarks> |
| | | 630 | | /// <param name="file">файл с расписанием</param> |
| | | 631 | | /// <param name="id">id склада</param> |
| | | 632 | | [HttpPost("UploadWorkSchedule/{id}")] |
| | | 633 | | [SwaggerResponse(200, "Успешно", typeof(UploadResultDTO))] |
| | | 634 | | [SwaggerResponse(500, "Ошибка на стороне сервера", typeof(ErrorDTO))] |
| | | 635 | | [Authorize(Roles = Role.SystemAdmin)] |
| | | 636 | | public async Task<IActionResult> UploadWorkSchedule(long id, IFormFile file) |
| | 0 | 637 | | { |
| | | 638 | | DataTable dataTable; |
| | | 639 | | string fileName; |
| | | 640 | | |
| | 0 | 641 | | using (var stream = _diskStorage.SaveUpload(file, out fileName)) |
| | 0 | 642 | | { |
| | 0 | 643 | | LogLoadBegins($"UploadWorkSchedule", fileName); |
| | | 644 | | |
| | 0 | 645 | | System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance); |
| | 0 | 646 | | var readerConfig = new ExcelReaderConfiguration() |
| | 0 | 647 | | { |
| | 0 | 648 | | AutodetectSeparators = new char[] { ',', ';', '\t', '|', '#' }, |
| | 0 | 649 | | AnalyzeInitialCsvRows = 0 |
| | 0 | 650 | | }; |
| | 0 | 651 | | using (var reader = file.ContentType switch |
| | 0 | 652 | | { |
| | 0 | 653 | | "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" => ExcelReaderFactory.CreateRead |
| | 0 | 654 | | "application/vnd.ms-excel" => ExcelReaderFactory.CreateCsvReader(stream, readerConfig), |
| | 0 | 655 | | "text/csv" => ExcelReaderFactory.CreateCsvReader(stream, readerConfig), |
| | 0 | 656 | | _ => throw new ArgumentException($"File type {file.Name} not supported"), |
| | 0 | 657 | | }) |
| | 0 | 658 | | { |
| | 0 | 659 | | var conf = new ExcelDataSetConfiguration |
| | 0 | 660 | | { |
| | 0 | 661 | | ConfigureDataTable = _ => new ExcelDataTableConfiguration |
| | 0 | 662 | | { |
| | 0 | 663 | | UseHeaderRow = true, |
| | 0 | 664 | | ReadHeaderRow = (rowReader) => |
| | 0 | 665 | | { |
| | 0 | 666 | | rowReader.Read(); // пропускаем описание шаблона |
| | 0 | 667 | | rowReader.Read(); // пропускаем русские колонки |
| | 0 | 668 | | } |
| | 0 | 669 | | } |
| | 0 | 670 | | }; |
| | 0 | 671 | | dataTable = reader.AsDataSet(conf).Tables[0]; |
| | 0 | 672 | | } |
| | 0 | 673 | | } |
| | | 674 | | |
| | 0 | 675 | | if (!dataTable.Columns.Contains("BeginTime")) |
| | 0 | 676 | | { |
| | 0 | 677 | | throw new ArgumentException("Поле 'BeginTime' не найдено"); |
| | | 678 | | } |
| | 0 | 679 | | if (!dataTable.Columns.Contains("EndTime")) |
| | 0 | 680 | | { |
| | 0 | 681 | | throw new ArgumentException("Поле 'EndTime' не найдено"); |
| | | 682 | | } |
| | 0 | 683 | | if (!dataTable.Columns.Contains("Offset")) |
| | 0 | 684 | | { |
| | 0 | 685 | | throw new ArgumentException("Поле 'Offset' не найдено"); |
| | | 686 | | } |
| | 0 | 687 | | if (!dataTable.Columns.Contains("IsWorkingDay")) |
| | 0 | 688 | | { |
| | 0 | 689 | | throw new ArgumentException("Поле 'IsWorkingDay' не найдено"); |
| | | 690 | | } |
| | | 691 | | |
| | 0 | 692 | | var list = new List<WorkScheduleFromFileDTO>(); |
| | 0 | 693 | | foreach (DataRow row in dataTable.Rows) |
| | 0 | 694 | | { |
| | 0 | 695 | | list.Add(new WorkScheduleFromFileDTO |
| | 0 | 696 | | { |
| | 0 | 697 | | BeginTime = (row["BeginTime"].ToString()?.Trim() ?? string.Empty).NormalizeName(), |
| | 0 | 698 | | EndTime = (row["EndTime"].ToString()?.Trim() ?? string.Empty).NormalizeName(), |
| | 0 | 699 | | Offset = (row["Offset"].ToString()?.Trim() ?? string.Empty).NormalizeName(), |
| | 0 | 700 | | IsWorkingDay = (row["IsWorkingDay"].ToString()?.Trim() ?? string.Empty).NormalizeName() |
| | 0 | 701 | | }); |
| | 0 | 702 | | } |
| | 0 | 703 | | var errors = await _uploadWorker.Load(id,list); |
| | | 704 | | |
| | 0 | 705 | | var result = new UploadResultDTO() { succeed = true }; |
| | 0 | 706 | | if (errors.Any()) |
| | 0 | 707 | | { |
| | 0 | 708 | | result.succeed = false; |
| | | 709 | | |
| | 0 | 710 | | var rows = new List<string[]>(); |
| | 0 | 711 | | rows.Add(new string[] { "Шаблон загрузки расписания работы платформы (* - поле обязательно для заполнени |
| | 0 | 712 | | rows.Add(new string[] { "*Дата и время начала дня", "*Дата и время окончания дня", "*Часовой пояс склада |
| | 0 | 713 | | rows.Add(new string[] { "BeginTime", "EndTime", "Offset", "IsWorkingDay", "Error" }); |
| | 0 | 714 | | errors.ForEach(x => |
| | 0 | 715 | | { |
| | 0 | 716 | | rows.Add(new string[] |
| | 0 | 717 | | { |
| | 0 | 718 | | x.BeginTime, |
| | 0 | 719 | | x.EndTime, |
| | 0 | 720 | | x.Offset, |
| | 0 | 721 | | x.IsWorkingDay, |
| | 0 | 722 | | x.Message |
| | 0 | 723 | | }); |
| | 0 | 724 | | }); |
| | 0 | 725 | | var csv = CsvUtil.ToCsv(rows); |
| | | 726 | | |
| | 0 | 727 | | result.errorCount = rows.Count; |
| | 0 | 728 | | result.reportUrl = _diskStorage.SaveDownload("work_schedule_errors.csv", csv); |
| | 0 | 729 | | } |
| | | 730 | | |
| | 0 | 731 | | LogLoadEnds($"UploadWorkSchedule", fileName, !result.succeed); |
| | | 732 | | |
| | 0 | 733 | | return Ok(result); |
| | 0 | 734 | | } |
| | | 735 | | |
| | | 736 | | /// <summary> |
| | | 737 | | /// Получение файла шаблона для загрузки расписания работы платформы |
| | | 738 | | /// </summary> |
| | | 739 | | /// <remarks>author i.rebenok</remarks> |
| | | 740 | | /// <param name="fileType">формат выходного файла (excel, csv), по умолчанию excel</param> |
| | | 741 | | /// <returns>file</returns> |
| | | 742 | | [HttpGet("GetWorkSchedulerTemplate")] |
| | | 743 | | [SwaggerResponse(200, "Успешно", typeof(FileStreamResult))] |
| | | 744 | | [SwaggerResponse(500, "Ошибка на стороне сервера", typeof(ErrorDTO))] |
| | | 745 | | [Authorize(Roles = Role.SystemAdmin + "," + Role.SystemOperator)] |
| | | 746 | | public IActionResult GetWorkSchedulerTemplate(string fileType = "excel") |
| | 0 | 747 | | { |
| | 0 | 748 | | var rows = new List<string[]>(); |
| | 0 | 749 | | rows.Add(new string[] { "Шаблон загрузки расписания работы платформы (* - поле обязательно для заполнения, * |
| | 0 | 750 | | rows.Add(new string[] { "*Дата и время начала дня", "*Дата и время окончания дня", "*Часовой пояс склада", " |
| | 0 | 751 | | rows.Add(new string[] { "BeginTime", "EndTime", "Offset", "IsWorkingDay" }); |
| | 0 | 752 | | rows.Add(new string[] { "2020-08-01 09:00:00", "2020-08-01 19:00:00", "3", "true" }); |
| | 0 | 753 | | rows.Add(new string[] { "02.08.2020 09:00:00", "02.08.2020 19:00:00", "3", "false" }); |
| | | 754 | | |
| | 0 | 755 | | var stream = new MemoryStream(); |
| | 0 | 756 | | if (fileType == "csv") |
| | 0 | 757 | | { |
| | 0 | 758 | | var csv = CsvUtil.ToCsv(rows); |
| | 0 | 759 | | using (var writer = new StreamWriter(stream, encoding: Encoding.UTF8, leaveOpen: true)) |
| | 0 | 760 | | { |
| | 0 | 761 | | writer.Write(csv); |
| | 0 | 762 | | } |
| | 0 | 763 | | stream.Position = 0; |
| | | 764 | | |
| | 0 | 765 | | Response.Headers.Add("Content-Disposition", $"attachment;filename=Schedule-template.csv"); |
| | 0 | 766 | | return File(stream, "text/csv"); |
| | | 767 | | } |
| | | 768 | | else |
| | 0 | 769 | | { |
| | 0 | 770 | | using (var book = CsvUtil.ToExcel(rows)) |
| | 0 | 771 | | { |
| | 0 | 772 | | book.SaveAs(stream); |
| | | 773 | | |
| | 0 | 774 | | return File(stream.ToArray(), |
| | 0 | 775 | | "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", |
| | 0 | 776 | | "Schedule-template.xlsx"); |
| | | 777 | | } |
| | | 778 | | } |
| | 0 | 779 | | } |
| | | 780 | | |
| | | 781 | | /// <summary> |
| | | 782 | | /// Временный метод для создания превью из оригиналов уже существующих картинок |
| | | 783 | | /// </summary> |
| | | 784 | | /// <remarks>author: i. rebenok</remarks> |
| | | 785 | | /// <returns></returns> |
| | | 786 | | [HttpGet("PhotoProcessing")] |
| | | 787 | | [SwaggerResponse(200, "Успешно", typeof(FileResult))] |
| | | 788 | | [SwaggerResponse(404, "Нет записей", typeof(ErrorDTO))] |
| | | 789 | | [SwaggerResponse(500, "Ошибка на стороне сервера", typeof(ErrorDTO))] |
| | | 790 | | [Authorize(Roles = Role.SystemAdmin)] |
| | | 791 | | public async Task<IActionResult> PhotoProcessing() |
| | 0 | 792 | | { |
| | 0 | 793 | | var photos = new List<Photo>(); |
| | 0 | 794 | | var rows = new List<string[]>(); |
| | 0 | 795 | | rows.Add(new string[] { "id", "Оригинал", "Высота", "Ширина", "Превью", "Высота", "Ширина", "Статус", "Комме |
| | 0 | 796 | | foreach (var item in await this.service.GetAllPhotos()) |
| | 0 | 797 | | { |
| | 0 | 798 | | int newPrevWidth = 0, newPrevHeight = 0; |
| | 0 | 799 | | var fullSizePath = _imageSettings.ImageSavePath.TrimEnd('/') + item.FullSizeUrl; |
| | 0 | 800 | | var previewSizePath = string.IsNullOrWhiteSpace(Path.GetExtension(fullSizePath)) ? fullSizePath + "_prev |
| | 0 | 801 | | if (!System.IO.File.Exists(fullSizePath)) |
| | 0 | 802 | | { |
| | 0 | 803 | | rows.Add(new string[] { item.Id.ToString(), item.FullSizeUrl, "", "", "", "", "", "Не обработано", " |
| | 0 | 804 | | continue; |
| | | 805 | | } |
| | | 806 | | try |
| | 0 | 807 | | { |
| | 0 | 808 | | using (var streamImg = System.IO.File.OpenRead(fullSizePath)) |
| | 0 | 809 | | using (var inputStream = new SKManagedStream(streamImg)) |
| | 0 | 810 | | using (var original = SKBitmap.Decode(inputStream)) |
| | 0 | 811 | | { |
| | 0 | 812 | | if (original.Width <= _imageSettings.ImagePreveiwWidth && original.Height <= _imageSettings.Imag |
| | 0 | 813 | | { |
| | 0 | 814 | | rows.Add(new string[] { item.Id.ToString(), item.FullSizeUrl, original.Height.ToString(), or |
| | 0 | 815 | | continue; |
| | | 816 | | } |
| | 0 | 817 | | if (original.Width > original.Height) //сохраняем пропорции |
| | 0 | 818 | | { |
| | 0 | 819 | | newPrevWidth = _imageSettings.ImagePreveiwWidth; |
| | 0 | 820 | | newPrevHeight = original.Height * _imageSettings.ImagePreveiwWidth / original.Width; |
| | 0 | 821 | | } |
| | | 822 | | else |
| | 0 | 823 | | { |
| | 0 | 824 | | newPrevWidth = original.Width * _imageSettings.ImagePreveiwHeight / original.Height; |
| | 0 | 825 | | newPrevHeight = _imageSettings.ImagePreveiwHeight; |
| | 0 | 826 | | } |
| | 0 | 827 | | using (var resized = original.Resize(new SKImageInfo(newPrevWidth, newPrevHeight), SKFilterQuali |
| | 0 | 828 | | using (var image = SKImage.FromBitmap(resized)) |
| | 0 | 829 | | using (var output = System.IO.File.OpenWrite(previewSizePath)) |
| | 0 | 830 | | image.Encode(SKEncodedImageFormat.Jpeg, 75).SaveTo(output); |
| | | 831 | | |
| | 0 | 832 | | item.FullSizeHeight = original.Height; |
| | 0 | 833 | | item.FullSizeWidth = original.Width; |
| | 0 | 834 | | item.PreviewUrl = Path.GetFileName(previewSizePath); |
| | 0 | 835 | | item.PreviewHeight = newPrevHeight; |
| | 0 | 836 | | item.PreviewWidth = newPrevWidth; |
| | 0 | 837 | | photos.Add(item); |
| | 0 | 838 | | rows.Add(new string[] { item.Id.ToString(), item.FullSizeUrl, original.Height.ToString(), origin |
| | 0 | 839 | | } |
| | 0 | 840 | | } |
| | 0 | 841 | | catch (Exception ex) |
| | 0 | 842 | | { |
| | 0 | 843 | | rows.Add(new string[] { item.Id.ToString(), item.FullSizeUrl, "", "", "", "", "", "Не обработано", e |
| | 0 | 844 | | continue; |
| | | 845 | | } |
| | 0 | 846 | | } |
| | 0 | 847 | | await this.service.UpdatePhotos(photos); |
| | 0 | 848 | | var csv = CsvUtil.ToCsv(rows); |
| | 0 | 849 | | Response.Headers.Add("Content-Disposition", $"attachment;filename=PhotoProcessingReport.csv"); |
| | 0 | 850 | | return File(Encoding.UTF8.GetBytes(csv), System.Net.Mime.MediaTypeNames.Text.Plain); |
| | 0 | 851 | | } |
| | | 852 | | |
| | 0 | 853 | | static char[] trimChars = new char[] { '/', '\\' }; |
| | | 854 | | |
| | 0 | 855 | | private static IMapper ToCatalogGoodDtoMapper(ImagesSettings _imagesSettings) => new MapperConfiguration(cfg => |
| | 0 | 856 | | { |
| | 0 | 857 | | cfg.CreateMap<Good, GoodCatalogDTO>() |
| | 0 | 858 | | .ForMember(d => d.Barcode, e => |
| | 0 | 859 | | e.MapFrom((s => s.GoodBarcodes.FirstOrDefault(g => g.IsPrimary) != null |
| | 0 | 860 | | ? s.GoodBarcodes.FirstOrDefault(g => g.IsPrimary).BarCode.Code |
| | 0 | 861 | | : s.DefaultBarCode.Code))) |
| | 0 | 862 | | .ForMember(d => d.BrandId, e => e.MapFrom(s => s.Brand.Id)); |
| | 0 | 863 | | cfg.CreateMap<Photo, PhotoDTO>() |
| | 0 | 864 | | .ForMember(d => d.PreviewUrl, e => e.MapFrom(s => _imagesSettings.ImageUrl.TrimEnd(trimChars) + '/' + s. |
| | 0 | 865 | | .ForMember(d => d.FullSizeUrl, e => e.MapFrom(s => _imagesSettings.ImageUrl.TrimEnd(trimChars) + '/' + s |
| | 0 | 866 | | cfg.CreateMap<Brand, IdNameDTO>(); |
| | 0 | 867 | | }).CreateMapper(); |
| | | 868 | | |
| | | 869 | | async Task CheckDepartment(long id) |
| | 0 | 870 | | { |
| | 0 | 871 | | var department = _authService.IsUserPlatform() |
| | 0 | 872 | | ? await _departmentService.GetDepartment(id) |
| | 0 | 873 | | : await _departmentService.GetDepartment(id, _authService.ContragentId); |
| | 0 | 874 | | if (department == null) |
| | 0 | 875 | | { |
| | 0 | 876 | | throw new KeyNotFoundException(); |
| | | 877 | | } |
| | 0 | 878 | | } |
| | | 879 | | } |
| | | 880 | | } |