|
|
@@ -0,0 +1,729 @@
|
|
|
1
|
+package com.sundot.airport.web.controller.exam;
|
|
|
2
|
+
|
|
|
3
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
4
|
+import com.sundot.airport.common.core.domain.entity.SysDept;
|
|
|
5
|
+import com.sundot.airport.common.core.domain.entity.SysRole;
|
|
|
6
|
+import com.sundot.airport.common.core.domain.model.LoginUser;
|
|
|
7
|
+import com.sundot.airport.common.enums.RoleTypeEnum;
|
|
|
8
|
+import com.sundot.airport.common.utils.SecurityUtils;
|
|
|
9
|
+import com.sundot.airport.exam.common.HttpResult;
|
|
|
10
|
+import com.sundot.airport.exam.common.service.QuesCatService;
|
|
|
11
|
+import com.sundot.airport.exam.domain.DailyTask;
|
|
|
12
|
+import com.sundot.airport.exam.domain.DailyTaskDetail;
|
|
|
13
|
+import com.sundot.airport.exam.domain.QuesCat;
|
|
|
14
|
+import com.sundot.airport.exam.mapper.DailyTaskDetailMapper;
|
|
|
15
|
+import com.sundot.airport.exam.mapper.DailyTaskMapper;
|
|
|
16
|
+import com.sundot.airport.system.domain.BaseCheckCategory;
|
|
|
17
|
+import com.sundot.airport.system.mapper.SysDeptMapper;
|
|
|
18
|
+import com.sundot.airport.system.service.IBaseCheckCategoryService;
|
|
|
19
|
+import org.slf4j.Logger;
|
|
|
20
|
+import org.slf4j.LoggerFactory;
|
|
|
21
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
22
|
+import org.springframework.web.bind.annotation.GetMapping;
|
|
|
23
|
+import org.springframework.web.bind.annotation.RequestMapping;
|
|
|
24
|
+import org.springframework.web.bind.annotation.RequestParam;
|
|
|
25
|
+import org.springframework.web.bind.annotation.RestController;
|
|
|
26
|
+
|
|
|
27
|
+import java.sql.Date;
|
|
|
28
|
+import java.text.SimpleDateFormat;
|
|
|
29
|
+import java.time.LocalDate;
|
|
|
30
|
+import java.time.format.DateTimeFormatter;
|
|
|
31
|
+import java.util.*;
|
|
|
32
|
+import java.util.stream.Collectors;
|
|
|
33
|
+
|
|
|
34
|
+/**
|
|
|
35
|
+ * 抽问抽答错题分析接口(美兰4级架构适配版)
|
|
|
36
|
+ * <p>
|
|
|
37
|
+ * 美兰组织架构:STATION(安检站)> BRIGADE(大队)> MANAGER(主管)> TEAMS(班组)
|
|
|
38
|
+ * <p>
|
|
|
39
|
+ * 接口1: GET /wrong-analysis/overview 总体问题分布(饼图,所有角色)
|
|
|
40
|
+ * 接口2: GET /wrong-analysis/radar 问题分布对比(雷达图,仅站长)
|
|
|
41
|
+ * 接口3: GET /wrong-analysis/category-detail 大类详情
|
|
|
42
|
+ * - 站长:各大队对比表格 + 各大队趋势
|
|
|
43
|
+ * - 大队长:各主管对比表格 + 各主管趋势
|
|
|
44
|
+ * - 主管:本主管各小类甜甜圈 + 本主管趋势
|
|
|
45
|
+ * - 班组长及以下:show=false
|
|
|
46
|
+ */
|
|
|
47
|
+@RestController
|
|
|
48
|
+@RequestMapping("/v1/cs/app/daily-exam/wrong-analysis")
|
|
|
49
|
+public class DailyExamWrongAnalysisController {
|
|
|
50
|
+
|
|
|
51
|
+ private static final Logger log = LoggerFactory.getLogger(DailyExamWrongAnalysisController.class);
|
|
|
52
|
+
|
|
|
53
|
+ @Autowired
|
|
|
54
|
+ private DailyTaskMapper dailyTaskMapper;
|
|
|
55
|
+
|
|
|
56
|
+ @Autowired
|
|
|
57
|
+ private DailyTaskDetailMapper dailyTaskDetailMapper;
|
|
|
58
|
+
|
|
|
59
|
+ @Autowired
|
|
|
60
|
+ private SysDeptMapper sysDeptMapper;
|
|
|
61
|
+
|
|
|
62
|
+ @Autowired
|
|
|
63
|
+ private QuesCatService quesCatService;
|
|
|
64
|
+
|
|
|
65
|
+ @Autowired
|
|
|
66
|
+ private IBaseCheckCategoryService baseCheckCategoryService;
|
|
|
67
|
+
|
|
|
68
|
+ // =====================================================================
|
|
|
69
|
+ // 接口1:总体问题分布(饼图)
|
|
|
70
|
+ // =====================================================================
|
|
|
71
|
+
|
|
|
72
|
+ /**
|
|
|
73
|
+ * 总体问题分布(按大类统计错题数)
|
|
|
74
|
+ * 站长/管理员/质检科:全站;大队长:本大队;主管:本主管室;班组长:本班组
|
|
|
75
|
+ *
|
|
|
76
|
+ * @param timeType year/month/custom,默认 month
|
|
|
77
|
+ * @param startDate 自定义开始日期 yyyy-MM-dd
|
|
|
78
|
+ * @param endDate 自定义结束日期 yyyy-MM-dd
|
|
|
79
|
+ */
|
|
|
80
|
+ @GetMapping("/overview")
|
|
|
81
|
+ public HttpResult<Map<String, Object>> getOverview(
|
|
|
82
|
+ @RequestParam(required = false, defaultValue = "month") String timeType,
|
|
|
83
|
+ @RequestParam(required = false) String startDate,
|
|
|
84
|
+ @RequestParam(required = false) String endDate) {
|
|
|
85
|
+
|
|
|
86
|
+ try {
|
|
|
87
|
+ RoleInfo role = getRoleInfo();
|
|
|
88
|
+ LocalDate[] range = resolveDateRange(timeType, startDate, endDate);
|
|
|
89
|
+
|
|
|
90
|
+ List<DailyTaskDetail> errorDetails = getErrorDetails(role, range[0], range[1]);
|
|
|
91
|
+
|
|
|
92
|
+ Map<String, BaseCheckCategory> qcIdToLevel1 = buildQcIdToLevel1Map(errorDetails);
|
|
|
93
|
+
|
|
|
94
|
+ Map<Long, long[]> countByCategory = new LinkedHashMap<>();
|
|
|
95
|
+ Map<Long, String> categoryNames = new LinkedHashMap<>();
|
|
|
96
|
+
|
|
|
97
|
+ for (DailyTaskDetail detail : errorDetails) {
|
|
|
98
|
+ BaseCheckCategory cat = qcIdToLevel1.get(detail.getDtdModuleId());
|
|
|
99
|
+ if (cat != null) {
|
|
|
100
|
+ countByCategory.merge(cat.getId(), new long[]{1, cat.getOrderNum() != null ? cat.getOrderNum() : 99},
|
|
|
101
|
+ (a, b) -> new long[]{a[0] + 1, a[1]});
|
|
|
102
|
+ categoryNames.put(cat.getId(), cat.getName());
|
|
|
103
|
+ }
|
|
|
104
|
+ }
|
|
|
105
|
+
|
|
|
106
|
+ List<Map<String, Object>> categories = countByCategory.entrySet().stream()
|
|
|
107
|
+ .sorted(Comparator.comparingLong(e -> e.getValue()[1]))
|
|
|
108
|
+ .map(e -> {
|
|
|
109
|
+ Map<String, Object> item = new LinkedHashMap<>();
|
|
|
110
|
+ item.put("id", e.getKey());
|
|
|
111
|
+ item.put("name", categoryNames.get(e.getKey()));
|
|
|
112
|
+ item.put("count", e.getValue()[0]);
|
|
|
113
|
+ return item;
|
|
|
114
|
+ })
|
|
|
115
|
+ .collect(Collectors.toList());
|
|
|
116
|
+
|
|
|
117
|
+ Map<String, Object> result = new LinkedHashMap<>();
|
|
|
118
|
+ result.put("categories", categories);
|
|
|
119
|
+ return HttpResult.success(result);
|
|
|
120
|
+
|
|
|
121
|
+ } catch (Exception e) {
|
|
|
122
|
+ log.error("获取总体问题分布失败", e);
|
|
|
123
|
+ return HttpResult.error("获取总体问题分布失败:" + e.getMessage());
|
|
|
124
|
+ }
|
|
|
125
|
+ }
|
|
|
126
|
+
|
|
|
127
|
+ // =====================================================================
|
|
|
128
|
+ // 接口2:问题分布对比(雷达图,仅站长)
|
|
|
129
|
+ // =====================================================================
|
|
|
130
|
+
|
|
|
131
|
+ /**
|
|
|
132
|
+ * 问题分布对比雷达图(仅站长可见,各大队在各大类的错题数对比)
|
|
|
133
|
+ *
|
|
|
134
|
+ * @param timeType year/month/custom,默认 month
|
|
|
135
|
+ * @param startDate 自定义开始日期
|
|
|
136
|
+ * @param endDate 自定义结束日期
|
|
|
137
|
+ */
|
|
|
138
|
+ @GetMapping("/radar")
|
|
|
139
|
+ public HttpResult<Map<String, Object>> getRadar(
|
|
|
140
|
+ @RequestParam(required = false, defaultValue = "month") String timeType,
|
|
|
141
|
+ @RequestParam(required = false) String startDate,
|
|
|
142
|
+ @RequestParam(required = false) String endDate) {
|
|
|
143
|
+
|
|
|
144
|
+ try {
|
|
|
145
|
+ RoleInfo role = getRoleInfo();
|
|
|
146
|
+ if (!role.isStation) {
|
|
|
147
|
+ Map<String, Object> noShow = new HashMap<>();
|
|
|
148
|
+ noShow.put("show", false);
|
|
|
149
|
+ return HttpResult.success(noShow);
|
|
|
150
|
+ }
|
|
|
151
|
+
|
|
|
152
|
+ LocalDate[] range = resolveDateRange(timeType, startDate, endDate);
|
|
|
153
|
+
|
|
|
154
|
+ // 查询所有大类,作为雷达图的维度
|
|
|
155
|
+ BaseCheckCategory level1Query = new BaseCheckCategory();
|
|
|
156
|
+ level1Query.setLevel(1);
|
|
|
157
|
+ List<BaseCheckCategory> level1Categories = baseCheckCategoryService.selectBaseCheckCategoryList(level1Query);
|
|
|
158
|
+ level1Categories.sort(Comparator.comparingInt(c -> c.getOrderNum() != null ? c.getOrderNum() : 99));
|
|
|
159
|
+
|
|
|
160
|
+ List<String> indicators = level1Categories.stream()
|
|
|
161
|
+ .map(BaseCheckCategory::getName)
|
|
|
162
|
+ .collect(Collectors.toList());
|
|
|
163
|
+ List<Long> indicatorIds = level1Categories.stream()
|
|
|
164
|
+ .map(BaseCheckCategory::getId)
|
|
|
165
|
+ .collect(Collectors.toList());
|
|
|
166
|
+
|
|
|
167
|
+ // 获取站下各大队(直接下级)
|
|
|
168
|
+ List<SysDept> subDepts = sysDeptMapper.selectChildrenDeptById(role.userDeptId);
|
|
|
169
|
+ List<SysDept> brigadeDepts = subDepts == null ? Collections.emptyList() :
|
|
|
170
|
+ subDepts.stream()
|
|
|
171
|
+ .filter(d -> role.userDeptId.equals(d.getParentId()) && "0".equals(d.getDelFlag()))
|
|
|
172
|
+ .collect(Collectors.toList());
|
|
|
173
|
+
|
|
|
174
|
+ // 为每个大队构建一条雷达折线
|
|
|
175
|
+ List<Map<String, Object>> series = new ArrayList<>();
|
|
|
176
|
+ for (SysDept brigade : brigadeDepts) {
|
|
|
177
|
+ // 查该大队下所有主管的错误明细(通过dtDepartmentId匹配主管子部门)
|
|
|
178
|
+ List<DailyTaskDetail> details = getErrorDetailsBySubDepts(brigade.getDeptId(), range[0], range[1]);
|
|
|
179
|
+ Map<String, BaseCheckCategory> qcIdToLevel1 = buildQcIdToLevel1Map(details);
|
|
|
180
|
+
|
|
|
181
|
+ Map<Long, Long> countMap = details.stream()
|
|
|
182
|
+ .filter(d -> qcIdToLevel1.get(d.getDtdModuleId()) != null)
|
|
|
183
|
+ .collect(Collectors.groupingBy(
|
|
|
184
|
+ d -> qcIdToLevel1.get(d.getDtdModuleId()).getId(),
|
|
|
185
|
+ Collectors.counting()));
|
|
|
186
|
+
|
|
|
187
|
+ List<Long> data = indicatorIds.stream()
|
|
|
188
|
+ .map(id -> countMap.getOrDefault(id, 0L))
|
|
|
189
|
+ .collect(Collectors.toList());
|
|
|
190
|
+
|
|
|
191
|
+ Map<String, Object> seriesItem = new LinkedHashMap<>();
|
|
|
192
|
+ seriesItem.put("name", brigade.getDeptName());
|
|
|
193
|
+ seriesItem.put("deptId", brigade.getDeptId());
|
|
|
194
|
+ seriesItem.put("data", data);
|
|
|
195
|
+ series.add(seriesItem);
|
|
|
196
|
+ }
|
|
|
197
|
+
|
|
|
198
|
+ Map<String, Object> result = new LinkedHashMap<>();
|
|
|
199
|
+ result.put("show", true);
|
|
|
200
|
+ result.put("indicators", indicators);
|
|
|
201
|
+ result.put("series", series);
|
|
|
202
|
+ return HttpResult.success(result);
|
|
|
203
|
+
|
|
|
204
|
+ } catch (Exception e) {
|
|
|
205
|
+ log.error("获取问题分布对比失败", e);
|
|
|
206
|
+ return HttpResult.error("获取问题分布对比失败:" + e.getMessage());
|
|
|
207
|
+ }
|
|
|
208
|
+ }
|
|
|
209
|
+
|
|
|
210
|
+ // =====================================================================
|
|
|
211
|
+ // 接口3:大类详情(站长/大队长/主管)
|
|
|
212
|
+ // =====================================================================
|
|
|
213
|
+
|
|
|
214
|
+ /**
|
|
|
215
|
+ * 大类详情 - 点击饼图某一块后展示该大类下的小类分布及趋势
|
|
|
216
|
+ * <p>
|
|
|
217
|
+ * 站长:各大队小类对比表 + 各大队按日趋势
|
|
|
218
|
+ * 大队长:各主管小类对比表 + 各主管按日趋势
|
|
|
219
|
+ * 主管:本主管各小类甜甜圈分布 + 本主管按日趋势
|
|
|
220
|
+ * 班组长及以下:show=false
|
|
|
221
|
+ *
|
|
|
222
|
+ * @param categoryId 大类ID(base_check_category.id,level=1)
|
|
|
223
|
+ * @param timeType year/month/custom,默认 month
|
|
|
224
|
+ * @param startDate 自定义开始日期
|
|
|
225
|
+ * @param endDate 自定义结束日期
|
|
|
226
|
+ */
|
|
|
227
|
+ @GetMapping("/category-detail")
|
|
|
228
|
+ public HttpResult<Map<String, Object>> getCategoryDetail(
|
|
|
229
|
+ @RequestParam Long categoryId,
|
|
|
230
|
+ @RequestParam(required = false, defaultValue = "month") String timeType,
|
|
|
231
|
+ @RequestParam(required = false) String startDate,
|
|
|
232
|
+ @RequestParam(required = false) String endDate) {
|
|
|
233
|
+
|
|
|
234
|
+ try {
|
|
|
235
|
+ RoleInfo role = getRoleInfo();
|
|
|
236
|
+
|
|
|
237
|
+ if (!role.isStation && !role.isBrigade && !role.isKezhang) {
|
|
|
238
|
+ Map<String, Object> noShow = new HashMap<>();
|
|
|
239
|
+ noShow.put("show", false);
|
|
|
240
|
+ return HttpResult.success(noShow);
|
|
|
241
|
+ }
|
|
|
242
|
+
|
|
|
243
|
+ LocalDate[] range = resolveDateRange(timeType, startDate, endDate);
|
|
|
244
|
+
|
|
|
245
|
+ BaseCheckCategory level1Cat = baseCheckCategoryService.selectBaseCheckCategoryById(categoryId);
|
|
|
246
|
+ if (level1Cat == null) {
|
|
|
247
|
+ return HttpResult.error("未找到该分类");
|
|
|
248
|
+ }
|
|
|
249
|
+
|
|
|
250
|
+ BaseCheckCategory level2Query = new BaseCheckCategory();
|
|
|
251
|
+ level2Query.setParentId(categoryId);
|
|
|
252
|
+ level2Query.setLevel(2);
|
|
|
253
|
+ List<BaseCheckCategory> subCategories = baseCheckCategoryService.selectBaseCheckCategoryList(level2Query);
|
|
|
254
|
+ subCategories.sort(Comparator.comparingInt(c -> c.getOrderNum() != null ? c.getOrderNum() : 99));
|
|
|
255
|
+
|
|
|
256
|
+ List<String> subCatNames = subCategories.stream()
|
|
|
257
|
+ .map(BaseCheckCategory::getName).collect(Collectors.toList());
|
|
|
258
|
+ Map<String, String> subCatNameToQcId = buildSubCatNameToQcIdMap(subCatNames);
|
|
|
259
|
+ Map<String, String> qcIdToSubCatName = new HashMap<>();
|
|
|
260
|
+ subCatNameToQcId.forEach((name, qcId) -> { if (qcId != null) qcIdToSubCatName.put(qcId, name); });
|
|
|
261
|
+
|
|
|
262
|
+ Map<String, Object> result = new LinkedHashMap<>();
|
|
|
263
|
+ result.put("show", true);
|
|
|
264
|
+ result.put("categoryId", categoryId);
|
|
|
265
|
+ result.put("categoryName", level1Cat.getName());
|
|
|
266
|
+
|
|
|
267
|
+ boolean byMonth = "year".equals(timeType);
|
|
|
268
|
+ List<String> xAxis = buildXAxis(range[0], range[1], byMonth);
|
|
|
269
|
+ SimpleDateFormat sdf = new SimpleDateFormat(byMonth ? "yyyy-MM" : "yyyy-MM-dd");
|
|
|
270
|
+
|
|
|
271
|
+ if (role.isStation) {
|
|
|
272
|
+ // ---- 站长视角:各大队对比 ----
|
|
|
273
|
+ List<SysDept> subDepts = sysDeptMapper.selectChildrenDeptById(role.userDeptId);
|
|
|
274
|
+ List<SysDept> brigadeDepts = subDepts == null ? Collections.emptyList() :
|
|
|
275
|
+ subDepts.stream()
|
|
|
276
|
+ .filter(d -> role.userDeptId.equals(d.getParentId()) && "0".equals(d.getDelFlag()))
|
|
|
277
|
+ .collect(Collectors.toList());
|
|
|
278
|
+
|
|
|
279
|
+ // 查各大队错误明细(按qcId过滤)
|
|
|
280
|
+ Map<Long, List<DailyTaskDetail>> detailsByBrigade = new LinkedHashMap<>();
|
|
|
281
|
+ for (SysDept brigade : brigadeDepts) {
|
|
|
282
|
+ List<DailyTaskDetail> details = getErrorDetailsBySubDeptsAndQcIds(
|
|
|
283
|
+ brigade.getDeptId(), new HashSet<>(qcIdToSubCatName.keySet()), range[0], range[1]);
|
|
|
284
|
+ detailsByBrigade.put(brigade.getDeptId(), details);
|
|
|
285
|
+ }
|
|
|
286
|
+
|
|
|
287
|
+ // 表格数据:行=小类,列=各大队
|
|
|
288
|
+ List<Map<String, Object>> tableRows = new ArrayList<>();
|
|
|
289
|
+ for (String subCatName : subCatNames) {
|
|
|
290
|
+ String qcId = subCatNameToQcId.get(subCatName);
|
|
|
291
|
+ Map<String, Object> row = new LinkedHashMap<>();
|
|
|
292
|
+ row.put("subCategory", subCatName);
|
|
|
293
|
+ List<Long> deptCounts = new ArrayList<>();
|
|
|
294
|
+ for (SysDept brigade : brigadeDepts) {
|
|
|
295
|
+ List<DailyTaskDetail> details = detailsByBrigade.get(brigade.getDeptId());
|
|
|
296
|
+ long cnt = details == null ? 0 : details.stream()
|
|
|
297
|
+ .filter(d -> qcId != null && qcId.equals(d.getDtdModuleId()))
|
|
|
298
|
+ .count();
|
|
|
299
|
+ deptCounts.add(cnt);
|
|
|
300
|
+ }
|
|
|
301
|
+ row.put("counts", deptCounts);
|
|
|
302
|
+ tableRows.add(row);
|
|
|
303
|
+ }
|
|
|
304
|
+
|
|
|
305
|
+ List<String> deptNames = brigadeDepts.stream()
|
|
|
306
|
+ .map(SysDept::getDeptName).collect(Collectors.toList());
|
|
|
307
|
+
|
|
|
308
|
+ Map<String, Object> table = new LinkedHashMap<>();
|
|
|
309
|
+ table.put("depts", deptNames);
|
|
|
310
|
+ table.put("rows", tableRows);
|
|
|
311
|
+ result.put("table", table);
|
|
|
312
|
+
|
|
|
313
|
+ // 趋势数据(各大队按日/月错题数)
|
|
|
314
|
+ List<Map<String, Object>> trendSeries = new ArrayList<>();
|
|
|
315
|
+ for (SysDept brigade : brigadeDepts) {
|
|
|
316
|
+ List<DailyTaskDetail> details = detailsByBrigade.get(brigade.getDeptId());
|
|
|
317
|
+ List<Integer> data = buildTrendData(xAxis, details, sdf);
|
|
|
318
|
+ Map<String, Object> s = new LinkedHashMap<>();
|
|
|
319
|
+ s.put("name", brigade.getDeptName());
|
|
|
320
|
+ s.put("deptId", brigade.getDeptId());
|
|
|
321
|
+ s.put("data", data);
|
|
|
322
|
+ trendSeries.add(s);
|
|
|
323
|
+ }
|
|
|
324
|
+ Map<String, Object> trend = new LinkedHashMap<>();
|
|
|
325
|
+ trend.put("xAxis", xAxis);
|
|
|
326
|
+ trend.put("series", trendSeries);
|
|
|
327
|
+ result.put("trend", trend);
|
|
|
328
|
+
|
|
|
329
|
+ } else if (role.isBrigade) {
|
|
|
330
|
+ // ---- 大队长视角:各主管对比 ----
|
|
|
331
|
+ List<SysDept> subDepts = sysDeptMapper.selectChildrenDeptById(role.userDeptId);
|
|
|
332
|
+ List<SysDept> managerDepts = subDepts == null ? Collections.emptyList() :
|
|
|
333
|
+ subDepts.stream()
|
|
|
334
|
+ .filter(d -> role.userDeptId.equals(d.getParentId()) && "0".equals(d.getDelFlag()))
|
|
|
335
|
+ .collect(Collectors.toList());
|
|
|
336
|
+
|
|
|
337
|
+ // 查各主管错误明细(按qcId过滤)
|
|
|
338
|
+ Map<Long, List<DailyTaskDetail>> detailsByManager = new LinkedHashMap<>();
|
|
|
339
|
+ for (SysDept manager : managerDepts) {
|
|
|
340
|
+ List<DailyTaskDetail> details = getErrorDetailsByDepartmentIdAndQcIds(
|
|
|
341
|
+ manager.getDeptId(), new HashSet<>(qcIdToSubCatName.keySet()), range[0], range[1]);
|
|
|
342
|
+ detailsByManager.put(manager.getDeptId(), details);
|
|
|
343
|
+ }
|
|
|
344
|
+
|
|
|
345
|
+ // 表格数据:行=小类,列=各主管
|
|
|
346
|
+ List<Map<String, Object>> tableRows = new ArrayList<>();
|
|
|
347
|
+ for (String subCatName : subCatNames) {
|
|
|
348
|
+ String qcId = subCatNameToQcId.get(subCatName);
|
|
|
349
|
+ Map<String, Object> row = new LinkedHashMap<>();
|
|
|
350
|
+ row.put("subCategory", subCatName);
|
|
|
351
|
+ List<Long> deptCounts = new ArrayList<>();
|
|
|
352
|
+ for (SysDept manager : managerDepts) {
|
|
|
353
|
+ List<DailyTaskDetail> details = detailsByManager.get(manager.getDeptId());
|
|
|
354
|
+ long cnt = details == null ? 0 : details.stream()
|
|
|
355
|
+ .filter(d -> qcId != null && qcId.equals(d.getDtdModuleId()))
|
|
|
356
|
+ .count();
|
|
|
357
|
+ deptCounts.add(cnt);
|
|
|
358
|
+ }
|
|
|
359
|
+ row.put("counts", deptCounts);
|
|
|
360
|
+ tableRows.add(row);
|
|
|
361
|
+ }
|
|
|
362
|
+
|
|
|
363
|
+ List<String> deptNames = managerDepts.stream()
|
|
|
364
|
+ .map(SysDept::getDeptName).collect(Collectors.toList());
|
|
|
365
|
+
|
|
|
366
|
+ Map<String, Object> table = new LinkedHashMap<>();
|
|
|
367
|
+ table.put("depts", deptNames);
|
|
|
368
|
+ table.put("rows", tableRows);
|
|
|
369
|
+ result.put("table", table);
|
|
|
370
|
+
|
|
|
371
|
+ // 趋势数据(各主管按日/月错题数)
|
|
|
372
|
+ List<Map<String, Object>> trendSeries = new ArrayList<>();
|
|
|
373
|
+ for (SysDept manager : managerDepts) {
|
|
|
374
|
+ List<DailyTaskDetail> details = detailsByManager.get(manager.getDeptId());
|
|
|
375
|
+ List<Integer> data = buildTrendData(xAxis, details, sdf);
|
|
|
376
|
+ Map<String, Object> s = new LinkedHashMap<>();
|
|
|
377
|
+ s.put("name", manager.getDeptName());
|
|
|
378
|
+ s.put("deptId", manager.getDeptId());
|
|
|
379
|
+ s.put("data", data);
|
|
|
380
|
+ trendSeries.add(s);
|
|
|
381
|
+ }
|
|
|
382
|
+ Map<String, Object> trend = new LinkedHashMap<>();
|
|
|
383
|
+ trend.put("xAxis", xAxis);
|
|
|
384
|
+ trend.put("series", trendSeries);
|
|
|
385
|
+ result.put("trend", trend);
|
|
|
386
|
+
|
|
|
387
|
+ } else {
|
|
|
388
|
+ // ---- 主管视角:甜甜圈 + 趋势 ----
|
|
|
389
|
+ List<DailyTaskDetail> details = getErrorDetailsByDepartmentIdAndQcIds(
|
|
|
390
|
+ role.userDeptId, new HashSet<>(qcIdToSubCatName.keySet()), range[0], range[1]);
|
|
|
391
|
+
|
|
|
392
|
+ Map<String, Long> qcIdCountMap = details.stream()
|
|
|
393
|
+ .collect(Collectors.groupingBy(
|
|
|
394
|
+ d -> d.getDtdModuleId() != null ? d.getDtdModuleId() : "",
|
|
|
395
|
+ Collectors.counting()));
|
|
|
396
|
+
|
|
|
397
|
+ List<Map<String, Object>> donut = new ArrayList<>();
|
|
|
398
|
+ for (String subCatName : subCatNames) {
|
|
|
399
|
+ String qcId = subCatNameToQcId.get(subCatName);
|
|
|
400
|
+ Map<String, Object> item = new LinkedHashMap<>();
|
|
|
401
|
+ item.put("name", subCatName);
|
|
|
402
|
+ item.put("count", qcId != null ? qcIdCountMap.getOrDefault(qcId, 0L) : 0L);
|
|
|
403
|
+ donut.add(item);
|
|
|
404
|
+ }
|
|
|
405
|
+ result.put("donut", donut);
|
|
|
406
|
+
|
|
|
407
|
+ String deptName = getDeptName(role.userDeptId);
|
|
|
408
|
+ List<Integer> trendData = buildTrendData(xAxis, details, sdf);
|
|
|
409
|
+ Map<String, Object> seriesItem = new LinkedHashMap<>();
|
|
|
410
|
+ seriesItem.put("name", deptName);
|
|
|
411
|
+ seriesItem.put("deptId", role.userDeptId);
|
|
|
412
|
+ seriesItem.put("data", trendData);
|
|
|
413
|
+
|
|
|
414
|
+ Map<String, Object> trend = new LinkedHashMap<>();
|
|
|
415
|
+ trend.put("xAxis", xAxis);
|
|
|
416
|
+ trend.put("series", Collections.singletonList(seriesItem));
|
|
|
417
|
+ result.put("trend", trend);
|
|
|
418
|
+ }
|
|
|
419
|
+
|
|
|
420
|
+ return HttpResult.success(result);
|
|
|
421
|
+
|
|
|
422
|
+ } catch (Exception e) {
|
|
|
423
|
+ log.error("获取大类详情失败 categoryId={}", categoryId, e);
|
|
|
424
|
+ return HttpResult.error("获取大类详情失败:" + e.getMessage());
|
|
|
425
|
+ }
|
|
|
426
|
+ }
|
|
|
427
|
+
|
|
|
428
|
+ // =====================================================================
|
|
|
429
|
+ // 私有工具方法
|
|
|
430
|
+ // =====================================================================
|
|
|
431
|
+
|
|
|
432
|
+ /**
|
|
|
433
|
+ * 获取当前用户角色信息
|
|
|
434
|
+ * 美兰角色优先级:站长 > 大队长(经理/行政) > 主管 > 班组长 > 安检员
|
|
|
435
|
+ */
|
|
|
436
|
+ private RoleInfo getRoleInfo() {
|
|
|
437
|
+ LoginUser loginUser = SecurityUtils.getLoginUser();
|
|
|
438
|
+ List<String> roleKeys = loginUser.getUser().getRoles().stream()
|
|
|
439
|
+ .map(SysRole::getRoleKey).collect(Collectors.toList());
|
|
|
440
|
+ Long userDeptId = loginUser.getDeptId();
|
|
|
441
|
+ Long userId = loginUser.getUserId();
|
|
|
442
|
+
|
|
|
443
|
+ boolean isStation = userId == 1L
|
|
|
444
|
+ || roleKeys.contains(RoleTypeEnum.admin.getCode())
|
|
|
445
|
+ || roleKeys.contains(RoleTypeEnum.test.getCode())
|
|
|
446
|
+ || roleKeys.contains(RoleTypeEnum.zhijianke.getCode());
|
|
|
447
|
+ boolean isBrigade = !isStation && (roleKeys.contains(RoleTypeEnum.jingli.getCode())
|
|
|
448
|
+ || roleKeys.contains(RoleTypeEnum.xingzheng.getCode()));
|
|
|
449
|
+ boolean isKezhang = !isStation && !isBrigade && roleKeys.contains(RoleTypeEnum.kezhang.getCode());
|
|
|
450
|
+
|
|
|
451
|
+ RoleInfo info = new RoleInfo();
|
|
|
452
|
+ info.isStation = isStation;
|
|
|
453
|
+ info.isBrigade = isBrigade;
|
|
|
454
|
+ info.isKezhang = isKezhang;
|
|
|
455
|
+ info.userDeptId = userDeptId;
|
|
|
456
|
+ return info;
|
|
|
457
|
+ }
|
|
|
458
|
+
|
|
|
459
|
+ private static class RoleInfo {
|
|
|
460
|
+ boolean isStation;
|
|
|
461
|
+ boolean isBrigade;
|
|
|
462
|
+ boolean isKezhang;
|
|
|
463
|
+ Long userDeptId;
|
|
|
464
|
+ }
|
|
|
465
|
+
|
|
|
466
|
+ /**
|
|
|
467
|
+ * 根据角色获取错误答题明细
|
|
|
468
|
+ */
|
|
|
469
|
+ private List<DailyTaskDetail> getErrorDetails(RoleInfo role, LocalDate start, LocalDate end) {
|
|
|
470
|
+ if (role.isStation) {
|
|
|
471
|
+ // 站长:站下所有子部门的任务(dtDeptId匹配)
|
|
|
472
|
+ List<SysDept> subDepts = sysDeptMapper.selectChildrenDeptById(role.userDeptId);
|
|
|
473
|
+ Set<Long> allDeptIds = new HashSet<>();
|
|
|
474
|
+ allDeptIds.add(role.userDeptId);
|
|
|
475
|
+ if (subDepts != null) {
|
|
|
476
|
+ subDepts.forEach(d -> allDeptIds.add(d.getDeptId()));
|
|
|
477
|
+ }
|
|
|
478
|
+ return getErrorDetailsByDeptIds(allDeptIds, start, end);
|
|
|
479
|
+ } else if (role.isBrigade) {
|
|
|
480
|
+ // 大队长:本大队下所有子部门的任务(dtDeptId匹配)
|
|
|
481
|
+ List<SysDept> subDepts = sysDeptMapper.selectChildrenDeptById(role.userDeptId);
|
|
|
482
|
+ Set<Long> allDeptIds = new HashSet<>();
|
|
|
483
|
+ allDeptIds.add(role.userDeptId);
|
|
|
484
|
+ if (subDepts != null) {
|
|
|
485
|
+ subDepts.forEach(d -> allDeptIds.add(d.getDeptId()));
|
|
|
486
|
+ }
|
|
|
487
|
+ return getErrorDetailsByDeptIds(allDeptIds, start, end);
|
|
|
488
|
+ } else if (role.isKezhang) {
|
|
|
489
|
+ // 主管:本主管室(dtDepartmentId匹配)
|
|
|
490
|
+ return getErrorDetailsByDepartmentId(role.userDeptId, start, end);
|
|
|
491
|
+ } else {
|
|
|
492
|
+ // 班组长:本班组(dtDeptId匹配)
|
|
|
493
|
+ return getErrorDetailsByDeptId(role.userDeptId, start, end);
|
|
|
494
|
+ }
|
|
|
495
|
+ }
|
|
|
496
|
+
|
|
|
497
|
+ /**
|
|
|
498
|
+ * 按 dt_dept_id 集合查询错误明细
|
|
|
499
|
+ */
|
|
|
500
|
+ private List<DailyTaskDetail> getErrorDetailsByDeptIds(Set<Long> deptIds, LocalDate start, LocalDate end) {
|
|
|
501
|
+ if (deptIds == null || deptIds.isEmpty()) return Collections.emptyList();
|
|
|
502
|
+ LambdaQueryWrapper<DailyTask> taskWrapper = new LambdaQueryWrapper<>();
|
|
|
503
|
+ taskWrapper.in(DailyTask::getDtDeptId, deptIds);
|
|
|
504
|
+ taskWrapper.ge(DailyTask::getDtBusinessDate, Date.valueOf(start));
|
|
|
505
|
+ taskWrapper.le(DailyTask::getDtBusinessDate, Date.valueOf(end));
|
|
|
506
|
+ taskWrapper.eq(DailyTask::getDelStatus, 0);
|
|
|
507
|
+ List<DailyTask> tasks = dailyTaskMapper.selectList(taskWrapper);
|
|
|
508
|
+ return getErrorDetailsFromTasks(tasks);
|
|
|
509
|
+ }
|
|
|
510
|
+
|
|
|
511
|
+ /**
|
|
|
512
|
+ * 按 dt_department_id(主管级别)查询错误明细
|
|
|
513
|
+ */
|
|
|
514
|
+ private List<DailyTaskDetail> getErrorDetailsByDepartmentId(Long departmentId, LocalDate start, LocalDate end) {
|
|
|
515
|
+ LambdaQueryWrapper<DailyTask> taskWrapper = new LambdaQueryWrapper<>();
|
|
|
516
|
+ taskWrapper.eq(DailyTask::getDtDepartmentId, departmentId);
|
|
|
517
|
+ taskWrapper.ge(DailyTask::getDtBusinessDate, Date.valueOf(start));
|
|
|
518
|
+ taskWrapper.le(DailyTask::getDtBusinessDate, Date.valueOf(end));
|
|
|
519
|
+ taskWrapper.eq(DailyTask::getDelStatus, 0);
|
|
|
520
|
+ List<DailyTask> tasks = dailyTaskMapper.selectList(taskWrapper);
|
|
|
521
|
+ return getErrorDetailsFromTasks(tasks);
|
|
|
522
|
+ }
|
|
|
523
|
+
|
|
|
524
|
+ /**
|
|
|
525
|
+ * 按 dt_department_id + 指定 qcId 集合查询错误明细
|
|
|
526
|
+ */
|
|
|
527
|
+ private List<DailyTaskDetail> getErrorDetailsByDepartmentIdAndQcIds(
|
|
|
528
|
+ Long departmentId, Set<String> qcIds, LocalDate start, LocalDate end) {
|
|
|
529
|
+ List<DailyTaskDetail> all = getErrorDetailsByDepartmentId(departmentId, start, end);
|
|
|
530
|
+ if (qcIds == null || qcIds.isEmpty()) return all;
|
|
|
531
|
+ return all.stream()
|
|
|
532
|
+ .filter(d -> qcIds.contains(d.getDtdModuleId()))
|
|
|
533
|
+ .collect(Collectors.toList());
|
|
|
534
|
+ }
|
|
|
535
|
+
|
|
|
536
|
+ /**
|
|
|
537
|
+ * 按 dt_dept_id(班组级别)查询错误明细
|
|
|
538
|
+ */
|
|
|
539
|
+ private List<DailyTaskDetail> getErrorDetailsByDeptId(Long deptId, LocalDate start, LocalDate end) {
|
|
|
540
|
+ LambdaQueryWrapper<DailyTask> taskWrapper = new LambdaQueryWrapper<>();
|
|
|
541
|
+ taskWrapper.eq(DailyTask::getDtDeptId, deptId);
|
|
|
542
|
+ taskWrapper.ge(DailyTask::getDtBusinessDate, Date.valueOf(start));
|
|
|
543
|
+ taskWrapper.le(DailyTask::getDtBusinessDate, Date.valueOf(end));
|
|
|
544
|
+ taskWrapper.eq(DailyTask::getDelStatus, 0);
|
|
|
545
|
+ List<DailyTask> tasks = dailyTaskMapper.selectList(taskWrapper);
|
|
|
546
|
+ return getErrorDetailsFromTasks(tasks);
|
|
|
547
|
+ }
|
|
|
548
|
+
|
|
|
549
|
+ /**
|
|
|
550
|
+ * 按上级部门ID(大队)查询其下所有主管的错误明细
|
|
|
551
|
+ * 通过查询大队的直接下级主管,再按 dtDepartmentId 匹配
|
|
|
552
|
+ */
|
|
|
553
|
+ private List<DailyTaskDetail> getErrorDetailsBySubDepts(Long parentDeptId, LocalDate start, LocalDate end) {
|
|
|
554
|
+ List<SysDept> subDepts = sysDeptMapper.selectChildrenDeptById(parentDeptId);
|
|
|
555
|
+ if (subDepts == null || subDepts.isEmpty()) return Collections.emptyList();
|
|
|
556
|
+ Set<Long> managerIds = subDepts.stream()
|
|
|
557
|
+ .filter(d -> parentDeptId.equals(d.getParentId()) && "0".equals(d.getDelFlag()))
|
|
|
558
|
+ .map(SysDept::getDeptId)
|
|
|
559
|
+ .collect(Collectors.toSet());
|
|
|
560
|
+ if (managerIds.isEmpty()) return Collections.emptyList();
|
|
|
561
|
+
|
|
|
562
|
+ LambdaQueryWrapper<DailyTask> taskWrapper = new LambdaQueryWrapper<>();
|
|
|
563
|
+ taskWrapper.in(DailyTask::getDtDepartmentId, managerIds);
|
|
|
564
|
+ taskWrapper.ge(DailyTask::getDtBusinessDate, Date.valueOf(start));
|
|
|
565
|
+ taskWrapper.le(DailyTask::getDtBusinessDate, Date.valueOf(end));
|
|
|
566
|
+ taskWrapper.eq(DailyTask::getDelStatus, 0);
|
|
|
567
|
+ List<DailyTask> tasks = dailyTaskMapper.selectList(taskWrapper);
|
|
|
568
|
+ return getErrorDetailsFromTasks(tasks);
|
|
|
569
|
+ }
|
|
|
570
|
+
|
|
|
571
|
+ /**
|
|
|
572
|
+ * 按上级部门ID(大队)查询其下所有主管的错误明细,并按 qcId 过滤
|
|
|
573
|
+ */
|
|
|
574
|
+ private List<DailyTaskDetail> getErrorDetailsBySubDeptsAndQcIds(
|
|
|
575
|
+ Long parentDeptId, Set<String> qcIds, LocalDate start, LocalDate end) {
|
|
|
576
|
+ List<DailyTaskDetail> all = getErrorDetailsBySubDepts(parentDeptId, start, end);
|
|
|
577
|
+ if (qcIds == null || qcIds.isEmpty()) return all;
|
|
|
578
|
+ return all.stream()
|
|
|
579
|
+ .filter(d -> qcIds.contains(d.getDtdModuleId()))
|
|
|
580
|
+ .collect(Collectors.toList());
|
|
|
581
|
+ }
|
|
|
582
|
+
|
|
|
583
|
+ /**
|
|
|
584
|
+ * 从任务列表中查询错误答题明细(dtdIsCorrect=false)
|
|
|
585
|
+ */
|
|
|
586
|
+ private List<DailyTaskDetail> getErrorDetailsFromTasks(List<DailyTask> tasks) {
|
|
|
587
|
+ if (tasks == null || tasks.isEmpty()) return Collections.emptyList();
|
|
|
588
|
+ Set<String> taskIds = tasks.stream().map(DailyTask::getDtId).collect(Collectors.toSet());
|
|
|
589
|
+
|
|
|
590
|
+ LambdaQueryWrapper<DailyTaskDetail> detailWrapper = new LambdaQueryWrapper<>();
|
|
|
591
|
+ detailWrapper.in(DailyTaskDetail::getDtdTaskId, taskIds);
|
|
|
592
|
+ detailWrapper.eq(DailyTaskDetail::getDtdIsCorrect, false);
|
|
|
593
|
+ detailWrapper.eq(DailyTaskDetail::getDelStatus, 0);
|
|
|
594
|
+ return dailyTaskDetailMapper.selectList(detailWrapper);
|
|
|
595
|
+ }
|
|
|
596
|
+
|
|
|
597
|
+ /**
|
|
|
598
|
+ * 构建 qcId -> level1 BaseCheckCategory 的映射(带缓存,避免重复查询)
|
|
|
599
|
+ */
|
|
|
600
|
+ private Map<String, BaseCheckCategory> buildQcIdToLevel1Map(List<DailyTaskDetail> details) {
|
|
|
601
|
+ Map<String, BaseCheckCategory> result = new HashMap<>();
|
|
|
602
|
+ Map<String, BaseCheckCategory> qcNameToLevel1Cache = new HashMap<>();
|
|
|
603
|
+
|
|
|
604
|
+ for (DailyTaskDetail detail : details) {
|
|
|
605
|
+ String qcId = detail.getDtdModuleId();
|
|
|
606
|
+ if (qcId == null || qcId.trim().isEmpty() || result.containsKey(qcId)) continue;
|
|
|
607
|
+
|
|
|
608
|
+ LambdaQueryWrapper<QuesCat> catWrapper = new LambdaQueryWrapper<>();
|
|
|
609
|
+ catWrapper.eq(QuesCat::getQcId, qcId);
|
|
|
610
|
+ catWrapper.eq(QuesCat::getDelStatus, 0);
|
|
|
611
|
+ QuesCat quesCat = quesCatService.getOne(catWrapper);
|
|
|
612
|
+
|
|
|
613
|
+ if (quesCat == null) {
|
|
|
614
|
+ result.put(qcId, null);
|
|
|
615
|
+ continue;
|
|
|
616
|
+ }
|
|
|
617
|
+
|
|
|
618
|
+ String qcName = quesCat.getQcName();
|
|
|
619
|
+ if (qcNameToLevel1Cache.containsKey(qcName)) {
|
|
|
620
|
+ result.put(qcId, qcNameToLevel1Cache.get(qcName));
|
|
|
621
|
+ } else {
|
|
|
622
|
+ BaseCheckCategory level1 = baseCheckCategoryService.selectLevel1ByLevel2Name(qcName);
|
|
|
623
|
+ qcNameToLevel1Cache.put(qcName, level1);
|
|
|
624
|
+ result.put(qcId, level1);
|
|
|
625
|
+ }
|
|
|
626
|
+ }
|
|
|
627
|
+ return result;
|
|
|
628
|
+ }
|
|
|
629
|
+
|
|
|
630
|
+ /**
|
|
|
631
|
+ * 构建小类名称 -> qcId 的映射
|
|
|
632
|
+ */
|
|
|
633
|
+ private Map<String, String> buildSubCatNameToQcIdMap(List<String> subCatNames) {
|
|
|
634
|
+ Map<String, String> result = new HashMap<>();
|
|
|
635
|
+ for (String name : subCatNames) {
|
|
|
636
|
+ LambdaQueryWrapper<QuesCat> wrapper = new LambdaQueryWrapper<>();
|
|
|
637
|
+ wrapper.eq(QuesCat::getQcName, name);
|
|
|
638
|
+ wrapper.eq(QuesCat::getDelStatus, 0);
|
|
|
639
|
+ wrapper.last("LIMIT 1");
|
|
|
640
|
+ QuesCat cat = quesCatService.getOne(wrapper);
|
|
|
641
|
+ result.put(name, cat != null ? cat.getQcId() : null);
|
|
|
642
|
+ }
|
|
|
643
|
+ return result;
|
|
|
644
|
+ }
|
|
|
645
|
+
|
|
|
646
|
+ /**
|
|
|
647
|
+ * 构建折线趋势数据,与 xAxis 对齐
|
|
|
648
|
+ */
|
|
|
649
|
+ private List<Integer> buildTrendData(List<String> xAxis, List<DailyTaskDetail> details, SimpleDateFormat sdf) {
|
|
|
650
|
+ if (details == null || details.isEmpty()) {
|
|
|
651
|
+ return xAxis.stream().map(x -> 0).collect(Collectors.toList());
|
|
|
652
|
+ }
|
|
|
653
|
+ Set<String> taskIds = details.stream().map(DailyTaskDetail::getDtdTaskId).collect(Collectors.toSet());
|
|
|
654
|
+ LambdaQueryWrapper<DailyTask> taskWrapper = new LambdaQueryWrapper<>();
|
|
|
655
|
+ taskWrapper.in(DailyTask::getDtId, taskIds);
|
|
|
656
|
+ taskWrapper.eq(DailyTask::getDelStatus, 0);
|
|
|
657
|
+ List<DailyTask> tasks = dailyTaskMapper.selectList(taskWrapper);
|
|
|
658
|
+ Map<String, String> taskIdToDate = tasks.stream()
|
|
|
659
|
+ .filter(t -> t.getDtBusinessDate() != null)
|
|
|
660
|
+ .collect(Collectors.toMap(DailyTask::getDtId, t -> sdf.format(t.getDtBusinessDate()), (a, b) -> a));
|
|
|
661
|
+
|
|
|
662
|
+ Map<String, Long> countByDate = details.stream()
|
|
|
663
|
+ .filter(d -> taskIdToDate.containsKey(d.getDtdTaskId()))
|
|
|
664
|
+ .collect(Collectors.groupingBy(d -> taskIdToDate.get(d.getDtdTaskId()), Collectors.counting()));
|
|
|
665
|
+
|
|
|
666
|
+ return xAxis.stream()
|
|
|
667
|
+ .map(label -> countByDate.getOrDefault(label, 0L).intValue())
|
|
|
668
|
+ .collect(Collectors.toList());
|
|
|
669
|
+ }
|
|
|
670
|
+
|
|
|
671
|
+ /**
|
|
|
672
|
+ * 根据时间类型计算起止日期
|
|
|
673
|
+ */
|
|
|
674
|
+ private LocalDate[] resolveDateRange(String timeType, String startDate, String endDate) {
|
|
|
675
|
+ LocalDate today = LocalDate.now();
|
|
|
676
|
+ LocalDate start;
|
|
|
677
|
+ LocalDate end = today;
|
|
|
678
|
+ switch (timeType == null ? "month" : timeType) {
|
|
|
679
|
+ case "year":
|
|
|
680
|
+ start = today.withDayOfYear(1);
|
|
|
681
|
+ end = today.withMonth(12).withDayOfMonth(31);
|
|
|
682
|
+ break;
|
|
|
683
|
+ case "custom":
|
|
|
684
|
+ start = (startDate != null) ? LocalDate.parse(startDate) : today.withDayOfMonth(1);
|
|
|
685
|
+ end = (endDate != null) ? LocalDate.parse(endDate) : today;
|
|
|
686
|
+ break;
|
|
|
687
|
+ default: // month
|
|
|
688
|
+ start = today.withDayOfMonth(1);
|
|
|
689
|
+ break;
|
|
|
690
|
+ }
|
|
|
691
|
+ return new LocalDate[]{start, end};
|
|
|
692
|
+ }
|
|
|
693
|
+
|
|
|
694
|
+ /**
|
|
|
695
|
+ * 生成 X 轴标签列表
|
|
|
696
|
+ */
|
|
|
697
|
+ private List<String> buildXAxis(LocalDate start, LocalDate end, boolean byMonth) {
|
|
|
698
|
+ List<String> xAxis = new ArrayList<>();
|
|
|
699
|
+ if (byMonth) {
|
|
|
700
|
+ LocalDate cursor = start.withDayOfMonth(1);
|
|
|
701
|
+ LocalDate endMonth = end.withDayOfMonth(1);
|
|
|
702
|
+ while (!cursor.isAfter(endMonth)) {
|
|
|
703
|
+ xAxis.add(cursor.format(DateTimeFormatter.ofPattern("yyyy-MM")));
|
|
|
704
|
+ cursor = cursor.plusMonths(1);
|
|
|
705
|
+ }
|
|
|
706
|
+ } else {
|
|
|
707
|
+ LocalDate cursor = start;
|
|
|
708
|
+ while (!cursor.isAfter(end)) {
|
|
|
709
|
+ xAxis.add(cursor.toString());
|
|
|
710
|
+ cursor = cursor.plusDays(1);
|
|
|
711
|
+ }
|
|
|
712
|
+ }
|
|
|
713
|
+ return xAxis;
|
|
|
714
|
+ }
|
|
|
715
|
+
|
|
|
716
|
+ /**
|
|
|
717
|
+ * 获取部门名称
|
|
|
718
|
+ */
|
|
|
719
|
+ private String getDeptName(Long deptId) {
|
|
|
720
|
+ try {
|
|
|
721
|
+ LoginUser loginUser = SecurityUtils.getLoginUser();
|
|
|
722
|
+ if (loginUser.getUser().getDept() != null) {
|
|
|
723
|
+ return loginUser.getUser().getDept().getDeptName();
|
|
|
724
|
+ }
|
|
|
725
|
+ } catch (Exception ignored) {
|
|
|
726
|
+ }
|
|
|
727
|
+ return "本主管";
|
|
|
728
|
+ }
|
|
|
729
|
+}
|