diff --git a/flyfish-data/src/main/java/com/flyfish/framework/annotations/Properties.java b/flyfish-data/src/main/java/com/flyfish/framework/annotations/Properties.java index 485dbe9..215b1c2 100644 --- a/flyfish-data/src/main/java/com/flyfish/framework/annotations/Properties.java +++ b/flyfish-data/src/main/java/com/flyfish/framework/annotations/Properties.java @@ -13,14 +13,9 @@ import java.lang.annotation.*; public @interface Properties { /** - * 需要映射的key - * @return 键们 + * 属性们 + * + * @return 结果 */ - String[] keys(); - - /** - * 需要映射的标题 - * @return 标题们 - */ - String[] titles(); + Property[] value(); } diff --git a/flyfish-data/src/main/java/com/flyfish/framework/annotations/Property.java b/flyfish-data/src/main/java/com/flyfish/framework/annotations/Property.java index 4882c7a..1dd9a0f 100644 --- a/flyfish-data/src/main/java/com/flyfish/framework/annotations/Property.java +++ b/flyfish-data/src/main/java/com/flyfish/framework/annotations/Property.java @@ -9,13 +9,22 @@ import java.lang.annotation.*; * * @author wangyu */ -@Target({ElementType.FIELD}) +@Target({ElementType.FIELD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented +@Repeatable(Properties.class) public @interface Property { + /** + * 手动指定key,用于覆盖模式 + * + * @return 结果 + */ + String key() default ""; + /** * 显示标题 + * * @return 结果 */ @AliasFor("title") @@ -23,6 +32,7 @@ public @interface Property { /** * 显示标题(别名) + * * @return 结果 */ @AliasFor("value") @@ -30,19 +40,36 @@ public @interface Property { /** * 描述 + * * @return 结果 */ String description() default ""; /** * 被继承的,用于父类,自动拼接名称 + * * @return 结果 */ boolean inherited() default false; /** * 是否只读 + * * @return 结果 */ boolean readonly() default false; + + /** + * 排序 + * + * @return 结果 + */ + int order() default 0; + + /** + * 分组 + * + * @return 结果 + */ + String group() default ""; } diff --git a/flyfish-data/src/main/java/com/flyfish/framework/annotations/PropertyGroup.java b/flyfish-data/src/main/java/com/flyfish/framework/annotations/PropertyGroup.java new file mode 100644 index 0000000..3948b80 --- /dev/null +++ b/flyfish-data/src/main/java/com/flyfish/framework/annotations/PropertyGroup.java @@ -0,0 +1,24 @@ +package com.flyfish.framework.annotations; + +import java.lang.annotation.*; + +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Repeatable(PropertyGroups.class) +public @interface PropertyGroup { + + /** + * 分组名称 + * + * @return 结果 + */ + String name(); + + /** + * 分组编码 + * + * @return 结果 + */ + String code(); +} diff --git a/flyfish-data/src/main/java/com/flyfish/framework/annotations/PropertyGroups.java b/flyfish-data/src/main/java/com/flyfish/framework/annotations/PropertyGroups.java new file mode 100644 index 0000000..a31a5f6 --- /dev/null +++ b/flyfish-data/src/main/java/com/flyfish/framework/annotations/PropertyGroups.java @@ -0,0 +1,15 @@ +package com.flyfish.framework.annotations; + +import java.lang.annotation.*; + +/** + * 属性组定义 + * @author wangyu + */ +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface PropertyGroups { + + PropertyGroup[] value(); +} diff --git a/flyfish-data/src/main/java/com/flyfish/framework/utils/DataUtils.java b/flyfish-data/src/main/java/com/flyfish/framework/utils/DataUtils.java index 9898159..eb53bd7 100644 --- a/flyfish-data/src/main/java/com/flyfish/framework/utils/DataUtils.java +++ b/flyfish-data/src/main/java/com/flyfish/framework/utils/DataUtils.java @@ -60,7 +60,10 @@ public final class DataUtils { return IntStream.range(0, Math.min(keys.length, names.length)) .boxed() .reduce(new ArrayList<>(), (result, index) -> { - result.add(mapper.apply(keys[index], names[index])); + R mapped = mapper.apply(keys[index], names[index]); + if (null != mapped) { + result.add(mapped); + } return result; }, (a, b) -> a); } diff --git a/flyfish-web/src/main/java/com/flyfish/framework/beans/meta/BeanController.java b/flyfish-web/src/main/java/com/flyfish/framework/beans/meta/BeanController.java index 9fc6a0c..fff926a 100644 --- a/flyfish-web/src/main/java/com/flyfish/framework/beans/meta/BeanController.java +++ b/flyfish-web/src/main/java/com/flyfish/framework/beans/meta/BeanController.java @@ -1,10 +1,12 @@ package com.flyfish.framework.beans.meta; +import com.flyfish.framework.annotations.PropertyGroups; import com.flyfish.framework.bean.Result; import com.flyfish.framework.beans.resolver.DynamicRestBeanResolver; import com.flyfish.framework.domain.base.Vo; import com.flyfish.framework.utils.StringFormats; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ClassUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.web.bind.annotation.GetMapping; @@ -13,6 +15,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; +import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; @@ -76,9 +79,22 @@ public class BeanController { if (!Vo.class.equals(annotation.listViewClass())) { info.setColumns(BeanProperty.from(annotation.listViewClass())); } + if (ArrayUtils.isNotEmpty(annotation.meta())) { + Arrays.stream(annotation.meta()).forEach(meta -> { + String[] kv = meta.split("="); + if (kv.length == 2) { + info.set(kv[0], kv[1]); + } + }); + } } else { info.setCode(StringFormats.camel2Line(ClassUtils.getShortClassName(clazz))); } + if (clazz.isAnnotationPresent(PropertyGroups.class)) { + PropertyGroups groups = clazz.getAnnotation(PropertyGroups.class); + info.setGroups(Arrays.stream(groups.value()).map(group -> new BeanPropertyGroup(group.name(), group.code())) + .collect(Collectors.toList())); + } info.setProperties(BeanProperty.from(clazz)); return info; } diff --git a/flyfish-web/src/main/java/com/flyfish/framework/beans/meta/BeanInfo.java b/flyfish-web/src/main/java/com/flyfish/framework/beans/meta/BeanInfo.java index 18e4799..b351df1 100644 --- a/flyfish-web/src/main/java/com/flyfish/framework/beans/meta/BeanInfo.java +++ b/flyfish-web/src/main/java/com/flyfish/framework/beans/meta/BeanInfo.java @@ -1,11 +1,14 @@ package com.flyfish.framework.beans.meta; import lombok.Data; +import org.springframework.beans.BeanUtils; import java.util.List; +import java.util.Objects; /** * bean的属性们 + * * @author wangyu */ @Data @@ -17,6 +20,12 @@ public class BeanInfo { // bean的显示名 private String name; + // bean的类型 + private String type; + + // 分组们 + private List groups; + // bean的属性们 private List properties; @@ -25,4 +34,19 @@ public class BeanInfo { // 表格查询属性 private List search; + + /** + * 自由设置值 + * + * @param key 键 + * @param value 值 + */ + public void set(String key, Object value) { + try { + Objects.requireNonNull(BeanUtils.getPropertyDescriptor(this.getClass(), key)) + .getWriteMethod().invoke(this, value); + } catch (Exception ignored) { + + } + } } diff --git a/flyfish-web/src/main/java/com/flyfish/framework/beans/meta/BeanProperty.java b/flyfish-web/src/main/java/com/flyfish/framework/beans/meta/BeanProperty.java index 7573f04..0867373 100644 --- a/flyfish-web/src/main/java/com/flyfish/framework/beans/meta/BeanProperty.java +++ b/flyfish-web/src/main/java/com/flyfish/framework/beans/meta/BeanProperty.java @@ -5,7 +5,6 @@ import com.flyfish.framework.annotations.Properties; import com.flyfish.framework.annotations.*; import com.flyfish.framework.domain.base.Qo; import com.flyfish.framework.domain.base.Vo; -import com.flyfish.framework.utils.DataUtils; import com.flyfish.framework.utils.ReflectionUtils; import com.flyfish.framework.utils.StringFormats; import lombok.Data; @@ -57,6 +56,12 @@ public class BeanProperty { // 类型为object时,拥有子表单 private List children; + // 排序变量,用于排序 + private transient int order; + + // 所属分组,该分组必须在元数据中定义 + private String group; + /** * 来自属性解释器构造 * 支持对象嵌套 @@ -85,6 +90,7 @@ public class BeanProperty { property.setTitle(props.inherited() ? parentName + props.title() : props.title()); property.setDescription(props.description()); property.setReadonly(props.readonly()); + property.setOrder(props.order()); } else if (strict) { property.setReadonly(true); return property; @@ -132,8 +138,9 @@ public class BeanProperty { parseSubClass(field).ifPresent(subClazz -> property.setChildren(from(subClazz))); } else if (field.isAnnotationPresent(DateRange.class)) { property.setType(BeanPropertyType.DATE); - property.prop("type", "range"); - property.prop("placeholder", Arrays.asList("开始时间", "结束时间")); + property + .prop("type", "range") + .prop("placeholder", Arrays.asList("开始时间", "结束时间")); } } break; @@ -194,7 +201,9 @@ public class BeanProperty { .map(descriptor -> BeanProperty.form(descriptor, clazz)) .filter(property -> !property.isReadonly()) .collect(Collectors.toList()); - return ListUtils.union(parseExtras(clazz), properties); + List result = ListUtils.union(parseExtras(clazz, properties), properties); + result.sort(Comparator.comparingInt(a -> a.order)); + return result; } /** @@ -210,21 +219,37 @@ public class BeanProperty { /** * 解析额外的注解 + * 以该注解的优先级更高 * - * @param clazz 类 + * @param clazz 类 + * @param origin 原本的元数据 * @return 结果 */ - private static List parseExtras(Class clazz) { + private static List parseExtras(Class clazz, List origin) { // 包含properties if (clazz.isAnnotationPresent(Properties.class)) { + List result = new ArrayList<>(); Properties properties = clazz.getAnnotation(Properties.class); - return DataUtils.zip(properties.keys(), properties.titles(), (key, name) -> { - BeanProperty property = new BeanProperty(); - property.setType(BeanPropertyType.STRING); - property.setName(key); - property.setTitle(name); - return property; - }); + for (Property prop : properties.value()) { + String key = prop.key(); + // 如果基础元数据已经包含了这部分,相当于重载属性,此时不会调整其他属性,而是替换名称(将来可能扩展更多) + Optional found = origin.stream().filter(property -> property.name.equals(key)) + .findFirst(); + if (found.isPresent()) { + BeanProperty property = found.get(); + property.title = prop.title(); + if (prop.order() != 0) { + property.order = prop.order(); + } + } else { + BeanProperty property = new BeanProperty(); + property.setType(BeanPropertyType.STRING); + property.setName(key); + property.setTitle(prop.title()); + result.add(property); + } + } + return result; } return Collections.emptyList(); } diff --git a/flyfish-web/src/main/java/com/flyfish/framework/beans/meta/BeanPropertyGroup.java b/flyfish-web/src/main/java/com/flyfish/framework/beans/meta/BeanPropertyGroup.java new file mode 100644 index 0000000..015c616 --- /dev/null +++ b/flyfish-web/src/main/java/com/flyfish/framework/beans/meta/BeanPropertyGroup.java @@ -0,0 +1,20 @@ +package com.flyfish.framework.beans.meta; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 对象属性分组 + * + * @author wangyu + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class BeanPropertyGroup { + + private String name; + + private String code; +} diff --git a/flyfish-web/src/main/java/com/flyfish/framework/beans/meta/RestBean.java b/flyfish-web/src/main/java/com/flyfish/framework/beans/meta/RestBean.java index 446ce53..d8d12e1 100644 --- a/flyfish-web/src/main/java/com/flyfish/framework/beans/meta/RestBean.java +++ b/flyfish-web/src/main/java/com/flyfish/framework/beans/meta/RestBean.java @@ -41,10 +41,18 @@ public @interface RestBean { /** * 排除的属性 + * * @return 结果 */ String[] exclude() default {}; + /** + * 元数据,可以自由地指定一些值 + * + * @return 结果 + */ + String[] meta() default {}; + /** * 必须指定qo *