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 0000000..6418ca7 --- /dev/null +++ b/docs/GBS前端开发规范.docx @@ -0,0 +1,778 @@ + + GBS前端开发规范 + + 项目初始化 + +空白模板git:http://47.92.113.182:3000/front_library/blank-react-template.git +项目拉取下来,根据后端项目名称修改项目名,在后面添加 -react +修改 jjb.config.js 文件 + appIdentifier 和后端 gateway 相同 + environment.development.API_HOST 开发环境请求后端的IP + contextInject.fileUrl 附件服务器IP +项目启动运行 serve:development +项目打包运行 build:production + + 注意事项 + +antd最新版是6.0,不要使用6.0的,使用5.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 尽量避免使用 resetFields 清空表单,有一些自定义组件里是包含请求的,自定义组件重新 mount,会导致重新发送请求。 + 跳转页面的表单不需要调用 resetFields 清空表单,弹窗里的可以在使用弹窗的时候使用三元运算符决定是否显示可以避免使用 resetFields 清空表单。 +定期删除一下node_modules下的.cache文件夹,不然会生成几十个G的缓存文件,把硬盘占满 + + 字段重命名 + +import { Interpolation } from '@cqsjjb/jjb-common-decorator/namespace'; + props.insert('XXX') +权限控制 +import {Permission} from "@cqsjjb/jjb-common-decorator/permission"; + props.permission('XXX') +Map组件使用 +需要在main.js注入window.mapLongitude = ""; window.mapLatitude = "";必选 +type为cesium的时候window.mapObliquePhotography = [];可选 +type为cesium的时候在html添加必选 +type为baidu的时候window.mapBaiDuKey = "";必选 +港务局: +window.mapLongitude = "119.69457721306945"; + +window.mapLatitude = "39.940504336846665"; + +window.mapBaiDuKey = "OElqFYoKiAH8KFtph8ftLKF5NlNrbCUr"; + +window.mapObliquePhotography = [ + + "http://192.168.192.215:8021/ware/upload/%E6%9B%B9%E5%A6%83%E7%94%B8%E6%B8%AF%E4%B8%9C/%E6%9B%B9%E5%A6%83%E7%94%B8%E6%B8%AF%E4%B8%9C/merge_tile.json", + + "http://192.168.192.215:8021/ware/upload/%E6%9B%B9%E5%A6%83%E7%94%B8%E6%B8%AF%E8%A5%BF/%E6%9B%B9%E5%A6%83%E7%94%B8%E6%B8%AF%E8%A5%BF/merge_tile.json", + + "http://192.168.192.215:8021/ware/upload/qhdxys/merge_tile.json", + + "http://192.168.192.215:8021/ware/upload/qhdgysh/merge_tile.json", + + "http://192.168.192.215:8021/ware/upload/%E6%B2%A7%E5%B7%9E%E6%B8%AF%E8%A5%BF/%E6%B2%A7%E5%B7%9E%E6%B8%AF%E8%A5%BF/merge_tile.json", + + "http://192.168.192.215:8021/ware/upload/%E6%B2%A7%E5%B7%9E%E6%B8%AF%E4%B8%9C/%E6%B2%A7%E5%B7%9E%E6%B8%AF%E4%B8%9C/merge_tile.json", + +]; + + 打印 + +安装 react-to-print 包 +import { useReactToPrint } from "react-to-print"; + +const contentRef = useRef(null); + +const handlePrint = useReactToPrint({ + + contentRef, + + pageStyle: `@page { + + size: landscape; // 横屏打印 + + margin: 0mm; + + } + + @media print { + + body { + + margin: 10px; + + padding: 10px; + + } + + } + + `, + + documentTitle: "", + + }); + + + {/* 页面展示使用 */} + + + + {/* 打印使用 */} + +
+ +
+ + + + + + + + + + + + + + {(props.data || []).map(item => ( + + + + + + + + ))} + + + +
xxx
{item.xxx}
+ + + +
+.print-table { + + display: none; + + border-collapse: collapse; + + width: 100%; + + font-size: 14px; + + + + td, th { + + border: 1px solid #eaeaea; + + padding: 8px; + + line-height: 1.6; + + text-align: center; + + } + + + + @media print { + + display: table; + + } + +} + + zy-react-library 组件库文档 + + + + + + + + + + +修订者 + + + +版本号 + + + +说明 + + + + + +刘佳楠 + + + +2025-10-22 V1.0.0 + + + +初始版本 + + + + + +刘佳楠 + + + +2025-11-3 V1.0.37 + + + +新增文件上传删除获取hooks + + + + + +刘佳楠 + + + +2025-11-6 V1.0.54 + + + +新增Cascader、LeftTree、Select、SelectTree系列组件 + + + + + +刘佳楠 + + + +2025-11-8 V1.0.70 + + + +新增数据字典系列组件和hooks + + + + + +刘佳楠 + + + +2025-11-15 V1.0.98 + + + +新增Signature签字组件 + + + + + +刘佳楠 + + + +2025-12-01 V1.0.131 + + + +新增Video视频播放组件 + + + + + +刘佳楠 + + + +2025-12-02 V1.0.135 + + + +FormBuilder表单渲染器增加对Form.List的支持 + + + + + +刘佳楠 + + + +2025-12-06 V1.0.141 + + + +新增Editor富文本编辑器组件 + + + + + +刘佳楠 + + + +2025-12-12 V1.0.159 + + + +新增useIdle + + + + + +刘佳楠 + + + +2025-12-24 V1.1.0 + + + +新增Page布局组件 + + + + + +刘佳楠 + + + +2025-12-31 V1.1.13 + + + +新增SeamlessScroll无缝滚动组件 + + + + + +刘佳楠 + + + +2026-01-05 V1.1.19 + + + +Map组件增加cesium地图选项 + + + + + +刘佳楠 + + + +2026-04-27 V1.3.4 + + + +FormBuilder表单渲染器修复Form.List嵌套Form.List、dependencies、shouldUpdate不生效的问题,补全TS类型 + + 📦 安装 + +# 使用 yarn 安装 + +yarn add zy-react-library + + + 📁 项目结构 + +zy-react-library/ + +├── components/ # 组件目录 + +│ ├── Cascader/ # 级联选择器组件 + +│ │ ├── Area/ # 区域 + +│ │ ├── Basic/ # 基础(只实现通用功能) + +│ │ ├── Dictionary/ # 字典 + +│ │ └── Industry/ # 行业类型 + +│ ├── Editor/ # 富文本编辑器组件 + +│ ├── FormBuilder/ # 表单构建器组件 + +│ ├── HeaderBack/ # 头部返回组件 + +│ ├── HiddenInfo/ # 隐患查看组件 + +│ ├── Icon/ # 图标组件集合 + +│ │ ├── AddIcon/ + +│ │ ├── BackIcon/ + +│ │ ├── DeleteIcon/ + +│ │ ├── DownloadIcon/ + +│ │ ├── EditIcon/ + +│ │ ├── ExportIcon/ + +│ │ ├── ImportIcon/ + +│ │ ├── LocationIcon/ + +│ │ ├── PrintIcon/ + +│ │ ├── ResetIcon/ + +│ │ ├── SearchIcon/ + +│ │ ├── VideoIcon/ + +│ │ └── ViewIcon/ + +│ ├── ImportFile/ # 文件导入组件 + +│ ├── LeftTree/ # 左侧树形菜单组件 + +│ │ ├── Area/ # 区域 + +│ │ ├── Basic/ # 基础(只实现通用功能) + +│ │ ├── Department/ # 部门 + +│ │ └── Dictionary/ # 字典 + +│ ├── Map/ # 地图相关组件 + +│ ├── Page/ # 布局组件 + +│ ├── Pdf/ # PDF处理组件 + +│ ├── PreviewImg/ # 图片预览组件 + +│ ├── PreviewPdf/ # PDF预览组件 + +│ ├── SeamlessScroll/ # 无缝滚动组件 + +│ ├── Search/ # 搜索组件 + +│ ├── Select/ # 选择器组件集合 + +│ │ ├── Basic/ # 基础(只实现通用功能) + +│ │ ├── Dictionary/ # 字典 + +│ │ └── Personnel/ # 人员 + +│ ├── SelectCreate/ # 创建选择器组件 + +│ ├── SelectTree/ # 树形选择器组件 + +│ │ ├── Area/ # 区域 + +│ │ ├── Basic/ # 基础(只实现通用功能) + +│ │ ├── Department/ # 部门 + +│ │ ├── Dictionary/ # 字典 + +│ │ ├── HiddenLevel/ # 隐患级别 + +│ │ ├── HiddenPart/ # 隐患部位 + +│ │ └── Industry/ # 行业类型 + +│ ├── Signature/ # 签字组件 + +│ ├── Table/ # 表格组件 + +│ ├── TooltipPreviewImg/# 图片提示预览组件 + +│ ├── Upload/ # 上传组件 + +│ └── Video/ # 视频播放组件 + +├── enum/ # 枚举定义目录 + +│ ├── dictionary/ # 字典的AppKey + +│ ├── formItemRender/ # FormBuilder的枚举 + +│ ├── hidden/ # 隐患相关 + +│ └── uploadFile/ # 上传hooks的枚举 + +├── hooks/ # 自定义Hooks目录 + +│ ├── useDeleteFile/ # 删除文件Hook + +│ ├── useDictionary/ # 字典Hook + +│ ├── useDownloadBlob/ # 下载Blob文件Hook + +│ ├── useDownloadFile/ # 下载文件Hook + +│ ├── useGetFile/ # 获取文件Hook + +│ ├── useGetUrlQuery/ # 获取URL查询参数Hook + +│ ├── useGetUserInfo/ # 获取用户信息Hook + +│ ├── useIdle/ # 检测当前用户是否处于活跃状态 + +│ ├── useImportFile/ # 导入文件Hook + +│ ├── useIsExistenceDuplicateSelection/ # 检查重复选择Hook + +│ ├── useTable/ # 表格Hook + +│ ├── useUploadFile/ # 上传文件Hook + +│ └── useUrlQueryCriteria/ # 操作搜索条件缓存Hook + +├── json/ # JSON数据文件目录 + +│ ├── area.json # 区域 + +│ ├── industry.json # 行业类型 + +│ └── nation.json # 民族 + +├── regular/ # 正则表达式目录 + +└── utils/ # 工具函数目录 + + 🧩 组件详情 + + + Cascader 级联选择器 + +提供多种类型的级联选择器: +Area: 地区级联选择器 +Basic: 基础级联选择器(只实现通用功能) +Dictionary: 字典级联选择器 +Industry: 行业类型级联选择器 + + Editor + +富文本编辑器组件 + + FormBuilder 表单构建器 + +动态表单构建组件,可根据配置动态生成表单。 + + HeaderBack 头部返回 + +页面返回导航组件,用于页面头部返回操作。 + + Icon 图标组件 + +包含多种常用图标组件: +AddIcon: 添加图标 +BackIcon: 返回图标 +DeleteIcon: 删除图标 +DownloadIcon: 下载图标 +EditIcon: 编辑图标 +ExportIcon: 导出图标 +ImportIcon: 导入图标 +LocationIcon:位置图标 +PrintIcon: 打印图标 +ResetIcon: 重置图标 +SearchIcon: 搜索图标 +VideoIcon: 视频图标 +ViewIcon: 查看图标 + + ImportFile 文件导入 + +文件导入功能组件,支持多种文件格式导入。 + + LeftTree 左侧树形菜单 + +提供多种类型的树形菜单: +Area: 地区树形菜单 +Basic: 基础树形菜单(只实现通用功能) +Department: 部门树形菜单 +Dictionary: 字典树形菜单 + + Map 地图组件 + +地图相关组件,用于地图展示和操作。 + + Page 布局组件 + +布局组件,所有页面的根节点,包括了头部的返回和底部的返回。 + + Pdf 组件 + +PDF处理组件,支持PDF文件的展示和操作。 + + PreviewImg 图片预览 + +图片预览组件,支持图片的预览功能。 + + PreviewPdf PDF预览 + +PDF预览组件,支持PDF文件的预览功能。 + + SeamlessScroll 无缝滚动 + +无缝滚动组件,大屏展示中列表展示使用。 + + Search 搜索 + +搜索组件,提供搜索功能。 + + Select 选择器 + +提供多种类型的选择器: +Basic: 基础选择器(只实现通用功能) +Dictionary: 字典选择器 +Personnel: 人员选择器 + + SelectCreate 创建选择器 + +创建选择器组件,支持创建新选项。 + + SelectTree 树形选择器 + +提供多种类型的树形选择器: +Area: 地区树形选择器 +Basic: 基础树形选择器(只实现通用功能) +Department: 部门树形选择器 +Dictionary: 字典树形选择器 +HiddenLevel: 隐患等级树形选择器 +HiddenPart: 隐患部位树形选择器 +Industry: 行业类型树形选择器 + + Signature 签字 + +签字组件 + + Table 表格 + +表格组件,支持数据展示和操作。 + + TooltipPreviewImg 图片提示预览 + +图片提示预览组件,鼠标悬停时预览图片。 + + Upload 上传 + +上传组件,支持文件上传功能。 + + Video 视频播放 + +视频播放组件,使用Aliplayer实现。 + + 🔗 Hooks详情 + + + useDeleteFile 删除文件Hook + +用于删除文件的自定义Hook。 + + useDictionary 字典Hook + +用于获取字典数据的自定义Hook。 + + useDownloadBlob 下载Blob文件Hook + +用于下载Blob文件的自定义Hook。 + + useDownloadFile 下载文件Hook + +用于下载文件的自定义Hook。 + + useGetFile 获取文件Hook + +用于获取文件的自定义Hook。 + + useGetUrlQuery 获取URL查询参数Hook + +用于获取URL查询参数的自定义Hook。 + + useIdle + +检测当前用户是否处于活跃状态。 + + useGetUserInfo 获取用户信息Hook + +用于获取用户信息的自定义Hook。 + + useImportFile 导入文件Hook + +用于导入文件的自定义Hook。 + + useIsExistenceDuplicateSelection 检查重复选择Hook + +用于检查是否存在重复选择的自定义Hook。 + + useTable 表格Hook + +用于表格操作的自定义Hook。 + + useUploadFile 上传文件Hook + +用于上传文件的自定义Hook。 + + useUrlQueryCriteria 操作搜索条件缓存Hook + +用于操作搜索条件缓存的自定义Hook。 + diff --git a/docs/JAVA规范.md b/docs/JAVA规范.md new file mode 100644 index 0000000..afd8658 --- /dev/null +++ b/docs/JAVA规范.md @@ -0,0 +1,365 @@ +# ***\*卓云科技JAVA规范\******* + +| 日期 | 修订版本 | 修订描述 | 编写人 | +| ---------- | --------- | -------- | ------ | +| 2024.05.23 | v01.00.00 | | | +| | | | | +| | | | | +| | | | | + +# **一、** ***\*命名风格\**** + +1. 【强制】代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束。 + +2. 【强制】代码中的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式。 + + ​ 说明:正确的英文拼写和语法可以让阅读者易于理解,避免歧义。注意,即使纯拼音命名方式 也要避免采用。 alibaba / taobao / youku / hangzhou 等国际通用的名称,可视同英文。 + +3. 【强制】类名使用 UpperCamelCase 风格,但以下情形例外:DO / BO / DTO / VO / AO / PO / UID 等。 + +4. 【强制】方法名、参数名、成员变量、局部变量都统一使用 lowerCamelCase 风格,必须遵从 驼峰形式。 + +5. 【强制】常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。 + +6. 【强制】抽象类命名使用 Abstract 或 Base 开头;异常类命名使用 Exception 结尾;测试类 命名以它要测试的类的名称开始,以 Test 结尾。 + +7. 【强制】包名统一使用小写。 + +8. 【强制】为了达到代码自解释的目标,任何自定义编程元素在命名时,使用尽量完整的单词 组合来表达其意。**禁止使用a, b, c这种随意的命名方式** + +9. 【推荐】如果模块、接口、类、方法使用了设计模式,在命名时需体现出具体模式。 + +10. 【推荐】接口类中的方法和属性不要加任何修饰符号(public 也不要加),保持代码的简洁 性,并加上有效的 Javadoc 注释。尽量不要在接口里定义变量,如果一定要定义变量,肯定是 与接口方法相关,并且是整个应用的基础常量。 + + ​ 说明:JDK8 中接口允许有默认实现,那么这个 default 方法,是对所有实现类都有价值的默 认实现。 + +11. 接口和实现类的命名有两套规则: + + 1. 【强制】对于 Service 和 DAO 类,基于 SOA 的理念,暴露出来的服务一定是接口,内部 的实现类用 Impl 的后缀与接口区别。 + + ​ 正例:CacheServiceImpl 实现 CacheService 接口。 + + 2. 【推荐】如果是形容能力的接口名称,取对应的形容词为接口名(通常是–able 的形式)。 + + ​ 正例:AbstractTranslator 实现 Translatable 接口。 + +12. 【参考】枚举类名建议带上 Enum 后缀,枚举成员名称需要全大写,单词间用下划线隔开 +13. + +# ***\*二、常量定义 \**** + +1. 【强制】在 long 或者 Long 赋值时,数值后使用大写的 L,不能是小写的 l,小写容易跟数字 1 混淆,造成误解。 +2. 【推荐】不要使用一个常量类维护所有常量,要按常量功能进行归类,分开维护。 + +# ***\*三、代码格式\**** + +1. 【强制】大括号的使用约定。如果是大括号内为空,则简洁地写成{}即可,不需要换行;如果 是非空代码块则: 1) 左大括号前不换行。 2) 左大括号后换行。 3) 右大括号前换行。 4) 右大括号后还有 else 等代码则不换行;表示终止的右大括号后必须换行。 + +2. 【强制】左小括号和字符之间不出现空格;同样,右小括号和字符之间也不出现空格;而左大 括号前需要空格。 + + ​ 反例:if (空格 a == b 空格) + +3. 【强制】任何二目、三目运算符的左右两边都需要加一个空格。 + + ​ 说明:运算符包括赋值运算符=、逻辑运算符&&、加减乘除符号等。 + +4. 【强制】单行代码超出屏幕前必须换行,编辑器禁止出现左右滚动条 + +5. 【强制】方法参数在定义和传入时,多个参数逗号后边必须加空格。 + +6. 【强制】IDE 的 text file encoding 设置为 UTF-8; IDE 中文件的换行符使用 Unix 格式, 不要使用 Windows 格式。 + +7. 【推荐】单个方法的总行数不要过长。 + +# ***\*四、卓云科技OOP规约\**** + +1. 【强制】避免通过一个类的对象引用访问此类的静态变量或静态方法,无谓增加编译器解析成 本,直接用类名来访问即可。 +2. 【强制】所有的覆写方法,必须加@Override 注解 +3. 【强制】Object 的 equals 方法容易抛空指针异常,应使用常量或确定有值的对象来调用 equals。 +4. 【强制】所有的相同类型的包装类对象之间值的比较,全部使用 equals 方法比较。 +5. 【强制】所有的 POJO 类属性必须使用包装数据类型。 +6. 【强制】构造方法里面禁止加入任何业务逻辑,如果有初始化逻辑,请放在 init 方法中。 +7. 【推荐】当一个类有多个构造方法,或者多个同名方法,这些方法应该按顺序放置在一起, 便于阅读 +8. 【推荐】循环体内,字符串的连接方式,使用 StringBuilder 的 append 方法进行扩展 + +# ***\*五、注释规约\**** + +1. 【强制】类、类属性、类方法的注释必须使用 Javadoc 规范,使用/**内容*/格式,不得使用 // xxx 方式。 +2. 【强制】所有的抽象方法(包括接口中的方法)必须要用 Javadoc 注释、除了返回值、参数、 异常说明外,还必须指出该方法做什么事情,实现什么功能。 +3. 【强制】方法内部单行注释,在被注释语句上方另起一行,使用//注释。方法内部多行注释 使用/* */注释,注意与代码对齐。 +4. 【强制】所有的枚举类型字段必须要有注释,说明每个数据项的用途 +5. 【推荐】代码修改的同时,注释也要进行相应的修改,尤其是参数、返回值、异常、核心逻辑 等的修改。 +6. 【推荐】谨慎注释掉代码。在上方详细说明,而不是简单地注释掉。如果无用,则删除。 + +# ***\*六、集合处理\**** + +1. 【强制】初始化集合时,指定集合初始值大小。 + ​ 说明:HashMap 使用如下构造方法,初始值大小为 (需要存储的元素个数 / 负载因子) + 1,负载因子默认为 0.75。 + +2. 【强制】使用 entrySet 遍历 Map 类集合 KV,而不是 keySet 方式进行遍历。 + ​ 说明:keySet 其实是遍历了 2 次,一次是转为 Iterator 对象,另一次是从 HashMap 中取出 key 所对应的 value。而 entrySet 只是遍历了一次就把 key 和 value 都放到了 entry 中,性能更优。 + +3. 【强制】使用 Collections.isEmpty() 检测空,而非 size() == 0。 + ​ 说明:某些集合类可能存在 size() 方法时间复杂度高的情况。 + +4. 【强制】不要在 foreach 循环里进行元素的 remove/add 操作。remove 元素请使用 Iterator 方式,如果并发操作,需要对 Iterator 对象加锁。 + ​ 正例: + ```java + Iterator 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