Browse Source

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

芋道源码 4 years ago
parent
commit
27e1fe4468
43 changed files with 1995 additions and 22 deletions
  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 生成日志内容 |
 | 🚀 | 操作日志 | 系统正常操作日志记录和查询,集成 Swagger 生成日志内容 |
-|  | 登录日志 | 系统登录日志记录查询包含登录异常 |
+|  | 登录日志 | 系统登录日志记录查询,包含登录异常 |
+| 🚀 | 错误码管理 | 系统所有错误码的管理,可在线修改错误提示,无需重启服务 |
 |  | 通知公告 | 系统通知公告信息发布维护 |
 |  | 通知公告 | 系统通知公告信息发布维护 |
 
 
 计划新增功能:
 计划新增功能:
@@ -53,7 +54,7 @@
 | 🚀 | 文件服务 | 支持本地文件存储,同时支持兼容 Amazon S3 协议的云服务、开源组件 | 
 | 🚀 | 文件服务 | 支持本地文件存储,同时支持兼容 Amazon S3 协议的云服务、开源组件 | 
 | 🚀 | API 日志 | 包括 RESTful API 访问日志、异常日志两部分,方便排查 API 相关的问题 |
 | 🚀 | API 日志 | 包括 RESTful API 访问日志、异常日志两部分,方便排查 API 相关的问题 |
 |  | MySQL 监控 | 监视当前系统数据库连接池状态,可进行分析SQL找出系统性能瓶颈 |
 |  | MySQL 监控 | 监视当前系统数据库连接池状态,可进行分析SQL找出系统性能瓶颈 |
-| | Redis 监控 |监控 Redis 数据库的使用情况,使用的 Redis Key 管理 |
+|  | Redis 监控 |监控 Redis 数据库的使用情况,使用的 Redis Key 管理 |
 | 🚀 |Java 监控 | 基于 Spring Boot Admin 实现 Java 应用的监控 |
 | 🚀 |Java 监控 | 基于 Spring Boot Admin 实现 Java 应用的监控 |
 | 🚀 | 链路追踪 | 基于 SkyWalking 实现性能监控,特别是链路的追踪 |
 | 🚀 | 链路追踪 | 基于 SkyWalking 实现性能监控,特别是链路的追踪 |
 | 🚀 | 分布式锁 | 基于 Redis 实现分布式锁,满足并发场景 |
 | 🚀 | 分布式锁 | 基于 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_TEMPLATE_TYPE: 'sys_sms_template_type',
   SYS_SMS_SEND_STATUS: 'sys_sms_send_status',
   SYS_SMS_SEND_STATUS: 'sys_sms_send_status',
   SYS_SMS_RECEIVE_STATUS: 'sys_sms_receive_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_REDIS_TIMEOUT_TYPE: 'inf_redis_timeout_type',
   INF_JOB_STATUS: 'inf_job_status',
   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
  Target Server Version : 50718
  File Encoding         : 65001
  File Encoding         : 65001
 
 
- Date: 18/04/2021 00:36:06
+ Date: 22/04/2021 00:48:26
 */
 */
 
 
 SET NAMES utf8mb4;
 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 '更新时间',
   `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   PRIMARY KEY (`id`) USING BTREE
   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
 -- 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 '更新时间',
   `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   PRIMARY KEY (`id`) USING BTREE
   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
 -- Records of inf_api_error_log
