Browse Source

配分事项录入排序

chenshudong 1 week ago
parent
commit
27c46788e9

+ 47 - 6
airport-admin/src/main/java/com/sundot/airport/web/controller/score/ScoreEventController.java

@@ -11,6 +11,7 @@ import com.sundot.airport.common.core.page.TableDataInfo;
11 11
 import com.sundot.airport.common.enums.BusinessType;
12 12
 import com.sundot.airport.common.enums.ScoreTypeEnum;
13 13
 import com.sundot.airport.common.utils.DateUtils;
14
+import com.sundot.airport.common.utils.NameUtils;
14 15
 import com.sundot.airport.common.utils.poi.ExcelUtil;
15 16
 import com.sundot.airport.ledger.domain.ScoreEvent;
16 17
 import com.sundot.airport.ledger.domain.ScoreIndicator;
@@ -28,7 +29,12 @@ import org.springframework.web.multipart.MultipartFile;
28 29
 import javax.servlet.http.HttpServletResponse;
29 30
 import java.math.BigDecimal;
30 31
 import java.util.ArrayList;
32
+import java.util.Collections;
33
+import java.util.HashSet;
34
+import java.util.LinkedHashMap;
31 35
 import java.util.List;
36
+import java.util.Map;
37
+import java.util.Set;
32 38
 import java.util.UUID;
33 39
 
34 40
 /**
@@ -38,6 +44,16 @@ import java.util.UUID;
38 44
 @RequestMapping("/score/event")
39 45
 public class ScoreEventController extends BaseController {
40 46
 
47
+    // 排序字段白名单(防 SQL 注入)
48
+    private static final Set<String> ALLOWED_SORT_COLUMNS;
49
+
50
+    static {
51
+        Set<String> set = new HashSet<>();
52
+        set.add("create_time");
53
+        set.add("event_time");
54
+        ALLOWED_SORT_COLUMNS = Collections.unmodifiableSet(set);
55
+    }
56
+
41 57
     @Autowired
42 58
     private IScoreEventService service;
43 59
 
@@ -51,6 +67,9 @@ public class ScoreEventController extends BaseController {
51 67
     @GetMapping("/list")
52 68
     public TableDataInfo list(ScoreEvent query) {
53 69
         startPage();
70
+        // 排序字段白名单过滤
71
+        Map<String, String> safeSort = buildSafeSort(query.getSorts());
72
+        query.setSorts(safeSort);
54 73
         return getDataTable(service.selectList(query));
55 74
     }
56 75
 
@@ -75,10 +94,10 @@ public class ScoreEventController extends BaseController {
75 94
         entity.setSourceType("1");
76 95
         entity.setCreateBy(getUsername());
77 96
         entity.setCreateTime(DateUtils.getNowDate());
78
-        
97
+
79 98
         // 通过指标名称查找并设置ID
80 99
         resolveIndicatorIds(entity);
81
-        
100
+
82 101
         // 计算 totalScore
83 102
         BigDecimal score = entity.getScoreValue() != null ? entity.getScoreValue() : BigDecimal.ZERO;
84 103
         BigDecimal cascade = entity.getCascadeScore() != null ? entity.getCascadeScore() : BigDecimal.ZERO;
@@ -99,10 +118,10 @@ public class ScoreEventController extends BaseController {
99 118
     public AjaxResult edit(@Validated @RequestBody ScoreEvent entity) {
100 119
         entity.setUpdateBy(getUsername());
101 120
         entity.setUpdateTime(DateUtils.getNowDate());
102
-        
121
+
103 122
         // 通过指标名称查找并设置ID
104 123
         resolveIndicatorIds(entity);
105
-        
124
+
106 125
         BigDecimal score = entity.getScoreValue() != null ? entity.getScoreValue() : BigDecimal.ZERO;
107 126
         BigDecimal cascade = entity.getCascadeScore() != null ? entity.getCascadeScore() : BigDecimal.ZERO;
108 127
         entity.setTotalScore(score.add(cascade));
@@ -156,7 +175,7 @@ public class ScoreEventController extends BaseController {
156 175
                 entity.setLevel2Id(level2Id);
157 176
             }
158 177
         }
159
-        
178
+
160 179
         // 通过三级指标名称查找ID
161 180
         if (entity.getLevel3Name() != null && !entity.getLevel3Name().trim().isEmpty() && entity.getLevel3Id() == null) {
162 181
             Long level3Id = findIndicatorIdByName(entity.getLevel3Name().trim());
@@ -164,7 +183,7 @@ public class ScoreEventController extends BaseController {
164 183
                 entity.setLevel3Id(level3Id);
165 184
             }
166 185
         }
167
-        
186
+
168 187
         // 通过四级指标名称查找ID
169 188
         if (entity.getLevel4Name() != null && !entity.getLevel4Name().trim().isEmpty() && entity.getLevel4Id() == null) {
170 189
             Long level4Id = findIndicatorIdByName(entity.getLevel4Name().trim());
@@ -210,4 +229,26 @@ public class ScoreEventController extends BaseController {
210 229
         }
211 230
         return basePositionService.selectBasePositionById(regional.getParentId());
212 231
     }
232
+
233
+    /**
234
+     * 构建安全的排序参数
235
+     */
236
+    private Map<String, String> buildSafeSort(Map<String, String> sorts) {
237
+        Map<String, String> result = new LinkedHashMap<>();
238
+
239
+        if (sorts == null) {
240
+            return result;
241
+        }
242
+
243
+        for (Map.Entry<String, String> entry : sorts.entrySet()) {
244
+            String field = NameUtils.camelToUnderscore(entry.getKey());
245
+            if (!ALLOWED_SORT_COLUMNS.contains(field)) {
246
+                continue;
247
+            }
248
+            String direction = "desc".equalsIgnoreCase(String.valueOf(entry.getValue())) ? "desc" : "asc";
249
+
250
+            result.put(field, direction);
251
+        }
252
+        return result;
253
+    }
213 254
 }

