POI报表的入门

news/2025/1/12 3:59:06/

POI报表的入门

理解员工管理的的业务逻辑

能够说出Eureka和Feign的作用

理解报表的两种形式和POI的基本操作熟练使用POI完成Excel的导入导出操作

员工管理

需求分析

企业员工管理是人事资源管理系统中最重要的一个环节,分为对员工入职,转正,离职,调岗,员工报表导入导出 等业务逻辑。需求看似复杂,实际上都是对数据库表的基本操作。

数据库表概述

对于员工操作而言,涉及到的数据库表如下表格说明:

数据库表名称

说明

em_archive

月度员工归档表

em_positive

转正申请表

em_resignation

离职申请表

em_transferposition

员工调岗申请表

em_user_company_jobs

员工岗位信息表

em_user_company_personal

员工详细信息表

代码实现

服务端实现

  • 创建员工微服务

ihrm_employee

  • 配置文件 application.yml
  • 配置Shiro核心配置类ShiroConfiguration
  • 配置启动类EmployeeApplication
  • 导入资源中提供的基本Controller,Service,Dao,Domain代码

前端实现

导入资源中提供的前端代码。

服务发现组件 Eureka

Eureka是Netflflix开发的服务发现框架,SpringCloud将它集成在自己的子项目spring-cloud-Netflflix中,实现SpringCloud的服务发现功能。Eureka包含两个组件:Eureka Server和Eureka Client。

Eureka Server提供服务注册服务,各个节点启动后,会在Eureka Server中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到。

Eureka Client是一个java客户端,用于简化与Eureka Server的交互,客户端同时也就别一个内置的、使用轮 询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳,默认周期为30秒, 如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90秒)。

Eureka服务端开发

  • 创建ihrm_eureka模块
  • 引入依赖 父工程pom.xml定义SpringCloud版本
<dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>Finchley.M9</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement>

ihrm_eureka模块pom.xml引入eureka-server

<dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId></dependency></dependencies>
  • 添加application.yml
server:port: 6868 #服务端口
eureka:client:registerWithEureka: false #是否将自己注册到Eureka服务中,本身就是所有无需
注册fetchRegistry: false #是否从Eureka中获取注册信息serviceUrl: #Eureka客户端与Eureka服务端进行交互的地址defaultZone: http://127.0.0.1:${server.port}/eureka/
  • 配置启动类
@SpringBootApplication
@EnableEurekaServer
public class EurekaServer {public static void main(String[] args) {SpringApplication.run(EurekaServer.class, args);}
}

微服务注册

我们现在就将所有的微服务都注册到Eureka中,这样所有的微服务之间都可以互相调用了。

  • 将其他微服务模块添加依赖
<dependency>        <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>         
</dependency>
  • 修改每个微服务的application.yml,添加注册eureka服务的配置
eureka:client:service-url:defaultZone: http://localhost:6868/eurekainstance:prefer-ip-address: true
  • 修改每个服务类的启动类,添加注解@EnableEurekaClient

骚戴理解:把微服务都注册到Eureka后可以通过访问:http://localhost:6868进入Eureka网站看微服务的存活情况

微服务调用组件Feign

简介

Feign是简化Java HTTP客户端开发的工具(java-to-httpclient-binder),它的灵感来自于Retrofit、JAXRS-2.0和WebSocket。Feign的初衷是降低统一绑定Denominator到HTTP API的复杂度,不区分是否为restful

快速体验

我们现在在系统微服务调用企业微服务的方法(根据ID查询部门)

  • 在ihrm_system模块添加依赖
<dependency>        <groupId>org.springframework.cloud</groupId>            <artifactId>spring-cloud-starter-openfeign</artifactId>            
</dependency>
  • 修改ihrm_system模块的启动类,添加注解
@EnableDiscoveryClient
@EnableFeignClients
  • 在Ihrm_system模块创建com.ihrm.system.client包,包下创建接口
//@FeignClient注解用于指定从哪个服务中调用功能 ,注意里面的名称与被调用的服务名保持一致
@FeignClient(value = "ihrm-company")
public interface DepartmentFeignClient {//@RequestMapping注解用于对被调用的微服务进行地址映射@RequestMapping(value = "/company/departments/{id}/", method = RequestMethod.GET)public Department findById(@PathVariable("id") String id) throws Exception;
}
  • 修改Ihrm_system模块的 UserController
 @Autowired    private DepartmentFeignClient departmentFeignClient;    //测试通过系统微服务调用企业微服务方法@RequestMapping(value = "/test/{id}")    public void findDeptById(@PathVariable  String id){    Department dept = departmentFeignClient.findById(id);  System.out.println(dept);        }

