Browse Source

!23 错误码
Merge pull request !23 from dylan/feature/errorcode

芋道源码 4 năm trước cách đây
mục cha
commit
27e1fe4468
43 tập tin đã thay đổi với 1995 bổ sung22 xóa
  1. 3 3
      README.md
  2. 54 0
      ruoyi-ui/src/api/system/errorCode.js
  3. 1 0
      ruoyi-ui/src/utils/dict.js
  4. 256 0
      ruoyi-ui/src/views/system/errorCode/index.vue
  5. 143 13
      sql/ruoyi-vue-pro.sql
  6. 36 0
      src/main/java/cn/iocoder/dashboard/framework/errorcode/config/ErrorCodeConfiguration.java
  7. 26 0
      src/main/java/cn/iocoder/dashboard/framework/errorcode/config/ErrorCodeProperties.java
  8. 34 0
      src/main/java/cn/iocoder/dashboard/framework/errorcode/core/dto/ErrorCodeAutoGenerateReqDTO.java
  9. 28 0
      src/main/java/cn/iocoder/dashboard/framework/errorcode/core/dto/ErrorCodeRespDTO.java
  10. 15 0
      src/main/java/cn/iocoder/dashboard/framework/errorcode/core/generator/ErrorCodeAutoGenerator.java
  11. 98 0
      src/main/java/cn/iocoder/dashboard/framework/errorcode/core/generator/ErrorCodeAutoGeneratorImpl.java
  12. 24 0
      src/main/java/cn/iocoder/dashboard/framework/errorcode/core/loader/ErrorCodeLoader.java
  13. 73 0
      src/main/java/cn/iocoder/dashboard/framework/errorcode/core/loader/ErrorCodeLoaderImpl.java
  14. 35 0
      src/main/java/cn/iocoder/dashboard/framework/errorcode/core/service/ErrorCodeFrameworkService.java
  15. 35 0
      src/main/java/cn/iocoder/dashboard/framework/validator/InEnum.java
  16. 44 0
      src/main/java/cn/iocoder/dashboard/framework/validator/InEnumValidator.java
  17. 3 0
      src/main/java/cn/iocoder/dashboard/modules/system/controller/dept/vo/post/SysPostExportReqVO.java
  18. 3 0
      src/main/java/cn/iocoder/dashboard/modules/system/controller/dept/vo/post/SysPostPageReqVO.java
  19. 12 0
      src/main/java/cn/iocoder/dashboard/modules/system/controller/errorcode/SysErrorCodeController.http
  20. 89 0
      src/main/java/cn/iocoder/dashboard/modules/system/controller/errorcode/SysErrorCodeController.java
  21. 30 0
      src/main/java/cn/iocoder/dashboard/modules/system/controller/errorcode/vo/SysErrorCodeBaseVO.java
  22. 14 0
      src/main/java/cn/iocoder/dashboard/modules/system/controller/errorcode/vo/SysErrorCodeCreateReqVO.java
  23. 42 0
      src/main/java/cn/iocoder/dashboard/modules/system/controller/errorcode/vo/SysErrorCodeExcelVO.java
  24. 36 0
      src/main/java/cn/iocoder/dashboard/modules/system/controller/errorcode/vo/SysErrorCodeExportReqVO.java
  25. 41 0
      src/main/java/cn/iocoder/dashboard/modules/system/controller/errorcode/vo/SysErrorCodePageReqVO.java
  26. 26 0
      src/main/java/cn/iocoder/dashboard/modules/system/controller/errorcode/vo/SysErrorCodeRespVO.java
  27. 21 0
      src/main/java/cn/iocoder/dashboard/modules/system/controller/errorcode/vo/SysErrorCodeUpdateReqVO.java
  28. 42 0
      src/main/java/cn/iocoder/dashboard/modules/system/convert/errorcode/SysErrorCodeConvert.java
  29. 50 0
      src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/errorcode/SysErrorCodeDO.java
  30. 4 1
      src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dept/SysPostMapper.java
  31. 52 0
      src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/errorcode/SysErrorCodeMapper.java
  32. 3 0
      src/main/java/cn/iocoder/dashboard/modules/system/enums/SysErrorCodeConstants.java
  33. 1 1
      src/main/java/cn/iocoder/dashboard/modules/system/enums/dict/SysDictTypeEnum.java
  34. 39 0
      src/main/java/cn/iocoder/dashboard/modules/system/enums/errorcode/SysErrorCodeTypeEnum.java
  35. 1 1
      src/main/java/cn/iocoder/dashboard/modules/system/service/auth/impl/SysAuthServiceImpl.java
  36. 67 0
      src/main/java/cn/iocoder/dashboard/modules/system/service/errorcode/SysErrorCodeService.java
  37. 174 0
      src/main/java/cn/iocoder/dashboard/modules/system/service/errorcode/impl/SysErrorCodeServiceImpl.java
  38. 10 0
      src/main/java/cn/iocoder/dashboard/util/date/DateUtils.java
  39. 6 0
      src/main/resources/application.yaml
  40. 1 1
      src/main/resources/codegen/java/service/serviceImpl.vm
  41. 305 0
      src/test/java/cn/iocoder/dashboard/modules/system/service/errorcode/SysErrorCodeServiceTest.java
  42. 1 0
      src/test/resources/sql/clean.sql
  43. 17 2
      src/test/resources/sql/create_tables.sql

+ 3 - 3
README.md

@@ -37,7 +37,8 @@
 |  | 字典管理 | 对系统中经常使用的一些较为固定的数据进行维护 |
 | 🚀 | 短信管理 | 短信渠道、短息模板、短信日志,对接阿里云、云片等主流短信平台 |
 | 🚀 | 操作日志 | 系统正常操作日志记录和查询,集成 Swagger 生成日志内容 |
-|  | 登录日志 | 系统登录日志记录查询包含登录异常 |
+|  | 登录日志 | 系统登录日志记录查询,包含登录异常 |
+| 🚀 | 错误码管理 | 系统所有错误码的管理,可在线修改错误提示,无需重启服务 |
 |  | 通知公告 | 系统通知公告信息发布维护 |
 
 计划新增功能:
@@ -53,7 +54,7 @@
 | 🚀 | 文件服务 | 支持本地文件存储,同时支持兼容 Amazon S3 协议的云服务、开源组件 | 
 | 🚀 | API 日志 | 包括 RESTful API 访问日志、异常日志两部分,方便排查 API 相关的问题 |
 |  | MySQL 监控 | 监视当前系统数据库连接池状态,可进行分析SQL找出系统性能瓶颈 |
-| | Redis 监控 |监控 Redis 数据库的使用情况,使用的 Redis Key 管理 |
+|  | Redis 监控 |监控 Redis 数据库的使用情况,使用的 Redis Key 管理 |
 | 🚀 |Java 监控 | 基于 Spring Boot Admin 实现 Java 应用的监控 |
 | 🚀 | 链路追踪 | 基于 SkyWalking 实现性能监控,特别是链路的追踪 |
 | 🚀 | 分布式锁 | 基于 Redis 实现分布式锁,满足并发场景 |
@@ -64,7 +65,6 @@
 
 计划新增:
 * 工作流
-* 错误码
 
 ### 研发工具
 

+ 54 - 0
ruoyi-ui/src/api/system/errorCode.js

@@ -0,0 +1,54 @@
+import request from '@/utils/request'
+
+// 创建错误码
+export function createErrorCode(data) {
+  return request({
+    url: '/system/error-code/create',
+    method: 'post',
+    data: data
+  })
+}
+
+// 更新错误码
+export function updateErrorCode(data) {
+  return request({
+    url: '/system/error-code/update',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除错误码
+export function deleteErrorCode(id) {
+  return request({
+    url: '/system/error-code/delete?id=' + id,
+    method: 'delete'
+  })
+}
+
+// 获得错误码
+export function getErrorCode(id) {
+  return request({
+    url: '/system/error-code/get?id=' + id,
+    method: 'get'
+  })
+}
+
+// 获得错误码分页
+export function getErrorCodePage(query) {
+  return request({
+    url: '/system/error-code/page',
+    method: 'get',
+    params: query
+  })
+}
+
+// 导出错误码 Excel
+export function exportErrorCodeExcel(query) {
+  return request({
+    url: '/system/error-code/export-excel',
+    method: 'get',
+    params: query,
+    responseType: 'blob'
+  })
+}

+ 1 - 0
ruoyi-ui/src/utils/dict.js

@@ -21,6 +21,7 @@ export const DICT_TYPE = {
   SYS_SMS_TEMPLATE_TYPE: 'sys_sms_template_type',
   SYS_SMS_SEND_STATUS: 'sys_sms_send_status',
   SYS_SMS_RECEIVE_STATUS: 'sys_sms_receive_status',
+  SYS_ERROR_CODE_TYPE: 'sys_error_code_type',
 
   INF_REDIS_TIMEOUT_TYPE: 'inf_redis_timeout_type',
   INF_JOB_STATUS: 'inf_job_status',

+ 256 - 0
ruoyi-ui/src/views/system/errorCode/index.vue

@@ -0,0 +1,256 @@
+<template>
+  <div class="app-container">
+
+    <!-- 搜索工作栏 -->
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="100px">
+      <el-form-item label="错误码类型" prop="type">
+        <el-select v-model="queryParams.type" placeholder="请选择错误码类型" clearable size="small">
+          <el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYS_ERROR_CODE_TYPE)"
+                     :key="dict.value" :label="dict.label" :value="dict.value"/>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="应用名" prop="applicationName">
+        <el-input v-model="queryParams.applicationName" placeholder="请输入应用名" clearable size="small" @keyup.enter.native="handleQuery"/>
+      </el-form-item>
+      <el-form-item label="错误码编码" prop="code">
+        <el-input v-model="queryParams.code" placeholder="请输入错误码编码" clearable size="small" @keyup.enter.native="handleQuery"/>
+      </el-form-item>
+      <el-form-item label="错误码提示" prop="message">
+        <el-input v-model="queryParams.message" placeholder="请输入错误码提示" clearable size="small" @keyup.enter.native="handleQuery"/>
+      </el-form-item>
+      <el-form-item label="创建时间">
+        <el-date-picker v-model="dateRangeCreateTime" size="small" style="width: 240px" value-format="yyyy-MM-dd"
+                        type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <!-- 操作工具栏 -->
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
+                   v-hasPermi="['system:error-code:create']">新增</el-button>
+      </el-col>
+      <el-col :span="1.5">
+        <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport"
+                   v-hasPermi="['system:error-code:export']">导出</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <!-- 列表 -->
+    <el-table v-loading="loading" :data="list">
+      <el-table-column label="编号" align="center" prop="id" />
+      <el-table-column label="类型" align="center" prop="type" width="80">
+        <template slot-scope="scope">
+          <span>{{ getDictDataLabel(DICT_TYPE.SYS_ERROR_CODE_TYPE, scope.row.type) }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="应用名" align="center" prop="applicationName" width="200" />
+      <el-table-column label="错误码编码" align="center" prop="code" width="100" />
+      <el-table-column label="错误码提示" align="center" prop="message" width="300" />
+      <el-table-column label="备注" align="center" prop="memo" width="200" />
+      <el-table-column label="创建时间" align="center" prop="createTime" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.createTime) }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
+                     v-hasPermi="['system:error-code:update']">修改</el-button>
+          <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
+                     v-hasPermi="['system:error-code:delete']">删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 分页组件 -->
+    <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
+                @pagination="getList"/>
+
+    <!-- 对话框(添加 / 修改) -->
+    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="100px">
+        <el-form-item label="应用名" prop="applicationName">
+          <el-input v-model="form.applicationName" placeholder="请输入应用名" />
+        </el-form-item>
+        <el-form-item label="错误码编码" prop="code">
+          <el-input v-model="form.code" placeholder="请输入错误码编码" />
+        </el-form-item>
+        <el-form-item label="错误码提示" prop="message">
+          <el-input v-model="form.message" placeholder="请输入错误码提示" />
+        </el-form-item>
+        <el-form-item label="备注" prop="memo">
+          <el-input v-model="form.memo" placeholder="请输入备注" />
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { createErrorCode, updateErrorCode, deleteErrorCode, getErrorCode, getErrorCodePage, exportErrorCodeExcel } from "@/api/system/errorCode";
+
+export default {
+  name: "ErrorCode",
+  components: {
+  },
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 错误码列表
+      list: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      dateRangeCreateTime: [],
+      // 查询参数
+      queryParams: {
+        pageNo: 1,
+        pageSize: 10,
+        type: null,
+        applicationName: null,
+        code: null,
+        message: null,
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+        applicationName: [{ required: true, message: "应用名不能为空", trigger: "blur" }],
+        code: [{ required: true, message: "错误码编码不能为空", trigger: "blur" }],
+        message: [{ required: true, message: "错误码提示不能为空", trigger: "blur" }],
+      }
+    };
+  },
+  created() {
+    this.getList();
+  },
+  methods: {
+    /** 查询列表 */
+    getList() {
+      this.loading = true;
+      // 处理查询参数
+      let params = {...this.queryParams};
+      this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime');
+      // 执行查询
+      getErrorCodePage(params).then(response => {
+        this.list = response.data.list;
+        this.total = response.data.total;
+        this.loading = false;
+      });
+    },
+    /** 取消按钮 */
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    /** 表单重置 */
+    reset() {
+      this.form = {
+        id: undefined,
+        applicationName: undefined,
+        code: undefined,
+        message: undefined,
+        memo: undefined,
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNo = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.dateRangeCreateTime = [];
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.open = true;
+      this.title = "添加错误码";
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      const id = row.id;
+      getErrorCode(id).then(response => {
+        this.form = response.data;
+        this.open = true;
+        this.title = "修改错误码";
+      });
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (!valid) {
+          return;
+        }
+        // 修改的提交
+        if (this.form.id != null) {
+          updateErrorCode(this.form).then(response => {
+            this.msgSuccess("修改成功");
+            this.open = false;
+            this.getList();
+          });
+          return;
+        }
+        // 添加的提交
+        createErrorCode(this.form).then(response => {
+          this.msgSuccess("新增成功");
+          this.open = false;
+          this.getList();
+        });
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const id = row.id;
+      this.$confirm('是否确认删除错误码编号为"' + id + '"的数据项?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function() {
+        return deleteErrorCode(id);
+      }).then(() => {
+        this.getList();
+        this.msgSuccess("删除成功");
+      })
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      // 处理查询参数
+      let params = {...this.queryParams};
+      params.pageNo = undefined;
+      params.pageSize = undefined;
+      this.addBeginAndEndTime(params, this.dateRangeCreateTime, 'createTime');
+      // 执行导出
+      this.$confirm('是否确认导出所有错误码数据项?', "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      }).then(function() {
+        return exportErrorCodeExcel(params);
+      }).then(response => {
+        this.downloadExcel(response, '错误码.xls');
+      })
+    }
+  }
+};
+</script>

