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-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
       <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
       <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前缀和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编码规范

Ling2编码规范#继承关系

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>