影落离风

The shadow falls away from the wind

0%

对视图模型构造相关参数的轮子

前言

清明放假没事在家写一些常用的轮子,以便减少一些重复繁琐的编码工作。

话不多说,继续往下看

有时我们的接口数据返回给前端或者导出为excel时,需要给相关值带单位(kg/立方米/%等)、或保留小数位等。尤其是一些统计类的报表,值特别多、单位也就贼多了。

做法

  1. 查询时在sql中拼接。给sql添加了好多无聊的字符,看起来都不好看,维护性不好,看起来都头疼
  2. 业务代码中循环拼接。这也很麻烦
  3. 交给前端回显时去拼接。前端有可能要骂娘

想法

  1. 既然是面向对象编程,那最好就是有维护一个视图对象,供我们返回到前端
  2. 一般我们通过sql查出来的po、dto模型或者通过业务处理后的bo模型不会直接给到前端,而是处理到vo再给到前端。
  3. 所以我就在想,我可以定义一些注解指明这个属性的单位是啥、保留小数位、或者替换符,然后通过一个对象转换的方法,获取到该属性注解的值,再通过反射赋予到vo模型中去。
  4. 想起来很简单,说干就干。不到两小时就撸出来了这个轮子,代码都很简单,主要是要有一些可以简化撸码的想法。也方便我们工作。

代码

可置于需要注解的对象的属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
* 响应体vo属性
*
* @author MinWeikai
* @date 2021/3/25 9:34
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RespVoProperty {

/**
* 保留小数位数
* 默认-1不进行保留
*
* @return
*/
int keepDecimal() default -1;

/**
* 单位
*
* @return
*/
String unit() default "";

/**
* 替换值
*
* @return
*/
String replaceStr() default "";
}

vo模型使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Data
public class PersonVo {

private String name;

@RespVoProperty(unit = "块")
private String money;

@RespVoProperty(unit = "岁", replaceStr = "-")
private String age;

@RespVoProperty(unit = "kg", keepDecimal = 2)
private String weight;

}

转换方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
/**
* 数值集合对象转换为字符集合对象,
* 可自定义数值保留位数,无值时的替换符、单位等
*
* @author MinWeikai
* @date 2021/4/3 11:20
*/
public class Converts {


/**
* 匹配并格式化属性
*
* @param source
* @param keepDecimal
* @param unit
* @param replaceStr
* @param data
* @param field
* @param <T>
*/
private static <T> void formatVoField(Object source, int keepDecimal, String unit, String replaceStr, T data, Field field) {
if (Objects.equals("serialVersionUID", field.getName())) {
return;
}
Object oldTemp = null;
try {
oldTemp = ReflectionUtil.invokeGetterMethod(source, field.getName());
} catch (Exception e) {
e.printStackTrace();
}

// 此处可自定义规则
switch (field.getType().getSimpleName()) {
case "Float":
if (ObjectUtil.isNotNull(oldTemp)) {
if (keepDecimal == -1) {
oldTemp = new BigDecimal((float) oldTemp);
} else {
oldTemp = new BigDecimal((float) oldTemp)
.setScale(keepDecimal, BigDecimal.ROUND_HALF_UP);
}
oldTemp = String.valueOf(oldTemp).concat(unit);
} else {
oldTemp = replaceStr;
}
break;
case "Integer":
if (ObjectUtil.isNotNull(oldTemp)) {
oldTemp = String.valueOf(oldTemp).concat(unit);
} else {
oldTemp = replaceStr;
}
break;
case "BigDecimal":
if (ObjectUtil.isNotNull(oldTemp)) {
if (keepDecimal == -1) {
oldTemp = new BigDecimal(oldTemp.toString());
} else {
oldTemp = new BigDecimal(oldTemp.toString())
.setScale(keepDecimal, BigDecimal.ROUND_HALF_UP);
}
oldTemp = String.valueOf(oldTemp).concat(unit);
} else {
oldTemp = replaceStr;
}
break;
default:
}
if (ObjectUtil.isNotNull(oldTemp)) {
ReflectionUtil.invokeSetterMethod(data, field.getName(), oldTemp);
}
}


/**
* 转化为响应模型
*
* @param source 源对象
* @param voClass vo类
* @param <T>
* @return
*/
public static <T> T toRespVo(Object source, final Class<T> voClass) {
T data = null;
try {
data = voClass.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
// 返回模型属性map
Map<String, Field> voFieldsMap = Arrays.stream(voClass.getDeclaredFields())
.collect(Collectors.toMap(Field::getName, Function.identity()));
// 对老属性赋予参数并置返回模型
for (Field field : source.getClass().getDeclaredFields()) {
RespVoProperty property = Optional.ofNullable(voFieldsMap.get(field.getName()))
.map(annotationClass -> annotationClass.getAnnotation(RespVoProperty.class))
.orElse(null);
if (Objects.isNull(property)) {
formatVoField(source, -1, "", "", data, field);
} else {
formatVoField(source, property.keepDecimal(), property.unit(), property.replaceStr(), data, field);
}
}
return data;
}


/**
* 集合转化为响应模型集合
*
* @param sources 源对象集合
* @param voClass vo类
* @param <T>
* @return
*/
public static <T> List<T> toRespVos(List<? extends Object> sources, final Class<T> voClass) {
List<T> targets = new ArrayList<>();
if (CollectionUtils.isEmpty(sources)) {
return targets;
}
sources.parallelStream().forEach(source -> targets.add(toRespVo(source, voClass)));
return targets;
}


// 测试方法
public static void main(String[] args) {
List<Person> list = new ArrayList<>();
Person data1 = new Person();
data1.setName("李三");
data1.setAge(14);
data1.setMoney(BigDecimal.ONE);
data1.setWeight(15.2f);

System.out.println("-----------------单对象转化vo----------------");

System.out.println(toRespVo(data1, PersonVo.class));

System.out.println("--------------------------------------------");


System.out.println("-----------------集合对象转化vo----------------");

list.add(data1);

Person data2 = new Person();
data2.setName("wangwu");
data2.setAge(45);
data2.setMoney(BigDecimal.valueOf(6.41f));
data2.setWeight(null);
list.add(data2);

Person data3 = new Person();
data3.setName("张思");
data3.setAge(null);
data3.setMoney(BigDecimal.valueOf(25f));
data3.setWeight(17.689f);
list.add(data3);

System.out.println(toRespVos(list, PersonVo.class));
}
}

方法中用到了一个反射方法工具

ReflectionUtil工具地址