新增业务实体字段配置功能,包含主表和明细表的数据库结构定义,支持业务打印绑定的字段映射。实现字段配置的增删改查操作,优化打印数据生成逻辑,提升系统的可维护性和扩展性。同时,新增异步同步功能以支持打印模板与业务数据的实时更新。
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
package org.jeecg.modules.print.catalog;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import org.jeecg.modules.print.vo.PrintBizDetailSlotVO;
|
||||
import org.jeecg.modules.print.vo.PrintBizFieldItemVO;
|
||||
|
||||
/**
|
||||
* 业务实体字段缓存(mes_xsl_biz_entity_field_*)供打印绑定使用;实现类位于 jeecg-module-xslmes,避免 system-biz 依赖业务模块。
|
||||
*
|
||||
* <p>bizCode 与 {@code print_biz_perm_entity.perm_id}、打印绑定 {@code biz_code} 一致。
|
||||
*/
|
||||
public interface IPrintBizEntityFieldCatalogProvider {
|
||||
|
||||
/**
|
||||
* 在一次接口内批量组装多个业务的 VO 前调用,将mes_xsl_biz_entity_field_profile 一次查出放入线程缓存,避免按业务 N
|
||||
* 次查询;必须与 {@link #endBulkLookup()} 成对(建议 finally)。
|
||||
*/
|
||||
default void beginBulkLookup(Collection<String> bizCodes) {}
|
||||
|
||||
/** 结束批量查找,清理线程缓存 */
|
||||
default void endBulkLookup() {}
|
||||
|
||||
/** 缓存中的主实体全限定名(无记录返回 null) */
|
||||
String getEntityClassFqn(String bizCode);
|
||||
|
||||
/** 是否已有缓存记录(存在即优先走缓存,即使字段为空) */
|
||||
boolean hasCatalogForBiz(String bizCode);
|
||||
|
||||
/** 主实体可选字段(与反射接口字段结构一致) */
|
||||
List<PrintBizFieldItemVO> listMainFields(String bizCode);
|
||||
|
||||
/** 明细数据来源槽位 */
|
||||
List<PrintBizDetailSlotVO> listDetailSlots(String bizCode);
|
||||
|
||||
/**
|
||||
* 明细槽位对应字段,fieldKey 已带「属性名.」前缀(与 {@link org.jeecg.modules.print.util.PrintBizDetailPropertyScanner#listPrefixedDetailFields}
|
||||
* 一致)。
|
||||
*/
|
||||
List<PrintBizFieldItemVO> listPrefixedDetailFields(String bizCode, String detailProperty, String slotKind);
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import org.jeecg.common.api.vo.Result;
|
||||
import org.jeecg.common.aspect.annotation.AutoLog;
|
||||
import org.jeecg.common.system.base.controller.JeecgController;
|
||||
import org.jeecg.common.system.query.QueryGenerator;
|
||||
import org.jeecg.modules.print.catalog.IPrintBizEntityFieldCatalogProvider;
|
||||
import org.jeecg.modules.print.entity.PrintBizTemplateBind;
|
||||
import org.jeecg.modules.print.entity.PrintTemplate;
|
||||
import org.jeecg.modules.print.service.IPrintBizBindPermWhitelistService;
|
||||
@@ -57,6 +58,9 @@ public class PrintBizTemplateBindController extends JeecgController<PrintBizTemp
|
||||
@Autowired private IPrintBizPermEntityService printBizPermEntityService;
|
||||
@Autowired private SimpMessagingTemplate messagingTemplate;
|
||||
|
||||
@Autowired(required = false)
|
||||
private IPrintBizEntityFieldCatalogProvider fieldCatalogProvider;
|
||||
|
||||
@Operation(summary = "业务打印绑定-分页列表")
|
||||
@GetMapping("/list")
|
||||
@RequiresPermissions("print:bizBind:list")
|
||||
@@ -185,7 +189,11 @@ public class PrintBizTemplateBindController extends JeecgController<PrintBizTemp
|
||||
if (StringUtils.isBlank(bizCode)) {
|
||||
return Result.error("bizCode 不能为空");
|
||||
}
|
||||
PrintBizTypeVO bizVo = printBizPermEntityService.resolveBizTypeVo(bizCode.trim());
|
||||
String code = bizCode.trim();
|
||||
if (fieldCatalogProvider != null && fieldCatalogProvider.hasCatalogForBiz(code)) {
|
||||
return Result.OK(fieldCatalogProvider.listDetailSlots(code));
|
||||
}
|
||||
PrintBizTypeVO bizVo = printBizPermEntityService.resolveBizTypeVo(code);
|
||||
if (bizVo == null || StringUtils.isBlank(bizVo.getDescription())) {
|
||||
return Result.OK(Collections.emptyList());
|
||||
}
|
||||
@@ -206,7 +214,12 @@ public class PrintBizTemplateBindController extends JeecgController<PrintBizTemp
|
||||
if (StringUtils.isAnyBlank(bizCode, detailProperty)) {
|
||||
return Result.error("bizCode 与 detailProperty 不能为空");
|
||||
}
|
||||
PrintBizTypeVO bizVo = printBizPermEntityService.resolveBizTypeVo(bizCode.trim());
|
||||
String code = bizCode.trim();
|
||||
if (fieldCatalogProvider != null && fieldCatalogProvider.hasCatalogForBiz(code)) {
|
||||
return Result.OK(
|
||||
fieldCatalogProvider.listPrefixedDetailFields(code, detailProperty.trim(), slotKind.trim()));
|
||||
}
|
||||
PrintBizTypeVO bizVo = printBizPermEntityService.resolveBizTypeVo(code);
|
||||
if (bizVo == null || StringUtils.isBlank(bizVo.getDescription())) {
|
||||
return Result.OK(Collections.emptyList());
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.jeecg.modules.print.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -10,11 +11,11 @@ import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jeecg.modules.print.entity.PrintBizPermEntity;
|
||||
import org.jeecg.modules.print.catalog.IPrintBizEntityFieldCatalogProvider;
|
||||
import org.jeecg.modules.print.mapper.PrintBizPermEntityMapper;
|
||||
import org.jeecg.modules.print.service.IPrintBizPermEntityService;
|
||||
import org.jeecg.modules.print.util.PrintBizEntityFieldIntrospector;
|
||||
import org.jeecg.modules.print.util.PrintBizMenuEntityInference;
|
||||
import org.jeecg.modules.print.vo.PrintBizFieldItemVO;
|
||||
import org.jeecg.modules.print.vo.PrintBizTypeVO;
|
||||
import org.jeecg.modules.system.entity.SysPermission;
|
||||
import org.jeecg.modules.system.service.ISysPermissionService;
|
||||
@@ -31,6 +32,9 @@ public class PrintBizPermEntityServiceImpl
|
||||
|
||||
@Autowired private ISysPermissionService sysPermissionService;
|
||||
|
||||
@Autowired(required = false)
|
||||
private IPrintBizEntityFieldCatalogProvider fieldCatalogProvider;
|
||||
|
||||
@Override
|
||||
public PrintBizPermEntity getByPermId(String permId) {
|
||||
if (StringUtils.isBlank(permId)) {
|
||||
@@ -41,15 +45,34 @@ public class PrintBizPermEntityServiceImpl
|
||||
|
||||
@Override
|
||||
public List<PrintBizTypeVO> listAllBizTypeVOs() {
|
||||
List<PrintBizTypeVO> out = new ArrayList<>();
|
||||
// 仅使用 print_biz_perm_entity,避免全库扫菜单+反射导致接口超时;由「保存白名单」时 upsert 写入表
|
||||
for (PrintBizPermEntity row : list()) {
|
||||
if (row == null || StringUtils.isBlank(row.getPermId())) {
|
||||
continue;
|
||||
List<PrintBizPermEntity> rows = list();
|
||||
if (rows == null || rows.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
List<String> permIds = new ArrayList<>(rows.size());
|
||||
for (PrintBizPermEntity row : rows) {
|
||||
if (row != null && StringUtils.isNotBlank(row.getPermId())) {
|
||||
permIds.add(row.getPermId().trim());
|
||||
}
|
||||
PrintBizTypeVO vo = buildVoForPermId(row.getPermId());
|
||||
if (vo != null) {
|
||||
out.add(vo);
|
||||
}
|
||||
Map<String, SysPermission> permMap = loadPermissionMap(permIds);
|
||||
List<PrintBizTypeVO> out = new ArrayList<>();
|
||||
try {
|
||||
if (fieldCatalogProvider != null) {
|
||||
fieldCatalogProvider.beginBulkLookup(permIds);
|
||||
}
|
||||
for (PrintBizPermEntity row : rows) {
|
||||
if (row == null || StringUtils.isBlank(row.getPermId())) {
|
||||
continue;
|
||||
}
|
||||
PrintBizTypeVO vo = buildVoForPermId(row.getPermId().trim(), row, permMap);
|
||||
if (vo != null) {
|
||||
out.add(vo);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (fieldCatalogProvider != null) {
|
||||
fieldCatalogProvider.endBulkLookup();
|
||||
}
|
||||
}
|
||||
return out;
|
||||
@@ -60,15 +83,40 @@ public class PrintBizPermEntityServiceImpl
|
||||
if (whitelistPermIds == null || whitelistPermIds.isEmpty()) {
|
||||
return listAllBizTypeVOs();
|
||||
}
|
||||
List<PrintBizTypeVO> out = new ArrayList<>();
|
||||
List<String> ids = new ArrayList<>();
|
||||
Set<String> seen = new HashSet<>();
|
||||
for (String raw : whitelistPermIds) {
|
||||
String id = StringUtils.trimToEmpty(raw);
|
||||
if (id.isEmpty()) {
|
||||
if (id.isEmpty() || seen.contains(id)) {
|
||||
continue;
|
||||
}
|
||||
PrintBizTypeVO vo = buildVoForPermId(id);
|
||||
if (vo != null) {
|
||||
out.add(vo);
|
||||
seen.add(id);
|
||||
ids.add(id);
|
||||
}
|
||||
if (ids.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
Map<String, SysPermission> permMap = loadPermissionMap(ids);
|
||||
Map<String, PrintBizPermEntity> entityMap = new HashMap<>(ids.size());
|
||||
for (PrintBizPermEntity e : listByIds(ids)) {
|
||||
if (e != null && StringUtils.isNotBlank(e.getPermId())) {
|
||||
entityMap.put(e.getPermId().trim(), e);
|
||||
}
|
||||
}
|
||||
List<PrintBizTypeVO> out = new ArrayList<>();
|
||||
try {
|
||||
if (fieldCatalogProvider != null) {
|
||||
fieldCatalogProvider.beginBulkLookup(ids);
|
||||
}
|
||||
for (String id : ids) {
|
||||
PrintBizTypeVO vo = buildVoForPermId(id, entityMap.get(id), permMap);
|
||||
if (vo != null) {
|
||||
out.add(vo);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (fieldCatalogProvider != null) {
|
||||
fieldCatalogProvider.endBulkLookup();
|
||||
}
|
||||
}
|
||||
return out;
|
||||
@@ -203,37 +251,86 @@ public class PrintBizPermEntityServiceImpl
|
||||
/**
|
||||
* 显式表优先;否则按 SysPermission.component 推断实体类名并加载字段。
|
||||
*/
|
||||
private Map<String, SysPermission> loadPermissionMap(List<String> permIds) {
|
||||
if (permIds == null || permIds.isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
List<SysPermission> plist = sysPermissionService.listByIds(permIds);
|
||||
Map<String, SysPermission> map = new HashMap<>(Math.max(16, plist.size() * 2));
|
||||
for (SysPermission p : plist) {
|
||||
if (p != null && StringUtils.isNotBlank(p.getId())) {
|
||||
map.put(p.getId().trim(), p);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private PrintBizTypeVO buildVoForPermId(String permId) {
|
||||
if (StringUtils.isBlank(permId)) {
|
||||
return null;
|
||||
}
|
||||
PrintBizPermEntity row = getById(permId.trim());
|
||||
String id = permId.trim();
|
||||
return buildVoForPermId(id, getById(id), null);
|
||||
}
|
||||
|
||||
private PrintBizTypeVO buildVoForPermId(
|
||||
String permId, PrintBizPermEntity row, Map<String, SysPermission> permCache) {
|
||||
if (StringUtils.isBlank(permId)) {
|
||||
return null;
|
||||
}
|
||||
String id = permId.trim();
|
||||
PrintBizPermEntity rowEff = row != null ? row : getById(id);
|
||||
String entityFqn = null;
|
||||
if (row != null && StringUtils.isNotBlank(row.getEntityClass())) {
|
||||
entityFqn = row.getEntityClass().trim();
|
||||
if (rowEff != null && StringUtils.isNotBlank(rowEff.getEntityClass())) {
|
||||
entityFqn = rowEff.getEntityClass().trim();
|
||||
} else {
|
||||
SysPermission p = sysPermissionService.getById(permId.trim());
|
||||
SysPermission p = permCache != null ? permCache.get(id) : sysPermissionService.getById(id);
|
||||
entityFqn = PrintBizMenuEntityInference.inferEntityClassFqn(p);
|
||||
}
|
||||
boolean catalogOk =
|
||||
fieldCatalogProvider != null && fieldCatalogProvider.hasCatalogForBiz(id);
|
||||
if (StringUtils.isBlank(entityFqn)) {
|
||||
return null;
|
||||
if (!catalogOk) {
|
||||
return null;
|
||||
}
|
||||
entityFqn = StringUtils.trimToEmpty(fieldCatalogProvider.getEntityClassFqn(id));
|
||||
}
|
||||
Class<?> clazz = PrintBizEntityFieldIntrospector.tryLoadClass(entityFqn);
|
||||
List<PrintBizFieldItemVO> fields =
|
||||
clazz == null ? new ArrayList<>() : PrintBizEntityFieldIntrospector.listFields(clazz);
|
||||
if (clazz == null) {
|
||||
// 类不在 classpath(模块未引入)时不生成下拉项,避免空字段误导
|
||||
return null;
|
||||
Class<?> clazz = null;
|
||||
if (!catalogOk) {
|
||||
clazz = PrintBizEntityFieldIntrospector.tryLoadClass(entityFqn);
|
||||
if (clazz == null) {
|
||||
// 类不在 classpath(模块未引入)时不生成下拉项,避免空字段误导
|
||||
return null;
|
||||
}
|
||||
}
|
||||
PrintBizTypeVO vo = new PrintBizTypeVO();
|
||||
vo.setBizCode(permId.trim());
|
||||
vo.setLinkedPermissionId(permId.trim());
|
||||
vo.setBizName(resolveMenuName(permId.trim()));
|
||||
vo.setDescription(entityFqn);
|
||||
vo.setFields(fields);
|
||||
vo.setBizCode(id);
|
||||
vo.setLinkedPermissionId(id);
|
||||
vo.setBizName(resolveMenuName(id, permCache));
|
||||
String desc = StringUtils.isNotBlank(entityFqn) ? entityFqn : "";
|
||||
if (StringUtils.isBlank(desc) && fieldCatalogProvider != null) {
|
||||
desc = StringUtils.defaultString(fieldCatalogProvider.getEntityClassFqn(id));
|
||||
}
|
||||
vo.setDescription(desc);
|
||||
if (catalogOk) {
|
||||
vo.setFields(fieldCatalogProvider.listMainFields(id));
|
||||
} else {
|
||||
vo.setFields(PrintBizEntityFieldIntrospector.listFields(clazz));
|
||||
}
|
||||
return vo;
|
||||
}
|
||||
|
||||
private String resolveMenuName(String permId, Map<String, SysPermission> permCache) {
|
||||
if (permCache != null) {
|
||||
SysPermission p = permCache.get(permId);
|
||||
if (p != null && StringUtils.isNotBlank(p.getName())) {
|
||||
return p.getName().trim();
|
||||
}
|
||||
return permId;
|
||||
}
|
||||
return resolveMenuName(permId);
|
||||
}
|
||||
|
||||
private String resolveMenuName(String permId) {
|
||||
SysPermission p = sysPermissionService.getById(permId);
|
||||
if (p != null && StringUtils.isNotBlank(p.getName())) {
|
||||
|
||||
@@ -77,7 +77,7 @@ public final class PrintBizDetailPropertyScanner {
|
||||
for (PrintBizFieldItemVO x : raw) {
|
||||
String path = prefix + "." + x.getFieldKey();
|
||||
String label = "明细「" + prefix + "」→ " + x.getLabel();
|
||||
out.add(new PrintBizFieldItemVO(path, label, x.getDescription()));
|
||||
out.add(PrintBizFieldItemVO.copyWithPrefixedPath(x, path, label));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -3,16 +3,28 @@ package org.jeecg.modules.print.util;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jeecg.modules.print.vo.PrintBizFieldItemVO;
|
||||
import org.jeecgframework.poi.excel.annotation.Excel;
|
||||
|
||||
/**
|
||||
* 从实体类反射可映射字段(供打印业务绑定下拉与映射);子类字段优先于父类同名字段。
|
||||
*
|
||||
* <p>写入缓存 JSON 时附带 {@code javaType}、{@code jdbcType}、{@code simpleKind}。
|
||||
*/
|
||||
public final class PrintBizEntityFieldIntrospector {
|
||||
|
||||
@@ -31,13 +43,174 @@ public final class PrintBizEntityFieldIntrospector {
|
||||
if ("serialVersionUID".equals(name)) {
|
||||
continue;
|
||||
}
|
||||
ordered.putIfAbsent(name, new PrintBizFieldItemVO(name, resolveLabel(f), ""));
|
||||
PrintBizFieldItemVO vo =
|
||||
new PrintBizFieldItemVO(name, resolveLabel(f), "");
|
||||
fillJavaJdbcSimple(vo, f.getType());
|
||||
ordered.putIfAbsent(name, vo);
|
||||
}
|
||||
c = c.getSuperclass();
|
||||
}
|
||||
return new ArrayList<>(ordered.values());
|
||||
}
|
||||
|
||||
/** 将反射得到的 Java 类型写成 VO 上的三类提示字段 */
|
||||
public static void fillJavaJdbcSimple(PrintBizFieldItemVO vo, Class<?> declaredType) {
|
||||
if (vo == null || declaredType == null) {
|
||||
return;
|
||||
}
|
||||
vo.setJavaType(resolveJavaTypeFqn(declaredType));
|
||||
vo.setSimpleKind(resolveSimpleKind(declaredType));
|
||||
vo.setJdbcType(resolveJdbcTypeName(declaredType));
|
||||
}
|
||||
|
||||
private static String resolveJavaTypeFqn(Class<?> t) {
|
||||
String cn = t.getCanonicalName();
|
||||
return cn != null ? cn : t.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* STRING / BOOLEAN / INTEGER / LONG / DECIMAL / DATE / TIME / DATETIME / ENUM / BINARY / JAVA_OBJECT /
|
||||
* OTHER
|
||||
*/
|
||||
private static String resolveSimpleKind(Class<?> t) {
|
||||
if (t.isPrimitive()) {
|
||||
if (t == boolean.class) {
|
||||
return "BOOLEAN";
|
||||
}
|
||||
if (t == byte.class || t == short.class || t == int.class || t == char.class) {
|
||||
return "INTEGER";
|
||||
}
|
||||
if (t == long.class) {
|
||||
return "LONG";
|
||||
}
|
||||
if (t == float.class || t == double.class) {
|
||||
return "DECIMAL";
|
||||
}
|
||||
return "OTHER";
|
||||
}
|
||||
if (t == Boolean.class) {
|
||||
return "BOOLEAN";
|
||||
}
|
||||
if (t.isEnum()) {
|
||||
return "ENUM";
|
||||
}
|
||||
if (CharSequence.class.isAssignableFrom(t)) {
|
||||
return "STRING";
|
||||
}
|
||||
if (Number.class.isAssignableFrom(t)) {
|
||||
if (BigDecimal.class.isAssignableFrom(t)
|
||||
|| Float.class.isAssignableFrom(t)
|
||||
|| Double.class.isAssignableFrom(t)) {
|
||||
return "DECIMAL";
|
||||
}
|
||||
if (BigInteger.class.isAssignableFrom(t) || Long.class.isAssignableFrom(t)) {
|
||||
return "LONG";
|
||||
}
|
||||
return "INTEGER";
|
||||
}
|
||||
if (UUID.class.isAssignableFrom(t)) {
|
||||
return "STRING";
|
||||
}
|
||||
if (Date.class.isAssignableFrom(t)) {
|
||||
return "DATETIME";
|
||||
}
|
||||
if (LocalDate.class.isAssignableFrom(t)) {
|
||||
return "DATE";
|
||||
}
|
||||
if (LocalTime.class.isAssignableFrom(t)) {
|
||||
return "TIME";
|
||||
}
|
||||
if (LocalDateTime.class.isAssignableFrom(t)
|
||||
|| Instant.class.isAssignableFrom(t)
|
||||
|| ZonedDateTime.class.isAssignableFrom(t)
|
||||
|| OffsetDateTime.class.isAssignableFrom(t)) {
|
||||
return "DATETIME";
|
||||
}
|
||||
if (byte[].class == t) {
|
||||
return "BINARY";
|
||||
}
|
||||
if (t.getName().startsWith("java.") || t.getName().startsWith("javax.")) {
|
||||
return "JAVA_OBJECT";
|
||||
}
|
||||
return "JAVA_OBJECT";
|
||||
}
|
||||
|
||||
/**
|
||||
* 粗粒度 JDBC 名称(非穷尽);自定义 Bean 等返回 JAVA_OBJECT。
|
||||
*/
|
||||
private static String resolveJdbcTypeName(Class<?> t) {
|
||||
if (t.isPrimitive()) {
|
||||
if (t == boolean.class) {
|
||||
return "BOOLEAN";
|
||||
}
|
||||
if (t == byte.class) {
|
||||
return "TINYINT";
|
||||
}
|
||||
if (t == short.class) {
|
||||
return "SMALLINT";
|
||||
}
|
||||
if (t == int.class) {
|
||||
return "INTEGER";
|
||||
}
|
||||
if (t == long.class) {
|
||||
return "BIGINT";
|
||||
}
|
||||
if (t == float.class) {
|
||||
return "FLOAT";
|
||||
}
|
||||
if (t == double.class) {
|
||||
return "DOUBLE";
|
||||
}
|
||||
if (t == char.class) {
|
||||
return "CHAR";
|
||||
}
|
||||
return "OTHER";
|
||||
}
|
||||
if (t == Boolean.class) {
|
||||
return "BOOLEAN";
|
||||
}
|
||||
if (t.isEnum()) {
|
||||
return "VARCHAR";
|
||||
}
|
||||
if (String.class == t || CharSequence.class.isAssignableFrom(t)) {
|
||||
return "VARCHAR";
|
||||
}
|
||||
if (UUID.class.isAssignableFrom(t)) {
|
||||
return "CHAR";
|
||||
}
|
||||
if (Integer.class == t || Short.class == t || Byte.class == t) {
|
||||
return "INTEGER";
|
||||
}
|
||||
if (Long.class == t || BigInteger.class.isAssignableFrom(t)) {
|
||||
return "BIGINT";
|
||||
}
|
||||
if (BigDecimal.class.isAssignableFrom(t)) {
|
||||
return "DECIMAL";
|
||||
}
|
||||
if (Float.class == t || Double.class == t) {
|
||||
return "DOUBLE";
|
||||
}
|
||||
if (Date.class.isAssignableFrom(t)) {
|
||||
return "TIMESTAMP";
|
||||
}
|
||||
if (LocalDate.class.isAssignableFrom(t)) {
|
||||
return "DATE";
|
||||
}
|
||||
if (LocalTime.class.isAssignableFrom(t)) {
|
||||
return "TIME";
|
||||
}
|
||||
if (LocalDateTime.class.isAssignableFrom(t)
|
||||
|| Instant.class.isAssignableFrom(t)
|
||||
|| ZonedDateTime.class.isAssignableFrom(t)
|
||||
|| OffsetDateTime.class.isAssignableFrom(t)) {
|
||||
return "TIMESTAMP";
|
||||
}
|
||||
if (byte[].class == t) {
|
||||
return "BLOB";
|
||||
}
|
||||
return "JAVA_OBJECT";
|
||||
}
|
||||
|
||||
private static String resolveLabel(Field f) {
|
||||
Schema schema = f.getAnnotation(Schema.class);
|
||||
if (schema != null && StringUtils.isNotBlank(schema.description())) {
|
||||
|
||||
@@ -2,15 +2,14 @@ package org.jeecg.modules.print.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import java.io.Serializable;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(description = "业务实体可选字段")
|
||||
public class PrintBizFieldItemVO implements Serializable {
|
||||
|
||||
@Schema(description = "字段路径(支持 a.b,与 JSON 一致)")
|
||||
private String fieldKey;
|
||||
|
||||
@@ -19,4 +18,51 @@ public class PrintBizFieldItemVO implements Serializable {
|
||||
|
||||
@Schema(description = "说明")
|
||||
private String description;
|
||||
|
||||
/** 声明类型全限定名(如 java.lang.String、java.time.LocalDateTime) */
|
||||
@Schema(description = "Java 声明类型全限定名")
|
||||
private String javaType;
|
||||
|
||||
/**
|
||||
* 与 JDBC 习惯对齐的类型名(如 VARCHAR、BIGINT、DECIMAL、TIMESTAMP);非 JDBC 标量填 JAVA_OBJECT。
|
||||
*/
|
||||
@Schema(description = "粗粒度 JDBC 风格类型名")
|
||||
private String jdbcType;
|
||||
|
||||
/**
|
||||
* 粗分类:STRING、BOOLEAN、INTEGER、LONG、DECIMAL、DATE、TIME、DATETIME、ENUM、BINARY、OTHER、JAVA_OBJECT
|
||||
*/
|
||||
@Schema(description = "简化种类,便于前端格式化")
|
||||
private String simpleKind;
|
||||
|
||||
/** 兼容旧三参构造(类型字段为空) */
|
||||
public PrintBizFieldItemVO(String fieldKey, String label, String description) {
|
||||
this.fieldKey = fieldKey;
|
||||
this.label = label;
|
||||
this.description = description == null ? "" : description;
|
||||
}
|
||||
|
||||
/** 复制前缀明细字段时保留类型元数据 */
|
||||
public static PrintBizFieldItemVO copyWithPrefixedPath(
|
||||
PrintBizFieldItemVO src, String prefixedFieldKey, String prefixedLabel) {
|
||||
PrintBizFieldItemVO o = new PrintBizFieldItemVO();
|
||||
o.setFieldKey(prefixedFieldKey);
|
||||
o.setLabel(prefixedLabel);
|
||||
o.setDescription(src != null ? src.getDescription() : "");
|
||||
if (src != null) {
|
||||
o.setJavaType(src.getJavaType());
|
||||
o.setJdbcType(src.getJdbcType());
|
||||
o.setSimpleKind(src.getSimpleKind());
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
/** 仅占位字符串数组解析出来的单项(视为字符串) */
|
||||
public static PrintBizFieldItemVO plainStringField(String fieldKey) {
|
||||
PrintBizFieldItemVO o = new PrintBizFieldItemVO(fieldKey, fieldKey, "");
|
||||
o.setJavaType(String.class.getName());
|
||||
o.setJdbcType("VARCHAR");
|
||||
o.setSimpleKind("STRING");
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
-- 业务实体字段缓存表:供「业务打印绑定」下拉读取;数据由启动任务根据 print_biz_perm_entity 异步扫描写入
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `mes_xsl_biz_entity_field_profile` (
|
||||
`id` varchar(32) NOT NULL COMMENT '主键',
|
||||
`business_name` varchar(200) NOT NULL COMMENT '业务名称',
|
||||
`business_code` varchar(64) NOT NULL COMMENT '业务编码(菜单 permission id,与 print 绑定 biz_code 一致)',
|
||||
`entity_class_name` varchar(512) DEFAULT NULL COMMENT '主实体 Java 全限定类名',
|
||||
`main_fields_json` text COMMENT '主表字段列表 JSON(PrintBizFieldItemVO 数组)',
|
||||
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
|
||||
`tenant_id` int DEFAULT NULL COMMENT '租户ID',
|
||||
`create_by` varchar(32) DEFAULT NULL COMMENT '创建人',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`update_by` varchar(32) DEFAULT NULL COMMENT '更新人',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_mxbefp_bcode` (`business_code`),
|
||||
KEY `idx_mxbefp_tenant` (`tenant_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MES业务实体字段配置-主表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `mes_xsl_biz_entity_field_detail` (
|
||||
`id` varchar(32) NOT NULL COMMENT '主键',
|
||||
`profile_id` varchar(32) NOT NULL COMMENT '主表ID',
|
||||
`detail_property_name` varchar(128) DEFAULT NULL COMMENT '主实体明细属性名(如 lines,与打印绑定 detailProperty 一致)',
|
||||
`detail_slot_kind` varchar(16) DEFAULT NULL COMMENT 'LIST 或 OBJECT',
|
||||
`detail_name` varchar(200) DEFAULT NULL COMMENT '明细展示名称',
|
||||
`detail_entity_class_name` varchar(512) DEFAULT NULL COMMENT '明细元素类型全限定名',
|
||||
`detail_fields_json` text COMMENT '明细元素类字段列表 JSON(无前缀,PrintBizFieldItemVO 数组)',
|
||||
`sort_no` int DEFAULT NULL COMMENT '排序号',
|
||||
`create_by` varchar(32) DEFAULT NULL COMMENT '创建人',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`update_by` varchar(32) DEFAULT NULL COMMENT '更新人',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_mxbefd_profile` (`profile_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='MES业务实体字段配置-明细槽位字段清单';
|
||||
@@ -0,0 +1,25 @@
|
||||
-- 旧库升级:明细表若早于完整脚本创建,可能缺少 detail_property_name / detail_slot_kind(兼容 MySQL 5.7+)
|
||||
|
||||
SELECT COUNT(*) INTO @jeecg_chk_dpn FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'mes_xsl_biz_entity_field_detail'
|
||||
AND COLUMN_NAME = 'detail_property_name';
|
||||
|
||||
SET @jeecg_sql_dpn := IF(@jeecg_chk_dpn = 0,
|
||||
'ALTER TABLE mes_xsl_biz_entity_field_detail ADD COLUMN detail_property_name varchar(128) DEFAULT NULL COMMENT ''主实体明细属性名(与打印绑定 detailProperty 一致)'' AFTER profile_id',
|
||||
'SELECT 1');
|
||||
PREPARE jeecg_stmt_dpn FROM @jeecg_sql_dpn;
|
||||
EXECUTE jeecg_stmt_dpn;
|
||||
DEALLOCATE PREPARE jeecg_stmt_dpn;
|
||||
|
||||
SELECT COUNT(*) INTO @jeecg_chk_dsk FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'mes_xsl_biz_entity_field_detail'
|
||||
AND COLUMN_NAME = 'detail_slot_kind';
|
||||
|
||||
SET @jeecg_sql_dsk := IF(@jeecg_chk_dsk = 0,
|
||||
'ALTER TABLE mes_xsl_biz_entity_field_detail ADD COLUMN detail_slot_kind varchar(16) DEFAULT NULL COMMENT ''LIST 或 OBJECT'' AFTER detail_property_name',
|
||||
'SELECT 1');
|
||||
PREPARE jeecg_stmt_dsk FROM @jeecg_sql_dsk;
|
||||
EXECUTE jeecg_stmt_dsk;
|
||||
DEALLOCATE PREPARE jeecg_stmt_dsk;
|
||||
Reference in New Issue
Block a user