学习目标:
- 理解SaaS的基本概念
- 了解SAAS-HRM的基本需求和开发方式掌握Power Designer的用例图
- 完成SAAS-HRM父模块及公共模块的环境搭建完成企业微服务中企业CRUD功能
1. 初识SaaS
1.1 云服务的三种模式
1.1.1 IaaS(基础设备即服务)
IaaS(Infrastructure as a Service),即基础设施即服务。提供给消费者的服务是对所有计算机基础设施的利用,包括处理CPU、内存、存储、网络和其他基本的计算资源,用户能够部署和运行任意软件,包括操作系统和应用程序。消费者不管理或控制任何云计算基础设施,但能控制操作系统的选择、存储空间、部署的应用,也有可能获得有限制的网络组建(例如路由器、防火墙、负载均衡器等)的控制。
1.1.2 PaaS(平台即服务)
Paas(Platform as a Service),即平台及服务。提供给消费者的服务是把客户端采用的开发语言和工具(例如java、python、.net等)开发的或收购的应用程序部署到供应商的云计算基础设施上去。客户不需要管理或控制底层的云基础设施,包括网络、服务器、操作系统、存储等,但客户端能控制部署的应用程序,也可能控制运行程序的托管环境配置。
1.1.3 SaaS(软件即服务)
SaaS(Software as a Service),即软件即服务。提供给消费者完整的软件解决方案,你可以从软件服务商处以租用或购买等方式获取软件应用,组织用户即可通过Internet连接到该应用(通常使用Web浏览器)。所有基础结构、中间件、应用软件和应用数据都位于服务器提供商的数据中心内。服务商负责管理硬件和软件,并根据适当的服务协议确保应用和数据的可用性和安全性。SaaS让组织能够通过最低前期成本的应用快速建成投产。
1.1.4区别和联系
1.2 SaaS的概述
1.2.1 Saas详解
SaaS(Software-as-a-service)的意思是软件即服务。简单说就是在线系统模式,即软件服务商提供的软件在线服务。
1.2.2 应用领域与行业前景
SaaS软件就适用对象而言,可以划分为针对个人的与针对企业的
面向个人的SaaS产品:在线文档,账务管理,文件管理,日程计划、照片管理、联系人管理,等等云类型的服务
而面向企业的SaaS产品主要包括:CRM(客户关系管理)、ERP(企业资源计划管理)、线上视频或者与群组通话会议、HRM(人力资源管理)、OA(办公系统)、外勤管理、财务管理、审批管理等。
1.2.3 Saas与传统软件对比
- 降低企业成本:按需购买,即租即用,无需关注软件的开发维护。
- 软件更新迭代快速:和传统软件相比,由于saas部署在云端,使得软件的更新迭代速度加快
- 支持远程办公:将数据存储到云后,用户即可通过任何连接到 Internet 的计算机或移动设备访问其信息
2. SaaS-HRM 需求分析
2.1 什么是SaaS-HRM
SaaS-HRM是基于saas模式的人力资源管理系统。他不同于传统的人力资源软件应用,使用者只需打开浏览器即可管理上百人的薪酬、绩效、社保、入职离职。
2.2 原型分析法
原型分析的理念是指在获取一组基本需求之后,快速地构造出一个能够反映用户需求的初始系统原型。让用户看到 未来系统的概貌,以 便判断哪些功能是符合要求的,哪些方面还需要改进,然后不断地对这些需求进一步补充、细化和修改。依次类推,反复进行,直到用户满意为止并由此开发出完整 的系统。
简单的说,原型分析法就是在最短的时间内,以最直观的方式获取用户最真实的需求
2.3 UML的用例图
2.3.1 UML统一建模语言
Unified Modeling Language (UML)又称统一建模语言或标准建模语言,是始于1997年一个OMG标准,它是一个支持模型化和软件系统开发的图形化语言,为软件开发的所有阶段提供模型化和可视化支持,包括由需求分析到 规格,到构造和配置。 面向对象的分析与设计(OOA&D,OOAD)方法的发展在80年代末至90年代中出现了一个高潮,UML是这个高潮的产物。它不仅统一了Booch、Rumbaugh和Jacobson的表示方法,而且对其作了进一步的 发展,并最终统一为大众所接受的标准建模语言。UML中包含很多图形(用例图,类图,状态图等等),其中用例 图是最能体现系统结构的图形
2.3.2用例图
用例图(use case)主要用来描述用户与用例之间的关联关系。说明的是谁要使用系统,以及他们使用该系统可以做些什么。一个用例图包含了多个模型元素,如系统、参与者和用例,并且显示这些元素之间的各种关系,如泛 化、关联和依赖。它展示了一个外部用户能够观察到的系统功能模型图。
3系统设计
3.1开发方式
SaaS-IHRM系统采用前后端分离的开发方式。
后端给前端提供数据,前端负责HTML渲染(可以在服务器渲染,也可以在浏览器渲染)和用户交互。双方通过文档的形式规范接口内容
3.2技术架构
(1)前端技术栈
以Node.js为核心的Vue.js前端技术生态架构
(2)后端技术栈
SpringBoot+SpringCloudAlibaba+SpringMVC+MyBatis
3.3系统结构图
执行文件启动 后,在地址栏输入http://localhost:801 即可访问API文档
4 工程搭建
4.1 前置知识点的说明
Saas-HRM系统后端采用SpringBoot+SpringCloudAlibaba+SpringMVC+MyBatis
Saas-HRM系统前端采用基于nodejs的vue框架完成编写使用element-ui组件库快速开发前端界面
4.2 开发环境要求
- JDK1.8
- 数据库mysql 5.7
- 开发工具 idea
- maven版本3.3.9
4.3 构建公共子模块
4.3.1 构建公共子模块ihrm-common
(1)导入pom.xml依赖
4.0.0
com.pyy.ihrm
ihrm-common
1.0-SNAPSHOT
IHRM-公用模块
UTF-8
1.8
1.8
1.8
2.9.2
1.5.21
1.5.21
1.2.58
1.16.16
3.8.1
org.projectlombok
lombok
${lombok.version}
io.springfox
springfox-swagger2
${swagger.version}
io.swagger
swagger-models
io.swagger
swagger-models
${swagger.model.version}
io.springfox
springfox-swagger-ui
${swagger.version}
com.alibaba
fastjson
${fastjson.version}
org.apache.commons
commons-lang3
${common.lang3.version}
org.springframework.boot
spring-boot-maven-plugin
(2)新建com.pyy.ihrm.common.response包,通用类
ResultCode.java 通用状态码
package cn.com.thtf.common.response;
/**
* ========================
* 通用响应状态
* Created with IntelliJ IDEA.
* User:pyy
* Date:2019/6/6
* Time:10:10
* Version: v1.0
* ========================
*/
public enum ResultCode {
/* 成功状态码 */
SUCCESS(200,"SUCCESS"),
/* 错误状态码 */
FAIL(500,"ERROR"),
/* 参数错误:10001-19999 */
PARAM_IS_INVALID(10001, "参数无效"),
PARAM_IS_BLANK(10002, "参数为空"),
PARAM_TYPE_BIND_ERROR(10003, "参数格式错误"),
ILLEGAL_PARAM(10004, "查询标识参数非法!(00=全部 01=结构化 02=非结构化)"),
FILE_MAX_SIZE_OVERFLOW(10005, "上传尺寸过大"),
FILE_ACCEPT_NOT_SUPPORT(10006, "上传文件格式不支持"),
SET_UP_AT_LEAST_ONE_ADMIN(10007, "至少指定一个管理员"),
URL_INVALID(10008, "地址不合法"),
LINK_AND_LOGOUT_NO_MATCH(10009, "主页地址和注销地址IP不一致"),
IP_AND_PORT_EXISTED(10010, "当前IP和端口已经被占中"),
LINK_IS_REQUIRED(10011, "生成第三方token认证信息: 主页地址不能为空,请完善信息"),
DICT_LENGTH_NOT_BLANK(10012, "字典值长度不能为空"),
PWD_NO_VALID(100013, "密码必须是6-20 位,字母、数字、字符(`~!@#$%^&*)"),
/* 用户错误:20001-29999*/
USER_NOT_LOGGED_IN(20001, "用户未登录"),
USER_LOGIN_ERROR(20002, "账号不存在或密码错误"),
USER_ACCOUNT_FORBIDDEN(20003, "账号已被禁用"),
USER_NOT_EXIST(20004, "用户不存在"),
USER_HAS_EXISTED(20005, "用户已存在"),
/* 业务错误:30001-39999 */
//BUSINESS_GROUP_NO_ALLOWED_DEL(30001, "应用分组已经被 {0} 个应用【{1}{2}】使用,不能删除"),
NAME_EXISTED(30001, "{0}名称已存在"),
CODE_EXISTED(30002, "{0}编码已存在"),
BUSINESS_OUTER_DATASOURCE_NO_ALLOWED_DEL(30002, "数据源已经被 {0} 个资源【{1}{2}】使用,不能删除"),
RESOURCE_CATEGORY_EXIST_DEPEND(30003, "当前分类下存在 {0} 个子分类【{1}{2}】,不能删除"),
RESOURCE_CATEGORY_EXIST_RESOURCE_DEPEND(30004, "资源分类已经被 {0} 个资源【{1}{2}】使用,不能删除"),
RESOURCE_CATEGORY_EXIST_TEMPLATE_DEPEND(30005, "资源分类已经被 {0} 个标签模板【{1}{2}】使用,不能删除"),
LABEL_EXIST_TEMPLATE_DEPEND_NOTALLOW_UPDATE(30006, "标签已经被 {0} 个数据资源【{1}{2}】使用,不能修改"),
LABEL_EXIST_TEMPLATE_DEPEND_NOTALLOW_DELETE(30007, "标签已经被 {0} 个数据资源【{1}{2}】使用,不能删除"),
DICT_ENGLISH_NOT_BLANK(30006, "标签值域依赖标签英文名不能为空"),
BUSINESS_IS_TOP(30006, "已经到最顶部"),
BUSINESS_IS_BOTTOM(30007, "已经到最底部"),
ONLY_ROOT_DEPARTMENT(30009, "组织机构只能存在一个根机构"),
DEPART_CODE_EXISTED(30010, "组织机构编码已存在"),
DEPART_CONTAINS_USERS(30011, "该机构下是存在 {0} 个用户 【{1}{2}】,不允许删除"),
DEPART_CONTAINS_SON(30012, "该机构下是存在子级机构,不允许删除"),
DEPART_PARENT_IS_SELF(30013, "选择的父机构不能为本身"),
DICT_EXIST_DEPEND(30014, "该字典数据存在详情依赖,不允许删除"),
DICT_DETAIL_LOCK(30015, "该字典数据被锁定,不允许修改或删除"),
DEPART_CODE_EXISTED_WITH_ARGS(30016, "组织机构编码【{0}】系统已存在"),
USER_GROUP_DEPEND_ROLE(30017, "角色被用户组关联,不允许删除"),
ROLE_NAME_EXISTS(30018, "角色名称已存在"),
USER_GROUP_NAME_EXISTS(30019, "用户组名称已存在"),
USER_GROUP_DEPEND_USER(30020, "用户组已经分配有 {0} 个用户【{1}{2}】,不能删除"),
PAGE_EXIST_DEPEND(30021, "系统页面关联有功能操作,不能删除"),
APP_EXIST_DEPEND_WITH_ARGS(30022, "当前功能菜单,已分配角色【{0}】不能删除"),
USER_EXISTED_WITH_GROUP(30017, "组织机构编码【{0}】系统已存在"),
USER_IS_ADMIN(30018, "当前用户为管理员不允许删除"),
PARENT_NODE_IS_SELF(30019, "当前节点父节点不能为自己"),
STARTTIME_ENDTIME_NOT_BLANK(30020, "生效时间和失效失效必须同时存在"),
ENDTIME_MORETHAN_STARTTIME(30021, "失效失效必须大于生效时间"),
CAS_TEMPLATE_NOT_FOUND(30022, "CAS Service 模板丢失,请至少一个CAS注册模板数据"),
LINK_LOGOUT_IP_NOT_EQUALS(30021, "主页地址和注销地址IP和端口不一致"),
DICT_TYPE_ERROR(30022, "字典类型参数不正确"),
DB_TYPE_NOT_SUPPORT(30023, "数据库类型系统不支持"),
/* 系统错误:40001-49999 */
SYSTEM_INNER_ERROR(40001, "系统繁忙,请稍后重试"),
UPLOAD_ERROR(40002, "系统异常,上传文件失败"),
/* 数据错误:50001-599999 */
RESULT_DATA_NONE(50001, "【{0}】数据未找到"),
DATA_YEAR_TO_LARGE(50002, "年份最大支持20年"),
DATA_IS_WRONG(50002, "数据有误"),
DATA_ALREADY_EXISTED(50003, "数据已存在"),
/* 接口错误:60001-69999 */
INTERFACE_INNER_INVOKE_ERROR(60001, "内部系统接口调用异常"),
INTERFACE_OUTTER_INVOKE_ERROR(60002, "外部系统接口调用异常"),
INTERFACE_FORBID_VISIT(60003, "该接口禁止访问"),
INTERFACE_ADDRESS_INVALID(60004, "接口地址无效"),
INTERFACE_REQUEST_TIMEOUT(60005, "接口请求超时"),
INTERFACE_EXCEED_LOAD(60006, "接口负载过高"),
/* 权限错误 */
PERMISSION_UNAUTHENTICATED(70001,"此操作需要登陆系统!"),
PERMISSION_UNAUTHORISE(70002,"权限不足,无权操作!"),
PERMISSION_EXPIRE(401,"登录状态过期!"),
PERMISSION_LIMIT(70004, "访问次数受限制");
//操作代码
int code;
//提示信息
String message;
ResultCode(int code, String message){
this.code = code;
this.message = message;
}
public int code() {
return code;
}
public String message() {
return message;
}
public void setCode(int code) {
this.code = code;
}
public void setMessage(String message) {
this.message = message;
}
}
Result.java 响应结果
package com.pyy.ihrm.common.response;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
/**
* ========================
* 统一响应结果集
* Created with IntelliJ IDEA.
* User:pyy
* Date:2019/6/6
* Time:10:10
* Version: v1.0
* ========================
*/
@JsonInclude(JsonInclude.Include.NON_NULL) // 非空数据不显示
@Setter
@Getter
@ApiModel(value = "Result",description = "API接口返回值")
public class Result {
@ApiModelProperty("操作编码")
int code;
@ApiModelProperty("响应信息")
String message;
@ApiModelProperty("结果数据")
T data;
@ApiModelProperty("请求响应时间戳")
Long timestamp;
public Result(ResultCode resultCode){
this.code = resultCode.code();
this.message = resultCode.message();
this.timestamp = System.currentTimeMillis();
}
public Result(int code, String msg){
this.code = code;
this.message = msg;
this.timestamp = System.currentTimeMillis();
}
public Result(ResultCode resultCode, T data){
this.code = resultCode.code();
this.message = resultCode.message();
this.data = data;
this.timestamp = System.currentTimeMillis();
}
public static Result SUCCESS(){
return new Result(ResultCode.SUCCESS);
}
public static Result SUCCESS(T data){
return new Result(ResultCode.SUCCESS, data);
}
public static Result FAIL(){
return new Result(ResultCode.FAIL);
}
}
QueryResult.java 查询结果
package com.pyy.ihrm.common.response;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.*;
import java.util.List;
/**
* ========================
* 查询结果
* Created with IntelliJ IDEA.
* User:pyy
* Date:2019/6/6
* Time:10:10
* Version: v1.0
* ========================
*/
@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(value = "QueryResult",description = "分页结果")
public class QueryResult {
@ApiModelProperty("数据列表")
private List list;
@ApiModelProperty("数据总数")
private Long total;
}
(3)新建com.pyy.ihrm.common.utils.SnowflakeId,分布式ID生成器
目前微服务架构盛行,在分布式系统中的操作中都会有一些全局性ID的需求,所以我们不能使用数据库本身的自增 功能来产生主键值,只能由程序来生成唯一的主键值。我们采用的是开源的twitter( 非官方中文惯称:推特.是国外的一个网站,是一个社交网络及微博客服务) 的snowflake (雪花)算法。
各个段解析:
分段 | 作用 | 说明 |
---|---|---|
1bit | 保留(不用) | — |
41bit | 时间戳,精确到毫秒 | 最多可以支持69年的跨度 |
5bit | 机器id | 最多支持2的5次方(32)个节点 |
5bit | 业务编码 | 最多支持2的5次方(32)个节点 |
12bit | 毫秒内的计数器 | 每个节点每毫秒最多产生2的12次方(4096)个id |
默认情况下41bit的时间戳可以支持该算法使用到2082年,10bit的工作机器id可以支持1024台机器,序列号 支持1毫秒产生4096个自增序列id . SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID 左右
package com.pyy.ihrm.common.utils;
import java.util.Random;
public class SnowflakeId {
// ==============================Fields===========================================
/**
* 开始时间截 (2015-01-01)
*/
private static final long TWEPOCH = 1420041600000L;
/**
* 机器id所占的位数
*/
private static final long WORKER_ID_BITS = 5L;
/**
* 数据标识id所占的位数
*/
private static final long DATA_CENTER_ID_BITS = 5L;
/**
* 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
*/
private static final long MAX_WORKER_ID = -1L ^ (-1L << WORKER_ID_BITS);
/**
* 支持的最大数据标识id,结果是31
*/
private static final long MAX_DATA_CENTER_ID = -1L ^ (-1L << DATA_CENTER_ID_BITS);
/**
* 序列在id中占的位数
*/
private static final long SEQUENCE_BITS = 12L;
/**
* 机器ID向左移12位
*/
private static final long WORKER_ID_SHIFT = SEQUENCE_BITS;
/**
* 数据标识id向左移17位(12+5)
*/
private static final long DATA_CENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
/**
* 时间截向左移22位(5+5+12)
*/
private static final long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATA_CENTER_ID_BITS;
/**
* 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)
*/
private static final long SEQUENCE_MASK = -1L ^ (-1L << SEQUENCE_BITS);
/**
* 工作机器ID(0~31)
*/
private static long workerId;
/**
* 数据中心ID(0~31)
*/
private static long datacenterId;
/**
* 毫秒内序列(0~4095)
*/
private static long sequence = 0L;
/**
* 上次生成ID的时间截
*/
private static long lastTimestamp = -1L;
//==============================Constructors=====================================
public SnowflakeId() {
}
public static SnowflakeId getInstance(){
int workerId=new Random().nextInt(30);
int datacenterId=new Random().nextInt(30);
return new SnowflakeId(workerId,datacenterId);
}
/**
* 构造函数
*
* @param workerId 工作ID (0~31)
* @param datacenterId 数据中心ID (0~31)
*/
private SnowflakeId(long workerId, long datacenterId) {
if (workerId > MAX_WORKER_ID || workerId < 0) {
throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", MAX_WORKER_ID));
}
if (datacenterId > MAX_DATA_CENTER_ID || datacenterId < 0) {
throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", MAX_DATA_CENTER_ID));
}
SnowflakeId.workerId = workerId;
SnowflakeId.datacenterId = datacenterId;
}
// ==============================Methods==========================================
/**
* 获得下一个ID (该方法是线程安全的)
*
* @return SnowflakeId
*/
public static synchronized long getId() {
long timestamp = timeGen();
//如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
if (timestamp < lastTimestamp) {
throw new RuntimeException(
String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
}
//如果是同一时间生成的,则进行毫秒内序列
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & SEQUENCE_MASK;
//毫秒内序列溢出
if (sequence == 0) {
//阻塞到下一个毫秒,获得新的时间戳
timestamp = tilNextMillis(lastTimestamp);
}
}
//时间戳改变,毫秒内序列重置
else {
sequence = 0L;
}
//上次生成ID的时间截
lastTimestamp = timestamp;
//移位并通过或运算拼到一起组成64位的ID
return ((timestamp - TWEPOCH) << TIMESTAMP_LEFT_SHIFT)
| (datacenterId << DATA_CENTER_ID_SHIFT)
| (workerId << WORKER_ID_SHIFT)
| sequence;
}
/**
* 阻塞到下一个毫秒,直到获得新的时间戳
*
* @param lastTimestamp 上次生成ID的时间截
* @return 当前时间戳
*/
protected static long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
/**
* 返回以毫秒为单位的当前时间
*
* @return 当前时间(毫秒)
*/
protected static long timeGen() {
return System.currentTimeMillis();
}
public static void main(String[] args) {
Long i = getId();
System.out.println(i);
}
}
4.3.2 搭建公共的实体类模块 ihrm_common_model
引入pom.xml
4.0.0
com.pyy.ihrm
ihrm-common-model
1.0-SNAPSHOT
IHRM-公共的实体类模块
UTF-8
1.8
1.8
1.8
2.9.2
1.5.21
1.5.21
1.2.58
com.pyy.ihrm
ihrm-common
1.0-SNAPSHOT
org.springframework.boot
spring-boot-maven-plugin
5企业微服务-企业CRUD
5.1模块搭建
(1)搭建企业微服务模块ihrm-company, pom.xml引入依赖
4.0.0
com.pyy.ihrm
ihrm-company
1.0-SNAPSHOT
企业微服务
org.springframework.boot
spring-boot-starter-parent
2.1.7.RELEASE
UTF-8
1.8
1.8
1.8
1.1.16
5.1.16
2.1.5
1.2.10
com.pyy.ihrm
ihrm-common
1.0-SNAPSHOT
com.pyy.ihrm
ihrm-common-model
1.0-SNAPSHOT
org.springframework.boot
spring-boot-starter-web
org.springframework
spring-aspects
mysql
mysql-connector-java
${mysql.version}
com.alibaba
druid-spring-boot-starter
${druid.version}
tk.mybatis
mapper-spring-boot-starter
${tk.mybatis.version}
com.github.pagehelper
pagehelper-spring-boot-starter
${pagehelper.version}
${project.artifactId}-${project.version}
org.springframework.boot
spring-boot-maven-plugin
src/main/resources
true
**/*Mapper.xml
**/*.yml
**/*.properties
**/*.html
**/*.xml
(2)添加配置文件
application.yml
server:
port: 9001
spring:
application:
name: ihrm-company #指定服务名
profiles:
active: dev
application-dev.yml
spring:
datasource:
#1.JDBC
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/ihrm?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
username: root
password: 123456
druid:
#2.连接池配置
#初始化连接池的连接数量 大小,最小,最大
initial-size: 5
min-idle: 5
max-active: 20
#配置获取连接等待超时的时间
max-wait: 60000
#配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
time-between-eviction-runs-millis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
min-evictable-idle-time-millis: 30000
validation-query: SELECT 1 FROM DUAL
test-while-idle: true
test-on-borrow: true
test-on-return: false
# 是否缓存preparedStatement,也就是PSCache 官方建议MySQL下建议关闭 个人建议如果想用SQL防火墙 建议打开
pool-prepared-statements: true
max-pool-prepared-statement-per-connection-size: 20
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
filter:
stat:
merge-sql: true
slow-sql-millis: 5000
#3.基础监控配置
web-stat-filter:
enabled: true
url-pattern: /*
#设置不统计哪些URL
exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"
session-stat-enable: true
session-stat-max-count: 100
stat-view-servlet:
enabled: true
url-pattern: /druid/*
reset-enable: true
#设置监控页面的登录名和密码
login-username: admin
login-password: admin
allow: 127.0.0.1
#deny: 192.168.1.100
#分页插件配置
pagehelper:
helperDialect: mysql
#分页合理化参数,默认值为false。当该参数设置为 true 时,pageNum<=0 时会查询第一页, pageNum>pages(超过总数时),会查询最后一页。默认false 时,直接根据参数进行查询
reasonable: true
#mybatis配置
mybatis:
type-aliases-package: com.pyy.ihrm.company.po
mapper-locations: 'classpath:mappers/*.xml'
configuration:
map-underscore-to-camel-case: true
(3)配置启动类
package com.pyy.ihrm.company;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import tk.mybatis.spring.annotation.MapperScan;
/**
* ========================
* 项目启动类
* Created with IntelliJ IDEA.
* User:pyy
* Date:2019/11/11 17:16
* Version: v1.0
* ========================
*/
@SpringBootApplication
@MapperScan(basePackages = {"com.pyy.ihrm.company.mapper"})
@EnableTransactionManagement
@EnableAspectJAutoProxy
@EnableScheduling
public class CompanyApplication {
public static void main(String[] args) {
SpringApplication.run(CompanyApplication.class, args);
}
}
(4)启动SpringBoot项目访问druid, http://localhost:tomcat端口号/项目名称/druid/
通过上面配置的用户名和密码登录:admin/admin,登录后可以查询数据源配置信息
5.2 公共异常处理
为了使我们的代码更容易维护,同时给用户最好的用户体验,有必要对系统中可能出现的异常进行处理。spring提供了@ControllerAdvice注解和@ExceptionHandler可以很好的在控制层对异常进行统一处理
(1)添加自定义的异常
package com.pyy.ihrm.company.exception;
import com.pyy.ihrm.common.response.ResultCode;
import lombok.Getter;
import lombok.Setter;
import java.text.MessageFormat;
/**
* ========================
* 自定义异常类
* Created with IntelliJ IDEA.
* User:pyy
* Date:2019/11/11 17:16
* Version: v1.0
* ========================
*/
@Setter
@Getter
public class CustomException extends RuntimeException {
//错误代码
private ResultCode resultCode;
private int code;
private String message;
public CustomException(ResultCode resultCode){
super(resultCode.message());
this.resultCode = resultCode;
}
public CustomException(ResultCode resultCode, Object... args){
code = resultCode.code();
message = MessageFormat.format(resultCode.message(), args);
}
}
(2)配置公共异常处理
package com.pyy.ihrm.company.exception.handler;
import com.pyy.ihrm.common.response.Result;
import com.pyy.ihrm.common.response.ResultCode;
import com.pyy.ihrm.company.exception.CustomException;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.List;
/**
* 全局异常处理器
* @author pyy
*/
@RestControllerAdvice
public class GlobalExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
/**
* 处理自定义异常
*/
@ExceptionHandler(CustomException.class)
public Result handleException(CustomException e) {
if (e.getResultCode() != null) {
log.error("### 异常信息:code={}, msg={} ###", e.getResultCode().code(), e.getResultCode().message());
return new Result(e.getResultCode());
} else {
log.error("### 异常信息:code={}, msg={} ###", e.getCode(), e.getMessage());
return new Result(e.getCode(), e.getMessage());
}
}
/**
* 参数错误异常
*/
@ExceptionHandler({MethodArgumentNotValidException.class, BindException.class})
public Result handleException(Exception e) {
StringBuffer errorMsg = new StringBuffer();
if (e instanceof MethodArgumentNotValidException) {
MethodArgumentNotValidException validException = (MethodArgumentNotValidException) e;
BindingResult result = validException.getBindingResult();
if (result.hasErrors()) {
List errors = result.getAllErrors();
errors.forEach(p ->{
FieldError fieldError = (FieldError) p;
errorMsg.append(fieldError.getDefaultMessage()).append(",");
log.error("### 请求参数错误:{"+fieldError.getObjectName()+"},field{"+fieldError.getField()+ "},errorMessage{"+fieldError.getDefaultMessage()+"} ###"); });
}
} else if (e instanceof BindException) {
BindException bindException = (BindException)e;
if (bindException.hasErrors()) {
log.error("### 请求参数错误: {} ###", bindException.getAllErrors());
}
}
return new Result(ResultCode.PARAM_IS_INVALID.code(), StringUtils.removeEnd(errorMsg.toString(), ","));
}
/**
* 处理所有不可知的异常
*/
@ExceptionHandler(Exception.class)
public Result handleOtherException(Exception e){
//打印异常堆栈信息
e.printStackTrace();
// 打印异常信息
log.error("### 不可知的异常 e=:{} ###", e.getMessage());
return new Result(ResultCode.SYSTEM_INNER_ERROR);
}
}
(3)swagger配置
package com.pyy.ihrm.company.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* ========================
* swagger 配置
* Created with IntelliJ IDEA.
* User:pyy
* Date:2019/10/12 15:00
* Version: v1.0
* ========================
*/
@Configuration
@EnableSwagger2
//@Profile({"local", "dev", "test"})
public class Swagger2Configuration {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.pyy.ihrm"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("企业管理后台api文档")
.description("企业管理后台api文档")
.version("1.0")
.build();
}
}
(4)跨域配置
跨域是什么?浏览器从一个域名的网页去请求另一个域名的资源时,域名、端口、协议任一不同,都是跨域 。我们是采用前后端分离开发的,也是前后端分离部署的,必然会存在跨域问题。
package com.pyy.ihrm.company.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
/**
* ========================
* 跨域配置
* Created with IntelliJ IDEA.
* User:pyy
* Date:2019/10/12 15:00
* Version: v1.0
* ========================
*/
@Configuration
public class CorsConfig {
private CorsConfiguration buildConfig() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOrigin("*"); // 1允许任何域名使用
corsConfiguration.addAllowedHeader("*"); // 2允许任何头
corsConfiguration.addAllowedMethod("*"); // 3允许任何方法(post、get等)
return corsConfiguration;
}
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", buildConfig()); // 4
return new CorsFilter(source);
}
}
————————————————
版权声明:本文为CSDN博主「小飞侠v科比」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/kai46385076/article/details/91348949
5.3 企业管理-CRUD
5.3.1 表结构分析
drop table if exists company;
/*==============================================================*/
/* Table: company */
/*==============================================================*/
create table company
(
id varchar(40) not null comment 'ID',
name varchar(255) comment '公司名称',
balance decimal comment '当前余额',
state tinyint comment '状态',
audit_state varchar(255) comment '审核状态',
remarks text comment '备注',
industry varchar(255) comment '所属行业',
company_size varchar(255) comment '公司规模',
mailbox varchar(255) comment '邮箱',
company_phone varchar(255) comment '公司电话',
legal_representative varchar(255) comment '法人代表',
business_license_id varchar(255) comment '营业执照-图片ID',
company_address varchar(255) comment '公司地址',
company_area varchar(255) comment '公司地区',
expiration_date datetime comment '到期时间',
renewal_date datetime comment '续期时间',
version varchar(255) comment '当前版本',
manager_id varchar(255) comment '企业登录账号ID',
create_id varchar(40) comment '创建人ID',
create_name varchar(40) comment '创建人名称',
create_time datetime comment '创建时间',
update_id varchar(40) comment '更新人ID',
update_name varchar(40) comment '更新人名称',
update_time datetime comment '更新时间',
is_deleted tinyint comment '删除标记 0:正常 1:删除',
primary key (id)
);
alter table company comment '企业信息';
5.3.2 完成企业增删改查操作
(1)在ihrm-company工程下创建 实体PO类
package com.pyy.ihrm.company.po;
import lombok.Data;
import javax.persistence.Id;
import javax.persistence.Table;
import java.io.Serializable;
/**
* ---------------------------
* 企业信息 (Company)
* ---------------------------
* 作者:
* 时间: 2019-11-12 10:10:10
* 版本: v1.0
* ---------------------------
*/
@Data
@Table(name = "company")
public class Company implements Serializable{
private static final long serialVersionUID = 8294633490615151005L;
/** ID */
@Id
private String id;
/** 公司名称 */
private String name;
/** 当前余额 */
private Double balance;
/** 状态 */
private Integer state;
/** 审核状态 */
private String auditState;
/** 备注 */
private String remarks;
/** 所属行业 */
private String industry;
/** 公司规模 */
private String companySize;
/** 邮箱 */
private String mailbox;
/** 公司电话 */
private String companyPhone;
/** 法人代表 */
private String legalRepresentative;
/** 营业执照-图片ID */
private String businessLicenseId;
/** 公司地址 */
private String companyAddress;
/** 公司地区 */
private String companyArea;
/** 到期时间 */
private java.util.Date expirationDate;
/** 续期时间 */
private java.util.Date renewalDate;
/** 当前版本 */
private String version;
/** 企业登录账号ID */
private String managerId;
/** 创建人ID */
private String createId;
/** 创建人名称 */
private String createName;
/** 创建时间 */
private java.util.Date createTime;
/** 更新人ID */
private String updateId;
/** 更新人名称 */
private String updateName;
/** 更新时间 */
private java.util.Date updateTime;
/** 删除标记 0:正常 1:删除 */
private Integer isDeleted;
}
(2)在 ihrm-common-model工程中创建 CompanyQueryConditionVO.java, CompanySaveOrUpdateVO.java,CompanyVO.java
这里之所以将VO类放在model工程中,原因是因为后面整合其他微服务时,会用到这些公共VO类。
CompanyQueryConditionVO.java
package com.pyy.ihrm.domain.company.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
/**
* ---------------------------
* 模糊查询条件VO
* ---------------------------
* 作者:
* 时间: 2019-11-12 10:10:10
* 版本: v1.0
* ---------------------------
*/
@Data
@ApiModel(value = "CompanyQueryConditionVO",description = "企业信息类")
public class CompanyQueryConditionVO {
@ApiModelProperty("名称")
private String name;
}
CompanySaveOrUpdateVO.java
package com.pyy.ihrm.domain.company.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* ---------------------------
* 企业信息 (Company)
* ---------------------------
* 作者:
* 时间: 2019-11-12 10:10:10
* 版本: v1.0
* ---------------------------
*/
@Data
@ApiModel(value = "Company",description = "企业信息保存和修改VO类")
public class CompanySaveOrUpdateVO {
@ApiModelProperty("ID")
private String id;
@ApiModelProperty("公司名称")
private String name;
@ApiModelProperty("当前余额")
private Double balance;
@ApiModelProperty("状态")
private Integer state;
@ApiModelProperty("审核状态")
private String auditState;
@ApiModelProperty("备注")
private String remarks;
@ApiModelProperty("所属行业")
private String industry;
@ApiModelProperty("公司规模")
private String companySize;
@ApiModelProperty("邮箱")
private String mailbox;
@ApiModelProperty("公司电话")
private String companyPhone;
@ApiModelProperty("法人代表")
private String legalRepresentative;
@ApiModelProperty("营业执照-图片ID")
private String businessLicenseId;
@ApiModelProperty("公司地址")
private String companyAddress;
@ApiModelProperty("公司地区")
private String companyArea;
@ApiModelProperty("到期时间")
private java.util.Date expirationDate;
@ApiModelProperty("续期时间")
private java.util.Date renewalDate;
@ApiModelProperty("当前版本")
private String version;
@ApiModelProperty("企业登录账号ID")
private String managerId;
@ApiModelProperty("操作人ID")
private String operaterId;
@ApiModelProperty("操作人名称")
private String operaterName;
}
CompanyVO.java
package com.pyy.ihrm.domain.company.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* ---------------------------
* 企业信息 (Company)
* ---------------------------
* 作者:
* 时间: 2019-11-12 10:10:10
* 版本: v1.0
* ---------------------------
*/
@Data
@ApiModel(value = "CompanyVO",description = "企业信息类")
public class CompanyVO {
@ApiModelProperty("ID")
private String id;
@ApiModelProperty("公司名称")
private String name;
@ApiModelProperty("当前余额")
private Double balance;
@ApiModelProperty("状态")
private Integer state;
@ApiModelProperty("审核状态")
private String auditState;
@ApiModelProperty("备注")
private String remarks;
@ApiModelProperty("所属行业")
private String industry;
@ApiModelProperty("公司规模")
private String companySize;
@ApiModelProperty("邮箱")
private String mailbox;
@ApiModelProperty("公司电话")
private String companyPhone;
@ApiModelProperty("法人代表")
private String legalRepresentative;
@ApiModelProperty("营业执照-图片ID")
private String businessLicenseId;
@ApiModelProperty("公司地址")
private String companyAddress;
@ApiModelProperty("公司地区")
private String companyArea;
@ApiModelProperty("到期时间")
private java.util.Date expirationDate;
@ApiModelProperty("续期时间")
private java.util.Date renewalDate;
@ApiModelProperty("当前版本")
private String version;
@ApiModelProperty("企业登录账号ID")
private String managerId;
@ApiModelProperty("创建人ID")
private String createId;
@ApiModelProperty("创建人名称")
private String createName;
@ApiModelProperty("创建时间")
private java.util.Date createTime;
@ApiModelProperty("更新人ID")
private String updateId;
@ApiModelProperty("更新人名称")
private String updateName;
@ApiModelProperty("更新时间")
private java.util.Date updateTime;
@ApiModelProperty("删除标记 0:正常 1:删除")
private Integer isDeleted;
}
(3)持久层Mapper(dao)
package com.pyy.ihrm.company.mapper;
import java.util.List;
import com.pyy.ihrm.company.po.Company;
import com.pyy.ihrm.domain.company.vo.CompanyQueryConditionVO;
import tk.mybatis.mapper.common.Mapper;
/**
* ---------------------------
* 企业信息 (CompanyMapper)
* ---------------------------
* 作者:
* 时间: 2019-11-12 10:10:10
* 版本: v1.0
* ---------------------------
*/
public interface CompanyMapper extends Mapper{
List selectByPageAndParam(CompanyQueryConditionVO queryConditionVO);
}
(4)业务逻辑层(service)
CompanyService.java
package com.pyy.ihrm.company.service;
import com.pyy.ihrm.common.response.QueryResult;
import com.pyy.ihrm.domain.company.vo.CompanyQueryConditionVO;
import com.pyy.ihrm.domain.company.vo.CompanySaveOrUpdateVO;
import com.pyy.ihrm.domain.company.vo.CompanyVO;
import java.util.List;
/**
* ---------------------------
* 企业信息 (CompanyService)
* ---------------------------
* 作者:
* 时间: 2019-11-12 10:10:10
* 版本: v1.0
* ---------------------------
*/
public interface CompanyService {
/**
* 企业信息保存
* @param companySaveOrUpdateVO
*/
void save(CompanySaveOrUpdateVO companySaveOrUpdateVO);
/**
* 企业信息修改
* @param id
* @param companySaveOrUpdateVO
*/
void update(String id, CompanySaveOrUpdateVO companySaveOrUpdateVO);
/**
* 企业信息删除
* @param id
*/
void delete(String id, String userId, String username);
/**
* 根据企业信息ID查询
* @param id
*/
CompanyVO findById(String id);
/**
* 企业信息模糊查询
* @param queryConditionVO
* @return
*/
List listByParams(CompanyQueryConditionVO queryConditionVO);
/**
* 企业信息分页模糊查询
* @param queryConditionVO
* @param page
* @param size
* @return
*/
QueryResult listByPageAndParams(CompanyQueryConditionVO queryConditionVO, Integer page, Integer size);
}
CompanyServiceImpl.java
package com.pyy.ihrm.company.service.impl;
import com.alibaba.fastjson.JSON;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.pyy.ihrm.common.response.QueryResult;
import com.pyy.ihrm.common.response.ResultCode;
import com.pyy.ihrm.common.utils.SnowflakeId;
import com.pyy.ihrm.company.constants.CommonConstants;
import com.pyy.ihrm.company.exception.CustomException;
import com.pyy.ihrm.company.mapper.CompanyMapper;
import com.pyy.ihrm.company.po.Company;
import com.pyy.ihrm.company.service.CompanyService;
import com.pyy.ihrm.company.utils.UserUtil;
import com.pyy.ihrm.domain.company.vo.CompanyQueryConditionVO;
import com.pyy.ihrm.domain.company.vo.CompanySaveOrUpdateVO;
import com.pyy.ihrm.domain.company.vo.CompanyVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* ---------------------------
* 企业信息 (CompanyServiceImpl)
* ---------------------------
* 作者:
* 时间: 2019-11-12 10:10:10
* 版本: v1.0
* ---------------------------
*/
@Slf4j
@Service
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT,timeout=36000,rollbackFor=Exception.class)
public class CompanyServiceImpl implements CompanyService {
@Autowired
private CompanyMapper companyMapper;
/**
* 企业信息保存
* @param companySaveOrUpdateVO
*/
@Override
public void save(CompanySaveOrUpdateVO companySaveOrUpdateVO) {
// 保存企业信息
Company companyModel = new Company();
BeanUtils.copyProperties(companySaveOrUpdateVO, companyModel);
companyModel.setId(SnowflakeId.getId() + "");
companyModel.setCreateId(companySaveOrUpdateVO.getOperaterId());
companyModel.setCreateName(companySaveOrUpdateVO.getOperaterName());
companyModel.setCreateTime(new Date());
companyModel.setIsDeleted(CommonConstants.UN_DELETED);
companyMapper.insert(companyModel);
log.info("### 企业信息保存成功 ###");
}
/**
* 企业信息修改
* @param id
* @param companySaveOrUpdateVO
*/
@Override
public void update(String id, CompanySaveOrUpdateVO companySaveOrUpdateVO) {
Company companyModel = companyMapper.selectByPrimaryKey(id);
if (companyModel == null) {
throw new CustomException(ResultCode.RESULT_DATA_NONE, "企业信息");
}
// 修改
BeanUtils.copyProperties(companySaveOrUpdateVO, companyModel);
companyModel.setUpdateTime(new Date());
companyModel.setUpdateId(companySaveOrUpdateVO.getOperaterId());
companyModel.setUpdateName(companySaveOrUpdateVO.getOperaterName());
companyMapper.updateByPrimaryKey(companyModel);
log.info("### 企业信息修改成功 ###");
}
/**
* 企业信息删除
* @param id
* @param userId
* @param username
*/
@Override
public void delete(String id, String userId, String username) {
Company companyModel = companyMapper.selectByPrimaryKey(id);
if (companyModel == null) {
throw new CustomException(ResultCode.RESULT_DATA_NONE, "企业信息,id=" + id);
}
// 逻辑删除
companyModel.setId(id);
companyModel.setUpdateId(userId);
companyModel.setUpdateName(username);
companyModel.setUpdateTime(new Date());
companyModel.setIsDeleted(CommonConstants.DELETED);
companyMapper.updateByPrimaryKeySelective(companyModel);
log.info("### 企业信息逻辑删除成功 ###");
}
/**
* 根据企业信息ID查询
* @param id
*/
@Override
public CompanyVO findById(String id) {
Company companyModel = companyMapper.selectByPrimaryKey(id);
if (companyModel == null) {
throw new CustomException(ResultCode.RESULT_DATA_NONE, "企业信息,id=" + id);
}
log.info("### 企业信息查询成功, company={}###", JSON.toJSONString(companyModel));
// model转换vo
CompanyVO companyVO = new CompanyVO();
BeanUtils.copyProperties(companyModel, companyVO);
log.info("### 企业信息Model转换VO成功, companyVO={}###", companyVO);
return companyVO;
}
/**
* 企业信息模糊查询
* @param queryConditionVO
* @return
*/
@Override
public List listByParams(CompanyQueryConditionVO queryConditionVO) {
List companyList = companyMapper.selectByPageAndParam(queryConditionVO);
log.info("### 企业信息Model模糊查询完毕,总条数:{}条###", companyList.size());
// 企业信息Model转换VO数据完毕
List companyVOList = new ArrayList<>();
companyList.forEach(company -> {
CompanyVO companyVO = new CompanyVO();
BeanUtils.copyProperties(company, companyVO);
companyVOList.add(companyVO);
});
log.info("### 企业信息Model转换VO数据完毕###");
return companyVOList;
}
/**
* 企业信息分页模糊查询
* @param queryConditionVO
* @param page
* @param size
* @return
*/
@Override
public QueryResult listByPageAndParams(CompanyQueryConditionVO queryConditionVO, Integer page, Integer size) {
// 分页查询
PageHelper.startPage(page, size);
List companyList = companyMapper.selectByPageAndParam(queryConditionVO);
// 获取分页后数据
PageInfo pageInfo = new PageInfo<>(companyList);
log.info("### 企业信息分页查询完毕,总条数:{} ###", pageInfo.getTotal());
List companyVOList = new ArrayList<>();
// 补全数据
companyList.forEach(company -> {
CompanyVO companyVO = new CompanyVO();
BeanUtils.copyProperties(company, companyVO);
companyVOList.add(companyVO);
});
log.info("### 企业信息Model转换VO数据完毕###");
// 封装需要返回的实体数据
QueryResult queryResult = new QueryResult();
queryResult.setTotal(pageInfo.getTotal());
queryResult.setList(companyVOList);
return queryResult;
}
}
(5)业务控制层(CompanyController)
package com.pyy.ihrm.company.controller;
import com.pyy.ihrm.common.response.QueryResult;
import com.pyy.ihrm.common.response.Result;
import com.pyy.ihrm.company.service.CompanyService;
import com.pyy.ihrm.domain.company.vo.CompanyQueryConditionVO;
import com.pyy.ihrm.domain.company.vo.CompanySaveOrUpdateVO;
import com.pyy.ihrm.domain.company.vo.CompanyVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
/**
* ---------------------------
* 企业信息 (CompanyController)
* ---------------------------
* 作者:
* 时间: 2019-11-12 10:10:10
* 版本: v1.0
* ---------------------------
*/
@Api(tags = "CompanyController", description = "企业信息相关接口")
@RestController
@RequestMapping(value = "/v1", produces = MediaType.APPLICATION_JSON_UTF8_VALUE )
public class CompanyController {
@Autowired
private CompanyService companyService;
/**
* 保存企业信息
* @param record
* @return
*/
@ApiOperation(value = "保存企业信息", notes = "创建新企业信息")
@ApiImplicitParam(name = "record", value = "企业信息对象", required = true, dataType = "Company", paramType = "body")
@PostMapping("/company")
public Result save(@Valid @RequestBody CompanySaveOrUpdateVO record) {
// 后面集成JWT后完善
String userId = "admin";
String username = "admin";
record.setUserId(userId);
record.setUsername(username);
companyService.save(record);
return Result.SUCCESS();
}
/**
* 修改企业信息
* @param id
* @param record
* @return
*/
@ApiOperation(value = "修改企业信息", notes = "根据ID修改企业信息")
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "企业信息ID", required = true, dataType = "String", paramType = "path"),
@ApiImplicitParam(name = "record", value = "企业信息对象", required = true, dataType = "Company", paramType = "body")
})
@PutMapping("/company/{id}")
public Result update(@Valid @PathVariable(value = "id") String id, @RequestBody CompanySaveOrUpdateVO record) {
// 后面集成JWT后完善
String userId = "admin";
String username = "admin";
record.setUserId(userId);
record.setUsername(username);
companyService.update(id, record);
return Result.SUCCESS();
}
/**
* 删除企业信息
* @param id
* @return
*/
@ApiOperation(value = "删除企业信息", notes = "根据ID企业信息")
@ApiImplicitParam(name = "id", value = "企业信息ID", required = true, dataType = "String", paramType = "path")
@DeleteMapping("/company/{id}")
public Result delete(@Valid @PathVariable(value = "id") String id) {
// 后面集成JWT后完善
String userId = "admin";
String username = "admin";
companyService.delete(id, userId, username);
return Result.SUCCESS();
}
/**
* 根据ID查询企业信息
* @param id
* @return
*/
@ApiOperation(value = "企业信息查询", notes = "根据ID企业信息查询")
@ApiImplicitParam(name = "id", value = "企业信息ID", required = true, dataType = "String", paramType = "path")
@GetMapping("/company")
public Result findById(@Valid @PathVariable(value = "id") String id) {
CompanyVO queryResult = companyService.findById(id);
return Result.SUCCESS(queryResult);
}
/**
* 企业信息模糊查询
* @param queryConditionVO
* @return
*/
@ApiOperation(value = "企业信息模糊查询", notes = "企业信息不带分页模糊查询")
@GetMapping("/companys")
public Result> listByParams(CompanyQueryConditionVO queryConditionVO) {
List queryResult = companyService.listByParams(queryConditionVO);
return Result.SUCCESS(queryResult);
}
/**
* 企业信息分页模糊查询
* @param queryConditionVO
* @param page
* @param size
* @return
*/
@ApiOperation(value = "企业信息分页查询", notes = "企业信息分页模糊查询")
@ApiImplicitParams({
@ApiImplicitParam(name = "page", value = "当前页码", required = true, dataType = "int", paramType = "query"),
@ApiImplicitParam(name = "size", value = "分页尺寸", required = true, dataType = "int", paramType = "query")
})
@GetMapping("/companys/page")
public Result> listByPageAndParams(CompanyQueryConditionVO queryConditionVO,
@RequestParam("page") Integer page,
@RequestParam("size") Integer size) {
QueryResult queryResult = companyService.listByPageAndParams(queryConditionVO, page, size);
return Result.SUCCESS(queryResult);
}
}
5.4 部门管理-CRUD
5.4.1 表结构分析
drop table if exists department;
/*==============================================================*/
/* Table: department */
/*==============================================================*/
create table department
(
id varchar(40) not null comment 'ID',
parent_id varchar(40) comment '父级ID',
company_id varchar(40) comment '企业ID',
code varchar(255) comment '部门编码',
name varchar(255) comment '部门名称',
manager_id varchar(255) comment '负责人ID',
manager_name varchar(255) comment '负责人名称',
introduce text comment '介绍',
create_id varchar(40) comment '创建人ID',
create_name varchar(40) comment '创建人名称',
create_time datetime comment '创建时间',
update_id varchar(40) comment '更新人ID',
update_name varchar(40) comment '更新人名称',
update_time datetime comment '更新时间',
is_deleted tinyint comment '删除标记 0:正常 1:删除',
primary key (id)
);
alter table department comment '部门';
5.3.2 完成部门增删改查操作
(1)在ihrm-company工程下创建 实体PO类
package com.pyy.ihrm.company.po;
import lombok.Data;
import javax.persistence.Id;
import javax.persistence.Table;
/**
* ---------------------------
* 部门 (Department)
* ---------------------------
* 作者:
* 时间: 2019-11-13 10:03:44
* 版本: v1.0
* ---------------------------
*/
@Data
@Table(name = "department")
public class Department {
/** ID */
@Id
private String id;
/** 父级ID */
private String parentId;
/** 企业ID */
private String companyId;
/** 部门编码 */
private String code;
/** 部门名称 */
private String name;
/** 负责人ID */
private String managerId;
/** 负责人名称 */
private String managerName;
/** 介绍 */
private String introduce;
/** 创建人ID */
private String createId;
/** 创建人名称 */
private String createName;
/** 创建时间 */
private java.util.Date createTime;
/** 更新人ID */
private String updateId;
/** 更新人名称 */
private String updateName;
/** 更新时间 */
private java.util.Date updateTime;
/** 删除标记 0:正常 1:删除 */
private Integer isDeleted;
}
(2)在 ihrm-common-model工程中创建 DepartmentQueryConditionVO.java, DepartmentSaveOrUpdateVO.java,DepartmentVO.java,CompanyDeptListVO
DepartmentQueryConditionVO.java
package com.pyy.ihrm.domain.company.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
/**
* ---------------------------
* 模糊查询条件VO
* ---------------------------
* 作者:
* 时间: 2019-11-13 10:03:44
* 版本: v1.0
* ---------------------------
*/
@Data
@ApiModel(value = "DepartmentQueryConditionVO",description = "部门查询条件类")
public class DepartmentQueryConditionVO {
@ApiModelProperty("名称")
private String name;
}
DepartmentSaveOrUpdateVO.java
package com.pyy.ihrm.domain.company.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* ---------------------------
* 部门 (Department)
* ---------------------------
* 作者:
* 时间: 2019-11-13 10:03:44
* 版本: v1.0
* ---------------------------
*/
@Data
@ApiModel(value = "DepartmentSaveOrUpdateVO",description = "部门保存和修改VO类")
public class DepartmentSaveOrUpdateVO {
@ApiModelProperty("ID")
private String id;
@ApiModelProperty("父级ID")
private String parentId;
@ApiModelProperty("企业ID")
private String companyId;
@ApiModelProperty("部门编码")
private String code;
@ApiModelProperty("部门名称")
private String name;
@ApiModelProperty("负责人ID")
private String managerId;
@ApiModelProperty("负责人名称")
private String managerName;
@ApiModelProperty("介绍")
private String introduce;
@ApiModelProperty("操作人ID")
private String operaterId;
@ApiModelProperty("操作人名称")
private String operaterName;
}
DepartmentVO.java
package com.pyy.ihrm.domain.company.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* ---------------------------
* 部门 (Department)
* ---------------------------
* 作者:
* 时间: 2019-11-13 10:03:44
* 版本: v1.0
* ---------------------------
*/
@Data
@ApiModel(value = "DepartmentVO",description = "部门类")
public class DepartmentVO {
@ApiModelProperty("ID")
private String id;
@ApiModelProperty("父级ID")
private String parentId;
@ApiModelProperty("企业ID")
private String companyId;
@ApiModelProperty("部门编码")
private String code;
@ApiModelProperty("部门名称")
private String name;
@ApiModelProperty("负责人ID")
private String managerId;
@ApiModelProperty("负责人名称")
private String managerName;
@ApiModelProperty("介绍")
private String introduce;
@ApiModelProperty("创建人ID")
private String createId;
@ApiModelProperty("创建人名称")
private String createName;
@ApiModelProperty("创建时间")
private java.util.Date createTime;
@ApiModelProperty("更新人ID")
private String updateId;
@ApiModelProperty("更新人名称")
private String updateName;
@ApiModelProperty("更新时间")
private java.util.Date updateTime;
@ApiModelProperty("删除标记 0:正常 1:删除")
private Integer isDeleted;
}
CompanyDeptListVO.java
package com.pyy.ihrm.domain.company.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* ---------------------------
* 企业部门 (Department)
* ---------------------------
* 作者:
* 时间: 2019-11-13 10:03:44
* 版本: v1.0
* ---------------------------
*/
@Data
@NoArgsConstructor
@ApiModel(value = "CompanyDeptListVO",description = "企业部门列表类")
public class CompanyDeptListVO {
@ApiModelProperty("企业ID")
private String companyId;
@ApiModelProperty("企业名称")
private String companyName;
@ApiModelProperty("企业联系人(法人代表)")
private String companyManager;
@ApiModelProperty("部门列表")
private List departmentVOList;
public CompanyDeptListVO(CompanyVO company, List departmentVOList) {
this.companyId = company.getId();
this.companyName = company.getName();
this.companyManager = company.getLegalRepresentative();
this.departmentVOList = departmentVOList;
}
}
这里之所以将VO类放在model工程中,原因是因为后面整合其他微服务时,会用到这些公共VO类。
(3)持久层Mapper(dao)
package com.pyy.ihrm.company.mapper;
import com.pyy.ihrm.company.po.Department;
import com.pyy.ihrm.domain.company.vo.DepartmentQueryConditionVO;
import tk.mybatis.mapper.common.Mapper;
import java.util.List;
/**
* ---------------------------
* 部门 (DepartmentMapper)
* ---------------------------
* 作者:
* 时间: 2019-11-13 09:56:28
* 版本: v1.0
* ---------------------------
*/
public interface DepartmentMapper extends Mapper {
List selectByPageAndParam(DepartmentQueryConditionVO queryConditionVO);
}
(4)业务逻辑层(service)
DepartmentService.java
package com.pyy.ihrm.company.service;
import com.pyy.ihrm.common.response.QueryResult;
import com.pyy.ihrm.domain.company.vo.CompanyDeptListVO;
import com.pyy.ihrm.domain.company.vo.DepartmentQueryConditionVO;
import com.pyy.ihrm.domain.company.vo.DepartmentSaveOrUpdateVO;
import com.pyy.ihrm.domain.company.vo.DepartmentVO;
import java.util.List;
/**
* ---------------------------
* 部门 (DepartmentService)
* ---------------------------
* 作者:
* 时间: 2019-11-13 10:03:44
* 版本: v1.0
* ---------------------------
*/
public interface DepartmentService {
/**
* 部门保存
* @param departmentSaveOrUpdateVO
*/
public void save(DepartmentSaveOrUpdateVO departmentSaveOrUpdateVO);
/**
* 部门修改
* @param id
* @param departmentSaveOrUpdateVO
*/
public void update(String id, DepartmentSaveOrUpdateVO departmentSaveOrUpdateVO);
/**
* 部门删除
* @param id
* @param userId
* @param username
*/
public void delete(String id, String userId, String username);
/**
* 根据部门ID查询
* @param id
*/
public DepartmentVO findById(String id);
/**
* 根据条件模糊查询所有部门信息
* @param queryConditionVO
* @return
*/
public List findByParams(DepartmentQueryConditionVO queryConditionVO);
/**
* 根据条件模糊查询某企业下部门信息
* @param companyId
* @param queryConditionVO
* @return
*/
public CompanyDeptListVO findByCompanyIdAndParams(String companyId, DepartmentQueryConditionVO queryConditionVO);
}
DepartmentServiceImpl.java
package com.pyy.ihrm.company.service.impl;
import com.alibaba.fastjson.JSON;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.pyy.ihrm.common.response.QueryResult;
import com.pyy.ihrm.common.response.ResultCode;
import com.pyy.ihrm.common.utils.SnowflakeId;
import com.pyy.ihrm.company.constants.CommonConstants;
import com.pyy.ihrm.company.exception.CustomException;
import com.pyy.ihrm.company.mapper.DepartmentMapper;
import com.pyy.ihrm.company.po.Department;
import com.pyy.ihrm.company.service.CompanyService;
import com.pyy.ihrm.company.service.DepartmentService;
import com.pyy.ihrm.domain.company.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* ---------------------------
* 部门 (DepartmentServiceImpl)
* ---------------------------
* 作者:
* 时间: 2019-11-13 10:03:44
* 版本: v1.0
* ---------------------------
*/
@Slf4j
@Service
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT,timeout=36000,rollbackFor=Exception.class)
public class DepartmentServiceImpl implements DepartmentService {
@Autowired
private DepartmentMapper departmentMapper;
@Autowired
private CompanyService companyService;
/**
* 部门保存
* @param departmentSaveOrUpdateVO
*/
@Override
public void save(DepartmentSaveOrUpdateVO departmentSaveOrUpdateVO) {
// 保存部门
Department departmentModel = new Department();
BeanUtils.copyProperties(departmentSaveOrUpdateVO, departmentModel);
departmentModel.setId(SnowflakeId.getId() + "");
departmentModel.setCreateId(departmentSaveOrUpdateVO.getOperaterId());
departmentModel.setCreateName(departmentSaveOrUpdateVO.getOperaterName());
departmentModel.setCreateTime(new Timestamp(System.currentTimeMillis()));
departmentModel.setIsDeleted(CommonConstants.UN_DELETED);
departmentMapper.insert(departmentModel);
log.info("### 部门保存成功 ###");
}
/**
* 部门修改
* @param id
* @param departmentSaveOrUpdateVO
*/
@Override
public void update(String id, DepartmentSaveOrUpdateVO departmentSaveOrUpdateVO) {
// 1.根据ID查询部门
Department departmentModel = departmentMapper.selectByPrimaryKey(id);
if (departmentModel == null) {
throw new CustomException(ResultCode.RESULT_DATA_NONE, "部门");
}
// 2.设置部门属性
BeanUtils.copyProperties(departmentSaveOrUpdateVO, departmentModel);
departmentModel.setUpdateId(departmentSaveOrUpdateVO.getOperaterId());
departmentModel.setUpdateName(departmentSaveOrUpdateVO.getOperaterName());
departmentModel.setUpdateTime(new Date());
// 3.更新部门
departmentMapper.updateByPrimaryKey(departmentModel);
log.info("### 部门修改成功 ###");
}
/**
* 部门删除
* @param id
*/
@Override
public void delete(String id, String userId, String username) {
Department departmentModel = departmentMapper.selectByPrimaryKey(id);
if (departmentModel == null) {
throw new CustomException(ResultCode.RESULT_DATA_NONE, "部门,id=" + id);
}
// 逻辑删除
departmentModel.setId(id);
departmentModel.setUpdateId(userId);
departmentModel.setUpdateName(username);
departmentModel.setUpdateTime(new Date());
departmentModel.setIsDeleted(CommonConstants.DELETED);
departmentMapper.updateByPrimaryKeySelective(departmentModel);
log.info("### 部门逻辑删除成功 ###");
}
/**
* 根据部门ID查询
* @param id
*/
@Override
public DepartmentVO findById(String id) {
Department departmentModel = departmentMapper.selectByPrimaryKey(id);
if (departmentModel == null) {
throw new CustomException(ResultCode.RESULT_DATA_NONE, "部门,id=" + id);
}
log.info("### 部门查询成功, department={}###", JSON.toJSONString(departmentModel));
// model转换vo
DepartmentVO departmentVO = new DepartmentVO();
BeanUtils.copyProperties(departmentModel, departmentVO);
log.info("### 部门Model转换VO成功, departmentVO={}###", departmentVO);
return departmentVO;
}
/**
* 根据条件模糊查询所有部门信息
* @param queryConditionVO
* @return
*/
@Override
public List findByParams(DepartmentQueryConditionVO queryConditionVO){
// 1.根据条件查询部门列表
List departmentList = departmentMapper.selectByPageAndParam(queryConditionVO);
log.info("### 部门Model模糊查询完毕,总条数:{}条###", departmentList.size());
// 1.1 部门Model转换VO数据
List departmentVOList = new ArrayList<>();
departmentList.forEach(department -> {
DepartmentVO departmentVO = new DepartmentVO();
BeanUtils.copyProperties(department, departmentVO);
departmentVOList.add(departmentVO);
});
log.info("### 部门Model转换VO数据完毕###");
return departmentVOList;
}
/**
* 根据条件模糊查询某企业下部门信息
* @param companyId
* @param queryConditionVO
* @return
*/
@Override
public CompanyDeptListVO findByCompanyIdAndParams(String companyId, DepartmentQueryConditionVO queryConditionVO){
// 1.根据条件查询部门列表
List departmentList = departmentMapper.selectByPageAndParam(queryConditionVO);
log.info("### 部门Model模糊查询完毕,总条数:{}条###", departmentList.size());
// 1.1 部
// 门Model转换VO数据
List departmentVOList = new ArrayList<>();
departmentList.forEach(department -> {
DepartmentVO departmentVO = new DepartmentVO();
BeanUtils.copyProperties(department, departmentVO);
departmentVOList.add(departmentVO);
});
log.info("### 部门Model转换VO数据完毕###");
// 2.根据企业ID查询企业信息
CompanyVO companyVO = companyService.findById(companyId);
// 3.构造结果
CompanyDeptListVO companyDeptListVO = new CompanyDeptListVO(companyVO, departmentVOList);
return companyDeptListVO;
}
}
(5)业务控制层(CompanyController)
package com.pyy.ihrm.company.controller;
import java.util.List;
import com.pyy.ihrm.common.response.QueryResult;
import com.pyy.ihrm.common.response.Result;
import com.pyy.ihrm.company.service.DepartmentService;
import com.pyy.ihrm.company.utils.UserUtil;
import com.pyy.ihrm.domain.company.vo.CompanyDeptListVO;
import com.pyy.ihrm.domain.company.vo.DepartmentQueryConditionVO;
import com.pyy.ihrm.domain.company.vo.DepartmentSaveOrUpdateVO;
import com.pyy.ihrm.domain.company.vo.DepartmentVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.http.MediaType;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import javax.validation.Valid;
/**
* ---------------------------
* 部门 (DepartmentController)
* ---------------------------
* 作者:
* 时间: 2019-11-13 10:03:44
* 版本: v1.0
* ---------------------------
*/
@Api(tags = "DepartmentController", description = "部门相关接口")
@RestController
@RequestMapping(value = "/v1", produces = MediaType.APPLICATION_JSON_UTF8_VALUE )
public class DepartmentController {
@Autowired
private DepartmentService departmentService;
/**
* 保存部门
* @param record
* @return
*/
@ApiOperation(value = "保存部门", notes = "创建新部门")
@ApiImplicitParam(name = "record", value = "部门对象", required = true, dataType = "Department", paramType = "body")
@PostMapping("/department")
public Result save(@Valid @RequestBody DepartmentSaveOrUpdateVO record) {
// 后面集成JWT后完善
String userId = "admin";
String username = "admin";
record.setOperaterId(userId);
record.setOperaterName(username);
departmentService.save(record);
return Result.SUCCESS();
}
/**
* 修改部门
* @param id
* @param record
* @return
*/
@ApiOperation(value = "修改部门", notes = "根据ID修改部门")
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "部门ID", required = true, dataType = "String", paramType = "path"),
@ApiImplicitParam(name = "record", value = "部门对象", required = true, dataType = "Department", paramType = "body")
})
@PutMapping("/department/{id}")
public Result update(@Valid @PathVariable(value = "id") String id, @RequestBody DepartmentSaveOrUpdateVO record) {
// 后面集成JWT后完善
String userId = "admin";
String username = "admin";
record.setOperaterId(userId);
record.setOperaterName(username);
departmentService.update(id, record);
return Result.SUCCESS();
}
/**
* 删除部门
* @param id
* @return
*/
@ApiOperation(value = "删除部门", notes = "根据ID部门")
@ApiImplicitParam(name = "id", value = "部门ID", required = true, dataType = "String", paramType = "path")
@DeleteMapping("/department/{id}")
public Result delete(@Valid @PathVariable(value = "id") String id) {
// 后面集成JWT后完善
String userId = "admin";
String username = "admin";
departmentService.delete(id, userId, username);
return Result.SUCCESS();
}
/**
* 根据ID查询部门
* @param id
* @return
*/
@ApiOperation(value = "部门查询", notes = "根据ID部门查询")
@ApiImplicitParam(name = "id", value = "部门ID", required = true, dataType = "String", paramType = "path")
@GetMapping("/department")
public Result findById(@Valid @PathVariable(value = "id") String id) {
DepartmentVO queryResult = departmentService.findById(id);
return Result.SUCCESS(queryResult);
}
/**
* 部门模糊查询
* @param queryConditionVO
* @return
*/
@ApiOperation(value = "部门模糊查询", notes = "查询所有部门列表")
@GetMapping("/departments")
public Result> findByParams(DepartmentQueryConditionVO queryConditionVO) {
List queryResult = departmentService.findByParams(queryConditionVO);
return Result.SUCCESS(queryResult);
}
/**
* 某企业下部门模糊查询
* @param companyId
* @param queryConditionVO
* @return
*/
@ApiOperation(value = "某企业下部门模糊查询", notes = "根据企业ID查询部门列表")
@ApiImplicitParam(name = "companyId", value = "企业ID", required = true, dataType = "String", paramType = "path")
@GetMapping("/{companyId}/departments")
public Result findByCompanyIdAndParams(@PathVariable("companyId") String companyId, DepartmentQueryConditionVO queryConditionVO) {
CompanyDeptListVO companyDeptListVO = departmentService.findByCompanyIdAndParams(companyId, queryConditionVO);
return Result.SUCCESS(companyDeptListVO);
}
}
启动项目,访问:http://localhost:9001/swagger-ui.html
源码:https://github.com/pyygithub/saas-ihrm