@@ -118,7 +118,7 @@ CREATE TABLE `inf_config` (
 -- ----------------------------
 -- ----------------------------
 BEGIN;
 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 (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 (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 (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');
 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 '更新时间',
   `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   PRIMARY KEY (`id`) USING BTREE
   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
 -- 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 '更新时间',
   `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   PRIMARY KEY (`id`) USING BTREE
   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
 -- 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 (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 (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 (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;
 COMMIT;
 
 
 -- ----------------------------
 -- ----------------------------
@@ -356,7 +358,7 @@ CREATE TABLE `sys_dict_type` (
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   PRIMARY KEY (`id`) USING BTREE,
   PRIMARY KEY (`id`) USING BTREE,
   UNIQUE KEY `dict_type` (`type`)
   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
 -- 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 (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 (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 (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;
 COMMIT;
 
 
 -- ----------------------------
 -- ----------------------------
@@ -403,7 +508,7 @@ CREATE TABLE `sys_login_log` (
   `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
   `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   PRIMARY KEY (`id`) USING BTREE
   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
 -- 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 '更新时间',
   `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   PRIMARY KEY (`id`) USING BTREE
   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
 -- 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 (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 (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 (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;
 COMMIT;
 
 
 -- ----------------------------
 -- ----------------------------
@@ -622,7 +733,7 @@ CREATE TABLE `sys_operate_log` (
   `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
   `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   PRIMARY KEY (`id`) USING BTREE
   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
 -- 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 '更新时间',
   `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   PRIMARY KEY (`id`) USING BTREE
   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
 -- 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 ('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 ('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 ('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 ('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 ('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');
 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 ('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 ('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 ('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 ('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 ('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 ('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 ('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 ('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 ('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 ('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 ('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 ('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 ('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 ('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 ('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');
 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 '更新时间',
   `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   PRIMARY KEY (`id`) USING BTREE
   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
 -- 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 (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 (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 (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;
 COMMIT;
 
 
 -- ----------------------------
 -- ----------------------------
@@ -1379,7 +1508,7 @@ CREATE TABLE `tool_codegen_table` (
   `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
   `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
   PRIMARY KEY (`id`) USING BTREE
   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
 -- 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 (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 (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 (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;
 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
 @Data
 public class SysPostExportReqVO {
 public class SysPostExportReqVO {
 
 
+    @ApiModelProperty(value = "岗位编码", example = "yudao", notes = "模糊匹配")
+    private String code;
+
     @ApiModelProperty(value = "岗位名称", example = "芋道", notes = "模糊匹配")
     @ApiModelProperty(value = "岗位名称", example = "芋道", notes = "模糊匹配")
     private String name;
     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)
 @EqualsAndHashCode(callSuper = true)
 public class SysPostPageReqVO extends PageParam {
 public class SysPostPageReqVO extends PageParam {
 
 
+    @ApiModelProperty(value = "岗位编码", example = "yudao", notes = "模糊匹配")
+    private String code;
+
     @ApiModelProperty(value = "岗位名称", example = "芋道", notes = "模糊匹配")
     @ApiModelProperty(value = "岗位名称", example = "芋道", notes = "模糊匹配")
     private String name;
     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) {
     default PageResult<SysPostDO> selectPage(SysPostPageReqVO reqVO) {
         return selectPage(reqVO, new QueryWrapperX<SysPostDO>()
         return selectPage(reqVO, new QueryWrapperX<SysPostDO>()
+                .likeIfPresent("code", reqVO.getCode())
                 .likeIfPresent("name", reqVO.getName())
                 .likeIfPresent("name", reqVO.getName())
                 .eqIfPresent("status", reqVO.getStatus()));
                 .eqIfPresent("status", reqVO.getStatus()));
     }
     }
 
 
     default List<SysPostDO> selectList(SysPostExportReqVO reqVO) {
     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()));
                 .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_NOT_EXISTS = new ErrorCode(1002012000, "手机号不存在");
     ErrorCode SMS_SEND_MOBILE_TEMPLATE_PARAM_MISS = new ErrorCode(1002012001, "模板参数({})缺失");
     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_TEMPLATE_TYPE("sys_sms_template_type"), // 短信模板类型
     SYS_SMS_SEND_STATUS("sys_sms_send_status"), // 短信发送状态
     SYS_SMS_SEND_STATUS("sys_sms_send_status"), // 短信发送状态
     SYS_SMS_RECEIVE_STATUS("sys_sms_receive_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_REDIS_TIMEOUT_TYPE("inf_redis_timeout_type"),  // Redis 超时类型
     INF_JOB_STATUS("inf_job_status"), // 定时任务状态的枚举
     INF_JOB_STATUS("inf_job_status"), // 定时任务状态的枚举
     INF_JOB_LOG_STATUS("inf_job_log_status"), // 定时任务日志状态的枚举
     INF_JOB_LOG_STATUS("inf_job_log_status"), // 定时任务日志状态的枚举
     INF_API_ERROR_LOG_PROCESS_STATUS("inf_api_error_log_process_status"), // API 错误日志的处理状态的枚举
     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
     @Override
     public String login(SysAuthLoginReqVO reqVO, String userIp, String userAgent) {
     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());
         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();
         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-file-size: 16MB # 单个文件大小
       max-request-size: 32MB # 设置总上传的文件大小
       max-request-size: 32MB # 设置总上传的文件大小
 
 
+  # Jackson 配置项
   jackson:
   jackson:
     serialization:
     serialization:
       write-dates-as-timestamps: true # 设置 Date 的格式,使用时间戳
       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) {
     public void delete${simpleClassName}(${primaryColumn.javaType} id) {
         // 校验存在
         // 校验存在
         this.validate${simpleClassName}Exists(id);
         this.validate${simpleClassName}Exists(id);
-        // 更新
+        // 删除
         ${classNameVar}Mapper.deleteById(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_channel";
 DELETE FROM "sys_sms_template";
 DELETE FROM "sys_sms_template";
 DELETE FROM "sys_sms_log";
 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")
    PRIMARY KEY ("id")
 ) COMMENT '短信渠道';
 ) COMMENT '短信渠道';
 
 
-CREATE TABLE "sys_sms_template" (
+CREATE TABLE IF NOT EXISTS "sys_sms_template" (
     "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
     "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
     "type" tinyint NOT NULL,
     "type" tinyint NOT NULL,
     "status" tinyint NOT NULL,
     "status" tinyint NOT NULL,
@@ -379,7 +379,7 @@ CREATE TABLE "sys_sms_template" (
     PRIMARY KEY ("id")
     PRIMARY KEY ("id")
 ) COMMENT '短信模板';
 ) COMMENT '短信模板';
 
 
-CREATE TABLE "sys_sms_log" (
+CREATE TABLE IF NOT EXISTS "sys_sms_log" (
    "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
    "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
    "channel_id" bigint NOT NULL,
    "channel_id" bigint NOT NULL,
    "channel_code" varchar(63) NOT NULL,
    "channel_code" varchar(63) NOT NULL,
@@ -411,3 +411,18 @@ CREATE TABLE "sys_sms_log" (
    "deleted" bit NOT NULL DEFAULT FALSE,
    "deleted" bit NOT NULL DEFAULT FALSE,
    PRIMARY KEY ("id")
    PRIMARY KEY ("id")
 ) COMMENT '短信日志';
 ) 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 '错误码表';