Untitled

 avatar
unknown
plain_text
10 months ago
16 kB
18
Indexable
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.policy.LoopRowTableRenderPolicy;
import org.apache.poi.xwpf.usermodel.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;

import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Service
public class TemplateRenderService {
    @Autowired
    private TemplateFieldResolverService fieldResolverService;

    public void renderTemplate(EsContract contract, String templatePath, Path outputPath) {
        try {
            ClassPathResource tpl = new ClassPathResource(templatePath);
            try (InputStream templateInputStream = tpl.getInputStream()) {
                // Parse template to get configuration
                TemplateParseResult parseResult = parseTemplate(templateInputStream);
                
                // Get only the data fields (not table markers or column names)
                Set<String> dataFieldPaths = parseResult.getDataFieldPaths();
                
                try (InputStream freshInputStream = tpl.getInputStream()) {
                    // Resolve data fields from contract
                    Map<String, Object> resolvedData = fieldResolverService.resolveTemplateFields(contract, dataFieldPaths);
                    
                    // Prepare render data with proper structure
                    Map<String, Object> renderData = prepareRenderData(resolvedData, parseResult);
                    
                    // Configure table policies
                    Configure.ConfigureBuilder configBuilder = Configure.builder();
                    for (TableConfig tableConfig : parseResult.getTableConfigs()) {
                        configBuilder.bind(tableConfig.getTableName(), new LoopRowTableRenderPolicy());
                    }
                    Configure config = configBuilder.build();
                    
                    try (XWPFTemplate template = XWPFTemplate.compile(freshInputStream, config).render(renderData);
                         OutputStream outputStream = Files.newOutputStream(outputPath)) {
                        template.write(outputStream);
                        System.out.println("模板渲染完成: " + outputPath);
                    }
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("模板渲染失败", e);
        }
    }
    
    /**
     * Parse template to extract configuration
     */
    private TemplateParseResult parseTemplate(InputStream templateInputStream) {
        TemplateParseResult result = new TemplateParseResult();
        
        try (XWPFDocument document = new XWPFDocument(templateInputStream)) {
            List<String> allTexts = extractAllTexts(document);
            
            for (String text : allTexts) {
                // Parse regular fields
                parseRegularFields(text, result);
                
                // Parse table configurations
                parseTableConfigurations(text, result);
            }
            
        } catch (Exception e) {
            throw new RuntimeException("解析模板失败", e);
        }
        
        return result;
    }
    
    /**
     * Extract all texts from document
     */
    private List<String> extractAllTexts(XWPFDocument document) {
        List<String> texts = new ArrayList<>();
        
        // From paragraphs
        for (XWPFParagraph paragraph : document.getParagraphs()) {
            texts.add(paragraph.getText());
        }
        
        // From tables
        for (XWPFTable table : document.getTables()) {
            for (XWPFTableRow row : table.getRows()) {
                // Store row text as a single unit to preserve context
                StringBuilder rowText = new StringBuilder();
                for (XWPFTableCell cell : row.getTableCells()) {
                    for (XWPFParagraph paragraph : cell.getParagraphs()) {
                        rowText.append(paragraph.getText()).append(" ");
                    }
                }
                texts.add(rowText.toString());
            }
        }
        
        return texts;
    }
    
    /**
     * Parse regular fields like {{contract.id}}
     */
    private void parseRegularFields(String text, TemplateParseResult result) {
        Pattern pattern = Pattern.compile("\\{\\{([^}#/]+)\\}\\}");
        Matcher matcher = pattern.matcher(text);
        
        while (matcher.find()) {
            String field = matcher.group(1).trim();
            
            // Check if this is a table placeholder or column field
            if (!isTableRelatedField(field, result)) {
                result.addDataField(field);
            }
        }
    }
    
    /**
     * Parse table configurations
     */
    private void parseTableConfigurations(String text, TemplateParseResult result) {
        // Pattern 1: {{#tableName}}...{{/tableName}} with column fields
        Pattern loopPattern = Pattern.compile("\\{\\{#(\\w+)\\}\\}(.*?)\\{\\{/\\1\\}\\}");
        Matcher loopMatcher = loopPattern.matcher(text);
        
        while (loopMatcher.find()) {
            String tableName = loopMatcher.group(1);
            String content = loopMatcher.group(2);
            
            TableConfig config = new TableConfig(tableName);
            
            // Extract column fields from content
            Pattern columnPattern = Pattern.compile("\\{\\{([^}]+)\\}\\}");
            Matcher columnMatcher = columnPattern.matcher(content);
            
            while (columnMatcher.find()) {
                String columnField = columnMatcher.group(1).trim();
                config.addColumnField(columnField);
            }
            
            // Infer data path from table name
            String dataPath = inferDataPath(tableName);
            config.setDataPath(dataPath);
            
            result.addTableConfig(config);
        }
        
        // Pattern 2: Simple {{tableName}} in table cell
        Pattern simpleTablePattern = Pattern.compile("\\{\\{(\\w+Table)\\}\\}");
        Matcher simpleTableMatcher = simpleTablePattern.matcher(text);
        
        while (simpleTableMatcher.find()) {
            String tableName = simpleTableMatcher.group(1);
            
            // Check if this table is already configured
            if (!result.hasTableConfig(tableName)) {
                TableConfig config = new TableConfig(tableName);
                
                // For simple table markers, infer columns from context or configuration
                String dataPath = inferDataPath(tableName);
                config.setDataPath(dataPath);
                
                // Infer columns based on table name
                inferTableColumns(tableName, config);
                
                result.addTableConfig(config);
            }
        }
    }
    
    /**
     * Check if a field is table-related (should not be resolved from contract)
     */
    private boolean isTableRelatedField(String field, TemplateParseResult result) {
        // Check if it's a table name
        if (field.endsWith("Table")) {
            return true;
        }
        
        // Check if it's a column field in any table configuration
        for (TableConfig config : result.getTableConfigs()) {
            if (config.getColumnFields().contains(field)) {
                return true;
            }
        }
        
        // Check common table column patterns
        return isCommonTableColumn(field);
    }
    
    /**
     * Check if field is a common table column name
     */
    private boolean isCommonTableColumn(String field) {
        Set<String> commonColumns = new HashSet<>(Arrays.asList(
            "paymentDate", "barrierPct", "barrierPrice", "observationDate",
            "date", "value", "price", "amount", "rate", "percentage"
        ));
        return commonColumns.contains(field);
    }
    
    /**
     * Infer data path from table name
     */
    private String inferDataPath(String tableName) {
        // This can be configured via properties or database
        Map<String, String> pathMappings = new HashMap<>();
        pathMappings.put("observationTable", "contractLeg.leg.modelVo.observation.value.records");
        
        // Remove "Table" suffix to get base name
        String baseName = tableName.replace("Table", "");
        
        // Try exact match first
        if (pathMappings.containsKey(tableName)) {
            return pathMappings.get(tableName) + "[]";
        }
        
        // Try base name
        if (pathMappings.containsKey(baseName)) {
            return pathMappings.get(baseName) + "[]";
        }
        
        // Default: assume it's a direct field
        return tableName + "[]";
    }
    
    /**
     * Infer table columns based on table name
     */
    private void inferTableColumns(String tableName, TableConfig config) {
        // This can be configured via properties or database
        if ("observationTable".equals(tableName)) {
            config.addColumnField("paymentDate");
            config.addColumnField("barrierPct");
            config.addColumnField("barrierPrice");
        }
        // Add more table configurations as needed
    }
    
    /**
     * Prepare render data
     */
    private Map<String, Object> prepareRenderData(Map<String, Object> resolvedData, TemplateParseResult parseResult) {
        Map<String, Object> renderData = new HashMap<>();
        
        // Process each resolved field
        for (Map.Entry<String, Object> entry : resolvedData.entrySet()) {
            String key = entry.getKey();
            Object value = entry.getValue();
            
            // Check if this is table data
            TableConfig tableConfig = findTableConfigByDataPath(key, parseResult);
            if (tableConfig != null && value instanceof List) {
                // Process table data
                List<Map<String, Object>> tableData = (List<Map<String, Object>>) value;
                List<Map<String, Object>> convertedData = convertTableData(tableData, tableConfig);
                renderData.put(tableConfig.getTableName(), convertedData);
            } else {
                // Process regular field
                if (key.contains(".")) {
                    buildNestedMap(renderData, key, value);
                } else {
                    renderData.put(key, value);
                }
            }
        }
        
        return renderData;
    }
    
    /**
     * Find table configuration by data path
     */
    private TableConfig findTableConfigByDataPath(String dataPath, TemplateParseResult parseResult) {
        for (TableConfig config : parseResult.getTableConfigs()) {
            String configPath = config.getDataPath().replace("[]", "");
            if (dataPath.equals(configPath) || dataPath.startsWith(configPath)) {
                return config;
            }
        }
        return null;
    }
    
    /**
     * Convert table data based on configuration
     */
    private List<Map<String, Object>> convertTableData(List<Map<String, Object>> tableData, TableConfig config) {
        List<Map<String, Object>> convertedData = new ArrayList<>();
        
        for (Map<String, Object> row : tableData) {
            Map<String, Object> convertedRow = new HashMap<>();
            
            // Map data fields to template fields
            for (Map.Entry<String, Object> entry : row.entrySet()) {
                String dataField = entry.getKey();
                Object value = entry.getValue();
                
                // Get template field name
                String templateField = config.getTemplateField(dataField);
                if (templateField != null) {
                    convertedRow.put(templateField, value);
                } else {
                    // Keep original if no mapping
                    convertedRow.put(dataField, value);
                }
            }
            
            convertedData.add(convertedRow);
        }
        
        return convertedData;
    }
    
    /**
     * Build nested map for dot notation fields
     */
    private void buildNestedMap(Map<String, Object> rootMap, String dottedPath, Object value) {
        String[] parts = dottedPath.split("\\.");
        Map<String, Object> currentMap = rootMap;
        
        for (int i = 0; i < parts.length - 1; i++) {
            String part = parts[i];
            if (!currentMap.containsKey(part)) {
                currentMap.put(part, new HashMap<String, Object>());
            }
            Object nextLevel = currentMap.get(part);
            if (nextLevel instanceof Map) {
                currentMap = (Map<String, Object>) nextLevel;
            } else {
                return;
            }
        }
        
        currentMap.put(parts[parts.length - 1], value);
    }
    
    /**
     * Template parse result
     */
    private static class TemplateParseResult {
        private Set<String> dataFieldPaths = new HashSet<>();
        private List<TableConfig> tableConfigs = new ArrayList<>();
        
        public void addDataField(String field) {
            dataFieldPaths.add(field);
        }
        
        public void addTableConfig(TableConfig config) {
            tableConfigs.add(config);
            // Add the data path to fields that need to be resolved
            if (config.getDataPath() != null) {
                // Convert path with [] to indicate table data
                String dataPath = config.getDataPath();
                for (String columnField : config.getColumnFields()) {
                    dataFieldPaths.add(dataPath + "." + mapTemplateFieldToDataField(columnField));
                }
            }
        }
        
        private String mapTemplateFieldToDataField(String templateField) {
            // Default field mappings
            Map<String, String> mappings = new HashMap<>();
            mappings.put("paymentDate", "p.date");
            mappings.put("barrierPct", "b.pct");
            mappings.put("barrierPrice", "b.price");
            mappings.put("observationDate", "value");
            
            return mappings.getOrDefault(templateField, templateField);
        }
        
        public boolean hasTableConfig(String tableName) {
            return tableConfigs.stream().anyMatch(c -> c.getTableName().equals(tableName));
        }
        
        public Set<String> getDataFieldPaths() {
            return dataFieldPaths;
        }
        
        public List<TableConfig> getTableConfigs() {
            return tableConfigs;
        }
    }
    
    /**
     * Table configuration
     */
    private static class TableConfig {
        private String tableName;
        private String dataPath;
        private List<String> columnFields = new ArrayList<>();
        private Map<String, String> fieldMappings = new HashMap<>();
        
        public TableConfig(String tableName) {
            this.tableName = tableName;
            initializeDefaultMappings();
        }
        
        private void initializeDefaultMappings() {
            // Default mappings - can be externalized to configuration
            fieldMappings.put("p.date", "paymentDate");
            fieldMappings.put("b.pct", "barrierPct");
            fieldMappings.put("b.price", "barrierPrice");
            fieldMappings.put("value", "observationDate");
        }
        
        public void addColumnField(String field) {
            columnFields.add(field);
        }
        
        public String getTemplateField(String dataField) {
            return fieldMappings.get(dataField);
        }
        
        // Getters and setters
        public String getTableName() {
            return tableName;
        }
        
        public String getDataPath() {
            return dataPath;
        }
        
        public void setDataPath(String dataPath) {
            this.dataPath = dataPath;
        }
        
        public List<String> getColumnFields() {
            return columnFields;
        }
    }
}
Editor is loading...
Leave a Comment