骚戴理解:Feign组件是谁调用就写在谁那里,例如微服务A远程调用微服务B的接口,那么上面的这些步骤都写在微服务A里面

  • 配置Feign拦截器添加请求头
package com.ihrm.common.feign;import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;@Configuration
public class FeignConfiguration {//配置feign拦截器,解决请求头问题@Beanpublic RequestInterceptor requestInterceptor() {return new RequestInterceptor() {//获取所有浏览器发送的请求属性,请求头赋值到feignpublic void apply(RequestTemplate requestTemplate) {//请求属性ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();if(attributes != null) {HttpServletRequest request = attributes.getRequest();//获取浏览器发起的请求头Enumeration<String> headerNames = request.getHeaderNames();if (headerNames != null) {while (headerNames.hasMoreElements()) {String name = headerNames.nextElement(); //请求头名称 AuthorizationString value = request.getHeader(name);//请求头数据 "Bearer b1dbb4cf-7de6-41e5-99e2-0e8b7e8fe6ee"requestTemplate.header(name,value);}}}}};}
}

骚戴理解:在使用Feign组件远程调用其他微服务的时候会丢失请求头,所以要写一个上面的配置类来解决这个问题,其实就是把请求头给配置好,在每次调用其他微服务的时候都把这个请求头设置进去,这样就不缺请求头了

POI报表的概述

需求说明

在企业级应用开发中,Excel报表是一种最常见的报表需求。Excel报表开发一般分为两种形式:

  • 为了方便操作,基于Excel的报表批量上传数据
  • 通过java代码生成Excel报表。

在Saas-HRM系统中,也有大量的报表操作,那么接下来的课程就是一起来学习企业级的报表开发。

Excel的两种形式

目前世面上的Excel分为两个大的版本Excel2003和Excel2007及以上两个版本,两者之间的区别如下:

Excel2003是一个特有的二进制格式,其核心结构是复合文档类型的结构,存储数据量较小;Excel2007 的核心结构是 XML 类型的结构,采用的是基于 XML 的压缩方式,使其占用的空间更小,操作效率更高

常见excel操作工具

Java中常见的用来操作Excl的方式一般有2种:JXL和POI。

JXL只能对Excel进行操作,属于比较老的框架,它只支持到Excel 95-2000的版本。现在已经停止更新和维护。POI是apache的项目,可对微软的Word,Excel,Ppt进行操作,包括office2003和2007,Excl2003和2007。poi现在一直有更新。所以现在主流使用POI。

POI的概述

Apache POI是Apache软件基金会的开源项目,由Java编写的免费开源的跨平台的 Java API,Apache POI提供API

给Java语言操作Microsoft Office的功能。

POI的应用场景

  • 数据报表生成
  • 数据备份
  • 数据批量上传

POI的入门操作

搭建环境

<dependencies><dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>4.0.1</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>4.0.1</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml-schemas</artifactId><version>4.0.1</version></dependency>
</dependencies>

POI结构说明

  • HSSF提供读写Microsoft Excel XLS格式档案的功能。
  • XSSF提供读写Microsoft Excel OOXML XLSX格式档案的功能。
  • HWPF提供读写Microsoft Word DOC格式档案的功能。
  • HSLF提供读写Microsoft PowerPoint格式档案的功能。
  • HDGF提供读Microsoft Visio格式档案的功能。
  • HPBF提供读Microsoft Publisher格式档案的功能。
  • HSMF提供读Microsoft Outlook格式档案的功能。

API介绍

API名称

Workbook

Excel的文档对象,针对不同的Excel类型分为:HSSFWorkbook【2003版本后缀为.xls】和XSSFWorkbool【2007版本后缀为.xlsx】

Sheet

Excel的表单

Row

Excel的行

Cell

Excel的格子单元

Font

Excel字体

CellStyle

格子单元样式

基本操作

创建Excel

public class PoiTest01 {//测试创建excel文件public static void main(String[] args) throws Exception {//1.创建workbook工作簿Workbook wb = new XSSFWorkbook();//2.创建表单SheetSheet sheet = wb.createSheet("test");//3.文件流FileOutputStream fos = new FileOutputStream("E:\\test.xlsx");//4.写入文件wb.write(fos);fos.close();}
}

骚戴理解:FileOutputStream fos = new FileOutputStream("E:\\test.xlsx");表示把创建的excel文件输出到E:\\test.xlsx路径下

