同步操作将从 心飞为你飞/九点钟 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
学习目标
了解九点钟项目 了解九点钟项目技术架构 独立搭建后台管理系统 独立搭建系统微服务 实现全局异常处理
企业自己部署办公项目
Saas软件即服务,传统方式软件交付必须有聚合安装包,代码。使用Saas服务以后不需要安装软件,也能享受响应服务。本质从买到租模式转变。 核心:将软件从所有权转为使用权。
九点钟移动办公云平台:全新的移动办公云平台,主要帮助企业轻松解决:考勤统计难,排班混乱、特殊工时难以有效管理等。 移动端轻松地实现考勤统计、轻简办公、沟通协作、企业管理,能够极大地节省企业日常考勤、排班、办公、沟通、管理的成本。集知识分享、审批流程、数据协作等应用于一体的移动办公云平台 。每天要做什么、做了什么、有什么收获;实时推进目标成果、构建高效率协作团队。
本项目主要利用当前最为流行的SpringCloud微服务技术架构及SaaS模式,解决很多企业所面临的管理困难。企业办公自动化方面我们抽取通用模块,做成通用平台,可以用SaaS方式解决传统办公自动化软件开发及使用的问题,非常类似近期很火爆的阿里钉钉的一款移动办公产品。涉及众多的实战业务,主要包括的服务模块:1.消息服务 ;2.系统服务;3.企业服务;4.签到服务;5.考勤服务;6文档服务;7.审批服务等。打造以人为本、数据驱动的专业行业解决方案,为各行业提供新工作方式。
致力于企业办公解决方案,业务亮点:移动办公新模式的体验,将移动办公和SaaS模式进行结合,消息服务解决企业内员工的沟通协调工作;可以实现微服务鉴权及细粒度权限控制;完善的审批服务可以更加快速解决流程制订,流程审批等工作;企业服务可以将九点钟项目打造成平台项目,让更多公司入驻到九点钟移动办公云平台;签到服务使用百度地图实现签到人所在位置的地图位置信息精准记录;报表解决方案实现数据整合抽象化提取。
PC端管理平台入口:https://9clock-admin-dev.itheima.net/
九点钟移动办公从用户角度出发分为两部分:PC端,APP端
PC端(企业管理员)
APP端:(企业员工)
微服务是一种架构方式,最终肯定需要技术架构去实施。
微服务的实现方式很多,但是最火的莫过于Spring Cloud了。为什么?
后台硬:作为Spring家族的一员,有整个Spring全家桶靠山,背景十分强大。
技术强:Spring作为Java领域的前辈,可以说是功力深厚。有强力的技术团队支撑,一般人还真比不了
群众基础好:可以说大多数程序员的成长都伴随着Spring框架,试问:现在有几家公司开发不用Spring?SpringCloud与Spring的各个框架无缝整合,对大家来说一切都是熟悉的配方,熟悉的味道。
使用方便:相信大家都体会到了SpringBoot给我们开发带来的便利,而SpringCloud完全支持SpringBoot的开发,用很少的配置就能完成微服务框架的搭建
后端技术:
九点钟移动办公架构缩略图,大图请参考课前资料:
移动互联网时代,你还在用Word管理接口文档吗?你还在使用拼接URL测试吗?你OUT了!
YApi 是高效、易用、功能强大的 api 管理平台,旨在为开发、产品、测试人员提供更优雅的接口管理服务。可以帮助开发者轻松创建、发布、维护 API,YApi 还为用户提供了优秀的交互体验,开发人员只需利用平台提供的接口数据写入工具以及简单的点击操作就可以实现接口的管理。
基于 Json 和 Mockjs 定义接口返回数据的结构和文档,效率提升多倍
扁平化权限设计,即保证了大型企业级项目的管理,又保证了易用性
类似 postman 的接口调试
自动化测试, 支持对 Response 断言
MockServer 除支持普通的随机 mock 外,还增加了 Mock 期望功能,根据设置的请求过滤规则,返回期望数据
支持 postman, har, swagger 数据导入
免费开源,内网部署,信息再也不怕泄露了
https://www.taojibao.cn/project/468/interface/api
登录账号:bj144@qq.com java123
系统模块:nc_sys
表名称 | 含义 |
---|---|
company_user | 企业员工表 |
sys_role | 角色表 |
sys_function | 菜单/权限 |
common_industry | 行业字典表 |
表名称 | 含义 |
---|---|
company | 企业信息表 |
department | 部门表 |
address_book_config | 企业通讯录字段表 |
文档模块:nc_doc
表名称 | 含义 |
---|---|
file_history | 文件操作历史表 |
doc_folder | 文件夹表 |
doc_file | 文件表 |
collaborations | 文件协同表 |
考勤模块:nc_attendance
表名称 | 含义 |
---|---|
company_common_setting | 公司通用设定 |
attend_group | 考勤组信息表 |
attend_group_part | 考勤人员详情表 |
overtime_rule | 加班规则表 |
overtime_rule_detail | 加班规则详情表 |
holiday_rule | 假期规则表 |
holiday_rule_age | 假期规则和司龄关联表 |
atte_pubch | 打卡信息表 |
签到模块:nc_sign
表名称 | 含义 |
---|---|
sign | 签到表 |
sign_picture | 签到图片资源表 |
消息模块:存入MongDB
表名称 | 含义 |
---|---|
notifyMessage | 消息基础表 |
审批模块:nc_approve
表名称 | 含义 |
---|---|
approve_definition_template | 审批定义模板表 |
approve_definition | 审批定义表 |
approve_inst | 审批实例表 |
approve_inst_node | 审批实例节点表 |
approve_inst_record | 审批实例记录表 |
后端技术:
为了保证开发环境的统一,希望每个人都按照我的环境来配置:
idea大家可以在我的课前资料中找到。另外,使用帮助大家可以参考课前资料的《idea使用指南.md》
我们在开发的过程中,为了保证以后的生产、测试环境统一。尽量都采用域名来访问项目。
一级域名:www.nineclock.com(PC端访问地址)
二级域名:api.nineclock.com(网关访问地址)
我们可以通过switchhost工具来修改自己的host对应的地址,只要把这些域名指向127.0.0.1,那么跟你用localhost的效果是完全一样的。
switchhost可以去课前资料寻找。
创建Maven工程(pom)nc_parent,用来管理依赖及其版本,注意是创建project,而不是moudle。
删除src目录
在pom.xml中引入依赖:
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.itheima</groupId>
<artifactId>nine_clock_parent</artifactId>
<version>1.0-SNAPSHOT</version>
<description>黑马程序员 - 九点钟项目</description>
<packaging>pom</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.7.RELEASE</version>
<relativePath/>
</parent>
<!--定义版本-->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR2</spring-cloud.version>
<mybatis.plus.starter.version>3.1.1</mybatis.plus.starter.version>
</properties>
<dependencyManagement>
<dependencies>
<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>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis.plus.starter.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
</project>
Consul 是 HashiCorp 公司推出的开源工具,采用go语言开发,go语言高并发有优势。用于实现分布式系统的服务发现与配置。与其它分布式服务注册与发现的方案,Consul 的方案更“一站式”(consul=eureka+springcloudConfig),内置了服务注册与发现框架、分布一致性协议实现、健康检查、Key/Value 存储、多数据中心方案,不再需要依赖其它工具(比如 ZooKeeper 等)。使用起来也较为简单。
Consul 使用 Go 语言编写,因此具有天然可移植性(支持Linux、windows和Mac OS X);安装包仅包含一个可执行文件,方便部署,与 Docker 等轻量级容器可无缝配合。
Consul特点:
Consul核心:agent组件
Agent是一个独立的程序,通过守护进程的方式,运行在consul集群中的每个节点上。每个Consul agent维护它自己的服务集合以及检查注册和健康信息。agent负责执行自己的健康检查和更新本地状态其中,Agent 根据节点的性质,分为: Agent Server 和 Agent Client
client将 HTTP 和 DNS 接口请求转发给局域网内的服务端集群。
server 保存client的注册信息,集群的配置信息, 维护集群高可用, 在局域网内与本地客户端通讯, 通过广域网与其它数据中心通讯。 每个数据中心的 server 数量推荐为 3 个或是 5 个,通过 Raft 算法来保证一致性。
SpringCloudConsul工作原理:
最大区别:C(一致性)A(可用性)P(分区容错性)原则 Eureka保证AP Consul保证CP
Consul强一致性带来: 保证强一致性但牺牲可用性
Eureka:保证高可用(A)和最终一致性: 保证可用性牺牲一致性
Eureka就是Servlet程序,运行在Serlvet容器中。 consul采用是GO语言编写
Feature | Consul | euerka |
---|---|---|
服务健康检查 | 服务状态,内存,硬盘等 | 可配支持 |
多数据中心 | 支持 | — |
kv 存储服务 | 支持 | — |
一致性 | raft | — |
cap | cp | ap |
使用接口(多语言能力) | 支持 http 和 dns | http(sidecar) |
watch 支持(集群监控) | 全量/支持long polling | 支持 long polling/大部分增量 |
自身监控 | metrics | metrics |
spring cloud 集成 | 已支持 | 已支持 |
window下安装
Consul 不同于 Eureka 需要单独部署项目安装,访问Consul 官网(https://consul.io/)下载 Consul 的最新版本,我这里是 consul_1.5.3。根据不同的系统类型选择不同的安装包,从下图也可以看出 Consul 支持所有主流系统。附下载地址:https://www.consul.io/downloads.html
我这里以 Windows 为例,下载下来是一个 consul_1.2.1_windows_amd64.zip 的压缩包,解压是是一个 consul.exe 的执行文件。
打开命令行,cd 到对应的目录下,使用 cmd 执行命令consul agent -dev:启动 Consul
consul agent -dev -client=0.0.0.0 # -dev表示开发模式运行,另外还有-server -client表示服务模式运行
为了方便期间,可以在同级目录下创建一个 consul-start.bat 脚本来启动,脚本内容如下:
consul agent -dev
pause
只需要双击脚本文件即可启动consul服务。
启动服务后,通过浏览器访问:http://localhost:8500,可以看到 Consul 的管理界面,说明consul服务已经成功启动了。
基本概念:
启动参数说明:
consul agent -server -bind=127.0.0.1 -client=0.0.0.0 -bootstrap-expect=3 -data-dir=H:\run_software\consul\data1 -node=server1
接下来我们来实现在一台服务器上安装三个server,一个client方式搭建集群环境来完成演示。consul 的搭建方式可以通过命令行方式也可以通过配置文件方式实现。
创建config1,data1,config2,data2,config3,data3,config4,data4目录,分别对应每个节点的配置文件和数据文件存放目录。
config1 目录下配置文件名 cluster.json 文件内容如下:
{
"data_dir": "H:\\run_software\\consul\\data1",
"node_name": "server1",
"server": true,
"bootstrap_expect": 3,
"bootstrap": false,
"datacenter": "aws",
"advertise_addr": "127.0.0.1",
"bind_addr": "127.0.0.1",
"log_level": "INFO",
"enable_syslog": false,
"ports": {
"http": 8500 ,
"https": 8501,
"dns": 8600,
"grpc": 8502,
"serf_lan": 8301,
"serf_wan": 8302,
"server": 8300
},
"disable_host_node_id":true,
"retry_join": ["127.0.0.1:8301","127.0.0.1:7302","127.0.0.1:6303"]
}
config2 目录下配置文件名 cluster.json 文件内容如下:
{
"data_dir": "H:\\run_software\\consul\\data2",
"node_name": "server2",
"server": true,
"bootstrap_expect": 3,
"bootstrap": false,
"datacenter": "aws",
"advertise_addr": "127.0.0.1",
"bind_addr": "127.0.0.1",
"log_level": "INFO",
"enable_syslog": false,
"ports": {
"http": 7501,
"https": 7502,
"dns": 7601,
"grpc": 7503,
"serf_lan": 7302,
"serf_wan": 7303,
"server": 7301
},
"disable_host_node_id": true,
"retry_join": ["127.0.0.1:8301", "127.0.0.1:7302", "127.0.0.1:6303"]
}
config3 目录下配置文件名 cluster.json 文件内容如下:
{
"data_dir": "H:\\run_software\\consul\\data3",
"node_name": "server3",
"server": true,
"bootstrap_expect": 3,
"bootstrap": false,
"datacenter": "aws",
"advertise_addr": "127.0.0.1",
"bind_addr": "127.0.0.1",
"log_level": "INFO",
"enable_syslog": false,
"ports": {
"http": 6502,
"https": 6503,
"dns": 6602,
"grpc": 6504,
"serf_lan": 6303,
"serf_wan": 6304,
"server": 6302
},
"disable_host_node_id": true,
"retry_join": ["127.0.0.1:8301", "127.0.0.1:7302", "127.0.0.1:6303"]
}
config4 目录下配置文件名 cluster.json 文件内容如下:
{
"data_dir": "D:\\run_software\\consul\\data4",
"node_name": "client-node",
"server": false,
"bootstrap": false,
"datacenter": "aws",
"advertise_addr": "127.0.0.1",
"bind_addr": "127.0.0.1",
"log_level": "INFO",
"enable_syslog": false,
"ui": true,
"ports": {
"http": 5503,
"https": 5504,
"dns": 5603,
"grpc": 5505,
"serf_lan": 5304,
"serf_wan": 5305,
"server": 5303
},
"disable_host_node_id": true,
"start_join": ["127.0.0.1:8301", "127.0.0.1:7302", "127.0.0.1:6303"]
}
consul 解压后的目录,然后依次启动server1,server2,server3,client1 节点,命令顺序如下:
consul agent -dev -client=0.0.0.0
启动完后,通过如下命令查看集群状态。
在浏览中中输入地址http://127.0.0.1:5503/ui访问查看集群结果
网关微服务也需要注册到Consul中,网关微服务要从Consul中拉取其他微服务的注册信息,所以注意在启动网关微服务前需要将Consul注册中心服务启动!
选择maven方式创建Module,然后填写项目名称,我们命名为:nine_clock_gateway
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
package com.itheima;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* @Author: 黑马程序员
* @description:
**/
@SpringBootApplication
@EnableDiscoveryClient
public class NcGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(NcGatewayApplication.class, args);
}
}
server:
port: 10010
spring:
application:
name: gateway-service
#注册中心 相关配置
cloud:
consul:
host: localhost #指定注册中心IP地址
port: 8500 #指定注册中心端口,默认为8500
discovery:
serviceName: gateway-service
locator:
lower-case-service-id: true
enabled: true
register: true
prefer-ip-address: true #这个必须配
tags: version=1.0.0 #写当前项目 开发版本1.1
instance-id: ${spring.application.name}:${spring.cloud.client.ip-address}:${server.port}
healthCheckInterval: 15s
health-check-url: http://${spring.cloud.client.ip-address}:${server.port}/actuator/health
#网关配置
gateway:
discovery:
locator:
enabled: true
routes:
#系统服务
- id: sys-service #路由名称 该名称任意写
uri: lb://sys-service #lb:load balacne 负载均衡
predicates:
- Path=/sys/** #匹配路径规则 地址中包含/sys 请求会转发到系统微服务
filters:
- StripPrefix=1 #转发请求到目标微服务,1代表去除一位前缀 /** 会被转发到其他微服务
# localhost:10010/sys/user /user这部分转发目标微服务
# 认证中心
- id: auth-service
uri: lb://auth-service
predicates:
- Path=/auth/**
filters:
- StripPrefix=1
目前,nc_parent下有一个子模块:
目前,服务的结构如图所示:
启动网关微服务测试:
访问注册中心,查看是否注册成功:
有些工具或通用的约定内容,我们希望各个服务模块共享,因此需要创建一个工具模块:nc_common
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>nineclock</artifactId>
<groupId>com.itheima</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.itheima</groupId>
<artifactId>nineclock_common</artifactId>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.10.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.10.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.10.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.gavaghan</groupId>
<artifactId>geodesy</artifactId>
<version>1.1.3</version>
</dependency>
<!-- mapstruct-->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
<version>1.2.0.CR1</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.2.0.CR1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<!--异常-->
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>1.5.21</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
<version>1.5.21</version>
</dependency>
<!-- 增加hutool工具包 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.1.4</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.9</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.9</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>4.3.9</version>
</dependency>
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.4.2</version>
</dependency>
</dependencies>
</project>
每个工具类的作用:
package com.itheima.common.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.io.Serializable;
/**
* @program: 黑马程序员
* @description: 服务端提供前端返回的结果
**/
@Data
public class Result<T> implements Serializable {
private Boolean success; //是否成功
private int code; //返回状态码
private String message; //提示消息
private T data; //返回数据
public Result() {
}
public Result(Boolean success, int code, String message) {
this.success = success;
this.code = code;
this.message = message;
}
public Result(Boolean success, int code, String message, T data) {
this.success = success;
this.code = code;
this.message = message;
this.data = data;
}
}
我们先完成项目的搭建:首先在mysql中创建系统微服务对应数据库:nc_sys导入对应系统微服务sql脚本
我们的工程命名为nc_sys
.
需要注意的是,我们的nc_sys
是一个微服务,那么将来肯定会有其它系统需要来调用服务中提供的接口,因此肯定也会使用到接口中关联的实体类。
因此这里我们需要使用聚合工程,将要提供的接口及相关实体类放到独立子工程中,以后别人引用的时候,只需要知道坐标即可。
我们会在nc_sys
中创建三个子工程:
调用关系如图所示:
创建父工程module,打包方式为pom
不需要任何依赖,我们可以把项目打包方式设置为pom,也可将src目录删除。
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>nc_parent</artifactId>
<groupId>com.itheima</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>nc_sys</artifactId>
<!--打包方式改为pom-->
<packaging>pom</packaging>
</project>
在nc_sys工程上点击右键,选择new > module:
与nc_sys_pojo
类似,我们选择在nc_sys
上右键,新建module,然后填写项目信息:
由于将来我们的业务模块一定会返回数据给前端/移动端,而返回的数据对象都为DTO数据传输对象,所以要在nc_sys_service的pom文件中增加nc_sys_pojo的依赖坐标信息。
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>nc_sys</artifactId>
<groupId>com.itheima</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>nc_sys_service</artifactId>
<dependencies>
<dependency>
<groupId>com.itheima</groupId>
<artifactId>nc_sys_pojo</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
与上面类似,我们选择在nc_sys
上右键,新建module,然后填写项目信息:
同理对外提供的接口模块也需要引入nc_sys_pojo的依赖坐标信息。
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>nc_sys</artifactId>
<groupId>com.itheima</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>nc_sys_interface</artifactId>
<dependencies>
<dependency>
<groupId>com.itheima</groupId>
<artifactId>nc_sys_pojo</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
我们打开nc_sys的pom查看,会发现nc_sys_pojo和nc_sys_service以及nc_sys_interface都已经成为module了:
接下来我们给nc_sys_service中添加依赖:
思考一下我们需要什么?
nc_sys_pojo
中的实体类<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>nc_sys</artifactId>
<groupId>com.itheima</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>nc_sys_service</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- mybatisplus与springboot整合 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.itheima</groupId>
<artifactId>nc_sys_pojo</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.itheima</groupId>
<artifactId>nc_common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
启动类:
package com.itheima;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
/**
* @Author: 黑马程序员
* @description:
**/
@SpringBootApplication
@EnableDiscoveryClient
public class NcSysApplication {
public static void main(String[] args) {
SpringApplication.run(NcSysApplication.class, args);
}
}
配置文件:
server:
port: 8081
spring:
application:
name: sys-service
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/nc_sys?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
username: root
password: root
cloud:
consul:
port: 8500 #指定注册中心端口,默认为8500
host: 127.0.0.1 #指定注册中心IP地址
discovery:
serviceName: sys-service
locator:
lower-case-service-id: true
enabled: true
register: true
prefer-ip-address: true #这个必须配
tags: version=1.2
instance-id: ${spring.application.name}:${spring.cloud.client.ip-address}
healthCheckInterval: 15s
health-check-url: http://${spring.cloud.client.ip-address}:${server.port}/actuator/health
mybatis-plus:
global-config:
id-type: 0
mapper-locations: classpath:/mapper/*.xml
type-aliases-package: com.itheima.sys.entity
configuration:
map-underscore-to-camel-case: true
logging:
level:
com.itheima: debug
Mybatis配置类:
package com.leyou.sys.config;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.plugins.PerformanceInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyBatisPlusConfig {
/*** * plus 的性能优化 * 生产环境不适用 * @return */
@Bean
public PerformanceInterceptor performanceInterceptor() {
PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor(); /**/
performanceInterceptor.setMaxTime(1000); /**/
performanceInterceptor.setFormat(true);
return performanceInterceptor;
}
/**
* @Description : mybatis-plus分页插件
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
}
打开nine_clock_gataway微服务配置文件增加以下内容
spring:
cloud:
gateway:
routes:
# 系统微服务
- id: sys-service #微服务名称
uri: lb://sys-service #即sys-service服务的负载均衡地址
predicates: #predicates用于匹配HTTP请求的不同属性
- Path=/sys/** #匹配到的URL地址
filters:
- StripPrefix=1 #在转发之前将/sys 去掉
为了快速测试,我们在nc_sys_service项中提供controller,新增restApi方法进行测试:
package com.itheima.sys.controller;
import com.itheima.common.vo.Result;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@GetMapping("/test")
public Result<String> hello(){
return new Result<>(true, 200, "请求成功!", "hello nineclock!");
}
}
由于在网关中增加了路由规则,故测试之前需要将网关微服务以及系统微服务重启访问测试:
通过系统微服务访问测试:
通过网关访问测试:
我们预设这样一个场景,假如我们做新增用户,需要接收下面的参数:
name:名称
age:年龄
然后对数据做简单校验:
需求:新增时,自动生成ID,然后随用户对象一起返回
在nc_sys_pojo中增加用户实体类:
package com.itheima.sys.entity;
import lombok.Data;
@Data
public class UserDomain {
private Long id;
private String name;
private Integer age;
}
在nc_sys_service中增加用户业务类:
package com.itheima.sys.service;
import com.itheima.sys.entity.UserDomain;
import org.springframework.stereotype.Service;
import java.util.Random;
@Service
public class UserDominService {
/**
* 模拟测试保存用户记录
* @param user
* @return
*/
public UserDomain saveUser(UserDomain user) {
Random random = new Random(1000);
user.setId(random.nextLong());
return user;
}
}
在nc_sys_service中增加用户控制器类:
package com.itheima.sys.controller;
import com.itheima.common.vo.Result;
import com.itheima.sys.entity.UserDomain;
import com.itheima.sys.service.UserDominService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@Autowired
private UserDominService userDominService;
@GetMapping("/test")
public Result<String> hello(){
return new Result<>(true, 200, "请求成功!", "hello nineclock!");
}
@PostMapping("/test/user")
public Result saveUser(@RequestBody UserDomain user){
//如果年龄为空,则抛出异常,返回400状态码,返回错误提示消息
if (user.getAge() == null) {
throw new RuntimeException("用户年龄为必填项!");
}
return new Result(true, 200, "保存成功", userDominService.saveUser(user));
}
}
使用POSTMAN发送请求进行测试,注意提交参数格式为Json.
没有提交年龄返回的结果为:
刚才的处理看似完美,但是仔细想想,我们在异常处理中,如果抛出RuntimeException交给框架处理返回状态码为固定的500了,仅凭用户抛出的异常,我们根本无法判断到底该返回怎样的状态码,可能是参数有误、数据库异常、没有权限,等等各种情况。
解决问题的思路为,所有的异常全部抛出,然后统一的拦截到异常处理,给前端返回结果。
接下来,我们使用SpringMVC提供的统一异常拦截器,因为是统一处理,我们放到nc_common
项目中:
新建一个类,名为:BasicExceptionAdvice
具体代码如下:
package com.itheima.common.exception.advice;
import com.itheima.common.vo.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
/**
* @Author: 黑马程序员
* @description:
**/
@Slf4j
@ResponseBody
@ControllerAdvice
public class BasicExceptionAdvice {
@ExceptionHandler(RuntimeException.class)
public Result handleException(RuntimeException e) {
// 我们暂定返回状态码为400, 然后从异常中获取友好提示信息
return new Result(false, 400, e.getMessage());
}
}
解读:
@ControllerAdvice
:默认情况下,会拦截所有加了@Controller
的类
@ExceptionHandler(RuntimeException.class)
:作用在方法上,声明要处理的异常类型,可以有多个,这里指定的是RuntimeException
。被声明的方法可以看做是一个SpringMVC的Handler
:
ModelAndView
、ResponseEntity
等,基本与handler
类似这里等于从新定义了返回结果,我们可以随意指定想要的返回类型。此处使用了String此处使用了spring的注解,因此需要在nc_common中引入web依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
要想在系统服务中扫描到这个advice,需要在nc_sys_service中引入nc_common依赖:
<dependency>
<groupId>com.itheima</groupId>
<artifactId>nc_common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
重启系统微服务项目进行测试:
成功返回了错误信息!
刚才的处理看似完美,但是仔细想想,我们在通用的异常处理中,把返回状态码写死为400了:
这样显然不太合理。
因此,用户抛出异常时,就必须传递两个内容:
但是RuntimeException
是无法接受状态码的,只能接受异常的消息,所以我们需要做两件事情:
枚举:把一件事情的所有可能性列举出来。在计算机中,枚举也可以叫多例,单例是多例的一种情况 。
单例:一个类只能有一个实例。
多例:一个类只能有有限个数的实例。
单例的实现:
我们定义一个枚举,用于封装异常状态码和异常信息:
package com.itheima.common.enums;
import lombok.Getter;
@Getter
public enum ResponseEnum {
//一定要将选择项放在最上
AGE_NOT_NULL(400, "年龄不能为空!"),
SUCCESS(200, "操作成功!"),
ERROR(500, "操作失败!"),
;
private Integer code;
private String message;
//提供私有无参构造
ResponseEnum(Integer code, String message) {
this.code = code;
this.message = message;
}
}
然后自定义异常,来获取枚举对象。
在nc_common中定义自定义异常类:
package com.itheima.common.exception;
import com.itheima.common.exception.enums.ResponseEnum;
import io.swagger.models.auth.In;
import lombok.Data;
@Data
public class NcException extends RuntimeException {
private Integer status;
public NcException(Integer status) {
this.status = status;
}
public NcException(ResponseEnum enums){
super(enums.getMessage());
this.status = enums.getCode();
}
public NcException(Integer status, String message) {
super(message);
this.status = status;
}
public NcException(Integer status, String message, Throwable cause) {
super(message, cause);
this.status = status;
}
}
修改Controller中代码:
package com.itheima.sys.controller;
import com.itheima.common.enums.ResponseEnum;
import com.itheima.common.exception.NcException;
import com.itheima.common.vo.Result;
import com.itheima.sys.entity.UserDomain;
import com.itheima.sys.service.UserDominService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@Autowired
private UserDominService userDominService;
@GetMapping("/test")
public Result<String> hello(){
return new Result<>(true, 200, "请求成功!", "hello nineclock!");
}
@PostMapping("/test/user")
public Result saveUser(@RequestBody UserDomain user){
//如果年龄为空,则抛出异常,返回400状态码,返回错误提示消息
if (user.getAge() == null) {
//throw new RuntimeException("用户年龄为必填项!");
throw new NcException(ResponseEnum.AGE_NOT_NULL);
}
return new Result(true, 200, "保存成功", userDominService.saveUser(user));
}
}
修改全局异常处理逻辑:
package com.itheima.common.exception;
import com.itheima.common.vo.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @Author: 黑马程序员
* @description:
**/
@Slf4j
@ResponseBody
@ControllerAdvice
public class BasicExceptionAdvice {
@ExceptionHandler(RuntimeException.class)
public Result handleException(RuntimeException e) {
// 我们暂定返回状态码为500, 然后从异常中获取友好提示信息
return new Result(false, 400, e.getMessage());
}
/**
* 处理自定义异常
* @return
*/
@ExceptionHandler(NcException.class)
public Result handlerNcException(NcException e){
return new Result(false, e.getStatus(), e.getMessage());
}
/**
* 处理其他异常
* @param exception
* @return
*/
@ExceptionHandler(Exception.class)
public Result handlerNcException(Exception e){
return new Result(false, 500, e.getMessage());
}
}
测试结果如下:
将来返回结果给前端,不管成功还是失败都需要返回结果对象Result,目前每次返回都必修手动New一个对象,使用起来比较繁琐。改为将成功,以及失败的Result对象返回的静态方法封装在Result类中。
package com.itheima.common.vo;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.itheima.common.exception.enums.ResponseEnum;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* 统一给客户端响应结果对象
*/
@Data
@NoArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL) //转json条件
public class Result<T> implements Serializable {
//是否执行成功
private Boolean success;
//响应业务状态码
private Integer code;
//提示信息
private String message;
//数据
private T data;
public Result(Boolean success, int code, String message) {
this.success = success;
this.code = code;
this.message = message;
}
public Result(Boolean success, int code) {
this.success = success;
this.code = code;
}
public Result(Boolean success, int code, T data) {
this.success = success;
this.code = code;
this.data = data;
}
public Result(Boolean success, int code, String message, T data) {
this.success = success;
this.code = code;
this.message = message;
this.data = data;
}
//TODO 问题 响应结果在controller方法中需要每次都new 解决:抽取静态方法代表成功 或者 失败结果
public static <T> Result<T> success() {
return new Result<T>(true, ResponseEnum.SUCCESS.getStatus());
}
public static <T> Result<T> successMessage(String message) {
return new Result<T>(true, ResponseEnum.SUCCESS.getStatus(), message);
}
public static <T> Result<T> success(T data) {
return new Result<T>(true, ResponseEnum.SUCCESS.getStatus(), data);
}
public static <T> Result<T> success(String message, T data) {
return new Result<T>(true, ResponseEnum.SUCCESS.getStatus(), message, data);
}
public static <T> Result<T> error() {
return new Result<T>(false, ResponseEnum.ERROR.getStatus(), ResponseEnum.ERROR.getMessage());
}
public static <T> Result<T> errorMessage(String errorMessage) {
return new Result<T>(false, ResponseEnum.ERROR.getStatus(), errorMessage);
}
public static <T> Result<T> errorCodeMessage(int errorCode, String errorMessage) {
return new Result<T>(false, errorCode, errorMessage);
}
}
修改项目中正常以及异常处理方法返回结果:
package com.itheima.common.exception;
import com.itheima.common.vo.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @Author: 黑马程序员
* @description:
**/
@Slf4j
@ResponseBody
@ControllerAdvice
public class BasicExceptionAdvice {
@ExceptionHandler(RuntimeException.class)
public Result handleException(RuntimeException e) {
// 我们暂定返回状态码为400, 然后从异常中获取友好提示信息
//return new Result(false, 500, e.getMessage());
return Result.errorCodeMessage(500, e.getMessage());
}
/**
* 处理自定义异常
* @return
*/
@ExceptionHandler(NcException.class)
public Result handlerNcException(NcException e){
//return new Result(false, exception.getStatus(), exception.getMessage());
return Result.errorCodeMessage(e.getStatus(), e.getMessage());
}
/**
* 处理其他异常
* @param e
* @return
*/
@ExceptionHandler(Exception.class)
public Result handlerNcException(Exception e){
//return new Result(false, 500, e.getMessage());
return Result.errorCodeMessage(500, e.getMessage());
}
}
再次修改controller中代码:
如果还有其他异常提示信息,只需要增加即可!
package com.itheima.common.enums;
import io.swagger.models.auth.In;
/**
* 异常结果枚举类型
* 本质上封装 业务状态码 提示消息
*/
public enum ResponseEnum {
//提供每一种提示消息类别
OK(200, "请求成功!"),
SUCCESS(200, "操作成功!"),
ERROR(500, "请求失败!"),
AGE_NOT_NULL(400, "年龄必须填写!!"),
INVALID_FILE_TYPE(400, "无效的文件类型!"),
INVALID_PARAM_ERROR(400, "无效的请求参数!"),
INVALID_PHONE_NUMBER(400, "无效的手机号码"),
INVALID_VERIFY_CODE(400, "验证码错误!"),
INVALID_USERNAME_PASSWORD(400, "无效的用户名和密码!"),
INVALID_SERVER_ID_SECRET(400, "无效的服务id和密钥!"),
INVALID_NOTIFY_PARAM(400, "回调参数有误!"),
INVALID_NOTIFY_SIGN(400, "回调签名有误!"),
DATA_TRANSFER_ERROR(500, "数据转换异常!"),
INSERT_OPERATION_FAIL(500, "新增操作失败!"),
UPDATE_OPERATION_FAIL(500, "更新操作失败!"),
DELETE_OPERATION_FAIL(500, "删除操作失败!"),
FILE_UPLOAD_ERROR(500, "文件上传失败!"),
DIRECTORY_WRITER_ERROR(500, "目录写入失败!"),
FILE_WRITER_ERROR(500, "文件写入失败!"),
SEND_MESSAGE_ERROR(500, "短信发送失败!"),
CODE_IMAGE_ERROR(500, "验证码错误!"),
USER_MOBILE_EXISTS(500, "该手机号已经被注册!"),
USER_NOT_REGISTER(500, "当前用户还未进行注册!"),
USER_NOT_JOIN_COMPANY(500, "请加入企业后再使用该功能!"),
USER_NOT_COMPANY_ADMIN(500, "您不是企业管理员!"),
USER_NOT_MATCH_ATTGROUP(500, "未查询到用户考勤组,请先配置!"),
USER_NOT_FOUND(500, "没有查询到用户!"),
COMPANY_ADMIN_NOT_EXISTS(500, "没有找到对应企业管理员!"),
COMPANY_NOT_FOUND(500, "企业不存在!"),
WROK_NUM_EXISTS(500, "当前工号已经存在!"),
COMPANY_USER_NOT_FOUND(404, "企业员工不存在!"),
SMS_CODE_TIMEOUT(404, "验证码超时,请重新发送!"),
PUNCH_INVALID_AREA(500, "打卡地点不在考勤点范围内!"),
PUNCH_INVALID_DAY(500, "非工作日无需打卡!"),
PUNCH_ALREADY(500, "已经打卡,无需重复打卡!"),
SIGN_DATA_NULL(404, "未检索到签到数据!"),
MESSAGE_PARAMS_LOST(500, "查询参数缺失!"),
UNAUTHORIZED(401, "登录失效或未登录!"),
FOBIDDEN(403, "禁止访问!"),
SIGNDATA_NOT_FOUND(500, "当前检索条件没有签到数据!"),
ROLE_NOT_FOUND(403, "角色列表不存在!"),
ROLE_SYS_NOT_FOUND(403, "系统管理员角色不存在!"),
SYS_PERMISSSION_NOT_FOUND(403, "当前企业无权限"),
DOC_NOT_FOUND(403, "文档不存在或者已经删除!"),
DOC_NOT_ALLOWED(403, "只有作者才能设置协作者!"),
FILE_NOT_ALLOWED_MODIFY(403, "您没有权限修改文档!"),
FILE_NOT_BELONG_YOU(403, "您不是该文档拥有者,无法设置权限!"),
;
//提供属性
private Integer status;//状态码
private String message; //提示消息
//提供私有构造
ResponseEnum(Integer status, String message) {
this.status = status;
this.message = message;
}
//提供get方法获取属性值
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
1.引入mybatisPlus依赖,mysql依赖
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- mybatisplus与springboot整合 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
2.开启MybatisPlus包扫描-扫描持久层接口
package com.itheima;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
@MapperScan("com.itheima.sys.mapper")
public class NcSysApplication {
public static void main(String[] args) {
SpringApplication.run(NcSysApplication.class, args);
}
}
3.yml文件中配置数据源,mybatisPlus配置
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/nc_sys?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
username: root
password: root
正常编写代码操作:提供实体类(PO,DTO) ,提供三层(Mapper,service,controller)完成注入,提供单表CURD代码
package com.itheima.sys.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@TableName
@Data
public class Company {
@TableId
private Long id;
private String name;
private String nameAbbr;
}
service业务层接口采用继承MybatisPlus提供统一IService,service实现了类继承ServiceImpl简化业务层代码。
package com.itheima.sys.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.sys.entity.Company;
public interface ICompanyService extends IService<Company> {
//提供单表CURD 分页方法
}
package com.itheima.sys.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.sys.entity.Company;
import com.itheima.sys.mapper.CompanyMapper;
import com.itheima.sys.service.ICompanyService;
import org.springframework.stereotype.Service;
@Service
public class CompanyServiceImpl extends ServiceImpl<CompanyMapper, Company> implements ICompanyService {
}
测试类如下:
package com.itheima.sys.service.impl;
import com.itheima.NcSysApplication;
import com.itheima.sys.entity.Company;
import com.itheima.sys.service.ICompanyService;
import jdk.nashorn.api.scripting.ScriptUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = NcSysApplication.class)
public class CompanyServiceImplTest {
@Autowired
private ICompanyService companyService;
@Test
public void findAll(){
List<Company> list = companyService.list();
System.out.println(list);
}
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。