需求
- 导出 Excel 时,所有整数、小数字段需要加千分位分隔符显示
例如:10000 导出成 10,000。 - 只影响“导出的显示效果”,不改变原始数据的语义。
实现思路
通过修改“ Excel 默认导出模版”的平台逻辑,将整型字段模版定义为文本类型,并自定义导出扩展,将所有整型字段的数据根据国际化配置进行分割。
代码示例
- 自定义导出扩展,分割整型字段
注意 所有已有的导出扩展必须由修改继承类ExcelExportSameQueryPageTemplate<Object> --> GlobalExportExt<Object>
否则,已有的导出扩展生成的Excel将无法正常导出整型字段。
package pro.shushi.pamirs.top.core.temp.exports;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Component;
import pro.shushi.pamirs.file.api.context.ExcelDefinitionContext;
import pro.shushi.pamirs.file.api.entity.EasyExcelBlockDefinition;
import pro.shushi.pamirs.file.api.entity.EasyExcelCellDefinition;
import pro.shushi.pamirs.file.api.entity.EasyExcelSheetDefinition;
import pro.shushi.pamirs.file.api.extpoint.ExcelExportFetchDataExtPoint;
import pro.shushi.pamirs.file.api.extpoint.impl.ExcelExportSameQueryPageTemplate;
import pro.shushi.pamirs.file.api.model.ExcelExportTask;
import pro.shushi.pamirs.meta.annotation.Ext;
import pro.shushi.pamirs.meta.annotation.ExtPoint;
import pro.shushi.pamirs.meta.api.dto.config.ModelFieldConfig;
import pro.shushi.pamirs.meta.api.session.PamirsSession;
import pro.shushi.pamirs.meta.enmu.TtypeEnum;
import pro.shushi.pamirs.meta.util.FieldUtils;
import pro.shushi.pamirs.resource.api.model.ResourceLang;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@Component
@Ext(ExcelExportTask.class)
public class GlobalExportExt<T> extends ExcelExportSameQueryPageTemplate<T> implements ExcelExportFetchDataExtPoint {
/**
* 缓存:modelModel -> (fieldCode -> ttype)
* ttype 是 ModelFieldConfig.getTtype() 的值
*/
private static final ConcurrentMap<String, ConcurrentMap<String, String>> MODEL_FIELD_TTYPE_CACHE =
new ConcurrentHashMap<>();
@ExtPoint.Implement(priority = 1)
@Override
public List<Object> fetchExportData(ExcelExportTask exportTask, ExcelDefinitionContext context) {
List<Object> results = super.fetchExportData(exportTask, context);
if (CollectionUtils.isEmpty(results)) {
return results;
}
return dataFormat(context, results);
}
public static List<Object> dataFormat(ExcelDefinitionContext context, List<Object> results) {
List<Map<String, Object>> exportList = new ArrayList<>();
ResourceLang resourceLang = new ResourceLang().setCode(PamirsSession.getLang()).queryOne();
if (resourceLang == null) {
return results;
}
// 小数分隔符
String decimalPoint = resourceLang.getDecimalPoint();
// 整数分隔符
String thousandsSep = resourceLang.getThousandsSep();
// 数字分组规则(每组多少位,比如 "3")
String groupingRule = resourceLang.getGroupingRule();
// 解析 groupSize,只做一次
int groupSize = 3;
if (groupingRule != null && groupingRule.matches("\\d+")) {
try {
groupSize = Integer.parseInt(groupingRule);
} catch (NumberFormatException ignore) {
}
}
boolean needGroup = groupSize > 0 && thousandsSep != null && !thousandsSep.isEmpty();
// 判断是否需要小数点替换
String dp = (decimalPoint == null || decimalPoint.isEmpty()) ? "." : decimalPoint;
boolean needDecimalReplace = !".".equals(dp);
EasyExcelSheetDefinition sheetDefinition = context.getSheetList().get(0);
EasyExcelBlockDefinition blockDefinition = sheetDefinition.getBlockDefinitions().get(0);
String modelModel = blockDefinition.getBindingModel();
// 记录Excel中整数字段编码
List<String> integerField = new ArrayList<>();
// 记录Excel中小数字段编码
List<String> decimalField = new ArrayList<>();
Map<String, EasyExcelCellDefinition> fieldCells = blockDefinition.getFieldCells();
for (String key : fieldCells.keySet()) {
String ttype = getFieldTtype(modelModel, key);
if (ttype == null) {
continue;
}
if (TtypeEnum.INTEGER.value().equals(ttype)) {
integerField.add(key);
} else if (TtypeEnum.FLOAT.value().equals(ttype)) {
decimalField.add(key);
}
}
if (integerField.isEmpty() && decimalField.isEmpty()) {
return results;
}
Object block = results.get(0);
if (block instanceof List) {
List<?> dataList = (List<?>) block;
for (Object row : dataList) {
// 处理整数字段:加千分位
if (needGroup) {
for (String field : integerField) {
Object value = FieldUtils.getFieldValue(row, field);
if (value != null) {
String formatted = formatInteger(value, groupSize, thousandsSep);
FieldUtils.setFieldValue(row, field, formatted);
}
}
}
// 处理小数字段:只替换小数点
if (needDecimalReplace) {
for (String field : decimalField) {
Object value = FieldUtils.getFieldValue(row, field);
if (value != null) {
String formatted = formatDecimal(value, dp);
FieldUtils.setFieldValue(row, field, formatted);
}
}
}
}
}
return results;
}
/**
* 整数格式化:按照 groupSize 与 thousandsSep 插入分隔符
*/
public static String formatInteger(Object value, int groupSize, String thousandsSep) {
String numStr;
if (value instanceof Integer || value instanceof Long || value instanceof Short) {
numStr = String.valueOf(value);
} else if (value instanceof BigDecimal || value instanceof Double || value instanceof Float) {
numStr = new BigDecimal(value.toString()).toPlainString();
} else {
numStr = value.toString().trim();
}
boolean negative = false;
if (numStr.startsWith("-")) {
negative = true;
numStr = numStr.substring(1);
} else if (numStr.startsWith("+")) {
numStr = numStr.substring(1);
}
// 去掉已有的分隔符(防止重复格式化)
numStr = numStr.replace(",", "").replace(" ", "");
// 非纯数字直接返回原始字符串
if (!numStr.matches("\\d+")) {
return (negative ? "-" : "") + numStr;
}
if (groupSize <= 0 || thousandsSep == null || thousandsSep.isEmpty()) {
return (negative ? "-" : "") + numStr;
}
StringBuilder sb = new StringBuilder();
int len = numStr.length();
int firstGroupLen = len % groupSize;
if (firstGroupLen == 0) {
firstGroupLen = groupSize;
}
sb.append(numStr, 0, firstGroupLen);
for (int i = firstGroupLen; i < len; i += groupSize) {
sb.append(thousandsSep).append(numStr, i, Math.min(i + groupSize, len));
}
return (negative ? "-" : "") + sb.toString();
}
/**
* 小数格式化
*/
public static String formatDecimal(Object value, String dp) {
String numStr;
if (value instanceof BigDecimal || value instanceof Double || value instanceof Float) {
numStr = new BigDecimal(value.toString()).toPlainString();
} else {
numStr = value.toString().trim();
}
if (dp == null || ".".equals(dp)) {
return numStr;
}
return numStr.replace(".", dp);
}
/**
* 从缓存中获取字段 ttype,没有则查询 PamirsSession 并写入缓存
*/
public static String getFieldTtype(String modelModel, String fieldCode) {
ConcurrentMap<String, String> fieldCache =
MODEL_FIELD_TTYPE_CACHE.computeIfAbsent(modelModel, m -> new ConcurrentHashMap<>());
return fieldCache.computeIfAbsent(fieldCode, f -> {
ModelFieldConfig modelField = PamirsSession.getContext().getModelField(modelModel, f);
if (modelField == null) {
return null;
}
return modelField.getTtype();
});
}
}
- 修改平台逻辑,整型字段模版定义为文本类型
启动工程同包同类名覆盖平台文件。
pro.shushi.pamirs.file.api.init.FileLifecycleCompletedInit
package pro.shushi.pamirs.file.api.init;
import com.alibaba.fastjson.JSON;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import pro.shushi.pamirs.boot.base.model.View;
import pro.shushi.pamirs.boot.common.api.command.AppLifecycleCommand;
import pro.shushi.pamirs.boot.common.api.init.LifecycleCompletedInit;
import pro.shushi.pamirs.core.common.InitializationUtil;
import pro.shushi.pamirs.core.common.LifecycleExecutorHelper;
import pro.shushi.pamirs.core.common.cache.MemoryIterableSearchCache;
import pro.shushi.pamirs.core.common.xstream.TreeNodeXStream;
import pro.shushi.pamirs.core.common.xstream.XMLNodeContent;
import pro.shushi.pamirs.file.api.FileModule;
import pro.shushi.pamirs.file.api.builder.BlockDefinitionBuilder;
import pro.shushi.pamirs.file.api.builder.HeaderDefinitionBuilder;
import pro.shushi.pamirs.file.api.builder.WorkbookDefinitionBuilder;
import pro.shushi.pamirs.file.api.config.FileConstant;
import pro.shushi.pamirs.file.api.config.FileProperties;
import pro.shushi.pamirs.file.api.enmu.*;
import pro.shushi.pamirs.file.api.model.ExcelWorkbookDefinition;
import pro.shushi.pamirs.file.api.service.ExcelFileService;
import pro.shushi.pamirs.file.api.util.ExcelHelper;
import pro.shushi.pamirs.framework.common.config.AsyncTaskExecutorConfiguration;
import pro.shushi.pamirs.framework.common.entry.TreeNode;
import pro.shushi.pamirs.framework.connectors.data.sql.Pops;
import pro.shushi.pamirs.framework.gateways.util.BooleanHelper;
import pro.shushi.pamirs.meta.annotation.fun.extern.Slf4j;
import pro.shushi.pamirs.meta.api.Models;
import pro.shushi.pamirs.meta.api.dto.config.ModelConfig;
import pro.shushi.pamirs.meta.api.dto.config.ModelFieldConfig;
import pro.shushi.pamirs.meta.api.session.PamirsSession;
import pro.shushi.pamirs.meta.api.session.RequestContext;
import pro.shushi.pamirs.meta.common.constants.CharacterConstants;
import pro.shushi.pamirs.meta.constant.FieldConstants;
import pro.shushi.pamirs.meta.domain.model.DataDictionary;
import pro.shushi.pamirs.meta.domain.model.DataDictionaryItem;
import pro.shushi.pamirs.meta.domain.model.ModelDefinition;
import pro.shushi.pamirs.meta.domain.module.ModuleDefinition;
import pro.shushi.pamirs.meta.enmu.*;
import java.util.*;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;
@Slf4j
@Component
@Order
public class FileLifecycleCompletedInit implements LifecycleCompletedInit {
private static final String DEFAULT_EXPORT_TEMPLATE_DISPLAY_NAME = "默认导出模板";
private static final String DEFAULT_IMPORT_TEMPLATE_DISPLAY_NAME = "默认导入模板";
private static final String DEFAULT_EXPORT_TEMPLATE_SUFFIX = "导出";
private static final String DEFAULT_IMPORT_TEMPLATE_SUFFIX = "导入";
private static final String CONFIG_KEY = "config";
private static final String CONFIG_EXCLUDED_ACTIONS_KEY = "excluded-actions";
private static final String ACTION_KEY = "action";
private static final String VIEW_KEY = "view";
private static final String ACTION_REFS_KEY = "refs";
private static final String FIELD_KEY = "field";
private static final String FIELDS_KEY = "fields";
private static final String FORM_KEY = "form";
private static final String TEMPLATE_KEY = "template";
private static final String SLOT_KEY = "slot";
private static final String PACK_KEY = "pack";
private static final String TABLE_KEY = "table";
private static final String DATA_KEY = "data";
private static final String TYPE_KEY = "type";
private static final String WIDGET_KEY = "widget";
private static final String NAME_KEY = "name";
private static final String FIELD_LABEL_KEY = "label";
private static final String FIELD_INVISIBLE_KEY = "invisible";
private static final String EXPORT_DIALOG_KEY = "$$internal_GotoListExportDialog";
private static final String EXPORT_DIALOG_KEY_V3 = "internal_GotoListExportDialog";
private static final String DEFAULT_RELATION_LABEL_SPLIT = "-";
@Autowired
private ExcelFileService excelFileService;
@Autowired
private FileProperties fileProperties;
@Autowired(required = false)
@Qualifier(AsyncTaskExecutorConfiguration.FIXED_THREAD_POOL_EXECUTOR)
private Executor globalFixedThreadPoolExecutor;
@Override
public void process(AppLifecycleCommand command, List<ModuleDefinition> installModules, List<ModuleDefinition> upgradeModules, List<ModuleDefinition> reloadModules) {
log.info("Automatically create import/export system templates: {}", fileProperties.getAutoCreateTemplate());
if (fileProperties.getAutoCreateTemplate()) {
InitializationUtil.lifecycleCompletedInit(installModules, upgradeModules, Collections.emptyList(), (lifecycle, module) -> LifecycleExecutorHelper.execute(globalFixedThreadPoolExecutor, this::initDefaultTemplateByTableView), FileModule.MODULE_MODULE);
}
}
private void initDefaultTemplateByTableView() {
List<ExcelWorkbookDefinition> existImportExportWorkbookDefinitionList = getExistWorkbookDefinitionList(ExcelTemplateTypeEnum.IMPORT_EXPORT);
// 默认导出模板(Table)
List<ExcelWorkbookDefinition> existExportWorkbookDefinitionList = getExistWorkbookDefinitionList(ExcelTemplateTypeEnum.EXPORT);
existExportWorkbookDefinitionList.addAll(existImportExportWorkbookDefinitionList);
List<ExcelWorkbookDefinition> exportWorkbookDefinitionList = getEffectiveList(existExportWorkbookDefinitionList, ViewTypeEnum.TABLE);
// 默认导入模板(Form)
List<ExcelWorkbookDefinition> existImportWorkbookDefinitionList = getExistWorkbookDefinitionList(ExcelTemplateTypeEnum.IMPORT);
existImportWorkbookDefinitionList.addAll(existImportExportWorkbookDefinitionList);
List<ExcelWorkbookDefinition> importWorkbookDefinitionList = getEffectiveList(existImportWorkbookDefinitionList, ViewTypeEnum.FORM);
log.info("Initialization system templates. import template: {}, export template: {}", exportWorkbookDefinitionList.size(), importWorkbookDefinitionList.size());
refreshWorkbookDefinition(exportWorkbookDefinitionList);
refreshWorkbookDefinition(importWorkbookDefinitionList);
}
private List<ExcelWorkbookDefinition> getExistWorkbookDefinitionList(ExcelTemplateTypeEnum type) {
List<ExcelWorkbookDefinition> workbookDefinitions = Models.data().queryListByWrapper(Pops.<ExcelWorkbookDefinition>lambdaQuery().from(ExcelWorkbookDefinition.MODEL_MODEL).eq(ExcelWorkbookDefinition::getType, type).setBatchSize(200));
if (CollectionUtils.isNotEmpty(workbookDefinitions)) {
workbookDefinitions = Models.data().listFieldQuery(workbookDefinitions, ExcelWorkbookDefinition::getLocations);
}
return workbookDefinitions;
}
private List<ExcelWorkbookDefinition> getEffectiveList(List<ExcelWorkbookDefinition> existWorkbookDefinitionList, ViewTypeEnum viewType) {
List<View> allViewList = Models.data().queryListByWrapper(Pops.<View>lambdaQuery().from(View.MODEL_MODEL).select(View::getModel, View::getName, View::getTitle, View::getTemplate).eq(View::getType, viewType).eq(View::getActive, ActiveEnum.ACTIVE.value()).orderByAsc(View::getPriority).setBatchSize(200));
List<View> distinctViewList = allViewList.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(View::getModel))), ArrayList::new));
TreeNodeXStream xs = new TreeNodeXStream();
Set<String> repeatSet = new HashSet<>();
Map<ExcelTemplateSourceEnum, List<ExcelWorkbookDefinition>> resultMap = new HashMap<>(existWorkbookDefinitionList.size());
for (ExcelWorkbookDefinition item : existWorkbookDefinitionList) {
resultMap.computeIfAbsent(item.getTemplateSource(), k -> new ArrayList<>()).add(item);
if (!ExcelTemplateSourceEnum.SYSTEM.equals(item.getTemplateSource())) {
repeatSet.add(item.getModel());
}
}
List<ExcelWorkbookDefinition> createOrUpdateList = new ArrayList<>();
MemoryIterableSearchCache<String, ExcelWorkbookDefinition> cache = new MemoryIterableSearchCache<>(resultMap.get(ExcelTemplateSourceEnum.SYSTEM), this::keyGenerator);
for (View view : distinctViewList) {
String model = view.getModel();
if (!repeatSet.contains(model)) {
ExcelWorkbookDefinition workbookDefinition;
try {
workbookDefinition = createOrUpdateExcelTemplate(xs, view);
} catch (Exception e) {
if (log.isErrorEnabled()) {
log.error("Excel模板生成失败 model: {}", model, e);
}
continue;
}
if (workbookDefinition != null) {
cache.compute(keyGenerator(workbookDefinition), (k, v) -> v);
createOrUpdateList.add(workbookDefinition);
}
}
}
cache.fill();
List<ExcelWorkbookDefinition> deleteList = new ArrayList<>(cache.getNotComputedCache().values());
if (!deleteList.isEmpty()) {
Models.data().deleteByPks(deleteList);
for (ExcelWorkbookDefinition item : deleteList) {
String originKey = keyGenerator(item);
for (int i = 0; i < existWorkbookDefinitionList.size(); i++) {
String targetKey = keyGenerator(existWorkbookDefinitionList.get(i));
if (originKey.equals(targetKey)) {
existWorkbookDefinitionList.remove(i);
break;
}
}
}
}
if (!createOrUpdateList.isEmpty()) {
Models.data().createOrUpdateBatch(createOrUpdateList);
List<ExcelWorkbookDefinition> needAddList = new ArrayList<>();
MemoryIterableSearchCache<String, ExcelWorkbookDefinition> existWorkbookDefinitionCache = new MemoryIterableSearchCache<>(existWorkbookDefinitionList, this::keyGenerator);
for (ExcelWorkbookDefinition item : createOrUpdateList) {
if (existWorkbookDefinitionCache.get(keyGenerator(item)) == null) {
needAddList.add(item);
}
}
existWorkbookDefinitionList.addAll(needAddList);
}
return existWorkbookDefinitionList;
}
private void refreshWorkbookDefinition(List<ExcelWorkbookDefinition> workbookDefinitionList) {
try {
excelFileService.refreshDefinitionContextBatch(workbookDefinitionList);
} catch (Exception e) {
log.error("Refresh excel workbook definition error.", e);
}
}
private String keyGenerator(ExcelWorkbookDefinition item) {
return item.getModel() + CharacterConstants.SEPARATOR_OCTOTHORPE + item.getName();
}
private ExcelWorkbookDefinition createOrUpdateExcelTemplate(TreeNodeXStream xs, View view) {
String xmlTemplate = view.getTemplate();
if (StringUtils.isBlank(xmlTemplate)) {
return null;
}
String model = view.getModel();
RequestContext requestContext = PamirsSession.getContext();
if (requestContext == null) {
return null;
}
ModelConfig modelConfig = requestContext.getModelConfig(model);
if (modelConfig == null) {
return null;
}
ModelTypeEnum modelType = modelConfig.getType();
if (!ModelTypeEnum.STORE.equals(modelType) && !ModelTypeEnum.PROXY.equals(modelType)) {
return null;
}
try {
TreeNode<XMLNodeContent> viewRoot = xs.fromXML(xmlTemplate);
XMLNodeContent rootContent = viewRoot.getValue();
String type = rootContent.getAttribute(TYPE_KEY);
if (StringUtils.isBlank(type)) {
type = rootContent.getAttribute(WIDGET_KEY);
}
String viewType = type.trim().toUpperCase();
if (StringUtils.isBlank(type) || (!ViewTypeEnum.TABLE.name().equals(viewType) && !ViewTypeEnum.FORM.name().equals(viewType))) {
return null;
}
String templateDisplayName = generatorDefaultTemplateDisplayName(view, modelConfig, viewType);
String sheetName = generatorDefaultSheetName(view, modelConfig, viewType);
WorkbookDefinitionBuilder workbookDefinitionBuilder = WorkbookDefinitionBuilder.newInstance(model, FileConstant.DEFAULT_TEMPLATE_NAME + "_" + view.getName());
workbookDefinitionBuilder.setBindingViewName(view.getName());
BlockDefinitionBuilder blockDefinitionBuilder = workbookDefinitionBuilder.createSheet().setName(sheetName).createBlock(model, ExcelAnalysisTypeEnum.FIXED_HEADER, ExcelDirectionEnum.HORIZONTAL, 0, 1, 0, 12).setPresetNumber(10);
HeaderDefinitionBuilder configHeaderDefinitionBuilder = blockDefinitionBuilder.createHeader().setStyleBuilder(ExcelHelper.createDefaultStyle()).setIsConfig(true);
HeaderDefinitionBuilder headerDefinitionBuilder = blockDefinitionBuilder.createHeader().setStyleBuilder(ExcelHelper.createDefaultStyle(v -> v.setBold(true)));
if (ViewTypeEnum.TABLE.name().equals(viewType)) {
if (!createExportCell(configHeaderDefinitionBuilder, headerDefinitionBuilder, viewRoot, requestContext, model)) {
return null;
}
log.info("生成默认导出模板成功 [Model {}] [ViewName {}] [ViewType {}]", view.getModel(), view.getName(), view.getType());
blockDefinitionBuilder.modifyDesignRange(0, 1, 0, configHeaderDefinitionBuilder.cellSize() - 1);
return workbookDefinitionBuilder.setType(ExcelTemplateTypeEnum.EXPORT).setDisplayName(templateDisplayName).build().setTemplateSource(ExcelTemplateSourceEnum.SYSTEM);
} else {
if (!createImportCell(configHeaderDefinitionBuilder, headerDefinitionBuilder, viewRoot, requestContext, model)) {
return null;
}
log.info("生成默认导入模板成功 [Model {}] [ViewName {}] [ViewType {}]", view.getModel(), view.getName(), view.getType());
blockDefinitionBuilder.modifyDesignRange(0, 1, 0, configHeaderDefinitionBuilder.cellSize() - 1);
return workbookDefinitionBuilder.setType(ExcelTemplateTypeEnum.IMPORT).setDisplayName(templateDisplayName).build().setTemplateSource(ExcelTemplateSourceEnum.SYSTEM);
}
} catch (Exception e) {
log.error("生成默认模板失败 [Model {}] [ViewName {}] [ViewType {}]", view.getModel(), view.getName(), view.getType(), e);
}
return null;
}
private String generatorDefaultTemplateDisplayName(View view, ModelConfig modelConfig, String viewType) {
if (ViewTypeEnum.TABLE.name().equals(viewType.trim().toUpperCase())) {
return generatorDefaultSheetName(view, modelConfig, viewType) + DEFAULT_EXPORT_TEMPLATE_SUFFIX;
} else {
return generatorDefaultSheetName(view, modelConfig, viewType) + DEFAULT_IMPORT_TEMPLATE_SUFFIX;
}
}
private String generatorDefaultSheetName(View view, ModelConfig modelConfig, String viewType) {
String name = view.getTitle();
if (StringUtils.isBlank(name)) {
name = modelConfig.getDisplayName();
if (StringUtils.isBlank(name)) {
if (ViewTypeEnum.TABLE.name().equals(viewType.trim().toUpperCase())) {
name = DEFAULT_EXPORT_TEMPLATE_DISPLAY_NAME;
} else {
name = DEFAULT_IMPORT_TEMPLATE_DISPLAY_NAME;
}
}
}
return name;
}
private boolean createExportCell(HeaderDefinitionBuilder configHeaderDefinitionBuilder,
HeaderDefinitionBuilder headerDefinitionBuilder,
TreeNode<XMLNodeContent> viewRoot,
RequestContext requestContext,
String model) {
boolean isCreate = false;
for (TreeNode<XMLNodeContent> child : viewRoot.getChildren()) {
XMLNodeContent childValue = child.getValue();
String key = child.getKey();
if (FIELD_KEY.equals(key)) {
boolean invisible = childValue.getBooleanAttribute(FIELD_INVISIBLE_KEY);
if (invisible) {
continue;
}
String field = childValue.getAttribute(DATA_KEY);
if (StringUtils.isBlank(field)) {
continue;
}
String label = childValue.getAttribute(FIELD_LABEL_KEY);
ModelFieldConfig modelFieldConfig = requestContext.getModelField(model, field);
if (modelFieldConfig == null) {
continue;
}
if (Boolean.TRUE.equals(modelFieldConfig.getInvisible())) {
continue;
}
if (StringUtils.isBlank(label) || !BooleanHelper.isFalseWithoutException(label)) {
label = modelFieldConfig.getDisplayName();
if (StringUtils.isBlank(label)) {
label = field;
}
}
if (createCell(configHeaderDefinitionBuilder, headerDefinitionBuilder, requestContext, modelFieldConfig, field, label, true)) {
isCreate = true;
}
} else if (CONFIG_KEY.equals(key)) {
String excludedActions = childValue.getAttribute(CONFIG_EXCLUDED_ACTIONS_KEY);
if (excludedActions != null) {
if (excludedActions.contains(EXPORT_DIALOG_KEY)) {
return false;
}
}
} else if (TEMPLATE_KEY.equals(key)) {
String slot = childValue.getAttribute(SLOT_KEY);
if (StringUtils.isBlank(slot)) {
continue;
}
slot = slot.trim().toLowerCase();
if (TABLE_KEY.equals(slot) || FIELDS_KEY.equals(slot)) {
isCreate = createExportCell(configHeaderDefinitionBuilder, headerDefinitionBuilder, child, requestContext, model);
}
}
}
return isCreate;
}
private boolean createImportCell(HeaderDefinitionBuilder configHeaderDefinitionBuilder,
HeaderDefinitionBuilder headerDefinitionBuilder,
TreeNode<XMLNodeContent> viewRoot,
RequestContext requestContext,
String model) {
boolean isCreate = false;
for (TreeNode<XMLNodeContent> child : viewRoot.getChildren()) {
XMLNodeContent childValue = child.getValue();
String key = child.getKey();
if (FIELD_KEY.equals(key)) {
boolean invisible = childValue.getBooleanAttribute(FIELD_INVISIBLE_KEY);
if (invisible) {
continue;
}
String field = childValue.getAttribute(DATA_KEY);
if (StringUtils.isBlank(field) || FieldConstants.CREATE_UID.equalsIgnoreCase(field) || FieldConstants.WRITE_UID.equalsIgnoreCase(field)) {
continue;
}
String label = childValue.getAttribute(FIELD_LABEL_KEY);
ModelFieldConfig modelFieldConfig = requestContext.getModelField(model, field);
if (modelFieldConfig == null) {
continue;
}
if (Boolean.TRUE.equals(modelFieldConfig.getInvisible())) {
continue;
}
if (StringUtils.isBlank(label) || !BooleanHelper.isFalseWithoutException(label)) {
label = modelFieldConfig.getDisplayName();
if (StringUtils.isBlank(label)) {
label = field;
}
}
if (createCell(configHeaderDefinitionBuilder, headerDefinitionBuilder, requestContext, modelFieldConfig, field, label, false)) {
isCreate = true;
}
} else if (CONFIG_KEY.equals(key)) {
String excludedActions = childValue.getAttribute(CONFIG_EXCLUDED_ACTIONS_KEY);
if (excludedActions != null) {
if (excludedActions.contains(EXPORT_DIALOG_KEY)) {
return false;
}
}
} else if (PACK_KEY.equals(key)) {
isCreate = createImportCell(configHeaderDefinitionBuilder, headerDefinitionBuilder, child, requestContext, model);
} else if (TEMPLATE_KEY.equals(key)) {
String slot = childValue.getAttribute(SLOT_KEY);
if (StringUtils.isBlank(slot)) {
continue;
}
slot = slot.trim().toLowerCase();
if (TABLE_KEY.equals(slot) || FIELDS_KEY.equals(slot) || FORM_KEY.equals(slot)) {
isCreate = createImportCell(configHeaderDefinitionBuilder, headerDefinitionBuilder, child, requestContext, model);
}
}
}
return isCreate;
}
private boolean createCell(HeaderDefinitionBuilder configHeaderDefinitionBuilder,
HeaderDefinitionBuilder headerDefinitionBuilder,
RequestContext requestContext,
ModelFieldConfig modelFieldConfig,
String field,
String label,
Boolean isExport) {
String ttype = modelFieldConfig.getTtype();
if (TtypeEnum.RELATED.value().equals(ttype)) {
if (Boolean.TRUE.equals(isExport) || Boolean.TRUE.equals(modelFieldConfig.getStore())) {
ttype = modelFieldConfig.getRelatedTtype();
} else {
return false;
}
}
boolean isMulti = Boolean.TRUE.equals(modelFieldConfig.getMulti());
ExcelValueTypeEnum valueType = ExcelValueTypeEnum.STRING;
String format = modelFieldConfig.getFormat();
boolean isSampleField = true;
if (TtypeEnum.STRING.value().equals(ttype) || TtypeEnum.INTEGER.value().equals(ttype)) {
if (isMulti) {
format = ExcelHelper.generatorMultiValueFormatExpression();
valueType = ExcelValueTypeEnum.OBJECT;
}
else if (TtypeEnum.INTEGER.value().equals(ttype)) {
format = ExcelValueTypeEnum.STRING.getDefaultFormat();
valueType = ExcelValueTypeEnum.STRING;
}
} else if (TtypeEnum.ENUM.value().equals(ttype)) {
Map<String, String> enumerationMapping = new HashMap<>();
DataDictionary dictionary = requestContext.getDictionary(modelFieldConfig.getDictionary());
for (DataDictionaryItem dictionaryItem : dictionary.getOptions()) {
enumerationMapping.put(dictionaryItem.getValue(), dictionaryItem.getDisplayName());
}
format = JSON.toJSONString(enumerationMapping);
valueType = ExcelValueTypeEnum.ENUMERATION;
} else if (TtypeEnum.DATE.value().equals(ttype)) {
if (StringUtils.isBlank(format)) {
format = DateFormatEnum.DATE.value();
}
valueType = ExcelValueTypeEnum.DATETIME;
} else if (TtypeEnum.TIME.value().equals(ttype)) {
if (StringUtils.isBlank(format)) {
format = DateFormatEnum.TIME.value();
}
valueType = ExcelValueTypeEnum.DATETIME;
} else if (TtypeEnum.DATETIME.value().equals(ttype)) {
if (StringUtils.isBlank(format)) {
format = DateFormatEnum.DATETIME.value();
}
valueType = ExcelValueTypeEnum.DATETIME;
} else if (TtypeEnum.BOOLEAN.value().equals(ttype)) {
format = ExcelValueTypeEnum.BOOLEAN.getDefaultFormat();
valueType = ExcelValueTypeEnum.BOOLEAN;
} else if (TtypeEnum.MONEY.value().equals(ttype) || TtypeEnum.FLOAT.value().equals(ttype)) {
if (isMulti) {
format = ExcelHelper.generatorMultiValueFormatExpression();
valueType = ExcelValueTypeEnum.OBJECT;
} else {
format = ExcelHelper.getNumberFormat(modelFieldConfig.getDecimal());
valueType = ExcelValueTypeEnum.NUMBER;
}
} else if (TtypeEnum.M2O.value().equals(ttype) || TtypeEnum.O2O.value().equals(ttype)) {
relationFieldProcess(configHeaderDefinitionBuilder, headerDefinitionBuilder, requestContext, modelFieldConfig, field, label, false, isExport);
isSampleField = false;
} else if (TtypeEnum.O2M.value().equals(ttype) || TtypeEnum.M2M.value().equals(ttype)) {
relationFieldProcess(configHeaderDefinitionBuilder, headerDefinitionBuilder, requestContext, modelFieldConfig, field, label, true, isExport);
isSampleField = false;
}
if (isSampleField) {
configHeaderDefinitionBuilder.createCell().setField(field).setType(valueType).setFormat(format);
headerDefinitionBuilder.createCell().setValue(label);
}
return true;
}
private void relationFieldProcess(HeaderDefinitionBuilder configHeaderDefinitionBuilder,
HeaderDefinitionBuilder headerDefinitionBuilder,
RequestContext requestContext,
ModelFieldConfig modelFieldConfig,
String field,
String label,
Boolean isMulti,
Boolean isExport) {
if (isExport) {
exportRelationFieldProcess(configHeaderDefinitionBuilder, headerDefinitionBuilder, requestContext, modelFieldConfig, field, label, isMulti);
} else {
importRelationFieldProcess(configHeaderDefinitionBuilder, headerDefinitionBuilder, requestContext, modelFieldConfig, field, label, isMulti);
}
}
private void exportRelationFieldProcess(HeaderDefinitionBuilder configHeaderDefinitionBuilder,
HeaderDefinitionBuilder headerDefinitionBuilder,
RequestContext requestContext,
ModelFieldConfig modelFieldConfig,
String field,
String label,
Boolean isMulti) {
String referenceModel = modelFieldConfig.getReferences();
ModelConfig referenceModelConfig = getReferenceModelConfig(requestContext, modelFieldConfig);
if (referenceModelConfig == null) {
return;
}
List<String> optionLabels = Optional.ofNullable(referenceModelConfig.getModelDefinition()).map(ModelDefinition::getLabelFields).filter(CollectionUtils::isNotEmpty).orElse(null);
if (CollectionUtils.isEmpty(optionLabels)) {
ModelFieldConfig labelFieldConfig = requestContext.getModelField(referenceModel, FieldConstants.NAME);
if (labelFieldConfig == null) {
labelFieldConfig = requestContext.getModelField(referenceModel, FieldConstants.CODE);
}
if (labelFieldConfig == null) {
labelFieldConfig = requestContext.getModelField(referenceModel, FieldConstants.ID);
}
if (labelFieldConfig == null) {
return;
}
optionLabels = Collections.singletonList(labelFieldConfig.getField());
}
String optionLabel = optionLabels.get(0);
String formatExpression;
if (isMulti) {
formatExpression = ExcelHelper.generatorMultiObjectFormatExpression(referenceModel, optionLabel);
} else {
formatExpression = ExcelHelper.generatorSingleObjectFormatExpression(optionLabel);
}
configHeaderDefinitionBuilder.createCell().setField(field).setType(ExcelValueTypeEnum.OBJECT).setFormat(formatExpression);
headerDefinitionBuilder.createCell().setValue(label);
}
/**
* 导入关联关系处理仅处理包含唯一键或主键的多对一字段
*/
private void importRelationFieldProcess(HeaderDefinitionBuilder configHeaderDefinitionBuilder,
HeaderDefinitionBuilder headerDefinitionBuilder,
RequestContext requestContext,
ModelFieldConfig modelFieldConfig,
String field,
String label,
Boolean isMulti) {
if (isMulti) {
// 过滤所有 o2m/m2m 类型字段
return;
}
String referenceModel = modelFieldConfig.getReferences();
ModelConfig referenceModelConfig = getReferenceModelConfig(requestContext, modelFieldConfig);
if (referenceModelConfig == null) {
return;
}
//此处仅处理指定唯一索引的关联对象
List<String> uniqueList = referenceModelConfig.getUniques();
if (CollectionUtils.isEmpty(uniqueList)) {
return;
}
List<String> labelFields = Optional.ofNullable(referenceModelConfig.getModelDefinition()).map(ModelDefinition::getLabelFields).filter(CollectionUtils::isNotEmpty).orElse(null);
if (CollectionUtils.isEmpty(labelFields)) {
return;
}
String firstUnique = uniqueList.get(0);
String[] uniques = firstUnique.split(CharacterConstants.SEPARATOR_COMMA);
Map<String, String> referenceFieldMap = new LinkedHashMap<>();
Map<String, ModelFieldConfig> referenceFieldConfigMap = new LinkedHashMap<>();
List<String> relationFields = new ArrayList<>(Arrays.asList(uniques));
relationFields.add(labelFields.get(0));
for (String relationField : relationFields) {
relationField = relationField.trim();
ModelFieldConfig referenceModelFieldConfig = requestContext.getModelField(referenceModel, relationField);
if (referenceModelFieldConfig == null) {
return;
}
String referenceTtype = referenceModelFieldConfig.getTtype();
if (TtypeEnum.isRelationType(referenceTtype)) {
return;
}
String referenceLabel = referenceModelFieldConfig.getDisplayName();
if (StringUtils.isBlank(referenceLabel)) {
referenceLabel = relationField;
}
String newRelationField;
if (isMulti) {
newRelationField = field + FileConstant.LIST_FLAG_CHARACTER + FileConstant.POINT_CHARACTER + relationField;
} else {
newRelationField = field + FileConstant.POINT_CHARACTER + relationField;
}
referenceFieldMap.put(newRelationField, label + DEFAULT_RELATION_LABEL_SPLIT + referenceLabel);
referenceFieldConfigMap.put(newRelationField, referenceModelFieldConfig);
}
for (Map.Entry<String, String> entry : referenceFieldMap.entrySet()) {
String key = entry.getKey();
createCell(configHeaderDefinitionBuilder, headerDefinitionBuilder, requestContext, referenceFieldConfigMap.get(key), key, entry.getValue(), null);
}
}
private ModelConfig getReferenceModelConfig(RequestContext requestContext, ModelFieldConfig modelFieldConfig) {
String referenceModel = modelFieldConfig.getReferences();
ModelConfig referenceModelConfig = requestContext.getModelConfig(referenceModel);
if (referenceModelConfig == null) {
log.error("Invalid reference model config. model: {}, field: {}, referenceModel: {}",
modelFieldConfig.getModel(), modelFieldConfig.getField(), referenceModel);
return null;
}
return referenceModelConfig;
}
}
效果图

Oinone社区 作者:yexiu原创文章,如若转载,请注明出处:https://doc.oinone.top/other/25118.html
访问Oinone官网:https://www.oinone.top获取数式Oinone低代码应用平台体验