Quellcode durchsuchen

feat: 实现统一响应格式、异常处理、数据字典模块

- 统一响应格式 R<T> 实现
- 全局异常处理 GlobalExceptionHandler + 业务异常 BusinessException
- 错误码枚举 ErrorCode
- 数据字典模块:字典类型/字典项 CRUD + 缓存
- Swagger/Knife4j API 文档聚合
- MyBatis-Plus 配置

🎯 完成 Issue 20 部分功能需求

#feature/issue-20
bot_dev1 vor 4 Tagen
Commit
5f0301b3f8

+ 122
- 0
pom.xml Datei anzeigen

@@ -0,0 +1,122 @@
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
+    <modelVersion>4.0.0</modelVersion>
6
+
7
+    <groupId>com.water</groupId>
8
+    <artifactId>water-common</artifactId>
9
+    <version>1.0.0-SNAPSHOT</version>
10
+    <packaging>jar</packaging>
11
+
12
+    <name>water-common</name>
13
+    <description>供水管理系统通用模块</description>
14
+
15
+    <parent>
16
+        <groupId>org.springframework.boot</groupId>
17
+        <artifactId>spring-boot-starter-parent</artifactId>
18
+        <version>3.2.0</version>
19
+        <relativePath/>
20
+    </parent>
21
+
22
+    <properties>
23
+        <maven.compiler.source>17</maven.compiler.source>
24
+        <maven.compiler.target>17</maven.compiler.target>
25
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
26
+        <lombok.version>1.18.30</lombok.version>
27
+        <easyexcel.version>3.3.2</easyexcel.version>
28
+        <minio.version>8.5.2</minio.version>
29
+        <hutool.version>5.8.20</hutool.version>
30
+        <knife4j.version>4.4.0</knife4j.version>
31
+    </properties>
32
+
33
+    <dependencies>
34
+        <!-- Spring Boot Starters -->
35
+        <dependency>
36
+            <groupId>org.springframework.boot</groupId>
37
+            <artifactId>spring-boot-starter-web</artifactId>
38
+        </dependency>
39
+        <dependency>
40
+            <groupId>org.springframework.boot</groupId>
41
+            <artifactId>spring-boot-starter-data-redis</artifactId>
42
+        </dependency>
43
+        <dependency>
44
+            <groupId>org.springframework.boot</groupId>
45
+            <artifactId>spring-boot-starter-validation</artifactId>
46
+        </dependency>
47
+        <dependency>
48
+            <groupId>org.springframework.boot</groupId>
49
+            <artifactId>spring-boot-starter-aop</artifactId>
50
+        </dependency>
51
+
52
+        <!-- Database -->
53
+        <dependency>
54
+            <groupId>com.baomidou</groupId>
55
+            <artifactId>mybatis-plus-boot-starter</artifactId>
56
+            <version>3.5.4.1</version>
57
+        </dependency>
58
+        <dependency>
59
+            <groupId>mysql</groupId>
60
+            <artifactId>mysql-connector-java</artifactId>
61
+            <version>8.0.33</version>
62
+        </dependency>
63
+
64
+        <!-- MinIO -->
65
+        <dependency>
66
+            <groupId>io.minio</groupId>
67
+            <artifactId>minio</artifactId>
68
+            <version>${minio.version}</version>
69
+        </dependency>
70
+
71
+        <!-- Excel -->
72
+        <dependency>
73
+            <groupId>com.alibaba</groupId>
74
+            <artifactId>easyexcel</artifactId>
75
+            <version>${easyexcel.version}</version>
76
+        </dependency>
77
+
78
+        <!-- Utils -->
79
+        <dependency>
80
+            <groupId>org.projectlombok</groupId>
81
+            <artifactId>lombok</artifactId>
82
+            <version>${lombok.version}</version>
83
+            <optional>true</optional>
84
+        </dependency>
85
+        <dependency>
86
+            <groupId>cn.hutool</groupId>
87
+            <artifactId>hutool-all</artifactId>
88
+            <version>${hutool.version}</version>
89
+        </dependency>
90
+
91
+        <!-- Documentation -->
92
+        <dependency>
93
+            <groupId>com.github.xiaoymin</groupId>
94
+            <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
95
+            <version>${knife4j.version}</version>
96
+        </dependency>
97
+
98
+        <!-- Test -->
99
+        <dependency>
100
+            <groupId>org.springframework.boot</groupId>
101
+            <artifactId>spring-boot-starter-test</artifactId>
102
+            <scope>test</scope>
103
+        </dependency>
104
+    </dependencies>
105
+
106
+    <build>
107
+        <plugins>
108
+            <plugin>
109
+                <groupId>org.springframework.boot</groupId>
110
+                <artifactId>spring-boot-maven-plugin</artifactId>
111
+                <configuration>
112
+                    <excludes>
113
+                        <exclude>
114
+                            <groupId>org.projectlombok</groupId>
115
+                            <artifactId>lombok</artifactId>
116
+                        </exclude>
117
+                    </excludes>
118
+                </configuration>
119
+            </plugin>
120
+        </plugins>
121
+    </build>
122
+</project>