创建单元格

    //测试创建单元格public static void main(String[] args) throws Exception {//1.创建workbook工作簿Workbook wb = new XSSFWorkbook();//2.创建表单SheetSheet sheet = wb.createSheet("test");//3.创建行对象,从0开始Row row = sheet.createRow(3);//4.创建单元格,从0开始Cell cell = row.createCell(0);//5.单元格写入数据cell.setCellValue("骚戴");//6.文件流FileOutputStream fos = new FileOutputStream("E:\\test.xlsx");//7.写入文件wb.write(fos);fos.close();}

骚戴理解: Row row = sheet.createRow(3);是指定第四行,Cell cell = row.createCell(0);是指定第1列,如图所示:

设置格式

package cn.itcast.poi.test;import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;import java.io.FileOutputStream;/*** 单元格样式处理*/
public class PoiTest03 {public static void main(String[] args) throws Exception {//创建工作簿  HSSFWorkbook -- 2003Workbook wb = new XSSFWorkbook(); //2007版本//创建表单sheetSheet sheet = wb.createSheet("test");//创建行对象  参数:索引(从0开始)Row row = sheet.createRow(2);//创建单元格对象  参数:索引(从0开始)Cell cell = row.createCell(2);//向单元格中写入内容cell.setCellValue("传智播客");//样式处理//创建样式对象CellStyle style = wb.createCellStyle();style.setBorderTop(BorderStyle.THIN);//上边框style.setBorderBottom(BorderStyle.THIN);//下边框style.setBorderLeft(BorderStyle.THIN);//左边框style.setBorderRight(BorderStyle.THIN);//右边框//创建字体对象Font font = wb.createFont();font.setFontName("华文行楷"); //字体font.setFontHeightInPoints((short)28);//字号style.setFont(font);//行高和列宽row.setHeightInPoints(50);//行高//列宽的宽度  字符宽度sheet.setColumnWidth(2,31 * 256);//列宽//剧中显示style.setAlignment(HorizontalAlignment.CENTER);//水平居中style.setVerticalAlignment(VerticalAlignment.CENTER);//垂直居中//向单元格设置样式cell.setCellStyle(style);//文件流FileOutputStream pis = new FileOutputStream("E:\\excel\\poi\\test2.xlsx");//写入文件wb.write(pis);pis.close();}
}

骚戴理解: sheet.setColumnWidth(0, 31 * 256);//设置第一列的宽度是31个字符宽度,注意这里要乘以256是因为源码里面设计的时候是会除以256

绘制图形

package cn.itcast.poi.test;import org.apache.poi.ss.usermodel.*;
import org.apache.poi.util.IOUtils;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;import java.io.FileInputStream;
import java.io.FileOutputStream;/*** 插入图片*/
public class PoiTest04 {public static void main(String[] args) throws Exception {//创建工作簿  HSSFWorkbook -- 2003Workbook wb = new XSSFWorkbook(); //2007版本//创建表单sheetSheet sheet = wb.createSheet("test");//读取图片流FileInputStream stream = new FileInputStream("E:\\excel\\poi\\logo.jpg");//转化二进制数组byte[] bytes = IOUtils.toByteArray(stream);stream.read(bytes);//向POI内存中添加一张图片,返回图片在图片集合中的索引int index = wb.addPicture(bytes, Workbook.PICTURE_TYPE_JPEG);//参数一:图片的二进制数据,参数二:图片类型//绘制图片工具类CreationHelper helper = wb.getCreationHelper();//创建一个绘图对象Drawing<?> patriarch = sheet.createDrawingPatriarch();//创建锚点,设置图片坐标ClientAnchor anchor = helper.createClientAnchor();anchor.setRow1(0);anchor.setCol1(0);//绘制图片Picture picture = patriarch.createPicture(anchor, index);//图片位置,图片的索引picture.resize();//自适应渲染图片//文件流FileOutputStream pis = new FileOutputStream("E:\\excel\\poi\\test3.xlsx");//写入文件wb.write(pis);pis.close();}
}

骚戴理解:下面的所谓的锚点其实就是图片的起始下标,例如下面设置就是在第一行第一列插入一张图片

//创建锚点,设置图片坐标
ClientAnchor anchor = helper.createClientAnchor();
anchor.setRow1(0);
anchor.setCol1(0);

加载Excel

package cn.itcast.poi.test;import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;import java.io.IOException;/*** 读取excel并解析*      sheet.getLastRowNum() : 最后一行的索引*      row.getLastCellNum() : 最后一个单元格的号码*/
public class PoiTest05 {public static void main(String[] args) throws Exception {//1.根据Excel文件创建工作簿Workbook wb = new XSSFWorkbook("E:\\excel\\poi\\demo.xlsx");//2.获取SheetSheet sheet = wb.getSheetAt(0);//参数:索引//3.获取Sheet中的每一行,和每一个单元格for (int rowNum = 0; rowNum<= sheet.getLastRowNum() ;rowNum ++) {Row row = sheet.getRow(rowNum);//根据索引获取每一个行StringBuilder sb = new StringBuilder();for(int cellNum=2;cellNum< row.getLastCellNum(); cellNum ++) {//根据索引获取每一个单元格Cell cell = row.getCell(cellNum);//获取每一个单元格的内容Object value = getCellValue(cell);sb.append(value).append("-");}System.out.println(sb.toString());}}//根据单元格数据类型获取数据public static Object getCellValue(Cell cell) {//1.获取到单元格的属性类型CellType cellType = cell.getCellType();//2.根据单元格数据类型获取数据Object value = null;switch (cellType) {case STRING:value = cell.getStringCellValue();break;case BOOLEAN:value = cell.getBooleanCellValue();break;case NUMERIC:if(DateUtil.isCellDateFormatted(cell)) {//日期格式value = cell.getDateCellValue();}else{//数字value = cell.getNumericCellValue();}break;case FORMULA: //公式value = cell.getCellFormula();break;default:break;}return value;}
}

骚戴理解:这里要注意Excel里面单元格有很多类型,所以需要判断是什么类型然后对应Java代码,其中日期和数字类型都是“NUMERIC”类型,所以需要调用DateUtil.isCellDateFormatted()去判断这个单元格的类型是不是日期类型,如果不是那就是数字类型,上面的getCellValue方法是可以提取处理作为一个工具类的

POI报表导入

需求分析

实现批量导入员工功能,页面端上传excel表格,服务端解析表格获取数据,批量新增用户

员工导入

搭建环境

父模块pom文件添加依赖

<dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>4.0.1</version>
</dependency>
<dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>4.0.1</version>
</dependency>
<dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml-schemas</artifactId><version>4.0.1</version>
</dependency>

实现Excel上传

