All checks were successful
Build and Deploy / deploy (push) Successful in 1m13s
支持按 box/sku 模板生成 PDF;待支付前可改产品并自动重排货重算费。 Co-authored-by: Cursor <cursoragent@cursor.com>
200 lines
6.9 KiB
C#
200 lines
6.9 KiB
C#
using Fake.Application;
|
||
using Microsoft.AspNetCore.Mvc;
|
||
using Oms.Application.Dtos.Channels;
|
||
using Oms.Application.Dtos.Shared;
|
||
using Oms.Application.Labeling;
|
||
using Oms.Application.Shared;
|
||
using Oms.Domain.ChannelsAggregate.Consts;
|
||
using Oms.Domain.ChannelsAggregate.Entities;
|
||
using Oms.Domain.ChannelsAggregate.Repositories;
|
||
|
||
namespace Oms.Application.Services.Channels;
|
||
|
||
/// <summary>
|
||
/// 面单模板 API 服务。
|
||
/// </summary>
|
||
[ApiExplorerSettings(GroupName = "OMS-Channel")]
|
||
public class LabelTemplateService : ApplicationService
|
||
{
|
||
private ILabelTemplateRepository LabelTemplateRepository => LazyServiceProvider.GetRequiredService<ILabelTemplateRepository>();
|
||
private LabelPdfGenerationService LabelPdfGenerationService =>
|
||
LazyServiceProvider.GetRequiredService<LabelPdfGenerationService>();
|
||
|
||
/// <summary>
|
||
/// 标签模板列表(不含 JSON,供浏览/下拉)。
|
||
/// </summary>
|
||
[HttpGet("/channel/label-template/list")]
|
||
public async Task<OmsItemsResult<LabelTemplateListItemOutput>> GetListAsync(
|
||
[FromQuery] string? keyword = null,
|
||
[FromQuery] string? templateType = null)
|
||
{
|
||
var keywordValue = keyword?.Trim();
|
||
var typeValue = templateType?.Trim();
|
||
var hasKeyword = !string.IsNullOrEmpty(keywordValue);
|
||
var hasType = !string.IsNullOrEmpty(typeValue);
|
||
|
||
var entities = await LabelTemplateRepository.GetListAsync(x =>
|
||
(!hasKeyword || x.Name.Contains(keywordValue!)) &&
|
||
(!hasType || x.TemplateType == typeValue));
|
||
|
||
var items = entities
|
||
.OrderByDescending(x => x.UpdateTime)
|
||
.ThenByDescending(x => x.Id)
|
||
.Select(x => new LabelTemplateListItemOutput
|
||
{
|
||
Id = x.Id,
|
||
Name = x.Name,
|
||
TemplateType = x.TemplateType,
|
||
CreateTime = x.CreateTime,
|
||
UpdateTime = OmsDocHelper.NullIfDefault(x.UpdateTime)
|
||
})
|
||
.ToList();
|
||
|
||
return new OmsItemsResult<LabelTemplateListItemOutput> { Items = items };
|
||
}
|
||
|
||
/// <summary>
|
||
/// 标签模板详情(含 TemplateJson,编辑用)。
|
||
/// </summary>
|
||
[HttpGet("/channel/label-template/{id:int}")]
|
||
public async Task<LabelTemplateSaveOutput> GetAsync(int id)
|
||
{
|
||
var entity = await GetEntityOrThrowAsync(id);
|
||
return MapOutput(entity);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设计器字段字典(可拖字段分组)。
|
||
/// </summary>
|
||
[HttpGet("/channel/label-template/field-dictionary")]
|
||
public Task<OmsItemsResult<LabelFieldGroup>> GetFieldDictionaryAsync()
|
||
{
|
||
return Task.FromResult(new OmsItemsResult<LabelFieldGroup>
|
||
{
|
||
Items = LabelFieldDictionary.GetGroups().ToList()
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 预览 PDF(同一套 ILabelPdfRenderer)。
|
||
/// </summary>
|
||
[HttpPost("/channel/label-template/preview-pdf")]
|
||
public async Task<FileContentResult> PreviewPdfAsync([FromBody] LabelTemplatePreviewInput input)
|
||
{
|
||
string? templateJson = null;
|
||
string? templateType = null;
|
||
if (input.TemplateId is > 0)
|
||
{
|
||
var entity = await GetEntityOrThrowAsync(input.TemplateId.Value);
|
||
templateJson = entity.TemplateJson;
|
||
templateType = entity.TemplateType;
|
||
}
|
||
else
|
||
{
|
||
templateJson = OmsDocHelper.SerializeJson(input.TemplateJson);
|
||
}
|
||
|
||
if (string.IsNullOrWhiteSpace(templateJson))
|
||
{
|
||
throw new BusinessException("模板 JSON 为空");
|
||
}
|
||
|
||
IReadOnlyDictionary<string, object?>? printData = null;
|
||
if (input.PrintData is { Count: > 0 })
|
||
{
|
||
printData = input.PrintData.ToDictionary(
|
||
x => x.Key,
|
||
x => x.Value,
|
||
StringComparer.OrdinalIgnoreCase);
|
||
}
|
||
else if (string.Equals(templateType, LabelTemplateTypeCodes.Box, StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
printData = InboundBoxLabelPrintDataBuilder.BuildMock();
|
||
}
|
||
else if (string.Equals(templateType, LabelTemplateTypeCodes.Sku, StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
printData = SkuLabelPrintDataBuilder.BuildMock();
|
||
}
|
||
|
||
var bytes = await LabelPdfGenerationService.PreviewAsync(templateJson, printData);
|
||
return new FileContentResult(bytes, "application/pdf")
|
||
{
|
||
FileDownloadName = $"label-preview-{DateTime.Now:yyyyMMddHHmmss}.pdf"
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 保存标签模板。
|
||
/// </summary>
|
||
[HttpPost("/channel/label-template/save")]
|
||
public async Task<LabelTemplateSaveOutput> SaveAsync(LabelTemplateSaveInput input)
|
||
{
|
||
LabelTemplate entity;
|
||
if (input.Id > 0)
|
||
{
|
||
entity = await GetEntityOrThrowAsync(input.Id);
|
||
await ValidateUniqueNameAsync(input.Name, input.TemplateType, entity.Id);
|
||
entity.Name = input.Name;
|
||
entity.TemplateType = input.TemplateType;
|
||
entity.TemplateJson = OmsDocHelper.SerializeJson(input.TemplateJson);
|
||
await LabelTemplateRepository.UpdateAsync(entity);
|
||
}
|
||
else
|
||
{
|
||
await ValidateUniqueNameAsync(input.Name, input.TemplateType, null);
|
||
entity = new LabelTemplate
|
||
{
|
||
Name = input.Name,
|
||
TemplateType = input.TemplateType,
|
||
TemplateJson = OmsDocHelper.SerializeJson(input.TemplateJson)
|
||
};
|
||
entity = await LabelTemplateRepository.InsertAsync(entity);
|
||
}
|
||
|
||
return MapOutput(entity);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 删除标签模板。
|
||
/// </summary>
|
||
[HttpPost("/channel/label-template/delete")]
|
||
public async Task DeleteAsync(IdInput input)
|
||
{
|
||
var entity = await GetEntityOrThrowAsync(input.Id);
|
||
await LabelTemplateRepository.DeleteAsync(entity);
|
||
}
|
||
|
||
private async Task ValidateUniqueNameAsync(string name, string? templateType, int? id)
|
||
{
|
||
var existed = await LabelTemplateRepository.GetByNameAndTypeAsync(name, templateType);
|
||
if (existed != null && existed.Id != id)
|
||
{
|
||
throw new BusinessException("标签模板名称已存在");
|
||
}
|
||
}
|
||
|
||
private async Task<LabelTemplate> GetEntityOrThrowAsync(int id)
|
||
{
|
||
var entity = await LabelTemplateRepository.FirstOrDefaultAsync(x => x.Id == id);
|
||
if (entity == null)
|
||
{
|
||
throw new BusinessException("标签模板不存在");
|
||
}
|
||
|
||
return entity;
|
||
}
|
||
|
||
private static LabelTemplateSaveOutput MapOutput(LabelTemplate entity)
|
||
{
|
||
return new LabelTemplateSaveOutput
|
||
{
|
||
Id = entity.Id,
|
||
Name = entity.Name,
|
||
TemplateType = entity.TemplateType,
|
||
TemplateJson = OmsDocHelper.DeserializeJsonObject(entity.TemplateJson),
|
||
CreateTime = entity.CreateTime,
|
||
UpdateTime = OmsDocHelper.NullIfDefault(entity.UpdateTime)
|
||
};
|
||
}
|
||
}
|