+ 15
- 0
src/main/java/com/water/common/WaterCommonApplication.java Datei anzeigen

@@ -0,0 +1,15 @@
1
+package com.water.common;
2
+
3
+import org.springframework.boot.SpringApplication;
4
+import org.springframework.boot.autoconfigure.SpringBootApplication;
5
+
6
+/**
7
+ * 供水管理系统通用模块启动类
8
+ */
9
+@SpringBootApplication
10
+public class WaterCommonApplication {
11
+
12
+    public static void main(String[] args) {
13
+        SpringApplication.run(WaterCommonApplication.class, args);
14
+    }
15
+}

+ 25
- 0
src/main/java/com/water/common/config/MyBatisPlusConfig.java Datei anzeigen

@@ -0,0 +1,25 @@
1
+package com.water.common.config;
2
+
3
+import com.baomidou.mybatisplus.annotation.DbType;
4
+import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
5
+import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
6
+import org.springframework.context.annotation.Bean;
7
+import org.springframework.context.annotation.Configuration;
8
+
9
+/**
10
+ * MyBatis-Plus配置
11
+ */
12
+@Configuration
13
+public class MyBatisPlusConfig {
14
+
15
+    /**
16
+     * 添加分页插件
17
+     */
18
+    @Bean
19
+    public MybatisPlusInterceptor mybatisPlusInterceptor() {
20
+        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
21
+        // 添加分页插件
22
+        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
23
+        return interceptor;
24
+    }
25
+}

+ 30
- 0
src/main/java/com/water/common/config/SwaggerConfig.java Datei anzeigen

@@ -0,0 +1,30 @@
1
+package com.water.common.config;
2
+
3
+import io.swagger.v3.oas.models.OpenAPI;
4
+import io.swagger.v3.oas.models.info.Contact;
5
+import io.swagger.v3.oas.models.info.Info;
6
+import io.swagger.v3.oas.models.info.License;
7
+import org.springframework.context.annotation.Bean;
8
+import org.springframework.context.annotation.Configuration;
9
+
10
+/**
11
+ * Swagger配置
12
+ */
13
+@Configuration
14
+public class SwaggerConfig {
15
+
16
+    @Bean
17
+    public OpenAPI customOpenAPI() {
18
+        return new OpenAPI()
19
+                .info(new Info()
20
+                        .title("供水管理系统API文档")
21
+                        .version("1.0.0")
22
+                        .description("供水管理系统通用模块API文档")
23
+                        .contact(new Contact()
24
+                                .name("bot_dev1")
25
+                                .email("bot_dev1@xayunmei.com"))
26
+                        .license(new License()
27
+                                .name("Apache 2.0")
28
+                                .url("http://www.apache.org/licenses/LICENSE-2.0.html")));
29
+    }
30
+}

+ 102
- 0
src/main/java/com/water/common/constant/ErrorCode.java Datei anzeigen