+ 143 - 13
sql/ruoyi-vue-pro.sql

@@ -11,7 +11,7 @@
  Target Server Version : 50718
  File Encoding         : 65001
 
- Date: 18/04/2021 00:36:06
+ Date: 22/04/2021 00:48:26
 */
 
 SET NAMES utf8mb4;
@@ -43,7 +43,7 @@ CREATE TABLE `inf_api_access_log` (
   `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   PRIMARY KEY (`id`) USING BTREE
-) ENGINE=InnoDB AUTO_INCREMENT=3750 DEFAULT CHARSET=utf8mb4 COMMENT='API 访问日志表';
+) ENGINE=InnoDB AUTO_INCREMENT=4229 DEFAULT CHARSET=utf8mb4 COMMENT='API 访问日志表';
 
 -- ----------------------------
 -- Records of inf_api_access_log
@@ -84,7 +84,7 @@ CREATE TABLE `inf_api_error_log` (
   `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   PRIMARY KEY (`id`) USING BTREE
-) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COMMENT='系统异常日志';
+) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COMMENT='系统异常日志';
 
 -- ----------------------------
 -- Records of inf_api_error_log
@@ -118,7 +118,7 @@ CREATE TABLE `inf_config` (
 -- ----------------------------
 BEGIN;
 INSERT INTO `inf_config` VALUES (1, 'ui', 1, '主框架页-默认皮肤样式名称', 'sys.index.skinName', 'skin-blue', b'0', '蓝色 skin-blue、绿色 skin-green、紫色 skin-purple、红色 skin-red、黄色 skin-yellow', 'admin', '2021-01-05 17:03:48', '', '2021-01-05 17:03:48', b'0');
-INSERT INTO `inf_config` VALUES (2, 'biz', 1, '用户管理-账号初始密码', 'sys.user.initPassword', '123456', b'0', '初始化密码 123456', 'admin', '2021-01-05 17:03:48', '', '2021-01-21 02:13:02', b'0');
+INSERT INTO `inf_config` VALUES (2, 'biz', 1, '用户管理-账号初始密码', 'sys.user.init-password', '123456', b'0', '初始化密码 123456', 'admin', '2021-01-05 17:03:48', '', '2021-04-13 03:48:02', b'0');
 INSERT INTO `inf_config` VALUES (3, 'ui', 1, '主框架页-侧边栏主题', 'sys.index.sideTheme', 'theme-dark', b'0', '深色主题theme-dark,浅色主题theme-light', 'admin', '2021-01-05 17:03:48', '', '2021-01-19 03:05:21', b'0');
 INSERT INTO `inf_config` VALUES (4, '1', 2, 'xxx', 'demo.test', '10', b'0', '5', '', '2021-01-19 03:10:26', '', '2021-01-20 09:25:55', b'0');
 INSERT INTO `inf_config` VALUES (5, 'xxx', 2, 'xxx', 'xxx', 'xxx', b'1', 'xxx', '', '2021-02-09 20:06:47', '', '2021-02-09 20:06:47', b'0');
@@ -201,7 +201,7 @@ CREATE TABLE `inf_job_log` (
   `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   PRIMARY KEY (`id`) USING BTREE
-) ENGINE=InnoDB AUTO_INCREMENT=2322 DEFAULT CHARSET=utf8mb4 COMMENT='定时任务日志表';
+) ENGINE=InnoDB AUTO_INCREMENT=2527 DEFAULT CHARSET=utf8mb4 COMMENT='定时任务日志表';
 
 -- ----------------------------
 -- Records of inf_job_log
@@ -264,7 +264,7 @@ CREATE TABLE `sys_dict_data` (
   `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   PRIMARY KEY (`id`) USING BTREE
-) ENGINE=InnoDB AUTO_INCREMENT=78 DEFAULT CHARSET=utf8mb4 COMMENT='字典数据表';
+) ENGINE=InnoDB AUTO_INCREMENT=80 DEFAULT CHARSET=utf8mb4 COMMENT='字典数据表';
 
 -- ----------------------------
 -- Records of sys_dict_data
@@ -337,6 +337,8 @@ INSERT INTO `sys_dict_data` VALUES (74, 0, '等待结果', '0', 'sys_sms_receive
 INSERT INTO `sys_dict_data` VALUES (75, 1, '接收成功', '10', 'sys_sms_receive_status', 0, NULL, '1', '2021-04-11 20:29:25', '1', '2021-04-11 20:29:35', b'0');
 INSERT INTO `sys_dict_data` VALUES (76, 2, '接收失败', '20', 'sys_sms_receive_status', 0, NULL, '1', '2021-04-11 20:29:31', '1', '2021-04-11 20:29:39', b'0');
 INSERT INTO `sys_dict_data` VALUES (77, 0, '调试(钉钉)', 'DEBUG_DING_TALK', 'sys_sms_channel_code', 0, NULL, '1', '2021-04-13 00:20:37', '1', '2021-04-13 00:20:37', b'0');
+INSERT INTO `sys_dict_data` VALUES (78, 1, '自动生成', '1', 'sys_error_code_type', 0, NULL, '1', '2021-04-21 00:06:48', '1', '2021-04-13 22:06:44', b'0');
+INSERT INTO `sys_dict_data` VALUES (79, 2, '手动编辑', '2', 'sys_error_code_type', 0, NULL, '1', '2021-04-21 00:07:14', '1', '2021-04-13 22:06:49', b'0');
 COMMIT;
 
 -- ----------------------------
@@ -356,7 +358,7 @@ CREATE TABLE `sys_dict_type` (
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   PRIMARY KEY (`id`) USING BTREE,
   UNIQUE KEY `dict_type` (`type`)
-) ENGINE=InnoDB AUTO_INCREMENT=115 DEFAULT CHARSET=utf8mb4 COMMENT='字典类型表';
+) ENGINE=InnoDB AUTO_INCREMENT=116 DEFAULT CHARSET=utf8mb4 COMMENT='字典类型表';
 
 -- ----------------------------
 -- Records of sys_dict_type
@@ -383,6 +385,109 @@ INSERT INTO `sys_dict_type` VALUES (111, '短信渠道编码', 'sys_sms_channel_
 INSERT INTO `sys_dict_type` VALUES (112, '短信模板的类型', 'sys_sms_template_type', 0, NULL, '1', '2021-04-05 21:50:43', '1', '2021-04-05 21:50:43', b'0');
 INSERT INTO `sys_dict_type` VALUES (113, '短信发送状态', 'sys_sms_send_status', 0, NULL, '1', '2021-04-11 20:18:03', '1', '2021-04-11 09:30:02', b'0');
 INSERT INTO `sys_dict_type` VALUES (114, '短信接收状态', 'sys_sms_receive_status', 0, NULL, '1', '2021-04-11 20:27:14', '1', '2021-04-11 20:27:14', b'0');
+INSERT INTO `sys_dict_type` VALUES (115, '错误码的类型', 'sys_error_code_type', 0, NULL, '1', '2021-04-21 00:06:30', '1', '2021-04-13 22:07:12', b'0');
+COMMIT;
+
+-- ----------------------------
+-- Table structure for sys_error_code
+-- ----------------------------
+DROP TABLE IF EXISTS `sys_error_code`;
+CREATE TABLE `sys_error_code` (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '错误码编号',
+  `type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '错误码类型',
+  `application_name` varchar(50) NOT NULL COMMENT '应用名',
+  `code` int(11) NOT NULL DEFAULT '0' COMMENT '错误码编码',
+  `message` varchar(512) NOT NULL DEFAULT '' COMMENT '错误码错误提示',
+  `memo` varchar(512) DEFAULT '' COMMENT '备注',
+  `creator` varchar(64) DEFAULT '' COMMENT '创建者',
+  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  `updater` varchar(64) DEFAULT '' COMMENT '更新者',
+  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE=InnoDB AUTO_INCREMENT=4016 DEFAULT CHARSET=utf8mb4 COMMENT='错误码表';
+
+-- ----------------------------
+-- Records of sys_error_code
+-- ----------------------------
+BEGIN;
+INSERT INTO `sys_error_code` VALUES (3939, 2, 'dashboard', 1001000001, '参数配置不存在', 'ceshi', NULL, '2021-04-20 23:52:56', '1', '2021-04-21 23:44:15', b'0');
+INSERT INTO `sys_error_code` VALUES (3940, 1, 'dashboard', 1001000002, '参数配置 key 重复', '', NULL, '2021-04-20 23:52:56', NULL, '2021-04-20 23:52:56', b'0');
+INSERT INTO `sys_error_code` VALUES (3941, 1, 'dashboard', 1001000003, '不能删除类型为系统内置的参数配置', '', NULL, '2021-04-20 23:52:56', NULL, '2021-04-20 23:52:56', b'0');
+INSERT INTO `sys_error_code` VALUES (3942, 1, 'dashboard', 1001000004, '不允许获取敏感配置到前端', '', NULL, '2021-04-20 23:52:56', NULL, '2021-04-20 23:52:56', b'0');
+INSERT INTO `sys_error_code` VALUES (3943, 1, 'dashboard', 1001001000, '定时任务不存在', '', NULL, '2021-04-20 23:52:56', NULL, '2021-04-20 23:52:56', b'0');
+INSERT INTO `sys_error_code` VALUES (3944, 1, 'dashboard', 1001001001, '定时任务的处理器已经存在', '', NULL, '2021-04-20 23:52:56', NULL, '2021-04-20 23:52:56', b'0');
+INSERT INTO `sys_error_code` VALUES (3945, 1, 'dashboard', 1001001002, '只允许修改为开启或者关闭状态', '', NULL, '2021-04-20 23:52:56', NULL, '2021-04-20 23:52:56', b'0');
+INSERT INTO `sys_error_code` VALUES (3946, 1, 'dashboard', 1001001003, '定时任务已经处于该状态,无需修改', '', NULL, '2021-04-20 23:52:56', NULL, '2021-04-20 23:52:56', b'0');
+INSERT INTO `sys_error_code` VALUES (3947, 1, 'dashboard', 1001001004, '只有开启状态的任务,才可以修改', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (3948, 1, 'dashboard', 1001001005, 'CRON 表达式不正确', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (3949, 2, 'dashboard', 1001002000, 'API 错误日志不存在', '', NULL, '2021-04-20 23:52:57', '1', '2021-04-13 21:55:55', b'1');
+INSERT INTO `sys_error_code` VALUES (3950, 1, 'dashboard', 1001002001, 'API 错误日志已处理', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (3951, 1, 'dashboard', 1001003000, '文件不存在', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (3952, 1, 'dashboard', 1002000000, '登录失败,账号密码不正确', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (3953, 1, 'dashboard', 1002000001, '登录失败,账号被禁用', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (3954, 1, 'dashboard', 1002000002, '登录失败', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (3955, 1, 'dashboard', 1002000003, '验证码不存在', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (3956, 1, 'dashboard', 1002000004, '验证码不正确', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (3957, 1, 'dashboard', 1002001000, 'Token 已经过期', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (3958, 1, 'dashboard', 1002001001, 'Token 解析失败', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (3959, 1, 'dashboard', 1002002000, '已经存在该名字的菜单', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (3960, 1, 'dashboard', 1002002001, '父菜单不存在', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (3961, 1, 'dashboard', 1002002002, '不能设置自己为父菜单', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (3962, 1, 'dashboard', 1002002003, '菜单不存在', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (3963, 1, 'dashboard', 1002002004, '存在子菜单,无法删除', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (3964, 1, 'dashboard', 1002002005, '父菜单的类型必须是目录或者菜单', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (3965, 1, 'dashboard', 1002003000, '角色不存在', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (3966, 1, 'dashboard', 1002003001, '已经存在名为【{}】的角色', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (3967, 1, 'dashboard', 1002003002, '已经存在编码为【{}】的角色', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (3968, 1, 'dashboard', 1002003004, '不能操作类型为系统内置的角色', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (3969, 1, 'dashboard', 1002004000, '用户账号已经存在', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (3970, 1, 'dashboard', 1002004001, '已经存在该名字的部门', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-22 00:04:06', b'0');
+INSERT INTO `sys_error_code` VALUES (3971, 1, 'dashboard', 1002004002, '父级部门不存在', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-22 00:04:06', b'0');
+INSERT INTO `sys_error_code` VALUES (3972, 1, 'dashboard', 1002004003, '用户不存在', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-22 00:04:06', b'0');
+INSERT INTO `sys_error_code` VALUES (3973, 1, 'dashboard', 1002004004, '存在子部门,无法删除', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-22 00:04:06', b'0');
+INSERT INTO `sys_error_code` VALUES (3974, 1, 'dashboard', 1002004005, '不能设置自己为父部门', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-22 00:04:06', b'0');
+INSERT INTO `sys_error_code` VALUES (3975, 1, 'dashboard', 1002004001, '已经存在该名字的部门', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (3976, 1, 'dashboard', 1002004002, '父级部门不存在', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (3977, 1, 'dashboard', 1002004003, '当前部门不存在', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (3978, 1, 'dashboard', 1002004004, '存在子部门,无法删除', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (3979, 1, 'dashboard', 1002004005, '不能设置自己为父部门', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (3980, 1, 'dashboard', 1002004006, '部门中存在员工,无法删除', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (3981, 1, 'dashboard', 1002004007, '部门不处于开启状态,不允许选择', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (3982, 1, 'dashboard', 1002004008, '不能设置自己的子部门为父部门', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (3983, 1, 'dashboard', 1002005001, '已经存在该标识的岗位', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-22 00:04:06', b'0');
+INSERT INTO `sys_error_code` VALUES (3984, 1, 'dashboard', 1002005002, '岗位({}) 不处于开启状态,不允许选择', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (3985, 1, 'dashboard', 1002005001, '已经存在该名字的岗位', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (3986, 1, 'dashboard', 1002005001, '已经存在该标识的岗位', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (3987, 1, 'dashboard', 1002006001, '当前字典类型不存在', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (3988, 1, 'dashboard', 1002006002, '字典类型不处于开启状态,不允许选择', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (3989, 1, 'dashboard', 1002006003, '已经存在该名字的字典类型', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (3990, 1, 'dashboard', 1002006004, '无法删除,该字典类型还有字典数据', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-22 00:04:06', b'0');
+INSERT INTO `sys_error_code` VALUES (3991, 1, 'dashboard', 1002006004, '无法删除,该字典类型还有字典数据', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (3992, 1, 'dashboard', 1002007001, '当前字典数据不存在', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (3993, 1, 'dashboard', 1002007002, '字典数据不处于开启状态,不允许选择', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (3994, 1, 'dashboard', 1002007003, '已经存在该值的字典数据', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (3995, 1, 'dashboard', 1002008001, '当前通知公告不存在', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (3996, 1, 'dashboard', 1002009001, '文件路径已经存在', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-21 00:03:20', b'0');
+INSERT INTO `sys_error_code` VALUES (3997, 1, 'dashboard', 1002009002, '文件上传失败', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (3998, 1, 'dashboard', 1002009003, '文件为空', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (3999, 1, 'dashboard', 1002011000, '短信模板不存在', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-22 00:04:06', b'0');
+INSERT INTO `sys_error_code` VALUES (4000, 1, 'dashboard', 1002011001, '已经存在编码为【{}】的短信模板', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-22 00:04:06', b'0');
+INSERT INTO `sys_error_code` VALUES (4001, 2, 'dashboard', 1002011002, '无法删除,该短信渠道还有短信模板', '', NULL, '2021-04-20 23:52:57', '1', '2021-04-22 00:06:52', b'0');
+INSERT INTO `sys_error_code` VALUES (4002, 1, 'dashboard', 1002011000, '短信模板不存在', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (4003, 1, 'dashboard', 1002011001, '已经存在编码为【{}】的短信模板', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (4004, 1, 'dashboard', 1002012000, '手机号不存在', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (4005, 1, 'dashboard', 1002012001, '模板参数({})缺失', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (4006, 1, 'dashboard', 1002009000, '错误码不存在', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (4007, 1, 'dashboard', 1002009001, '已经存在编码为【{}}】的错误码', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (4008, 1, 'dashboard', 1002004003, '不能修改类型为系统内置的错误码', '', NULL, '2021-04-20 23:52:57', NULL, '2021-04-20 23:52:57', b'0');
+INSERT INTO `sys_error_code` VALUES (4009, 1, 'dashboard', 1001004000, '错误码不存在', '', NULL, '2021-04-21 00:38:01', NULL, '2021-04-21 00:38:01', b'0');
+INSERT INTO `sys_error_code` VALUES (4010, 1, 'dashboard', 1001004001, '已经存在编码为【{}】的错误码', '', NULL, '2021-04-21 00:38:01', NULL, '2021-04-21 23:48:44', b'0');
+INSERT INTO `sys_error_code` VALUES (4011, 1, 'dashboard', 1001004002, '不能修改类型为系统内置的错误码', '', NULL, '2021-04-21 00:38:01', NULL, '2021-04-21 00:38:01', b'0');
+INSERT INTO `sys_error_code` VALUES (4012, 2, 'dashboard', 1201002000, '啦啦啦啦', 'biubiub', '1', '2021-04-21 23:46:02', '1', '2021-04-21 23:46:02', b'0');
+INSERT INTO `sys_error_code` VALUES (4013, 1, 'dashboard', 1001002000, 'API 错误日志不存在', '', NULL, '2021-04-21 23:48:44', NULL, '2021-04-21 23:48:44', b'0');
+INSERT INTO `sys_error_code` VALUES (4014, 1, 'dashboard', 1002013000, '错误码不存在', '', NULL, '2021-04-22 00:04:06', NULL, '2021-04-22 00:04:06', b'0');
+INSERT INTO `sys_error_code` VALUES (4015, 1, 'dashboard', 1002013001, '已经存在编码为【{}】的错误码', '', NULL, '2021-04-22 00:04:06', NULL, '2021-04-22 00:04:06', b'0');
 COMMIT;
 
 -- ----------------------------
@@ -403,7 +508,7 @@ CREATE TABLE `sys_login_log` (
   `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   PRIMARY KEY (`id`) USING BTREE
-) ENGINE=InnoDB AUTO_INCREMENT=84 DEFAULT CHARSET=utf8mb4 COMMENT='系统访问记录';
+) ENGINE=InnoDB AUTO_INCREMENT=98 DEFAULT CHARSET=utf8mb4 COMMENT='系统访问记录';
 
 -- ----------------------------
 -- Records of sys_login_log
