Ling3编码规范
来自ling
目录
todo
model如何设计不被依赖的项目初始化表结构(baseModel在core?defaultModel在service?)参考Ling2编码规范#继承关系web项目使用的死nodejs调试,如何和当前的restful项目组合参考Ling3编码规范#app调试最佳实践- ling-cloud-xxx-web项目如何打包为native移动端项目
core中存放自动代码的interface还是手动整理后的interface已经由restful风格取代了interface,不再有interface的编码风格- ling-cloud-xxx-parent存在的必要性,是否可以用ling-cloud-xxx-core替代
- 是否需要ling-cloud-web 所有web项目的parent,不依赖ling-cloud-parent
- 最佳前端模板.约定优于配置,自动生成大于手工代码
集成react-antd-admin,因为导入导出的时候需要一个json格式的定义.直接使用antd控件虽然增加了可定制化灵活性,但从长远来说是个问题.可以由DBTable解决- 关于灵活的可定制化,需要引入Dorado对控件的定义和方法的定义
- DBTable 行渲染
- ag-grid 使用
- 多客户多版本,个性化支持
- antd批量更新
maven项目结构和命名
- 命名规范参考spring boot maven结构
- 将配置写入配置文件中,不再用maven的profile来适配不同的环境.yml方式参考application.yml
- 达到一个包多个环境都能运行的目的,满足测试即发布的目的
- 更多可以参考maven profiles
- 可以在pom.xml的properties节点中配置全局性质属性,然后在配置文件中引用,例如
eureka.instance.hostname=@eureka.instance.hostname@
maven项目分为以下几种类型
pomrepository
<repositories>
<repository>
<id>oschinaRepository</id>
<name>local private nexus</name>
<url>http://maven.oschina.net/content/groups/public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>ling2</id>
<name>ling2 nexus</name>
<url>http://nexus.ling2.cn/repository/maven-public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>bsdn</id>
<name>bsdn nexus</name>
<url>http://nexus.bsdn.org/content/groups/public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<!-- 官方maven仓库 -->
<!-- <repository> <id>maven</id> <name>Maven Repository Switchboard</name> <layout>default</layout> <url>http://repo1.maven.org/maven2</url> <snapshots> <enabled>true</enabled> </snapshots> </repository> -->
</repositories>
<!-- 指定maven plugin仓库 -->
<distributionManagement>
<repository>
<id>ling2-releases</id>
<name>Nexus Release Repository</name>
<url>http://nexus.ling2.cn/repository/maven-releases</url>
</repository>
<snapshotRepository>
<id>ling2-snapshots</id>
<name>Nexus Snapshot Repository</name>
<url>http://nexus.ling2.cn/repository/maven-snapshots/</url>
</snapshotRepository>
</distributionManagement>
<!-- 指定maven plugin仓库 -->
<pluginRepositories>
<!-- oschina的maven plugin仓库 -->
<pluginRepository>
<id>oschinaPluginRepository</id>
<name>local private nexus</name>
<url>http://maven.oschina.net/content/groups/public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
pom顺序
-->parent-->modules-->properties-->profiles-->dependencies-->dependencyManagement-->build-->distributionManagement-->repositories
所有需要jar包运行的添加
<build>
<plugins>
<!--run able jar-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
<goal>build-info</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
基础组件
为云服务所需的基础组件
bootstrap.properties
#eureka
eureka.instance.hostname=@eureka.instance.hostname@
eureka.instance.perferIpAddress=@eureka.instance.perferIpAddress@
eureka.client.serviceUrl.defaultZone=@eureka.client.serviceUrl.defaultZone@
#CLOUD-CONFIG
spring.profiles.active=routing
spring.cloud.config.discovery.enabled=true
spring.cloud.config.discovery.service-id=LING-CLOUD-CONFIG
spring.cloud.config.fail-fast=true
spring.cloud.config.retry.initial-interval=10000
spring.cloud.config.retry.multiplier=2
spring.cloud.config.retry.max-interval=60000
spring.cloud.config.retry.max-attempts=10
application.properties
项目中不再需要application.properties,并需要将spring.application.name移动到bootstrap中
server.port=6870
spring.application.name=LING-CLOUD-ADMIN
spring.profiles.active=insecure
#cloud-admin
eureka.instance.leaseRenewalIntervalInSeconds=@eureka.instance.leaseRenewalIntervalInSeconds@
eureka.instance.health-check-url-path=@eureka.instance.health-check-url-path@
eureka.client.registryFetchIntervalSeconds=@eureka.client.registryFetchIntervalSeconds@
management.endpoint.health.show-details=@management.endpoint.health.show-details@
#management.endpoints.web.exposure.include=info,health,env,beans
management.endpoints.web.exposure.include=@management.endpoints.web.exposure.include@
#management.endpoints.web.exposure.exclude=@management.endpoints.web.exposure.exclude@
ling-cloud-dependencies
顶层项目,定义的ling-cloud需要的基本maven依赖版本,docker打包设置和编译设置等内容Ling-cloud-dependencies.pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.ling</groupId>
<artifactId>ling-cloud-dependencies</artifactId>
<version>ling2-SNAPSHOT</version>
<packaging>pom</packaging>
<name>ling-cloud-dependencies</name>
<description>ling-cloud-dependencies</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<javadoc.version>3.0.0</javadoc.version>
<spring-boot.version>2.1.1.RELEASE</spring-boot.version>
<spring-cloud.version>Finchley.SR2</spring-cloud.version>
<!--<spring-cloud.version>Finchley.RELEASE</spring-cloud.version>-->
<boot.admin.version>2.1.1</boot.admin.version>
<swagger2.version>2.8.0</swagger2.version>
<hutool.version>3.3.2</hutool.version>
<ling.cloud.version>ling2-SNAPSHOT</ling.cloud.version>
<ling.cloud.core.version>ling2-SNAPSHOT</ling.cloud.core.version>
<!-- 镜像前缀,推送镜像到远程库时需要,这里配置了一个阿里云的私有库 -->
<docker.image.prefix>registry.cn-hangzhou.aliyuncs.com/ling</docker.image.prefix>
<docker.tag>latest</docker.tag>
<!--cloud admin-->
<eureka.instance.leaseRenewalIntervalInSeconds>30</eureka.instance.leaseRenewalIntervalInSeconds>
<eureka.instance.health-check-url-path>/actuator/health</eureka.instance.health-check-url-path>
<eureka.client.registryFetchIntervalSeconds>30</eureka.client.registryFetchIntervalSeconds>
<management.endpoint.health.show-details>always</management.endpoint.health.show-details>
<management.endpoints.web.exposure.include>*</management.endpoints.web.exposure.include>
<management.endpoints.web.exposure.exclude>*</management.endpoints.web.exposure.exclude>
<!--log-->
<log.fileRoot>c:/logs</log.fileRoot>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<javadoc.version>3.0.0</javadoc.version>
<ling.cloud.core.version>ling2-SNAPSHOT</ling.cloud.core.version>
<spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
<boot.admin.version>2.0.1</boot.admin.version>
<!--from core begin-->
<!--Lombok-->
<lombok.version>1.16.20</lombok.version>
<!--4.2.2.Final 5.0.9.Final 5.2.2.Final --><!--当前boot-jpa用的5.0.9 -->
<org.hibernate.version>5.0.9.Final</org.hibernate.version>
<jjwt.version>0.9.0</jjwt.version>
<fastjson.version>1.2.46</fastjson.version>
<hutool.version>3.3.2</hutool.version>
<commons-io.version>2.6</commons-io.version>
<jasypt.version>1.18</jasypt.version>
<mybatis-plus.version>2.1.9</mybatis-plus.version>
<mybatisplus-spring-boot-starter.version>1.0.5</mybatisplus-spring-boot-starter.version>
<hibernate-validator.version>5.4.1.Final</hibernate-validator.version>
<swagger2.version>2.8.0</swagger2.version>
<fastdfs.version>0.2.0</fastdfs.version>
<ttl.version>2.2.0</ttl.version>
<sharding-jdbc-core-spring-boot-starter.version>2.0.1</sharding-jdbc-core-spring-boot-starter.version>
<velocity-engine-core.version>2.0</velocity-engine-core.version>
<qiniu.version>[7.2.0, 7.2.99]</qiniu.version>
<kaptcha.version>0.0.9</kaptcha.version>
<javadoc.version>3.0.0</javadoc.version>
<spring-boot-admin-server.version>1.5.6</spring-boot-admin-server.version>
<spring-boot-admin-server-ui.version>1.5.6</spring-boot-admin-server-ui.version>
<spring-boot-admin-server-ui-turbine.version>1.5.6</spring-boot-admin-server-ui-turbine.version>
<!--from core end-->
</properties>
<dependencyManagement>
<dependencies>
<!--spring cloud and boot-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--cloud admin-->
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
<version>${boot.admin.version}</version>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-server</artifactId>
<version>${boot.admin.version}</version>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
<version>${boot.admin.version}</version>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-server-ui</artifactId>
<version>${boot.admin.version}</version>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-server-ui</artifactId>
<version>${boot.admin.version}</version>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-server-ui-login</artifactId>
<version>${boot.admin.version}</version>
</dependency>
<dependency>
<groupId>com.marcosbarbero.cloud</groupId>
<artifactId>spring-cloud-zuul-ratelimit</artifactId>
<version>1.5.0.RELEASE</version>
</dependency>
<!--ling cloud-->
<dependency>
<groupId>com.ling</groupId>
<artifactId>ling-cloud-core</artifactId>
<version>${ling.cloud.core.version}</version>
</dependency>
<dependency>
<groupId>com.ling</groupId>
<artifactId>ling-cloud-parent</artifactId>
<version>ling2-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.ling</groupId>
<artifactId>ling-cloud-authorization</artifactId>
<version>ling2-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.ling</groupId>
<artifactId>ling-cloud-gateway</artifactId>
<version>ling2-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.ling</groupId>
<artifactId>ling-cloud-config</artifactId>
<version>ling2-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.ling</groupId>
<artifactId>ling-cloud-dependencies</artifactId>
<version>ling2-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.ling</groupId>
<artifactId>ling-cloud-register</artifactId>
<version>ling2-SNAPSHOT</version>
</dependency>
<!--swagger ui-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger2.version}</version>
</dependency>
<!--other-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.39</version>
<scope>runtime</scope>
</dependency>
</dependencies>
</dependencyManagement>
<distributionManagement>
<repository>
<id>ling-releases</id>
<name>ling-releases</name>
<url>http://nexus.ling2.cn/repository/ling-releases/</url>
</repository>
<snapshotRepository>
<id>ling-snapshots</id>
<name>ling-snapshots</name>
<url>http://nexus.ling2.cn/repository/ling-snapshots/</url>
</snapshotRepository>
</distributionManagement>
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>aliyunRepository</id>
<name>local private nexus</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>bsdn-maven-repository</id>
<url>http://nexus.bsdn.org/content/groups/public/</url>
</repository>
<repository>
<id>ling-maven-repository</id>
<url>http://nexus.ling2.cn/repository/maven-public/</url>
</repository>
<repository>
<id>sonatype-nexus-staging</id>
<url>https://oss.sonatype.org/content/groups/public/</url>
</repository>
</repositories>
</project>
ling-cloud-register 服务发现于注册中心
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka-server</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
ling-cloud-config 全局配置中心,当前为基于文件形式的实现
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
ling-cloud-routing 全局路由中心
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zuul</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
ling-cloud-admin
<dependency> <groupId>de.codecentric</groupId> <artifactId>spring-boot-admin-starter-server</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.jolokia</groupId> <artifactId>jolokia-core</artifactId> </dependency>
功能组件
提供接口或restful,承载一定业务的组件
- ling-cloud-pl 基础模块
- ling-cloud-pl-core
- parent为ling-cloud-pl
- 包含了一些公共的constant,utls
- 同时承担ling-cloud-pl-api所承载的内容
- 基础的功能接口,比如消息队列,任务调度,数据库操作规范,大数据,权限等,可以拆分为只模块,使用时按需使用
- ling-cloud-pl-service 为ling-cloud-pl-core中定义的接口提供集群式的restful实现 parent为ling-cloud-pl
ling-cloud-pl-web 提供管理界面 parent为ling-cloud-web
- ling-cloud-pl-core
功能组件命名规范
- 模块划分
- ling-cloud-xxx
- 供其他模块依赖
- parent是ling-cloud-parentce
- ling-cloud-xxx-api
- parent是ling-cloud-xxx-parent
- 定义模块相关功能的utils,interface,vo
- ling-cloud-xxx-biz
- parent是ling-cloud-xxx-parent
- 实现了模块相关功能的interface
- 以restful形式提供集群式服务
- 不可被其他项目依赖
- resources\META-INF\spring.factories中设置启动的config XXXAutoConfiguration,通过@Import引用XXXConfiguration
- com.ling.xxx.config中设置XXXConfiguration,表示此包下的内容被扫描
com.ling.starter下XXXServiceApplication,在starter下是避免扫描到了不同的模块内容(SpringBootApplication默认会扫描自身包及其子包下的spring,jpa内容,但@Configuration的类不会被扫描到,需要在SpringBootApplication中通过@Import)- 必须在@SpringCloudApplication使用@ComponentScan(basePackages = {"com.ling.pl.gateway", "com.ling.pl..."})标明扫描的包
- @Configuration 因为@ConditionalOnMissingBean(IGroupService.class)在类上标注无效,所以覆盖需要使用@Primary来覆盖
- 引用的cloud jar spring-cloud-starter-config,spring-cloud-starter-eureka,spring-boot-starter-actuator
- ling-cloud-xxx
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
- ling-cloud-xxx-app
- 提供管理界面
- 不依赖ling-cloud-xxx-core等java项目,可以理解为纯静态项目
- nodejs项目,编译output路径为ling-cloud-xxx-web\src\main\resources\static\xxx
- ling-cloud-xxx-web
- ling-cloud-xxx-app
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.bstek.dorado</groupId> <artifactId>dorado-core</artifactId> </dependency> <dependency> <groupId>com.bstek.bdf3</groupId> <artifactId>bdf3-autoconfigure</artifactId> </dependency> <dependency> <groupId>com.bstek.bdf3</groupId> <artifactId>bdf3-dorado</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency>
- ling-cloud-xxx-app项目编译结果输出项目,用于供其他模块依赖后组成项目的一部分
- 如果是dorado 版本web,添加provided类型的cloudo用于编辑
<dependency> <groupId>com.ling</groupId> <artifactId>ling-cloud-pl-dorado7-cloudo</artifactId> <scope>provided</scope> </dependency>
restful数据结构和命名
- 更多参考RESTful API 设计指南
模块restful前缀和routing命名规范如下
zuul.routes.base-api.path=/xxx-api/** zuul.routes.base-api.serviceId=LING-CLOUD-XXX-SERVICE zuul.routes.base-api.stripPrefix=true
restful数据格式规范
返回数据格式为ResposeInfo 服务异常捕获由GlobalExceptionHandler完成 正常请求返回封装由ResponseBodyWrapFactoryBean自动完成
restful数据mock规范
返回数据格式参考ResposeInfo
dbtable mock
用于dbtable类型业务界面展示数据,能dist本地运行
.roadhogrc mock
用于复杂mock api,需要启动nodejs服务器,不能dist本地运行
主要用于对接外部系统数据
何时使用roadhogrc mock何时使用dbtable mock
- dbtable 内部,能根据配置文件生产mock测试数据时使用dbtable mock
- 请求路径不规律,返回数据类型没有相关配置文件描述时使用roadhogrc mock,需要注意roadhogrc mock不能本地dist运行
Feign下的使用
- 为兼容非ResposeInfo返回的restful服务.所有ling3下的feign接口返回对象都是ResposeInfo
- 返回处理
ResposeInfo resposeInfo= calendarService.findCalendarByList(new HashMap());
if(resposeInfo.getStatus()== ResultStatus.SUCCESS) {
List<Calendar> datas= JSONObject.parseArray(JSONObject.toJSONString(resposeInfo.getData()),Calendar.class);
return datas;
}else{
throw new BusinessException(resposeInfo.getError());
}
localStorage的使用
- 为了能dist运行,除非用于移动端开发,不使用localStorage
- 关于用户权限问题,大家应该都知道对服务器请求时都会带上session,所以登录后讲用户信息的token写入cookie,每次请求(比如查询能访问的菜单)后服务端都会根据token从缓存中知道当前请求是谁,是否有权限.如果是mock是,默认都返回有权限的数据就可以了
- 更多参考OAuth2
- mockStorge.js备份
model编码规范
build方式
自启动项目
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
公共jar项目
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
<!--资源文件的${...}表达式能被maven替换-->
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.*</include>
</includes>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</resource>
</resources>
</build>
docker
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!--配置docker maven插件,绑定install生命周期,在运行maven install时生成docker镜像-->
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>0.4.13</version>
<executions>
<execution>
<phase>deploy</phase>
<goals>
<goal>build</goal>
<goal>tag</goal>
</goals>
</execution>
</executions>
<configuration>
<!--https://github.com/spotify/docker-maven-plugin-->
<!-- 修改这里的docker节点ip,需要打开docker节点的远程管理端口2375,具体如何配置可以参照之前的docker安装和配置的文章-->
<!-- http://surface.ling2.cn:2375/version-->
<dockerHost>http://surface.ling2.cn:2375</dockerHost>
<imageName>${docker.image.prefix}/${project.build.finalName}</imageName>
<baseImage>java</baseImage>
<!--<dockerDirectory>docker</dockerDirectory>-->
<!-- 这里的entryPoint定义了容器启动时的运行命令,容器启动时运行
java -jar 包名 , -Djava.security.egd这个配置解决tomcat8启动时,因为需要收集环境噪声来生成安全随机数导致启动过慢的问题
这里的entryPoint不能有任何换行-->
<!--spring.profiles.active=${spring.profiles.active}对config server =native 可以开启文件方式配置冲突-->
<entryPoint>
["java","-Djava.security.egd=file:/dev/./urandom","-jar","/${project.build.finalName}.jar","--eureka.client.serviceUrl.defaultZone=${registerer.servers}"]
</entryPoint>
<resources>
<resource>
<targetPath>/</targetPath>
<directory>${project.build.directory}</directory>
<include>${project.build.finalName}.jar</include>
</resource>
</resources>
<image>${docker.image.prefix}/${project.build.finalName}</image>
<newName>${docker.image.prefix}/${project.build.finalName}:${docker.tag}</newName>
<forceTags>true</forceTags>
<!-- 如果需要在生成镜像时推送到远程库,pushImage设为true -->
<pushImage>false</pushImage>
</configuration>
</plugin>
</plugins>
</build>
web端编码规范
- 约定优于配置,自动生成大于手工代码
- 能通过配置解决的事就不要自定义代码了
- 自动生成代码优先,不支持的功能让配置界面支持
- 配置界面和模板一起进步
文件目录规范
- 以Ant Design Pro为基础
- 以ling为根目录区分和其他代码的区别,减少合并的冲突可能
- 二级目录为子项目名称
- 三级目录为项目模块
- 四级目录components,routes,services等实现
- ling
- 子项目名称(小写)
- 子模块
- components
- routes
- services
- ......
- 子模块
- 子项目名称(小写)
service定义
完整定义参考
import { request } from '../../utils'
const base_url = '/builder-api/ability/';
export async function getAbilityById (id) {
return request({
url: base_url + id,
method: 'get',
data: {id: id}
})
}
export async function saveAbility (entity) {
if (entity != null) {
let url = '';
if (entity.id != null) {
return request({
url: base_url+entity.id,
method: 'put',
data: entity
})
} else {
return request({
url: base_url,
method: 'post',
data: entity
})
}
} else {
erro("保存的信息不能为空");
}
}
export async function deleteAbility (entity) {
if (entity != null) {
if (entity.id != null) {
return request({
url: base_url + entity.id,
method: 'delete'
})
} else {
erro("删除的数据还没有保存");
}
} else {
erro("保存的信息不能为空");
}
}
export async function findAbilityByList (params) {
return request({
url: base_url ,
method: 'get',
data: params
})
}
export async function findAbilityByPage (params) {
return request({
url: base_url + 'findByPage',
method: 'get',
data: params
})
}
export async function saveTemplate (abilityId,entity) {
if (entity != null) {
let url = '';
if (entity.id != null) {
return request({
url: base_url + 'template/'+abilityId+'/'+ entity.id,
method: 'put',
data: entity
})
} else {
return request({
url: base_url + 'template/'+abilityId,
method: 'post',
data: entity
})
}
} else {
erro("保存的信息不能为空");
}
}
export async function deleteTemplate (abilityId,entity) {
if (entity != null) {
if (entity.id != null) {
return request({
url: base_url + 'template/'+abilityId+'/'+ entity.id,
method: 'delete'
})
} else {
erro("删除的数据还没有保存");
}
} else {
erro("保存的信息不能为空");
}
}
service使用
query = async function (payload) {
let {pageIndex, pageSize} = payload;
pageIndex = (pageIndex == null ? 1 : pageIndex);
pageSize = (pageSize == null ? config.defaultPageSize : pageSize);
let query = {...payload, pageIndex, pageSize};
const data = await findAbilityByPage(parse(query))
if (data) {
this.setState({
abilityDataSource: data.data.result,
abilityPagination: {
current: Number(payload.pageIndex) || 1,
pageSize: Number(payload.pageSize) || config.defaultPageSize,
total: data.data.total,
abilityExpandedRowKeys: []
},
})
}
}
语法备注
定义接口
export interface TreeData {
key: string;
value: string;
label: React.ReactNode;
id?: string;
/** `light` `dark` */
theme?: 'light' | 'dark';
/** enum: `vertical` `horizontal` `inline` */
mode?: 'vertical' | 'horizontal' | 'inline';
children?: Array<TreeData>;
selectedKeys?: Array<string>;
/** 默认选中的树节点 */
defaultSelectedKeys?: Array<string>;
/** 展开/收起节点时触发 */
onExpand?: (expandedKeys: Array<string>, info: { node: AntTreeNode, expanded: boolean }) => void | PromiseLike<any>;
/** 点击复选框触发 */
onCheck?: (checkedKeys: Array<string>, e: AntTreeNodeEvent) => void;
/** 点击树节点触发 */
onSelect?: (selectedKeys: Array<string>, e: AntTreeNodeEvent) => void;
/** filter some AntTreeNodes as you need. it should return true */
filterAntTreeNode?: (node: AntTreeNode) => boolean;
/** 异步加载数据 */
loadData?: (node: AntTreeNode) => PromiseLike<any>;
/** 响应右键点击 */
onRightClick?: (options: AntTreeNodeMouseEvent) => void;
/** 设置节点可拖拽(IE>8)*/
draggable?: boolean;
/** 开始拖拽时调用 */
onDragStart?: (options: AntTreeNodeMouseEvent) => void;
/** dragenter 触发时调用 */
onDragEnter?: (options: AntTreeNodeMouseEvent) => void;
/** dragover 触发时调用 */
onDragOver?: (options: AntTreeNodeMouseEvent) => void;
/** dragleave 触发时调用 */
onDragLeave?: (options: AntTreeNodeMouseEvent) => void;
/** drop 触发时调用 */
onDrop?: (options: AntTreeNodeMouseEvent) => void;
style?: React.CSSProperties;
prefixCls?: string;
filterTreeNode?: (node: AntTreeNode) => boolean;
}
export interface AntTreeNodeMouseEvent {
node: AntTreeNode;
event: React.MouseEventHandler<any>;
}
abstract函数和接口实现
abstract class TreeSelect extends React.Component<TreeSelectProps, any> {
static TreeNode = TreeNode;
static SHOW_ALL = SHOW_ALL;
static SHOW_PARENT = SHOW_PARENT;
static SHOW_CHILD = SHOW_CHILD;
static defaultProps = {
prefixCls: 'ant-select',
transitionName: 'slide-up',
choiceTransitionName: 'zoom',
showSearch: false,
dropdownClassName: 'ant-select-tree-dropdown',
};
abstract getLocale()
}
访问权限
/** @access private */
var foo = 0;
/** @access protected */
this._bar = 1;
/** @access public */
this.pez = 2;
方法定义与暴露
- 内部为handelXXX
- 外部实现为onXXX
handleCancel = (e) => {
const onCancel = this.props.onCancel;
if (onCancel) {
onCancel(e);
}
}
备份
- 向父组件传状态, 通过回调函数的形式 从父组件接收状态, 通过props的形式
- Js注释规范
// Bad
class Navigation extends Component {
static propTypes = { items: PropTypes.array.isRequired };
render() {
return <nav><ul>{this.props.items.map(x => <li>{x.text}</li>}</ul></nav>;
}
}
// Better
function Navigation({ items }) {
return (
<nav><ul>{items.map(x => <li>{x.text}</li>}</ul></nav>;
);
}
Navigation.propTypes = { items: PropTypes.array.isRequired };
const Navigation = ({
items
}) => (
<nav>
<ul>{items.map(x => <li>{x.text}</li>}</ul>
</nav>;
);
配置文件和内容
使用maven profile 能解决不同环境打包问题,但不能解决一个包不同环境通用问题
SpringCloud配置加载流程
通过自己实践后总结的SpringCloud配置加载流程,不一定准确。
- 应用启动时,在读取配置信息完成之前,仅是一个最小启动,用以完成配置加载过程
- 读取bootstrap配置
- 获得应用本地配置路径(spring.config,默认值:application)
- 读取本地配置,覆盖bootstrap配置
- 获得配置服务器连接参数
- 尝试获取远程配置,覆盖本地配置
- 正式启动应用服务(比如启动服务端口监听等,所以服务端口也是可以在远程配置的。)
- 另外,我们还可以在启动程序时通过命令行参数设定配置,命令行指定的配置值的优先级在本地似乎是最高的,但是会被远程配置覆盖。
所以,几种配置的优先级顺序大致如下:
- 远程配置 > 命令行参数配置 > application > bootstrap**
bootstrap.properties
- 项目启动时需要找到注册中心位置,从而找到配置 放application.properties中无效
eureka.instance.hostname=@eureka.instance.hostname@ eureka.instance.perferIpAddress=@eureka.instance.perferIpAddress@ eureka.client.serviceUrl.defaultZone=@eureka.client.serviceUrl.defaultZone@
spring.profiles.active=routing
spring.application.name=ling-cloud-routing
- 需要获取的配置信息
spring.cloud.config.discovery.enabled=true spring.cloud.config.discovery.service-id=LING-CLOUD-CONFIG spring.cloud.config.fail-fast=true spring.cloud.config.retry.initial-interval=10000 spring.cloud.config.retry.multiplier=2 spring.cloud.config.retry.max-interval=60000 spring.cloud.config.retry.max-attempts=10
application.properties
server.port=80 spring.application.name=LING-CLOUD-ROUTING
app调试最佳实践
- 开发阶段,使用.roadhogrc.js配置apicontext
export default {
entry: 'src/index.js',
svgSpriteLoaderDirs: svgSpriteDirs,
"theme": "./theme.config.js",
// 接口代理示例
"proxy": {
"/builder-api": {
"target": "http://127.0.0.1:6001",
"changeOrigin": true,
"pathRewrite": { "^/builder-api" : "/" }
},
"/api/v2": {
"target": "http://192.168.0.110",
"changeOrigin": true,
"pathRewrite": { "^/api/v2" : "/api/v2" }
}
},
......
- 为spring boot发布时去除proxy设置,使用java程序自带的context
- 当为app发布时,改为正式设置的proxy
问题解决
- 不设置<relativePath>../</relativePath>会找不到parent
- 因为spring-boot-maven-plugin会将jar打包为可执行文件,
所以每个项目必须要一个main函数,即使xx-core不需要也放一个 - ling-cloud-pl-service 编译出错,找不到package等.... 使用spring-boot-maven-plugin编译的结果不具有被其他项目引用的可能性,所以core上不能有spring-boot-maven-plugin
- 需要打包为可执行jar的项目需要添加一下代码
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<dependencies>
<!-- spring热部署 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>springloaded</artifactId>
<version>1.2.6.RELEASE</version>
</dependency>
</dependencies>
<configuration>
<executable>true</executable>
</configuration>
</plugin>
</plugins>
</build>