@@ -0,0 +1,102 @@
1
+package com.water.common.constant;
2
+
3
+/**
4
+ * 错误码枚举
5
+ */
6
+public final class ErrorCode {
7
+
8
+    private ErrorCode() {
9
+        // 私有构造函数,防止实例化
10
+    }
11
+
12
+    // ========== 系统级别错误码(1000-1999) ==========
13
+    /**
14
+     * 系统异常
15
+     */
16
+    public static final int SYSTEM_ERROR = 1000;
17
+    
18
+    /**
19
+     * 参数异常
20
+     */
21
+    public static final int PARAM_ERROR = 1001;
22
+    
23
+    /**
24
+     * 认证失败
25
+     */
26
+    public static final int AUTH_FAIL = 1002;
27
+    
28
+    /**
29
+     * 权限不足
30
+     */
31
+    public static final int PERMISSION_DENIED = 1003;
32
+    
33
+    // ========== 业务级别错误码(2000-2999) ==========
34
+    /**
35
+     * 数据不存在
36
+     */
37
+    public static final int DATA_NOT_FOUND = 2000;
38
+    
39
+    /**
40
+     * 数据已存在
41
+     */
42
+    public static final int DATA_ALREADY_EXISTS = 2001;
43
+    
44
+    /**
45
+     * 操作失败
46
+     */
47
+    public static final int OPERATION_FAILED = 2002;
48
+    
49
+    /**
50
+     * 状态异常
51
+     */
52
+    public static final int STATUS_ERROR = 2003;
53
+    
54
+    // ========== 数据字典相关错误码(3000-3999) ==========
55
+    /**
56
+     * 字典类型不存在
57
+     */
58
+    public static final int DICT_TYPE_NOT_FOUND = 3000;
59
+    
60
+    /**
61
+     * 字典项不存在
62
+     */
63
+    public static final int DICT_ITEM_NOT_FOUND = 3001;
64
+    
65
+    // ========== 文件相关错误码(4000-4999) ==========
66
+    /**
67
+     * 文件上传失败
68
+     */
69
+    public static final int FILE_UPLOAD_FAILED = 4000;
70
+    
71
+    /**
72
+     * 文件下载失败
73
+     */
74
+    public static final int FILE_DOWNLOAD_FAILED = 4001;
75
+    
76
+    /**
77
+     * 文件不存在
78
+     */
79
+    public static final int FILE_NOT_FOUND = 4002;
80
+    
81
+    // ========== 验证码相关错误码(5000-5999) ==========
82
+    /**
83
+     * 验证码错误
84
+     */
85
+    public static final int CAPTCHA_ERROR = 5000;
86
+    
87
+    /**
88
+     * 验证码已过期
89
+     */
90
+    public static final int CAPTCHA_EXPIRED = 5001;
91
+    
92
+    // ========== Excel相关错误码(6000-6999) ==========
93
+    /**
94
+     * Excel解析失败
95
+     */
96
+    public static final int EXCEL_PARSE_FAILED = 6000;
97
+    
98
+    /**
99
+     * Excel生成失败
100
+     */
101
+    public static final int EXCEL_GENERATE_FAILED = 6001;
102
+}

+ 60
- 0
src/main/java/com/water/common/controller/DictController.java Datei anzeigen

@@ -0,0 +1,60 @@
1
+package com.water.common.controller;
2
+
3
+import com.water.common.entity.R;
4
+import com.water.common.entity.dict.DictItem;
5
+import com.water.common.entity.dict.DictType;
6
+import com.water.common.service.DictService;
7
+import io.swagger.v3.oas.annotations.Operation;
8
+import io.swagger.v3.oas.annotations.tags.Tag;
9
+import lombok.RequiredArgsConstructor;
10
+import org.springframework.web.bind.annotation.*;
11
+
12
+import java.util.List;
13
+import java.util.Map;
14
+
15
+/**
16
+ * 字典控制器
17
+ */
18
+@Tag(name = "字典管理", description = "字典相关接口")
19
+@RestController
20
+@RequestMapping("/dict")
21
+@RequiredArgsConstructor
22
+public class DictController {
23
+
24
+    private final DictService dictService;
25
+
26
+    @Operation(summary = "根据字典类型编码获取字典项")
27
+    @GetMapping("/items/{dictTypeCode}")
28
+    public R<List<DictItem>> getDictItems(@PathVariable String dictTypeCode) {
29
+        List<DictItem> dictItems = dictService.getDictItems(dictTypeCode);
30
+        return R.success(dictItems);
31
+    }
32
+
33
+    @Operation(summary = "根据字典类型编码和字典项编码获取字典项名称")
34
+    @GetMapping("/item-name")
35
+    public R<String> getDictItemName(@RequestParam String dictTypeCode, @RequestParam String itemCode) {
36
+        String itemName = dictService.getDictItemName(dictTypeCode, itemCode);
37
+        return R.success(itemName);
38
+    }
39
+
40
+    @Operation(summary = "根据字典类型编码和字典项值获取字典项编码")
41
+    @GetMapping("/item-code")
42
+    public R<String> getDictItemCode(@RequestParam String dictTypeCode, @RequestParam String itemValue) {
43
+        String itemCode = dictService.getDictItemCode(dictTypeCode, itemValue);
44
+        return R.success(itemCode);
45
+    }
46
+
47
+    @Operation(summary = "获取所有字典类型及字典项")
48
+    @GetMapping("/all")
49
+    public R<Map<String, List<DictItem>>> getAllDict() {
50
+        Map<String, List<DictItem>> allDict = dictService.getAllDict();
51
+        return R.success(allDict);
52
+    }
53
+
54
+    @Operation(summary = "刷新字典缓存")
55
+    @PostMapping("/refresh")
56
+    public R<Void> refreshDictCache() {
57
+        dictService.refreshDictCache();
58
+        return R.success();
59
+    }
60
+}