+ 15 - 0
airport-common/src/main/java/com/sundot/airport/common/core/domain/BaseEntity.java

@@ -59,6 +59,13 @@ public class BaseEntity implements Serializable {
59 59
     @JsonInclude(JsonInclude.Include.NON_EMPTY)
60 60
     private Map<String, Object> params;
61 61
 
62
+    /**
63
+     * 排序参数
64
+     */
65
+    @TableField(exist = false)
66
+    @JsonInclude(JsonInclude.Include.NON_EMPTY)
67
+    private Map<String, String> sorts;
68
+
62 69
     public String getSearchValue() {
63 70
         return searchValue;
64 71
     }
@@ -117,4 +124,12 @@ public class BaseEntity implements Serializable {
117 124
     public void setParams(Map<String, Object> params) {
118 125
         this.params = params;
119 126
     }
127
+
128
+    public Map<String, String> getSorts() {
129
+        return sorts;
130
+    }
131
+
132
+    public void setSorts(Map<String, String> sorts) {
133
+        this.sorts = sorts;
134
+    }
120 135
 }

+ 28 - 0
airport-common/src/main/java/com/sundot/airport/common/utils/NameUtils.java

@@ -0,0 +1,28 @@
1
+package com.sundot.airport.common.utils;
2
+
3
+/**
4
+ * 字段名称工具类
5
+ */
6
+public class NameUtils {
7
+
8
+    /**
9
+     * 驼峰转下划线
10
+     * @param name
11
+     * @return
12
+     */
13
+    public static String camelToUnderscore(String name) {
14
+        if (name == null || name.isEmpty()) {
15
+            return name;
16
+        }
17
+        StringBuilder sb = new StringBuilder();
18
+        for (char c : name.toCharArray()) {
19
+            if (Character.isUpperCase(c)) {
20
+                sb.append("_").append(Character.toLowerCase(c));
21
+            } else {
22
+                sb.append(c);
23
+            }
24
+        }
25
+        return sb.toString();
26
+    }
27
+
28
+}

+ 18 - 1
airport-ledger/src/main/resources/mapper/ledger/ScoreEventMapper.xml

@@ -62,6 +62,23 @@
62 62
         WHERE del_flag = '0'
63 63
     </sql>
64 64
 
65
+    <sql id="order_by">
66
+        <choose>
67
+            <when test="sorts != null and sorts.size() > 0">
68
+                order by
69
+                <foreach collection="sorts.entrySet()"
70
+                         index="field"
71
+                         item="dir"
72
+                         separator=",">
73
+                    ${field} ${dir}
74
+                </foreach>
75
+            </when>
76
+            <otherwise>
77
+                order by event_time desc
78
+            </otherwise>
79
+        </choose>
80
+    </sql>
81
+
65 82
     <select id="selectList" parameterType="com.sundot.airport.ledger.domain.ScoreEvent"
66 83
             resultMap="ScoreEventResult">
67 84
         <include refid="selectVo"/>
@@ -96,7 +113,7 @@
96 113
         <if test="params != null and params.endTime != null and params.endTime != ''">
97 114
             AND event_time &lt;= #{params.endTime}
98 115
         </if>
99
-        ORDER BY event_time DESC, id DESC
116
+        <include refid="order_by"/>
100 117
     </select>
101 118
 
102 119
 </mapper>