From 0f504bf6deee76934eb36282aef7be0ed0a2872d Mon Sep 17 00:00:00 2001 From: huwei <3313749341@qq.com> Date: Thu, 18 Jun 2026 10:23:51 +0800 Subject: [PATCH] init --- docs/EasyCodeConfig-GBS.json | 173 ++++++ docs/GBS前端开发规范.docx | Bin 0 -> 27101 bytes docs/JAVA规范.md | 365 +++++++++++++ docs/settingsfjk.xml | 221 ++++++++ docs/sql/database-design.md | 993 +++++++++++++++++++++++++++++++++++ docs/数据库规范.md | 197 +++++++ 6 files changed, 1949 insertions(+) create mode 100644 docs/EasyCodeConfig-GBS.json create mode 100644 docs/GBS前端开发规范.docx create mode 100644 docs/JAVA规范.md create mode 100644 docs/settingsfjk.xml create mode 100644 docs/sql/database-design.md create mode 100644 docs/数据库规范.md diff --git a/docs/EasyCodeConfig-GBS.json b/docs/EasyCodeConfig-GBS.json new file mode 100644 index 0000000..db356ee --- /dev/null +++ b/docs/EasyCodeConfig-GBS.json @@ -0,0 +1,173 @@ +{ + "author" : "fangjiakai", + "version" : "1.2.8", + "userSecure" : "", + "currTypeMapperGroupName" : "Default", + "currTemplateGroupName" : "zcloud-gbs", + "currColumnConfigGroupName" : "Default", + "currGlobalConfigGroupName" : "Default", + "typeMapper" : { + "Default" : { + "name" : "Default", + "elementList" : [ { + "matchType" : "REGEX", + "columnType" : "varchar(\\(\\d+\\))?", + "javaType" : "java.lang.String" + }, { + "matchType" : "REGEX", + "columnType" : "char(\\(\\d+\\))?", + "javaType" : "java.lang.String" + }, { + "matchType" : "REGEX", + "columnType" : "(tiny|medium|long)*text", + "javaType" : "java.lang.String" + }, { + "matchType" : "REGEX", + "columnType" : "decimal(\\(\\d+,\\d+\\))?", + "javaType" : "java.lang.Double" + }, { + "matchType" : "ORDINARY", + "columnType" : "integer", + "javaType" : "java.lang.Integer" + }, { + "matchType" : "REGEX", + "columnType" : "(tiny|small|medium)*int(\\(\\d+\\))?", + "javaType" : "java.lang.Integer" + }, { + "matchType" : "ORDINARY", + "columnType" : "int4", + "javaType" : "java.lang.Integer" + }, { + "matchType" : "ORDINARY", + "columnType" : "int8", + "javaType" : "java.lang.Long" + }, { + "matchType" : "REGEX", + "columnType" : "bigint(\\(\\d+\\))?", + "javaType" : "java.lang.Long" + }, { + "matchType" : "ORDINARY", + "columnType" : "date", + "javaType" : "java.util.Date" + }, { + "matchType" : "ORDINARY", + "columnType" : "datetime", + "javaType" : "java.util.Date" + }, { + "matchType" : "ORDINARY", + "columnType" : "timestamp", + "javaType" : "java.util.Date" + }, { + "matchType" : "ORDINARY", + "columnType" : "time", + "javaType" : "java.time.LocalTime" + }, { + "matchType" : "ORDINARY", + "columnType" : "boolean", + "javaType" : "java.lang.Boolean" + }, { + "matchType" : "ORDINARY", + "columnType" : "double(10,2)", + "javaType" : "java.lang.Double" + }, { + "matchType" : "ORDINARY", + "columnType" : "int(2) unsigned zerofill", + "javaType" : "java.lang.Integer" + }, { + "matchType" : "ORDINARY", + "columnType" : "double(11,2)", + "javaType" : "java.lang.Double" + }, { + "matchType" : "ORDINARY", + "columnType" : "double(10,1)", + "javaType" : "java.lang.Double" + }, { + "matchType" : "ORDINARY", + "columnType" : "double(20,2)", + "javaType" : "java.lang.Double" + }, { + "matchType" : "ORDINARY", + "columnType" : "decimal(10)", + "javaType" : "java.math.BigDecimal" + }, { + "matchType" : "ORDINARY", + "columnType" : "json", + "javaType" : "com.alibaba.fastjson.JSONObject" + } ] + } + }, + "template" : { + "zcloud-gbs" : { + "name" : "zcloud-gbs", + "elementList" : [ { + "name" : "addCmd.vm", + "code" : "##定义初始变量\n#set($tableName = $tool.append($tableInfo.name, \"AddCmd\"))\n##设置回调\n$!callback.setFileName($tool.append($tableName, \".java\"))\n$!callback.setSavePath($tool.append($tableInfo.savePath, \"/dto\"))\n\n#foreach($column in $tableInfo.fullColumn)\n #if(${column.comment} == \"业务主键id\")\n #set($uuid = $column.name)\n #end\n#end\n\n#if($tableInfo.savePackageName)package $!{tableInfo.savePackageName}.dto#{end};\n\nimport com.alibaba.cola.dto.Command;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport javax.validation.constraints.*;\n\n/**\n* web-client\n* @Author $!author\n* @Date $!time.currTime()\n*/\n@EqualsAndHashCode(callSuper = true)\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class $!{tableInfo.name}AddCmd extends Command {\n#foreach($column in $tableInfo.fullColumn)\n#if(${column.name} != \"id\" && ${column.name} != \"createTime\" && ${column.name} != \"updateTime\" && ${column.name} != \"createId\"\n&& ${column.name} != \"updateId\" && ${column.name} != \"env\" && ${column.name} != \"deleteEnum\"&& ${column.name} != \"remarks\" \n&& ${column.name} != \"createName\" && ${column.name} != \"updateName\" && ${column.name} != \"tenantId\" && ${column.name} != \"orgId\"\n&& ${column.name} != \"version\" && ${column.name} != ${uuid})\n @ApiModelProperty(value = \"${column.comment}\", name = \"$!{column.name}\", required = true)\n #if($!{tool.getClsNameByFullName($column.type)} == \"Integer\" || $!{tool.getClsNameByFullName($column.type)} == \"Long\"\n || $!{tool.getClsNameByFullName($column.type)} == \"Date\" || $!{tool.getClsNameByFullName($column.type)} == \"BigDecimal\"\n || $!{tool.getClsNameByFullName($column.type)} == \"Float\" || $!{tool.getClsNameByFullName($column.type)} == \"Double\")\n @NotNull(message = \"${column.comment}不能为空\")\n #else\n @NotEmpty(message = \"${column.comment}不能为空\")\n #end\n #if($!{tool.getClsNameByFullName($column.type)} == \"Date\")\n @JsonFormat(pattern = \"yyyy-MM-dd\")\n #end\n #if($!{tool.getClsNameByFullName($column.type)} == \"LocalDateTime\")\n @JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\")\n #end\n private $!{tool.getClsNameByFullName($column.type)} $!{column.name};\n \n#end\n#end\n}\n" + }, { + "name" : "addExe.vm", + "code" : "##定义初始变量\n#set($tableName = $tool.append($tableInfo.name, \"AddExe\"))\n##设置回调\n$!callback.setFileName($tool.append($tableName, \".java\"))\n$!callback.setSavePath($tool.append($tableInfo.savePath, \"/command\"))\n\n#if($tableInfo.savePackageName)package $!{tableInfo.savePackageName}.command#{end};\n\nimport $!{tableInfo.savePackageName}.domain.gateway.$!{tableInfo.name}Gateway;\nimport $!{tableInfo.savePackageName}.domain.model.$!{tableInfo.name}E;\nimport $!{tableInfo.savePackageName}.dto.$!{tableInfo.name}AddCmd;\nimport com.alibaba.cola.exception.BizException;\nimport lombok.AllArgsConstructor;\nimport org.springframework.beans.BeanUtils;\nimport org.springframework.stereotype.Component;\nimport org.springframework.transaction.annotation.Transactional;\n\n\n\n\n/**\n* web-app\n* @Author $!author\n* @Date $!time.currTime()\n*/\n@Component\n@AllArgsConstructor\npublic class $!{tableInfo.name}AddExe {\n private final $!{tableInfo.name}Gateway $!{tool.firstLowerCase($tableInfo.name)}Gateway;\n\n @Transactional(rollbackFor = Exception.class)\n public boolean execute($!{tableInfo.name}AddCmd cmd) {\n $!{tableInfo.name}E $!{tool.firstLowerCase($tableInfo.name)}E = new $!{tableInfo.name}E();\n BeanUtils.copyProperties(cmd, $!{tool.firstLowerCase($tableInfo.name)}E);\n boolean res = false;\n try {\n res = $!{tool.firstLowerCase($tableInfo.name)}Gateway.add($!{tool.firstLowerCase($tableInfo.name)}E);\n } catch (Exception e) {\n throw new RuntimeException(e);\n }\n if (!res) {\n throw new BizException(\"保存失败\");\n }\n return true;\n }\n}\n" + }, { + "name" : "co.vm", + "code" : "##定义初始变量\n#set($tableName = $tool.append($tableInfo.name, \"CO\"))\n##设置回调\n$!callback.setFileName($tool.append($tableName, \".java\"))\n$!callback.setSavePath($tool.append($tableInfo.savePath, \"/dto/clientobject\"))\n\n#set($firstColumn = $tableInfo.fullColumn.get(1))\n\n#if($tableInfo.savePackageName)package $!{tableInfo.savePackageName}.dto.clientobject#{end};\n\nimport com.alibaba.cola.dto.ClientObject;\nimport com.fasterxml.jackson.annotation.JsonFormat;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\nimport java.sql.Date;\n\n\n\n/**\n* web-client\n* @Author $!author\n* @Date $!time.currTime()\n*/\n@Data\npublic class $!{tableInfo.name}CO extends ClientObject {\n#foreach($column in $tableInfo.fullColumn)\n #if(${column.comment})\n //${column.comment}\n @ApiModelProperty(value = \"${column.comment}\")\n #end\n #if($!{tool.getClsNameByFullName($column.type)} == \"Date\")\n @JsonFormat(pattern = \"yyyy-MM-dd\")\n #end\n #if($!{tool.getClsNameByFullName($column.type)} == \"LocalDateTime\")\n @JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\")\n #end\n private $!{tool.getClsNameByFullName($column.type)} $!{column.name};\n#end\n}\n" + }, { + "name" : "controller.vm", + "code" : "##定义初始变量\n#set($tableName = $tool.append($tableInfo.name, \"Controller\"))\n##设置回调\n$!callback.setFileName($tool.append($tableName, \".java\"))\n$!callback.setSavePath($tool.append($tableInfo.savePath, \"/web\"))\n\n#if($tableInfo.savePackageName)package $!{tableInfo.savePackageName}.web#{end};\n\n\nimport $!{tableInfo.savePackageName}.api.$!{tableInfo.name}ServiceI;\nimport $!{tableInfo.savePackageName}.dto.$!{tableInfo.name}AddCmd;\nimport $!{tableInfo.savePackageName}.dto.$!{tableInfo.name}PageQry;\nimport $!{tableInfo.savePackageName}.dto.$!{tableInfo.name}UpdateCmd;\nimport $!{tableInfo.savePackageName}.dto.clientobject.$!{tableInfo.name}CO;\nimport com.alibaba.cola.dto.MultiResponse;\nimport com.alibaba.cola.dto.PageResponse;\nimport com.alibaba.cola.dto.Response;\nimport com.alibaba.cola.dto.SingleResponse;\nimport com.jjb.saas.framework.auth.model.SSOUser;\nimport com.jjb.saas.framework.auth.utils.AuthContext;\nimport io.swagger.annotations.Api;\nimport io.swagger.annotations.ApiOperation;\nimport lombok.AllArgsConstructor;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.ArrayList;\n/**\n* web-adapter\n* @Author $!author\n* @Date $!time.currTime()\n*/\n@Api(tags = \"$!{tableInfo.comment}\")\n@RequestMapping(\"/${application.gateway}/$!{tool.firstLowerCase($tableInfo.name)}\")\n@RestController\n@AllArgsConstructor\npublic class $!{tableInfo.name}Controller {\n private final $!{tableInfo.name}ServiceI $!{tool.firstLowerCase($tableInfo.name)}Service;\n\n @ApiOperation(\"新增\")\n @PostMapping(\"/save\")\n public SingleResponse<$!{tableInfo.name}CO> add(@Validated @RequestBody $!{tableInfo.name}AddCmd cmd) {\n SSOUser ssoUser = AuthContext.getCurrentUser();\n return $!{tool.firstLowerCase($tableInfo.name)}Service.add(cmd);\n }\n\n @ApiOperation(\"分页\")\n @PostMapping(\"/list\")\n public PageResponse<$!{tableInfo.name}CO> page(@RequestBody $!{tableInfo.name}PageQry qry) {\n return $!{tool.firstLowerCase($tableInfo.name)}Service.listPage(qry);\n }\n\n @ApiOperation(\"所有数据\")\n @GetMapping(\"/listAll\")\n public MultiResponse<$!{tableInfo.name}CO> listAll() {\n return MultiResponse.of(new ArrayList<$!{tableInfo.name}CO>());\n }\n\n @ApiOperation(\"详情\")\n @GetMapping(\"/{id}\")\n public SingleResponse<$!{tableInfo.name}CO> getInfoById(@PathVariable(\"id\") Long id) {\n return SingleResponse.of($!{tool.firstLowerCase($tableInfo.name)}Service.queryById(id));\n }\n\n @ApiOperation(\"删除\")\n @DeleteMapping(\"/{id}\")\n public Response remove(@PathVariable(\"id\") Long id) {\n $!{tool.firstLowerCase($tableInfo.name)}Service.remove(id);\n return SingleResponse.buildSuccess();\n }\n\n @ApiOperation(\"删除多个\")\n @DeleteMapping(\"/ids\")\n public Response removeBatch(@RequestParam Long[] ids) {\n $!{tool.firstLowerCase($tableInfo.name)}Service.removeBatch(ids);\n return SingleResponse.buildSuccess();\n }\n\n @ApiOperation(\"修改\")\n @PutMapping(\"/edit\")\n public SingleResponse edit(@Validated @RequestBody $!{tableInfo.name}UpdateCmd $!{tool.firstLowerCase($tableInfo.name)}UpdateCmd) {\n $!{tool.firstLowerCase($tableInfo.name)}Service.edit($!{tool.firstLowerCase($tableInfo.name)}UpdateCmd);\n return SingleResponse.buildSuccess();\n }\n}\n" + }, { + "name" : "convertor.vm", + "code" : "##定义初始变量\n#set($tableName = $tool.append($tableInfo.name, \"CoConvertor\"))\n##设置回调\n$!callback.setFileName($tool.append($tableName, \".java\"))\n$!callback.setSavePath($tool.append($tableInfo.savePath, \"/command/convertor\"))\n\n#if($tableInfo.savePackageName)package $!{tableInfo.savePackageName}.command.convertor#{end};\n\nimport $!{tableInfo.savePackageName}.dto.clientobject.$!{tableInfo.name}CO;\nimport $!{tableInfo.savePackageName}.persistence.dataobject.$!{tableInfo.name}DO;\nimport org.mapstruct.Mapper;\n\nimport java.util.List;\n\n\n\n/**\n* web-app\n* @Author $!author\n* @Date $!time.currTime()\n*/\n@Mapper(componentModel = \"spring\")\npublic interface $!{tableInfo.name}CoConvertor {\n /**\n * @param $!{tool.firstLowerCase($tableInfo.name)}DOs\n * @return\n */\n List<$!{tableInfo.name}CO> converDOsToCOs(List<$!{tableInfo.name}DO> $!{tool.firstLowerCase($tableInfo.name)}DOs);\n \n $!{tableInfo.name}CO converDOToCO($!{tableInfo.name}DO $!{tool.firstLowerCase($tableInfo.name)}DO);\n}\n" + }, { + "name" : "e.vm", + "code" : "##定义初始变量\n#set($tableName = $tool.append($tableInfo.name, \"E\"))\n##设置回调\n$!callback.setFileName($tool.append($tableName, \".java\"))\n$!callback.setSavePath($tool.append($tableInfo.savePath, \"/domain/model\"))\n\n#if($tableInfo.savePackageName)package $!{tableInfo.savePackageName}.domain.model#{end};\n\nimport com.alibaba.cola.domain.Entity;\nimport com.jjb.saas.framework.domain.model.BaseE;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\n\n/**\n* web-domain\n* @Author $!author\n* @Date $!time.currTime()\n*/\n@Data\npublic class $!{tableName} extends BaseE {\n#foreach($column in $tableInfo.fullColumn)\n#if(${column.name} != \"id\" && ${column.name} != \"createTime\" && ${column.name} != \"updateTime\" && ${column.name} != \"createId\"\n&& ${column.name} != \"updateId\" && ${column.name} != \"env\" && ${column.name} != \"deleteEnum\"&& ${column.name} != \"remarks\" \n&& ${column.name} != \"createName\" && ${column.name} != \"updateName\" && ${column.name} != \"tenantId\" && ${column.name} != \"orgId\"\n&& ${column.name} != \"version\" && ${column.name} != ${uuid})\n #if(${column.comment})\n //${column.comment}\n #end\n private $!{tool.getClsNameByFullName($column.type)} $!{column.name};\n#end\n#end\n}\n" + }, { + "name" : "entity.java.vm", + "code" : "##定义初始变量\n#set($tableName = $tool.append($tableInfo.name, \"DO\"))\n##设置回调\n$!callback.setFileName($tool.append($tableName, \".java\"))\n$!callback.setSavePath($tool.append($tableInfo.savePath, \"/persistence/dataobject\"))\n\n##拿到主键\n#if(!$tableInfo.pkColumn.isEmpty())\n #set($pk = $tableInfo.pkColumn.get(0))\n#end\n\n#foreach($column in $tableInfo.fullColumn)\n #if(${column.comment} == \"业务主键id\")\n #set($uuid = $column.name)\n #end\n#end\n\n##自动导入包(全局变量)\n#if($tableInfo.savePackageName)package $!{tableInfo.savePackageName}.persistence.dataobject#{end};\n\nimport com.baomidou.mybatisplus.annotation.TableName;\nimport com.jjb.saas.framework.repository.basedo.BaseDO;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\n\n/**\n* web-infrastructure\n* @Author $!author\n* @Date $!time.currTime()\n*/\n@Data\n@TableName(\"$!{tableInfo.obj.name}\")\n@NoArgsConstructor\n@EqualsAndHashCode(callSuper = true)\npublic class $!{tableName} extends BaseDO {\n#foreach($column in $tableInfo.fullColumn)\n#if(${column.name} != \"id\" && ${column.name} != \"createTime\" && ${column.name} != \"updateTime\" && ${column.name} != \"createId\"\n&& ${column.name} != \"updateId\" && ${column.name} != \"env\" && ${column.name} != \"deleteEnum\"&& ${column.name} != \"remarks\" \n&& ${column.name} != \"createName\" && ${column.name} != \"updateName\" && ${column.name} != \"tenantId\" && ${column.name} != \"orgId\"\n&& ${column.name} != \"version\")\n #if(${column.comment})\n //${column.comment}\n @ApiModelProperty(value = \"${column.comment}\")\n #end\n #if(${column.name}==$!pk.name)\n @TableId(type = IdType.ASSIGN_ID)\n #end\n #if($!{tool.getClsNameByFullName($column.type)} == \"JSONObject\")\n private String $!{column.name};\n \n #else\n private $!{tool.getClsNameByFullName($column.type)} $!{column.name};\n #end\n#end\n#end\n\n public $!{tableName}(String $!{uuid}) {\n this.$!{uuid} = $!{uuid};\n }\n\n}\n" + }, { + "name" : "gateway.vm", + "code" : "##定义初始变量\n#set($tableName = $tool.append($tableInfo.name, \"Gateway\"))\n##设置回调\n$!callback.setFileName($tool.append($tableName, \".java\"))\n$!callback.setSavePath($tool.append($tableInfo.savePath, \"/domain/gateway\"))\n\n#if($tableInfo.savePackageName)package $!{tableInfo.savePackageName}.domain.gateway#{end};\n\nimport $!{tableInfo.savePackageName}.domain.model.$!{tableInfo.name}E;\n\n/**\n* web-domain\n* @Author $!author\n* @Date $!time.currTime()\n*/\npublic interface $!{tableInfo.name}Gateway {\n\n /**\n * 新增\n */\n Boolean add($!{tableInfo.name}E $!{tool.firstLowerCase($tableInfo.name)}E) ;\n\n /**\n * 修改\n */\n Boolean update($!{tableInfo.name}E $!{tool.firstLowerCase($tableInfo.name)}E);\n\n /**\n * 删除\n */\n Boolean deleted$!{tableInfo.name}ById(Long id);\n Boolean deleted$!{tableInfo.name}ByIds(Long[] id);\n}\n" + }, { + "name" : "gatewayimpl.vm", + "code" : "##定义初始变量\n#set($tableName = $tool.append($tableInfo.name, \"GatewayImpl\"))\n##设置回调\n$!callback.setFileName($tool.append($tableName, \".java\"))\n$!callback.setSavePath($tool.append($tableInfo.savePath, \"/gatewayimpl\"))\n\n#foreach($column in $tableInfo.fullColumn)\n #if(${column.comment} == \"业务主键id\")\n #set($uuid = $column.name)\n #end\n#end\n\n#if($tableInfo.savePackageName)package $!{tableInfo.savePackageName}.gatewayimpl#{end};\n\nimport $!{tableInfo.savePackageName}.domain.gateway.$!{tableInfo.name}Gateway;\nimport $!{tableInfo.savePackageName}.domain.model.$!{tableInfo.name}E;\nimport $!{tableInfo.savePackageName}.persistence.dataobject.$!{tableInfo.name}DO;\nimport $!{tableInfo.savePackageName}.persistence.repository.$!{tableInfo.name}Repository;\nimport lombok.AllArgsConstructor;\nimport com.zcloud.gbscommon.utils.Tools;\nimport org.springframework.beans.BeanUtils;\nimport org.springframework.stereotype.Service;\n\nimport java.util.Arrays;\n\n/**\n* web-infrastructure\n* @Author $!author\n* @Date $!time.currTime()\n*/\n@Service\n@AllArgsConstructor\npublic class $!{tableInfo.name}GatewayImpl implements $!{tableInfo.name}Gateway {\n private final $!{tableInfo.name}Repository $!{tool.firstLowerCase($tableInfo.name)}Repository;\n\n @Override\n public Boolean add($!{tableInfo.name}E $!{tool.firstLowerCase($tableInfo.name)}E) {\n $!{tableInfo.name}DO d = new $!{tableInfo.name}DO(Tools.get32UUID());\n BeanUtils.copyProperties($!{tool.firstLowerCase($tableInfo.name)}E, d,\"${uuid}\");\n $!{tool.firstLowerCase($tableInfo.name)}Repository.save(d);\n return true;\n }\n\n @Override\n public Boolean update($!{tableInfo.name}E $!{tool.firstLowerCase($tableInfo.name)}E) {\n $!{tableInfo.name}DO d = new $!{tableInfo.name}DO();\n BeanUtils.copyProperties($!{tool.firstLowerCase($tableInfo.name)}E, d);\n $!{tool.firstLowerCase($tableInfo.name)}Repository.updateById(d);\n return true;\n }\n\n @Override\n public Boolean deleted$!{tableInfo.name}ById(Long id) {\n return $!{tool.firstLowerCase($tableInfo.name)}Repository.removeById(id);\n }\n\n @Override\n public Boolean deleted$!{tableInfo.name}ByIds(Long[] ids) {\n return $!{tool.firstLowerCase($tableInfo.name)}Repository.removeByIds(Arrays.asList(ids));\n }\n}\n" + }, { + "name" : "mapper.vm", + "code" : "##定义初始变量\n#set($tableName = $tool.append($tableInfo.name, \"Mapper\"))\n##设置回调\n$!callback.setFileName($tool.append($tableName, \".java\"))\n$!callback.setSavePath($tool.append($tableInfo.savePath, \"/persistence/mapper\"))\n\n#if($tableInfo.savePackageName)package $!{tableInfo.savePackageName}.persistence.mapper#{end};\n\nimport $!{tableInfo.savePackageName}.persistence.dataobject.$!{tableInfo.name}DO;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport org.apache.ibatis.annotations.Mapper;\n\n/**\n* web-infrastructure\n* @Author $!author\n* @Date $!time.currTime()\n*/\n@Mapper\npublic interface $!{tableName} extends BaseMapper<$!{tableInfo.name}DO> {\n\n }\n" + }, { + "name" : "mapper.xml.vm", + "code" : "##定义初始变量\n#set($tableName = $tool.append($tableInfo.name, \"Mapper\"))\n##设置回调\n$!callback.setFileName($tool.append($tableName, \".xml\"))\n$!callback.setSavePath($tool.append($tableInfo.savePath, \"/persistence/dataobject\"))\n\n\n\n\n\n\n\n" + }, { + "name" : "pageQry.vm", + "code" : "##定义初始变量\n#set($tableName = $tool.append($tableInfo.name, \"PageQry\"))\n##设置回调\n$!callback.setFileName($tool.append($tableName, \".java\"))\n$!callback.setSavePath($tool.append($tableInfo.savePath, \"/dto\"))\n\n#set($firstColumn = $tableInfo.fullColumn.get(1))\n\n#if($tableInfo.savePackageName)package $!{tableInfo.savePackageName}.dto#{end};\n\nimport com.alibaba.cola.dto.PageQuery;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.Data;\n\n\n\n/**\n* web-client\n* @Author $!author\n* @Date $!time.currTime()\n*/\n@Data\npublic class $!{tableInfo.name}PageQry extends PageQuery {\n\n/**\n* 查询条件操作前缀,支持以下几种数据库查询操作:\n* - `like`: 模糊匹配查询,对应SQL的LIKE操作符\n* - `eq`: 等值查询,对应SQL的=操作符\n* - `gt`: 大于比较查询\n* - `lt`: 小于比较查询\n* - `ge`: 大于等于比较查询\n* - `le`: 小于等于比较查询\n* - `ne`: 不等比较查询,对应SQL的!=操作符\n*/\n private $!{tool.getClsNameByFullName($firstColumn.type)} like$!{tool.firstUpperCase($firstColumn.name)};\n}\n" + }, { + "name" : "queryExe.vm", + "code" : "##定义初始变量\n#set($tableName = $tool.append($tableInfo.name, \"QueryExe\"))\n##设置回调\n$!callback.setFileName($tool.append($tableName, \".java\"))\n$!callback.setSavePath($tool.append($tableInfo.savePath, \"/command/query\"))\n\n#if($tableInfo.savePackageName)package $!{tableInfo.savePackageName}.command.query#{end};\n\nimport $!{tableInfo.savePackageName}.command.convertor.$!{tableInfo.name}CoConvertor;\nimport $!{tableInfo.savePackageName}.dto.$!{tableInfo.name}PageQry;\nimport $!{tableInfo.savePackageName}.dto.clientobject.$!{tableInfo.name}CO;\nimport $!{tableInfo.savePackageName}.persistence.dataobject.$!{tableInfo.name}DO;\nimport $!{tableInfo.savePackageName}.persistence.repository.$!{tableInfo.name}Repository;\nimport com.zcloud.gbscommon.utils.PageQueryHelper;\nimport com.alibaba.cola.dto.PageResponse;\nimport lombok.AllArgsConstructor;\nimport org.springframework.stereotype.Component;\n\nimport java.util.List;\nimport java.util.Map;\n\n\n\n/**\n* web-app\n* @Author $!author\n* @Date $!time.currTime()\n*/\n@Component\n@AllArgsConstructor\npublic class $!{tableInfo.name}QueryExe {\n private final $!{tableInfo.name}Repository $!{tool.firstLowerCase($tableInfo.name)}Repository;\n private final $!{tableInfo.name}CoConvertor $!{tool.firstLowerCase($tableInfo.name)}CoConvertor;\n\n /**\n * 根据id查询\n * @param id\n * @return\n */\n public $!{tableInfo.name}CO queryById(Long id) {\n return $!{tool.firstLowerCase($tableInfo.name)}CoConvertor.converDOToCO($!{tool.firstLowerCase($tableInfo.name)}Repository.getById(id));\n }\n /**\n * 分页\n *\n * @param $!{tool.firstLowerCase($tableInfo.name)}PageQry\n * @return\n */\n public PageResponse<$!{tableInfo.name}CO> execute($!{tableInfo.name}PageQry $!{tool.firstLowerCase($tableInfo.name)}PageQry) {\n Map params = PageQueryHelper.toHashMap($!{tool.firstLowerCase($tableInfo.name)}PageQry);\n PageResponse<$!{tableInfo.name}DO> pageResponse = $!{tool.firstLowerCase($tableInfo.name)}Repository.listPage(params);\n List<$!{tableInfo.name}CO> examCenterCOS = $!{tool.firstLowerCase($tableInfo.name)}CoConvertor.converDOsToCOs(pageResponse.getData());\n return PageResponse.of(examCenterCOS, pageResponse.getTotalCount(), pageResponse.getPageSize(), pageResponse.getPageIndex());\n }\n}\n" + }, { + "name" : "removeExe.vm", + "code" : "##定义初始变量\n#set($tableName = $tool.append($tableInfo.name, \"RemoveExe\"))\n##设置回调\n$!callback.setFileName($tool.append($tableName, \".java\"))\n$!callback.setSavePath($tool.append($tableInfo.savePath, \"/command\"))\n\n#if($tableInfo.savePackageName)package $!{tableInfo.savePackageName}.command#{end};\n\nimport $!{tableInfo.savePackageName}.domain.gateway.$!{tableInfo.name}Gateway;\nimport com.alibaba.cola.exception.BizException;\nimport lombok.AllArgsConstructor;\nimport org.springframework.stereotype.Component;\nimport org.springframework.transaction.annotation.Transactional;\n\n\n\n\n/**\n* web-app\n* @Author $!author\n* @Date $!time.currTime()\n*/\n@Component\n@AllArgsConstructor\npublic class $!{tableInfo.name}RemoveExe {\n private final $!{tableInfo.name}Gateway $!{tool.firstLowerCase($tableInfo.name)}Gateway;\n\n @Transactional(rollbackFor = Exception.class)\n public boolean execute(Long id) {\n boolean res = $!{tool.firstLowerCase($tableInfo.name)}Gateway.deleted$!{tableInfo.name}ById(id);\n if(!res){\n throw new BizException(\"删除失败\");\n }\n return true;\n }\n\n @Transactional(rollbackFor = Exception.class)\n public boolean execute(Long[] ids) {\n boolean res = $!{tool.firstLowerCase($tableInfo.name)}Gateway.deleted$!{tableInfo.name}ByIds(ids);\n if(!res){\n throw new BizException(\"删除失败\");\n }\n return true;\n }\n}\n" + }, { + "name" : "repository.vm", + "code" : "##定义初始变量\n#set($tableName = $tool.append($tableInfo.name, \"Repository\"))\n##设置回调\n$!callback.setFileName($tool.append($tableName, \".java\"))\n$!callback.setSavePath($tool.append($tableInfo.savePath, \"/persistence/repository\"))\n\n#if($tableInfo.savePackageName)package $!{tableInfo.savePackageName}.persistence.repository#{end};\n\nimport $!{tableInfo.savePackageName}.persistence.dataobject.$!{tableInfo.name}DO;\nimport com.alibaba.cola.dto.PageResponse;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.jjb.saas.framework.repository.repo.BaseRepository;\nimport java.util.Map;\n\n/**\n* web-infrastructure\n* @Author $!author\n* @Date $!time.currTime()\n*/\npublic interface $!{tableInfo.name}Repository extends BaseRepository<$!{tableInfo.name}DO> {\n PageResponse<$!{tableInfo.name}DO> listPage(Map params);\n}\n" + }, { + "name" : "repositoryImpl.vm", + "code" : "##定义初始变量\n#set($tableName = $tool.append($tableInfo.name, \"RepositoryImpl\"))\n##设置回调\n$!callback.setFileName($tool.append($tableName, \".java\"))\n$!callback.setSavePath($tool.append($tableInfo.savePath, \"/persistence/repository/impl\"))\n\n#if($tableInfo.savePackageName)package $!{tableInfo.savePackageName}.persistence.repository.impl#{end};\n\nimport $!{tableInfo.savePackageName}.persistence.dataobject.$!{tableInfo.name}DO;\nimport $!{tableInfo.savePackageName}.persistence.mapper.$!{tableInfo.name}Mapper;\nimport $!{tableInfo.savePackageName}.persistence.repository.$!{tableInfo.name}Repository;\nimport com.alibaba.cola.dto.PageResponse;\nimport com.jjb.saas.framework.repository.common.PageHelper;\nimport com.zcloud.gbscommon.utils.PageQueryHelper;\nimport com.zcloud.gbscommon.utils.Query;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.jjb.saas.framework.repository.repo.impl.BaseRepositoryImpl;\nimport lombok.RequiredArgsConstructor;\nimport org.springframework.stereotype.Service;\nimport java.util.Map;\n\n/**\n* web-infrastructure\n* @Author $!author\n* @Date $!time.currTime()\n*/\n@Service\n@RequiredArgsConstructor\npublic class $!{tableInfo.name}RepositoryImpl extends BaseRepositoryImpl<$!{tableInfo.name}Mapper, $!{tableInfo.name}DO> implements $!{tableInfo.name}Repository {\n private final $!{tableInfo.name}Mapper $!{tool.firstLowerCase($tableInfo.name)}Mapper;\n\n @Override\n public PageResponse<$!{tableInfo.name}DO> listPage(Map params) {\n IPage<$!{tableInfo.name}DO> iPage = new Query<$!{tableInfo.name}DO>().getPage(params);\n QueryWrapper<$!{tableInfo.name}DO> queryWrapper = new QueryWrapper<>();\n queryWrapper = PageQueryHelper.createPageQueryWrapper(queryWrapper, params);\n queryWrapper.orderByDesc(\"create_time\");\n IPage<$!{tableInfo.name}DO> result = $!{tool.firstLowerCase($tableInfo.name)}Mapper.selectPage(iPage, queryWrapper);\n return PageHelper.pageToResponse(result, result.getRecords());\n }\n}\n" + }, { + "name" : "serviceI.vm", + "code" : "##定义初始变量\n#set($tableName = $tool.append($tableInfo.name, \"ServiceI\"))\n##设置回调\n$!callback.setFileName($tool.append($tableName, \".java\"))\n$!callback.setSavePath($tool.append($tableInfo.savePath, \"/api\"))\n\n#if($tableInfo.savePackageName)package $!{tableInfo.savePackageName}.api#{end};\n\nimport $!{tableInfo.savePackageName}.dto.$!{tableInfo.name}AddCmd;\nimport $!{tableInfo.savePackageName}.dto.$!{tableInfo.name}PageQry;\nimport $!{tableInfo.savePackageName}.dto.$!{tableInfo.name}UpdateCmd;\nimport $!{tableInfo.savePackageName}.dto.clientobject.$!{tableInfo.name}CO;\n\nimport com.alibaba.cola.dto.PageResponse;\nimport com.alibaba.cola.dto.SingleResponse;\n\n/**\n* web-client\n* @Author $!author\n* @Date $!time.currTime()\n*/\npublic interface $!{tableInfo.name}ServiceI {\n $!{tableInfo.name}CO queryById(Long id);\n \n PageResponse<$!{tableInfo.name}CO> listPage($!{tableInfo.name}PageQry qry);\n\n SingleResponse<$!{tableInfo.name}CO> add($!{tableInfo.name}AddCmd cmd);\n\n void edit($!{tableInfo.name}UpdateCmd cmd);\n\n void remove(Long id);\n\n void removeBatch(Long[] ids);\n}\n" + }, { + "name" : "serviceImpl.vm", + "code" : "##定义初始变量\n#set($tableName = $tool.append($tableInfo.name, \"ServiceImpl\"))\n##设置回调\n$!callback.setFileName($tool.append($tableName, \".java\"))\n$!callback.setSavePath($tool.append($tableInfo.savePath, \"/service\"))\n\n#if($tableInfo.savePackageName)package $!{tableInfo.savePackageName}.service#{end};\n\nimport $!{tableInfo.savePackageName}.api.$!{tableInfo.name}ServiceI;\nimport $!{tableInfo.savePackageName}.command.$!{tableInfo.name}AddExe;\nimport $!{tableInfo.savePackageName}.command.$!{tableInfo.name}RemoveExe;\nimport $!{tableInfo.savePackageName}.command.$!{tableInfo.name}UpdateExe;\nimport $!{tableInfo.savePackageName}.command.query.$!{tableInfo.name}QueryExe;\nimport $!{tableInfo.savePackageName}.dto.$!{tableInfo.name}AddCmd;\nimport $!{tableInfo.savePackageName}.dto.$!{tableInfo.name}PageQry;\nimport $!{tableInfo.savePackageName}.dto.$!{tableInfo.name}UpdateCmd;\nimport $!{tableInfo.savePackageName}.dto.clientobject.$!{tableInfo.name}CO;\n\nimport com.alibaba.cola.dto.PageResponse;\nimport com.alibaba.cola.dto.SingleResponse;\nimport lombok.AllArgsConstructor;\nimport org.springframework.stereotype.Service;\n\n/**\n* web-app\n* @Author $!author\n* @Date $!time.currTime()\n*/\n@Service\n@AllArgsConstructor\npublic class $!{tableInfo.name}ServiceImpl implements $!{tableInfo.name}ServiceI {\n private final $!{tableInfo.name}AddExe $!{tool.firstLowerCase($tableInfo.name)}AddExe;\n private final $!{tableInfo.name}UpdateExe $!{tool.firstLowerCase($tableInfo.name)}UpdateExe;\n private final $!{tableInfo.name}RemoveExe $!{tool.firstLowerCase($tableInfo.name)}RemoveExe;\n private final $!{tableInfo.name}QueryExe $!{tool.firstLowerCase($tableInfo.name)}QueryExe;\n\n @Override\n public $!{tableInfo.name}CO queryById(Long id){\n return $!{tool.firstLowerCase($tableInfo.name)}QueryExe.queryById(id);\n }\n \n @Override\n public PageResponse<$!{tableInfo.name}CO> listPage($!{tableInfo.name}PageQry qry){\n\n return $!{tool.firstLowerCase($tableInfo.name)}QueryExe.execute(qry);\n }\n\n @Override\n public SingleResponse add($!{tableInfo.name}AddCmd cmd) {\n\n $!{tool.firstLowerCase($tableInfo.name)}AddExe.execute(cmd);\n return SingleResponse.buildSuccess();\n }\n\n @Override\n public void edit($!{tableInfo.name}UpdateCmd $!{tool.firstLowerCase($tableInfo.name)}UpdateCmd) {\n $!{tool.firstLowerCase($tableInfo.name)}UpdateExe.execute($!{tool.firstLowerCase($tableInfo.name)}UpdateCmd);\n }\n\n @Override\n public void remove(Long id) {\n $!{tool.firstLowerCase($tableInfo.name)}RemoveExe.execute(id);\n }\n\n @Override\n public void removeBatch(Long[] ids) {\n $!{tool.firstLowerCase($tableInfo.name)}RemoveExe.execute(ids);\n }\n}\n" + }, { + "name" : "updateCmd.vm", + "code" : "##定义初始变量\n#set($tableName = $tool.append($tableInfo.name, \"UpdateCmd\"))\n##设置回调\n$!callback.setFileName($tool.append($tableName, \".java\"))\n$!callback.setSavePath($tool.append($tableInfo.savePath, \"/dto\"))\n\n#if($tableInfo.savePackageName)package $!{tableInfo.savePackageName}.dto#{end};\n\nimport com.alibaba.cola.dto.Command;\nimport io.swagger.annotations.ApiModelProperty;\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.EqualsAndHashCode;\nimport lombok.NoArgsConstructor;\nimport javax.validation.constraints.*;\n\n/**\n* web-client\n* @Author $!author\n* @Date $!time.currTime()\n*/\n@EqualsAndHashCode(callSuper = true)\n@Data\n@NoArgsConstructor\n@AllArgsConstructor\npublic class $!{tableInfo.name}UpdateCmd extends Command {\n#foreach($column in $tableInfo.fullColumn)\n#if(${column.name} != \"createTime\" && ${column.name} != \"updateTime\" && ${column.name} != \"createId\"\n&& ${column.name} != \"updateId\" && ${column.name} != \"env\" && ${column.name} != \"deleteEnum\"&& ${column.name} != \"remarks\" \n&& ${column.name} != \"createName\" && ${column.name} != \"updateName\" && ${column.name} != \"tenantId\" && ${column.name} != \"orgId\"\n&& ${column.name} != \"version\")\n @ApiModelProperty(value = \"${column.comment}\", name = \"$!{column.name}\", required = true)\n #if($!{tool.getClsNameByFullName($column.type)} == \"Integer\" || $!{tool.getClsNameByFullName($column.type)} == \"Long\"\n || $!{tool.getClsNameByFullName($column.type)} == \"Date\" || $!{tool.getClsNameByFullName($column.type)} == \"BigDecimal\"\n || $!{tool.getClsNameByFullName($column.type)} == \"Float\" || $!{tool.getClsNameByFullName($column.type)} == \"Double\")\n @NotNull(message = \"${column.comment}不能为空\")\n #else\n @NotEmpty(message = \"${column.comment}不能为空\")\n #end\n #if($!{tool.getClsNameByFullName($column.type)} == \"Date\")\n @JsonFormat(pattern = \"yyyy-MM-dd\")\n #end\n #if($!{tool.getClsNameByFullName($column.type)} == \"LocalDateTime\")\n @JsonFormat(pattern = \"yyyy-MM-dd HH:mm:ss\")\n #end\n private $!{tool.getClsNameByFullName($column.type)} $!{column.name};\n#end\n#end\n}\n" + }, { + "name" : "updateExe.vm", + "code" : "##定义初始变量\n#set($tableName = $tool.append($tableInfo.name, \"UpdateExe\"))\n##设置回调\n$!callback.setFileName($tool.append($tableName, \".java\"))\n$!callback.setSavePath($tool.append($tableInfo.savePath, \"/command\"))\n\n#if($tableInfo.savePackageName)package $!{tableInfo.savePackageName}.command#{end};\n\nimport com.alibaba.cola.exception.BizException;\nimport $!{tableInfo.savePackageName}.domain.gateway.$!{tableInfo.name}Gateway;\nimport $!{tableInfo.savePackageName}.domain.model.$!{tableInfo.name}E;\nimport $!{tableInfo.savePackageName}.dto.$!{tableInfo.name}UpdateCmd;\nimport lombok.AllArgsConstructor;\nimport org.springframework.beans.BeanUtils;\nimport org.springframework.stereotype.Component;\nimport org.springframework.transaction.annotation.Transactional;\n\n\n\n\n/**\n* web-app\n* @Author $!author\n* @Date $!time.currTime()\n*/\n@Component\n@AllArgsConstructor\npublic class $!{tableInfo.name}UpdateExe {\n private final $!{tableInfo.name}Gateway $!{tool.firstLowerCase($tableInfo.name)}Gateway;\n\n @Transactional(rollbackFor = Exception.class)\n public void execute($!{tableInfo.name}UpdateCmd $!{tool.firstLowerCase($tableInfo.name)}UpdateCmd) {\n $!{tableInfo.name}E $!{tool.firstLowerCase($tableInfo.name)}E = new $!{tableInfo.name}E();\n BeanUtils.copyProperties($!{tool.firstLowerCase($tableInfo.name)}UpdateCmd, $!{tool.firstLowerCase($tableInfo.name)}E);\n boolean res = $!{tool.firstLowerCase($tableInfo.name)}Gateway.update($!{tool.firstLowerCase($tableInfo.name)}E);\n if (!res) {\n throw new BizException(\"修改失败\");\n }\n }\n}\n" + }, { + "name" : "index.vue.vm", + "code" : "$!callback.setFileName(\"index.js\")\n$!callback.setSavePath($tool.append($tableInfo.savePath, \"/Container/\",$tableInfo.name))\n\n##拿到主键\n#if(!$tableInfo.pkColumn.isEmpty())\n #set($pk = $tableInfo.pkColumn.get(0))\n#end\n\n#foreach($column in $tableInfo.fullColumn)\n #if(${column.comment} == \"业务主键id\")\n #set($uuid = $column.name)\n #end\n#end\n\n\nimport { Connect } from \"@cqsjjb/jjb-dva-runtime\";\nimport { Button, Form, message, Modal, Space ,Descriptions} from \"antd\";\nimport { useEffect, useState } from \"react\";\nimport FormBuilder from \"zy-react-library/components/FormBuilder\";\nimport Search from \"zy-react-library/components/Search\";\nimport Table from \"zy-react-library/components/Table\";\nimport { FORM_ITEM_RENDER_ENUM } from \"zy-react-library/enum/formItemRender\";\nimport useTable from \"zy-react-library/hooks/useTable\";\nimport { NS_$!{tableInfo.name} } from \"~/enumerate/namespace\";\nimport AddIcon from \"zy-react-library/components/Icon/AddIcon\";\nimport DeleteIcon from \"zy-react-library/components/Icon/DeleteIcon\";\n\nfunction $!{tableInfo.name}(props) {\n const [addModalVisible, setAddModalVisible] = useState(false);\n const [infoModalVisible, setInfoModalVisible] = useState(false);\n const [currentId, setCurrentId] = useState(\"\");\n const [selectedRowKeys, setSelectedRowKeys] = useState([]);\n const [form] = Form.useForm();\n const { tableProps, getData } = useTable(props[\"$!{tool.firstLowerCase($tableInfo.name)}List\"], {\n form,\n });\n return (\n
\n \n setSelectedRowKeys(selectedRowKeys),\n }}\n toolBarRender={() => (\n \n \n }\n type=\"primary\"\n danger\n onClick={() => {\n if (!selectedRowKeys.length)\n return message.warning(\"请选择要删除的行\");\n Modal.confirm({\n title: \"确定删除吗?\",\n onOk: async () => {\n await props[\"$!{tool.firstLowerCase($tableInfo.name)}BatchDelete\"]({ ids: selectedRowKeys });\n message.success(\"删除成功\");\n getData();\n },\n });\n }}\n >\n 批量删除\n \n \n )}\n columns={[\n #foreach($column in $tableInfo.fullColumn)\n #if(${column.name} != \"id\" && ${column.name} != \"createTime\" && ${column.name} != \"updateTime\" && ${column.name} != \"createId\"\n&& ${column.name} != \"updateId\" && ${column.name} != \"env\" && ${column.name} != \"deleteEnum\"&& ${column.name} != \"remarks\" \n&& ${column.name} != \"createName\" && ${column.name} != \"updateName\" && ${column.name} != \"tenantId\" && ${column.name} != \"orgId\"\n&& ${column.name} != \"version\" && ${column.name} != ${uuid})\n { dataIndex: \"${column.name}\", title: \"${column.comment}\" },\n #end\n #end\n {\n title: \"操作\",\n align: \"center\",\n width: 200,\n render: (_, record) => (\n \n {\n setCurrentId(record.id);\n setInfoModalVisible(true);\n }}\n >\n 查看\n \n {\n setCurrentId(record.id);\n setAddModalVisible(true);\n }}\n >\n 编辑\n \n {\n Modal.confirm({\n title: \"确定删除吗?\",\n onOk: async () => {\n await props[\"$!{tool.firstLowerCase($tableInfo.name)}Delete\"]({ id: record.id });\n message.success(\"删除成功\");\n getData();\n },\n });\n }}\n >\n 删除\n \n \n ),\n },\n ]}\n {...tableProps}\n />\n {\n setAddModalVisible(false);\n setCurrentId(\"\");\n }}\n getData={getData}\n />\n\n {\n setInfoModalVisible(false);\n setCurrentId(\"\");\n }}\n getData={getData}\n />\n
\n );\n}\n\nfunction AddModalComponent(props) {\nconst [form] = Form.useForm();\nuseEffect(() => {\nif (props.currentId) {\nprops[\"$!{tool.firstLowerCase($tableInfo.name)}Info\"]({ id: props.currentId }).then((res) => {\nform.setFieldsValue(res.data);\n});\n}\n}, [props.currentId]);\nconst onCancel = () => {\nform.resetFields();\nprops.onCancel();\n};\nconst submit = async (values) => {\nawait props[!props.currentId ? \"$!{tool.firstLowerCase($tableInfo.name)}Add\" : \"$!{tool.firstLowerCase($tableInfo.name)}Edit\"]({ ...values, id: props.currentId });\nonCancel();\nprops.getData();\n};\nreturn (\n\n \n\n);\n}\n\nfunction InfoModalComponent(props){\nconst [info,setInfo] = useState({});\nuseEffect(() => {\nif (props.currentId) {\nprops[\"$!{tool.firstLowerCase($tableInfo.name)}Info\"]({ id: props.currentId }).then((res) => {\nsetInfo(res.data);\n});\n}\n}, [props.currentId]);\n\nreturn (\n关闭}\n title=\"查看\"\n loading={props.$!{tool.firstLowerCase($tableInfo.name)}.loading}\n >\n \n\n);\n}\nconst AddModal = Connect([NS_$!{tableInfo.name}], true)(AddModalComponent);\nconst InfoModal = Connect([NS_$!{tableInfo.name}], true)(InfoModalComponent);\nexport default Connect([NS_$!{tableInfo.name}], true)($!{tableInfo.name});\n" + }, { + "name" : "index.js.vm", + "code" : "$!callback.setFileName(\"index.js\")\n$!callback.setSavePath($tool.append($tableInfo.savePath, \"/api/\",$tableInfo.name))\n\nimport { declareRequest } from '@cqsjjb/jjb-dva-runtime';\n\nexport const $!{tool.firstLowerCase($tableInfo.name)}List = declareRequest(\n '$!{tool.firstLowerCase($tableInfo.name)}Loading',\n 'Post > @/risk/$!{tool.firstLowerCase($tableInfo.name)}/page',\n);\nexport const $!{tool.firstLowerCase($tableInfo.name)}Add = declareRequest(\n '$!{tool.firstLowerCase($tableInfo.name)}Loading',\n 'Post > @/risk/$!{tool.firstLowerCase($tableInfo.name)}/save'\n);\nexport const $!{tool.firstLowerCase($tableInfo.name)}Edit = declareRequest(\n '$!{tool.firstLowerCase($tableInfo.name)}Loading',\n 'Put > @/risk/$!{tool.firstLowerCase($tableInfo.name)}/edit'\n);\nexport const $!{tool.firstLowerCase($tableInfo.name)}Delete = declareRequest(\n '$!{tool.firstLowerCase($tableInfo.name)}Loading',\n 'Delete > @/risk/$!{tool.firstLowerCase($tableInfo.name)}/{id}'\n);\nexport const $!{tool.firstLowerCase($tableInfo.name)}BatchDelete = declareRequest(\n '$!{tool.firstLowerCase($tableInfo.name)}Loading',\n 'Delete > @/risk/$!{tool.firstLowerCase($tableInfo.name)}/ids/{ids}'\n);\nexport const $!{tool.firstLowerCase($tableInfo.name)}Info = declareRequest('$!{tool.firstLowerCase($tableInfo.name)}Loading', 'Get > /risk/$!{tool.firstLowerCase($tableInfo.name)}/{id}');\n" + } ] + } + }, + "columnConfig" : { }, + "globalConfig" : { } +} \ No newline at end of file diff --git a/docs/GBS前端开发规范.docx b/docs/GBS前端开发规范.docx new file mode 100644 index 0000000000000000000000000000000000000000..6418ca7ffa4fb70d7b67244269237e9f6445d929 GIT binary patch literal 27101 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 iterator = list.iterator(); + while (iterator.hasNext()) { + String item = iterator.next(); + if (需要删除) { + iterator.remove(); + } + } + ``` + +5. 【强制】在使用 java.util.stream.Collectors 类的 toMap() 方法转为 Map 集合时,必须注意当 value 为 null 时会抛 NPE 异常。 + +6. 【推荐】集合使用时,尽量指明适用场景,区分 ArrayList、LinkedList 的适用场景。 + ​ 说明:ArrayList 查询快,增删慢;LinkedList 增删快,查询慢。 + +7. 【推荐】高度注意 Map 类集合 K/V 能不能存储 null 值的情况。 + ​ 说明:HashMap 可以存储 null 的 key 和 value;ConcurrentHashMap 不允许 null 的 key 和 value;TreeMap 不允许 null 的 key。 + +# ***\*七、异常处理\**** + +1. 【强制】不要捕获 Java 类库中定义的继承自 RuntimeException 的运行时异常类,如:IndexOutOfBoundsException / NullPointerException,这类异常由程序员预检查来规避,保证程序健壮性。 + +2. 【强制】不要使用 try-catch 捕获 Throwable, Throwable 是所有错误和异常的父类,捕获它会包含 Error 等严重问题。 + +3. 【强制】捕获异常后,必须记录异常堆栈信息,不要只输出异常名称。 + ​ 反例:e.printStackTrace() + ​ 正例:logger.error("处理失败", e); + +4. 【强制】不要在 finally 块中使用 return。 + ​ 说明:finally 块中的 return 返回后,try 块中的 return 失效。 + +5. 【强制】捕获后的异常,若需要重新抛出,必须保留原有异常信息。 + ​ 正例:throw new BusinessException("系统异常", e); + +6. 【强制】在调用 RPC、二方包、或动态生成类的相关方法时,必须要捕获异常。 + +7. 【强制】NPE 是程序员最常遇到的异常,必须进行预防性检查。 + ​ 说明:当方法返回值为集合或对象时,先判空再操作。 + +8. 【推荐】尽量使用 try-with-resources 语句关闭资源。 + ​ 正例: + ```java + try (InputStream is = new FileInputStream(path)) { + // 业务逻辑 + } + ``` + +9. 【推荐】捕获异常与抛出异常必须是完全匹配,或者捕获异常是抛异常的父类。 + ​ 说明:如果预期抛出的是 A 异常,实际捕获了 B 异常(A 的父类),可能导致问题掩盖。 + +# ***\*八、并发/多线程\**** + +1. 【强制】创建线程或线程池时请指定有意义的线程名称,方便出错时回溯。 + +2. 【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 + ​ 说明:Executors 返回的线程池对象的弊端如下: + 1)FixedThreadPool 和 SingleThreadPool: 允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量请求,从而导致 OOM。 + 2)CachedThreadPool 和 ScheduledThreadPool: 允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量线程,从而导致 OOM。 + +3. 【强制】SimpleDateFormat 是线程不安全的类,一般不要定义为 static 变量,如果定义为 static,必须加锁,或者使用 DateUtils 工具类。 + ​ 正例:注意线程安全,使用 DateUtils。亦推荐如下处理: + ```java + private static final ThreadLocal formatter = + ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd")); + ``` + +4. 【强制】高并发时,同步调用应该去考量锁的性能损耗。能用无锁数据结构,就不要用锁;能锁区块,就不要锁整个方法体;能用对象锁,就不要用类锁。 + +5. 【强制】对多个资源、数据库表、对象同时加锁时,需要保持一致的加锁顺序,否则可能会造成死锁。 + +6. 【强制】并发修改同一记录时,避免更新丢失,需要加锁。要么在应用层加锁,要么在缓存加锁,要么在数据库使用乐观锁,使用 version 作为更新依据。 + ​ 说明:如果每次访问冲突概率小于 20%,推荐使用乐观锁,否则使用悲观锁。乐观锁的重试次数不得小于 3 次。 + +7. 【强制】避免 Random 实例被多线程使用,虽然共享该实例是线程安全的,但会因竞争同一 seed 导致的性能下降。 + ​ 说明:在 JDK7 之后,可以直接使用 ThreadLocalRandom。 + +8. 【推荐】volatile 解决多线程内存不可见问题。对于一写多读,是可以解决变量同步问题的,但是如果多写,同样无法解决线程安全问题。 + ​ 说明:如果是 count++ 操作,使用如下类实现:AtomicInteger count = new AtomicInteger(); count.addAndGet(1); 如果是 JDK8,推荐使用 LongAdder。 + +9. 【推荐】HashMap 在容量不够进行扩容时,rehash 操作非常耗费性能,所以在能够预估容量大小的情况下,使用初始化容量可以减少扩容次数。 + +# ***\*九、日志规范\**** + +1. 【强制】应用中不可直接使用日志系统(Log4j、Logback)中的 API,而应依赖使用日志框架(SLF4J、JCL)中的 API。 + ​ 说明:使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。 + +2. 【强制】所有日志文件至少保存 15 天,因为有些异常具备以"周"为频次发生的特点。 + +3. 【强制】应用中的扩展日志(如打点、临时监控、访问日志等)命名方式:appName_logType_logName.log。 + ​ 说明:推荐使用工具类统一生成日志名称,避免手动命名造成的混乱。 + +4. 【强制】在日志输出时,字符串变量之间的拼接使用占位符的方式。 + ​ 说明:因为 String 字符串的拼接会使用 StringBuilder 的 append() 方式,有一定的性能损耗。使用占位符仅是替换动作,可以有效提升性能。 + ​ 正例:logger.debug("处理结果:{}", result); + +5. 【强制】对于 trace/debug/info 级别的日志输出,必须进行日志级别的开关判断。 + ​ 说明:虽然在 debug(parameters) 方法的内部会判断日志级别,但参数的字符串拼接仍然会执行,影响性能。 + ​ 正例: + ```java + if (logger.isDebugEnabled()) { + logger.debug("处理结果:{}", result); + } + ``` + +6. 【强制】避免重复打印日志,浪费磁盘空间,务必在 log4j.xml 中设置 additivity=false。 + ​ 正例: + +7. 【强制】谨慎地记录日志。生产环境禁止输出 debug 日志;有选择地输出 info 日志;如果使用 warn 来记录刚上线时的业务行为信息,一定要注意日志输出量的问题,避免把服务器磁盘撑爆,并记得及时删除这些观察日志。 + +8. 【强制】生产环境禁止使用 System.out 或 System.err 输出日志。 + +9. 【强制】异常信息应该包括两类信息:案发现场信息和异常堆栈信息。如果不处理,那么通过关键字往上找。 + +10. 【强制】日志打印时禁止使用行号来记录日志位置信息。 + +11. 【推荐】谨慎地记录日志。生产环境禁止输出 debug 日志;有选择地输出 info 日志。 + +12. 【推荐】可以使用 warn 日志级别来记录用户输入参数错误的情况,避免用户投诉时,无所适从。注意日志输出的级别,error 级别只记录系统逻辑出错、异常等重要的错误信息。 + +# ***\*十、控制语句\**** + +1. 【强制】switch 块必须包含 default 分支,且 default 分支应该是最后一个分支。 + +2. 【强制】switch 语句中的每个 case 分支必须包含 break、return 或 throw 语句,防止 case 穿透。 + ​ 说明:如果确实需要 case 穿透,必须显式注释说明。 + +3. 【强制】在 if/else/for/while/do 语句中必须使用大括号。即使只有一行代码,避免采用 单行的编码方式:if (condition) statements; + +4. 【推荐】表达异常的分支时,少用 if-else 方式,推荐使用卫语句。 + ​ 说明:if-else 方式会嵌套过深,卫语句可以使代码更清晰。 + ​ 反例: + ```java + if (condition) { + // 业务逻辑 + } else { + return; + } + ``` + ​ 正例: + ```java + if (!condition) { + return; + } + // 业务逻辑 + ``` + +5. 【推荐】不要在条件判断中执行复杂逻辑或复杂表达式,以提高代码可读性。 + +# ***\*十一、NPE 防护\**** + +1. 【强制】禁止使用对象直接调用 Object 类中的 equals 方法,而应该使用常量或确定有值的对象来调用 equals。 + ​ 正例:"test".equals(object); + ​ 反例:object.equals("test"); + +2. 【强制】所有相同类型的包装类对象之间值的比较,全部使用 equals 方法比较。 + ​ 说明:对于 Integer var = ? 在-128 至 127 之间的赋值,Integer 对象是在 IntegerCache.cache 产生,会复用已有对象,这个区间内的 Integer 值可以进行 == 判断,但是这个区间之外的所有数据,都会在堆上产生,并不会复用已有对象,推荐使用 equals 方法。 + +3. 【强制】任何数据的金额、金额计算,必须使用 BigDecimal,严禁使用 float、double。 + ​ 说明:float 和 double 存在精度丢失问题。 + +4. 【强制】BigDecimal 的等值比较应使用 compareTo() 方法,而不是 equals() 方法。 + ​ 说明:equals() 会比较精度,如 new BigDecimal("1.0").equals(new BigDecimal("1.00")) 为 false。 + +5. 【强制】基本数据类型与包装数据类型的使用标准: + 1) 所有的 POJO 类属性必须使用包装数据类型。 + 2) RPC 方法的返回值和参数必须使用包装数据类型。 + 3) 所有的局部变量推荐使用基本数据类型。 + ​ 说明:POJO 类属性没有初值是提醒使用者在需要使用时,必须自己显式赋值,任何 NPE 问题,或者入库检查,都由使用者来保证。 + +6. 【推荐】当方法返回值为集合或对象时,若可能为 null,建议使用空集合或空对象返回,避免 NPE。 + ​ 说明:可以使用 Collections.emptyList() 返回空列表。 + +7. 【推荐】使用 Optional 类来包装可能为 null 的值。 + ​ 正例: + ```java + Optional name = Optional.ofNullable(getName()); + name.ifPresent(n -> System.out.println(n)); + ``` + +8. 【推荐】在进行数据库查询时,若结果可能为空,必须判空后再使用。 + +# ***\*十二、序列化规范\**** + +1. 【强制】序列化类新增属性时,请不要修改 serialVersionUID 字段,避免反序列化失败;如果完全不兼容升级,避免反序列化混乱,那么请修改 serialVersionUID 值。 + ​ 说明:注意 serialVersionUID 不一致会抛出序列化运行时异常。 + +2. 【强制】禁止在序列化类中,使用未声明为 static final 的 serialVersionUID 字段。 + ​ 说明:private static final long serialVersionUID = 1L; + +3. 【强制】序列化类中的属性必须实现 Serializable 接口。 + +4. 【强制】序列化类中不建议使用非静态的内部类。 + ​ 说明:内部类会持有外部类的引用,可能导致序列化问题。 + +5. 【推荐】当序列化类中有自定义的序列化逻辑时,请使用 readObject、writeObject 方法,并声明为 private。 + +6. 【推荐】序列化类中的敏感字段(如密码、密钥)建议标记为 transient,避免序列化到外部存储中。 + +# ***\*十三、安全规范\**** + +1. 【强制】用户请求的参数必须进行校验,包括:非空校验、长度校验、格式校验、范围校验等。 + ​ 说明:避免恶意参数导致系统异常或被攻击。 + +2. 【强制】禁止直接将用户输入拼接到 SQL 语句中,必须使用参数化查询(PreparedStatement)。 + ​ 说明:防止 SQL 注入攻击。 + +3. 【强制】用户输入的 HTML 标签必须进行转义或过滤,防止 XSS 攻击。 + ​ 说明:可以使用 OWASP ESAPI 或框架自带的转义工具。 + +4. 【强制】禁止在日志中输出敏感信息,包括但不限于:密码、身份证号、银行卡号、手机号、Token、密钥等。 + ​ 说明:敏感信息需要脱敏处理,如:138****1234。 + +5. 【强制】密码必须使用不可逆加密算法存储,推荐使用 BCrypt、PBKDF2、Argon2、国密 等算法。 + 反例:使用 MD5、SHA1 等弱哈希算法存储密码。 + 正例:使用 BCrypt.encode(password) 加密存储。 + +6. 【强制】密钥、密码等敏感信息禁止硬编码在代码中,必须配置在配置文件或密钥管理系统中。 + ​ 说明:代码可能会被泄露,配置文件可以单独管理和加密。 + +7. 【强制】文件上传必须进行严格校验: + 1) 校验文件类型(通过文件头校验,而不仅仅是文件后缀)。 + 2) 校验文件大小。 + 3) 重命名上传的文件,防止路径遍历攻击。 + 4) 上传文件必须存储在非 Web 根目录下。 + 5) 禁止上传可执行文件(如 .jsp、.exe、.sh 等)。 + +8. 【强制】对外提供的接口必须进行权限校验,防止越权访问。 + ​ 说明:包括接口鉴权、数据权限校验、操作权限校验等。 + +9. 【强制】涉及资金、重要数据的操作,必须进行二次验证或审批,防止误操作或恶意操作。 + +10. 【强制】使用 HTTPS 协议传输敏感数据,禁止使用 HTTP 传输明文敏感信息。 + +11. 【强制】敏感数据的加密密钥必须定期更换,并建立密钥管理机制。 + +12. 【强制】禁止在 URL、Cookie 中传输敏感信息,如密码、Token 等。 + +13. 【强制】对于重定向操作,必须校验目标 URL,防止开放重定向攻击。 + ​ 说明:禁止直接使用用户输入的 URL 进行重定向。 + +14. 【强制】定时任务、批处理任务等后台任务,必须进行权限控制和操作日志记录。 + +15. 【推荐】使用成熟的权限框架,如 Spring Security、Apache Shiro 等。 + +16. 【推荐】对于重要的接口,推荐添加防重放攻击机制,如:Nonce、时间戳、签名等。 + +17. 【推荐】对于第三方接口调用,必须添加超时控制和重试机制,防止资源耗尽。 + +18. 【推荐】定期进行安全扫描和漏洞检测,及时修复已知漏洞。 + +19. 【推荐】建立安全事件响应机制,发现安全问题时能及时处理和追溯。 + +# ***\*十四、其他\**** + +1. 【推荐】循环体中的语句要考量性能,以下操作尽量移至循环体外处理,如定义对象、变量、获取数据库连接,进行不必要的 try-catch 操作 +2. 【推荐】避免采用取反逻辑运算符。 +3. 【推荐】对外提供的开放接口,尽量进行参数校验 +4. 【推荐】及时清理不再使用的代码段或配置信息 + diff --git a/docs/settingsfjk.xml b/docs/settingsfjk.xml new file mode 100644 index 0000000..177f582 --- /dev/null +++ b/docs/settingsfjk.xml @@ -0,0 +1,221 @@ + + + + + + + + D:\maven\maven-repository + + + + + + + + + + + + + + + + + + + + + + + + + zcloud + admin + Zcloud@88888 + + + releases + admin + Zcloud@88888 + + + snapshots + admin + Zcloud@88888 + + + + + zcloud + zcloud maven + http://192.168.20.100:8578/repository/maven-public/ + * + + + + + + + + + nexus + + + + releases + + http://192.168.20.100:8578/repository/maven-releases + + + true + + + true + + + + snapshots + http://192.168.20.100:8578/repository/maven-snapshots + + true + + + true + + + + + + + + releases + http://192.168.20.100:8578/repository/maven-releases + + true + + + true + + + + snapshots + http://192.168.20.100:8578/repository/maven-snapshots + + true + + + true + + + + + + allow-http + + true + + + + + + + + nexus + allow-http + + diff --git a/docs/sql/database-design.md b/docs/sql/database-design.md new file mode 100644 index 0000000..6aee83a --- /dev/null +++ b/docs/sql/database-design.md @@ -0,0 +1,993 @@ +# 巫溪县安全评价在线监管一件事 — 数据库设计文档 + +> 版本:v1.2 +> 依据:产品原型 PDF(设计文件 1–12) +> 数据库:MySQL 8.0+ / InnoDB / utf8mb4_unicode_ci +> ORM:MyBatis-Plus(通用字段与 `@TableLogic` 对齐) + +--- + +## 1. 文档说明 + +### 1.1 设计目标 + +| 业务域 | 原型模块 | 核心表 | +|--------|----------|--------| +| 机构认证 | 认证信息(填写→审核中→通过) | `org_info`、`org_certification_audit` | +| 企业信息管理 | 机构信息、资质、人员、部门岗位、装备 | `org_*`、`staff_*`、`equip_info` | +| 人员异动 | 人员变更、离职申请 | `staff_change_log`、`staff_resignation_apply` | +| 资质备案 | 备案申请(4步)、变更、已备案 | `filing_*` | +| 系统支撑 | 用户、附件、审核、字典、区划 | `sys_*`、`audit_record` | + +> **阅读指引**:§2 ER 图与关联矩阵 → **§3 每张表作用与关系(核心)** → §4–§8 字段明细 → §11 表清单总览 + +### 1.2 命名规范 + +| 项 | 规范 | +|----|------| +| 表名 | 小写 + 下划线;前缀 `sys_` / `org_` / `staff_` / `filing_` / `equip_` | +| 字段 | Java 驼峰 → 数据库下划线,如 `createTime` → `create_time` | +| 主键 | `id` BIGINT UNSIGNED | +| 外键 | `{实体}_id`,逻辑外键,应用层约束 | +| 日期 | **DATE**:仅年月日(出生日期、证书有效期、签署日期等) | +| 时间 | **DATETIME**:年月日时分秒(创建、提交、审核、登录等) | +| 逻辑删除 | 字段名固定 **`deleted`**(不转下划线),`0` 未删 / `1` 已删 | + +### 1.3 通用字段(与 MyBatis-Plus BaseEntity 对齐) + +业务表(可增删改)均包含以下 5 个字段: + +```java +/** 创建时间 */ +@TableField(fill = FieldFill.INSERT) +private LocalDateTime createTime; + +/** 创建人 */ +@TableField(fill = FieldFill.INSERT) +private String createBy; + +/** 更新时间 */ +@TableField(fill = FieldFill.INSERT_UPDATE) +private LocalDateTime updateTime; + +/** 更新人 */ +@TableField(fill = FieldFill.INSERT_UPDATE) +private String updateBy; + +/** 删除标识 0-否 1-是 */ +@TableLogic(value = "0", delval = "1") +@TableField(fill = FieldFill.INSERT) +private Integer deleted; +``` + +对应 DDL: + +```sql +create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', +create_by VARCHAR(64) DEFAULT NULL COMMENT '创建人', +update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', +update_by VARCHAR(64) DEFAULT NULL COMMENT '更新人', +deleted TINYINT NOT NULL DEFAULT 0 COMMENT '删除标识0-否1-是' +``` + +**例外(只增不改的流水表)** 仅保留 `create_time`、`create_by`(及业务时间字段): + +- `audit_record` — 审核流水 +- `org_certification_audit` — 认证审核历史 +- `staff_change_log` — 人员变更记录 +- `filing_change_detail` — 备案变更明细 + +### 1.4 附件设计 + +证书图片、材料扫描件、离职报告、单位介绍附件等 **不单独建业务字段存 URL**,统一走 `sys_attachment`: + +| biz_type | 关联 biz_id | 原型场景 | +|----------|-------------|----------| +| org_intro | filing_application.id / org_info.id | 单位基本情况介绍上传 | +| org_cert | org_qualification_cert.id | 机构资质证书图片 | +| staff_cert | staff_certificate.id | 人员证书附件 | +| filing_material | filing_application_material.id | 申请材料扫描件 | +| filing_commitment_sign | filing_commitment.id | 法人签名 | +| filing_commitment_seal | filing_commitment.id | 单位盖章 | +| resign_report | staff_resignation_apply.id | 离职通知报告 PDF | +| staff_ability_proof | staff_info.id | 申报专业能力证明材料 | + +--- + +## 2. ER 关系总览 + +> 以下为全局 ER 与关联矩阵;**每张表的业务职责、上游引用、下游被引用** 见 [§3 数据表作用与关系说明](#3-数据表作用与关系说明)。 + +```mermaid +erDiagram + org_info ||--o{ org_department : "org_id" + org_department ||--o{ org_position : "dept_id" + org_info ||--o{ staff_info : "org_id" + org_department ||--o{ staff_info : "dept_id" + org_position ||--o{ staff_info : "position_id" + staff_info ||--o{ staff_certificate : "staff_id" + org_info ||--o{ org_qualification_cert : "org_id" + org_info ||--o{ org_certification_audit : "org_id" + org_info ||--o{ equip_info : "org_id" + + org_info ||--o{ filing_application : "org_id" + filing_application ||--o{ filing_application_material : "application_id" + filing_application ||--o{ filing_application_staff : "application_id" + filing_application_staff ||--o{ filing_application_staff_cert : "application_staff_id" + filing_application ||--o| filing_commitment : "application_id" + filing_application ||--o| filing_record : "application_id" + filing_record ||--o{ filing_change_record : "filing_record_id" + filing_change_record ||--o{ filing_change_detail : "change_record_id" + + staff_info ||--o{ staff_change_log : "staff_id" + staff_info ||--o{ staff_resignation_apply : "staff_id" + sys_user ||--o| staff_info : "user_id" + sys_user }o--|| org_info : "org_id" + sys_region ||--o{ filing_record : "filing_region_id" + sys_region ||--o{ org_info : "region_*_id" +``` + +### 2.1 关联矩阵 + +| 主表 | 从表 | 关系 | 关联字段 | 说明 | +|------|------|------|----------|------| +| org_info | org_department | 1:N | org_id | 部门树 | +| org_department | org_position | 1:N | dept_id | 部门下岗位 | +| org_info | staff_info | 1:N | org_id | 机构人员 | +| org_department | staff_info | 1:N | dept_id | 人员所属部门 | +| org_position | staff_info | 1:N | position_id | 人员岗位 | +| staff_info | staff_certificate | 1:N | staff_id | 人员证书 | +| staff_info | staff_change_log | 1:N | staff_id | 变更追溯 | +| staff_info | staff_resignation_apply | 1:N | staff_id | 离职申请 | +| org_info | org_qualification_cert | 1:N | org_id | 机构资质 | +| org_info | equip_info | 1:N | org_id | 装备 | +| org_info | filing_application | 1:N | org_id | 备案申请 | +| filing_application | filing_application_material | 1:N | application_id | 步骤2材料 | +| filing_application | filing_application_staff | 1:N | application_id | 步骤3人员 | +| filing_application_staff | filing_application_staff_cert | 1:N | application_staff_id | 步骤3人员证书 | +| filing_application | filing_commitment | 1:1 | application_id | 步骤4承诺书 | +| filing_application | filing_record | 1:1 | application_id | 审核通过后生成 | +| filing_record | filing_change_record | 1:N | filing_record_id | 备案变更 | +| filing_change_record | filing_change_detail | 1:N | change_record_id | 变更明细 | +| filing_change_record | filing_application | N:1 | change_application_id | 变更类申请 | +| sys_user | staff_info | 1:1 | user_id | 可选绑定 | +| sys_attachment | 各业务 | N:1 | biz_type + biz_id | 附件 | +| sys_region | org_info | 1:N | region_county_id 等 | 机构所属区划 | +| sys_region | filing_application / filing_record | 1:N | filing_region_id | 备案属地 | +| sys_dict | sys_dict_item | 1:N | dict_id | 字典项 | +| audit_record | 各审核业务 | N:1 | biz_type + biz_id | 审核流水 | +| org_info | org_certification_audit | 1:N | org_id | 认证审核历史 | +| org_info | sys_user | 1:N | org_id | 机构用户 | +| staff_info | filing_application_staff | 1:N | staff_id | 备案时引用人员(可选) | +| staff_certificate | filing_application_staff_cert | 1:N | staff_cert_id | 备案时引用证书(可选) | + +--- + +## 3. 数据表作用与关系说明 + +> 本节按业务域列出 **全部 24 张表** 的职责及与其他表的关联,便于开发理解数据流向。 +> 箭头含义:`A → B` 表示 A 表通过外键字段引用 B 表(或多对一归属 B)。 + +### 3.1 系统支撑域 + +#### sys_user — 系统用户 + +| 项 | 说明 | +|----|------| +| **作用** | 存储平台登录账号,支撑机构端、监管端身份认证;人员开通账号、重置密码均操作此表。 | +| **对应原型** | 人员信息管理(账号列、重置密码);认证/备案各模块的操作主体。 | +| **引用(上游)** | `org_info`(`org_id`,机构用户必填;监管用户为空) | +| **被引用(下游)** | `staff_info.user_id`(人员与账号 1:1 可选绑定) | +| **关联方式** | 机构用户:`sys_user.org_id = org_info.id`;人员账号:`staff_info.user_id = sys_user.id` | + +#### sys_region — 行政区划 + +| 项 | 说明 | +|----|------| +| **作用** | 维护省→市→区县→街道→社区树形区划,供机构地址、备案属地下拉选择与列表展示。 | +| **对应原型** | 机构信息「所属县区/镇街道/村社区」;备案列表「备案属地」。 | +| **引用(上游)** | 自关联 `parent_id → sys_region.id`(树形结构) | +| **被引用(下游)** | `org_info.region_county_id / region_street_id / region_community_id`;`filing_application.filing_region_id`;`filing_record.filing_region_id` | +| **关联方式** | 树查询:`parent_id`;机构/备案:`filing_region_id = sys_region.id` | + +#### sys_dict — 数据字典 + +| 项 | 说明 | +|----|------| +| **作用** | 字典类型主表,定义下拉选项分类(备案状态、审核状态、设备状态等)。 | +| **对应原型** | 各页面「请选择」类筛选项、状态标签的后端枚举来源。 | +| **引用(上游)** | 无 | +| **被引用(下游)** | `sys_dict_item.dict_id` | +| **关联方式** | `sys_dict_item.dict_id = sys_dict.id` | + +#### sys_dict_item — 字典项 + +| 项 | 说明 | +|----|------| +| **作用** | 字典具体选项值与展示标签,如「已备案」「未审核」「启用/禁用」。 | +| **对应原型** | 备案状态、离职审核状态、设备状态等下拉框选项。 | +| **引用(上游)** | `sys_dict`(`dict_id`) | +| **被引用(下游)** | 业务表通过 `dict_code + item_value` 逻辑引用,无物理外键 | +| **关联方式** | 查询:`sys_dict.dict_code = 'filing_status'` JOIN `sys_dict_item` | + +#### sys_attachment — 通用附件 + +| 项 | 说明 | +|----|------| +| **作用** | 统一管理各业务上传文件(PDF 扫描件、证书图片、签名盖章等),避免各表重复存 URL。 | +| **对应原型** | 资质证书图片、申请材料上传、离职通知报告、承诺书签章、单位介绍附件等。 | +| **引用(上游)** | 无物理 FK;通过 `biz_type + biz_id` 逻辑关联各业务主键 | +| **被引用(下游)** | 见 §1.4 `biz_type` 映射表 | +| **关联方式** | `WHERE biz_type = 'org_cert' AND biz_id = org_qualification_cert.id` | + +#### audit_record — 审核流水 + +| 项 | 说明 | +|----|------| +| **作用** | 记录各类业务的提交、通过、驳回、退回操作及意见,用于审计追溯与状态机还原。 | +| **对应原型** | 机构认证审核、备案审核、离职审核、备案变更审核的状态流转历史。 | +| **引用(上游)** | 无;`biz_type + biz_id` 指向具体业务记录 | +| **被引用(下游)** | 无(只增不改的流水表) | +| **关联方式** | `biz_type='filing_application' AND biz_id=filing_application.id` | + +--- + +### 3.2 机构与企业域 + +#### org_info — 机构基本信息 + +| 项 | 说明 | +|----|------| +| **作用** | **核心主数据表**。存储安全评价机构主体信息,承载首次「认证信息」填报、日常「机构信息管理」维护,并为备案申请提供基础数据。 | +| **对应原型** | 认证信息(填写→审核中→通过);机构信息管理;备案申请第一步单位信息。 | +| **引用(上游)** | `sys_region`(三区划 ID) | +| **被引用(下游)** | `sys_user`、`org_department`、`org_position`、`staff_info`、`org_qualification_cert`、`equip_info`、`filing_application`、`filing_record`、`org_certification_audit`、`staff_change_log`、`staff_resignation_apply`(均含 `org_id`) | +| **关联方式** | 机构维度数据隔离:`*.org_id = org_info.id` | + +#### org_certification_audit — 机构认证审核记录 + +| 项 | 说明 | +|----|------| +| **作用** | 每次提交机构认证时生成一条审核记录,保留提交快照与审核结果,支持「认证审核中/通过/驳回」历史查询。 | +| **对应原型** | 认证信息步骤条:填写信息 → 认证审核中 → 认证通过。 | +| **引用(上游)** | `org_info`(`org_id`) | +| **被引用(下游)** | 无;可与 `audit_record`(`biz_type=org_certification`)并行写入 | +| **关联方式** | `org_certification_audit.org_id = org_info.id` | + +#### org_qualification_cert — 机构资质证书 + +| 项 | 说明 | +|----|------| +| **作用** | 维护机构持有的安全评价等资质证书(等级、编号、有效期、发证机关),证书图片走 `sys_attachment`。 | +| **对应原型** | 企业信息管理 → 资质信息管理(新增/编辑/禁用/删除证书)。 | +| **引用(上游)** | `org_info`(`org_id`);附件 `sys_attachment`(`biz_type=org_cert`) | +| **被引用(下游)** | 备案变更:资质变更时触发 `filing_change_record`(`change_type=qualification`) | +| **关联方式** | `org_qualification_cert.org_id = org_info.id` | + +--- + +### 3.3 组织与人员域 + +#### org_department — 部门 + +| 项 | 说明 | +|----|------| +| **作用** | 机构内部组织架构,支持多级部门树(原型左侧过滤树:第一级别、第二级别…)。 | +| **对应原型** | 企业信息管理 → 部门岗位管理 → 部门列表/新增/编辑。 | +| **引用(上游)** | `org_info`(`org_id`);自关联 `parent_id` | +| **被引用(下游)** | `org_position.dept_id`;`staff_info.dept_id` | +| **关联方式** | 树:`parent_id = 0` 为根;人员:`staff_info.dept_id = org_department.id` | + +#### org_position — 岗位 + +| 项 | 说明 | +|----|------| +| **作用** | 定义部门下的岗位及职责,人员录入时选择岗位。 | +| **对应原型** | 部门岗位管理 → 添加岗位/编辑岗位(岗位名称、岗位职责、备注)。 | +| **引用(上游)** | `org_info`(`org_id`);`org_department`(`dept_id`) | +| **被引用(下游)** | `staff_info.position_id` | +| **关联方式** | `org_position.dept_id = org_department.id`;`staff_info.position_id = org_position.id` | + +#### staff_info — 人员信息 + +| 项 | 说明 | +|----|------| +| **作用** | 机构从业人员主数据,含基本信息、学历经历、专业能力;列表展示部门/岗位/证照摘要;变更时写 `staff_change_log`。 | +| **对应原型** | 人员信息管理(列表、新增、查看、编辑);备案向导第三步人员(可引用本表)。 | +| **引用(上游)** | `org_info`、`org_department`、`org_position`、`sys_user`(均可空) | +| **被引用(下游)** | `staff_certificate`、`staff_change_log`、`staff_resignation_apply`;`filing_application_staff.staff_id`(可选) | +| **关联方式** | `staff_info.org_id = org_info.id`;证书:`staff_certificate.staff_id = staff_info.id` | + +#### staff_certificate — 人员证书 + +| 项 | 说明 | +|----|------| +| **作用** | 人员持有的各类执业/作业证书,含类别、编号、有效期;证书附件走 `sys_attachment`。 | +| **对应原型** | 人员信息管理 → 添加证书/编辑证书/查看证书详情。 | +| **引用(上游)** | `staff_info`(`staff_id`);`org_info`(`org_id` 冗余便于按机构查);`sys_attachment`(`biz_type=staff_cert`) | +| **被引用(下游)** | `filing_application_staff_cert.staff_cert_id`(备案快照可选来源) | +| **关联方式** | `staff_certificate.staff_id = staff_info.id` | + +#### staff_change_log — 人员变更记录 + +| 项 | 说明 | +|----|------| +| **作用** | 人员信息修改时逐字段记录变更前后值,支撑「人员变更管理」列表的变更次数与变更明细查看。 | +| **对应原型** | 人员变更管理(变更事项、变更时间、操作人);变更内容如「法人:张三→李四」。 | +| **引用(上游)** | `staff_info`(`staff_id`);`org_info`(`org_id`) | +| **被引用(下游)** | 无;变更可能间接触发 `filing_change_record`(机构已有备案时) | +| **关联方式** | `staff_change_log.staff_id = staff_info.id`;同步更新 `staff_info.change_count` | + +#### staff_resignation_apply — 人员离职申请 + +| 项 | 说明 | +|----|------| +| **作用** | 人员离职申请单,含离职原因、预计离职日期、离职通知报告;机构管理员审核后更新人员就职状态。 | +| **对应原型** | 人员离职申请(新增/查看);人员变更管理 → 离职审核(通过/退回)。 | +| **引用(上游)** | `staff_info`(`staff_id`);`org_info`(`org_id`);`sys_attachment`(`biz_type=resign_report`) | +| **被引用(下游)** | `audit_record`(`biz_type=staff_resignation`);审核通过 → 更新 `staff_info.employment_status=2` | +| **关联方式** | `staff_resignation_apply.staff_id = staff_info.id` | + +--- + +### 3.4 装备域 + +#### equip_info — 装备信息 + +| 项 | 说明 | +|----|------| +| **作用** | 评价机构检测仪器设备台账,含分类、型号、流量、校准信息及启用/禁用状态。 | +| **对应原型** | 企业信息管理 → 装备信息管理(新增/编辑/查看/启用禁用/删除)。 | +| **引用(上游)** | `org_info`(`org_id`) | +| **被引用(下游)** | 无(独立主数据;二期监控模块可引用) | +| **关联方式** | `equip_info.org_id = org_info.id` | + +--- + +### 3.5 资质备案域 + +#### filing_application — 备案申请主表 + +| 项 | 说明 | +|----|------| +| **作用** | 备案业务**流程主表**,驱动 4 步向导(基本→材料→人员→承诺书),管理草稿/提交/审核状态;新备案与变更备案共用,通过后生成 `filing_record`。 | +| **对应原型** | 资质申请管理 → 资质备案申请;备案变更管理中的编辑表单。 | +| **引用(上游)** | `org_info`(`org_id`);`sys_region`(`filing_region_id`);`filing_record`(`parent_filing_record_id`,变更备案时) | +| **被引用(下游)** | `filing_application_material`、`filing_application_staff`、`filing_commitment`;审核通过 → `filing_record`;`filing_change_record.change_application_id` | +| **关联方式** | 子表均 `application_id = filing_application.id`;通过后 `filing_record.application_id = filing_application.id` | + +#### filing_application_material — 备案申请材料 + +| 项 | 说明 | +|----|------| +| **作用** | 备案向导第 2 步「申请材料清单」行数据,每条材料对应扫描件附件。 | +| **对应原型** | 资质备案申请 → 申请材料清单(内容、格式 pdf、注释、上传附件)。 | +| **引用(上游)** | `filing_application`(`application_id`);`sys_attachment`(`biz_type=filing_material`) | +| **被引用(下游)** | 无 | +| **关联方式** | `filing_application_material.application_id = filing_application.id` | + +#### filing_application_staff — 备案申请人员快照 + +| 项 | 说明 | +|----|------| +| **作用** | 备案向导第 3 步人员列表;申请提交时从 `staff_info` 复制或手工录入,**与主数据隔离**,保证备案历史不受后续人员变更影响。 | +| **对应原型** | 资质备案申请 → 人员信息(姓名、类型、职务、职称、查看证书)。 | +| **引用(上游)** | `filing_application`(`application_id`);`staff_info`(`staff_id`,可选) | +| **被引用(下游)** | `filing_application_staff_cert`(`application_staff_id`) | +| **关联方式** | `filing_application_staff.application_id = filing_application.id` | + +#### filing_application_staff_cert — 备案申请人员证书快照 + +| 项 | 说明 | +|----|------| +| **作用** | 备案人员关联证书的时点快照,字段对齐 `staff_certificate`,支持「查看证书」而不回查已变更的主数据。 | +| **对应原型** | 备案人员信息 → 查看证书(证书类型、作业类别、编号、有效期等)。 | +| **引用(上游)** | `filing_application`(`application_id`);`filing_application_staff`(`application_staff_id`);`staff_certificate`(`staff_cert_id`,可选) | +| **被引用(下游)** | `sys_attachment`(`biz_type=filing_staff_cert`) | +| **关联方式** | `application_staff_id = filing_application_staff.id` | + +#### filing_commitment — 法定代表人承诺书 + +| 项 | 说明 | +|----|------| +| **作用** | 备案向导第 4 步,记录法人签署信息;签名、盖章文件存 `sys_attachment`。 | +| **对应原型** | 资质备案申请 → 申请单位法定代表人承诺书(签名、盖章、日期)。 | +| **引用(上游)** | `filing_application`(`application_id`,1:1) | +| **被引用(下游)** | `sys_attachment`(`filing_commitment_sign` / `filing_commitment_seal`) | +| **关联方式** | `filing_commitment.application_id = filing_application.id`(唯一) | + +#### filing_record — 已备案资质记录 + +| 项 | 说明 | +|----|------| +| **作用** | 备案审核通过后的**正式备案台账**,含备案编号、属地、业务范围、状态、变更次数;列表即「已备案资质管理」。 | +| **对应原型** | 已备案资质管理;资质备案/变更列表中的「已备案」状态行。 | +| **引用(上游)** | `org_info`(`org_id`);`filing_application`(`application_id`);`sys_region`(`filing_region_id`) | +| **被引用(下游)** | `filing_application.filing_record_id`;`filing_change_record.filing_record_id`;`filing_application.parent_filing_record_id`(变更备案) | +| **关联方式** | `filing_record.application_id = filing_application.id`;变更:`filing_change_record.filing_record_id = filing_record.id` | + +#### filing_change_record — 备案变更记录 + +| 项 | 说明 | +|----|------| +| **作用** | 已备案机构发生人员/资质/单位信息变更时的变更批次主记录,对应「备案变更管理」与「历史变更记录」。 | +| **对应原型** | 备案变更管理(变更次数、备案状态);说明:人员或资质变更后备案数据进入变更管理。 | +| **引用(上游)** | `filing_record`(`filing_record_id`);`org_info`(`org_id`);`filing_application`(`change_application_id`,变更类申请) | +| **被引用(下游)** | `filing_change_detail`(`change_record_id`);完成后 `filing_record.change_count + 1` | +| **关联方式** | `filing_change_record.filing_record_id = filing_record.id` | + +#### filing_change_detail — 备案变更明细 + +| 项 | 说明 | +|----|------| +| **作用** | 单次备案变更的字段级明细,展示「由 A 变更为 B」,如法人、联系电话变更。 | +| **对应原型** | 备案变更查看 → 变更内容(由张三变更为李四、由152…变更为187…)。 | +| **引用(上游)** | `filing_change_record`(`change_record_id`) | +| **被引用(下游)** | 无(只增不改) | +| **关联方式** | `filing_change_detail.change_record_id = filing_change_record.id` | + +--- + +### 3.6 表关系分层图 + +``` +【租户根】 org_info + ├── sys_user 登录账号 + ├── org_certification_audit 认证审核历史 + ├── org_qualification_cert ──→ sys_attachment(org_cert) + ├── org_department + │ └── org_position + ├── staff_info ──→ sys_user + │ ├── staff_certificate ──→ sys_attachment(staff_cert) + │ ├── staff_change_log + │ └── staff_resignation_apply ──→ sys_attachment(resign_report) + ├── equip_info + └── filing_application ──→ sys_region(备案属地) + ├── sys_attachment(org_intro) + ├── filing_application_material ──→ sys_attachment(filing_material) + ├── filing_application_staff ──→ staff_info(可选) + │ └── filing_application_staff_cert ──→ staff_certificate(可选) + ├── filing_commitment ──→ sys_attachment(sign/seal) + └── [审核通过] filing_record ──→ sys_region + └── filing_change_record ──→ filing_application(变更类) + └── filing_change_detail + +【全局】 sys_region / sys_dict / sys_dict_item / sys_attachment / audit_record +``` + +--- + +## 4. 系统与基础表(字段明细) + +### 4.1 sys_user — 系统用户 + +> **作用**:平台登录账号,机构用户绑定机构,人员可关联账号。 +> **关系**:`org_id → org_info`;被 `staff_info.user_id` 引用。 + +原型:人员列表「账号」、重置密码;机构/监管登录主体。 + +| 字段 | 类型 | 说明 | +|------|------|------| +| id | BIGINT | PK | +| org_id | BIGINT | 所属机构,监管用户 NULL | +| username | VARCHAR(64) | 登录账号,UK(username, deleted) | +| password_hash | VARCHAR(255) | 密码 | +| real_name | VARCHAR(64) | 姓名 | +| phone | VARCHAR(20) | 手机 | +| user_type | TINYINT | 1机构 2监管 3管理员 | +| status | TINYINT | 0禁用 1正常 | +| last_login_time | **DATETIME** | 最后登录 | +| + 通用字段 | | | + +### 4.2 sys_region — 行政区划 + +> **作用**:省市区街道社区树形区划,支撑机构地址与备案属地下拉。 +> **关系**:自关联 `parent_id`;被 `org_info`、`filing_application`、`filing_record` 引用。 + +原型:备案属地列表(省市区)、机构「所属县区/镇街道/村社区」。 + +| 字段 | 类型 | 说明 | +|------|------|------| +| id | BIGINT | PK | +| parent_id | BIGINT | 父级 | +| region_code | VARCHAR(12) | 国标码 | +| region_name | VARCHAR(64) | 名称 | +| region_level | TINYINT | 1省2市3区县4街道5社区 | +| sort_order | INT | 排序 | +| + 通用字段 | | | + +### 4.3 sys_dict / sys_dict_item — 数据字典 + +> **作用**:`sys_dict` 定义字典类型;`sys_dict_item` 存储具体选项值,供各业务状态下拉与展示。 +> **关系**:`sys_dict_item.dict_id → sys_dict.id`;业务表通过 dict_code 逻辑引用。 + +| dict_code | 用途 | +|-----------|------| +| filing_status | 未备案 / 已备案 / 驳回 | +| cert_audit_status | 草稿 / 审核中 / 已通过 / 已驳回 | +| org_business_status | 开业 / 停业等 | +| staff_employment_status | 在职 / 离职 | +| resign_audit_status | 未审核 / 已审核 / 已退回 | +| equip_enable_status | 启用 / 禁用 | +| material_format | pdf 等 | + +### 4.4 sys_attachment — 通用附件 + +> **作用**:各业务上传文件的统一存储入口,通过 `biz_type + biz_id` 关联业务记录。 +> **关系**:逻辑关联多表,无物理外键;详见 §1.4。 + +见 §1.4。 + +### 4.5 audit_record — 审核流水(无 deleted) + +> **作用**:跨业务审核操作流水,记录提交/通过/驳回/退回及意见。 +> **关系**:`biz_type + biz_id` 指向 org_certification / filing_application / staff_resignation / filing_change 等业务记录。 + +| 字段 | 类型 | 说明 | +|------|------|------| +| biz_type | VARCHAR(32) | org_certification / filing_application / staff_resignation / filing_change | +| biz_id | BIGINT | 业务ID | +| action | VARCHAR(16) | submit / approve / reject / return | +| from_status / to_status | VARCHAR(32) | 状态流转 | +| opinion | TEXT | 意见 | +| operate_time | **DATETIME** | 操作时间 | +| create_time | **DATETIME** | | +| create_by | VARCHAR(64) | 操作人 | + +--- + +## 5. 机构与企业信息(字段明细) + +### 5.1 org_info — 机构基本信息 + +> **作用**:机构核心主数据,承载认证填报与日常维护,是多数业务表的租户根。 +> **关系**:`region_*_id → sys_region`;被 org/staff/filing/equip 等全部 `org_id` 子表引用。详见 §3.2。 + +原型:认证信息、机构信息管理、备案申请第一步字段。 + +| 字段 | 类型 | 原型字段 | +|------|------|----------| +| org_name | VARCHAR(200) | 生产经营单位名称 | +| credit_code | VARCHAR(18) | 统一社会信用代码 | +| register_address | VARCHAR(500) | 注册地址 | +| business_address | VARCHAR(500) | 经营地址 / 办公地址 | +| longitude / latitude | DECIMAL | 所在地坐标 | +| region_county_id / region_street_id / region_community_id | BIGINT | 所属县区、镇街道、村社区 | +| safety_industry_category | VARCHAR(100) | 安全生产监管行业类别 | +| ownership_type | VARCHAR(32) | 归属类型 | +| gb_industry_code | VARCHAR(20) | 国民经济行业分类 | +| legal_representative / legal_rep_phone | | 法定代表人及电话 | +| principal / principal_phone | | 主要负责人 | +| safety_dept_head / safety_dept_phone | | 安全管理部门负责人 | +| safety_vp / safety_vp_phone | | 主管安全副总 | +| production_date | **DATE** | 投产日期 | +| business_status | VARCHAR(16) | 企业经营状态 | +| disclosure_url | VARCHAR(500) | 信息公开网址 | +| workplace_area / archive_room_area | DECIMAL | 工作场所/档案室面积 | +| full_time_evaluator_count | INT | 专职安全评价师数量 | +| registered_safety_engineer_count | INT | 注册安全工程师数量 | +| fixed_asset_total | DECIMAL | 固定资产总值(万元) | +| org_intro | TEXT | 单位基本情况介绍 | +| cert_status | TINYINT | 0草稿1审核中2已通过3已驳回 | +| cert_submit_time | **DATETIME** | 认证提交 | +| cert_approve_time | **DATETIME** | 认证通过 | +| + 通用字段 | | | + +### 5.2 org_certification_audit — 机构认证审核 + +> **作用**:记录每次机构认证提交与审核结果,保留提交快照。 +> **关系**:`org_id → org_info`;可与 `audit_record`(biz_type=org_certification)并行。 + +| 字段 | 类型 | 说明 | +|------|------|------| +| org_id | BIGINT | 机构 | +| audit_status | TINYINT | 1审核中2通过3驳回 | +| submit_snapshot | JSON | 提交快照 | +| audit_opinion | TEXT | 意见 | +| audit_by | VARCHAR(64) | 审核人 | +| audit_time | **DATETIME** | 审核时间 | +| create_time / create_by | | 仅创建字段 | + +### 5.3 org_qualification_cert — 机构资质证书 + +> **作用**:机构持有的资质证书台账,变更时可触发备案变更流程。 +> **关系**:`org_id → org_info`;附件 `sys_attachment(biz_type=org_cert)`。 + +原型:资质信息管理(证照类型、证书名称、编号、有效期、发证机关、证书图片)。 + +| 字段 | 类型 | 说明 | +|------|------|------| +| org_id | BIGINT | 机构 | +| cert_type | VARCHAR(64) | 证照类型 | +| cert_name | VARCHAR(128) | 证书名称 | +| cert_no | VARCHAR(64) | 证明编号 | +| issue_org | VARCHAR(128) | 发证机关 | +| issue_date | **DATE** | 发证日期 | +| valid_start_date / valid_end_date | **DATE** | 有效期 | +| remark | VARCHAR(500) | 备注 | +| enable_status | TINYINT | 1正常0禁用(原型「禁用」) | +| 附件 | sys_attachment | biz_type=org_cert | +| + 通用字段 | | | + +--- + +## 6. 组织与人员(字段明细) + +### 6.1 org_department — 部门 + +> **作用**:机构组织架构树,支撑部门岗位管理与人员部门归属。 +> **关系**:`org_id → org_info`;`parent_id` 自关联;被 `org_position`、`staff_info` 引用。 + +原型:部门岗位管理,树形筛选(第一级别、第二级别…)。 + +| 字段 | 类型 | 说明 | +|------|------|------| +| org_id | BIGINT | 机构 | +| parent_id | BIGINT | 父部门,0根 | +| dept_name | VARCHAR(64) | 部门名称 | +| dept_level | VARCHAR(32) | 部门级别 | +| leader_name | VARCHAR(64) | 负责人 | +| sort_order | INT | 排序 | +| tree_path | VARCHAR(256) | 物化路径 | +| + 通用字段 | | | + +### 6.2 org_position — 岗位 + +> **作用**:部门下岗位定义,含岗位职责说明。 +> **关系**:`org_id → org_info`;`dept_id → org_department`;被 `staff_info.position_id` 引用。 + +| 字段 | 类型 | 说明 | +|------|------|------| +| org_id / dept_id | BIGINT | 机构、部门 | +| position_name | VARCHAR(64) | 岗位名称 | +| duty_desc | VARCHAR(500) | 岗位职责 | +| remark | VARCHAR(255) | 备注 | +| + 通用字段 | | | + +### 6.3 staff_info — 人员信息 + +> **作用**:机构从业人员主数据,关联部门岗位与账号,是证书/变更/离职的上游。 +> **关系**:`org_id → org_info`;`dept_id → org_department`;`position_id → org_position`;`user_id → sys_user`;下游见 staff_* / filing_application_staff。 + +原型:人员列表(用户名称、账号、部门、岗位、证照名称);详情(出生日期、性别、身份证、学历、资质范围、专业能力等)。 + +| 字段 | 类型 | 原型 | +|------|------|------| +| staff_name | VARCHAR(64) | 姓名 | +| gender | TINYINT | 性别 | +| id_card_no | VARCHAR(18) | 身份证号 | +| birth_date | **DATE** | 出生日期 | +| account | VARCHAR(32) | 账号 | +| dept_id / position_id | BIGINT | 部门、岗位 | +| education / graduate_school / major | | 学历、院校、专业 | +| home_address / office_address | | 现住/办公地址 | +| person_type | VARCHAR(32) | 人员类型 | +| job_title / professional_title | | 职务、职称 | +| qualification_scope | VARCHAR(255) | 资质范围 | +| career_level_cert_no | VARCHAR(64) | 职业等级及证书编号 | +| is_registered_safety_engineer | TINYINT | 是否注册安全工程师 | +| self_declared_ability | TEXT | 自我申报专业能力 | +| work_experience | TEXT | 主要学习工作经历 | +| publications | TEXT | 专著专利论文等 | +| employment_status | TINYINT | 1在职2离职 | +| change_count | INT | 信息变更数(列表冗余) | +| user_id | BIGINT | 关联 sys_user | +| + 通用字段 | | | + +### 6.4 staff_certificate — 人员证书 + +> **作用**:人员执业/作业证书明细,列表「证照名称」数据来源。 +> **关系**:`staff_id → staff_info`;`org_id → org_info`;附件 `sys_attachment(staff_cert)`;可被 `filing_application_staff_cert` 快照引用。 + +原型:证书类型、作业类别、编号、有效期、复核日期、证书图片。 + +| 字段 | 类型 | 说明 | +|------|------|------| +| staff_id / org_id | BIGINT | 人员、机构 | +| cert_name / cert_category / cert_work_category | | 证照名称、类别、作业类别 | +| cert_no | VARCHAR(64) | 证书编号 | +| issue_org | VARCHAR(128) | 发证机关 | +| valid_start_date / valid_end_date / review_date | **DATE** | 有效期、复核 | +| 附件 | sys_attachment | biz_type=staff_cert | +| + 通用字段 | | | + +### 6.5 staff_change_log — 人员变更记录 + +> **作用**:人员字段变更的审计日志,驱动「信息变更数」统计。 +> **关系**:`staff_id → staff_info`;`org_id → org_info`;只增不改,无 deleted。 + +原型:变更事项、变更时间、操作人;人员变更管理列表「信息变更数」。 + +| 字段 | 类型 | 说明 | +|------|------|------| +| staff_id / org_id | BIGINT | | +| change_item | VARCHAR(64) | 变更事项 | +| field_name | VARCHAR(64) | 程序字段名 | +| old_value / new_value | VARCHAR(500) | 变更前后 | +| change_time | **DATETIME** | 变更时间 | +| create_time / create_by | | 操作人 | + +### 6.6 staff_resignation_apply — 人员离职申请 + +> **作用**:离职申请与审核,通过后更新 `staff_info.employment_status`。 +> **关系**:`staff_id → staff_info`;`org_id → org_info`;附件 `sys_attachment(resign_report)`;可写 `audit_record`。 + +原型:申请人、申请时间、离职原因、备注、预计离职日期、离职通知报告;审核状态、退回原因。 + +| 字段 | 类型 | 说明 | +|------|------|------| +| staff_id / org_id | BIGINT | | +| applicant_name | VARCHAR(64) | 申请人 | +| apply_time | **DATETIME** | 申请时间 | +| expected_leave_date | **DATE** | 预计离职日期 | +| leave_reason / remark | TEXT | 离职原因、备注 | +| audit_status | TINYINT | 0未审核1已审核2已退回 | +| reject_reason | TEXT | 退回原因 | +| audit_by / audit_time | | 审核人、审核时间 | +| 附件 | sys_attachment | biz_type=resign_report | +| + 通用字段 | | | + +--- + +## 7. 装备管理(字段明细) + +### 7.1 equip_info — 装备信息 + +> **作用**:评价机构仪器设备台账,独立主数据。 +> **关系**:`org_id → org_info`;无下游业务表(二期监控可引用)。 + +原型:仪器分类、设备类型、设备名称、型号、流量、厂家、校准单位/初值、现场校验类型、是否双路、启用/禁用。 + +| 字段 | 类型 | 说明 | +|------|------|------| +| instrument_category / instrument_type / device_type | VARCHAR | 分类 | +| device_name / model | | 名称、型号 | +| flow_desc / min_flow / max_flow | | 流量说明 | +| manufacturer | VARCHAR(128) | 厂家 | +| calibration_unit / calibration_initial_value | | 校准 | +| on_site_calibration_type | VARCHAR(64) | 现场校验类型 | +| is_dual_channel | TINYINT | 是否双路 | +| enable_status | TINYINT | 1启用0禁用 | +| purchase_date | **DATE** | 登记日期 | +| + 通用字段 | | | + +--- + +## 8. 资质备案(字段明细) + +### 8.1 filing_application — 备案申请主表 + +> **作用**:备案 4 步向导流程主表,串联材料/人员/承诺书子表,审核通过后生成 `filing_record`。 +> **关系**:`org_id → org_info`;`filing_region_id → sys_region`;子表 `application_id`;详见 §3.5。 + +原型:4 步向导 + 备案变更编辑;列表筛选项(备案编号、备案属地、备案状态、备案单位)。 + +| 字段 | 类型 | 步骤/说明 | +|------|------|-----------| +| application_no | VARCHAR(32) | 申请编号 | +| application_type | TINYINT | 1新备案2变更备案 | +| filing_region_id | BIGINT | 备案属地 | +| filing_unit_name / filing_unit_type | | 备案单位、类型 | +| register_address / office_address | | 注册/办公地址 | +| credit_code / qualification_cert_no | | 信用代码、资质证书编号 | +| legal_rep_and_phone / contact_and_phone | | 法人及电话、联系人及电话 | +| disclosure_url | | 信息公开网址 | +| fixed_asset_total / archive_room_area | | 固定资产、档案室面积 | +| full_time_evaluator_count / registered_safety_engineer_count | | 评价师、注安师数量 | +| workplace_area / org_intro | | 建筑面积、单位介绍 | +| business_scope | VARCHAR(500) | 备案安全评价业务范围 | +| current_step | TINYINT | 1基本2材料3人员4承诺书 | +| apply_status | TINYINT | 0草稿1提交2审核中3通过4驳回 | +| filing_record_id | BIGINT | 通过后关联 | +| parent_filing_record_id | BIGINT | 变更备案原记录 | +| submit_time / approve_time | **DATETIME** | 提交、通过时间 | +| 附件 | sys_attachment | biz_type=org_intro | +| + 通用字段 | | | + +### 8.2 filing_application_material — 申请材料(步骤2) + +> **作用**:备案向导第 2 步材料清单行。 +> **关系**:`application_id → filing_application`;附件 `sys_attachment(filing_material)`。 + +| 字段 | 类型 | 原型 | +|------|------|------| +| material_content | VARCHAR(255) | 申请材料目录 | +| material_type | VARCHAR(32) | 纸质版扫描件 | +| file_format | VARCHAR(16) | pdf | +| remark | VARCHAR(500) | 注释 | +| sort_order | INT | 内容序号 | +| 附件 | sys_attachment | biz_type=filing_material | +| + 通用字段 | | | + +### 8.3 filing_application_staff — 备案人员快照(步骤3) + +> **作用**:备案时点人员快照,与 `staff_info` 隔离。 +> **关系**:`application_id → filing_application`;`staff_id → staff_info`(可选);下游 `filing_application_staff_cert`。 + +| 字段 | 类型 | 原型 | +|------|------|------| +| staff_id | BIGINT | 可选,来自 staff_info | +| staff_name | VARCHAR(64) | 人员姓名 | +| person_type / job_title / professional_title | | 类型、职务、职称 | +| sort_order | INT | 序号 | +| + 通用字段 | | | + +### 8.4 filing_application_staff_cert — 备案人员证书快照 + +> **作用**:备案人员证书时点快照,支持「查看证书」。 +> **关系**:`application_staff_id → filing_application_staff`;`staff_cert_id → staff_certificate`(可选)。 + +原型:人员信息步骤「查看证书」— 与 `staff_certificate` 字段对齐,申请时独立快照。 + +| 字段 | 类型 | 说明 | +|------|------|------| +| application_id | BIGINT | 备案申请 | +| application_staff_id | BIGINT | 备案人员 | +| staff_cert_id | BIGINT | 来源证书,可空 | +| cert_name / cert_category / cert_work_category / cert_no | | 同 staff_certificate | +| valid_start_date / valid_end_date / review_date | **DATE** | | +| 附件 | sys_attachment | biz_type=filing_staff_cert | +| + 通用字段 | | | + +### 8.5 filing_commitment — 法定代表人承诺书(步骤4) + +> **作用**:备案第 4 步法人承诺签署记录。 +> **关系**:`application_id → filing_application`(1:1);签章附件走 `sys_attachment`。 + +| 字段 | 类型 | 说明 | +|------|------|------| +| application_id | BIGINT | 1:1 | +| legal_rep_name | VARCHAR(64) | 法定代表人 | +| sign_date | **DATE** | 签署日期 | +| content_version | VARCHAR(16) | 模板版本 | +| 签名/盖章 | sys_attachment | filing_commitment_sign / seal | +| + 通用字段 | | | + +### 8.6 filing_record — 已备案资质 + +> **作用**:备案通过后正式台账,「已备案资质管理」列表数据源。 +> **关系**:`org_id → org_info`;`application_id → filing_application`;`filing_region_id → sys_region`;下游 `filing_change_record`。 + +原型列表:备案属地、备案单位、备案编号、备案业务范围、备案状态、变更次数。 + +| 字段 | 类型 | 说明 | +|------|------|------| +| org_id | BIGINT | 机构 | +| application_id | BIGINT | 来源申请 | +| filing_no | VARCHAR(32) | 备案编号 | +| filing_region_id | BIGINT | 备案属地 | +| filing_unit_name | VARCHAR(200) | 备案单位 | +| business_scope | VARCHAR(500) | 备案业务范围 | +| filing_status | VARCHAR(16) | 未备案/已备案/驳回 | +| change_count | INT | 变更次数 | +| filing_time | **DATETIME** | 备案时间 | +| snapshot_json | JSON | 通过时全量快照 | +| + 通用字段 | | | + +### 8.7 filing_change_record — 备案变更 + +> **作用**:已备案机构变更批次主记录,含变更次数统计。 +> **关系**:`filing_record_id → filing_record`;`change_application_id → filing_application`;下游 `filing_change_detail`。 + +原型:备案变更管理;人员/资质变更后进入;历史变更记录。 + +| 字段 | 类型 | 说明 | +|------|------|------| +| filing_record_id / org_id | BIGINT | | +| change_application_id | BIGINT | 变更类 filing_application | +| change_type | VARCHAR(32) | personnel / qualification / org_info / mixed | +| change_summary | VARCHAR(500) | 摘要 | +| change_status | TINYINT | 0进行中1完成2驳回 | +| change_time | **DATETIME** | 完成时间 | +| + 通用字段 | | | + +### 8.8 filing_change_detail — 备案变更明细 + +> **作用**:备案变更字段级前后值对比明细。 +> **关系**:`change_record_id → filing_change_record`;只增不改。 + +原型:「由张三变更为李四」「由152…变更为187…」。 + +| 字段 | 类型 | 说明 | +|------|------|------| +| change_record_id | BIGINT | 变更记录 | +| change_field / change_field_label | | 字段、展示名 | +| old_value / new_value | VARCHAR(500) | 变更前后 | +| create_time / create_by | | 仅创建 | + +--- + +## 9. 业务流程与状态 + +### 9.1 机构认证(org_info.cert_status) + +``` +0草稿 --暂存/提交--> 1审核中 --通过--> 2已通过 + └──驳回--> 3已驳回 --再提交--> 1审核中 +``` + +### 9.2 备案申请向导 + +| current_step | 页面 | 表 | +|:--:|------|-----| +| 1 | 基本信息 + 上传附件 | filing_application + sys_attachment(org_intro) | +| 2 | 申请材料清单 | filing_application_material + sys_attachment | +| 3 | 人员信息 + 证书 | filing_application_staff + filing_application_staff_cert | +| 4 | 法定代表人承诺书 | filing_commitment + sys_attachment | + +提交后 `apply_status` → 1/2;监管审核通过 → 写入 `filing_record`,`filing_application.filing_record_id` 回写。 + +### 9.3 备案变更触发 + +机构存在有效 `filing_record` 时,以下变更应生成 `filing_change_record` + `filing_change_detail`,并 `change_count + 1`: + +- `staff_info` / `staff_certificate` 增删改 +- `org_qualification_cert` 变更 +- `org_info` 法人、联系方式等关键字段变更 + +### 9.4 人员离职 + +``` +staff_resignation_apply.audit_status: + 0未审核 --通过--> 1已审核 → staff_info.employment_status = 2 + └──退回--> 2已退回 +``` + +--- + +## 10. 索引与查询建议 + +1. 列表:`org_id + deleted + 状态字段 + create_time DESC` +2. 唯一键与逻辑删除:业务唯一键使用 `(code, deleted)` 组合,避免软删后无法重建 +3. MyBatis-Plus 全局:`@TableLogic` 自动追加 `deleted = 0` +4. 流水表不加 `deleted`,不做更新 +5. 大 JSON(snapshot_json)可按年归档 + +--- + +## 11. 表清单总览(共 24 张) + +| # | 表名 | 作用 | 主要关联 | 通用字段 | +|---|------|------|----------|----------| +| 1 | sys_user | 登录账号与权限主体 | org_info ← org_id;→ staff_info | 完整 | +| 2 | sys_region | 行政区划树 | 自关联 parent_id;→ org_info / filing_* | 完整 | +| 3 | sys_dict | 字典类型 | → sys_dict_item | 完整 | +| 4 | sys_dict_item | 字典选项值 | sys_dict ← dict_id | 完整 | +| 5 | sys_attachment | 统一文件存储 | 逻辑关联各业务 biz_type+biz_id | 完整 | +| 6 | audit_record | 跨业务审核流水 | 逻辑关联各审核业务 | 仅 create | +| 7 | org_info | **机构租户根**、认证与主数据 | → 几乎全部 org_id 子表 | 完整 | +| 8 | org_certification_audit | 机构认证审核历史 | org_info ← org_id | 仅 create | +| 9 | org_qualification_cert | 机构资质证书 | org_info ← org_id;→ sys_attachment | 完整 | +| 10 | org_department | 部门组织树 | org_info ← org_id;→ org_position / staff_info | 完整 | +| 11 | org_position | 部门岗位 | org_department ← dept_id;→ staff_info | 完整 | +| 12 | staff_info | 人员主数据 | org_info / dept / position / sys_user;→ staff_* | 完整 | +| 13 | staff_certificate | 人员证书 | staff_info ← staff_id;→ filing_staff_cert 快照 | 完整 | +| 14 | staff_change_log | 人员变更审计 | staff_info ← staff_id | 仅 create | +| 15 | staff_resignation_apply | 离职申请与审核 | staff_info ← staff_id;→ sys_attachment | 完整 | +| 16 | equip_info | 仪器设备台账 | org_info ← org_id | 完整 | +| 17 | filing_application | **备案流程主表** | org_info / sys_region;→ filing_* 子表 / filing_record | 完整 | +| 18 | filing_application_material | 备案材料(步骤2) | filing_application ← application_id | 完整 | +| 19 | filing_application_staff | 备案人员快照(步骤3) | filing_application ← application_id;→ staff_cert 快照 | 完整 | +| 20 | filing_application_staff_cert | 备案人员证书快照 | filing_application_staff ← application_staff_id | 完整 | +| 21 | filing_commitment | 法人承诺书(步骤4) | filing_application ← application_id(1:1) | 完整 | +| 22 | filing_record | **已备案正式台账** | filing_application ← application_id;→ filing_change_record | 完整 | +| 23 | filing_change_record | 备案变更批次 | filing_record ← filing_record_id;→ change_detail | 完整 | +| 24 | filing_change_detail | 备案变更字段明细 | filing_change_record ← change_record_id | 仅 create | + +> 各表详细说明见 **§3 数据表作用与关系说明**;字段定义见 **§4–§8**。 + +--- + +## 12. DDL 脚本 + +完整建表见 [`database-schema.sql`](./database-schema.sql)。 + +--- + +## 13. 修订记录 + +| 版本 | 日期 | 说明 | +|------|------|------| +| v1.0 | 2026-06-17 | 初版 | +| v1.1 | 2026-06-17 | 对齐 MyBatis-Plus 通用字段;DATE/DATETIME 区分;新增 filing_application_staff_cert;附件统一 sys_attachment | +| v1.2 | 2026-06-17 | 补充每张表作用与关系说明(§3)、表清单关联列(§11)、字段节引用块 | diff --git a/docs/数据库规范.md b/docs/数据库规范.md new file mode 100644 index 0000000..7038d3d --- /dev/null +++ b/docs/数据库规范.md @@ -0,0 +1,197 @@ +# ***\*数据库规范\**** + +| 日期 | 修订版本 | 修订描述 | 编写人 | +| ---------- | --------- | -------- | ------ | +| 2024.05.23 | v01.00.00 | | | +| | | | | +| | | | | +| | | | | + +# **一、** ***\*安全规范\**** + +1. 【强制】禁止在数据库中存储明文密码,需把密码加密后存储 + +2. 【推荐】利用审计插件(如`mysqlaudit`)记录重要SQL的访问情况,对重要sql的访问频率或次数要做历史趋势监控,及时发现异常行为 + +3. 【推荐】线上连接数据库统一使用数据库服务器内网IP。外网链接使用的用户名、密码定期进行更换,并且统一交由@王朋管理 + +# ***\*二、基础规范\**** + +1. 【强制】库,表。字符型字段,mysql8:字符集要求:utf8mb4。排序规则:utf8mb4_0900_ai_ci。(为了兼容老业务系统框架 mysql5.7:字符集要求:utf8。排序规则:utf8_general_ci + +2. 【推荐】尽量不在数据库做运算,复杂运算需移到业务应用里完成 + +3. 【推荐】拒绝大sql语句、拒绝大事务、拒绝大批量,可转化到业务端完成 + + 说明:大批量操作可能会造成严重的主从延迟,binlog日志为row格式会产生大量的日志 + +4. 【推荐】避免使用存储过程、触发器、函数等,容易造成业务逻辑与DB耦合。 + + 说明:数据库擅长存储与索引、要解放数据库CPU,将计算转移到服务层、也具备更好的扩展性 + +5. 【强制】数据表、数据字段必须加入中文注释。说明:后续维护的人看到后才清楚表是干什么用的。 + +6. 【强制】不在数据库中存储图片、文件等大数据 说明:大文件和图片需要储在文件系统。 + +7. 【推荐】对于程序连接数据库账号,遵循权限最小原则 + +8. 【推荐】数据库设计时,需要问下自己是否对以后的扩展性进行了考虑。 + +9. 【推荐】数据库开启slow_query_log,并定期分析,进行优化。 + +10. 【推荐】如果数据量或数据增长在前期规划时就较大,那么在设计评审时就应加入分表策略。 + +11. 【推荐】要求所有研发SQL关键字全部是小写,每个词只允许有一个空格。 + +12. 【强制】定期自动执行数据库备份,包括全量备份和增量备份。 + +# ***\*三、命名规范\**** + +1. 【强制】mysql8:库名、表名、字段名要小写,下划线风格,不超过32个字符,(为了兼容老业务系统框架 mysql5.7:库名表名为小写+下划线,字段名为大写+下划线)必须见名知意,建议使用名词而不是动词,词义与业务、产品线等相关联,禁止拼音英文混用。 +2. 【强制】库名、表名、字段名禁止使用MySQL保留字 +3. 【强制】临时库表名必须以tmp为前缀,并以日期为后缀 +4. 【强制】备份库表必须以back_日期为后缀 +5. 【推荐】按日期时间分表需符合按年分表:表名_ YYYY 按月分表:表名_ YYYYMM格式 +8. 【强制】表达是与否概念的字段,禁止使用 is_xxx 的方式命名,一律使用xxx_flag的方式命名,数据类型是 tinyint(1 表示是,2 表示否,尽量不使用0) + +# ***\*四、库设计规范\**** + +1. 【推荐】数据库使用InnoDB存储引擎 + +2. 【强制】禁止使用外键,如果有外键完整性约束,需要应用程序控制 + +3. 【强制】每个Innodb 表必须有且只有一个主键。 + + 说明:多主键可能导致聚簇索引无法正确被使用。 + +4. 【推荐】单表列数目最好小于50 + +6. 【推荐】拆分大字段和访问频率低的字段,分离冷热数据 + +7. 【推荐】采用合适的分库分表策略,例如千库十表、十库百表等(建议表大小控制在2G) + +8. 【推荐】单表不超过50个int字段;不超过20个char字段,不超过2个text字段 + +9. 【推荐】日志类型的表可以考虑按创建时间水平切割,定期归档历史数据 + +10. 【强制】禁止使用order by rand() + + 说明:order by rand()会为表增加一个伪列,然后用rand()函数为每一行数据计算出rand()值,基于该行排序,这通常都会生成磁盘上的临时表,因此效率非常低。 + +11. 【参考】可以结合使用hash、range、lookup table进行散表 + +12. 【强制】业务主表中必须有creator,create_time,operator,operate_time,is_delete五个字段(垂直拆分的附表可以没有) + +13. 【强制】禁止在表中建立预留字段 + + 说明:预留字段的命名很难做到见名识义,预留字段无法确认存储的数据类型,所以无法选择合适的类型;对预留字段类型的修改,会对表进行锁定 + +# ***\*五\*******\*、\*******\*字段设计规范\**** + +1. 【强制】禁止使用小数存储国币、使用“分”作为单位,这样数据库里就是整数了 + +2. 【强制】用DECIMAL代替FLOAT和DOUBLE存储精确浮点数 + +3. 【强制】字段长度尽量按实际需要进行分配,不要随意分配一个很大的容量 + +4. 【推荐】适当增加冗余字段,减少JOIN + +5. 【强制】表示状态的字段,如is_delete、state等使用INT,禁止使用 VARCHAR + +6. 【参考】VARCHAR(N),N表示的是字符数不是字节数,比如VARCHAR(255),可以最大可存储255个汉字,需要根据实际的宽度来选择N + +7. 【参考】VARCHAR(N),N尽可能小,因为MySQL一个表中所有的VARCHAR字段最大长度是65535个字节,进行排序和创建临时表一类的内存操作时,会使用N的长度申请内存 + +8. 【推荐】VARCHAR(N),N>5000时,使用BLOB类型 + +9. 【推荐】使用短数据类型,比如取值范围为0~80时,使用TINYINT UNSIGNED + +10. 【强制】存储状态,性别等,用TINYINT + +11. 【强制】所有存储相同数据的列名和列类型必须一致(在多个表中的字段如user_id,它们类型必须一致) + +12. 【推荐】优先选择符合存储需要的最小数据类型 + +13. 【推荐】如果存储的字符串长度几乎相等,使用 char 定长字符串类型 + +# **六、索引设计规范** + +1. 【强制】索引命名必须遵循规范:普通索引使用`idx_字段名`,唯一索引使用`uk_字段名` + +2. 【推荐】核心SQL优先考虑覆盖索引 + +3. 【推荐】区分度高(重复值少)、频繁作为WHERE条件的字段应建立索引 + + 说明:区分度计算公式:count(distinct col)/count(col),区分度越高索引效果越好,一般认为超过0.1的字段适合建索引 + +4. 【参考】避免冗余和重复索引 + +5. 【推荐】研发要经常使用explain,如果发现索引选择性差,必须要学会使用hint + +6. 【推荐】能使用唯一索引就要使用唯一索引,提高查询效率 + +7. 【强制】了解索引失效场景,避免以下用法: + - 使用 `LIKE '%xxx'` 或 `LIKE '%xxx%'`(前缀模糊 `LIKE 'xxx%'` 可使用索引) + - 在索引列上进行计算、函数操作 + - 隐式类型转换(如字符串字段用数字查询) + - 使用 `OR` 连接条件(部分情况) + - 使用 `!=`、`<>`、`NOT IN`、`IS NULL`、`IS NOT NULL` + - 联合索引未遵循最左前缀原则 + +8. 【推荐】多条字段重复的语句,要修改语句条件字段的顺序,为其建立一条联合索引,减少索引数量 + +9. 【推荐】WHERE条件中的非等值条件(IN、BETWEEN、<、<=、>、>=)会导致后面的条件使用不了索引 + +10. 【参考】合理创建联合索引(避免冗余),如(a,b,c) 相当于 (a) 、(a,b) 、(a,b,c) + +11. 【推荐】复合索引中的字段数建议不超过5个 + +12. 【推荐】联合索引遵循最左前缀原则,将区分度高的字段放在前面 + + 说明:查询条件必须包含联合索引的第一列(最左列),索引才会生效 + +13. 【强制】禁止给表中的每一列都建立单独的索引 + + + +# ***\*七、SQL使用规范\**** + +1. 【强制】WHERE条件中必须使用合适的类型,避免MySQL进行隐式类型转化 + + 说明:因为MySQL进行隐式类型转化之后,可能会将索引字段类型转化成=号右边值的类型,导致使用不到索引,原因和避免在索引字段中使用函数是类似的,例子 select uid from t_user where phone=15855550101(phone为 varchar 类型,此时查询中使用数字查询,会导致索引失效) + +2. 【推荐】禁止在WHERE条件的属性上使用函数或者表达式 + +3. 【强制】应用程序必须捕获SQL异常,并有相应处理 + +4. 【推荐】sql语句尽可能简单、大的sql想办法拆成小的sql语句,减少锁表时间 + +5. 【推荐】事务要简单,整个事务的时间长度不要太长 + +6. 【强制】避免在数据库中进行数学运算或者函数运算(MySQL不擅长数学运算和逻辑判断,也容易将业务逻辑和DB耦合在一起) + +7. 【推荐】sql中使用到OR的改写为用IN() (or的效率没有in的效率高) + +8. 【推荐】尽量使用union all替代union + +9. 【参考】避免使用大表JOIN + +10. 【推荐】使用合理的SQL语句减少与数据库的交互次数 + +11. 【强制】禁止单条SQL语句同时更新多个表 + +12. 【推荐】获取大量数据时,建议分批次获取数据,每次获取数据少于2000条,结果集应小于1M + +13. 【强制】禁止跨库查询(为数据迁移和分库分表留出余地,降低耦合度,降低风险) + +14. 【推荐】尽量避免使用子查询,可以把子查询优化为join操作(子查询的结果集无法使用索引,子查询会产生临时表操作,如果子查询数据量大会影响效率,消耗过多的CPU及IO资源) + +15. 【推荐】SQL 性能优化的目标:至少要达到 range 级别,要求是 ref 级别,如果可以是 consts最好 + +16. 【强制】不要使用物理删除。0表示未删除,1表示已删除 + +17. 【强制】在代码中写分页查询逻辑时,若 count 为 0 应直接返回,避免执行后面的分页语句 + +18. 【推荐】使用 ISNULL()来判断是否为 NULL 值 + + \ No newline at end of file