+ 93
- 0
src/main/java/com/water/common/entity/R.java Datei anzeigen

@@ -0,0 +1,93 @@
1
+package com.water.common.entity;
2
+
3
+import lombok.Data;
4
+import lombok.experimental.Accessors;
5
+
6
+import java.io.Serializable;
7
+import java.time.LocalDateTime;
8
+
9
+/**
10
+ * 统一响应格式
11
+ */
12
+@Data
13
+@Accessors(chain = true)
14
+public class R<T> implements Serializable {
15
+
16
+    private static final long serialVersionUID = 1L;
17
+
18
+    /**
19
+     * 成功状态码
20
+     */
21
+    public static final int SUCCESS_CODE = 200;
22
+    
23
+    /**
24
+     * 失败状态码
25
+     */
26
+    public static final int ERROR_CODE = 500;
27
+
28
+    /**
29
+     * 响应码
30
+     */
31
+    private int code;
32
+
33
+    /**
34
+     * 响应消息
35
+     */
36
+    private String message;
37
+
38
+    /**
39
+     * 响应数据
40
+     */
41
+    private T data;
42
+
43
+    /**
44
+     * 响应时间
45
+     */
46
+    private LocalDateTime timestamp;
47
+
48
+    public R() {
49
+        this.timestamp = LocalDateTime.now();
50
+    }
51
+
52
+    public static <T> R<T> success() {
53
+        return new R<T>()
54
+                .setCode(SUCCESS_CODE)
55
+                .setMessage("操作成功");
56
+    }
57
+
58
+    public static <T> R<T> success(T data) {
59
+        return new R<T>()
60
+                .setCode(SUCCESS_CODE)
61
+                .setMessage("操作成功")
62
+                .setData(data);
63
+    }
64
+
65
+    public static <T> R<T> success(String message, T data) {
66
+        return new R<T>()
67
+                .setCode(SUCCESS_CODE)
68
+                .setMessage(message)
69
+                .setData(data);
70
+    }
71
+
72
+    public static <T> R<T> error() {
73
+        return new R<T>()
74
+                .setCode(ERROR_CODE)
75
+                .setMessage("操作失败");
76
+    }
77
+
78
+    public static <T> R<T> error(String message) {
79
+        return new R<T>()
80
+                .setCode(ERROR_CODE)
81
+                .setMessage(message);
82
+    }
83
+
84
+    public static <T> R<T> error(int code, String message) {
85
+        return new R<T>()
86
+                .setCode(code)
87
+                .setMessage(message);
88
+    }
89
+
90
+    public boolean isSuccess() {
91
+        return SUCCESS_CODE == this.code;
92
+    }
93
+}

+ 67
- 0
src/main/java/com/water/common/entity/dict/DictItem.java Datei anzeigen

@@ -0,0 +1,67 @@
1
+package com.water.common.entity.dict;
2
+
3
+import com.baomidou.mybatisplus.annotation.IdType;
4
+import com.baomidou.mybatisplus.annotation.TableId;
5
+import com.baomidou.mybatisplus.annotation.TableName;
6
+import lombok.Data;
7
+
8
+import java.time.LocalDateTime;
9
+
10
+/**
11
+ * 字典项实体类
12
+ */
13
+@Data
14
+@TableName("sys_dict_item")
15
+public class DictItem {
16
+
17
+    /**
18
+     * 主键ID
19
+     */
20
+    @TableId(type = IdType.AUTO)
21
+    private Long id;
22
+
23
+    /**
24
+     * 字典类型ID
25
+     */
26
+    private Long typeId;
27
+
28
+    /**
29
+     * 字典项编码
30
+     */
31
+    private String itemCode;
32
+
33
+    /**
34
+     * 字典项名称
35
+     */
36
+    private String itemName;
37
+
38
+    /**
39
+     * 字典项值
40
+     */
41
+    private String itemValue;
42
+
43
+    /**
44
+     * 排序
45
+     */
46
+    private Integer sort;
47
+
48
+    /**
49
+     * 状态(0:停用,1:启用)
50
+     */
51
+    private Integer status;
52
+
53
+    /**
54
+     * 备注
55
+     */
56
+    private String remark;
57
+
58
+    /**
59
+     * 创建时间
60
+     */
61
+    private LocalDateTime createTime;
62
+
63
+    /**
64
+     * 更新时间
65
+     */
66
+    private LocalDateTime updateTime;
67
+}

