namespace GB5Shared.DocumentMerge;
///
/// Result of a document merge — the merged file's bytes plus enough metadata for a caller to
/// save it (e.g. via the ECM/TATTACHMENT pattern) or stream it back in an HTTP response.
///
public class DocumentMergeResultDTO
{
public byte[] Content { get; set; } = System.Array.Empty();
public string ContentType { get; set; } = string.Empty;
public string FileName { get; set; } = string.Empty;
}
///
/// Shared, module-independent Word/Excel template merge engine (plan: "Document Generation
/// Engine — shared foundation, not Correspondence-specific"). Consumers (Correspondence today;
/// a future Sales/CRM quotation generator later) own their own template storage and data
/// assembly — this service only knows how to merge bytes + field values into a finished
/// document. It never touches TATTACHMENT/ECM or any DB table directly (GB5Shared must not
/// depend on FrameworkDAL/FrameworkBLL) — callers load template bytes and pass them in.
///
public interface IDocumentMergeService
{
///
/// Merges a .docx or .xlsx template (detected from 's
/// extension) against (flat placeholder values, ##FieldName##) and
/// (named repeating-row datasets — each key is the repeat-region
/// name used in the template via a ##REPEAT:Name## marker row/table-row, each value is the
/// list of per-row field dictionaries). Conditional blocks (##IF:Field##...##ENDIF##) and
/// image fields (##IMG:Field## with a byte[] value in ) are
/// resolved automatically by whichever engine handles the file type.
///
Task MergeAsync(
byte[] templateBytes,
string templateFileName,
IReadOnlyDictionary fields,
IReadOnlyDictionary>>? tables = null,
CancellationToken ct = default);
}
///
/// Facade that dispatches to or
/// based on the template's file extension.
///
public sealed class DocumentMergeService(
IWordMergeEngine _wordEngine,
IExcelMergeEngine _excelEngine
) : IDocumentMergeService
{
public async Task MergeAsync(
byte[] templateBytes,
string templateFileName,
IReadOnlyDictionary fields,
IReadOnlyDictionary>>? tables = null,
CancellationToken ct = default)
{
if (templateBytes is null || templateBytes.Length == 0)
throw new ArgumentException("Template bytes cannot be empty.", nameof(templateBytes));
var ext = Path.GetExtension(templateFileName).ToLowerInvariant();
return ext switch
{
".docx" => await _wordEngine.MergeAsync(templateBytes, fields, tables, ct).ConfigureAwait(false),
".xlsx" => await _excelEngine.MergeAsync(templateBytes, fields, tables, ct).ConfigureAwait(false),
_ => throw new NotSupportedException(
$"Unsupported template file type '{ext}'. Only .docx and .xlsx templates are supported.")
};
}
}