@@ -432,7 +537,7 @@ CREATE TABLE `sys_menu` (
   `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   PRIMARY KEY (`id`) USING BTREE
-) ENGINE=InnoDB AUTO_INCREMENT=1110 DEFAULT CHARSET=utf8mb4 COMMENT='菜单权限表';
+) ENGINE=InnoDB AUTO_INCREMENT=1116 DEFAULT CHARSET=utf8mb4 COMMENT='菜单权限表';
 
 -- ----------------------------
 -- Records of sys_menu
@@ -563,6 +668,12 @@ INSERT INTO `sys_menu` VALUES (1106, '发送测试短信', 'system:sms-template:
 INSERT INTO `sys_menu` VALUES (1107, '短信日志', '', 2, 2, 1093, 'sms-log', 'phone', 'system/sms/smsLog', 0, '', '2021-04-11 08:37:05', '1', '2021-04-11 19:34:25', b'0');
 INSERT INTO `sys_menu` VALUES (1108, '短信日志查询', 'system:sms-log:query', 3, 1, 1107, '', '', '', 0, '', '2021-04-11 08:37:05', '', '2021-04-11 08:37:05', b'0');
 INSERT INTO `sys_menu` VALUES (1109, '短信日志导出', 'system:sms-log:export', 3, 5, 1107, '', '', '', 0, '', '2021-04-11 08:37:05', '', '2021-04-11 08:37:05', b'0');
+INSERT INTO `sys_menu` VALUES (1110, '错误码管理', '', 2, 12, 1, 'error-code', 'code', 'system/errorCode/index', 0, '', '2021-04-13 21:46:42', '1', '2021-04-22 00:04:35', b'0');
+INSERT INTO `sys_menu` VALUES (1111, '错误码查询', 'system:error-code:query', 3, 1, 1110, '', '', '', 0, '', '2021-04-13 21:46:42', '', '2021-04-13 22:09:37', b'0');
+INSERT INTO `sys_menu` VALUES (1112, '错误码创建', 'system:error-code:create', 3, 2, 1110, '', '', '', 0, '', '2021-04-13 21:46:42', '', '2021-04-13 22:09:43', b'0');
+INSERT INTO `sys_menu` VALUES (1113, '错误码更新', 'system:error-code:update', 3, 3, 1110, '', '', '', 0, '', '2021-04-13 21:46:42', '', '2021-04-13 22:09:47', b'0');
+INSERT INTO `sys_menu` VALUES (1114, '错误码删除', 'system:error-code:delete', 3, 4, 1110, '', '', '', 0, '', '2021-04-13 21:46:42', '', '2021-04-13 22:09:51', b'0');
+INSERT INTO `sys_menu` VALUES (1115, '错误码导出', 'system:error-code:export', 3, 5, 1110, '', '', '', 0, '', '2021-04-13 21:46:42', '', '2021-04-13 22:09:55', b'0');
 COMMIT;
 
 -- ----------------------------
@@ -622,7 +733,7 @@ CREATE TABLE `sys_operate_log` (
   `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   PRIMARY KEY (`id`) USING BTREE
-) ENGINE=InnoDB AUTO_INCREMENT=177 DEFAULT CHARSET=utf8mb4 COMMENT='操作日志记录';
+) ENGINE=InnoDB AUTO_INCREMENT=193 DEFAULT CHARSET=utf8mb4 COMMENT='操作日志记录';
 
 -- ----------------------------
 -- Records of sys_operate_log
@@ -933,7 +1044,7 @@ CREATE TABLE `sys_sms_log` (
   `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   PRIMARY KEY (`id`) USING BTREE
-) ENGINE=InnoDB AUTO_INCREMENT=35 DEFAULT CHARSET=utf8mb4 COMMENT='短信日志';
+) ENGINE=InnoDB AUTO_INCREMENT=36 DEFAULT CHARSET=utf8mb4 COMMENT='短信日志';
 
 -- ----------------------------
 -- Records of sys_sms_log
@@ -1074,6 +1185,7 @@ INSERT INTO `sys_user_session` VALUES ('12166cd28b4f448ea468d13c471dfc6e', 1, '2
 INSERT INTO `sys_user_session` VALUES ('134d908ae33146bd9b5291471c04f604', 1, '2021-04-10 00:29:28', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-09 23:10:26', NULL, '2021-04-02 08:08:17', b'1');
 INSERT INTO `sys_user_session` VALUES ('1407ce21e47947b9b8d93bff1b55c7d6', 1, '2021-04-07 01:09:11', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-07 00:07:43', NULL, '2021-04-01 18:10:44', b'1');
 INSERT INTO `sys_user_session` VALUES ('1477c38290ff4cee8887ebfe593faa02', 1, '2021-04-17 23:34:57', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-17 23:04:57', NULL, '2021-04-12 21:36:29', b'1');
+INSERT INTO `sys_user_session` VALUES ('347ec49c06d74d138a95266cbb5535a0', 1, '2021-04-21 00:45:41', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-21 00:03:50', NULL, '2021-04-13 18:38:39', b'1');
 INSERT INTO `sys_user_session` VALUES ('3c75ea73e13b4857a18eb57ca2eea80f', 1, '2021-04-11 20:06:52', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-11 19:36:52', NULL, '2021-04-11 09:10:53', b'1');
 INSERT INTO `sys_user_session` VALUES ('40d532d8900c43b791266429a7911751', 1, '2021-04-05 22:11:34', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-05 21:41:34', NULL, '2021-04-01 12:28:20', b'1');
 INSERT INTO `sys_user_session` VALUES ('43676e85d0e04980b2a67181f8d9933b', 1, '2021-04-11 10:41:09', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-11 09:56:31', NULL, '2021-04-02 17:32:07', b'1');
@@ -1084,16 +1196,22 @@ INSERT INTO `sys_user_session` VALUES ('5c30d80eb72048daa1a24d3d4f01317b', 1, '2
 INSERT INTO `sys_user_session` VALUES ('5dca80a5c61541479a4dbb6e004c2e28', 1, '2021-04-14 00:57:25', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-13 23:38:30', NULL, '2021-04-12 17:33:48', b'1');
 INSERT INTO `sys_user_session` VALUES ('7324a76b029a49ee95bf54ceb4164ba9', 1, '2021-04-13 01:29:14', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-13 00:18:40', NULL, '2021-04-11 23:15:00', b'1');
 INSERT INTO `sys_user_session` VALUES ('749619894bc441bb9773902515f81e6a', 1, '2021-04-11 00:39:51', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-11 00:09:51', NULL, '2021-04-02 16:50:56', b'1');
+INSERT INTO `sys_user_session` VALUES ('750dca41b315488b8c17e1c0f8b6a519', 1, '2021-04-21 01:24:28', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-21 00:54:28', NULL, '2021-04-13 21:42:10', b'1');
 INSERT INTO `sys_user_session` VALUES ('7768ae62ad974fd989f5159649a4be82', 1, '2021-04-11 00:53:39', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-10 23:23:27', NULL, '2021-04-02 17:04:58', b'1');
 INSERT INTO `sys_user_session` VALUES ('79efcb8f64aa42af9f4b327fb383532f', 1, '2021-04-11 22:44:07', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-11 21:34:53', NULL, '2021-04-11 14:10:45', b'1');
+INSERT INTO `sys_user_session` VALUES ('83b227ad356f4343b01d321ad26807ce', 1, '2021-04-22 00:49:12', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-21 23:33:02', NULL, '2021-04-22 00:19:12', b'0');
 INSERT INTO `sys_user_session` VALUES ('87d5b95fdad9447189a95abf8a5152df', 1, '2021-04-17 23:01:18', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-17 22:31:18', NULL, '2021-04-12 21:03:27', b'1');
 INSERT INTO `sys_user_session` VALUES ('8b3eac5e4a104a4191c8070e03d553ea', 1, '2021-04-05 02:45:12', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-05 02:15:12', NULL, '2021-04-01 11:05:25', b'1');
 INSERT INTO `sys_user_session` VALUES ('9ae27346d8b7491aad1385f51e8aa196', 1, '2021-03-13 14:02:12', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', NULL, '2021-03-13 10:43:06', NULL, '2021-03-13 06:40:35', b'1');
 INSERT INTO `sys_user_session` VALUES ('a2fb443b31c049008975ff8ee5499db1', 1, '2021-04-11 09:42:09', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-11 09:12:09', NULL, '2021-04-02 17:16:00', b'1');
 INSERT INTO `sys_user_session` VALUES ('a71a74adf9d141e2849d2a411d558205', 1, '2021-04-17 18:24:44', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-17 17:54:44', NULL, '2021-04-12 17:54:50', b'1');
+INSERT INTO `sys_user_session` VALUES ('ab2099c12f5c4b0288c60abe8cfff307', 1, '2021-04-18 01:41:46', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-18 01:01:35', NULL, '2021-04-13 05:14:54', b'1');
 INSERT INTO `sys_user_session` VALUES ('ae9ee7452ee54e4b983d658188c15c4d', 1, '2021-03-14 21:32:57', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36', NULL, '2021-03-14 20:25:00', NULL, '2021-03-13 15:19:10', b'1');
 INSERT INTO `sys_user_session` VALUES ('b727853eccea4c8589e006ffea985146', 1, '2021-04-12 01:36:00', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-12 01:06:00', NULL, '2021-04-11 19:40:07', b'1');
+INSERT INTO `sys_user_session` VALUES ('b9ee6fde7bf74ed49cad99abf86c94d6', 1, '2021-04-18 20:21:02', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-18 19:18:52', NULL, '2021-04-13 06:36:39', b'1');
+INSERT INTO `sys_user_session` VALUES ('bb4ad4579bd1436c859b94228967582f', 1, '2021-04-18 20:52:38', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-18 20:22:38', NULL, '2021-04-13 07:14:58', b'1');
 INSERT INTO `sys_user_session` VALUES ('c095616db95044c5bed66a3f84519b8b', 1, '2021-04-11 19:59:33', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-11 19:29:33', NULL, '2021-04-11 09:04:19', b'1');
+INSERT INTO `sys_user_session` VALUES ('c8805f37eb76432c89d6d54feb14756f', 1, '2021-04-18 21:54:56', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-18 21:00:38', NULL, '2021-04-13 17:45:30', b'1');
 INSERT INTO `sys_user_session` VALUES ('d0adf48f82914212b947e5ab04d9fb65', 1, '2021-03-21 19:16:28', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-03-21 18:13:37', NULL, '2021-03-15 05:53:20', b'1');
 INSERT INTO `sys_user_session` VALUES ('dfbce0af867547f4bb01ac6f2e583337', 1, '2021-04-11 17:06:15', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-11 16:36:15', NULL, '2021-04-11 08:24:03', b'1');
 INSERT INTO `sys_user_session` VALUES ('e5ecf10e40a5463b8f9b5b453cb1649b', 1, '2021-04-11 17:06:22', 'admin', '127.0.0.1', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36', NULL, '2021-04-11 16:36:22', NULL, '2021-04-11 08:24:03', b'1');
@@ -1134,7 +1252,7 @@ CREATE TABLE `tool_codegen_column` (
   `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   PRIMARY KEY (`id`) USING BTREE
-) ENGINE=InnoDB AUTO_INCREMENT=447 DEFAULT CHARSET=utf8mb4 COMMENT='代码生成表字段定义';
+) ENGINE=InnoDB AUTO_INCREMENT=458 DEFAULT CHARSET=utf8mb4 COMMENT='代码生成表字段定义';
 
 -- ----------------------------
 -- Records of tool_codegen_column
