Selaa lähdekoodia

第一次提交

wangxx 5 kuukautta sitten
vanhempi
commit
6edb659c81
89 muutettua tiedostoa jossa 13586 lisäystä ja 0 poistoa
  1. 114 0
      airport-admin/pom.xml
  2. 30 0
      airport-admin/src/main/java/com/sundot/airport/Application.java
  3. 18 0
      airport-admin/src/main/java/com/sundot/airport/ServletInitializer.java
  4. 323 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/approval/ApprovalController.java
  5. 363 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/approval/CheckApprovalController.java
  6. 147 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/approval/dto/ApprovalDTO.java
  7. 72 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/approval/dto/PersonalCheckDTO.java
  8. 24 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/approval/dto/RejectDTO.java
  9. 70 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/approval/dto/SectionCheckDTO.java
  10. 82 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/approval/dto/SeizureReportDTO.java
  11. 50 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/attendance/AttendanceAreaController.java
  12. 91 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/attendance/AttendanceCheckRecordController.java
  13. 1106 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/attendance/AttendancePostRecordController.java
  14. 275 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/attendance/AttendanceRecordController.java
  15. 142 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/attendance/AttendanceTeamUserRecordController.java
  16. 63 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/attendance/api/AttendanceController.java
  17. 31 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/cache/StatisticsCacheController.java
  18. 178 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/check/CheckCorrectionController.java
  19. 196 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/check/CheckLargeScreenController.java
  20. 98 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/check/CheckProjectItemController.java
  21. 180 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/check/CheckRecordController.java
  22. 198 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/check/CheckTaskController.java
  23. 98 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/check/CheckUserController.java
  24. 94 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/common/CaptchaController.java
  25. 162 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/common/CommonController.java
  26. 52 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/common/DataConfigController.java
  27. 143 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/daily/DailyTaskConfigController.java
  28. 210 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/daily/DeptProfileController.java
  29. 208 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/daily/SiteProfileController.java
  30. 20 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/exam/BaseController.java
  31. 281 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/exam/DailyExamController.java
  32. 109 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/exam/EighteenController.java
  33. 139 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/exam/ExamController.java
  34. 104 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/exam/QuesCatController.java
  35. 143 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/exam/QuesController.java
  36. 109 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/exam/TestPaperCatController.java
  37. 135 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/exam/TestPaperController.java
  38. 138 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/exam/WeakModuleRuleController.java
  39. 150 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/item/ItemLargeScreenController.java
  40. 274 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/item/ItemSeizureItemsController.java
  41. 234 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/item/ItemSeizureRecordController.java
  42. 121 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/monitor/CacheController.java
  43. 27 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/monitor/ServerController.java
  44. 82 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/monitor/SysLogininforController.java
  45. 69 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/monitor/SysOperlogController.java
  46. 83 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/monitor/SysUserOnlineController.java
  47. 201 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/portrait/AttendanceStatsController.java
  48. 290 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/portrait/ItemUserRankingController.java
  49. 147 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/portrait/UserBasicPortraitController.java
  50. 100 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/system/BaseAttachmentController.java
  51. 108 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/system/BaseCheckCategoryController.java
  52. 108 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/system/BaseCheckPointController.java
  53. 117 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/system/BaseDefaultChoiseController.java
  54. 107 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/system/BasePositionController.java
  55. 194 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/system/BaseProjectController.java
  56. 100 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/system/BaseReadController.java
  57. 109 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/system/BaseSeizeCategoryController.java
  58. 109 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/system/BaseSeizeItemController.java
  59. 121 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/system/SysAppController.java
  60. 133 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/system/SysConfigController.java
  61. 154 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/system/SysDeptController.java
  62. 121 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/system/SysDictDataController.java
  63. 131 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/system/SysDictTypeController.java
  64. 101 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/system/SysDynamicSqlController.java
  65. 29 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/system/SysIndexController.java
  66. 160 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/system/SysLearningGrowthController.java
  67. 130 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/system/SysLoginController.java
  68. 125 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/system/SysMenuController.java
  69. 88 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/system/SysNoticeController.java
  70. 136 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/system/SysPostController.java
  71. 148 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/system/SysProfileController.java
  72. 38 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/system/SysRegisterController.java
  73. 239 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/system/SysRoleController.java
  74. 895 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/system/SysUserController.java
  75. 102 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/system/SysWorkingDocumentController.java
  76. 183 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/tool/TestController.java
  77. 97 0
      airport-admin/src/main/java/com/sundot/airport/web/core/cache/UserCache.java
  78. 125 0
      airport-admin/src/main/java/com/sundot/airport/web/core/config/SwaggerConfig.java
  79. 166 0
      airport-admin/src/main/java/com/sundot/airport/web/core/utils/DataPermissionUtils.java
  80. 1 0
      airport-admin/src/main/resources/META-INF/spring-devtools.properties
  81. 71 0
      airport-admin/src/main/resources/application-druid.yml
  82. 150 0
      airport-admin/src/main/resources/application.yml
  83. 24 0
      airport-admin/src/main/resources/banner.txt
  84. 38 0
      airport-admin/src/main/resources/i18n/messages.properties
  85. 15 0
      airport-admin/src/main/resources/level-config.yml
  86. 93 0
      airport-admin/src/main/resources/logback.xml
  87. 20 0
      airport-admin/src/main/resources/mybatis/mybatis-config.xml
  88. 475 0
      airport-admin/src/main/resources/static/approval-frontend-example.js
  89. 551 0
      airport-admin/src/main/resources/static/approval-page-example.html

+ 114 - 0
airport-admin/pom.xml

@@ -0,0 +1,114 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<project xmlns="http://maven.apache.org/POM/4.0.0"
3
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5
+    <parent>
6
+        <artifactId>airport</artifactId>
7
+        <groupId>com.sundot.airport</groupId>
8
+        <version>3.9.0</version>
9
+    </parent>
10
+    <modelVersion>4.0.0</modelVersion>
11
+    <packaging>jar</packaging>
12
+    <artifactId>airport-admin</artifactId>
13
+
14
+    <description>
15
+        web服务入口
16
+    </description>
17
+
18
+    <dependencies>
19
+
20
+        <!-- spring-boot-devtools -->
21
+        <dependency>
22
+            <groupId>org.springframework.boot</groupId>
23
+            <artifactId>spring-boot-devtools</artifactId>
24
+            <optional>true</optional> <!-- 表示依赖不会传递 -->
25
+        </dependency>
26
+
27
+        <!-- swagger3-->
28
+        <dependency>
29
+            <groupId>io.springfox</groupId>
30
+            <artifactId>springfox-boot-starter</artifactId>
31
+        </dependency>
32
+
33
+        <!-- 防止进入swagger页面报类型转换错误,排除3.0.0中的引用,手动增加1.6.2版本 -->
34
+        <dependency>
35
+            <groupId>io.swagger</groupId>
36
+            <artifactId>swagger-models</artifactId>
37
+            <version>1.6.2</version>
38
+        </dependency>
39
+
40
+        <!-- Mysql驱动包 -->
41
+        <dependency>
42
+            <groupId>mysql</groupId>
43
+            <artifactId>mysql-connector-java</artifactId>
44
+        </dependency>
45
+
46
+        <!-- 核心模块-->
47
+        <dependency>
48
+            <groupId>com.sundot.airport</groupId>
49
+            <artifactId>airport-framework</artifactId>
50
+        </dependency>
51
+
52
+        <!-- 定时任务-->
53
+        <dependency>
54
+            <groupId>com.sundot.airport</groupId>
55
+            <artifactId>airport-quartz</artifactId>
56
+        </dependency>
57
+
58
+        <!-- 代码生成-->
59
+        <dependency>
60
+            <groupId>com.sundot.airport</groupId>
61
+            <artifactId>airport-generator</artifactId>
62
+        </dependency>
63
+
64
+        <!-- 查获物品管理模块-->
65
+        <dependency>
66
+            <groupId>com.sundot.airport</groupId>
67
+            <artifactId>airport-item</artifactId>
68
+        </dependency>
69
+
70
+        <!-- 巡检管理模块-->
71
+        <dependency>
72
+            <groupId>com.sundot.airport</groupId>
73
+            <artifactId>airport-check</artifactId>
74
+        </dependency>
75
+
76
+        <dependency>
77
+            <groupId>org.projectlombok</groupId>
78
+            <artifactId>lombok</artifactId>
79
+            <scope>provided</scope>
80
+        </dependency>
81
+
82
+    </dependencies>
83
+
84
+    <build>
85
+        <plugins>
86
+            <plugin>
87
+                <groupId>org.springframework.boot</groupId>
88
+                <artifactId>spring-boot-maven-plugin</artifactId>
89
+                <version>2.5.15</version>
90
+                <configuration>
91
+                    <fork>true</fork> <!-- 如果没有该配置,devtools不会生效 -->
92
+                </configuration>
93
+                <executions>
94
+                    <execution>
95
+                        <goals>
96
+                            <goal>repackage</goal>
97
+                        </goals>
98
+                    </execution>
99
+                </executions>
100
+            </plugin>
101
+            <plugin>
102
+                <groupId>org.apache.maven.plugins</groupId>
103
+                <artifactId>maven-war-plugin</artifactId>
104
+                <version>3.1.0</version>
105
+                <configuration>
106
+                    <failOnMissingWebXml>false</failOnMissingWebXml>
107
+                    <warName>${project.artifactId}</warName>
108
+                </configuration>
109
+            </plugin>
110
+        </plugins>
111
+        <finalName>${project.artifactId}</finalName>
112
+    </build>
113
+
114
+</project>

+ 30 - 0
airport-admin/src/main/java/com/sundot/airport/Application.java

@@ -0,0 +1,30 @@
1
+package com.sundot.airport;
2
+
3
+import org.springframework.boot.SpringApplication;
4
+import org.springframework.boot.autoconfigure.SpringBootApplication;
5
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
6
+
7
+/**
8
+ * 启动程序
9
+ * 
10
+ * @author ruoyi
11
+ */
12
+@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
13
+public class Application
14
+{
15
+    public static void main(String[] args)
16
+    {
17
+        // System.setProperty("spring.devtools.restart.enabled", "false");
18
+        SpringApplication.run(Application.class, args);
19
+        System.out.println("(♥◠‿◠)ノ゙  若依启动成功   ლ(´ڡ`ლ)゙  \n" +
20
+                " .-------.       ____     __        \n" +
21
+                " |  _ _   \\      \\   \\   /  /    \n" +
22
+                " | ( ' )  |       \\  _. /  '       \n" +
23
+                " |(_ o _) /        _( )_ .'         \n" +
24
+                " | (_,_).' __  ___(_ o _)'          \n" +
25
+                " |  |\\ \\  |  ||   |(_,_)'         \n" +
26
+                " |  | \\ `'   /|   `-'  /           \n" +
27
+                " |  |  \\    /  \\      /           \n" +
28
+                " ''-'   `'-'    `-..-'              ");
29
+    }
30
+}

+ 18 - 0
airport-admin/src/main/java/com/sundot/airport/ServletInitializer.java

@@ -0,0 +1,18 @@
1
+package com.sundot.airport;
2
+
3
+import org.springframework.boot.builder.SpringApplicationBuilder;
4
+import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
5
+
6
+/**
7
+ * web容器中进行部署
8
+ * 
9
+ * @author ruoyi
10
+ */
11
+public class ServletInitializer extends SpringBootServletInitializer
12
+{
13
+    @Override
14
+    protected SpringApplicationBuilder configure(SpringApplicationBuilder application)
15
+    {
16
+        return application.sources(Application.class);
17
+    }
18
+}

+ 323 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/approval/ApprovalController.java

@@ -0,0 +1,323 @@
1
+package com.sundot.airport.web.controller.approval;
2
+
3
+import java.util.List;
4
+import java.util.Map;
5
+import javax.servlet.http.HttpServletResponse;
6
+
7
+import com.sundot.airport.common.utils.SecurityUtils;
8
+import com.sundot.airport.system.domain.approval.ApprovalInstance;
9
+import com.sundot.airport.system.domain.approval.ApprovalTask;
10
+import com.sundot.airport.system.service.approval.IApprovalEngineService;
11
+import org.springframework.security.access.prepost.PreAuthorize;
12
+import org.springframework.beans.factory.annotation.Autowired;
13
+import org.springframework.web.bind.annotation.GetMapping;
14
+import org.springframework.web.bind.annotation.PostMapping;
15
+import org.springframework.web.bind.annotation.PutMapping;
16
+import org.springframework.web.bind.annotation.DeleteMapping;
17
+import org.springframework.web.bind.annotation.PathVariable;
18
+import org.springframework.web.bind.annotation.RequestBody;
19
+import org.springframework.web.bind.annotation.RequestMapping;
20
+import org.springframework.web.bind.annotation.RestController;
21
+import com.sundot.airport.common.annotation.Log;
22
+import com.sundot.airport.common.core.controller.BaseController;
23
+import com.sundot.airport.common.core.domain.AjaxResult;
24
+import com.sundot.airport.common.enums.BusinessType;
25
+import com.sundot.airport.common.utils.poi.ExcelUtil;
26
+import com.sundot.airport.common.core.page.TableDataInfo;
27
+
28
+/**
29
+ * 审批流程Controller
30
+ * 
31
+ * @author simon lin
32
+ * @date 2025-09-06
33
+ */
34
+@RestController
35
+@RequestMapping("/system/approval")
36
+public class ApprovalController extends BaseController
37
+{
38
+    @Autowired
39
+    private IApprovalEngineService approvalEngineService;
40
+
41
+    /**
42
+     * 启动审批流程
43
+     */
44
+    @PreAuthorize("@ss.hasPermi('system:approval:start')")
45
+    @Log(title = "启动审批流程", businessType = BusinessType.INSERT)
46
+    @PostMapping("/start")
47
+    public AjaxResult startProcess(@RequestBody Map<String, Object> params)
48
+    {
49
+        String workflowCode = (String) params.get("workflowCode");
50
+        String businessType = (String) params.get("businessType");
51
+        Long businessId = Long.valueOf(params.get("businessId").toString());
52
+        String title = (String) params.get("title");
53
+        Long submitterId = SecurityUtils.getUserId();
54
+        String submitterName = SecurityUtils.getUsername();
55
+        Map<String, Object> formData = (Map<String, Object>) params.get("formData");
56
+        Map<String, Object> businessData = (Map<String, Object>) params.get("businessData");
57
+        
58
+        ApprovalInstance instance = approvalEngineService.startProcess(
59
+            workflowCode, businessType, businessId, title, 
60
+            submitterId, submitterName, formData, businessData);
61
+            
62
+        return AjaxResult.success("流程启动成功", instance);
63
+    }
64
+
65
+    /**
66
+     * 启动个人级别审批流程
67
+     */
68
+    @PreAuthorize("@ss.hasPermi('system:approval:start')")
69
+    @Log(title = "启动个人级别审批流程", businessType = BusinessType.INSERT)
70
+    @PostMapping("/start/personal")
71
+    public AjaxResult startPersonalLevelProcess(@RequestBody Map<String, Object> params)
72
+    {
73
+        String businessType = (String) params.get("businessType");
74
+        Long businessId = Long.valueOf(params.get("businessId").toString());
75
+        String title = (String) params.get("title");
76
+        Long submitterId = SecurityUtils.getUserId();
77
+        String submitterName = SecurityUtils.getUsername();
78
+        
79
+        // 新的个人级别检查流程参数
80
+        Long sectionLeaderId = null;
81
+        Object sectionLeaderIdObj = params.get("sectionLeaderId");
82
+        if (sectionLeaderIdObj != null) {
83
+            if (sectionLeaderIdObj instanceof Number) {
84
+                sectionLeaderId = ((Number) sectionLeaderIdObj).longValue();
85
+            } else if (sectionLeaderIdObj instanceof String) {
86
+                sectionLeaderId = Long.parseLong((String) sectionLeaderIdObj);
87
+            }
88
+        }
89
+        
90
+        if (sectionLeaderId == null) {
91
+            return AjaxResult.error("sectionLeaderId参数不能为空");
92
+        }
93
+        
94
+        Map<String, Object> formData = (Map<String, Object>) params.get("formData");
95
+        Map<String, Object> businessData = (Map<String, Object>) params.get("businessData");
96
+        
97
+        ApprovalInstance instance = approvalEngineService.startPersonalLevelProcess(
98
+            businessType, businessId, title, submitterId, submitterName, 
99
+            sectionLeaderId, formData, businessData);
100
+            
101
+        return AjaxResult.success("个人级别流程启动成功", instance);
102
+    }
103
+
104
+    /**
105
+     * 启动科级审批流程
106
+     */
107
+    @PreAuthorize("@ss.hasPermi('system:approval:start')")
108
+    @Log(title = "启动科级审批流程", businessType = BusinessType.INSERT)
109
+    @PostMapping("/start/section")
110
+    public AjaxResult startSectionLevelProcess(@RequestBody Map<String, Object> params)
111
+    {
112
+        String businessType = (String) params.get("businessType");
113
+        Long businessId = Long.valueOf(params.get("businessId").toString());
114
+        String title = (String) params.get("title");
115
+        Long submitterId = SecurityUtils.getUserId();
116
+        String submitterName = SecurityUtils.getUsername();
117
+        
118
+        // 新的科级审批流程参数
119
+        Long sectionLeaderId = null;
120
+        Object sectionLeaderIdObj = params.get("sectionLeaderId");
121
+        if (sectionLeaderIdObj != null) {
122
+            if (sectionLeaderIdObj instanceof Number) {
123
+                sectionLeaderId = ((Number) sectionLeaderIdObj).longValue();
124
+            } else if (sectionLeaderIdObj instanceof String) {
125
+                sectionLeaderId = Long.parseLong((String) sectionLeaderIdObj);
126
+            }
127
+        }
128
+        
129
+        if (sectionLeaderId == null) {
130
+            return AjaxResult.error("sectionLeaderId参数不能为空");
131
+        }
132
+        
133
+        Map<String, Object> formData = (Map<String, Object>) params.get("formData");
134
+        Map<String, Object> businessData = (Map<String, Object>) params.get("businessData");
135
+        
136
+        ApprovalInstance instance = approvalEngineService.startSectionLevelProcess(
137
+            businessType, businessId, title, submitterId, submitterName, sectionLeaderId, formData, businessData);
138
+            
139
+        return AjaxResult.success("科级审批流程启动成功", instance);
140
+    }
141
+
142
+    /**
143
+     * 启动班组级审批流程
144
+     */
145
+    @PreAuthorize("@ss.hasPermi('system:approval:start')")
146
+    @Log(title = "启动班组级审批流程", businessType = BusinessType.INSERT)
147
+    @PostMapping("/start/group")
148
+    public AjaxResult startGroupLevelProcess(@RequestBody Map<String, Object> params)
149
+    {
150
+        String businessType = (String) params.get("businessType");
151
+        Long businessId = Long.valueOf(params.get("businessId").toString());
152
+        String title = (String) params.get("title");
153
+        Long submitterId = SecurityUtils.getUserId();
154
+        String submitterName = SecurityUtils.getUsername();
155
+        Map<String, Object> formData = (Map<String, Object>) params.get("formData");
156
+        Map<String, Object> businessData = (Map<String, Object>) params.get("businessData");
157
+        
158
+        ApprovalInstance instance = approvalEngineService.startGroupLevelProcess(
159
+            businessType, businessId, title, submitterId, submitterName, formData, businessData);
160
+            
161
+        return AjaxResult.success("班组级审批流程启动成功", instance);
162
+    }
163
+
164
+    /**
165
+     * 启动查获上报审批流程
166
+     */
167
+    @PreAuthorize("@ss.hasPermi('system:approval:start')")
168
+    @Log(title = "启动查获上报审批流程", businessType = BusinessType.INSERT)
169
+    @PostMapping("/start/seizure")
170
+    public AjaxResult startSeizureReportProcess(@RequestBody Map<String, Object> params)
171
+    {
172
+        String businessType = (String) params.get("businessType");
173
+        Long businessId = Long.valueOf(params.get("businessId").toString());
174
+        String title = (String) params.get("title");
175
+        Long submitterId = SecurityUtils.getUserId();
176
+        String submitterName = SecurityUtils.getUsername();
177
+        String submitterRole = (String) params.get("submitterRole");
178
+        Map<String, Object> formData = (Map<String, Object>) params.get("formData");
179
+        Map<String, Object> businessData = (Map<String, Object>) params.get("businessData");
180
+        
181
+        ApprovalInstance instance = approvalEngineService.startSeizureReportProcess(
182
+            businessType, businessId, title, submitterId, submitterName, 
183
+            submitterRole, formData, businessData);
184
+            
185
+        return AjaxResult.success("查获上报审批流程启动成功", instance);
186
+    }
187
+
188
+    /**
189
+     * 审批任务(同意)
190
+     */
191
+    @PreAuthorize("@ss.hasPermi('system:approval:approve')")
192
+    @Log(title = "审批任务", businessType = BusinessType.UPDATE)
193
+    @PutMapping("/approve/{taskId}")
194
+    public AjaxResult approveTask(@PathVariable("taskId") Long taskId, @RequestBody Map<String, Object> params)
195
+    {
196
+        Long operatorId = SecurityUtils.getUserId();
197
+        String operatorName = SecurityUtils.getUsername();
198
+        String comment = (String) params.get("comment");
199
+        Map<String, Object> formData = (Map<String, Object>) params.get("formData");
200
+        
201
+        boolean result = approvalEngineService.approveTask(taskId, operatorId, operatorName, comment, formData);
202
+        return result ? AjaxResult.success("审批成功") : AjaxResult.error("审批失败");
203
+    }
204
+
205
+    /**
206
+     * 驳回任务
207
+     */
208
+    @PreAuthorize("@ss.hasPermi('system:approval:reject')")
209
+    @Log(title = "驳回任务", businessType = BusinessType.UPDATE)
210
+    @PutMapping("/reject/{taskId}")
211
+    public AjaxResult rejectTask(@PathVariable("taskId") Long taskId, @RequestBody Map<String, Object> params)
212
+    {
213
+        Long operatorId = SecurityUtils.getUserId();
214
+        String operatorName = SecurityUtils.getUsername();
215
+        String comment = (String) params.get("comment");
216
+        
217
+        boolean result = approvalEngineService.rejectTask(taskId, operatorId, operatorName, comment);
218
+        return result ? AjaxResult.success("驳回成功") : AjaxResult.error("驳回失败");
219
+    }
220
+
221
+    /**
222
+     * 取消审批流程
223
+     */
224
+    @PreAuthorize("@ss.hasPermi('system:approval:cancel')")
225
+    @Log(title = "取消审批流程", businessType = BusinessType.UPDATE)
226
+    @PutMapping("/cancel/{instanceId}")
227
+    public AjaxResult cancelProcess(@PathVariable("instanceId") Long instanceId, @RequestBody Map<String, Object> params)
228
+    {
229
+        Long operatorId = SecurityUtils.getUserId();
230
+        String operatorName = SecurityUtils.getUsername();
231
+        String comment = (String) params.get("comment");
232
+        
233
+        boolean result = approvalEngineService.cancelProcess(instanceId, operatorId, operatorName, comment);
234
+        return result ? AjaxResult.success("取消成功") : AjaxResult.error("取消失败");
235
+    }
236
+
237
+    /**
238
+     * 获取用户待办任务列表
239
+     */
240
+    @PreAuthorize("@ss.hasPermi('system:approval:query')")
241
+    @GetMapping("/tasks/pending")
242
+    public TableDataInfo getPendingTasks()
243
+    {
244
+        Long userId = SecurityUtils.getUserId();
245
+        List<ApprovalTask> list = approvalEngineService.getUserPendingTasks(userId);
246
+        return getDataTable(list);
247
+    }
248
+
249
+    /**
250
+     * 获取用户已办任务列表
251
+     */
252
+    @PreAuthorize("@ss.hasPermi('system:approval:query')")
253
+    @GetMapping("/tasks/completed")
254
+    public TableDataInfo getCompletedTasks()
255
+    {
256
+        Long userId = SecurityUtils.getUserId();
257
+        List<ApprovalTask> list = approvalEngineService.getUserCompletedTasks(userId);
258
+        return getDataTable(list);
259
+    }
260
+
261
+    /**
262
+     * 获取用户发起的审批实例列表
263
+     */
264
+    @PreAuthorize("@ss.hasPermi('system:approval:query')")
265
+    @GetMapping("/instances/submitted")
266
+    public TableDataInfo getSubmittedInstances(String status)
267
+    {
268
+        Long userId = SecurityUtils.getUserId();
269
+        List<ApprovalInstance> list = approvalEngineService.getUserSubmittedInstances(userId, status);
270
+        return getDataTable(list);
271
+    }
272
+
273
+    /**
274
+     * 根据业务类型和级别获取流程代码
275
+     */
276
+    @GetMapping("/workflow/code")
277
+    public AjaxResult getWorkflowCode(String businessType, String level, String submitterRole)
278
+    {
279
+        String workflowCode = approvalEngineService.getWorkflowCodeByBusinessAndLevel(businessType, level, submitterRole);
280
+        return AjaxResult.success(workflowCode);
281
+    }
282
+
283
+    /**
284
+     * 统计用户待办任务数量
285
+     */
286
+    @GetMapping("/tasks/pending/count")
287
+    public AjaxResult getPendingTasksCount()
288
+    {
289
+        Long userId = SecurityUtils.getUserId();
290
+        List<ApprovalTask> tasks = approvalEngineService.getUserPendingTasks(userId);
291
+        return AjaxResult.success(tasks.size());
292
+    }
293
+
294
+    /**
295
+     * 批量审批任务(同意)
296
+     * @param ids 任务ID列表
297
+     */
298
+    @PreAuthorize("@ss.hasPermi('system:approval:approve')")
299
+    @Log(title = "审批任务", businessType = BusinessType.UPDATE)
300
+    @PutMapping("/approve/list")
301
+    public AjaxResult approveTaskList(@RequestBody List<Long> ids) {
302
+        if (ids == null || ids.isEmpty()) {
303
+            return AjaxResult.error("ID列表不能为空");
304
+        }
305
+        boolean result = approvalEngineService.approveTaskList(ids);
306
+        return result ? AjaxResult.success("审批成功") : AjaxResult.error("审批失败");
307
+    }
308
+
309
+    /**
310
+     * 批量驳回任务
311
+     * @param ids 任务ID列表
312
+     */
313
+    @PreAuthorize("@ss.hasPermi('system:approval:reject')")
314
+    @Log(title = "驳回任务", businessType = BusinessType.UPDATE)
315
+    @PutMapping("/reject/list")
316
+    public AjaxResult rejectTaskList(@RequestBody List<Long> ids) {
317
+        if (ids == null || ids.isEmpty()) {
318
+            return AjaxResult.error("ID列表不能为空");
319
+        }
320
+        boolean result = approvalEngineService.rejectTaskList(ids);
321
+        return result ? AjaxResult.success("驳回成功") : AjaxResult.error("驳回失败");
322
+    }
323
+}

+ 363 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/approval/CheckApprovalController.java

@@ -0,0 +1,363 @@
1
+package com.sundot.airport.web.controller.approval;
2
+
3
+import java.util.List;
4
+import java.util.Map;
5
+
6
+import javax.validation.Valid;
7
+
8
+import org.springframework.beans.factory.annotation.Autowired;
9
+import org.springframework.security.access.prepost.PreAuthorize;
10
+import org.springframework.web.bind.annotation.*;
11
+
12
+import com.sundot.airport.common.annotation.Log;
13
+import com.sundot.airport.common.core.controller.BaseController;
14
+import com.sundot.airport.common.core.domain.AjaxResult;
15
+import com.sundot.airport.common.core.page.TableDataInfo;
16
+import com.sundot.airport.common.enums.BusinessType;
17
+import com.sundot.airport.common.utils.SecurityUtils;
18
+import com.sundot.airport.system.domain.approval.ApprovalInstance;
19
+import com.sundot.airport.system.domain.approval.ApprovalTask;
20
+import com.sundot.airport.system.service.approval.IApprovalEngineService;
21
+import com.sundot.airport.system.service.approval.IApprovalUserService;
22
+import com.sundot.airport.system.service.approval.ICheckApprovalService;
23
+import com.sundot.airport.web.controller.approval.dto.*;
24
+import com.sundot.airport.system.domain.approval.dto.ApprovalCcDetailDTO;
25
+
26
+/**
27
+ * 安检审批流程Controller
28
+ * 
29
+ * @author simon lin
30
+ * @date 2025-09-06
31
+ */
32
+@RestController
33
+@RequestMapping("/system/check")
34
+public class CheckApprovalController extends BaseController
35
+{
36
+    @Autowired
37
+    private IApprovalEngineService approvalEngineService;
38
+    
39
+    @Autowired
40
+    private IApprovalUserService approvalUserService;
41
+    
42
+    @Autowired
43
+    private ICheckApprovalService checkApprovalService;
44
+
45
+    /**
46
+     * 1. 启动个人级别审批流程
47
+     */
48
+    @PreAuthorize("@ss.hasPermi('system:approval:start')")
49
+    @Log(title = "个人级别安检检查", businessType = BusinessType.INSERT)
50
+    @PostMapping("/personal")
51
+    public AjaxResult submitPersonalCheck(@RequestBody Map<String, Object> requestData) {
52
+        // 获取备注参数
53
+        String remark = (String) requestData.get("remark");
54
+        
55
+        try {
56
+            ApprovalInstance instance = checkApprovalService.submitPersonalCheck(
57
+                requestData, 
58
+                SecurityUtils.getUserId(), 
59
+                SecurityUtils.getUsername(), 
60
+                remark
61
+            );
62
+            return AjaxResult.success("个人级别检查流程启动成功", instance);
63
+        } catch (Exception e) {
64
+            logger.error("启动个人级别检查流程失败", e);
65
+            return AjaxResult.error("启动流程失败:" + e.getMessage());
66
+        }
67
+    }
68
+
69
+    /**
70
+     * 2. 启动科级审批流程
71
+     */
72
+    @PreAuthorize("@ss.hasPermi('system:approval:start')")
73
+    @Log(title = "科级安检检查", businessType = BusinessType.INSERT)
74
+    @PostMapping("/section")
75
+    public AjaxResult submitSectionCheck(@RequestBody Map<String, Object> requestData) {
76
+        // 获取备注参数
77
+        String remark = (String) requestData.get("remark");
78
+        
79
+        try {
80
+            ApprovalInstance instance = checkApprovalService.submitSectionCheck(
81
+                requestData, 
82
+                SecurityUtils.getUserId(), 
83
+                SecurityUtils.getUsername(), 
84
+                remark
85
+            );
86
+            return AjaxResult.success("科级检查流程启动成功", instance);
87
+        } catch (Exception e) {
88
+            logger.error("启动科级检查流程失败", e);
89
+            return AjaxResult.error("启动流程失败:" + e.getMessage());
90
+        }
91
+    }
92
+
93
+    /**
94
+     * 3. 启动班组级审批流程
95
+     */
96
+    @PreAuthorize("@ss.hasPermi('system:approval:start')")
97
+    @Log(title = "班组级安检检查", businessType = BusinessType.INSERT)
98
+    @PostMapping("/group")
99
+    public AjaxResult submitGroupCheck(@RequestBody Map<String, Object> requestData) {
100
+        // 获取备注参数
101
+        String remark = (String) requestData.get("remark");
102
+        
103
+        try {
104
+            ApprovalInstance instance = checkApprovalService.submitGroupCheck(
105
+                requestData, 
106
+                SecurityUtils.getUserId(), 
107
+                SecurityUtils.getUsername(), 
108
+                remark
109
+            );
110
+            return AjaxResult.success("班组级检查流程启动成功", instance);
111
+        } catch (Exception e) {
112
+            logger.error("启动班组级检查流程失败", e);
113
+            return AjaxResult.error("启动流程失败:" + e.getMessage());
114
+        }
115
+    }
116
+
117
+    /**
118
+     * 4. 启动查获上报流程
119
+     */
120
+    @PreAuthorize("@ss.hasPermi('system:approval:start')")
121
+    @Log(title = "查获上报", businessType = BusinessType.INSERT)
122
+    @PostMapping("/seizure")
123
+    public AjaxResult submitSeizureReport(@RequestBody Map<String, Object> requestData) {
124
+        // 获取备注参数
125
+        String remark = (String) requestData.get("remark");
126
+        
127
+        try {
128
+            ApprovalInstance instance = checkApprovalService.submitSeizureReport(
129
+                requestData, 
130
+                SecurityUtils.getUserId(), 
131
+                SecurityUtils.getUsername(), 
132
+                remark
133
+            );
134
+            return AjaxResult.success("查获上报流程启动成功", instance);
135
+        } catch (Exception e) {
136
+            logger.error("启动查获上报流程失败", e);
137
+            return AjaxResult.error("启动流程失败:" + e.getMessage());
138
+        }
139
+    }
140
+
141
+    /**
142
+     * 5. 审批任务(同意)
143
+     */
144
+    @PreAuthorize("@ss.hasPermi('system:approval:approve')")
145
+    @Log(title = "审批任务", businessType = BusinessType.UPDATE)
146
+    @PutMapping("/approval/approve/{taskId}")
147
+    public AjaxResult approveTask(@PathVariable Long taskId, @Valid @RequestBody ApprovalDTO approvalData) {
148
+        try {
149
+            ApprovalTask task = checkApprovalService.approveTask(
150
+                taskId, 
151
+                approvalData.getComment(), 
152
+                approvalData.getFormData(), 
153
+                SecurityUtils.getUserId(), 
154
+                SecurityUtils.getUsername()
155
+            );
156
+            return AjaxResult.success("任务审批成功", task);
157
+        } catch (Exception e) {
158
+            logger.error("审批任务失败", e);
159
+            return AjaxResult.error("审批失败:" + e.getMessage());
160
+        }
161
+    }
162
+
163
+    /**
164
+     * 6. 审批任务(驳回)
165
+     */
166
+    @PreAuthorize("@ss.hasPermi('system:approval:reject')")
167
+    @Log(title = "驳回任务", businessType = BusinessType.UPDATE)
168
+    @PutMapping("/approval/reject/{taskId}")
169
+    public AjaxResult rejectTask(@PathVariable Long taskId, @Valid @RequestBody RejectDTO rejectData) {
170
+        try {
171
+            ApprovalTask task = checkApprovalService.rejectTask(
172
+                taskId, 
173
+                rejectData.getRejectReason(), 
174
+                SecurityUtils.getUserId(), 
175
+                SecurityUtils.getUsername()
176
+            );
177
+            return AjaxResult.success("任务驳回成功", task);
178
+        } catch (Exception e) {
179
+            logger.error("驳回任务失败", e);
180
+            return AjaxResult.error("驳回失败:" + e.getMessage());
181
+        }
182
+    }
183
+
184
+    /**
185
+     * 7. 获取待办任务列表
186
+     */
187
+    @PreAuthorize("@ss.hasPermi('system:approval:query')")
188
+    @GetMapping("/approval/tasks/pending")
189
+    public TableDataInfo getPendingTasks() {
190
+        try {
191
+            List<ApprovalTask> list = checkApprovalService.getPendingTasks(SecurityUtils.getUserId());
192
+            return getDataTable(list);
193
+        } catch (Exception e) {
194
+            logger.error("获取待办任务列表失败", e);
195
+            return getDataTable(null);
196
+        }
197
+    }
198
+
199
+    /**
200
+     * 8. 获取已办任务列表
201
+     */
202
+    @PreAuthorize("@ss.hasPermi('system:approval:query')")
203
+    @GetMapping("/approval/tasks/completed")
204
+    public TableDataInfo getCompletedTasks() {
205
+        try {
206
+            List<ApprovalTask> list = checkApprovalService.getCompletedTasks(SecurityUtils.getUserId());
207
+            return getDataTable(list);
208
+        } catch (Exception e) {
209
+            logger.error("获取已办任务列表失败", e);
210
+            return getDataTable(null);
211
+        }
212
+    }
213
+
214
+    /**
215
+     * 9. 获取我发起的审批实例
216
+     */
217
+    @PreAuthorize("@ss.hasPermi('system:approval:query')")
218
+    @GetMapping("/approval/instances/submitted")
219
+    public TableDataInfo getSubmittedInstances(@RequestParam(required = false) String status) {
220
+        try {
221
+            List<ApprovalInstance> list = checkApprovalService.getSubmittedInstances(SecurityUtils.getUserId(), status);
222
+            return getDataTable(list);
223
+        } catch (Exception e) {
224
+            logger.error("获取提交实例列表失败", e);
225
+            return getDataTable(null);
226
+        }
227
+    }
228
+
229
+    /**
230
+     * 10. 取消流程
231
+     */
232
+    @PreAuthorize("@ss.hasPermi('system:approval:cancel')")
233
+    @Log(title = "取消审批流程", businessType = BusinessType.UPDATE)
234
+    @PutMapping("/approval/cancel/{instanceId}")
235
+    public AjaxResult cancelProcess(@PathVariable Long instanceId, @RequestParam String comment) {
236
+        try {
237
+            boolean success = approvalEngineService.cancelProcess(instanceId, SecurityUtils.getUserId(), SecurityUtils.getUsername(), comment);
238
+            if (!success) {
239
+                return AjaxResult.error("取消失败");
240
+            }
241
+            ApprovalInstance instance = null; // 返回空对象或查询实例
242
+            return AjaxResult.success("流程取消成功", instance);
243
+        } catch (Exception e) {
244
+            logger.error("取消流程失败", e);
245
+            return AjaxResult.error("取消失败:" + e.getMessage());
246
+        }
247
+    }
248
+
249
+    /**
250
+     * 11. 根据业务类型获取工作流代码
251
+     */
252
+    @PreAuthorize("@ss.hasPermi('system:approval:query')")
253
+    @GetMapping("/approval/workflow-code")
254
+    public AjaxResult getWorkflowCode(@RequestParam String businessType, 
255
+                                     @RequestParam(required = false) String submitterRole) {
256
+        try {
257
+            // 暂时返回空值,需要实现该方法
258
+            String workflowCode = null;
259
+            return AjaxResult.success("获取工作流代码成功", workflowCode);
260
+        } catch (Exception e) {
261
+            logger.error("获取工作流代码失败", e);
262
+            return AjaxResult.error("获取失败:" + e.getMessage());
263
+        }
264
+    }
265
+
266
+    /**
267
+     * 12. 获取待办任务数量
268
+     */
269
+    @PreAuthorize("@ss.hasPermi('system:approval:query')")
270
+    @GetMapping("/approval/tasks/pending/count")
271
+    public AjaxResult getPendingTasksCount() {
272
+        try {
273
+            Long count = checkApprovalService.getPendingTaskCount(SecurityUtils.getUserId());
274
+            return AjaxResult.success("获取待办任务数量成功", count);
275
+        } catch (Exception e) {
276
+            logger.error("获取待办任务数量失败", e);
277
+            return AjaxResult.error("获取失败:" + e.getMessage());
278
+        }
279
+    }
280
+
281
+    /**
282
+     * 13. 获取当前用户的抄送列表详情
283
+     */
284
+    @PreAuthorize("@ss.hasPermi('system:approval:query')")
285
+    @GetMapping("/approval/cc/details")
286
+    public TableDataInfo getCcDetails() {
287
+        try {
288
+            startPage();
289
+            List<ApprovalCcDetailDTO> list = checkApprovalService.getCcDetailsByUserId(SecurityUtils.getUserId());
290
+            return getDataTable(list);
291
+        } catch (Exception e) {
292
+            logger.error("获取抄送详情列表失败", e);
293
+            return getDataTable(null);
294
+        }
295
+    }
296
+    
297
+    /**
298
+     * 14. 根据实例ID获取审批历史
299
+     */
300
+    @PreAuthorize("@ss.hasPermi('system:approval:query')")
301
+    @GetMapping("/approval/history/instance/{instanceId}")
302
+    public TableDataInfo getApprovalHistoryByInstanceId(@PathVariable Long instanceId) {
303
+        try {
304
+            List<com.sundot.airport.system.domain.approval.ApprovalHistory> list = 
305
+                checkApprovalService.getApprovalHistoryByInstanceId(instanceId);
306
+            return getDataTable(list);
307
+        } catch (Exception e) {
308
+            logger.error("根据实例ID获取审批历史失败", e);
309
+            return getDataTable(null);
310
+        }
311
+    }
312
+    
313
+    /**
314
+     * 15. 根据任务ID获取审批历史
315
+     */
316
+    @PreAuthorize("@ss.hasPermi('system:approval:query')")
317
+    @GetMapping("/approval/history/task/{taskId}")
318
+    public TableDataInfo getApprovalHistoryByTaskId(@PathVariable Long taskId) {
319
+        try {
320
+            List<com.sundot.airport.system.domain.approval.ApprovalHistory> list = 
321
+                checkApprovalService.getApprovalHistoryByTaskId(taskId);
322
+            return getDataTable(list);
323
+        } catch (Exception e) {
324
+            logger.error("根据任务ID获取审批历史失败", e);
325
+            return getDataTable(null);
326
+        }
327
+    }
328
+    
329
+    /**
330
+     * 16. 获取用户的审批历史记录
331
+     */
332
+    @PreAuthorize("@ss.hasPermi('system:approval:query')")
333
+    @GetMapping("/approval/history/user")
334
+    public TableDataInfo getUserApprovalHistory(@RequestParam(required = false) String action) {
335
+        try {
336
+            List<com.sundot.airport.system.domain.approval.ApprovalHistory> list = 
337
+                checkApprovalService.getUserApprovalHistory(SecurityUtils.getUserId(), action);
338
+            return getDataTable(list);
339
+        } catch (Exception e) {
340
+            logger.error("获取用户审批历史失败", e);
341
+            return getDataTable(null);
342
+        }
343
+    }
344
+    
345
+    /**
346
+     * 17. 批量更新抄送消息已读状态
347
+     */
348
+    @PreAuthorize("@ss.hasPermi('system:approval:update')")
349
+    @Log(title = "批量更新抄送已读状态", businessType = BusinessType.UPDATE)
350
+    @PutMapping("/approval/cc/batch-read")
351
+    public AjaxResult batchUpdateCcReadStatus(@RequestBody List<Long> ids) {
352
+        try {
353
+            if (ids == null || ids.isEmpty()) {
354
+                return AjaxResult.error("ID列表不能为空");
355
+            }
356
+            int rows = checkApprovalService.batchUpdateCcReadStatus(ids);
357
+            return AjaxResult.success("批量更新成功", rows);
358
+        } catch (Exception e) {
359
+            logger.error("批量更新抄送已读状态失败", e);
360
+            return AjaxResult.error("更新失败:" + e.getMessage());
361
+        }
362
+    }
363
+}

+ 147 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/approval/dto/ApprovalDTO.java

@@ -0,0 +1,147 @@
1
+package com.sundot.airport.web.controller.approval.dto;
2
+
3
+import javax.validation.constraints.NotBlank;
4
+import java.util.Date;
5
+import java.util.Map;
6
+
7
+/**
8
+ * 审批DTO
9
+ * 
10
+ * @author simon lin
11
+ * @date 2025-09-06
12
+ */
13
+public class ApprovalDTO
14
+{
15
+    /** 审批意见 */
16
+    @NotBlank(message = "审批意见不能为空")
17
+    private String comment;
18
+    
19
+    /** 节点类型 */
20
+    private String nodeType;
21
+    
22
+    /** 目标班组ID */
23
+    private Long targetGroupId;
24
+    
25
+    /** 整改要求 */
26
+    private String rectificationRequirement;
27
+    
28
+    /** 整改详情 */
29
+    private String rectificationDetail;
30
+    
31
+    /** 整改时间 */
32
+    private Date rectificationTime;
33
+    
34
+    /** 整改结果 */
35
+    private String rectificationResult;
36
+    
37
+    /** 复查结果 */
38
+    private String reviewResult;
39
+    
40
+    /** 复查意见 */
41
+    private String reviewComment;
42
+    
43
+    /** 最终决定 */
44
+    private String finalDecision;
45
+    
46
+    /** 复查时间 */
47
+    private Date reviewTime;
48
+    
49
+    /** 表单数据 */
50
+    private Map<String, Object> formData;
51
+
52
+    public String getComment() {
53
+        return comment;
54
+    }
55
+
56
+    public void setComment(String comment) {
57
+        this.comment = comment;
58
+    }
59
+
60
+    public String getNodeType() {
61
+        return nodeType;
62
+    }
63
+
64
+    public void setNodeType(String nodeType) {
65
+        this.nodeType = nodeType;
66
+    }
67
+
68
+    public Long getTargetGroupId() {
69
+        return targetGroupId;
70
+    }
71
+
72
+    public void setTargetGroupId(Long targetGroupId) {
73
+        this.targetGroupId = targetGroupId;
74
+    }
75
+
76
+    public String getRectificationRequirement() {
77
+        return rectificationRequirement;
78
+    }
79
+
80
+    public void setRectificationRequirement(String rectificationRequirement) {
81
+        this.rectificationRequirement = rectificationRequirement;
82
+    }
83
+
84
+    public String getRectificationDetail() {
85
+        return rectificationDetail;
86
+    }
87
+
88
+    public void setRectificationDetail(String rectificationDetail) {
89
+        this.rectificationDetail = rectificationDetail;
90
+    }
91
+
92
+    public Date getRectificationTime() {
93
+        return rectificationTime;
94
+    }
95
+
96
+    public void setRectificationTime(Date rectificationTime) {
97
+        this.rectificationTime = rectificationTime;
98
+    }
99
+
100
+    public String getRectificationResult() {
101
+        return rectificationResult;
102
+    }
103
+
104
+    public void setRectificationResult(String rectificationResult) {
105
+        this.rectificationResult = rectificationResult;
106
+    }
107
+
108
+    public String getReviewResult() {
109
+        return reviewResult;
110
+    }
111
+
112
+    public void setReviewResult(String reviewResult) {
113
+        this.reviewResult = reviewResult;
114
+    }
115
+
116
+    public String getReviewComment() {
117
+        return reviewComment;
118
+    }
119
+
120
+    public void setReviewComment(String reviewComment) {
121
+        this.reviewComment = reviewComment;
122
+    }
123
+
124
+    public String getFinalDecision() {
125
+        return finalDecision;
126
+    }
127
+
128
+    public void setFinalDecision(String finalDecision) {
129
+        this.finalDecision = finalDecision;
130
+    }
131
+
132
+    public Date getReviewTime() {
133
+        return reviewTime;
134
+    }
135
+
136
+    public void setReviewTime(Date reviewTime) {
137
+        this.reviewTime = reviewTime;
138
+    }
139
+
140
+    public Map<String, Object> getFormData() {
141
+        return formData;
142
+    }
143
+
144
+    public void setFormData(Map<String, Object> formData) {
145
+        this.formData = formData;
146
+    }
147
+}

+ 72 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/approval/dto/PersonalCheckDTO.java

@@ -0,0 +1,72 @@
1
+package com.sundot.airport.web.controller.approval.dto;
2
+
3
+import javax.validation.constraints.NotBlank;
4
+import javax.validation.constraints.NotNull;
5
+import java.util.List;
6
+
7
+/**
8
+ * 个人级别检查DTO
9
+ * 
10
+ * @author simon lin
11
+ * @date 2025-09-06
12
+ */
13
+public class PersonalCheckDTO
14
+{
15
+    /** 业务ID */
16
+    @NotNull(message = "业务ID不能为空")
17
+    private Long id;
18
+    
19
+    /** 检查描述 */
20
+    @NotBlank(message = "检查描述不能为空")
21
+    private String description;
22
+    
23
+    /** 检查类型 */
24
+    private String checkType;
25
+    
26
+    /** 检查位置 */
27
+    private String location;
28
+    
29
+    /** 目标用户ID列表 */
30
+    @NotNull(message = "目标用户ID列表不能为空")
31
+    private Long[] targetUserIds;
32
+
33
+    public Long getId() {
34
+        return id;
35
+    }
36
+
37
+    public void setId(Long id) {
38
+        this.id = id;
39
+    }
40
+
41
+    public String getDescription() {
42
+        return description;
43
+    }
44
+
45
+    public void setDescription(String description) {
46
+        this.description = description;
47
+    }
48
+
49
+    public String getCheckType() {
50
+        return checkType;
51
+    }
52
+
53
+    public void setCheckType(String checkType) {
54
+        this.checkType = checkType;
55
+    }
56
+
57
+    public String getLocation() {
58
+        return location;
59
+    }
60
+
61
+    public void setLocation(String location) {
62
+        this.location = location;
63
+    }
64
+
65
+    public Long[] getTargetUserIds() {
66
+        return targetUserIds;
67
+    }
68
+
69
+    public void setTargetUserIds(Long[] targetUserIds) {
70
+        this.targetUserIds = targetUserIds;
71
+    }
72
+}

+ 24 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/approval/dto/RejectDTO.java

@@ -0,0 +1,24 @@
1
+package com.sundot.airport.web.controller.approval.dto;
2
+
3
+import javax.validation.constraints.NotBlank;
4
+
5
+/**
6
+ * 驳回DTO
7
+ * 
8
+ * @author simon lin
9
+ * @date 2025-09-06
10
+ */
11
+public class RejectDTO
12
+{
13
+    /** 驳回理由 */
14
+    @NotBlank(message = "驳回理由不能为空")
15
+    private String rejectReason;
16
+
17
+    public String getRejectReason() {
18
+        return rejectReason;
19
+    }
20
+
21
+    public void setRejectReason(String rejectReason) {
22
+        this.rejectReason = rejectReason;
23
+    }
24
+}

+ 70 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/approval/dto/SectionCheckDTO.java

@@ -0,0 +1,70 @@
1
+package com.sundot.airport.web.controller.approval.dto;
2
+
3
+import javax.validation.constraints.NotBlank;
4
+import javax.validation.constraints.NotNull;
5
+
6
+/**
7
+ * 科级检查DTO
8
+ * 
9
+ * @author simon lin
10
+ * @date 2025-09-06
11
+ */
12
+public class SectionCheckDTO
13
+{
14
+    /** 业务ID */
15
+    @NotNull(message = "业务ID不能为空")
16
+    private Long id;
17
+    
18
+    /** 检查描述 */
19
+    @NotBlank(message = "检查描述不能为空")
20
+    private String description;
21
+    
22
+    /** 检查类型 */
23
+    private String checkType;
24
+    
25
+    /** 严重程度 */
26
+    private String severity;
27
+    
28
+    /** 目标部门ID */
29
+    private Long targetDeptId;
30
+
31
+    public Long getId() {
32
+        return id;
33
+    }
34
+
35
+    public void setId(Long id) {
36
+        this.id = id;
37
+    }
38
+
39
+    public String getDescription() {
40
+        return description;
41
+    }
42
+
43
+    public void setDescription(String description) {
44
+        this.description = description;
45
+    }
46
+
47
+    public String getCheckType() {
48
+        return checkType;
49
+    }
50
+
51
+    public void setCheckType(String checkType) {
52
+        this.checkType = checkType;
53
+    }
54
+
55
+    public String getSeverity() {
56
+        return severity;
57
+    }
58
+
59
+    public void setSeverity(String severity) {
60
+        this.severity = severity;
61
+    }
62
+
63
+    public Long getTargetDeptId() {
64
+        return targetDeptId;
65
+    }
66
+
67
+    public void setTargetDeptId(Long targetDeptId) {
68
+        this.targetDeptId = targetDeptId;
69
+    }
70
+}

+ 82 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/approval/dto/SeizureReportDTO.java

@@ -0,0 +1,82 @@
1
+package com.sundot.airport.web.controller.approval.dto;
2
+
3
+import javax.validation.constraints.NotBlank;
4
+import javax.validation.constraints.NotNull;
5
+import java.util.Date;
6
+
7
+/**
8
+ * 查获上报DTO
9
+ * 
10
+ * @author simon lin
11
+ * @date 2025-09-06
12
+ */
13
+public class SeizureReportDTO
14
+{
15
+    /** 业务ID */
16
+    @NotNull(message = "业务ID不能为空")
17
+    private Long id;
18
+    
19
+    /** 物品类型 */
20
+    @NotBlank(message = "物品类型不能为空")
21
+    private String itemType;
22
+    
23
+    /** 数量 */
24
+    private Integer quantity;
25
+    
26
+    /** 位置 */
27
+    private String location;
28
+    
29
+    /** 查获时间 */
30
+    private Date seizureTime;
31
+    
32
+    /** 旅客信息 */
33
+    private String passengerInfo;
34
+
35
+    public Long getId() {
36
+        return id;
37
+    }
38
+
39
+    public void setId(Long id) {
40
+        this.id = id;
41
+    }
42
+
43
+    public String getItemType() {
44
+        return itemType;
45
+    }
46
+
47
+    public void setItemType(String itemType) {
48
+        this.itemType = itemType;
49
+    }
50
+
51
+    public Integer getQuantity() {
52
+        return quantity;
53
+    }
54
+
55
+    public void setQuantity(Integer quantity) {
56
+        this.quantity = quantity;
57
+    }
58
+
59
+    public String getLocation() {
60
+        return location;
61
+    }
62
+
63
+    public void setLocation(String location) {
64
+        this.location = location;
65
+    }
66
+
67
+    public Date getSeizureTime() {
68
+        return seizureTime;
69
+    }
70
+
71
+    public void setSeizureTime(Date seizureTime) {
72
+        this.seizureTime = seizureTime;
73
+    }
74
+
75
+    public String getPassengerInfo() {
76
+        return passengerInfo;
77
+    }
78
+
79
+    public void setPassengerInfo(String passengerInfo) {
80
+        this.passengerInfo = passengerInfo;
81
+    }
82
+}

+ 50 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/attendance/AttendanceAreaController.java

@@ -0,0 +1,50 @@
1
+package com.sundot.airport.web.controller.attendance;
2
+
3
+import cn.hutool.core.collection.CollectionUtil;
4
+import com.sundot.airport.attendance.domain.AttendanceArea;
5
+import com.sundot.airport.attendance.service.AttendanceAreaService;
6
+import com.sundot.airport.common.core.controller.BaseController;
7
+import com.sundot.airport.common.core.domain.AjaxResult;
8
+import org.springframework.beans.factory.annotation.Autowired;
9
+import org.springframework.security.access.prepost.PreAuthorize;
10
+import org.springframework.web.bind.annotation.*;
11
+
12
+import java.math.BigDecimal;
13
+import java.util.List;
14
+
15
+@RestController
16
+@RequestMapping("/attendance/area")
17
+public class AttendanceAreaController extends BaseController {
18
+
19
+    @Autowired
20
+    private AttendanceAreaService attendanceService;
21
+
22
+    /**
23
+     * 查询是否在考勤范围
24
+     * @param lng 经度
25
+     * @param lat 纬度
26
+     * @return
27
+     */
28
+    @PostMapping("/check-in")
29
+    public AjaxResult checkUserInValidArea(@RequestParam BigDecimal lng,
30
+                                           @RequestParam BigDecimal lat) {
31
+        List<AttendanceArea> validAreas =  attendanceService.checkUserInValidArea(lng, lat);
32
+        if (CollectionUtil.isEmpty(validAreas)) {
33
+            return AjaxResult.error("不在任何考勤范围内");
34
+        }
35
+        return success(validAreas);
36
+    }
37
+
38
+    /**
39
+     * 考勤打卡列表
40
+     * @return
41
+     */
42
+    @PostMapping("/list")
43
+    public AjaxResult areaList() {
44
+        List<AttendanceArea> validAreas =  attendanceService.areaList();
45
+        if (CollectionUtil.isEmpty(validAreas)) {
46
+            return AjaxResult.error("没有考勤范围");
47
+        }
48
+        return success(validAreas);
49
+    }
50
+}

+ 91 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/attendance/AttendanceCheckRecordController.java

@@ -0,0 +1,91 @@
1
+package com.sundot.airport.web.controller.attendance;
2
+
3
+import com.sundot.airport.attendance.domain.AttendanceCheckRecord;
4
+import com.sundot.airport.attendance.service.IAttendanceCheckRecordService;
5
+import com.sundot.airport.common.annotation.Log;
6
+import com.sundot.airport.common.core.controller.BaseController;
7
+import com.sundot.airport.common.core.domain.AjaxResult;
8
+import com.sundot.airport.common.core.page.TableDataInfo;
9
+import com.sundot.airport.common.enums.BusinessType;
10
+import com.sundot.airport.common.utils.poi.ExcelUtil;
11
+import org.springframework.beans.factory.annotation.Autowired;
12
+import org.springframework.security.access.prepost.PreAuthorize;
13
+import org.springframework.web.bind.annotation.*;
14
+
15
+import javax.servlet.http.HttpServletResponse;
16
+import java.util.List;
17
+
18
+/**
19
+ * 打卡记录Controller
20
+ *
21
+ * @author wangchong
22
+ * @date 2025-07-10
23
+ */
24
+@RestController
25
+@RequestMapping("/attendance/checkRecord")
26
+public class AttendanceCheckRecordController extends BaseController {
27
+    @Autowired
28
+    private IAttendanceCheckRecordService attendanceCheckRecordService;
29
+
30
+    /**
31
+     * 查询打卡记录列表
32
+     */
33
+    @PreAuthorize("@ss.hasPermi('attendance:checkRecord:list')")
34
+    @GetMapping("/list")
35
+    public TableDataInfo list(AttendanceCheckRecord attendanceCheckRecord) {
36
+        startPage();
37
+        List<AttendanceCheckRecord> list = attendanceCheckRecordService.selectAttendanceCheckRecordList(attendanceCheckRecord);
38
+        return getDataTable(list);
39
+    }
40
+
41
+    /**
42
+     * 导出打卡记录列表
43
+     */
44
+    @PreAuthorize("@ss.hasPermi('attendance:checkRecord:export')")
45
+    @Log(title = "打卡记录", businessType = BusinessType.EXPORT)
46
+    @PostMapping("/export")
47
+    public void export(HttpServletResponse response, AttendanceCheckRecord attendanceCheckRecord) {
48
+        List<AttendanceCheckRecord> list = attendanceCheckRecordService.selectAttendanceCheckRecordList(attendanceCheckRecord);
49
+        ExcelUtil<AttendanceCheckRecord> util = new ExcelUtil<AttendanceCheckRecord>(AttendanceCheckRecord.class);
50
+        util.exportExcel(response, list, "打卡记录数据");
51
+    }
52
+
53
+    /**
54
+     * 获取打卡记录详细信息
55
+     */
56
+    @PreAuthorize("@ss.hasPermi('attendance:checkRecord:query')")
57
+    @GetMapping(value = "/{id}")
58
+    public AjaxResult getInfo(@PathVariable("id") Long id) {
59
+        return success(attendanceCheckRecordService.selectAttendanceCheckRecordById(id));
60
+    }
61
+
62
+    /**
63
+     * 新增打卡记录
64
+     */
65
+    @PreAuthorize("@ss.hasPermi('attendance:checkRecord:add')")
66
+    @Log(title = "打卡记录", businessType = BusinessType.INSERT)
67
+    @PostMapping
68
+    public AjaxResult add(@RequestBody AttendanceCheckRecord attendanceCheckRecord) {
69
+        return toAjax(attendanceCheckRecordService.insertAttendanceCheckRecord(attendanceCheckRecord));
70
+    }
71
+
72
+    /**
73
+     * 修改打卡记录
74
+     */
75
+    @PreAuthorize("@ss.hasPermi('attendance:checkRecord:edit')")
76
+    @Log(title = "打卡记录", businessType = BusinessType.UPDATE)
77
+    @PutMapping
78
+    public AjaxResult edit(@RequestBody AttendanceCheckRecord attendanceCheckRecord) {
79
+        return toAjax(attendanceCheckRecordService.updateAttendanceCheckRecord(attendanceCheckRecord));
80
+    }
81
+
82
+    /**
83
+     * 删除打卡记录
84
+     */
85
+    @PreAuthorize("@ss.hasPermi('attendance:checkRecord:remove')")
86
+    @Log(title = "打卡记录", businessType = BusinessType.DELETE)
87
+    @DeleteMapping("/{ids}")
88
+    public AjaxResult remove(@PathVariable Long[] ids) {
89
+        return toAjax(attendanceCheckRecordService.deleteAttendanceCheckRecordByIds(ids));
90
+    }
91
+}

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 1106 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/attendance/AttendancePostRecordController.java


+ 275 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/attendance/AttendanceRecordController.java

@@ -0,0 +1,275 @@
1
+package com.sundot.airport.web.controller.attendance;
2
+
3
+import cn.hutool.core.collection.CollectionUtil;
4
+import cn.hutool.core.date.DateUtil;
5
+import cn.hutool.core.util.ObjUtil;
6
+import cn.hutool.core.util.StrUtil;
7
+import com.sundot.airport.attendance.domain.AttendanceRecord;
8
+import com.sundot.airport.attendance.dto.AttendanceRecordImportVO;
9
+import com.sundot.airport.attendance.service.IAttendanceRecordService;
10
+import com.sundot.airport.common.annotation.Log;
11
+import com.sundot.airport.common.core.controller.BaseController;
12
+import com.sundot.airport.common.core.domain.AjaxResult;
13
+import com.sundot.airport.common.core.page.TableDataInfo;
14
+import com.sundot.airport.common.dto.UserInfo;
15
+import com.sundot.airport.common.enums.BusinessType;
16
+import com.sundot.airport.common.exception.ServiceException;
17
+import com.sundot.airport.common.utils.poi.ExcelUtil;
18
+import com.sundot.airport.web.core.utils.DataPermissionUtils;
19
+import com.sundot.airport.system.service.ISysDeptService;
20
+import com.sundot.airport.web.core.cache.UserCache;
21
+import com.sundot.airport.common.core.domain.DataPermissionResult;
22
+import com.sundot.airport.common.enums.DataPermissionType;
23
+import org.springframework.beans.factory.annotation.Autowired;
24
+import org.springframework.security.access.prepost.PreAuthorize;
25
+import org.springframework.web.bind.annotation.*;
26
+import org.springframework.web.multipart.MultipartFile;
27
+
28
+import javax.servlet.http.HttpServletResponse;
29
+import java.util.Date;
30
+import java.util.List;
31
+import java.util.Map;
32
+import java.util.Objects;
33
+
34
+/**
35
+ * 考勤记录Controller
36
+ *
37
+ * @author wangchong
38
+ * @date 2025-07-10
39
+ */
40
+@RestController
41
+@RequestMapping("/attendance/attendanceRecord")
42
+public class AttendanceRecordController extends BaseController {
43
+    @Autowired
44
+    private IAttendanceRecordService attendanceRecordService;
45
+
46
+    @Autowired
47
+    private UserCache userCache;
48
+    
49
+    @Autowired
50
+    private ISysDeptService sysDeptService;
51
+
52
+    /**
53
+     * 查询考勤记录列表
54
+     */
55
+    @PreAuthorize("@ss.hasPermi('attendance:attendanceRecord:list')")
56
+    @GetMapping("/list")
57
+    public TableDataInfo list(AttendanceRecord attendanceRecord) {
58
+        // 应用数据权限过滤
59
+        DataPermissionResult dataPermission = DataPermissionUtils.getDataPermission(getUserId(), getDeptId(), getLoginUser());
60
+        switch (dataPermission.getPermissionType()) {
61
+            case SELF:
62
+                attendanceRecord.setUserId(dataPermission.getValue());
63
+                break;
64
+            case STATION:
65
+                attendanceRecord.setStationCode(dataPermission.getValue().toString());
66
+                break;
67
+            case DEPARTMENT:
68
+                attendanceRecord.setDepartmentCode(dataPermission.getValue().toString());
69
+                break;
70
+            case TEAM:
71
+                attendanceRecord.setTeamCode(dataPermission.getValue().toString());
72
+                break;
73
+            case ALL:
74
+            default:
75
+                // 不设置过滤条件,查看所有数据
76
+                break;
77
+        }
78
+        
79
+        startPage();
80
+        List<AttendanceRecord> list = attendanceRecordService.selectAttendanceRecordList(attendanceRecord);
81
+        return getDataTable(list);
82
+    }
83
+
84
+    /**
85
+     * 导出考勤记录列表
86
+     */
87
+    @PreAuthorize("@ss.hasPermi('attendance:attendanceRecord:export')")
88
+    @Log(title = "考勤记录", businessType = BusinessType.EXPORT)
89
+    @PostMapping("/export")
90
+    public void export(HttpServletResponse response, AttendanceRecord attendanceRecord) {
91
+        // 应用数据权限过滤
92
+        DataPermissionResult dataPermission = DataPermissionUtils.getDataPermission(getUserId(), getDeptId(), getLoginUser());
93
+        switch (dataPermission.getPermissionType()) {
94
+            case SELF:
95
+                attendanceRecord.setUserId(dataPermission.getValue());
96
+                break;
97
+            case STATION:
98
+                attendanceRecord.setStationCode(dataPermission.getValue().toString());
99
+                break;
100
+            case DEPARTMENT:
101
+                attendanceRecord.setDepartmentCode(dataPermission.getValue().toString());
102
+                break;
103
+            case TEAM:
104
+                attendanceRecord.setTeamCode(dataPermission.getValue().toString());
105
+                break;
106
+            case ALL:
107
+            default:
108
+                // 不设置过滤条件,查看所有数据
109
+                break;
110
+        }
111
+        
112
+        List<AttendanceRecord> list = attendanceRecordService.selectAttendanceRecordList(attendanceRecord);
113
+        ExcelUtil<AttendanceRecord> util = new ExcelUtil<AttendanceRecord>(AttendanceRecord.class);
114
+        util.exportExcel(response, list, "考勤记录数据");
115
+    }
116
+
117
+    /**
118
+     * 获取考勤记录详细信息
119
+     */
120
+    @PreAuthorize("@ss.hasPermi('attendance:attendanceRecord:query')")
121
+    @GetMapping(value = "/{id}")
122
+    public AjaxResult getInfo(@PathVariable("id") Long id) {
123
+        return success(attendanceRecordService.selectAttendanceRecordById(id));
124
+    }
125
+
126
+    /**
127
+     * 新增考勤记录
128
+     */
129
+    @PreAuthorize("@ss.hasPermi('attendance:attendanceRecord:add')")
130
+    @Log(title = "考勤记录", businessType = BusinessType.INSERT)
131
+    @PostMapping
132
+    public AjaxResult add(@RequestBody AttendanceRecord attendanceRecord) {
133
+        UserInfo userInfo = userCache.getUserInfo(attendanceRecord.getUserId());
134
+        if (Objects.nonNull(userInfo.getTeamsId())) {
135
+            attendanceRecord.setTeamCode(userInfo.getTeamsId().toString());
136
+        }
137
+        attendanceRecord.setTeamName(userInfo.getTeamsName());
138
+        if (Objects.nonNull(userInfo.getDepartmentId())) {
139
+            attendanceRecord.setDepartmentCode(userInfo.getDepartmentId().toString());
140
+        }
141
+        attendanceRecord.setDepartmentName(userInfo.getDepartmentName());
142
+        if (Objects.nonNull(userInfo.getStationId())) {
143
+            attendanceRecord.setStationCode(userInfo.getStationId().toString());
144
+        }
145
+        attendanceRecord.setStationName(userInfo.getStationName());
146
+        attendanceRecord.setUserName(userInfo.getNickName());
147
+        attendanceRecord.setRevision(1L);
148
+        attendanceRecord.setWorkDuration((attendanceRecord.getCheckOutTime().getTime() - attendanceRecord.getCheckInTime().getTime()) / 1000 / 60);
149
+        attendanceRecord.setAttendanceDate(DateUtil.date(attendanceRecord.getCheckInTime()));
150
+        attendanceRecord.setOverDuration(0L);
151
+        attendanceRecord.setCreateBy(getUserId().toString());
152
+        return toAjax(attendanceRecordService.insertAttendanceRecord(attendanceRecord));
153
+    }
154
+
155
+    /**
156
+     * 修改考勤记录
157
+     */
158
+    @PreAuthorize("@ss.hasPermi('attendance:attendanceRecord:edit')")
159
+    @Log(title = "考勤记录", businessType = BusinessType.UPDATE)
160
+    @PutMapping
161
+    public AjaxResult edit(@RequestBody AttendanceRecord attendanceRecord) {
162
+        attendanceRecord.setUpdateBy(getUserId().toString());
163
+        attendanceRecord.setRevision(attendanceRecord.getRevision() + 1);
164
+        attendanceRecord.setWorkDuration((attendanceRecord.getCheckOutTime().getTime() - attendanceRecord.getCheckInTime().getTime()) / 1000 / 60);
165
+        attendanceRecord.setAttendanceDate(DateUtil.date(attendanceRecord.getCheckInTime()));
166
+        return toAjax(attendanceRecordService.updateAttendanceRecord(attendanceRecord));
167
+    }
168
+
169
+    /**
170
+     * 删除考勤记录
171
+     */
172
+    @PreAuthorize("@ss.hasPermi('attendance:attendanceRecord:remove')")
173
+    @Log(title = "考勤记录", businessType = BusinessType.DELETE)
174
+    @DeleteMapping("/{ids}")
175
+    public AjaxResult remove(@PathVariable Long[] ids) {
176
+        return toAjax(attendanceRecordService.deleteAttendanceRecordByIds(ids));
177
+    }
178
+
179
+    @PostMapping("/importTemplate")
180
+    public void importTemplate(HttpServletResponse response) {
181
+        ExcelUtil<AttendanceRecordImportVO> util = new ExcelUtil<AttendanceRecordImportVO>(AttendanceRecordImportVO.class);
182
+        util.importTemplateExcel(response, "用户数据");
183
+    }
184
+
185
+    @Log(title = "考勤管理", businessType = BusinessType.IMPORT)
186
+    @PreAuthorize("@ss.hasPermi('attendance:attendanceRecord:import')")
187
+    @PostMapping("/importData")
188
+    public AjaxResult importData(MultipartFile file) throws Exception {
189
+        ExcelUtil<AttendanceRecordImportVO> util = new ExcelUtil<AttendanceRecordImportVO>(AttendanceRecordImportVO.class);
190
+        List<AttendanceRecordImportVO> list = util.importExcel(file.getInputStream());
191
+        if (CollectionUtil.isEmpty(list)) {
192
+            throw new ServiceException("导入用户数据不能为空");
193
+        }
194
+        validateData(list);
195
+        String message = importData(list);
196
+        return success(message);
197
+    }
198
+
199
+    private void validateData(List<AttendanceRecordImportVO> list) {
200
+        StringBuilder failureMsg = new StringBuilder();
201
+        int failureNum = 2;
202
+        for (AttendanceRecordImportVO vo : list) {
203
+            if (StrUtil.isBlank(vo.getUserName())) {
204
+                failureMsg.append("<br/>第 ").append(failureNum).append(" 行用户姓名不能为空");
205
+            }
206
+            UserInfo userInfo = userCache.getUserInfo(vo.getUserName());
207
+            if (Objects.isNull(userInfo) || Objects.isNull(userInfo.getUserId())) {
208
+                failureMsg.append("<br/>第 ").append(failureNum).append(" 行用户不存在");
209
+            }
210
+            if (ObjUtil.isNull(vo.getAttendanceDate())) {
211
+                failureMsg.append("<br/>第 ").append(failureNum).append(" 行考勤日期格式错误或者不能为空");
212
+            }
213
+            if (ObjUtil.isNull(vo.getCheckInTime())) {
214
+                failureMsg.append("<br/>第 ").append(failureNum).append(" 行签到时间格式错误或者不能为空");
215
+            }
216
+            if (ObjUtil.isNull(vo.getCheckOutTime())) {
217
+                failureMsg.append("<br/>第 ").append(failureNum).append(" 行签退时间格式错误或者不能为空");
218
+            }
219
+            failureNum++;
220
+        }
221
+        if (StrUtil.isNotBlank(failureMsg)) {
222
+            throw new ServiceException(failureMsg.toString());
223
+        }
224
+    }
225
+
226
+    private String importData(List<AttendanceRecordImportVO> list) {
227
+        int successNum = 0;
228
+        int failureNum = 0;
229
+        StringBuilder successMsg = new StringBuilder();
230
+        StringBuilder failureMsg = new StringBuilder();
231
+        for (AttendanceRecordImportVO vo : list) {
232
+            UserInfo userInfo = userCache.getUserInfo(vo.getUserName());
233
+            try {
234
+                AttendanceRecord attendanceRecord = new AttendanceRecord();
235
+                attendanceRecord.setRevision(1L);
236
+                attendanceRecord.setUserId(userInfo.getUserId());
237
+                attendanceRecord.setCheckInTime(vo.getCheckInTime());
238
+                attendanceRecord.setCheckOutTime(vo.getCheckOutTime());
239
+                attendanceRecord.setWorkDuration((attendanceRecord.getCheckOutTime().getTime() - attendanceRecord.getCheckInTime().getTime()) / 1000 / 60);
240
+                if (Objects.nonNull(userInfo.getTeamsId())) {
241
+                    attendanceRecord.setTeamCode(userInfo.getTeamsId().toString());
242
+                }
243
+                attendanceRecord.setTeamName(userInfo.getTeamsName());
244
+                if (Objects.nonNull(userInfo.getDepartmentId())) {
245
+                    attendanceRecord.setDepartmentCode(userInfo.getDepartmentId().toString());
246
+                }
247
+                attendanceRecord.setDepartmentName(userInfo.getDepartmentName());
248
+                if (Objects.nonNull(userInfo.getStationId())) {
249
+                    attendanceRecord.setStationCode(userInfo.getStationId().toString());
250
+                }
251
+                attendanceRecord.setStationName(userInfo.getStationName());
252
+                attendanceRecord.setUserName(userInfo.getNickName());
253
+                attendanceRecord.setRemark(vo.getRemark());
254
+                attendanceRecord.setAttendanceDate(vo.getAttendanceDate());
255
+                attendanceRecord.setOverDuration(0L);
256
+                attendanceRecord.setCreateBy(getUserId().toString());
257
+                attendanceRecord.setCreateTime(new Date());
258
+                attendanceRecordService.insertAttendanceRecord(attendanceRecord);
259
+                successNum++;
260
+                successMsg.append("<br/>" + successNum + "、账号 " + userInfo.getUserName() + " 导入成功");
261
+            } catch (Exception e) {
262
+                failureNum++;
263
+                String msg = "<br/>" + failureNum + "、账号 " + userInfo.getUserName() + " 导入失败:";
264
+                failureMsg.append(msg + e.getMessage());
265
+            }
266
+        }
267
+        if (failureNum > 0) {
268
+            failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:");
269
+            throw new ServiceException(failureMsg.toString());
270
+        } else {
271
+            successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:");
272
+        }
273
+        return successMsg.toString();
274
+    }
275
+}

+ 142 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/attendance/AttendanceTeamUserRecordController.java

@@ -0,0 +1,142 @@
1
+package com.sundot.airport.web.controller.attendance;
2
+
3
+import java.util.Date;
4
+import java.util.List;
5
+import javax.servlet.http.HttpServletResponse;
6
+import org.springframework.security.access.prepost.PreAuthorize;
7
+import org.springframework.beans.factory.annotation.Autowired;
8
+import org.springframework.web.bind.annotation.GetMapping;
9
+import org.springframework.web.bind.annotation.PostMapping;
10
+import org.springframework.web.bind.annotation.PutMapping;
11
+import org.springframework.web.bind.annotation.DeleteMapping;
12
+import org.springframework.web.bind.annotation.PathVariable;
13
+import org.springframework.web.bind.annotation.RequestBody;
14
+import org.springframework.web.bind.annotation.RequestMapping;
15
+import org.springframework.web.bind.annotation.RestController;
16
+import com.sundot.airport.common.annotation.Log;
17
+import com.sundot.airport.common.core.controller.BaseController;
18
+import com.sundot.airport.common.core.domain.AjaxResult;
19
+import com.sundot.airport.common.enums.BusinessType;
20
+import com.sundot.airport.attendance.domain.AttendanceTeamUserRecord;
21
+import com.sundot.airport.attendance.service.IAttendanceTeamUserRecordService;
22
+import com.sundot.airport.common.utils.poi.ExcelUtil;
23
+import com.sundot.airport.common.core.page.TableDataInfo;
24
+
25
+/**
26
+ * 考勤班组成员Controller
27
+ * 
28
+ * @author ruoyi
29
+ * @date 2025-09-06
30
+ */
31
+@RestController
32
+@RequestMapping("/attendance/record")
33
+public class AttendanceTeamUserRecordController extends BaseController
34
+{
35
+    @Autowired
36
+    private IAttendanceTeamUserRecordService attendanceTeamUserRecordService;
37
+
38
+    /**
39
+     * 查询考勤班组成员列表
40
+     * @param attendanceTeamUserRecord
41
+     * @return
42
+     */
43
+    @PreAuthorize("@ss.hasPermi('attendance:record:list')")
44
+    @GetMapping("/pageList")
45
+    public TableDataInfo pageList(AttendanceTeamUserRecord attendanceTeamUserRecord)
46
+    {
47
+        startPage();
48
+        List<AttendanceTeamUserRecord> list = attendanceTeamUserRecordService.selectAttendanceTeamUserRecordPageList(attendanceTeamUserRecord);
49
+        return getDataTable(list);
50
+    }
51
+
52
+    /**
53
+     * 上岗记录中查询考勤班组成员列表
54
+     */
55
+    @PreAuthorize("@ss.hasPermi('attendance:record:list')")
56
+    @GetMapping("/list")
57
+    public TableDataInfo list(AttendanceTeamUserRecord attendanceTeamUserRecord)
58
+    {
59
+        startPage();
60
+        List<AttendanceTeamUserRecord> list = attendanceTeamUserRecordService.selectAttendanceTeamUserRecordList(attendanceTeamUserRecord);
61
+        return getDataTable(list);
62
+    }
63
+
64
+    /**
65
+     * 导出考勤班组成员列表
66
+     */
67
+    @PreAuthorize("@ss.hasPermi('attendance:record:export')")
68
+    @Log(title = "考勤班组成员", businessType = BusinessType.EXPORT)
69
+    @PostMapping("/export")
70
+    public void export(HttpServletResponse response, AttendanceTeamUserRecord attendanceTeamUserRecord)
71
+    {
72
+        List<AttendanceTeamUserRecord> list = attendanceTeamUserRecordService.selectAttendanceTeamUserRecordList(attendanceTeamUserRecord);
73
+        ExcelUtil<AttendanceTeamUserRecord> util = new ExcelUtil<AttendanceTeamUserRecord>(AttendanceTeamUserRecord.class);
74
+        util.exportExcel(response, list, "考勤班组成员数据");
75
+    }
76
+
77
+    /**
78
+     * 获取考勤班组成员详细信息
79
+     */
80
+    @PreAuthorize("@ss.hasPermi('attendance:record:query')")
81
+    @GetMapping(value = "/{userId}")
82
+    public AjaxResult getInfo(@PathVariable("userId") Long userId, @PathVariable("attendanceDate") Date attendanceDate)
83
+    {
84
+        return success(attendanceTeamUserRecordService.selectAttendanceTeamUserRecordByUserId(userId, attendanceDate));
85
+    }
86
+
87
+    /**
88
+     * 新增考勤班组成员
89
+     */
90
+    @PreAuthorize("@ss.hasPermi('attendance:record:add')")
91
+    @Log(title = "考勤班组成员", businessType = BusinessType.INSERT)
92
+    @PostMapping
93
+    public AjaxResult add(@RequestBody AttendanceTeamUserRecord attendanceTeamUserRecord)
94
+    {
95
+        return toAjax(attendanceTeamUserRecordService.insertAttendanceTeamUserRecord(attendanceTeamUserRecord));
96
+    }
97
+
98
+    /**
99
+     * 修改考勤班组成员
100
+     */
101
+    @PreAuthorize("@ss.hasPermi('attendance:record:edit')")
102
+    @Log(title = "考勤班组成员", businessType = BusinessType.UPDATE)
103
+    @PutMapping
104
+    public AjaxResult edit(@RequestBody AttendanceTeamUserRecord attendanceTeamUserRecord)
105
+    {
106
+        return toAjax(attendanceTeamUserRecordService.updateAttendanceTeamUserRecord(attendanceTeamUserRecord));
107
+    }
108
+
109
+    /**
110
+     * 删除考勤班组成员
111
+     */
112
+    @PreAuthorize("@ss.hasPermi('attendance:record:remove')")
113
+    @Log(title = "考勤班组成员", businessType = BusinessType.DELETE)
114
+	@DeleteMapping("/{userIds}")
115
+    public AjaxResult remove(@PathVariable Long[] userIds)
116
+    {
117
+        return toAjax(attendanceTeamUserRecordService.deleteAttendanceTeamUserRecordByUserIds(userIds));
118
+    }
119
+
120
+    /**
121
+     *  批量新增考勤班组成员
122
+     * @param attendanceTeamUserRecord
123
+     * @return
124
+     */
125
+    @PreAuthorize("@ss.hasPermi('attendance:record:add:list')")
126
+    @Log(title = "考勤班组成员", businessType = BusinessType.INSERT)
127
+    @PostMapping(value = "/add/list")
128
+    public AjaxResult addList(@RequestBody List<AttendanceTeamUserRecord> attendanceTeamUserRecord)
129
+    {
130
+        return toAjax(attendanceTeamUserRecordService.insertAttendanceTeamUserRecordList(attendanceTeamUserRecord));
131
+    }
132
+
133
+    /**
134
+     * 获取考勤班组成员列表
135
+     */
136
+    @GetMapping("/list/teamLeader")
137
+    public AjaxResult listTeamLeader()
138
+    {
139
+        return success(attendanceTeamUserRecordService.selectAttendanceTeamLeaderId());
140
+    }
141
+
142
+}

+ 63 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/attendance/api/AttendanceController.java

@@ -0,0 +1,63 @@
1
+package com.sundot.airport.web.controller.attendance.api;
2
+
3
+import cn.hutool.core.util.ObjectUtil;
4
+import cn.hutool.core.util.StrUtil;
5
+import com.alibaba.fastjson2.JSON;
6
+import com.sundot.airport.attendance.domain.AttendanceRecord;
7
+import com.sundot.airport.attendance.service.IAttendanceRecordService;
8
+import com.sundot.airport.common.annotation.Log;
9
+import com.sundot.airport.common.core.controller.BaseController;
10
+import com.sundot.airport.common.core.domain.AjaxResult;
11
+import com.sundot.airport.common.dto.AttendanceRecordDTO;
12
+import com.sundot.airport.common.dto.AttendanceRecordReq;
13
+import com.sundot.airport.common.dto.UserInfo;
14
+import com.sundot.airport.common.enums.BusinessType;
15
+import com.sundot.airport.system.service.ISysConfigService;
16
+import com.sundot.airport.web.core.cache.UserCache;
17
+import org.springframework.beans.factory.annotation.Autowired;
18
+import org.springframework.transaction.annotation.Transactional;
19
+import org.springframework.web.bind.annotation.PostMapping;
20
+import org.springframework.web.bind.annotation.RequestBody;
21
+import org.springframework.web.bind.annotation.RequestMapping;
22
+import org.springframework.web.bind.annotation.RestController;
23
+
24
+import java.util.Date;
25
+
26
+/**
27
+ * 考勤管理 h5 api
28
+ *
29
+ * @Author: wangchong
30
+ * @Date: 2025/7/10 14:49
31
+ **/
32
+@RestController
33
+@RequestMapping("/attendance")
34
+public class AttendanceController extends BaseController {
35
+
36
+    @Autowired
37
+    private IAttendanceRecordService attendanceRecordService;
38
+
39
+    @Autowired
40
+    private UserCache userCache;
41
+
42
+    /**
43
+     * 打卡签到
44
+     *
45
+     * @param dto 打卡信息
46
+     * @return 今日打卡历史
47
+     */
48
+    @Log(title = "打卡签到", businessType = BusinessType.INSERT)
49
+    @PostMapping("/v1/record")
50
+    @Transactional(rollbackFor = Exception.class)
51
+    public AjaxResult record(@RequestBody AttendanceRecordDTO dto) {
52
+        UserInfo userInfo = userCache.getUserInfo(dto.getUserId());
53
+        return success(attendanceRecordService.record(dto, userInfo));
54
+    }
55
+
56
+    /**
57
+     * 打卡签到列表
58
+     */
59
+    @PostMapping("/v1/record-list")
60
+    public AjaxResult recordList(@RequestBody AttendanceRecordReq dto) {
61
+        return success(attendanceRecordService.recordList(dto));
62
+    }
63
+}

+ 31 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/cache/StatisticsCacheController.java

@@ -0,0 +1,31 @@
1
+package com.sundot.airport.web.controller.cache;
2
+
3
+import com.sundot.airport.common.core.controller.BaseController;
4
+import com.sundot.airport.common.core.domain.AjaxResult;
5
+import lombok.extern.slf4j.Slf4j;
6
+import org.springframework.cache.annotation.CacheEvict;
7
+import org.springframework.web.bind.annotation.PostMapping;
8
+import org.springframework.web.bind.annotation.RequestMapping;
9
+import org.springframework.web.bind.annotation.RestController;
10
+
11
+/**
12
+ * 统计缓存缓存管理控制器
13
+ */
14
+@Slf4j
15
+@RestController
16
+@RequestMapping("/statistics/cache")
17
+public class StatisticsCacheController extends BaseController {
18
+
19
+    /**
20
+     * 清理统计缓存
21
+     */
22
+    @PostMapping("/clearStatistics")
23
+    @CacheEvict(value = {
24
+            "statistics_data",
25
+            "statistics_list"
26
+    }, allEntries = true)
27
+    public AjaxResult clearStatisticsCache() {
28
+        return AjaxResult.success("统计缓存清理成功");
29
+    }
30
+
31
+}

+ 178 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/check/CheckCorrectionController.java

@@ -0,0 +1,178 @@
1
+package com.sundot.airport.web.controller.check;
2
+
3
+import java.util.Collections;
4
+import java.util.List;
5
+import java.util.stream.Collectors;
6
+import javax.servlet.http.HttpServletResponse;
7
+
8
+import cn.hutool.core.collection.CollUtil;
9
+import com.sundot.airport.common.core.domain.entity.SysRole;
10
+import com.sundot.airport.common.enums.RoleTypeEnum;
11
+import com.sundot.airport.common.utils.SecurityUtils;
12
+import org.springframework.security.access.prepost.PreAuthorize;
13
+import org.springframework.beans.factory.annotation.Autowired;
14
+import org.springframework.web.bind.annotation.GetMapping;
15
+import org.springframework.web.bind.annotation.PostMapping;
16
+import org.springframework.web.bind.annotation.PutMapping;
17
+import org.springframework.web.bind.annotation.DeleteMapping;
18
+import org.springframework.web.bind.annotation.PathVariable;
19
+import org.springframework.web.bind.annotation.RequestBody;
20
+import org.springframework.web.bind.annotation.RequestMapping;
21
+import org.springframework.web.bind.annotation.RestController;
22
+import com.sundot.airport.common.annotation.Log;
23
+import com.sundot.airport.common.core.controller.BaseController;
24
+import com.sundot.airport.common.core.domain.AjaxResult;
25
+import com.sundot.airport.common.enums.BusinessType;
26
+import com.sundot.airport.check.domain.CheckCorrection;
27
+import com.sundot.airport.check.service.ICheckCorrectionService;
28
+import com.sundot.airport.common.utils.poi.ExcelUtil;
29
+import com.sundot.airport.common.core.page.TableDataInfo;
30
+
31
+/**
32
+ * 问题整改Controller
33
+ *
34
+ * @author ruoyi
35
+ * @date 2025-09-08
36
+ */
37
+@RestController
38
+@RequestMapping("/check/checkCorrection")
39
+public class CheckCorrectionController extends BaseController {
40
+    @Autowired
41
+    private ICheckCorrectionService checkCorrectionService;
42
+
43
+    /**
44
+     * 查询问题整改列表
45
+     */
46
+    @PreAuthorize("@ss.hasPermi('check:checkCorrection:list')")
47
+    @GetMapping("/list")
48
+    public TableDataInfo list(CheckCorrection checkCorrection) {
49
+        startPage();
50
+        List<SysRole> roles = SecurityUtils.getLoginUser().getUser().getRoles();
51
+        if (CollUtil.isEmpty(roles)) {
52
+            return getDataTable(Collections.emptyList());
53
+        }
54
+        List<String> roleKeyList = roles.stream().map(SysRole::getRoleKey).collect(Collectors.toList());
55
+        if (roleKeyList.contains(RoleTypeEnum.admin.getCode())) {
56
+
57
+        } else if (roleKeyList.contains(RoleTypeEnum.test.getCode()) || roleKeyList.contains(RoleTypeEnum.zhijianke.getCode())) {
58
+
59
+        } else if (roleKeyList.contains(RoleTypeEnum.kezhang.getCode())) {
60
+            checkCorrection.setCheckedDepartmentId(SecurityUtils.getLoginUser().getDeptId());
61
+        } else if (roleKeyList.contains(RoleTypeEnum.banzuzhang.getCode())) {
62
+            return getDataTable(Collections.emptyList());
63
+        } else {
64
+            return getDataTable(Collections.emptyList());
65
+        }
66
+        List<CheckCorrection> list = checkCorrectionService.selectCheckCorrectionList(checkCorrection);
67
+        return getDataTable(list);
68
+    }
69
+
70
+    /**
71
+     * 导出问题整改列表
72
+     */
73
+    @PreAuthorize("@ss.hasPermi('check:checkCorrection:export')")
74
+    @Log(title = "问题整改", businessType = BusinessType.EXPORT)
75
+    @PostMapping("/export")
76
+    public void export(HttpServletResponse response, CheckCorrection checkCorrection) {
77
+        List<CheckCorrection> list = Collections.emptyList();
78
+        List<SysRole> roles = SecurityUtils.getLoginUser().getUser().getRoles();
79
+        if (CollUtil.isNotEmpty(roles)) {
80
+            List<String> roleKeyList = roles.stream().map(SysRole::getRoleKey).collect(Collectors.toList());
81
+            if (roleKeyList.contains(RoleTypeEnum.admin.getCode())) {
82
+                list = checkCorrectionService.selectCheckCorrectionList(checkCorrection);
83
+            } else if (roleKeyList.contains(RoleTypeEnum.test.getCode()) || roleKeyList.contains(RoleTypeEnum.zhijianke.getCode())) {
84
+                list = checkCorrectionService.selectCheckCorrectionList(checkCorrection);
85
+            } else if (roleKeyList.contains(RoleTypeEnum.kezhang.getCode())) {
86
+                checkCorrection.setCheckedDepartmentId(SecurityUtils.getLoginUser().getDeptId());
87
+                list = checkCorrectionService.selectCheckCorrectionList(checkCorrection);
88
+            } else if (roleKeyList.contains(RoleTypeEnum.banzuzhang.getCode())) {
89
+                list = Collections.emptyList();
90
+            } else {
91
+                list = Collections.emptyList();
92
+            }
93
+        }
94
+        ExcelUtil<CheckCorrection> util = new ExcelUtil<CheckCorrection>(CheckCorrection.class);
95
+        util.exportExcel(response, list, "问题整改数据");
96
+    }
97
+
98
+    /**
99
+     * 获取问题整改详细信息
100
+     */
101
+    @PreAuthorize("@ss.hasPermi('check:checkCorrection:query')")
102
+    @GetMapping(value = "/{id}")
103
+    public AjaxResult getInfo(@PathVariable("id") Long id) {
104
+        return success(checkCorrectionService.selectCheckCorrectionById(id));
105
+    }
106
+
107
+    /**
108
+     * 新增问题整改
109
+     */
110
+    @PreAuthorize("@ss.hasPermi('check:checkCorrection:add')")
111
+    @Log(title = "问题整改", businessType = BusinessType.INSERT)
112
+    @PostMapping
113
+    public AjaxResult add(@RequestBody CheckCorrection checkCorrection) {
114
+        checkCorrection.setCreateBy(getUsername());
115
+        return toAjax(checkCorrectionService.insertCheckCorrection(checkCorrection));
116
+    }
117
+
118
+    /**
119
+     * 修改问题整改
120
+     */
121
+    @PreAuthorize("@ss.hasPermi('check:checkCorrection:edit')")
122
+    @Log(title = "问题整改", businessType = BusinessType.UPDATE)
123
+    @PutMapping
124
+    public AjaxResult edit(@RequestBody CheckCorrection checkCorrection) {
125
+        checkCorrection.setUpdateBy(getUsername());
126
+        if (CollUtil.isNotEmpty(checkCorrection.getCheckProjectItemList())) {
127
+            checkCorrection.getCheckProjectItemList().forEach(checkProjectItem -> {
128
+                checkProjectItem.setUpdateBy(getUsername());
129
+                if (CollUtil.isNotEmpty(checkProjectItem.getCheckUserList())) {
130
+                    checkProjectItem.getCheckUserList().forEach(checkUser -> {
131
+                        checkUser.setUpdateBy(getUsername());
132
+                    });
133
+                }
134
+            });
135
+        }
136
+        if (CollUtil.isNotEmpty(checkCorrection.getBaseAttachmentList())) {
137
+            checkCorrection.getBaseAttachmentList().forEach(baseAttachment -> {
138
+                baseAttachment.setUpdateBy(getUsername());
139
+            });
140
+        }
141
+        if (CollUtil.isNotEmpty(checkCorrection.getCheckRecordBaseAttachmentList())) {
142
+            checkCorrection.getCheckRecordBaseAttachmentList().forEach(baseAttachment -> {
143
+                baseAttachment.setUpdateBy(getUsername());
144
+            });
145
+        }
146
+        return toAjax(checkCorrectionService.updateCheckCorrection(checkCorrection));
147
+    }
148
+
149
+    /**
150
+     * 删除问题整改
151
+     */
152
+    @PreAuthorize("@ss.hasPermi('check:checkCorrection:remove')")
153
+    @Log(title = "问题整改", businessType = BusinessType.DELETE)
154
+    @DeleteMapping("/{ids}")
155
+    public AjaxResult remove(@PathVariable Long[] ids) {
156
+        return toAjax(checkCorrectionService.deleteCheckCorrectionByIds(ids));
157
+    }
158
+
159
+    /**
160
+     * 审批任务(同意)
161
+     */
162
+    @PreAuthorize("@ss.hasPermi('check:checkCorrection:edit')")
163
+    @Log(title = "问题整改", businessType = BusinessType.UPDATE)
164
+    @PostMapping("/approveTask")
165
+    public AjaxResult approveTask(@RequestBody CheckCorrection checkCorrection) {
166
+        return success(checkCorrectionService.approveTask(checkCorrection));
167
+    }
168
+
169
+    /**
170
+     * 审批任务(驳回)
171
+     */
172
+    @PreAuthorize("@ss.hasPermi('check:checkCorrection:edit')")
173
+    @Log(title = "问题整改", businessType = BusinessType.UPDATE)
174
+    @PostMapping("/rejectTask")
175
+    public AjaxResult rejectTask(@RequestBody CheckCorrection checkCorrection) {
176
+        return success(checkCorrectionService.rejectTask(checkCorrection));
177
+    }
178
+}

+ 196 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/check/CheckLargeScreenController.java

@@ -0,0 +1,196 @@
1
+package com.sundot.airport.web.controller.check;
2
+
3
+import com.sundot.airport.check.domain.CheckLargeScreenCommonDto;
4
+import com.sundot.airport.check.domain.CheckLargeScreenCorrectionDto;
5
+import com.sundot.airport.check.domain.CheckLargeScreenCorrectionPortraitDto;
6
+import com.sundot.airport.check.domain.CheckLargeScreenCorrectionQueryParamDto;
7
+import com.sundot.airport.check.domain.CheckLargeScreenInspectionExecuteDto;
8
+import com.sundot.airport.check.domain.CheckLargeScreenPlanOverviewDto;
9
+import com.sundot.airport.check.domain.CheckLargeScreenPlanQueryParamDto;
10
+import com.sundot.airport.check.domain.CheckLargeScreenProblemDto;
11
+import com.sundot.airport.check.domain.CheckLargeScreenProblemQueryParamDto;
12
+import com.sundot.airport.check.domain.CheckLargeScreenProblemTrendDto;
13
+import com.sundot.airport.check.domain.CheckTask;
14
+import com.sundot.airport.check.service.ICheckLargeScreenService;
15
+import com.sundot.airport.common.core.controller.BaseController;
16
+import com.sundot.airport.common.core.domain.AjaxResult;
17
+import com.sundot.airport.common.core.domain.BaseLargeScreenQueryParamDto;
18
+import org.springframework.beans.factory.annotation.Autowired;
19
+import org.springframework.cache.annotation.Cacheable;
20
+import org.springframework.security.access.prepost.PreAuthorize;
21
+import org.springframework.web.bind.annotation.GetMapping;
22
+import org.springframework.web.bind.annotation.RequestMapping;
23
+import org.springframework.web.bind.annotation.RestController;
24
+
25
+import java.util.List;
26
+
27
+/**
28
+ * 巡检大屏Controller
29
+ *
30
+ * @author ruoyi
31
+ * @date 2025-09-07
32
+ */
33
+@RestController
34
+@RequestMapping("/check/largeScreen")
35
+public class CheckLargeScreenController extends BaseController {
36
+
37
+    @Autowired
38
+    private ICheckLargeScreenService checkLargeScreenService;
39
+
40
+    /**
41
+     * 巡检计划-计划安排总览
42
+     */
43
+    @PreAuthorize("@ss.hasPermi('check:checkTask:list')")
44
+    @GetMapping("/planOverview")
45
+    public AjaxResult planOverview(CheckLargeScreenPlanQueryParamDto dto) {
46
+        List<CheckLargeScreenPlanOverviewDto> result = checkLargeScreenService.planOverview(dto);
47
+        return success(result);
48
+    }
49
+
50
+    /**
51
+     * 巡检计划-日常任务检查指标累积分布
52
+     */
53
+    @PreAuthorize("@ss.hasPermi('check:checkTask:list')")
54
+    @GetMapping("/planDistribution")
55
+    public AjaxResult planDistribution(CheckLargeScreenPlanQueryParamDto dto) {
56
+        List<CheckLargeScreenCommonDto> result = checkLargeScreenService.planDistribution(dto);
57
+        return success(result);
58
+    }
59
+
60
+    /**
61
+     * 巡检计划-任务明细统计表
62
+     */
63
+    @PreAuthorize("@ss.hasPermi('check:checkTask:list')")
64
+    @GetMapping("/planStatistics")
65
+    public AjaxResult planStatistics(CheckLargeScreenPlanQueryParamDto dto) {
66
+        List<CheckTask> result = checkLargeScreenService.planStatistics(dto);
67
+        return success(result);
68
+    }
69
+
70
+    /**
71
+     * 问题发现-总体问题分布
72
+     */
73
+    @PreAuthorize("@ss.hasPermi('check:checkTask:list')")
74
+    @GetMapping("/problemDistribution")
75
+    public AjaxResult problemDistribution(CheckLargeScreenProblemQueryParamDto dto) {
76
+        List<CheckLargeScreenProblemDto> result = checkLargeScreenService.problemDistribution(dto);
77
+        return success(result);
78
+    }
79
+
80
+    /**
81
+     * 问题发现-问题分布对比
82
+     */
83
+    @PreAuthorize("@ss.hasPermi('check:checkTask:list')")
84
+    @GetMapping("/problemComparison")
85
+    public AjaxResult problemComparison(CheckLargeScreenProblemQueryParamDto dto) {
86
+        List<CheckLargeScreenProblemDto> result = checkLargeScreenService.problemComparison(dto);
87
+        return success(result);
88
+    }
89
+
90
+    /**
91
+     * 问题发现-通道面貌-问题对比
92
+     */
93
+    @PreAuthorize("@ss.hasPermi('check:checkTask:list')")
94
+    @GetMapping("/problemComparisonTwo")
95
+    public AjaxResult problemComparisonTwo(CheckLargeScreenProblemQueryParamDto dto) {
96
+        List<CheckLargeScreenProblemDto> result = checkLargeScreenService.problemComparisonTwo(dto);
97
+        return success(result);
98
+    }
99
+
100
+    /**
101
+     * 问题发现-通道面貌-问题趋势
102
+     */
103
+    @PreAuthorize("@ss.hasPermi('check:checkTask:list')")
104
+    @GetMapping("/problemTrend")
105
+    public AjaxResult problemTrend(CheckLargeScreenProblemQueryParamDto dto) {
106
+        List<CheckLargeScreenProblemTrendDto> result = checkLargeScreenService.problemTrend(dto);
107
+        return success(result);
108
+    }
109
+
110
+    /**
111
+     * 问题整改-整改状态总计
112
+     */
113
+    @PreAuthorize("@ss.hasPermi('check:checkTask:list')")
114
+    @GetMapping("/correction")
115
+    public AjaxResult correction(CheckLargeScreenCorrectionQueryParamDto dto) {
116
+        CheckLargeScreenCorrectionDto result = checkLargeScreenService.correction(dto);
117
+        return success(result);
118
+    }
119
+
120
+    /**
121
+     * 问题整改-整改状态分布-科级对比
122
+     */
123
+    @PreAuthorize("@ss.hasPermi('check:checkTask:list')")
124
+    @GetMapping("/correctionDistribution")
125
+    public AjaxResult correctionDistribution(CheckLargeScreenCorrectionQueryParamDto dto) {
126
+        List<CheckLargeScreenCorrectionDto> result = checkLargeScreenService.correctionDistribution(dto);
127
+        return success(result);
128
+    }
129
+
130
+    /**
131
+     * 巡检执行
132
+     */
133
+    @PreAuthorize("@ss.hasPermi('check:checkTask:list')")
134
+    @GetMapping("/inspectionExecute")
135
+    public AjaxResult inspectionExecute(BaseLargeScreenQueryParamDto dto) {
136
+        List<CheckLargeScreenInspectionExecuteDto> result = checkLargeScreenService.inspectionExecute(dto);
137
+        return success(result);
138
+    }
139
+
140
+    /**
141
+     * 巡检画像
142
+     */
143
+    @Cacheable(
144
+            value = "statistics_data",
145
+            keyGenerator = "statisticsKeyGenerator",
146
+            unless = "!T(com.sundot.airport.common.cache.StatisticsCacheConditionUtil).isSuccess(#result) ||T(com.sundot.airport.common.cache.StatisticsCacheConditionUtil).isEmptyData(#result)"
147
+    )
148
+    @GetMapping("/portrait")
149
+    public AjaxResult portrait(CheckLargeScreenCorrectionQueryParamDto dto) {
150
+        CheckLargeScreenCorrectionPortraitDto result = checkLargeScreenService.portrait(dto);
151
+        return success(result);
152
+    }
153
+
154
+    /**
155
+     * 工作画像-管理推动-检查单
156
+     */
157
+    @Cacheable(
158
+            value = "statistics_list",
159
+            keyGenerator = "statisticsKeyGenerator",
160
+            unless = "!T(com.sundot.airport.common.cache.StatisticsCacheConditionUtil).isSuccess(#result) ||T(com.sundot.airport.common.cache.StatisticsCacheConditionUtil).isEmptyData(#result)"
161
+    )
162
+    @GetMapping("/managementPromotionRecord")
163
+    public AjaxResult managementPromotionRecord(CheckLargeScreenCorrectionQueryParamDto dto) {
164
+        List<CheckLargeScreenPlanOverviewDto> result = checkLargeScreenService.managementPromotionRecord(dto);
165
+        return success(result);
166
+    }
167
+
168
+    /**
169
+     * 工作画像-管理推动-整改单
170
+     */
171
+    @Cacheable(
172
+            value = "statistics_data",
173
+            keyGenerator = "statisticsKeyGenerator",
174
+            unless = "!T(com.sundot.airport.common.cache.StatisticsCacheConditionUtil).isSuccess(#result) ||T(com.sundot.airport.common.cache.StatisticsCacheConditionUtil).isEmptyData(#result)"
175
+    )
176
+    @GetMapping("/managementPromotionCorrection")
177
+    public AjaxResult managementPromotionCorrection(CheckLargeScreenCorrectionQueryParamDto dto) {
178
+        CheckLargeScreenCorrectionDto result = checkLargeScreenService.managementPromotionCorrection(dto);
179
+        return success(result);
180
+    }
181
+
182
+    /**
183
+     * 工作画像-工作产出-巡检问题统计图
184
+     */
185
+    @Cacheable(
186
+            value = "statistics_list",
187
+            keyGenerator = "statisticsKeyGenerator",
188
+            unless = "!T(com.sundot.airport.common.cache.StatisticsCacheConditionUtil).isSuccess(#result) ||T(com.sundot.airport.common.cache.StatisticsCacheConditionUtil).isEmptyData(#result)"
189
+    )
190
+    @GetMapping("/workOutputCheck")
191
+    public AjaxResult workOutputCheck(CheckLargeScreenCorrectionQueryParamDto dto) {
192
+        List<CheckLargeScreenCommonDto> result = checkLargeScreenService.workOutputCheck(dto);
193
+        return success(result);
194
+    }
195
+
196
+}

+ 98 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/check/CheckProjectItemController.java

@@ -0,0 +1,98 @@
1
+package com.sundot.airport.web.controller.check;
2
+
3
+import java.util.List;
4
+import javax.servlet.http.HttpServletResponse;
5
+
6
+import org.springframework.security.access.prepost.PreAuthorize;
7
+import org.springframework.beans.factory.annotation.Autowired;
8
+import org.springframework.web.bind.annotation.GetMapping;
9
+import org.springframework.web.bind.annotation.PostMapping;
10
+import org.springframework.web.bind.annotation.PutMapping;
11
+import org.springframework.web.bind.annotation.DeleteMapping;
12
+import org.springframework.web.bind.annotation.PathVariable;
13
+import org.springframework.web.bind.annotation.RequestBody;
14
+import org.springframework.web.bind.annotation.RequestMapping;
15
+import org.springframework.web.bind.annotation.RestController;
16
+import com.sundot.airport.common.annotation.Log;
17
+import com.sundot.airport.common.core.controller.BaseController;
18
+import com.sundot.airport.common.core.domain.AjaxResult;
19
+import com.sundot.airport.common.enums.BusinessType;
20
+import com.sundot.airport.check.domain.CheckProjectItem;
21
+import com.sundot.airport.check.service.ICheckProjectItemService;
22
+import com.sundot.airport.common.utils.poi.ExcelUtil;
23
+import com.sundot.airport.common.core.page.TableDataInfo;
24
+
25
+/**
26
+ * 检查单和检查记录和问题整改的检查项明细Controller
27
+ *
28
+ * @author ruoyi
29
+ * @date 2025-07-11
30
+ */
31
+@RestController
32
+@RequestMapping("/check/projectItem")
33
+public class CheckProjectItemController extends BaseController {
34
+    @Autowired
35
+    private ICheckProjectItemService checkProjectItemService;
36
+
37
+    /**
38
+     * 查询检查单和检查记录和问题整改的检查项明细列表
39
+     */
40
+    @PreAuthorize("@ss.hasPermi('check:projectItem:list')")
41
+    @GetMapping("/list")
42
+    public TableDataInfo list(CheckProjectItem checkProjectItem) {
43
+        startPage();
44
+        List<CheckProjectItem> list = checkProjectItemService.selectCheckProjectItemList(checkProjectItem);
45
+        return getDataTable(list);
46
+    }
47
+
48
+    /**
49
+     * 导出检查单和检查记录和问题整改的检查项明细列表
50
+     */
51
+    @PreAuthorize("@ss.hasPermi('check:projectItem:export')")
52
+    @Log(title = "检查单和检查记录和问题整改的检查项明细", businessType = BusinessType.EXPORT)
53
+    @PostMapping("/export")
54
+    public void export(HttpServletResponse response, CheckProjectItem checkProjectItem) {
55
+        List<CheckProjectItem> list = checkProjectItemService.selectCheckProjectItemList(checkProjectItem);
56
+        ExcelUtil<CheckProjectItem> util = new ExcelUtil<CheckProjectItem>(CheckProjectItem.class);
57
+        util.exportExcel(response, list, "检查单和检查记录和问题整改的检查项明细数据");
58
+    }
59
+
60
+    /**
61
+     * 获取检查单和检查记录和问题整改的检查项明细详细信息
62
+     */
63
+    @PreAuthorize("@ss.hasPermi('check:projectItem:query')")
64
+    @GetMapping(value = "/{id}")
65
+    public AjaxResult getInfo(@PathVariable("id") Long id) {
66
+        return success(checkProjectItemService.selectCheckProjectItemById(id));
67
+    }
68
+
69
+    /**
70
+     * 新增检查单和检查记录和问题整改的检查项明细
71
+     */
72
+    @PreAuthorize("@ss.hasPermi('check:projectItem:add')")
73
+    @Log(title = "检查单和检查记录和问题整改的检查项明细", businessType = BusinessType.INSERT)
74
+    @PostMapping
75
+    public AjaxResult add(@RequestBody CheckProjectItem checkProjectItem) {
76
+        return toAjax(checkProjectItemService.insertCheckProjectItem(checkProjectItem));
77
+    }
78
+
79
+    /**
80
+     * 修改检查单和检查记录和问题整改的检查项明细
81
+     */
82
+    @PreAuthorize("@ss.hasPermi('check:projectItem:edit')")
83
+    @Log(title = "检查单和检查记录和问题整改的检查项明细", businessType = BusinessType.UPDATE)
84
+    @PutMapping
85
+    public AjaxResult edit(@RequestBody CheckProjectItem checkProjectItem) {
86
+        return toAjax(checkProjectItemService.updateCheckProjectItem(checkProjectItem));
87
+    }
88
+
89
+    /**
90
+     * 删除检查单和检查记录和问题整改的检查项明细
91
+     */
92
+    @PreAuthorize("@ss.hasPermi('check:projectItem:remove')")
93
+    @Log(title = "检查单和检查记录和问题整改的检查项明细", businessType = BusinessType.DELETE)
94
+    @DeleteMapping("/{ids}")
95
+    public AjaxResult remove(@PathVariable Long[] ids) {
96
+        return toAjax(checkProjectItemService.deleteCheckProjectItemByIds(ids));
97
+    }
98
+}

+ 180 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/check/CheckRecordController.java

@@ -0,0 +1,180 @@
1
+package com.sundot.airport.web.controller.check;
2
+
3
+import java.util.Collections;
4
+import java.util.List;
5
+import java.util.stream.Collectors;
6
+import javax.servlet.http.HttpServletResponse;
7
+
8
+import cn.hutool.core.collection.CollUtil;
9
+import cn.hutool.core.util.StrUtil;
10
+import com.sundot.airport.common.core.domain.entity.SysRole;
11
+import com.sundot.airport.common.core.domain.entity.SysUser;
12
+import com.sundot.airport.common.enums.CheckRecordStatusEnum;
13
+import com.sundot.airport.common.enums.RoleTypeEnum;
14
+import com.sundot.airport.common.enums.SourceTypeEnum;
15
+import com.sundot.airport.common.utils.SecurityUtils;
16
+import com.sundot.airport.system.service.ISysUserService;
17
+import org.springframework.security.access.prepost.PreAuthorize;
18
+import org.springframework.beans.factory.annotation.Autowired;
19
+import org.springframework.web.bind.annotation.GetMapping;
20
+import org.springframework.web.bind.annotation.PostMapping;
21
+import org.springframework.web.bind.annotation.PutMapping;
22
+import org.springframework.web.bind.annotation.DeleteMapping;
23
+import org.springframework.web.bind.annotation.PathVariable;
24
+import org.springframework.web.bind.annotation.RequestBody;
25
+import org.springframework.web.bind.annotation.RequestHeader;
26
+import org.springframework.web.bind.annotation.RequestMapping;
27
+import org.springframework.web.bind.annotation.RestController;
28
+import com.sundot.airport.common.annotation.Log;
29
+import com.sundot.airport.common.core.controller.BaseController;
30
+import com.sundot.airport.common.core.domain.AjaxResult;
31
+import com.sundot.airport.common.enums.BusinessType;
32
+import com.sundot.airport.check.domain.CheckRecord;
33
+import com.sundot.airport.check.service.ICheckRecordService;
34
+import com.sundot.airport.common.utils.poi.ExcelUtil;
35
+import com.sundot.airport.common.core.page.TableDataInfo;
36
+
37
+/**
38
+ * 检查记录Controller
39
+ *
40
+ * @author ruoyi
41
+ * @date 2025-07-11
42
+ */
43
+@RestController
44
+@RequestMapping("/check/checkRecord")
45
+public class CheckRecordController extends BaseController {
46
+    @Autowired
47
+    private ICheckRecordService checkRecordService;
48
+    @Autowired
49
+    private ISysUserService sysUserService;
50
+
51
+    /**
52
+     * 查询检查记录列表
53
+     */
54
+    @PreAuthorize("@ss.hasPermi('check:checkRecord:list')")
55
+    @GetMapping("/list")
56
+    public TableDataInfo list(CheckRecord checkRecord, @RequestHeader(value = "X-Request-Source", defaultValue = "mobile") String source) {
57
+        List<CheckRecord> list;
58
+        List<SysRole> roles = SecurityUtils.getLoginUser().getUser().getRoles();
59
+        if (CollUtil.isEmpty(roles)) {
60
+            return getDataTable(Collections.emptyList());
61
+        }
62
+        List<String> roleKeyList = roles.stream().map(SysRole::getRoleKey).collect(Collectors.toList());
63
+        if (StrUtil.equals(SourceTypeEnum.mobile.getCode(), source)) {
64
+            startPage();
65
+            list = checkRecordService.selectCheckRecordList(checkRecord);
66
+        } else if (StrUtil.equals(SourceTypeEnum.web.getCode(), source)) {
67
+            if (roleKeyList.contains(RoleTypeEnum.admin.getCode())) {
68
+
69
+            } else if (roleKeyList.contains(RoleTypeEnum.test.getCode()) || roleKeyList.contains(RoleTypeEnum.zhijianke.getCode())) {
70
+
71
+            } else if (roleKeyList.contains(RoleTypeEnum.kezhang.getCode())) {
72
+                Long deptId = SecurityUtils.getLoginUser().getDeptId();
73
+                List<SysUser> sysUserList = sysUserService.selectUserByDeptId(deptId);
74
+                if (CollUtil.isEmpty(sysUserList)) {
75
+                    return getDataTable(Collections.emptyList());
76
+                }
77
+                List<Long> userIdList = sysUserList.stream().map(SysUser::getUserId).distinct().collect(Collectors.toList());
78
+                checkRecord.setQueryCheckerIdList(userIdList);
79
+            } else if (roleKeyList.contains(RoleTypeEnum.banzuzhang.getCode())) {
80
+                return getDataTable(Collections.emptyList());
81
+            } else {
82
+                return getDataTable(Collections.emptyList());
83
+            }
84
+            startPage();
85
+            list = checkRecordService.selectCheckRecordList(checkRecord);
86
+        } else {
87
+            return getDataTable(Collections.emptyList());
88
+        }
89
+        return getDataTable(list);
90
+    }
91
+
92
+    /**
93
+     * 导出检查记录列表
94
+     */
95
+    @PreAuthorize("@ss.hasPermi('check:checkRecord:export')")
96
+    @Log(title = "检查记录", businessType = BusinessType.EXPORT)
97
+    @PostMapping("/export")
98
+    public void export(HttpServletResponse response, CheckRecord checkRecord) {
99
+        List<CheckRecord> list = Collections.emptyList();
100
+        List<SysRole> roles = SecurityUtils.getLoginUser().getUser().getRoles();
101
+        if (CollUtil.isNotEmpty(roles)) {
102
+            List<String> roleKeyList = roles.stream().map(SysRole::getRoleKey).collect(Collectors.toList());
103
+            if (roleKeyList.contains(RoleTypeEnum.admin.getCode())) {
104
+                list = checkRecordService.selectCheckRecordList(checkRecord);
105
+            } else if (roleKeyList.contains(RoleTypeEnum.test.getCode()) || roleKeyList.contains(RoleTypeEnum.zhijianke.getCode())) {
106
+                list = checkRecordService.selectCheckRecordList(checkRecord);
107
+            } else if (roleKeyList.contains(RoleTypeEnum.kezhang.getCode())) {
108
+                Long deptId = SecurityUtils.getLoginUser().getDeptId();
109
+                List<SysUser> sysUserList = sysUserService.selectUserByDeptId(deptId);
110
+                if (CollUtil.isNotEmpty(sysUserList)) {
111
+                    List<Long> userIdList = sysUserList.stream().map(SysUser::getUserId).distinct().collect(Collectors.toList());
112
+                    checkRecord.setQueryCheckerIdList(userIdList);
113
+                    list = checkRecordService.selectCheckRecordList(checkRecord);
114
+                }
115
+            } else if (roleKeyList.contains(RoleTypeEnum.banzuzhang.getCode())) {
116
+                list = Collections.emptyList();
117
+            } else {
118
+                list = Collections.emptyList();
119
+            }
120
+        }
121
+        ExcelUtil<CheckRecord> util = new ExcelUtil<CheckRecord>(CheckRecord.class);
122
+        util.exportExcel(response, list, "检查记录数据");
123
+    }
124
+
125
+    /**
126
+     * 获取检查记录详细信息
127
+     */
128
+    @PreAuthorize("@ss.hasPermi('check:checkRecord:query')")
129
+    @GetMapping(value = "/{id}")
130
+    public AjaxResult getInfo(@PathVariable("id") Long id) {
131
+        return success(checkRecordService.selectCheckRecordById(id));
132
+    }
133
+
134
+    /**
135
+     * 新增检查记录
136
+     */
137
+    @PreAuthorize("@ss.hasPermi('check:checkRecord:add')")
138
+    @Log(title = "检查记录", businessType = BusinessType.INSERT)
139
+    @PostMapping("/add")
140
+    public AjaxResult add(@RequestBody CheckRecord checkRecord) {
141
+        checkRecord.setCreateBy(getUsername());
142
+        checkRecord.setCheckRecordStatus(CheckRecordStatusEnum.FORMAL.getCode());
143
+        checkRecord.setCheckRecordStatusDesc(CheckRecordStatusEnum.FORMAL.getDesc());
144
+        return toAjax(checkRecordService.insertOrUpdateCheckRecord(checkRecord, true));
145
+    }
146
+
147
+    /**
148
+     * 新增草稿检查记录
149
+     */
150
+    @PreAuthorize("@ss.hasPermi('check:checkRecord:add')")
151
+    @Log(title = "草稿检查记录", businessType = BusinessType.INSERT)
152
+    @PostMapping("/draft")
153
+    public AjaxResult draft(@RequestBody CheckRecord checkRecord) {
154
+        checkRecord.setCreateBy(getUsername());
155
+        checkRecord.setCheckRecordStatus(CheckRecordStatusEnum.DRAFT.getCode());
156
+        checkRecord.setCheckRecordStatusDesc(CheckRecordStatusEnum.DRAFT.getDesc());
157
+        return toAjax(checkRecordService.insertOrUpdateCheckRecord(checkRecord, false));
158
+    }
159
+
160
+    /**
161
+     * 修改检查记录
162
+     */
163
+    @PreAuthorize("@ss.hasPermi('check:checkRecord:edit')")
164
+    @Log(title = "检查记录", businessType = BusinessType.UPDATE)
165
+    @PutMapping
166
+    public AjaxResult edit(@RequestBody CheckRecord checkRecord) {
167
+        checkRecord.setUpdateBy(getUsername());
168
+        return toAjax(checkRecordService.updateCheckRecord(checkRecord));
169
+    }
170
+
171
+    /**
172
+     * 删除检查记录
173
+     */
174
+    @PreAuthorize("@ss.hasPermi('check:checkRecord:remove')")
175
+    @Log(title = "检查记录", businessType = BusinessType.DELETE)
176
+    @DeleteMapping("/{ids}")
177
+    public AjaxResult remove(@PathVariable Long[] ids) {
178
+        return toAjax(checkRecordService.deleteCheckRecordByIds(ids));
179
+    }
180
+}

+ 198 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/check/CheckTaskController.java

@@ -0,0 +1,198 @@
1
+package com.sundot.airport.web.controller.check;
2
+
3
+import java.util.Collections;
4
+import java.util.List;
5
+import java.util.stream.Collectors;
6
+import javax.servlet.http.HttpServletResponse;
7
+
8
+import cn.hutool.core.collection.CollUtil;
9
+import cn.hutool.core.util.StrUtil;
10
+import com.sundot.airport.common.core.domain.entity.SysRole;
11
+import com.sundot.airport.common.enums.CheckLevelEnum;
12
+import com.sundot.airport.common.enums.CheckTaskStatusEnum;
13
+import com.sundot.airport.common.enums.RoleTypeEnum;
14
+import com.sundot.airport.common.enums.SourceTypeEnum;
15
+import com.sundot.airport.common.utils.SecurityUtils;
16
+import org.springframework.security.access.prepost.PreAuthorize;
17
+import org.springframework.beans.factory.annotation.Autowired;
18
+import org.springframework.web.bind.annotation.GetMapping;
19
+import org.springframework.web.bind.annotation.PostMapping;
20
+import org.springframework.web.bind.annotation.PutMapping;
21
+import org.springframework.web.bind.annotation.DeleteMapping;
22
+import org.springframework.web.bind.annotation.PathVariable;
23
+import org.springframework.web.bind.annotation.RequestBody;
24
+import org.springframework.web.bind.annotation.RequestHeader;
25
+import org.springframework.web.bind.annotation.RequestMapping;
26
+import org.springframework.web.bind.annotation.RestController;
27
+import com.sundot.airport.common.annotation.Log;
28
+import com.sundot.airport.common.core.controller.BaseController;
29
+import com.sundot.airport.common.core.domain.AjaxResult;
30
+import com.sundot.airport.common.enums.BusinessType;
31
+import com.sundot.airport.check.domain.CheckTask;
32
+import com.sundot.airport.check.service.ICheckTaskService;
33
+import com.sundot.airport.common.utils.poi.ExcelUtil;
34
+import com.sundot.airport.common.core.page.TableDataInfo;
35
+
36
+/**
37
+ * 检查任务Controller
38
+ *
39
+ * @author ruoyi
40
+ * @date 2025-09-07
41
+ */
42
+@RestController
43
+@RequestMapping("/check/checkTask")
44
+public class CheckTaskController extends BaseController {
45
+    @Autowired
46
+    private ICheckTaskService checkTaskService;
47
+
48
+    /**
49
+     * 查询检查任务列表
50
+     */
51
+    @PreAuthorize("@ss.hasPermi('check:checkTask:list')")
52
+    @GetMapping("/list")
53
+    public TableDataInfo list(CheckTask checkTask, @RequestHeader(value = "X-Request-Source", defaultValue = "mobile") String source) {
54
+        startPage();
55
+        List<CheckTask> list;
56
+        List<SysRole> roles = SecurityUtils.getLoginUser().getUser().getRoles();
57
+        if (CollUtil.isEmpty(roles)) {
58
+            return getDataTable(Collections.emptyList());
59
+        }
60
+        List<String> roleKeyList = roles.stream().map(SysRole::getRoleKey).collect(Collectors.toList());
61
+        if (StrUtil.equals(SourceTypeEnum.mobile.getCode(), source)) {
62
+            if (roleKeyList.contains(RoleTypeEnum.admin.getCode())) {
63
+                checkTask.setCheckLevel(null);
64
+            } else if (roleKeyList.contains(RoleTypeEnum.test.getCode()) || roleKeyList.contains(RoleTypeEnum.zhijianke.getCode())) {
65
+                checkTask.setCheckLevel(CheckLevelEnum.STATION_LEVEL.getCode());
66
+            } else if (roleKeyList.contains(RoleTypeEnum.kezhang.getCode())) {
67
+                checkTask.setCheckLevel(CheckLevelEnum.DEPARTMENT_LEVEL.getCode());
68
+                checkTask.setSelfCheckDeptId(SecurityUtils.getLoginUser().getUser().getDept().getDeptId());
69
+            } else if (roleKeyList.contains(RoleTypeEnum.banzuzhang.getCode())) {
70
+                checkTask.setCheckLevel(CheckLevelEnum.TEAM_LEVEL.getCode());
71
+                checkTask.setSelfCheckDeptId(SecurityUtils.getLoginUser().getUser().getDept().getParentId());
72
+            } else {
73
+                return getDataTable(Collections.emptyList());
74
+            }
75
+            list = checkTaskService.selectCheckTaskList(checkTask);
76
+        } else if (StrUtil.equals(SourceTypeEnum.web.getCode(), source)) {
77
+            if (roleKeyList.contains(RoleTypeEnum.admin.getCode())) {
78
+                list = checkTaskService.selectCheckTaskList(checkTask);
79
+            } else if (roleKeyList.contains(RoleTypeEnum.test.getCode()) || roleKeyList.contains(RoleTypeEnum.zhijianke.getCode())) {
80
+                list = checkTaskService.selectCheckTaskList(checkTask);
81
+            } else if (roleKeyList.contains(RoleTypeEnum.kezhang.getCode())) {
82
+                checkTask.setSelfCheckDeptId(SecurityUtils.getLoginUser().getUser().getDept().getDeptId());
83
+                list = checkTaskService.selectCheckTaskListWeb(checkTask);
84
+            } else if (roleKeyList.contains(RoleTypeEnum.banzuzhang.getCode())) {
85
+                return getDataTable(Collections.emptyList());
86
+            } else {
87
+                return getDataTable(Collections.emptyList());
88
+            }
89
+        } else {
90
+            return getDataTable(Collections.emptyList());
91
+        }
92
+        return getDataTable(list);
93
+    }
94
+
95
+    /**
96
+     * 导出检查任务列表
97
+     */
98
+    @PreAuthorize("@ss.hasPermi('check:checkTask:export')")
99
+    @Log(title = "检查任务", businessType = BusinessType.EXPORT)
100
+    @PostMapping("/export")
101
+    public void export(HttpServletResponse response, CheckTask checkTask) {
102
+        List<CheckTask> list = Collections.emptyList();
103
+        List<SysRole> roles = SecurityUtils.getLoginUser().getUser().getRoles();
104
+        if (CollUtil.isNotEmpty(roles)) {
105
+            List<String> roleKeyList = roles.stream().map(SysRole::getRoleKey).collect(Collectors.toList());
106
+            if (roleKeyList.contains(RoleTypeEnum.admin.getCode())) {
107
+                list = checkTaskService.selectCheckTaskList(checkTask);
108
+            } else if (roleKeyList.contains(RoleTypeEnum.test.getCode()) || roleKeyList.contains(RoleTypeEnum.zhijianke.getCode())) {
109
+                list = checkTaskService.selectCheckTaskList(checkTask);
110
+            } else if (roleKeyList.contains(RoleTypeEnum.kezhang.getCode())) {
111
+                checkTask.setSelfCheckDeptId(SecurityUtils.getLoginUser().getDeptId());
112
+                list = checkTaskService.selectCheckTaskListWeb(checkTask);
113
+            } else if (roleKeyList.contains(RoleTypeEnum.banzuzhang.getCode())) {
114
+                list = Collections.emptyList();
115
+            } else {
116
+                list = Collections.emptyList();
117
+            }
118
+        }
119
+        ExcelUtil<CheckTask> util = new ExcelUtil<CheckTask>(CheckTask.class);
120
+        util.exportExcel(response, list, "检查任务数据");
121
+    }
122
+
123
+    /**
124
+     * 获取检查任务详细信息
125
+     */
126
+    @PreAuthorize("@ss.hasPermi('check:checkTask:query')")
127
+    @GetMapping(value = "/{id}")
128
+    public AjaxResult getInfo(@PathVariable("id") Long id) {
129
+        return success(checkTaskService.selectCheckTaskById(id));
130
+    }
131
+
132
+    /**
133
+     * 新增检查任务
134
+     */
135
+    @PreAuthorize("@ss.hasPermi('check:checkTask:add')")
136
+    @Log(title = "检查任务", businessType = BusinessType.INSERT)
137
+    @PostMapping
138
+    public AjaxResult add(@RequestBody CheckTask checkTask) {
139
+        checkTask.setCreateId(getUserId());
140
+        checkTask.setCreateBy(getUsername());
141
+        return toAjax(checkTaskService.insertCheckTask(checkTask));
142
+    }
143
+
144
+    /**
145
+     * 修改检查任务
146
+     */
147
+    @PreAuthorize("@ss.hasPermi('check:checkTask:edit')")
148
+    @Log(title = "检查任务", businessType = BusinessType.UPDATE)
149
+    @PutMapping
150
+    public AjaxResult edit(@RequestBody CheckTask checkTask) {
151
+        checkTask.setUpdateId(getUserId());
152
+        checkTask.setUpdateBy(getUsername());
153
+        return toAjax(checkTaskService.updateCheckTask(checkTask));
154
+    }
155
+
156
+    /**
157
+     * 删除检查任务
158
+     */
159
+    @PreAuthorize("@ss.hasPermi('check:checkTask:remove')")
160
+    @Log(title = "检查任务", businessType = BusinessType.DELETE)
161
+    @DeleteMapping("/{ids}")
162
+    public AjaxResult remove(@PathVariable Long[] ids) {
163
+        return toAjax(checkTaskService.deleteCheckTaskByIds(ids));
164
+    }
165
+
166
+    /**
167
+     * 查询检查任务未读数量
168
+     */
169
+    @PreAuthorize("@ss.hasPermi('check:checkTask:list')")
170
+    @GetMapping("/unReadNum")
171
+    public AjaxResult unReadNum() {
172
+        List<SysRole> roles = SecurityUtils.getLoginUser().getUser().getRoles();
173
+        if (CollUtil.isEmpty(roles)) {
174
+            return success(0);
175
+        }
176
+        CheckTask checkTask = new CheckTask();
177
+        checkTask.setStatus(CheckTaskStatusEnum.IN_PROGRESS.getCode());
178
+        List<String> roleKeyList = roles.stream().map(SysRole::getRoleKey).collect(Collectors.toList());
179
+        if (roleKeyList.contains(RoleTypeEnum.admin.getCode())) {
180
+            checkTask.setCheckLevel(null);
181
+        } else if (roleKeyList.contains(RoleTypeEnum.test.getCode()) || roleKeyList.contains(RoleTypeEnum.zhijianke.getCode())) {
182
+            checkTask.setCheckLevel(CheckLevelEnum.STATION_LEVEL.getCode());
183
+        } else if (roleKeyList.contains(RoleTypeEnum.kezhang.getCode())) {
184
+            checkTask.setCheckLevel(CheckLevelEnum.DEPARTMENT_LEVEL.getCode());
185
+            checkTask.setSelfCheckDeptId(SecurityUtils.getLoginUser().getDeptId());
186
+        } else if (roleKeyList.contains(RoleTypeEnum.banzuzhang.getCode())) {
187
+            checkTask.setCheckLevel(CheckLevelEnum.TEAM_LEVEL.getCode());
188
+            checkTask.setSelfCheckDeptId(SecurityUtils.getLoginUser().getUser().getDept().getParentId());
189
+        } else {
190
+            return success(0);
191
+        }
192
+        List<CheckTask> list = checkTaskService.selectCheckTaskList(checkTask);
193
+        if (CollUtil.isEmpty(list)) {
194
+            return success(0);
195
+        }
196
+        return success(list.stream().filter(item -> !item.isRead()).count());
197
+    }
198
+}

+ 98 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/check/CheckUserController.java

@@ -0,0 +1,98 @@
1
+package com.sundot.airport.web.controller.check;
2
+
3
+import java.util.List;
4
+import javax.servlet.http.HttpServletResponse;
5
+
6
+import org.springframework.security.access.prepost.PreAuthorize;
7
+import org.springframework.beans.factory.annotation.Autowired;
8
+import org.springframework.web.bind.annotation.GetMapping;
9
+import org.springframework.web.bind.annotation.PostMapping;
10
+import org.springframework.web.bind.annotation.PutMapping;
11
+import org.springframework.web.bind.annotation.DeleteMapping;
12
+import org.springframework.web.bind.annotation.PathVariable;
13
+import org.springframework.web.bind.annotation.RequestBody;
14
+import org.springframework.web.bind.annotation.RequestMapping;
15
+import org.springframework.web.bind.annotation.RestController;
16
+import com.sundot.airport.common.annotation.Log;
17
+import com.sundot.airport.common.core.controller.BaseController;
18
+import com.sundot.airport.common.core.domain.AjaxResult;
19
+import com.sundot.airport.common.enums.BusinessType;
20
+import com.sundot.airport.check.domain.CheckUser;
21
+import com.sundot.airport.check.service.ICheckUserService;
22
+import com.sundot.airport.common.utils.poi.ExcelUtil;
23
+import com.sundot.airport.common.core.page.TableDataInfo;
24
+
25
+/**
26
+ * 检查单检查人员和检查记录责任人和检查记录及问题整改检查项责任人Controller
27
+ *
28
+ * @author ruoyi
29
+ * @date 2025-07-11
30
+ */
31
+@RestController
32
+@RequestMapping("/check/checkUser")
33
+public class CheckUserController extends BaseController {
34
+    @Autowired
35
+    private ICheckUserService checkUserService;
36
+
37
+    /**
38
+     * 查询检查单检查人员和检查记录责任人和检查记录及问题整改检查项责任人列表
39
+     */
40
+    @PreAuthorize("@ss.hasPermi('check:checkUser:list')")
41
+    @GetMapping("/list")
42
+    public TableDataInfo list(CheckUser checkUser) {
43
+        startPage();
44
+        List<CheckUser> list = checkUserService.selectCheckUserList(checkUser);
45
+        return getDataTable(list);
46
+    }
47
+
48
+    /**
49
+     * 导出检查单检查人员和检查记录责任人和检查记录及问题整改检查项责任人列表
50
+     */
51
+    @PreAuthorize("@ss.hasPermi('check:checkUser:export')")
52
+    @Log(title = "检查单检查人员和检查记录责任人和检查记录及问题整改检查项责任人", businessType = BusinessType.EXPORT)
53
+    @PostMapping("/export")
54
+    public void export(HttpServletResponse response, CheckUser checkUser) {
55
+        List<CheckUser> list = checkUserService.selectCheckUserList(checkUser);
56
+        ExcelUtil<CheckUser> util = new ExcelUtil<CheckUser>(CheckUser.class);
57
+        util.exportExcel(response, list, "检查单检查人员和检查记录责任人和检查记录及问题整改检查项责任人数据");
58
+    }
59
+
60
+    /**
61
+     * 获取检查单检查人员和检查记录责任人和检查记录及问题整改检查项责任人详细信息
62
+     */
63
+    @PreAuthorize("@ss.hasPermi('check:checkUser:query')")
64
+    @GetMapping(value = "/{id}")
65
+    public AjaxResult getInfo(@PathVariable("id") Long id) {
66
+        return success(checkUserService.selectCheckUserById(id));
67
+    }
68
+
69
+    /**
70
+     * 新增检查单检查人员和检查记录责任人和检查记录及问题整改检查项责任人
71
+     */
72
+    @PreAuthorize("@ss.hasPermi('check:checkUser:add')")
73
+    @Log(title = "检查单检查人员和检查记录责任人和检查记录及问题整改检查项责任人", businessType = BusinessType.INSERT)
74
+    @PostMapping
75
+    public AjaxResult add(@RequestBody CheckUser checkUser) {
76
+        return toAjax(checkUserService.insertCheckUser(checkUser));
77
+    }
78
+
79
+    /**
80
+     * 修改检查单检查人员和检查记录责任人和检查记录及问题整改检查项责任人
81
+     */
82
+    @PreAuthorize("@ss.hasPermi('check:checkUser:edit')")
83
+    @Log(title = "检查单检查人员和检查记录责任人和检查记录及问题整改检查项责任人", businessType = BusinessType.UPDATE)
84
+    @PutMapping
85
+    public AjaxResult edit(@RequestBody CheckUser checkUser) {
86
+        return toAjax(checkUserService.updateCheckUser(checkUser));
87
+    }
88
+
89
+    /**
90
+     * 删除检查单检查人员和检查记录责任人和检查记录及问题整改检查项责任人
91
+     */
92
+    @PreAuthorize("@ss.hasPermi('check:checkUser:remove')")
93
+    @Log(title = "检查单检查人员和检查记录责任人和检查记录及问题整改检查项责任人", businessType = BusinessType.DELETE)
94
+    @DeleteMapping("/{ids}")
95
+    public AjaxResult remove(@PathVariable Long[] ids) {
96
+        return toAjax(checkUserService.deleteCheckUserByIds(ids));
97
+    }
98
+}

+ 94 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/common/CaptchaController.java

@@ -0,0 +1,94 @@
1
+package com.sundot.airport.web.controller.common;
2
+
3
+import java.awt.image.BufferedImage;
4
+import java.io.IOException;
5
+import java.util.concurrent.TimeUnit;
6
+import javax.annotation.Resource;
7
+import javax.imageio.ImageIO;
8
+import javax.servlet.http.HttpServletResponse;
9
+import org.springframework.beans.factory.annotation.Autowired;
10
+import org.springframework.util.FastByteArrayOutputStream;
11
+import org.springframework.web.bind.annotation.GetMapping;
12
+import org.springframework.web.bind.annotation.RestController;
13
+import com.google.code.kaptcha.Producer;
14
+import com.sundot.airport.common.config.RuoYiConfig;
15
+import com.sundot.airport.common.constant.CacheConstants;
16
+import com.sundot.airport.common.constant.Constants;
17
+import com.sundot.airport.common.core.domain.AjaxResult;
18
+import com.sundot.airport.common.core.redis.RedisCache;
19
+import com.sundot.airport.common.utils.sign.Base64;
20
+import com.sundot.airport.common.utils.uuid.IdUtils;
21
+import com.sundot.airport.system.service.ISysConfigService;
22
+
23
+/**
24
+ * 验证码操作处理
25
+ * 
26
+ * @author ruoyi
27
+ */
28
+@RestController
29
+public class CaptchaController
30
+{
31
+    @Resource(name = "captchaProducer")
32
+    private Producer captchaProducer;
33
+
34
+    @Resource(name = "captchaProducerMath")
35
+    private Producer captchaProducerMath;
36
+
37
+    @Autowired
38
+    private RedisCache redisCache;
39
+    
40
+    @Autowired
41
+    private ISysConfigService configService;
42
+    /**
43
+     * 生成验证码
44
+     */
45
+    @GetMapping("/captchaImage")
46
+    public AjaxResult getCode(HttpServletResponse response) throws IOException
47
+    {
48
+        AjaxResult ajax = AjaxResult.success();
49
+        boolean captchaEnabled = configService.selectCaptchaEnabled();
50
+        ajax.put("captchaEnabled", captchaEnabled);
51
+        if (!captchaEnabled)
52
+        {
53
+            return ajax;
54
+        }
55
+
56
+        // 保存验证码信息
57
+        String uuid = IdUtils.simpleUUID();
58
+        String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid;
59
+
60
+        String capStr = null, code = null;
61
+        BufferedImage image = null;
62
+
63
+        // 生成验证码
64
+        String captchaType = RuoYiConfig.getCaptchaType();
65
+        if ("math".equals(captchaType))
66
+        {
67
+            String capText = captchaProducerMath.createText();
68
+            capStr = capText.substring(0, capText.lastIndexOf("@"));
69
+            code = capText.substring(capText.lastIndexOf("@") + 1);
70
+            image = captchaProducerMath.createImage(capStr);
71
+        }
72
+        else if ("char".equals(captchaType))
73
+        {
74
+            capStr = code = captchaProducer.createText();
75
+            image = captchaProducer.createImage(capStr);
76
+        }
77
+
78
+        redisCache.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
79
+        // 转换流信息写出
80
+        FastByteArrayOutputStream os = new FastByteArrayOutputStream();
81
+        try
82
+        {
83
+            ImageIO.write(image, "jpg", os);
84
+        }
85
+        catch (IOException e)
86
+        {
87
+            return AjaxResult.error(e.getMessage());
88
+        }
89
+
90
+        ajax.put("uuid", uuid);
91
+        ajax.put("img", Base64.encode(os.toByteArray()));
92
+        return ajax;
93
+    }
94
+}

+ 162 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/common/CommonController.java

@@ -0,0 +1,162 @@
1
+package com.sundot.airport.web.controller.common;
2
+
3
+import java.util.ArrayList;
4
+import java.util.List;
5
+import javax.servlet.http.HttpServletRequest;
6
+import javax.servlet.http.HttpServletResponse;
7
+import org.slf4j.Logger;
8
+import org.slf4j.LoggerFactory;
9
+import org.springframework.beans.factory.annotation.Autowired;
10
+import org.springframework.http.MediaType;
11
+import org.springframework.web.bind.annotation.GetMapping;
12
+import org.springframework.web.bind.annotation.PostMapping;
13
+import org.springframework.web.bind.annotation.RequestMapping;
14
+import org.springframework.web.bind.annotation.RestController;
15
+import org.springframework.web.multipart.MultipartFile;
16
+import com.sundot.airport.common.config.RuoYiConfig;
17
+import com.sundot.airport.common.core.domain.AjaxResult;
18
+import com.sundot.airport.common.utils.StringUtils;
19
+import com.sundot.airport.common.utils.file.FileUploadUtils;
20
+import com.sundot.airport.common.utils.file.FileUtils;
21
+import com.sundot.airport.framework.config.ServerConfig;
22
+
23
+/**
24
+ * 通用请求处理
25
+ * 
26
+ * @author ruoyi
27
+ */
28
+@RestController
29
+@RequestMapping("/common")
30
+public class CommonController
31
+{
32
+    private static final Logger log = LoggerFactory.getLogger(CommonController.class);
33
+
34
+    @Autowired
35
+    private ServerConfig serverConfig;
36
+
37
+    private static final String FILE_DELIMETER = ",";
38
+
39
+    /**
40
+     * 通用下载请求
41
+     * 
42
+     * @param fileName 文件名称
43
+     * @param delete 是否删除
44
+     */
45
+    @GetMapping("/download")
46
+    public void fileDownload(String fileName, Boolean delete, HttpServletResponse response, HttpServletRequest request)
47
+    {
48
+        try
49
+        {
50
+            if (!FileUtils.checkAllowDownload(fileName))
51
+            {
52
+                throw new Exception(StringUtils.format("文件名称({})非法,不允许下载。 ", fileName));
53
+            }
54
+            String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1);
55
+            String filePath = RuoYiConfig.getDownloadPath() + fileName;
56
+
57
+            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
58
+            FileUtils.setAttachmentResponseHeader(response, realFileName);
59
+            FileUtils.writeBytes(filePath, response.getOutputStream());
60
+            if (delete)
61
+            {
62
+                FileUtils.deleteFile(filePath);
63
+            }
64
+        }
65
+        catch (Exception e)
66
+        {
67
+            log.error("下载文件失败", e);
68
+        }
69
+    }
70
+
71
+    /**
72
+     * 通用上传请求(单个)
73
+     */
74
+    @PostMapping("/upload")
75
+    public AjaxResult uploadFile(MultipartFile file) throws Exception
76
+    {
77
+        try
78
+        {
79
+            // 上传文件路径
80
+            String filePath = RuoYiConfig.getUploadPath();
81
+            // 上传并返回新文件名称
82
+            String fileName = FileUploadUtils.upload(filePath, file);
83
+            String url = serverConfig.getUrl() + fileName;
84
+            AjaxResult ajax = AjaxResult.success();
85
+            ajax.put("url", url);
86
+            ajax.put("fileName", fileName);
87
+            ajax.put("newFileName", FileUtils.getName(fileName));
88
+            ajax.put("originalFilename", file.getOriginalFilename());
89
+            return ajax;
90
+        }
91
+        catch (Exception e)
92
+        {
93
+            return AjaxResult.error(e.getMessage());
94
+        }
95
+    }
96
+
97
+    /**
98
+     * 通用上传请求(多个)
99
+     */
100
+    @PostMapping("/uploads")
101
+    public AjaxResult uploadFiles(List<MultipartFile> files) throws Exception
102
+    {
103
+        try
104
+        {
105
+            // 上传文件路径
106
+            String filePath = RuoYiConfig.getUploadPath();
107
+            List<String> urls = new ArrayList<String>();
108
+            List<String> fileNames = new ArrayList<String>();
109
+            List<String> newFileNames = new ArrayList<String>();
110
+            List<String> originalFilenames = new ArrayList<String>();
111
+            for (MultipartFile file : files)
112
+            {
113
+                // 上传并返回新文件名称
114
+                String fileName = FileUploadUtils.upload(filePath, file);
115
+                String url = serverConfig.getUrl() + fileName;
116
+                urls.add(url);
117
+                fileNames.add(fileName);
118
+                newFileNames.add(FileUtils.getName(fileName));
119
+                originalFilenames.add(file.getOriginalFilename());
120
+            }
121
+            AjaxResult ajax = AjaxResult.success();
122
+            ajax.put("urls", StringUtils.join(urls, FILE_DELIMETER));
123
+            ajax.put("fileNames", StringUtils.join(fileNames, FILE_DELIMETER));
124
+            ajax.put("newFileNames", StringUtils.join(newFileNames, FILE_DELIMETER));
125
+            ajax.put("originalFilenames", StringUtils.join(originalFilenames, FILE_DELIMETER));
126
+            return ajax;
127
+        }
128
+        catch (Exception e)
129
+        {
130
+            return AjaxResult.error(e.getMessage());
131
+        }
132
+    }
133
+
134
+    /**
135
+     * 本地资源通用下载
136
+     */
137
+    @GetMapping("/download/resource")
138
+    public void resourceDownload(String resource, HttpServletRequest request, HttpServletResponse response)
139
+            throws Exception
140
+    {
141
+        try
142
+        {
143
+            if (!FileUtils.checkAllowDownload(resource))
144
+            {
145
+                throw new Exception(StringUtils.format("资源文件({})非法,不允许下载。 ", resource));
146
+            }
147
+            // 本地资源路径
148
+            String localPath = RuoYiConfig.getProfile();
149
+            // 数据库资源地址
150
+            String downloadPath = localPath + FileUtils.stripPrefix(resource);
151
+            // 下载名称
152
+            String downloadName = StringUtils.substringAfterLast(downloadPath, "/");
153
+            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
154
+            FileUtils.setAttachmentResponseHeader(response, downloadName);
155
+            FileUtils.writeBytes(downloadPath, response.getOutputStream());
156
+        }
157
+        catch (Exception e)
158
+        {
159
+            log.error("下载文件失败", e);
160
+        }
161
+    }
162
+}

+ 52 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/common/DataConfigController.java

@@ -0,0 +1,52 @@
1
+package com.sundot.airport.web.controller.common;
2
+
3
+import com.sundot.airport.common.config.RuoYiConfig;
4
+import com.sundot.airport.common.core.domain.AjaxResult;
5
+import com.sundot.airport.common.core.domain.TreeQuery;
6
+import com.sundot.airport.common.core.domain.entity.SysDept;
7
+import com.sundot.airport.common.utils.StringUtils;
8
+import com.sundot.airport.common.utils.file.FileUploadUtils;
9
+import com.sundot.airport.common.utils.file.FileUtils;
10
+import com.sundot.airport.framework.config.ServerConfig;
11
+import com.sundot.airport.system.service.ISysTreeService;
12
+import org.slf4j.Logger;
13
+import org.slf4j.LoggerFactory;
14
+import org.springframework.beans.factory.annotation.Autowired;
15
+import org.springframework.http.MediaType;
16
+import org.springframework.security.access.prepost.PreAuthorize;
17
+import org.springframework.web.bind.annotation.GetMapping;
18
+import org.springframework.web.bind.annotation.PostMapping;
19
+import org.springframework.web.bind.annotation.RequestMapping;
20
+import org.springframework.web.bind.annotation.RestController;
21
+import org.springframework.web.multipart.MultipartFile;
22
+
23
+import javax.servlet.http.HttpServletRequest;
24
+import javax.servlet.http.HttpServletResponse;
25
+import java.util.ArrayList;
26
+import java.util.List;
27
+
28
+import static com.sundot.airport.common.core.domain.AjaxResult.success;
29
+
30
+/**
31
+ * 通用数据配置树结构请求处理
32
+ * 
33
+ * @author ruoyi
34
+ */
35
+@RestController
36
+@RequestMapping("/dataConfig")
37
+public class DataConfigController
38
+{
39
+    private static final Logger log = LoggerFactory.getLogger(DataConfigController.class);
40
+
41
+    @Autowired
42
+    private ISysTreeService sysTreeService ;
43
+
44
+    /**
45
+     * 获取部门树列表
46
+     */
47
+    @GetMapping("/dataConfigTree")
48
+    public AjaxResult dataConfigTree(TreeQuery treeQuery)
49
+    {
50
+        return success(sysTreeService.selectTreeList(treeQuery));
51
+    }
52
+}

+ 143 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/daily/DailyTaskConfigController.java

@@ -0,0 +1,143 @@
1
+package com.sundot.airport.web.controller.daily;
2
+
3
+import com.sundot.airport.common.annotation.Log;
4
+import com.sundot.airport.common.core.controller.BaseController;
5
+import com.sundot.airport.common.core.domain.AjaxResult;
6
+import com.sundot.airport.common.core.page.TableDataInfo;
7
+import com.sundot.airport.common.enums.BusinessType;
8
+import com.sundot.airport.exam.domain.DailyTaskConfig;
9
+import com.sundot.airport.exam.dto.DailyTaskConfigDTO;
10
+import com.sundot.airport.exam.dto.DailyTaskConfigQueryDTO;
11
+import com.sundot.airport.exam.service.IDailyTaskConfigService;
12
+import org.springframework.beans.factory.annotation.Autowired;
13
+import org.springframework.security.access.prepost.PreAuthorize;
14
+import org.springframework.validation.annotation.Validated;
15
+import org.springframework.web.bind.annotation.*;
16
+
17
+/**
18
+ * <b>功能名:</b>DailyTaskConfigController<br>
19
+ * <b>说明:</b> 每日答题任务配置Controller <br>
20
+ * <b>著作权:</b> Copyright (C) 2025 SUNDOT CORPORATION<br>
21
+ * <b>修改履历:
22
+ *
23
+ * @author Simon Lin
24
+ */
25
+@RestController
26
+@RequestMapping("/exam/daily/config")
27
+public class DailyTaskConfigController extends BaseController {
28
+
29
+    @Autowired
30
+    private IDailyTaskConfigService dailyTaskConfigService;
31
+
32
+    /**
33
+     * 查询每日答题任务配置列表
34
+     */
35
+    @PreAuthorize("@ss.hasPermi('exam:daily:config:list')")
36
+    @PostMapping("/list")
37
+    public TableDataInfo list(@RequestBody DailyTaskConfigQueryDTO queryDTO) {
38
+        return dailyTaskConfigService.selectConfigList(queryDTO);
39
+    }
40
+
41
+    /**
42
+     * 获取每日答题任务配置详细信息
43
+     */
44
+    @PreAuthorize("@ss.hasPermi('exam:daily:config:query')")
45
+    @GetMapping("/{dtcId}")
46
+    public AjaxResult getInfo(@PathVariable("dtcId") String dtcId) {
47
+        return success(dailyTaskConfigService.selectConfigById(dtcId));
48
+    }
49
+
50
+    /**
51
+     * 新增每日答题任务配置
52
+     */
53
+    @PreAuthorize("@ss.hasPermi('exam:daily:config:add')")
54
+    @Log(title = "每日答题任务配置", businessType = BusinessType.INSERT)
55
+    @PostMapping
56
+    public AjaxResult add(@Validated @RequestBody DailyTaskConfigDTO dto) {
57
+        return toAjax(dailyTaskConfigService.insertConfig(dto));
58
+    }
59
+
60
+    /**
61
+     * 修改每日答题任务配置
62
+     */
63
+    @PreAuthorize("@ss.hasPermi('exam:daily:config:edit')")
64
+    @Log(title = "每日答题任务配置", businessType = BusinessType.UPDATE)
65
+    @PutMapping
66
+    public AjaxResult edit(@Validated @RequestBody DailyTaskConfigDTO dto) {
67
+        return toAjax(dailyTaskConfigService.updateConfig(dto));
68
+    }
69
+
70
+    /**
71
+     * 删除每日答题任务配置
72
+     */
73
+    @PreAuthorize("@ss.hasPermi('exam:daily:config:remove')")
74
+    @Log(title = "每日答题任务配置", businessType = BusinessType.DELETE)
75
+    @DeleteMapping("/{dtcIds}")
76
+    public AjaxResult remove(@PathVariable String[] dtcIds) {
77
+        return toAjax(dailyTaskConfigService.deleteConfigByIds(dtcIds));
78
+    }
79
+
80
+    /**
81
+     * 启用/停用每日答题任务配置
82
+     */
83
+    @PreAuthorize("@ss.hasPermi('exam:daily:config:edit')")
84
+    @Log(title = "每日答题任务配置", businessType = BusinessType.UPDATE)
85
+    @PutMapping("/changeStatus")
86
+    public AjaxResult changeStatus(@RequestParam("dtcId") String dtcId,
87
+                                    @RequestParam("enableStatus") Boolean enableStatus) {
88
+        return toAjax(dailyTaskConfigService.updateConfigStatus(dtcId, enableStatus));
89
+    }
90
+
91
+    /**
92
+     * 根据当前用户获取有效配置
93
+     * 遍历用户的所有角色,收集所有匹配的有效配置,并选择最新的配置
94
+     */
95
+    @GetMapping("/effective")
96
+    public AjaxResult getEffectiveConfig() {
97
+        // 获取当前用户的角色ID列表和部门
98
+        Long deptId = getDeptId();
99
+
100
+        // 获取用户的所有角色ID
101
+        java.util.List<Long> roleIds = getLoginUser().getUser().getRoles().stream()
102
+                .map(role -> role.getRoleId())
103
+                .collect(java.util.stream.Collectors.toList());
104
+
105
+        if (roleIds.isEmpty()) {
106
+            return AjaxResult.error("用户未分配角色");
107
+        }
108
+
109
+        // 收集所有角色匹配的有效配置
110
+        java.util.List<DailyTaskConfig> matchedConfigs = new java.util.ArrayList<>();
111
+        for (Long roleId : roleIds) {
112
+            DailyTaskConfig config = dailyTaskConfigService.getEffectiveConfig(roleId, deptId);
113
+            if (config != null) {
114
+                matchedConfigs.add(config);
115
+            }
116
+        }
117
+
118
+        if (matchedConfigs.isEmpty()) {
119
+            return AjaxResult.error("未找到适用的答题配置");
120
+        }
121
+
122
+        // 选择最新的配置(根据创建时间降序排序,取第一个)
123
+        DailyTaskConfig latestConfig = matchedConfigs.stream()
124
+                .sorted((c1, c2) -> c2.getCreateTime().compareTo(c1.getCreateTime()))
125
+                .findFirst()
126
+                .orElse(null);
127
+
128
+        return success(latestConfig);
129
+    }
130
+
131
+    /**
132
+     * 获取默认配置(第一条配置)
133
+     * 用于单配置管理页面
134
+     */
135
+    @GetMapping("/default")
136
+    public AjaxResult getDefaultConfig() {
137
+        DailyTaskConfig config = dailyTaskConfigService.getDefaultConfig();
138
+        if (config == null) {
139
+            return AjaxResult.error("暂无配置数据");
140
+        }
141
+        return success(config);
142
+    }
143
+}

+ 210 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/daily/DeptProfileController.java

@@ -0,0 +1,210 @@
1
+package com.sundot.airport.web.controller.daily;
2
+
3
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
4
+import com.sundot.airport.common.core.controller.BaseController;
5
+import com.sundot.airport.common.core.domain.AjaxResult;
6
+import com.sundot.airport.common.core.domain.entity.SysDept;
7
+import com.sundot.airport.exam.domain.DailyTask;
8
+import com.sundot.airport.exam.dto.DeptProfileDTO;
9
+import com.sundot.airport.exam.dto.DeptProfileQueryDTO;
10
+import com.sundot.airport.exam.mapper.DailyTaskMapper;
11
+import com.sundot.airport.exam.service.IDeptProfileService;
12
+import com.sundot.airport.system.service.ISysDeptService;
13
+import io.swagger.annotations.Api;
14
+import io.swagger.annotations.ApiOperation;
15
+import io.swagger.annotations.ApiParam;
16
+import org.springframework.beans.factory.annotation.Autowired;
17
+import org.springframework.web.bind.annotation.GetMapping;
18
+import org.springframework.web.bind.annotation.RequestMapping;
19
+import org.springframework.web.bind.annotation.RequestParam;
20
+import org.springframework.web.bind.annotation.RestController;
21
+
22
+import java.math.BigDecimal;
23
+import java.math.RoundingMode;
24
+import java.sql.Date;
25
+import java.text.SimpleDateFormat;
26
+import java.time.LocalDate;
27
+import java.util.*;
28
+import java.util.stream.Collectors;
29
+
30
+/**
31
+ * <b>功能名:</b>DeptProfileController<br>
32
+ * <b>说明:</b> 组织画像Controller <br>
33
+ * <b>著作权:</b> Copyright (C) 2025 SUNDOT CORPORATION<br>
34
+ * <b>修改履历:
35
+ *
36
+ * @author Claude
37
+ */
38
+@Api(tags = "组织画像")
39
+@RestController
40
+@RequestMapping("/exam/daily/dept-profile")
41
+public class DeptProfileController extends BaseController {
42
+
43
+    @Autowired
44
+    private IDeptProfileService deptProfileService;
45
+
46
+    @Autowired
47
+    private DailyTaskMapper dailyTaskMapper;
48
+
49
+    @Autowired
50
+    private ISysDeptService deptService;
51
+
52
+    /**
53
+     * 获取组织画像
54
+     */
55
+    @ApiOperation("获取组织画像")
56
+    @GetMapping
57
+    public AjaxResult getDeptProfile(
58
+            @ApiParam(value = "部门ID", required = true)
59
+            @RequestParam Long deptId,
60
+
61
+            @ApiParam(value = "开始日期", required = false, example = "2025-01-01")
62
+            @RequestParam(required = false) String startDate,
63
+
64
+            @ApiParam(value = "结束日期", required = false, example = "2025-01-31")
65
+            @RequestParam(required = false) String endDate) {
66
+
67
+        // 如果没有传日期范围,则默认为91天
68
+        if (startDate == null || endDate == null) {
69
+            java.time.LocalDate today = java.time.LocalDate.now();
70
+            endDate = today.toString();
71
+            startDate = today.minusDays(91).toString();
72
+        }
73
+
74
+        DeptProfileQueryDTO query = new DeptProfileQueryDTO();
75
+        query.setDeptId(deptId);
76
+        query.setStartDate(startDate);
77
+        query.setEndDate(endDate);
78
+
79
+        DeptProfileDTO profile = deptProfileService.getDeptProfile(query);
80
+
81
+        return success(profile);
82
+    }
83
+
84
+    /**
85
+     * 获取部门每日抽问抽答完成率
86
+     * 统计该部门及其所有下级部门成员的抽问抽答完成率
87
+     * 例如:传入科级ID,统计该科级下所有班组成员的完成率
88
+     */
89
+    @ApiOperation("获取部门每日抽问抽答完成率")
90
+    @GetMapping("/daily-completion-rate")
91
+    public AjaxResult getDailyCompletionRate(
92
+            @ApiParam(value = "部门ID(可以是科级ID、班组ID等)", required = true)
93
+            @RequestParam Long deptId,
94
+
95
+            @ApiParam(value = "开始日期", required = false, example = "2025-01-01")
96
+            @RequestParam(required = false) String startDate,
97
+
98
+            @ApiParam(value = "结束日期", required = false, example = "2025-01-31")
99
+            @RequestParam(required = false) String endDate) {
100
+
101
+        try {
102
+            // 如果没有传日期范围,则默认为91天
103
+            if (startDate == null || endDate == null) {
104
+                LocalDate today = LocalDate.now();
105
+                endDate = today.toString();
106
+                startDate = today.minusDays(91).toString();
107
+            }
108
+
109
+            // 获取该部门及所有下级部门的ID列表(例如:科级 -> 所有班组)
110
+            List<Long> deptIds = getAllSubDeptIds(deptId);
111
+            logger.info("查询部门ID={} 的下级部门,共找到 {} 个部门: {}", deptId, deptIds.size(), deptIds);
112
+
113
+            if (deptIds.isEmpty()) {
114
+                return success(new ArrayList<>());
115
+            }
116
+
117
+            // 转换日期
118
+            LocalDate start = LocalDate.parse(startDate);
119
+            LocalDate end = LocalDate.parse(endDate);
120
+
121
+            // 构建日期范围的Map,存储每一天的完成率
122
+            Map<String, Map<String, Object>> dailyStats = new LinkedHashMap<>();
123
+
124
+            // 初始化每一天的数据
125
+            LocalDate currentDate = start;
126
+            while (!currentDate.isAfter(end)) {
127
+                String dateStr = currentDate.toString();
128
+                Map<String, Object> dayData = new HashMap<>();
129
+                dayData.put("date", dateStr);
130
+                dayData.put("completionRate", BigDecimal.ZERO);
131
+                dayData.put("totalTasks", 0);
132
+                dayData.put("completedTasks", 0);
133
+                dailyStats.put(dateStr, dayData);
134
+                currentDate = currentDate.plusDays(1);
135
+            }
136
+
137
+            // 查询该时间段内所有任务
138
+            LambdaQueryWrapper<DailyTask> wrapper = new LambdaQueryWrapper<>();
139
+            wrapper.in(DailyTask::getDtDeptId, deptIds);
140
+            wrapper.ge(DailyTask::getDtBusinessDate, Date.valueOf(startDate));
141
+            wrapper.le(DailyTask::getDtBusinessDate, Date.valueOf(endDate));
142
+            wrapper.eq(DailyTask::getDelStatus, 0);
143
+
144
+            List<DailyTask> tasks = dailyTaskMapper.selectList(wrapper);
145
+            logger.info("查询到 {} 条任务记录", tasks.size());
146
+
147
+            // 按日期分组统计(使用SimpleDateFormat格式化日期)
148
+            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
149
+            Map<String, List<DailyTask>> tasksByDate = tasks.stream()
150
+                    .collect(Collectors.groupingBy(task -> sdf.format(task.getDtBusinessDate())));
151
+
152
+            // 计算每一天的完成率
153
+            for (Map.Entry<String, List<DailyTask>> entry : tasksByDate.entrySet()) {
154
+                String dateStr = entry.getKey();
155
+                List<DailyTask> dayTasks = entry.getValue();
156
+
157
+                int totalTasks = dayTasks.size();
158
+                long completedTasks = dayTasks.stream()
159
+                        .filter(task -> "COMPLETED".equals(task.getDtStatus()))
160
+                        .count();
161
+
162
+                BigDecimal completionRate = BigDecimal.ZERO;
163
+                if (totalTasks > 0) {
164
+                    completionRate = new BigDecimal(completedTasks)
165
+                            .multiply(new BigDecimal(100))
166
+                            .divide(new BigDecimal(totalTasks), 2, RoundingMode.HALF_UP);
167
+                }
168
+
169
+                if (dailyStats.containsKey(dateStr)) {
170
+                    Map<String, Object> dayData = dailyStats.get(dateStr);
171
+                    dayData.put("completionRate", completionRate);
172
+                    dayData.put("totalTasks", totalTasks);
173
+                    dayData.put("completedTasks", completedTasks);
174
+                }
175
+            }
176
+
177
+            // 转换为数组返回
178
+            List<Map<String, Object>> result = new ArrayList<>(dailyStats.values());
179
+
180
+            return success(result);
181
+        } catch (Exception e) {
182
+            logger.error("获取部门每日完成率失败", e);
183
+            return error("获取部门每日完成率失败:" + e.getMessage());
184
+        }
185
+    }
186
+
187
+    /**
188
+     * 获取指定部门及其所有下级部门的ID列表
189
+     * 根据sys_dept表的层级关系递归查找
190
+     * 例如:传入科级ID,返回该科级ID及其所有下级班组ID
191
+     */
192
+    private List<Long> getAllSubDeptIds(Long deptId) {
193
+        List<Long> result = new ArrayList<>();
194
+        result.add(deptId); // 包含自己
195
+
196
+        // 查询直接下级部门
197
+        SysDept queryDept = new SysDept();
198
+        queryDept.setParentId(deptId);
199
+        List<SysDept> subDepts = deptService.selectDeptInfoAll(queryDept);
200
+
201
+        // 递归查询所有下级部门
202
+        for (SysDept subDept : subDepts) {
203
+            if ("0".equals(subDept.getDelFlag())) { // 只包含未删除的部门
204
+                result.addAll(getAllSubDeptIds(subDept.getDeptId()));
205
+            }
206
+        }
207
+
208
+        return result;
209
+    }
210
+}

+ 208 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/daily/SiteProfileController.java

@@ -0,0 +1,208 @@
1
+package com.sundot.airport.web.controller.daily;
2
+
3
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
4
+import com.sundot.airport.common.core.controller.BaseController;
5
+import com.sundot.airport.common.core.domain.AjaxResult;
6
+import com.sundot.airport.common.core.domain.entity.SysDept;
7
+import com.sundot.airport.exam.domain.DailyTask;
8
+import com.sundot.airport.exam.dto.SiteProfileDTO;
9
+import com.sundot.airport.exam.dto.SiteProfileQueryDTO;
10
+import com.sundot.airport.exam.mapper.DailyTaskMapper;
11
+import com.sundot.airport.exam.service.ISiteProfileService;
12
+import com.sundot.airport.system.service.ISysDeptService;
13
+import io.swagger.annotations.Api;
14
+import io.swagger.annotations.ApiOperation;
15
+import io.swagger.annotations.ApiParam;
16
+import org.springframework.beans.factory.annotation.Autowired;
17
+import org.springframework.web.bind.annotation.GetMapping;
18
+import org.springframework.web.bind.annotation.RequestMapping;
19
+import org.springframework.web.bind.annotation.RequestParam;
20
+import org.springframework.web.bind.annotation.RestController;
21
+
22
+import java.math.BigDecimal;
23
+import java.math.RoundingMode;
24
+import java.sql.Date;
25
+import java.text.SimpleDateFormat;
26
+import java.time.LocalDate;
27
+import java.util.*;
28
+import java.util.stream.Collectors;
29
+
30
+/**
31
+ * <b>功能名:</b>SiteProfileController<br>
32
+ * <b>说明:</b> 站级画像Controller <br>
33
+ * <b>著作权:</b> Copyright (C) 2025 SUNDOT CORPORATION<br>
34
+ * <b>修改履历:
35
+ *
36
+ * @author Claude
37
+ */
38
+@Api(tags = "站级画像")
39
+@RestController
40
+@RequestMapping("/exam/daily/site-profile")
41
+public class SiteProfileController extends BaseController {
42
+
43
+    @Autowired
44
+    private ISiteProfileService siteProfileService;
45
+
46
+    @Autowired
47
+    private DailyTaskMapper dailyTaskMapper;
48
+
49
+    @Autowired
50
+    private ISysDeptService deptService;
51
+
52
+    /**
53
+     * 获取站级画像
54
+     */
55
+    @ApiOperation("获取站级画像")
56
+    @GetMapping
57
+    public AjaxResult getSiteProfile(
58
+            @ApiParam(value = "站级ID", required = true)
59
+            @RequestParam Long siteId,
60
+
61
+            @ApiParam(value = "开始日期", required = false, example = "2025-01-01")
62
+            @RequestParam(required = false) String startDate,
63
+
64
+            @ApiParam(value = "结束日期", required = false, example = "2025-01-31")
65
+            @RequestParam(required = false) String endDate) {
66
+
67
+        // 如果没有传日期范围,则默认为91天
68
+        if (startDate == null || endDate == null) {
69
+            java.time.LocalDate today = java.time.LocalDate.now();
70
+            endDate = today.toString();
71
+            startDate = today.minusDays(91).toString();
72
+        }
73
+
74
+        SiteProfileQueryDTO query = new SiteProfileQueryDTO();
75
+        query.setSiteId(siteId);
76
+        query.setStartDate(startDate);
77
+        query.setEndDate(endDate);
78
+
79
+        SiteProfileDTO profile = siteProfileService.getSiteProfile(query);
80
+
81
+        return success(profile);
82
+    }
83
+
84
+    /**
85
+     * 获取站级每日抽问抽答完成率
86
+     * 统计该站级及其所有下级部门(科级、班组等)成员的抽问抽答完成率
87
+     * 例如:传入站级ID,统计该站级下所有科级、班组成员的完成率
88
+     */
89
+    @ApiOperation("获取站级每日抽问抽答完成率")
90
+    @GetMapping("/daily-completion-rate")
91
+    public AjaxResult getDailyCompletionRate(
92
+            @ApiParam(value = "站级ID", required = true)
93
+            @RequestParam Long siteId,
94
+
95
+            @ApiParam(value = "开始日期", required = false, example = "2025-01-01")
96
+            @RequestParam(required = false) String startDate,
97
+
98
+            @ApiParam(value = "结束日期", required = false, example = "2025-01-31")
99
+            @RequestParam(required = false) String endDate) {
100
+
101
+        try {
102
+            // 如果没有传日期范围,则默认为91天
103
+            if (startDate == null || endDate == null) {
104
+                LocalDate today = LocalDate.now();
105
+                endDate = today.toString();
106
+                startDate = today.minusDays(91).toString();
107
+            }
108
+
109
+            // 获取该站级及所有下级部门的ID列表(站级 -> 科级 -> 班组)
110
+            List<Long> deptIds = getAllSubDeptIds(siteId);
111
+
112
+            if (deptIds.isEmpty()) {
113
+                return success(new ArrayList<>());
114
+            }
115
+
116
+            // 转换日期
117
+            LocalDate start = LocalDate.parse(startDate);
118
+            LocalDate end = LocalDate.parse(endDate);
119
+
120
+            // 构建日期范围的Map,存储每一天的完成率
121
+            Map<String, Map<String, Object>> dailyStats = new LinkedHashMap<>();
122
+
123
+            // 初始化每一天的数据
124
+            LocalDate currentDate = start;
125
+            while (!currentDate.isAfter(end)) {
126
+                String dateStr = currentDate.toString();
127
+                Map<String, Object> dayData = new HashMap<>();
128
+                dayData.put("date", dateStr);
129
+                dayData.put("completionRate", BigDecimal.ZERO);
130
+                dayData.put("totalTasks", 0);
131
+                dayData.put("completedTasks", 0);
132
+                dailyStats.put(dateStr, dayData);
133
+                currentDate = currentDate.plusDays(1);
134
+            }
135
+
136
+            // 查询该时间段内所有任务
137
+            LambdaQueryWrapper<DailyTask> wrapper = new LambdaQueryWrapper<>();
138
+            wrapper.in(DailyTask::getDtDeptId, deptIds);
139
+            wrapper.ge(DailyTask::getDtBusinessDate, Date.valueOf(startDate));
140
+            wrapper.le(DailyTask::getDtBusinessDate, Date.valueOf(endDate));
141
+            wrapper.eq(DailyTask::getDelStatus, 0);
142
+
143
+            List<DailyTask> tasks = dailyTaskMapper.selectList(wrapper);
144
+
145
+            // 按日期分组统计(使用SimpleDateFormat格式化日期)
146
+            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
147
+            Map<String, List<DailyTask>> tasksByDate = tasks.stream()
148
+                    .collect(Collectors.groupingBy(task -> sdf.format(task.getDtBusinessDate())));
149
+
150
+            // 计算每一天的完成率
151
+            for (Map.Entry<String, List<DailyTask>> entry : tasksByDate.entrySet()) {
152
+                String dateStr = entry.getKey();
153
+                List<DailyTask> dayTasks = entry.getValue();
154
+
155
+                int totalTasks = dayTasks.size();
156
+                long completedTasks = dayTasks.stream()
157
+                        .filter(task -> "COMPLETED".equals(task.getDtStatus()))
158
+                        .count();
159
+
160
+                BigDecimal completionRate = BigDecimal.ZERO;
161
+                if (totalTasks > 0) {
162
+                    completionRate = new BigDecimal(completedTasks)
163
+                            .multiply(new BigDecimal(100))
164
+                            .divide(new BigDecimal(totalTasks), 2, RoundingMode.HALF_UP);
165
+                }
166
+
167
+                if (dailyStats.containsKey(dateStr)) {
168
+                    Map<String, Object> dayData = dailyStats.get(dateStr);
169
+                    dayData.put("completionRate", completionRate);
170
+                    dayData.put("totalTasks", totalTasks);
171
+                    dayData.put("completedTasks", completedTasks);
172
+                }
173
+            }
174
+
175
+            // 转换为数组返回
176
+            List<Map<String, Object>> result = new ArrayList<>(dailyStats.values());
177
+
178
+            return success(result);
179
+        } catch (Exception e) {
180
+            logger.error("获取站级每日完成率失败", e);
181
+            return error("获取站级每日完成率失败:" + e.getMessage());
182
+        }
183
+    }
184
+
185
+    /**
186
+     * 获取指定站级及其所有下级部门的ID列表
187
+     * 根据sys_dept表的层级关系递归查找
188
+     * 例如:传入站级ID,返回该站级ID及其所有下级科级、班组ID
189
+     */
190
+    private List<Long> getAllSubDeptIds(Long siteId) {
191
+        List<Long> result = new ArrayList<>();
192
+        result.add(siteId); // 包含自己
193
+
194
+        // 查询直接下级部门
195
+        SysDept queryDept = new SysDept();
196
+        queryDept.setParentId(siteId);
197
+        List<SysDept> subDepts = deptService.selectDeptInfoAll(queryDept);
198
+
199
+        // 递归查询所有下级部门
200
+        for (SysDept subDept : subDepts) {
201
+            if ("0".equals(subDept.getDelFlag())) { // 只包含未删除的部门
202
+                result.addAll(getAllSubDeptIds(subDept.getDeptId()));
203
+            }
204
+        }
205
+
206
+        return result;
207
+    }
208
+}

+ 20 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/exam/BaseController.java

@@ -0,0 +1,20 @@
1
+package com.sundot.airport.web.controller.exam;
2
+
3
+import org.slf4j.Logger;
4
+import org.slf4j.LoggerFactory;
5
+
6
+/**
7
+ * <b>功能名:</b>BaseController<br>
8
+ * <b>说明:</b> 基本controller管理抽象类 <br>
9
+ * <b>著作权:</b> Copyright (C) 2021 HUIFANEDU  CORPORATION<br>
10
+ * <b>修改履历:
11
+ *
12
+ * @author 2021-06-17 liangxiao
13
+ */
14
+public abstract class BaseController {
15
+
16
+    /**
17
+     * 日志记录
18
+     */
19
+    protected final Logger log = LoggerFactory.getLogger(this.getClass());
20
+}

+ 281 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/exam/DailyExamController.java

@@ -0,0 +1,281 @@
1
+package com.sundot.airport.web.controller.exam;
2
+
3
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
4
+import com.sundot.airport.common.utils.SecurityUtils;
5
+import com.sundot.airport.exam.adapter.DailyTaskToExamAdapter;
6
+import com.sundot.airport.exam.common.ClientTypeConstant;
7
+import com.sundot.airport.exam.common.HttpResult;
8
+import com.sundot.airport.exam.common.PageData;
9
+import com.sundot.airport.exam.domain.DailyTask;
10
+import com.sundot.airport.exam.domain.DailyTaskDetail;
11
+import com.sundot.airport.exam.dto.SubmitAnswerDTO;
12
+import com.sundot.airport.exam.dto.SubmitResultDTO;
13
+import com.sundot.airport.exam.logic.eighteen.model.params.ExamRecAddParam;
14
+import com.sundot.airport.exam.logic.eighteen.model.params.Get18ExamIntroParams;
15
+import com.sundot.airport.exam.logic.eighteen.model.params.SubmitExamParam;
16
+import com.sundot.airport.exam.logic.eighteen.model.result.ExamGetResult;
17
+import com.sundot.airport.exam.logic.eighteen.model.result.Get18ExamIntroResult;
18
+import com.sundot.airport.exam.logic.eighteen.model.result.SubmitExamResult;
19
+import com.sundot.airport.exam.logic.eighteen.model.result.subresult.ItemIn18ExamRecordListResult;
20
+import com.sundot.airport.exam.mapper.DailyTaskDetailMapper;
21
+import com.sundot.airport.exam.mapper.DailyTaskMapper;
22
+import com.sundot.airport.exam.service.IDailyAnswerService;
23
+import org.slf4j.Logger;
24
+import org.slf4j.LoggerFactory;
25
+import org.springframework.beans.factory.annotation.Autowired;
26
+import org.springframework.validation.annotation.Validated;
27
+import org.springframework.web.bind.annotation.*;
28
+
29
+import javax.annotation.Resource;
30
+import java.text.SimpleDateFormat;
31
+import java.util.ArrayList;
32
+import java.util.HashMap;
33
+import java.util.List;
34
+import java.util.Map;
35
+
36
+/**
37
+ * <b>功能名:</b>DailyExamController<br>
38
+ * <b>说明:</b> 每日抽问抽答接口控制层(兼容十八大考试格式) <br>
39
+ * <b>著作权:</b> Copyright (C) 2025 SUNDOT CORPORATION<br>
40
+ * <b>修改履历:</b><br>
41
+ *
42
+ * @author 2025-01-16
43
+ */
44
+@RestController
45
+@RequestMapping("/v1/cs/app/daily-exam")
46
+public class DailyExamController {
47
+
48
+    private static final Logger log = LoggerFactory.getLogger(DailyExamController.class);
49
+
50
+    @Autowired
51
+    private DailyTaskMapper dailyTaskMapper;
52
+
53
+    @Autowired
54
+    private DailyTaskDetailMapper dailyTaskDetailMapper;
55
+
56
+    @Autowired
57
+    private IDailyAnswerService dailyAnswerService;
58
+
59
+    @Autowired
60
+    private DailyTaskToExamAdapter dailyTaskAdapter;
61
+
62
+    /**
63
+     * <b>方法名: </b> getExamIntro <br>
64
+     * <b>说明: </b> 查询考试基本信息(兼容接口,实际返回当日任务信息) <br>
65
+     *
66
+     * @param param Get18ExamIntroParams 查询考试基本信息参数类
67
+     * @return HttpResult<Get18ExamIntroResult>
68
+     * <b>修改履历: </b>
69
+     * @author 2025-01-16
70
+     */
71
+    @PostMapping("/get_exam_intro")
72
+    public HttpResult<Get18ExamIntroResult> getExamIntro(@Validated @RequestBody Get18ExamIntroParams param) {
73
+        log.info("获取每日任务基本信息: userId={}", SecurityUtils.getUserId());
74
+
75
+        // 构造返回结果(固定信息)
76
+        Get18ExamIntroResult result = new Get18ExamIntroResult();
77
+        result.setExId("DAILY_TASK");
78
+        result.setExName("每日抽问抽答");
79
+        result.setExDetailImg("");
80
+        result.setExBgImg("");
81
+
82
+        return HttpResult.success(result);
83
+    }
84
+
85
+    /**
86
+     * <b>方法名: </b> applyExam <br>
87
+     * <b>说明: </b> 开始答题(获取今日待完成任务) <br>
88
+     *
89
+     * @param token java.lang.String token信息
90
+     * @param param com.sundot.airport.exam.logic.eighteen.model.params.ExamRecAddParam 获取考试信息和试题请求参数
91
+     * @return com.sundot.airport.exam.common.HttpResult<com.sundot.airport.exam.logic.eighteen.model.result.ExamGetResult><br>
92
+     * <b>修改履历: </b>
93
+     * @author 2025-01-16
94
+     */
95
+    @PostMapping("/get_exam")
96
+    public HttpResult<ExamGetResult> applyExam(
97
+            @RequestHeader(value = "Authorization", required = true) String token,
98
+            @Validated @RequestBody ExamRecAddParam param) {
99
+
100
+        Long userId = SecurityUtils.getUserId();
101
+        log.info("用户申请每日答题: userId={}, param={}", userId, param);
102
+
103
+        try {
104
+            // 1. 查询今日待完成任务
105
+            LambdaQueryWrapper<DailyTask> wrapper = new LambdaQueryWrapper<>();
106
+            wrapper.eq(DailyTask::getDtUserId, userId);
107
+            wrapper.eq(DailyTask::getDtStatus, "PENDING");
108
+            wrapper.eq(DailyTask::getDelStatus, 0);
109
+            wrapper.orderByAsc(DailyTask::getCreateTime);
110
+            wrapper.last("LIMIT 1");
111
+
112
+            DailyTask task = dailyTaskMapper.selectOne(wrapper);
113
+
114
+            if (task == null) {
115
+                log.warn("用户今日无待完成任务: userId={}", userId);
116
+                return HttpResult.error("今日暂无答题任务");
117
+            }
118
+
119
+            // 2. 更新任务状态为答题中
120
+            task.setDtStatus("IN_PROGRESS");
121
+            task.setDtStartTime(new java.util.Date());
122
+            dailyTaskMapper.updateById(task);
123
+
124
+            // 3. 查询任务明细(题目列表)
125
+            LambdaQueryWrapper<DailyTaskDetail> detailWrapper = new LambdaQueryWrapper<>();
126
+            detailWrapper.eq(DailyTaskDetail::getDtdTaskId, task.getDtId());
127
+            detailWrapper.eq(DailyTaskDetail::getDelStatus, 0);
128
+            detailWrapper.orderByAsc(DailyTaskDetail::getDtdQuestionNo);
129
+            List<DailyTaskDetail> details = dailyTaskDetailMapper.selectList(detailWrapper);
130
+
131
+            // 4. 转换为考试格式返回
132
+            ExamGetResult result = dailyTaskAdapter.convertDailyTaskToExam(task, details);
133
+
134
+            log.info("每日答题获取成功: userId={}, taskId={}, questionCount={}",
135
+                    userId, task.getDtId(), details.size());
136
+
137
+            return HttpResult.success(result);
138
+
139
+        } catch (Exception e) {
140
+            log.error("获取每日答题失败: userId={}", userId, e);
141
+            return HttpResult.error("获取每日答题失败:" + e.getMessage());
142
+        }
143
+    }
144
+
145
+    /**
146
+     * <b>方法名: </b> getExamRecordList <br>
147
+     * <b>说明: </b> 查询测试记录列表(用户历史任务列表) <br>
148
+     *
149
+     * @return HttpResult<PageData < ItemIn18ExamRecordListResult>>
150
+     * <b>修改履历: </b>
151
+     * @author 2025-01-16
152
+     */
153
+    @PostMapping("/get_exam_record_list")
154
+    public HttpResult<PageData<ItemIn18ExamRecordListResult>> getExamRecordList() {
155
+        Long userId = SecurityUtils.getUserId();
156
+        log.info("查询用户任务记录列表: userId={}", userId);
157
+
158
+        try {
159
+            // 查询用户所有任务记录
160
+            LambdaQueryWrapper<DailyTask> wrapper = new LambdaQueryWrapper<>();
161
+            wrapper.eq(DailyTask::getDtUserId, userId);
162
+            wrapper.eq(DailyTask::getDelStatus, 0);
163
+            wrapper.orderByDesc(DailyTask::getDtBusinessDate);
164
+            wrapper.orderByDesc(DailyTask::getCreateTime);
165
+
166
+            List<DailyTask> tasks = dailyTaskMapper.selectList(wrapper);
167
+
168
+            // 转换为考试记录列表格式
169
+            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
170
+            List<ItemIn18ExamRecordListResult> results = new ArrayList<>();
171
+
172
+            for (DailyTask task : tasks) {
173
+                ItemIn18ExamRecordListResult item = new ItemIn18ExamRecordListResult();
174
+                item.setErId(task.getDtId());
175
+                item.setErExId("DAILY_TASK_" + task.getDtBusinessDate());
176
+                item.setErExName("每日抽问抽答");
177
+                item.setErScore(task.getDtTotalScore() != null ? task.getDtTotalScore().intValue() : 0);
178
+                item.setCreateTime(task.getCreateTime() != null ? sdf.format(task.getCreateTime()) : "");
179
+                item.setErExEnableCert(false); // 每日任务不启用证书
180
+                item.setGetCert(false);
181
+
182
+                results.add(item);
183
+            }
184
+
185
+            PageData<ItemIn18ExamRecordListResult> pageData = new PageData<>();
186
+            pageData.setList(results);
187
+            pageData.setTotal(results.size());
188
+
189
+            return HttpResult.success(pageData);
190
+
191
+        } catch (Exception e) {
192
+            log.error("查询任务记录列表失败: userId={}", userId, e);
193
+            return HttpResult.error("查询记录列表失败:" + e.getMessage());
194
+        }
195
+    }
196
+
197
+    /**
198
+     * <b>方法名: </b> getExamRecord <br>
199
+     * <b>说明: </b> 查询测试记录详情(任务详情) <br>
200
+     *
201
+     * @param erId 任务ID
202
+     * @return HttpResult
203
+     * <b>修改履历: </b>
204
+     * @author 2025-01-16
205
+     */
206
+    @PostMapping("/get_exam_record")
207
+    public HttpResult getExamRecord(@RequestParam String erId) {
208
+        Long userId = SecurityUtils.getUserId();
209
+        log.info("查询任务记录详情: userId={}, taskId={}", userId, erId);
210
+
211
+        try {
212
+            // 查询任务主记录
213
+            LambdaQueryWrapper<DailyTask> taskWrapper = new LambdaQueryWrapper<>();
214
+            taskWrapper.eq(DailyTask::getDtId, erId);
215
+            taskWrapper.eq(DailyTask::getDelStatus, 0);
216
+            DailyTask task = dailyTaskMapper.selectOne(taskWrapper);
217
+
218
+            if (task == null) {
219
+                return HttpResult.error("任务不存在");
220
+            }
221
+
222
+            // 验证权限
223
+            if (!task.getDtUserId().equals(userId)) {
224
+                return HttpResult.error("无权查看该任务");
225
+            }
226
+
227
+            // 查询任务明细
228
+            LambdaQueryWrapper<DailyTaskDetail> detailWrapper = new LambdaQueryWrapper<>();
229
+            detailWrapper.eq(DailyTaskDetail::getDtdTaskId, erId);
230
+            detailWrapper.eq(DailyTaskDetail::getDelStatus, 0);
231
+            detailWrapper.orderByAsc(DailyTaskDetail::getDtdQuestionNo);
232
+            List<DailyTaskDetail> details = dailyTaskDetailMapper.selectList(detailWrapper);
233
+
234
+            // 组装返回结果
235
+            Map<String, Object> data = new HashMap<>();
236
+            data.put("task", task);
237
+            data.put("details", details);
238
+
239
+            return HttpResult.success(data);
240
+
241
+        } catch (Exception e) {
242
+            log.error("查询任务记录详情失败: userId={}, taskId={}", userId, erId, e);
243
+            return HttpResult.error("查询记录详情失败:" + e.getMessage());
244
+        }
245
+    }
246
+
247
+    /**
248
+     * <b>方法名: </b> submitExam <br>
249
+     * <b>说明: </b> 提交考试答案 <br>
250
+     *
251
+     * @param param SubmitExamParam 提交考试参数类
252
+     * @return HttpResult<SubmitExamResult>
253
+     * <b>修改履历: </b>
254
+     * @author 2025-01-16
255
+     */
256
+    @PostMapping("/submit_exam")
257
+    public HttpResult<SubmitExamResult> submitExam(@Validated @RequestBody SubmitExamParam param) {
258
+        Long userId = SecurityUtils.getUserId();
259
+        log.info("提交每日答题: userId={}, param={}", userId, param);
260
+
261
+        try {
262
+            // 1. 转换参数格式
263
+            SubmitAnswerDTO taskDto = dailyTaskAdapter.convertExamParamToTaskSubmit(param);
264
+
265
+            // 2. 调用答题服务提交
266
+            SubmitResultDTO taskResult = dailyAnswerService.submitAnswer(userId, taskDto);
267
+
268
+            // 3. 转换为考试提交结果格式
269
+            SubmitExamResult examResult = dailyTaskAdapter.convertTaskResultToExamResult(taskResult);
270
+
271
+            log.info("每日答题提交成功: userId={}, taskId={}, score={}",
272
+                    userId, taskResult.getTaskId(), taskResult.getTotalScore());
273
+
274
+            return HttpResult.success(examResult);
275
+
276
+        } catch (Exception e) {
277
+            log.error("提交每日答题失败: userId={}, erId={}", userId, param.getErId(), e);
278
+            return HttpResult.error("提交答题失败:" + e.getMessage());
279
+        }
280
+    }
281
+}

+ 109 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/exam/EighteenController.java

@@ -0,0 +1,109 @@
1
+package com.sundot.airport.web.controller.exam;
2
+
3
+import com.sundot.airport.exam.common.ClientTypeConstant;
4
+import com.sundot.airport.exam.common.HttpResult;
5
+import com.sundot.airport.exam.common.PageData;
6
+import com.sundot.airport.exam.logic.eighteen.model.params.*;
7
+import com.sundot.airport.exam.logic.eighteen.model.result.*;
8
+import com.sundot.airport.exam.logic.eighteen.model.result.subresult.ItemIn18ExamRecordListResult;
9
+import com.sundot.airport.exam.logic.eighteen.service.EighteenService;
10
+import com.sundot.airport.exam.logic.eighteen.service.ExamOnlineService;
11
+import org.springframework.validation.annotation.Validated;
12
+import org.springframework.web.bind.annotation.*;
13
+
14
+import javax.annotation.Resource;
15
+
16
+/**
17
+ * <b>功能名:</b>EighteenController<br>
18
+ * <b>说明:</b> 十八大接口控制层 <br>
19
+ * <b>著作权:</b> Copyright (C) 2021 HUIFANEDU  CORPORATION<br>
20
+ * <b>修改履历:
21
+ *
22
+ * @author 2021-06-04 huifan
23
+ */
24
+@RestController
25
+@RequestMapping("/v1/cs/app/eighteen")
26
+public class EighteenController {
27
+
28
+    @Resource
29
+    private EighteenService eighteenService;
30
+    @Resource
31
+    private ExamOnlineService examOnlineService;
32
+
33
+
34
+
35
+    /**
36
+     * <b>方法名: </b> getExam <br>
37
+     * <b>说明: </b> 查询考试基本信息 <br>
38
+     *
39
+     * @param param Get18ExamIntroParams 查询考试基本信息参数类
40
+     * @return HttpResult<Get18ExamIntroResult>
41
+     * <b>修改履历: </b>
42
+     * @author 2021/7/5 Mokou
43
+     */
44
+    @PostMapping("/get_exam_intro")
45
+    public HttpResult<Get18ExamIntroResult> getExamIntro(@Validated @RequestBody Get18ExamIntroParams param) {
46
+        return eighteenService.getExamIntro(param);
47
+    }
48
+
49
+    /**
50
+     * <b>方法名: </b> applyExam <br>
51
+     * <b>说明: </b> 开始考试 <br>
52
+     *
53
+     * @param token java.lang.String token信息
54
+     * @param param com.sundot.airport.logic.eighteen.model.params.ExamRecAddParam 在线测试用户获取考试基本信息和试题请求参数
55
+     * @return com.huifanedu.kernel.model.response.HttpResult<com.sundot.airport.logic.eighteen.model.result.Check18VideoFinishResult><br>
56
+     * <b>修改履历: </b>
57
+     * @author 2021/7/5 liangxiao
58
+     */
59
+    @PostMapping("/get_exam")
60
+
61
+    public HttpResult<ExamGetResult> applyExam(@RequestHeader(value="Authorization", required=true) String token,
62
+                                               @Validated @RequestBody ExamRecAddParam param) {
63
+        // 十八大对应的院长端
64
+        param.setClientType(ClientTypeConstant.CLIENT_TYPE_KG_APP);
65
+        return examOnlineService.applyExam(token, param);
66
+    }
67
+
68
+    /**
69
+     * <b>方法名: </b> getExamRecordList <br>
70
+     * <b>说明: </b> 查询测试记录列表 <br>
71
+     *
72
+     * @param param Get18ExamRecordListParams 查询测试记录列表参数类
73
+     * @return HttpResult<PageData < ItemIn18ExamRecordListResult>>
74
+     * <b>修改履历: </b>
75
+     * @author 2021/7/5 Mokou
76
+     */
77
+    @PostMapping("/get_exam_record_list")
78
+    public HttpResult<PageData<ItemIn18ExamRecordListResult>> getExamRecordList(@Validated @RequestBody Get18ExamRecordListParams param) {
79
+        return eighteenService.getExamRecordList(param);
80
+    }
81
+
82
+    /**
83
+     * <b>方法名: </b> getExamRecord <br>
84
+     * <b>说明: </b> 查询测试记录详情 <br>
85
+     *
86
+     * @param param Get18ExamRecordParams 查询测试记录详情参数类
87
+     * @return HttpResult<ItemIn18ExamRecordResult>
88
+     * <b>修改履历: </b>
89
+     * @author 2021/7/5 Mokou
90
+     */
91
+    @PostMapping("/get_exam_record")
92
+    public HttpResult<ItemIn18ExamRecordResult> getExamRecord(@Validated @RequestBody Get18ExamRecordParams param) {
93
+        return eighteenService.getExamRecord(param);
94
+    }
95
+
96
+    /**
97
+     * <b>方法名: </b> submitExam <br>
98
+     * <b>说明: </b> 提交考试 <br>
99
+     *
100
+     * @param param SubmitExamParam 提交考试参数类
101
+     * @return HttpResult<SubmitExamResult>
102
+     * <b>修改履历: </b>
103
+     * @author 2021/7/6 Mokou
104
+     */
105
+    @PostMapping("/submit_exam")
106
+    public HttpResult<SubmitExamResult> submitExam(@Validated @RequestBody SubmitExamParam param) {
107
+        return eighteenService.submitExam(param);
108
+    }
109
+}

+ 139 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/exam/ExamController.java

@@ -0,0 +1,139 @@
1
+package com.sundot.airport.web.controller.exam;
2
+
3
+import com.sundot.airport.exam.common.HttpResult;
4
+import com.sundot.airport.exam.common.PageData;
5
+import com.sundot.airport.exam.logic.manage.model.params.*;
6
+import com.sundot.airport.exam.logic.manage.model.result.*;
7
+import com.sundot.airport.exam.logic.manage.service.ExamBizService;
8
+import org.springframework.web.bind.annotation.*;
9
+
10
+import javax.annotation.Resource;
11
+import javax.validation.Valid;
12
+
13
+/**
14
+ * <b>功能名:</b>ExamController<br>
15
+ * <b>说明:</b> 考试管理表管理 225200 - 225299 <br>
16
+ * <b>著作权:</b> Copyright (C) 2021 HUIFANEDU  CORPORATION<br>
17
+ * <b>修改履历:
18
+ *
19
+ * @author 2021-06-23 liangxiao
20
+ */
21
+@RestController
22
+@RequestMapping("/v1/cs/manage/exam")
23
+public class ExamController extends BaseController {
24
+
25
+    @Resource
26
+    private ExamBizService examBizService;
27
+
28
+    /**
29
+     * <b>方法名: </b> page <br>
30
+     * <b>说明: </b> 考试管理表分页列表 <br>
31
+     *
32
+     * @param param com.sundot.airport.model.params.ExamPageParam 考试管理表分页列表请求参数
33
+     * @return com.huifanedu.kernel.model.response.HttpResult<com.huifanedu.kernel.model.pager.PageData < com.sundot.airport.model.result.ExamPageResult>>
34
+     * <b>修改履历: </b>
35
+     * @author 2021-06-23 liangxiao
36
+     */
37
+    @PostMapping("/list")
38
+    public HttpResult<PageData<ExamPageResult>> page(@RequestBody ExamPageParam param) {
39
+        return examBizService.pageList(param);
40
+    }
41
+
42
+    /**
43
+     * <b>方法名: </b> get <br>
44
+     * <b>说明: </b> 考试管理表详情 <br>
45
+     *
46
+     * @param exId java.lang.String 考试业务id
47
+     * @return com.huifanedu.kernel.model.response.HttpResult<com.sundot.airport.model.result.ExamGetResult>
48
+     * <b>修改履历: </b>
49
+     * @author 2021-06-23 liangxiao
50
+     */
51
+    @GetMapping("")
52
+    public HttpResult<ExamGetResult> get(@RequestParam("exId") String exId) {
53
+        HttpResult<ExamGetResult> res = examBizService.get(exId);
54
+        if (res.getModel().getExExamTime() != null && res.getModel().getExExamTime() > 0) {
55
+            // 数据库中保存的时间为秒 前端需要分钟
56
+            res.getModel().setExExamTime(res.getModel().getExExamTime() / 60);
57
+        }
58
+        return res;
59
+    }
60
+
61
+    /**
62
+     * <b>方法名: </b> add <br>
63
+     * <b>说明: </b> 考试管理表添加 <br>
64
+     *
65
+     * @param param com.sundot.airport.model.params.ExamAddParam 考试管理表添加请求参数
66
+     * @return com.huifanedu.kernel.model.response.HttpResult<com.sundot.airport.model.result.ExamAddResult>
67
+     * <b>修改履历: </b>
68
+     * @author 2021-06-23 liangxiao
69
+     */
70
+    @PostMapping("")
71
+    public HttpResult<ExamAddResult> add(@Valid @RequestBody ExamAddParam param) {
72
+        if (param.getExExamTime() > 0) {
73
+            // 前端传入分钟 数据库中为秒
74
+            param.setExExamTime(param.getExExamTime() * 60);
75
+        }
76
+        return examBizService.add(param);
77
+    }
78
+
79
+    /**
80
+     * <b>方法名: </b> delete <br>
81
+     * <b>说明: </b> 考试管理表删除 <br>
82
+     *
83
+     * @param param com.sundot.airport.model.params.ExamDelParam 考试管理表删除请求参数
84
+     * @return com.huifanedu.kernel.model.response.HttpResult<java.lang.Boolean>
85
+     * <b>修改履历: </b>
86
+     * @author 2021-06-23 liangxiao
87
+     */
88
+    @DeleteMapping("")
89
+    public HttpResult<Boolean> del(@Valid @RequestBody ExamDelParam param) {
90
+        return examBizService.delete(param);
91
+    }
92
+
93
+    /**
94
+     * <b>方法名: </b> update <br>
95
+     * <b>说明: </b> 考试管理表修改 <br>
96
+     *
97
+     * @param param com.sundot.airport.model.params.ExamUpdParam 考试管理表修改请求参数
98
+     * @return com.huifanedu.kernel.model.response.HttpResult<java.lang.Boolean>
99
+     * <b>修改履历: </b>
100
+     * @author 2021-06-23 liangxiao
101
+     */
102
+    @PutMapping("")
103
+    public HttpResult<Boolean> update(@Valid @RequestBody ExamUpdParam param) {
104
+        if (param.getExExamTime() > 0) {
105
+            // 前端传入分钟 数据库为秒
106
+            param.setExExamTime(param.getExExamTime() * 60);
107
+        }
108
+        return examBizService.update(param);
109
+    }
110
+
111
+    /**
112
+     * <b>方法名: </b> recordList <br>
113
+     * <b>说明: </b> 考试批阅列表 <br>
114
+     *
115
+     * @param param com.sundot.airport.logic.manage.model.params.ExamRecordPageParam 考试记录查询的参数封装
116
+     * @return com.huifanedu.kernel.model.response.HttpResult<com.huifanedu.kernel.model.pager.PageData < com.sundot.airport.logic.manage.model.result.ExamRecordPageResult>><br>
117
+     * <b>修改履历: </b>
118
+     * @author 2021/7/6 liangxiao
119
+     */
120
+    @PostMapping("/record_list")
121
+    public HttpResult<PageData<ExamRecordPageResult>> recordList(@Valid @RequestBody ExamRecordPageParam param) {
122
+        return examBizService.recordList(param);
123
+    }
124
+
125
+    /**
126
+     * <b>方法名: </b> recordDetail <br>
127
+     * <b>说明: </b> 考试记录详情 <br>
128
+     *
129
+     * @param erId java.lang.String 测试记录业务id
130
+     * @return com.huifanedu.kernel.model.response.HttpResult<com.huifanedu.kernel.model.pager.PageData < com.sundot.airport.logic.manage.model.result.ExamRecordDetailResult>><br>
131
+     * <b>修改履历: </b>
132
+     * @author 2021/7/6 liangxiao
133
+     */
134
+    @GetMapping("/record_detail")
135
+    public HttpResult<ExamRecordDetailResult> recordDetail(@RequestParam("erId") String erId) {
136
+        return examBizService.recordDetail(erId);
137
+    }
138
+
139
+}

+ 104 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/exam/QuesCatController.java

@@ -0,0 +1,104 @@
1
+package com.sundot.airport.web.controller.exam;
2
+
3
+import com.sundot.airport.exam.common.HttpResult;
4
+import com.sundot.airport.exam.common.PageData;
5
+import com.sundot.airport.exam.logic.manage.model.params.QuesCatAddParam;
6
+import com.sundot.airport.exam.logic.manage.model.params.QuesCatDelParam;
7
+import com.sundot.airport.exam.logic.manage.model.params.QuesCatPageParam;
8
+import com.sundot.airport.exam.logic.manage.model.params.QuesCatUpdParam;
9
+import com.sundot.airport.exam.logic.manage.model.result.QuesCatAddResult;
10
+import com.sundot.airport.exam.logic.manage.model.result.QuesCatGetResult;
11
+import com.sundot.airport.exam.logic.manage.model.result.QuesCatPageResult;
12
+import com.sundot.airport.exam.logic.manage.service.QuesCatBizService;
13
+import org.springframework.web.bind.annotation.*;
14
+
15
+import javax.annotation.Resource;
16
+import javax.validation.Valid;
17
+
18
+/**
19
+ * <b>功能名:</b>QuesCatController<br>
20
+ * <b>说明:</b> 题目分类表管理 <br>
21
+ * <b>著作权:</b> Copyright (C) 2021 HUIFANEDU  CORPORATION<br>
22
+ * <b>修改履历:
23
+ *
24
+ * @author 2021-06-17 liangxiao
25
+ */
26
+@RestController
27
+@RequestMapping("/v1/cs/manage/ques_cat")
28
+public class QuesCatController extends BaseController {
29
+
30
+    @Resource
31
+    private QuesCatBizService quesCatBizService;
32
+
33
+    /**
34
+     * <b>方法名: </b> page <br>
35
+     * <b>说明: </b> 题目分类表分页列表 <br>
36
+     *
37
+     * @param param com.sundot.airport.model.params.QuesCatPageParam 题目分类表分页列表请求参数
38
+     * @return com.huifanedu.kernel.model.response.HttpResult<com.huifanedu.kernel.model.pager.PageData < com.sundot.airport.model.result.QuesCatPageResult>>
39
+     * <b>修改履历: </b>
40
+     * @author 2021-06-17 liangxiao
41
+     */
42
+    @PostMapping("/list")
43
+    public HttpResult<PageData<QuesCatPageResult>> page(@RequestBody QuesCatPageParam param) {
44
+        return quesCatBizService.pageList(param);
45
+    }
46
+
47
+    /**
48
+     * <b>方法名: </b> get <br>
49
+     * <b>说明: </b> 题目分类表详情 <br>
50
+     *
51
+     * @param qcId java.lang.String 题目分类的业务id
52
+     * @return com.huifanedu.kernel.model.response.HttpResult<com.sundot.airport.model.result.QuesCatGetResult>
53
+     * <b>修改履历: </b>
54
+     * @author 2021-06-17 liangxiao
55
+     */
56
+    @GetMapping("")
57
+    public HttpResult<QuesCatGetResult> get(@Valid @RequestParam("qcId") String qcId) {
58
+        return quesCatBizService.get(qcId);
59
+    }
60
+
61
+    /**
62
+     * <b>方法名: </b> add <br>
63
+     * <b>说明: </b> 题目分类表添加 <br>
64
+     *
65
+     * @param param com.sundot.airport.model.params.QuesCatDelParam 题目分类表添加请求参数
66
+     * @return com.huifanedu.kernel.model.response.HttpResult<com.sundot.airport.logic.manage.model.result.QuesCatAddResult><br>
67
+     * <b>修改履历: </b>
68
+     * @author 2021/6/18 liangxiao
69
+     */
70
+    @PostMapping("")
71
+    public HttpResult<QuesCatAddResult> add(@Valid @RequestBody QuesCatAddParam param) {
72
+        return quesCatBizService.add(param);
73
+    }
74
+
75
+    /**
76
+     * <b>方法名: </b> delete <br>
77
+     * <b>说明: </b> 题目分类表删除 <br>
78
+     *
79
+     * @param param com.sundot.airport.model.params.QuesCatDelParam 题目分类表删除请求参数
80
+     * @return com.huifanedu.kernel.model.response.HttpResult<java.lang.Boolean>
81
+     * <b>修改履历: </b>
82
+     * @author 2021-06-17 liangxiao
83
+     */
84
+    @DeleteMapping("")
85
+    public HttpResult<Boolean> del(@Valid @RequestBody QuesCatDelParam param) {
86
+        return quesCatBizService.delete(param);
87
+    }
88
+
89
+    /**
90
+     * <b>方法名: </b> update <br>
91
+     * <b>说明: </b> 题目分类表修改 <br>
92
+     *
93
+     * @param param com.sundot.airport.model.params.QuesCatUpdParam 题目分类表修改请求参数
94
+     * @return com.huifanedu.kernel.model.response.HttpResult<java.lang.Boolean>
95
+     * <b>修改履历: </b>
96
+     * @author 2021-06-17 liangxiao
97
+     */
98
+    @PutMapping("")
99
+    public HttpResult<Boolean> update(@Valid @RequestBody QuesCatUpdParam param) {
100
+        return quesCatBizService.update(param);
101
+    }
102
+
103
+
104
+}

+ 143 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/exam/QuesController.java

@@ -0,0 +1,143 @@
1
+package com.sundot.airport.web.controller.exam;
2
+
3
+import cn.hutool.core.collection.CollectionUtil;
4
+import com.sundot.airport.attendance.dto.AttendancePostRecordImportVO;
5
+import com.sundot.airport.common.annotation.Log;
6
+import com.sundot.airport.common.core.domain.AjaxResult;
7
+import com.sundot.airport.common.enums.BusinessType;
8
+import com.sundot.airport.common.exception.ServiceException;
9
+import com.sundot.airport.common.utils.poi.ExcelUtil;
10
+import com.sundot.airport.exam.common.HttpResult;
11
+import com.sundot.airport.exam.common.PageData;
12
+import com.sundot.airport.exam.dto.QuestionImportDTO;
13
+import com.sundot.airport.exam.logic.manage.model.params.*;
14
+import com.sundot.airport.exam.logic.manage.model.result.QuesAddResult;
15
+import com.sundot.airport.exam.logic.manage.model.result.QuesGetResult;
16
+import com.sundot.airport.exam.logic.manage.model.result.QuesPageResult;
17
+import com.sundot.airport.exam.logic.manage.service.QuesBizService;
18
+import org.springframework.web.bind.annotation.*;
19
+import org.springframework.web.multipart.MultipartFile;
20
+
21
+import javax.annotation.Resource;
22
+import javax.servlet.http.HttpServletResponse;
23
+import javax.validation.Valid;
24
+import java.util.List;
25
+
26
+/**
27
+ * <b>功能名:</b>QuesController<br>
28
+ * <b>说明:</b> 题目操作管理 <br>
29
+ * <b>著作权:</b> Copyright (C) 2021 HUIFANEDU  CORPORATION<br>
30
+ * <b>修改履历:
31
+ *
32
+ * @author 2021-06-18 liangxiao
33
+ */
34
+@RestController
35
+@RequestMapping("/v1/cs/manage/ques")
36
+public class QuesController extends BaseController {
37
+
38
+    @Resource
39
+    private QuesBizService quesBizService;
40
+
41
+    /**
42
+     * <b>方法名: </b> page <br>
43
+     * <b>说明: </b> 题目分页列表 <br>
44
+     *
45
+     * @param param com.sundot.airport.model.params.QuesPageParam 题目分页列表请求参数
46
+     * @return com.huifanedu.kernel.model.response.HttpResult<com.huifanedu.kernel.model.pager.PageData < com.sundot.airport.model.result.QuesPageResult>>
47
+     * <b>修改履历: </b>
48
+     * @author 2021-06-18 liangxiao
49
+     */
50
+    @PostMapping("/list")
51
+    public HttpResult<PageData<QuesPageResult>> page(@RequestBody QuesPageParam param) {
52
+        return quesBizService.pageList(param);
53
+    }
54
+
55
+    /**
56
+     * <b>方法名: </b> get <br>
57
+     * <b>说明: </b> 题目详情 <br>
58
+     *
59
+     * @param quId java.lang.String 题目业务id
60
+     * @return com.huifanedu.kernel.model.response.HttpResult<com.sundot.airport.model.result.QuesGetResult>
61
+     * <b>修改履历: </b>
62
+     * @author 2021-06-18 liangxiao
63
+     */
64
+    @GetMapping("")
65
+    public HttpResult<QuesGetResult> get(@RequestParam("quId") String quId) {
66
+        return quesBizService.get(quId);
67
+    }
68
+
69
+    /**
70
+     * <b>方法名: </b> add <br>
71
+     * <b>说明: </b> 题目添加 <br>
72
+     *
73
+     * @param param com.sundot.airport.model.params.QuesAddParam 新增题目添加请求参数
74
+     * @return com.huifanedu.kernel.model.response.HttpResult<com.sundot.airport.model.result.QuesAddResult>
75
+     * <b>修改履历: </b>
76
+     * @author 2021-06-18 liangxiao
77
+     */
78
+    @PostMapping("")
79
+    public HttpResult<QuesAddResult> add(@Valid @RequestBody QuesAddParam param) {
80
+        return quesBizService.add(param);
81
+    }
82
+
83
+    /**
84
+     * <b>方法名: </b> delete <br>
85
+     * <b>说明: </b> 题目删除 <br>
86
+     *
87
+     * @param param com.sundot.airport.model.params.QuesDelParam 题目删除请求参数
88
+     * @return com.huifanedu.kernel.model.response.HttpResult<java.lang.Boolean>
89
+     * <b>修改履历: </b>
90
+     * @author 2021-06-18 liangxiao
91
+     */
92
+    @DeleteMapping("")
93
+    public HttpResult<Boolean> del(@Valid @RequestBody QuesDelParam param) {
94
+        return quesBizService.delete(param);
95
+    }
96
+
97
+    /**
98
+     * <b>方法名: </b> update <br>
99
+     * <b>说明: </b> 题目修改 <br>
100
+     *
101
+     * @param param com.sundot.airport.model.params.QuesUpdParam QuesUpdParam
102
+     * @return com.huifanedu.kernel.model.response.HttpResult<java.lang.Boolean>
103
+     * <b>修改履历: </b>
104
+     * @author 2021-06-18 liangxiao
105
+     */
106
+    @PutMapping("")
107
+    public HttpResult<Boolean> update(@Valid @RequestBody QuesUpdParam param) {
108
+        return quesBizService.update(param);
109
+    }
110
+
111
+    /**
112
+     * <b>方法名: </b> update <br>
113
+     * <b>说明: </b> 题目分类转移 <br>
114
+     *
115
+     * @param param com.sundot.airport.model.params.QuesUpdParam 题目修改分类请求参数
116
+     * @return com.huifanedu.kernel.model.response.HttpResult<java.lang.Boolean>
117
+     * <b>修改履历: </b>
118
+     * @author 2021-06-18 liangxiao
119
+     */
120
+    @PutMapping("/cat")
121
+    public HttpResult<Boolean> batchUpdCat(@Valid @RequestBody QuesUpdCatParam param) {
122
+        return quesBizService.batchCatUpdate(param);
123
+    }
124
+
125
+    @PostMapping("/importTemplate")
126
+    public void importTemplate(HttpServletResponse response) {
127
+        ExcelUtil<QuestionImportDTO> util = new ExcelUtil<QuestionImportDTO>(QuestionImportDTO.class);
128
+        util.importTemplateExcel(response, "题目操作");
129
+    }
130
+
131
+    @Log(title = "题目操作管理", businessType = BusinessType.IMPORT)
132
+    @PostMapping("/importData")
133
+    public AjaxResult importData(MultipartFile file) throws Exception {
134
+        ExcelUtil<QuestionImportDTO> util = new ExcelUtil<>(QuestionImportDTO.class);
135
+        List<QuestionImportDTO> list = util.importExcel(file.getInputStream());
136
+        if (CollectionUtil.isEmpty(list)) {
137
+            throw new ServiceException("导入用户数据不能为空");
138
+        }
139
+        quesBizService.importData(list);
140
+        return AjaxResult.success();
141
+    }
142
+
143
+}

+ 109 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/exam/TestPaperCatController.java

@@ -0,0 +1,109 @@
1
+package com.sundot.airport.web.controller.exam;
2
+
3
+import com.sundot.airport.exam.common.HttpResult;
4
+import com.sundot.airport.exam.common.PageData;
5
+import com.sundot.airport.exam.logic.manage.model.params.TestPaperCatAddParam;
6
+import com.sundot.airport.exam.logic.manage.model.params.TestPaperCatDelParam;
7
+import com.sundot.airport.exam.logic.manage.model.params.TestPaperCatPageParam;
8
+import com.sundot.airport.exam.logic.manage.model.params.TestPaperCatUpdParam;
9
+import com.sundot.airport.exam.logic.manage.model.result.TestPaperCatAddResult;
10
+import com.sundot.airport.exam.logic.manage.model.result.TestPaperCatGetResult;
11
+import com.sundot.airport.exam.logic.manage.model.result.TestPaperCatPageResult;
12
+import com.sundot.airport.exam.logic.manage.service.TestPaperCatBizService;
13
+import org.springframework.web.bind.annotation.*;
14
+
15
+import javax.annotation.Resource;
16
+import javax.validation.Valid;
17
+
18
+/**
19
+ * <b>功能名:</b>TestPaperCatController<br>
20
+ * <b>说明:</b> 试卷分类表管理 <br>
21
+ * <b>著作权:</b> Copyright (C) 2021 HUIFANEDU  CORPORATION<br>
22
+ * <b>修改履历:
23
+ *
24
+ * @author 2021-06-18 liangxiao
25
+ */
26
+@RestController
27
+@RequestMapping("/v1/cs/manage/test_paper_cat")
28
+public class TestPaperCatController extends BaseController {
29
+
30
+    @Resource
31
+    private TestPaperCatBizService testPaperCatBizService;
32
+
33
+    /**
34
+     * <b>方法名: </b> page <br>
35
+     * <b>说明: </b> 试卷分类表分页列表 <br>
36
+     *
37
+     * @param param com.sundot.airport.model.params.TestPaperCatPageParam 试卷分类表分页列表请求参数
38
+     * @return com.huifanedu.kernel.model.response.HttpResult<com.huifanedu.kernel.model.pager.PageData < com.sundot.airport.model.result.TestPaperCatPageResult>>
39
+     * <b>修改履历: </b>
40
+     * @author 2021-06-18 liangxiao
41
+     */
42
+    @PostMapping("/list")
43
+//    @Log(key = 225101, value = "试卷分类表分页列表", mobile = SystemConstant.YGHD_LIANG_XIAO)
44
+    public HttpResult<PageData<TestPaperCatPageResult>> page(@RequestBody TestPaperCatPageParam param) {
45
+        return testPaperCatBizService.pageList(param);
46
+    }
47
+
48
+    /**
49
+     * <b>方法名: </b> get <br>
50
+     * <b>说明: </b> 试卷分类表详情 <br>
51
+     *
52
+     * @param tpcId java.lang.String 试卷分类的业务id
53
+     * @return com.huifanedu.kernel.model.response.HttpResult<com.sundot.airport.model.result.TestPaperCatGetResult>
54
+     * <b>修改履历: </b>
55
+     * @author 2021-06-18 liangxiao
56
+     */
57
+    @GetMapping("")
58
+//    @Log(key = 225102, value = "试卷分类表详情", mobile = SystemConstant.YGHD_LIANG_XIAO)
59
+    public HttpResult<TestPaperCatGetResult> get(@Valid @RequestParam("tpcId") String tpcId) {
60
+        return testPaperCatBizService.get(tpcId);
61
+    }
62
+
63
+    /**
64
+     * <b>方法名: </b> add <br>
65
+     * <b>说明: </b> 试卷分类表添加 <br>
66
+     *
67
+     * @param param com.sundot.airport.model.params.TestPaperCatAddParam 试卷分类表添加请求参数
68
+     * @return com.huifanedu.kernel.model.response.HttpResult<com.sundot.airport.model.result.TestPaperCatAddResult>
69
+     * <b>修改履历: </b>
70
+     * @author 2021-06-18 liangxiao
71
+     */
72
+    @PostMapping("")
73
+//    @Log(key = 225103, value = "试卷分类表添加", mobile = SystemConstant.YGHD_LIANG_XIAO)
74
+    public HttpResult<TestPaperCatAddResult> add(@Valid @RequestBody TestPaperCatAddParam param) {
75
+        return testPaperCatBizService.add(param);
76
+    }
77
+
78
+    /**
79
+     * <b>方法名: </b> delete <br>
80
+     * <b>说明: </b> 试卷分类表删除 <br>
81
+     *
82
+     * @param param com.sundot.airport.model.params.TestPaperCatDelParam 试卷分类表删除请求参数
83
+     * @return com.huifanedu.kernel.model.response.HttpResult<java.lang.Boolean>
84
+     * <b>修改履历: </b>
85
+     * @author 2021-06-18 liangxiao
86
+     */
87
+    @DeleteMapping("")
88
+//    @Log(key = 225104, value = "试卷分类表删除", mobile = SystemConstant.YGHD_LIANG_XIAO)
89
+    public HttpResult<Boolean> del(@Valid @RequestBody TestPaperCatDelParam param) {
90
+        return testPaperCatBizService.delete(param);
91
+    }
92
+
93
+    /**
94
+     * <b>方法名: </b> update <br>
95
+     * <b>说明: </b> 试卷分类表修改 <br>
96
+     *
97
+     * @param param com.sundot.airport.model.params.TestPaperCatUpdParam 试卷分类表修改请求参数
98
+     * @return com.huifanedu.kernel.model.response.HttpResult<java.lang.Boolean>
99
+     * <b>修改履历: </b>
100
+     * @author 2021-06-18 liangxiao
101
+     */
102
+    @PutMapping("")
103
+//    @Log(key = 225105, value = "试卷分类表修改", mobile = SystemConstant.YGHD_LIANG_XIAO)
104
+    public HttpResult<Boolean> update(@Valid @RequestBody TestPaperCatUpdParam param) {
105
+        return testPaperCatBizService.update(param);
106
+    }
107
+
108
+
109
+}

+ 135 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/exam/TestPaperController.java

@@ -0,0 +1,135 @@
1
+package com.sundot.airport.web.controller.exam;
2
+
3
+import com.sundot.airport.exam.common.HttpResult;
4
+import com.sundot.airport.exam.common.PageData;
5
+import com.sundot.airport.exam.logic.manage.model.params.*;
6
+import com.sundot.airport.exam.logic.manage.model.result.TestPaperAddResult;
7
+import com.sundot.airport.exam.logic.manage.model.result.TestPaperGetResult;
8
+import com.sundot.airport.exam.logic.manage.model.result.TestPaperPageResult;
9
+import com.sundot.airport.exam.logic.manage.service.TestPaperBizService;
10
+import org.springframework.web.bind.annotation.*;
11
+
12
+import javax.annotation.Resource;
13
+import javax.validation.Valid;
14
+
15
+/**
16
+ * <b>功能名:</b>TestPaperController<br>
17
+ * <b>说明:</b> 试卷表管理 <br>
18
+ * <b>著作权:</b> Copyright (C) 2021 HUIFANEDU  CORPORATION<br>
19
+ * <b>修改履历:
20
+ *
21
+ * @author 2021-06-22 liangxiao
22
+ */
23
+@RestController
24
+@RequestMapping("/v1/cs/manage/test_paper")
25
+public class TestPaperController extends BaseController {
26
+
27
+    @Resource
28
+    private TestPaperBizService testPaperBizService;
29
+
30
+    /**
31
+     * <b>方法名: </b> page <br>
32
+     * <b>说明: </b> 试卷表分页列表 <br>
33
+     *
34
+     * @param param com.sundot.airport.model.params.TestPaperPageParam 试卷表分页列表请求参数
35
+     * @return com.huifanedu.kernel.model.response.HttpResult<com.huifanedu.kernel.model.pager.PageData < com.sundot.airport.model.result.TestPaperPageResult>>
36
+     * <b>修改履历: </b>
37
+     * @author 2021-06-22 liangxiao
38
+     */
39
+    @PostMapping("/list")
40
+//    @Log(key = 225151, value = "试卷表分页列表", mobile = SystemConstant.YGHD_LIANG_XIAO)
41
+    public HttpResult<PageData<TestPaperPageResult>> page(@RequestBody TestPaperPageParam param) {
42
+        return testPaperBizService.pageList(param);
43
+    }
44
+
45
+    /**
46
+     * <b>方法名: </b> get <br>
47
+     * <b>说明: </b> 试卷表详情 <br>
48
+     *
49
+     * @param tpId java.lang.String 试卷业务id
50
+     * @return com.huifanedu.kernel.model.response.HttpResult<com.sundot.airport.model.result.TestPaperGetResult>
51
+     * <b>修改履历: </b>
52
+     * @author 2021-06-22 liangxiao
53
+     */
54
+    @GetMapping("")
55
+//    @Log(key = 225152, value = "试卷表详情", mobile = SystemConstant.YGHD_LIANG_XIAO)
56
+    public HttpResult<TestPaperGetResult> get(@RequestParam("tpId") String tpId) {
57
+        return testPaperBizService.get(tpId);
58
+    }
59
+
60
+    /**
61
+     * <b>方法名: </b> add <br>
62
+     * <b>说明: </b> 试卷表添加 <br>
63
+     *
64
+     * @param param com.sundot.airport.model.params.TestPaperAddParam 试卷表添加请求参数
65
+     * @return com.huifanedu.kernel.model.response.HttpResult<com.sundot.airport.model.result.TestPaperAddResult>
66
+     * <b>修改履历: </b>
67
+     * @author 2021-06-22 liangxiao
68
+     */
69
+    @PostMapping("")
70
+//    @Log(key = 225153, value = "试卷表添加", mobile = SystemConstant.YGHD_LIANG_XIAO)
71
+    public HttpResult<TestPaperAddResult> add(@Valid @RequestBody TestPaperAddParam param) {
72
+        return testPaperBizService.add(param);
73
+    }
74
+
75
+    /**
76
+     * <b>方法名: </b> delete <br>
77
+     * <b>说明: </b> 试卷表删除 <br>
78
+     *
79
+     * @param param com.sundot.airport.model.params.TestPaperDelParam 试卷表删除请求参数
80
+     * @return com.huifanedu.kernel.model.response.HttpResult<java.lang.Boolean>
81
+     * <b>修改履历: </b>
82
+     * @author 2021-06-22 liangxiao
83
+     */
84
+    @DeleteMapping("")
85
+//    @Log(key = 225154, value = "试卷表删除", mobile = SystemConstant.YGHD_LIANG_XIAO)
86
+    public HttpResult<Boolean> del(@Valid @RequestBody TestPaperDelParam param) {
87
+        return testPaperBizService.delete(param);
88
+    }
89
+
90
+    /**
91
+     * <b>方法名: </b> update <br>
92
+     * <b>说明: </b> 试卷表修改 <br>
93
+     *
94
+     * @param param com.sundot.airport.model.params.TestPaperUpdParam 试卷表修改请求参数
95
+     * @return com.huifanedu.kernel.model.response.HttpResult<java.lang.Boolean>
96
+     * <b>修改履历: </b>
97
+     * @author 2021-06-22 liangxiao
98
+     */
99
+    @PutMapping("")
100
+//    @Log(key = 225155, value = "试卷表修改", mobile = SystemConstant.YGHD_LIANG_XIAO)
101
+    public HttpResult<Boolean> update(@Valid @RequestBody TestPaperUpdParam param) {
102
+        return testPaperBizService.update(param);
103
+    }
104
+
105
+
106
+    /**
107
+     * <b>方法名: </b> update <br>
108
+     * <b>说明: </b> 试卷分类转移 <br>
109
+     *
110
+     * @param param com.sundot.airport.model.params.TestPaperUpdCatParam 试卷修改分类请求参数
111
+     * @return com.huifanedu.kernel.model.response.HttpResult<java.lang.Boolean>
112
+     * <b>修改履历: </b>
113
+     * @author 2021-06-18 liangxiao
114
+     */
115
+    @PutMapping("/cat")
116
+    //@Log(key = 225156, value = "试卷分类转移", mobile = SystemConstant.YGHD_LIANG_XIAO)
117
+    public HttpResult<Boolean> batchUpdCat(@Valid @RequestBody TestPaperUpdCatParam param) {
118
+        return testPaperBizService.batchCatUpdate(param);
119
+    }
120
+
121
+    /**
122
+     * <b>方法名: </b> copy <br>
123
+     * <b>说明: </b> 试卷复制 <br>
124
+     *
125
+     * @param param com.sundot.airport.model.params.TestPaperAddParam 试卷表添加请求参数
126
+     * @return com.huifanedu.kernel.model.response.HttpResult<com.sundot.airport.model.result.TestPaperAddResult>
127
+     * <b>修改履历: </b>
128
+     * @author 2021-06-22 liangxiao
129
+     */
130
+    @PostMapping("/copy")
131
+    //@Log(key = 225157, value = "试卷复制", mobile = SystemConstant.YGHD_LIANG_XIAO)
132
+    public HttpResult<Boolean> copy(@Valid @RequestBody TestPaperCopyParam param) {
133
+        return testPaperBizService.copy(param);
134
+    }
135
+}

+ 138 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/exam/WeakModuleRuleController.java

@@ -0,0 +1,138 @@
1
+package com.sundot.airport.web.controller.exam;
2
+
3
+import com.sundot.airport.common.core.controller.BaseController;
4
+import com.sundot.airport.common.core.domain.AjaxResult;
5
+import com.sundot.airport.common.core.page.TableDataInfo;
6
+import com.sundot.airport.common.dto.WeakModuleDetailDTO;
7
+import com.sundot.airport.exam.domain.WeakModuleRule;
8
+import com.sundot.airport.exam.dto.WeakModuleRuleDTO;
9
+import com.sundot.airport.exam.service.IWeakModuleRuleService;
10
+import com.sundot.airport.exam.service.IWeakModuleStatisticsService;
11
+import io.swagger.annotations.Api;
12
+import io.swagger.annotations.ApiOperation;
13
+import io.swagger.annotations.ApiParam;
14
+import org.springframework.beans.factory.annotation.Autowired;
15
+import org.springframework.security.access.prepost.PreAuthorize;
16
+import org.springframework.validation.annotation.Validated;
17
+import org.springframework.web.bind.annotation.*;
18
+
19
+import java.util.List;
20
+
21
+/**
22
+ * <b>功能名:</b>WeakModuleRuleController<br>
23
+ * <b>说明:</b> 薄弱项出题规则管理Controller <br>
24
+ * <b>著作权:</b> Copyright (C) 2025 SUNDOT CORPORATION<br>
25
+ * <b>修改履历:
26
+ *
27
+ * @author Simon Lin
28
+ */
29
+@Api(tags = "薄弱项出题规则管理")
30
+@RestController
31
+@RequestMapping("/exam/weakModuleRule")
32
+public class WeakModuleRuleController extends BaseController {
33
+
34
+    @Autowired
35
+    private IWeakModuleRuleService weakModuleRuleService;
36
+
37
+    @Autowired
38
+    private IWeakModuleStatisticsService weakModuleStatisticsService;
39
+
40
+    /**
41
+     * 查询规则列表
42
+     */
43
+    @ApiOperation("查询规则列表")
44
+    @PreAuthorize("@ss.hasPermi('exam:weakModuleRule:list')")
45
+    @GetMapping("/list")
46
+    public TableDataInfo list() {
47
+        startPage();
48
+        List<WeakModuleRule> list = weakModuleRuleService.list();
49
+        return getDataTable(list);
50
+    }
51
+
52
+    /**
53
+     * 获取规则详情
54
+     */
55
+    @ApiOperation("获取规则详情")
56
+    @PreAuthorize("@ss.hasPermi('exam:weakModuleRule:query')")
57
+    @GetMapping("/{id}")
58
+    public AjaxResult getInfo(@PathVariable Long id) {
59
+        return success(weakModuleRuleService.getById(id));
60
+    }
61
+
62
+    /**
63
+     * 新增规则
64
+     */
65
+    @ApiOperation("新增规则")
66
+    @PreAuthorize("@ss.hasPermi('exam:weakModuleRule:add')")
67
+    @PostMapping
68
+    public AjaxResult add(@Validated @RequestBody WeakModuleRuleDTO dto) {
69
+        return toAjax(weakModuleRuleService.insert(dto));
70
+    }
71
+
72
+    /**
73
+     * 修改规则
74
+     */
75
+    @ApiOperation("修改规则")
76
+    @PreAuthorize("@ss.hasPermi('exam:weakModuleRule:edit')")
77
+    @PutMapping
78
+    public AjaxResult edit(@Validated @RequestBody WeakModuleRuleDTO dto) {
79
+        return toAjax(weakModuleRuleService.update(dto));
80
+    }
81
+
82
+    /**
83
+     * 删除规则
84
+     */
85
+    @ApiOperation("删除规则")
86
+    @PreAuthorize("@ss.hasPermi('exam:weakModuleRule:remove')")
87
+    @DeleteMapping("/{id}")
88
+    public AjaxResult remove(@PathVariable Long id) {
89
+        return toAjax(weakModuleRuleService.deleteById(id));
90
+    }
91
+
92
+    /**
93
+     * 启用规则
94
+     */
95
+    @ApiOperation("启用规则(同时只能有一个启用)")
96
+    @PreAuthorize("@ss.hasPermi('exam:weakModuleRule:edit')")
97
+    @PutMapping("/enable/{id}")
98
+    public AjaxResult enable(@PathVariable Long id) {
99
+        return toAjax(weakModuleRuleService.enable(id));
100
+    }
101
+
102
+    /**
103
+     * 获取当前启用的规则
104
+     */
105
+    @ApiOperation("获取当前启用的规则")
106
+    @GetMapping("/active")
107
+    public AjaxResult getActive() {
108
+        WeakModuleRule activeRule = weakModuleRuleService.getActiveRule();
109
+        if (activeRule == null) {
110
+            return warn("当前没有启用的规则");
111
+        }
112
+        return success(activeRule);
113
+    }
114
+
115
+    /**
116
+     * 获取用户的TOP3薄弱模块详细信息(用于验证算法)
117
+     */
118
+    @ApiOperation("获取用户的TOP3薄弱模块详细信息")
119
+    @GetMapping("/weakModules/{userId}")
120
+    public AjaxResult getWeakModulesDetail(
121
+            @ApiParam(value = "用户ID", required = true) @PathVariable Long userId) {
122
+        List<WeakModuleDetailDTO> details = weakModuleStatisticsService.getTop3WeakModulesDetail(userId);
123
+        return success(details);
124
+    }
125
+
126
+    /**
127
+     * 获取用户的TOP3薄弱模块详细信息及出题计划(用于验证出题算法)
128
+     */
129
+    @ApiOperation("获取用户的TOP3薄弱模块详细信息及出题计划")
130
+    @GetMapping("/weakModules/{userId}/allocation")
131
+    public AjaxResult getWeakModulesDetailWithAllocation(
132
+            @ApiParam(value = "用户ID", required = true) @PathVariable Long userId,
133
+            @ApiParam(value = "题目数量", required = true) @RequestParam Integer questionCount) {
134
+        List<WeakModuleDetailDTO> details = weakModuleStatisticsService
135
+                .getTop3WeakModulesDetailWithAllocation(userId, questionCount);
136
+        return success(details);
137
+    }
138
+}

+ 150 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/item/ItemLargeScreenController.java

@@ -0,0 +1,150 @@
1
+package com.sundot.airport.web.controller.item;
2
+
3
+import com.sundot.airport.common.core.controller.BaseController;
4
+import com.sundot.airport.common.core.domain.AjaxResult;
5
+import com.sundot.airport.common.core.domain.BaseLargeScreenQueryParamDto;
6
+import com.sundot.airport.item.domain.*;
7
+import com.sundot.airport.item.service.ItemLargeScreenService;
8
+import org.springframework.beans.factory.annotation.Autowired;
9
+import org.springframework.security.access.prepost.PreAuthorize;
10
+import org.springframework.web.bind.annotation.*;
11
+
12
+import java.util.List;
13
+
14
+/**
15
+ * 查获大屏Controller
16
+ *
17
+ * @author ruoyi
18
+ * @date 2025-07-25
19
+ */
20
+@RestController
21
+@RequestMapping("/item/largeScreen")
22
+public class ItemLargeScreenController extends BaseController {
23
+
24
+    @Autowired
25
+    private ItemLargeScreenService itemLargeScreenService;
26
+
27
+    /**
28
+     * 移交公安情况
29
+     */
30
+    @PreAuthorize("@ss.hasPermi('item:record:list')")
31
+    @GetMapping("/police")
32
+    public AjaxResult police(BaseLargeScreenQueryParamDto dto) {
33
+        List<ItemLargeScreenInfoDto> result = itemLargeScreenService.police(dto);
34
+        return success(result);
35
+    }
36
+
37
+    /**
38
+     * 故意隐匿情况
39
+     */
40
+    @PreAuthorize("@ss.hasPermi('item:record:list')")
41
+    @GetMapping("/conceal")
42
+    public AjaxResult conceal(BaseLargeScreenQueryParamDto dto) {
43
+        List<ItemLargeScreenInfoDto> result = itemLargeScreenService.conceal(dto);
44
+        return success(result);
45
+    }
46
+
47
+    /**
48
+     * 查获类别分布
49
+     */
50
+    @PreAuthorize("@ss.hasPermi('item:record:list')")
51
+    @GetMapping("/category")
52
+    public AjaxResult category(BaseLargeScreenQueryParamDto dto) {
53
+        List<ItemLargeScreenCommonDto> result = itemLargeScreenService.category(dto);
54
+        return success(result);
55
+    }
56
+
57
+    /**
58
+     * 查获岗位分布
59
+     */
60
+    @PreAuthorize("@ss.hasPermi('item:record:list')")
61
+    @GetMapping("/post")
62
+    public AjaxResult post(BaseLargeScreenQueryParamDto dto) {
63
+        List<ItemLargeScreenCommonDto> result = itemLargeScreenService.post(dto);
64
+        return success(result);
65
+    }
66
+
67
+    /**
68
+     * 查获总数量+移交公安数量+故意隐匿数量
69
+     */
70
+    @PreAuthorize("@ss.hasPermi('item:record:list')")
71
+    @GetMapping("/getTotalSome")
72
+    public AjaxResult getTotalSome(BaseLargeScreenQueryParamDto dto) {
73
+        ItemLargeScreenTotalSomeDto result = itemLargeScreenService.getTotalSome(dto);
74
+        return success(result);
75
+    }
76
+
77
+    /**
78
+     * app端查获总数量+移交公安数量+故意隐匿数量
79
+     */
80
+    @PreAuthorize("@ss.hasPermi('item:record:list')")
81
+    @GetMapping("/getAppTotalSome")
82
+    public AjaxResult getAppTotalSome(BaseLargeScreenQueryParamDto dto) {
83
+        ItemLargeScreenTotalSomeDto result = itemLargeScreenService.getAppTotalSome(dto);
84
+        return success(result);
85
+    }
86
+
87
+    /**
88
+     * 查获排名
89
+     */
90
+    @PreAuthorize("@ss.hasPermi('item:record:list')")
91
+    @GetMapping("/rank")
92
+    public AjaxResult rank(BaseLargeScreenQueryParamDto dto, int type) {
93
+        List<ItemLargeScreenCommonDto> result = itemLargeScreenService.rank(dto, type);
94
+        return success(result);
95
+    }
96
+
97
+    /**
98
+     * 查获时段分布
99
+     */
100
+    @PreAuthorize("@ss.hasPermi('item:record:list')")
101
+    @GetMapping("/timeSpan")
102
+    public AjaxResult timeSpan(BaseLargeScreenQueryParamDto dto) {
103
+        List<ItemLargeScreenTimeSpanDto> result = itemLargeScreenService.timeSpan(dto);
104
+        return success(result);
105
+    }
106
+
107
+    /**
108
+     * 查获时段分布
109
+     */
110
+    @PreAuthorize("@ss.hasPermi('item:record:list')")
111
+    @GetMapping("/appTimeSpan")
112
+    public AjaxResult appTimeSpan(BaseLargeScreenQueryParamDto dto) {
113
+        List<ItemLargeScreenTimeSpanDto> result = itemLargeScreenService.appTimeSpan(dto);
114
+        return success(result);
115
+    }
116
+
117
+    /**
118
+     * 查获位置分布
119
+     */
120
+    @PreAuthorize("@ss.hasPermi('item:record:list')")
121
+    @GetMapping("/position")
122
+    public AjaxResult position(BaseLargeScreenQueryParamDto dto) {
123
+        List<ItemLargeScreenPositionDto> result = itemLargeScreenService.position(dto);
124
+        return success(result);
125
+    }
126
+
127
+    /**
128
+     *  app端 查获位置分布
129
+     * @param dto 大屏查询参数
130
+     * @return 查获位置分布
131
+     */
132
+    @PreAuthorize("@ss.hasPermi('item:record:list')")
133
+    @GetMapping("/appPosition")
134
+    public AjaxResult appPosition(BaseLargeScreenQueryParamDto dto) {
135
+        List<ItemLargeScreenPositionAppDto> result = itemLargeScreenService.appPosition(dto);
136
+        return success(result);
137
+    }
138
+
139
+    /**
140
+     * 查获通道分布
141
+     */
142
+    @PreAuthorize("@ss.hasPermi('item:record:list')")
143
+    @GetMapping("/channel")
144
+    public AjaxResult channel(BaseLargeScreenQueryParamDto dto) {
145
+        List<ItemLargeScreenChannelDto> result = itemLargeScreenService.channel(dto);
146
+        return success(result);
147
+    }
148
+
149
+
150
+}

+ 274 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/item/ItemSeizureItemsController.java

@@ -0,0 +1,274 @@
1
+package com.sundot.airport.web.controller.item;
2
+
3
+import java.util.Comparator;
4
+import java.util.List;
5
+import java.util.stream.Collectors;
6
+import javax.servlet.http.HttpServletResponse;
7
+
8
+import com.sundot.airport.common.core.domain.entity.SysDept;
9
+import com.sundot.airport.common.enums.DeptTypeEnum;
10
+import com.sundot.airport.common.enums.PostTypeEnum;
11
+import com.sundot.airport.item.domain.*;
12
+import com.sundot.airport.system.domain.SysPost;
13
+import com.sundot.airport.system.service.ISysDeptService;
14
+import com.sundot.airport.system.service.ISysPostService;
15
+import org.apache.commons.lang3.ObjectUtils;
16
+import org.springframework.security.access.prepost.PreAuthorize;
17
+import org.springframework.beans.factory.annotation.Autowired;
18
+import org.springframework.util.CollectionUtils;
19
+import org.springframework.web.bind.annotation.GetMapping;
20
+import org.springframework.web.bind.annotation.PostMapping;
21
+import org.springframework.web.bind.annotation.PutMapping;
22
+import org.springframework.web.bind.annotation.DeleteMapping;
23
+import org.springframework.web.bind.annotation.PathVariable;
24
+import org.springframework.web.bind.annotation.RequestBody;
25
+import org.springframework.web.bind.annotation.RequestMapping;
26
+import org.springframework.web.bind.annotation.RestController;
27
+import com.sundot.airport.common.annotation.Log;
28
+import com.sundot.airport.common.core.controller.BaseController;
29
+import com.sundot.airport.common.core.domain.AjaxResult;
30
+import com.sundot.airport.common.enums.BusinessType;
31
+import com.sundot.airport.item.service.IItemSeizureItemsService;
32
+import com.sundot.airport.common.utils.poi.ExcelUtil;
33
+import com.sundot.airport.common.core.page.TableDataInfo;
34
+
35
+/**
36
+ * 查获物品明细Controller
37
+ *
38
+ * @author ruoyi
39
+ * @date 2025-07-10
40
+ */
41
+@RestController
42
+@RequestMapping("/item/items")
43
+public class ItemSeizureItemsController extends BaseController {
44
+    @Autowired
45
+    private ISysPostService sysPostService;
46
+    @Autowired
47
+    private ISysDeptService sysDeptService;
48
+    @Autowired
49
+    private IItemSeizureItemsService itemSeizureItemsService;
50
+
51
+    /**
52
+     * 查询查获物品明细列表
53
+     */
54
+    @PreAuthorize("@ss.hasPermi('item:items:list')")
55
+    @GetMapping("/list")
56
+    public TableDataInfo list(ItemSeizureItems itemSeizureItems) {
57
+        startPage();
58
+        List<ItemSeizureItems> list = itemSeizureItemsService.selectItemSeizureItemsList(itemSeizureItems);
59
+        return getDataTable(list);
60
+    }
61
+
62
+    /**
63
+     * 导出查获物品明细列表
64
+     */
65
+    @PreAuthorize("@ss.hasPermi('item:items:export')")
66
+    @Log(title = "查获物品明细", businessType = BusinessType.EXPORT)
67
+    @PostMapping("/export")
68
+    public void export(HttpServletResponse response, ItemSeizureItems itemSeizureItems) {
69
+        List<ItemSeizureItems> list = itemSeizureItemsService.selectItemSeizureItemsList(itemSeizureItems);
70
+        ExcelUtil<ItemSeizureItems> util = new ExcelUtil<ItemSeizureItems>(ItemSeizureItems.class);
71
+        util.exportExcel(response, list, "查获物品明细数据");
72
+    }
73
+
74
+    /**
75
+     * 获取查获物品明细详细信息
76
+     */
77
+    @PreAuthorize("@ss.hasPermi('item:items:query')")
78
+    @GetMapping(value = "/{id}")
79
+    public AjaxResult getInfo(@PathVariable("id") Long id) {
80
+        return success(itemSeizureItemsService.selectItemSeizureItemsById(id));
81
+    }
82
+
83
+    /**
84
+     * 新增查获物品明细
85
+     */
86
+    @PreAuthorize("@ss.hasPermi('item:items:add')")
87
+    @Log(title = "查获物品明细", businessType = BusinessType.INSERT)
88
+    @PostMapping
89
+    public AjaxResult add(@RequestBody ItemSeizureItems itemSeizureItems) {
90
+        itemSeizureItems.setCreateBy(getUsername());
91
+        return toAjax(itemSeizureItemsService.insertItemSeizureItems(itemSeizureItems));
92
+    }
93
+
94
+    /**
95
+     * 修改查获物品明细
96
+     */
97
+    @PreAuthorize("@ss.hasPermi('item:items:edit')")
98
+    @Log(title = "查获物品明细", businessType = BusinessType.UPDATE)
99
+    @PutMapping
100
+    public AjaxResult edit(@RequestBody ItemSeizureItems itemSeizureItems) {
101
+        itemSeizureItems.setUpdateBy(getUsername());
102
+        return toAjax(itemSeizureItemsService.updateItemSeizureItems(itemSeizureItems));
103
+    }
104
+
105
+    /**
106
+     * 删除查获物品明细
107
+     */
108
+    @PreAuthorize("@ss.hasPermi('item:items:remove')")
109
+    @Log(title = "查获物品明细", businessType = BusinessType.DELETE)
110
+    @DeleteMapping("/{ids}")
111
+    public AjaxResult remove(@PathVariable Long[] ids) {
112
+        return toAjax(itemSeizureItemsService.deleteItemSeizureItemsByIds(ids));
113
+    }
114
+
115
+    /**
116
+     * 根据记录ID查询查获物品明细列表
117
+     */
118
+    @PreAuthorize("@ss.hasPermi('item:items:list')")
119
+    @GetMapping("/listByRecorId")
120
+    public AjaxResult listByRecorId(Long recordId) {
121
+        return success(itemSeizureItemsService.listByRecorId(recordId));
122
+    }
123
+
124
+    /**
125
+     * 根据筛选条件查询查获物品明细列表
126
+     */
127
+    @PreAuthorize("@ss.hasPermi('item:items:list')")
128
+    @GetMapping("/selectByConditions")
129
+    public TableDataInfo selectByConditions(ItemSeizureParam itemSeizureParam) {
130
+        if (itemSeizureParam.getRecordType() == 1) {
131
+            itemSeizureParam.setInspectUserId(getUserId());
132
+        } else {
133
+            itemSeizureParam.setInspectUserId(null);
134
+            List<Long> userPostList = sysPostService.selectPostListByUserId(getUserId());
135
+            SysDept sysDept = sysDeptService.selectDeptById(getDeptId());
136
+            if (CollectionUtils.isEmpty(userPostList) || ObjectUtils.isEmpty(sysDept)) {
137
+                itemSeizureParam.setInspectUserId(getUserId());
138
+            } else {
139
+                if (sysDept.getDeptType().equals(DeptTypeEnum.STATION.getCode())) {
140
+                    itemSeizureParam.setInspectStationId(sysDept.getDeptId());
141
+                } else if (sysDept.getDeptType().equals(DeptTypeEnum.DEPARTMENT.getCode())) {
142
+                    itemSeizureParam.setInspectDepartmentId(sysDept.getDeptId());
143
+                } else if (sysDept.getDeptType().equals(DeptTypeEnum.TEAMS.getCode())) {
144
+                    itemSeizureParam.setInspectTeamId(sysDept.getDeptId());
145
+                } else {
146
+                    itemSeizureParam.setInspectUserId(getUserId());
147
+                }
148
+//                List<SysPost> sysPostList = sysPostService.selectPostAll();
149
+//                sysPostList = sysPostList.stream().filter(sysPost -> userPostList.contains(sysPost.getPostId())).collect(Collectors.toList());
150
+//                PostTypeEnum typeEnum = sysPostList.stream()
151
+//                        .map(item -> {
152
+//                            String postCode = item.getPostCode();
153
+//                            return postCode != null ? PostTypeEnum.getByCode(postCode) : null;
154
+//                        })
155
+//                        .filter(java.util.Objects::nonNull)
156
+//                        .min(Comparator.comparingInt(PostTypeEnum::getLevel))
157
+//                        .orElse(null);
158
+//                if (typeEnum == null || typeEnum == PostTypeEnum.security_inspector) {
159
+//                    itemSeizureParam.setInspectUserId(getUserId());
160
+//                } else {
161
+//                    if (sysDept.getDeptType().equals(DeptTypeEnum.STATION.getCode())) {
162
+//                        itemSeizureParam.setInspectStationId(sysDept.getDeptId());
163
+//                    } else if (sysDept.getDeptType().equals(DeptTypeEnum.DEPARTMENT.getCode())) {
164
+//                        itemSeizureParam.setInspectDepartmentId(sysDept.getDeptId());
165
+//                    } else if (sysDept.getDeptType().equals(DeptTypeEnum.TEAMS.getCode())) {
166
+//                        itemSeizureParam.setInspectTeamId(sysDept.getDeptId());
167
+//                    } else {
168
+//                        itemSeizureParam.setInspectUserId(getUserId());
169
+//                    }
170
+//                }
171
+            }
172
+        }
173
+        startPage();
174
+        List<ItemSeizureDto> list = itemSeizureItemsService.selectByConditions(itemSeizureParam);
175
+        return getDataTable(list);
176
+    }
177
+
178
+    /**
179
+     * 根据筛选条件查询分类数量
180
+     */
181
+    @PreAuthorize("@ss.hasPermi('item:items:list')")
182
+    @GetMapping("/selectGroupCount")
183
+    public AjaxResult selectGroupCount(ItemSeizureParam itemSeizureParam) {
184
+        if (itemSeizureParam.getRecordType() == 1) {
185
+            itemSeizureParam.setInspectUserId(getUserId());
186
+        } else {
187
+            itemSeizureParam.setInspectUserId(null);
188
+            List<Long> userPostList = sysPostService.selectPostListByUserId(getUserId());
189
+            SysDept sysDept = sysDeptService.selectDeptById(getDeptId());
190
+            if (CollectionUtils.isEmpty(userPostList) || ObjectUtils.isEmpty(sysDept)) {
191
+                itemSeizureParam.setInspectUserId(getUserId());
192
+            } else {
193
+                List<SysPost> sysPostList = sysPostService.selectPostAll();
194
+                sysPostList = sysPostList.stream().filter(sysPost -> userPostList.contains(sysPost.getPostId())).collect(Collectors.toList());
195
+                PostTypeEnum typeEnum = sysPostList.stream()
196
+                        .map(item -> {
197
+                            String postCode = item.getPostCode();
198
+                            return postCode != null ? PostTypeEnum.getByCode(postCode) : null;
199
+                        })
200
+                        .filter(java.util.Objects::nonNull)
201
+                        .min(Comparator.comparingInt(PostTypeEnum::getLevel))
202
+                        .orElse(null);
203
+                if (typeEnum == null || typeEnum == PostTypeEnum.security_inspector) {
204
+                    itemSeizureParam.setInspectUserId(getUserId());
205
+                } else {
206
+                    if (sysDept.getDeptType().equals(DeptTypeEnum.STATION.getCode())) {
207
+                        itemSeizureParam.setInspectStationId(sysDept.getDeptId());
208
+                    } else if (sysDept.getDeptType().equals(DeptTypeEnum.DEPARTMENT.getCode())) {
209
+                        itemSeizureParam.setInspectDepartmentId(sysDept.getDeptId());
210
+                    } else if (sysDept.getDeptType().equals(DeptTypeEnum.TEAMS.getCode())) {
211
+                        itemSeizureParam.setInspectTeamId(sysDept.getDeptId());
212
+                    } else {
213
+                        itemSeizureParam.setInspectUserId(getUserId());
214
+                    }
215
+                }
216
+            }
217
+        }
218
+        List<ItemSeizureCount> list = itemSeizureItemsService.selectGroupCount(itemSeizureParam);
219
+        return success(list);
220
+    }
221
+
222
+    /**
223
+     * 根据筛选条件查询数量
224
+     */
225
+    @PreAuthorize("@ss.hasPermi('item:items:list')")
226
+    @GetMapping("/selectCount")
227
+    public AjaxResult selectCount(ItemSeizureParam itemSeizureParam) {
228
+        if (itemSeizureParam.getRecordType() == 1) {
229
+            itemSeizureParam.setInspectUserId(getUserId());
230
+        } else {
231
+            itemSeizureParam.setInspectUserId(null);
232
+            List<Long> userPostList = sysPostService.selectPostListByUserId(getUserId());
233
+            SysDept sysDept = sysDeptService.selectDeptById(getDeptId());
234
+            if (CollectionUtils.isEmpty(userPostList) || ObjectUtils.isEmpty(sysDept)) {
235
+                itemSeizureParam.setInspectUserId(getUserId());
236
+            } else {
237
+                if (sysDept.getDeptType().equals(DeptTypeEnum.STATION.getCode())) {
238
+                    itemSeizureParam.setInspectStationId(sysDept.getDeptId());
239
+                } else if (sysDept.getDeptType().equals(DeptTypeEnum.DEPARTMENT.getCode())) {
240
+                    itemSeizureParam.setInspectDepartmentId(sysDept.getDeptId());
241
+                } else if (sysDept.getDeptType().equals(DeptTypeEnum.TEAMS.getCode())) {
242
+                    itemSeizureParam.setInspectTeamId(sysDept.getDeptId());
243
+                } else {
244
+                    itemSeizureParam.setInspectUserId(getUserId());
245
+                }
246
+//                List<SysPost> sysPostList = sysPostService.selectPostAll();
247
+//                sysPostList = sysPostList.stream().filter(sysPost -> userPostList.contains(sysPost.getPostId())).collect(Collectors.toList());
248
+//                PostTypeEnum typeEnum = sysPostList.stream()
249
+//                        .map(item -> {
250
+//                            String postCode = item.getPostCode();
251
+//                            return postCode != null ? PostTypeEnum.getByCode(postCode) : null;
252
+//                        })
253
+//                        .filter(java.util.Objects::nonNull)
254
+//                        .min(Comparator.comparingInt(PostTypeEnum::getLevel))
255
+//                        .orElse(null);
256
+//                if (typeEnum == null || typeEnum == PostTypeEnum.security_inspector) {
257
+//                    itemSeizureParam.setInspectUserId(getUserId());
258
+//                } else {
259
+//                    if (sysDept.getDeptType().equals(DeptTypeEnum.STATION.getCode())) {
260
+//                        itemSeizureParam.setInspectStationId(sysDept.getDeptId());
261
+//                    } else if (sysDept.getDeptType().equals(DeptTypeEnumDeptTypeEnumDeptTypeEnumDeptTypeEnum)) {
262
+//                        itemSeizureParam.setInspectDepartmentId(sysDept.getDeptId());
263
+//                    } else if (sysDept.getDeptType().equals(DeptTypeEnum.TEAMS.getCode())) {
264
+//                        itemSeizureParam.setInspectTeamId(sysDept.getDeptId());
265
+//                    } else {
266
+//                        itemSeizureParam.setInspectUserId(getUserId());
267
+//                    }
268
+//                }
269
+            }
270
+        }
271
+        ItemSeizureTotal result = itemSeizureItemsService.selectCount(itemSeizureParam);
272
+        return success(result);
273
+    }
274
+}

+ 234 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/item/ItemSeizureRecordController.java

@@ -0,0 +1,234 @@
1
+package com.sundot.airport.web.controller.item;
2
+
3
+import java.util.ArrayList;
4
+import java.util.List;
5
+import javax.servlet.http.HttpServletResponse;
6
+
7
+import cn.hutool.core.util.ObjectUtil;
8
+import com.sundot.airport.common.dto.UserInfo;
9
+import com.sundot.airport.common.exception.ServiceException;
10
+import com.sundot.airport.common.utils.DictUtils;
11
+import com.sundot.airport.common.utils.StringUtils;
12
+import com.sundot.airport.web.core.utils.DataPermissionUtils;
13
+import com.sundot.airport.item.domain.ItemSeizureItems;
14
+import com.sundot.airport.item.domain.ItemSeizureRecordDTO;
15
+import com.sundot.airport.item.domain.vo.ItemSeizureRecordExportVO;
16
+import com.sundot.airport.system.service.ISysDeptService;
17
+import com.sundot.airport.system.service.ISysPostService;
18
+import com.sundot.airport.web.core.cache.UserCache;
19
+import com.sundot.airport.common.core.domain.DataPermissionResult;
20
+import org.springframework.security.access.prepost.PreAuthorize;
21
+import org.springframework.beans.factory.annotation.Autowired;
22
+import org.springframework.web.bind.annotation.GetMapping;
23
+import org.springframework.web.bind.annotation.PostMapping;
24
+import org.springframework.web.bind.annotation.PutMapping;
25
+import org.springframework.web.bind.annotation.DeleteMapping;
26
+import org.springframework.web.bind.annotation.PathVariable;
27
+import org.springframework.web.bind.annotation.RequestBody;
28
+import org.springframework.web.bind.annotation.RequestMapping;
29
+import org.springframework.web.bind.annotation.RestController;
30
+import com.sundot.airport.common.annotation.Log;
31
+import com.sundot.airport.common.core.controller.BaseController;
32
+import com.sundot.airport.common.core.domain.AjaxResult;
33
+import com.sundot.airport.common.enums.BusinessType;
34
+import com.sundot.airport.item.domain.ItemSeizureRecord;
35
+import com.sundot.airport.item.service.IItemSeizureRecordService;
36
+import com.sundot.airport.common.utils.poi.ExcelUtil;
37
+import com.sundot.airport.common.core.page.TableDataInfo;
38
+
39
+/**
40
+ * 查获记录Controller
41
+ *
42
+ * @author ruoyi
43
+ * @date 2025-07-08
44
+ */
45
+@RestController
46
+@RequestMapping("/item/record")
47
+public class ItemSeizureRecordController extends BaseController {
48
+    @Autowired
49
+    private UserCache userCache;
50
+    @Autowired
51
+    private ISysPostService sysPostService;
52
+    @Autowired
53
+    private ISysDeptService sysDeptService;
54
+    @Autowired
55
+    private IItemSeizureRecordService itemSeizureRecordService;
56
+
57
+    /**
58
+     * 查询查获记录列表
59
+     */
60
+    @PreAuthorize("@ss.hasPermi('item:record:list')")
61
+    @GetMapping("/list")
62
+    public TableDataInfo list(ItemSeizureRecordDTO itemSeizureRecord) {
63
+        // 应用数据权限过滤
64
+        DataPermissionResult dataPermission = DataPermissionUtils.getDataPermission(getUserId(), getDeptId(), getLoginUser());
65
+        switch (dataPermission.getPermissionType()) {
66
+            case SELF:
67
+                itemSeizureRecord.setInspectUserId(dataPermission.getValue());
68
+                break;
69
+            case STATION:
70
+                itemSeizureRecord.setInspectStationId(dataPermission.getValue());
71
+                break;
72
+            case DEPARTMENT:
73
+                itemSeizureRecord.setInspectDepartmentId(dataPermission.getValue());
74
+                break;
75
+            case TEAM:
76
+                itemSeizureRecord.setInspectTeamId(dataPermission.getValue());
77
+                break;
78
+            case ALL:
79
+            default:
80
+                // 不设置过滤条件,查看所有数据
81
+                break;
82
+        }
83
+        
84
+        startPage();
85
+        List<ItemSeizureRecord> list = itemSeizureRecordService.selectItemSeizureRecordList(itemSeizureRecord);
86
+        return getDataTable(list);
87
+    }
88
+
89
+    /**
90
+     * 导出查获记录列表
91
+     */
92
+    @PreAuthorize("@ss.hasPermi('item:record:export')")
93
+    @Log(title = "查获记录", businessType = BusinessType.EXPORT)
94
+    @PostMapping("/export")
95
+    public void export(HttpServletResponse response, ItemSeizureRecordDTO itemSeizureRecord) {
96
+        // 应用数据权限过滤
97
+        DataPermissionResult dataPermission = DataPermissionUtils.getDataPermission(getUserId(), getDeptId(), getLoginUser());
98
+        switch (dataPermission.getPermissionType()) {
99
+            case SELF:
100
+                itemSeizureRecord.setInspectUserId(dataPermission.getValue());
101
+                break;
102
+            case STATION:
103
+                itemSeizureRecord.setInspectStationId(dataPermission.getValue());
104
+                break;
105
+            case DEPARTMENT:
106
+                itemSeizureRecord.setInspectDepartmentId(dataPermission.getValue());
107
+                break;
108
+            case TEAM:
109
+                itemSeizureRecord.setInspectTeamId(dataPermission.getValue());
110
+                break;
111
+            case ALL:
112
+            default:
113
+                // 不设置过滤条件,查看所有数据
114
+                break;
115
+        }
116
+        
117
+        List<ItemSeizureRecord> list = itemSeizureRecordService.selectItemSeizureRecordListForExport(itemSeizureRecord);
118
+        exportData(response, list);
119
+    }
120
+    
121
+    /**
122
+     * 导出查获记录数据
123
+     * 
124
+     * @param response 响应对象
125
+     * @param list 查获记录列表
126
+     */
127
+    private void exportData(HttpServletResponse response, List<ItemSeizureRecord> list) {
128
+        List<ItemSeizureRecordExportVO> exportList = new ArrayList<>();
129
+        
130
+        // 直接使用已查询到的记录,避免再次查询数据库
131
+        for (ItemSeizureRecord record : list) {
132
+            if (record != null) {
133
+                // 处理没有物品的记录
134
+                if (record.getItemSeizureItemsList() == null || record.getItemSeizureItemsList().isEmpty()) {
135
+                    ItemSeizureRecordExportVO exportVO = new ItemSeizureRecordExportVO();
136
+                    // 基本信息
137
+                    exportVO.setInspectUserName(record.getInspectUserName());
138
+                    exportVO.setSeizureTime(record.getSeizureTime());
139
+                    exportVO.setChannelName(record.getChannelName());
140
+                    exportVO.setCheckMethodDesc(record.getCheckMethodDesc());
141
+                    exportVO.setInspectTeamName(record.getInspectTeamName());
142
+                    exportVO.setAttendanceTeamName(record.getAttendanceTeamName());
143
+                    
144
+                    // 物品信息为空
145
+                    exportVO.setCategoryNameTwo("");
146
+                    exportVO.setQuantity(null);
147
+                    exportVO.setCheckPositionNameTwo("");
148
+                    exportVO.setHandlingMethod("");
149
+                    exportVO.setIsActiveConcealmentDesc("");
150
+                    
151
+                    exportList.add(exportVO);
152
+                } else {
153
+                    // 处理有物品的记录
154
+                    for (ItemSeizureItems item : record.getItemSeizureItemsList()) {
155
+                        ItemSeizureRecordExportVO exportVO = new ItemSeizureRecordExportVO();
156
+                        // 基本信息
157
+                        exportVO.setInspectUserName(record.getInspectUserName());
158
+                        exportVO.setSeizureTime(record.getSeizureTime());
159
+                        exportVO.setChannelName(record.getChannelName());
160
+                        exportVO.setCheckMethodDesc(record.getCheckMethodDesc());
161
+                        exportVO.setInspectTeamName(record.getInspectTeamName());
162
+                        exportVO.setAttendanceTeamName(record.getAttendanceTeamName());
163
+                        
164
+                        // 物品信息
165
+                        exportVO.setCategoryNameTwo(item.getCategoryNameOne()+"/"+item.getCategoryNameTwo());
166
+                        exportVO.setQuantity(item.getQuantity());
167
+                        exportVO.setCheckPositionNameTwo(item.getCheckPositionNameOne()+"/"+item.getCheckPositionNameTwo());
168
+                        //处理方式查字典
169
+                        String handlingMethodLabel = DictUtils.getDictLabel("item_handling_method", item.getHandlingMethod());
170
+                        exportVO.setHandlingMethod(StringUtils.isNotEmpty(handlingMethodLabel) ? handlingMethodLabel : item.getHandlingMethod());
171
+                        exportVO.setIsActiveConcealmentDesc("1".equals(String.valueOf(item.getIsActiveConcealment())) ? "是" : "否");
172
+                        
173
+                        exportList.add(exportVO);
174
+                    }
175
+                }
176
+            }
177
+        }
178
+        
179
+        ExcelUtil<ItemSeizureRecordExportVO> util = new ExcelUtil<ItemSeizureRecordExportVO>(ItemSeizureRecordExportVO.class);
180
+        util.exportExcel(response, exportList, "查获记录数据");
181
+    }
182
+
183
+    /**
184
+     * 获取查获记录详细信息
185
+     */
186
+    @PreAuthorize("@ss.hasPermi('item:record:query')")
187
+    @GetMapping(value = "/{id}")
188
+    public AjaxResult getInfo(@PathVariable("id") Long id) {
189
+        return success(itemSeizureRecordService.selectItemSeizureRecordById(id));
190
+    }
191
+
192
+    /**
193
+     * 新增查获记录
194
+     */
195
+    @PreAuthorize("@ss.hasPermi('item:record:add')")
196
+    @Log(title = "查获记录", businessType = BusinessType.INSERT)
197
+    @PostMapping("/add")
198
+    public AjaxResult add(@RequestBody ItemSeizureRecord itemSeizureRecord) {
199
+        itemSeizureRecord.setCreateBy(getUsername());
200
+        UserInfo userInfo = userCache.getUserInfo(itemSeizureRecord.getInspectUserId());
201
+        if (ObjectUtil.isNull(userInfo) || ObjectUtil.isNull(userInfo.getStationId()) || ObjectUtil.isNull(userInfo.getDepartmentId()) || ObjectUtil.isNull(userInfo.getTeamsId())) {
202
+            throw new ServiceException("操作失败,检查人员非班组成员");
203
+        }
204
+        itemSeizureRecord.setInspectStationId(userInfo.getStationId());
205
+        itemSeizureRecord.setInspectStationName(userInfo.getStationName());
206
+        itemSeizureRecord.setInspectDepartmentId(userInfo.getDepartmentId());
207
+        itemSeizureRecord.setInspectDepartmentName(userInfo.getDepartmentName());
208
+        itemSeizureRecord.setInspectTeamId(userInfo.getTeamsId());
209
+        itemSeizureRecord.setInspectTeamName(userInfo.getTeamsName());
210
+        itemSeizureRecord.getItemSeizureItemsList().forEach(itemSeizureItems -> itemSeizureItems.setCreateBy(getUsername()));
211
+        return success(itemSeizureRecordService.insertItemSeizureRecord(itemSeizureRecord));
212
+    }
213
+
214
+    /**
215
+     * 修改查获记录
216
+     */
217
+    @PreAuthorize("@ss.hasPermi('item:record:edit')")
218
+    @Log(title = "查获记录", businessType = BusinessType.UPDATE)
219
+    @PutMapping
220
+    public AjaxResult edit(@RequestBody ItemSeizureRecord itemSeizureRecord) {
221
+        itemSeizureRecord.setUpdateBy(getUsername());
222
+        return toAjax(itemSeizureRecordService.updateItemSeizureRecord(itemSeizureRecord));
223
+    }
224
+
225
+    /**
226
+     * 删除查获记录
227
+     */
228
+    @PreAuthorize("@ss.hasPermi('item:record:remove')")
229
+    @Log(title = "查获记录", businessType = BusinessType.DELETE)
230
+    @DeleteMapping("/{ids}")
231
+    public AjaxResult remove(@PathVariable Long[] ids) {
232
+        return toAjax(itemSeizureRecordService.deleteItemSeizureRecordByIds(ids));
233
+    }
234
+}

+ 121 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/monitor/CacheController.java

@@ -0,0 +1,121 @@
1
+package com.sundot.airport.web.controller.monitor;
2
+
3
+import java.util.ArrayList;
4
+import java.util.Collection;
5
+import java.util.HashMap;
6
+import java.util.List;
7
+import java.util.Map;
8
+import java.util.Properties;
9
+import java.util.Set;
10
+import java.util.TreeSet;
11
+import org.springframework.beans.factory.annotation.Autowired;
12
+import org.springframework.data.redis.core.RedisCallback;
13
+import org.springframework.data.redis.core.RedisTemplate;
14
+import org.springframework.security.access.prepost.PreAuthorize;
15
+import org.springframework.web.bind.annotation.DeleteMapping;
16
+import org.springframework.web.bind.annotation.GetMapping;
17
+import org.springframework.web.bind.annotation.PathVariable;
18
+import org.springframework.web.bind.annotation.RequestMapping;
19
+import org.springframework.web.bind.annotation.RestController;
20
+import com.sundot.airport.common.constant.CacheConstants;
21
+import com.sundot.airport.common.core.domain.AjaxResult;
22
+import com.sundot.airport.common.utils.StringUtils;
23
+import com.sundot.airport.system.domain.SysCache;
24
+
25
+/**
26
+ * 缓存监控
27
+ * 
28
+ * @author ruoyi
29
+ */
30
+@RestController
31
+@RequestMapping("/monitor/cache")
32
+public class CacheController
33
+{
34
+    @Autowired
35
+    private RedisTemplate<String, String> redisTemplate;
36
+
37
+    private final static List<SysCache> caches = new ArrayList<SysCache>();
38
+    {
39
+        caches.add(new SysCache(CacheConstants.LOGIN_TOKEN_KEY, "用户信息"));
40
+        caches.add(new SysCache(CacheConstants.SYS_CONFIG_KEY, "配置信息"));
41
+        caches.add(new SysCache(CacheConstants.SYS_DICT_KEY, "数据字典"));
42
+        caches.add(new SysCache(CacheConstants.CAPTCHA_CODE_KEY, "验证码"));
43
+        caches.add(new SysCache(CacheConstants.REPEAT_SUBMIT_KEY, "防重提交"));
44
+        caches.add(new SysCache(CacheConstants.RATE_LIMIT_KEY, "限流处理"));
45
+        caches.add(new SysCache(CacheConstants.PWD_ERR_CNT_KEY, "密码错误次数"));
46
+    }
47
+
48
+    @PreAuthorize("@ss.hasPermi('monitor:cache:list')")
49
+    @GetMapping()
50
+    public AjaxResult getInfo() throws Exception
51
+    {
52
+        Properties info = (Properties) redisTemplate.execute((RedisCallback<Object>) connection -> connection.info());
53
+        Properties commandStats = (Properties) redisTemplate.execute((RedisCallback<Object>) connection -> connection.info("commandstats"));
54
+        Object dbSize = redisTemplate.execute((RedisCallback<Object>) connection -> connection.dbSize());
55
+
56
+        Map<String, Object> result = new HashMap<>(3);
57
+        result.put("info", info);
58
+        result.put("dbSize", dbSize);
59
+
60
+        List<Map<String, String>> pieList = new ArrayList<>();
61
+        commandStats.stringPropertyNames().forEach(key -> {
62
+            Map<String, String> data = new HashMap<>(2);
63
+            String property = commandStats.getProperty(key);
64
+            data.put("name", StringUtils.removeStart(key, "cmdstat_"));
65
+            data.put("value", StringUtils.substringBetween(property, "calls=", ",usec"));
66
+            pieList.add(data);
67
+        });
68
+        result.put("commandStats", pieList);
69
+        return AjaxResult.success(result);
70
+    }
71
+
72
+    @PreAuthorize("@ss.hasPermi('monitor:cache:list')")
73
+    @GetMapping("/getNames")
74
+    public AjaxResult cache()
75
+    {
76
+        return AjaxResult.success(caches);
77
+    }
78
+
79
+    @PreAuthorize("@ss.hasPermi('monitor:cache:list')")
80
+    @GetMapping("/getKeys/{cacheName}")
81
+    public AjaxResult getCacheKeys(@PathVariable String cacheName)
82
+    {
83
+        Set<String> cacheKeys = redisTemplate.keys(cacheName + "*");
84
+        return AjaxResult.success(new TreeSet<>(cacheKeys));
85
+    }
86
+
87
+    @PreAuthorize("@ss.hasPermi('monitor:cache:list')")
88
+    @GetMapping("/getValue/{cacheName}/{cacheKey}")
89
+    public AjaxResult getCacheValue(@PathVariable String cacheName, @PathVariable String cacheKey)
90
+    {
91
+        String cacheValue = redisTemplate.opsForValue().get(cacheKey);
92
+        SysCache sysCache = new SysCache(cacheName, cacheKey, cacheValue);
93
+        return AjaxResult.success(sysCache);
94
+    }
95
+
96
+    @PreAuthorize("@ss.hasPermi('monitor:cache:list')")
97
+    @DeleteMapping("/clearCacheName/{cacheName}")
98
+    public AjaxResult clearCacheName(@PathVariable String cacheName)
99
+    {
100
+        Collection<String> cacheKeys = redisTemplate.keys(cacheName + "*");
101
+        redisTemplate.delete(cacheKeys);
102
+        return AjaxResult.success();
103
+    }
104
+
105
+    @PreAuthorize("@ss.hasPermi('monitor:cache:list')")
106
+    @DeleteMapping("/clearCacheKey/{cacheKey}")
107
+    public AjaxResult clearCacheKey(@PathVariable String cacheKey)
108
+    {
109
+        redisTemplate.delete(cacheKey);
110
+        return AjaxResult.success();
111
+    }
112
+
113
+    @PreAuthorize("@ss.hasPermi('monitor:cache:list')")
114
+    @DeleteMapping("/clearCacheAll")
115
+    public AjaxResult clearCacheAll()
116
+    {
117
+        Collection<String> cacheKeys = redisTemplate.keys("*");
118
+        redisTemplate.delete(cacheKeys);
119
+        return AjaxResult.success();
120
+    }
121
+}

+ 27 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/monitor/ServerController.java

@@ -0,0 +1,27 @@
1
+package com.sundot.airport.web.controller.monitor;
2
+
3
+import org.springframework.security.access.prepost.PreAuthorize;
4
+import org.springframework.web.bind.annotation.GetMapping;
5
+import org.springframework.web.bind.annotation.RequestMapping;
6
+import org.springframework.web.bind.annotation.RestController;
7
+import com.sundot.airport.common.core.domain.AjaxResult;
8
+import com.sundot.airport.framework.web.domain.Server;
9
+
10
+/**
11
+ * 服务器监控
12
+ * 
13
+ * @author ruoyi
14
+ */
15
+@RestController
16
+@RequestMapping("/monitor/server")
17
+public class ServerController
18
+{
19
+    @PreAuthorize("@ss.hasPermi('monitor:server:list')")
20
+    @GetMapping()
21
+    public AjaxResult getInfo() throws Exception
22
+    {
23
+        Server server = new Server();
24
+        server.copyTo();
25
+        return AjaxResult.success(server);
26
+    }
27
+}

+ 82 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/monitor/SysLogininforController.java

@@ -0,0 +1,82 @@
1
+package com.sundot.airport.web.controller.monitor;
2
+
3
+import java.util.List;
4
+import javax.servlet.http.HttpServletResponse;
5
+import org.springframework.beans.factory.annotation.Autowired;
6
+import org.springframework.security.access.prepost.PreAuthorize;
7
+import org.springframework.web.bind.annotation.DeleteMapping;
8
+import org.springframework.web.bind.annotation.GetMapping;
9
+import org.springframework.web.bind.annotation.PathVariable;
10
+import org.springframework.web.bind.annotation.PostMapping;
11
+import org.springframework.web.bind.annotation.RequestMapping;
12
+import org.springframework.web.bind.annotation.RestController;
13
+import com.sundot.airport.common.annotation.Log;
14
+import com.sundot.airport.common.core.controller.BaseController;
15
+import com.sundot.airport.common.core.domain.AjaxResult;
16
+import com.sundot.airport.common.core.page.TableDataInfo;
17
+import com.sundot.airport.common.enums.BusinessType;
18
+import com.sundot.airport.common.utils.poi.ExcelUtil;
19
+import com.sundot.airport.framework.web.service.SysPasswordService;
20
+import com.sundot.airport.system.domain.SysLogininfor;
21
+import com.sundot.airport.system.service.ISysLogininforService;
22
+
23
+/**
24
+ * 系统访问记录
25
+ * 
26
+ * @author ruoyi
27
+ */
28
+@RestController
29
+@RequestMapping("/monitor/logininfor")
30
+public class SysLogininforController extends BaseController
31
+{
32
+    @Autowired
33
+    private ISysLogininforService logininforService;
34
+
35
+    @Autowired
36
+    private SysPasswordService passwordService;
37
+
38
+    @PreAuthorize("@ss.hasPermi('monitor:logininfor:list')")
39
+    @GetMapping("/list")
40
+    public TableDataInfo list(SysLogininfor logininfor)
41
+    {
42
+        startPage();
43
+        List<SysLogininfor> list = logininforService.selectLogininforList(logininfor);
44
+        return getDataTable(list);
45
+    }
46
+
47
+    @Log(title = "登录日志", businessType = BusinessType.EXPORT)
48
+    @PreAuthorize("@ss.hasPermi('monitor:logininfor:export')")
49
+    @PostMapping("/export")
50
+    public void export(HttpServletResponse response, SysLogininfor logininfor)
51
+    {
52
+        List<SysLogininfor> list = logininforService.selectLogininforList(logininfor);
53
+        ExcelUtil<SysLogininfor> util = new ExcelUtil<SysLogininfor>(SysLogininfor.class);
54
+        util.exportExcel(response, list, "登录日志");
55
+    }
56
+
57
+    @PreAuthorize("@ss.hasPermi('monitor:logininfor:remove')")
58
+    @Log(title = "登录日志", businessType = BusinessType.DELETE)
59
+    @DeleteMapping("/{infoIds}")
60
+    public AjaxResult remove(@PathVariable Long[] infoIds)
61
+    {
62
+        return toAjax(logininforService.deleteLogininforByIds(infoIds));
63
+    }
64
+
65
+    @PreAuthorize("@ss.hasPermi('monitor:logininfor:remove')")
66
+    @Log(title = "登录日志", businessType = BusinessType.CLEAN)
67
+    @DeleteMapping("/clean")
68
+    public AjaxResult clean()
69
+    {
70
+        logininforService.cleanLogininfor();
71
+        return success();
72
+    }
73
+
74
+    @PreAuthorize("@ss.hasPermi('monitor:logininfor:unlock')")
75
+    @Log(title = "账户解锁", businessType = BusinessType.OTHER)
76
+    @GetMapping("/unlock/{userName}")
77
+    public AjaxResult unlock(@PathVariable("userName") String userName)
78
+    {
79
+        passwordService.clearLoginRecordCache(userName);
80
+        return success();
81
+    }
82
+}

+ 69 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/monitor/SysOperlogController.java

@@ -0,0 +1,69 @@
1
+package com.sundot.airport.web.controller.monitor;
2
+
3
+import java.util.List;
4
+import javax.servlet.http.HttpServletResponse;
5
+import org.springframework.beans.factory.annotation.Autowired;
6
+import org.springframework.security.access.prepost.PreAuthorize;
7
+import org.springframework.web.bind.annotation.DeleteMapping;
8
+import org.springframework.web.bind.annotation.GetMapping;
9
+import org.springframework.web.bind.annotation.PathVariable;
10
+import org.springframework.web.bind.annotation.PostMapping;
11
+import org.springframework.web.bind.annotation.RequestMapping;
12
+import org.springframework.web.bind.annotation.RestController;
13
+import com.sundot.airport.common.annotation.Log;
14
+import com.sundot.airport.common.core.controller.BaseController;
15
+import com.sundot.airport.common.core.domain.AjaxResult;
16
+import com.sundot.airport.common.core.page.TableDataInfo;
17
+import com.sundot.airport.common.enums.BusinessType;
18
+import com.sundot.airport.common.utils.poi.ExcelUtil;
19
+import com.sundot.airport.system.domain.SysOperLog;
20
+import com.sundot.airport.system.service.ISysOperLogService;
21
+
22
+/**
23
+ * 操作日志记录
24
+ * 
25
+ * @author ruoyi
26
+ */
27
+@RestController
28
+@RequestMapping("/monitor/operlog")
29
+public class SysOperlogController extends BaseController
30
+{
31
+    @Autowired
32
+    private ISysOperLogService operLogService;
33
+
34
+    @PreAuthorize("@ss.hasPermi('monitor:operlog:list')")
35
+    @GetMapping("/list")
36
+    public TableDataInfo list(SysOperLog operLog)
37
+    {
38
+        startPage();
39
+        List<SysOperLog> list = operLogService.selectOperLogList(operLog);
40
+        return getDataTable(list);
41
+    }
42
+
43
+    @Log(title = "操作日志", businessType = BusinessType.EXPORT)
44
+    @PreAuthorize("@ss.hasPermi('monitor:operlog:export')")
45
+    @PostMapping("/export")
46
+    public void export(HttpServletResponse response, SysOperLog operLog)
47
+    {
48
+        List<SysOperLog> list = operLogService.selectOperLogList(operLog);
49
+        ExcelUtil<SysOperLog> util = new ExcelUtil<SysOperLog>(SysOperLog.class);
50
+        util.exportExcel(response, list, "操作日志");
51
+    }
52
+
53
+    @Log(title = "操作日志", businessType = BusinessType.DELETE)
54
+    @PreAuthorize("@ss.hasPermi('monitor:operlog:remove')")
55
+    @DeleteMapping("/{operIds}")
56
+    public AjaxResult remove(@PathVariable Long[] operIds)
57
+    {
58
+        return toAjax(operLogService.deleteOperLogByIds(operIds));
59
+    }
60
+
61
+    @Log(title = "操作日志", businessType = BusinessType.CLEAN)
62
+    @PreAuthorize("@ss.hasPermi('monitor:operlog:remove')")
63
+    @DeleteMapping("/clean")
64
+    public AjaxResult clean()
65
+    {
66
+        operLogService.cleanOperLog();
67
+        return success();
68
+    }
69
+}

+ 83 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/monitor/SysUserOnlineController.java

@@ -0,0 +1,83 @@
1
+package com.sundot.airport.web.controller.monitor;
2
+
3
+import java.util.ArrayList;
4
+import java.util.Collection;
5
+import java.util.Collections;
6
+import java.util.List;
7
+import org.springframework.beans.factory.annotation.Autowired;
8
+import org.springframework.security.access.prepost.PreAuthorize;
9
+import org.springframework.web.bind.annotation.DeleteMapping;
10
+import org.springframework.web.bind.annotation.GetMapping;
11
+import org.springframework.web.bind.annotation.PathVariable;
12
+import org.springframework.web.bind.annotation.RequestMapping;
13
+import org.springframework.web.bind.annotation.RestController;
14
+import com.sundot.airport.common.annotation.Log;
15
+import com.sundot.airport.common.constant.CacheConstants;
16
+import com.sundot.airport.common.core.controller.BaseController;
17
+import com.sundot.airport.common.core.domain.AjaxResult;
18
+import com.sundot.airport.common.core.domain.model.LoginUser;
19
+import com.sundot.airport.common.core.page.TableDataInfo;
20
+import com.sundot.airport.common.core.redis.RedisCache;
21
+import com.sundot.airport.common.enums.BusinessType;
22
+import com.sundot.airport.common.utils.StringUtils;
23
+import com.sundot.airport.system.domain.SysUserOnline;
24
+import com.sundot.airport.system.service.ISysUserOnlineService;
25
+
26
+/**
27
+ * 在线用户监控
28
+ * 
29
+ * @author ruoyi
30
+ */
31
+@RestController
32
+@RequestMapping("/monitor/online")
33
+public class SysUserOnlineController extends BaseController
34
+{
35
+    @Autowired
36
+    private ISysUserOnlineService userOnlineService;
37
+
38
+    @Autowired
39
+    private RedisCache redisCache;
40
+
41
+    @PreAuthorize("@ss.hasPermi('monitor:online:list')")
42
+    @GetMapping("/list")
43
+    public TableDataInfo list(String ipaddr, String userName)
44
+    {
45
+        Collection<String> keys = redisCache.keys(CacheConstants.LOGIN_TOKEN_KEY + "*");
46
+        List<SysUserOnline> userOnlineList = new ArrayList<SysUserOnline>();
47
+        for (String key : keys)
48
+        {
49
+            LoginUser user = redisCache.getCacheObject(key);
50
+            if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName))
51
+            {
52
+                userOnlineList.add(userOnlineService.selectOnlineByInfo(ipaddr, userName, user));
53
+            }
54
+            else if (StringUtils.isNotEmpty(ipaddr))
55
+            {
56
+                userOnlineList.add(userOnlineService.selectOnlineByIpaddr(ipaddr, user));
57
+            }
58
+            else if (StringUtils.isNotEmpty(userName) && StringUtils.isNotNull(user.getUser()))
59
+            {
60
+                userOnlineList.add(userOnlineService.selectOnlineByUserName(userName, user));
61
+            }
62
+            else
63
+            {
64
+                userOnlineList.add(userOnlineService.loginUserToUserOnline(user));
65
+            }
66
+        }
67
+        Collections.reverse(userOnlineList);
68
+        userOnlineList.removeAll(Collections.singleton(null));
69
+        return getDataTable(userOnlineList);
70
+    }
71
+
72
+    /**
73
+     * 强退用户
74
+     */
75
+    @PreAuthorize("@ss.hasPermi('monitor:online:forceLogout')")
76
+    @Log(title = "在线用户", businessType = BusinessType.FORCE)
77
+    @DeleteMapping("/{tokenId}")
78
+    public AjaxResult forceLogout(@PathVariable String tokenId)
79
+    {
80
+        redisCache.deleteObject(CacheConstants.LOGIN_TOKEN_KEY + tokenId);
81
+        return success();
82
+    }
83
+}

+ 201 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/portrait/AttendanceStatsController.java

@@ -0,0 +1,201 @@
1
+package com.sundot.airport.web.controller.portrait;
2
+
3
+import com.sundot.airport.attendance.domain.AttendancePostRecord;
4
+import com.sundot.airport.attendance.portrait.StationWorkStatsService;
5
+import com.sundot.airport.attendance.service.IAttendancePostRecordService;
6
+import com.sundot.airport.common.domain.portrait.IndicatorCalculateParams;
7
+import com.sundot.airport.common.domain.portrait.ModularIndicatorResult;
8
+import com.sundot.airport.common.enums.portrait.UserType;
9
+import com.sundot.airport.common.exception.ServiceException;
10
+import com.sundot.airport.common.service.portrait.ModuleIndicatorManager;
11
+import com.sundot.airport.common.utils.DateUtils;
12
+import com.sundot.airport.common.utils.StringUtils;
13
+import com.sundot.airport.system.domain.vo.PositionInfoVO;
14
+import com.sundot.airport.system.service.IBasePositionService;
15
+import com.sundot.airport.system.service.ISysConfigService;
16
+import org.springframework.beans.factory.annotation.Autowired;
17
+import org.springframework.cache.annotation.Cacheable;
18
+import org.springframework.web.bind.annotation.*;
19
+import springfox.documentation.spring.web.json.JacksonModuleRegistrar;
20
+
21
+import java.math.BigDecimal;
22
+import java.math.RoundingMode;
23
+import java.time.LocalDate;
24
+import java.time.ZoneId;
25
+import java.time.temporal.ChronoUnit;
26
+import java.util.*;
27
+import java.util.stream.Collectors;
28
+
29
+/**
30
+ * 考勤统计控制器
31
+ * 提供考勤指标计算的REST API接口
32
+ */
33
+@RestController
34
+@RequestMapping("/attendance/stats")
35
+public class AttendanceStatsController {
36
+    
37
+    @Autowired
38
+    private ModuleIndicatorManager moduleIndicatorManager;
39
+    
40
+    @Autowired
41
+    private StationWorkStatsService stationWorkStatsService;
42
+    
43
+    @Autowired
44
+    private IAttendancePostRecordService attendancePostRecordService;
45
+    
46
+    @Autowired
47
+    private IBasePositionService basePositionService;
48
+
49
+    @Autowired
50
+    private ISysConfigService configService;
51
+
52
+
53
+    /**
54
+     * 计算站级考勤工作统计
55
+     * @param params  指标计算参数
56
+     * @return 站级工作统计数据
57
+     */
58
+    @Cacheable(
59
+            value = "attendance_station",
60
+            keyGenerator = "statisticsKeyGenerator",
61
+            unless = "!T(com.sundot.airport.common.cache.StatisticsCacheConditionUtil).isSuccess(#result) ||T(com.sundot.airport.common.cache.StatisticsCacheConditionUtil).isEmptyData(#result)"
62
+    )
63
+    @GetMapping("/station")
64
+    public Object getStationWorkStats(IndicatorCalculateParams params) {
65
+        return stationWorkStatsService.calculateStationWorkStats(params);
66
+    }
67
+
68
+
69
+    /**
70
+     * 通道开放趋势图(折线图)
71
+     * 通道开放数、开放时长、通道开放平均线
72
+     */
73
+    @Cacheable(
74
+            value = "attendance_open_trend",
75
+            keyGenerator = "statisticsKeyGenerator",
76
+            unless = "!T(com.sundot.airport.common.cache.StatisticsCacheConditionUtil).isSuccess(#result) ||T(com.sundot.airport.common.cache.StatisticsCacheConditionUtil).isEmptyData(#result)"
77
+    )
78
+    @GetMapping("/channel/open/trend")
79
+    public Object getChannelOpenTrend(IndicatorCalculateParams params) {
80
+        // 处理默认时间范围(91天)
81
+        Date startDate = params.getStartTime();
82
+        Date endDate = params.getEndTime();
83
+        
84
+        // 如果没有提供时间范围,默认为最近91天
85
+        if (startDate == null || endDate == null) {
86
+            endDate = new Date();
87
+            startDate = DateUtils.addDays(endDate, -90); // 91天包括今天
88
+        }
89
+        
90
+        // 生成日期范围
91
+        List<String> dateRange = generateDateRange(startDate, endDate);
92
+        Map<String,Object> channelOpenTrend = new HashMap<>();
93
+        // 按日期分组处理数据
94
+        List<Map<String, Object>> trendData = new ArrayList<>();
95
+        try {
96
+            // 获取班次开始和结束时间
97
+            String startTime = configService.selectConfigByKey("attendance.post.record.start.time");
98
+            String endTime = configService.selectConfigByKey("attendance.post.record.end.time");
99
+            Date shiftStartTime = DateUtils.parseDate( DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, startDate)+ " " + startTime);
100
+            Date shiftEndTime = DateUtils.parseDate( DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, DateUtils.addDays(endDate, 1))+ " " + endTime);
101
+            
102
+            // 一次性查询整个时间范围内的所有在岗记录
103
+            AttendancePostRecord queryPostRecord = new AttendancePostRecord();
104
+            queryPostRecord.setCheckInTimeStart(shiftStartTime);
105
+            queryPostRecord.setCheckInTimeEnd(shiftEndTime);
106
+            if(Objects.nonNull(params.getDeptId())){
107
+                queryPostRecord.setAttendanceDepartmentId(params.getDeptId());
108
+            }
109
+            List<AttendancePostRecord> postRecords = attendancePostRecordService.selectAttendancePostRecordList(queryPostRecord);
110
+            
111
+
112
+            for (String date : dateRange) {
113
+                Map<String, Object> dataPoint = new HashMap<>();
114
+                dataPoint.put("date", date);
115
+                
116
+                // 过滤出当前日期的数据
117
+                List<AttendancePostRecord> currentDateRecords = filterRecordsByDate(postRecords, date);
118
+                
119
+                // 统计开放通道数
120
+                long openChannelCount = currentDateRecords.stream()
121
+                        .map(AttendancePostRecord::getChannelCode)
122
+                        .filter(Objects::nonNull)
123
+                        .count();
124
+                
125
+                // 计算总开放时长(分钟),直接使用数据库中的工作时长字段
126
+                long totalOpenDuration = currentDateRecords.stream()
127
+                        .filter(item-> StringUtils.isNotEmpty(item.getChannelCode()))
128
+                        .mapToLong(record -> {
129
+                            Long workDuration = record.getWorkDuration();
130
+                            // 如果工作时长为null或小于等于0,则返回0
131
+                            return workDuration != null && workDuration > 0 ?  workDuration : 0;
132
+                        })
133
+                        .sum();
134
+
135
+
136
+                //开放通道
137
+                dataPoint.put("openCount", openChannelCount);
138
+                //开放时长(小时)
139
+                dataPoint.put("openDuration", totalOpenDuration==0?0:new BigDecimal(totalOpenDuration).divide(new BigDecimal(60), 2,RoundingMode.HALF_UP));
140
+                trendData.add(dataPoint);
141
+            }
142
+            BigDecimal divide = new BigDecimal(postRecords.size()).divide(new BigDecimal(dateRange.size()), 1,RoundingMode.HALF_UP);
143
+            channelOpenTrend.put("avg", divide);
144
+            channelOpenTrend.put("trend", trendData);
145
+            return channelOpenTrend;
146
+        } catch (Exception e) {
147
+           throw new ServiceException("通道开放趋势计算错误: " + e.getMessage());
148
+        }
149
+    }
150
+    
151
+    /**
152
+     * 根据日期过滤记录
153
+     * @param records 所有记录
154
+     * @param targetDate 目标日期
155
+     * @return 过滤后的记录列表
156
+     */
157
+    private List<AttendancePostRecord> filterRecordsByDate(List<AttendancePostRecord> records, String targetDate) {
158
+        return records.stream()
159
+                .filter(record -> {
160
+                    Date attendanceDate = record.getAttendanceDate();
161
+                    // 检查签到时间是否在目标日期
162
+                    return attendanceDate != null && DateUtils.parseDateToStr("yyyy-MM-dd", attendanceDate).equals(targetDate);
163
+                })
164
+                .collect(Collectors.toList());
165
+    }
166
+    
167
+    /**
168
+     * 获取所有通道信息
169
+     * @return 所有通道列表
170
+     */
171
+    private List<PositionInfoVO> getAllChannels() {
172
+        // 这里传入空列表表示获取所有通道
173
+        return basePositionService.selectChannelsUnderArea(new ArrayList<>());
174
+    }
175
+    
176
+    /**
177
+     * 生成日期范围列表
178
+     * @param startDate 开始日期
179
+     * @param endDate 结束日期
180
+     * @return 日期范围列表
181
+     */
182
+    private List<String> generateDateRange(Date startDate, Date endDate) {
183
+        List<String> dateRange = new ArrayList<>();
184
+        
185
+        // 将Date转换为LocalDate
186
+        LocalDate start = startDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
187
+        LocalDate end = endDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
188
+        
189
+        // 计算天数差
190
+        long daysBetween = ChronoUnit.DAYS.between(start, end);
191
+        
192
+        // 生成日期范围(包括起始和结束日期)
193
+        for (int i = 0; i <= daysBetween; i++) {
194
+            LocalDate currentDate = start.plusDays(i);
195
+            dateRange.add(currentDate.toString());
196
+        }
197
+        
198
+        return dateRange;
199
+    }
200
+
201
+}

+ 290 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/portrait/ItemUserRankingController.java

@@ -0,0 +1,290 @@
1
+package com.sundot.airport.web.controller.portrait;
2
+
3
+import cn.hutool.core.collection.CollectionUtil;
4
+import com.sundot.airport.common.core.domain.AjaxResult;
5
+import com.sundot.airport.common.core.domain.BaseLargeScreenQueryParamDto;
6
+import com.sundot.airport.common.core.domain.entity.SysUser;
7
+import com.sundot.airport.common.domain.portrait.ApprovalDurationStats;
8
+import com.sundot.airport.common.domain.portrait.IndicatorCalculateParams;
9
+import com.sundot.airport.common.enums.RoleTypeEnum;
10
+import com.sundot.airport.common.enums.portrait.UserType;
11
+import com.sundot.airport.common.utils.DateUtils;
12
+import com.sundot.airport.common.utils.SecurityUtils;
13
+import com.sundot.airport.item.domain.ItemLargeScreenCommonDto;
14
+import com.sundot.airport.item.domain.ItemLargeScreenDailyTrendDto;
15
+import com.sundot.airport.item.domain.ItemLargeScreenTimeSpanDto;
16
+import com.sundot.airport.item.service.UserRankingService;
17
+import com.sundot.airport.system.domain.approval.ApprovalInstance;
18
+import com.sundot.airport.system.mapper.approval.ApprovalHistoryMapper;
19
+import com.sundot.airport.system.mapper.approval.ApprovalInstanceMapper;
20
+import com.sundot.airport.system.mapper.SysUserMapper;
21
+import org.springframework.beans.factory.annotation.Autowired;
22
+import org.springframework.cache.annotation.Cacheable;
23
+import org.springframework.security.access.prepost.PreAuthorize;
24
+import org.springframework.web.bind.annotation.*;
25
+
26
+import javax.annotation.PostConstruct;
27
+import java.math.BigDecimal;
28
+import java.text.ParseException;
29
+import java.time.LocalDate;
30
+import java.time.ZoneId;
31
+import java.time.temporal.ChronoUnit;
32
+import java.util.*;
33
+import java.util.concurrent.TimeUnit;
34
+import java.util.stream.Collectors;
35
+
36
+@RestController
37
+@RequestMapping("/item/user-ranking")
38
+public class ItemUserRankingController {
39
+
40
+    @Autowired
41
+    private UserRankingService userRankingService;
42
+    
43
+    @Autowired
44
+    private ApprovalInstanceMapper approvalInstanceMapper;
45
+    
46
+    @Autowired
47
+    private ApprovalHistoryMapper approvalHistoryMapper;
48
+    
49
+    @Autowired
50
+    private SysUserMapper sysUserMapper;
51
+
52
+    /**
53
+     * 获取用户在指定层级的详细排名信息
54
+     *
55
+     * @param dto 查询参数
56
+     * @param level 层级类型:station(全站), department(科室), team(班组)
57
+     * @return 用户在该层级的详细排名信息
58
+     */
59
+    @Cacheable(
60
+            value = "item_ranking_detail",
61
+            keyGenerator = "statisticsKeyGenerator",
62
+            unless = "!T(com.sundot.airport.common.cache.StatisticsCacheConditionUtil).isSuccess(#result) ||T(com.sundot.airport.common.cache.StatisticsCacheConditionUtil).isEmptyData(#result)"
63
+    )
64
+    @PreAuthorize("@ss.hasPermi('item:record:list')")
65
+    @GetMapping("/ranking-detail")
66
+    public AjaxResult getUserRankingByLevel(BaseLargeScreenQueryParamDto dto,
67
+                                            @RequestParam("level") String level) {
68
+        ItemLargeScreenCommonDto ranking = userRankingService.getUserRankingByLevel(dto, level);
69
+        return AjaxResult.success(ranking);
70
+    }
71
+
72
+
73
+    /**
74
+     * 计算站级查获统计
75
+     * @param params 参数
76
+     * @return 站级查获统计
77
+     */
78
+    @Cacheable(
79
+            value = "item_station",
80
+            keyGenerator = "statisticsKeyGenerator",
81
+            unless = "!T(com.sundot.airport.common.cache.StatisticsCacheConditionUtil).isSuccess(#result) ||T(com.sundot.airport.common.cache.StatisticsCacheConditionUtil).isEmptyData(#result)"
82
+    )
83
+    @GetMapping("/station")
84
+    public AjaxResult getStationItemSeizure(IndicatorCalculateParams params) {
85
+        return AjaxResult.success(userRankingService.calculateStationItemSeizure(params));
86
+    }
87
+
88
+    /**
89
+     * 查获审批时长统计
90
+     * @param params 查询参数
91
+     * @return 平均审批时长 最长审批时长 最短审批时长
92
+     */
93
+    @Cacheable(
94
+            value = "item_duration",
95
+            keyGenerator = "statisticsKeyGenerator",
96
+            unless = "!T(com.sundot.airport.common.cache.StatisticsCacheConditionUtil).isSuccess(#result) ||T(com.sundot.airport.common.cache.StatisticsCacheConditionUtil).isEmptyData(#result)"
97
+    )
98
+    @GetMapping("/seizure-approval/duration")
99
+    public AjaxResult getSeizureApprovalDurationStats(IndicatorCalculateParams params) {
100
+        // 设置默认时间范围
101
+        java.util.Date endTime = Optional.ofNullable(params.getEndTime()).orElse(new java.util.Date());
102
+        java.util.Date startTime = Optional.ofNullable(params.getStartTime())
103
+                .orElse(java.util.Date.from(endTime.toInstant().minusSeconds(7862400L))); // 91天
104
+
105
+        // 如果传了部门ID,则先查询该部门及其子部门下的所有用户ID
106
+        List<Long> userIds = null;
107
+        if (params.getDeptId() != null) {
108
+            List<SysUser> sysUserList = sysUserMapper.selectUserListByRoleKeyAndDeptId(
109
+                    Arrays.asList(RoleTypeEnum.banzuzhang.getCode(), RoleTypeEnum.SecurityCheck.getCode()),
110
+                    params.getDeptId());
111
+            userIds = sysUserList.stream().map(SysUser::getUserId).collect(Collectors.toList());
112
+        }
113
+        
114
+        // 查询workflow_id为4或5的审批实例(查获审批流程)
115
+        List<ApprovalInstance> instances = approvalInstanceMapper.selectApprovalInstancesByWorkflowIdsAndTimeRangeAndUsers(
116
+                Arrays.asList(4L, 5L),
117
+                startTime,
118
+                endTime,
119
+                userIds);
120
+        
121
+        if (instances.isEmpty()) {
122
+            ApprovalDurationStats result = new ApprovalDurationStats();
123
+            result.setAverageDuration(0L);
124
+            result.setMaxDuration(0L);
125
+            result.setMinDuration(0L);
126
+            return AjaxResult.success(result);
127
+        }
128
+        
129
+        // 计算每个实例的审批时长(毫秒)
130
+        List<Long> durations = instances.stream()
131
+                .map(instance -> {
132
+                    Date submitTime = instance.getSubmitTime();
133
+                    Date completionTime = instance.getCompletionTime();
134
+                    if (submitTime != null && completionTime != null) {
135
+                        return completionTime.getTime() - submitTime.getTime();
136
+                    }
137
+                    return 0L;
138
+                })
139
+                .filter(duration -> duration > 0)
140
+                .collect(Collectors.toList());
141
+        
142
+        if (durations.isEmpty()) {
143
+            ApprovalDurationStats result = new ApprovalDurationStats();
144
+            result.setAverageDuration(0L);
145
+            result.setMaxDuration(0L);
146
+            result.setMinDuration(0L);
147
+            return AjaxResult.success(result);
148
+        }
149
+        
150
+        // 计算统计数据
151
+        long totalDuration = durations.stream().mapToLong(Long::longValue).sum();
152
+        long averageDuration = totalDuration / durations.size();
153
+        long maxDuration = durations.stream().mapToLong(Long::longValue).max().orElse(0L);
154
+        long minDuration = durations.stream().mapToLong(Long::longValue).min().orElse(0L);
155
+        
156
+        ApprovalDurationStats result = new ApprovalDurationStats();
157
+        result.setAverageDuration(averageDuration);
158
+        result.setMaxDuration(maxDuration);
159
+        result.setMinDuration(minDuration);
160
+        
161
+        // 转换为小时和分钟显示
162
+        result.setAverageDurationText(formatDuration(averageDuration));
163
+        result.setMaxDurationText(formatDuration(maxDuration));
164
+        result.setMinDurationText(formatDuration(minDuration));
165
+        
166
+        return AjaxResult.success(result);
167
+    }
168
+    
169
+    /**
170
+     * 格式化持续时间显示
171
+     * @param durationMillis 毫秒数
172
+     * @return 格式化后的字符串,如"1天2小时30分钟45秒"
173
+     */
174
+    private String formatDuration(long durationMillis) {
175
+        long seconds = durationMillis / 1000;
176
+        long minutes = seconds / 60;
177
+        long hours = minutes / 60;
178
+        long days = hours / 24;
179
+        
180
+        seconds = seconds % 60;
181
+        minutes = minutes % 60;
182
+        hours = hours % 24;
183
+        
184
+        StringBuilder sb = new StringBuilder();
185
+        if (days > 0) {
186
+            sb.append(days).append("天");
187
+        }
188
+        if (hours > 0) {
189
+            sb.append(hours).append("小时");
190
+        }
191
+        if (minutes > 0) {
192
+            sb.append(minutes).append("分钟");
193
+        }
194
+        if (seconds > 0 || sb.length() == 0) {
195
+            sb.append(seconds).append("秒");
196
+        }
197
+        return sb.toString();
198
+    }
199
+    
200
+    /**
201
+     * 查获趋势图,获取有效查获趋势数据(默认近90天)
202
+     * @param params 查询参数(科室就传部门id,站就可以不传参数)
203
+     * @return 查获趋势数据
204
+     */
205
+    @Cacheable(
206
+            value = "item_seizure_trend",
207
+            keyGenerator = "statisticsKeyGenerator",
208
+            unless = "!T(com.sundot.airport.common.cache.StatisticsCacheConditionUtil).isSuccess(#result) ||T(com.sundot.airport.common.cache.StatisticsCacheConditionUtil).isEmptyData(#result)"
209
+    )
210
+    @GetMapping("/seizure-trend")
211
+    public AjaxResult getSeizureTrendData(IndicatorCalculateParams params) {
212
+        try {
213
+            // 设置默认时间范围为近91天
214
+            Date endTime = Optional.ofNullable(params.getEndTime()).orElse(new Date());
215
+            Date startTime = Optional.ofNullable(params.getStartTime())
216
+                    .orElse(Date.from(endTime.toInstant().minusSeconds(TimeUnit.DAYS.toSeconds(91))));
217
+
218
+            // 参数校验
219
+            if (startTime.after(endTime)) {
220
+                return AjaxResult.error("开始时间不能晚于结束时间");
221
+            }
222
+
223
+            params.setStartTime(startTime);
224
+            params.setEndTime(endTime);
225
+
226
+            // 创建查询参数对象
227
+            BaseLargeScreenQueryParamDto queryDto = new BaseLargeScreenQueryParamDto();
228
+            queryDto.setStartDate(startTime);
229
+            queryDto.setEndDate(endTime);
230
+
231
+            // 如果传了科室ID,则设置到查询参数中
232
+            if (params.getDeptId() != null) {
233
+                queryDto.setInspectDepartmentId(params.getDeptId());
234
+            }
235
+
236
+            // 调用service获取按天统计的查获趋势数据,没有的补0
237
+            List<ItemLargeScreenDailyTrendDto> trendData = userRankingService.getDailyTrendData(queryDto);
238
+
239
+            List<Date> dateRange = generateDateRange(startTime, endTime);
240
+            // 创建一个Map来快速查找已有数据
241
+            Map<Date, ItemLargeScreenDailyTrendDto> trendDataMap = trendData.stream()
242
+                    .collect(Collectors.toMap(ItemLargeScreenDailyTrendDto::getDate, dto -> dto));
243
+
244
+            // 构建完整的趋势数据列表,缺失日期补0
245
+            List<ItemLargeScreenDailyTrendDto> completeTrendData = new ArrayList<>();
246
+            for (Date date : dateRange) {
247
+                if (trendDataMap.containsKey(date)) {
248
+                    completeTrendData.add(trendDataMap.get(date));
249
+                } else {
250
+                    // 创建一个新的对象并设置日期和默认值
251
+                    ItemLargeScreenDailyTrendDto emptyDto = new ItemLargeScreenDailyTrendDto();
252
+                    emptyDto.setDate(date);
253
+                    emptyDto.setTotal(BigDecimal.ZERO); // 假设没有查获记录时值为0
254
+                    completeTrendData.add(emptyDto);
255
+                }
256
+            }
257
+
258
+            return AjaxResult.success(completeTrendData);
259
+        } catch (Exception e) {
260
+            return AjaxResult.error("获取趋势数据失败:" + e.getMessage());
261
+        }
262
+    }
263
+
264
+    /**
265
+     * 生成日期范围列表
266
+     *
267
+     * @param startDate 开始日期
268
+     * @param endDate   结束日期
269
+     * @return 日期范围列表
270
+     */
271
+    private List<Date> generateDateRange(Date startDate, Date endDate) throws ParseException {
272
+        List<Date> dateRange = new ArrayList<>();
273
+
274
+        // 使用UTC时区确保一致性
275
+        ZoneId zoneId = ZoneId.of("UTC");
276
+        LocalDate start = startDate.toInstant().atZone(zoneId).toLocalDate();
277
+        LocalDate end = endDate.toInstant().atZone(zoneId).toLocalDate();
278
+
279
+        // 计算天数差
280
+        long daysBetween = ChronoUnit.DAYS.between(start, end);
281
+
282
+        // 生成日期范围(包括起始和结束日期)
283
+        for (int i = 0; i <= daysBetween; i++) {
284
+            LocalDate currentDate = start.plusDays(i);
285
+            dateRange.add(DateUtils.parseDate(currentDate.toString(), "yyyy-MM-dd"));
286
+        }
287
+
288
+        return dateRange;
289
+    }
290
+}

+ 147 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/portrait/UserBasicPortraitController.java

@@ -0,0 +1,147 @@
1
+package com.sundot.airport.web.controller.portrait;
2
+
3
+import cn.hutool.core.collection.CollectionUtil;
4
+import cn.hutool.core.util.ObjectUtil;
5
+import com.sundot.airport.common.core.controller.BaseController;
6
+import com.sundot.airport.common.core.domain.AjaxResult;
7
+import com.sundot.airport.common.core.domain.entity.SysDept;
8
+import com.sundot.airport.common.core.domain.entity.SysUser;
9
+import com.sundot.airport.common.domain.portrait.IndicatorCalculateParams;
10
+import com.sundot.airport.common.domain.portrait.ModularIndicatorResult;
11
+import com.sundot.airport.common.dto.UserInfo;
12
+import com.sundot.airport.common.enums.DeptTypeEnum;
13
+import com.sundot.airport.common.enums.RoleTypeEnum;
14
+import com.sundot.airport.common.enums.portrait.UserType;
15
+import com.sundot.airport.system.mapper.SysUserMapper;
16
+import com.sundot.airport.system.service.ISysDeptService;
17
+import com.sundot.airport.system.service.portrait.UserPortraitService;
18
+import com.sundot.airport.web.core.cache.UserCache;
19
+import org.springframework.beans.factory.annotation.Autowired;
20
+import org.springframework.cache.annotation.Cacheable;
21
+import org.springframework.web.bind.annotation.*;
22
+
23
+import java.util.ArrayList;
24
+import java.util.Arrays;
25
+import java.util.List;
26
+import java.util.Optional;
27
+import java.util.stream.Collectors;
28
+
29
+/**
30
+ * 用户画像基本信息
31
+ */
32
+@RestController
33
+@RequestMapping("/user/basic/portrait")
34
+public class UserBasicPortraitController extends BaseController {
35
+
36
+    @Autowired
37
+    private UserPortraitService userPortraitService;
38
+
39
+    @Autowired
40
+    private UserCache userCache;
41
+
42
+    @Autowired
43
+    private SysUserMapper sysUserMapper;
44
+
45
+    @Autowired
46
+    private ISysDeptService sysDeptService;
47
+
48
+    
49
+    /**
50
+     * 获取指定模块的指标值
51
+     * @param modules 指定的模块列表,用逗号分隔
52
+     * @return 指定模块的指标值集合
53
+     */
54
+    @Cacheable(
55
+            value = "base_module_info",
56
+            keyGenerator = "statisticsKeyGenerator",
57
+            unless = "!T(com.sundot.airport.common.cache.StatisticsCacheConditionUtil).isSuccess(#result) ||T(com.sundot.airport.common.cache.StatisticsCacheConditionUtil).isEmptyData(#result)"
58
+    )
59
+    @GetMapping("/module/info")
60
+    public AjaxResult moduleInfo(IndicatorCalculateParams params,
61
+                                 @RequestParam(required = false) String modules) {
62
+        ModularIndicatorResult result=new ModularIndicatorResult();
63
+        try {
64
+            // 将字符串转换为枚举
65
+            UserType userTypeEnum = UserType.fromString(params.getUserTypeStr());
66
+            params.setUserType(userTypeEnum);
67
+
68
+            // 设置默认时间范围
69
+            java.util.Date endTime = Optional.ofNullable(params.getEndTime()).orElse(new java.util.Date());
70
+            java.util.Date startTime = Optional.ofNullable(params.getStartTime())
71
+                    .orElse(java.util.Date.from(endTime.toInstant().minusSeconds(7862400L)));
72
+            params.setStartTime(startTime);
73
+            params.setEndTime(endTime);
74
+            if (!UserType.PERSONAL.equals(userTypeEnum)) {
75
+                if(ObjectUtil.isNotEmpty(params.getUserId()) && ObjectUtil.isEmpty(params.getDeptId())){
76
+                    SysUser user = sysUserMapper.selectUserById(params.getUserId());
77
+                    if(ObjectUtil.isEmpty(user)){
78
+                        return AjaxResult.success(result);
79
+                    }
80
+                    params.setDeptId(user.getDeptId());
81
+                }
82
+                // 根据这个人的部门id,获取这个人部门下的所有安检员和班组长
83
+                List<SysUser> deptUsers = new ArrayList<>();
84
+                if (params.getDeptId() != null) {
85
+                    deptUsers = sysUserMapper.selectUserListByRoleKeyAndDeptId(
86
+                            Arrays.asList(RoleTypeEnum.banzuzhang.getCode(), RoleTypeEnum.SecurityCheck.getCode()),
87
+                            params.getDeptId());
88
+                    if(CollectionUtil.isEmpty(deptUsers)){
89
+                        return AjaxResult.success(result);
90
+                    }
91
+                }
92
+                if (CollectionUtil.isEmpty(deptUsers)) {
93
+                    return AjaxResult.success(result);
94
+                }
95
+                List<Long> userIds = deptUsers.stream()
96
+                        .map(SysUser::getUserId)
97
+                        .collect(Collectors.toList());
98
+                if(CollectionUtil.isEmpty(userIds)){
99
+                    return AjaxResult.success(result);
100
+                }
101
+                params.setUserIds(userIds);
102
+            }
103
+            
104
+
105
+            if (modules != null && !modules.isEmpty()) {
106
+                // 解析模块列表
107
+                List<String> moduleList = Arrays.asList(modules.split(","));
108
+                result = userPortraitService.getIndicators(params, moduleList);
109
+            } else {
110
+                // 获取所有模块的指标数据
111
+                result = userPortraitService.getIndicators(params);
112
+            }
113
+            
114
+            return AjaxResult.success(result);
115
+        } catch (Exception e) {
116
+            return AjaxResult.error("获取用户画像信息失败:" + e.getMessage());
117
+        }
118
+    }
119
+
120
+    /**
121
+     * 根据用户查找其所属的站点ID
122
+     * @param user 用户
123
+     * @return 站点ID
124
+     */
125
+    private Long findStationIdByUser(SysUser user) {
126
+        // 获取用户所在部门
127
+        SysDept userDept = user.getDept();
128
+        if (userDept == null) {
129
+            return null;
130
+        }
131
+
132
+        // 如果用户所在部门就是站点
133
+        if (DeptTypeEnum.STATION.getCode().equals(userDept.getDeptType())) {
134
+            return userDept.getDeptId();
135
+        }
136
+
137
+        // 向上查找父级部门,直到找到站点
138
+        SysDept currentDept = userDept;
139
+        while (currentDept != null && !DeptTypeEnum.STATION.getCode().equals(currentDept.getDeptType())) {
140
+            currentDept = sysDeptService.selectDeptById(currentDept.getParentId());
141
+        }
142
+
143
+        return currentDept != null ? currentDept.getDeptId() : null;
144
+    }
145
+
146
+
147
+}

+ 100 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/system/BaseAttachmentController.java

@@ -0,0 +1,100 @@
1
+package com.sundot.airport.web.controller.system;
2
+
3
+import java.util.List;
4
+import javax.servlet.http.HttpServletResponse;
5
+
6
+import org.springframework.security.access.prepost.PreAuthorize;
7
+import org.springframework.beans.factory.annotation.Autowired;
8
+import org.springframework.web.bind.annotation.GetMapping;
9
+import org.springframework.web.bind.annotation.PostMapping;
10
+import org.springframework.web.bind.annotation.PutMapping;
11
+import org.springframework.web.bind.annotation.DeleteMapping;
12
+import org.springframework.web.bind.annotation.PathVariable;
13
+import org.springframework.web.bind.annotation.RequestBody;
14
+import org.springframework.web.bind.annotation.RequestMapping;
15
+import org.springframework.web.bind.annotation.RestController;
16
+import com.sundot.airport.common.annotation.Log;
17
+import com.sundot.airport.common.core.controller.BaseController;
18
+import com.sundot.airport.common.core.domain.AjaxResult;
19
+import com.sundot.airport.common.enums.BusinessType;
20
+import com.sundot.airport.common.domain.BaseAttachment;
21
+import com.sundot.airport.common.service.IBaseAttachmentService;
22
+import com.sundot.airport.common.utils.poi.ExcelUtil;
23
+import com.sundot.airport.common.core.page.TableDataInfo;
24
+
25
+/**
26
+ * 附件Controller
27
+ *
28
+ * @author ruoyi
29
+ * @date 2025-07-14
30
+ */
31
+@RestController
32
+@RequestMapping("/system/attachment")
33
+public class BaseAttachmentController extends BaseController {
34
+    @Autowired
35
+    private IBaseAttachmentService baseAttachmentService;
36
+
37
+    /**
38
+     * 查询附件列表
39
+     */
40
+    @PreAuthorize("@ss.hasPermi('system:attachment:list')")
41
+    @GetMapping("/list")
42
+    public TableDataInfo list(BaseAttachment baseAttachment) {
43
+        startPage();
44
+        List<BaseAttachment> list = baseAttachmentService.selectBaseAttachmentList(baseAttachment);
45
+        return getDataTable(list);
46
+    }
47
+
48
+    /**
49
+     * 导出附件列表
50
+     */
51
+    @PreAuthorize("@ss.hasPermi('system:attachment:export')")
52
+    @Log(title = "附件", businessType = BusinessType.EXPORT)
53
+    @PostMapping("/export")
54
+    public void export(HttpServletResponse response, BaseAttachment baseAttachment) {
55
+        List<BaseAttachment> list = baseAttachmentService.selectBaseAttachmentList(baseAttachment);
56
+        ExcelUtil<BaseAttachment> util = new ExcelUtil<BaseAttachment>(BaseAttachment.class);
57
+        util.exportExcel(response, list, "附件数据");
58
+    }
59
+
60
+    /**
61
+     * 获取附件详细信息
62
+     */
63
+    @PreAuthorize("@ss.hasPermi('system:attachment:query')")
64
+    @GetMapping(value = "/{id}")
65
+    public AjaxResult getInfo(@PathVariable("id") Long id) {
66
+        return success(baseAttachmentService.selectBaseAttachmentById(id));
67
+    }
68
+
69
+    /**
70
+     * 新增附件
71
+     */
72
+    @PreAuthorize("@ss.hasPermi('system:attachment:add')")
73
+    @Log(title = "附件", businessType = BusinessType.INSERT)
74
+    @PostMapping
75
+    public AjaxResult add(@RequestBody BaseAttachment baseAttachment) {
76
+        baseAttachment.setCreateBy(getUsername());
77
+        return toAjax(baseAttachmentService.insertBaseAttachment(baseAttachment));
78
+    }
79
+
80
+    /**
81
+     * 修改附件
82
+     */
83
+    @PreAuthorize("@ss.hasPermi('system:attachment:edit')")
84
+    @Log(title = "附件", businessType = BusinessType.UPDATE)
85
+    @PutMapping
86
+    public AjaxResult edit(@RequestBody BaseAttachment baseAttachment) {
87
+        baseAttachment.setUpdateBy(getUsername());
88
+        return toAjax(baseAttachmentService.updateBaseAttachment(baseAttachment));
89
+    }
90
+
91
+    /**
92
+     * 删除附件
93
+     */
94
+    @PreAuthorize("@ss.hasPermi('system:attachment:remove')")
95
+    @Log(title = "附件", businessType = BusinessType.DELETE)
96
+    @DeleteMapping("/{ids}")
97
+    public AjaxResult remove(@PathVariable Long[] ids) {
98
+        return toAjax(baseAttachmentService.deleteBaseAttachmentByIds(ids));
99
+    }
100
+}

+ 108 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/system/BaseCheckCategoryController.java

@@ -0,0 +1,108 @@
1
+package com.sundot.airport.web.controller.system;
2
+
3
+import java.util.List;
4
+import javax.servlet.http.HttpServletResponse;
5
+
6
+import org.springframework.security.access.prepost.PreAuthorize;
7
+import org.springframework.beans.factory.annotation.Autowired;
8
+import org.springframework.web.bind.annotation.GetMapping;
9
+import org.springframework.web.bind.annotation.PostMapping;
10
+import org.springframework.web.bind.annotation.PutMapping;
11
+import org.springframework.web.bind.annotation.DeleteMapping;
12
+import org.springframework.web.bind.annotation.PathVariable;
13
+import org.springframework.web.bind.annotation.RequestBody;
14
+import org.springframework.web.bind.annotation.RequestMapping;
15
+import org.springframework.web.bind.annotation.RestController;
16
+import com.sundot.airport.common.annotation.Log;
17
+import com.sundot.airport.common.core.controller.BaseController;
18
+import com.sundot.airport.common.core.domain.AjaxResult;
19
+import com.sundot.airport.common.enums.BusinessType;
20
+import com.sundot.airport.system.domain.BaseCheckCategory;
21
+import com.sundot.airport.system.service.IBaseCheckCategoryService;
22
+import com.sundot.airport.common.utils.poi.ExcelUtil;
23
+
24
+/**
25
+ * 检查项分类Controller
26
+ *
27
+ * @author ruoyi
28
+ * @date 2025-07-11
29
+ */
30
+@RestController
31
+@RequestMapping("/system/checkCategory")
32
+public class BaseCheckCategoryController extends BaseController {
33
+    @Autowired
34
+    private IBaseCheckCategoryService baseCheckCategoryService;
35
+
36
+    /**
37
+     * 查询检查项分类列表
38
+     */
39
+    @PreAuthorize("@ss.hasPermi('system:checkCategory:list')")
40
+    @GetMapping("/list")
41
+    public AjaxResult list(BaseCheckCategory baseCheckCategory) {
42
+        List<BaseCheckCategory> list = baseCheckCategoryService.selectBaseCheckCategoryList(baseCheckCategory);
43
+        return success(list);
44
+    }
45
+
46
+    /**
47
+     * 导出检查项分类列表
48
+     */
49
+    @PreAuthorize("@ss.hasPermi('system:checkCategory:export')")
50
+    @Log(title = "检查项分类", businessType = BusinessType.EXPORT)
51
+    @PostMapping("/export")
52
+    public void export(HttpServletResponse response, BaseCheckCategory baseCheckCategory) {
53
+        List<BaseCheckCategory> list = baseCheckCategoryService.selectBaseCheckCategoryList(baseCheckCategory);
54
+        ExcelUtil<BaseCheckCategory> util = new ExcelUtil<BaseCheckCategory>(BaseCheckCategory.class);
55
+        util.exportExcel(response, list, "检查项分类数据");
56
+    }
57
+
58
+    /**
59
+     * 获取检查项分类详细信息
60
+     */
61
+    @PreAuthorize("@ss.hasPermi('system:checkCategory:query')")
62
+    @GetMapping(value = "/{id}")
63
+    public AjaxResult getInfo(@PathVariable("id") Long id) {
64
+        return success(baseCheckCategoryService.selectBaseCheckCategoryById(id));
65
+    }
66
+
67
+    /**
68
+     * 新增检查项分类
69
+     */
70
+    @PreAuthorize("@ss.hasPermi('system:checkCategory:add')")
71
+    @Log(title = "检查项分类", businessType = BusinessType.INSERT)
72
+    @PostMapping
73
+    public AjaxResult add(@RequestBody BaseCheckCategory baseCheckCategory) {
74
+        baseCheckCategory.setCreateBy(getUsername());
75
+        return toAjax(baseCheckCategoryService.insertBaseCheckCategory(baseCheckCategory));
76
+    }
77
+
78
+    /**
79
+     * 修改检查项分类
80
+     */
81
+    @PreAuthorize("@ss.hasPermi('system:checkCategory:edit')")
82
+    @Log(title = "检查项分类", businessType = BusinessType.UPDATE)
83
+    @PutMapping
84
+    public AjaxResult edit(@RequestBody BaseCheckCategory baseCheckCategory) {
85
+        baseCheckCategory.setUpdateBy(getUsername());
86
+        return toAjax(baseCheckCategoryService.updateBaseCheckCategory(baseCheckCategory));
87
+    }
88
+
89
+    /**
90
+     * 删除检查项分类
91
+     */
92
+    @PreAuthorize("@ss.hasPermi('system:checkCategory:remove')")
93
+    @Log(title = "检查项分类", businessType = BusinessType.DELETE)
94
+    @DeleteMapping("/{ids}")
95
+    public AjaxResult remove(@PathVariable Long[] ids) {
96
+        return toAjax(baseCheckCategoryService.deleteBaseCheckCategoryByIds(ids));
97
+    }
98
+
99
+    /**
100
+     * 查询检查项分类列表树形结构
101
+     */
102
+    @PreAuthorize("@ss.hasPermi('system:checkCategory:list')")
103
+    @GetMapping("/listTree")
104
+    public AjaxResult listTree(BaseCheckCategory baseCheckCategory) {
105
+        List<BaseCheckCategory> list = baseCheckCategoryService.selectBaseCheckCategoryListTree(baseCheckCategory);
106
+        return success(list);
107
+    }
108
+}

+ 108 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/system/BaseCheckPointController.java

@@ -0,0 +1,108 @@
1
+package com.sundot.airport.web.controller.system;
2
+
3
+import java.util.List;
4
+import javax.servlet.http.HttpServletResponse;
5
+
6
+import org.springframework.security.access.prepost.PreAuthorize;
7
+import org.springframework.beans.factory.annotation.Autowired;
8
+import org.springframework.web.bind.annotation.GetMapping;
9
+import org.springframework.web.bind.annotation.PostMapping;
10
+import org.springframework.web.bind.annotation.PutMapping;
11
+import org.springframework.web.bind.annotation.DeleteMapping;
12
+import org.springframework.web.bind.annotation.PathVariable;
13
+import org.springframework.web.bind.annotation.RequestBody;
14
+import org.springframework.web.bind.annotation.RequestMapping;
15
+import org.springframework.web.bind.annotation.RestController;
16
+import com.sundot.airport.common.annotation.Log;
17
+import com.sundot.airport.common.core.controller.BaseController;
18
+import com.sundot.airport.common.core.domain.AjaxResult;
19
+import com.sundot.airport.common.enums.BusinessType;
20
+import com.sundot.airport.system.domain.BaseCheckPoint;
21
+import com.sundot.airport.system.service.IBaseCheckPointService;
22
+import com.sundot.airport.common.utils.poi.ExcelUtil;
23
+
24
+/**
25
+ * 检查部位Controller
26
+ *
27
+ * @author ruoyi
28
+ * @date 2025-07-08
29
+ */
30
+@RestController
31
+@RequestMapping("/system/point")
32
+public class BaseCheckPointController extends BaseController {
33
+    @Autowired
34
+    private IBaseCheckPointService baseCheckPointService;
35
+
36
+    /**
37
+     * 查询检查部位列表
38
+     */
39
+    @PreAuthorize("@ss.hasPermi('system:point:list')")
40
+    @GetMapping("/list")
41
+    public AjaxResult list(BaseCheckPoint baseCheckPoint) {
42
+        List<BaseCheckPoint> list = baseCheckPointService.selectBaseCheckPointList(baseCheckPoint);
43
+        return success(list);
44
+    }
45
+
46
+    /**
47
+     * 导出检查部位列表
48
+     */
49
+    @PreAuthorize("@ss.hasPermi('system:point:export')")
50
+    @Log(title = "检查部位", businessType = BusinessType.EXPORT)
51
+    @PostMapping("/export")
52
+    public void export(HttpServletResponse response, BaseCheckPoint baseCheckPoint) {
53
+        List<BaseCheckPoint> list = baseCheckPointService.selectBaseCheckPointList(baseCheckPoint);
54
+        ExcelUtil<BaseCheckPoint> util = new ExcelUtil<BaseCheckPoint>(BaseCheckPoint.class);
55
+        util.exportExcel(response, list, "检查部位数据");
56
+    }
57
+
58
+    /**
59
+     * 获取检查部位详细信息
60
+     */
61
+    @PreAuthorize("@ss.hasPermi('system:point:query')")
62
+    @GetMapping(value = "/{id}")
63
+    public AjaxResult getInfo(@PathVariable("id") Long id) {
64
+        return success(baseCheckPointService.selectBaseCheckPointById(id));
65
+    }
66
+
67
+    /**
68
+     * 新增检查部位
69
+     */
70
+    @PreAuthorize("@ss.hasPermi('system:point:add')")
71
+    @Log(title = "检查部位", businessType = BusinessType.INSERT)
72
+    @PostMapping
73
+    public AjaxResult add(@RequestBody BaseCheckPoint baseCheckPoint) {
74
+        baseCheckPoint.setCreateBy(getUsername());
75
+        return toAjax(baseCheckPointService.insertBaseCheckPoint(baseCheckPoint));
76
+    }
77
+
78
+    /**
79
+     * 修改检查部位
80
+     */
81
+    @PreAuthorize("@ss.hasPermi('system:point:edit')")
82
+    @Log(title = "检查部位", businessType = BusinessType.UPDATE)
83
+    @PutMapping
84
+    public AjaxResult edit(@RequestBody BaseCheckPoint baseCheckPoint) {
85
+        baseCheckPoint.setUpdateBy(getUsername());
86
+        return toAjax(baseCheckPointService.updateBaseCheckPoint(baseCheckPoint));
87
+    }
88
+
89
+    /**
90
+     * 删除检查部位
91
+     */
92
+    @PreAuthorize("@ss.hasPermi('system:point:remove')")
93
+    @Log(title = "检查部位", businessType = BusinessType.DELETE)
94
+    @DeleteMapping("/{ids}")
95
+    public AjaxResult remove(@PathVariable Long[] ids) {
96
+        return toAjax(baseCheckPointService.deleteBaseCheckPointByIds(ids));
97
+    }
98
+
99
+    /**
100
+     * 查询检查部位列表树形结构
101
+     */
102
+    @PreAuthorize("@ss.hasPermi('system:point:list')")
103
+    @GetMapping("/listTree")
104
+    public AjaxResult listTree(BaseCheckPoint baseCheckPoint) {
105
+        List<BaseCheckPoint> list = baseCheckPointService.selectBaseCheckPointListTree(baseCheckPoint);
106
+        return success(list);
107
+    }
108
+}

+ 117 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/system/BaseDefaultChoiseController.java

@@ -0,0 +1,117 @@
1
+package com.sundot.airport.web.controller.system;
2
+
3
+import java.util.List;
4
+import javax.servlet.http.HttpServletResponse;
5
+
6
+import org.springframework.security.access.prepost.PreAuthorize;
7
+import org.springframework.beans.factory.annotation.Autowired;
8
+import org.springframework.web.bind.annotation.GetMapping;
9
+import org.springframework.web.bind.annotation.PostMapping;
10
+import org.springframework.web.bind.annotation.PutMapping;
11
+import org.springframework.web.bind.annotation.DeleteMapping;
12
+import org.springframework.web.bind.annotation.PathVariable;
13
+import org.springframework.web.bind.annotation.RequestBody;
14
+import org.springframework.web.bind.annotation.RequestMapping;
15
+import org.springframework.web.bind.annotation.RestController;
16
+import com.sundot.airport.common.annotation.Log;
17
+import com.sundot.airport.common.annotation.Anonymous;
18
+import com.sundot.airport.common.core.controller.BaseController;
19
+import com.sundot.airport.common.core.domain.AjaxResult;
20
+import com.sundot.airport.common.enums.BusinessType;
21
+import com.sundot.airport.system.domain.BaseDefaultChoise;
22
+import com.sundot.airport.system.service.IBaseDefaultChoiseService;
23
+import com.sundot.airport.common.utils.poi.ExcelUtil;
24
+import com.sundot.airport.common.core.page.TableDataInfo;
25
+
26
+/**
27
+ * 默认选择配置Controller
28
+ * 
29
+ * @author ruoyi
30
+ * @date 2025-08-25
31
+ */
32
+@RestController
33
+@RequestMapping("/system/defaultChoise")
34
+public class BaseDefaultChoiseController extends BaseController
35
+{
36
+    @Autowired
37
+    private IBaseDefaultChoiseService baseDefaultChoiseService;
38
+
39
+    /**
40
+     * 查询默认选择配置列表
41
+     */
42
+    @Anonymous
43
+    @GetMapping("/list")
44
+    public TableDataInfo list(BaseDefaultChoise baseDefaultChoise)
45
+    {
46
+        startPage();
47
+        List<BaseDefaultChoise> list = baseDefaultChoiseService.selectBaseDefaultChoiseList(baseDefaultChoise);
48
+        return getDataTable(list);
49
+    }
50
+
51
+    /**
52
+     * 导出默认选择配置列表
53
+     */
54
+    @PreAuthorize("@ss.hasPermi('system:defaultChoise:export')")
55
+    @Log(title = "默认选择配置", businessType = BusinessType.EXPORT)
56
+    @PostMapping("/export")
57
+    public void export(HttpServletResponse response, BaseDefaultChoise baseDefaultChoise)
58
+    {
59
+        List<BaseDefaultChoise> list = baseDefaultChoiseService.selectBaseDefaultChoiseList(baseDefaultChoise);
60
+        ExcelUtil<BaseDefaultChoise> util = new ExcelUtil<BaseDefaultChoise>(BaseDefaultChoise.class);
61
+        util.exportExcel(response, list, "默认选择配置数据");
62
+    }
63
+
64
+    /**
65
+     * 获取默认选择配置详细信息
66
+     */
67
+    @PreAuthorize("@ss.hasPermi('system:defaultChoise:query')")
68
+    @GetMapping(value = "/{id}")
69
+    public AjaxResult getInfo(@PathVariable("id") Long id)
70
+    {
71
+        return success(baseDefaultChoiseService.selectBaseDefaultChoiseById(id));
72
+    }
73
+
74
+    /**
75
+     * 新增默认选择配置
76
+     */
77
+    @PreAuthorize("@ss.hasPermi('system:defaultChoise:add')")
78
+    @Log(title = "默认选择配置", businessType = BusinessType.INSERT)
79
+    @PostMapping
80
+    public AjaxResult add(@RequestBody BaseDefaultChoise baseDefaultChoise)
81
+    {
82
+        return toAjax(baseDefaultChoiseService.insertBaseDefaultChoise(baseDefaultChoise));
83
+    }
84
+
85
+    /**
86
+     * 修改默认选择配置
87
+     */
88
+    @PreAuthorize("@ss.hasPermi('system:defaultChoise:edit')")
89
+    @Log(title = "默认选择配置", businessType = BusinessType.UPDATE)
90
+    @PutMapping
91
+    public AjaxResult edit(@RequestBody BaseDefaultChoise baseDefaultChoise)
92
+    {
93
+        return toAjax(baseDefaultChoiseService.updateBaseDefaultChoise(baseDefaultChoise));
94
+    }
95
+
96
+    /**
97
+     * 删除默认选择配置
98
+     */
99
+    @PreAuthorize("@ss.hasPermi('system:defaultChoise:remove')")
100
+    @Log(title = "默认选择配置", businessType = BusinessType.DELETE)
101
+	@DeleteMapping("/{ids}")
102
+    public AjaxResult remove(@PathVariable Long[] ids)
103
+    {
104
+        return toAjax(baseDefaultChoiseService.deleteBaseDefaultChoiseByIds(ids));
105
+    }
106
+
107
+    /**
108
+     * 获取默认选择的分类信息(包含ID、名称及父类信息)
109
+     */
110
+    @Anonymous
111
+    @GetMapping("/categoryInfo/{categoryType}")
112
+    public AjaxResult getCategoryInfo(@PathVariable("categoryType") Integer categoryType)
113
+    {
114
+        List<BaseDefaultChoise> list = baseDefaultChoiseService.selectDefaultCategoryWithInfo(categoryType);
115
+        return success(list);
116
+    }
117
+}

+ 107 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/system/BasePositionController.java

@@ -0,0 +1,107 @@
1
+package com.sundot.airport.web.controller.system;
2
+
3
+import java.util.List;
4
+import javax.servlet.http.HttpServletResponse;
5
+
6
+import org.springframework.security.access.prepost.PreAuthorize;
7
+import org.springframework.beans.factory.annotation.Autowired;
8
+import org.springframework.web.bind.annotation.GetMapping;
9
+import org.springframework.web.bind.annotation.PostMapping;
10
+import org.springframework.web.bind.annotation.PutMapping;
11
+import org.springframework.web.bind.annotation.DeleteMapping;
12
+import org.springframework.web.bind.annotation.PathVariable;
13
+import org.springframework.web.bind.annotation.RequestBody;
14
+import org.springframework.web.bind.annotation.RequestMapping;
15
+import org.springframework.web.bind.annotation.RestController;
16
+import com.sundot.airport.common.annotation.Log;
17
+import com.sundot.airport.common.core.controller.BaseController;
18
+import com.sundot.airport.common.core.domain.AjaxResult;
19
+import com.sundot.airport.common.enums.BusinessType;
20
+import com.sundot.airport.system.domain.BasePosition;
21
+import com.sundot.airport.system.service.IBasePositionService;
22
+import com.sundot.airport.common.utils.poi.ExcelUtil;
23
+
24
+/**
25
+ * 位置Controller
26
+ *
27
+ * @author ruoyi
28
+ * @date 2025-07-09
29
+ */
30
+@RestController
31
+@RequestMapping("/system/position")
32
+public class BasePositionController extends BaseController {
33
+    @Autowired
34
+    private IBasePositionService basePositionService;
35
+
36
+    /**
37
+     * 查询位置列表
38
+     */
39
+    @PreAuthorize("@ss.hasPermi('system:position:list')")
40
+    @GetMapping("/list")
41
+    public AjaxResult list(BasePosition basePosition) {
42
+        List<BasePosition> list = basePositionService.selectBasePositionList(basePosition);
43
+        return success(list);
44
+    }
45
+
46
+    /**
47
+     * 导出位置列表
48
+     */
49
+    @PreAuthorize("@ss.hasPermi('system:position:export')")
50
+    @Log(title = "位置", businessType = BusinessType.EXPORT)
51
+    @PostMapping("/export")
52
+    public void export(HttpServletResponse response, BasePosition basePosition) {
53
+        List<BasePosition> list = basePositionService.selectBasePositionList(basePosition);
54
+        ExcelUtil<BasePosition> util = new ExcelUtil<BasePosition>(BasePosition.class);
55
+        util.exportExcel(response, list, "位置数据");
56
+    }
57
+
58
+    /**
59
+     * 获取位置详细信息
60
+     */
61
+    @PreAuthorize("@ss.hasPermi('system:position:query')")
62
+    @GetMapping(value = "/{id}")
63
+    public AjaxResult getInfo(@PathVariable("id") Long id) {
64
+        return success(basePositionService.selectBasePositionById(id));
65
+    }
66
+
67
+    /**
68
+     * 新增位置
69
+     */
70
+    @PreAuthorize("@ss.hasPermi('system:position:add')")
71
+    @Log(title = "位置", businessType = BusinessType.INSERT)
72
+    @PostMapping
73
+    public AjaxResult add(@RequestBody BasePosition basePosition) {
74
+        basePosition.setCreateBy(getUsername());
75
+        return toAjax(basePositionService.insertBasePosition(basePosition));
76
+    }
77
+
78
+    /**
79
+     * 修改位置
80
+     */
81
+    @PreAuthorize("@ss.hasPermi('system:position:edit')")
82
+    @Log(title = "位置", businessType = BusinessType.UPDATE)
83
+    @PutMapping
84
+    public AjaxResult edit(@RequestBody BasePosition basePosition) {
85
+        basePosition.setUpdateBy(getUsername());
86
+        return toAjax(basePositionService.updateBasePosition(basePosition));
87
+    }
88
+
89
+    /**
90
+     * 删除位置
91
+     */
92
+    @PreAuthorize("@ss.hasPermi('system:position:remove')")
93
+    @Log(title = "位置", businessType = BusinessType.DELETE)
94
+    @DeleteMapping("/{ids}")
95
+    public AjaxResult remove(@PathVariable Long[] ids) {
96
+        return toAjax(basePositionService.deleteBasePositionByIds(ids));
97
+    }
98
+
99
+    /**
100
+     * 查询位置列表树形结构
101
+     */
102
+    @PreAuthorize("@ss.hasPermi('system:position:list')")
103
+    @GetMapping("/listTree")
104
+    public AjaxResult listTree(BasePosition basePosition) {
105
+        return success(basePositionService.selectBasePositionListTree(basePosition));
106
+    }
107
+}

+ 194 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/system/BaseProjectController.java

@@ -0,0 +1,194 @@
1
+package com.sundot.airport.web.controller.system;
2
+
3
+import java.util.ArrayList;
4
+import java.util.Collections;
5
+import java.util.List;
6
+import java.util.Map;
7
+import java.util.stream.Collectors;
8
+import javax.servlet.http.HttpServletResponse;
9
+
10
+import cn.hutool.core.collection.CollUtil;
11
+import com.sundot.airport.system.domain.vo.CheckProjectItemTreeVo;
12
+import com.sundot.airport.system.domain.vo.CheckProjectItemVo;
13
+import org.springframework.security.access.prepost.PreAuthorize;
14
+import org.springframework.beans.factory.annotation.Autowired;
15
+import org.springframework.web.bind.annotation.GetMapping;
16
+import org.springframework.web.bind.annotation.PostMapping;
17
+import org.springframework.web.bind.annotation.PutMapping;
18
+import org.springframework.web.bind.annotation.DeleteMapping;
19
+import org.springframework.web.bind.annotation.PathVariable;
20
+import org.springframework.web.bind.annotation.RequestBody;
21
+import org.springframework.web.bind.annotation.RequestMapping;
22
+import org.springframework.web.bind.annotation.RestController;
23
+import com.sundot.airport.common.annotation.Log;
24
+import com.sundot.airport.common.core.controller.BaseController;
25
+import com.sundot.airport.common.core.domain.AjaxResult;
26
+import com.sundot.airport.common.enums.BusinessType;
27
+import com.sundot.airport.system.domain.BaseProject;
28
+import com.sundot.airport.system.service.IBaseProjectService;
29
+import com.sundot.airport.common.utils.poi.ExcelUtil;
30
+import com.sundot.airport.common.core.page.TableDataInfo;
31
+
32
+/**
33
+ * 检查项目Controller
34
+ *
35
+ * @author ruoyi
36
+ * @date 2025-07-11
37
+ */
38
+@RestController
39
+@RequestMapping("/system/project")
40
+public class BaseProjectController extends BaseController {
41
+    @Autowired
42
+    private IBaseProjectService baseProjectService;
43
+
44
+    /**
45
+     * 查询检查项目列表
46
+     */
47
+    @PreAuthorize("@ss.hasPermi('system:project:list')")
48
+    @GetMapping("/list")
49
+    public TableDataInfo list(BaseProject baseProject) {
50
+        startPage();
51
+        List<BaseProject> list = baseProjectService.selectBaseProjectList(baseProject);
52
+        return getDataTable(list);
53
+    }
54
+
55
+    /**
56
+     * 导出检查项目列表
57
+     */
58
+    @PreAuthorize("@ss.hasPermi('system:project:export')")
59
+    @Log(title = "检查项目", businessType = BusinessType.EXPORT)
60
+    @PostMapping("/export")
61
+    public void export(HttpServletResponse response, BaseProject baseProject) {
62
+        List<BaseProject> list = baseProjectService.selectBaseProjectList(baseProject);
63
+        ExcelUtil<BaseProject> util = new ExcelUtil<BaseProject>(BaseProject.class);
64
+        util.exportExcel(response, list, "检查项目数据");
65
+    }
66
+
67
+    /**
68
+     * 获取检查项目详细信息
69
+     */
70
+    @PreAuthorize("@ss.hasPermi('system:project:query')")
71
+    @GetMapping(value = "/{id}")
72
+    public AjaxResult getInfo(@PathVariable("id") Long id) {
73
+        return success(baseProjectService.selectBaseProjectById(id));
74
+    }
75
+
76
+    /**
77
+     * 新增检查项目
78
+     */
79
+    @PreAuthorize("@ss.hasPermi('system:project:add')")
80
+    @Log(title = "检查项目", businessType = BusinessType.INSERT)
81
+    @PostMapping
82
+    public AjaxResult add(@RequestBody BaseProject baseProject) {
83
+        baseProject.setCreateBy(getUsername());
84
+        return toAjax(baseProjectService.insertBaseProject(baseProject));
85
+    }
86
+
87
+    /**
88
+     * 修改检查项目
89
+     */
90
+    @PreAuthorize("@ss.hasPermi('system:project:edit')")
91
+    @Log(title = "检查项目", businessType = BusinessType.UPDATE)
92
+    @PutMapping
93
+    public AjaxResult edit(@RequestBody BaseProject baseProject) {
94
+        baseProject.setUpdateBy(getUsername());
95
+        return toAjax(baseProjectService.updateBaseProject(baseProject));
96
+    }
97
+
98
+    /**
99
+     * 删除检查项目
100
+     */
101
+    @PreAuthorize("@ss.hasPermi('system:project:remove')")
102
+    @Log(title = "检查项目", businessType = BusinessType.DELETE)
103
+    @DeleteMapping("/{ids}")
104
+    public AjaxResult remove(@PathVariable Long[] ids) {
105
+        return toAjax(baseProjectService.deleteBaseProjectByIds(ids));
106
+    }
107
+
108
+    /**
109
+     * 根据分类编码查询检查项目列表
110
+     */
111
+    @PreAuthorize("@ss.hasPermi('system:project:list')")
112
+    @GetMapping(value = "/list/{categoryCode}")
113
+    public AjaxResult list(@PathVariable("categoryCode") String categoryCode) {
114
+        return success(baseProjectService.selectProjectListByCategoryCode(categoryCode));
115
+    }
116
+
117
+    /**
118
+     * 根据检查级别查询检查项目列表
119
+     */
120
+    @PreAuthorize("@ss.hasPermi('system:project:list')")
121
+    @GetMapping(value = "/listByCheckLevel/{checkLevel}")
122
+    public AjaxResult listByCheckedLevel(@PathVariable("checkLevel") String checkLevel) {
123
+        List<CheckProjectItemVo> result = baseProjectService.listByCheckLevel(checkLevel);
124
+        return success(convertToTree(result));
125
+    }
126
+
127
+    /**
128
+     * 转换为树形结构
129
+     */
130
+    public List<CheckProjectItemTreeVo> convertToTree(List<CheckProjectItemVo> itemList) {
131
+        if (CollUtil.isEmpty(itemList)) {
132
+            return Collections.emptyList();
133
+        }
134
+        List<CheckProjectItemTreeVo> result = new ArrayList<>();
135
+
136
+        // 第一层分组:按一级分类
137
+        Map<String, List<CheckProjectItemVo>> levelOneMap = itemList.stream()
138
+                .collect(Collectors.groupingBy(CheckProjectItemVo::getCategoryCodeOne));
139
+
140
+
141
+        // 处理一级分类
142
+        for (Map.Entry<String, List<CheckProjectItemVo>> levelOneEntry : levelOneMap.entrySet()) {
143
+            String levelOneCode = levelOneEntry.getKey();
144
+            List<CheckProjectItemVo> levelOneItems = levelOneEntry.getValue();
145
+
146
+            // 获取一级分类名称(取第一个元素的名称)
147
+            String levelOneName = levelOneItems.get(0).getCategoryNameOne();
148
+
149
+            // 创建一级节点
150
+            CheckProjectItemTreeVo levelOneNode = new CheckProjectItemTreeVo();
151
+            levelOneNode.setCode(levelOneCode);
152
+            levelOneNode.setName(levelOneName);
153
+
154
+            // 第二层分组:按二级分类
155
+            Map<String, List<CheckProjectItemVo>> levelTwoMap = levelOneItems.stream()
156
+                    .collect(Collectors.groupingBy(CheckProjectItemVo::getCategoryCodeTwo));
157
+
158
+            List<CheckProjectItemTreeVo> levelTwoNodes = new ArrayList<>();
159
+
160
+            // 处理二级分类
161
+            for (Map.Entry<String, List<CheckProjectItemVo>> levelTwoEntry : levelTwoMap.entrySet()) {
162
+                String levelTwoCode = levelTwoEntry.getKey();
163
+                List<CheckProjectItemVo> levelTwoItems = levelTwoEntry.getValue();
164
+
165
+                // 获取二级分类名称(取第一个元素的名称)
166
+                String levelTwoName = levelTwoItems.get(0).getCategoryNameTwo();
167
+
168
+                // 创建二级节点
169
+                CheckProjectItemTreeVo levelTwoNode = new CheckProjectItemTreeVo();
170
+                levelTwoNode.setCode(levelTwoCode);
171
+                levelTwoNode.setName(levelTwoName);
172
+
173
+                // 处理检查项目
174
+                List<CheckProjectItemTreeVo> projectNodes = levelTwoItems.stream()
175
+                        .map(item -> {
176
+                            CheckProjectItemTreeVo projectNode = new CheckProjectItemTreeVo();
177
+                            projectNode.setCode(item.getProjectCode());
178
+                            projectNode.setName(item.getProjectName());
179
+                            projectNode.setList(null); // 项目没有子节点
180
+                            return projectNode;
181
+                        })
182
+                        .collect(Collectors.toList());
183
+
184
+                levelTwoNode.setList(projectNodes);
185
+                levelTwoNodes.add(levelTwoNode);
186
+            }
187
+
188
+            levelOneNode.setList(levelTwoNodes);
189
+            result.add(levelOneNode);
190
+        }
191
+        return result;
192
+    }
193
+
194
+}

+ 100 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/system/BaseReadController.java

@@ -0,0 +1,100 @@
1
+package com.sundot.airport.web.controller.system;
2
+
3
+import java.util.List;
4
+import javax.servlet.http.HttpServletResponse;
5
+
6
+import com.sundot.airport.common.domain.BaseRead;
7
+import com.sundot.airport.common.service.IBaseReadService;
8
+import org.springframework.security.access.prepost.PreAuthorize;
9
+import org.springframework.beans.factory.annotation.Autowired;
10
+import org.springframework.web.bind.annotation.GetMapping;
11
+import org.springframework.web.bind.annotation.PostMapping;
12
+import org.springframework.web.bind.annotation.PutMapping;
13
+import org.springframework.web.bind.annotation.DeleteMapping;
14
+import org.springframework.web.bind.annotation.PathVariable;
15
+import org.springframework.web.bind.annotation.RequestBody;
16
+import org.springframework.web.bind.annotation.RequestMapping;
17
+import org.springframework.web.bind.annotation.RestController;
18
+import com.sundot.airport.common.annotation.Log;
19
+import com.sundot.airport.common.core.controller.BaseController;
20
+import com.sundot.airport.common.core.domain.AjaxResult;
21
+import com.sundot.airport.common.enums.BusinessType;
22
+import com.sundot.airport.common.utils.poi.ExcelUtil;
23
+import com.sundot.airport.common.core.page.TableDataInfo;
24
+
25
+/**
26
+ * 已读Controller
27
+ *
28
+ * @author ruoyi
29
+ * @date 2025-09-09
30
+ */
31
+@RestController
32
+@RequestMapping("/system/read")
33
+public class BaseReadController extends BaseController {
34
+    @Autowired
35
+    private IBaseReadService baseReadService;
36
+
37
+    /**
38
+     * 查询已读列表
39
+     */
40
+    @PreAuthorize("@ss.hasPermi('system:read:list')")
41
+    @GetMapping("/list")
42
+    public TableDataInfo list(BaseRead baseRead) {
43
+        startPage();
44
+        List<BaseRead> list = baseReadService.selectBaseReadList(baseRead);
45
+        return getDataTable(list);
46
+    }
47
+
48
+    /**
49
+     * 导出已读列表
50
+     */
51
+    @PreAuthorize("@ss.hasPermi('system:read:export')")
52
+    @Log(title = "已读", businessType = BusinessType.EXPORT)
53
+    @PostMapping("/export")
54
+    public void export(HttpServletResponse response, BaseRead baseRead) {
55
+        List<BaseRead> list = baseReadService.selectBaseReadList(baseRead);
56
+        ExcelUtil<BaseRead> util = new ExcelUtil<BaseRead>(BaseRead.class);
57
+        util.exportExcel(response, list, "已读数据");
58
+    }
59
+
60
+    /**
61
+     * 获取已读详细信息
62
+     */
63
+    @PreAuthorize("@ss.hasPermi('system:read:query')")
64
+    @GetMapping(value = "/{id}")
65
+    public AjaxResult getInfo(@PathVariable("id") Long id) {
66
+        return success(baseReadService.selectBaseReadById(id));
67
+    }
68
+
69
+    /**
70
+     * 新增已读
71
+     */
72
+    @PreAuthorize("@ss.hasPermi('system:read:add')")
73
+    @Log(title = "已读", businessType = BusinessType.INSERT)
74
+    @PostMapping
75
+    public AjaxResult add(@RequestBody BaseRead baseRead) {
76
+        baseRead.setCreateBy(getUsername());
77
+        return toAjax(baseReadService.insertBaseRead(baseRead));
78
+    }
79
+
80
+    /**
81
+     * 修改已读
82
+     */
83
+    @PreAuthorize("@ss.hasPermi('system:read:edit')")
84
+    @Log(title = "已读", businessType = BusinessType.UPDATE)
85
+    @PutMapping
86
+    public AjaxResult edit(@RequestBody BaseRead baseRead) {
87
+        baseRead.setUpdateBy(getUsername());
88
+        return toAjax(baseReadService.updateBaseRead(baseRead));
89
+    }
90
+
91
+    /**
92
+     * 删除已读
93
+     */
94
+    @PreAuthorize("@ss.hasPermi('system:read:remove')")
95
+    @Log(title = "已读", businessType = BusinessType.DELETE)
96
+    @DeleteMapping("/{ids}")
97
+    public AjaxResult remove(@PathVariable Long[] ids) {
98
+        return toAjax(baseReadService.deleteBaseReadByIds(ids));
99
+    }
100
+}

+ 109 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/system/BaseSeizeCategoryController.java

@@ -0,0 +1,109 @@
1
+package com.sundot.airport.web.controller.system;
2
+
3
+import java.util.List;
4
+import javax.servlet.http.HttpServletResponse;
5
+
6
+import com.sundot.airport.common.annotation.Anonymous;
7
+import org.springframework.security.access.prepost.PreAuthorize;
8
+import org.springframework.beans.factory.annotation.Autowired;
9
+import org.springframework.web.bind.annotation.GetMapping;
10
+import org.springframework.web.bind.annotation.PostMapping;
11
+import org.springframework.web.bind.annotation.PutMapping;
12
+import org.springframework.web.bind.annotation.DeleteMapping;
13
+import org.springframework.web.bind.annotation.PathVariable;
14
+import org.springframework.web.bind.annotation.RequestBody;
15
+import org.springframework.web.bind.annotation.RequestMapping;
16
+import org.springframework.web.bind.annotation.RestController;
17
+import com.sundot.airport.common.annotation.Log;
18
+import com.sundot.airport.common.core.controller.BaseController;
19
+import com.sundot.airport.common.core.domain.AjaxResult;
20
+import com.sundot.airport.common.enums.BusinessType;
21
+import com.sundot.airport.system.domain.BaseSeizeCategory;
22
+import com.sundot.airport.system.service.IBaseSeizeCategoryService;
23
+import com.sundot.airport.common.utils.poi.ExcelUtil;
24
+
25
+/**
26
+ * 查获物品分类Controller
27
+ *
28
+ * @author ruoyi
29
+ * @date 2025-07-08
30
+ */
31
+@RestController
32
+@RequestMapping("/system/category")
33
+public class BaseSeizeCategoryController extends BaseController {
34
+    @Autowired
35
+    private IBaseSeizeCategoryService baseSeizeCategoryService;
36
+
37
+    /**
38
+     * 查询查获物品分类列表
39
+     */
40
+    @Anonymous
41
+    @GetMapping("/list")
42
+    public AjaxResult list(BaseSeizeCategory baseSeizeCategory) {
43
+        List<BaseSeizeCategory> list = baseSeizeCategoryService.selectBaseSeizeCategoryList(baseSeizeCategory);
44
+        return success(list);
45
+    }
46
+
47
+    /**
48
+     * 导出查获物品分类列表
49
+     */
50
+    @PreAuthorize("@ss.hasPermi('system:category:export')")
51
+    @Log(title = "查获物品分类", businessType = BusinessType.EXPORT)
52
+    @PostMapping("/export")
53
+    public void export(HttpServletResponse response, BaseSeizeCategory baseSeizeCategory) {
54
+        List<BaseSeizeCategory> list = baseSeizeCategoryService.selectBaseSeizeCategoryList(baseSeizeCategory);
55
+        ExcelUtil<BaseSeizeCategory> util = new ExcelUtil<BaseSeizeCategory>(BaseSeizeCategory.class);
56
+        util.exportExcel(response, list, "查获物品分类数据");
57
+    }
58
+
59
+    /**
60
+     * 获取查获物品分类详细信息
61
+     */
62
+    @PreAuthorize("@ss.hasPermi('system:category:query')")
63
+    @GetMapping(value = "/{id}")
64
+    public AjaxResult getInfo(@PathVariable("id") Long id) {
65
+        return success(baseSeizeCategoryService.selectBaseSeizeCategoryById(id));
66
+    }
67
+
68
+    /**
69
+     * 新增查获物品分类
70
+     */
71
+    @PreAuthorize("@ss.hasPermi('system:category:add')")
72
+    @Log(title = "查获物品分类", businessType = BusinessType.INSERT)
73
+    @PostMapping
74
+    public AjaxResult add(@RequestBody BaseSeizeCategory baseSeizeCategory) {
75
+        baseSeizeCategory.setCreateBy(getUsername());
76
+        return toAjax(baseSeizeCategoryService.insertBaseSeizeCategory(baseSeizeCategory));
77
+    }
78
+
79
+    /**
80
+     * 修改查获物品分类
81
+     */
82
+    @PreAuthorize("@ss.hasPermi('system:category:edit')")
83
+    @Log(title = "查获物品分类", businessType = BusinessType.UPDATE)
84
+    @PutMapping
85
+    public AjaxResult edit(@RequestBody BaseSeizeCategory baseSeizeCategory) {
86
+        baseSeizeCategory.setUpdateBy(getUsername());
87
+        return toAjax(baseSeizeCategoryService.updateBaseSeizeCategory(baseSeizeCategory));
88
+    }
89
+
90
+    /**
91
+     * 删除查获物品分类
92
+     */
93
+    @PreAuthorize("@ss.hasPermi('system:category:remove')")
94
+    @Log(title = "查获物品分类", businessType = BusinessType.DELETE)
95
+    @DeleteMapping("/{ids}")
96
+    public AjaxResult remove(@PathVariable Long[] ids) {
97
+        return toAjax(baseSeizeCategoryService.deleteBaseSeizeCategoryByIds(ids));
98
+    }
99
+
100
+    /**
101
+     * 查询查获物品分类列表树形结构
102
+     */
103
+    @PreAuthorize("@ss.hasPermi('system:category:list')")
104
+    @GetMapping("/listTree")
105
+    public AjaxResult listTree(BaseSeizeCategory baseSeizeCategory) {
106
+        List<BaseSeizeCategory> list = baseSeizeCategoryService.selectBaseSeizeCategoryListTree(baseSeizeCategory);
107
+        return success(list);
108
+    }
109
+}

+ 109 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/system/BaseSeizeItemController.java

@@ -0,0 +1,109 @@
1
+package com.sundot.airport.web.controller.system;
2
+
3
+import java.util.List;
4
+import javax.servlet.http.HttpServletResponse;
5
+
6
+import org.springframework.security.access.prepost.PreAuthorize;
7
+import org.springframework.beans.factory.annotation.Autowired;
8
+import org.springframework.web.bind.annotation.GetMapping;
9
+import org.springframework.web.bind.annotation.PostMapping;
10
+import org.springframework.web.bind.annotation.PutMapping;
11
+import org.springframework.web.bind.annotation.DeleteMapping;
12
+import org.springframework.web.bind.annotation.PathVariable;
13
+import org.springframework.web.bind.annotation.RequestBody;
14
+import org.springframework.web.bind.annotation.RequestMapping;
15
+import org.springframework.web.bind.annotation.RestController;
16
+import com.sundot.airport.common.annotation.Log;
17
+import com.sundot.airport.common.core.controller.BaseController;
18
+import com.sundot.airport.common.core.domain.AjaxResult;
19
+import com.sundot.airport.common.enums.BusinessType;
20
+import com.sundot.airport.system.domain.BaseSeizeItem;
21
+import com.sundot.airport.system.service.IBaseSeizeItemService;
22
+import com.sundot.airport.common.utils.poi.ExcelUtil;
23
+import com.sundot.airport.common.core.page.TableDataInfo;
24
+
25
+/**
26
+ * 查获物品Controller
27
+ *
28
+ * @author ruoyi
29
+ * @date 2025-07-08
30
+ */
31
+@RestController
32
+@RequestMapping("/system/item")
33
+public class BaseSeizeItemController extends BaseController {
34
+    @Autowired
35
+    private IBaseSeizeItemService baseSeizeItemService;
36
+
37
+    /**
38
+     * 查询查获物品列表
39
+     */
40
+    @PreAuthorize("@ss.hasPermi('system:item:list')")
41
+    @GetMapping("/list")
42
+    public TableDataInfo list(BaseSeizeItem baseSeizeItem) {
43
+        startPage();
44
+        List<BaseSeizeItem> list = baseSeizeItemService.selectBaseSeizeItemList(baseSeizeItem);
45
+        return getDataTable(list);
46
+    }
47
+
48
+    /**
49
+     * 导出查获物品列表
50
+     */
51
+    @PreAuthorize("@ss.hasPermi('system:item:export')")
52
+    @Log(title = "查获物品", businessType = BusinessType.EXPORT)
53
+    @PostMapping("/export")
54
+    public void export(HttpServletResponse response, BaseSeizeItem baseSeizeItem) {
55
+        List<BaseSeizeItem> list = baseSeizeItemService.selectBaseSeizeItemList(baseSeizeItem);
56
+        ExcelUtil<BaseSeizeItem> util = new ExcelUtil<BaseSeizeItem>(BaseSeizeItem.class);
57
+        util.exportExcel(response, list, "查获物品数据");
58
+    }
59
+
60
+    /**
61
+     * 获取查获物品详细信息
62
+     */
63
+    @PreAuthorize("@ss.hasPermi('system:item:query')")
64
+    @GetMapping(value = "/{id}")
65
+    public AjaxResult getInfo(@PathVariable("id") Long id) {
66
+        return success(baseSeizeItemService.selectBaseSeizeItemById(id));
67
+    }
68
+
69
+    /**
70
+     * 新增查获物品
71
+     */
72
+    @PreAuthorize("@ss.hasPermi('system:item:add')")
73
+    @Log(title = "查获物品", businessType = BusinessType.INSERT)
74
+    @PostMapping
75
+    public AjaxResult add(@RequestBody BaseSeizeItem baseSeizeItem) {
76
+        baseSeizeItem.setCreateBy(getUsername());
77
+        return toAjax(baseSeizeItemService.insertBaseSeizeItem(baseSeizeItem));
78
+    }
79
+
80
+    /**
81
+     * 修改查获物品
82
+     */
83
+    @PreAuthorize("@ss.hasPermi('system:item:edit')")
84
+    @Log(title = "查获物品", businessType = BusinessType.UPDATE)
85
+    @PutMapping
86
+    public AjaxResult edit(@RequestBody BaseSeizeItem baseSeizeItem) {
87
+        baseSeizeItem.setUpdateBy(getUsername());
88
+        return toAjax(baseSeizeItemService.updateBaseSeizeItem(baseSeizeItem));
89
+    }
90
+
91
+    /**
92
+     * 删除查获物品
93
+     */
94
+    @PreAuthorize("@ss.hasPermi('system:item:remove')")
95
+    @Log(title = "查获物品", businessType = BusinessType.DELETE)
96
+    @DeleteMapping("/{ids}")
97
+    public AjaxResult remove(@PathVariable Long[] ids) {
98
+        return toAjax(baseSeizeItemService.deleteBaseSeizeItemByIds(ids));
99
+    }
100
+
101
+    /**
102
+     * 根据分类编码查询查获物品列表
103
+     */
104
+    @PreAuthorize("@ss.hasPermi('system:item:list')")
105
+    @GetMapping(value = "/list/{categoryCode}")
106
+    public AjaxResult list(@PathVariable("categoryCode") String categoryCode) {
107
+        return success(baseSeizeItemService.selectSeizeItemListByCategoryCode(categoryCode));
108
+    }
109
+}

+ 121 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/system/SysAppController.java

@@ -0,0 +1,121 @@
1
+package com.sundot.airport.web.controller.system;
2
+
3
+import java.util.List;
4
+import javax.servlet.http.HttpServletResponse;
5
+
6
+import org.springframework.security.access.prepost.PreAuthorize;
7
+import org.springframework.beans.factory.annotation.Autowired;
8
+import org.springframework.web.bind.annotation.GetMapping;
9
+import org.springframework.web.bind.annotation.PostMapping;
10
+import org.springframework.web.bind.annotation.PutMapping;
11
+import org.springframework.web.bind.annotation.DeleteMapping;
12
+import org.springframework.web.bind.annotation.PathVariable;
13
+import org.springframework.web.bind.annotation.RequestBody;
14
+import org.springframework.web.bind.annotation.RequestMapping;
15
+import org.springframework.web.bind.annotation.RestController;
16
+import com.sundot.airport.common.annotation.Log;
17
+import com.sundot.airport.common.core.controller.BaseController;
18
+import com.sundot.airport.common.core.domain.AjaxResult;
19
+import com.sundot.airport.common.enums.BusinessType;
20
+import com.sundot.airport.system.domain.SysApp;
21
+import com.sundot.airport.system.service.ISysAppService;
22
+import com.sundot.airport.common.utils.poi.ExcelUtil;
23
+import com.sundot.airport.common.core.page.TableDataInfo;
24
+
25
+/**
26
+ * 应用Controller
27
+ *
28
+ * @author ruoyi
29
+ * @date 2025-11-20
30
+ */
31
+@RestController
32
+@RequestMapping("/system/app")
33
+public class SysAppController extends BaseController {
34
+    @Autowired
35
+    private ISysAppService sysAppService;
36
+
37
+    /**
38
+     * 查询应用列表
39
+     */
40
+    @PreAuthorize("@ss.hasPermi('system:app:list')")
41
+    @GetMapping("/list")
42
+    public TableDataInfo list(SysApp sysApp) {
43
+        startPage();
44
+        List<SysApp> list = sysAppService.selectSysAppList(sysApp);
45
+        return getDataTable(list);
46
+    }
47
+
48
+    /**
49
+     * 导出应用列表
50
+     */
51
+    @PreAuthorize("@ss.hasPermi('system:app:export')")
52
+    @Log(title = "应用", businessType = BusinessType.EXPORT)
53
+    @PostMapping("/export")
54
+    public void export(HttpServletResponse response, SysApp sysApp) {
55
+        List<SysApp> list = sysAppService.selectSysAppList(sysApp);
56
+        ExcelUtil<SysApp> util = new ExcelUtil<SysApp>(SysApp.class);
57
+        util.exportExcel(response, list, "应用数据");
58
+    }
59
+
60
+    /**
61
+     * 获取应用详细信息
62
+     */
63
+    @PreAuthorize("@ss.hasPermi('system:app:query')")
64
+    @GetMapping(value = "/{appId}")
65
+    public AjaxResult getInfo(@PathVariable("appId") Long appId) {
66
+        return success(sysAppService.selectSysAppByAppId(appId));
67
+    }
68
+
69
+    /**
70
+     * 新增应用
71
+     */
72
+    @PreAuthorize("@ss.hasPermi('system:app:add')")
73
+    @Log(title = "应用", businessType = BusinessType.INSERT)
74
+    @PostMapping
75
+    public AjaxResult add(@RequestBody SysApp sysApp) {
76
+        sysApp.setCreateByUserId(getUserId());
77
+        sysApp.setCreateBy(getLoginUser().getUser().getNickName());
78
+        return toAjax(sysAppService.insertSysApp(sysApp));
79
+    }
80
+
81
+    /**
82
+     * 修改应用
83
+     */
84
+    @PreAuthorize("@ss.hasPermi('system:app:edit')")
85
+    @Log(title = "应用", businessType = BusinessType.UPDATE)
86
+    @PutMapping
87
+    public AjaxResult edit(@RequestBody SysApp sysApp) {
88
+        sysApp.setUpdateByUserId(getUserId());
89
+        sysApp.setUpdateBy(getLoginUser().getUser().getNickName());
90
+        return toAjax(sysAppService.updateSysApp(sysApp));
91
+    }
92
+
93
+    /**
94
+     * 删除应用
95
+     */
96
+    @PreAuthorize("@ss.hasPermi('system:app:remove')")
97
+    @Log(title = "应用", businessType = BusinessType.DELETE)
98
+    @DeleteMapping("/{appIds}")
99
+    public AjaxResult remove(@PathVariable Long[] appIds) {
100
+        return toAjax(sysAppService.deleteSysAppByAppIds(appIds));
101
+    }
102
+
103
+    /**
104
+     * 查询全部应用列表
105
+     */
106
+    @PreAuthorize("@ss.hasPermi('system:app:list')")
107
+    @GetMapping("/listAll")
108
+    public AjaxResult listAll(SysApp sysApp) {
109
+        List<SysApp> list = sysAppService.selectSysAppList(sysApp);
110
+        return success(list);
111
+    }
112
+
113
+    /**
114
+     * 根据角色ID查询应用列表
115
+     */
116
+    @GetMapping(value = "/roleAppSelect/{roleId}")
117
+    public AjaxResult roleAppSelect(@PathVariable("roleId") Long roleId) {
118
+        AjaxResult result = sysAppService.roleAppSelect(roleId);
119
+        return result;
120
+    }
121
+}

+ 133 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/system/SysConfigController.java

@@ -0,0 +1,133 @@
1
+package com.sundot.airport.web.controller.system;
2
+
3
+import java.util.List;
4
+import javax.servlet.http.HttpServletResponse;
5
+import org.springframework.beans.factory.annotation.Autowired;
6
+import org.springframework.security.access.prepost.PreAuthorize;
7
+import org.springframework.validation.annotation.Validated;
8
+import org.springframework.web.bind.annotation.DeleteMapping;
9
+import org.springframework.web.bind.annotation.GetMapping;
10
+import org.springframework.web.bind.annotation.PathVariable;
11
+import org.springframework.web.bind.annotation.PostMapping;
12
+import org.springframework.web.bind.annotation.PutMapping;
13
+import org.springframework.web.bind.annotation.RequestBody;
14
+import org.springframework.web.bind.annotation.RequestMapping;
15
+import org.springframework.web.bind.annotation.RestController;
16
+import com.sundot.airport.common.annotation.Log;
17
+import com.sundot.airport.common.core.controller.BaseController;
18
+import com.sundot.airport.common.core.domain.AjaxResult;
19
+import com.sundot.airport.common.core.page.TableDataInfo;
20
+import com.sundot.airport.common.enums.BusinessType;
21
+import com.sundot.airport.common.utils.poi.ExcelUtil;
22
+import com.sundot.airport.system.domain.SysConfig;
23
+import com.sundot.airport.system.service.ISysConfigService;
24
+
25
+/**
26
+ * 参数配置 信息操作处理
27
+ * 
28
+ * @author ruoyi
29
+ */
30
+@RestController
31
+@RequestMapping("/system/config")
32
+public class SysConfigController extends BaseController
33
+{
34
+    @Autowired
35
+    private ISysConfigService configService;
36
+
37
+    /**
38
+     * 获取参数配置列表
39
+     */
40
+    @PreAuthorize("@ss.hasPermi('system:config:list')")
41
+    @GetMapping("/list")
42
+    public TableDataInfo list(SysConfig config)
43
+    {
44
+        startPage();
45
+        List<SysConfig> list = configService.selectConfigList(config);
46
+        return getDataTable(list);
47
+    }
48
+
49
+    @Log(title = "参数管理", businessType = BusinessType.EXPORT)
50
+    @PreAuthorize("@ss.hasPermi('system:config:export')")
51
+    @PostMapping("/export")
52
+    public void export(HttpServletResponse response, SysConfig config)
53
+    {
54
+        List<SysConfig> list = configService.selectConfigList(config);
55
+        ExcelUtil<SysConfig> util = new ExcelUtil<SysConfig>(SysConfig.class);
56
+        util.exportExcel(response, list, "参数数据");
57
+    }
58
+
59
+    /**
60
+     * 根据参数编号获取详细信息
61
+     */
62
+    @PreAuthorize("@ss.hasPermi('system:config:query')")
63
+    @GetMapping(value = "/{configId}")
64
+    public AjaxResult getInfo(@PathVariable Long configId)
65
+    {
66
+        return success(configService.selectConfigById(configId));
67
+    }
68
+
69
+    /**
70
+     * 根据参数键名查询参数值
71
+     */
72
+    @GetMapping(value = "/configKey/{configKey}")
73
+    public AjaxResult getConfigKey(@PathVariable String configKey)
74
+    {
75
+        return success(configService.selectConfigByKey(configKey));
76
+    }
77
+
78
+    /**
79
+     * 新增参数配置
80
+     */
81
+    @PreAuthorize("@ss.hasPermi('system:config:add')")
82
+    @Log(title = "参数管理", businessType = BusinessType.INSERT)
83
+    @PostMapping
84
+    public AjaxResult add(@Validated @RequestBody SysConfig config)
85
+    {
86
+        if (!configService.checkConfigKeyUnique(config))
87
+        {
88
+            return error("新增参数'" + config.getConfigName() + "'失败,参数键名已存在");
89
+        }
90
+        config.setCreateBy(getUsername());
91
+        return toAjax(configService.insertConfig(config));
92
+    }
93
+
94
+    /**
95
+     * 修改参数配置
96
+     */
97
+    @PreAuthorize("@ss.hasPermi('system:config:edit')")
98
+    @Log(title = "参数管理", businessType = BusinessType.UPDATE)
99
+    @PutMapping
100
+    public AjaxResult edit(@Validated @RequestBody SysConfig config)
101
+    {
102
+        if (!configService.checkConfigKeyUnique(config))
103
+        {
104
+            return error("修改参数'" + config.getConfigName() + "'失败,参数键名已存在");
105
+        }
106
+        config.setUpdateBy(getUsername());
107
+        return toAjax(configService.updateConfig(config));
108
+    }
109
+
110
+    /**
111
+     * 删除参数配置
112
+     */
113
+    @PreAuthorize("@ss.hasPermi('system:config:remove')")
114
+    @Log(title = "参数管理", businessType = BusinessType.DELETE)
115
+    @DeleteMapping("/{configIds}")
116
+    public AjaxResult remove(@PathVariable Long[] configIds)
117
+    {
118
+        configService.deleteConfigByIds(configIds);
119
+        return success();
120
+    }
121
+
122
+    /**
123
+     * 刷新参数缓存
124
+     */
125
+    @PreAuthorize("@ss.hasPermi('system:config:remove')")
126
+    @Log(title = "参数管理", businessType = BusinessType.CLEAN)
127
+    @DeleteMapping("/refreshCache")
128
+    public AjaxResult refreshCache()
129
+    {
130
+        configService.resetConfigCache();
131
+        return success();
132
+    }
133
+}

+ 154 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/system/SysDeptController.java

@@ -0,0 +1,154 @@
1
+package com.sundot.airport.web.controller.system;
2
+
3
+import cn.hutool.core.collection.CollectionUtil;
4
+import cn.hutool.core.util.StrUtil;
5
+import com.sundot.airport.common.annotation.Log;
6
+import com.sundot.airport.common.constant.UserConstants;
7
+import com.sundot.airport.common.core.controller.BaseController;
8
+import com.sundot.airport.common.core.domain.AjaxResult;
9
+import com.sundot.airport.common.core.domain.entity.SysDept;
10
+import com.sundot.airport.common.enums.BusinessType;
11
+import com.sundot.airport.common.enums.DeptTypeEnum;
12
+import com.sundot.airport.common.utils.StringUtils;
13
+import com.sundot.airport.system.service.ISysDeptService;
14
+import org.apache.commons.lang3.ArrayUtils;
15
+import org.springframework.beans.factory.annotation.Autowired;
16
+import org.springframework.security.access.prepost.PreAuthorize;
17
+import org.springframework.validation.annotation.Validated;
18
+import org.springframework.web.bind.annotation.*;
19
+
20
+import java.util.*;
21
+import java.util.stream.Collectors;
22
+
23
+/**
24
+ * 部门信息
25
+ *
26
+ * @author ruoyi
27
+ */
28
+@RestController
29
+@RequestMapping("/system/dept")
30
+public class SysDeptController extends BaseController {
31
+    @Autowired
32
+    private ISysDeptService deptService;
33
+
34
+    /**
35
+     * 获取部门列表
36
+     */
37
+    @PreAuthorize("@ss.hasPermi('system:dept:list')")
38
+    @GetMapping("/list")
39
+    public AjaxResult list(SysDept dept) {
40
+        List<SysDept> depts = deptService.selectDeptList(dept);
41
+        return success(depts);
42
+    }
43
+
44
+    /**
45
+     * 查询部门列表(排除节点)
46
+     */
47
+    @PreAuthorize("@ss.hasPermi('system:dept:list')")
48
+    @GetMapping("/list/exclude/{deptId}")
49
+    public AjaxResult excludeChild(@PathVariable(value = "deptId", required = false) Long deptId) {
50
+        List<SysDept> depts = deptService.selectDeptList(new SysDept());
51
+        depts.removeIf(d -> d.getDeptId().intValue() == deptId || ArrayUtils.contains(StringUtils.split(d.getAncestors(), ","), deptId + ""));
52
+        return success(depts);
53
+    }
54
+
55
+    /**
56
+     * 根据部门编号获取详细信息
57
+     */
58
+    @PreAuthorize("@ss.hasPermi('system:dept:query')")
59
+    @GetMapping(value = "/{deptId}")
60
+    public AjaxResult getInfo(@PathVariable Long deptId) {
61
+        deptService.checkDeptDataScope(deptId);
62
+        return success(deptService.selectDeptById(deptId));
63
+    }
64
+
65
+    /**
66
+     * 新增部门
67
+     */
68
+    @PreAuthorize("@ss.hasPermi('system:dept:add')")
69
+    @Log(title = "部门管理", businessType = BusinessType.INSERT)
70
+    @PostMapping
71
+    public AjaxResult add(@Validated @RequestBody SysDept dept) {
72
+        if (!deptService.checkDeptNameUnique(dept)) {
73
+            return error("新增部门'" + dept.getDeptName() + "'失败,部门名称已存在");
74
+        }
75
+        dept.setCreateBy(getUsername());
76
+        return toAjax(deptService.insertDept(dept));
77
+    }
78
+
79
+    /**
80
+     * 修改部门
81
+     */
82
+    @PreAuthorize("@ss.hasPermi('system:dept:edit')")
83
+    @Log(title = "部门管理", businessType = BusinessType.UPDATE)
84
+    @PutMapping
85
+    public AjaxResult edit(@Validated @RequestBody SysDept dept) {
86
+        Long deptId = dept.getDeptId();
87
+        deptService.checkDeptDataScope(deptId);
88
+        if (!deptService.checkDeptNameUnique(dept)) {
89
+            return error("修改部门'" + dept.getDeptName() + "'失败,部门名称已存在");
90
+        } else if (dept.getParentId().equals(deptId)) {
91
+            return error("修改部门'" + dept.getDeptName() + "'失败,上级部门不能是自己");
92
+        } else if (StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus()) && deptService.selectNormalChildrenDeptById(deptId) > 0) {
93
+            return error("该部门包含未停用的子部门!");
94
+        }
95
+        dept.setUpdateBy(getUsername());
96
+        return toAjax(deptService.updateDept(dept));
97
+    }
98
+
99
+    /**
100
+     * 删除部门
101
+     */
102
+    @PreAuthorize("@ss.hasPermi('system:dept:remove')")
103
+    @Log(title = "部门管理", businessType = BusinessType.DELETE)
104
+    @DeleteMapping("/{deptId}")
105
+    public AjaxResult remove(@PathVariable Long deptId) {
106
+        if (deptService.hasChildByDeptId(deptId)) {
107
+            return warn("存在下级部门,不允许删除");
108
+        }
109
+        if (deptService.checkDeptExistUser(deptId)) {
110
+            return warn("部门存在用户,不允许删除");
111
+        }
112
+        deptService.checkDeptDataScope(deptId);
113
+        return toAjax(deptService.deleteDeptById(deptId));
114
+    }
115
+
116
+    @PostMapping("/teamList")
117
+    public AjaxResult teamList(@RequestBody SysDept dept) {
118
+        return success(buildTree(deptService.selectDeptList(dept)));
119
+    }
120
+
121
+    private Map<String, String> buildTree(List<SysDept> list) {
122
+        Map<String, String> result = new HashMap<>();
123
+        // 从班组开始往上推,最多3级
124
+        List<SysDept> teams = list.stream().filter(x -> StrUtil.equals(x.getDeptType(), DeptTypeEnum.TEAMS.getCode())).collect(Collectors.toList());
125
+        if (CollectionUtil.isEmpty(teams)) {
126
+            return result;
127
+        }
128
+        teams.forEach(team -> {
129
+            String val = team.getDeptId().toString();
130
+            StringBuffer label = new StringBuffer(team.getDeptName());
131
+            if (StrUtil.isNotBlank(team.getAncestors())) {
132
+                String[] splits = team.getAncestors().split(",");
133
+                // splits 去掉 第一个元素,并且反转
134
+                List<String> reverse = Arrays.stream(splits).skip(1).collect(Collectors.toList());
135
+                // reverse 元素反转
136
+                Collections.reverse(reverse);
137
+                reverse.forEach(parent -> {
138
+                    SysDept department = list.stream().filter(x -> StrUtil.equals(parent, x.getDeptId().toString())).findFirst().orElse(new SysDept());
139
+                    label.insert(0, department.getDeptName() + "/");
140
+                });
141
+            }
142
+            result.put(val, label.toString());
143
+        });
144
+        return result;
145
+    }
146
+
147
+    /**
148
+     * 查询部门负责人
149
+     */
150
+    @GetMapping(value = "/deptLeader/{deptId}")
151
+    public AjaxResult deptLeader(@PathVariable Long deptId) {
152
+        return success(deptService.deptLeader(deptId));
153
+    }
154
+}

+ 121 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/system/SysDictDataController.java

@@ -0,0 +1,121 @@
1
+package com.sundot.airport.web.controller.system;
2
+
3
+import java.util.ArrayList;
4
+import java.util.List;
5
+import javax.servlet.http.HttpServletResponse;
6
+import org.springframework.beans.factory.annotation.Autowired;
7
+import org.springframework.security.access.prepost.PreAuthorize;
8
+import org.springframework.validation.annotation.Validated;
9
+import org.springframework.web.bind.annotation.DeleteMapping;
10
+import org.springframework.web.bind.annotation.GetMapping;
11
+import org.springframework.web.bind.annotation.PathVariable;
12
+import org.springframework.web.bind.annotation.PostMapping;
13
+import org.springframework.web.bind.annotation.PutMapping;
14
+import org.springframework.web.bind.annotation.RequestBody;
15
+import org.springframework.web.bind.annotation.RequestMapping;
16
+import org.springframework.web.bind.annotation.RestController;
17
+import com.sundot.airport.common.annotation.Log;
18
+import com.sundot.airport.common.core.controller.BaseController;
19
+import com.sundot.airport.common.core.domain.AjaxResult;
20
+import com.sundot.airport.common.core.domain.entity.SysDictData;
21
+import com.sundot.airport.common.core.page.TableDataInfo;
22
+import com.sundot.airport.common.enums.BusinessType;
23
+import com.sundot.airport.common.utils.StringUtils;
24
+import com.sundot.airport.common.utils.poi.ExcelUtil;
25
+import com.sundot.airport.system.service.ISysDictDataService;
26
+import com.sundot.airport.system.service.ISysDictTypeService;
27
+
28
+/**
29
+ * 数据字典信息
30
+ * 
31
+ * @author ruoyi
32
+ */
33
+@RestController
34
+@RequestMapping("/system/dict/data")
35
+public class SysDictDataController extends BaseController
36
+{
37
+    @Autowired
38
+    private ISysDictDataService dictDataService;
39
+
40
+    @Autowired
41
+    private ISysDictTypeService dictTypeService;
42
+
43
+    @PreAuthorize("@ss.hasPermi('system:dict:list')")
44
+    @GetMapping("/list")
45
+    public TableDataInfo list(SysDictData dictData)
46
+    {
47
+        startPage();
48
+        List<SysDictData> list = dictDataService.selectDictDataList(dictData);
49
+        return getDataTable(list);
50
+    }
51
+
52
+    @Log(title = "字典数据", businessType = BusinessType.EXPORT)
53
+    @PreAuthorize("@ss.hasPermi('system:dict:export')")
54
+    @PostMapping("/export")
55
+    public void export(HttpServletResponse response, SysDictData dictData)
56
+    {
57
+        List<SysDictData> list = dictDataService.selectDictDataList(dictData);
58
+        ExcelUtil<SysDictData> util = new ExcelUtil<SysDictData>(SysDictData.class);
59
+        util.exportExcel(response, list, "字典数据");
60
+    }
61
+
62
+    /**
63
+     * 查询字典数据详细
64
+     */
65
+    @PreAuthorize("@ss.hasPermi('system:dict:query')")
66
+    @GetMapping(value = "/{dictCode}")
67
+    public AjaxResult getInfo(@PathVariable Long dictCode)
68
+    {
69
+        return success(dictDataService.selectDictDataById(dictCode));
70
+    }
71
+
72
+    /**
73
+     * 根据字典类型查询字典数据信息
74
+     */
75
+    @GetMapping(value = "/type/{dictType}")
76
+    public AjaxResult dictType(@PathVariable String dictType)
77
+    {
78
+        List<SysDictData> data = dictTypeService.selectDictDataByType(dictType);
79
+        if (StringUtils.isNull(data))
80
+        {
81
+            data = new ArrayList<SysDictData>();
82
+        }
83
+        return success(data);
84
+    }
85
+
86
+    /**
87
+     * 新增字典类型
88
+     */
89
+    @PreAuthorize("@ss.hasPermi('system:dict:add')")
90
+    @Log(title = "字典数据", businessType = BusinessType.INSERT)
91
+    @PostMapping
92
+    public AjaxResult add(@Validated @RequestBody SysDictData dict)
93
+    {
94
+        dict.setCreateBy(getUsername());
95
+        return toAjax(dictDataService.insertDictData(dict));
96
+    }
97
+
98
+    /**
99
+     * 修改保存字典类型
100
+     */
101
+    @PreAuthorize("@ss.hasPermi('system:dict:edit')")
102
+    @Log(title = "字典数据", businessType = BusinessType.UPDATE)
103
+    @PutMapping
104
+    public AjaxResult edit(@Validated @RequestBody SysDictData dict)
105
+    {
106
+        dict.setUpdateBy(getUsername());
107
+        return toAjax(dictDataService.updateDictData(dict));
108
+    }
109
+
110
+    /**
111
+     * 删除字典类型
112
+     */
113
+    @PreAuthorize("@ss.hasPermi('system:dict:remove')")
114
+    @Log(title = "字典类型", businessType = BusinessType.DELETE)
115
+    @DeleteMapping("/{dictCodes}")
116
+    public AjaxResult remove(@PathVariable Long[] dictCodes)
117
+    {
118
+        dictDataService.deleteDictDataByIds(dictCodes);
119
+        return success();
120
+    }
121
+}

+ 131 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/system/SysDictTypeController.java

@@ -0,0 +1,131 @@
1
+package com.sundot.airport.web.controller.system;
2
+
3
+import java.util.List;
4
+import javax.servlet.http.HttpServletResponse;
5
+import org.springframework.beans.factory.annotation.Autowired;
6
+import org.springframework.security.access.prepost.PreAuthorize;
7
+import org.springframework.validation.annotation.Validated;
8
+import org.springframework.web.bind.annotation.DeleteMapping;
9
+import org.springframework.web.bind.annotation.GetMapping;
10
+import org.springframework.web.bind.annotation.PathVariable;
11
+import org.springframework.web.bind.annotation.PostMapping;
12
+import org.springframework.web.bind.annotation.PutMapping;
13
+import org.springframework.web.bind.annotation.RequestBody;
14
+import org.springframework.web.bind.annotation.RequestMapping;
15
+import org.springframework.web.bind.annotation.RestController;
16
+import com.sundot.airport.common.annotation.Log;
17
+import com.sundot.airport.common.core.controller.BaseController;
18
+import com.sundot.airport.common.core.domain.AjaxResult;
19
+import com.sundot.airport.common.core.domain.entity.SysDictType;
20
+import com.sundot.airport.common.core.page.TableDataInfo;
21
+import com.sundot.airport.common.enums.BusinessType;
22
+import com.sundot.airport.common.utils.poi.ExcelUtil;
23
+import com.sundot.airport.system.service.ISysDictTypeService;
24
+
25
+/**
26
+ * 数据字典信息
27
+ * 
28
+ * @author ruoyi
29
+ */
30
+@RestController
31
+@RequestMapping("/system/dict/type")
32
+public class SysDictTypeController extends BaseController
33
+{
34
+    @Autowired
35
+    private ISysDictTypeService dictTypeService;
36
+
37
+    @PreAuthorize("@ss.hasPermi('system:dict:list')")
38
+    @GetMapping("/list")
39
+    public TableDataInfo list(SysDictType dictType)
40
+    {
41
+        startPage();
42
+        List<SysDictType> list = dictTypeService.selectDictTypeList(dictType);
43
+        return getDataTable(list);
44
+    }
45
+
46
+    @Log(title = "字典类型", businessType = BusinessType.EXPORT)
47
+    @PreAuthorize("@ss.hasPermi('system:dict:export')")
48
+    @PostMapping("/export")
49
+    public void export(HttpServletResponse response, SysDictType dictType)
50
+    {
51
+        List<SysDictType> list = dictTypeService.selectDictTypeList(dictType);
52
+        ExcelUtil<SysDictType> util = new ExcelUtil<SysDictType>(SysDictType.class);
53
+        util.exportExcel(response, list, "字典类型");
54
+    }
55
+
56
+    /**
57
+     * 查询字典类型详细
58
+     */
59
+    @PreAuthorize("@ss.hasPermi('system:dict:query')")
60
+    @GetMapping(value = "/{dictId}")
61
+    public AjaxResult getInfo(@PathVariable Long dictId)
62
+    {
63
+        return success(dictTypeService.selectDictTypeById(dictId));
64
+    }
65
+
66
+    /**
67
+     * 新增字典类型
68
+     */
69
+    @PreAuthorize("@ss.hasPermi('system:dict:add')")
70
+    @Log(title = "字典类型", businessType = BusinessType.INSERT)
71
+    @PostMapping
72
+    public AjaxResult add(@Validated @RequestBody SysDictType dict)
73
+    {
74
+        if (!dictTypeService.checkDictTypeUnique(dict))
75
+        {
76
+            return error("新增字典'" + dict.getDictName() + "'失败,字典类型已存在");
77
+        }
78
+        dict.setCreateBy(getUsername());
79
+        return toAjax(dictTypeService.insertDictType(dict));
80
+    }
81
+
82
+    /**
83
+     * 修改字典类型
84
+     */
85
+    @PreAuthorize("@ss.hasPermi('system:dict:edit')")
86
+    @Log(title = "字典类型", businessType = BusinessType.UPDATE)
87
+    @PutMapping
88
+    public AjaxResult edit(@Validated @RequestBody SysDictType dict)
89
+    {
90
+        if (!dictTypeService.checkDictTypeUnique(dict))
91
+        {
92
+            return error("修改字典'" + dict.getDictName() + "'失败,字典类型已存在");
93
+        }
94
+        dict.setUpdateBy(getUsername());
95
+        return toAjax(dictTypeService.updateDictType(dict));
96
+    }
97
+
98
+    /**
99
+     * 删除字典类型
100
+     */
101
+    @PreAuthorize("@ss.hasPermi('system:dict:remove')")
102
+    @Log(title = "字典类型", businessType = BusinessType.DELETE)
103
+    @DeleteMapping("/{dictIds}")
104
+    public AjaxResult remove(@PathVariable Long[] dictIds)
105
+    {
106
+        dictTypeService.deleteDictTypeByIds(dictIds);
107
+        return success();
108
+    }
109
+
110
+    /**
111
+     * 刷新字典缓存
112
+     */
113
+    @PreAuthorize("@ss.hasPermi('system:dict:remove')")
114
+    @Log(title = "字典类型", businessType = BusinessType.CLEAN)
115
+    @DeleteMapping("/refreshCache")
116
+    public AjaxResult refreshCache()
117
+    {
118
+        dictTypeService.resetDictCache();
119
+        return success();
120
+    }
121
+
122
+    /**
123
+     * 获取字典选择框列表
124
+     */
125
+    @GetMapping("/optionselect")
126
+    public AjaxResult optionselect()
127
+    {
128
+        List<SysDictType> dictTypes = dictTypeService.selectDictTypeAll();
129
+        return success(dictTypes);
130
+    }
131
+}

+ 101 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/system/SysDynamicSqlController.java

@@ -0,0 +1,101 @@
1
+package com.sundot.airport.web.controller.system;
2
+
3
+import com.sundot.airport.common.annotation.Log;
4
+import com.sundot.airport.common.core.controller.BaseController;
5
+import com.sundot.airport.common.core.domain.AjaxResult;
6
+import com.sundot.airport.common.core.page.TableDataInfo;
7
+import com.sundot.airport.common.enums.BusinessType;
8
+import com.sundot.airport.common.utils.poi.ExcelUtil;
9
+import com.sundot.airport.system.domain.SysDynamicSql;
10
+import com.sundot.airport.system.service.ISysDynamicSqlService;
11
+import org.springframework.beans.factory.annotation.Autowired;
12
+import org.springframework.web.bind.annotation.*;
13
+
14
+import javax.servlet.http.HttpServletResponse;
15
+import java.util.List;
16
+import java.util.Map;
17
+
18
+/**
19
+ * 动态sql执行configController
20
+ *
21
+ * @author ruoyi
22
+ * @date 2025-06-20
23
+ */
24
+@RestController
25
+@RequestMapping("/system/sql")
26
+public class SysDynamicSqlController extends BaseController
27
+{
28
+    @Autowired
29
+    private ISysDynamicSqlService sysDynamicSqlService;
30
+
31
+    /**
32
+     * 查询动态sql执行config列表
33
+     */
34
+    @GetMapping("/list")
35
+    public TableDataInfo list(SysDynamicSql sysDynamicSql)
36
+    {
37
+        startPage();
38
+        List<SysDynamicSql> list = sysDynamicSqlService.selectSysDynamicSqlList(sysDynamicSql);
39
+        return getDataTable(list);
40
+    }
41
+
42
+    /**
43
+     * 导出动态sql执行config列表
44
+     */
45
+    @Log(title = "动态sql执行config", businessType = BusinessType.EXPORT)
46
+    @PostMapping("/export")
47
+    public void export(HttpServletResponse response, SysDynamicSql sysDynamicSql)
48
+    {
49
+        List<SysDynamicSql> list = sysDynamicSqlService.selectSysDynamicSqlList(sysDynamicSql);
50
+        ExcelUtil<SysDynamicSql> util = new ExcelUtil<SysDynamicSql>(SysDynamicSql.class);
51
+        util.exportExcel(response, list, "动态sql执行config数据");
52
+    }
53
+
54
+    /**
55
+     * 获取动态sql执行config详细信息
56
+     */
57
+    @GetMapping(value = "/{id}")
58
+    public AjaxResult getInfo(@PathVariable("id") Long id)
59
+    {
60
+        return success(sysDynamicSqlService.selectSysDynamicSqlById(id));
61
+    }
62
+
63
+    /**
64
+     * 新增动态sql执行config
65
+     */
66
+    @Log(title = "动态sql执行config", businessType = BusinessType.INSERT)
67
+    @PostMapping
68
+    public AjaxResult add(@RequestBody SysDynamicSql sysDynamicSql)
69
+    {
70
+        return toAjax(sysDynamicSqlService.insertSysDynamicSql(sysDynamicSql));
71
+    }
72
+
73
+    /**
74
+     * 修改动态sql执行config
75
+     */
76
+    @Log(title = "动态sql执行config", businessType = BusinessType.UPDATE)
77
+    @PutMapping
78
+    public AjaxResult edit(@RequestBody SysDynamicSql sysDynamicSql)
79
+    {
80
+        return toAjax(sysDynamicSqlService.updateSysDynamicSql(sysDynamicSql));
81
+    }
82
+
83
+    /**
84
+     * 删除动态sql执行config
85
+     */
86
+    @Log(title = "动态sql执行config", businessType = BusinessType.DELETE)
87
+    @DeleteMapping("/{ids}")
88
+    public AjaxResult remove(@PathVariable Long[] ids)
89
+    {
90
+        return toAjax(sysDynamicSqlService.deleteSysDynamicSqlByIds(ids));
91
+    }
92
+
93
+    /**
94
+     * 删除动态sql执行config
95
+     */
96
+    @PostMapping("/executeSqlByKeyAndParam/{sqlKey}")
97
+    public AjaxResult executeSqlByKeyAndParam(@PathVariable(name = "sqlKey") String sqlkey,@RequestBody Map<String, Object> params)
98
+    {
99
+        return success(sysDynamicSqlService.executeSqlByKeyAndParam(sqlkey,params));
100
+    }
101
+}

+ 29 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/system/SysIndexController.java

@@ -0,0 +1,29 @@
1
+package com.sundot.airport.web.controller.system;
2
+
3
+import org.springframework.beans.factory.annotation.Autowired;
4
+import org.springframework.web.bind.annotation.RequestMapping;
5
+import org.springframework.web.bind.annotation.RestController;
6
+import com.sundot.airport.common.config.RuoYiConfig;
7
+import com.sundot.airport.common.utils.StringUtils;
8
+
9
+/**
10
+ * 首页
11
+ *
12
+ * @author ruoyi
13
+ */
14
+@RestController
15
+public class SysIndexController
16
+{
17
+    /** 系统基础配置 */
18
+    @Autowired
19
+    private RuoYiConfig ruoyiConfig;
20
+
21
+    /**
22
+     * 访问首页,提示语
23
+     */
24
+    @RequestMapping("/")
25
+    public String index()
26
+    {
27
+        return StringUtils.format("欢迎使用{}后台管理框架,当前版本:v{},请通过前端地址访问。", ruoyiConfig.getName(), ruoyiConfig.getVersion());
28
+    }
29
+}

+ 160 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/system/SysLearningGrowthController.java

@@ -0,0 +1,160 @@
1
+package com.sundot.airport.web.controller.system;
2
+
3
+import java.util.List;
4
+import javax.servlet.http.HttpServletResponse;
5
+
6
+import com.sundot.airport.common.core.domain.BaseLargeScreenQueryParamDto;
7
+import com.sundot.airport.system.domain.SysLargeScreenLearningGrowthDto;
8
+import com.sundot.airport.system.domain.SysLargeScreenLearningGrowthOrganizationalSupportDto;
9
+import com.sundot.airport.system.domain.SysLargeScreenLearningGrowthQueryParamDto;
10
+import lombok.SneakyThrows;
11
+import org.springframework.cache.annotation.Cacheable;
12
+import org.springframework.security.access.prepost.PreAuthorize;
13
+import org.springframework.beans.factory.annotation.Autowired;
14
+import org.springframework.web.bind.annotation.GetMapping;
15
+import org.springframework.web.bind.annotation.PostMapping;
16
+import org.springframework.web.bind.annotation.DeleteMapping;
17
+import org.springframework.web.bind.annotation.PathVariable;
18
+import org.springframework.web.bind.annotation.RequestBody;
19
+import org.springframework.web.bind.annotation.RequestMapping;
20
+import org.springframework.web.bind.annotation.RestController;
21
+import com.sundot.airport.common.annotation.Log;
22
+import com.sundot.airport.common.core.controller.BaseController;
23
+import com.sundot.airport.common.core.domain.AjaxResult;
24
+import com.sundot.airport.common.enums.BusinessType;
25
+import com.sundot.airport.system.domain.SysLearningGrowth;
26
+import com.sundot.airport.system.service.ISysLearningGrowthService;
27
+import com.sundot.airport.common.utils.poi.ExcelUtil;
28
+import com.sundot.airport.common.core.page.TableDataInfo;
29
+import org.springframework.web.multipart.MultipartFile;
30
+
31
+/**
32
+ * 学习成长Controller
33
+ *
34
+ * @author ruoyi
35
+ * @date 2025-10-22
36
+ */
37
+@RestController
38
+@RequestMapping("/system/growth")
39
+public class SysLearningGrowthController extends BaseController {
40
+    @Autowired
41
+    private ISysLearningGrowthService sysLearningGrowthService;
42
+
43
+    /**
44
+     * 查询学习成长列表
45
+     */
46
+    @PreAuthorize("@ss.hasPermi('system:growth:list')")
47
+    @GetMapping("/list")
48
+    public TableDataInfo list(SysLearningGrowth sysLearningGrowth) {
49
+        startPage();
50
+        List<SysLearningGrowth> list = sysLearningGrowthService.selectSysLearningGrowthList(sysLearningGrowth);
51
+        return getDataTable(list);
52
+    }
53
+
54
+    /**
55
+     * 导出学习成长列表
56
+     */
57
+    @PreAuthorize("@ss.hasPermi('system:growth:export')")
58
+    @Log(title = "学习成长", businessType = BusinessType.EXPORT)
59
+    @PostMapping("/export")
60
+    public void export(HttpServletResponse response, SysLearningGrowth sysLearningGrowth) {
61
+        List<SysLearningGrowth> list = sysLearningGrowthService.selectSysLearningGrowthList(sysLearningGrowth);
62
+        ExcelUtil<SysLearningGrowth> util = new ExcelUtil<SysLearningGrowth>(SysLearningGrowth.class);
63
+        util.exportExcel(response, list, "学习成长数据");
64
+    }
65
+
66
+    /**
67
+     * 获取学习成长详细信息
68
+     */
69
+    @PreAuthorize("@ss.hasPermi('system:growth:query')")
70
+    @GetMapping(value = "/{id}")
71
+    public AjaxResult getInfo(@PathVariable("id") Long id) {
72
+        return success(sysLearningGrowthService.selectSysLearningGrowthById(id));
73
+    }
74
+
75
+    /**
76
+     * 新增学习成长
77
+     */
78
+    @PreAuthorize("@ss.hasPermi('system:growth:add')")
79
+    @Log(title = "学习成长", businessType = BusinessType.INSERT)
80
+    @PostMapping("/add")
81
+    public AjaxResult add(@RequestBody SysLearningGrowth sysLearningGrowth) {
82
+        sysLearningGrowth.setCreateBy(getUsername());
83
+        return toAjax(sysLearningGrowthService.insertSysLearningGrowth(sysLearningGrowth));
84
+    }
85
+
86
+    /**
87
+     * 修改学习成长
88
+     */
89
+    @PreAuthorize("@ss.hasPermi('system:growth:edit')")
90
+    @Log(title = "学习成长", businessType = BusinessType.UPDATE)
91
+    @PostMapping("/edit")
92
+    public AjaxResult edit(@RequestBody SysLearningGrowth sysLearningGrowth) {
93
+        sysLearningGrowth.setUpdateBy(getUsername());
94
+        return toAjax(sysLearningGrowthService.updateSysLearningGrowth(sysLearningGrowth));
95
+    }
96
+
97
+    /**
98
+     * 删除学习成长
99
+     */
100
+    @PreAuthorize("@ss.hasPermi('system:growth:remove')")
101
+    @Log(title = "学习成长", businessType = BusinessType.DELETE)
102
+    @DeleteMapping("/{ids}")
103
+    public AjaxResult remove(@PathVariable Long[] ids) {
104
+        return toAjax(sysLearningGrowthService.deleteSysLearningGrowthByIds(ids));
105
+    }
106
+
107
+    /**
108
+     * 下载模板
109
+     */
110
+    @Log(title = "学习成长", businessType = BusinessType.IMPORT)
111
+    @PreAuthorize("@ss.hasPermi('system:growth:import')")
112
+    @PostMapping("/importTemplate")
113
+    public void importTemplate(HttpServletResponse response) {
114
+        ExcelUtil<SysLearningGrowth> util = new ExcelUtil<SysLearningGrowth>(SysLearningGrowth.class);
115
+        util.importTemplateExcel(response, "学习成长");
116
+    }
117
+
118
+    /**
119
+     * 导入数据
120
+     */
121
+    @SneakyThrows
122
+    @Log(title = "学习成长", businessType = BusinessType.IMPORT)
123
+    @PreAuthorize("@ss.hasPermi('system:growth:import')")
124
+    @PostMapping("/importData")
125
+    public AjaxResult importData(MultipartFile file, boolean updateSupport) {
126
+        ExcelUtil<SysLearningGrowth> util = new ExcelUtil<SysLearningGrowth>(SysLearningGrowth.class);
127
+        List<SysLearningGrowth> list = util.importExcel(file.getInputStream());
128
+        String operName = getUsername();
129
+        String message = sysLearningGrowthService.importData(list, updateSupport, operName);
130
+        return success(message);
131
+    }
132
+
133
+    /**
134
+     * 能力画像-学习成长
135
+     */
136
+    @Cacheable(
137
+            value = "statistics_list",
138
+            keyGenerator = "statisticsKeyGenerator",
139
+            unless = "!T(com.sundot.airport.common.cache.StatisticsCacheConditionUtil).isSuccess(#result) ||T(com.sundot.airport.common.cache.StatisticsCacheConditionUtil).isEmptyData(#result)"
140
+    )
141
+    @GetMapping("/portrait")
142
+    public AjaxResult portrait(SysLargeScreenLearningGrowthQueryParamDto dto) {
143
+        List<SysLargeScreenLearningGrowthDto> result = sysLearningGrowthService.portrait(dto);
144
+        return success(result);
145
+    }
146
+
147
+    /**
148
+     * 工作画像-组织支撑-培训测试平均分趋势图
149
+     */
150
+    @Cacheable(
151
+            value = "statistics_list",
152
+            keyGenerator = "statisticsKeyGenerator",
153
+            unless = "!T(com.sundot.airport.common.cache.StatisticsCacheConditionUtil).isSuccess(#result) ||T(com.sundot.airport.common.cache.StatisticsCacheConditionUtil).isEmptyData(#result)"
154
+    )
155
+    @GetMapping("/organizationalSupport")
156
+    public AjaxResult organizationalSupport(BaseLargeScreenQueryParamDto dto) {
157
+        List<SysLargeScreenLearningGrowthOrganizationalSupportDto> result = sysLearningGrowthService.organizationalSupport(dto);
158
+        return success(result);
159
+    }
160
+}

+ 130 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/system/SysLoginController.java

@@ -0,0 +1,130 @@
1
+package com.sundot.airport.web.controller.system;
2
+
3
+import java.util.Date;
4
+import java.util.List;
5
+import java.util.Set;
6
+
7
+import com.sundot.airport.common.dto.UserInfo;
8
+import com.sundot.airport.web.core.cache.UserCache;
9
+import org.springframework.beans.factory.annotation.Autowired;
10
+import org.springframework.web.bind.annotation.GetMapping;
11
+import org.springframework.web.bind.annotation.PostMapping;
12
+import org.springframework.web.bind.annotation.RequestBody;
13
+import org.springframework.web.bind.annotation.RestController;
14
+import com.sundot.airport.common.constant.Constants;
15
+import com.sundot.airport.common.core.domain.AjaxResult;
16
+import com.sundot.airport.common.core.domain.entity.SysMenu;
17
+import com.sundot.airport.common.core.domain.entity.SysUser;
18
+import com.sundot.airport.common.core.domain.model.LoginBody;
19
+import com.sundot.airport.common.core.domain.model.LoginUser;
20
+import com.sundot.airport.common.core.text.Convert;
21
+import com.sundot.airport.common.utils.DateUtils;
22
+import com.sundot.airport.common.utils.SecurityUtils;
23
+import com.sundot.airport.common.utils.StringUtils;
24
+import com.sundot.airport.framework.web.service.SysLoginService;
25
+import com.sundot.airport.framework.web.service.SysPermissionService;
26
+import com.sundot.airport.framework.web.service.TokenService;
27
+import com.sundot.airport.system.service.ISysConfigService;
28
+import com.sundot.airport.system.service.ISysMenuService;
29
+
30
+/**
31
+ * 登录验证
32
+ *
33
+ * @author ruoyi
34
+ */
35
+@RestController
36
+public class SysLoginController {
37
+    @Autowired
38
+    private UserCache userCache;
39
+
40
+    @Autowired
41
+    private SysLoginService loginService;
42
+
43
+    @Autowired
44
+    private ISysMenuService menuService;
45
+
46
+    @Autowired
47
+    private SysPermissionService permissionService;
48
+
49
+    @Autowired
50
+    private TokenService tokenService;
51
+
52
+    @Autowired
53
+    private ISysConfigService configService;
54
+
55
+    /**
56
+     * 登录方法
57
+     *
58
+     * @param loginBody 登录信息
59
+     * @return 结果
60
+     */
61
+    @PostMapping("/login")
62
+    public AjaxResult login(@RequestBody LoginBody loginBody) {
63
+        AjaxResult ajax = AjaxResult.success();
64
+        // 生成令牌
65
+        String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(),
66
+                loginBody.getUuid());
67
+        ajax.put(Constants.TOKEN, token);
68
+        return ajax;
69
+    }
70
+
71
+    /**
72
+     * 获取用户信息
73
+     *
74
+     * @return 用户信息
75
+     */
76
+    @GetMapping("getInfo")
77
+    public AjaxResult getInfo() {
78
+        LoginUser loginUser = SecurityUtils.getLoginUser();
79
+        SysUser user = loginUser.getUser();
80
+        // 角色集合
81
+        Set<String> roles = permissionService.getRolePermission(user);
82
+        // 权限集合
83
+        Set<String> permissions = permissionService.getMenuPermission(user);
84
+        if (!loginUser.getPermissions().equals(permissions)) {
85
+            loginUser.setPermissions(permissions);
86
+            tokenService.refreshToken(loginUser);
87
+        }
88
+        UserInfo userInfo = userCache.getUserInfo(user.getUserId());
89
+        AjaxResult ajax = AjaxResult.success();
90
+        ajax.put("user", user);
91
+        ajax.put("roles", roles);
92
+        ajax.put("permissions", permissions);
93
+        ajax.put("isDefaultModifyPwd", initPasswordIsModify(user.getPwdUpdateDate()));
94
+        ajax.put("isPasswordExpired", passwordIsExpiration(user.getPwdUpdateDate()));
95
+        ajax.put("userInfo", userInfo);
96
+        return ajax;
97
+    }
98
+
99
+    /**
100
+     * 获取路由信息
101
+     *
102
+     * @return 路由信息
103
+     */
104
+    @GetMapping("getRouters")
105
+    public AjaxResult getRouters() {
106
+        Long userId = SecurityUtils.getUserId();
107
+        List<SysMenu> menus = menuService.selectMenuTreeByUserId(userId);
108
+        return AjaxResult.success(menuService.buildMenus(menus));
109
+    }
110
+
111
+    // 检查初始密码是否提醒修改
112
+    public boolean initPasswordIsModify(Date pwdUpdateDate) {
113
+        Integer initPasswordModify = Convert.toInt(configService.selectConfigByKey("sys.account.initPasswordModify"));
114
+        return initPasswordModify != null && initPasswordModify == 1 && pwdUpdateDate == null;
115
+    }
116
+
117
+    // 检查密码是否过期
118
+    public boolean passwordIsExpiration(Date pwdUpdateDate) {
119
+        Integer passwordValidateDays = Convert.toInt(configService.selectConfigByKey("sys.account.passwordValidateDays"));
120
+        if (passwordValidateDays != null && passwordValidateDays > 0) {
121
+            if (StringUtils.isNull(pwdUpdateDate)) {
122
+                // 如果从未修改过初始密码,直接提醒过期
123
+                return true;
124
+            }
125
+            Date nowDate = DateUtils.getNowDate();
126
+            return DateUtils.differentDaysByMillisecond(nowDate, pwdUpdateDate) > passwordValidateDays;
127
+        }
128
+        return false;
129
+    }
130
+}

+ 125 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/system/SysMenuController.java

@@ -0,0 +1,125 @@
1
+package com.sundot.airport.web.controller.system;
2
+
3
+import java.util.List;
4
+
5
+import org.springframework.beans.factory.annotation.Autowired;
6
+import org.springframework.security.access.prepost.PreAuthorize;
7
+import org.springframework.validation.annotation.Validated;
8
+import org.springframework.web.bind.annotation.DeleteMapping;
9
+import org.springframework.web.bind.annotation.GetMapping;
10
+import org.springframework.web.bind.annotation.PathVariable;
11
+import org.springframework.web.bind.annotation.PostMapping;
12
+import org.springframework.web.bind.annotation.PutMapping;
13
+import org.springframework.web.bind.annotation.RequestBody;
14
+import org.springframework.web.bind.annotation.RequestMapping;
15
+import org.springframework.web.bind.annotation.RestController;
16
+import com.sundot.airport.common.annotation.Log;
17
+import com.sundot.airport.common.constant.UserConstants;
18
+import com.sundot.airport.common.core.controller.BaseController;
19
+import com.sundot.airport.common.core.domain.AjaxResult;
20
+import com.sundot.airport.common.core.domain.entity.SysMenu;
21
+import com.sundot.airport.common.enums.BusinessType;
22
+import com.sundot.airport.common.utils.StringUtils;
23
+import com.sundot.airport.system.service.ISysMenuService;
24
+
25
+/**
26
+ * 菜单信息
27
+ *
28
+ * @author ruoyi
29
+ */
30
+@RestController
31
+@RequestMapping("/system/menu")
32
+public class SysMenuController extends BaseController {
33
+    @Autowired
34
+    private ISysMenuService menuService;
35
+
36
+    /**
37
+     * 获取菜单列表
38
+     */
39
+    @PreAuthorize("@ss.hasPermi('system:menu:list')")
40
+    @GetMapping("/list")
41
+    public AjaxResult list(SysMenu menu) {
42
+        List<SysMenu> menus = menuService.selectMenuList(menu, getUserId());
43
+        return success(menus);
44
+    }
45
+
46
+    /**
47
+     * 根据菜单编号获取详细信息
48
+     */
49
+    @PreAuthorize("@ss.hasPermi('system:menu:query')")
50
+    @GetMapping(value = "/{menuId}")
51
+    public AjaxResult getInfo(@PathVariable Long menuId) {
52
+        return success(menuService.selectMenuById(menuId));
53
+    }
54
+
55
+    /**
56
+     * 获取菜单下拉树列表
57
+     */
58
+    @GetMapping("/treeselect")
59
+    public AjaxResult treeselect(SysMenu menu) {
60
+        List<SysMenu> menus = menuService.selectMenuList(menu, getUserId());
61
+        return success(menuService.buildMenuTreeSelect(menus));
62
+    }
63
+
64
+    /**
65
+     * 加载对应角色菜单列表树
66
+     */
67
+    @GetMapping(value = "/roleMenuTreeselect/{roleId}")
68
+    public AjaxResult roleMenuTreeselect(@PathVariable("roleId") Long roleId) {
69
+        List<SysMenu> menus = menuService.selectMenuList(getUserId());
70
+        AjaxResult ajax = AjaxResult.success();
71
+        ajax.put("checkedKeys", menuService.selectMenuListByRoleId(roleId));
72
+        ajax.put("menus", menuService.buildMenuTreeSelect(menus));
73
+        return ajax;
74
+    }
75
+
76
+    /**
77
+     * 新增菜单
78
+     */
79
+    @PreAuthorize("@ss.hasPermi('system:menu:add')")
80
+    @Log(title = "菜单管理", businessType = BusinessType.INSERT)
81
+    @PostMapping
82
+    public AjaxResult add(@Validated @RequestBody SysMenu menu) {
83
+        if (!menuService.checkMenuNameUnique(menu)) {
84
+            return error("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
85
+        } else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) {
86
+            return error("新增菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头");
87
+        }
88
+        menu.setCreateBy(getUsername());
89
+        return toAjax(menuService.insertMenu(menu));
90
+    }
91
+
92
+    /**
93
+     * 修改菜单
94
+     */
95
+    @PreAuthorize("@ss.hasPermi('system:menu:edit')")
96
+    @Log(title = "菜单管理", businessType = BusinessType.UPDATE)
97
+    @PutMapping
98
+    public AjaxResult edit(@Validated @RequestBody SysMenu menu) {
99
+        if (!menuService.checkMenuNameUnique(menu)) {
100
+            return error("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
101
+        } else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) {
102
+            return error("修改菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头");
103
+        } else if (menu.getMenuId().equals(menu.getParentId())) {
104
+            return error("修改菜单'" + menu.getMenuName() + "'失败,上级菜单不能选择自己");
105
+        }
106
+        menu.setUpdateBy(getUsername());
107
+        return toAjax(menuService.updateMenu(menu));
108
+    }
109
+
110
+    /**
111
+     * 删除菜单
112
+     */
113
+    @PreAuthorize("@ss.hasPermi('system:menu:remove')")
114
+    @Log(title = "菜单管理", businessType = BusinessType.DELETE)
115
+    @DeleteMapping("/{menuId}")
116
+    public AjaxResult remove(@PathVariable("menuId") Long menuId) {
117
+        if (menuService.hasChildByMenuId(menuId)) {
118
+            return warn("存在子菜单,不允许删除");
119
+        }
120
+        if (menuService.checkMenuExistRole(menuId)) {
121
+            return warn("菜单已分配,不允许删除");
122
+        }
123
+        return toAjax(menuService.deleteMenuById(menuId));
124
+    }
125
+}

+ 88 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/system/SysNoticeController.java

@@ -0,0 +1,88 @@
1
+package com.sundot.airport.web.controller.system;
2
+
3
+import java.util.List;
4
+
5
+import org.springframework.beans.factory.annotation.Autowired;
6
+import org.springframework.security.access.prepost.PreAuthorize;
7
+import org.springframework.validation.annotation.Validated;
8
+import org.springframework.web.bind.annotation.DeleteMapping;
9
+import org.springframework.web.bind.annotation.GetMapping;
10
+import org.springframework.web.bind.annotation.PathVariable;
11
+import org.springframework.web.bind.annotation.PostMapping;
12
+import org.springframework.web.bind.annotation.PutMapping;
13
+import org.springframework.web.bind.annotation.RequestBody;
14
+import org.springframework.web.bind.annotation.RequestMapping;
15
+import org.springframework.web.bind.annotation.RestController;
16
+import com.sundot.airport.common.annotation.Log;
17
+import com.sundot.airport.common.core.controller.BaseController;
18
+import com.sundot.airport.common.core.domain.AjaxResult;
19
+import com.sundot.airport.common.core.page.TableDataInfo;
20
+import com.sundot.airport.common.enums.BusinessType;
21
+import com.sundot.airport.system.domain.SysNotice;
22
+import com.sundot.airport.system.service.ISysNoticeService;
23
+
24
+/**
25
+ * 公告 信息操作处理
26
+ *
27
+ * @author ruoyi
28
+ */
29
+@RestController
30
+@RequestMapping("/system/notice")
31
+public class SysNoticeController extends BaseController {
32
+    @Autowired
33
+    private ISysNoticeService noticeService;
34
+
35
+    /**
36
+     * 获取通知公告列表
37
+     */
38
+    @PreAuthorize("@ss.hasPermi('system:notice:list')")
39
+    @GetMapping("/list")
40
+    public TableDataInfo list(SysNotice notice) {
41
+        startPage();
42
+        List<SysNotice> list = noticeService.selectNoticeList(notice);
43
+        return getDataTable(list);
44
+    }
45
+
46
+    /**
47
+     * 根据通知公告编号获取详细信息
48
+     */
49
+    @PreAuthorize("@ss.hasPermi('system:notice:query')")
50
+    @GetMapping(value = "/{noticeId}")
51
+    public AjaxResult getInfo(@PathVariable Long noticeId) {
52
+        return success(noticeService.selectNoticeById(noticeId));
53
+    }
54
+
55
+    /**
56
+     * 新增通知公告
57
+     */
58
+    @PreAuthorize("@ss.hasPermi('system:notice:add')")
59
+    @Log(title = "通知公告", businessType = BusinessType.INSERT)
60
+    @PostMapping
61
+    public AjaxResult add(@Validated @RequestBody SysNotice notice) {
62
+        notice.setCreateByUserId(getUserId());
63
+        notice.setCreateBy(getLoginUser().getUser().getNickName());
64
+        return toAjax(noticeService.insertNotice(notice));
65
+    }
66
+
67
+    /**
68
+     * 修改通知公告
69
+     */
70
+    @PreAuthorize("@ss.hasPermi('system:notice:edit')")
71
+    @Log(title = "通知公告", businessType = BusinessType.UPDATE)
72
+    @PutMapping
73
+    public AjaxResult edit(@Validated @RequestBody SysNotice notice) {
74
+        notice.setUpdateByUserId(getUserId());
75
+        notice.setUpdateBy(getLoginUser().getUser().getNickName());
76
+        return toAjax(noticeService.updateNotice(notice));
77
+    }
78
+
79
+    /**
80
+     * 删除通知公告
81
+     */
82
+    @PreAuthorize("@ss.hasPermi('system:notice:remove')")
83
+    @Log(title = "通知公告", businessType = BusinessType.DELETE)
84
+    @DeleteMapping("/{noticeIds}")
85
+    public AjaxResult remove(@PathVariable Long[] noticeIds) {
86
+        return toAjax(noticeService.deleteNoticeByIds(noticeIds));
87
+    }
88
+}

+ 136 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/system/SysPostController.java

@@ -0,0 +1,136 @@
1
+package com.sundot.airport.web.controller.system;
2
+
3
+import java.util.List;
4
+import javax.servlet.http.HttpServletResponse;
5
+
6
+import org.springframework.beans.factory.annotation.Autowired;
7
+import org.springframework.security.access.prepost.PreAuthorize;
8
+import org.springframework.validation.annotation.Validated;
9
+import org.springframework.web.bind.annotation.DeleteMapping;
10
+import org.springframework.web.bind.annotation.GetMapping;
11
+import org.springframework.web.bind.annotation.PathVariable;
12
+import org.springframework.web.bind.annotation.PostMapping;
13
+import org.springframework.web.bind.annotation.PutMapping;
14
+import org.springframework.web.bind.annotation.RequestBody;
15
+import org.springframework.web.bind.annotation.RequestMapping;
16
+import org.springframework.web.bind.annotation.RestController;
17
+import com.sundot.airport.common.annotation.Log;
18
+import com.sundot.airport.common.core.controller.BaseController;
19
+import com.sundot.airport.common.core.domain.AjaxResult;
20
+import com.sundot.airport.common.core.page.TableDataInfo;
21
+import com.sundot.airport.common.enums.BusinessType;
22
+import com.sundot.airport.common.utils.poi.ExcelUtil;
23
+import com.sundot.airport.system.domain.SysPost;
24
+import com.sundot.airport.system.service.ISysPostService;
25
+
26
+/**
27
+ * 岗位信息操作处理
28
+ *
29
+ * @author ruoyi
30
+ */
31
+@RestController
32
+@RequestMapping("/system/post")
33
+public class SysPostController extends BaseController {
34
+    @Autowired
35
+    private ISysPostService postService;
36
+
37
+    /**
38
+     * 获取岗位列表
39
+     */
40
+    @PreAuthorize("@ss.hasPermi('system:post:list')")
41
+    @GetMapping("/list")
42
+    public TableDataInfo list(SysPost post) {
43
+        startPage();
44
+        List<SysPost> list = postService.selectPostList(post);
45
+        return getDataTable(list);
46
+    }
47
+
48
+    @Log(title = "岗位管理", businessType = BusinessType.EXPORT)
49
+    @PreAuthorize("@ss.hasPermi('system:post:export')")
50
+    @PostMapping("/export")
51
+    public void export(HttpServletResponse response, SysPost post) {
52
+        List<SysPost> list = postService.selectPostList(post);
53
+        ExcelUtil<SysPost> util = new ExcelUtil<SysPost>(SysPost.class);
54
+        util.exportExcel(response, list, "岗位数据");
55
+    }
56
+
57
+    /**
58
+     * 根据岗位编号获取详细信息
59
+     */
60
+    @PreAuthorize("@ss.hasPermi('system:post:query')")
61
+    @GetMapping(value = "/{postId}")
62
+    public AjaxResult getInfo(@PathVariable Long postId) {
63
+        return success(postService.selectPostById(postId));
64
+    }
65
+
66
+    /**
67
+     * 新增岗位
68
+     */
69
+    @PreAuthorize("@ss.hasPermi('system:post:add')")
70
+    @Log(title = "岗位管理", businessType = BusinessType.INSERT)
71
+    @PostMapping
72
+    public AjaxResult add(@Validated @RequestBody SysPost post) {
73
+        if (!postService.checkPostNameUnique(post)) {
74
+            return error("新增岗位'" + post.getPostName() + "'失败,岗位名称已存在");
75
+        } else if (!postService.checkPostCodeUnique(post)) {
76
+            return error("新增岗位'" + post.getPostName() + "'失败,岗位编码已存在");
77
+        }
78
+        post.setCreateBy(getUsername());
79
+        return toAjax(postService.insertPost(post));
80
+    }
81
+
82
+    /**
83
+     * 修改岗位
84
+     */
85
+    @PreAuthorize("@ss.hasPermi('system:post:edit')")
86
+    @Log(title = "岗位管理", businessType = BusinessType.UPDATE)
87
+    @PutMapping
88
+    public AjaxResult edit(@Validated @RequestBody SysPost post) {
89
+        if (!postService.checkPostNameUnique(post)) {
90
+            return error("修改岗位'" + post.getPostName() + "'失败,岗位名称已存在");
91
+        } else if (!postService.checkPostCodeUnique(post)) {
92
+            return error("修改岗位'" + post.getPostName() + "'失败,岗位编码已存在");
93
+        }
94
+        post.setUpdateBy(getUsername());
95
+        return toAjax(postService.updatePost(post));
96
+    }
97
+
98
+    /**
99
+     * 删除岗位
100
+     */
101
+    @PreAuthorize("@ss.hasPermi('system:post:remove')")
102
+    @Log(title = "岗位管理", businessType = BusinessType.DELETE)
103
+    @DeleteMapping("/{postIds}")
104
+    public AjaxResult remove(@PathVariable Long[] postIds) {
105
+        return toAjax(postService.deletePostByIds(postIds));
106
+    }
107
+
108
+    /**
109
+     * 获取岗位选择框列表
110
+     */
111
+    @GetMapping("/optionselect")
112
+    public AjaxResult optionselect() {
113
+        List<SysPost> posts = postService.selectPostAll();
114
+        return success(posts);
115
+    }
116
+
117
+    /**
118
+     * 获取所有岗位列表
119
+     */
120
+    @PreAuthorize("@ss.hasPermi('system:post:list')")
121
+    @GetMapping("/listAll")
122
+    public AjaxResult listAll(SysPost post) {
123
+        List<SysPost> list = postService.selectPostList(post);
124
+        return success(list);
125
+    }
126
+
127
+    /**
128
+     * 查询岗位信息集合树形结构
129
+     */
130
+    @PreAuthorize("@ss.hasPermi('system:post:list')")
131
+    @GetMapping("/listAllTree")
132
+    public AjaxResult listAllTree(SysPost post) {
133
+        List<SysPost> list = postService.selectPostListTree(post);
134
+        return success(list);
135
+    }
136
+}

+ 148 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/system/SysProfileController.java

@@ -0,0 +1,148 @@
1
+package com.sundot.airport.web.controller.system;
2
+
3
+import java.util.Map;
4
+import org.springframework.beans.factory.annotation.Autowired;
5
+import org.springframework.web.bind.annotation.GetMapping;
6
+import org.springframework.web.bind.annotation.PostMapping;
7
+import org.springframework.web.bind.annotation.PutMapping;
8
+import org.springframework.web.bind.annotation.RequestBody;
9
+import org.springframework.web.bind.annotation.RequestMapping;
10
+import org.springframework.web.bind.annotation.RequestParam;
11
+import org.springframework.web.bind.annotation.RestController;
12
+import org.springframework.web.multipart.MultipartFile;
13
+import com.sundot.airport.common.annotation.Log;
14
+import com.sundot.airport.common.config.RuoYiConfig;
15
+import com.sundot.airport.common.core.controller.BaseController;
16
+import com.sundot.airport.common.core.domain.AjaxResult;
17
+import com.sundot.airport.common.core.domain.entity.SysUser;
18
+import com.sundot.airport.common.core.domain.model.LoginUser;
19
+import com.sundot.airport.common.enums.BusinessType;
20
+import com.sundot.airport.common.utils.DateUtils;
21
+import com.sundot.airport.common.utils.SecurityUtils;
22
+import com.sundot.airport.common.utils.StringUtils;
23
+import com.sundot.airport.common.utils.file.FileUploadUtils;
24
+import com.sundot.airport.common.utils.file.FileUtils;
25
+import com.sundot.airport.common.utils.file.MimeTypeUtils;
26
+import com.sundot.airport.framework.web.service.TokenService;
27
+import com.sundot.airport.system.service.ISysUserService;
28
+
29
+/**
30
+ * 个人信息 业务处理
31
+ * 
32
+ * @author ruoyi
33
+ */
34
+@RestController
35
+@RequestMapping("/system/user/profile")
36
+public class SysProfileController extends BaseController
37
+{
38
+    @Autowired
39
+    private ISysUserService userService;
40
+
41
+    @Autowired
42
+    private TokenService tokenService;
43
+
44
+    /**
45
+     * 个人信息
46
+     */
47
+    @GetMapping
48
+    public AjaxResult profile()
49
+    {
50
+        LoginUser loginUser = getLoginUser();
51
+        SysUser user = loginUser.getUser();
52
+        AjaxResult ajax = AjaxResult.success(user);
53
+        ajax.put("roleGroup", userService.selectUserRoleGroup(loginUser.getUsername()));
54
+        ajax.put("postGroup", userService.selectUserPostGroup(loginUser.getUsername()));
55
+        return ajax;
56
+    }
57
+
58
+    /**
59
+     * 修改用户
60
+     */
61
+    @Log(title = "个人信息", businessType = BusinessType.UPDATE)
62
+    @PutMapping
63
+    public AjaxResult updateProfile(@RequestBody SysUser user)
64
+    {
65
+        LoginUser loginUser = getLoginUser();
66
+        SysUser currentUser = loginUser.getUser();
67
+        currentUser.setNickName(user.getNickName());
68
+        currentUser.setEmail(user.getEmail());
69
+        currentUser.setPhonenumber(user.getPhonenumber());
70
+        currentUser.setSex(user.getSex());
71
+        if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(currentUser))
72
+        {
73
+            return error("修改用户'" + loginUser.getUsername() + "'失败,手机号码已存在");
74
+        }
75
+        if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(currentUser))
76
+        {
77
+            return error("修改用户'" + loginUser.getUsername() + "'失败,邮箱账号已存在");
78
+        }
79
+        if (userService.updateUserProfile(currentUser) > 0)
80
+        {
81
+            // 更新缓存用户信息
82
+            tokenService.setLoginUser(loginUser);
83
+            return success();
84
+        }
85
+        return error("修改个人信息异常,请联系管理员");
86
+    }
87
+
88
+    /**
89
+     * 重置密码
90
+     */
91
+    @Log(title = "个人信息", businessType = BusinessType.UPDATE)
92
+    @PutMapping("/updatePwd")
93
+    public AjaxResult updatePwd(@RequestBody Map<String, String> params)
94
+    {
95
+        String oldPassword = params.get("oldPassword");
96
+        String newPassword = params.get("newPassword");
97
+        LoginUser loginUser = getLoginUser();
98
+        Long userId = loginUser.getUserId();
99
+        String password = loginUser.getPassword();
100
+        if (!SecurityUtils.matchesPassword(oldPassword, password))
101
+        {
102
+            return error("修改密码失败,旧密码错误");
103
+        }
104
+        if (SecurityUtils.matchesPassword(newPassword, password))
105
+        {
106
+            return error("新密码不能与旧密码相同");
107
+        }
108
+        newPassword = SecurityUtils.encryptPassword(newPassword);
109
+        if (userService.resetUserPwd(userId, newPassword) > 0)
110
+        {
111
+            // 更新缓存用户密码&密码最后更新时间
112
+            loginUser.getUser().setPwdUpdateDate(DateUtils.getNowDate());
113
+            loginUser.getUser().setPassword(newPassword);
114
+            tokenService.setLoginUser(loginUser);
115
+            return success();
116
+        }
117
+        return error("修改密码异常,请联系管理员");
118
+    }
119
+
120
+    /**
121
+     * 头像上传
122
+     */
123
+    @Log(title = "用户头像", businessType = BusinessType.UPDATE)
124
+    @PostMapping("/avatar")
125
+    public AjaxResult avatar(@RequestParam("avatarfile") MultipartFile file) throws Exception
126
+    {
127
+        if (!file.isEmpty())
128
+        {
129
+            LoginUser loginUser = getLoginUser();
130
+            String avatar = FileUploadUtils.upload(RuoYiConfig.getAvatarPath(), file, MimeTypeUtils.IMAGE_EXTENSION, true);
131
+            if (userService.updateUserAvatar(loginUser.getUserId(), avatar))
132
+            {
133
+                String oldAvatar = loginUser.getUser().getAvatar();
134
+                if (StringUtils.isNotEmpty(oldAvatar))
135
+                {
136
+                    FileUtils.deleteFile(RuoYiConfig.getProfile() + FileUtils.stripPrefix(oldAvatar));
137
+                }
138
+                AjaxResult ajax = AjaxResult.success();
139
+                ajax.put("imgUrl", avatar);
140
+                // 更新缓存用户头像
141
+                loginUser.getUser().setAvatar(avatar);
142
+                tokenService.setLoginUser(loginUser);
143
+                return ajax;
144
+            }
145
+        }
146
+        return error("上传图片异常,请联系管理员");
147
+    }
148
+}

+ 38 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/system/SysRegisterController.java

@@ -0,0 +1,38 @@
1
+package com.sundot.airport.web.controller.system;
2
+
3
+import org.springframework.beans.factory.annotation.Autowired;
4
+import org.springframework.web.bind.annotation.PostMapping;
5
+import org.springframework.web.bind.annotation.RequestBody;
6
+import org.springframework.web.bind.annotation.RestController;
7
+import com.sundot.airport.common.core.controller.BaseController;
8
+import com.sundot.airport.common.core.domain.AjaxResult;
9
+import com.sundot.airport.common.core.domain.model.RegisterBody;
10
+import com.sundot.airport.common.utils.StringUtils;
11
+import com.sundot.airport.framework.web.service.SysRegisterService;
12
+import com.sundot.airport.system.service.ISysConfigService;
13
+
14
+/**
15
+ * 注册验证
16
+ * 
17
+ * @author ruoyi
18
+ */
19
+@RestController
20
+public class SysRegisterController extends BaseController
21
+{
22
+    @Autowired
23
+    private SysRegisterService registerService;
24
+
25
+    @Autowired
26
+    private ISysConfigService configService;
27
+
28
+    @PostMapping("/register")
29
+    public AjaxResult register(@RequestBody RegisterBody user)
30
+    {
31
+        if (!("true".equals(configService.selectConfigByKey("sys.account.registerUser"))))
32
+        {
33
+            return error("当前系统没有开启注册功能!");
34
+        }
35
+        String msg = registerService.register(user);
36
+        return StringUtils.isEmpty(msg) ? success() : error(msg);
37
+    }
38
+}

+ 239 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/system/SysRoleController.java

@@ -0,0 +1,239 @@
1
+package com.sundot.airport.web.controller.system;
2
+
3
+import java.util.List;
4
+import javax.servlet.http.HttpServletResponse;
5
+
6
+import org.springframework.beans.factory.annotation.Autowired;
7
+import org.springframework.security.access.prepost.PreAuthorize;
8
+import org.springframework.validation.annotation.Validated;
9
+import org.springframework.web.bind.annotation.DeleteMapping;
10
+import org.springframework.web.bind.annotation.GetMapping;
11
+import org.springframework.web.bind.annotation.PathVariable;
12
+import org.springframework.web.bind.annotation.PostMapping;
13
+import org.springframework.web.bind.annotation.PutMapping;
14
+import org.springframework.web.bind.annotation.RequestBody;
15
+import org.springframework.web.bind.annotation.RequestMapping;
16
+import org.springframework.web.bind.annotation.RestController;
17
+import com.sundot.airport.common.annotation.Log;
18
+import com.sundot.airport.common.core.controller.BaseController;
19
+import com.sundot.airport.common.core.domain.AjaxResult;
20
+import com.sundot.airport.common.core.domain.entity.SysDept;
21
+import com.sundot.airport.common.core.domain.entity.SysRole;
22
+import com.sundot.airport.common.core.domain.entity.SysUser;
23
+import com.sundot.airport.common.core.domain.model.LoginUser;
24
+import com.sundot.airport.common.core.page.TableDataInfo;
25
+import com.sundot.airport.common.enums.BusinessType;
26
+import com.sundot.airport.common.utils.StringUtils;
27
+import com.sundot.airport.common.utils.poi.ExcelUtil;
28
+import com.sundot.airport.framework.web.service.SysPermissionService;
29
+import com.sundot.airport.framework.web.service.TokenService;
30
+import com.sundot.airport.system.domain.SysUserRole;
31
+import com.sundot.airport.system.service.ISysDeptService;
32
+import com.sundot.airport.system.service.ISysRoleService;
33
+import com.sundot.airport.system.service.ISysUserService;
34
+
35
+/**
36
+ * 角色信息
37
+ *
38
+ * @author ruoyi
39
+ */
40
+@RestController
41
+@RequestMapping("/system/role")
42
+public class SysRoleController extends BaseController {
43
+    @Autowired
44
+    private ISysRoleService roleService;
45
+
46
+    @Autowired
47
+    private TokenService tokenService;
48
+
49
+    @Autowired
50
+    private SysPermissionService permissionService;
51
+
52
+    @Autowired
53
+    private ISysUserService userService;
54
+
55
+    @Autowired
56
+    private ISysDeptService deptService;
57
+
58
+    @PreAuthorize("@ss.hasPermi('system:role:list')")
59
+    @GetMapping("/list")
60
+    public TableDataInfo list(SysRole role) {
61
+        startPage();
62
+        List<SysRole> list = roleService.selectRoleList(role);
63
+        return getDataTable(list);
64
+    }
65
+
66
+    @Log(title = "角色管理", businessType = BusinessType.EXPORT)
67
+    @PreAuthorize("@ss.hasPermi('system:role:export')")
68
+    @PostMapping("/export")
69
+    public void export(HttpServletResponse response, SysRole role) {
70
+        List<SysRole> list = roleService.selectRoleList(role);
71
+        ExcelUtil<SysRole> util = new ExcelUtil<SysRole>(SysRole.class);
72
+        util.exportExcel(response, list, "角色数据");
73
+    }
74
+
75
+    /**
76
+     * 根据角色编号获取详细信息
77
+     */
78
+    @PreAuthorize("@ss.hasPermi('system:role:query')")
79
+    @GetMapping(value = "/{roleId}")
80
+    public AjaxResult getInfo(@PathVariable Long roleId) {
81
+        roleService.checkRoleDataScope(roleId);
82
+        return success(roleService.selectRoleById(roleId));
83
+    }
84
+
85
+    /**
86
+     * 新增角色
87
+     */
88
+    @PreAuthorize("@ss.hasPermi('system:role:add')")
89
+    @Log(title = "角色管理", businessType = BusinessType.INSERT)
90
+    @PostMapping
91
+    public AjaxResult add(@Validated @RequestBody SysRole role) {
92
+        if (!roleService.checkRoleNameUnique(role)) {
93
+            return error("新增角色'" + role.getRoleName() + "'失败,角色名称已存在");
94
+        } else if (!roleService.checkRoleKeyUnique(role)) {
95
+            return error("新增角色'" + role.getRoleName() + "'失败,角色权限已存在");
96
+        }
97
+        role.setCreateBy(getUsername());
98
+        return toAjax(roleService.insertRole(role));
99
+
100
+    }
101
+
102
+    /**
103
+     * 修改保存角色
104
+     */
105
+    @PreAuthorize("@ss.hasPermi('system:role:edit')")
106
+    @Log(title = "角色管理", businessType = BusinessType.UPDATE)
107
+    @PutMapping
108
+    public AjaxResult edit(@Validated @RequestBody SysRole role) {
109
+        roleService.checkRoleAllowed(role);
110
+        roleService.checkRoleDataScope(role.getRoleId());
111
+        if (!roleService.checkRoleNameUnique(role)) {
112
+            return error("修改角色'" + role.getRoleName() + "'失败,角色名称已存在");
113
+        } else if (!roleService.checkRoleKeyUnique(role)) {
114
+            return error("修改角色'" + role.getRoleName() + "'失败,角色权限已存在");
115
+        }
116
+        role.setUpdateBy(getUsername());
117
+
118
+        if (roleService.updateRole(role) > 0) {
119
+            // 更新缓存用户权限
120
+            LoginUser loginUser = getLoginUser();
121
+            if (StringUtils.isNotNull(loginUser.getUser()) && !loginUser.getUser().isAdmin()) {
122
+                loginUser.setUser(userService.selectUserByUserName(loginUser.getUser().getUserName()));
123
+                loginUser.setPermissions(permissionService.getMenuPermission(loginUser.getUser()));
124
+                tokenService.setLoginUser(loginUser);
125
+            }
126
+            return success();
127
+        }
128
+        return error("修改角色'" + role.getRoleName() + "'失败,请联系管理员");
129
+    }
130
+
131
+    /**
132
+     * 修改保存数据权限
133
+     */
134
+    @PreAuthorize("@ss.hasPermi('system:role:edit')")
135
+    @Log(title = "角色管理", businessType = BusinessType.UPDATE)
136
+    @PutMapping("/dataScope")
137
+    public AjaxResult dataScope(@RequestBody SysRole role) {
138
+        roleService.checkRoleAllowed(role);
139
+        roleService.checkRoleDataScope(role.getRoleId());
140
+        return toAjax(roleService.authDataScope(role));
141
+    }
142
+
143
+    /**
144
+     * 状态修改
145
+     */
146
+    @PreAuthorize("@ss.hasPermi('system:role:edit')")
147
+    @Log(title = "角色管理", businessType = BusinessType.UPDATE)
148
+    @PutMapping("/changeStatus")
149
+    public AjaxResult changeStatus(@RequestBody SysRole role) {
150
+        roleService.checkRoleAllowed(role);
151
+        roleService.checkRoleDataScope(role.getRoleId());
152
+        role.setUpdateBy(getUsername());
153
+        return toAjax(roleService.updateRoleStatus(role));
154
+    }
155
+
156
+    /**
157
+     * 删除角色
158
+     */
159
+    @PreAuthorize("@ss.hasPermi('system:role:remove')")
160
+    @Log(title = "角色管理", businessType = BusinessType.DELETE)
161
+    @DeleteMapping("/{roleIds}")
162
+    public AjaxResult remove(@PathVariable Long[] roleIds) {
163
+        return toAjax(roleService.deleteRoleByIds(roleIds));
164
+    }
165
+
166
+    /**
167
+     * 获取角色选择框列表
168
+     */
169
+    @PreAuthorize("@ss.hasPermi('system:role:query')")
170
+    @GetMapping("/optionselect")
171
+    public AjaxResult optionselect() {
172
+        return success(roleService.selectRoleAll());
173
+    }
174
+
175
+    /**
176
+     * 查询已分配用户角色列表
177
+     */
178
+    @PreAuthorize("@ss.hasPermi('system:role:list')")
179
+    @GetMapping("/authUser/allocatedList")
180
+    public TableDataInfo allocatedList(SysUser user) {
181
+        startPage();
182
+        List<SysUser> list = userService.selectAllocatedList(user);
183
+        return getDataTable(list);
184
+    }
185
+
186
+    /**
187
+     * 查询未分配用户角色列表
188
+     */
189
+    @PreAuthorize("@ss.hasPermi('system:role:list')")
190
+    @GetMapping("/authUser/unallocatedList")
191
+    public TableDataInfo unallocatedList(SysUser user) {
192
+        startPage();
193
+        List<SysUser> list = userService.selectUnallocatedList(user);
194
+        return getDataTable(list);
195
+    }
196
+
197
+    /**
198
+     * 取消授权用户
199
+     */
200
+    @PreAuthorize("@ss.hasPermi('system:role:edit')")
201
+    @Log(title = "角色管理", businessType = BusinessType.GRANT)
202
+    @PutMapping("/authUser/cancel")
203
+    public AjaxResult cancelAuthUser(@RequestBody SysUserRole userRole) {
204
+        return toAjax(roleService.deleteAuthUser(userRole));
205
+    }
206
+
207
+    /**
208
+     * 批量取消授权用户
209
+     */
210
+    @PreAuthorize("@ss.hasPermi('system:role:edit')")
211
+    @Log(title = "角色管理", businessType = BusinessType.GRANT)
212
+    @PutMapping("/authUser/cancelAll")
213
+    public AjaxResult cancelAuthUserAll(Long roleId, Long[] userIds) {
214
+        return toAjax(roleService.deleteAuthUsers(roleId, userIds));
215
+    }
216
+
217
+    /**
218
+     * 批量选择用户授权
219
+     */
220
+    @PreAuthorize("@ss.hasPermi('system:role:edit')")
221
+    @Log(title = "角色管理", businessType = BusinessType.GRANT)
222
+    @PutMapping("/authUser/selectAll")
223
+    public AjaxResult selectAuthUserAll(Long roleId, Long[] userIds) {
224
+        roleService.checkRoleDataScope(roleId);
225
+        return toAjax(roleService.insertAuthUsers(roleId, userIds));
226
+    }
227
+
228
+    /**
229
+     * 获取对应角色部门树列表
230
+     */
231
+    @PreAuthorize("@ss.hasPermi('system:role:query')")
232
+    @GetMapping(value = "/deptTree/{roleId}")
233
+    public AjaxResult deptTree(@PathVariable("roleId") Long roleId) {
234
+        AjaxResult ajax = AjaxResult.success();
235
+        ajax.put("checkedKeys", deptService.selectDeptListByRoleId(roleId));
236
+        ajax.put("depts", deptService.selectDeptTreeList(new SysDept()));
237
+        return ajax;
238
+    }
239
+}

+ 895 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/system/SysUserController.java

@@ -0,0 +1,895 @@
1
+package com.sundot.airport.web.controller.system;
2
+
3
+import java.math.BigDecimal;
4
+import java.math.RoundingMode;
5
+import java.sql.Date;
6
+import java.util.*;
7
+import java.util.stream.Collectors;
8
+import javax.servlet.http.HttpServletResponse;
9
+
10
+import cn.hutool.core.collection.CollUtil;
11
+import cn.hutool.core.collection.CollectionUtil;
12
+import cn.hutool.core.util.ObjectUtil;
13
+import cn.hutool.core.util.StrUtil;
14
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
15
+import com.sundot.airport.attendance.domain.portrait.WorkDateStats;
16
+import com.sundot.airport.attendance.domain.portrait.WorkHoursStats;
17
+import com.sundot.airport.check.domain.CheckLargeScreenCommonDto;
18
+import com.sundot.airport.check.domain.CheckLargeScreenCorrectionDto;
19
+import com.sundot.airport.check.domain.CheckLargeScreenCorrectionQueryParamDto;
20
+import com.sundot.airport.common.core.domain.SysLargeScreenWorkingPortraitDto;
21
+import com.sundot.airport.common.enums.RoleTypeEnum;
22
+import com.sundot.airport.exam.domain.DailyTask;
23
+import com.sundot.airport.attendance.portrait.AttendanceModuleIndicatorResult;
24
+import com.sundot.airport.check.service.ICheckLargeScreenService;
25
+import com.sundot.airport.common.constant.CacheConstants;
26
+import com.sundot.airport.common.core.domain.BaseLargeScreenQueryParamDto;
27
+import com.sundot.airport.common.core.domain.SysLargeScreenDetailDto;
28
+import com.sundot.airport.common.core.redis.RedisCache;
29
+import com.sundot.airport.common.domain.portrait.IndicatorCalculateParams;
30
+import com.sundot.airport.common.domain.portrait.ModularIndicatorResult;
31
+import com.sundot.airport.common.enums.DeptTypeEnum;
32
+import com.sundot.airport.common.enums.portrait.UserType;
33
+import com.sundot.airport.item.domain.portrait.ItemSeizureModuleIndicatorResult;
34
+import com.sundot.airport.item.domain.portrait.SeizureStats;
35
+import com.sundot.airport.system.domain.SysLargeScreenCooperationDto;
36
+import com.sundot.airport.system.domain.SysLargeScreenCooperationQueryParamDto;
37
+import com.sundot.airport.system.domain.portrait.IndicatorResult;
38
+import com.sundot.airport.system.domain.portrait.QualificationStats;
39
+import com.sundot.airport.system.domain.portrait.WorkExperienceStats;
40
+import com.sundot.airport.system.service.ISysLearningGrowthService;
41
+import com.sundot.airport.system.service.portrait.UserPortraitService;
42
+import org.apache.commons.lang3.ArrayUtils;
43
+import org.springframework.beans.factory.annotation.Autowired;
44
+import org.springframework.cache.annotation.Cacheable;
45
+import org.springframework.security.access.prepost.PreAuthorize;
46
+import org.springframework.validation.annotation.Validated;
47
+import org.springframework.web.bind.annotation.DeleteMapping;
48
+import org.springframework.web.bind.annotation.GetMapping;
49
+import org.springframework.web.bind.annotation.PathVariable;
50
+import org.springframework.web.bind.annotation.PostMapping;
51
+import org.springframework.web.bind.annotation.PutMapping;
52
+import org.springframework.web.bind.annotation.RequestBody;
53
+import org.springframework.web.bind.annotation.RequestMapping;
54
+import org.springframework.web.bind.annotation.RequestParam;
55
+import org.springframework.web.bind.annotation.RestController;
56
+import org.springframework.web.multipart.MultipartFile;
57
+import com.sundot.airport.common.annotation.Log;
58
+import com.sundot.airport.common.constant.UserConstants;
59
+import com.sundot.airport.common.core.controller.BaseController;
60
+import com.sundot.airport.common.core.domain.AjaxResult;
61
+import com.sundot.airport.common.core.domain.entity.SysDept;
62
+import com.sundot.airport.common.core.domain.entity.SysRole;
63
+import com.sundot.airport.common.core.domain.entity.SysUser;
64
+import com.sundot.airport.common.core.page.TableDataInfo;
65
+import com.sundot.airport.common.enums.BusinessType;
66
+import com.sundot.airport.common.utils.SecurityUtils;
67
+import com.sundot.airport.common.utils.StringUtils;
68
+import com.sundot.airport.common.utils.poi.ExcelUtil;
69
+import com.sundot.airport.system.service.ISysDeptService;
70
+import com.sundot.airport.system.service.ISysPostService;
71
+import com.sundot.airport.system.service.ISysRoleService;
72
+import com.sundot.airport.system.service.ISysUserService;
73
+
74
+/**
75
+ * 用户信息
76
+ *
77
+ * @author ruoyi
78
+ */
79
+@RestController
80
+@RequestMapping("/system/user")
81
+public class SysUserController extends BaseController {
82
+    @Autowired
83
+    private ISysUserService userService;
84
+
85
+    @Autowired
86
+    private ISysRoleService roleService;
87
+
88
+    @Autowired
89
+    private ISysDeptService deptService;
90
+
91
+    @Autowired
92
+    private ISysPostService postService;
93
+
94
+    @Autowired
95
+    private RedisCache redisCache;
96
+
97
+    @Autowired
98
+    private ICheckLargeScreenService checkLargeScreenService;
99
+
100
+    @Autowired
101
+    private ISysLearningGrowthService sysLearningGrowthService;
102
+
103
+    @Autowired
104
+    private ISysUserService iSysUserService;
105
+
106
+    @Autowired
107
+    private UserPortraitService userPortraitService;
108
+
109
+    @Autowired
110
+    private com.sundot.airport.exam.mapper.DailyTaskMapper dailyTaskMapper;
111
+
112
+    /**
113
+     * 获取用户列表
114
+     */
115
+    @PreAuthorize("@ss.hasPermi('system:user:list')")
116
+    @GetMapping("/list")
117
+    public TableDataInfo list(SysUser user) {
118
+        startPage();
119
+        List<SysUser> list = userService.selectUserList(user);
120
+        return getDataTable(list);
121
+    }
122
+
123
+    @Log(title = "用户管理", businessType = BusinessType.EXPORT)
124
+    @PreAuthorize("@ss.hasPermi('system:user:export')")
125
+    @PostMapping("/export")
126
+    public void export(HttpServletResponse response, SysUser user) {
127
+        List<SysUser> list = userService.selectUserList(user);
128
+        ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
129
+        util.exportExcel(response, list, "用户数据");
130
+    }
131
+
132
+    @Log(title = "用户管理", businessType = BusinessType.IMPORT)
133
+    @PreAuthorize("@ss.hasPermi('system:user:import')")
134
+    @PostMapping("/importData")
135
+    public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception {
136
+        ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class);
137
+        List<SysUser> userList = util.importExcel(file.getInputStream(), 1);
138
+        String operName = getUsername();
139
+        String message = userService.importUser(userList, updateSupport, operName);
140
+        return success(message);
141
+    }
142
+
143
+    @PostMapping("/importTemplate")
144
+    public void importTemplate(HttpServletResponse response) {
145
+        List<SysDept> deptList = deptService.selectDeptList(new SysDept());
146
+        ExcelUtil<SysUser> util = new ExcelUtil<>(SysUser.class);
147
+        util.exportTemplateWithDeptRef(response, deptList, "用户数据导入模板.xlsx");
148
+    }
149
+
150
+    /**
151
+     * 能力画像-协同配合
152
+     */
153
+    @Cacheable(
154
+            value = "statistics_data",
155
+            keyGenerator = "statisticsKeyGenerator",
156
+            unless = "!T(com.sundot.airport.common.cache.StatisticsCacheConditionUtil).isSuccess(#result) ||T(com.sundot.airport.common.cache.StatisticsCacheConditionUtil).isEmptyData(#result)"
157
+    )
158
+    @GetMapping("/cooperation")
159
+    public AjaxResult cooperation(SysLargeScreenCooperationQueryParamDto dto) {
160
+        SysLargeScreenCooperationDto sysLargeScreenCooperationDto = userService.cooperation(dto);
161
+        return success(sysLargeScreenCooperationDto);
162
+    }
163
+
164
+    /**
165
+     * 能力画像-总体概览
166
+     */
167
+    @Cacheable(
168
+            value = "statistics_data",
169
+            keyGenerator = "statisticsKeyGenerator",
170
+            unless = "!T(com.sundot.airport.common.cache.StatisticsCacheConditionUtil).isSuccess(#result) ||T(com.sundot.airport.common.cache.StatisticsCacheConditionUtil).isEmptyData(#result)"
171
+    )
172
+    @GetMapping("/population")
173
+    public AjaxResult population(BaseLargeScreenQueryParamDto dto) {
174
+        // 默认查询当前登录用户的数据
175
+        if (ObjectUtil.isNull(dto.getUserId()) && ObjectUtil.isNull(dto.getDeptId())) {
176
+            dto.setUserId(getUserId());
177
+        }
178
+        // 传入的用户角色为部门管理员时,则查询部门数据。其中班组长不做处理,由前端处理
179
+        if (Objects.nonNull(dto.getUserId())) {
180
+            SysUser sysUser = userService.selectUserById(dto.getUserId());
181
+            List<SysRole> roles = sysUser.getRoles();
182
+            if (CollUtil.isNotEmpty(roles)) {
183
+                List<String> roleKeyList = roles.stream().map(SysRole::getRoleKey).collect(Collectors.toList());
184
+                if (roleKeyList.contains(RoleTypeEnum.admin.getCode()) || roleKeyList.contains(RoleTypeEnum.test.getCode()) || roleKeyList.contains(RoleTypeEnum.zhijianke.getCode()) || roleKeyList.contains(RoleTypeEnum.kezhang.getCode())) {
185
+                    dto.setUserId(null);
186
+                    dto.setDeptId(sysUser.getDeptId());
187
+                }
188
+            }
189
+        }
190
+
191
+        // 返回结果
192
+        SysLargeScreenDetailDto result = new SysLargeScreenDetailDto();
193
+        IndicatorCalculateParams indicatorCalculateParams = this.appendParams(dto);
194
+        if (Objects.nonNull(dto.getDeptId())) {
195
+            SysDept sysDept = deptService.selectDeptById(dto.getDeptId());
196
+            if (StrUtil.equals(DeptTypeEnum.STATION.getCode(), sysDept.getDeptType()) || StrUtil.equals(DeptTypeEnum.DEPARTMENT.getCode(), sysDept.getDeptType()) || StrUtil.equals(DeptTypeEnum.TEAMS.getCode(), sysDept.getDeptType())) {
197
+                indicatorCalculateParams.setUserType(StrUtil.equals(DeptTypeEnum.STATION.getCode(), sysDept.getDeptType()) ? UserType.STATION : StrUtil.equals(DeptTypeEnum.DEPARTMENT.getCode(), sysDept.getDeptType()) ? UserType.DEPARTMENT : UserType.TEAM);
198
+                List<SysUser> sysUsers = iSysUserService.selectUserListByRoleKeyAndDeptId(Arrays.asList(RoleTypeEnum.banzuzhang.getCode(), RoleTypeEnum.SecurityCheck.getCode()), dto.getDeptId());
199
+                indicatorCalculateParams.setUserIds(sysUsers.stream().map(SysUser::getUserId).collect(Collectors.toList()));
200
+                indicatorCalculateParams.setDeptId(dto.getDeptId());
201
+            }
202
+        } else {
203
+            indicatorCalculateParams.setUserType(UserType.PERSONAL);
204
+            indicatorCalculateParams.setUserId(dto.getUserId());
205
+        }
206
+
207
+        // 巡检问题总次数
208
+        result.setCheckCount(checkLargeScreenService.population(dto));
209
+        // 学习成长平均分
210
+        result.setLearningGrowthScore(sysLearningGrowthService.population(dto));
211
+        // 工作风格
212
+        result.setWorkingStyle(userService.getWorkingStyle(dto));
213
+        // 主观印象
214
+        result.setSubjectiveImpression(userService.getSubjectiveImpression(dto));
215
+        // 测试平均分(抽问抽答)
216
+        result.setTestScore(calculatePopulationTestScore(dto));
217
+
218
+        //工作产出(有效查获数量)
219
+        this.avgSeizureCount(indicatorCalculateParams, result);
220
+        // 资质等级
221
+        this.qualificationLevel(indicatorCalculateParams, result);
222
+        // 工作履历(安检工作年限)
223
+        this.workingYears(indicatorCalculateParams, result);
224
+        // 出勤投入(上岗时长)
225
+        this.avgWorkingHours(indicatorCalculateParams, result);
226
+
227
+        return success(result);
228
+    }
229
+
230
+    /**
231
+     * 能力画像-明细
232
+     */
233
+    @Cacheable(
234
+            value = "statistics_list",
235
+            keyGenerator = "statisticsKeyGenerator",
236
+            unless = "!T(com.sundot.airport.common.cache.StatisticsCacheConditionUtil).isSuccess(#result) ||T(com.sundot.airport.common.cache.StatisticsCacheConditionUtil).isEmptyData(#result)"
237
+    )
238
+    @GetMapping("/detail")
239
+    public AjaxResult detail(BaseLargeScreenQueryParamDto dto) {
240
+        // 判空处理
241
+        if (ObjectUtil.isNull(dto.getDeptId())) {
242
+            dto.setDeptId(getDeptId());
243
+        }
244
+        if (ObjectUtil.isNull(dto.getDeptId())) {
245
+            return error("部门ID不能为空");
246
+        }
247
+
248
+        IndicatorCalculateParams indicatorCalculateParams = this.appendParams(dto);
249
+
250
+        // 返回结果
251
+        List<SysLargeScreenDetailDto> result = new ArrayList<>();
252
+
253
+        // 人均巡检问题数
254
+        List<SysLargeScreenDetailDto> checkList = checkLargeScreenService.detail(dto);
255
+        Map<Long, BigDecimal> checkCountMap = checkList.stream().collect(Collectors.toMap(SysLargeScreenDetailDto::getId, SysLargeScreenDetailDto::getCheckCount, (oldValue, newValue) -> newValue));
256
+
257
+        // 返回结果处理
258
+        SysDept sysDept = deptService.selectDeptById(dto.getDeptId());
259
+        if (StrUtil.equals(DeptTypeEnum.STATION.getCode(), sysDept.getDeptType()) || StrUtil.equals(DeptTypeEnum.DEPARTMENT.getCode(), sysDept.getDeptType())) {
260
+            indicatorCalculateParams.setUserType(StrUtil.equals(DeptTypeEnum.STATION.getCode(), sysDept.getDeptType()) ? UserType.DEPARTMENT : UserType.TEAM);
261
+            SysDept temp = new SysDept();
262
+            temp.setParentId(dto.getDeptId());
263
+            List<SysDept> deptList = deptService.selectDeptInfoAll(temp);
264
+            deptList.forEach(item -> {
265
+                SysLargeScreenDetailDto resultItem = new SysLargeScreenDetailDto();
266
+                resultItem.setId(item.getDeptId());
267
+                resultItem.setName(item.getDeptName());
268
+                resultItem.setCheckCount(checkCountMap.getOrDefault(item.getDeptId(), BigDecimal.ZERO));
269
+                //获取指标
270
+                indicatorCalculateParams.setDeptId(item.getDeptId());
271
+                List<SysUser> sysUsers = iSysUserService.selectUserListByRoleKeyAndDeptId(Arrays.asList(RoleTypeEnum.banzuzhang.getCode(), RoleTypeEnum.SecurityCheck.getCode()), item.getDeptId());
272
+                indicatorCalculateParams.setUserIds(sysUsers.stream().map(SysUser::getUserId).collect(Collectors.toList()));
273
+                this.qualificationLevel(indicatorCalculateParams, resultItem);
274
+                this.avgSeizureCount(indicatorCalculateParams, resultItem);
275
+                this.avgWorkingDays(indicatorCalculateParams, resultItem);
276
+                this.avgWorkingHours(indicatorCalculateParams, resultItem);
277
+                this.avgTestScore(indicatorCalculateParams, resultItem);
278
+                result.add(resultItem);
279
+            });
280
+        } else if (StrUtil.equals(DeptTypeEnum.TEAMS.getCode(), sysDept.getDeptType())) {
281
+            indicatorCalculateParams.setUserType(UserType.PERSONAL);
282
+            List<SysUser> sysUsers = iSysUserService.selectUserListByRoleKeyAndDeptId(Arrays.asList(RoleTypeEnum.banzuzhang.getCode(), RoleTypeEnum.SecurityCheck.getCode()), dto.getDeptId());
283
+            sysUsers.forEach(item -> {
284
+                SysLargeScreenDetailDto resultItem = new SysLargeScreenDetailDto();
285
+                resultItem.setId(item.getUserId());
286
+                resultItem.setName(item.getNickName());
287
+                resultItem.setCheckCount(checkCountMap.getOrDefault(item.getUserId(), BigDecimal.ZERO));
288
+                //获取指标
289
+                indicatorCalculateParams.setUserId(item.getUserId());
290
+                this.qualificationLevel(indicatorCalculateParams, resultItem);
291
+                this.avgSeizureCount(indicatorCalculateParams, resultItem);
292
+                this.avgWorkingDays(indicatorCalculateParams, resultItem);
293
+                this.avgWorkingHours(indicatorCalculateParams, resultItem);
294
+                this.avgTestScore(indicatorCalculateParams, resultItem);
295
+                result.add(resultItem);
296
+            });
297
+        } else {
298
+            return error("部门类型数据错误");
299
+        }
300
+
301
+        return success(result);
302
+    }
303
+
304
+    /**
305
+     * 工作画像-总体概览
306
+     */
307
+    @Cacheable(
308
+            value = "statistics_data",
309
+            keyGenerator = "statisticsKeyGenerator",
310
+            unless = "!T(com.sundot.airport.common.cache.StatisticsCacheConditionUtil).isSuccess(#result) ||T(com.sundot.airport.common.cache.StatisticsCacheConditionUtil).isEmptyData(#result)"
311
+    )
312
+    @GetMapping("/workingPortrait")
313
+    public AjaxResult workingPortrait(BaseLargeScreenQueryParamDto dto) {
314
+        // 返回结果
315
+        SysLargeScreenWorkingPortraitDto result = new SysLargeScreenWorkingPortraitDto();
316
+
317
+        // 工作产出和管理推动查询条件
318
+        CheckLargeScreenCorrectionQueryParamDto checkQueryParam = new CheckLargeScreenCorrectionQueryParamDto();
319
+        checkQueryParam.setDeptId(dto.getDeptId());
320
+        checkQueryParam.setStartDate(dto.getStartDate());
321
+        checkQueryParam.setEndDate(dto.getEndDate());
322
+        checkQueryParam.setSpecifiedDate(dto.getSpecifiedDate());
323
+        checkQueryParam.setProcessStatus("3");//ProcessStatusEnum.ARCHIVED.getCode()-归档
324
+        // 工作产出
325
+        List<CheckLargeScreenCommonDto> workOutputCheckList = checkLargeScreenService.workOutputCheck(checkQueryParam);
326
+        result.setWorkOutput(workOutputCheckList.stream().map(CheckLargeScreenCommonDto::getTotal).reduce(BigDecimal.ZERO, BigDecimal::add));
327
+        // 管理推动
328
+        CheckLargeScreenCorrectionDto checkLargeScreenCorrectionDto = checkLargeScreenService.managementPromotionCorrection(checkQueryParam);
329
+        result.setManagementPromotion(checkLargeScreenCorrectionDto.getOverTimeCompletedCount().add(checkLargeScreenCorrectionDto.getOverTimeUnfinishedCount()));
330
+        //在岗时长
331
+        SysDept sysDept = deptService.selectDeptById(dto.getDeptId());
332
+        IndicatorCalculateParams indicatorCalculateParams = this.appendParams(dto);
333
+        indicatorCalculateParams.setUserType(StrUtil.equals(DeptTypeEnum.STATION.getCode(), sysDept.getDeptType()) ? UserType.STATION : UserType.DEPARTMENT);
334
+        indicatorCalculateParams.setDeptId(dto.getDeptId());
335
+        List<SysUser> sysUsers = iSysUserService.selectUserListByRoleKeyAndDeptId(Arrays.asList(RoleTypeEnum.banzuzhang.getCode(), RoleTypeEnum.SecurityCheck.getCode()), dto.getDeptId());
336
+        indicatorCalculateParams.setUserIds(sysUsers.stream().map(SysUser::getUserId).collect(Collectors.toList()));
337
+        SysLargeScreenDetailDto detail = new SysLargeScreenDetailDto();
338
+        this.avgWorkingHours(indicatorCalculateParams, detail);
339
+        result.setAvgWorkingHours(detail.getAvgWorkingHours());
340
+        return success(result);
341
+    }
342
+
343
+    /**
344
+     * 根据用户编号获取详细信息
345
+     */
346
+    @PreAuthorize("@ss.hasPermi('system:user:query')")
347
+    @GetMapping(value = {"/", "/{userId}"})
348
+    public AjaxResult getInfo(@PathVariable(value = "userId", required = false) Long userId) {
349
+        AjaxResult ajax = AjaxResult.success();
350
+        if (StringUtils.isNotNull(userId)) {
351
+            userService.checkUserDataScope(userId);
352
+            SysUser sysUser = userService.selectUserById(userId);
353
+            if (StringUtils.isNotEmpty(sysUser.getTeamCooperation())) {
354
+                List<Long> list = Arrays.stream(sysUser.getTeamCooperation().split(",")).map(Long::valueOf).collect(Collectors.toList());
355
+                sysUser.setTeamCooperationList(userService.selectByUserIdList(list));
356
+            }
357
+            ajax.put(AjaxResult.DATA_TAG, sysUser);
358
+            ajax.put("postIds", postService.selectPostListByUserId(userId));
359
+            ajax.put("roleIds", sysUser.getRoles().stream().map(SysRole::getRoleId).collect(Collectors.toList()));
360
+        }
361
+        List<SysRole> roles = roleService.selectRoleAll();
362
+        ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
363
+        ajax.put("posts", postService.selectPostAll());
364
+        return ajax;
365
+    }
366
+
367
+    /**
368
+     * 新增用户
369
+     */
370
+    @PreAuthorize("@ss.hasPermi('system:user:add')")
371
+    @Log(title = "用户管理", businessType = BusinessType.INSERT)
372
+    @PostMapping
373
+    public AjaxResult add(@Validated @RequestBody SysUser user) {
374
+        deptService.checkDeptDataScope(user.getDeptId());
375
+        roleService.checkRoleDataScope(user.getRoleIds());
376
+        if (!userService.checkUserNameUnique(user)) {
377
+            return error("新增用户'" + user.getUserName() + "'失败,登录账号已存在");
378
+        } else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) {
379
+            return error("新增用户'" + user.getUserName() + "'失败,手机号码已存在");
380
+        } else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) {
381
+            return error("新增用户'" + user.getUserName() + "'失败,邮箱账号已存在");
382
+        }
383
+        user.setCreateBy(getUsername());
384
+        user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
385
+        return toAjax(userService.insertUser(user));
386
+    }
387
+
388
+    /**
389
+     * 修改用户
390
+     */
391
+    @PreAuthorize("@ss.hasPermi('system:user:edit')")
392
+    @Log(title = "用户管理", businessType = BusinessType.UPDATE)
393
+    @PutMapping
394
+    public AjaxResult edit(@Validated @RequestBody SysUser user) {
395
+        userService.checkUserAllowed(user);
396
+        userService.checkUserDataScope(user.getUserId());
397
+        deptService.checkDeptDataScope(user.getDeptId());
398
+        roleService.checkRoleDataScope(user.getRoleIds());
399
+        if (!userService.checkUserNameUnique(user)) {
400
+            return error("修改用户'" + user.getUserName() + "'失败,登录账号已存在");
401
+        } else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) {
402
+            return error("修改用户'" + user.getUserName() + "'失败,手机号码已存在");
403
+        } else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) {
404
+            return error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在");
405
+        }
406
+        user.setUpdateBy(getUsername());
407
+        redisCache.expire(CacheConstants.USER_INFO_KEY + user.getUserId(), 0);
408
+        redisCache.expire(CacheConstants.USER_INFO_KEY + user.getUserName(), 0);
409
+        return toAjax(userService.updateUser(user));
410
+    }
411
+
412
+    /**
413
+     * 删除用户
414
+     */
415
+    @PreAuthorize("@ss.hasPermi('system:user:remove')")
416
+    @Log(title = "用户管理", businessType = BusinessType.DELETE)
417
+    @DeleteMapping("/{userIds}")
418
+    public AjaxResult remove(@PathVariable Long[] userIds) {
419
+        if (ArrayUtils.contains(userIds, getUserId())) {
420
+            return error("当前用户不能删除");
421
+        }
422
+        return toAjax(userService.deleteUserByIds(userIds));
423
+    }
424
+
425
+    /**
426
+     * 重置密码
427
+     */
428
+    @PreAuthorize("@ss.hasPermi('system:user:resetPwd')")
429
+    @Log(title = "用户管理", businessType = BusinessType.UPDATE)
430
+    @PutMapping("/resetPwd")
431
+    public AjaxResult resetPwd(@RequestBody SysUser user) {
432
+        userService.checkUserAllowed(user);
433
+        userService.checkUserDataScope(user.getUserId());
434
+        user.setPassword(SecurityUtils.encryptPassword(user.getPassword()));
435
+        user.setUpdateBy(getUsername());
436
+        return toAjax(userService.resetPwd(user));
437
+    }
438
+
439
+    /**
440
+     * 状态修改
441
+     */
442
+    @PreAuthorize("@ss.hasPermi('system:user:edit')")
443
+    @Log(title = "用户管理", businessType = BusinessType.UPDATE)
444
+    @PutMapping("/changeStatus")
445
+    public AjaxResult changeStatus(@RequestBody SysUser user) {
446
+        userService.checkUserAllowed(user);
447
+        userService.checkUserDataScope(user.getUserId());
448
+        user.setUpdateBy(getUsername());
449
+        return toAjax(userService.updateUserStatus(user));
450
+    }
451
+
452
+    /**
453
+     * 根据用户编号获取授权角色
454
+     */
455
+    @PreAuthorize("@ss.hasPermi('system:user:query')")
456
+    @GetMapping("/authRole/{userId}")
457
+    public AjaxResult authRole(@PathVariable("userId") Long userId) {
458
+        AjaxResult ajax = AjaxResult.success();
459
+        SysUser user = userService.selectUserById(userId);
460
+        List<SysRole> roles = roleService.selectRolesByUserId(userId);
461
+        ajax.put("user", user);
462
+        ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList()));
463
+        redisCache.expire(CacheConstants.USER_INFO_KEY + user.getUserId(), 0);
464
+        redisCache.expire(CacheConstants.USER_INFO_KEY + user.getUserName(), 0);
465
+        return ajax;
466
+    }
467
+
468
+    /**
469
+     * 用户授权角色
470
+     */
471
+    @PreAuthorize("@ss.hasPermi('system:user:edit')")
472
+    @Log(title = "用户管理", businessType = BusinessType.GRANT)
473
+    @PutMapping("/authRole")
474
+    public AjaxResult insertAuthRole(Long userId, Long[] roleIds) {
475
+        userService.checkUserDataScope(userId);
476
+        roleService.checkRoleDataScope(roleIds);
477
+        userService.insertUserAuth(userId, roleIds);
478
+        redisCache.expire(CacheConstants.USER_INFO_KEY + userId, 0);
479
+        return success();
480
+    }
481
+
482
+    /**
483
+     * 获取部门树列表
484
+     */
485
+    @PreAuthorize("@ss.hasPermi('system:user:list')")
486
+    @GetMapping("/deptTree")
487
+    public AjaxResult deptTree(SysDept dept) {
488
+        return success(deptService.selectDeptTreeList(dept));
489
+    }
490
+
491
+    /**
492
+     * 获取部门及用户树列表
493
+     */
494
+    @PreAuthorize("@ss.hasPermi('system:user:list')")
495
+    @GetMapping("/deptUserTree")
496
+    public AjaxResult deptUserTree(SysDept dept) {
497
+        return success(deptService.selectDeptUserTreeList(dept));
498
+    }
499
+
500
+    /**
501
+     * 获取全部用户列表
502
+     */
503
+    @PreAuthorize("@ss.hasPermi('system:user:list')")
504
+    @GetMapping("/listAll")
505
+    public AjaxResult listAll(SysUser user) {
506
+        List<SysUser> list = userService.selectUserList(user);
507
+        return success(list);
508
+    }
509
+
510
+    /**
511
+     * 通过用户ID查询用户各级领导
512
+     */
513
+    @PreAuthorize("@ss.hasPermi('system:user:query')")
514
+    @GetMapping("/selectDeptLeaderByUserId")
515
+    public AjaxResult selectDeptLeaderByUserId(@RequestParam("userId") Long userId, @RequestParam("deptType") String deptType) {
516
+        return AjaxResult.success(userService.selectDeptLeaderByUserId(userId, deptType));
517
+    }
518
+
519
+    /**
520
+     * 重置班组长和安检员的岗位为旅检的所有二级岗位
521
+     */
522
+    @PreAuthorize("@ss.hasPermi('system:user:edit')")
523
+    @Log(title = "重置班组长和安检员的岗位为旅检的所有二级岗位", businessType = BusinessType.UPDATE)
524
+    @GetMapping("/resetUserPost")
525
+    public AjaxResult resetUserPost() {
526
+        return toAjax(userService.resetUserPost());
527
+    }
528
+
529
+    /**
530
+     * 根据用户ID获取岗位选择框列表
531
+     */
532
+    @PreAuthorize("@ss.hasPermi('system:user:query')")
533
+    @GetMapping(value = {"/getPostListsByUserId/{userId}"})
534
+    public AjaxResult getPostListsByUserId(@PathVariable("userId") Long userId) {
535
+        return AjaxResult.success(postService.selectPostListsByUserId(userId));
536
+    }
537
+
538
+    /**
539
+     * 获取用户画像-指标
540
+     *
541
+     * @return
542
+     */
543
+    private IndicatorCalculateParams appendParams(BaseLargeScreenQueryParamDto dto) {
544
+        IndicatorCalculateParams params = new IndicatorCalculateParams();
545
+        params.setIndicatorTypes(Arrays.asList("qualificationLevel", "workingDate", "workingHours", "effectiveSeizureCount", "workYears", "securityWorkYears"));
546
+        // 设置默认时间范围
547
+        java.util.Date endTime = Optional.ofNullable(dto.getEndDate()).orElse(new java.util.Date());
548
+        java.util.Date startTime = Optional.ofNullable(dto.getStartDate())
549
+                .orElse(java.util.Date.from(endTime.toInstant().minusSeconds(7862400L))); // 91天
550
+
551
+        params.setStartTime(startTime);
552
+        params.setEndTime(endTime);
553
+        return params;
554
+    }
555
+
556
+    /**
557
+     * 获取平均查获数量
558
+     *
559
+     * @param params
560
+     * @param detail
561
+     */
562
+    private void avgSeizureCount(IndicatorCalculateParams params, SysLargeScreenDetailDto detail) {
563
+        ModularIndicatorResult indicators = userPortraitService.getIndicators(params, Arrays.asList("item"));
564
+        if (indicators == null) {
565
+            detail.setAvgSeizureCount(BigDecimal.ZERO);
566
+            return;
567
+        }
568
+
569
+        ItemSeizureModuleIndicatorResult itemResult = (ItemSeizureModuleIndicatorResult) indicators.getModuleResult("item");
570
+        if (itemResult == null) {
571
+            detail.setAvgSeizureCount(BigDecimal.ZERO);
572
+            return;
573
+        }
574
+
575
+        if (UserType.PERSONAL.equals(params.getUserType())) {
576
+            detail.setAvgSeizureCount(itemResult.getEffectiveSeizureCount() != null ?
577
+                    new BigDecimal(itemResult.getEffectiveSeizureCount().toString()) : BigDecimal.ZERO);
578
+        } else {
579
+            Object effectiveSeizureCount = itemResult.getEffectiveSeizureCount();
580
+            if (effectiveSeizureCount instanceof SeizureStats) {
581
+                SeizureStats seizureStats = (SeizureStats) effectiveSeizureCount;
582
+                if (seizureStats.getTotalCount() != 0) {
583
+                    detail.setAvgSeizureCount(new BigDecimal(seizureStats.getTotalCount()));
584
+                } else {
585
+                    detail.setAvgSeizureCount(BigDecimal.ZERO);
586
+                }
587
+            } else {
588
+                detail.setAvgSeizureCount(BigDecimal.ZERO);
589
+            }
590
+        }
591
+    }
592
+
593
+    /**
594
+     * 获取平均上岗时长
595
+     *
596
+     * @param params
597
+     * @param detail
598
+     */
599
+    private void avgWorkingHours(IndicatorCalculateParams params, SysLargeScreenDetailDto detail) {
600
+        ModularIndicatorResult indicators = userPortraitService.getIndicators(params, Arrays.asList("attendance"));
601
+        if (indicators == null) {
602
+            detail.setAvgWorkingHours(BigDecimal.ZERO);
603
+            return;
604
+        }
605
+
606
+        AttendanceModuleIndicatorResult attendanceResult = (AttendanceModuleIndicatorResult) indicators.getModuleResult("attendance");
607
+        if (attendanceResult == null) {
608
+            detail.setAvgWorkingHours(BigDecimal.ZERO);
609
+            return;
610
+        }
611
+
612
+        if (UserType.PERSONAL.equals(params.getUserType())) {
613
+            detail.setAvgWorkingHours(attendanceResult.getWorkingHours() != null ?
614
+                    new BigDecimal(attendanceResult.getWorkingHours().toString()) : BigDecimal.ZERO);
615
+        } else {
616
+            Object workingHours = attendanceResult.getWorkingHours();
617
+            if (workingHours instanceof WorkHoursStats) {
618
+                WorkHoursStats workHoursStats = (WorkHoursStats) workingHours;
619
+                detail.setAvgWorkingHours(workHoursStats.getAveragePersonnel() != null ?
620
+                        workHoursStats.getAveragePersonnel() : BigDecimal.ZERO);
621
+            } else {
622
+                detail.setAvgWorkingHours(BigDecimal.ZERO);
623
+            }
624
+        }
625
+    }
626
+
627
+    /**
628
+     * 获取平均出勤天数
629
+     *
630
+     * @param params
631
+     * @param detail
632
+     */
633
+    private void avgWorkingDays(IndicatorCalculateParams params, SysLargeScreenDetailDto detail) {
634
+        ModularIndicatorResult indicators = userPortraitService.getIndicators(params, Arrays.asList("attendance"));
635
+        if (indicators == null) {
636
+            detail.setAvgWorkingDays(BigDecimal.ZERO);
637
+            return;
638
+        }
639
+
640
+        AttendanceModuleIndicatorResult attendanceResult = (AttendanceModuleIndicatorResult) indicators.getModuleResult("attendance");
641
+        if (attendanceResult == null) {
642
+            detail.setAvgWorkingDays(BigDecimal.ZERO);
643
+            return;
644
+        }
645
+
646
+        if (UserType.PERSONAL.equals(params.getUserType())) {
647
+            detail.setAvgWorkingDays(attendanceResult.getWorkingDate() != null ?
648
+                    new BigDecimal(attendanceResult.getWorkingDate().toString()) : BigDecimal.ZERO);
649
+        } else {
650
+            Object workingDate = attendanceResult.getWorkingDate();
651
+            if (workingDate instanceof WorkDateStats) {
652
+                WorkDateStats workDateStats = (WorkDateStats) workingDate;
653
+                detail.setAvgWorkingDays(workDateStats.getAveragePersonnel() != null ?
654
+                        workDateStats.getAveragePersonnel() : BigDecimal.ZERO);
655
+            } else {
656
+                detail.setAvgWorkingDays(BigDecimal.ZERO);
657
+            }
658
+        }
659
+    }
660
+
661
+    /**
662
+     * 获取资质等级
663
+     *
664
+     * @param params
665
+     * @param detail
666
+     */
667
+    private void qualificationLevel(IndicatorCalculateParams params, SysLargeScreenDetailDto detail) {
668
+        ModularIndicatorResult indicators = userPortraitService.getIndicators(params, Arrays.asList("system"));
669
+        if (indicators == null) {
670
+            detail.setQualificationLevel("0");
671
+            return;
672
+        }
673
+
674
+        IndicatorResult systemResult = (IndicatorResult) indicators.getModuleResult("system");
675
+        if (systemResult == null) {
676
+            detail.setQualificationLevel("0");
677
+            return;
678
+        }
679
+
680
+        if (UserType.PERSONAL.equals(params.getUserType())) {
681
+            String qualificationLevel = systemResult.getQualificationLevel();
682
+            detail.setQualificationLevel(qualificationLevel != null ? qualificationLevel : "0");
683
+        } else {
684
+            List<QualificationStats> qualificationLevelStats = systemResult.getQualificationLevelStats();
685
+            if (qualificationLevelStats != null) {
686
+                final String FIRST_LEVEL = "LEVEL_ONE";
687
+                Integer reduce = qualificationLevelStats.stream()
688
+                        .filter(qualificationStats -> FIRST_LEVEL.equals(qualificationStats.getLevelName()))
689
+                        .map(QualificationStats::getCount)
690
+                        .reduce(0, Integer::sum);
691
+                detail.setQualificationLevel(reduce.toString());
692
+            } else {
693
+                detail.setQualificationLevel("0");
694
+            }
695
+        }
696
+    }
697
+
698
+    /**
699
+     * 个人获取工作年限
700
+     */
701
+    private void workingYears(IndicatorCalculateParams params, SysLargeScreenDetailDto detail) {
702
+        ModularIndicatorResult indicators = userPortraitService.getIndicators(params, Arrays.asList("system"));
703
+        if (indicators == null) {
704
+            detail.setWorkYears(BigDecimal.ZERO);
705
+        }
706
+        IndicatorResult systemResult = (IndicatorResult) indicators.getModuleResult("system");
707
+        if (systemResult == null) {
708
+            detail.setWorkYears(BigDecimal.ZERO);
709
+            return;
710
+        }
711
+        if (UserType.PERSONAL.equals(params.getUserType())) {
712
+            Integer workYears = systemResult.getSecurityWorkYears();
713
+            detail.setWorkYears(workYears != null ? new BigDecimal(workYears) : BigDecimal.ZERO);
714
+        } else {
715
+            WorkExperienceStats securityWorkYearsStats = systemResult.getSecurityWorkYearsStats();
716
+            if (securityWorkYearsStats != null && securityWorkYearsStats.getAverageWorkYears() != null) {
717
+                detail.setWorkYears(BigDecimal.valueOf(securityWorkYearsStats.getAverageWorkYears()));
718
+            } else {
719
+                detail.setWorkYears(BigDecimal.ZERO);
720
+            }
721
+        }
722
+    }
723
+
724
+
725
+    /**
726
+     * 获取测试平均分
727
+     *
728
+     * @param params
729
+     * @param detail
730
+     */
731
+    private void avgTestScore(IndicatorCalculateParams params, SysLargeScreenDetailDto detail) {
732
+        java.util.Date startTime = params.getStartTime();
733
+        java.util.Date endTime = params.getEndTime();
734
+
735
+        // 根据用户类型查询
736
+        if (UserType.PERSONAL.equals(params.getUserType())) {
737
+            // 个人:查询该用户的平均分
738
+            Long userId = params.getUserId();
739
+            BigDecimal avgScore = calculateUserAvgScore(userId, startTime, endTime);
740
+            detail.setTestScore(avgScore);
741
+        } else {
742
+            // 组织:查询该部门下所有成员的平均分
743
+            Long deptId = params.getDeptId();
744
+            BigDecimal avgScore = calculateDeptAvgScore(deptId, startTime, endTime);
745
+            detail.setTestScore(avgScore);
746
+        }
747
+    }
748
+
749
+    /**
750
+     * 计算个人平均分
751
+     */
752
+    private BigDecimal calculateUserAvgScore(Long userId, java.util.Date startTime, java.util.Date endTime) {
753
+        LambdaQueryWrapper<DailyTask> wrapper = new LambdaQueryWrapper<>();
754
+        wrapper.eq(DailyTask::getDtUserId, userId);
755
+        wrapper.ge(DailyTask::getDtBusinessDate, Date.valueOf(
756
+                new java.text.SimpleDateFormat("yyyy-MM-dd").format(startTime)));
757
+        wrapper.le(DailyTask::getDtBusinessDate, Date.valueOf(
758
+                new java.text.SimpleDateFormat("yyyy-MM-dd").format(endTime)));
759
+        wrapper.in(DailyTask::getDtStatus, "COMPLETED", "EXPIRED");
760
+        wrapper.eq(DailyTask::getDelStatus, 0);
761
+
762
+        List<DailyTask> tasks = dailyTaskMapper.selectList(wrapper);
763
+
764
+        if (tasks.isEmpty()) {
765
+            return BigDecimal.ZERO;
766
+        }
767
+
768
+        BigDecimal totalScore = tasks.stream()
769
+                .map(task -> "EXPIRED".equals(task.getDtStatus()) ? BigDecimal.ZERO : task.getDtTotalScore())
770
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
771
+
772
+        return totalScore.divide(new BigDecimal(tasks.size()), 2, RoundingMode.HALF_UP);
773
+    }
774
+
775
+    /**
776
+     * 计算部门平均分(所有成员的平均分)
777
+     */
778
+    private BigDecimal calculateDeptAvgScore(Long deptId, java.util.Date startTime, java.util.Date endTime) {
779
+        // 1. 获取部门下所有用户
780
+        List<SysUser> users = iSysUserService.selectUserByDeptId(deptId);
781
+
782
+        if (users.isEmpty()) {
783
+            return BigDecimal.ZERO;
784
+        }
785
+
786
+        // 2. 收集所有用户ID
787
+        List<Long> userIds = users.stream()
788
+                .map(SysUser::getUserId)
789
+                .collect(Collectors.toList());
790
+
791
+        // 3. 查询这些用户的所有任务
792
+        LambdaQueryWrapper<DailyTask> wrapper = new LambdaQueryWrapper<>();
793
+        wrapper.in(DailyTask::getDtUserId, userIds);
794
+        wrapper.ge(DailyTask::getDtBusinessDate, Date.valueOf(
795
+                new java.text.SimpleDateFormat("yyyy-MM-dd").format(startTime)));
796
+        wrapper.le(DailyTask::getDtBusinessDate, Date.valueOf(
797
+                new java.text.SimpleDateFormat("yyyy-MM-dd").format(endTime)));
798
+        wrapper.in(DailyTask::getDtStatus, "COMPLETED", "EXPIRED");
799
+        wrapper.eq(DailyTask::getDelStatus, 0);
800
+
801
+        List<DailyTask> tasks = dailyTaskMapper.selectList(wrapper);
802
+
803
+        if (tasks.isEmpty()) {
804
+            return BigDecimal.ZERO;
805
+        }
806
+
807
+        // 4. 计算所有任务的平均分
808
+        BigDecimal totalScore = tasks.stream()
809
+                .map(task -> "EXPIRED".equals(task.getDtStatus()) ? BigDecimal.ZERO : task.getDtTotalScore())
810
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
811
+
812
+        return totalScore.divide(new BigDecimal(tasks.size()), 2, RoundingMode.HALF_UP);
813
+    }
814
+
815
+    /**
816
+     * 计算population接口的测试平均分
817
+     * 根据userId或deptId查询抽问抽答的平均分
818
+     */
819
+    private BigDecimal calculatePopulationTestScore(BaseLargeScreenQueryParamDto dto) {
820
+        // 设置时间范围
821
+        java.util.Date endTime = Optional.ofNullable(dto.getEndDate()).orElse(new java.util.Date());
822
+        java.util.Date startTime = Optional.ofNullable(dto.getStartDate())
823
+                .orElse(java.util.Date.from(endTime.toInstant().minusSeconds(7862400L))); // 91天
824
+
825
+        // 判断是个人还是部门
826
+        if (ObjectUtil.isNotNull(dto.getUserId())) {
827
+            // 个人维度:直接查询该用户的平均分
828
+            return calculateUserAvgScore(dto.getUserId(), startTime, endTime);
829
+        } else if (ObjectUtil.isNotNull(dto.getDeptId())) {
830
+            // 部门维度:查询该部门及所有下级部门的平均分
831
+            return calculateDeptAvgScoreByDeptId(dto.getDeptId(), startTime, endTime);
832
+        }
833
+
834
+        return BigDecimal.ZERO;
835
+    }
836
+
837
+    /**
838
+     * 根据部门ID计算平均分(基于dt_dept_id查询)
839
+     * 需要递归查找所有下级部门
840
+     */
841
+    private BigDecimal calculateDeptAvgScoreByDeptId(Long deptId, java.util.Date startTime, java.util.Date endTime) {
842
+        // 1. 获取该部门及所有下级部门的ID列表
843
+        List<Long> deptIds = getAllSubDeptIds(deptId);
844
+
845
+        if (deptIds.isEmpty()) {
846
+            return BigDecimal.ZERO;
847
+        }
848
+
849
+        // 2. 查询这些部门的所有任务(通过dt_dept_id)
850
+        LambdaQueryWrapper<DailyTask> wrapper = new LambdaQueryWrapper<>();
851
+        wrapper.in(DailyTask::getDtDeptId, deptIds);
852
+        wrapper.ge(DailyTask::getDtBusinessDate, Date.valueOf(
853
+                new java.text.SimpleDateFormat("yyyy-MM-dd").format(startTime)));
854
+        wrapper.le(DailyTask::getDtBusinessDate, Date.valueOf(
855
+                new java.text.SimpleDateFormat("yyyy-MM-dd").format(endTime)));
856
+        wrapper.in(DailyTask::getDtStatus, "COMPLETED", "EXPIRED");
857
+        wrapper.eq(DailyTask::getDelStatus, 0);
858
+
859
+        List<DailyTask> tasks = dailyTaskMapper.selectList(wrapper);
860
+
861
+        if (tasks.isEmpty()) {
862
+            return BigDecimal.ZERO;
863
+        }
864
+
865
+        // 3. 计算平均分(EXPIRED的任务分数算0)
866
+        BigDecimal totalScore = tasks.stream()
867
+                .map(task -> "EXPIRED".equals(task.getDtStatus()) ? BigDecimal.ZERO : task.getDtTotalScore())
868
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
869
+
870
+        return totalScore.divide(new BigDecimal(tasks.size()), 2, RoundingMode.HALF_UP);
871
+    }
872
+
873
+    /**
874
+     * 获取指定部门及其所有下级部门的ID列表
875
+     * 根据sys_dept表的层级关系递归查找
876
+     */
877
+    private List<Long> getAllSubDeptIds(Long deptId) {
878
+        List<Long> result = new ArrayList<>();
879
+        result.add(deptId); // 包含自己
880
+
881
+        // 查询直接下级部门
882
+        SysDept queryDept = new SysDept();
883
+        queryDept.setParentId(deptId);
884
+        List<SysDept> subDepts = deptService.selectDeptInfoAll(queryDept);
885
+
886
+        // 递归查询所有下级部门
887
+        for (SysDept subDept : subDepts) {
888
+            if ("0".equals(subDept.getDelFlag())) { // 只包含未删除的部门
889
+                result.addAll(getAllSubDeptIds(subDept.getDeptId()));
890
+            }
891
+        }
892
+
893
+        return result;
894
+    }
895
+}

+ 102 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/system/SysWorkingDocumentController.java

@@ -0,0 +1,102 @@
1
+package com.sundot.airport.web.controller.system;
2
+
3
+import java.util.List;
4
+import javax.servlet.http.HttpServletResponse;
5
+
6
+import org.springframework.security.access.prepost.PreAuthorize;
7
+import org.springframework.beans.factory.annotation.Autowired;
8
+import org.springframework.web.bind.annotation.GetMapping;
9
+import org.springframework.web.bind.annotation.PostMapping;
10
+import org.springframework.web.bind.annotation.PutMapping;
11
+import org.springframework.web.bind.annotation.DeleteMapping;
12
+import org.springframework.web.bind.annotation.PathVariable;
13
+import org.springframework.web.bind.annotation.RequestBody;
14
+import org.springframework.web.bind.annotation.RequestMapping;
15
+import org.springframework.web.bind.annotation.RestController;
16
+import com.sundot.airport.common.annotation.Log;
17
+import com.sundot.airport.common.core.controller.BaseController;
18
+import com.sundot.airport.common.core.domain.AjaxResult;
19
+import com.sundot.airport.common.enums.BusinessType;
20
+import com.sundot.airport.system.domain.SysWorkingDocument;
21
+import com.sundot.airport.system.service.ISysWorkingDocumentService;
22
+import com.sundot.airport.common.utils.poi.ExcelUtil;
23
+import com.sundot.airport.common.core.page.TableDataInfo;
24
+
25
+/**
26
+ * 工作文档Controller
27
+ *
28
+ * @author ruoyi
29
+ * @date 2025-11-18
30
+ */
31
+@RestController
32
+@RequestMapping("/system/workingDocument")
33
+public class SysWorkingDocumentController extends BaseController {
34
+    @Autowired
35
+    private ISysWorkingDocumentService sysWorkingDocumentService;
36
+
37
+    /**
38
+     * 查询工作文档列表
39
+     */
40
+    @PreAuthorize("@ss.hasPermi('system:workingDocument:list')")
41
+    @GetMapping("/list")
42
+    public TableDataInfo list(SysWorkingDocument sysWorkingDocument) {
43
+        startPage();
44
+        List<SysWorkingDocument> list = sysWorkingDocumentService.selectSysWorkingDocumentList(sysWorkingDocument);
45
+        return getDataTable(list);
46
+    }
47
+
48
+    /**
49
+     * 导出工作文档列表
50
+     */
51
+    @PreAuthorize("@ss.hasPermi('system:workingDocument:export')")
52
+    @Log(title = "工作文档", businessType = BusinessType.EXPORT)
53
+    @PostMapping("/export")
54
+    public void export(HttpServletResponse response, SysWorkingDocument sysWorkingDocument) {
55
+        List<SysWorkingDocument> list = sysWorkingDocumentService.selectSysWorkingDocumentList(sysWorkingDocument);
56
+        ExcelUtil<SysWorkingDocument> util = new ExcelUtil<SysWorkingDocument>(SysWorkingDocument.class);
57
+        util.exportExcel(response, list, "工作文档数据");
58
+    }
59
+
60
+    /**
61
+     * 获取工作文档详细信息
62
+     */
63
+    @PreAuthorize("@ss.hasPermi('system:workingDocument:query')")
64
+    @GetMapping(value = "/{documentId}")
65
+    public AjaxResult getInfo(@PathVariable("documentId") Long documentId) {
66
+        return success(sysWorkingDocumentService.selectSysWorkingDocumentByDocumentId(documentId));
67
+    }
68
+
69
+    /**
70
+     * 新增工作文档
71
+     */
72
+    @PreAuthorize("@ss.hasPermi('system:workingDocument:add')")
73
+    @Log(title = "工作文档", businessType = BusinessType.INSERT)
74
+    @PostMapping
75
+    public AjaxResult add(@RequestBody SysWorkingDocument sysWorkingDocument) {
76
+        sysWorkingDocument.setCreateByUserId(getUserId());
77
+        sysWorkingDocument.setCreateBy(getLoginUser().getUser().getNickName());
78
+        return toAjax(sysWorkingDocumentService.insertSysWorkingDocument(sysWorkingDocument));
79
+    }
80
+
81
+    /**
82
+     * 修改工作文档
83
+     */
84
+    @PreAuthorize("@ss.hasPermi('system:workingDocument:edit')")
85
+    @Log(title = "工作文档", businessType = BusinessType.UPDATE)
86
+    @PutMapping
87
+    public AjaxResult edit(@RequestBody SysWorkingDocument sysWorkingDocument) {
88
+        sysWorkingDocument.setUpdateByUserId(getUserId());
89
+        sysWorkingDocument.setUpdateBy(getLoginUser().getUser().getNickName());
90
+        return toAjax(sysWorkingDocumentService.updateSysWorkingDocument(sysWorkingDocument));
91
+    }
92
+
93
+    /**
94
+     * 删除工作文档
95
+     */
96
+    @PreAuthorize("@ss.hasPermi('system:workingDocument:remove')")
97
+    @Log(title = "工作文档", businessType = BusinessType.DELETE)
98
+    @DeleteMapping("/{documentIds}")
99
+    public AjaxResult remove(@PathVariable Long[] documentIds) {
100
+        return toAjax(sysWorkingDocumentService.deleteSysWorkingDocumentByDocumentIds(documentIds));
101
+    }
102
+}

+ 183 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/tool/TestController.java

@@ -0,0 +1,183 @@
1
+package com.sundot.airport.web.controller.tool;
2
+
3
+import java.util.ArrayList;
4
+import java.util.LinkedHashMap;
5
+import java.util.List;
6
+import java.util.Map;
7
+import org.springframework.web.bind.annotation.DeleteMapping;
8
+import org.springframework.web.bind.annotation.GetMapping;
9
+import org.springframework.web.bind.annotation.PathVariable;
10
+import org.springframework.web.bind.annotation.PostMapping;
11
+import org.springframework.web.bind.annotation.PutMapping;
12
+import org.springframework.web.bind.annotation.RequestBody;
13
+import org.springframework.web.bind.annotation.RequestMapping;
14
+import org.springframework.web.bind.annotation.RestController;
15
+import com.sundot.airport.common.core.controller.BaseController;
16
+import com.sundot.airport.common.core.domain.R;
17
+import com.sundot.airport.common.utils.StringUtils;
18
+import io.swagger.annotations.Api;
19
+import io.swagger.annotations.ApiImplicitParam;
20
+import io.swagger.annotations.ApiImplicitParams;
21
+import io.swagger.annotations.ApiModel;
22
+import io.swagger.annotations.ApiModelProperty;
23
+import io.swagger.annotations.ApiOperation;
24
+
25
+/**
26
+ * swagger 用户测试方法
27
+ * 
28
+ * @author ruoyi
29
+ */
30
+@Api("用户信息管理")
31
+@RestController
32
+@RequestMapping("/test/user")
33
+public class TestController extends BaseController
34
+{
35
+    private final static Map<Integer, UserEntity> users = new LinkedHashMap<Integer, UserEntity>();
36
+    {
37
+        users.put(1, new UserEntity(1, "admin", "admin123", "15888888888"));
38
+        users.put(2, new UserEntity(2, "ry", "admin123", "15666666666"));
39
+    }
40
+
41
+    @ApiOperation("获取用户列表")
42
+    @GetMapping("/list")
43
+    public R<List<UserEntity>> userList()
44
+    {
45
+        List<UserEntity> userList = new ArrayList<UserEntity>(users.values());
46
+        return R.ok(userList);
47
+    }
48
+
49
+    @ApiOperation("获取用户详细")
50
+    @ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path", dataTypeClass = Integer.class)
51
+    @GetMapping("/{userId}")
52
+    public R<UserEntity> getUser(@PathVariable Integer userId)
53
+    {
54
+        if (!users.isEmpty() && users.containsKey(userId))
55
+        {
56
+            return R.ok(users.get(userId));
57
+        }
58
+        else
59
+        {
60
+            return R.fail("用户不存在");
61
+        }
62
+    }
63
+
64
+    @ApiOperation("新增用户")
65
+    @ApiImplicitParams({
66
+        @ApiImplicitParam(name = "userId", value = "用户id", dataType = "Integer", dataTypeClass = Integer.class),
67
+        @ApiImplicitParam(name = "username", value = "用户名称", dataType = "String", dataTypeClass = String.class),
68
+        @ApiImplicitParam(name = "password", value = "用户密码", dataType = "String", dataTypeClass = String.class),
69
+        @ApiImplicitParam(name = "mobile", value = "用户手机", dataType = "String", dataTypeClass = String.class)
70
+    })
71
+    @PostMapping("/save")
72
+    public R<String> save(UserEntity user)
73
+    {
74
+        if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId()))
75
+        {
76
+            return R.fail("用户ID不能为空");
77
+        }
78
+        users.put(user.getUserId(), user);
79
+        return R.ok();
80
+    }
81
+
82
+    @ApiOperation("更新用户")
83
+    @PutMapping("/update")
84
+    public R<String> update(@RequestBody UserEntity user)
85
+    {
86
+        if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId()))
87
+        {
88
+            return R.fail("用户ID不能为空");
89
+        }
90
+        if (users.isEmpty() || !users.containsKey(user.getUserId()))
91
+        {
92
+            return R.fail("用户不存在");
93
+        }
94
+        users.remove(user.getUserId());
95
+        users.put(user.getUserId(), user);
96
+        return R.ok();
97
+    }
98
+
99
+    @ApiOperation("删除用户信息")
100
+    @ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path", dataTypeClass = Integer.class)
101
+    @DeleteMapping("/{userId}")
102
+    public R<String> delete(@PathVariable Integer userId)
103
+    {
104
+        if (!users.isEmpty() && users.containsKey(userId))
105
+        {
106
+            users.remove(userId);
107
+            return R.ok();
108
+        }
109
+        else
110
+        {
111
+            return R.fail("用户不存在");
112
+        }
113
+    }
114
+}
115
+
116
+@ApiModel(value = "UserEntity", description = "用户实体")
117
+class UserEntity
118
+{
119
+    @ApiModelProperty("用户ID")
120
+    private Integer userId;
121
+
122
+    @ApiModelProperty("用户名称")
123
+    private String username;
124
+
125
+    @ApiModelProperty("用户密码")
126
+    private String password;
127
+
128
+    @ApiModelProperty("用户手机")
129
+    private String mobile;
130
+
131
+    public UserEntity()
132
+    {
133
+
134
+    }
135
+
136
+    public UserEntity(Integer userId, String username, String password, String mobile)
137
+    {
138
+        this.userId = userId;
139
+        this.username = username;
140
+        this.password = password;
141
+        this.mobile = mobile;
142
+    }
143
+
144
+    public Integer getUserId()
145
+    {
146
+        return userId;
147
+    }
148
+
149
+    public void setUserId(Integer userId)
150
+    {
151
+        this.userId = userId;
152
+    }
153
+
154
+    public String getUsername()
155
+    {
156
+        return username;
157
+    }
158
+
159
+    public void setUsername(String username)
160
+    {
161
+        this.username = username;
162
+    }
163
+
164
+    public String getPassword()
165
+    {
166
+        return password;
167
+    }
168
+
169
+    public void setPassword(String password)
170
+    {
171
+        this.password = password;
172
+    }
173
+
174
+    public String getMobile()
175
+    {
176
+        return mobile;
177
+    }
178
+
179
+    public void setMobile(String mobile)
180
+    {
181
+        this.mobile = mobile;
182
+    }
183
+}

+ 97 - 0
airport-admin/src/main/java/com/sundot/airport/web/core/cache/UserCache.java

@@ -0,0 +1,97 @@
1
+package com.sundot.airport.web.core.cache;
2
+
3
+import cn.hutool.core.collection.CollectionUtil;
4
+import cn.hutool.core.util.StrUtil;
5
+import com.sundot.airport.common.constant.CacheConstants;
6
+import com.sundot.airport.common.core.domain.entity.SysDept;
7
+import com.sundot.airport.common.core.domain.entity.SysUser;
8
+import com.sundot.airport.common.core.redis.RedisCache;
9
+import com.sundot.airport.common.dto.UserInfo;
10
+import com.sundot.airport.common.enums.DeptType;
11
+import com.sundot.airport.system.service.ISysDeptService;
12
+import com.sundot.airport.system.service.ISysUserService;
13
+import org.springframework.beans.BeanUtils;
14
+import org.springframework.beans.factory.annotation.Autowired;
15
+import org.springframework.stereotype.Component;
16
+
17
+import java.util.Collections;
18
+import java.util.List;
19
+import java.util.Objects;
20
+
21
+/**
22
+ * 用户信息缓存
23
+ *
24
+ * @Author: wangchong
25
+ * @Date: 2025/7/11 14:49
26
+ **/
27
+@Component
28
+public class UserCache {
29
+
30
+    @Autowired
31
+    private ISysUserService sysUserService;
32
+
33
+    @Autowired
34
+    private ISysDeptService sysDeptService;
35
+
36
+    @Autowired
37
+    private RedisCache redisCache;
38
+
39
+    /**
40
+     * 获取用户信息
41
+     *
42
+     * @param userId 用户id
43
+     * @return 用户信息
44
+     */
45
+    public UserInfo getUserInfo(Long userId) {
46
+        UserInfo userInfo = redisCache.getCacheObject(CacheConstants.USER_INFO_KEY + userId);
47
+        if (Objects.isNull(userInfo)) {
48
+            userInfo = new UserInfo();
49
+            SysUser sysUser = sysUserService.selectUserById(userId);
50
+            BeanUtils.copyProperties(sysUser, userInfo);
51
+            updateDeptInfo(userInfo);
52
+            redisCache.setCacheObject(CacheConstants.USER_INFO_KEY + userId, userInfo);
53
+            redisCache.expire(CacheConstants.USER_INFO_KEY + userId, 300);
54
+        }
55
+        return userInfo;
56
+    }
57
+
58
+
59
+    /**
60
+     * 获取用户信息
61
+     *
62
+     * @param userName 用户登录账号
63
+     * @return 用户信息
64
+     */
65
+    public UserInfo getUserInfo(String userName) {
66
+        UserInfo userInfo = redisCache.getCacheObject(CacheConstants.USER_INFO_KEY + userName);
67
+        if (Objects.isNull(userInfo)) {
68
+            userInfo = new UserInfo();
69
+            SysUser sysUser = sysUserService.selectUserByUserName(userName);
70
+            BeanUtils.copyProperties(sysUser, userInfo);
71
+            updateDeptInfo(userInfo);
72
+            redisCache.setCacheObject(CacheConstants.USER_INFO_KEY + userName, userInfo);
73
+            redisCache.expire(CacheConstants.USER_INFO_KEY + userName, 300);
74
+        }
75
+        return userInfo;
76
+    }
77
+
78
+    private void updateDeptInfo(UserInfo userInfo) {
79
+        if (Objects.isNull(userInfo.getDeptId())) {
80
+            return;
81
+        }
82
+        List<SysDept> deptList = sysDeptService.selectAllDept(userInfo.getDeptId());
83
+        if (CollectionUtil.isEmpty(deptList)) {
84
+            return;
85
+        }
86
+        Collections.reverse(deptList);
87
+        SysDept teams = deptList.stream().filter(x -> StrUtil.equals(DeptType.TEAMS.getCode(), x.getDeptType())).findFirst().orElse(new SysDept());
88
+        SysDept department = deptList.stream().filter(x -> StrUtil.equals(DeptType.DEPARTMENT.getCode(), x.getDeptType())).findFirst().orElse(new SysDept());
89
+        SysDept station = deptList.stream().filter(x -> StrUtil.equals(DeptType.STATION.getCode(), x.getDeptType())).findFirst().orElse(new SysDept());
90
+        userInfo.setTeamsId(teams.getDeptId());
91
+        userInfo.setTeamsName(teams.getDeptName());
92
+        userInfo.setDepartmentId(department.getDeptId());
93
+        userInfo.setDepartmentName(department.getDeptName());
94
+        userInfo.setStationId(station.getDeptId());
95
+        userInfo.setStationName(station.getDeptName());
96
+    }
97
+}

+ 125 - 0
airport-admin/src/main/java/com/sundot/airport/web/core/config/SwaggerConfig.java

@@ -0,0 +1,125 @@
1
+package com.sundot.airport.web.core.config;
2
+
3
+import java.util.ArrayList;
4
+import java.util.List;
5
+import org.springframework.beans.factory.annotation.Autowired;
6
+import org.springframework.beans.factory.annotation.Value;
7
+import org.springframework.context.annotation.Bean;
8
+import org.springframework.context.annotation.Configuration;
9
+import com.sundot.airport.common.config.RuoYiConfig;
10
+import io.swagger.annotations.ApiOperation;
11
+import io.swagger.models.auth.In;
12
+import springfox.documentation.builders.ApiInfoBuilder;
13
+import springfox.documentation.builders.PathSelectors;
14
+import springfox.documentation.builders.RequestHandlerSelectors;
15
+import springfox.documentation.service.ApiInfo;
16
+import springfox.documentation.service.ApiKey;
17
+import springfox.documentation.service.AuthorizationScope;
18
+import springfox.documentation.service.Contact;
19
+import springfox.documentation.service.SecurityReference;
20
+import springfox.documentation.service.SecurityScheme;
21
+import springfox.documentation.spi.DocumentationType;
22
+import springfox.documentation.spi.service.contexts.SecurityContext;
23
+import springfox.documentation.spring.web.plugins.Docket;
24
+
25
+/**
26
+ * Swagger2的接口配置
27
+ * 
28
+ * @author ruoyi
29
+ */
30
+@Configuration
31
+public class SwaggerConfig
32
+{
33
+    /** 系统基础配置 */
34
+    @Autowired
35
+    private RuoYiConfig ruoyiConfig;
36
+
37
+    /** 是否开启swagger */
38
+    @Value("${swagger.enabled}")
39
+    private boolean enabled;
40
+
41
+    /** 设置请求的统一前缀 */
42
+    @Value("${swagger.pathMapping}")
43
+    private String pathMapping;
44
+
45
+    /**
46
+     * 创建API
47
+     */
48
+    @Bean
49
+    public Docket createRestApi()
50
+    {
51
+        return new Docket(DocumentationType.OAS_30)
52
+                // 是否启用Swagger
53
+                .enable(enabled)
54
+                // 用来创建该API的基本信息,展示在文档的页面中(自定义展示的信息)
55
+                .apiInfo(apiInfo())
56
+                // 设置哪些接口暴露给Swagger展示
57
+                .select()
58
+                // 扫描所有有注解的api,用这种方式更灵活
59
+                .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
60
+                // 扫描指定包中的swagger注解
61
+                // .apis(RequestHandlerSelectors.basePackage("com.sundot.airport.project.tool.swagger"))
62
+                // 扫描所有 .apis(RequestHandlerSelectors.any())
63
+                .paths(PathSelectors.any())
64
+                .build()
65
+                /* 设置安全模式,swagger可以设置访问token */
66
+                .securitySchemes(securitySchemes())
67
+                .securityContexts(securityContexts())
68
+                .pathMapping(pathMapping);
69
+    }
70
+
71
+    /**
72
+     * 安全模式,这里指定token通过Authorization头请求头传递
73
+     */
74
+    private List<SecurityScheme> securitySchemes()
75
+    {
76
+        List<SecurityScheme> apiKeyList = new ArrayList<SecurityScheme>();
77
+        apiKeyList.add(new ApiKey("Authorization", "Authorization", In.HEADER.toValue()));
78
+        return apiKeyList;
79
+    }
80
+
81
+    /**
82
+     * 安全上下文
83
+     */
84
+    private List<SecurityContext> securityContexts()
85
+    {
86
+        List<SecurityContext> securityContexts = new ArrayList<>();
87
+        securityContexts.add(
88
+                SecurityContext.builder()
89
+                        .securityReferences(defaultAuth())
90
+                        .operationSelector(o -> o.requestMappingPattern().matches("/.*"))
91
+                        .build());
92
+        return securityContexts;
93
+    }
94
+
95
+    /**
96
+     * 默认的安全上引用
97
+     */
98
+    private List<SecurityReference> defaultAuth()
99
+    {
100
+        AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
101
+        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
102
+        authorizationScopes[0] = authorizationScope;
103
+        List<SecurityReference> securityReferences = new ArrayList<>();
104
+        securityReferences.add(new SecurityReference("Authorization", authorizationScopes));
105
+        return securityReferences;
106
+    }
107
+
108
+    /**
109
+     * 添加摘要信息
110
+     */
111
+    private ApiInfo apiInfo()
112
+    {
113
+        // 用ApiInfoBuilder进行定制
114
+        return new ApiInfoBuilder()
115
+                // 设置标题
116
+                .title("标题:若依管理系统_接口文档")
117
+                // 描述
118
+                .description("描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...")
119
+                // 作者信息
120
+                .contact(new Contact(ruoyiConfig.getName(), null, null))
121
+                // 版本
122
+                .version("版本号:" + ruoyiConfig.getVersion())
123
+                .build();
124
+    }
125
+}

+ 166 - 0
airport-admin/src/main/java/com/sundot/airport/web/core/utils/DataPermissionUtils.java

@@ -0,0 +1,166 @@
1
+package com.sundot.airport.web.core.utils;
2
+
3
+import com.sundot.airport.common.core.domain.entity.SysDept;
4
+import com.sundot.airport.common.core.domain.entity.SysRole;
5
+import com.sundot.airport.common.core.domain.model.LoginUser;
6
+import com.sundot.airport.common.enums.RoleTypeEnum;
7
+import com.sundot.airport.system.service.ISysDeptService;
8
+import com.sundot.airport.common.enums.DataPermissionType;
9
+import com.sundot.airport.common.core.domain.DataPermissionResult;
10
+import org.springframework.beans.factory.annotation.Autowired;
11
+import org.springframework.stereotype.Component;
12
+import org.springframework.util.CollectionUtils;
13
+import com.sundot.airport.common.core.domain.BaseEntity;
14
+
15
+import java.util.List;
16
+import java.util.stream.Collectors;
17
+
18
+/**
19
+ * 数据权限工具类
20
+ *
21
+ * @author ruoyi
22
+ */
23
+@Component
24
+public class DataPermissionUtils {
25
+    
26
+    private static ISysDeptService sysDeptService;
27
+    
28
+    @Autowired
29
+    public void setSysDeptService(ISysDeptService sysDeptService) {
30
+        DataPermissionUtils.sysDeptService = sysDeptService;
31
+    }
32
+
33
+    /**
34
+     * 获取数据权限结果
35
+     *
36
+     * @param userId 当前用户ID
37
+     * @param deptId 当前用户部门ID
38
+     * @param loginUser 当前登录用户信息
39
+     * @return 数据权限结果
40
+     */
41
+    public static DataPermissionResult getDataPermission(Long userId, Long deptId, LoginUser loginUser) {
42
+        // 管理员查看所有数据
43
+        if (userId == 1) {
44
+            return new DataPermissionResult(DataPermissionType.ALL, null);
45
+        }
46
+
47
+        // 获取用户角色列表
48
+        List<SysRole> roles = loginUser.getUser().getRoles();
49
+
50
+        // 如果没有角色,则只查看自己的数据
51
+        if (CollectionUtils.isEmpty(roles)) {
52
+            return new DataPermissionResult(DataPermissionType.SELF, userId);
53
+        }
54
+
55
+        // 提取用户的角色键值列表
56
+        List<String> roleKeys = roles.stream().map(SysRole::getRoleKey).collect(Collectors.toList());
57
+
58
+        // 检查是否包含特定角色
59
+        boolean isAdminRole = roleKeys.contains(RoleTypeEnum.admin.getCode());
60
+        boolean isStationRole = roleKeys.contains(RoleTypeEnum.test.getCode()) || roleKeys.contains(RoleTypeEnum.zhijianke.getCode());
61
+        boolean isDepartmentRole = roleKeys.contains(RoleTypeEnum.kezhang.getCode());
62
+        boolean isTeamRole = roleKeys.contains(RoleTypeEnum.banzuzhang.getCode());
63
+        boolean isInspectorRole = roleKeys.contains(RoleTypeEnum.SecurityCheck.getCode());
64
+
65
+        SysDept sysDept = sysDeptService.selectDeptById(deptId);
66
+
67
+        // 管理员角色查看所有数据
68
+        if (isAdminRole) {
69
+            return new DataPermissionResult(DataPermissionType.ALL, null);
70
+        }
71
+        // 站级角色查看本站数据
72
+        else if (isStationRole) {
73
+            if (sysDept != null) {
74
+                return new DataPermissionResult(DataPermissionType.STATION, sysDept.getDeptId());
75
+            } else {
76
+                return new DataPermissionResult(DataPermissionType.SELF, userId);
77
+            }
78
+        }
79
+        // 科室角色查看本科室数据
80
+        else if (isDepartmentRole) {
81
+            if (sysDept != null) {
82
+                return new DataPermissionResult(DataPermissionType.DEPARTMENT, sysDept.getDeptId());
83
+            } else {
84
+                return new DataPermissionResult(DataPermissionType.SELF, userId);
85
+            }
86
+        }
87
+        // 班组角色查看本班组数据
88
+        else if (isTeamRole) {
89
+            if (sysDept != null) {
90
+                return new DataPermissionResult(DataPermissionType.TEAM, sysDept.getDeptId());
91
+            } else {
92
+                return new DataPermissionResult(DataPermissionType.SELF, userId);
93
+            }
94
+        }
95
+        // 安检员角色只查看自己的数据
96
+        else if (isInspectorRole) {
97
+            return new DataPermissionResult(DataPermissionType.SELF, userId);
98
+        }
99
+        // 默认情况只查看自己的数据
100
+        else {
101
+            return new DataPermissionResult(DataPermissionType.SELF, userId);
102
+        }
103
+    }
104
+    
105
+    /**
106
+     * 判断用户是否具有管理员角色
107
+     * @param loginUser 当前登录用户
108
+     * @return 是否是管理员
109
+     */
110
+    public static boolean isAdmin(LoginUser loginUser) {
111
+        return hasRole(loginUser, RoleTypeEnum.admin.getCode());
112
+    }
113
+    
114
+    /**
115
+     * 判断用户是否具有站级角色(站长或质检科)
116
+     * @param loginUser 当前登录用户
117
+     * @return 是否是站级角色
118
+     */
119
+    public static boolean isStationRole(LoginUser loginUser) {
120
+        return hasRole(loginUser, RoleTypeEnum.test.getCode()) || 
121
+               hasRole(loginUser, RoleTypeEnum.zhijianke.getCode());
122
+    }
123
+    
124
+    /**
125
+     * 判断用户是否具有科室角色
126
+     * @param loginUser 当前登录用户
127
+     * @return 是否是科室角色
128
+     */
129
+    public static boolean isDepartmentRole(LoginUser loginUser) {
130
+        return hasRole(loginUser, RoleTypeEnum.kezhang.getCode());
131
+    }
132
+    
133
+    /**
134
+     * 判断用户是否具有班组角色
135
+     * @param loginUser 当前登录用户
136
+     * @return 是否是班组角色
137
+     */
138
+    public static boolean isTeamRole(LoginUser loginUser) {
139
+        return hasRole(loginUser, RoleTypeEnum.banzuzhang.getCode());
140
+    }
141
+    
142
+    /**
143
+     * 判断用户是否具有安检员角色
144
+     * @param loginUser 当前登录用户
145
+     * @return 是否是安检员角色
146
+     */
147
+    public static boolean isInspectorRole(LoginUser loginUser) {
148
+        return hasRole(loginUser, RoleTypeEnum.SecurityCheck.getCode());
149
+    }
150
+    
151
+    /**
152
+     * 判断用户是否具有指定角色
153
+     * @param loginUser 当前登录用户
154
+     * @param roleKey 角色键值
155
+     * @return 是否具有指定角色
156
+     */
157
+    private static boolean hasRole(LoginUser loginUser, String roleKey) {
158
+        if (loginUser == null || loginUser.getUser() == null || 
159
+            CollectionUtils.isEmpty(loginUser.getUser().getRoles())) {
160
+            return false;
161
+        }
162
+        
163
+        return loginUser.getUser().getRoles().stream()
164
+                .anyMatch(role -> roleKey.equals(role.getRoleKey()));
165
+    }
166
+}

+ 1 - 0
airport-admin/src/main/resources/META-INF/spring-devtools.properties

@@ -0,0 +1 @@
1
+restart.include.json=/com.alibaba.fastjson2.*.jar

+ 71 - 0
airport-admin/src/main/resources/application-druid.yml

@@ -0,0 +1,71 @@
1
+# 数据源配置
2
+spring:
3
+    datasource:
4
+        type: com.alibaba.druid.pool.DruidDataSource
5
+        driverClassName: com.mysql.cj.jdbc.Driver
6
+        druid:
7
+            # 主库数据源
8
+            master:
9
+#                url: jdbc:mysql://192.168.3.222:3306/airport?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
10
+#                username: root
11
+#                password: Abcd-1234samsundot
12
+#                url: jdbc:mysql://127.0.0.1:3307/airport?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8
13
+#                username: root
14
+#                password: 1234Samsundot
15
+                url: jdbc:mysql://127.0.0.1:3306/airport?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8
16
+                username: root
17
+                password: Samsundot@123!
18
+#                url: jdbc:mysql://60.205.166.0:3307/airport?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8
19
+#                username: root
20
+#                password: 1234Samsundot
21
+
22
+            # 从库数据源
23
+            slave:
24
+                # 从数据源开关/默认关闭
25
+                enabled: false
26
+                url: 
27
+                username: 
28
+                password: 
29
+            # 初始连接数
30
+            initialSize: 5
31
+            # 最小连接池数量
32
+            minIdle: 10
33
+            # 最大连接池数量
34
+            maxActive: 20
35
+            # 配置获取连接等待超时的时间
36
+            maxWait: 60000
37
+            # 配置连接超时时间
38
+            connectTimeout: 30000
39
+            # 配置网络超时时间
40
+            socketTimeout: 60000
41
+            # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
42
+            timeBetweenEvictionRunsMillis: 60000
43
+            # 配置一个连接在池中最小生存的时间,单位是毫秒
44
+            minEvictableIdleTimeMillis: 300000
45
+            # 配置一个连接在池中最大生存的时间,单位是毫秒
46
+            maxEvictableIdleTimeMillis: 900000
47
+            # 配置检测连接是否有效
48
+            validationQuery: SELECT 1 FROM DUAL
49
+            testWhileIdle: true
50
+            testOnBorrow: false
51
+            testOnReturn: false
52
+            webStatFilter: 
53
+                enabled: true
54
+            statViewServlet:
55
+                enabled: true
56
+                # 设置白名单,不填则允许所有访问
57
+                allow:
58
+                url-pattern: /druid/*
59
+                # 控制台管理用户名和密码
60
+                login-username: ruoyi
61
+                login-password: 123456
62
+            filter:
63
+                stat:
64
+                    enabled: true
65
+                    # 慢SQL记录
66
+                    log-slow-sql: true
67
+                    slow-sql-millis: 1000
68
+                    merge-sql: true
69
+                wall:
70
+                    config:
71
+                        multi-statement-allow: true

+ 150 - 0
airport-admin/src/main/resources/application.yml

@@ -0,0 +1,150 @@
1
+# 项目相关配置
2
+ruoyi:
3
+  # 名称
4
+  name: airport
5
+  # 版本
6
+  version: 3.9.0
7
+  # 版权年份
8
+  copyrightYear: 2025
9
+  # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath)
10
+  profile: /home/airport/uploadPath
11
+  # 获取ip地址开关
12
+  addressEnabled: false
13
+  # 验证码类型 math 数字计算 char 字符验证
14
+  captchaType: math
15
+
16
+# 开发环境配置
17
+server:
18
+  # 服务器的HTTP端口,默认为8080
19
+  port: 8088
20
+  servlet:
21
+    # 应用的访问路径
22
+    context-path: /
23
+  # Undertow 服务器配置
24
+  undertow:
25
+    # URI编码
26
+    url-charset: UTF-8
27
+    # HTTP请求头最大大小(解决文件名过长问题)
28
+    max-http-header-size: 65536
29
+    # 设置IO线程数,它主要执行非阻塞的任务
30
+    io-threads: 16
31
+    # 阻塞任务线程池,当执行类似servlet请求阻塞操作,undertow会从这个线程池中取得线程
32
+    worker-threads: 256
33
+    # 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作
34
+    # 每块buffer的空间大小,越小空间被利用越充分
35
+    buffer-size: 1024
36
+    # 每个区分配的buffer数量,所以pool的大小是buffer-size * buffers-per-region
37
+    # buffers-per-region: 1024
38
+    # 是否分配的直接内存(NIO直接分配的堆外内存)
39
+    direct-buffers: true
40
+
41
+# 日志配置
42
+logging:
43
+  level:
44
+    com.sundot.airport: debug
45
+    org.springframework: warn
46
+    # 打印完整SQL和参数
47
+    com.sundot.airport.attendance.mapper: debug
48
+
49
+# 用户配置
50
+user:
51
+  password:
52
+    # 密码最大错误次数
53
+    maxRetryCount: 5
54
+    # 密码锁定时间(默认10分钟)
55
+    lockTime: 10
56
+
57
+# Spring配置
58
+spring:
59
+  # 资源信息
60
+  messages:
61
+    # 国际化资源文件路径
62
+    basename: i18n/messages
63
+  profiles:
64
+    active: druid
65
+  # 文件上传
66
+  servlet:
67
+    multipart:
68
+      # 单个文件大小
69
+      max-file-size: 10MB
70
+      # 设置总上传的文件大小
71
+      max-request-size: 20MB
72
+  # 服务模块
73
+  devtools:
74
+    restart:
75
+      # 热部署开关
76
+      enabled: true
77
+  # redis 配置
78
+  redis:
79
+    # 地址
80
+#    host: 192.168.3.222
81
+    host: 127.0.0.1
82
+    # 端口,默认为6379
83
+    port: 6379
84
+    # 数据库索引
85
+    database: 1
86
+    # 密码
87
+    password: Qwer+1234
88
+    # 连接超时时间
89
+    timeout: 10s
90
+    lettuce:
91
+      pool:
92
+        # 连接池中的最小空闲连接
93
+        min-idle: 0
94
+        # 连接池中的最大空闲连接
95
+        max-idle: 8
96
+        # 连接池的最大数据库连接数
97
+        max-active: 8
98
+        # #连接池最大阻塞等待时间(使用负值表示没有限制)
99
+        max-wait: -1ms
100
+  config:
101
+    import: classpath:level-config.yml
102
+
103
+# token配置
104
+token:
105
+  # 令牌自定义标识
106
+  header: Authorization
107
+  # 令牌密钥
108
+  secret: abcdefghijklmnopqrstuvwxyz
109
+  # 令牌有效期(默认30分钟)
110
+  expireTime: 30
111
+
112
+# MyBatis配置
113
+mybatis:
114
+  # 搜索指定包别名
115
+  typeAliasesPackage: com.sundot.airport.**.domain
116
+  # 配置mapper的扫描,找到所有的mapper.xml映射文件
117
+  mapperLocations: classpath*:mapper/**/*Mapper.xml
118
+  # 加载全局的配置文件
119
+  configLocation: classpath:mybatis/mybatis-config.xml
120
+
121
+# MyBatis Plus配置
122
+mybatis-plus:
123
+  # 搜索指定包别名
124
+  typeAliasesPackage: com.sundot.airport.**.domain
125
+  # 配置mapper的扫描,找到所有的mapper.xml映射文件
126
+  mapperLocations: classpath*:mapper/**/*Mapper.xml
127
+  # 加载全局的配置文件
128
+  configLocation: classpath:mybatis/mybatis-config.xml
129
+
130
+# PageHelper分页插件
131
+pagehelper:
132
+  helperDialect: mysql
133
+  supportMethodsArguments: true
134
+  params: count=countSql
135
+
136
+# Swagger配置
137
+swagger:
138
+  # 是否开启swagger
139
+  enabled: true
140
+  # 请求前缀
141
+  pathMapping: /dev-api
142
+
143
+# 防止XSS攻击
144
+xss:
145
+  # 过滤开关
146
+  enabled: true
147
+  # 排除链接(多个用逗号分隔)
148
+  excludes: /system/notice
149
+  # 匹配链接
150
+  urlPatterns: /system/*,/monitor/*,/tool/*

+ 24 - 0
airport-admin/src/main/resources/banner.txt

@@ -0,0 +1,24 @@
1
+Application Version: ${ruoyi.version}
2
+Spring Boot Version: ${spring-boot.version}
3
+////////////////////////////////////////////////////////////////////
4
+//                          _ooOoo_                               //
5
+//                         o8888888o                              //
6
+//                         88" . "88                              //
7
+//                         (| ^_^ |)                              //
8
+//                         O\  =  /O                              //
9
+//                      ____/`---'\____                           //
10
+//                    .'  \\|     |//  `.                         //
11
+//                   /  \\|||  :  |||//  \                        //
12
+//                  /  _||||| -:- |||||-  \                       //
13
+//                  |   | \\\  -  /// |   |                       //
14
+//                  | \_|  ''\---/''  |   |                       //
15
+//                  \  .-\__  `-`  ___/-. /                       //
16
+//                ___`. .'  /--.--\  `. . ___                     //
17
+//              ."" '<  `.___\_<|>_/___.'  >'"".                  //
18
+//            | | :  `- \`.;`\ _ /`;.`/ - ` : | |                 //
19
+//            \  \ `-.   \_ __\ /__ _/   .-` /  /                 //
20
+//      ========`-.____`-.___\_____/___.-`____.-'========         //
21
+//                           `=---='                              //
22
+//      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^        //
23
+//             佛祖保佑       永不宕机      永无BUG               //
24
+////////////////////////////////////////////////////////////////////

+ 38 - 0
airport-admin/src/main/resources/i18n/messages.properties

@@ -0,0 +1,38 @@
1
+#错误消息
2
+not.null=* 必须填写
3
+user.jcaptcha.error=验证码错误
4
+user.jcaptcha.expire=验证码已失效
5
+user.not.exists=用户不存在/密码错误
6
+user.password.not.match=用户不存在/密码错误
7
+user.password.retry.limit.count=密码输入错误{0}次
8
+user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟
9
+user.password.delete=对不起,您的账号已被删除
10
+user.blocked=用户已封禁,请联系管理员
11
+role.blocked=角色已封禁,请联系管理员
12
+login.blocked=很遗憾,访问IP已被列入系统黑名单
13
+user.logout.success=退出成功
14
+
15
+length.not.valid=长度必须在{min}到{max}个字符之间
16
+
17
+user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头
18
+user.password.not.valid=* 5-50个字符
19
+ 
20
+user.email.not.valid=邮箱格式错误
21
+user.mobile.phone.number.not.valid=手机号格式错误
22
+user.login.success=登录成功
23
+user.register.success=注册成功
24
+user.notfound=请重新登录
25
+user.forcelogout=管理员强制退出,请重新登录
26
+user.unknown.error=未知错误,请重新登录
27
+
28
+##文件上传消息
29
+upload.exceed.maxSize=上传的文件大小超出限制的文件大小!<br/>允许的文件最大大小是:{0}MB!
30
+upload.filename.exceed.length=上传的文件名最长{0}个字符
31
+
32
+##权限
33
+no.permission=您没有数据的权限,请联系管理员添加权限 [{0}]
34
+no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}]
35
+no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}]
36
+no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}]
37
+no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}]
38
+no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}]

+ 15 - 0
airport-admin/src/main/resources/level-config.yml

@@ -0,0 +1,15 @@
1
+level:
2
+  positionLevel: 3
3
+  positionLevelMap:
4
+    1: "TERMINL"
5
+    2: "REGIONAL"
6
+    3: "CHANNEL"
7
+  checkPointLevel: 2
8
+  checkCategoryLevel: 2
9
+  seizeCategoryLevel: 2
10
+  sysDeptLevel: 3
11
+  sysDeptLevelMap:
12
+    1: "STATION"
13
+    2: "DEPARTMENT"
14
+    3: "TEAMS"
15
+  sysPostLevel: 2

+ 93 - 0
airport-admin/src/main/resources/logback.xml

@@ -0,0 +1,93 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<configuration>
3
+    <!-- 日志存放路径 -->
4
+	<property name="log.path" value="/opt/data/logs/airport_admin" />
5
+    <!-- 日志输出格式 -->
6
+	<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n" />
7
+
8
+	<!-- 控制台输出 -->
9
+	<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
10
+		<encoder>
11
+			<pattern>${log.pattern}</pattern>
12
+		</encoder>
13
+	</appender>
14
+	
15
+	<!-- 系统日志输出 -->
16
+	<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
17
+	    <file>${log.path}/sys-info.log</file>
18
+        <!-- 循环政策:基于时间创建日志文件 -->
19
+		<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
20
+            <!-- 日志文件名格式 -->
21
+			<fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
22
+			<!-- 日志最大的历史 60天 -->
23
+			<maxHistory>10</maxHistory>
24
+		</rollingPolicy>
25
+		<encoder>
26
+			<pattern>${log.pattern}</pattern>
27
+		</encoder>
28
+		<filter class="ch.qos.logback.classic.filter.LevelFilter">
29
+            <!-- 过滤的级别 -->
30
+            <level>INFO</level>
31
+            <!-- 匹配时的操作:接收(记录) -->
32
+            <onMatch>ACCEPT</onMatch>
33
+            <!-- 不匹配时的操作:拒绝(不记录) -->
34
+            <onMismatch>DENY</onMismatch>
35
+        </filter>
36
+	</appender>
37
+	
38
+	<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
39
+	    <file>${log.path}/sys-error.log</file>
40
+        <!-- 循环政策:基于时间创建日志文件 -->
41
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
42
+            <!-- 日志文件名格式 -->
43
+            <fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
44
+			<!-- 日志最大的历史 60天 -->
45
+			<maxHistory>10</maxHistory>
46
+        </rollingPolicy>
47
+        <encoder>
48
+            <pattern>${log.pattern}</pattern>
49
+        </encoder>
50
+        <filter class="ch.qos.logback.classic.filter.LevelFilter">
51
+            <!-- 过滤的级别 -->
52
+            <level>ERROR</level>
53
+			<!-- 匹配时的操作:接收(记录) -->
54
+            <onMatch>ACCEPT</onMatch>
55
+			<!-- 不匹配时的操作:拒绝(不记录) -->
56
+            <onMismatch>DENY</onMismatch>
57
+        </filter>
58
+    </appender>
59
+	
60
+	<!-- 用户访问日志输出  -->
61
+    <appender name="sys-user" class="ch.qos.logback.core.rolling.RollingFileAppender">
62
+		<file>${log.path}/sys-user.log</file>
63
+        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
64
+            <!-- 按天回滚 daily -->
65
+            <fileNamePattern>${log.path}/sys-user.%d{yyyy-MM-dd}.log</fileNamePattern>
66
+            <!-- 日志最大的历史 60天 -->
67
+            <maxHistory>10</maxHistory>
68
+        </rollingPolicy>
69
+        <encoder>
70
+            <pattern>${log.pattern}</pattern>
71
+        </encoder>
72
+    </appender>
73
+	
74
+	<!-- 系统模块日志级别控制  -->
75
+	<logger name="com.sundot.airport" level="info" />
76
+	<!-- Spring日志级别控制  -->
77
+	<logger name="org.springframework" level="warn" />
78
+
79
+	<root level="info">
80
+		<appender-ref ref="console" />
81
+	</root>
82
+	
83
+	<!--系统操作日志-->
84
+    <root level="info">
85
+        <appender-ref ref="file_info" />
86
+        <appender-ref ref="file_error" />
87
+    </root>
88
+	
89
+	<!--系统用户操作日志-->
90
+    <logger name="sys-user" level="info">
91
+        <appender-ref ref="sys-user"/>
92
+    </logger>
93
+</configuration> 

+ 20 - 0
airport-admin/src/main/resources/mybatis/mybatis-config.xml

@@ -0,0 +1,20 @@
1
+<?xml version="1.0" encoding="UTF-8" ?>
2
+<!DOCTYPE configuration
3
+PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
4
+"http://mybatis.org/dtd/mybatis-3-config.dtd">
5
+<configuration>
6
+    <!-- 全局参数 -->
7
+    <settings>
8
+        <!-- 使全局的映射器启用或禁用缓存 -->
9
+        <setting name="cacheEnabled"             value="true"   />
10
+        <!-- 允许JDBC 支持自动生成主键 -->
11
+        <setting name="useGeneratedKeys"         value="true"   />
12
+        <!-- 配置默认的执行器.SIMPLE就是普通执行器;REUSE执行器会重用预处理语句(prepared statements);BATCH执行器将重用语句并执行批量更新 -->
13
+        <setting name="defaultExecutorType"      value="SIMPLE" />
14
+		<!-- 指定 MyBatis 所用日志的具体实现 -->
15
+        <setting name="logImpl"                  value="SLF4J"  />
16
+        <!-- 使用驼峰命名法转换字段 -->
17
+		<!-- <setting name="mapUnderscoreToCamelCase" value="true"/> -->
18
+	</settings>
19
+	
20
+</configuration>

+ 475 - 0
airport-admin/src/main/resources/static/approval-frontend-example.js

@@ -0,0 +1,475 @@
1
+/**
2
+ * 审批流程前端调用示例
3
+ * @author simon lin
4
+ * @date 2025-09-06
5
+ */
6
+
7
+// 审批流程API调用类
8
+class ApprovalAPI {
9
+    constructor() {
10
+        this.baseURL = '/system/check';
11
+    }
12
+
13
+    /**
14
+     * 1. 提交个人级别检查
15
+     */
16
+    async submitPersonalCheck(checkData) {
17
+        const params = {
18
+            id: checkData.id,
19
+            description: checkData.description,
20
+            checkType: checkData.checkType,
21
+            location: checkData.location,
22
+            targetUserIds: checkData.targetUserIds
23
+        };
24
+
25
+        try {
26
+            const response = await $.ajax({
27
+                url: `${this.baseURL}/personal`,
28
+                type: 'POST',
29
+                contentType: 'application/json',
30
+                data: JSON.stringify(params)
31
+            });
32
+
33
+            if (response.code === 200) {
34
+                $.modal.msgSuccess("个人级别检查提交成功");
35
+                return response.data;
36
+            } else {
37
+                $.modal.msgError(response.msg);
38
+                return null;
39
+            }
40
+        } catch (error) {
41
+            $.modal.msgError("提交失败:" + error.responseText);
42
+            return null;
43
+        }
44
+    }
45
+
46
+    /**
47
+     * 2. 提交科级检查
48
+     */
49
+    async submitSectionCheck(checkData) {
50
+        const params = {
51
+            id: checkData.id,
52
+            description: checkData.description,
53
+            checkType: checkData.checkType,
54
+            severity: checkData.severity,
55
+            targetDeptId: checkData.targetDeptId
56
+        };
57
+
58
+        try {
59
+            const response = await $.ajax({
60
+                url: `${this.baseURL}/section`,
61
+                type: 'POST',
62
+                contentType: 'application/json',
63
+                data: JSON.stringify(params)
64
+            });
65
+
66
+            if (response.code === 200) {
67
+                $.modal.msgSuccess("科级检查提交成功");
68
+                return response.data;
69
+            } else {
70
+                $.modal.msgError(response.msg);
71
+                return null;
72
+            }
73
+        } catch (error) {
74
+            $.modal.msgError("提交失败:" + error.responseText);
75
+            return null;
76
+        }
77
+    }
78
+
79
+    /**
80
+     * 3. 提交班组级检查
81
+     */
82
+    async submitGroupCheck(checkData) {
83
+        const params = {
84
+            id: checkData.id,
85
+            description: checkData.description,
86
+            checkType: checkData.checkType,
87
+            severity: checkData.severity,
88
+            targetDeptId: checkData.targetDeptId
89
+        };
90
+
91
+        try {
92
+            const response = await $.ajax({
93
+                url: `${this.baseURL}/group`,
94
+                type: 'POST',
95
+                contentType: 'application/json',
96
+                data: JSON.stringify(params)
97
+            });
98
+
99
+            if (response.code === 200) {
100
+                $.modal.msgSuccess("班组级检查提交成功");
101
+                return response.data;
102
+            } else {
103
+                $.modal.msgError(response.msg);
104
+                return null;
105
+            }
106
+        } catch (error) {
107
+            $.modal.msgError("提交失败:" + error.responseText);
108
+            return null;
109
+        }
110
+    }
111
+
112
+    /**
113
+     * 4. 提交查获上报
114
+     */
115
+    async submitSeizureReport(reportData) {
116
+        const params = {
117
+            id: reportData.id,
118
+            itemType: reportData.itemType,
119
+            quantity: reportData.quantity,
120
+            location: reportData.location,
121
+            seizureTime: reportData.seizureTime,
122
+            passengerInfo: reportData.passengerInfo
123
+        };
124
+
125
+        try {
126
+            const response = await $.ajax({
127
+                url: `${this.baseURL}/seizure/report`,
128
+                type: 'POST',
129
+                contentType: 'application/json',
130
+                data: JSON.stringify(params)
131
+            });
132
+
133
+            if (response.code === 200) {
134
+                $.modal.msgSuccess("查获上报提交成功");
135
+                return response.data;
136
+            } else {
137
+                $.modal.msgError(response.msg);
138
+                return null;
139
+            }
140
+        } catch (error) {
141
+            $.modal.msgError("提交失败:" + error.responseText);
142
+            return null;
143
+        }
144
+    }
145
+
146
+    /**
147
+     * 5. 审批任务(同意)
148
+     */
149
+    async approveTask(taskId, approvalData) {
150
+        const params = {
151
+            comment: approvalData.comment,
152
+            nodeType: approvalData.nodeType,
153
+            targetGroupId: approvalData.targetGroupId,
154
+            rectificationRequirement: approvalData.rectificationRequirement,
155
+            rectificationDetail: approvalData.rectificationDetail,
156
+            rectificationTime: approvalData.rectificationTime,
157
+            rectificationResult: approvalData.rectificationResult
158
+        };
159
+
160
+        try {
161
+            const response = await $.ajax({
162
+                url: `${this.baseURL}/approval/approve/${taskId}`,
163
+                type: 'PUT',
164
+                contentType: 'application/json',
165
+                data: JSON.stringify(params)
166
+            });
167
+
168
+            if (response.code === 200) {
169
+                $.modal.msgSuccess("审批成功");
170
+                return true;
171
+            } else {
172
+                $.modal.msgError(response.msg);
173
+                return false;
174
+            }
175
+        } catch (error) {
176
+            $.modal.msgError("审批失败:" + error.responseText);
177
+            return false;
178
+        }
179
+    }
180
+
181
+    /**
182
+     * 6. 驳回任务
183
+     */
184
+    async rejectTask(taskId, rejectReason) {
185
+        const params = {
186
+            rejectReason: rejectReason
187
+        };
188
+
189
+        try {
190
+            const response = await $.ajax({
191
+                url: `${this.baseURL}/approval/reject/${taskId}`,
192
+                type: 'PUT',
193
+                contentType: 'application/json',
194
+                data: JSON.stringify(params)
195
+            });
196
+
197
+            if (response.code === 200) {
198
+                $.modal.msgSuccess("驳回成功");
199
+                return true;
200
+            } else {
201
+                $.modal.msgError(response.msg);
202
+                return false;
203
+            }
204
+        } catch (error) {
205
+            $.modal.msgError("驳回失败:" + error.responseText);
206
+            return false;
207
+        }
208
+    }
209
+
210
+    /**
211
+     * 7. 获取待办任务列表
212
+     */
213
+    async getPendingTasks() {
214
+        try {
215
+            const response = await $.get(`${this.baseURL}/approval/tasks/pending`);
216
+            return response;
217
+        } catch (error) {
218
+            console.error("获取待办任务失败", error);
219
+            return { code: 500, rows: [] };
220
+        }
221
+    }
222
+
223
+    /**
224
+     * 8. 获取已办任务列表
225
+     */
226
+    async getCompletedTasks() {
227
+        try {
228
+            const response = await $.get(`${this.baseURL}/approval/tasks/completed`);
229
+            return response;
230
+        } catch (error) {
231
+            console.error("获取已办任务失败", error);
232
+            return { code: 500, rows: [] };
233
+        }
234
+    }
235
+
236
+    /**
237
+     * 9. 获取我发起的审批实例
238
+     */
239
+    async getSubmittedInstances(status = null) {
240
+        const url = status ? 
241
+            `${this.baseURL}/approval/instances/submitted?status=${status}` : 
242
+            `${this.baseURL}/approval/instances/submitted`;
243
+
244
+        try {
245
+            const response = await $.get(url);
246
+            return response;
247
+        } catch (error) {
248
+            console.error("获取提交实例失败", error);
249
+            return { code: 500, rows: [] };
250
+        }
251
+    }
252
+
253
+    /**
254
+     * 10. 取消审批流程
255
+     */
256
+    async cancelProcess(instanceId, comment) {
257
+        try {
258
+            const response = await $.ajax({
259
+                url: `${this.baseURL}/approval/cancel/${instanceId}?comment=${encodeURIComponent(comment)}`,
260
+                type: 'PUT'
261
+            });
262
+
263
+            if (response.code === 200) {
264
+                $.modal.msgSuccess("取消成功");
265
+                return true;
266
+            } else {
267
+                $.modal.msgError(response.msg);
268
+                return false;
269
+            }
270
+        } catch (error) {
271
+            $.modal.msgError("取消失败:" + error.responseText);
272
+            return false;
273
+        }
274
+    }
275
+
276
+    /**
277
+     * 11. 获取待办任务数量
278
+     */
279
+    async getPendingTasksCount() {
280
+        try {
281
+            const response = await $.get(`${this.baseURL}/approval/tasks/pending/count`);
282
+            return response.code === 200 ? response.data : 0;
283
+        } catch (error) {
284
+            console.error("获取待办数量失败", error);
285
+            return 0;
286
+        }
287
+    }
288
+}
289
+
290
+// 审批流程页面管理类
291
+class ApprovalPageManager {
292
+    constructor() {
293
+        this.api = new ApprovalAPI();
294
+    }
295
+
296
+    /**
297
+     * 初始化待办任务页面
298
+     */
299
+    async initPendingTasksPage() {
300
+        const response = await this.api.getPendingTasks();
301
+        if (response.code === 200) {
302
+            this.renderTaskList(response.rows, 'pending');
303
+        }
304
+    }
305
+
306
+    /**
307
+     * 初始化已办任务页面
308
+     */
309
+    async initCompletedTasksPage() {
310
+        const response = await this.api.getCompletedTasks();
311
+        if (response.code === 200) {
312
+            this.renderTaskList(response.rows, 'completed');
313
+        }
314
+    }
315
+
316
+    /**
317
+     * 渲染任务列表
318
+     */
319
+    renderTaskList(tasks, type) {
320
+        const tbody = $(`#${type}TasksTable tbody`);
321
+        tbody.empty();
322
+
323
+        tasks.forEach(task => {
324
+            const row = this.createTaskRow(task, type);
325
+            tbody.append(row);
326
+        });
327
+    }
328
+
329
+    /**
330
+     * 创建任务行
331
+     */
332
+    createTaskRow(task, type) {
333
+        const actions = type === 'pending' ? 
334
+            `<button class="btn btn-success btn-sm" onclick="approvalManager.showApprovalModal(${task.id})">审批</button>
335
+             <button class="btn btn-danger btn-sm" onclick="approvalManager.showRejectModal(${task.id})">驳回</button>` :
336
+            `<span class="badge ${task.status === 'APPROVED' ? 'badge-success' : 'badge-danger'}">${task.status}</span>`;
337
+
338
+        return `
339
+            <tr>
340
+                <td>${task.taskNo}</td>
341
+                <td>${task.taskName}</td>
342
+                <td>${task.instance?.title || ''}</td>
343
+                <td>${task.assigneeName}</td>
344
+                <td>${task.assignTime}</td>
345
+                <td>${actions}</td>
346
+            </tr>
347
+        `;
348
+    }
349
+
350
+    /**
351
+     * 显示审批模态框
352
+     */
353
+    showApprovalModal(taskId) {
354
+        $('#approvalTaskId').val(taskId);
355
+        $('#approvalModal').modal('show');
356
+    }
357
+
358
+    /**
359
+     * 显示驳回模态框
360
+     */
361
+    showRejectModal(taskId) {
362
+        $('#rejectTaskId').val(taskId);
363
+        $('#rejectModal').modal('show');
364
+    }
365
+
366
+    /**
367
+     * 处理审批
368
+     */
369
+    async handleApproval() {
370
+        const taskId = $('#approvalTaskId').val();
371
+        const comment = $('#approvalComment').val();
372
+        const nodeType = $('#approvalNodeType').val();
373
+        
374
+        const approvalData = {
375
+            comment: comment,
376
+            nodeType: nodeType
377
+        };
378
+
379
+        // 根据节点类型添加额外数据
380
+        if (nodeType === 'SECTION_LEADER') {
381
+            approvalData.targetGroupId = $('#targetGroupId').val();
382
+            approvalData.rectificationRequirement = $('#rectificationRequirement').val();
383
+        } else if (nodeType === 'GROUP_LEADER_RECTIFY') {
384
+            approvalData.rectificationDetail = $('#rectificationDetail').val();
385
+            approvalData.rectificationTime = $('#rectificationTime').val();
386
+            approvalData.rectificationResult = $('#rectificationResult').val();
387
+        }
388
+
389
+        const success = await this.api.approveTask(taskId, approvalData);
390
+        if (success) {
391
+            $('#approvalModal').modal('hide');
392
+            this.initPendingTasksPage(); // 刷新列表
393
+        }
394
+    }
395
+
396
+    /**
397
+     * 处理驳回
398
+     */
399
+    async handleReject() {
400
+        const taskId = $('#rejectTaskId').val();
401
+        const rejectReason = $('#rejectReason').val();
402
+
403
+        const success = await this.api.rejectTask(taskId, rejectReason);
404
+        if (success) {
405
+            $('#rejectModal').modal('hide');
406
+            this.initPendingTasksPage(); // 刷新列表
407
+        }
408
+    }
409
+
410
+    /**
411
+     * 更新待办数量徽章
412
+     */
413
+    async updatePendingBadge() {
414
+        const count = await this.api.getPendingTasksCount();
415
+        $('.pending-count-badge').text(count);
416
+    }
417
+}
418
+
419
+// 全局实例
420
+const approvalManager = new ApprovalPageManager();
421
+
422
+// 页面加载完成后初始化
423
+$(document).ready(function() {
424
+    // 定时更新待办数量
425
+    approvalManager.updatePendingBadge();
426
+    setInterval(() => {
427
+        approvalManager.updatePendingBadge();
428
+    }, 30000); // 30秒更新一次
429
+});
430
+
431
+// 使用示例
432
+/*
433
+// 1. 提交个人级别检查
434
+const personalCheckData = {
435
+    id: 1,
436
+    description: '发现个人违规行为',
437
+    checkType: 'PERSONAL_VIOLATION',
438
+    location: 'A区域安检口',
439
+    targetUserIds: [10, 11, 12]
440
+};
441
+approvalManager.api.submitPersonalCheck(personalCheckData);
442
+
443
+// 2. 提交科级检查
444
+const sectionCheckData = {
445
+    id: 2,
446
+    description: '发现科级管理问题',
447
+    checkType: 'MANAGEMENT_ISSUE',
448
+    severity: 'HIGH',
449
+    targetDeptId: 100
450
+};
451
+approvalManager.api.submitSectionCheck(sectionCheckData);
452
+
453
+// 3. 提交查获上报
454
+const seizureData = {
455
+    id: 3,
456
+    itemType: '危险品',
457
+    quantity: 1,
458
+    location: 'B区域',
459
+    seizureTime: new Date(),
460
+    passengerInfo: '张三,身份证:123456'
461
+};
462
+approvalManager.api.submitSeizureReport(seizureData);
463
+
464
+// 4. 审批任务
465
+const approvalData = {
466
+    comment: '同意处理',
467
+    nodeType: 'SECTION_LEADER',
468
+    targetGroupId: 200,
469
+    rectificationRequirement: '请在3天内完成整改'
470
+};
471
+approvalManager.api.approveTask(1, approvalData);
472
+
473
+// 5. 驳回任务
474
+approvalManager.api.rejectTask(1, '信息不完整,请补充详细情况');
475
+*/

+ 551 - 0
airport-admin/src/main/resources/static/approval-page-example.html

@@ -0,0 +1,551 @@
1
+<!DOCTYPE html>
2
+<html lang="zh">
3
+<head>
4
+    <meta charset="UTF-8">
5
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+    <title>审批流程管理</title>
7
+    <link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/bootstrap/4.6.1/css/bootstrap.min.css">
8
+    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
9
+    <script src="https://cdn.bootcdn.net/ajax/libs/bootstrap/4.6.1/js/bootstrap.bundle.min.js"></script>
10
+    <style>
11
+        .nav-tabs .nav-link.active {
12
+            background-color: #007bff;
13
+            color: white;
14
+        }
15
+        .badge-count {
16
+            position: absolute;
17
+            top: -8px;
18
+            right: -8px;
19
+            background: #dc3545;
20
+            color: white;
21
+            border-radius: 10px;
22
+            padding: 2px 6px;
23
+            font-size: 12px;
24
+        }
25
+        .task-item {
26
+            border-left: 4px solid #007bff;
27
+            margin-bottom: 10px;
28
+        }
29
+        .task-item.urgent {
30
+            border-left-color: #dc3545;
31
+        }
32
+    </style>
33
+</head>
34
+<body>
35
+    <div class="container-fluid mt-3">
36
+        <div class="row">
37
+            <div class="col-12">
38
+                <h2>审批流程管理</h2>
39
+                
40
+                <!-- 导航标签页 -->
41
+                <ul class="nav nav-tabs" id="approvalTabs" role="tablist">
42
+                    <li class="nav-item" role="presentation">
43
+                        <a class="nav-link active" id="pending-tab" data-toggle="tab" href="#pending" role="tab">
44
+                            待办任务 <span class="badge badge-danger pending-count-badge">0</span>
45
+                        </a>
46
+                    </li>
47
+                    <li class="nav-item" role="presentation">
48
+                        <a class="nav-link" id="completed-tab" data-toggle="tab" href="#completed" role="tab">
49
+                            已办任务
50
+                        </a>
51
+                    </li>
52
+                    <li class="nav-item" role="presentation">
53
+                        <a class="nav-link" id="submitted-tab" data-toggle="tab" href="#submitted" role="tab">
54
+                            我的申请
55
+                        </a>
56
+                    </li>
57
+                    <li class="nav-item" role="presentation">
58
+                        <a class="nav-link" id="submit-tab" data-toggle="tab" href="#submit" role="tab">
59
+                            发起申请
60
+                        </a>
61
+                    </li>
62
+                </ul>
63
+
64
+                <!-- 标签页内容 -->
65
+                <div class="tab-content" id="approvalTabContent">
66
+                    <!-- 待办任务 -->
67
+                    <div class="tab-pane fade show active" id="pending" role="tabpanel">
68
+                        <div class="card mt-3">
69
+                            <div class="card-header">
70
+                                <h5>待办任务列表</h5>
71
+                                <button class="btn btn-primary btn-sm float-right" onclick="approvalManager.initPendingTasksPage()">
72
+                                    刷新
73
+                                </button>
74
+                            </div>
75
+                            <div class="card-body">
76
+                                <div class="table-responsive">
77
+                                    <table class="table table-striped" id="pendingTasksTable">
78
+                                        <thead>
79
+                                            <tr>
80
+                                                <th>任务编号</th>
81
+                                                <th>任务名称</th>
82
+                                                <th>申请标题</th>
83
+                                                <th>分配时间</th>
84
+                                                <th>紧急程度</th>
85
+                                                <th>操作</th>
86
+                                            </tr>
87
+                                        </thead>
88
+                                        <tbody>
89
+                                            <!-- 动态生成 -->
90
+                                        </tbody>
91
+                                    </table>
92
+                                </div>
93
+                            </div>
94
+                        </div>
95
+                    </div>
96
+
97
+                    <!-- 已办任务 -->
98
+                    <div class="tab-pane fade" id="completed" role="tabpanel">
99
+                        <div class="card mt-3">
100
+                            <div class="card-header">
101
+                                <h5>已办任务列表</h5>
102
+                                <button class="btn btn-primary btn-sm float-right" onclick="approvalManager.initCompletedTasksPage()">
103
+                                    刷新
104
+                                </button>
105
+                            </div>
106
+                            <div class="card-body">
107
+                                <div class="table-responsive">
108
+                                    <table class="table table-striped" id="completedTasksTable">
109
+                                        <thead>
110
+                                            <tr>
111
+                                                <th>任务编号</th>
112
+                                                <th>任务名称</th>
113
+                                                <th>申请标题</th>
114
+                                                <th>完成时间</th>
115
+                                                <th>处理结果</th>
116
+                                            </tr>
117
+                                        </thead>
118
+                                        <tbody>
119
+                                            <!-- 动态生成 -->
120
+                                        </tbody>
121
+                                    </table>
122
+                                </div>
123
+                            </div>
124
+                        </div>
125
+                    </div>
126
+
127
+                    <!-- 我的申请 -->
128
+                    <div class="tab-pane fade" id="submitted" role="tabpanel">
129
+                        <div class="card mt-3">
130
+                            <div class="card-header">
131
+                                <h5>我的申请列表</h5>
132
+                                <button class="btn btn-primary btn-sm float-right" onclick="approvalManager.initSubmittedInstancesPage()">
133
+                                    刷新
134
+                                </button>
135
+                            </div>
136
+                            <div class="card-body">
137
+                                <div class="table-responsive">
138
+                                    <table class="table table-striped" id="submittedInstancesTable">
139
+                                        <thead>
140
+                                            <tr>
141
+                                                <th>申请编号</th>
142
+                                                <th>申请标题</th>
143
+                                                <th>申请类型</th>
144
+                                                <th>提交时间</th>
145
+                                                <th>状态</th>
146
+                                                <th>操作</th>
147
+                                            </tr>
148
+                                        </thead>
149
+                                        <tbody>
150
+                                            <!-- 动态生成 -->
151
+                                        </tbody>
152
+                                    </table>
153
+                                </div>
154
+                            </div>
155
+                        </div>
156
+                    </div>
157
+
158
+                    <!-- 发起申请 -->
159
+                    <div class="tab-pane fade" id="submit" role="tabpanel">
160
+                        <div class="row mt-3">
161
+                            <!-- 个人级别检查 -->
162
+                            <div class="col-md-6">
163
+                                <div class="card">
164
+                                    <div class="card-header">
165
+                                        <h5>个人级别检查</h5>
166
+                                    </div>
167
+                                    <div class="card-body">
168
+                                        <form id="personalCheckForm">
169
+                                            <div class="form-group">
170
+                                                <label>检查描述</label>
171
+                                                <textarea class="form-control" name="description" required></textarea>
172
+                                            </div>
173
+                                            <div class="form-group">
174
+                                                <label>检查类型</label>
175
+                                                <select class="form-control" name="checkType">
176
+                                                    <option value="PERSONAL_VIOLATION">个人违规</option>
177
+                                                    <option value="EQUIPMENT_ISSUE">设备问题</option>
178
+                                                    <option value="PROCEDURE_ISSUE">流程问题</option>
179
+                                                </select>
180
+                                            </div>
181
+                                            <div class="form-group">
182
+                                                <label>检查位置</label>
183
+                                                <input type="text" class="form-control" name="location">
184
+                                            </div>
185
+                                            <div class="form-group">
186
+                                                <label>通知人员ID(逗号分隔)</label>
187
+                                                <input type="text" class="form-control" name="targetUserIds" placeholder="如:10,11,12">
188
+                                            </div>
189
+                                            <button type="submit" class="btn btn-primary">提交</button>
190
+                                        </form>
191
+                                    </div>
192
+                                </div>
193
+                            </div>
194
+
195
+                            <!-- 科级检查 -->
196
+                            <div class="col-md-6">
197
+                                <div class="card">
198
+                                    <div class="card-header">
199
+                                        <h5>科级检查</h5>
200
+                                    </div>
201
+                                    <div class="card-body">
202
+                                        <form id="sectionCheckForm">
203
+                                            <div class="form-group">
204
+                                                <label>检查描述</label>
205
+                                                <textarea class="form-control" name="description" required></textarea>
206
+                                            </div>
207
+                                            <div class="form-group">
208
+                                                <label>检查类型</label>
209
+                                                <select class="form-control" name="checkType">
210
+                                                    <option value="MANAGEMENT_ISSUE">管理问题</option>
211
+                                                    <option value="SECURITY_BREACH">安全违规</option>
212
+                                                    <option value="PROCESS_VIOLATION">流程违规</option>
213
+                                                </select>
214
+                                            </div>
215
+                                            <div class="form-group">
216
+                                                <label>严重程度</label>
217
+                                                <select class="form-control" name="severity">
218
+                                                    <option value="LOW">低</option>
219
+                                                    <option value="MEDIUM">中</option>
220
+                                                    <option value="HIGH">高</option>
221
+                                                    <option value="CRITICAL">紧急</option>
222
+                                                </select>
223
+                                            </div>
224
+                                            <div class="form-group">
225
+                                                <label>目标部门ID</label>
226
+                                                <input type="number" class="form-control" name="targetDeptId">
227
+                                            </div>
228
+                                            <button type="submit" class="btn btn-warning">提交</button>
229
+                                        </form>
230
+                                    </div>
231
+                                </div>
232
+                            </div>
233
+
234
+                            <!-- 查获上报 -->
235
+                            <div class="col-md-6 mt-3">
236
+                                <div class="card">
237
+                                    <div class="card-header">
238
+                                        <h5>查获上报</h5>
239
+                                    </div>
240
+                                    <div class="card-body">
241
+                                        <form id="seizureReportForm">
242
+                                            <div class="form-group">
243
+                                                <label>物品类型</label>
244
+                                                <input type="text" class="form-control" name="itemType" required>
245
+                                            </div>
246
+                                            <div class="form-group">
247
+                                                <label>数量</label>
248
+                                                <input type="number" class="form-control" name="quantity">
249
+                                            </div>
250
+                                            <div class="form-group">
251
+                                                <label>查获位置</label>
252
+                                                <input type="text" class="form-control" name="location">
253
+                                            </div>
254
+                                            <div class="form-group">
255
+                                                <label>旅客信息</label>
256
+                                                <textarea class="form-control" name="passengerInfo"></textarea>
257
+                                            </div>
258
+                                            <button type="submit" class="btn btn-danger">提交</button>
259
+                                        </form>
260
+                                    </div>
261
+                                </div>
262
+                            </div>
263
+
264
+                            <!-- 班组级检查 -->
265
+                            <div class="col-md-6 mt-3">
266
+                                <div class="card">
267
+                                    <div class="card-header">
268
+                                        <h5>班组级检查</h5>
269
+                                    </div>
270
+                                    <div class="card-body">
271
+                                        <form id="groupCheckForm">
272
+                                            <div class="form-group">
273
+                                                <label>检查描述</label>
274
+                                                <textarea class="form-control" name="description" required></textarea>
275
+                                            </div>
276
+                                            <div class="form-group">
277
+                                                <label>检查类型</label>
278
+                                                <select class="form-control" name="checkType">
279
+                                                    <option value="TEAM_MANAGEMENT">班组管理</option>
280
+                                                    <option value="WORKFLOW_ISSUE">工作流程</option>
281
+                                                    <option value="TEAM_COORDINATION">团队协作</option>
282
+                                                </select>
283
+                                            </div>
284
+                                            <div class="form-group">
285
+                                                <label>严重程度</label>
286
+                                                <select class="form-control" name="severity">
287
+                                                    <option value="LOW">低</option>
288
+                                                    <option value="MEDIUM">中</option>
289
+                                                    <option value="HIGH">高</option>
290
+                                                </select>
291
+                                            </div>
292
+                                            <div class="form-group">
293
+                                                <label>目标部门ID</label>
294
+                                                <input type="number" class="form-control" name="targetDeptId">
295
+                                            </div>
296
+                                            <button type="submit" class="btn btn-info">提交</button>
297
+                                        </form>
298
+                                    </div>
299
+                                </div>
300
+                            </div>
301
+                        </div>
302
+                    </div>
303
+                </div>
304
+            </div>
305
+        </div>
306
+    </div>
307
+
308
+    <!-- 审批模态框 -->
309
+    <div class="modal fade" id="approvalModal" tabindex="-1" role="dialog">
310
+        <div class="modal-dialog modal-lg" role="document">
311
+            <div class="modal-content">
312
+                <div class="modal-header">
313
+                    <h5 class="modal-title">任务审批</h5>
314
+                    <button type="button" class="close" data-dismiss="modal">
315
+                        <span>&times;</span>
316
+                    </button>
317
+                </div>
318
+                <div class="modal-body">
319
+                    <input type="hidden" id="approvalTaskId">
320
+                    <div class="form-group">
321
+                        <label>审批意见</label>
322
+                        <textarea class="form-control" id="approvalComment" rows="3" required></textarea>
323
+                    </div>
324
+                    <div class="form-group">
325
+                        <label>节点类型</label>
326
+                        <select class="form-control" id="approvalNodeType">
327
+                            <option value="">请选择</option>
328
+                            <option value="SECTION_LEADER">科长节点</option>
329
+                            <option value="GROUP_LEADER_RECTIFY">班组长整改</option>
330
+                            <option value="QUALITY_REVIEW">质检科审批</option>
331
+                        </select>
332
+                    </div>
333
+                    
334
+                    <!-- 科长节点额外字段 -->
335
+                    <div id="sectionLeaderFields" style="display:none;">
336
+                        <div class="form-group">
337
+                            <label>整改班组ID</label>
338
+                            <input type="number" class="form-control" id="targetGroupId">
339
+                        </div>
340
+                        <div class="form-group">
341
+                            <label>整改要求</label>
342
+                            <textarea class="form-control" id="rectificationRequirement" rows="2"></textarea>
343
+                        </div>
344
+                    </div>
345
+                    
346
+                    <!-- 班组长整改节点额外字段 -->
347
+                    <div id="groupLeaderFields" style="display:none;">
348
+                        <div class="form-group">
349
+                            <label>整改详情</label>
350
+                            <textarea class="form-control" id="rectificationDetail" rows="3"></textarea>
351
+                        </div>
352
+                        <div class="form-group">
353
+                            <label>整改时间</label>
354
+                            <input type="datetime-local" class="form-control" id="rectificationTime">
355
+                        </div>
356
+                        <div class="form-group">
357
+                            <label>整改结果</label>
358
+                            <textarea class="form-control" id="rectificationResult" rows="2"></textarea>
359
+                        </div>
360
+                    </div>
361
+                </div>
362
+                <div class="modal-footer">
363
+                    <button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
364
+                    <button type="button" class="btn btn-success" onclick="approvalManager.handleApproval()">确认审批</button>
365
+                </div>
366
+            </div>
367
+        </div>
368
+    </div>
369
+
370
+    <!-- 驳回模态框 -->
371
+    <div class="modal fade" id="rejectModal" tabindex="-1" role="dialog">
372
+        <div class="modal-dialog" role="document">
373
+            <div class="modal-content">
374
+                <div class="modal-header">
375
+                    <h5 class="modal-title">任务驳回</h5>
376
+                    <button type="button" class="close" data-dismiss="modal">
377
+                        <span>&times;</span>
378
+                    </button>
379
+                </div>
380
+                <div class="modal-body">
381
+                    <input type="hidden" id="rejectTaskId">
382
+                    <div class="form-group">
383
+                        <label>驳回理由</label>
384
+                        <textarea class="form-control" id="rejectReason" rows="4" required placeholder="请说明驳回理由..."></textarea>
385
+                    </div>
386
+                </div>
387
+                <div class="modal-footer">
388
+                    <button type="button" class="btn btn-secondary" data-dismiss="modal">取消</button>
389
+                    <button type="button" class="btn btn-danger" onclick="approvalManager.handleReject()">确认驳回</button>
390
+                </div>
391
+            </div>
392
+        </div>
393
+    </div>
394
+
395
+    <!-- 包含审批API脚本 -->
396
+    <script src="approval-frontend-example.js"></script>
397
+    
398
+    <script>
399
+        // 页面初始化
400
+        $(document).ready(function() {
401
+            // 扩展审批管理器
402
+            approvalManager.initSubmittedInstancesPage = async function() {
403
+                const response = await this.api.getSubmittedInstances();
404
+                if (response.code === 200) {
405
+                    this.renderInstanceList(response.rows);
406
+                }
407
+            };
408
+
409
+            approvalManager.renderInstanceList = function(instances) {
410
+                const tbody = $('#submittedInstancesTable tbody');
411
+                tbody.empty();
412
+
413
+                instances.forEach(instance => {
414
+                    const statusBadge = this.getStatusBadge(instance.status);
415
+                    const actions = instance.status === 'RUNNING' ? 
416
+                        `<button class="btn btn-warning btn-sm" onclick="approvalManager.cancelInstance(${instance.id})">取消</button>` :
417
+                        '';
418
+
419
+                    const row = `
420
+                        <tr>
421
+                            <td>${instance.instanceNo}</td>
422
+                            <td>${instance.title}</td>
423
+                            <td>${instance.businessType}</td>
424
+                            <td>${instance.submitTime}</td>
425
+                            <td>${statusBadge}</td>
426
+                            <td>${actions}</td>
427
+                        </tr>
428
+                    `;
429
+                    tbody.append(row);
430
+                });
431
+            };
432
+
433
+            approvalManager.getStatusBadge = function(status) {
434
+                const statusMap = {
435
+                    'RUNNING': '<span class="badge badge-primary">运行中</span>',
436
+                    'COMPLETED': '<span class="badge badge-success">已完成</span>',
437
+                    'REJECTED': '<span class="badge badge-danger">已驳回</span>',
438
+                    'CANCELLED': '<span class="badge badge-secondary">已取消</span>'
439
+                };
440
+                return statusMap[status] || status;
441
+            };
442
+
443
+            approvalManager.cancelInstance = async function(instanceId) {
444
+                if (!confirm('确定要取消这个申请吗?')) return;
445
+                
446
+                const comment = prompt('请输入取消理由:');
447
+                if (!comment) return;
448
+                
449
+                const success = await this.api.cancelProcess(instanceId, comment);
450
+                if (success) {
451
+                    this.initSubmittedInstancesPage();
452
+                }
453
+            };
454
+
455
+            // 节点类型变化事件
456
+            $('#approvalNodeType').change(function() {
457
+                const nodeType = $(this).val();
458
+                $('#sectionLeaderFields, #groupLeaderFields').hide();
459
+                
460
+                if (nodeType === 'SECTION_LEADER') {
461
+                    $('#sectionLeaderFields').show();
462
+                } else if (nodeType === 'GROUP_LEADER_RECTIFY') {
463
+                    $('#groupLeaderFields').show();
464
+                }
465
+            });
466
+
467
+            // 表单提交事件
468
+            $('#personalCheckForm').submit(async function(e) {
469
+                e.preventDefault();
470
+                const formData = new FormData(this);
471
+                const data = {
472
+                    id: Date.now(), // 模拟ID
473
+                    description: formData.get('description'),
474
+                    checkType: formData.get('checkType'),
475
+                    location: formData.get('location'),
476
+                    targetUserIds: formData.get('targetUserIds').split(',').map(id => parseInt(id.trim()))
477
+                };
478
+                
479
+                await approvalManager.api.submitPersonalCheck(data);
480
+                this.reset();
481
+            });
482
+
483
+            $('#sectionCheckForm').submit(async function(e) {
484
+                e.preventDefault();
485
+                const formData = new FormData(this);
486
+                const data = {
487
+                    id: Date.now(),
488
+                    description: formData.get('description'),
489
+                    checkType: formData.get('checkType'),
490
+                    severity: formData.get('severity'),
491
+                    targetDeptId: parseInt(formData.get('targetDeptId'))
492
+                };
493
+                
494
+                await approvalManager.api.submitSectionCheck(data);
495
+                this.reset();
496
+            });
497
+
498
+            $('#groupCheckForm').submit(async function(e) {
499
+                e.preventDefault();
500
+                const formData = new FormData(this);
501
+                const data = {
502
+                    id: Date.now(),
503
+                    description: formData.get('description'),
504
+                    checkType: formData.get('checkType'),
505
+                    severity: formData.get('severity'),
506
+                    targetDeptId: parseInt(formData.get('targetDeptId'))
507
+                };
508
+                
509
+                await approvalManager.api.submitGroupCheck(data);
510
+                this.reset();
511
+            });
512
+
513
+            $('#seizureReportForm').submit(async function(e) {
514
+                e.preventDefault();
515
+                const formData = new FormData(this);
516
+                const data = {
517
+                    id: Date.now(),
518
+                    itemType: formData.get('itemType'),
519
+                    quantity: parseInt(formData.get('quantity')),
520
+                    location: formData.get('location'),
521
+                    seizureTime: new Date(),
522
+                    passengerInfo: formData.get('passengerInfo')
523
+                };
524
+                
525
+                await approvalManager.api.submitSeizureReport(data);
526
+                this.reset();
527
+            });
528
+
529
+            // 标签页切换事件
530
+            $('#approvalTabs a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
531
+                const target = $(e.target).attr("href");
532
+                
533
+                switch(target) {
534
+                    case '#pending':
535
+                        approvalManager.initPendingTasksPage();
536
+                        break;
537
+                    case '#completed':
538
+                        approvalManager.initCompletedTasksPage();
539
+                        break;
540
+                    case '#submitted':
541
+                        approvalManager.initSubmittedInstancesPage();
542
+                        break;
543
+                }
544
+            });
545
+
546
+            // 初始化页面
547
+            approvalManager.initPendingTasksPage();
548
+        });
549
+    </script>
550
+</body>
551
+</html>