第1章 SAAS-HRM系统概述和构建环境
学习目标:理解SaaS了解基本概念SAAS-HRM掌握基本需求和发展模式Power Designer用例图完成SAAS-HRM在企业微服务中完成企业微服务CRUD功能
1 初识SaaS1.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-HRMSaaS-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)它主要用于描述用户和用例之间的关系。它解释了谁想使用系统,他们可以做什么。例子包含多个模型元素,如系统、参与者和用例,并显示这些元素之间的各种关系,如泛化、关联和依赖。它显示了外部用户可以观察到的系统功能模型图。2.3.3 需求分析软件Power Designer 是Sybase公司的CASE该工具集可以很容易地分析和设计管理信息系统,它几乎包括数据库模型设计的整个过程。Power Designer您可以制作数据流程图、概念数据模型、物理数据模型、数据仓库结构模型和团队设计模型。(1)在下载和安装使用的第一天准备好的安装包Power Designer,安装过程略(2)Power Designer绘制用例图绘制步骤:文件=>建立新模型=>选择Modeltypes=>Use Case
基本用例图:
3 系统设计3.1 开发方式SaaS-IHRM该系统采用前后分离的开发方法。
前端负责为前端提供数据HTML渲染(可在服务器或浏览器中渲染)与用户互动。双方以文档的形式规范接口内容
3.2 技术架构(1) 前端技术栈Node.js为核心的Vue.js 后端技术栈SpringBoot SpringCloud SpringMVC SpringData(Spring全家桶)
3.3 系统结构3.4 API文档课程提供前后端开发界面文档(使用)Swagger写语言),和语言一起,Ngin整合Nginx执行文件启动后,输入地址栏http://localhost:801 即可访问API文档
4 工程搭建4.1 前置知识点的说明Saas-HRM采用系统后端SpringBoot SpringCloud SpringMVC SpringDataSaas-HRM基于系统前端nodejs的vue编写和使用框架element-ui组件库快速开发前端界面学生对上述前后端技术有初步了解
4.2 开发环境要求JDK1.8数据库mysql 5.7开发工具 idea 2017.1.2 ** ven版本3.3.94.2.1 lombok 插件lombok是可以精减的java编译期间自动生成代码、提高开发人员生产效率的辅助工具setter/getter/toString()/constructor代码(1) idea中安装插件
(2) 在pom在文件中添加插件的依赖
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.16.16</version></dependency>
(3)常见注释@Data 注释在类上; 提供类别所有属性getting 和 setting除此之外,还提供了 方法equals、canEqual、hashCode、toString 方法@Setter :属性注释;为属性提供 setting 方法@Setter :属性注释;为属性提供 getting 方法@NoArgsConstructor :注释在类上;为类提供无参结构方法@AllArgsConstructor :注解在类上;为类提供一个全参的构造方法
4.3 建设父工程在IDEA创建父工程ihrm_parent并导入以下坐标:<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.16.16</version></dependency><packaging>pom</packaging><name>ihrm_parent</name><description>IHRM-黑马程序员</description><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.0.5.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><fastjson.version>1.2.47</fastjson.version></properties><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>Finchley.SR1</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>${fastjson.version}</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency></dependencies><repositories>
4 工程搭建4.4 构建公共子模块4.4.1 构建公共子模块ihrm-common
4.4.2 创建返回结果实体类(1)新建com.ihrm.common.entity包,包下创建类Result,用于控制器类返回结果<version>2.12.4</version><configuration><skipTests>true</skipTests></configuration></plugin></plugins></build>package com.ihrm.common.entity;import com.fasterxml.jackson.annotation.JsonInclude;import com.fasterxml.jackson.databind.annotation.JsonSerialize;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;@Data@NoArgsConstructor//非空数据不显示@JsonInclude(JsonInclude.Include.NON_NULL)public class Result {private boolean success;//是否成功private Integer code;// 返回码private String message;//返回信息private Object data;// 返回数据public Result(ResultCode code) {this.success = code.success;this.code = code.code;this.message = code.message;}public Result(ResultCode code,Object data) {this.success = code.success;this.code = code.code;
this.message = code.message;this.data = data;}public Result(Integer code,String message,boolean success) {this.code = code;this.message = message;this.success = success;}public static Result SUCCESS(){return new Result(ResultCode.SUCCESS);}public static Result ERROR(){return new Result(ResultCode.SERVER_ERROR);}public static Result FAIL(){return new Result(ResultCode.FAIL);}}
(2)创建类PageResult ,用于返回分页结果package com.ihrm.common.entity;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import java.util.List;@Data@AllArgsConstructor@NoArgsConstructorpublic class PageResult<T> {private Long total;private List<T> rows;}
4.4.3 返回码定义类public enum ResultCode {SUCCESS(true,10000,"操作成功!"),//---系统错误返回码-----FAIL(false,10001,"操作失败"),UNAUTHENTICATED(false,10002,"您还未登录"),UNAUTHORISE(false,10003,"权限不足"),SERVER_ERROR(false,99999,"抱歉,系统繁忙,请稍后重试!");//---用户操作返回码----//---企业操作返回码----//---权限操作返回码----//---其他操作返回码----//操作是否成功boolean success;//操作代码int code;//提示信息String message;ResultCode(boolean success,int code, String message){this.success = success;this.code = code;this.message = message;}public boolean success() {return success;}public int code() {return code;}public String message() {return message;}}
4.4.4 分布式ID生成器目前微服务架构盛行,在分布式系统中的操作中都会有一些全局性ID的需求,所以我们不能使用数据库本身的自增功能来产生主键值,只能由程序来生成唯一的主键值。我们采用的是开源的twitter( 非官方中文惯称:推特.是国外的一个网站,是一个社交网络及微博客服务) 的snowflake (雪花)算法。
各个段解析:
默认情况下41bit的时间戳可以支持该算法使用到2082年,10bit的工作机器id可以支持1024台机器,序列号支持1毫秒产生4096个自增序列id . SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右//雪花算法代码实现public class IdWorker {// 时间起始标记点,作为基准,一般取系统的最近时间(一旦确定不能变动)private final static long twepoch = 1288834974657L;// 机器标识位数private final static long workerIdBits = 5L;// 数据中心标识位数private final static long datacenterIdBits = 5L;// 机器ID最大值private final static long ** xWorkerId = -1L ^ (-1L << workerIdBits);// 数据中心ID最大值private final static long ** xDatacenterId = -1L ^ (-1L << datacenterIdBits);// 毫秒内自增位private final static long sequenceBits = 12L;// 机器ID偏左移12位private final static long workerIdShift = sequenceBits;// 数据中心ID左移17位private final static long datacenterIdShift = sequenceBits + workerIdBits;// 时间毫秒左移22位private final static long timestampLeftShift = sequenceBits + workerIdBits +datacenterIdBits;private final static long sequenceMask = -1L ^ (-1L << sequenceBits);/* 上次生产id时间戳 */private static long lastTimestamp = -1L;// 0,并发控制private long sequence = 0L;private final long workerId;// 数据标识id部分private final long datacenterId;public IdWorker(){this.datacenterId = getDatacenterId( ** xDatacenterId);
this.workerId = getMaxWorkerId(datacenterId, ** xWorkerId);}/*** @param workerId* 工作机器ID* @param datacenterId* 序列号*/public IdWorker(long workerId, long datacenterId) {if (workerId > ** xWorkerId || workerId < 0) {throw new IllegalArgumentException(String.for ** t("worker Id can't begreater than %d or less than 0", ** xWorkerId));}if (datacenterId > ** xDatacenterId || datacenterId < 0) {throw new IllegalArgumentException(String.for ** t("datacenter Id can't begreater than %d or less than 0", ** xDatacenterId));}this.workerId = workerId;this.datacenterId = datacenterId;}/*** 获取下一个ID** @return*/public synchronized long nextId() {long timestamp = timeGen();if (timestamp < lastTimestamp) {throw new RuntimeException(String.for ** t("Clock moved backwards. Refusingto generate id for %d milliseconds", lastTimestamp - timestamp));}if (lastTimestamp == timestamp) {// 当前毫秒内,则+1sequence = (sequence + 1) & sequenceMask;if (sequence == 0) {// 当前毫秒内计数满了,则等待下一秒timestamp = tilNextMillis(lastTimestamp);}} else {sequence = 0L;}lastTimestamp = timestamp;// ID偏移组合生成最终的ID,并返回IDlong nextId = ((timestamp - twepoch) << timestampLeftShift)| (datacenterId << datacenterIdShift)| (workerId << workerIdShift) | sequence;return nextId;}private long tilNextMillis(final long lastTimestamp) {long timestamp = this.timeGen();while (timestamp <= lastTimestamp) {timestamp = this.timeGen();}return timestamp;}private long timeGen() {return System.currentTimeMillis();}/*** <p>* 获取 ** xWorkerId* </p>*/protected static long getMaxWorkerId(long datacenterId, long ** xWorkerId) {StringBuffer mpid = new StringBuffer();mpid.append(datacenterId);String name = ManagementFactory.getRuntimeMXBean().getName();if (!name.isEmpty()) {/** GET jvmPid*/mpid.append(name.split("@")[0]);}/** MAC + PID 的 hashcode 获取16个低位*/return (mpid.toString().hashCode() & 0xffff) % ( ** xWorkerId + 1);}/*** <p>* 数据标识id部分* </p>*/protected static long getDatacenterId(long ** xDatacenterId) {long id = 0L;try {InetAddress ip = InetAddress.getLocalHost();NetworkInte ** ce network = NetworkInte ** ce.getByInetAddress(ip);if (network == null) {id = 1L;} else {byte[] ** c = network.getHardwareAddress();id = ((0x000000FF & (long) ** c[ ** c.length - 1])| (0x0000FF00 & (((long) ** c[ ** c.length - 2]) << 8))) >> 6;id = id % ( ** xDatacenterId + 1);}} catch (Exception e) {System.out.println(" getDatacenterId: " + e.getMessage());}return id;}}
4.5 搭建公共的实体类模块(1)构建公共子模块ihrm_common_model
(2)引入坐标
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>com.ihrm</groupId><artifactId>ihrm_common</artifactId><version>1.0-SNAPSHOT</version></dependency></dependencies>
5 企业微服务-企业CRUD5.1 模块搭建(1)搭建企业微服务模块ihrm_company, pom.xml引入依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>com.ihrm</groupId><artifactId>ihrm_common</artifactId><version>1.0-SNAPSHOT</version></dependency>(2)添加配置文件application.yml
server:port: 9001spring:application:name: ihrm-company #指定服务名datasource:driver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://localhost:3306/ihrm?useUnicode=true&characterEncoding=utf8username: rootpassword: 111111jpa:database: MySQLshow-sql: trueopen-in-view: true}}
(3)配置启动类
@SpringBootApplication(scanBasePackages = "com.ihrm")@EntityScan("com.ihrm")public class CompanyApplication {public static void ** in(String[] args) {SpringApplication.run(CompanyApplication.class, args);}@Beanpublic IdWorker idWorkker() {return new IdWorker(1, 1);}}
5.2 企业管理-CRUD5.2.1 表结构分析
CREATE TABLE `co_company` (`id` varchar(40) NOT NULL COMMENT 'ID',`name` varchar(255) NOT NULL COMMENT '公司名称',` ** nager_id` varchar(255) NOT NULL COMMENT '企业登录账号ID',`version` varchar(255) DEFAULT NULL COMMENT '当前版本',`renewal_date` datetime DEFAULT NULL COMMENT '续期时间',`expiration_date` datetime DEFAULT NULL COMMENT '到期时间',`company_area` varchar(255) DEFAULT NULL COMMENT '公司地区',`company_address` text COMMENT '公司地址',`business_license_id` varchar(255) DEFAULT NULL COMMENT '营业执照-图片ID',`legal_representative` varchar(255) DEFAULT NULL COMMENT '法人代表',`company_phone` varchar(255) DEFAULT NULL COMMENT '公司电话',` ** ilbox` varchar(255) DEFAULT NULL COMMENT '邮箱',`company_size` varchar(255) DEFAULT NULL COMMENT '公司规模',`industry` varchar(255) DEFAULT NULL COMMENT '所属行业',`re ** rks` text COMMENT '备注',
`audit_state` varchar(255) DEFAULT NULL COMMENT '审核状态',`state` tinyint(2) NOT NULL DEFAULT '1' COMMENT '状态',`balance` double NOT NULL COMMENT '当前余额',`create_time` datetime NOT NULL COMMENT '创建时间') ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
5.2.2 完成企业增删改查操作
(1)实体类(do ** in)
@Entity@Table(name = "co_company")@Data@AllArgsConstructor@NoArgsConstructorpublic class Company implements Serializable {private static final long serialVersionUID = 594829320797158219L;//ID@Idprivate String id;/*** 公司名称*/private String name;/*** 企业登录账号ID*/private String ** nagerId;/*** 当前版本*/private String version;/*** 续期时间*/private Date renewalDate;/*** 到期时间*/private Date expirationDate;/*** 公司地区*/private String companyArea;/*** 公司地址*/private String companyAddress;/*** 营业执照-图片ID*/private String businessLicenseId;/**
* 法人代表*/private String legalRepresentative;/*** 公司电话*/private String companyPhone;/*** 邮箱*/private String ** ilbox;/*** 公司规模*/private String companySize;/*** 所属行业*/private String industry;/*** 备注*/private String re ** rks;/*** 审核状态*/private String auditState;/*** 状态*/private Integer state;/*** 当前余额*/private Double balance;/*** 创建时间*/private Date createTime;
}
(2)持久层(dao)
/*** 企业数据访问接口*/public inte ** ce CompanyDao extends JpaRepository<Company, String>,JpaSpecificationExecutor<Company> {}
JpaRepository提供了基本的增删改查 JpaSpecificationExecutor用于做复杂的条件查询(3)业务逻辑层(service)
@Servicepublic class CompanyService {@Autowiredprivate CompanyDao companyDao;@Autowiredprivate IdWorker idWorker;/*** 添加企业** @param company 企业信息*/public Company add(Company company) {company.setId(idWorker.nextId() + "");company.setCreateTime(new Date());company.setState(1); //启用company.setAuditState("0"); //待审核company.setBalance(0d);return companyDao.save(company);}public Company update(Company company) {return companyDao.save(company);}public Company findById(String id) {return companyDao.findById(id).get();}public void deleteById(String id) {companyDao.deleteById(id);}public List<Company> findAll() {return companyDao.findAll();}}
(4)控制器
@RestController@RequestMapping("/company")public class CompanyController{@Autowiredprivate CompanyService companyService;/*** 添加企业*/@RequestMapping(value = "", method = RequestMethod.POST)public Result add(@RequestBody Company company) throws Exception {companyService.add(company);
return Result.SUCCESS();}/*** 根据id更新企业信息*/@RequestMapping(value = "/{id}", method = RequestMethod.PUT)public Result update(@PathVariable(name = "id") String id, @RequestBody Companycompany) throws Exception {Company one = companyService.findById(id);one.setName(company.getName());one.setRe ** rks(company.getRe ** rks());one.setState(company.getState());one.setAuditState(company.getAuditState());companyService.update(company);return Result.SUCCESS();}/*** 根据id删除企业信息*/@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)public Result delete(@PathVariable(name = "id") String id) throws Exception {companyService.deleteById(id);return Result.SUCCESS();}/*** 根据ID获取公司信息*/@RequestMapping(value = "/{id}", method = RequestMethod.GET)public Result findById(@PathVariable(name = "id") String id) throws Exception {Company company = companyService.findById(id);return new Result(ResultCode.SUCCESS);}/*** 获取企业列表*/@RequestMapping(value = "", method = RequestMethod.GET)public Result findAll() throws Exception {List<Company> companyList = companyService.findAll();return new Result(ResultCode.SUCCESS);}}
5.2.3 测试
(1) 测试工具post ** nPost ** n提供功能强大的Web API & HTTP 请求调试。软件功能非常强大,界面简洁明晰、操作方便快捷,设计得很人性化,
能够发送任何类型的HTTP 请求 (GET, HEAD, POST, PUT..),附带任何数量的参数。
使用资料中提供的post ** n安装包进行安装,注册成功之后即可使用(2) 使用post ** n测试企业接口
5.3 公共异常处理为了使我们的代码更容易维护,同时给用户最好的用户体验,有必要对系统中可能出现的异常进行处理。spring提供了@ControllerAdvice注解和@ExceptionHandler可以很好的在控制层对异常进行统一处理(1)添加自定义的异常package com.ihrm.common.exception;import com.ihrm.common.entity.ResultCode;import lombok.Getter;@Getterpublic class CommonException extends RuntimeException {private static final long serialVersionUID = 1L;private ResultCode code = ResultCode.SERVER_ERROR;public CommonException(){}public CommonException(ResultCode resultCode) {super(resultCode.message());this.code = resultCode;}}
(2)配置公共异常处理
package com.ihrm.common.exception;import com.alibaba.fastjson.JSON;import com.ihrm.common.entity.Result;import org.springframework.http.HttpStatus;import org.springframework.web.bind.annotation.ControllerAdvice;import org.springframework.web.bind.annotation.ExceptionHandler;import org.springframework.web.bind.annotation.ResponseBody;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;/*** 全局异常处理*/@ControllerAdvicepublic class BaseExceptionHandler {@ResponseBody@ExceptionHandler(value = Exception.class)public Result error(HttpServletRequest request, HttpServletResponse response,Exception e) throws IOException {e.printStackTrace();if (e.getClass() == CommonException.class) {CommonException ce = (CommonException) e;return new Result(ce.getCode());} else {return Result.ERROR();}}}
5.4 跨域处理跨域是什么?浏览器从一个域名的网页去请求另一个域名的资源时,域名、端口、协议任一不同,都是跨域 。我们是采用前后端分离开发的,也是前后端分离部署的,必然会存在跨域问题。 怎么解决跨域?很简单,只需要在controller类上添加注解@CrossOrigin 即可!这个注解其实是CORS的实现。 CORS(Cross-Origin ResourceSharing, 跨源资源共享)是W3C出的一个标准,其思想是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功,还是应该失败。因此,要想实现CORS进行跨域,需要服务器进行一些设置,同时前端也需要做一些配置和分析。本文简单的对服务端的配置和前端的一些设置进行分析。
第一章到此结束!!!
以下是视频截图!!!
扫码咨询与免费使用
申请免费使用