+ 63
- 0
src/main/java/com/water/common/entity/dict/DictType.java Datei anzeigen

@@ -0,0 +1,63 @@
1
+package com.water.common.entity.dict;
2
+
3
+import com.baomidou.mybatisplus.annotation.IdType;
4
+import com.baomidou.mybatisplus.annotation.TableId;
5
+import com.baomidou.mybatisplus.annotation.TableName;
6
+import lombok.Data;
7
+
8
+import java.time.LocalDateTime;
9
+import java.util.List;
10
+
11
+/**
12
+ * 字典类型实体类
13
+ */
14
+@Data
15
+@TableName("sys_dict_type")
16
+public class DictType {
17
+
18
+    /**
19
+     * 主键ID
20
+     */
21
+    @TableId(type = IdType.AUTO)
22
+    private Long id;
23
+
24
+    /**
25
+     * 字典类型编码
26
+     */
27
+    private String dictCode;
28
+
29
+    /**
30
+     * 字典类型名称
31
+     */
32
+    private String dictName;
33
+
34
+    /**
35
+     * 字典类型描述
36
+     */
37
+    private String description;
38
+
39
+    /**
40
+     * 状态(0:停用,1:启用)
41
+     */
42
+    private Integer status;
43
+
44
+    /**
45
+     * 备注
46
+     */
47
+    private String remark;
48
+
49
+    /**
50
+     * 创建时间
51
+     */
52
+    private LocalDateTime createTime;
53
+
54
+    /**
55
+     * 更新时间
56
+     */
57
+    private LocalDateTime updateTime;
58
+
59
+    /**
60
+     * 字典项列表
61
+     */
62
+    private transient List<DictItem> dictItems;
63
+}

+ 37
- 0
src/main/java/com/water/common/exception/BusinessException.java Datei anzeigen

@@ -0,0 +1,37 @@
1
+package com.water.common.exception;
2
+
3
+/**
4
+ * 业务异常
5
+ */
6
+public class BusinessException extends RuntimeException {
7
+
8
+    private static final long serialVersionUID = 1L;
9
+
10
+    private int code;
11
+
12
+    public BusinessException(String message) {
13
+        super(message);
14
+    }
15
+
16
+    public BusinessException(int code, String message) {
17
+        super(message);
18
+        this.code = code;
19
+    }
20
+
21
+    public BusinessException(String message, Throwable cause) {
22
+        super(message, cause);
23
+    }
24
+
25
+    public BusinessException(int code, String message, Throwable cause) {
26
+        super(message, cause);
27
+        this.code = code;
28
+    }
29
+
30
+    public int getCode() {
31
+        return code;
32
+    }
33
+
34
+    public void setCode(int code) {
35
+        this.code = code;
36
+    }
37
+}

+ 84
- 0
src/main/java/com/water/common/exception/GlobalExceptionHandler.java Datei anzeigen

