diff --git a/pom.xml b/pom.xml index de35cf7..5cf0e96 100644 --- a/pom.xml +++ b/pom.xml @@ -70,6 +70,17 @@ 3.5.2 + + org.springframework.boot + spring-boot-starter-data-redis + + + + org.hibernate + hibernate-validator + 6.0.1.Final + + diff --git a/src/main/java/com/gmh/config/RedisConfig.java b/src/main/java/com/gmh/config/RedisConfig.java new file mode 100644 index 0000000..0a8a7e6 --- /dev/null +++ b/src/main/java/com/gmh/config/RedisConfig.java @@ -0,0 +1,64 @@ +package com.gmh.config; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; +import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +/** + * redis配置 + * + */ +@Configuration +@EnableCaching +public class RedisConfig extends CachingConfigurerSupport { + + /** + * value的序列化方式为JDK默认序列化 + */ + @Bean(name = "redisTemplateForJdkSerializer") + public RedisTemplate redisTemplateForJdkSerializer(RedisConnectionFactory connectionFactory) { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(connectionFactory); + Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class); + ObjectMapper om = new ObjectMapper(); + om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + jackson2JsonRedisSerializer.setObjectMapper(om); + template.setKeySerializer(new StringRedisSerializer()); + template.setHashKeySerializer(new StringRedisSerializer()); + template.afterPropertiesSet(); + return template; + } + + /** + * value的序列化方式为Json + */ + @Bean + public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(connectionFactory); + Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class); + ObjectMapper om = new ObjectMapper(); + om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); + jackson2JsonRedisSerializer.setObjectMapper(om); + // 使用StringRedisSerializer来序列化和反序列化redis的key值 + template.setKeySerializer(new StringRedisSerializer()); + template.setValueSerializer(jackson2JsonRedisSerializer); + // Hash的key也采用StringRedisSerializer的序列化方式 + template.setHashKeySerializer(new StringRedisSerializer()); + template.setHashValueSerializer(jackson2JsonRedisSerializer); + template.afterPropertiesSet(); + return template; + } + +} diff --git a/src/main/java/com/gmh/controller/ExcelController.java b/src/main/java/com/gmh/controller/ExcelController.java index 26a562d..56860bd 100644 --- a/src/main/java/com/gmh/controller/ExcelController.java +++ b/src/main/java/com/gmh/controller/ExcelController.java @@ -1,19 +1,28 @@ package com.gmh.controller; +import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.gmh.entity.GmhUser; import com.gmh.entity.R; +import com.gmh.entity.RowCellNum; import com.gmh.entity.SysBaijiaxing; import com.gmh.entity.vo.ReadNameVo; import com.gmh.service.SysBaijiaxingService; import com.gmh.utils.POIExcelUtil; +import com.gmh.utils.RedisUtils; +import com.gmh.utils.RedisUtilsDefault; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.util.CellReference; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; +import java.io.FileOutputStream; import java.io.IOException; import java.util.*; import java.util.stream.Collectors; @@ -22,10 +31,17 @@ import java.util.stream.Collectors; @RequestMapping("/excel") public class ExcelController { - public final ThreadLocal workbookThreadLocal = new ThreadLocal<>(); + private Integer templateSheetNum = null; + + private Workbook templateWorkbook = null; + + private List readNameVoList = null; private static final String KEY_XINGMING = "姓名"; + @Value("${gmh.upload-path}") + private String uploadPath; + @RequestMapping("/t01") public R test01() { return R.ok("ok"); @@ -34,7 +50,7 @@ public class ExcelController { @RequestMapping("/readData") public R readSourceData(MultipartFile file) throws IOException { Workbook workbook = POIExcelUtil.readExcelFromInputStream(file.getInputStream(), file.getOriginalFilename()); - List resultList = new ArrayList<>(); + readNameVoList = new ArrayList<>(); for (int i = 0; i < workbook.getNumberOfSheets(); i++) { ReadNameVo readNameVo = new ReadNameVo(); Sheet sheet = workbook.getSheetAt(i); @@ -59,7 +75,7 @@ public class ExcelController { for (Map excelMap : excelMaps) { for (String key : excelMap.keySet()) { String value = excelMap.get(key); - if (isChinaName(value)) { + if (baijiaxingService.likeChineseName(value)) { Integer count = map.getOrDefault(key, 0); map.put(key, ++count); } @@ -72,9 +88,10 @@ public class ExcelController { readNameVo = getReadNameDataForNameKey(excelMaps, key); } readNameVo.setSheetName(sheet.getSheetName()); - resultList.add(readNameVo); + readNameVoList.add(readNameVo); } - return R.ok().setData(resultList); + + return R.ok().setData(readNameVoList); } public ReadNameVo getReadNameDataForNameKey(List> excelMaps, final String key) { @@ -97,11 +114,90 @@ public class ExcelController { } @RequestMapping("/template") - public R templateUpload(MultipartFile file) throws IOException { + public R templateUpload(MultipartFile file, String templateSheetNum) throws IOException { Workbook workbook = POIExcelUtil.readExcelFromInputStream(file.getInputStream(), file.getOriginalFilename()); - List> maps = POIExcelUtil.toListMap(workbook.getSheetAt(0)); - workbookThreadLocal.set(workbook); - return R.ok().setData(maps); + this.templateSheetNum = Integer.parseInt(templateSheetNum) - 1; + Sheet sheet = workbook.getSheetAt(this.templateSheetNum); + if (sheet == null) { + throw new RuntimeException("sheet页不存在"); + } + this.templateWorkbook = workbook; + return R.ok(); + } + + @RequestMapping("/cellNumSearch") + public R analysisCell(String cellNum) { + String cellStrNum = cellNum.replaceAll("[^a-z^A-Z]", ""); + String rowNum = cellNum.replaceAll("[^0-9]", ""); + if (cellStrNum.length() > 2) { + return R.error(cellNum + "单元格不存在"); + } + if (this.templateWorkbook == null) { + return R.error("模板文件出问题了,请重新上传"); + } + Sheet sheet = this.templateWorkbook.getSheetAt(this.templateSheetNum); + Row row = sheet.getRow(Integer.parseInt(rowNum) - 1); + if (row == null) { + return R.error(cellNum + "单元格无内容"); + } + int i = CellReference.convertColStringToIndex(cellStrNum); + Cell cell = row.getCell(i); + if (cell == null) { + return R.error(cellNum + "单元格无内容"); + } + String stringCellVal = POIExcelUtil.getStringCellVal(cell); + if (StrUtil.isEmpty(stringCellVal)) { + return R.error(cellNum + "单元格无内容"); + } + return R.ok().setData(stringCellVal); + } + + public RowCellNum analysisCellNum(String cellNum) { + return null; + } + + + + @RequestMapping("/executeTemplate") + public R executeTemplate(String newCellData) throws IOException { + System.out.println(newCellData); + String expr = newCellData.substring(newCellData.indexOf("@{"), newCellData.indexOf("}") + 1); + String exprVal = newCellData.substring(newCellData.indexOf("@{") + 2, newCellData.indexOf("}")); + String[] exprArr = exprVal.split("\\."); + String sheetName = exprArr[0]; + String field = exprArr[1]; + + Optional first = readNameVoList + .stream() + .filter(item -> item.getSheetName().equalsIgnoreCase(sheetName)) + .findFirst(); + if (!first.isPresent()) { + return R.error("未找到sheet页,可能是表达式错误"); + } + ReadNameVo readNameVo = first.get(); + POIExcelUtil.removeModelSheet(this.templateWorkbook, sheetName); + POIExcelUtil.batchCloneSheet(this.templateWorkbook, this.templateSheetNum, readNameVo.getNameList().size()); + ArrayList gmhUsers = new ArrayList<>(readNameVo.getNameList()); + for (int i = 0; i < this.templateWorkbook.getNumberOfSheets(); i++) { + Sheet sheet = templateWorkbook.getSheetAt(i); + if (sheet == null) { + continue; + } + Row row = sheet.getRow(2); + Cell cell = row.getCell(0); + cell.setCellValue(newCellData.replace(expr, gmhUsers.get(i).getName())); + } + + try { + FileOutputStream out = new FileOutputStream("C:\\Users\\admin\\Desktop\\tempfile\\output\\out的222.xls"); + out.flush(); + templateWorkbook.write(out); + out.close(); + System.out.println("文件输出完成"); + } catch (IOException e) { + e.printStackTrace(); + } + return R.ok(); } @Autowired diff --git a/src/main/java/com/gmh/entity/RowCellNum.java b/src/main/java/com/gmh/entity/RowCellNum.java new file mode 100644 index 0000000..62c2388 --- /dev/null +++ b/src/main/java/com/gmh/entity/RowCellNum.java @@ -0,0 +1,11 @@ +package com.gmh.entity; + +import lombok.Data; + +@Data +public class RowCellNum { + + private Integer rowNum; + + private Integer cellNum; +} diff --git a/src/main/java/com/gmh/service/SysBaijiaxingService.java b/src/main/java/com/gmh/service/SysBaijiaxingService.java index 298480e..cdf759b 100644 --- a/src/main/java/com/gmh/service/SysBaijiaxingService.java +++ b/src/main/java/com/gmh/service/SysBaijiaxingService.java @@ -4,4 +4,6 @@ import com.baomidou.mybatisplus.extension.service.IService; import com.gmh.entity.SysBaijiaxing; public interface SysBaijiaxingService extends IService { + + boolean likeChineseName(String val); } diff --git a/src/main/java/com/gmh/service/impl/SysBaijiaxingServiceImpl.java b/src/main/java/com/gmh/service/impl/SysBaijiaxingServiceImpl.java index 5ddac19..8d411ad 100644 --- a/src/main/java/com/gmh/service/impl/SysBaijiaxingServiceImpl.java +++ b/src/main/java/com/gmh/service/impl/SysBaijiaxingServiceImpl.java @@ -1,11 +1,49 @@ package com.gmh.service.impl; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.gmh.dao.SysBaijiaxingDao; import com.gmh.entity.SysBaijiaxing; import com.gmh.service.SysBaijiaxingService; +import com.gmh.utils.RedisUtils; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.util.Collection; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + @Service -public class SysBaijiaxingServiceImpl extends ServiceImpl implements SysBaijiaxingService { +public class SysBaijiaxingServiceImpl extends ServiceImpl implements SysBaijiaxingService, InitializingBean { + + @Autowired + private RedisUtils redisUtils; + + @Override + public boolean likeChineseName(String val) { + if (StrUtil.isBlank(val)) { + return false; + } + String first = val.substring(0, 1); + Collection keys = redisUtils.keys(KEY_XINGSHI + first + "*"); + return keys.size() > 0; +// QueryWrapper queryWrapper = new QueryWrapper<>(); +// queryWrapper.apply("{0} LIKE CONCAT(xingshi,'%')", val); +// List list = list(queryWrapper); +// return !list.isEmpty(); + } + + private static final String KEY_XINGSHI = "cache_xingshi:"; + + @Override + public void afterPropertiesSet() { + List list = list(); + Set collect = list.stream().map(SysBaijiaxing::getXingshi).collect(Collectors.toSet()); + for (String xingshi : collect) { + redisUtils.setCacheObject(KEY_XINGSHI + xingshi, xingshi); + } + } } diff --git a/src/main/java/com/gmh/utils/POIExcelUtil.java b/src/main/java/com/gmh/utils/POIExcelUtil.java index 0f1e03f..e291c1c 100644 --- a/src/main/java/com/gmh/utils/POIExcelUtil.java +++ b/src/main/java/com/gmh/utils/POIExcelUtil.java @@ -208,4 +208,34 @@ public class POIExcelUtil { } return list; } + + /** + * 删除模板表格(除了 noDelSheet之外所有的) + */ + public static void removeModelSheet(Workbook wb, Sheet noDelSheet) { + int sheetIndex = wb.getSheetIndex(noDelSheet); + removeModelSheet(wb,sheetIndex); + } + + /** + * 删除模板表格(除了 name之外所有的) + */ + public static void removeModelSheet(Workbook wb, String name) { + int sheetIndex = wb.getSheetIndex(wb.getSheet(name)); + removeModelSheet(wb,sheetIndex); + } + + /** + * 删除模板表格(除了 id之外所有的) + */ + public static void removeModelSheet(Workbook wb, int id) { + int numberOfSheets = wb.getNumberOfSheets(); + for (int i = numberOfSheets - 1; i > -1; i--) { + if (i != id) { + wb.removeSheetAt(i); + } + } + //设置默认显示第一页 + wb.setActiveSheet(0); + } } \ No newline at end of file diff --git a/src/main/java/com/gmh/utils/RedisUtils.java b/src/main/java/com/gmh/utils/RedisUtils.java new file mode 100644 index 0000000..3119cd4 --- /dev/null +++ b/src/main/java/com/gmh/utils/RedisUtils.java @@ -0,0 +1,288 @@ +package com.gmh.utils; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.BoundSetOperations; +import org.springframework.data.redis.core.HashOperations; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ValueOperations; +import org.springframework.stereotype.Component; + +/** + * spring redis 工具类 + * + * @author callCenter + **/ +@SuppressWarnings(value = {"unchecked", "rawtypes"}) +@Component +public class RedisUtils { + + @Autowired + public RedisTemplate redisTemplate; + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + */ + public void setCacheObject(final String key, final T value) { + redisTemplate.opsForValue().set(key, value); + } + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + * @param timeout 时间 + * @param timeUnit 时间颗粒度 + */ + public void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) { + redisTemplate.opsForValue().set(key, value, timeout, timeUnit); + } + + /** + * 设置有效时间 + * + * @param key Redis键 + * @param timeout 超时时间 + * @return true=设置成功;false=设置失败 + */ + public boolean expire(final String key, final long timeout) { + return expire(key, timeout, TimeUnit.SECONDS); + } + + /** + * 设置有效时间 + * + * @param key Redis键 + * @param timeout 超时时间 + * @param unit 时间单位 + * @return true=设置成功;false=设置失败 + */ + public boolean expire(final String key, final long timeout, final TimeUnit unit) { + return redisTemplate.expire(key, timeout, unit); + } + + /** + * 获得缓存的基本对象。 + * + * @param key 缓存键值 + * @return 缓存键值对应的数据 + */ + public T getCacheObject(final String key) { + ValueOperations operation = redisTemplate.opsForValue(); + return operation.get(key); + } + + /** + * 删除单个对象 + * + * @param key + */ + public boolean deleteObject(final String key) { + return redisTemplate.delete(key); + } + + /** + * 删除集合对象 + * + * @param collection 多个对象 + * @return + */ + public long deleteObject(final Collection collection) { + return redisTemplate.delete(collection); + } + + /** + * 缓存List数据 + * + * @param key 缓存的键值 + * @param dataList 待缓存的List数据 + * @return 缓存的对象 + */ + public long setCacheList(final String key, final List dataList) { + Long count = redisTemplate.opsForList().rightPushAll(key, dataList); + return count == null ? 0 : count; + } + + /** + * 获得缓存的list对象 + * + * @param key 缓存的键值 + * @return 缓存键值对应的数据 + */ + public List getCacheList(final String key) { + return redisTemplate.opsForList().range(key, 0, -1); + } + + /** + * 缓存Set + * + * @param key 缓存键值 + * @param dataSet 缓存的数据 + * @return 缓存数据的对象 + */ + public BoundSetOperations setCacheSet(final String key, final Set dataSet) { + BoundSetOperations setOperation = redisTemplate.boundSetOps(key); + Iterator it = dataSet.iterator(); + while (it.hasNext()) { + setOperation.add(it.next()); + } + return setOperation; + } + + /** + * 获得缓存的set + * + * @param key + * @return + */ + public Set getCacheSet(final String key) { + return redisTemplate.opsForSet().members(key); + } + + /** + * 缓存Map + * + * @param key + * @param dataMap + */ + public void setCacheMap(final String key, final Map dataMap) { + if (dataMap != null) { + redisTemplate.opsForHash().putAll(key, dataMap); + } + } + + /** + * 获得缓存的Map + * + * @param key + * @return + */ + public Map getCacheMap(final String key) { + return redisTemplate.opsForHash().entries(key); + } + + /** + * 往Hash中存入数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @param value 值 + */ + public void setCacheMapValue(final String key, final String hKey, final T value) { + redisTemplate.opsForHash().put(key, hKey, value); + } + + /** + * 获取Hash中的数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @return Hash中的对象 + */ + public T getCacheMapValue(final String key, final String hKey) { + HashOperations opsForHash = redisTemplate.opsForHash(); + return opsForHash.get(key, hKey); + } + + /** + * 删除Hash中的数据 + * + * @param key + * @param hKey + */ + public void delCacheMapValue(final String key, final String hKey) { + HashOperations hashOperations = redisTemplate.opsForHash(); + hashOperations.delete(key, hKey); + } + + /** + * 获取多个Hash中的数据 + * + * @param key Redis键 + * @param hKeys Hash键集合 + * @return Hash对象集合 + */ + public List getMultiCacheMapValue(final String key, final Collection hKeys) { + return redisTemplate.opsForHash().multiGet(key, hKeys); + } + + /** + * 获得缓存的基本对象列表 + * + * @param pattern 字符串前缀 + * @return 对象列表 + */ + public Collection keys(final String pattern) { + return redisTemplate.keys(pattern); + } + + /** + * 判断set集合中是否包含 + * + * @param key key + * @param value value + * @return + */ + public boolean setContains(String key, String value) { + Boolean member = redisTemplate.opsForSet().isMember(key, value); + if (member != null) { + return member; + } + return false; + } + + /** + * 是否包含key + * + * @param key + * @return + */ + public boolean hasKey(String key) { + Boolean hasKey = redisTemplate.hasKey(key); + if (hasKey == null) { + return false; + } + return hasKey; + } + + /** + * 查看 key 的过期时间 + * + * @param key + * @return + */ + public Long expireTime(String key) { + return redisTemplate.opsForValue().getOperations().getExpire(key); + } + + + /** + * 缓存List的size + * + * @param key 缓存的键值 + * @return 缓存List的size + */ + public long getCacheListSisz(final String key) { + return redisTemplate.opsForList().size(key); + } + + /** + * 缓存的list范围查询 + * + * @param key 缓存的键值 + * @return 缓存键值对应的数据 + */ + public List getCacheListRange(final String key, long start, long end) { + return redisTemplate.opsForList().range(key, start, end); + } +} diff --git a/src/main/java/com/gmh/utils/RedisUtilsDefault.java b/src/main/java/com/gmh/utils/RedisUtilsDefault.java new file mode 100644 index 0000000..4c8882c --- /dev/null +++ b/src/main/java/com/gmh/utils/RedisUtilsDefault.java @@ -0,0 +1,55 @@ +package com.gmh.utils; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ValueOperations; +import org.springframework.stereotype.Component; + +import java.util.concurrent.TimeUnit; + +/** + * value为jdk默认序列化方式的redis工具类 + * 兼容pthink-plus项目中的redis value + */ +@Component +public class RedisUtilsDefault { + + @Autowired + @Qualifier("redisTemplateForJdkSerializer") + public RedisTemplate redisTemplate; + + /** + * 获得缓存的基本对象。 + * + * @param key 缓存键值 + * @return 缓存键值对应的数据 + */ + public T getCacheObject(final String key) { + ValueOperations operation = redisTemplate.opsForValue(); + return operation.get(key); + } + + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + */ + public void setCacheObject(final String key, final T value) { + redisTemplate.opsForValue().set(key, value); + } + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + * @param timeout 时间 + * @param timeUnit 时间颗粒度 + */ + public void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) { + redisTemplate.opsForValue().set(key, value, timeout, timeUnit); + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 505afbb..a779b5b 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -3,3 +3,13 @@ spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/gmh?useSSL=false&useUnicode=true&characterEncoding=utf-8&autoReconnect=true&serverTimezone=Asia/Shanghai spring.datasource.username=root spring.datasource.password=root + +spring.redis.host=127.0.0.1 +spring.redis.port=6379 +spring.redis.database=6 + +gmh.upload-path="C:/develop/projUpload + +#???????? +spring.servlet.multipart.max-file-size=31457280 +spring.servlet.multipart.max-request-size=31457280