从绫开始的后台管理系统(四)
我们开始正式写代码。
这一章主要讲前后端的工程结构设计和搭建。
后端
工程结构
工程结构这里我们使用pom管理的方式,项目结构我们采用DDD模型。
parent工程
我们创建一个parent用来管理子项目,依赖,属性以及打包方式。我们先预置三个子工程,core包负责核心配置,common包负责工具管理,system包负责对外提供服务。
parent的packaging为pom结构,它本身不对外提供功能,而是作为管理工程的工程。
依赖工程版本需要用properties维护起来,并用${}的格式引用,这样改版本就可以做到统一修改,还有一些全局配置也可以放在properties中,供其他项目引用。
构建配置build这里我们会构建两个包执行包和源码包方便在其他子工程中查看源码,子工程可直接进行构建,不需要单独配置build。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cn.lm</groupId>
<artifactId>parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>parent</name>
<description>parent</description>
<packaging>pom</packaging>
<modules>
<module>0moe-core</module>
<module>0moe-common</module>
<module>0moe-system</module>
</modules>
<properties>
<java.version>1.8</java.version>
<redis-starter.version>2.4.3</redis-starter.version>
<security-starter.version>2.4.3</security-starter.version>
<web-starter.version>2.4.3</web-starter.version>
<mybatis-starter.version>2.1.4</mybatis-starter.version>
<mysql-driver.version>8.0.23</mysql-driver.version>
<lombok.version>1.18.18</lombok.version>
<hutool.version>5.5.7</hutool.version>
<servlet-api.version>4.0.1</servlet-api.version>
<multi-redis-starter.version>1.0.0-SNAPSHOT</multi-redis-starter.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>${redis-starter.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>${security-starter.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis-starter.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
<version>${mysql-driver.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
<version>${lombok.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/cn.hutool/hutool-all -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
<!-- servlet包 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>${servlet-api.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${web-starter.version}</version>
</dependency>
<dependency>
<groupId>cn.lm</groupId>
<artifactId>0moe-multi-redis-starter</artifactId>
<version>${multi-redis-starter.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<testSource>${java.version}</testSource>
<testTarget>${java.version}</testTarget>
</configuration>
</plugin>
<!--配置生成源码包-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.0.1</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
core工程
core工程负责各种核心组件的配置以及管理组件,以starter的形式向外部提供服务。仅允许api服务引入一次
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.lm</groupId>
<artifactId>parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>cn.lm</groupId>
<artifactId>core</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>core</name>
<description>core</description>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>cn.lm</groupId>
<artifactId>0moe-multi-redis-starter</artifactId>
</dependency>
<dependency>
<groupId>cn.lm</groupId>
<artifactId>common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>我们每个核心组件都会放在一个包中,并且在CoreAutoConfiguration中使用@Import进行统一引入。这样我们在增删组件的时候直接修改@Import就可以,无需每个组件修改一次。

@Import(SecurityConfig.class)
@Configuration
@Enable0moeMultiDbRedis
public class CoreAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public UserInfoService userInfoService() {
return new UserInfoServiceImpl();
}
@Bean
public TokenAuthenticationFilter tokenAuthenticationFilter() {
return new TokenAuthenticationFilter();
}
}要记得在spring.factories引入自动配置类。
# spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
cn.lm.core.CoreAutoConfiguration####common工程
common工程会集成一些标准工具类jar(如hutool)和项目通用的utils,方便其他项目使用。允许多工程重复引入
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.lm</groupId>
<artifactId>parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>cn.lm</groupId>
<artifactId>common</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>common</name>
<description>common</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
</dependencies>
</project>system工程
system工程作为我们对外提供服务的工程,使用DDD模型驱动。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.lm</groupId>
<artifactId>parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>cn.lm</groupId>
<artifactId>system</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>system</name>
<description>system</description>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>cn.lm</groupId>
<artifactId>core</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
</project>工程结构如下:

其他
lombok
lombok是一把双刃剑,我们使用lombok来简化开发的同时应谨慎一些使用。值得注意的是,lombok应仅用在类pojo文件的编写简化上,可以使用Getter、Setter,log以及Builder等,禁止使用@Data。其他地方酌情按照项目需要,使用lombok一些特性,比如自动注入简化等。
前端
前端没有后端那么复杂,但是仍需要区分各个模块,以及遵守原则:core模块仅允许单次引入,shared模块允许多次引入。
工程创建
# 创建admin-ui工程, 使用严格模式,css使用scss,使用路由
ng new admin-ui --strict --style scss --routing
# 引入material-ui, 主题选择custom,应用全局排版样式,应用浏览器动画
ng add @angular/material模块划分
core
core模块负责工程所有犬儒引入的模块和全局配置,比如BrowserModule、BrowserAnimationsModule、ServiceModule等,这些都仅需要在整个工程中引入一次即可,不需要重复引用,都由core包管理起来,然后将core包放在appModule中引入。勇士appModule应只引入core包
@NgModule({
declarations: [],
imports: [
SharedModule,
BrowserModule,
RoutesModule,
BrowserAnimationsModule,
ServiceModule,
LayoutModule,
],
exports: [
SharedModule,
RoutesModule,
]
})
export class CoreModule {
constructor(@SkipSelf() @Optional() parentModule: CoreModule) {
if (parentModule){
throw new Error('core只能在appModule引入');
}
}
}appModule
@NgModule({
declarations: [
AppComponent
],
imports: [
CoreModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {
}shared
shared模块类似于后端的common工程,本质上属于组件包,可在各个模块中引入。他存在的意义是为了方便一方组件和三方组件的管理,在使用第三方组件时应直接引入shared包。
@NgModule({
declarations: [],
imports: [
CommonModule,
RouterModule,
MatIconModule,
],
exports: [
CommonModule,
RouterModule,
MatIconModule,
]
})
export class SharedModule { }service
service包用于资源管理。他的绝大部分作用时管理api,当然有些兄弟组件的联动也可以通过service(或ngrx)来驱动,他也是放在core包中一次性引入。
@NgModule({
declarations: [],
imports: [
]
})
export class ServiceModule { }routes
routes负责业务路由视图的管理。我们以后的业务代码都会放到routes包中。

@NgModule({
declarations: [],
imports: [
RoutesRoutingModule,
SharedModule
]
})
export class RoutesModule { }const routes: Routes = [
{
path: 'passport',
component: PassportComponent,
loadChildren: () => import('./passport/passport.module').then(m => m.PassportModule)
}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class RoutesRoutingModule { }layout
layout负责模块布局。比如在这个passport页面,外部的背景、copyright都属于layout的一部分,登录路由(routesModule)渲染的部分仅包括红框内
在比如这里,这个就进入了业务布局,外部包括菜单等都是layout的一步分,只有红框内才是业务视图(routesModule)真正渲染的地方。

全局配置
ng的组件生成需要改动一些全局配置。以下配置放在projects.admin-ui.schematics中
"@schematics/angular:component": {
"style": "scss",
"changeDetection": "OnPush"
},
"@schematics/angular:module": {
"commonModule": false
}