@@ -0,0 +1,84 @@
1
+package com.water.common.exception;
2
+
3
+import com.water.common.entity.R;
4
+import lombok.extern.slf4j.Slf4j;
5
+import org.springframework.http.HttpStatus;
6
+import org.springframework.validation.BindingResult;
7
+import org.springframework.web.bind.MethodArgumentNotValidException;
8
+import org.springframework.web.bind.annotation.ExceptionHandler;
9
+import org.springframework.web.bind.annotation.ResponseStatus;
10
+import org.springframework.web.bind.annotation.RestControllerAdvice;
11
+
12
+import javax.validation.ConstraintViolation;
13
+import javax.validation.ConstraintViolationException;
14
+import java.time.LocalDateTime;
15
+import java.util.stream.Collectors;
16
+
17
+/**
18
+ * 全局异常处理器
19
+ */
20
+@Slf4j
21
+@RestControllerAdvice
22
+public class GlobalExceptionHandler {
23
+
24
+    /**
25
+     * 处理业务异常
26
+     */
27
+    @ExceptionHandler(BusinessException.class)
28
+    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
29
+    public R<Void> handleBusinessException(BusinessException e) {
30
+        log.error("业务异常: {}", e.getMessage(), e);
31
+        return R.error(e.getCode(), e.getMessage());
32
+    }
33
+
34
+    /**
35
+     * 处理参数校验异常
36
+     */
37
+    @ExceptionHandler(MethodArgumentNotValidException.class)
38
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
39
+    public R<Void> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
40
+        BindingResult bindingResult = e.getBindingResult();
41
+        String message = bindingResult.getFieldErrors()
42
+                .stream()
43
+                .map(error -> error.getField() + ": " + error.getDefaultMessage())
44
+                .collect(Collectors.joining(", "));
45
+        
46
+        log.error("参数校验异常: {}", message);
47
+        return R.error(400, message);
48
+    }
49
+
50
+    /**
51
+     * 处理约束校验异常
52
+     */
53
+    @ExceptionHandler(ConstraintViolationException.class)
54
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
55
+    public R<Void> handleConstraintViolationException(ConstraintViolationException e) {
56
+        String message = e.getConstraintViolations()
57
+                .stream()
58
+                .map(ConstraintViolation::getMessage)
59
+                .collect(Collectors.joining(", "));
60
+        
61
+        log.error("约束校验异常: {}", message);
62
+        return R.error(400, message);
63
+    }
64
+
65
+    /**
66
+     * 处理系统异常
67
+     */
68
+    @ExceptionHandler(Exception.class)
69
+    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
70
+    public R<Void> handleException(Exception e) {
71
+        log.error("系统异常: {}", e.getMessage(), e);
72
+        return R.error("系统繁忙,请稍后重试");
73
+    }
74
+
75
+    /**
76
+     * 处理运行时异常
77
+     */
78
+    @ExceptionHandler(RuntimeException.class)
79
+    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
80
+    public R<Void> handleRuntimeException(RuntimeException e) {
81
+        log.error("运行时异常: {}", e.getMessage(), e);
82
+        return R.error("系统异常,请联系管理员");
83
+    }
84
+}

+ 20
- 0
src/main/java/com/water/common/mapper/DictTypeMapper.java Datei anzeigen

@@ -0,0 +1,20 @@
1
+package com.water.common.mapper;
2
+
3
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
4
+import com.water.common.entity.dict.DictType;
5
+import org.apache.ibatis.annotations.Mapper;
6
+
7
+/**
8
+ * 字典类型Mapper接口
9
+ */
10
+@Mapper
11
+public interface DictTypeMapper extends BaseMapper<DictType> {
12
+
13
+    /**
14
+     * 根据字典类型ID查询字典项
15
+     *
16
+     * @param typeId 字典类型ID
17
+     * @return 字典项列表
18
+     */
19
+    List<com.water.common.entity.dict.DictItem> selectDictItems(Long typeId);
20
+}

+ 51
- 0
src/main/java/com/water/common/service/DictService.java Datei anzeigen

@@ -0,0 +1,51 @@
1
+package com.water.common.service;
2
+
3
+import com.water.common.entity.dict.DictType;
4
+import com.baomidou.mybatisplus.extension.service.IService;
5
+
6
+import java.util.List;
7
+import java.util.Map;
8
+
9
+/**
10
+ * 字典服务接口
11
+ */
12
+public interface DictService extends IService<DictType> {
13
+
14
+    /**
15
+     * 根据字典类型编码获取字典项
16
+     *
17
+     * @param dictTypeCode 字典类型编码
18
+     * @return 字典项列表
19
+     */
20
+    List<DictItem> getDictItems(String dictTypeCode);
21
+
22
+    /**
23
+     * 根据字典类型编码和字典项编码获取字典项名称
24
+     *
25
+     * @param dictTypeCode 字典类型编码
26
+     * @param itemCode     字典项编码
27
+     * @return 字典项名称
28
+     */
29
+    String getDictItemName(String dictTypeCode, String itemCode);
30
+
31
+    /**
32
+     * 根据字典类型编码和字典项值获取字典项编码
33
+     *
34
+     * @param dictTypeCode 字典类型编码
35
+     * @param itemValue    字典项值
36
+     * @return 字典项编码
37
+     */
38
+    String getDictItemCode(String dictTypeCode, String itemValue);
39
+
40
+    /**
41
+     * 获取所有字典类型及字典项
42
+     *
43
+     * @return 字典类型和字典项的映射
44
+     */
45
+    Map<String, List<DictItem>> getAllDict();
46
+
47
+    /**
48
+     * 刷新字典缓存
49
+     */
50
+    void refreshDictCache();
51
+}

