diff --git a/cloud-market-server/pom.xml b/cloud-market-server/pom.xml index ebff469..ff2667a 100644 --- a/cloud-market-server/pom.xml +++ b/cloud-market-server/pom.xml @@ -137,11 +137,53 @@ 2.2.0 + + + org.apache.httpcomponents + httpmime + 4.5.14 + + + + + com.alibaba + fastjson + 1.2.41 + + + + + org.apache.poi + poi-ooxml + 3.16 + + + + + org.apache.poi + poi-ooxml + 3.17 + + + org.apache.poi + poi-ooxml-schemas + 3.17 + + + org.apache.commons + commons-lang3 + 3.12.0 + + + + + com.alibaba + easyexcel + 2.2.7 + - - cloud-market @@ -156,6 +198,7 @@ + org.apache.maven.plugins diff --git a/cloud-market-server/src/main/java/com/muyu/cloud/market/config/AliPayConfig.java b/cloud-market-server/src/main/java/com/muyu/cloud/market/config/AliPayConfig.java index d39ff8f..0093f34 100644 --- a/cloud-market-server/src/main/java/com/muyu/cloud/market/config/AliPayConfig.java +++ b/cloud-market-server/src/main/java/com/muyu/cloud/market/config/AliPayConfig.java @@ -16,13 +16,19 @@ public class AliPayConfig { private String alipayPublicKey; private String notifyUrl; + private String gatewayUrl; + private String returnUrl; + private String format; + private String charset; + private String signType; + @PostConstruct public void init() { // 设置参数(全局只需设置一次) Config config = new Config(); config.protocol = "https"; - config.gatewayHost = "openapi-sandbox.dl.alipaydev.com"; + config.gatewayHost = "openapi.alipaydev.com"; config.signType = "RSA2"; config.appId = this.appId; config.merchantPrivateKey = this.appPrivateKey; diff --git a/cloud-market-server/src/main/java/com/muyu/cloud/market/controller/AliPayController.java b/cloud-market-server/src/main/java/com/muyu/cloud/market/controller/AliPayController.java index 14fa37b..bf690dd 100644 --- a/cloud-market-server/src/main/java/com/muyu/cloud/market/controller/AliPayController.java +++ b/cloud-market-server/src/main/java/com/muyu/cloud/market/controller/AliPayController.java @@ -17,6 +17,7 @@ import java.util.Map; @RequestMapping("/alipay") public class AliPayController { + @Resource private OrderShowMapper ordersMapper; diff --git a/cloud-market-server/src/main/java/com/muyu/cloud/market/controller/OrdersController.java b/cloud-market-server/src/main/java/com/muyu/cloud/market/controller/OrdersController.java index 7623303..abf7816 100644 --- a/cloud-market-server/src/main/java/com/muyu/cloud/market/controller/OrdersController.java +++ b/cloud-market-server/src/main/java/com/muyu/cloud/market/controller/OrdersController.java @@ -7,14 +7,17 @@ import com.muyu.cloud.market.domin.req.OrdersAddReq; import com.muyu.cloud.market.domin.req.OrdersListReq; import com.muyu.cloud.market.domin.req.OrdersUpdReq; import com.muyu.cloud.market.service.OrderShowService; +import com.muyu.cloud.market.util.poi.ExcelUtil; import com.muyu.common.core.domain.Result; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; import lombok.extern.log4j.Log4j2; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; +import java.util.List; @Log4j2 @RestController @@ -80,7 +83,7 @@ public class OrdersController { } /** - * 逻辑删除->根据orderid修改exist字段 1->0 + * 逻辑复原->根据orderid修改exist字段 0->1 * 订单回收站 * @param ordersId * @return @@ -114,7 +117,16 @@ public class OrdersController { return orderShowService.findAllById(ordersId); } - + //导出 + @RequestMapping("/getOutAll") + public void getOutAll(@Validated @RequestBody HttpServletResponse response){ + //创建工具类对象 + ExcelUtil excelUtil = new ExcelUtil<>(Orders.class); + //去数据库查询要导出来的数据 + List list = orderShowService.getOutAll(); + //调用工具类导出的方法 + excelUtil.exportExcel(response,list,"订单信息表"); + } diff --git a/cloud-market-server/src/main/java/com/muyu/cloud/market/controller/ProductApiController.java b/cloud-market-server/src/main/java/com/muyu/cloud/market/controller/ProductApiController.java index 7e68cd4..a6e399d 100644 --- a/cloud-market-server/src/main/java/com/muyu/cloud/market/controller/ProductApiController.java +++ b/cloud-market-server/src/main/java/com/muyu/cloud/market/controller/ProductApiController.java @@ -124,4 +124,6 @@ public class ProductApiController { } + + } diff --git a/cloud-market-server/src/main/java/com/muyu/cloud/market/mapper/OrderShowMapper.java b/cloud-market-server/src/main/java/com/muyu/cloud/market/mapper/OrderShowMapper.java index 02e8dab..e8e2cb4 100644 --- a/cloud-market-server/src/main/java/com/muyu/cloud/market/mapper/OrderShowMapper.java +++ b/cloud-market-server/src/main/java/com/muyu/cloud/market/mapper/OrderShowMapper.java @@ -5,6 +5,7 @@ import com.muyu.cloud.market.domin.req.OrdersAddReq; import com.muyu.cloud.market.domin.req.OrdersListReq; import com.muyu.cloud.market.domin.req.OrdersUpdReq; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import java.util.List; @@ -66,13 +67,6 @@ public interface OrderShowMapper { Orders findAllById (Integer ordersId); - /** - * 支付宝 - * @param ordersNum - * @return - */ - @Select("select * from orders where orders_num = #{ordersNum}") - Orders selectByordersNum(String ordersNum); void updateState(String tradeNo, String 已支付, String gmtPayment, String alipayTradeNo); @@ -80,5 +74,22 @@ public interface OrderShowMapper { * 订单修改支付状态后,更改该商品的销量 */ + //导出 + List getOutAll(); + //选中导出 + List getseleOut(@Param("ids") Integer[] ids); + //搜索导出 + List getsearOut(@Param("empName") String empName); + + + /** + * 支付宝 + * @param ordersNum + * @return + */ + ; + + @Select("select * from orders where orders_num = #{ordersNum}") + Orders selectByordersNum(String ordersNum); } diff --git a/cloud-market-server/src/main/java/com/muyu/cloud/market/mapper/ProductApiMapper.java b/cloud-market-server/src/main/java/com/muyu/cloud/market/mapper/ProductApiMapper.java index acbee5e..a898434 100644 --- a/cloud-market-server/src/main/java/com/muyu/cloud/market/mapper/ProductApiMapper.java +++ b/cloud-market-server/src/main/java/com/muyu/cloud/market/mapper/ProductApiMapper.java @@ -27,4 +27,7 @@ public interface ProductApiMapper extends BaseMapper { */ List selectspecification(ChosehowpayResp chosehowpayResp); + + + } diff --git a/cloud-market-server/src/main/java/com/muyu/cloud/market/service/OrderShowService.java b/cloud-market-server/src/main/java/com/muyu/cloud/market/service/OrderShowService.java index dbf895d..ca732e6 100644 --- a/cloud-market-server/src/main/java/com/muyu/cloud/market/service/OrderShowService.java +++ b/cloud-market-server/src/main/java/com/muyu/cloud/market/service/OrderShowService.java @@ -7,6 +7,8 @@ import com.muyu.cloud.market.domin.req.OrdersListReq; import com.muyu.cloud.market.domin.req.OrdersUpdReq; import com.muyu.common.core.domain.Result; +import java.util.List; + public interface OrderShowService { @@ -71,7 +73,11 @@ public interface OrderShowService { */ Orders selectByOrderNo(String ordersNum); - + /** + * 导出 + * @return + */ + List getOutAll(); } diff --git a/cloud-market-server/src/main/java/com/muyu/cloud/market/service/impl/OrderShowServiceImpl.java b/cloud-market-server/src/main/java/com/muyu/cloud/market/service/impl/OrderShowServiceImpl.java index 45ccb0a..d335d25 100644 --- a/cloud-market-server/src/main/java/com/muyu/cloud/market/service/impl/OrderShowServiceImpl.java +++ b/cloud-market-server/src/main/java/com/muyu/cloud/market/service/impl/OrderShowServiceImpl.java @@ -123,6 +123,11 @@ public class OrderShowServiceImpl implements OrderShowService { } + @Override + public List getOutAll() { + return orderShowMapper.getOutAll(); + } + public Orders selectByOrderNo(String ordersNum) { return orderShowMapper.selectByordersNum(ordersNum); } diff --git a/cloud-market-server/src/main/java/com/muyu/cloud/market/util/core/date/DateUtils.java b/cloud-market-server/src/main/java/com/muyu/cloud/market/util/core/date/DateUtils.java new file mode 100644 index 0000000..8a8781f --- /dev/null +++ b/cloud-market-server/src/main/java/com/muyu/cloud/market/util/core/date/DateUtils.java @@ -0,0 +1,137 @@ +package com.muyu.cloud.market.util.core.date; + +import org.apache.commons.lang3.time.DateFormatUtils; + +import java.io.File; +import java.lang.management.ManagementFactory; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * 时间工具类 + * + * @author markGuo + */ +public class DateUtils extends org.apache.commons.lang3.time.DateUtils +{ + public static String YYYY = "yyyy"; + + public static String YYYY_MM = "yyyy-MM"; + + public static String YYYY_MM_DD = "yyyy-MM-dd"; + + public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss"; + + public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss"; + + private static String[] parsePatterns = { + "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", + "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM", + "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"}; + + /** + * 获取当前Date型日期 + * + * @return Date() 当前日期 + */ + public static Date getNowDate() { + return new Date(); + } + + /** + * 获取当前日期, 默认格式为yyyy-MM-dd + * + * @return String + */ + public static String getDate() { + return dateTimeNow(YYYY_MM_DD); + } + + public static final String getTime() { + return dateTimeNow(YYYY_MM_DD_HH_MM_SS); + } + + public static final String dateTimeNow() { + return dateTimeNow(YYYYMMDDHHMMSS); + } + + public static final String dateTimeNow(final String format) { + return parseDateToStr(format, new Date()); + } + + public static final String dateTime(final Date date) { + return parseDateToStr(YYYY_MM_DD, date); + } + + public static final String parseDateToStr(final String format, final Date date) { + return new SimpleDateFormat(format).format(date); + } + + public static final Date dateTime(final String format, final String ts) { + try { + return new SimpleDateFormat(format).parse(ts); + } catch (ParseException e) { + throw new RuntimeException(e); + } + } + + /** + * 日期路径 即年/月/日 如2018/08/08 + */ + public static final String datePath() { + Date now = new Date(); + return DateFormatUtils.format(now, "yyyy"+File.separator+"MM"+ File.separator +"dd"); + } + + /** + * 日期路径 即年/月/日 如20180808 + */ + public static final String dateTime() { + Date now = new Date(); + return DateFormatUtils.format(now, "yyyyMMdd"); + } + + /** + * 日期型字符串转化为日期 格式 + */ + public static Date parseDate(Object str) { + if (str == null) { + return null; + } + try { + return parseDate(str.toString(), parsePatterns); + } catch (ParseException e) { + return null; + } + } + + /** + * 获取服务器启动时间 + */ + public static Date getServerStartDate() { + long time = ManagementFactory.getRuntimeMXBean().getStartTime(); + return new Date(time); + } + + /** + * 计算两个时间差 + */ + public static String getDatePoor(Date endDate, Date nowDate) { + long nd = 1000 * 24 * 60 * 60; + long nh = 1000 * 60 * 60; + long nm = 1000 * 60; + // long ns = 1000; + // 获得两个时间的毫秒时间差异 + long diff = endDate.getTime() - nowDate.getTime(); + // 计算差多少天 + long day = diff / nd; + // 计算差多少小时 + long hour = diff % nd / nh; + // 计算差多少分钟 + long min = diff % nd % nh / nm; + // 计算差多少秒//输出结果 + // long sec = diff % nd % nh % nm / ns; + return day + "天" + hour + "小时" + min + "分钟"; + } +} diff --git a/cloud-market-server/src/main/java/com/muyu/cloud/market/util/core/reflect/ReflectUtils.java b/cloud-market-server/src/main/java/com/muyu/cloud/market/util/core/reflect/ReflectUtils.java new file mode 100644 index 0000000..4072f7a --- /dev/null +++ b/cloud-market-server/src/main/java/com/muyu/cloud/market/util/core/reflect/ReflectUtils.java @@ -0,0 +1,387 @@ +package com.muyu.cloud.market.util.core.reflect; + + +import com.muyu.cloud.market.util.core.date.DateUtils; +import com.muyu.cloud.market.util.core.text.Convert; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Validate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.*; +import java.util.Date; + +/** + * 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数. + * + * @author markGuo + */ +@SuppressWarnings("rawtypes") +public class ReflectUtils { + /** + * set方法前缀 + */ + private static final String SETTER_PREFIX = "set"; + + /** + * get方法前缀 + */ + private static final String GETTER_PREFIX = "get"; + + /** + * CGLIB 类分隔符 + */ + private static final String CGLIB_CLASS_SEPARATOR = "$$"; + + /** + * 日志 + */ + private static Logger logger = LoggerFactory.getLogger(ReflectUtils.class); + + /** + * 调用Getter方法. + * 支持多级,如:对象名.对象名.方法 + * @param obj + * @param propertyName + * @param + * @return + */ + @SuppressWarnings("unchecked") + public static E invokeGetter(Object obj, String propertyName) { + Object object = obj; + for (String name : StringUtils.split(propertyName, ".")) { + String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name); + object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {}); + } + return (E) object; + } + + /** + * 调用Setter方法, 仅匹配方法名。 + * 支持多级,如:对象名.对象名.方法 + * @param obj + * @param propertyName + * @param value + * @param + */ + public static void invokeSetter(Object obj, String propertyName, E value) { + Object object = obj; + String[] names = StringUtils.split(propertyName, "."); + for (int i = 0; i < names.length; i++) { + if (i < names.length - 1) { + String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]); + object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {}); + } else { + String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]); + invokeMethodByName(object, setterMethodName, new Object[] { value }); + } + } + } + + /** + * 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数. + * @param obj + * @param fieldName + * @param + * @return + */ + @SuppressWarnings("unchecked") + public static E getFieldValue(final Object obj, final String fieldName) { + Field field = getAccessibleField(obj, fieldName); + if (field == null) { + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); + return null; + } + E result = null; + try { + result = (E) field.get(obj); + } catch (IllegalAccessException e) { + logger.error("不可能抛出的异常{}", e.getMessage()); + } + return result; + } + + /** + * 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数. + * @param obj + * @param fieldName + * @param value + * @param + */ + public static void setFieldValue(final Object obj, final String fieldName, final E value) { + Field field = getAccessibleField(obj, fieldName); + if (field == null) { + // throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); + return; + } + try { + field.set(obj, value); + } catch (IllegalAccessException e) { + logger.error("不可能抛出的异常: {}", e.getMessage()); + } + } + + /** + * 直接调用对象方法, 无视private/protected修饰符. + * 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用. + * 同时匹配方法名+参数类型, + * @param obj + * @param methodName + * @param parameterTypes + * @param args + * @param + * @return + */ + @SuppressWarnings("unchecked") + public static E invokeMethod(final Object obj, final String methodName, final Class[] parameterTypes, + final Object[] args) { + if (obj == null || methodName == null) { + return null; + } + Method method = getAccessibleMethod(obj, methodName, parameterTypes); + if (method == null) { + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 "); + return null; + } + try { + return (E) method.invoke(obj, args); + } catch (Exception e) { + String msg = "method: " + method + ", obj: " + obj + ", args: " + args + ""; + throw convertReflectionExceptionToUnchecked(msg, e); + } + } + + /** + * 直接调用对象方法, 无视private/protected修饰符, + * 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用. + * 只匹配函数名,如果有多个同名函数调用第一个。 + * @param obj + * @param methodName + * @param args + * @param + * @return + */ + @SuppressWarnings("unchecked") + public static E invokeMethodByName(final Object obj, final String methodName, final Object[] args) { + Method method = getAccessibleMethodByName(obj, methodName, args.length); + if (method == null) { + // 如果为空不报错,直接返回空。 + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 "); + return null; + } + try { + // 类型转换(将参数数据类型转换为目标方法参数类型) + Class[] cs = method.getParameterTypes(); + for (int i = 0; i < cs.length; i++) { + if (args[i] != null && !args[i].getClass().equals(cs[i])) { + if (cs[i] == String.class) { + args[i] = Convert.toStr(args[i]); + if (StringUtils.endsWith((String) args[i], ".0")) { + args[i] = StringUtils.substringBefore((String) args[i], ".0"); + } + } else if (cs[i] == Integer.class) { + args[i] = Convert.toInt(args[i]); + } else if (cs[i] == Long.class) { + args[i] = Convert.toLong(args[i]); + } else if (cs[i] == Double.class) { + args[i] = Convert.toDouble(args[i]); + } else if (cs[i] == Float.class) { + args[i] = Convert.toFloat(args[i]); + } else if (cs[i] == Date.class) { + if (args[i] instanceof String) { + args[i] = DateUtils.parseDate(args[i]); + } + } + } + } + return (E) method.invoke(obj, args); + } catch (Exception e) { + String msg = "method: " + method + ", obj: " + obj + ", args: " + args + ""; + throw convertReflectionExceptionToUnchecked(msg, e); + } + } + + /** + * 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问. + * 如向上转型到Object仍无法找到, 返回null. + * @param obj + * @param fieldName + * @return + */ + public static Field getAccessibleField(final Object obj, final String fieldName) { + // 为空不报错。直接返回 null + if (obj == null) { + return null; + } + Validate.notBlank(fieldName, "fieldName can't be blank"); + for (Class superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) { + try { + Field field = superClass.getDeclaredField(fieldName); + makeAccessible(field); + return field; + } + catch (NoSuchFieldException e) { + continue; + } + } + return null; + } + + /** + * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. + * 如向上转型到Object仍无法找到, 返回null. + * 匹配函数名+参数类型。 + * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) + * @param obj + * @param methodName + * @param parameterTypes + * @return + */ + public static Method getAccessibleMethod(final Object obj, final String methodName, + final Class... parameterTypes) { + // 为空不报错。直接返回 null + if (obj == null) { + return null; + } + Validate.notBlank(methodName, "methodName can't be blank"); + for (Class searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) { + try { + Method method = searchType.getDeclaredMethod(methodName, parameterTypes); + makeAccessible(method); + return method; + } catch (NoSuchMethodException e) { + continue; + } + } + return null; + } + + /** + * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. + * 如向上转型到Object仍无法找到, 返回null. + * 只匹配函数名。 + * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) + * @param obj + * @param methodName + * @param argsNum + * @return + */ + public static Method getAccessibleMethodByName(final Object obj, final String methodName, int argsNum) { + // 为空不报错。直接返回 null + if (obj == null) { + return null; + } + Validate.notBlank(methodName, "methodName can't be blank"); + for (Class searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) { + Method[] methods = searchType.getDeclaredMethods(); + for (Method method : methods) { + if (method.getName().equals(methodName) && method.getParameterTypes().length == argsNum) { + makeAccessible(method); + return method; + } + } + } + return null; + } + + /** + * 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 + * @param method + */ + public static void makeAccessible(Method method) { + if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) + && !method.isAccessible()) { + method.setAccessible(true); + } + } + + /** + * 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 + * @param field + */ + public static void makeAccessible(Field field) { + if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) + || Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) { + field.setAccessible(true); + } + } + + /** + * 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处 + * 如无法找到, 返回Object.class. + * @param clazz + * @param + * @return + */ + @SuppressWarnings("unchecked") + public static Class getClassGenricType(final Class clazz) { + return getClassGenricType(clazz, 0); + } + + /** + * 通过反射, 获得Class定义中声明的父类的泛型参数的类型. + * 如无法找到, 返回Object.class. + * @param clazz + * @param index + * @return + */ + public static Class getClassGenricType(final Class clazz, final int index) { + Type genType = clazz.getGenericSuperclass(); + + if (!(genType instanceof ParameterizedType)) { + logger.debug(clazz.getSimpleName() + "'s superclass not ParameterizedType"); + return Object.class; + } + + Type[] params = ((ParameterizedType) genType).getActualTypeArguments(); + + if (index >= params.length || index < 0) { + logger.debug("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: " + + params.length); + return Object.class; + } + if (!(params[index] instanceof Class)) { + logger.debug(clazz.getSimpleName() + " not set the actual class on superclass generic parameter"); + return Object.class; + } + + return (Class) params[index]; + } + + /** + * 通过实例化对象获取反射对象 + * @param instance + * @return + */ + public static Class getUserClass(Object instance) { + if (instance == null) { + throw new RuntimeException("Instance must not be null"); + } + Class clazz = instance.getClass(); + if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) { + Class superClass = clazz.getSuperclass(); + if (superClass != null && !Object.class.equals(superClass)) { + return superClass; + } + } + return clazz; + + } + + /** + * 将反射时的checked exception转换为unchecked exception. + * @param msg + * @param e + * @return + */ + public static RuntimeException convertReflectionExceptionToUnchecked(String msg, Exception e) { + if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException + || e instanceof NoSuchMethodException) { + return new IllegalArgumentException(msg, e); + } else if (e instanceof InvocationTargetException) { + return new RuntimeException(msg, ((InvocationTargetException) e).getTargetException()); + } + return new RuntimeException(msg, e); + } +} diff --git a/cloud-market-server/src/main/java/com/muyu/cloud/market/util/core/spring/SpringUtils.java b/cloud-market-server/src/main/java/com/muyu/cloud/market/util/core/spring/SpringUtils.java new file mode 100644 index 0000000..d732e02 --- /dev/null +++ b/cloud-market-server/src/main/java/com/muyu/cloud/market/util/core/spring/SpringUtils.java @@ -0,0 +1,106 @@ +package com.muyu.cloud.market.util.core.spring; + +import org.springframework.aop.framework.AopContext; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.stereotype.Component; + +/** + * spring工具类 方便在非spring管理环境中获取bean + * + * @author markGuo + */ +@Component +public final class SpringUtils implements BeanFactoryPostProcessor +{ + /** Spring应用上下文环境 */ + private static ConfigurableListableBeanFactory beanFactory; + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + SpringUtils.beanFactory = beanFactory; + } + + /** + * 获取对象 + * + * @param name + * @return Object 一个以所给名字注册的bean的实例 + * @throws BeansException + * + */ + @SuppressWarnings("unchecked") + public static T getBean(String name) throws BeansException { + return (T) beanFactory.getBean(name); + } + + /** + * 获取类型为requiredType的对象 + * + * @param clz + * @return + * @throws BeansException + * + */ + public static T getBean(Class clz) throws BeansException { + T result = beanFactory.getBean(clz); + return result; + } + + /** + * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true + * + * @param name + * @return boolean + */ + public static boolean containsBean(String name) { + return beanFactory.containsBean(name); + } + + /** + * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException) + * + * @param name + * @return boolean + * @throws NoSuchBeanDefinitionException + * + */ + public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException { + return beanFactory.isSingleton(name); + } + + /** + * @param name + * @return Class 注册对象的类型 + * @throws NoSuchBeanDefinitionException + * + */ + public static Class getType(String name) throws NoSuchBeanDefinitionException { + return beanFactory.getType(name); + } + + /** + * 如果给定的bean名字在bean定义中有别名,则返回这些别名 + * + * @param name + * @return + * @throws NoSuchBeanDefinitionException + * + */ + public static String[] getAliases(String name) throws NoSuchBeanDefinitionException { + return beanFactory.getAliases(name); + } + + /** + * 获取aop代理对象 + * + * @param invoker + * @return + */ + @SuppressWarnings("unchecked") + public static T getAopProxy(T invoker) { + return (T) AopContext.currentProxy(); + } +} diff --git a/cloud-market-server/src/main/java/com/muyu/cloud/market/util/core/text/CharsetKit.java b/cloud-market-server/src/main/java/com/muyu/cloud/market/util/core/text/CharsetKit.java new file mode 100644 index 0000000..77edd3b --- /dev/null +++ b/cloud-market-server/src/main/java/com/muyu/cloud/market/util/core/text/CharsetKit.java @@ -0,0 +1,78 @@ +package com.muyu.cloud.market.util.core.text; + +import org.apache.commons.lang3.StringUtils; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; + +/** + * 字符集工具类 + * + * @author markGuo + */ +public class CharsetKit +{ + /** ISO-8859-1 */ + public static final String ISO_8859_1 = "ISO-8859-1"; + /** UTF-8 */ + public static final String UTF_8 = "UTF-8"; + /** GBK */ + public static final String GBK = "GBK"; + + /** ISO-8859-1 */ + public static final Charset CHARSET_ISO_8859_1 = Charset.forName(ISO_8859_1); + /** UTF-8 */ + public static final Charset CHARSET_UTF_8 = Charset.forName(UTF_8); + /** GBK */ + public static final Charset CHARSET_GBK = Charset.forName(GBK); + + /** + * 转换为Charset对象 + * + * @param charset 字符集,为空则返回默认字符集 + * @return Charset + */ + public static Charset charset(String charset) { + return StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset); + } + + /** + * 转换字符串的字符集编码 + * + * @param source 字符串 + * @param srcCharset 源字符集,默认ISO-8859-1 + * @param destCharset 目标字符集,默认UTF-8 + * @return 转换后的字符集 + */ + public static String convert(String source, String srcCharset, String destCharset) { + return convert(source, Charset.forName(srcCharset), Charset.forName(destCharset)); + } + + /** + * 转换字符串的字符集编码 + * + * @param source 字符串 + * @param srcCharset 源字符集,默认ISO-8859-1 + * @param destCharset 目标字符集,默认UTF-8 + * @return 转换后的字符集 + */ + public static String convert(String source, Charset srcCharset, Charset destCharset) { + if (null == srcCharset) { + srcCharset = StandardCharsets.ISO_8859_1; + } + if (null == destCharset) { + srcCharset = StandardCharsets.UTF_8; + } + if (StringUtils.isEmpty(source) || srcCharset.equals(destCharset)) { + return source; + } + return new String(source.getBytes(srcCharset), destCharset); + } + + /** + * @return 系统字符集编码 + */ + public static String systemCharset() { + return Charset.defaultCharset().name(); + } +} diff --git a/cloud-market-server/src/main/java/com/muyu/cloud/market/util/core/text/Convert.java b/cloud-market-server/src/main/java/com/muyu/cloud/market/util/core/text/Convert.java new file mode 100644 index 0000000..b206705 --- /dev/null +++ b/cloud-market-server/src/main/java/com/muyu/cloud/market/util/core/text/Convert.java @@ -0,0 +1,818 @@ +package com.muyu.cloud.market.util.core.text; + + +import org.apache.commons.lang3.StringUtils; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.text.NumberFormat; +import java.util.Set; + +/** + * 类型转换器 + * + * @author markGuo + */ +public class Convert { + /** + * 转换为字符串
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static String toStr(Object value, String defaultValue) { + if (null == value) { + return defaultValue; + } + if (value instanceof String) { + return (String) value; + } + return value.toString(); + } + + /** + * 转换为字符串
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static String toStr(Object value) { + return toStr(value, null); + } + + /** + * 转换为字符
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Character toChar(Object value, Character defaultValue) { + if (null == value) { + return defaultValue; + } + if (value instanceof Character) { + return (Character) value; + } + + final String valueStr = toStr(value, null); + return StringUtils.isEmpty(valueStr) ? defaultValue : valueStr.charAt(0); + } + + /** + * 转换为字符
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Character toChar(Object value) { + return toChar(value, null); + } + + /** + * 转换为byte
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Byte toByte(Object value, Byte defaultValue) { + if (value == null) { + return defaultValue; + } else if (value instanceof Byte) { + return (Byte) value; + }else if (value instanceof Number) { + return ((Number) value).byteValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + try { + return Byte.parseByte(valueStr); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * 转换为byte
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Byte toByte(Object value) { + return toByte(value, null); + } + + /** + * 转换为Short
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Short toShort(Object value, Short defaultValue) { + if (value == null) { + return defaultValue; + }else if (value instanceof Short) { + return (Short) value; + } else if (value instanceof Number) { + return ((Number) value).shortValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + try { + return Short.parseShort(valueStr.trim()); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * 转换为Short
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Short toShort(Object value) { + return toShort(value, null); + } + + /** + * 转换为Number
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Number toNumber(Object value, Number defaultValue) { + if (value == null) { + return defaultValue; + } else if (value instanceof Number) { + return (Number) value; + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + try { + return NumberFormat.getInstance().parse(valueStr); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * 转换为Number
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Number toNumber(Object value) { + return toNumber(value, null); + } + + /** + * 转换为int
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Integer toInt(Object value, Integer defaultValue) { + if (value == null) { + return defaultValue; + } else if (value instanceof Integer) { + return (Integer) value; + } else if (value instanceof Number) { + return ((Number) value).intValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + try { + return Integer.parseInt(valueStr.trim()); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * 转换为int
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Integer toInt(Object value) { + return toInt(value, null); + } + + /** + * 转换为Integer数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static Integer[] toIntArray(String str) { + return toIntArray(",", str); + } + + /** + * 转换为Long数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static Long[] toLongArray(String str) { + return toLongArray(",", str); + } + + /** + * 转换为Integer数组
+ * + * @param split 分隔符 + * @param split 被转换的值 + * @return 结果 + */ + public static Integer[] toIntArray(String split, String str) { + if (StringUtils.isEmpty(str)) { + return new Integer[] {}; + } + String[] arr = str.split(split); + final Integer[] ints = new Integer[arr.length]; + for (int i = 0; i < arr.length; i++) { + final Integer v = toInt(arr[i], 0); + ints[i] = v; + } + return ints; + } + + /** + * 转换为Long数组
+ * + * @param split 分隔符 + * @param str 被转换的值 + * @return 结果 + */ + public static Long[] toLongArray(String split, String str) { + if (StringUtils.isEmpty(str)) { + return new Long[] {}; + } + String[] arr = str.split(split); + final Long[] longs = new Long[arr.length]; + for (int i = 0; i < arr.length; i++) { + final Long v = toLong(arr[i], null); + longs[i] = v; + } + return longs; + } + + /** + * 转换为String数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static String[] toStrArray(String str) { + return toStrArray(",", str); + } + + /** + * 转换为String数组
+ * + * @param split 分隔符 + * @param split 被转换的值 + * @return 结果 + */ + public static String[] toStrArray(String split, String str) { + return str.split(split); + } + + /** + * 转换为long
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Long toLong(Object value, Long defaultValue) { + if (value == null) { + return defaultValue; + } else if (value instanceof Long) { + return (Long) value; + } else if (value instanceof Number) { + return ((Number) value).longValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + try { + // 支持科学计数法 + return new BigDecimal(valueStr.trim()).longValue(); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * 转换为long
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Long toLong(Object value) { + return toLong(value, null); + } + + /** + * 转换为double
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Double toDouble(Object value, Double defaultValue) { + if (value == null) { + return defaultValue; + } else if (value instanceof Double) { + return (Double) value; + } else if (value instanceof Number) { + return ((Number) value).doubleValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + try { + // 支持科学计数法 + return new BigDecimal(valueStr.trim()).doubleValue(); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * 转换为double
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Double toDouble(Object value) { + return toDouble(value, null); + } + + /** + * 转换为Float
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Float toFloat(Object value, Float defaultValue) { + if (value == null) { + return defaultValue; + } else if (value instanceof Float) { + return (Float) value; + } else if (value instanceof Number) { + return ((Number) value).floatValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + try { + return Float.parseFloat(valueStr.trim()); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * 转换为Float
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Float toFloat(Object value) { + return toFloat(value, null); + } + + /** + * 转换为boolean
+ * String支持的值为:true、false、yes、ok、no,1,0 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Boolean toBool(Object value, Boolean defaultValue) { + if (value == null) { + return defaultValue; + } else if (value instanceof Boolean) { + return (Boolean) value; + } + String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + valueStr = valueStr.trim().toLowerCase(); + switch (valueStr) { + case "true": + return true; + case "false": + return false; + case "yes": + return true; + case "ok": + return true; + case "no": + return false; + case "1": + return true; + case "0": + return false; + default: + return defaultValue; + } + } + + /** + * 转换为boolean
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Boolean toBool(Object value) { + return toBool(value, null); + } + + /** + * 转换为Enum对象
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * + * @param clazz Enum的Class + * @param value 值 + * @param defaultValue 默认值 + * @return Enum + */ + public static > E toEnum(Class clazz, Object value, E defaultValue) { + if (value == null) { + return defaultValue; + } else if (clazz.isAssignableFrom(value.getClass())) { + @SuppressWarnings("unchecked") + E myE = (E) value; + return myE; + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + try { + return Enum.valueOf(clazz, valueStr); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * 转换为Enum对象
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * + * @param clazz Enum的Class + * @param value 值 + * @return Enum + */ + public static > E toEnum(Class clazz, Object value) { + return toEnum(clazz, value, null); + } + + /** + * 转换为BigInteger
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static BigInteger toBigInteger(Object value, BigInteger defaultValue) { + if (value == null) { + return defaultValue; + } else if (value instanceof BigInteger) { + return (BigInteger) value; + } else if (value instanceof Long) { + return BigInteger.valueOf((Long) value); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + try { + return new BigInteger(valueStr); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * 转换为BigInteger
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static BigInteger toBigInteger(Object value) { + return toBigInteger(value, null); + } + + /** + * 转换为BigDecimal
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static BigDecimal toBigDecimal(Object value, BigDecimal defaultValue) { + if (value == null) { + return defaultValue; + } else if (value instanceof BigDecimal) { + return (BigDecimal) value; + } else if (value instanceof Long) { + return new BigDecimal((Long) value); + } else if (value instanceof Double) { + return new BigDecimal((Double) value); + } else if (value instanceof Integer) { + return new BigDecimal((Integer) value); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) { + return defaultValue; + } + try { + return new BigDecimal(valueStr); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * 转换为BigDecimal
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static BigDecimal toBigDecimal(Object value) { + return toBigDecimal(value, null); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @return 字符串 + */ + public static String utf8Str(Object obj) { + return str(obj, CharsetKit.CHARSET_UTF_8); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @param charsetName 字符集 + * @return 字符串 + */ + public static String str(Object obj, String charsetName) { + return str(obj, Charset.forName(charsetName)); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @param charset 字符集 + * @return 字符串 + */ + public static String str(Object obj, Charset charset) { + if (null == obj) { + return null; + } else if (obj instanceof String) { + return (String) obj; + } else if (obj instanceof byte[] || obj instanceof Byte[]) { + return str(obj, charset); + } else if (obj instanceof ByteBuffer) { + return str((ByteBuffer) obj, charset); + } + return obj.toString(); + } + + /** + * 将byte数组转为字符串 + * + * @param bytes byte数组 + * @param charset 字符集 + * @return 字符串 + */ + public static String str(byte[] bytes, String charset) { + return str(bytes, StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset)); + } + + /** + * 解码字节码 + * + * @param data 字符串 + * @param charset 字符集,如果此字段为空,则解码的结果取决于平台 + * @return 解码后的字符串 + */ + public static String str(byte[] data, Charset charset) { + if (data == null) { + return null; + } else if (null == charset) { + return new String(data); + } + return new String(data, charset); + } + + /** + * 将编码的byteBuffer数据转换为字符串 + * + * @param data 数据 + * @param charset 字符集,如果为空使用当前系统字符集 + * @return 字符串 + */ + public static String str(ByteBuffer data, String charset) { + if (data == null) { + return null; + } + return str(data, Charset.forName(charset)); + } + + /** + * 将编码的byteBuffer数据转换为字符串 + * + * @param data 数据 + * @param charset 字符集,如果为空使用当前系统字符集 + * @return 字符串 + */ + public static String str(ByteBuffer data, Charset charset) { + if (null == charset) { + charset = Charset.defaultCharset(); + } + return charset.decode(data).toString(); + } + + // ----------------------------------------------------------------------- 全角半角转换 + /** + * 半角转全角 + * + * @param input String. + * @return 全角字符串. + */ + public static String toSBC(String input) { + return toSBC(input, null); + } + + /** + * 半角转全角 + * + * @param input String + * @param notConvertSet 不替换的字符集合 + * @return 全角字符串. + */ + public static String toSBC(String input, Set notConvertSet) { + char c[] = input.toCharArray(); + for (int i = 0; i < c.length; i++) { + if (null != notConvertSet && notConvertSet.contains(c[i])) { + // 跳过不替换的字符 + continue; + } else if (c[i] == ' ') { + c[i] = '\u3000'; + } else if (c[i] < '\177') { + c[i] = (char) (c[i] + 65248); + + } + } + return new String(c); + } + + /** + * 全角转半角 + * + * @param input String. + * @return 半角字符串 + */ + public static String toDBC(String input) { + return toDBC(input, null); + } + + /** + * 替换全角为半角 + * + * @param text 文本 + * @param notConvertSet 不替换的字符集合 + * @return 替换后的字符 + */ + public static String toDBC(String text, Set notConvertSet) { + char c[] = text.toCharArray(); + for (int i = 0; i < c.length; i++) { + if (null != notConvertSet && notConvertSet.contains(c[i])) { + // 跳过不替换的字符 + continue; + } else if (c[i] == '\u3000') { + c[i] = ' '; + } else if (c[i] > '\uFF00' && c[i] < '\uFF5F') { + c[i] = (char) (c[i] - 65248); + } + } + return new String(c); + } + + /** + * 数字金额大写转换 先写个完整的然后将如零拾替换成零 + * + * @param n 数字 + * @return 中文大写数字 + */ + public static String digitUppercase(double n) { + String[] fraction = { "角", "分" }; + String[] digit = { "零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖" }; + String[][] unit = { { "元", "万", "亿" }, { "", "拾", "佰", "仟" } }; + + String head = n < 0 ? "负" : ""; + n = Math.abs(n); + + String s = ""; + for (int i = 0; i < fraction.length; i++) { + s += (digit[(int) (Math.floor(n * 10 * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", ""); + } + if (s.length() < 1) { + s = "整"; + } + int integerPart = (int) Math.floor(n); + + for (int i = 0; i < unit[0].length && integerPart > 0; i++) { + String p = ""; + for (int j = 0; j < unit[1].length && n > 0; j++) { + p = digit[integerPart % 10] + unit[1][j] + p; + integerPart = integerPart / 10; + } + s = p.replaceAll("(零.)*零$", "").replaceAll("^$", "零") + unit[0][i] + s; + } + return head + s.replaceAll("(零.)*零元", "元").replaceFirst("(零.)+", "").replaceAll("(零.)+", "零").replaceAll("^整$", "零元整"); + } +} diff --git a/cloud-market-server/src/main/java/com/muyu/cloud/market/util/poi/ExcelUtil.java b/cloud-market-server/src/main/java/com/muyu/cloud/market/util/poi/ExcelUtil.java new file mode 100644 index 0000000..bc2a82b --- /dev/null +++ b/cloud-market-server/src/main/java/com/muyu/cloud/market/util/poi/ExcelUtil.java @@ -0,0 +1,787 @@ +package com.muyu.cloud.market.util.poi; + +import com.muyu.cloud.market.util.core.date.DateUtils; +import com.muyu.cloud.market.util.core.reflect.ReflectUtils; +import com.muyu.cloud.market.util.core.spring.SpringUtils; +import com.muyu.cloud.market.util.core.text.Convert; +import com.muyu.cloud.market.util.poi.annotation.Excel.Type; +import com.muyu.cloud.market.util.poi.annotation.Excels; +import com.muyu.cloud.market.util.poi.exception.PoiException; +import com.muyu.cloud.market.util.poi.annotation.Excel; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.log4j.Log4j2; +import org.apache.commons.lang3.StringUtils; +import org.apache.poi.hssf.usermodel.HSSFDateUtil; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.ss.util.CellRangeAddressList; +import org.apache.poi.util.IOUtils; +import org.apache.poi.xssf.streaming.SXSSFWorkbook; +import org.apache.poi.xssf.usermodel.XSSFDataValidation; + + +import java.io.*; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.math.BigDecimal; +import java.text.DecimalFormat; +import java.util.*; + +/** + * Excel相关处理 + * + * @author + */ +@Log4j2 +public class ExcelUtil +{ + + /** + * Excel sheet最大行数,默认65536 + */ + public static final int sheetSize = 65536; + + /** + * 工作表名称 + */ + private String sheetName; + + /** + * 导出类型(EXPORT:导出数据;IMPORT:导入模板) + */ + private Type type; + + /** + * 工作薄对象 + */ + private Workbook wb; + + /** + * 工作表对象 + */ + private Sheet sheet; + + /** + * 样式列表 + */ + private Map styles; + + /** + * 导入导出数据列表 + */ + private List list; + + /** + * 注解列表 + */ + private List fields; + + /** + * 实体对象 + */ + public Class clazz; + + public ExcelUtil(Class clazz) { + this.clazz = clazz; + } + + public void init(List list, String sheetName, Type type) { + if (list == null) { + list = new ArrayList(); + } + this.list = list; + this.sheetName = sheetName; + this.type = type; + createExcelField(); + createWorkbook(); + } + + /** + * 对excel表单默认第一个索引名转换成list + * + * @param is 输入流 + * @return 转换后集合 + */ + public List importExcel(InputStream is) throws Exception { + return importExcel(StringUtils.EMPTY, is); + } + + /** + * 对excel表单指定表格索引名转换成list + * + * @param sheetName 表格索引名 + * @param is 输入流 + * @return 转换后集合 + */ + public List importExcel(String sheetName, InputStream is) throws Exception { + this.type = Type.IMPORT; + this.wb = WorkbookFactory.create(is); + List list = new ArrayList(); + Sheet sheet = null; + if (StringUtils.isNotEmpty(sheetName)) { + // 如果指定sheet名,则取指定sheet中的内容. + sheet = wb.getSheet(sheetName); + } else { + // 如果传入的sheet名不存在则默认指向第1个sheet. + sheet = wb.getSheetAt(0); + } + + if (sheet == null) { + throw new IOException("文件sheet不存在"); + } + + int rows = sheet.getPhysicalNumberOfRows(); + + if (rows > 0) { + // 定义一个map用于存放excel列的序号和field. + Map cellMap = new HashMap(); + // 获取表头 + Row heard = sheet.getRow(0); + for (int i = 0; i < heard.getPhysicalNumberOfCells(); i++) { + Cell cell = heard.getCell(i); + if (cell != null) { + String value = this.getCellValue(heard, i).toString(); + cellMap.put(value, i); + } + else { + cellMap.put(null, i); + } + } + // 有数据时才处理 得到类的所有field. + Field[] allFields = clazz.getDeclaredFields(); + // 定义一个map用于存放列的序号和field. + Map fieldsMap = new HashMap(); + for (int col = 0; col < allFields.length; col++) { + Field field = allFields[col]; + Excel attr = field.getAnnotation(Excel.class); + if (attr != null && (attr.type() == Type.ALL || attr.type() == type)) { + // 设置类的私有字段属性可访问. + field.setAccessible(true); + Integer column = cellMap.get(attr.name()); + fieldsMap.put(column, field); + } + } + for (int i = 1; i < rows; i++) { + // 从第2行开始取数据,默认第一行是表头. + Row row = sheet.getRow(i); + T entity = null; + for (Map.Entry entry : fieldsMap.entrySet()) { + if (entry.getKey() == null){ + continue; + } + Object val = this.getCellValue(row, entry.getKey()); + + // 如果不存在实例则新建. + entity = (entity == null ? clazz.newInstance() : entity); + // 从map中得到对应列的field. + Field field = fieldsMap.get(entry.getKey()); + // 取得类型,并根据对象类型设置值. + Class fieldType = field.getType(); + if (String.class == fieldType) { + String s = Convert.toStr(val); + if (StringUtils.endsWith(s, ".0")) { + val = StringUtils.substringBefore(s, ".0"); + } else { + String dateFormat = field.getAnnotation(Excel.class).dateFormat(); + if (StringUtils.isNotEmpty(dateFormat)) { + val = DateUtils.parseDateToStr(dateFormat, (Date) val); + } else { + val = Convert.toStr(val); + } + } + } else if ((Integer.TYPE == fieldType) || (Integer.class == fieldType)) { + val = Convert.toInt(val); + } else if ((Long.TYPE == fieldType) || (Long.class == fieldType)) { + val = Convert.toLong(val); + } else if ((Double.TYPE == fieldType) || (Double.class == fieldType)) { + val = Convert.toDouble(val); + } else if ((Float.TYPE == fieldType) || (Float.class == fieldType)) { + val = Convert.toFloat(val); + } else if (BigDecimal.class == fieldType) { + val = Convert.toBigDecimal(val); + } else if (Date.class == fieldType) { + if (val instanceof String) { + val = DateUtils.parseDate(val); + } else if (val instanceof Double) { + val = DateUtil.getJavaDate((Double) val); + } + } + if (fieldType != null) { + Excel attr = field.getAnnotation(Excel.class); + String propertyName = field.getName(); + if (StringUtils.isNotEmpty(attr.targetAttr())) { + propertyName = field.getName() + "." + attr.targetAttr(); + } else if (StringUtils.isNotEmpty(attr.readConverterExp())) { + val = reverseByExp(String.valueOf(val), attr.readConverterExp()); + } + ReflectUtils.invokeSetter(entity, propertyName, val); + } + } + list.add(entity); + } + } + return list; + } + + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param response 返回数据 + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @return 结果 + * @throws IOException + */ + public void exportExcel(HttpServletResponse response, List list, String sheetName){ + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("utf-8"); + this.init(list, sheetName, Type.EXPORT); + writeSheet(); + exportExcel(response); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @return 结果 + */ + public void exportExcel(HttpServletResponse response){ + try{ + wb.write(response.getOutputStream()); + } catch (Exception e){ + log.error("导出Excel异常{}", e.getMessage()); + } finally { + IOUtils.closeQuietly(wb); + } + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @return 结果 + */ + public String exportExcel(List list, String sheetName) { + this.init(list, sheetName, Type.EXPORT); + return exportExcel(); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param sheetName 工作表的名称 + * @return 结果 + */ + public String importTemplateExcel(String sheetName) { + this.init(null, sheetName, Type.IMPORT); + return exportExcel(); + } + + /** + * 写入sheet数据 + */ + public void writeSheet(){ + // 取出一共有多少个sheet. + double sheetNo = Math.ceil(list.size() / sheetSize); + for (int index = 0; index <= sheetNo; index++) { + createSheet(sheetNo, index); + + // 产生一行 + Row row = sheet.createRow(0); + int column = 0; + // 写入各个字段的列头名称 + for (Object[] os : fields) { + Excel excel = (Excel) os[1]; + this.createCell(excel, row, column++); + } + if (Type.EXPORT.equals(type)) { + fillExcelData(index, row); + } + } + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @return 结果 + */ + public String exportExcel() { + OutputStream out = null; + try { + writeSheet(); + String filename = encodingFilename(sheetName); + out = new FileOutputStream(getAbsoluteFile(filename)); + wb.write(out); + return filename; + } catch (Exception e) { + log.error("导出Excel异常{}", e.getMessage()); + throw new PoiException("导出Excel失败,请联系管理员!"); + } finally { + try { + if (wb != null) { + wb.close(); + } + if (out != null) { + out.close(); + } + } catch (IOException e1) { + e1.printStackTrace(); + } + + } + } + + /** + * 填充excel数据 + * + * @param index 序号 + * @param row 单元格行 + */ + public void fillExcelData(int index, Row row) { + int startNo = index * sheetSize; + int endNo = Math.min(startNo + sheetSize, list.size()); + for (int i = startNo; i < endNo; i++) { + row = sheet.createRow(i + 1 - startNo); + // 得到导出对象. + T vo = (T) list.get(i); + int column = 0; + for (Object[] os : fields) { + Field field = (Field) os[0]; + Excel excel = (Excel) os[1]; + // 设置实体类私有属性可访问 + field.setAccessible(true); + this.addCell(excel, row, vo, field, column++); + } + } + } + + /** + * 创建表格样式 + * + * @param wb 工作薄对象 + * @return 样式列表 + */ + private Map createStyles(Workbook wb) { + // 写入各条记录,每条记录对应excel表中的一行 + Map styles = new HashMap(); + CellStyle style = wb.createCellStyle(); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setBorderRight(BorderStyle.THIN); + style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderLeft(BorderStyle.THIN); + style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderTop(BorderStyle.THIN); + style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderBottom(BorderStyle.THIN); + style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + Font dataFont = wb.createFont(); + dataFont.setFontName("Arial"); + dataFont.setFontHeightInPoints((short) 10); + style.setFont(dataFont); + styles.put("data", style); + + style = wb.createCellStyle(); + style.cloneStyleFrom(styles.get("data")); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setFillForegroundColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setFillPattern(FillPatternType.SOLID_FOREGROUND); + Font headerFont = wb.createFont(); + headerFont.setFontName("Arial"); + headerFont.setFontHeightInPoints((short) 10); + headerFont.setBold(true); + headerFont.setColor(IndexedColors.WHITE.getIndex()); + style.setFont(headerFont); + styles.put("header", style); + + return styles; + } + + /** + * 创建单元格 + */ + public Cell createCell(Excel attr, Row row, int column) { + // 创建列 + Cell cell = row.createCell(column); + // 写入列信息 + cell.setCellValue(attr.name()); + setDataValidation(attr, row, column); + cell.setCellStyle(styles.get("header")); + return cell; + } + + /** + * 设置单元格信息 + * + * @param value 单元格值 + * @param attr 注解相关 + * @param cell 单元格信息 + */ + public void setCellVo(Object value, Excel attr, Cell cell) { + if (Excel.ColumnType.STRING == attr.cellType()) { + cell.setCellType(CellType.NUMERIC); + cell.setCellValue(value == null ? attr.defaultValue() : value + attr.suffix()); + } else if (Excel.ColumnType.NUMERIC == attr.cellType()) { + cell.setCellType(CellType.NUMERIC); + cell.setCellValue(Integer.parseInt(value + "")); + } + } + + /** + * 创建表格样式 + */ + public void setDataValidation(Excel attr, Row row, int column) { + if (attr.name().indexOf("注:") >= 0) { + sheet.setColumnWidth(column, 6000); + } else { + // 设置列宽 + sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256)); + row.setHeight((short) (attr.height() * 20)); + } + // 如果设置了提示信息则鼠标放上去提示. + if (StringUtils.isNotEmpty(attr.prompt())) { + // 这里默认设了2-101列提示. + setXSSFPrompt(sheet, "", attr.prompt(), 1, 100, column, column); + } + // 如果设置了combo属性则本列只能选择不能输入 + if (attr.combo().length > 0) { + // 这里默认设了2-101列只能选择不能输入. + setXSSFValidation(sheet, attr.combo(), 1, 100, column, column); + } + } + + /** + * 添加单元格 + */ + public Cell addCell(Excel attr, Row row, T vo, Field field, int column) { + Cell cell = null; + try { + // 设置行高 + row.setHeight((short) (attr.height() * 20)); + // 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列. + if (attr.isExport()) { + // 创建cell + cell = row.createCell(column); + cell.setCellStyle(styles.get("data")); + + // 用于读取对象中的属性 + Object value = getTargetValue(vo, field, attr); + //时间类型格式化 + String dateFormat = attr.dateFormat(); + //读取表达式 + String readConverterExp = attr.readConverterExp(); + //分隔符 + String splitStr = attr.splitStr(); + //反射对象 + Class invokeClass = attr.invokeClass(); + //反射方法名称 + String invokeMethod = attr.invokeMethod(); + if (StringUtils.isNotEmpty(dateFormat) && value != null) { + cell.setCellValue(DateUtils.parseDateToStr(dateFormat, (Date) value)); + } else if (StringUtils.isNotEmpty(readConverterExp) && value != null) { + // 判断是否有分隔符 + if (StringUtils.isNotEmpty(splitStr)){ + StringBuilder cellValue = new StringBuilder(); + String valueStr = String.valueOf(value); + String[] valueStrSplit = valueStr.split(splitStr); + int valueStrSplitLength = valueStrSplit.length; + for (int i = 0; ; i++) { + cellValue.append(convertByExp(valueStrSplit[i], readConverterExp)); + if (i == valueStrSplitLength-1){ + break; + } + cellValue.append(","); + } + cell.setCellValue(cellValue.toString()); + } else { + cell.setCellValue(convertByExp(String.valueOf(value), readConverterExp)); + } + + } + // 判断是否需要取值 + else if((!invokeClass.getName().equals(String.class.getName())) && StringUtils.isNotEmpty(invokeMethod)){ + Object bean = SpringUtils.getBean(invokeClass); + Method thisMethod = null; + Method[] methods = invokeClass.getMethods(); + for (Method method : methods) { + if (method.getName().equals(invokeMethod)){ + thisMethod = method; + continue; + } + } + cell.setCellValue(thisMethod.invoke(bean,value).toString()); + } else { + // 设置列类型 + setCellVo(value, attr, cell); + } + } + } catch (Exception e) { + log.error("导出Excel失败{}", e); + } + return cell; + } + + /** + * 设置 POI XSSFSheet 单元格提示 + * + * @param sheet 表单 + * @param promptTitle 提示标题 + * @param promptContent 提示内容 + * @param firstRow 开始行 + * @param endRow 结束行 + * @param firstCol 开始列 + * @param endCol 结束列 + */ + public void setXSSFPrompt(Sheet sheet, String promptTitle, String promptContent, int firstRow, int endRow, + int firstCol, int endCol) { + DataValidationHelper helper = sheet.getDataValidationHelper(); + DataValidationConstraint constraint = helper.createCustomConstraint("DD1"); + CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol); + DataValidation dataValidation = helper.createValidation(constraint, regions); + dataValidation.createPromptBox(promptTitle, promptContent); + dataValidation.setShowPromptBox(true); + sheet.addValidationData(dataValidation); + } + + /** + * 设置某些列的值只能输入预制的数据,显示下拉框. + * + * @param sheet 要设置的sheet. + * @param textlist 下拉框显示的内容 + * @param firstRow 开始行 + * @param endRow 结束行 + * @param firstCol 开始列 + * @param endCol 结束列 + * @return 设置好的sheet. + */ + public void setXSSFValidation(Sheet sheet, String[] textlist, int firstRow, int endRow, int firstCol, int endCol) { + DataValidationHelper helper = sheet.getDataValidationHelper(); + // 加载下拉列表内容 + DataValidationConstraint constraint = helper.createExplicitListConstraint(textlist); + // 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列 + CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol); + // 数据有效性对象 + DataValidation dataValidation = helper.createValidation(constraint, regions); + // 处理Excel兼容性问题 + if (dataValidation instanceof XSSFDataValidation) { + dataValidation.setSuppressDropDownArrow(true); + dataValidation.setShowErrorBox(true); + } else { + dataValidation.setSuppressDropDownArrow(false); + } + + sheet.addValidationData(dataValidation); + } + + /** + * 解析导出值 0=男,1=女,2=未知 + * + * @param propertyValue 参数值 + * @param converterExp 翻译注解 + * @return 解析后值 + * @throws Exception + */ + public static String convertByExp(String propertyValue, String converterExp) throws Exception { + try { + String[] convertSource = converterExp.split(","); + for (String item : convertSource) { + String[] itemArray = item.split("="); + if (itemArray[0].equals(propertyValue)) { + return itemArray[1]; + } + } + } catch (Exception e) { + throw e; + } + return propertyValue; + } + + /** + * 反向解析值 男=0,女=1,未知=2 + * + * @param propertyValue 参数值 + * @param converterExp 翻译注解 + * @return 解析后值 + * @throws Exception + */ + public static String reverseByExp(String propertyValue, String converterExp) throws Exception { + try { + String[] convertSource = converterExp.split(","); + for (String item : convertSource) { + String[] itemArray = item.split("="); + if (itemArray[1].equals(propertyValue)) { + return itemArray[0]; + } + } + } catch (Exception e) { + throw e; + } + return propertyValue; + } + + /** + * 编码文件名 + */ + public String encodingFilename(String filename) { + filename = UUID.randomUUID().toString() + "_" + filename + ".xlsx"; + return filename; + } + + /** + * 获取下载路径 + * + * @param filename 文件名称 + */ + public String getAbsoluteFile(String filename) { + String downloadPath = System.getProperty("user.dir") + File.separator + filename; + File desc = new File(downloadPath); + if (!desc.getParentFile().exists()) { + desc.getParentFile().mkdirs(); + } + return downloadPath; + } + + /** + * 获取bean中的属性值 + * + * @param vo 实体对象 + * @param field 字段 + * @param excel 注解 + * @return 最终的属性值 + * @throws Exception + */ + private Object getTargetValue(T vo, Field field, Excel excel) throws Exception { + Object o = field.get(vo); + if (StringUtils.isNotEmpty(excel.targetAttr())) { + String target = excel.targetAttr(); + if (target.indexOf(".") > -1) { + String[] targets = target.split("[.]"); + for (String name : targets) { + o = getValue(o, name); + } + } else { + o = getValue(o, target); + } + } + return o; + } + + /** + * 以类的属性的get方法方法形式获取值 + * + * @param o + * @param name + * @return value + * @throws Exception + */ + private Object getValue(Object o, String name) throws Exception { + if (StringUtils.isNotEmpty(name)) { + Class clazz = o.getClass(); + String methodName = "get" + name.substring(0, 1).toUpperCase() + name.substring(1); + Method method = clazz.getMethod(methodName); + o = method.invoke(o); + } + return o; + } + + /** + * 得到所有定义字段 + */ + private void createExcelField() { + this.fields = new ArrayList<>(); + List tempFields = new ArrayList<>(); + tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields())); + tempFields.addAll(Arrays.asList(clazz.getDeclaredFields())); + for (Field field : tempFields) { + // 单注解 + if (field.isAnnotationPresent(Excel.class)) { + putToField(field, field.getAnnotation(Excel.class)); + } + + // 多注解 + if (field.isAnnotationPresent(Excels.class)) { + Excels attrs = field.getAnnotation(Excels.class); + Excel[] excels = attrs.value(); + for (Excel excel : excels) { + putToField(field, excel); + } + } + } + } + + /** + * 放到字段集合中 + */ + private void putToField(Field field, Excel attr) { + if (attr != null && (attr.type() == Type.ALL || attr.type() == type)) { + this.fields.add(new Object[] { field, attr }); + } + } + + /** + * 创建一个工作簿 + */ + public void createWorkbook() { + this.wb = new SXSSFWorkbook(500); + } + + /** + * 创建工作表 + * + * @param sheetNo sheet数量 + * @param index 序号 + */ + public void createSheet(double sheetNo, int index) { + this.sheet = wb.createSheet(); + this.styles = createStyles(wb); + // 设置工作表的名称. + if (sheetNo == 0) { + wb.setSheetName(index, sheetName); + } else { + wb.setSheetName(index, sheetName + index); + } + } + + /** + * 获取单元格值 + * + * @param row 获取的行 + * @param column 获取单元格列号 + * @return 单元格值 + */ + public Object getCellValue(Row row, int column) { + if (row == null) { + return row; + } + Object val = ""; + try { + Cell cell = row.getCell(column); + if (cell != null) { + if (cell.getCellTypeEnum() == CellType.NUMERIC || cell.getCellTypeEnum() == CellType.FORMULA) { + val = cell.getNumericCellValue(); + if (HSSFDateUtil.isCellDateFormatted(cell)) { + val = DateUtil.getJavaDate((Double) val); // POI Excel 日期格式转换 + } else { + if ((Double) val % 1 > 0) { + val = new DecimalFormat("0.00").format(val); + } else { + val = new DecimalFormat("0").format(val); + } + } + } else if (cell.getCellTypeEnum() == CellType.STRING) { + val = cell.getStringCellValue(); + } else if (cell.getCellTypeEnum() == CellType.BOOLEAN) { + val = cell.getBooleanCellValue(); + } else if (cell.getCellTypeEnum() == CellType.ERROR) { + val = cell.getErrorCellValue(); + } + + } + } catch (Exception e) { + return val; + } + return val; + } +} diff --git a/cloud-market-server/src/main/java/com/muyu/cloud/market/util/poi/annotation/Excel.java b/cloud-market-server/src/main/java/com/muyu/cloud/market/util/poi/annotation/Excel.java new file mode 100644 index 0000000..d897db9 --- /dev/null +++ b/cloud-market-server/src/main/java/com/muyu/cloud/market/util/poi/annotation/Excel.java @@ -0,0 +1,122 @@ +package com.muyu.cloud.market.util.poi.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 自定义导出Excel数据注解 + * + * @author + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface Excel +{ + /** + * 导出到Excel中的名字. + */ + public String name() default ""; + + /** + * 日期格式, 如: yyyy-MM-dd + */ + public String dateFormat() default ""; + + /** + * 读取内容转表达式 (如: 0=男,1=女,2=未知) + */ + public String readConverterExp() default ""; + + /** + * 分隔符 + */ + public String splitStr() default ""; + + /** + * 可执行的反射方法值为1个 + */ + public String invokeMethod() default ""; + + /** + * 可执行的反射类 从SpringIoc容器当中取值 + */ + public Class invokeClass() default String.class; + + /** + * 导出类型(0数字 1字符串) + */ + public ColumnType cellType() default ColumnType.STRING; + + /** + * 导出时在excel中每个列的高度 单位为字符 + */ + public double height() default 14; + + /** + * 导出时在excel中每个列的宽 单位为字符 + */ + public double width() default 16; + + /** + * 文字后缀,如% 90 变成90% + */ + public String suffix() default ""; + + /** + * 当值为空时,字段的默认值 + */ + public String defaultValue() default ""; + + /** + * 提示信息 + */ + public String prompt() default ""; + + /** + * 设置只能选择不能输入的列内容. + */ + public String[] combo() default {}; + + /** + * 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写. + */ + public boolean isExport() default true; + + /** + * 另一个类中的属性名称,支持多级获取,以小数点隔开 + */ + public String targetAttr() default ""; + + /** + * 字段类型(0:导出导入;1:仅导出;2:仅导入) + */ + Type type() default Type.ALL; + + public enum Type { + ALL(0), EXPORT(1), IMPORT(2); + private final int value; + + Type(int value) { + this.value = value; + } + + public int value() { + return this.value; + } + } + + public enum ColumnType { + NUMERIC(0), STRING(1); + private final int value; + + ColumnType(int value) { + this.value = value; + } + + public int value() { + return this.value; + } + } +} diff --git a/cloud-market-server/src/main/java/com/muyu/cloud/market/util/poi/annotation/Excels.java b/cloud-market-server/src/main/java/com/muyu/cloud/market/util/poi/annotation/Excels.java new file mode 100644 index 0000000..cffac2d --- /dev/null +++ b/cloud-market-server/src/main/java/com/muyu/cloud/market/util/poi/annotation/Excels.java @@ -0,0 +1,18 @@ +package com.muyu.cloud.market.util.poi.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Excel注解集 + * + * @author + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Excels +{ + Excel[] value(); +} diff --git a/cloud-market-server/src/main/java/com/muyu/cloud/market/util/poi/exception/PoiException.java b/cloud-market-server/src/main/java/com/muyu/cloud/market/util/poi/exception/PoiException.java new file mode 100644 index 0000000..bc235b1 --- /dev/null +++ b/cloud-market-server/src/main/java/com/muyu/cloud/market/util/poi/exception/PoiException.java @@ -0,0 +1,27 @@ +package com.muyu.cloud.market.util.poi.exception; + +/** + * 业务异常 + * + * @author + */ +public class PoiException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + protected final String message; + + public PoiException(String message) { + this.message = message; + } + + public PoiException(String message, Throwable e) { + super(message, e); + this.message = message; + } + + @Override + public String getMessage() { + return message; + } +} diff --git a/cloud-market-server/src/main/java/com/muyu/cloud/market/util/poi3/CustomCellStyle.java b/cloud-market-server/src/main/java/com/muyu/cloud/market/util/poi3/CustomCellStyle.java new file mode 100644 index 0000000..42b215a --- /dev/null +++ b/cloud-market-server/src/main/java/com/muyu/cloud/market/util/poi3/CustomCellStyle.java @@ -0,0 +1,77 @@ +package com.muyu.cloud.market.util.poi3; + +import com.alibaba.excel.metadata.CellData; +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.write.handler.CellWriteHandler; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; +import com.alibaba.excel.write.metadata.holder.WriteTableHolder; +import com.alibaba.excel.write.metadata.style.WriteCellStyle; +import com.alibaba.excel.write.metadata.style.WriteFont; +import com.alibaba.excel.write.style.HorizontalCellStyleStrategy; +import org.apache.poi.ss.usermodel.*; + +import java.util.List; + +public class CustomCellStyle implements CellWriteHandler { + + + @Override + public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) { + + } + + @Override + public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { + + + } + + @Override + public void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { + + } + + @Override + public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { + //获取工作簿 + Workbook workbook = writeSheetHolder.getSheet().getWorkbook(); + + //获取单元格样式 + CellStyle cellStyle = workbook.createCellStyle(); + + //判断非首行 + if(!isHead && cell.getColumnIndex()==4){ + + double numericCellValue = cell.getNumericCellValue(); + if(numericCellValue>2.0){ + //填充背景色 + cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); + cellStyle.setFillForegroundColor(IndexedColors.RED.index); + + //设置字体 + Font font = workbook.createFont(); + font.setBold(true); + font.setColor(IndexedColors.BLACK.getIndex()); + cellStyle.setFont(font); + } + + + }else{} + + //设置边框 + cellStyle.setBorderTop(BorderStyle.THIN); + cellStyle.setTopBorderColor(IndexedColors.BLACK.getIndex()); + + cellStyle.setBorderRight(BorderStyle.THIN); + cellStyle.setRightBorderColor(IndexedColors.BLACK.getIndex()); + + cellStyle.setBorderBottom(BorderStyle.THIN); + cellStyle.setBottomBorderColor(IndexedColors.BLACK.getIndex()); + + cellStyle.setBorderLeft(BorderStyle.THIN); + cellStyle.setLeftBorderColor(IndexedColors.BLACK.getIndex()); + + cell.setCellStyle(cellStyle); + + } +} diff --git a/cloud-market-server/src/main/java/com/muyu/cloud/market/util/poi3/EasyExcelUtil.java b/cloud-market-server/src/main/java/com/muyu/cloud/market/util/poi3/EasyExcelUtil.java new file mode 100644 index 0000000..6545720 --- /dev/null +++ b/cloud-market-server/src/main/java/com/muyu/cloud/market/util/poi3/EasyExcelUtil.java @@ -0,0 +1,160 @@ +package com.muyu.cloud.market.util.poi3; + +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.ExcelWriter; +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.support.ExcelTypeEnum; +import com.alibaba.excel.util.CollectionUtils; +import com.alibaba.excel.write.merge.AbstractMergeStrategy; +import com.alibaba.excel.write.metadata.WriteSheet; +import com.alibaba.excel.write.metadata.style.WriteCellStyle; +import com.alibaba.excel.write.metadata.style.WriteFont; +import com.alibaba.excel.write.style.HorizontalCellStyleStrategy; +import com.muyu.cloud.market.domin.Orders; +import jakarta.servlet.http.HttpServletResponse; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.apache.poi.ss.formula.functions.T; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.FillPatternType; +import org.apache.poi.ss.usermodel.IndexedColors; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.util.CellRangeAddress; +import org.springframework.stereotype.Component; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +// 自定义合并策略 该类继承了AbstractMergeStrategy抽象合并策略,需要重写merge()方法 +@Component +@Data +@NoArgsConstructor +public class EasyExcelUtil extends AbstractMergeStrategy { + + + + /** + * 分组,每几行合并一次 + */ + private List exportFieldGroupCountList; + + /** + * 目标合并列index + */ + private Integer targetColumnIndex; + + // 需要开始合并单元格的首行index + private Integer rowIndex; + + // exportDataList为待合并目标列的值 + public EasyExcelUtil(List exportDataList, Integer targetColumnIndex) { + this.exportFieldGroupCountList = getGroupCountList(exportDataList); + this.targetColumnIndex = targetColumnIndex; + } + + + @Override + protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) { + + if (null == rowIndex) { + rowIndex = cell.getRowIndex(); + } + // 仅从首行以及目标列的单元格开始合并,忽略其他 + if (cell.getRowIndex() == rowIndex && cell.getColumnIndex() == targetColumnIndex) { + mergeGroupColumn(sheet); + } + } + + private void mergeGroupColumn(Sheet sheet) { + int rowCount = rowIndex; + for (Integer count : exportFieldGroupCountList) { + if(count == 1) { + rowCount += count; + continue ; + } + // 合并单元格 + CellRangeAddress cellRangeAddress = new CellRangeAddress(rowCount, rowCount + count - 1, targetColumnIndex, targetColumnIndex); + sheet.addMergedRegionUnsafe(cellRangeAddress); + rowCount += count; + } + } + + // 该方法将目标列根据值是否相同连续可合并,存储可合并的行数 + private List getGroupCountList(List exportDataList){ + if (CollectionUtils.isEmpty(exportDataList)) { + return new ArrayList<>(); + } + + List groupCountList = new ArrayList<>(); + int count = 1; + + for (int i = 1; i < exportDataList.size(); i++) { + if (exportDataList.get(i).equals(exportDataList.get(i - 1))) { + count++; + } else { + groupCountList.add(count); + count = 1; + } + } + // 处理完最后一条后 + groupCountList.add(count); + return groupCountList; + } + + + // 修改WriteSheet的代码如下 + /** + * @param response HttpServletResponse对象,用于输出Excel文件。 + * @param fileName 导出的Excel文件名 + * @param list 待导出的数据列表。 + * @param mergeIndex 合并的列数,Excel列是从0开始的 + * @param firstTitle 第一行标题. + * @param secondTile 第二行标题. + */ + public void writeExcel(HttpServletResponse response, String fileName, List list, Integer mergeIndex, String firstTitle, String [] secondTile) { + // 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman + response.setContentType("application/vnd.ms-excel"); + response.setCharacterEncoding("utf-8"); + // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系 + try { + String name = URLEncoder.encode(fileName, "UTF-8"); + + response.setHeader("Content-disposition", "attachment;filename=" + + name + ".xlsx"); + + List> heads = new ArrayList<>(); + for (int i = 0; i < secondTile.length; i++) { + heads.add(Arrays.asList(firstTitle,secondTile[i])); + } + + +// EasyExcel.write(response.getOutputStream(), Orders.class) +// .useDefaultStyle(false) +// .sheet("模板").head(Orders.class) +// .head(heads) +// //合并单元格new EasyExcelUtil(sexlist, mergeIndex) +// // 参数1:合并单元格的属性集合,参数2:合并的下标 +// .registerWriteHandler(new EasyExcelUtil(list.stream().map(Orders::getOrdersSpecification).collect(Collectors.toList()), mergeIndex)) +// .registerWriteHandler(new EasyExcelUtil(list.stream().map(Orders -> String.valueOf(Emp.getEmpName())).collect(Collectors.toList()), mergeIndex)) +// //设置样式使用 +// .registerWriteHandler(new CustomCellStyle()) +// .doWrite(list); +// + + + + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + + } + + + +} diff --git a/cloud-market-server/src/main/resources/bootstrap.yml b/cloud-market-server/src/main/resources/bootstrap.yml index 815ec85..e73c91a 100644 --- a/cloud-market-server/src/main/resources/bootstrap.yml +++ b/cloud-market-server/src/main/resources/bootstrap.yml @@ -59,3 +59,4 @@ spring: alipayPublicKey: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7rRdHzrcyEdSOLPm3ITg+t/C3tGp9+wl739nOgFExrSD1j1C95tTRGUCqNUQtatiYmlAfwOKzkNhr/XEH/Vvpq5Ei8C4ZBWJqOMw7fQfBE6m5erO/FOuS4pyZCtQZyP9C5nhtYOupnGtq0fmgQaGKk657vszYSMEtpuQaZ5/odUYxRbYP3IHbcK+++PQBarfOdnhNcnR/Ry7TVOC3+da0dogmClPEconZQMwCJ81CJ7Hj4TKKyLTe9bz+J2tkUeNr9Mc72g4ljjftX7k0RBpBpi4HSHJ3SmPLhktiuDOeOGOef88pBgBtFV03/+aFpUEHx/imiHhBMBeNT3kqFV03QIDAQAB notifyUrl: http://rnkig5.natappfree.cc/alipay/notify + returnUrl: http://localhost/market/apitest/index diff --git a/cloud-market-server/src/main/resources/mapper/market/OrderShowMapper.xml b/cloud-market-server/src/main/resources/mapper/market/OrderShowMapper.xml index 8b45afb..14e7c17 100644 --- a/cloud-market-server/src/main/resources/mapper/market/OrderShowMapper.xml +++ b/cloud-market-server/src/main/resources/mapper/market/OrderShowMapper.xml @@ -93,5 +93,14 @@ WHERE `orders`.`orders_id` = #{ordersId} + + +