@@ -1354,6 +1472,17 @@ INSERT INTO `tool_codegen_column` VALUES (443, 36, 'create_time', 'datetime', '
 INSERT INTO `tool_codegen_column` VALUES (444, 36, 'updater', 'varchar(64)', '更新者', b'1', b'0', '0', 27, 'String', 'updater', '', NULL, b'0', b'0', b'0', '=', b'0', 'input', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0');
 INSERT INTO `tool_codegen_column` VALUES (445, 36, 'update_time', 'datetime', '更新时间', b'0', b'0', '0', 28, 'Date', 'updateTime', '', NULL, b'0', b'0', b'0', 'BETWEEN', b'0', 'datetime', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0');
 INSERT INTO `tool_codegen_column` VALUES (446, 36, 'deleted', 'bit(1)', '是否删除', b'0', b'0', '0', 29, 'Boolean', 'deleted', '', NULL, b'0', b'0', b'0', '=', b'0', 'radio', '1', '2021-04-11 09:13:48', '1', '2021-04-11 20:33:54', b'0');
+INSERT INTO `tool_codegen_column` VALUES (447, 37, 'id', 'bigint(20)', '错误码编号', b'0', b'1', '1', 1, 'Long', 'id', '', '1024', b'0', b'1', b'0', '=', b'1', 'input', '1', '2021-04-21 00:04:13', '1', '2021-04-21 00:55:37', b'0');
+INSERT INTO `tool_codegen_column` VALUES (448, 37, 'type', 'tinyint(4)', '错误码类型', b'0', b'0', '0', 2, 'Integer', 'type', 'inf_error_code_type', '1', b'0', b'0', b'1', '=', b'1', 'select', '1', '2021-04-21 00:04:13', '1', '2021-04-21 00:55:37', b'0');
+INSERT INTO `tool_codegen_column` VALUES (449, 37, 'application_name', 'varchar(50)', '应用名', b'0', b'0', '0', 3, 'String', 'applicationName', '', 'dashboard', b'1', b'1', b'1', 'LIKE', b'1', 'input', '1', '2021-04-21 00:04:13', '1', '2021-04-21 00:55:37', b'0');
+INSERT INTO `tool_codegen_column` VALUES (450, 37, 'code', 'int(11)', '错误码编码', b'0', b'0', '0', 4, 'Integer', 'code', '', '1234', b'1', b'1', b'1', '=', b'1', 'input', '1', '2021-04-21 00:04:13', '1', '2021-04-21 00:55:37', b'0');
+INSERT INTO `tool_codegen_column` VALUES (451, 37, 'message', 'varchar(512)', '错误码错误提示', b'0', b'0', '0', 5, 'String', 'message', '', '帅气', b'1', b'1', b'1', 'LIKE', b'1', 'input', '1', '2021-04-21 00:04:13', '1', '2021-04-21 00:55:37', b'0');
+INSERT INTO `tool_codegen_column` VALUES (452, 37, 'memo', 'varchar(512)', '备注', b'1', b'0', '0', 6, 'String', 'memo', '', '哈哈哈', b'1', b'1', b'0', '=', b'1', 'input', '1', '2021-04-21 00:04:13', '1', '2021-04-21 00:55:37', b'0');
+INSERT INTO `tool_codegen_column` VALUES (453, 37, 'creator', 'varchar(64)', '创建者', b'1', b'0', '0', 7, 'String', 'creator', '', NULL, b'0', b'0', b'0', '=', b'0', 'input', '1', '2021-04-21 00:04:13', '1', '2021-04-21 00:55:37', b'0');
+INSERT INTO `tool_codegen_column` VALUES (454, 37, 'create_time', 'datetime', '创建时间', b'0', b'0', '0', 8, 'Date', 'createTime', '', NULL, b'0', b'0', b'1', 'BETWEEN', b'1', 'datetime', '1', '2021-04-21 00:04:13', '1', '2021-04-21 00:55:37', b'0');
+INSERT INTO `tool_codegen_column` VALUES (455, 37, 'updater', 'varchar(64)', '更新者', b'1', b'0', '0', 9, 'String', 'updater', '', NULL, b'0', b'0', b'0', '=', b'0', 'input', '1', '2021-04-21 00:04:13', '1', '2021-04-21 00:55:37', b'0');
+INSERT INTO `tool_codegen_column` VALUES (456, 37, 'update_time', 'datetime', '更新时间', b'0', b'0', '0', 10, 'Date', 'updateTime', '', NULL, b'0', b'0', b'0', 'BETWEEN', b'0', 'datetime', '1', '2021-04-21 00:04:13', '1', '2021-04-21 00:55:37', b'0');
+INSERT INTO `tool_codegen_column` VALUES (457, 37, 'deleted', 'bit(1)', '是否删除', b'0', b'0', '0', 11, 'Boolean', 'deleted', '', NULL, b'0', b'0', b'0', '=', b'0', 'radio', '1', '2021-04-21 00:04:13', '1', '2021-04-21 00:55:37', b'0');
 COMMIT;
 
 -- ----------------------------
@@ -1379,7 +1508,7 @@ CREATE TABLE `tool_codegen_table` (
   `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   PRIMARY KEY (`id`) USING BTREE
-) ENGINE=InnoDB AUTO_INCREMENT=37 DEFAULT CHARSET=utf8mb4 COMMENT='代码生成表定义';
+) ENGINE=InnoDB AUTO_INCREMENT=38 DEFAULT CHARSET=utf8mb4 COMMENT='代码生成表定义';
 
 -- ----------------------------
 -- Records of tool_codegen_table
@@ -1400,6 +1529,7 @@ INSERT INTO `tool_codegen_table` VALUES (33, 1, 'inf_file', '文件表', NULL, '
 INSERT INTO `tool_codegen_table` VALUES (34, 1, 'sys_sms_channel', '短信渠道', NULL, 'system', 'sms', 'SysSmsChannel', '短信渠道', '芋道源码', 1, 1093, '1', '2021-04-03 13:39:06', '1', '2021-04-05 20:52:09', b'0');
 INSERT INTO `tool_codegen_table` VALUES (35, 1, 'sys_sms_template', '短信模板', NULL, 'system', 'sms', 'SysSmsTemplate', '短信模板', '芋道源码', 1, 1093, '1', '2021-04-03 13:58:55', '1', '2021-04-05 22:23:38', b'0');
 INSERT INTO `tool_codegen_table` VALUES (36, 1, 'sys_sms_log', '短信日志', NULL, 'system', 'sms', 'SysSmsLog', '短信日志', '芋道源码', 1, 1093, '1', '2021-04-11 01:12:57', '1', '2021-04-11 20:33:54', b'0');
+INSERT INTO `tool_codegen_table` VALUES (37, 1, 'inf_error_code', '错误码表', NULL, 'infra', 'errorcode', 'InfErrorCode', '错误码', '芋道源码', 1, 2, '1', '2021-04-20 15:27:45', '1', '2021-04-21 00:55:37', b'0');
 COMMIT;
 
 -- ----------------------------

+ 36 - 0
src/main/java/cn/iocoder/dashboard/framework/errorcode/config/ErrorCodeConfiguration.java

@@ -0,0 +1,36 @@
+package cn.iocoder.dashboard.framework.errorcode.config;
+
+import cn.iocoder.dashboard.framework.errorcode.core.generator.ErrorCodeAutoGenerator;
+import cn.iocoder.dashboard.framework.errorcode.core.loader.ErrorCodeLoader;
+import cn.iocoder.dashboard.framework.errorcode.core.service.ErrorCodeFrameworkService;
+import cn.iocoder.dashboard.framework.errorcode.core.loader.ErrorCodeLoaderImpl;
+import cn.iocoder.dashboard.framework.errorcode.core.generator.ErrorCodeAutoGeneratorImpl;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+/**
+ * 错误码配置类
+ */
+@Configuration
+@EnableConfigurationProperties(ErrorCodeProperties.class)
+@EnableScheduling // 开启调度任务的功能,因为 ErrorCodeRemoteLoader 通过定时刷新错误码
+public class ErrorCodeConfiguration {
+
+    @Bean
+    public ErrorCodeAutoGenerator errorCodeAutoGenerator(@Value("${spring.application.name}") String applicationName,
+                                                         ErrorCodeProperties errorCodeProperties,
+                                                         ErrorCodeFrameworkService errorCodeFrameworkService) {
+        return new ErrorCodeAutoGeneratorImpl(applicationName, errorCodeProperties.getConstantsClassList(),
+                errorCodeFrameworkService);
+    }
+
+    @Bean
+    public ErrorCodeLoader errorCodeLoader(@Value("${spring.application.name}") String applicationName,
+                                           ErrorCodeFrameworkService errorCodeFrameworkService) {
+        return new ErrorCodeLoaderImpl(applicationName, errorCodeFrameworkService);
+    }
+
+}

+ 26 - 0
src/main/java/cn/iocoder/dashboard/framework/errorcode/config/ErrorCodeProperties.java

@@ -0,0 +1,26 @@
+package cn.iocoder.dashboard.framework.errorcode.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.validation.annotation.Validated;
+
+import javax.validation.constraints.NotNull;
+import java.util.List;
+
+/**
+ * 错误码的配置属性类
+ *
+ * @author dlyan
+ */
+@ConfigurationProperties("yudao.error-code")
+@Data
+@Validated
+public class ErrorCodeProperties {
+
+    /**
+     * 错误码枚举类
+     */
+    @NotNull(message = "错误码枚举类不能为空")
+    private List<String> constantsClassList;
+
+}

+ 34 - 0
src/main/java/cn/iocoder/dashboard/framework/errorcode/core/dto/ErrorCodeAutoGenerateReqDTO.java

@@ -0,0 +1,34 @@
+package cn.iocoder.dashboard.framework.errorcode.core.dto;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+
+/**
+ * 错误码自动生成 DTO
+ *
+ * @author dylan
+ */
+@Data
+@Accessors(chain = true)
+public class ErrorCodeAutoGenerateReqDTO {
+
+    /**
+     * 应用名
+     */
+    @NotNull(message = "应用名不能为空")
+    private String applicationName;
+    /**
+     * 错误码编码
+     */
+    @NotNull(message = "错误码编码不能为空")
+    private Integer code;
+    /**
+     * 错误码错误提示
+     */
+    @NotEmpty(message = "错误码错误提示不能为空")
+    private String message;
+
+}

+ 28 - 0
src/main/java/cn/iocoder/dashboard/framework/errorcode/core/dto/ErrorCodeRespDTO.java

@@ -0,0 +1,28 @@
+package cn.iocoder.dashboard.framework.errorcode.core.dto;
+
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * 错误码的 Response DTO
+ *
+ * @author 芋道源码
+ */
+@Data
+public class ErrorCodeRespDTO {
+
+    /**
+     * 错误码编码
+     */
+    private Integer code;
+    /**
+     * 错误码错误提示
+     */
+    private String message;
+    /**
+     * 更新时间
+     */
+    private Date updateTime;
+
+}

+ 15 - 0
src/main/java/cn/iocoder/dashboard/framework/errorcode/core/generator/ErrorCodeAutoGenerator.java

@@ -0,0 +1,15 @@
+package cn.iocoder.dashboard.framework.errorcode.core.generator;
+
+/**
+ * 错误码的自动生成器
+ *
+ * @author dylan
+ */
+public interface ErrorCodeAutoGenerator {
+
+    /**
+     * 将配置类到错误码写入数据库
+     */
+    void execute();
+
+}

+ 98 - 0
src/main/java/cn/iocoder/dashboard/framework/errorcode/core/generator/ErrorCodeAutoGeneratorImpl.java

@@ -0,0 +1,98 @@
+package cn.iocoder.dashboard.framework.errorcode.core.generator;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ClassUtil;
+import cn.hutool.core.util.ReflectUtil;
+import cn.iocoder.dashboard.common.exception.ErrorCode;
+import cn.iocoder.dashboard.framework.errorcode.core.dto.ErrorCodeAutoGenerateReqDTO;
+import cn.iocoder.dashboard.framework.errorcode.core.service.ErrorCodeFrameworkService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.context.event.ApplicationReadyEvent;
+import org.springframework.context.event.EventListener;
+import org.springframework.scheduling.annotation.Async;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * ErrorCodeAutoGenerator 的实现类
+ * 目的是,扫描指定的 {@link #constantsClassList} 类,写入到 system 服务中
+ *
+ * @author dylan
+ */
+@RequiredArgsConstructor
+@Slf4j
+public class ErrorCodeAutoGeneratorImpl implements ErrorCodeAutoGenerator {
+
+    /**
+     * 应用分组
+     */
+    private final String applicationName;
+    /**
+     * 错误码枚举类
+     */
+    private final List<String> constantsClassList;
+    /**
+     * 错误码 Service
+     */
+    private final ErrorCodeFrameworkService errorCodeService;
+
+    @Override
+    @EventListener(ApplicationReadyEvent.class)
+    @Async // 异步,保证项目的启动过程,毕竟非关键流程
+    public void execute() {
+        // 第一步,解析错误码
+        List<ErrorCodeAutoGenerateReqDTO> autoGenerateDTOs = parseErrorCode();
+        log.info("[execute][解析到错误码数量为 ({}) 个]", autoGenerateDTOs.size());
+
+        // 第二步,写入到 system 服务
+        errorCodeService.autoGenerateErrorCodes(autoGenerateDTOs);
+        log.info("[execute][写入到 system 组件完成]");
+    }
+
+    /**
+     * 解析 constantsClassList 变量,转换成错误码数组
+     *
+     * @return 错误码数组
+     */
+    private List<ErrorCodeAutoGenerateReqDTO>  parseErrorCode() {
+        // 校验 errorCodeConstantsClass 参数
+        if (CollUtil.isEmpty(constantsClassList)) {
+            log.info("[execute][未配置 yudao.error-code.constants-class-list 配置项,不进行自动写入到 system 服务中]");
+            return new ArrayList<>();
+        }
+
+        // 解析错误码
+        List<ErrorCodeAutoGenerateReqDTO> autoGenerateDTOs = new ArrayList<>();
+        constantsClassList.forEach(constantsClass -> {
+            // 解析错误码枚举类
+            Class<?> errorCodeConstantsClazz = ClassUtil.loadClass(constantsClass);
+            // 解析错误码
+            autoGenerateDTOs.addAll(parseErrorCode(errorCodeConstantsClazz));
+        });
+        return autoGenerateDTOs;
+    }
+
+    /**
+     * 解析错误码类,获得错误码数组
+     *
+     * @return 错误码数组
+     */
+    private List<ErrorCodeAutoGenerateReqDTO> parseErrorCode(Class<?> constantsClass) {
+        List<ErrorCodeAutoGenerateReqDTO> autoGenerateDTOs = new ArrayList<>();
+        Arrays.stream(constantsClass.getFields()).forEach(field -> {
+            if (field.getType() != ErrorCode.class) {
+                return;
+            }
+            // 转换成 ErrorCodeAutoGenerateReqDTO 对象
+            ErrorCode errorCode = (ErrorCode) ReflectUtil.getFieldValue(constantsClass, field);
+            autoGenerateDTOs.add(new ErrorCodeAutoGenerateReqDTO().setApplicationName(applicationName)
+                    .setCode(errorCode.getCode()).setMessage(errorCode.getMsg()));
+        });
+        return autoGenerateDTOs;
+    }
+
+}
+

+ 24 - 0
src/main/java/cn/iocoder/dashboard/framework/errorcode/core/loader/ErrorCodeLoader.java

@@ -0,0 +1,24 @@
+package cn.iocoder.dashboard.framework.errorcode.core.loader;
+
+import cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil;
+
+/**
+ * 错误码加载器
+ *
+ * 注意,错误码最终加载到 {@link cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil} 的 MESSAGES 变量中!
+ *
+ * @author dlyan
+ */
+public interface ErrorCodeLoader {
+
+    /**
+     * 添加错误码
+     *
+     * @param code 错误码的编号
+     * @param msg 错误码的提示
+     */
+    default void putErrorCode(Integer code, String msg) {
+        ServiceExceptionUtil.put(code, msg);
+    }
+
+}

+ 73 - 0
src/main/java/cn/iocoder/dashboard/framework/errorcode/core/loader/ErrorCodeLoaderImpl.java

@@ -0,0 +1,73 @@
+package cn.iocoder.dashboard.framework.errorcode.core.loader;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.iocoder.dashboard.framework.errorcode.core.dto.ErrorCodeRespDTO;
+import cn.iocoder.dashboard.framework.errorcode.core.service.ErrorCodeFrameworkService;
+import cn.iocoder.dashboard.util.date.DateUtils;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.context.event.ApplicationReadyEvent;
+import org.springframework.context.event.EventListener;
+import org.springframework.scheduling.annotation.Scheduled;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * ErrorCodeLoader 的实现类,从 infra 的数据库中,加载错误码。
+ *
+ * 考虑到错误码会刷新,所以按照 {@link #REFRESH_ERROR_CODE_PERIOD} 频率,增量加载错误码。
+ *
+ * @author dlyan
+ */
+@RequiredArgsConstructor
+@Slf4j
+public class ErrorCodeLoaderImpl implements ErrorCodeLoader {
+
+    /**
+     * 刷新错误码的频率,单位:毫秒
+     */
+    private static final int REFRESH_ERROR_CODE_PERIOD = 60 * 1000;
+
+    /**
+     * 应用分组
+     */
+    private final String applicationName;
+    /**
+     * 错误码 Service
+     */
+    private final ErrorCodeFrameworkService errorCodeService;
+
+    /**
+     * 缓存错误码的最大更新时间,用于后续的增量轮询,判断是否有更新
+     */
+    private Date maxUpdateTime;
+
+    @EventListener(ApplicationReadyEvent.class)
+    public void loadErrorCodes() {
+        this.loadErrorCodes0();
+    }
+
+    @Scheduled(fixedDelay = REFRESH_ERROR_CODE_PERIOD, initialDelay = REFRESH_ERROR_CODE_PERIOD)
+    public void refreshErrorCodes() {
+        this.loadErrorCodes0();
+    }
+
+    private void loadErrorCodes0() {
+        // 加载错误码
+        List<ErrorCodeRespDTO> errorCodeRespDTOs = errorCodeService.getErrorCodeList(applicationName, maxUpdateTime);
+        if (CollUtil.isEmpty(errorCodeRespDTOs)) {
+            return;
+        }
+        log.info("[loadErrorCodes0][加载到 ({}) 个错误码]", errorCodeRespDTOs.size());
+
+        // 刷新错误码的缓存
+        errorCodeRespDTOs.forEach(errorCodeRespDTO -> {
+            // 写入到错误码的缓存
+            putErrorCode(errorCodeRespDTO.getCode(), errorCodeRespDTO.getMessage());
+            // 记录下更新时间,方便增量更新
+            maxUpdateTime = DateUtils.max(maxUpdateTime, errorCodeRespDTO.getUpdateTime());
+        });
+    }
+
+}

+ 35 - 0
src/main/java/cn/iocoder/dashboard/framework/errorcode/core/service/ErrorCodeFrameworkService.java

@@ -0,0 +1,35 @@
+package cn.iocoder.dashboard.framework.errorcode.core.service;
+
+import cn.iocoder.dashboard.framework.errorcode.core.dto.ErrorCodeAutoGenerateReqDTO;
+import cn.iocoder.dashboard.framework.errorcode.core.dto.ErrorCodeRespDTO;
+
+import javax.validation.Valid;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 错误码 Framework Service 接口
+ *
+ * @author 芋道源码
+ */
+public interface ErrorCodeFrameworkService {
+
+    /**
+     * 自动创建错误码
+     *
+     * @param autoGenerateDTOs 错误码信息
+     */
+    void autoGenerateErrorCodes(@Valid List<ErrorCodeAutoGenerateReqDTO> autoGenerateDTOs);
+
+    /**
+     * 增量获得错误码数组
+     *
+     * 如果 minUpdateTime 为空时,则获取所有错误码
+     *
+     * @param applicationName 应用名
+     * @param minUpdateTime 最小更新时间
+     * @return 错误码数组
+     */
+    List<ErrorCodeRespDTO> getErrorCodeList(String applicationName, Date minUpdateTime);
+
+}

+ 35 - 0
src/main/java/cn/iocoder/dashboard/framework/validator/InEnum.java

@@ -0,0 +1,35 @@
+package cn.iocoder.dashboard.framework.validator;
+
+import cn.iocoder.dashboard.common.core.IntArrayValuable;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import java.lang.annotation.*;
+
+@Target({
+        ElementType.METHOD,
+        ElementType.FIELD,
+        ElementType.ANNOTATION_TYPE,
+        ElementType.CONSTRUCTOR,
+        ElementType.PARAMETER,
+        ElementType.TYPE_USE
+})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Constraint(
+        validatedBy = InEnumValidator.class
+)
+public @interface InEnum {
+
+    /**
+     * @return 实现 EnumValuable 接口的
+     */
+    Class<? extends IntArrayValuable> value();
+
+    String message() default "必须在指定范围 {value}";
+
+    Class<?>[] groups() default {};
+
+    Class<? extends Payload>[] payload() default {};
+
+}

+ 44 - 0
src/main/java/cn/iocoder/dashboard/framework/validator/InEnumValidator.java

@@ -0,0 +1,44 @@
+package cn.iocoder.dashboard.framework.validator;
+
+import cn.iocoder.dashboard.common.core.IntArrayValuable;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class InEnumValidator implements ConstraintValidator<InEnum, Integer> {
+
+    private List<Integer> values;
+
+    @Override
+    public void initialize(InEnum annotation) {
+        IntArrayValuable[] values = annotation.value().getEnumConstants();
+        if (values.length == 0) {
+            this.values = Collections.emptyList();
+        } else {
+            this.values = Arrays.stream(values[0].array()).boxed().collect(Collectors.toList());
+        }
+    }
+
+    @Override
+    public boolean isValid(Integer value, ConstraintValidatorContext context) {
+        // 为空时,默认不校验,即认为通过
+        if (value == null) {
+            return true;
+        }
+        // 校验通过
+        if (values.contains(value)) {
+            return true;
+        }
+        // 校验不通过,自定义提示语句(因为,注解上的 value 是枚举类,无法获得枚举类的实际值)
+        context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值
+        context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()
+                .replaceAll("\\{value}", values.toString())).addConstraintViolation(); // 重新添加错误提示语句
+        return false;
+    }
+
+}
+

+ 3 - 0
src/main/java/cn/iocoder/dashboard/modules/system/controller/dept/vo/post/SysPostExportReqVO.java

@@ -8,6 +8,9 @@ import lombok.Data;
 @Data
 public class SysPostExportReqVO {
 
+    @ApiModelProperty(value = "岗位编码", example = "yudao", notes = "模糊匹配")
+    private String code;
+
     @ApiModelProperty(value = "岗位名称", example = "芋道", notes = "模糊匹配")
     private String name;
 

+ 3 - 0
src/main/java/cn/iocoder/dashboard/modules/system/controller/dept/vo/post/SysPostPageReqVO.java

@@ -11,6 +11,9 @@ import lombok.EqualsAndHashCode;
 @EqualsAndHashCode(callSuper = true)
 public class SysPostPageReqVO extends PageParam {
 
+    @ApiModelProperty(value = "岗位编码", example = "yudao", notes = "模糊匹配")
+    private String code;
+
     @ApiModelProperty(value = "岗位名称", example = "芋道", notes = "模糊匹配")
     private String name;
 

+ 12 - 0
src/main/java/cn/iocoder/dashboard/modules/system/controller/errorcode/SysErrorCodeController.http

@@ -0,0 +1,12 @@
+###
+POST {{baseUrl}}/inra/error-code/create
+Authorization: Bearer {{token}}
+Content-Type:application/json
+
+{
+  "code": 200,
+  "message": "成功",
+  "group": "test",
+  "type": 1
+}
+

+ 89 - 0
src/main/java/cn/iocoder/dashboard/modules/system/controller/errorcode/SysErrorCodeController.java

@@ -0,0 +1,89 @@
+package cn.iocoder.dashboard.modules.system.controller.errorcode;
+
+import cn.iocoder.dashboard.common.pojo.CommonResult;
+import cn.iocoder.dashboard.common.pojo.PageResult;
+import cn.iocoder.dashboard.framework.excel.core.util.ExcelUtils;
+import cn.iocoder.dashboard.framework.logger.operatelog.core.annotations.OperateLog;
+import cn.iocoder.dashboard.modules.system.convert.errorcode.SysErrorCodeConvert;
+import cn.iocoder.dashboard.modules.system.controller.errorcode.vo.*;
+import cn.iocoder.dashboard.modules.system.dal.dataobject.errorcode.SysErrorCodeDO;
+import cn.iocoder.dashboard.modules.system.service.errorcode.SysErrorCodeService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.Valid;
+import java.io.IOException;
+import java.util.List;
+
+import static cn.iocoder.dashboard.common.pojo.CommonResult.success;
+import static cn.iocoder.dashboard.framework.logger.operatelog.core.enums.OperateTypeEnum.EXPORT;
+
+@Api(tags = "错误码")
+@RestController
+@RequestMapping("/system/error-code")
+@Validated
+public class SysErrorCodeController {
+
+    @Resource
+    private SysErrorCodeService errorCodeService;
+
+    @PostMapping("/create")
+    @ApiOperation("创建错误码")
+    @PreAuthorize("@ss.hasPermission('system:error-code:create')")
+    public CommonResult<Long> createErrorCode(@Valid @RequestBody SysErrorCodeCreateReqVO createReqVO) {
+        return success(errorCodeService.createErrorCode(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @ApiOperation("更新错误码")
+    @PreAuthorize("@ss.hasPermission('system:error-code:update')")
+    public CommonResult<Boolean> updateErrorCode(@Valid @RequestBody SysErrorCodeUpdateReqVO updateReqVO) {
+        errorCodeService.updateErrorCode(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @ApiOperation("删除错误码")
+    @ApiImplicitParam(name = "id", value = "编号", required = true)
+    @PreAuthorize("@ss.hasPermission('system:error-code:delete')")
+    public CommonResult<Boolean> deleteErrorCode(@RequestParam("id") Long id) {
+        errorCodeService.deleteErrorCode(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @ApiOperation("获得错误码")
+    @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
+    @PreAuthorize("@ss.hasPermission('system:error-code:query')")
+    public CommonResult<SysErrorCodeRespVO> getErrorCode(@RequestParam("id") Long id) {
+        SysErrorCodeDO errorCode = errorCodeService.getErrorCode(id);
+        return success(SysErrorCodeConvert.INSTANCE.convert(errorCode));
+    }
+
+    @GetMapping("/page")
+    @ApiOperation("获得错误码分页")
+    @PreAuthorize("@ss.hasPermission('system:error-code:query')")
+    public CommonResult<PageResult<SysErrorCodeRespVO>> getErrorCodePage(@Valid SysErrorCodePageReqVO pageVO) {
+        PageResult<SysErrorCodeDO> pageResult = errorCodeService.getErrorCodePage(pageVO);
+        return success(SysErrorCodeConvert.INSTANCE.convertPage(pageResult));
+    }
+
+    @GetMapping("/export-excel")
+    @ApiOperation("导出错误码 Excel")
+    @PreAuthorize("@ss.hasPermission('system:error-code:export')")
+    @OperateLog(type = EXPORT)
+    public void exportErrorCodeExcel(@Valid SysErrorCodeExportReqVO exportReqVO,
+              HttpServletResponse response) throws IOException {
+        List<SysErrorCodeDO> list = errorCodeService.getErrorCodeList(exportReqVO);
+        // 导出 Excel
+        List<SysErrorCodeExcelVO> datas = SysErrorCodeConvert.INSTANCE.convertList02(list);
+        ExcelUtils.write(response, "错误码.xls", "数据", SysErrorCodeExcelVO.class, datas);
+    }
+
+}

+ 30 - 0
src/main/java/cn/iocoder/dashboard/modules/system/controller/errorcode/vo/SysErrorCodeBaseVO.java

@@ -0,0 +1,30 @@
+package cn.iocoder.dashboard.modules.system.controller.errorcode.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+/**
+* 错误码 Base VO,提供给添加、修改、详细的子 VO 使用
+* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
+*/
+@Data
+public class SysErrorCodeBaseVO {
+
+    @ApiModelProperty(value = "应用名", required = true, example = "dashboard")
+    @NotNull(message = "应用名不能为空")
+    private String applicationName;
+
+    @ApiModelProperty(value = "错误码编码", required = true, example = "1234")
+    @NotNull(message = "错误码编码不能为空")
+    private Integer code;
+
+    @ApiModelProperty(value = "错误码错误提示", required = true, example = "帅气")
+    @NotNull(message = "错误码错误提示不能为空")
+    private String message;
+
+    @ApiModelProperty(value = "备注", example = "哈哈哈")
+    private String memo;
+
+}

+ 14 - 0
src/main/java/cn/iocoder/dashboard/modules/system/controller/errorcode/vo/SysErrorCodeCreateReqVO.java

@@ -0,0 +1,14 @@
+package cn.iocoder.dashboard.modules.system.controller.errorcode.vo;
+
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+@ApiModel("错误码创建 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class SysErrorCodeCreateReqVO extends SysErrorCodeBaseVO {
+
+}

+ 42 - 0
src/main/java/cn/iocoder/dashboard/modules/system/controller/errorcode/vo/SysErrorCodeExcelVO.java

@@ -0,0 +1,42 @@
+package cn.iocoder.dashboard.modules.system.controller.errorcode.vo;
+
+import cn.iocoder.dashboard.framework.excel.core.annotations.DictFormat;
+import cn.iocoder.dashboard.framework.excel.core.convert.DictConvert;
+import com.alibaba.excel.annotation.ExcelProperty;
+import lombok.Data;
+
+import java.util.Date;
+
+import static cn.iocoder.dashboard.modules.system.enums.dict.SysDictTypeEnum.SYS_ERROR_CODE_TYPE;
+
+/**
+ * 错误码 Excel VO
+ *
+ * @author 芋道源码
+ */
+@Data
+public class SysErrorCodeExcelVO {
+
+    @ExcelProperty("错误码编号")
+    private Long id;
+
+    @ExcelProperty(value = "错误码类型", converter = DictConvert.class)
+    @DictFormat(SYS_ERROR_CODE_TYPE)
+    private Integer type;
+
+    @ExcelProperty("应用名")
+    private String applicationName;
+
+    @ExcelProperty("错误码编码")
+    private Integer code;
+
+    @ExcelProperty("错误码错误提示")
+    private String message;
+
+    @ExcelProperty("备注")
+    private String memo;
+
+    @ExcelProperty("创建时间")
+    private Date createTime;
+
+}

+ 36 - 0
src/main/java/cn/iocoder/dashboard/modules/system/controller/errorcode/vo/SysErrorCodeExportReqVO.java

@@ -0,0 +1,36 @@
+package cn.iocoder.dashboard.modules.system.controller.errorcode.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.util.Date;
+
+import static cn.iocoder.dashboard.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@ApiModel(value = "错误码 Excel 导出 Request VO", description = "参数和 InfErrorCodePageReqVO 是一致的")
+@Data
+public class SysErrorCodeExportReqVO {
+
+    @ApiModelProperty(value = "错误码类型", example = "1")
+    private Integer type;
+
+    @ApiModelProperty(value = "应用名", example = "dashboard")
+    private String applicationName;
+
+    @ApiModelProperty(value = "错误码编码", example = "1234")
+    private Integer code;
+
+    @ApiModelProperty(value = "错误码错误提示", example = "帅气")
+    private String message;
+
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    @ApiModelProperty(value = "开始创建时间")
+    private Date beginCreateTime;
+
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    @ApiModelProperty(value = "结束创建时间")
+    private Date endCreateTime;
+
+}

+ 41 - 0
src/main/java/cn/iocoder/dashboard/modules/system/controller/errorcode/vo/SysErrorCodePageReqVO.java

@@ -0,0 +1,41 @@
+package cn.iocoder.dashboard.modules.system.controller.errorcode.vo;
+
+import cn.iocoder.dashboard.common.pojo.PageParam;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.util.Date;
+
+import static cn.iocoder.dashboard.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+
+@ApiModel("错误码分页 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class SysErrorCodePageReqVO extends PageParam {
+
+    @ApiModelProperty(value = "错误码类型", example = "1", notes = "参见 SysErrorCodeTypeEnum 枚举类")
+    private Integer type;
+
+    @ApiModelProperty(value = "应用名", example = "dashboard")
+    private String applicationName;
+
+    @ApiModelProperty(value = "错误码编码", example = "1234")
+    private Integer code;
+
+    @ApiModelProperty(value = "错误码错误提示", example = "帅气")
+    private String message;
+
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    @ApiModelProperty(value = "开始创建时间")
+    private Date beginCreateTime;
+
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
+    @ApiModelProperty(value = "结束创建时间")
+    private Date endCreateTime;
+
+}

+ 26 - 0
src/main/java/cn/iocoder/dashboard/modules/system/controller/errorcode/vo/SysErrorCodeRespVO.java

@@ -0,0 +1,26 @@
+package cn.iocoder.dashboard.modules.system.controller.errorcode.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+import java.util.Date;
+
+@ApiModel("错误码 Response VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class SysErrorCodeRespVO extends SysErrorCodeBaseVO {
+
+    @ApiModelProperty(value = "错误码编号", required = true, example = "1024")
+    private Long id;
+
+    @ApiModelProperty(value = "错误码类型", required = true, example = "1", notes = "参见 SysErrorCodeTypeEnum 枚举类")
+    private Integer type;
+
+    @ApiModelProperty(value = "创建时间", required = true)
+    private Date createTime;
+
+}

+ 21 - 0
src/main/java/cn/iocoder/dashboard/modules/system/controller/errorcode/vo/SysErrorCodeUpdateReqVO.java

@@ -0,0 +1,21 @@
+package cn.iocoder.dashboard.modules.system.controller.errorcode.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+import javax.validation.constraints.NotNull;
+
+@ApiModel("错误码更新 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class SysErrorCodeUpdateReqVO extends SysErrorCodeBaseVO {
+
+    @ApiModelProperty(value = "错误码编号", required = true, example = "1024")
+    @NotNull(message = "错误码编号不能为空")
+    private Long id;
+
+}

+ 42 - 0
src/main/java/cn/iocoder/dashboard/modules/system/convert/errorcode/SysErrorCodeConvert.java

@@ -0,0 +1,42 @@
+package cn.iocoder.dashboard.modules.system.convert.errorcode;
+
+import cn.iocoder.dashboard.common.pojo.PageResult;
+import cn.iocoder.dashboard.framework.errorcode.core.dto.ErrorCodeAutoGenerateReqDTO;
+import cn.iocoder.dashboard.framework.errorcode.core.dto.ErrorCodeRespDTO;
+import cn.iocoder.dashboard.modules.system.controller.errorcode.vo.SysErrorCodeCreateReqVO;
+import cn.iocoder.dashboard.modules.system.controller.errorcode.vo.SysErrorCodeExcelVO;
+import cn.iocoder.dashboard.modules.system.controller.errorcode.vo.SysErrorCodeRespVO;
+import cn.iocoder.dashboard.modules.system.controller.errorcode.vo.SysErrorCodeUpdateReqVO;
+import cn.iocoder.dashboard.modules.system.dal.dataobject.errorcode.SysErrorCodeDO;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+
+import java.util.List;
+
+/**
+ * 错误码 Convert
+ *
+ * @author 芋道源码
+ */
+@Mapper
+public interface SysErrorCodeConvert {
+
+    SysErrorCodeConvert INSTANCE = Mappers.getMapper(SysErrorCodeConvert.class);
+
+    SysErrorCodeDO convert(SysErrorCodeCreateReqVO bean);
+
+    SysErrorCodeDO convert(SysErrorCodeUpdateReqVO bean);
+
+    SysErrorCodeRespVO convert(SysErrorCodeDO bean);
+
+    List<SysErrorCodeRespVO> convertList(List<SysErrorCodeDO> list);
+
+    PageResult<SysErrorCodeRespVO> convertPage(PageResult<SysErrorCodeDO> page);
+
+    List<SysErrorCodeExcelVO> convertList02(List<SysErrorCodeDO> list);
+
+    SysErrorCodeDO convert(ErrorCodeAutoGenerateReqDTO bean);
+
+    List<ErrorCodeRespDTO> convertList03(List<SysErrorCodeDO> list);
+
+}

+ 50 - 0
src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/errorcode/SysErrorCodeDO.java

@@ -0,0 +1,50 @@
+package cn.iocoder.dashboard.modules.system.dal.dataobject.errorcode;
+
+import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO;
+import cn.iocoder.dashboard.modules.system.enums.errorcode.SysErrorCodeTypeEnum;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+/**
+ * 错误码表
+ *
+ * @author 芋道源码
+ */
+@TableName(value = "sys_error_code")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class SysErrorCodeDO extends BaseDO {
+
+    /**
+     * 错误码编号,自增
+     */
+    @TableId
+    private Long id;
+    /**
+     * 错误码类型
+     *
+     * 枚举 {@link SysErrorCodeTypeEnum}
+     */
+    private Integer type;
+    /**
+     * 应用名
+     */
+    private String applicationName;
+    /**
+     * 错误码编码
+     */
+    private Integer code;
+    /**
+     * 错误码错误提示
+     */
+    private String message;
+    /**
+     * 错误码备注
+     */
+    private String memo;
+
+}

+ 4 - 1
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dept/SysPostMapper.java

@@ -22,12 +22,15 @@ public interface SysPostMapper extends BaseMapperX<SysPostDO> {
 
     default PageResult<SysPostDO> selectPage(SysPostPageReqVO reqVO) {
         return selectPage(reqVO, new QueryWrapperX<SysPostDO>()
+                .likeIfPresent("code", reqVO.getCode())
                 .likeIfPresent("name", reqVO.getName())
                 .eqIfPresent("status", reqVO.getStatus()));
     }
 
     default List<SysPostDO> selectList(SysPostExportReqVO reqVO) {
-        return selectList(new QueryWrapperX<SysPostDO>().likeIfPresent("name", reqVO.getName())
+        return selectList(new QueryWrapperX<SysPostDO>()
+                .likeIfPresent("code", reqVO.getCode())
+                .likeIfPresent("name", reqVO.getName())
                 .eqIfPresent("status", reqVO.getStatus()));
     }
 

+ 52 - 0
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/errorcode/SysErrorCodeMapper.java

@@ -0,0 +1,52 @@
+package cn.iocoder.dashboard.modules.system.dal.mysql.errorcode;
+
+import cn.iocoder.dashboard.common.pojo.PageResult;
+import cn.iocoder.dashboard.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.dashboard.framework.mybatis.core.query.QueryWrapperX;
+import cn.iocoder.dashboard.modules.system.controller.errorcode.vo.SysErrorCodeExportReqVO;
+import cn.iocoder.dashboard.modules.system.controller.errorcode.vo.SysErrorCodePageReqVO;
+import cn.iocoder.dashboard.modules.system.dal.dataobject.errorcode.SysErrorCodeDO;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+
+@Mapper
+public interface SysErrorCodeMapper extends BaseMapperX<SysErrorCodeDO> {
+
+    default PageResult<SysErrorCodeDO> selectPage(SysErrorCodePageReqVO reqVO) {
+        return selectPage(reqVO, new QueryWrapperX<SysErrorCodeDO>()
+                .eqIfPresent("type", reqVO.getType())
+                .likeIfPresent("application_name", reqVO.getApplicationName())
+                .eqIfPresent("code", reqVO.getCode())
+                .likeIfPresent("message", reqVO.getMessage())
+                .betweenIfPresent("create_time", reqVO.getBeginCreateTime(), reqVO.getEndCreateTime())
+                .orderByAsc("application_name", "code"));
+    }
+
+    default List<SysErrorCodeDO> selectList(SysErrorCodeExportReqVO reqVO) {
+        return selectList(new QueryWrapperX<SysErrorCodeDO>()
+                .eqIfPresent("type", reqVO.getType())
+                .likeIfPresent("application_name", reqVO.getApplicationName())
+                .eqIfPresent("code", reqVO.getCode())
+                .likeIfPresent("message", reqVO.getMessage())
+                .betweenIfPresent("create_time", reqVO.getBeginCreateTime(), reqVO.getEndCreateTime())
+                .orderByAsc("application_name", "code"));
+    }
+
+    default List<SysErrorCodeDO> selectListByCodes(Collection<Integer> codes) {
+        return selectList(new QueryWrapper<SysErrorCodeDO>().in("code", codes));
+    }
+
+    default SysErrorCodeDO selectByCode(Integer code) {
+        return selectOne(new QueryWrapper<SysErrorCodeDO>().eq("code", code));
+    }
+
+    default List<SysErrorCodeDO> selectListByApplicationNameAndUpdateTimeGt(String applicationName, Date minUpdateTime) {
+        return selectList(new QueryWrapperX<SysErrorCodeDO>().eq("application_name", applicationName)
+                .gtIfPresent("update_time", minUpdateTime));
+    }
+
+}

+ 3 - 0
src/main/java/cn/iocoder/dashboard/modules/system/enums/SysErrorCodeConstants.java

@@ -91,5 +91,8 @@ public interface SysErrorCodeConstants {
     ErrorCode SMS_SEND_MOBILE_NOT_EXISTS = new ErrorCode(1002012000, "手机号不存在");
     ErrorCode SMS_SEND_MOBILE_TEMPLATE_PARAM_MISS = new ErrorCode(1002012001, "模板参数({})缺失");
 
+    // ========== 错误码模块 1002013000 ==========
+    ErrorCode ERROR_CODE_NOT_EXISTS = new ErrorCode(1002013000, "错误码不存在");
+    ErrorCode ERROR_CODE_DUPLICATE = new ErrorCode(1002013001, "已经存在编码为【{}】的错误码");
 
 }

+ 1 - 1
src/main/java/cn/iocoder/dashboard/modules/system/enums/dict/SysDictTypeEnum.java

@@ -22,12 +22,12 @@ public enum SysDictTypeEnum {
     SYS_SMS_TEMPLATE_TYPE("sys_sms_template_type"), // 短信模板类型
     SYS_SMS_SEND_STATUS("sys_sms_send_status"), // 短信发送状态
     SYS_SMS_RECEIVE_STATUS("sys_sms_receive_status"), // 短信接收状态
+    SYS_ERROR_CODE_TYPE("inf_error_code_type"), // 错误码的类型枚举
 
     INF_REDIS_TIMEOUT_TYPE("inf_redis_timeout_type"),  // Redis 超时类型
     INF_JOB_STATUS("inf_job_status"), // 定时任务状态的枚举
     INF_JOB_LOG_STATUS("inf_job_log_status"), // 定时任务日志状态的枚举
     INF_API_ERROR_LOG_PROCESS_STATUS("inf_api_error_log_process_status"), // API 错误日志的处理状态的枚举
-
     ;
 
 

+ 39 - 0
src/main/java/cn/iocoder/dashboard/modules/system/enums/errorcode/SysErrorCodeTypeEnum.java

@@ -0,0 +1,39 @@
+package cn.iocoder.dashboard.modules.system.enums.errorcode;
+
+import cn.iocoder.dashboard.common.core.IntArrayValuable;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Arrays;
+
+/**
+ * 错误码的类型枚举
+ *
+ * @author dylan
+ */
+@AllArgsConstructor
+@Getter
+public enum SysErrorCodeTypeEnum implements IntArrayValuable {
+
+    /**
+     * 自动生成
+     */
+    AUTO_GENERATION(1),
+    /**
+     * 手动编辑
+     */
+    MANUAL_OPERATION(2);
+
+    public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(SysErrorCodeTypeEnum::getType).toArray();
+
+    /**
+     * 类型
+     */
+    private final Integer type;
+
+    @Override
+    public int[] array() {
+        return ARRAYS;
+    }
+
+}

+ 1 - 1
src/main/java/cn/iocoder/dashboard/modules/system/service/auth/impl/SysAuthServiceImpl.java

@@ -85,7 +85,7 @@ public class SysAuthServiceImpl implements SysAuthService {
     @Override
     public String login(SysAuthLoginReqVO reqVO, String userIp, String userAgent) {
         // 判断验证码是否正确
-        this.verifyCaptcha(reqVO.getUsername(), reqVO.getUuid(), reqVO.getCode());
+//        this.verifyCaptcha(reqVO.getUsername(), reqVO.getUuid(), reqVO.getCode());
 
         // 使用账号密码,进行登陆。
         LoginUser loginUser = this.login0(reqVO.getUsername(), reqVO.getPassword());

+ 67 - 0
src/main/java/cn/iocoder/dashboard/modules/system/service/errorcode/SysErrorCodeService.java

@@ -0,0 +1,67 @@
+package cn.iocoder.dashboard.modules.system.service.errorcode;
+
+import cn.iocoder.dashboard.common.pojo.PageResult;
+import cn.iocoder.dashboard.framework.errorcode.core.service.ErrorCodeFrameworkService;
+import cn.iocoder.dashboard.modules.system.controller.errorcode.vo.SysErrorCodeCreateReqVO;
+import cn.iocoder.dashboard.modules.system.controller.errorcode.vo.SysErrorCodeExportReqVO;
+import cn.iocoder.dashboard.modules.system.controller.errorcode.vo.SysErrorCodePageReqVO;
+import cn.iocoder.dashboard.modules.system.controller.errorcode.vo.SysErrorCodeUpdateReqVO;
+import cn.iocoder.dashboard.modules.system.dal.dataobject.errorcode.SysErrorCodeDO;
+
+import javax.validation.Valid;
+import java.util.List;
+
+/**
+ * 错误码 Service 接口
+ *
+ * @author 芋道源码
+ */
+public interface SysErrorCodeService extends ErrorCodeFrameworkService {
+
+    /**
+     * 创建错误码
+     *
+     * @param createReqVO 创建信息
+     * @return 编号
+     */
+    Long createErrorCode(@Valid SysErrorCodeCreateReqVO createReqVO);
+
+    /**
+     * 更新错误码
+     *
+     * @param updateReqVO 更新信息
+     */
+    void updateErrorCode(@Valid SysErrorCodeUpdateReqVO updateReqVO);
+
+    /**
+     * 删除错误码
+     *
+     * @param id 编号
+     */
+    void deleteErrorCode(Long id);
+
+    /**
+     * 获得错误码
+     *
+     * @param id 编号
+     * @return 错误码
+     */
+    SysErrorCodeDO getErrorCode(Long id);
+
+    /**
+     * 获得错误码分页
+     *
+     * @param pageReqVO 分页查询
+     * @return 错误码分页
+     */
+    PageResult<SysErrorCodeDO> getErrorCodePage(SysErrorCodePageReqVO pageReqVO);
+
+    /**
+     * 获得错误码列表, 用于 Excel 导出
+     *
+     * @param exportReqVO 查询条件
+     * @return 错误码列表
+     */
+    List<SysErrorCodeDO> getErrorCodeList(SysErrorCodeExportReqVO exportReqVO);
+
+}

+ 174 - 0
src/main/java/cn/iocoder/dashboard/modules/system/service/errorcode/impl/SysErrorCodeServiceImpl.java

@@ -0,0 +1,174 @@
+package cn.iocoder.dashboard.modules.system.service.errorcode.impl;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.iocoder.dashboard.common.pojo.PageResult;
+import cn.iocoder.dashboard.framework.errorcode.core.dto.ErrorCodeAutoGenerateReqDTO;
+import cn.iocoder.dashboard.framework.errorcode.core.dto.ErrorCodeRespDTO;
+import cn.iocoder.dashboard.modules.system.convert.errorcode.SysErrorCodeConvert;
+import cn.iocoder.dashboard.modules.system.controller.errorcode.vo.SysErrorCodeCreateReqVO;
+import cn.iocoder.dashboard.modules.system.controller.errorcode.vo.SysErrorCodeExportReqVO;
+import cn.iocoder.dashboard.modules.system.controller.errorcode.vo.SysErrorCodePageReqVO;
+import cn.iocoder.dashboard.modules.system.controller.errorcode.vo.SysErrorCodeUpdateReqVO;
+import cn.iocoder.dashboard.modules.system.dal.dataobject.errorcode.SysErrorCodeDO;
+import cn.iocoder.dashboard.modules.system.dal.mysql.errorcode.SysErrorCodeMapper;
+import cn.iocoder.dashboard.modules.system.enums.errorcode.SysErrorCodeTypeEnum;
+import cn.iocoder.dashboard.modules.system.service.errorcode.SysErrorCodeService;
+import com.google.common.annotations.VisibleForTesting;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.validation.annotation.Validated;
+
+import javax.annotation.Resource;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import static cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*;
+import static cn.iocoder.dashboard.util.collection.CollectionUtils.convertMap;
+import static cn.iocoder.dashboard.util.collection.CollectionUtils.convertSet;
+
+/**
+ * 错误码 Service 实现类
+ *
+ * @author dlyan
+ */
+@Service
+@Validated
+@Slf4j
+public class SysErrorCodeServiceImpl implements SysErrorCodeService {
+
+    @Resource
+    private SysErrorCodeMapper errorCodeMapper;
+
+    @Override
+    public Long createErrorCode(SysErrorCodeCreateReqVO createReqVO) {
+        // 校验 code 重复
+        validateCodeDuplicate(createReqVO.getCode(), null);
+
+        // 插入
+        SysErrorCodeDO errorCode = SysErrorCodeConvert.INSTANCE.convert(createReqVO)
+                .setType(SysErrorCodeTypeEnum.MANUAL_OPERATION.getType());
+        errorCodeMapper.insert(errorCode);
+        // 返回
+        return errorCode.getId();
+    }
+
+    @Override
+    public void updateErrorCode(SysErrorCodeUpdateReqVO updateReqVO) {
+        // 校验存在
+        this.validateErrorCodeExists(updateReqVO.getId());
+        // 校验 code 重复
+        validateCodeDuplicate(updateReqVO.getCode(), updateReqVO.getId());
+
+        // 更新
+        SysErrorCodeDO updateObj = SysErrorCodeConvert.INSTANCE.convert(updateReqVO)
+                .setType(SysErrorCodeTypeEnum.MANUAL_OPERATION.getType());
+        errorCodeMapper.updateById(updateObj);
+    }
+
+    @Override
+    public void deleteErrorCode(Long id) {
+        // 校验存在
+        this.validateErrorCodeExists(id);
+        // 删除
+        errorCodeMapper.deleteById(id);
+    }
+
+    /**
+     * 校验错误码的唯一字段是否重复
+     *
+     * 是否存在相同编码的错误码
+     *
+     * @param code 错误码编码
+     * @param id 错误码编号
+     */
+    @VisibleForTesting
+    public void validateCodeDuplicate(Integer code, Long id) {
+        SysErrorCodeDO errorCodeDO = errorCodeMapper.selectByCode(code);
+        if (errorCodeDO == null) {
+            return;
+        }
+        // 如果 id 为空,说明不用比较是否为相同 id 的错误码
+        if (id == null) {
+            throw exception(ERROR_CODE_DUPLICATE);
+        }
+        if (!errorCodeDO.getId().equals(id)) {
+            throw exception(ERROR_CODE_DUPLICATE);
+        }
+    }
+
+    @VisibleForTesting
+    public void validateErrorCodeExists(Long id) {
+        if (errorCodeMapper.selectById(id) == null) {
+            throw exception(ERROR_CODE_NOT_EXISTS);
+        }
+    }
+
+    @Override
+    public SysErrorCodeDO getErrorCode(Long id) {
+        return errorCodeMapper.selectById(id);
+    }
+
+    @Override
+    public PageResult<SysErrorCodeDO> getErrorCodePage(SysErrorCodePageReqVO pageReqVO) {
+        return errorCodeMapper.selectPage(pageReqVO);
+    }
+
+    @Override
+    public List<SysErrorCodeDO> getErrorCodeList(SysErrorCodeExportReqVO exportReqVO) {
+        return errorCodeMapper.selectList(exportReqVO);
+    }
+
+    @Override
+    @Transactional
+    public void autoGenerateErrorCodes(List<ErrorCodeAutoGenerateReqDTO> autoGenerateDTOs) {
+        if (CollUtil.isEmpty(autoGenerateDTOs)) {
+            return;
+        }
+        // 获得错误码
+        List<SysErrorCodeDO> errorCodeDOs = errorCodeMapper.selectListByCodes(
+                convertSet(autoGenerateDTOs, ErrorCodeAutoGenerateReqDTO::getCode));
+        Map<Integer, SysErrorCodeDO> errorCodeDOMap = convertMap(errorCodeDOs, SysErrorCodeDO::getCode);
+
+        // 遍历 autoGenerateBOs 数组,逐个插入或更新。考虑到每次量级不大,就不走批量了
+        autoGenerateDTOs.forEach(autoGenerateDTO -> {
+            SysErrorCodeDO errorCodeDO = errorCodeDOMap.get(autoGenerateDTO.getCode());
+            // 不存在,则进行新增
+            if (errorCodeDO == null) {
+                errorCodeDO = SysErrorCodeConvert.INSTANCE.convert(autoGenerateDTO)
+                        .setType(SysErrorCodeTypeEnum.AUTO_GENERATION.getType());
+                errorCodeMapper.insert(errorCodeDO);
+                return;
+            }
+            // 存在,则进行更新。更新有三个前置条件:
+            // 条件 1. 只更新自动生成的错误码,即 Type 为 ErrorCodeTypeEnum.AUTO_GENERATION
+            if (!SysErrorCodeTypeEnum.AUTO_GENERATION.getType().equals(errorCodeDO.getType())) {
+                return;
+            }
+            // 条件 2. 分组 applicationName 必须匹配,避免存在错误码冲突的情况
+            if (!autoGenerateDTO.getApplicationName().equals(errorCodeDO.getApplicationName())) {
+                log.error("[autoGenerateErrorCodes][自动创建({}/{}) 错误码失败,数据库中已经存在({}/{})]",
+                        autoGenerateDTO.getCode(), autoGenerateDTO.getApplicationName(),
+                        errorCodeDO.getCode(), errorCodeDO.getApplicationName());
+                return;
+            }
+            // 条件 3. 错误提示语存在差异
+            if (autoGenerateDTO.getMessage().equals(errorCodeDO.getMessage())) {
+                return;
+            }
+            // 最终匹配,进行更新
+            errorCodeMapper.updateById(new SysErrorCodeDO().setId(errorCodeDO.getId()).setMessage(autoGenerateDTO.getMessage()));
+        });
+    }
+
+    @Override
+    public List<ErrorCodeRespDTO> getErrorCodeList(String applicationName, Date minUpdateTime) {
+        List<SysErrorCodeDO> errorCodeDOs = errorCodeMapper.selectListByApplicationNameAndUpdateTimeGt(
+                applicationName, minUpdateTime);
+        return SysErrorCodeConvert.INSTANCE.convertList03(errorCodeDOs);
+    }
+
+}
+

+ 10 - 0
src/main/java/cn/iocoder/dashboard/util/date/DateUtils.java

@@ -64,4 +64,14 @@ public class DateUtils {
         return calendar.getTime();
     }
 
+    public static Date max(Date a, Date b) {
+        if (a == null) {
+            return b;
+        }
+        if (b == null) {
+            return a;
+        }
+        return a.compareTo(b) > 0 ? a : b;
+    }
+
 }

+ 6 - 0
src/main/resources/application.yaml

@@ -12,6 +12,7 @@ spring:
       max-file-size: 16MB # 单个文件大小
       max-request-size: 32MB # 设置总上传的文件大小
 
+  # Jackson 配置项
   jackson:
     serialization:
       write-dates-as-timestamps: true # 设置 Date 的格式,使用时间戳
@@ -34,3 +35,8 @@ mybatis-plus:
 
 --- #################### 芋道相关配置 ####################
 
+yudao:
+  error-code: # 错误码相关配置项
+    constants-class-list:
+      - cn.iocoder.dashboard.modules.infra.enums.InfErrorCodeConstants
+      - cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants

+ 1 - 1
src/main/resources/codegen/java/service/serviceImpl.vm

@@ -53,7 +53,7 @@ public class ${table.className}ServiceImpl implements ${table.className}Service
     public void delete${simpleClassName}(${primaryColumn.javaType} id) {
         // 校验存在
         this.validate${simpleClassName}Exists(id);
-        // 更新
+        // 删除
         ${classNameVar}Mapper.deleteById(id);
     }
 

+ 305 - 0
src/test/java/cn/iocoder/dashboard/modules/system/service/errorcode/SysErrorCodeServiceTest.java

@@ -0,0 +1,305 @@
+package cn.iocoder.dashboard.modules.system.service.errorcode;
+
+import cn.iocoder.dashboard.BaseDbUnitTest;
+import cn.iocoder.dashboard.common.pojo.PageResult;
+import cn.iocoder.dashboard.framework.errorcode.core.dto.ErrorCodeAutoGenerateReqDTO;
+import cn.iocoder.dashboard.modules.infra.enums.config.InfConfigTypeEnum;
+import cn.iocoder.dashboard.modules.system.controller.errorcode.vo.SysErrorCodeCreateReqVO;
+import cn.iocoder.dashboard.modules.system.controller.errorcode.vo.SysErrorCodeExportReqVO;
+import cn.iocoder.dashboard.modules.system.controller.errorcode.vo.SysErrorCodePageReqVO;
+import cn.iocoder.dashboard.modules.system.controller.errorcode.vo.SysErrorCodeUpdateReqVO;
+import cn.iocoder.dashboard.modules.system.dal.dataobject.errorcode.SysErrorCodeDO;
+import cn.iocoder.dashboard.modules.system.dal.mysql.errorcode.SysErrorCodeMapper;
+import cn.iocoder.dashboard.modules.system.enums.errorcode.SysErrorCodeTypeEnum;
+import cn.iocoder.dashboard.modules.system.service.errorcode.impl.SysErrorCodeServiceImpl;
+import cn.iocoder.dashboard.util.collection.ArrayUtils;
+import cn.iocoder.dashboard.util.object.ObjectUtils;
+import org.assertj.core.util.Lists;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+import org.slf4j.Logger;
+import org.springframework.context.annotation.Import;
+
+import javax.annotation.Resource;
+import java.util.List;
+import java.util.function.Consumer;
+
+import static cn.hutool.core.util.RandomUtil.randomEle;
+import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.ERROR_CODE_DUPLICATE;
+import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.ERROR_CODE_NOT_EXISTS;
+import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals;
+import static cn.iocoder.dashboard.util.AssertUtils.assertServiceException;
+import static cn.iocoder.dashboard.util.RandomUtils.*;
+import static cn.iocoder.dashboard.util.date.DateUtils.buildTime;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+* {@link SysErrorCodeServiceImpl} 的单元测试类
+*
+* @author 芋道源码
+*/
+@Import(SysErrorCodeServiceImpl.class)
+public class SysErrorCodeServiceTest extends BaseDbUnitTest {
+
+    @Resource
+    private SysErrorCodeServiceImpl errorCodeService;
+
+    @Resource
+    private SysErrorCodeMapper errorCodeMapper;
+
+    @Mock
+    private Logger log;
+
+    @Test
+    public void testCreateErrorCode_success() {
+        // 准备参数
+        SysErrorCodeCreateReqVO reqVO = randomPojo(SysErrorCodeCreateReqVO.class);
+
+        // 调用
+        Long errorCodeId = errorCodeService.createErrorCode(reqVO);
+        // 断言
+        assertNotNull(errorCodeId);
+        // 校验记录的属性是否正确
+        SysErrorCodeDO errorCode = errorCodeMapper.selectById(errorCodeId);
+        assertPojoEquals(reqVO, errorCode);
+        assertEquals(SysErrorCodeTypeEnum.MANUAL_OPERATION.getType(), errorCode.getType());
+    }
+
+    @Test
+    public void testUpdateErrorCode_success() {
+        // mock 数据
+        SysErrorCodeDO dbErrorCode = randomInfErrorCodeDO();
+        errorCodeMapper.insert(dbErrorCode);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        SysErrorCodeUpdateReqVO reqVO = randomPojo(SysErrorCodeUpdateReqVO.class, o -> {
+            o.setId(dbErrorCode.getId()); // 设置更新的 ID
+        });
+
+        // 调用
+        errorCodeService.updateErrorCode(reqVO);
+        // 校验是否更新正确
+        SysErrorCodeDO errorCode = errorCodeMapper.selectById(reqVO.getId()); // 获取最新的
+        assertPojoEquals(reqVO, errorCode);
+        assertEquals(SysErrorCodeTypeEnum.MANUAL_OPERATION.getType(), errorCode.getType());
+    }
+
+    @Test
+    public void testDeleteErrorCode_success() {
+        // mock 数据
+        SysErrorCodeDO dbErrorCode = randomInfErrorCodeDO();
+        errorCodeMapper.insert(dbErrorCode);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbErrorCode.getId();
+
+        // 调用
+        errorCodeService.deleteErrorCode(id);
+       // 校验数据不存在了
+       assertNull(errorCodeMapper.selectById(id));
+    }
+
+    @Test
+    public void testGetErrorCodePage() {
+       // mock 数据
+       SysErrorCodeDO dbErrorCode = initGetErrorCodePage();
+       // 准备参数
+       SysErrorCodePageReqVO reqVO = new SysErrorCodePageReqVO();
+       reqVO.setType(SysErrorCodeTypeEnum.AUTO_GENERATION.getType());
+       reqVO.setApplicationName("yudao");
+       reqVO.setCode(1);
+       reqVO.setMessage("yu");
+       reqVO.setBeginCreateTime(buildTime(2020, 11, 1));
+       reqVO.setEndCreateTime(buildTime(2020, 11, 30));
+
+       // 调用
+       PageResult<SysErrorCodeDO> pageResult = errorCodeService.getErrorCodePage(reqVO);
+       // 断言
+       assertEquals(1, pageResult.getTotal());
+       assertEquals(1, pageResult.getList().size());
+       assertPojoEquals(dbErrorCode, pageResult.getList().get(0));
+    }
+
+    /**
+     * 初始化 getErrorCodePage 方法的测试数据
+     */
+    private SysErrorCodeDO initGetErrorCodePage() {
+        SysErrorCodeDO dbErrorCode = randomInfErrorCodeDO(o -> { // 等会查询到
+            o.setType(SysErrorCodeTypeEnum.AUTO_GENERATION.getType());
+            o.setApplicationName("yudaoyuanma");
+            o.setCode(1);
+            o.setMessage("yudao");
+            o.setCreateTime(buildTime(2020, 11, 11));
+        });
+        errorCodeMapper.insert(dbErrorCode);
+        // 测试 type 不匹配
+        errorCodeMapper.insert(ObjectUtils.clone(dbErrorCode, o -> o.setType(SysErrorCodeTypeEnum.MANUAL_OPERATION.getType())));
+        // 测试 applicationName 不匹配
+        errorCodeMapper.insert(ObjectUtils.clone(dbErrorCode, o -> o.setApplicationName("yunai")));
+        // 测试 code 不匹配
+        errorCodeMapper.insert(ObjectUtils.clone(dbErrorCode, o -> o.setCode(2)));
+        // 测试 message 不匹配
+        errorCodeMapper.insert(ObjectUtils.clone(dbErrorCode, o -> o.setMessage("nai")));
+        // 测试 createTime 不匹配
+        errorCodeMapper.insert(ObjectUtils.clone(dbErrorCode, o -> o.setCreateTime(buildTime(2020, 12, 12))));
+        return dbErrorCode;
+    }
+
+    @Test
+    public void testGetErrorCodeList() {
+        // mock 数据
+        SysErrorCodeDO dbErrorCode = initGetErrorCodePage();
+        // 准备参数
+        SysErrorCodeExportReqVO reqVO = new SysErrorCodeExportReqVO();
+        reqVO.setType(SysErrorCodeTypeEnum.AUTO_GENERATION.getType());
+        reqVO.setApplicationName("yudao");
+        reqVO.setCode(1);
+        reqVO.setMessage("yu");
+        reqVO.setBeginCreateTime(buildTime(2020, 11, 1));
+        reqVO.setEndCreateTime(buildTime(2020, 11, 30));
+
+        // 调用
+        List<SysErrorCodeDO> list = errorCodeService.getErrorCodeList(reqVO);
+        // 断言
+        assertEquals(1, list.size());
+        assertPojoEquals(dbErrorCode, list.get(0));
+    }
+
+    @Test
+    public void testValidateCodeDuplicate_codeDuplicateForCreate() {
+        // 准备参数
+        Integer code = randomInteger();
+        // mock 数据
+        errorCodeMapper.insert(randomInfErrorCodeDO(o -> o.setCode(code)));
+
+        // 调用,校验异常
+        assertServiceException(() -> errorCodeService.validateCodeDuplicate(code, null),
+                ERROR_CODE_DUPLICATE);
+    }
+
+    @Test
+    public void testValidateCodeDuplicate_codeDuplicateForUpdate() {
+        // 准备参数
+        Long id = randomLongId();
+        Integer code = randomInteger();
+        // mock 数据
+        errorCodeMapper.insert(randomInfErrorCodeDO(o -> o.setCode(code)));
+
+        // 调用,校验异常
+        assertServiceException(() -> errorCodeService.validateCodeDuplicate(code, id),
+                ERROR_CODE_DUPLICATE);
+    }
+
+    @Test
+    public void testValidateErrorCodeExists_notExists() {
+        assertServiceException(() -> errorCodeService.validateErrorCodeExists(null),
+                ERROR_CODE_NOT_EXISTS);
+    }
+
+    /**
+     * 情况 1,错误码不存在的情况
+     */
+    @Test
+    public void testAutoGenerateErrorCodes_01() {
+        // 准备参数
+        ErrorCodeAutoGenerateReqDTO generateReqDTO = randomPojo(ErrorCodeAutoGenerateReqDTO.class);
+        // mock 方法
+
+        // 调用
+        errorCodeService.autoGenerateErrorCodes(Lists.newArrayList(generateReqDTO));
+        // 断言
+        SysErrorCodeDO errorCode = errorCodeMapper.selectOne(null);
+        assertPojoEquals(generateReqDTO, errorCode);
+        assertEquals(SysErrorCodeTypeEnum.AUTO_GENERATION.getType(), errorCode.getType());
+    }
+
+    /**
+     * 情况 2.1,错误码存在,但是是 SysErrorCodeTypeEnum.MANUAL_OPERATION 类型
+     */
+    @Test
+    public void testAutoGenerateErrorCodes_021() {
+        // mock 数据
+        SysErrorCodeDO dbErrorCode = randomInfErrorCodeDO(o -> o.setType(SysErrorCodeTypeEnum.MANUAL_OPERATION.getType()));
+        errorCodeMapper.insert(dbErrorCode);
+        // 准备参数
+        ErrorCodeAutoGenerateReqDTO generateReqDTO = randomPojo(ErrorCodeAutoGenerateReqDTO.class,
+                o -> o.setCode(dbErrorCode.getCode()));
+        // mock 方法
+
+        // 调用
+        errorCodeService.autoGenerateErrorCodes(Lists.newArrayList(generateReqDTO));
+        // 断言,相等,说明不会更新
+        SysErrorCodeDO errorCode = errorCodeMapper.selectById(dbErrorCode.getId());
+        assertPojoEquals(dbErrorCode, errorCode);
+    }
+
+    /**
+     * 情况 2.2,错误码存在,但是是 applicationName 不匹配
+     */
+    @Test
+    public void testAutoGenerateErrorCodes_022() {
+        // mock 数据
+        SysErrorCodeDO dbErrorCode = randomInfErrorCodeDO(o -> o.setType(SysErrorCodeTypeEnum.AUTO_GENERATION.getType()));
+        errorCodeMapper.insert(dbErrorCode);
+        // 准备参数
+        ErrorCodeAutoGenerateReqDTO generateReqDTO = randomPojo(ErrorCodeAutoGenerateReqDTO.class,
+                o -> o.setCode(dbErrorCode.getCode()).setApplicationName(randomString()));
+        // mock 方法
+
+        // 调用
+        errorCodeService.autoGenerateErrorCodes(Lists.newArrayList(generateReqDTO));
+        // 断言,相等,说明不会更新
+        SysErrorCodeDO errorCode = errorCodeMapper.selectById(dbErrorCode.getId());
+        assertPojoEquals(dbErrorCode, errorCode);
+    }
+
+    /**
+     * 情况 2.3,错误码存在,但是是 message 相同
+     */
+    @Test
+    public void testAutoGenerateErrorCodes_023() {
+        // mock 数据
+        SysErrorCodeDO dbErrorCode = randomInfErrorCodeDO(o -> o.setType(SysErrorCodeTypeEnum.AUTO_GENERATION.getType()));
+        errorCodeMapper.insert(dbErrorCode);
+        // 准备参数
+        ErrorCodeAutoGenerateReqDTO generateReqDTO = randomPojo(ErrorCodeAutoGenerateReqDTO.class,
+                o -> o.setCode(dbErrorCode.getCode()).setApplicationName(dbErrorCode.getApplicationName())
+                    .setMessage(dbErrorCode.getMessage()));
+        // mock 方法
+
+        // 调用
+        errorCodeService.autoGenerateErrorCodes(Lists.newArrayList(generateReqDTO));
+        // 断言,相等,说明不会更新
+        SysErrorCodeDO errorCode = errorCodeMapper.selectById(dbErrorCode.getId());
+        assertPojoEquals(dbErrorCode, errorCode);
+    }
+
+    /**
+     * 情况 2.3,错误码存在,但是是 message 不同,则进行更新
+     */
+    @Test
+    public void testAutoGenerateErrorCodes_024() {
+        // mock 数据
+        SysErrorCodeDO dbErrorCode = randomInfErrorCodeDO(o -> o.setType(SysErrorCodeTypeEnum.AUTO_GENERATION.getType()));
+        errorCodeMapper.insert(dbErrorCode);
+        // 准备参数
+        ErrorCodeAutoGenerateReqDTO generateReqDTO = randomPojo(ErrorCodeAutoGenerateReqDTO.class,
+                o -> o.setCode(dbErrorCode.getCode()).setApplicationName(dbErrorCode.getApplicationName()));
+        // mock 方法
+
+        // 调用
+        errorCodeService.autoGenerateErrorCodes(Lists.newArrayList(generateReqDTO));
+        // 断言,匹配
+        SysErrorCodeDO errorCode = errorCodeMapper.selectById(dbErrorCode.getId());
+        assertPojoEquals(generateReqDTO, errorCode);
+    }
+
+    // ========== 随机对象 ==========
+
+    @SafeVarargs
+    private static SysErrorCodeDO randomInfErrorCodeDO(Consumer<SysErrorCodeDO>... consumers) {
+        Consumer<SysErrorCodeDO> consumer = (o) -> {
+            o.setType(randomEle(InfConfigTypeEnum.values()).getType()); // 保证 key 的范围
+        };
+        return randomPojo(SysErrorCodeDO.class, ArrayUtils.append(consumer, consumers));
+    }
+
+}

+ 1 - 0
src/test/resources/sql/clean.sql

@@ -22,3 +22,4 @@ DELETE FROM "sys_user";
 DELETE FROM "sys_sms_channel";
 DELETE FROM "sys_sms_template";
 DELETE FROM "sys_sms_log";
+DELETE FROM "sys_error_code";

+ 17 - 2
src/test/resources/sql/create_tables.sql

@@ -359,7 +359,7 @@ CREATE TABLE IF NOT EXISTS "sys_sms_channel" (
    PRIMARY KEY ("id")
 ) COMMENT '短信渠道';
 
-CREATE TABLE "sys_sms_template" (
+CREATE TABLE IF NOT EXISTS "sys_sms_template" (
     "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
     "type" tinyint NOT NULL,
     "status" tinyint NOT NULL,
@@ -379,7 +379,7 @@ CREATE TABLE "sys_sms_template" (
     PRIMARY KEY ("id")
 ) COMMENT '短信模板';
 
-CREATE TABLE "sys_sms_log" (
+CREATE TABLE IF NOT EXISTS "sys_sms_log" (
    "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
    "channel_id" bigint NOT NULL,
    "channel_code" varchar(63) NOT NULL,
@@ -411,3 +411,18 @@ CREATE TABLE "sys_sms_log" (
    "deleted" bit NOT NULL DEFAULT FALSE,
    PRIMARY KEY ("id")
 ) COMMENT '短信日志';
+
+CREATE TABLE IF NOT EXISTS "sys_error_code" (
+  "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
+  "type" tinyint NOT NULL DEFAULT '0',
+  "application_name" varchar(50) NOT NULL,
+  "code" int NOT NULL DEFAULT '0',
+  "message" varchar(512) NOT NULL DEFAULT '',
+  "memo" varchar(512) DEFAULT '',
+  "creator" varchar(64) DEFAULT '',
+  "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+  "updater" varchar(64) DEFAULT '',
+  "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+  "deleted" bit NOT NULL DEFAULT FALSE,
+  PRIMARY KEY ("id")
+) COMMENT '错误码表';