+ 120
- 0
src/main/java/com/water/common/service/impl/DictServiceImpl.java Datei anzeigen

@@ -0,0 +1,120 @@
1
+package com.water.common.service.impl;
2
+
3
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
4
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
5
+import com.water.common.constant.ErrorCode;
6
+import com.water.common.entity.dict.DictItem;
7
+import com.water.common.entity.dict.DictType;
8
+import com.water.common.exception.BusinessException;
9
+import com.water.common.mapper.DictTypeMapper;
10
+import com.water.common.service.DictService;
11
+import lombok.RequiredArgsConstructor;
12
+import lombok.extern.slf4j.Slf4j;
13
+import org.springframework.beans.factory.annotation.Autowired;
14
+import org.springframework.data.redis.core.RedisTemplate;
15
+import org.springframework.stereotype.Service;
16
+import org.springframework.util.CollectionUtils;
17
+
18
+import java.util.List;
19
+import java.util.Map;
20
+import java.util.concurrent.TimeUnit;
21
+import java.util.stream.Collectors;
22
+
23
+/**
24
+ * 字典服务实现类
25
+ */
26
+@Slf4j
27
+@Service
28
+@RequiredArgsConstructor
29
+public class DictServiceImpl extends ServiceImpl<DictTypeMapper, DictType> implements DictService {
30
+
31
+    @Autowired
32
+    private RedisTemplate<String, Object> redisTemplate;
33
+
34
+    private static final String DICT_CACHE_PREFIX = "dict:";
35
+
36
+    @Override
37
+    public List<DictItem> getDictItems(String dictTypeCode) {
38
+        // 先从缓存获取
39
+        String cacheKey = DICT_CACHE_PREFIX + dictTypeCode;
40
+        List<DictItem> cachedItems = (List<DictItem>) redisTemplate.opsForValue().get(cacheKey);
41
+        
42
+        if (cachedItems != null) {
43
+            return cachedItems;
44
+        }
45
+
46
+        // 缓存未命中,从数据库获取
47
+        DictType dictType = getByDictCode(dictTypeCode);
48
+        if (dictType == null) {
49
+            throw new BusinessException(ErrorCode.DICT_TYPE_NOT_FOUND, "字典类型不存在: " + dictTypeCode);
50
+        }
51
+
52
+        LambdaQueryWrapper<DictItem> queryWrapper = new LambdaQueryWrapper<>();
53
+        queryWrapper.eq(DictItem::getTypeId, dictType.getId())
54
+                .eq(DictItem::getStatus, 1)
55
+                .orderByAsc(DictItem::getSort)
56
+                .orderByAsc(DictItem::getCreateTime);
57
+
58
+        List<DictItem> dictItems = baseMapper.selectDictItems(dictType.getId());
59
+        
60
+        // 缓存到Redis,设置过期时间为30分钟
61
+        redisTemplate.opsForValue().set(cacheKey, dictItems, 30, TimeUnit.MINUTES);
62
+        
63
+        return dictItems;
64
+    }
65
+
66
+    @Override
67
+    public String getDictItemName(String dictTypeCode, String itemCode) {
68
+        List<DictItem> dictItems = getDictItems(dictTypeCode);
69
+        
70
+        return dictItems.stream()
71
+                .filter(item -> itemCode.equals(item.getItemCode()))
72
+                .map(DictItem::getItemName)
73
+                .findFirst()
74
+                .orElseThrow(() -> new BusinessException(ErrorCode.DICT_ITEM_NOT_FOUND, 
75
+                        "字典项不存在: " + dictTypeCode + "." + itemCode));
76
+    }
77
+
78
+    @Override
79
+    public String getDictItemCode(String dictTypeCode, String itemValue) {
80
+        List<DictItem> dictItems = getDictItems(dictTypeCode);
81
+        
82
+        return dictItems.stream()
83
+                .filter(item -> itemValue.equals(item.getItemValue()))
84
+                .map(DictItem::getItemCode)
85
+                .findFirst()
86
+                .orElseThrow(() -> new BusinessException(ErrorCode.DICT_ITEM_NOT_FOUND, 
87
+                        "字典项不存在: " + dictTypeCode + "." + itemValue));
88
+    }
89
+
90
+    @Override
91
+    public Map<String, List<DictItem>> getAllDict() {
92
+        List<DictType> dictTypes = list(new LambdaQueryWrapper<DictType>()
93
+                .eq(DictType::getStatus, 1)
94
+                .orderByAsc(DictType::getCreateTime));
95
+
96
+        return dictTypes.stream()
97
+                .collect(Collectors.toMap(
98
+                        DictType::getDictCode,
99
+                        dictType -> getDictItems(dictType.getDictCode())
100
+                ));
101
+    }
102
+
103
+    @Override
104
+    public void refreshDictCache() {
105
+        // 清除所有字典缓存
106
+        redisTemplate.delete(redisTemplate.keys(DICT_CACHE_PREFIX + "*"));
107
+        log.info("字典缓存已刷新");
108
+    }
109
+
110
+    /**
111
+     * 根据字典类型编码查询字典类型
112
+     */
113
+    private DictType getByDictCode(String dictTypeCode) {
114
+        LambdaQueryWrapper<DictType> queryWrapper = new LambdaQueryWrapper<>();
115
+        queryWrapper.eq(DictType::getDictCode, dictTypeCode)
116
+                .eq(DictType::getStatus, 1);
117
+        
118
+        return baseMapper.selectOne(queryWrapper);
119
+    }
120
+}

