创建 maven 工程空项目,来放置自己的工具类
<build>
<finalName>${project.version}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<encoding>UTF-8</encoding>
<source>1.8</source>
<target>1.8</target>
<showWarnings>true</showWarnings>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.0.1</version>
<configuration>
<attach>true</attach>
</configuration>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
会吧打包在本地磁盘上,命令后会有显示位置在哪里,之后的项目就可以直接引入这个maven的依赖来使用里面的一些工具类
创建 springboot 项目工程,来引入打包后的 maven 依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.mhn</groupId>
<artifactId>mhn-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
同上创建 maven 项目,进行打包
```xml
<snapshots.id>home-snapshots</snapshots.id>
<snapshots.name>maven</snapshots.name>
<snapshots.url>https://maven.guanweiming.com:8081/repository/tulan-snapshot/</snapshots.url>
</properties>
<repositories>
<repository>
<id>central</id>
<url>https://maven.guanweiming.com:8081/repository/maven-tulan</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>snapshots</id>
<url>https://maven.guanweiming.com:8081/repository/maven-tulan</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>home-releases</id>
<url>https://maven.guanweiming.com:8081/repository/tulan-release/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>home-snapshots</id>
<url>https://maven.guanweiming.com:8081/repository/tulan-snapshot/</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>central</id>
<url>https://maven.guanweiming.com:8081/repository/maven-tulan</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>snapshots</id>
<url>https://maven.guanweiming.com:8081/repository/maven-tulan</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>home-releases</id>
<url>https://maven.guanweiming.com:8081/repository/tulan-release/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>home-snapshots</id>
<url>https://maven.guanweiming.com:8081/repository/tulan-snapshot/</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
- pom.xml 中添加配置服务器地址
```xml
<repositories>
<repository>
<id>home</id>
<url>https://maven.guanweiming.com:8081/repository/maven-public</url>
</repository>
</repositories>
同上创建 springBoot 项目,来直接导入相应依赖就可以使用该依赖中存在的工具类
Optional(java.util.Optional)类是一个容器类,可以保存类型 T 的值,代表这个值存在。或仅仅保存 null,表示这个值不存在。原来用 null 表示一个值不存在,现在 Optional 可以更好的表达这个概念,并且可以避免空指针异常。Optional 提供了很多方法,可以不用显式进行空值检测
@Data
@AllArgsConstructor
@NoArgsConstructor
class Student{
private String name;
private Integer age;
}
创建 Optional 类对象的方法
public void test(){
// 声明空的 Optional
Optional<Object> empty = Optional.empty();
// 依据一个非空值创建 Optional
Student student = new Student();
Optional<Student> o1 = Optional.of(student);
// 可接受 null 的 Optional
Student student1 = null;
Optional<Student> o2 = Optional.ofNullable(student1);
}
判断 Optional 容器中是否包含对象
public void test(){
Student studnet = new Student();
boolean isNull = Optional.ofNullable(student).isPresent();
System.out.println(isNull); // fasle
// 如果不为空,则set值
Optioanl.ofNullable(student).isPresnet(item -> item.setName("浩楠"));
}
获取 Optional 容器的对象
public void test() throws Excpetion{
Student student = null;
Optional<Student> o1 = Optional.ofNullable(student);
// 使用get时注意,如果为空,则会报错
Student student1 = o1.get();
// 当 student 为空的时候,返回新建的这个对象
Student student2 = o1.orElse(new Student("浩楠",22));
// 当 student 为空的时候,返回指定对应的函数结果
Student sutdent3 = o1.orElseGet(() -> new Student("浩楠",22));
// 当 student 为空的时候,抛出指定异常
o1.orElseThrow(() -> new NullPointException());
}
过滤
public void test(){
Student student = new Student("浩楠",22);
Optional.ofNullable(student).filter(item -> item.getName().equals("heihei").ifPresent(item -> System.out.println("ok")));
}
映射
Optional.ofNullable(CategoEnum.getItem(projectVO.getHPcategory())).map(CategoEnum::getLabel).ifPresent(projectVO::setHPcategoryStr);
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
<version>2.6.13</version>
</dependency>
freemarker:
cache: false #是否启用缓存,开发环境不建议启动因为涉及经常修改模板调试
settings:
classic_compatible: true
suffix: .xml #一般格式tpl居多
charset: UTF-8
template-loader-path: classpath:/templates/ #模板路径,一般都是这个
@Slf4j
@Api(tags = "doc 导出")
@RequiredArgsConstructor
@RestController
@RequestMapping(Const.ADMIN + "export")
public class ExportController {
private final IExportService exportService;
@ApiModelProperty("doc文件导出")
@RequestMapping(value = "/doc", method = RequestMethod.GET)
public void exportWord(@ApiParam(required = true)@RequestParam Long applyId,
@ApiParam(required = true)@RequestParam Long type) throws Exception {
String fileName = null; //文件名称
// 设置头部数据
Map<String, Object> dataMap = null;
String templateName = null;
ApproveRuleTypeEnum typeEnum = ApproveRuleTypeEnum.getItem(type);
switch (typeEnum) {
case CAR:
fileName = LocalDateTime.now() + "11.doc";
templateName = "车辆.xml";
dataMap = getCarDate(applyId);
break;
case SEAL:
fileName = LocalDateTime.now() + "印章使用审批单.doc";
templateName = "印章使用审批单.xml.ftl";
dataMap = getSealDate(applyId);
break;
}
exportService.exportDocToClient(fileName, templateName, dataMap);
}
// 车辆审批数据
private Map<String, Object> getCarDate(long applyId) {
Map<String, Object> dataMap = new HashMap<>();
// 设置表格数据
List<String> list = new ArrayList<>();
list.add("123");
list.add("456");
String reason = "回家看病人";
dataMap.put("name", "小明");
dataMap.put("reason", reason);
dataMap.put("regAddress", "苏州");
dataMap.put("courseList", list);
return dataMap;
}
}
public interface IExportService {
void exportDocToClient(String fileName, String templateName, Map<String, Object> dataMap) throws Exception;
}
@Slf4j
@RequiredArgsConstructor
@Service
public class IExportServiceImpl implements IExportService {
private final FreeMarkerConfigurer freeMarkerConfigurer;
@Override
// 参数一:导出文件的名字(注意要存在后缀) 参数二:xml 模板文件名字(存在后缀) 参数三:映射数据的 map
public void exportDocToClient(String fileName, String tplName, Map<String, Object> data) throws Exception {
HttpServletResponse response = HttpKit.getHttpResponse();
Writer out = null;
try {
response.reset();
response.setCharacterEncoding("UTF-8");
response.setContentType("application/msword");
response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8"));
// 把本地文件发送给客户端
out = response.getWriter();
Template template = getTemplate(tplName);
template.process(data, out);
CloseUtil.closeQuietly(out);
} finally {
CloseUtil.closeQuietly(out);
}
}
public Template getTemplate(String name) throws Exception {
return freeMarkerConfigurer.getConfiguration().getTemplate(name);
}
}
遇见到的问题
根据 freemarker 提供的接口传入的 map 集合,集合与 xml 中的 ${} 数据进行映射渲染
.docx 转为 doc 格式,再转为 xml 格式(WPS转换会导致错误,当前使用 office 2013)
freemarker 存在表达式,可以在 xml 中进行流程控制的判断
@Service
@Slf4j
@RequiredArgsConstructor
public class WordExportServiceImpl implements WordExeportService {
private final FreeMarkerConfigurer freeMarkerConfigurer;
@Override
public byte[] wordExport(String fileName, String templateName, Map<String, Object> dataMap) throws Exception{
ByteArrayOutputStream bos=new ByteArrayOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(bos);
try {
Template template = getTemplate(templateName);
template.process(dataMap, osw);
// 返回字节数组
return bos.toByteArray();
} finally {
CloseUtil.closeQuietly(osw);
CloseUtil.closeQuietly(bos);
}
}
public Template getTemplate(String name) throws Exception {
return freeMarkerConfigurer.getConfiguration().getTemplate(name);
}
}
public void wordBatchExport(long statFeaturePlanId) {
List<StatFeaturePlanTask> tasks = statFeaturePlanTaskDao.findTaskIdsByPlanId(statFeaturePlanId);
HttpServletResponse response = HttpKit.getHttpResponse();
ZipOutputStream zos = null;
try {
// 解压流
zos = new ZipOutputStream(response.getOutputStream());
response.reset();
response.setCharacterEncoding("UTF-8");
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(LocalDateTime.now() + "批量导出.zip", "UTF-8"));
for (StatFeaturePlanTask task : tasks) {
byte[] bytes = wordExport(task.getStatFeaturePlanTaskId(), false);
// 将doc写入压缩包中
zos.putNextEntry(new ZipEntry(sciDisciplineDao.findBySubKey(task.getSubKey()).getSubName()+".doc"));
zos.write(bytes,0,bytes.length);
zos.flush();
zos.closeEntry();
}
zos.flush();
} catch (Exception e) {
log.info("批量导出有误:{}", e.getMessage());
} finally {
CloseUtil.closeQuietly(zos);
}
}
<!--导出excel-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-spring-boot-starter</artifactId>
<version>4.4.0</version>
</dependency>
public static void doExport(Workbook workbook, String name) {
HttpServletResponse response = HttpKit.getHttpResponse();
ServletOutputStream outputStream = null;
try {
String filename = URLEncoder.encode(name + ".xls", "UTF-8");
response.setContentType("application/vnd.ms-excel;charset=gb2312");
response.setHeader("Content-Disposition", "attachment; filename=" + filename);
outputStream = response.getOutputStream();
workbook.write(outputStream);
} catch (Exception e) {
log.error("导出报错", e);
} finally {
CloseUtil.closeQuietly(outputStream);
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class HProjectDTO {
@Excel(name = "合同负责人姓名")
private String name;
@Excel(name = "合同状态")
private String hstateStr;
private String hadmin;
@Excel(name = "合同成员")
private String hnum;
@Excel(name = "合同金额")
private BigDecimal hamount;
@Excel(name = "立项时间")
private LocalDate hbegin;
}
}
// 参数意义:ExcelExportUtils.exportExcel(ExportParams对象,实体类.class,数据集合);
// 参数意义:ExportParams(标题,sheetName);
ExcelUtil.doExport(ExcelExportUtil.exportExcel(new ExportParams(null,"数据"),ZProjectDTO.class,voList),"纵向项目数据");
public R<String> sciPlatImport(MultipartFile file) {
ImportParams importParams = new ImportParams();
//设置标题的行数,有标题时一定要有
importParams.setTitleRows(0);
//设置表头的行数
importParams.setHeadRows(1);
try{
// 获取到当前导入的内容
// 参数一:获取文件流,参数二:实体类(也就是表明了 Excel 注解的),参数三:ImportParams参数
List<SciPlatDTO> sciPlatDTOS = ExcelImportUtil.importExcel(file.getInputStream(), SciPlatDTO.class, importParams);
for (SciPlatDTO sciPlatDTO : sciPlatDTOS) {
com.zhenghang.entity.SciPlat sciPlat = SciPlatDTO.toSciPlat(sciPlatDTO);
log.info("导入数据:{}", JsonUtil.toJson(sciPlatDTO,true));
Optional.ofNullable(natPlatnormalDao.findDateByGrade1Str(sciPlatDTO.getPlatGrade1Str())).map(com.zhenghang.entity.NatPlatnormal::getPlatKey).ifPresent(data -> sciPlat.setPlatGrade1(data.toString()));
log.info("插入数据:{}",JsonUtil.toJson(sciPlat,true));
int result = sciPlatDao.insert(sciPlat);
if(result == 0){
R.createByErrorMessage("导入失败,数据可能导致有误");
}
}
return R.createBySuccess("导入成功");
}catch(Exception e){
log.info("导入报错:{}",e.getMessage(),e);
}
return R.createByErrorMessage("导入失败");
}
@Slf4j
public class RequestUtils {
public static String getRemoteHost(javax.servlet.http.HttpServletRequest request) {
List<String> keyList = Lists.newArrayList();
keyList.add("x-original-forwarded-for");
keyList.add("x-forwarded-for");
keyList.add("x-real-ip");
keyList.add("Proxy-Client-IP");
keyList.add("WL-Proxy-Client-IP");
for (String key : keyList) {
String content = request.getHeader(key);
if (StringUtils.isBlank(content)) {
continue;
}
if ("unknown".equals(content)) {
continue;
}
if (content.contains(",")) {
String[] arr = content.split(",");
for (String temIp : Lists.newArrayList(arr)) {
temIp = temIp.trim();
if (StringUtils.isBlank(temIp)) {
continue;
}
if ("unknown".equals(temIp)) {
continue;
}
return temIp;
}
}
return content;
}
// 获取到请求头的所有名字信息,通过request.getHeader(名字信息)来获取到要的信息
// 原有:可以通过这样的方式来查看自己想要的请求头数据,当下就是检验上面逻辑未能找到ip的原因
Enumeration<String> headerNames = request.getHeaderNames();
Map<String, Object> headerMap = Maps.newHashMap();
while (headerNames.hasMoreElements()) {
String key = headerNames.nextElement();
if (StringUtils.isNotBlank(key)) {
headerMap.put(key, request.getHeader(key));
}
}
log.warn("找不到真实ip:{}", JsonUtil.toJson(headerMap));
return "unknown";
}
}
<dependency>
<groupId>com.jthinking.common</groupId>
<artifactId>ip-info</artifactId>
<version>2.2.0</version>
</dependency>
IPInfo ipInfo = IPInfoUtils.getIpInfo(参数为ip地址);
System.out.println("---start");
System.out.println(ip); // 国家中文名称
System.out.println(ipInfo.getCountry()); // 国家中文名称
System.out.println(ipInfo.getProvince()); // 中国省份中文名称
System.out.println(ipInfo.getAddress()); // 详细地址
System.out.println(ipInfo.getIsp()); // 互联网服务提供商
System.out.println(ipInfo.isOverseas()); // 是否是国外
System.out.println(ipInfo.getLat()); // 纬度
System.out.println(ipInfo.getLng()); // 经度
System.out.println("---end");
存在包:org.springframework.security.crypto.password.PasswordEncoder
@Autowired
private PasswordEncoder passwordEncoder;
@Test
void setDefaultRole() {
for (int i = 0; i < 3; i++) {
System.out.println(passwordEncoder.encode("123456"));
}
}
// 参数一:前端接受密码参数。参数二:获取用户密码
passwordEncoder.matches(param.getUserPassword(), user.getUserPassword());//【true/false】
该注解用于竖向上,作用是把该属性的名称序列化为另一个名称,【比如把 trueName 属性序列化为 true_name,这样前端接受的属性名称就为 true_name】
import com.fasterxml.jackson.annotation.JsonProperty;
public class User {
@JsonProperty(value = "true_name")
private String trueName;
}
该注解用在属性或方法上(最好是属性),用来完全忽略被注释的字段和方法对应的属性,即便这个字段或方法可以被自动检测到或者还有其他注解,一般标记在属性上,返回给前端的 json 数据即不包含该属性【使用场景:通常为返回的密码】
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
@ApiModel(value = "User", description = "用户实体,用户相关属性信息")
public class User {
@JsonIgnore
@ApiModelProperty(value = "密码", name = "password", required = false)
private String password;
}
该注解用来设置时间字段的格式,但不能直接使用,在中国会和北京时间相差 8 小时,所以格式化的时候需要指定时区(timezone)
import com.fasterxml.jackson.annotation.JsonFormat;
import java.util.Date;
public class MenuStatisticsDetail {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date updateTime;
}
@JsonFormat 注解用于定义属性的序列化和反序列化格式,而 DateTimeFormat 用于解析和格式化日期事件类型的属性,通常与 @RequestMapping 来一起使用
public class Event {
private String name;
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date eventDate;
// Getter and setter methods
}
@JsonInclude
前后端分离的项目,框架中封装了返回给前端的结果,如果属性存在该注解并且值为 null 的情况下,返回给前端的时候会过滤掉这个属性不返回【如果放在类上,则类的全部属性都会起作用】
import com.fasterxml.jackson.annotation.JsonInclude;
public class User {
@JsonInclude(JsonInclude.Include.NON_NULL) //当电话为null时就不传
private String phone;
}
在将 java 对象序列化为 json 字符串的时候,使用该注解可以指定属性在 json 字符串中的顺序
@JsonPropertyOrder(value={"userName","userId"})
public class User {
private String userId;
private String userName;
}