Browse Source

证书管理定时任务和新增修改删除动态更新到期日期、到期天数、状态,并同步用户资质等级

wangxx 1 month ago
parent
commit
9411e171e2

+ 36 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/ledger/LedgerCertificateInfoController.java

@@ -76,6 +76,10 @@ public class LedgerCertificateInfoController extends BaseController {
76 76
     @Log(title = "证书信息", businessType = BusinessType.INSERT)
77 77
     @PostMapping
78 78
     public AjaxResult add(@RequestBody LedgerCertificateInfo ledgerCertificateInfo) {
79
+        String errorMsg = validateCertificateInfo(ledgerCertificateInfo);
80
+        if (errorMsg != null) {
81
+            return error(errorMsg);
82
+        }
79 83
         return toAjax(certificateInfoService.insertCertificateInfo(ledgerCertificateInfo));
80 84
     }
81 85
 
@@ -86,6 +90,10 @@ public class LedgerCertificateInfoController extends BaseController {
86 90
     @Log(title = "证书信息", businessType = BusinessType.UPDATE)
87 91
     @PutMapping
88 92
     public AjaxResult edit(@RequestBody LedgerCertificateInfo ledgerCertificateInfo) {
93
+        String errorMsg = validateCertificateInfo(ledgerCertificateInfo);
94
+        if (errorMsg != null) {
95
+            return error(errorMsg);
96
+        }
89 97
         return toAjax(certificateInfoService.updateCertificateInfo(ledgerCertificateInfo));
90 98
     }
91 99
 
@@ -129,4 +137,32 @@ public class LedgerCertificateInfoController extends BaseController {
129 137
         ExcelUtil<LedgerCertificateInfo> util = new ExcelUtil<LedgerCertificateInfo>(LedgerCertificateInfo.class);
130 138
         util.importTemplateExcel(response, "证书信息数据");
131 139
     }
140
+
141
+    /**
142
+     * 校验证书信息必填参数
143
+     * 
144
+     * @param cert 证书信息
145
+     * @return 校验失败返回错误信息,校验通过返回null
146
+     */
147
+    private String validateCertificateInfo(LedgerCertificateInfo cert) {
148
+        if (cert == null) {
149
+            return "证书信息不能为空";
150
+        }
151
+        if (cert.getPersonName() == null || cert.getPersonName().trim().isEmpty()) {
152
+            return "姓名不能为空";
153
+        }
154
+        if (cert.getCertLevel() == null || cert.getCertLevel().trim().isEmpty()) {
155
+            return "证书等级不能为空";
156
+        }
157
+        if (cert.getIssueDate() == null) {
158
+            return "发证日期不能为空";
159
+        }
160
+        if (cert.getServiceMonths() == null) {
161
+            return "服务期(月)不能为空";
162
+        }
163
+        if (cert.getReviewRecords() == null || cert.getReviewRecords().isEmpty()) {
164
+            return "复审记录不能为空";
165
+        }
166
+        return null;
167
+    }
132 168
 }

+ 16 - 6
airport-ledger/src/main/java/com/sundot/airport/ledger/domain/LedgerCertificateInfo.java

@@ -108,7 +108,7 @@ public class LedgerCertificateInfo extends BaseEntity {
108 108
      * 评定成绩
109 109
      */
110 110
     @Excel(name = "评定成绩")
111
-    private BigDecimal evalScore;
111
+    private String evalScore;
112 112
 
113 113
     /**
114 114
      * 用工形式
@@ -126,19 +126,16 @@ public class LedgerCertificateInfo extends BaseEntity {
126 126
      * 到期日期
127 127
      */
128 128
     @JsonFormat(pattern = "yyyy-MM-dd")
129
-    @Excel(name = "到期日期", width = 30, dateFormat = "yyyy-MM-dd")
130 129
     private Date expiryDate;
131 130
 
132 131
     /**
133 132
      * 状态
134 133
      */
135
-    @Excel(name = "状态")
136 134
     private String status;
137 135
 
138 136
     /**
139 137
      * 到期天数
140 138
      */
141
-    @Excel(name = "到期天数")
142 139
     private Integer expireDays;
143 140
 
144 141
     /**
@@ -148,6 +145,11 @@ public class LedgerCertificateInfo extends BaseEntity {
148 145
     private String remark;
149 146
 
150 147
     /**
148
+     * 删除标志(0代表存在 2代表删除)
149
+     */
150
+    private String delFlag;
151
+
152
+    /**
151 153
      * 复审记录列表(非数据库字段)
152 154
      */
153 155
     @TableField(exist = false)
@@ -257,11 +259,11 @@ public class LedgerCertificateInfo extends BaseEntity {
257 259
         return practiceScore;
258 260
     }
259 261
 
260
-    public void setEvalScore(BigDecimal evalScore) {
262
+    public void setEvalScore(String evalScore) {
261 263
         this.evalScore = evalScore;
262 264
     }
263 265
 
264
-    public BigDecimal getEvalScore() {
266
+    public String getEvalScore() {
265 267
         return evalScore;
266 268
     }
267 269
 
@@ -313,6 +315,14 @@ public class LedgerCertificateInfo extends BaseEntity {
313 315
         return remark;
314 316
     }
315 317
 
318
+    public void setDelFlag(String delFlag) {
319
+        this.delFlag = delFlag;
320
+    }
321
+
322
+    public String getDelFlag() {
323
+        return delFlag;
324
+    }
325
+
316 326
     public void setReviewRecords(List<LedgerCertificateReviewRecord> reviewRecords) {
317 327
         this.reviewRecords = reviewRecords;
318 328
     }

+ 14 - 1
airport-ledger/src/main/java/com/sundot/airport/ledger/domain/LedgerCertificateReviewRecord.java

@@ -12,7 +12,7 @@ import org.apache.commons.lang3.builder.ToStringBuilder;
12 12
 import org.apache.commons.lang3.builder.ToStringStyle;
13 13
 
14 14
 /**
15
- * 证书复审记录对象 certificate_review_record
15
+ * 证书复审记录对象 ledger_certificate_review_record
16 16
  *
17 17
  * @author ruoyi
18 18
  * @date 2026-06-29
@@ -52,6 +52,11 @@ public class LedgerCertificateReviewRecord extends BaseEntity {
52 52
     @Excel(name = "备注")
53 53
     private String remark;
54 54
 
55
+    /**
56
+     * 删除标志(0代表存在 2代表删除)
57
+     */
58
+    private String delFlag;
59
+
55 60
     public void setId(Long id) {
56 61
         this.id = id;
57 62
     }
@@ -84,11 +89,18 @@ public class LedgerCertificateReviewRecord extends BaseEntity {
84 89
         return reviewTime;
85 90
     }
86 91
 
92
+    public void setDelFlag(String delFlag) {
93
+        this.delFlag = delFlag;
94
+    }
87 95
 
96
+    public String getDelFlag() {
97
+        return delFlag;
98
+    }
88 99
     public void setRemark(String remark) {
89 100
         this.remark = remark;
90 101
     }
91 102
 
103
+
92 104
     public String getRemark() {
93 105
         return remark;
94 106
     }
@@ -101,6 +113,7 @@ public class LedgerCertificateReviewRecord extends BaseEntity {
101 113
                 .append("reviewSeq", getReviewSeq())
102 114
                 .append("reviewTime", getReviewTime())
103 115
                 .append("remark", getRemark())
116
+                .append("delFlag", getDelFlag())
104 117
                 .toString();
105 118
     }
106 119
 }

+ 0 - 231
airport-ledger/src/main/java/com/sundot/airport/ledger/dto/LedgerCertificateImportDTO.java

@@ -1,231 +0,0 @@
1
-package com.sundot.airport.ledger.dto;
2
-
3
-import java.util.Date;
4
-
5
-import com.sundot.airport.common.annotation.Excel;
6
-import com.sundot.airport.common.core.domain.BaseEntity;
7
-
8
-/**
9
- * 证书信息导入对象(用于Excel导入)
10
- * 
11
- * @author ruoyi
12
- * @date 2026-06-29
13
- */
14
-public class LedgerCertificateImportDTO extends BaseEntity {
15
-    
16
-    @Excel(name = "序号")
17
-    private Integer rowNum;
18
-    
19
-    @Excel(name = "单位")
20
-    private String unitName;
21
-    
22
-    @Excel(name = "姓名")
23
-    private String personName;
24
-    
25
-    @Excel(name = "性别")
26
-    private String gender;
27
-    
28
-    @Excel(name = "出生日期", dateFormat = "yyyy-MM-dd")
29
-    private Date birthDate;
30
-    
31
-    @Excel(name = "身份证号")
32
-    private String idCard;
33
-    
34
-    @Excel(name = "证书等级")
35
-    private String certLevel;
36
-    
37
-    @Excel(name = "发证日期", dateFormat = "yyyy-MM-dd")
38
-    private Date issueDate;
39
-    
40
-    @Excel(name = "证书编号")
41
-    private String certNo;
42
-    
43
-    @Excel(name = "理论考核成绩")
44
-    private Double theoryScore;
45
-    
46
-    @Excel(name = "实操考核成绩")
47
-    private Double practiceScore;
48
-    
49
-    @Excel(name = "评定成绩")
50
-    private Double evalScore;
51
-    
52
-    @Excel(name = "用工形式")
53
-    private String employmentType;
54
-    
55
-    @Excel(name = "服务期(月)")
56
-    private Integer serviceMonths;
57
-    
58
-    @Excel(name = "到期日期", dateFormat = "yyyy-MM-dd")
59
-    private Date expiryDate;
60
-    
61
-    @Excel(name = "状态")
62
-    private String status;
63
-    
64
-    @Excel(name = "到期天数")
65
-    private Integer expireDays;
66
-    
67
-    @Excel(name = "备注")
68
-    private String remark;
69
-    
70
-    // 注意:复审时间不在这里定义,因为数量不固定
71
-    // 在导入时需要动态解析"第1次复审时间"、"第2次复审时间"等列
72
-    
73
-    /**
74
-     * 动态字段:存储所有复审时间
75
-     * Key: 复审次序(1,2,3...)
76
-     * Value: 复审时间
77
-     */
78
-    private java.util.Map<Integer, Date> reviewTimeMap;
79
-
80
-    public Integer getRowNum() {
81
-        return rowNum;
82
-    }
83
-
84
-    public void setRowNum(Integer rowNum) {
85
-        this.rowNum = rowNum;
86
-    }
87
-
88
-    public String getUnitName() {
89
-        return unitName;
90
-    }
91
-
92
-    public void setUnitName(String unitName) {
93
-        this.unitName = unitName;
94
-    }
95
-
96
-    public String getPersonName() {
97
-        return personName;
98
-    }
99
-
100
-    public void setPersonName(String personName) {
101
-        this.personName = personName;
102
-    }
103
-
104
-    public String getGender() {
105
-        return gender;
106
-    }
107
-
108
-    public void setGender(String gender) {
109
-        this.gender = gender;
110
-    }
111
-
112
-    public Date getBirthDate() {
113
-        return birthDate;
114
-    }
115
-
116
-    public void setBirthDate(Date birthDate) {
117
-        this.birthDate = birthDate;
118
-    }
119
-
120
-    public String getIdCard() {
121
-        return idCard;
122
-    }
123
-
124
-    public void setIdCard(String idCard) {
125
-        this.idCard = idCard;
126
-    }
127
-
128
-    public String getCertLevel() {
129
-        return certLevel;
130
-    }
131
-
132
-    public void setCertLevel(String certLevel) {
133
-        this.certLevel = certLevel;
134
-    }
135
-
136
-    public Date getIssueDate() {
137
-        return issueDate;
138
-    }
139
-
140
-    public void setIssueDate(Date issueDate) {
141
-        this.issueDate = issueDate;
142
-    }
143
-
144
-    public String getCertNo() {
145
-        return certNo;
146
-    }
147
-
148
-    public void setCertNo(String certNo) {
149
-        this.certNo = certNo;
150
-    }
151
-
152
-    public Double getTheoryScore() {
153
-        return theoryScore;
154
-    }
155
-
156
-    public void setTheoryScore(Double theoryScore) {
157
-        this.theoryScore = theoryScore;
158
-    }
159
-
160
-    public Double getPracticeScore() {
161
-        return practiceScore;
162
-    }
163
-
164
-    public void setPracticeScore(Double practiceScore) {
165
-        this.practiceScore = practiceScore;
166
-    }
167
-
168
-    public Double getEvalScore() {
169
-        return evalScore;
170
-    }
171
-
172
-    public void setEvalScore(Double evalScore) {
173
-        this.evalScore = evalScore;
174
-    }
175
-
176
-    public String getEmploymentType() {
177
-        return employmentType;
178
-    }
179
-
180
-    public void setEmploymentType(String employmentType) {
181
-        this.employmentType = employmentType;
182
-    }
183
-
184
-    public Integer getServiceMonths() {
185
-        return serviceMonths;
186
-    }
187
-
188
-    public void setServiceMonths(Integer serviceMonths) {
189
-        this.serviceMonths = serviceMonths;
190
-    }
191
-
192
-    public Date getExpiryDate() {
193
-        return expiryDate;
194
-    }
195
-
196
-    public void setExpiryDate(Date expiryDate) {
197
-        this.expiryDate = expiryDate;
198
-    }
199
-
200
-    public String getStatus() {
201
-        return status;
202
-    }
203
-
204
-    public void setStatus(String status) {
205
-        this.status = status;
206
-    }
207
-
208
-    public Integer getExpireDays() {
209
-        return expireDays;
210
-    }
211
-
212
-    public void setExpireDays(Integer expireDays) {
213
-        this.expireDays = expireDays;
214
-    }
215
-
216
-    public String getRemark() {
217
-        return remark;
218
-    }
219
-
220
-    public void setRemark(String remark) {
221
-        this.remark = remark;
222
-    }
223
-
224
-    public java.util.Map<Integer, Date> getReviewTimeMap() {
225
-        return reviewTimeMap;
226
-    }
227
-
228
-    public void setReviewTimeMap(java.util.Map<Integer, Date> reviewTimeMap) {
229
-        this.reviewTimeMap = reviewTimeMap;
230
-    }
231
-}

+ 61 - 0
airport-ledger/src/main/java/com/sundot/airport/ledger/enums/CertificateStatusEnum.java

@@ -0,0 +1,61 @@
1
+package com.sundot.airport.ledger.enums;
2
+
3
+import lombok.AllArgsConstructor;
4
+import lombok.Getter;
5
+
6
+/**
7
+ * 证书状态枚举
8
+ * 
9
+ * @author ruoyi
10
+ */
11
+@Getter
12
+@AllArgsConstructor
13
+public enum CertificateStatusEnum {
14
+
15
+    NORMAL("NORMAL", "正常"),
16
+    RETRAINING("RETRAINING", "复训"),
17
+    EXPIRED("EXPIRED", "到期");
18
+
19
+    private final String code;
20
+    private final String desc;
21
+
22
+    /**
23
+     * 根据 code 获取枚举
24
+     */
25
+    public static CertificateStatusEnum getByCode(String code) {
26
+        for (CertificateStatusEnum itemEnum : values()) {
27
+            if (itemEnum.getCode().equals(code)) {
28
+                return itemEnum;
29
+            }
30
+        }
31
+        return null;
32
+    }
33
+
34
+    /**
35
+     * 根据描述获取枚举
36
+     */
37
+    public static CertificateStatusEnum getByDesc(String desc) {
38
+        for (CertificateStatusEnum itemEnum : values()) {
39
+            if (itemEnum.getDesc().equals(desc)) {
40
+                return itemEnum;
41
+            }
42
+        }
43
+        return null;
44
+    }
45
+
46
+    /**
47
+     * 根据到期天数计算证书状态
48
+     * 
49
+     * @param expireDays 到期天数
50
+     * @return 证书状态枚举
51
+     */
52
+    public static CertificateStatusEnum calculateByExpireDays(int expireDays) {
53
+        if (expireDays > 180) {
54
+            return NORMAL;
55
+        } else if (expireDays > 0) {
56
+            return RETRAINING;
57
+        } else {
58
+            return EXPIRED;
59
+        }
60
+    }
61
+}

+ 56 - 0
airport-ledger/src/main/java/com/sundot/airport/ledger/mapper/LedgerCertificateInfoMapper.java

@@ -83,4 +83,60 @@ public interface LedgerCertificateInfoMapper {
83 83
      * @return 证书信息
84 84
      */
85 85
     public LedgerCertificateInfo selectCertificateInfoByUserIdAndCertNo(@Param("userId") Long userId, @Param("certNo") String certNo);
86
+
87
+    /**
88
+     * 根据用户ID和证书等级查询证书信息
89
+     * 
90
+     * @param userId 用户ID
91
+     * @param certLevel 证书等级
92
+     * @return 证书信息
93
+     */
94
+    public LedgerCertificateInfo selectCertificateInfoByUserIdAndCertLevel(@Param("userId") Long userId, @Param("certLevel") String certLevel);
95
+
96
+    /**
97
+     * 根据用户ID查询该用户所有证书信息
98
+     * 
99
+     * @param userId 用户ID
100
+     * @return 证书信息列表
101
+     */
102
+    public List<LedgerCertificateInfo> selectCertificateInfoByUserId(@Param("userId") Long userId);
103
+
104
+    /**
105
+     * 查询所有有效证书信息(用于定时任务)
106
+     * 
107
+     * @return 证书信息列表
108
+     */
109
+    public List<LedgerCertificateInfo> selectAllCertificateInfo();
110
+
111
+    /**
112
+     * 批量更新证书到期信息
113
+     * 
114
+     * @param ledgerCertificateInfo 证书信息(包含expiryDate, expireDays, status)
115
+     * @return 结果
116
+     */
117
+    public int updateCertificateExpiryInfo(LedgerCertificateInfo ledgerCertificateInfo);
118
+
119
+    /**
120
+     * 根据userId和certLevel组合条件批量查询已存在的证书
121
+     * 
122
+     * @param list 证书列表(只需设置userId和certLevel)
123
+     * @return 已存在的证书列表
124
+     */
125
+    List<LedgerCertificateInfo> selectExistingCertificates(@Param("list") List<LedgerCertificateInfo> list);
126
+
127
+    /**
128
+     * 批量插入证书信息
129
+     * 
130
+     * @param list 证书列表
131
+     * @return 结果
132
+     */
133
+    int batchInsertCertificateInfo(@Param("list") List<LedgerCertificateInfo> list);
134
+
135
+    /**
136
+     * 批量更新证书信息
137
+     * 
138
+     * @param list 证书列表
139
+     * @return 结果
140
+     */
141
+    int batchUpdateCertificateInfo(@Param("list") List<LedgerCertificateInfo> list);
86 142
 }

+ 17 - 0
airport-ledger/src/main/java/com/sundot/airport/ledger/mapper/LedgerCertificateReviewRecordMapper.java

@@ -1,6 +1,7 @@
1 1
 package com.sundot.airport.ledger.mapper;
2 2
 
3 3
 import java.util.List;
4
+import org.apache.ibatis.annotations.Param;
4 5
 import com.sundot.airport.ledger.domain.LedgerCertificateReviewRecord;
5 6
 
6 7
 /**
@@ -73,4 +74,20 @@ public interface LedgerCertificateReviewRecordMapper {
73 74
      * @return 结果
74 75
      */
75 76
     public int deleteCertificateReviewRecordByCertId(Long certId);
77
+
78
+    /**
79
+     * 根据多个证书ID批量删除复审记录
80
+     * 
81
+     * @param certIds 证书ID列表
82
+     * @return 结果
83
+     */
84
+    public int deleteReviewRecordsByCertIds(@Param("certIds") List<Long> certIds);
85
+
86
+    /**
87
+     * 批量插入复审记录
88
+     * 
89
+     * @param list 复审记录列表
90
+     * @return 结果
91
+     */
92
+    public int batchInsertReviewRecords(@Param("list") List<LedgerCertificateReviewRecord> list);
76 93
 }

+ 19 - 0
airport-ledger/src/main/java/com/sundot/airport/ledger/service/ILedgerCertificateInfoService.java

@@ -67,4 +67,23 @@ public interface ILedgerCertificateInfoService {
67 67
      * @return 结果
68 68
      */
69 69
     public String importCertificate(List<LedgerCertificateInfo> certificateList, Boolean isUpdateSupport, String operName);
70
+
71
+    /**
72
+     * 计算证书到期信息(到期日期、到期天数、状态)
73
+     * 
74
+     * @param certificate 证书信息
75
+     */
76
+    public void calculateExpiryInfo(LedgerCertificateInfo certificate);
77
+
78
+    /**
79
+     * 重新计算用户资质等级(根据用户所有证书到期状态)
80
+     * 
81
+     * @param userId 用户ID
82
+     */
83
+    public void recalculateUserQualification(Long userId);
84
+
85
+    /**
86
+     * 每日定时更新证书到期信息
87
+     */
88
+    public void dailyUpdateExpiryInfo();
70 89
 }

+ 341 - 96
airport-ledger/src/main/java/com/sundot/airport/ledger/service/impl/LedgerLedgerCertificateInfoServiceImpl.java

@@ -1,11 +1,15 @@
1 1
 package com.sundot.airport.ledger.service.impl;
2 2
 
3 3
 import java.util.ArrayList;
4
+import java.util.Calendar;
5
+import java.util.Date;
4 6
 import java.util.HashMap;
5 7
 import java.util.List;
6 8
 import java.util.Map;
7 9
 import java.util.stream.Collectors;
8 10
 
11
+import org.slf4j.Logger;
12
+import org.slf4j.LoggerFactory;
9 13
 import org.springframework.beans.factory.annotation.Autowired;
10 14
 import org.springframework.stereotype.Service;
11 15
 import org.springframework.transaction.annotation.Transactional;
@@ -15,6 +19,7 @@ import com.sundot.airport.ledger.mapper.LedgerCertificateInfoMapper;
15 19
 import com.sundot.airport.ledger.mapper.LedgerCertificateReviewRecordMapper;
16 20
 import com.sundot.airport.ledger.domain.LedgerCertificateInfo;
17 21
 import com.sundot.airport.ledger.domain.LedgerCertificateReviewRecord;
22
+import com.sundot.airport.ledger.enums.CertificateStatusEnum;
18 23
 import com.sundot.airport.ledger.service.ILedgerCertificateInfoService;
19 24
 import com.sundot.airport.system.service.ISysUserService;
20 25
 
@@ -26,6 +31,8 @@ import com.sundot.airport.system.service.ISysUserService;
26 31
  */
27 32
 @Service
28 33
 public class LedgerLedgerCertificateInfoServiceImpl implements ILedgerCertificateInfoService {
34
+    private static final Logger log = LoggerFactory.getLogger(LedgerLedgerCertificateInfoServiceImpl.class);
35
+
29 36
     @Autowired
30 37
     private LedgerCertificateInfoMapper ledgerCertificateInfoMapper;
31 38
 
@@ -73,11 +80,20 @@ public class LedgerLedgerCertificateInfoServiceImpl implements ILedgerCertificat
73 80
     @Transactional
74 81
     public int insertCertificateInfo(LedgerCertificateInfo ledgerCertificateInfo) {
75 82
         ledgerCertificateInfo.setCreateTime(DateUtils.getNowDate());
83
+        
84
+        // 计算到期信息
85
+        calculateExpiryInfo(ledgerCertificateInfo);
86
+        
76 87
         int rows = ledgerCertificateInfoMapper.insertCertificateInfo(ledgerCertificateInfo);
77 88
         
78 89
         // 保存复审记录
79 90
         saveReviewRecords(ledgerCertificateInfo.getId(), ledgerCertificateInfo.getReviewRecords());
80 91
         
92
+        // 同步用户资质等级
93
+        if (ledgerCertificateInfo.getUserId() != null) {
94
+            recalculateUserQualification(ledgerCertificateInfo.getUserId());
95
+        }
96
+        
81 97
         return rows;
82 98
     }
83 99
 
@@ -91,12 +107,21 @@ public class LedgerLedgerCertificateInfoServiceImpl implements ILedgerCertificat
91 107
     @Transactional
92 108
     public int updateCertificateInfo(LedgerCertificateInfo ledgerCertificateInfo) {
93 109
         ledgerCertificateInfo.setUpdateTime(DateUtils.getNowDate());
110
+        
111
+        // 计算到期信息
112
+        calculateExpiryInfo(ledgerCertificateInfo);
113
+        
94 114
         int rows = ledgerCertificateInfoMapper.updateCertificateInfo(ledgerCertificateInfo);
95 115
         
96 116
         // 先删除旧的复审记录,再保存新的
97 117
         ledgerCertificateReviewRecordMapper.deleteCertificateReviewRecordByCertId(ledgerCertificateInfo.getId());
98 118
         saveReviewRecords(ledgerCertificateInfo.getId(), ledgerCertificateInfo.getReviewRecords());
99 119
         
120
+        // 同步用户资质等级
121
+        if (ledgerCertificateInfo.getUserId() != null) {
122
+            recalculateUserQualification(ledgerCertificateInfo.getUserId());
123
+        }
124
+        
100 125
         return rows;
101 126
     }
102 127
 
@@ -109,11 +134,23 @@ public class LedgerLedgerCertificateInfoServiceImpl implements ILedgerCertificat
109 134
     @Override
110 135
     @Transactional
111 136
     public int deleteCertificateInfoByIds(Long[] ids) {
112
-        // 删除复审记录
137
+        // 收集需要刷新资质的userId
138
+        List<Long> userIds = new ArrayList<>();
113 139
         for (Long id : ids) {
140
+            LedgerCertificateInfo cert = ledgerCertificateInfoMapper.selectCertificateInfoById(id);
141
+            if (cert != null && cert.getUserId() != null) {
142
+                userIds.add(cert.getUserId());
143
+            }
114 144
             ledgerCertificateReviewRecordMapper.deleteCertificateReviewRecordByCertId(id);
115 145
         }
116
-        return ledgerCertificateInfoMapper.deleteCertificateInfoByIds(ids);
146
+        int rows = ledgerCertificateInfoMapper.deleteCertificateInfoByIds(ids);
147
+        
148
+        // 同步用户资质等级
149
+        for (Long userId : userIds) {
150
+            recalculateUserQualification(userId);
151
+        }
152
+        
153
+        return rows;
117 154
     }
118 155
 
119 156
     /**
@@ -125,9 +162,17 @@ public class LedgerLedgerCertificateInfoServiceImpl implements ILedgerCertificat
125 162
     @Override
126 163
     @Transactional
127 164
     public int deleteCertificateInfoById(Long id) {
165
+        LedgerCertificateInfo cert = ledgerCertificateInfoMapper.selectCertificateInfoById(id);
128 166
         // 删除复审记录
129 167
         ledgerCertificateReviewRecordMapper.deleteCertificateReviewRecordByCertId(id);
130
-        return ledgerCertificateInfoMapper.deleteCertificateInfoById(id);
168
+        int rows = ledgerCertificateInfoMapper.deleteCertificateInfoById(id);
169
+        
170
+        // 同步用户资质等级
171
+        if (cert != null && cert.getUserId() != null) {
172
+            recalculateUserQualification(cert.getUserId());
173
+        }
174
+        
175
+        return rows;
131 176
     }
132 177
 
133 178
     /**
@@ -145,11 +190,6 @@ public class LedgerLedgerCertificateInfoServiceImpl implements ILedgerCertificat
145 190
             throw new RuntimeException("导入证书数据不能为空!");
146 191
         }
147 192
         
148
-        int successNum = 0;
149
-        int failureNum = 0;
150
-        StringBuilder successMsg = new StringBuilder();
151
-        StringBuilder failureMsg = new StringBuilder();
152
-        
153 193
         // 第一步: 收集所有姓名,批量查询用户信息
154 194
         List<String> personNames = certificateList.stream()
155 195
                 .map(LedgerCertificateInfo::getPersonName)
@@ -163,7 +203,6 @@ public class LedgerLedgerCertificateInfoServiceImpl implements ILedgerCertificat
163 203
         // 构建姓名到用户的映射 Map<姓名, SysUser>
164 204
         Map<String, SysUser> userMap = new HashMap<>();
165 205
         for (SysUser user : users) {
166
-            // 优先使用 nickName(用户名称),如果没有则使用 userName(登录名称)
167 206
             String key = user.getNickName() != null && !user.getNickName().isEmpty() 
168 207
                     ? user.getNickName() : user.getUserName();
169 208
             userMap.put(key, user);
@@ -177,7 +216,6 @@ public class LedgerLedgerCertificateInfoServiceImpl implements ILedgerCertificat
177 216
             }
178 217
         }
179 218
         
180
-        // 如果有未找到的用户,报错提醒
181 219
         if (!notFoundNames.isEmpty()) {
182 220
             throw new RuntimeException("以下人员未在系统中找到:" + String.join("、", notFoundNames));
183 221
         }
@@ -191,77 +229,102 @@ public class LedgerLedgerCertificateInfoServiceImpl implements ILedgerCertificat
191 229
             }
192 230
         }
193 231
         
194
-        // 第四步: 批量查询已存在的证书信息(根据 userId 和 certNo)
232
+        // 第四步: 批量查询已存在的证书
233
+        List<LedgerCertificateInfo> queryKeys = certificateList.stream()
234
+                .filter(c -> c.getUserId() != null && c.getCertLevel() != null)
235
+                .collect(Collectors.toList());
236
+        
195 237
         Map<String, LedgerCertificateInfo> existingCertMap = new HashMap<>();
196
-        for (LedgerCertificateInfo certificate : certificateList) {
197
-            if (certificate.getUserId() != null && certificate.getCertNo() != null) {
198
-                LedgerCertificateInfo existCert = ledgerCertificateInfoMapper.selectCertificateInfoByUserIdAndCertNo(
199
-                        certificate.getUserId(), certificate.getCertNo());
200
-                if (existCert != null) {
201
-                    String key = certificate.getUserId() + "_" + certificate.getCertNo();
202
-                    existingCertMap.put(key, existCert);
203
-                }
238
+        if (!queryKeys.isEmpty()) {
239
+            List<LedgerCertificateInfo> existingCerts = ledgerCertificateInfoMapper.selectExistingCertificates(queryKeys);
240
+            for (LedgerCertificateInfo cert : existingCerts) {
241
+                String mapKey = cert.getUserId() + "_" + cert.getCertLevel();
242
+                existingCertMap.put(mapKey, cert);
204 243
             }
205 244
         }
206 245
         
207
-        // 第五步: 处理每个证书的导入(新增或更新)
246
+        // 第五步: 分类为新增和更新列表,计算到期信息
247
+        List<LedgerCertificateInfo> toInsert = new ArrayList<>();
248
+        List<LedgerCertificateInfo> toUpdate = new ArrayList<>();
249
+        List<Long> affectedUserIds = new ArrayList<>();
250
+        
208 251
         for (LedgerCertificateInfo certificate : certificateList) {
209
-            try {
210
-                String key = certificate.getUserId() + "_" + certificate.getCertNo();
211
-                LedgerCertificateInfo existCert = existingCertMap.get(key);
212
-                
213
-                if (existCert != null) {
214
-                    // 存在则更新
215
-                    if (isUpdateSupport) {
216
-                        certificate.setId(existCert.getId());
217
-                        certificate.setUpdateBy(operName);
218
-                        ledgerCertificateInfoMapper.updateCertificateInfo(certificate);
219
-                        
220
-                        // 更新复审记录
221
-                        ledgerCertificateReviewRecordMapper.deleteCertificateReviewRecordByCertId(existCert.getId());
222
-                        saveReviewRecords(existCert.getId(), certificate.getReviewRecords());
223
-                        
224
-                        successNum++;
225
-                        successMsg.append("<br/>").append(successNum).append("、")
226
-                                .append(certificate.getPersonName()).append(" 的证书编号 ")
227
-                                .append(certificate.getCertNo()).append(" 更新成功");
228
-                    } else {
229
-                        failureNum++;
230
-                        failureMsg.append("<br/>").append(failureNum).append("、")
231
-                                .append(certificate.getPersonName()).append(" 的证书编号 ")
232
-                                .append(certificate.getCertNo()).append(" 已存在");
233
-                    }
234
-                } else {
235
-                    // 不存在则新增
236
-                    certificate.setCreateBy(operName);
237
-                    ledgerCertificateInfoMapper.insertCertificateInfo(certificate);
238
-                    
239
-                    // 保存复审记录
240
-                    saveReviewRecords(certificate.getId(), certificate.getReviewRecords());
241
-                    
242
-                    successNum++;
243
-                    successMsg.append("<br/>").append(successNum).append("、")
244
-                            .append(certificate.getPersonName()).append(" 的证书编号 ")
245
-                            .append(certificate.getCertNo()).append(" 导入成功");
252
+            String key = certificate.getUserId() + "_" + certificate.getCertLevel();
253
+            LedgerCertificateInfo existCert = existingCertMap.get(key);
254
+            
255
+            // 计算到期信息
256
+            calculateExpiryInfo(certificate);
257
+            
258
+            if (existCert != null) {
259
+                certificate.setId(existCert.getId());
260
+                certificate.setUpdateBy(operName);
261
+                toUpdate.add(certificate);
262
+            } else {
263
+                certificate.setCreateBy(operName);
264
+                toInsert.add(certificate);
265
+            }
266
+            
267
+            if (!affectedUserIds.contains(certificate.getUserId())) {
268
+                affectedUserIds.add(certificate.getUserId());
269
+            }
270
+        }
271
+        
272
+        // 第六步: 批量新增
273
+        if (!toInsert.isEmpty()) {
274
+            ledgerCertificateInfoMapper.batchInsertCertificateInfo(toInsert);
275
+        }
276
+        
277
+        // 第七步: 批量更新
278
+        if (!toUpdate.isEmpty()) {
279
+            ledgerCertificateInfoMapper.batchUpdateCertificateInfo(toUpdate);
280
+        }
281
+        
282
+        // 第八步: 批量处理复审记录
283
+        // 批量删除需要更新的证书的复审记录
284
+        List<Long> updateCertIds = toUpdate.stream()
285
+                .map(LedgerCertificateInfo::getId)
286
+                .collect(Collectors.toList());
287
+        if (!updateCertIds.isEmpty()) {
288
+            ledgerCertificateReviewRecordMapper.deleteReviewRecordsByCertIds(updateCertIds);
289
+        }
290
+        
291
+        // 批量插入所有复审记录
292
+        List<LedgerCertificateReviewRecord> allReviewRecords = new ArrayList<>();
293
+        for (LedgerCertificateInfo cert : toUpdate) {
294
+            if (cert.getReviewRecords() != null) {
295
+                for (int i = 0; i < cert.getReviewRecords().size(); i++) {
296
+                    LedgerCertificateReviewRecord record = cert.getReviewRecords().get(i);
297
+                    record.setCertId(cert.getId());
298
+                    record.setReviewSeq(i + 1);
299
+                    record.setCreateTime(DateUtils.getNowDate());
300
+                    allReviewRecords.add(record);
301
+                }
302
+            }
303
+        }
304
+        for (LedgerCertificateInfo cert : toInsert) {
305
+            if (cert.getReviewRecords() != null) {
306
+                for (int i = 0; i < cert.getReviewRecords().size(); i++) {
307
+                    LedgerCertificateReviewRecord record = cert.getReviewRecords().get(i);
308
+                    record.setCertId(cert.getId());
309
+                    record.setReviewSeq(i + 1);
310
+                    record.setCreateTime(DateUtils.getNowDate());
311
+                    allReviewRecords.add(record);
246 312
                 }
247
-                
248
-                // 第六步: 同步更新用户表的证书等级(5<4<3<2<1,数字越小等级越高)
249
-                syncUserQualificationLevel(certificate.getUserId(), certificate.getCertLevel());
250
-                
251
-            } catch (Exception e) {
252
-                failureNum++;
253
-                String msg = "<br/>" + failureNum + "、" + certificate.getPersonName() + " 的证书编号 " 
254
-                        + certificate.getCertNo() + " 导入失败:";
255
-                failureMsg.append(msg + e.getMessage());
256 313
             }
257 314
         }
315
+        if (!allReviewRecords.isEmpty()) {
316
+            ledgerCertificateReviewRecordMapper.batchInsertReviewRecords(allReviewRecords);
317
+        }
258 318
         
259
-        if (failureNum > 0) {
260
-            failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:");
261
-            throw new RuntimeException(failureMsg.toString());
262
-        } else {
263
-            successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:");
319
+        // 第九步: 刷新所有受影响用户的资质等级
320
+        for (Long userId : affectedUserIds) {
321
+            recalculateUserQualification(userId);
264 322
         }
323
+        
324
+        // 构建返回消息
325
+        int successNum = toInsert.size() + toUpdate.size();
326
+        StringBuilder successMsg = new StringBuilder();
327
+        successMsg.insert(0, "数据已全部导入成功!共 " + successNum + " 条");
265 328
         return successMsg.toString();
266 329
     }
267 330
 
@@ -276,7 +339,7 @@ public class LedgerLedgerCertificateInfoServiceImpl implements ILedgerCertificat
276 339
             for (int i = 0; i < reviewRecords.size(); i++) {
277 340
                 LedgerCertificateReviewRecord record = reviewRecords.get(i);
278 341
                 record.setCertId(certId);
279
-                record.setReviewSeq(i + 1); // 设置复审次序
342
+                record.setReviewSeq(i + 1);
280 343
                 record.setCreateTime(DateUtils.getNowDate());
281 344
                 ledgerCertificateReviewRecordMapper.insertCertificateReviewRecord(record);
282 345
             }
@@ -284,46 +347,228 @@ public class LedgerLedgerCertificateInfoServiceImpl implements ILedgerCertificat
284 347
     }
285 348
 
286 349
     /**
287
-     * 同步更新用户表的证书等级
288
-     * 等级规则: 5<4<3<2<1 (数字越小等级越高)
289
-     * 如果出现更高等级,就更新用户表
350
+     * 计算证书到期信息(到期日期、到期天数、状态)
351
+     * 到期日期 = 最后复审时间 + 服务期月 - 1天,如果复审时间为空则取发证时间
352
+     * 到期天数 = 到期日期 - 当前时间
353
+     * 状态: >180天=正常, >0且<=180=复训, <=0=到期
290 354
      * 
291
-     * @param userId 用户ID
292
-     * @param newCertLevel 新证书等级
355
+     * @param certificate 证书信息
293 356
      */
294
-    private void syncUserQualificationLevel(Long userId, String newCertLevel) {
295
-        if (userId == null || newCertLevel == null || newCertLevel.trim().isEmpty()) {
357
+    @Override
358
+    public void calculateExpiryInfo(LedgerCertificateInfo certificate) {
359
+        if (certificate == null) return;
360
+        
361
+        Date baseDate = calculateExpiryBaseDate(certificate);
362
+        if (baseDate == null || certificate.getServiceMonths() == null) {
296 363
             return;
297 364
         }
298 365
         
366
+        // 计算到期日期: baseDate + serviceMonths - 1天
367
+        Date expiryDate = calculateExpiryDate(baseDate, certificate.getServiceMonths());
368
+        certificate.setExpiryDate(expiryDate);
369
+        
370
+        // 计算到期天数
371
+        int expireDays = calculateExpireDays(expiryDate);
372
+        certificate.setExpireDays(expireDays);
373
+        
374
+        // 计算状态
375
+        String status = calculateStatus(expireDays);
376
+        certificate.setStatus(status);
377
+    }
378
+
379
+    /**
380
+     * 计算到期基准日期:取最后复审时间,如果为空则取发证时间
381
+     */
382
+    private Date calculateExpiryBaseDate(LedgerCertificateInfo certificate) {
383
+        // 优先从传入的复审记录中查找(用于新增/导入场景)
384
+        List<LedgerCertificateReviewRecord> reviewRecords = certificate.getReviewRecords();
385
+        
386
+        // 如果没有传入复审记录,从数据库查询
387
+        if ((reviewRecords == null || reviewRecords.isEmpty()) && certificate.getId() != null) {
388
+            reviewRecords = ledgerCertificateReviewRecordMapper.selectCertificateReviewRecordByCertId(certificate.getId());
389
+        }
390
+        
391
+        if (reviewRecords != null && !reviewRecords.isEmpty()) {
392
+            // 找到最新的复审时间(列表最后一个,按 reviewSeq 升序)
393
+            LedgerCertificateReviewRecord latestRecord = reviewRecords.get(reviewRecords.size() - 1);
394
+            if (latestRecord.getReviewTime() != null) {
395
+                return latestRecord.getReviewTime();
396
+            }
397
+        }
398
+        
399
+        // 如果没有复审记录或复审时间为空,取发证时间
400
+        return certificate.getIssueDate();
401
+    }
402
+
403
+    /**
404
+     * 计算到期日期: baseDate + months 月 - 1天
405
+     */
406
+    private Date calculateExpiryDate(Date baseDate, int months) {
407
+        Calendar cal = Calendar.getInstance();
408
+        cal.setTime(baseDate);
409
+        cal.add(Calendar.MONTH, months);
410
+        cal.add(Calendar.DAY_OF_MONTH, -1);
411
+        return cal.getTime();
412
+    }
413
+
414
+    /**
415
+     * 计算到期天数: expiryDate - 当前日期
416
+     */
417
+    private int calculateExpireDays(Date expiryDate) {
418
+        if (expiryDate == null) return 0;
419
+        Date now = DateUtils.getNowDate();
420
+        long diff = expiryDate.getTime() - now.getTime();
421
+        return (int) (diff / (1000 * 60 * 60 * 24));
422
+    }
423
+
424
+    /**
425
+     * 计算状态: >180天=正常, >0且<=180=复训, <=0=到期
426
+     */
427
+    private String calculateStatus(int expireDays) {
428
+        return CertificateStatusEnum.calculateByExpireDays(expireDays).getDesc();
429
+    }
430
+
431
+    /**
432
+     * 重新计算用户资质等级
433
+     * 规则: 查询用户所有证书,找到最高等级(数字最小)且未到期的证书
434
+     * 如果所有证书都到期,则清空资质
435
+     * 
436
+     * @param userId 用户ID
437
+     */
438
+    @Override
439
+    public void recalculateUserQualification(Long userId) {
440
+        if (userId == null) return;
441
+        
299 442
         try {
300
-            // 查询用户当前资质等级
301
-            SysUser user = sysUserService.selectUserById(userId);
302
-            if (user == null) {
443
+            // 查询用户所有证书
444
+            List<LedgerCertificateInfo> allCerts = ledgerCertificateInfoMapper.selectCertificateInfoByUserId(userId);
445
+            
446
+            if (allCerts == null || allCerts.isEmpty()) {
447
+                // 没有证书,清空资质
448
+                updateUserQualification(userId, null);
303 449
                 return;
304 450
             }
305 451
             
306
-            String currentLevel = user.getQualificationLevel();
452
+            // 重新计算每张证书的到期信息
453
+            for (LedgerCertificateInfo cert : allCerts) {
454
+                // 对于已有id的证书,查询复审记录来计算到期信息
455
+                Date baseDate = calculateExpiryBaseDate(cert);
456
+                if (baseDate != null && cert.getServiceMonths() != null) {
457
+                    Date expiryDate = calculateExpiryDate(baseDate, cert.getServiceMonths());
458
+                    cert.setExpiryDate(expiryDate);
459
+                    int expireDays = calculateExpireDays(expiryDate);
460
+                    cert.setExpireDays(expireDays);
461
+                    cert.setStatus(calculateStatus(expireDays));
462
+                    
463
+                    // 更新到期信息到数据库
464
+                    ledgerCertificateInfoMapper.updateCertificateExpiryInfo(cert);
465
+                }
466
+            }
467
+            
468
+            // 找到最高等级(数字最小)且未到期的证书
469
+            LedgerCertificateInfo bestValidCert = null;
470
+            int bestLevel = Integer.MAX_VALUE;
307 471
             
308
-            // 如果用户当前没有等级,直接设置
309
-            if (currentLevel == null || currentLevel.trim().isEmpty()) {
310
-                updateUserQualificationLevel(userId, newCertLevel);
472
+            for (LedgerCertificateInfo cert : allCerts) {
473
+                // 使用刚计算的状态
474
+                if (!CertificateStatusEnum.EXPIRED.getDesc().equals(cert.getStatus())) {
475
+                    int level = parseLevelToNumber(cert.getCertLevel());
476
+                    if (level < bestLevel) {
477
+                        bestLevel = level;
478
+                        bestValidCert = cert;
479
+                    }
480
+                }
481
+            }
482
+            
483
+            if (bestValidCert != null) {
484
+                // 找到未到期证书,设置资质为最高等级
485
+                String qualificationLevel = convertCertLevelToQualificationFormat(bestValidCert.getCertLevel());
486
+                updateUserQualification(userId, qualificationLevel);
487
+            } else {
488
+                // 所有证书都到期,清空资质
489
+                updateUserQualification(userId, null);
490
+            }
491
+        } catch (Exception e) {
492
+            log.error("重新计算用户资质等级失败, userId={}", userId, e);
493
+        }
494
+    }
495
+
496
+    /**
497
+     * 每日定时更新证书到期信息
498
+     * 更新所有证书的到期日期、到期天数、状态,并同步用户资质等级
499
+     */
500
+    @Override
501
+    public void dailyUpdateExpiryInfo() {
502
+        log.info("========== 开始执行证书到期信息每日更新 ==========");
503
+        
504
+        try {
505
+            // 查询所有有效证书
506
+            List<LedgerCertificateInfo> allCerts = ledgerCertificateInfoMapper.selectAllCertificateInfo();
507
+            
508
+            if (allCerts == null || allCerts.isEmpty()) {
509
+                log.info("没有证书数据需要更新");
311 510
                 return;
312 511
             }
313 512
             
314
-            // 比较等级高低(数字越小等级越高)
315
-            int currentLevelNum = parseLevelToNumber(currentLevel);
316
-            int newLevelNum = parseLevelToNumber(newCertLevel);
513
+            int updatedCount = 0;
514
+            List<Long> processedUserIds = new ArrayList<>();
317 515
             
318
-            // 如果新等级更高,则更新用户表
319
-            if (newLevelNum < currentLevelNum) {
320
-                updateUserQualificationLevel(userId, newCertLevel);
516
+            for (LedgerCertificateInfo cert : allCerts) {
517
+                // 计算到期基准日期
518
+                Date baseDate = calculateExpiryBaseDate(cert);
519
+                if (baseDate != null && cert.getServiceMonths() != null) {
520
+                    Date expiryDate = calculateExpiryDate(baseDate, cert.getServiceMonths());
521
+                    cert.setExpiryDate(expiryDate);
522
+                    int expireDays = calculateExpireDays(expiryDate);
523
+                    cert.setExpireDays(expireDays);
524
+                    cert.setStatus(calculateStatus(expireDays));
525
+                    
526
+                    // 更新到期信息到数据库
527
+                    ledgerCertificateInfoMapper.updateCertificateExpiryInfo(cert);
528
+                    updatedCount++;
529
+                }
530
+                
531
+                // 收集用户ID
532
+                if (cert.getUserId() != null && !processedUserIds.contains(cert.getUserId())) {
533
+                    processedUserIds.add(cert.getUserId());
534
+                }
321 535
             }
536
+            
537
+            log.info("已更新 {} 条证书到期信息", updatedCount);
538
+            
539
+            // 统一刷新所有用户的资质等级
540
+            for (Long userId : processedUserIds) {
541
+                recalculateUserQualification(userId);
542
+            }
543
+            
544
+            log.info("已刷新 {} 个用户的资质等级", processedUserIds.size());
545
+            log.info("========== 证书到期信息每日更新完成 ==========");
546
+            
322 547
         } catch (Exception e) {
323
-            e.printStackTrace();
548
+            log.error("证书到期信息每日更新失败", e);
324 549
         }
325 550
     }
326
-    
551
+
552
+    /**
553
+     * 将证书等级格式转换为资质等级格式
554
+     * 如: "一级" -> "LEVEL_ONE", "2" -> "LEVEL_TWO"
555
+     */
556
+    private String convertCertLevelToQualificationFormat(String certLevel) {
557
+        if (certLevel == null || certLevel.trim().isEmpty()) return null;
558
+        
559
+        certLevel = certLevel.trim();
560
+        
561
+        if (certLevel.contains("一") || certLevel.equals("1")) return "LEVEL_ONE";
562
+        if (certLevel.contains("二") || certLevel.equals("2")) return "LEVEL_TWO";
563
+        if (certLevel.contains("三") || certLevel.equals("3")) return "LEVEL_THREE";
564
+        if (certLevel.contains("四") || certLevel.equals("4")) return "LEVEL_FOUR";
565
+        if (certLevel.contains("五") || certLevel.equals("5")) return "LEVEL_FIVE";
566
+        
567
+        if (certLevel.toUpperCase().startsWith("LEVEL_")) return certLevel.toUpperCase();
568
+        
569
+        return certLevel;
570
+    }
571
+
327 572
     /**
328 573
      * 将等级字符串转换为数字进行比较
329 574
      * 支持格式: "一级"、"LEVEL_ONE"、"1" 等
@@ -367,14 +612,14 @@ public class LedgerLedgerCertificateInfoServiceImpl implements ILedgerCertificat
367 612
      * @param userId 用户ID
368 613
      * @param qualificationLevel 资质等级
369 614
      */
370
-    private void updateUserQualificationLevel(Long userId, String qualificationLevel) {
615
+    private void updateUserQualification(Long userId, String qualificationLevel) {
371 616
         try {
372 617
             SysUser user = new SysUser();
373 618
             user.setUserId(userId);
374 619
             user.setQualificationLevel(qualificationLevel);
375 620
             sysUserService.updateUserProfile(user);
376 621
         } catch (Exception e) {
377
-            e.printStackTrace();
622
+            log.error("更新用户资质等级失败, userId={}, level={}", userId, qualificationLevel, e);
378 623
         }
379 624
     }
380 625
 }

+ 79 - 0
airport-ledger/src/main/resources/mapper/personnel/LedgerCertificateInfoMapper.xml

@@ -94,6 +94,31 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
94 94
         <include refid="selectCertificateInfoVo"/>
95 95
         where user_id = #{userId} and cert_no = #{certNo} and del_flag = '0' limit 1
96 96
     </select>
97
+
98
+    <select id="selectCertificateInfoByUserIdAndCertLevel" resultMap="CertificateInfoResult">
99
+        <include refid="selectCertificateInfoVo"/>
100
+        where user_id = #{userId} and cert_level = #{certLevel} and del_flag = '0' limit 1
101
+    </select>
102
+
103
+    <select id="selectCertificateInfoByUserId" resultMap="CertificateInfoResult">
104
+        <include refid="selectCertificateInfoVo"/>
105
+        where user_id = #{userId} and del_flag = '0'
106
+        order by cert_level asc
107
+    </select>
108
+
109
+    <select id="selectAllCertificateInfo" resultMap="CertificateInfoResult">
110
+        <include refid="selectCertificateInfoVo"/>
111
+        where del_flag = '0'
112
+    </select>
113
+
114
+    <update id="updateCertificateExpiryInfo" parameterType="LedgerCertificateInfo">
115
+        update ledger_certificate_info
116
+        set expiry_date = #{expiryDate},
117
+            expire_days = #{expireDays},
118
+            status = #{status},
119
+            update_time = sysdate()
120
+        where id = #{id}
121
+    </update>
97 122
         
98 123
     <insert id="insertCertificateInfo" parameterType="LedgerCertificateInfo" useGeneratedKeys="true" keyProperty="id">
99 124
         insert into ledger_certificate_info
@@ -185,4 +210,58 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
185 210
             #{id}
186 211
         </foreach>
187 212
     </delete>
213
+
214
+    <select id="selectExistingCertificates" resultMap="CertificateInfoResult">
215
+        <include refid="selectCertificateInfoVo"/>
216
+        where del_flag = '0' and (
217
+        <foreach collection="list" item="item" separator=" OR ">
218
+            (user_id = #{item.userId} AND cert_level = #{item.certLevel})
219
+        </foreach>
220
+        )
221
+    </select>
222
+
223
+    <insert id="batchInsertCertificateInfo" useGeneratedKeys="true" keyProperty="list.id">
224
+        insert into ledger_certificate_info
225
+        (tenant_id, unit_name, user_id, person_name, gender, birth_date, id_card,
226
+         cert_level, issue_date, cert_no, theory_score, practice_score, eval_score,
227
+         employment_type, service_months, expiry_date, status, expire_days, remark,
228
+         create_by, create_time, del_flag)
229
+        values
230
+        <foreach collection="list" item="item" separator=",">
231
+            (#{item.tenantId}, #{item.unitName}, #{item.userId}, #{item.personName},
232
+             #{item.gender}, #{item.birthDate}, #{item.idCard}, #{item.certLevel},
233
+             #{item.issueDate}, #{item.certNo}, #{item.theoryScore}, #{item.practiceScore},
234
+             #{item.evalScore}, #{item.employmentType}, #{item.serviceMonths},
235
+             #{item.expiryDate}, #{item.status}, #{item.expireDays}, #{item.remark},
236
+             #{item.createBy}, sysdate(), '0')
237
+        </foreach>
238
+    </insert>
239
+
240
+    <update id="batchUpdateCertificateInfo">
241
+        <foreach collection="list" item="item" separator=";">
242
+            update ledger_certificate_info
243
+            set tenant_id = #{item.tenantId},
244
+                unit_name = #{item.unitName},
245
+                user_id = #{item.userId},
246
+                person_name = #{item.personName},
247
+                gender = #{item.gender},
248
+                birth_date = #{item.birthDate},
249
+                id_card = #{item.idCard},
250
+                cert_level = #{item.certLevel},
251
+                issue_date = #{item.issueDate},
252
+                cert_no = #{item.certNo},
253
+                theory_score = #{item.theoryScore},
254
+                practice_score = #{item.practiceScore},
255
+                eval_score = #{item.evalScore},
256
+                employment_type = #{item.employmentType},
257
+                service_months = #{item.serviceMonths},
258
+                expiry_date = #{item.expiryDate},
259
+                status = #{item.status},
260
+                expire_days = #{item.expireDays},
261
+                remark = #{item.remark},
262
+                update_by = #{item.updateBy},
263
+                update_time = sysdate()
264
+            where id = #{item.id}
265
+        </foreach>
266
+    </update>
188 267
 </mapper>

+ 23 - 6
airport-ledger/src/main/resources/mapper/personnel/LedgerCertificateReviewRecordMapper.xml

@@ -20,7 +20,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
20 20
     <sql id="selectCertificateReviewRecordVo">
21 21
         select id, cert_id, review_seq, review_time,  remark,
22 22
                create_by, create_time, update_by, update_time, del_flag
23
-        from certificate_review_record
23
+        from ledger_certificate_review_record
24 24
     </sql>
25 25
 
26 26
     <select id="selectCertificateReviewRecordList" parameterType="LedgerCertificateReviewRecord" resultMap="CertificateReviewRecordResult">
@@ -51,7 +51,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
51 51
     </select>
52 52
         
53 53
     <insert id="insertCertificateReviewRecord" parameterType="LedgerCertificateReviewRecord" useGeneratedKeys="true" keyProperty="id">
54
-        insert into certificate_review_record
54
+        insert into ledger_certificate_review_record
55 55
         <trim prefix="(" suffix=")" suffixOverrides=",">
56 56
             <if test="certId != null">cert_id,</if>
57 57
             <if test="reviewSeq != null">review_seq,</if>
@@ -73,7 +73,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
73 73
     </insert>
74 74
 
75 75
     <update id="updateCertificateReviewRecord" parameterType="LedgerCertificateReviewRecord">
76
-        update certificate_review_record
76
+        update ledger_certificate_review_record
77 77
         <trim prefix="SET" suffixOverrides=",">
78 78
             <if test="certId != null">cert_id = #{certId},</if>
79 79
             <if test="reviewSeq != null">review_seq = #{reviewSeq},</if>
@@ -86,17 +86,34 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
86 86
     </update>
87 87
 
88 88
     <delete id="deleteCertificateReviewRecordById" parameterType="Long">
89
-        update certificate_review_record set del_flag = '2' where id = #{id}
89
+        update ledger_certificate_review_record set del_flag = '2' where id = #{id}
90 90
     </delete>
91 91
 
92 92
     <delete id="deleteCertificateReviewRecordByIds" parameterType="Long">
93
-        update certificate_review_record set del_flag = '2' where id in
93
+        update ledger_certificate_review_record set del_flag = '2' where id in
94 94
         <foreach item="id" collection="array" open="(" separator="," close=")">
95 95
             #{id}
96 96
         </foreach>
97 97
     </delete>
98 98
 
99 99
     <delete id="deleteCertificateReviewRecordByCertId" parameterType="Long">
100
-        update certificate_review_record set del_flag = '2' where cert_id = #{certId}
100
+        update ledger_certificate_review_record set del_flag = '2' where cert_id = #{certId}
101 101
     </delete>
102
+
103
+    <delete id="deleteReviewRecordsByCertIds">
104
+        update ledger_certificate_review_record set del_flag = '2' where cert_id in
105
+        <foreach collection="certIds" item="certId" open="(" separator="," close=")">
106
+            #{certId}
107
+        </foreach>
108
+    </delete>
109
+
110
+    <insert id="batchInsertReviewRecords">
111
+        insert into ledger_certificate_review_record
112
+        (cert_id, review_seq, review_time, remark, create_by, create_time, del_flag)
113
+        values
114
+        <foreach collection="list" item="item" separator=",">
115
+            (#{item.certId}, #{item.reviewSeq}, #{item.reviewTime}, #{item.remark},
116
+             #{item.createBy}, sysdate(), '0')
117
+        </foreach>
118
+    </insert>
102 119
 </mapper>

+ 55 - 0
airport-quartz/src/main/java/com/sundot/airport/quartz/task/CertificateExpiryTask.java

@@ -0,0 +1,55 @@
1
+package com.sundot.airport.quartz.task;
2
+
3
+import org.slf4j.Logger;
4
+import org.slf4j.LoggerFactory;
5
+import org.springframework.beans.factory.annotation.Autowired;
6
+import org.springframework.stereotype.Component;
7
+import com.sundot.airport.ledger.service.ILedgerCertificateInfoService;
8
+import org.springframework.web.bind.annotation.RequestMapping;
9
+
10
+/**
11
+ * 证书到期信息每日更新定时任务
12
+ * 
13
+ * <p>
14
+ * 定时任务配置说明:
15
+ * - 执行时间:每天凌晨2:00执行
16
+ * - Cron表达式:0 0 2 * * ?
17
+ * - 调用目标:certificateExpiryTask.updateExpiryInfo
18
+ * </p>
19
+ * 
20
+ * @author ruoyi
21
+ */
22
+@Component("certificateExpiryTask")
23
+public class CertificateExpiryTask {
24
+
25
+    private static final Logger log = LoggerFactory.getLogger(CertificateExpiryTask.class);
26
+
27
+    @Autowired
28
+    private ILedgerCertificateInfoService certificateInfoService;
29
+
30
+    /**
31
+     * 每日更新证书到期信息
32
+     * <p>
33
+     * 更新所有证书的到期日期、到期天数、状态,并同步更新用户资质等级
34
+     * </p>
35
+     */
36
+    public void updateExpiryInfo() {
37
+        try {
38
+            log.info("========== 开始执行证书到期信息每日更新任务 ==========");
39
+            
40
+            certificateInfoService.dailyUpdateExpiryInfo();
41
+            
42
+            log.info("========== 证书到期信息每日更新任务执行完成 ==========");
43
+        } catch (Exception e) {
44
+            log.error("执行证书到期信息每日更新任务失败", e);
45
+        }
46
+    }
47
+
48
+    /**
49
+     * 手动触发证书到期信息更新(用于测试)
50
+     */
51
+    public void updateExpiryInfoManual() {
52
+        log.info("手动触发证书到期信息更新");
53
+        updateExpiryInfo();
54
+    }
55
+}