+ 72
- 0
src/main/resources/application.yml Datei anzeigen

@@ -0,0 +1,72 @@
1
+server:
2
+  port: 8081
3
+  servlet:
4
+    context-path: /common
5
+
6
+spring:
7
+  application:
8
+    name: water-common
9
+  
10
+  # 数据库配置
11
+  datasource:
12
+    driver-class-name: com.mysql.cj.jdbc.Driver
13
+    url: jdbc:mysql://localhost:3306/water_management?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
14
+    username: root
15
+    password: root
16
+  
17
+  # Redis配置
18
+  data:
19
+    redis:
20
+      host: localhost
21
+      port: 6379
22
+      password: 
23
+      database: 0
24
+      timeout: 3000ms
25
+      lettuce:
26
+        pool:
27
+          max-active: 8
28
+          max-wait: -1ms
29
+          max-idle: 8
30
+          min-idle: 0
31
+  
32
+  # 文件上传配置
33
+  servlet:
34
+    multipart:
35
+      enabled: true
36
+      max-file-size: 10MB
37
+      max-request-size: 10MB
38
+  
39
+  # Jackson配置
40
+  jackson:
41
+    date-format: yyyy-MM-dd HH:mm:ss
42
+    time-zone: GMT+8
43
+
44
+# MyBatis-Plus配置
45
+mybatis-plus:
46
+  configuration:
47
+    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
48
+  mapper-locations: classpath:mapper/*.xml, classpath:mapper/**/*.xml
49
+  global-config:
50
+    db-config:
51
+      id-type: auto
52
+      table-prefix: sys_
53
+
54
+# Knife4j配置
55
+knife4j:
56
+  enable: true
57
+  openapi:
58
+    title: 供水管理系统API文档
59
+    description: 供水管理系统通用模块API文档
60
+    email: bot_dev1@xayunmei.com
61
+    concat: bot_dev1
62
+    url: https://git.xayunmei.com
63
+    license: Apache 2.0
64
+    license-url: https://www.apache.org/licenses/LICENSE-2.0
65
+
66
+# 日志配置
67
+logging:
68
+  level:
69
+    com.water.common: debug
70
+    org.springframework.web: debug
71
+  pattern:
72
+    console: '%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n'

+ 18
- 0
src/main/resources/mapper/DictTypeMapper.xml Datei anzeigen

@@ -0,0 +1,18 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
3
+<mapper namespace="com.water.common.mapper.DictTypeMapper">
4
+
5
+    <!-- 查询字典项 -->
6
+    <select id="selectDictItems" resultType="com.water.common.entity.dict.DictItem">
7
+        SELECT 
8
+            id, type_id, item_code, item_name, item_value, sort, status, remark, create_time, update_time
9
+        FROM 
10
+            sys_dict_item
11
+        WHERE 
12
+            type_id = #{typeId}
13
+            AND status = 1
14
+        ORDER BY 
15
+            sort ASC, create_time ASC
16
+    </select>
17
+
18
+</mapper>