  • 用户实体类配置构造方法
    //objs数据位置和excel上传位置一致。public User(Object []objs,String companyId,String companyName) {//用户名    手机号    工号    聘用 形式    入职 时间    部门编码this.username = values[1].toString();this.mobile = values[2].toString();this.createTime = new Date();//默认手机号excel读取为字符串会存在科学记数法问题,转化处理this.workNumber = new DecimalFormat("#").format(values[3]).toString();this.formOfEmployment =((Double) values[4]).intValue();this.timeOfEntry = (Date) values[5];this.departmentId = values[6].toString(); //部门编码 != 部门id}

骚戴理解:这里代码设计的就有问题,这个Excel里面搞了一个部门编码,然后在User里面压根就没有部门编码这个属性,所以就把部门编码的属性放到了departmentId里面,才会有部门编码 != 部门id,我一开始都被绕晕了,这样搞应该是为了用到远程调用,为了调用ihrm_company微服务来根据部门编码和公司id去查询部门id,这样设计太牵强了!

this.workNumber = new DecimalFormat("#").format(values[3]).toString(); //默认手机号excel读取为字符串会存在科学记数法问题,转化处理。

DecimalFormat("#") 在这里创建了一个 DecimalFormat 的对象,其中 # 是特殊字符,表示数字的占位符。

.format(values[3]) 表示将values[3]这个数字转换为字符串,并按照构造函数中制定的格式进行格式化。

举个例子:如果 objs[2]=1234.5678 ,那么 new DecimalFormat("#").format(objs[2]) 的返回结果就是一个字符串 "1235",因为格式中只有整数部分的占位符 #

  • 在系统微服务UserController中添加上传方法
    /*** 导入Excel,添加用户*  文件上传:springboot*/@RequestMapping(value="/user/import",method = RequestMethod.POST)public Result importUser(@RequestParam(name="file") MultipartFile file) throws Exception {//1.解析Excel//1.1.根据Excel文件创建工作簿Workbook wb = new XSSFWorkbook(file.getInputStream());//1.2.获取SheetSheet sheet = wb.getSheetAt(0);//参数:索引//1.3.获取Sheet中的每一行,和每一个单元格//2.获取用户数据列表List<User> list = new ArrayList<>();System.out.println(sheet.getLastRowNum());for (int rowNum = 1; rowNum<= sheet.getLastRowNum() ;rowNum ++) {Row row = sheet.getRow(rowNum);//根据索引获取每一个行Object [] values = new Object[row.getLastCellNum()];for(int cellNum=1;cellNum< row.getLastCellNum(); cellNum ++) {Cell cell = row.getCell(cellNum);Object value = getCellValue(cell);values[cellNum] = value;}User user = new User(values);list.add(user);}//3.批量保存用户userService.saveAll(list,companyId,companyName);return new Result(ResultCode.SUCCESS);}//根据单元格数据类型获取数据public static Object getCellValue(Cell cell) {//1.获取到单元格的属性类型CellType cellType = cell.getCellType();//2.根据单元格数据类型获取数据Object value = null;switch (cellType) {case STRING:value = cell.getStringCellValue();break;case BOOLEAN:value = cell.getBooleanCellValue();break;case NUMERIC:if(DateUtil.isCellDateFormatted(cell)) {//日期格式value = cell.getDateCellValue();}else{//数字value = cell.getNumericCellValue();}break;case FORMULA: //公式value = cell.getCellFormula();break;default:break;}return value;}

调用企业微服务获取部门数据

  • 在Ihrm_system模块创建com.ihrm.system.client包,包下创建接口
@FeignClient(value = "ihrm-company")
package com.ihrm.system.client;import com.ihrm.common.entity.Result;
import com.ihrm.domain.company.Department;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;/*** 声明接口,通过feign调用其他微服务*/
//声明调用的微服务名称
@FeignClient("ihrm-company")
public interface DepartmentFeignClient {/*** 调用微服务的接口*/@RequestMapping(value="/company/department/{id}",method = RequestMethod.GET)public Result findById(@PathVariable(value="id") String id);//远程调用企业微服务,根据企业编码code和企业名称获取企业信息@RequestMapping(value="/company/department/search",method = RequestMethod.POST)public Department findByCode(@RequestParam(value="code") String code, @RequestParam(value="companyId") String companyId);
}
  • 修改UserService,注入DepartmentFeignClient
@Autowired
private DepartmentFeignClient departmentFeignClient;

保存全部用户

UserService中添加保存全部的方法

    @Transactionalpublic void save(List<User> users) throws Exception {for (User user : users) {//配置密码user.setPassword(new Md5Hash("123456",user.getMobile(),3).toString());//配置iduser.setId(idWorker.nextId()+"");//其他基本属性user.setInServiceStatus(1);user.setEnableState(1);user.setLevel("user");//获取部门信息Department dept = departmentFeignClient.findById(user.getDepartmentId(),user.getCompanyId());if(dept != null) {user.setDepartmentId(dept.getId());user.setDepartmentName(dept.getName());}userDao.save(user);}}

POI报表导出

需求分析

完成当月人事报表的导出:包含当月入职员工信息,离职员工信息

骚戴理解:也就是要把em_user_company_personal(员工详细信息表)和em_resignation(离职申请表)进行左外连接

人事报表导出

步骤分析

  • 构造Excel表格数据创建工作簿
  • 创建sheet 创建行对象
  • 创建单元格对象
  • 填充数据,设置样式下载

代码实现

  • EmployeeReportResult
package com.ihrm.domain.employee.response;import com.ihrm.domain.employee.EmployeeResignation;
import com.ihrm.domain.employee.UserCompanyPersonal;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import org.springframework.beans.BeanUtils;/*** 导出报表构造的人事报表对象*/
@Getter
@Setter
@NoArgsConstructor
@ToString
public class EmployeeReportResult {private String userId;private String username;private String departmentName;private String mobile;private String timeOfEntry;private String companyId;private String sex;/*** 出生日期*/private String dateOfBirth;/*** 最高学历*/private String theHighestDegreeOfEducation;/*** 国家地区*/private String nationalArea;/*** 护照号*/private String passportNo;/*** 身份证号*/private String idNumber;/*** 身份证照片-正面*/private String idCardPhotoPositive;/*** 身份证照片-背面*/private String idCardPhotoBack;/*** 籍贯*/private String nativePlace;/*** 民族*/private String nation;/*** 英文名*/private String englishName;/*** 婚姻状况*/private String maritalStatus;/*** 员工照片*/private String staffPhoto;/*** 生日*/private String birthday;/*** 属相*/private String zodiac;/*** 年龄*/private String age;/*** 星座*/private String constellation;/*** 血型*/private String bloodType;/*** 户籍所在地*/private String domicile;/*** 政治面貌*/private String politicalOutlook;/*** 入党时间*/private String timeToJoinTheParty;/*** 存档机构*/private String archivingOrganization;/*** 子女状态*/private String stateOfChildren;/*** 子女有无商业保险*/private String doChildrenHaveCommercialInsurance;/*** 有无违法违纪行为*/private String isThereAnyViolationOfLawOrDiscipline;/*** 有无重大病史*/private String areThereAnyMajorMedicalHistories;/*** QQ*/private String qq;/*** 微信*/private String wechat;/*** 居住证城市*/private String residenceCardCity;/*** 居住证办理日期*/private String dateOfResidencePermit;/*** 居住证截止日期*/private String residencePermitDeadline;/*** 现居住地*/private String placeOfResidence;/*** 通讯地址*/private String postalAddress;/*** 联系手机*/private String contactTheMobilePhone;/*** 个人邮箱*/private String personalMailbox;/*** 紧急联系人*/private String emergencyContact;/*** 紧急联系电话*/private String emergencyContactNumber;/*** 社保电脑号*/private String socialSecurityComputerNumber;/*** 公积金账号*/private String providentFundAccount;/*** 银行卡号*/private String bankCardNumber;/*** 开户行*/private String openingBank;/*** 学历类型*/private String educationalType;/*** 毕业学校*/private String graduateSchool;/*** 入学时间*/private String enrolmentTime;/*** 毕业时间*/private String graduationTime;/*** 专业*/private String major;/*** 毕业证书*/private String graduationCertificate;/*** 学位证书*/private String certificateOfAcademicDegree;/*** 上家公司*/private String homeCompany;/*** 职称*/private String title;/*** 简历*/private String resume;/*** 有无竞业限制*/private String isThereAnyCompetitionRestriction;/*** 前公司离职证明*/private String proofOfDepartureOfFormerCompany;/*** 备注*/private String remarks;/*** 离职时间*/private String resignationTime;/*** 离职类型*/private String typeOfTurnover;/*** 申请离职原因*/private String reasonsForLeaving;public EmployeeReportResult(UserCompanyPersonal personal, EmployeeResignation resignation) {BeanUtils.copyProperties(personal,this);if(resignation != null) {BeanUtils.copyProperties(resignation,this);}}
}
  • 配置EmployeeController
   /*** 导出人事报表* @param month* @throws Exception*/
@RequestMapping(value = "/export/{month}", method = RequestMethod.GET)public void export(@PathVariable(name = "month") String month) throws Exception {//1.构造数据List<EmployeeReportResult> list = userCompanyPersonalService.findByReport(companyId,month+"%");//2.创建工作簿XSSFWorkbook workbook = new XSSFWorkbook();//3.构造sheetString[] titles = {"编号", "姓名", "手机","最高学历", "国家地区", "护照号", "籍贯", "生日", "属相","入职时间","离职类型","离职原因","离职时间"};Sheet sheet = workbook.createSheet(); Row row = sheet.createRow(0);AtomicInteger headersAi = new AtomicInteger();for (String title : titles) {Cell cell = row.createCell(headersAi.getAndIncrement());cell.setCellValue(title);}AtomicInteger datasAi = new AtomicInteger(1);Cell cell = null;for (EmployeeReportResult report : list) {Row dataRow = sheet.createRow(datasAi.getAndIncrement());//编号cell = dataRow.createCell(0);cell.setCellValue(report.getUserId());//姓名cell = dataRow.createCell(1);cell.setCellValue(report.getUsername());//手机cell = dataRow.createCell(2);cell.setCellValue(report.getMobile());//最高学历cell = dataRow.createCell(3);cell.setCellValue(report.getTheHighestDegreeOfEducation());//国家地区cell = dataRow.createCell(4);cell.setCellValue(report.getNationalArea());//护照号cell = dataRow.createCell(5);cell.setCellValue(report.getPassportNo());//籍贯cell = dataRow.createCell(6);cell.setCellValue(report.getNativePlace());//生日cell = dataRow.createCell(7);cell.setCellValue(report.getBirthday());//属相cell = dataRow.createCell(8);cell.setCellValue(report.getZodiac());//入职时间cell = dataRow.createCell(9);cell.setCellValue(report.getTimeOfEntry());//离职类型cell = dataRow.createCell(10);cell.setCellValue(report.getTypeOfTurnover());//离职原因cell = dataRow.createCell(11);cell.setCellValue(report.getReasonsForLeaving());//离职时间cell = dataRow.createCell(12);cell.setCellValue(report.getResignationTime());}String fileName = URLEncoder.encode(month+"人员信息.xlsx", "UTF-8");response.setContentType("application/octet-stream");response.setHeader("content-disposition", "attachment;filename=" + new String(fileName.getBytes("ISO8859-1")));response.setHeader("filename", fileName);workbook.write(response.getOutputStream());}

骚戴理解:AtomicInteger datasAi = new AtomicInteger(1);和datasAi.getAndIncrement()这是Java中使用AtomicInteger类来实现原子操作的例子。

  • AtomicInteger datasAi = new AtomicInteger(1);创建了一个对象datasAi,它是AtomicInteger类型的并初始化为1。因此,如果多个线程同时访问这个对象进行操作,则可以保证对其进行操作时是线程安全的。
  • datasAi.getAndIncrement() 可以理解为"获取并增加",它的作用是先获取当前值(即值为1),然后将值自增1,返回的结果是二者相加后的值。这个操作是原子性的,也就是说,在同时有多个线程操作该对象时,只有一个线程能够成功执行它,其他线程则被阻塞等待。

export方法里的response是父控制器BaseController里的定义的

package com.ihrm.common.controller;import com.ihrm.domain.system.response.ProfileResult;
import io.jsonwebtoken.Claims;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.web.bind.annotation.ModelAttribute;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class BaseController {protected HttpServletRequest request;protected HttpServletResponse response;protected String companyId;private String userId;protected String companyName;protected Claims claims;//使用jwt方式获取
//    @ModelAttribute
//    public void setResAnReq(HttpServletRequest request,HttpServletResponse response) {
//        this.request = request;
//        this.response = response;
//
//        Object obj = request.getAttribute("user_claims");
//
//        if(obj != null) {
//            this.claims = (Claims) obj;
//            this.companyId = (String)claims.get("companyId");
//            this.companyName = (String)claims.get("companyName");
//        }
//    }//使用shiro获取@ModelAttributepublic void setResAnReq(HttpServletRequest request,HttpServletResponse response) {this.request = request;this.response = response;//获取session中的安全数据Subject subject = SecurityUtils.getSubject();//1.subject获取所有的安全数据集合PrincipalCollection principals = subject.getPrincipals();if(principals != null && !principals.isEmpty()){//2.获取安全数据ProfileResult result = (ProfileResult)principals.getPrimaryPrincipal();this.companyId = result.getCompanyId();this.companyName = result.getCompany();this.userId = result.getUserId();System.out.println(companyId);}}}
  • 添加service
    //根据企业id和年月查询public List<EmployeeReportResult> findByReport(String companyId, String month) {return userCompanyPersonalDao.findByReport(companyId,month);}
  • dao层实现
 @Query(value = "select new com.ihrm.domain.employee.response.EmployeeReportResult(a,b) " +"FROM UserCompanyPersonal a LEFT JOIN EmployeeResignation b ON a.userId=b.userId WHERE a.companyId = ?1 AND a.timeOfEntry LIKE ?2 OR (b.resignationTime LIKE ?2)")List<EmployeeReportResult> findByReport(String companyId, String month);

骚戴理解:这是一个使用JPA自定义查询语句的例子,可以通过该语句查询符合条件的员工信息。

该查询语句包含了两个数据表 UserCompanyPersonal 和 EmployeeResignation,并依据它们中的特定字段获取数据。其中:

  • new com.ihrm.domain.employee.response.EmployeeReportResult() 指定查询结果返回的对象为 EmployeeReportResult 对象。
  • LEFT JOIN 表示使用左连接查询,根据a.userId=b.userId实现两张表的关联查询。
  • ON a.userId=b.userId 为JOIN语句中的ON条件,表示UserCompanyPersonal和EmployeeResignation表中userId字段相等时关联查询两个表的员工信息。
  • WHERE 子句中的 a.companyId = ?1 条件表示筛选companyId等于第一个参数的员工信息。
  • AND 连接符号指定 后续筛选条件需要满足该条件,后面跟着的 a.timeOfEntry LIKE ?2 OR (b.resignationTime LIKE ?2) 是一个复杂的逻辑运算符,经过或与运算判断timeOfEntry或resignationTime是否匹配,具体如下:
    • a.timeOfEntry LIKE ?2 表示筛选timeOfEntry字段类似于第二个参数的查询值(即时间模糊匹配),满足该条件之一即可。
    • OR 表示或逻辑运算符。
    • (b.resignationTime LIKE ?2) 表示筛选resignationTime字段类似于第二个参数的查询值。

因此,该自定义查询语句最终返回符合条件的实体集合,并且每个EmployeeReportResult对象包含UserCompanyPersonal和EmployeeResignation表中领取的一行数据。

SELECT* 
FROMem_user_company_personal pLEFT JOIN em_resignation r ON p.user_id = r.user_id 
WHEREp.company_id = 1 AND p.time_of_entry LIKE '2023-01%' OR r.resignation_time LIKE '2023-01%'

骚戴理解:这里我更喜欢转成sql语句更好理解,看着更舒服,需要注意的是2023-01%能够查出90多条数据,但是2023-1%只能查出3条数据,我一开始以为这两种写法查出来的结果是一样的!


http://www.ppmy.cn/news/217154.html

相关文章

12代CPU启用SR-IOV vGPU,实现一台电脑当七台用

背景 虚拟桌面基础设施&#xff08;VDI&#xff09;技术一般部署在服务器&#xff0c;可以实现多个用户连接到服务器上的虚拟桌面。随着桌面计算机性能的日益提升&#xff0c;桌面计算机在性能在很多场景下已经非常富余&#xff0c;足够同时满足多个用户同时使用的需求。实际项…

华为首款台式机计算机发布,华为首款台式电脑现身官网!搭载锐龙4000处理器...

原标题&#xff1a;华为首款台式电脑现身官网&#xff01;搭载锐龙4000处理器 华为进军笔记本电脑行业已经有几年了&#xff0c;它们凭借精美的外形、出色的屏幕、创新的多屏协同功能&#xff0c;给大家留下了深刻的印象&#xff0c;许多人也在期待华为是否会推出台式电脑。 今…

请至Android设备,通过NFC将数据从Android设备传输到PC

我正在尝试为医院开发Android应用程序.在该系统中,需要使用NFC技术将存储在Android手机中的数据库中的患者信息获取到台式计算机中.无论如何我在哪里可以使用NFC USB读取设备(ACR122U NFC智能卡读卡器RFID编写器5 Mifare USB)将数据从手机传输到我的台式电脑&#xff1f; 真实情…

华为怎么显示返回按键_华为台式机上线官网:搭配智慧键盘,支持指纹识别和一碰连...

点击右上角关注我们&#xff0c;每天给您带来最新最潮的科技资讯&#xff0c;让您足不出户也知道科技圈大事&#xff01; 在多次曝光之后&#xff0c;华为 MateStation B515 台式机套装目前已经上线华为官网。产品包括主机、显示器和配套的键鼠套装&#xff0c;其中键盘内置了指…

蓝牙的文件传输服务器,文件传输速度大比拼:Wi-Fi完爆蓝牙

1蓝牙4.0和无线WiFi技术 处在信息时代,各种各样日新月异的新变化让人应接不暇。遥想几年前,蓝牙还是一项“时兴”的技术,笔者记得那时候很多人互相之间用蓝牙传输歌曲、打游戏也玩联机对战、不亦乐乎,甚至蓝牙耳机等“稀罕物”也应运而生。然而转眼几年之间,Wi-Fi技术的发…

非接触式NFC读卡器和作家从中国RFID

非接触式NFC读卡器和作家从中国RFID 中国RFID&#xff08;射频识别日报&#xff09;已经推出了几款ontactless的NFC读取器&#xff08;NFC&#xff1a;近场通信&#xff09;&#xff0c;以帮助在NFC系统。这些NFC读者的作家给予不同的非接触式表演用实际的NFC系统&#xff0c;以…

三星新旗舰手机 GALAXY S III正式发布

今天凌晨2点&#xff0c;三星在英国伦敦举行了GALAXY S III的发布会。GALAXY S III 采用的是4核处理器、4.8英寸的Super AMOLED显示屏&#xff0c;800万像素的摄像头&#xff0c;并新增加了S Voice&#xff0c;S Bean、AllShare Play等功能。GALAXY S III将于五月底在欧洲上市&…

华为一碰传常见异常问题及解决方案(超详细)

文章目录 一、关于Huawei share一碰传二、常见问题及解决方案 1.一碰传激活失败 2.一碰传连接失败 3.一碰传传文件失败(一直转圈、出现感叹号) 4.华为电脑管家提示"正在加载服务,请稍候...” 5.一碰连接后提示“硬件解码器工作异常...” 6.华为电脑管家提示“…