apiRest = message(ex.getCode(), ex.getMsg(), null);
+ return apiRest;
+ }
+
+ /**
+ * 响应返回结果
+ *
+ * @param rows 影响行数
+ * @return 操作结果
+ */
+ protected AjaxResult toAjax(int rows) {
+ return rows > 0 ? AjaxResult.success() : AjaxResult.error();
+ }
+}
diff --git a/src/main/java/org/example/core/api/dto/BaseDTO.java b/src/main/java/org/example/core/api/dto/BaseDTO.java
new file mode 100644
index 0000000..099a8b9
--- /dev/null
+++ b/src/main/java/org/example/core/api/dto/BaseDTO.java
@@ -0,0 +1,14 @@
+package org.example.core.api.dto;
+
+import java.io.Serializable;
+import lombok.Data;
+
+/**
+ * 请求和响应的基础类,用于处理序列化
+ * @author dav
+ * @date 2019/3/16 15:56
+ */
+@Data
+public class BaseDTO implements Serializable {
+
+}
diff --git a/src/main/java/org/example/core/api/dto/BaseIdReqDTO.java b/src/main/java/org/example/core/api/dto/BaseIdReqDTO.java
new file mode 100644
index 0000000..b996953
--- /dev/null
+++ b/src/main/java/org/example/core/api/dto/BaseIdReqDTO.java
@@ -0,0 +1,27 @@
+package org.example.core.api.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ *
+ * 主键通用请求类,用于根据ID查询
+ *
+ *
+ * @author 聪明笨狗
+ * @since 2019-04-20 12:15
+ */
+@Data
+@ApiModel(value="主键通用请求类", description="主键通用请求类")
+public class BaseIdReqDTO extends BaseDTO {
+
+
+ @ApiModelProperty(value = "主键ID", required=true)
+ private String id;
+
+ @JsonIgnore
+ private String userId;
+
+}
diff --git a/src/main/java/org/example/core/api/dto/BaseIdRespDTO.java b/src/main/java/org/example/core/api/dto/BaseIdRespDTO.java
new file mode 100644
index 0000000..04cfbc6
--- /dev/null
+++ b/src/main/java/org/example/core/api/dto/BaseIdRespDTO.java
@@ -0,0 +1,25 @@
+package org.example.core.api.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ *
+ * 主键通用响应类,用于添加后返回内容
+ *
+ *
+ * @author 聪明笨狗
+ * @since 2019-04-20 12:15
+ */
+@Data
+@ApiModel(value="主键通用响应类", description="主键通用响应类")
+@AllArgsConstructor
+@NoArgsConstructor
+public class BaseIdRespDTO extends BaseDTO {
+
+ @ApiModelProperty(value = "主键ID", required=true)
+ private String id;
+}
diff --git a/src/main/java/org/example/core/api/dto/BaseIdsReqDTO.java b/src/main/java/org/example/core/api/dto/BaseIdsReqDTO.java
new file mode 100644
index 0000000..9db5f97
--- /dev/null
+++ b/src/main/java/org/example/core/api/dto/BaseIdsReqDTO.java
@@ -0,0 +1,24 @@
+package org.example.core.api.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import java.util.List;
+import lombok.Data;
+
+/**
+ * 通用ID列表类操作,用于批量删除、修改状态等
+ * @author bool
+ * @date 2019-08-01 19:07
+ */
+@Data
+@ApiModel(value="删除参数", description="删除参数")
+public class BaseIdsReqDTO extends BaseDTO {
+
+
+ @JsonIgnore
+ private String userId;
+
+ @ApiModelProperty(value = "要删除的ID列表", required = true)
+ private List ids;
+}
diff --git a/src/main/java/org/example/core/api/dto/BaseStateReqDTO.java b/src/main/java/org/example/core/api/dto/BaseStateReqDTO.java
new file mode 100644
index 0000000..3038ea0
--- /dev/null
+++ b/src/main/java/org/example/core/api/dto/BaseStateReqDTO.java
@@ -0,0 +1,30 @@
+package org.example.core.api.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import java.util.List;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ *
+ * 通用状态请求类,用于修改状态什么的
+ *
+ *
+ * @author 聪明笨狗
+ * @since 2019-04-20 12:15
+ */
+@Data
+@ApiModel(value="通用状态请求类", description="通用状态请求类")
+@AllArgsConstructor
+@NoArgsConstructor
+public class BaseStateReqDTO extends BaseDTO {
+
+
+ @ApiModelProperty(value = "要修改对象的ID列表", required=true)
+ private List ids;
+
+ @ApiModelProperty(value = "通用状态,0为正常,1为禁用", required=true)
+ private Integer state;
+}
diff --git a/src/main/java/org/example/core/api/dto/PagingReqDTO.java b/src/main/java/org/example/core/api/dto/PagingReqDTO.java
new file mode 100644
index 0000000..7de79b9
--- /dev/null
+++ b/src/main/java/org/example/core/api/dto/PagingReqDTO.java
@@ -0,0 +1,47 @@
+package org.example.core.api.dto;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 分页查询类
+ * @param
+ * @author bool
+ */
+@ApiModel(value="分页参数", description="分页参数")
+@Data
+public class PagingReqDTO {
+
+
+ @ApiModelProperty(value = "当前页码", required = true, example = "1")
+ private Integer current;
+
+ @ApiModelProperty(value = "每页数量", required = true, example = "10")
+ private Integer size;
+
+ @ApiModelProperty(value = "查询参数")
+ private T params;
+
+ @ApiModelProperty(value = "排序字符")
+ private String orderBy;
+
+ @JsonIgnore
+ @ApiModelProperty(value = "当前用户的ID")
+ private String userId;
+
+ /**
+ * 转换成MyBatis的简单分页对象
+ * @return
+ */
+ public Page toPage(){
+ Page page = new Page();
+ page.setCurrent(this.current);
+ page.setSize(this.size);
+ return page;
+ }
+
+
+}
diff --git a/src/main/java/org/example/core/api/dto/PagingRespDTO.java b/src/main/java/org/example/core/api/dto/PagingRespDTO.java
new file mode 100644
index 0000000..df9a994
--- /dev/null
+++ b/src/main/java/org/example/core/api/dto/PagingRespDTO.java
@@ -0,0 +1,30 @@
+package org.example.core.api.dto;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+
+/**
+ * 分页响应类
+ * @author bool
+ * @date 2019-07-20 15:17
+ * @param
+ */
+public class PagingRespDTO extends Page {
+
+ /**
+ * 获取页面总数量
+ * @return
+ */
+ @Override
+ public long getPages() {
+ if (this.getSize() == 0L) {
+ return 0L;
+ } else {
+ long pages = this.getTotal() / this.getSize();
+ if (this.getTotal() % this.getSize() != 0L) {
+ ++pages;
+ }
+ return pages;
+ }
+ }
+
+}
diff --git a/src/main/java/org/example/core/api/utils/JsonConverter.java b/src/main/java/org/example/core/api/utils/JsonConverter.java
new file mode 100644
index 0000000..cead204
--- /dev/null
+++ b/src/main/java/org/example/core/api/utils/JsonConverter.java
@@ -0,0 +1,47 @@
+package org.example.core.api.utils;
+
+import com.alibaba.fastjson.serializer.SerializerFeature;
+import com.alibaba.fastjson.support.config.FastJsonConfig;
+import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.List;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.HttpMessageConverter;
+
+/**
+ * JSON数据转换器,用于转换返回消息的格式
+ * @author dav
+ * @date 2018/9/11 19:30
+ */
+public class JsonConverter {
+
+ /**
+ * FastJson消息转换器
+ *
+ * @return
+ */
+ public static HttpMessageConverter fastConverter() {
+ // 定义一个convert转换消息的对象
+ FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
+ // 添加FastJson的配置信息
+ FastJsonConfig fastJsonConfig = new FastJsonConfig();
+ // 默认转换器
+ fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat,
+ SerializerFeature.WriteNullNumberAsZero,
+ SerializerFeature.MapSortField,
+ SerializerFeature.WriteNullStringAsEmpty,
+ SerializerFeature.DisableCircularReferenceDetect,
+ SerializerFeature.WriteDateUseDateFormat,
+ SerializerFeature.WriteNullListAsEmpty);
+ fastJsonConfig.setCharset(Charset.forName("UTF-8"));
+ // 处理中文乱码问题
+ List fastMediaTypes = new ArrayList<>();
+ fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
+ fastConverter.setSupportedMediaTypes(fastMediaTypes);
+ // 在convert中添加配置信息
+ fastConverter.setFastJsonConfig(fastJsonConfig);
+
+ return fastConverter;
+ }
+}
diff --git a/src/main/java/org/example/core/domain/AjaxResult.java b/src/main/java/org/example/core/domain/AjaxResult.java
new file mode 100644
index 0000000..867df3d
--- /dev/null
+++ b/src/main/java/org/example/core/domain/AjaxResult.java
@@ -0,0 +1,165 @@
+package org.example.core.domain;
+
+import java.util.HashMap;
+
+/**
+ * 操作消息提醒
+ *
+ * @author xinyilu
+ */
+public class AjaxResult extends HashMap {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 状态码
+ */
+ public static final String CODE_TAG = "code";
+
+ /**
+ * 返回内容
+ */
+ public static final String MSG_TAG = "msg";
+
+ /**
+ * 数据对象
+ */
+ public static final String DATA_TAG = "data";
+
+ /**
+ * 成功默认消息
+ */
+ private static final Integer CODE_SUCCESS = 0;
+ private static final String MSG_SUCCESS = "操作成功!";
+
+ /**
+ * 失败默认消息
+ */
+ private static final Integer CODE_FAILURE = 1;
+ private static final String MSG_FAILURE = "请求失败!";
+
+ /**
+ * 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。
+ */
+ public AjaxResult() {
+ }
+
+ /**
+ * 初始化一个新创建的 AjaxResult 对象
+ *
+ * @param code 状态码
+ * @param msg 返回内容
+ */
+ public AjaxResult(int code, String msg) {
+ super.put(CODE_TAG, code);
+ super.put(MSG_TAG, msg);
+ }
+
+ /**
+ * 初始化一个新创建的 AjaxResult 对象
+ *
+ * @param code 状态码
+ * @param msg 返回内容
+ * @param data 数据对象
+ */
+ public AjaxResult(int code, String msg, Object data) {
+ super.put(CODE_TAG, code);
+ super.put(MSG_TAG, msg);
+ if (!(data == null)) {
+ super.put(DATA_TAG, data);
+ }
+ }
+
+ /**
+ * 返回成功消息
+ *
+ * @return 成功消息
+ */
+ public static AjaxResult success() {
+ return AjaxResult.success("操作成功");
+ }
+
+ /**
+ * 返回成功数据
+ *
+ * @return 成功消息
+ */
+ public static AjaxResult success(Object data) {
+ return AjaxResult.success("操作成功", data);
+ }
+
+ /**
+ * 返回成功消息
+ *
+ * @param msg 返回内容
+ * @return 成功消息
+ */
+ public static AjaxResult success(String msg) {
+ return AjaxResult.success(msg, null);
+ }
+
+ /**
+ * 返回成功消息
+ *
+ * @param msg 返回内容
+ * @param data 数据对象
+ * @return 成功消息
+ */
+ public static AjaxResult success(String msg, Object data) {
+ return new AjaxResult(CODE_SUCCESS, msg, data);
+ }
+
+ /**
+ * 返回错误消息
+ *
+ * @return
+ */
+ public static AjaxResult error() {
+ return AjaxResult.error("操作失败");
+ }
+
+ /**
+ * 返回错误消息
+ *
+ * @param msg 返回内容
+ * @return 警告消息
+ */
+ public static AjaxResult error(String msg) {
+ return AjaxResult.error(msg, null);
+ }
+
+ /**
+ * 返回错误消息
+ *
+ * @param msg 返回内容
+ * @param data 数据对象
+ * @return 警告消息
+ */
+ public static AjaxResult error(String msg, Object data) {
+ return new AjaxResult(CODE_FAILURE, msg, data);
+ }
+
+ /**
+ * 返回错误消息
+ *
+ * @param code 状态码
+ * @param msg 返回内容
+ * @return 警告消息
+ */
+ public static AjaxResult error(int code, String msg) {
+ return new AjaxResult(code, msg, null);
+ }
+
+ /**
+ * 方便链式调用
+ *
+ * @param key 键
+ * @param value 值
+ * @return 数据对象
+ */
+ @Override
+ public AjaxResult put(String key, Object value) {
+ super.put(key, value);
+ return this;
+ }
+
+}
diff --git a/src/main/java/org/example/core/enums/CommonState.java b/src/main/java/org/example/core/enums/CommonState.java
new file mode 100644
index 0000000..8d177c1
--- /dev/null
+++ b/src/main/java/org/example/core/enums/CommonState.java
@@ -0,0 +1,19 @@
+package org.example.core.enums;
+
+/**
+ * 通用的状态枚举信息
+ *
+ * @author bool
+ * @date 2019-09-17 17:57
+ */
+public interface CommonState {
+
+ /**
+ * 普通状态,正常的
+ */
+ Integer NORMAL = 0;
+ /**
+ * 非正常状态,禁用,下架等
+ */
+ Integer ABNORMAL = 1;
+}
diff --git a/src/main/java/org/example/core/enums/ConfirmRefundStatusEnum.java b/src/main/java/org/example/core/enums/ConfirmRefundStatusEnum.java
new file mode 100644
index 0000000..9273eb0
--- /dev/null
+++ b/src/main/java/org/example/core/enums/ConfirmRefundStatusEnum.java
@@ -0,0 +1,29 @@
+package org.example.core.enums;
+
+import lombok.Getter;
+
+/**
+ * @Description 确认退款状态枚举
+ * @Author 纪寒
+ * @Date 2022-10-26 11:15:21
+ * @Version 1.0
+ */
+@Getter
+public enum ConfirmRefundStatusEnum {
+
+ /**
+ * 未确认
+ */
+ NOT_CONFIRM("NOT_CONFIRM"),
+
+ /**
+ * 已确认
+ */
+ CONFIRMED("CONFIRMED"),
+ ;
+ final private String info;
+
+ ConfirmRefundStatusEnum(String info) {
+ this.info = info;
+ }
+}
diff --git a/src/main/java/org/example/core/enums/GooodsOrderStatusEnum.java b/src/main/java/org/example/core/enums/GooodsOrderStatusEnum.java
new file mode 100644
index 0000000..2dd2df7
--- /dev/null
+++ b/src/main/java/org/example/core/enums/GooodsOrderStatusEnum.java
@@ -0,0 +1,69 @@
+package org.example.core.enums;
+
+import lombok.Getter;
+
+/**
+ * @Description 商品订单状态枚举
+ * @Author 纪寒
+ * @Date 2022-10-19 09:15:38
+ * @Version 1.0
+ */
+@Getter
+public enum GooodsOrderStatusEnum {
+
+ /**
+ * 待付款
+ */
+ WAIT_PAY("WAIT_PAY"),
+
+ /**
+ * 已付款
+ */
+ PAY("PAY"),
+
+ /**
+ * 已取消
+ */
+ CANCEL("CANCEL"),
+
+ /**
+ * 待收货
+ */
+ WAIT_RECEIVED_GOODS("WAIT_RECEIVED_GOODS"),
+
+ /**
+ * 已收货
+ */
+ RECEIVED_GOODS("RECEIVED_GOODS"),
+
+ /**
+ * 退款中
+ */
+ WAIT_REFUND("WAIT_REFUND"),
+
+ /**
+ * 已退款
+ */
+ REFUNDED("REFUNDED"),
+
+ /**
+ * 待退货
+ */
+ WAIT_RETURNED_GOODS("WAIT_RETURNED_GOODS"),
+
+ /**
+ * 已退货
+ */
+ RETURNED_GOODS("RETURNED_GOODS"),
+
+ /**
+ * 已评价
+ */
+ EVALUATED("EVALUATED"),
+ ;
+ final private String info;
+
+ GooodsOrderStatusEnum(String info) {
+ this.info = info;
+ }
+}
diff --git a/src/main/java/org/example/core/enums/OpenType.java b/src/main/java/org/example/core/enums/OpenType.java
new file mode 100644
index 0000000..504661c
--- /dev/null
+++ b/src/main/java/org/example/core/enums/OpenType.java
@@ -0,0 +1,18 @@
+package org.example.core.enums;
+
+/**
+ * 开放方式
+ * @author bool
+ */
+public interface OpenType {
+
+ /**
+ * 完全开放
+ */
+ Integer OPEN = 1;
+
+ /**
+ * 部门开放
+ */
+ Integer DEPT_OPEN = 2;
+}
diff --git a/src/main/java/org/example/core/enums/PayTypeEnum.java b/src/main/java/org/example/core/enums/PayTypeEnum.java
new file mode 100644
index 0000000..71e629e
--- /dev/null
+++ b/src/main/java/org/example/core/enums/PayTypeEnum.java
@@ -0,0 +1,30 @@
+package org.example.core.enums;
+
+import lombok.Getter;
+
+/**
+ * @Description 支付了类型枚举
+ * @Author 纪寒
+ * @Date 2022-10-18 15:16:02
+ * @Version 1.0
+ */
+@Getter
+public enum PayTypeEnum {
+
+ /**
+ * 微信
+ */
+ WECHAT_PAY("WECHAT_PAY"),
+
+ /**
+ * 支付宝
+ */
+ ALI_PAY("ALI_PAY"),
+ ;
+
+ final private String info;
+
+ PayTypeEnum(String info) {
+ this.info = info;
+ }
+}
diff --git a/src/main/java/org/example/core/enums/RefundStatusEnum.java b/src/main/java/org/example/core/enums/RefundStatusEnum.java
new file mode 100644
index 0000000..c75e93d
--- /dev/null
+++ b/src/main/java/org/example/core/enums/RefundStatusEnum.java
@@ -0,0 +1,41 @@
+package org.example.core.enums;
+
+import lombok.Getter;
+
+/**
+ * @Description 微信退款通知枚举
+ * @Author 纪寒
+ * @Date 2022-10-25 13:12:03
+ * @Version 1.0
+ */
+@Getter
+public enum RefundStatusEnum {
+ /**
+ * 退款成功
+ */
+ SUCCESS("SUCCESS"),
+
+ /**
+ * 退款关闭
+ */
+ CLOSED("CLOSED"),
+
+ /**
+ * 退款处理中
+ */
+ PROCESSING("PROCESSING"),
+
+ /**
+ * 退款异常,退款到银行发现用户的卡作废或者冻结了,
+ * 导致原路退款银行卡失败,可前往【商户平台—>交易中心】,手动处理此笔退款
+ */
+ ABNORMAL("ABNORMAL"),
+
+ ;
+
+ final private String info;
+
+ RefundStatusEnum(String info) {
+ this.info = info;
+ }
+}
diff --git a/src/main/java/org/example/core/enums/WeChatTradeStateEnum.java b/src/main/java/org/example/core/enums/WeChatTradeStateEnum.java
new file mode 100644
index 0000000..b68ec6e
--- /dev/null
+++ b/src/main/java/org/example/core/enums/WeChatTradeStateEnum.java
@@ -0,0 +1,52 @@
+package org.example.core.enums;
+
+import lombok.Getter;
+
+/**
+ * 微信支付回调通知状态
+ */
+@Getter
+public enum WeChatTradeStateEnum {
+
+ /**
+ * 支付成功
+ */
+ SUCCESS("SUCCESS"),
+
+ /**
+ * 未支付
+ */
+ NOTPAY("NOTPAY"),
+
+ /**
+ * 已关闭
+ */
+ CLOSED("CLOSED"),
+
+ /**
+ * 转入退款
+ */
+ REFUND("REFUND"),
+
+ /**
+ * 已撤销(付款码支付)
+ */
+ REVOKED("REVOKED"),
+
+ /**
+ * 用户支付中(付款码支付)
+ */
+ USERPAYING("USERPAYING"),
+
+ /**
+ * 支付失败(其他原因,如银行返回失败)
+ */
+ PAYERROR("PAYERROR")
+ ;
+
+ final private String info;
+
+ WeChatTradeStateEnum(String info) {
+ this.info = info;
+ }
+}
diff --git a/src/main/java/org/example/core/exception/ServiceException.java b/src/main/java/org/example/core/exception/ServiceException.java
new file mode 100644
index 0000000..6f11b6f
--- /dev/null
+++ b/src/main/java/org/example/core/exception/ServiceException.java
@@ -0,0 +1,51 @@
+package org.example.core.exception;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.example.core.api.ApiError;
+import org.example.core.api.ApiRest;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class ServiceException extends RuntimeException{
+
+ /**
+ * 错误码
+ */
+ private Integer code;
+
+ /**
+ * 错误消息
+ */
+ private String msg;
+
+ /**
+ * 从结果初始化
+ * @param apiRest
+ */
+ public ServiceException(ApiRest apiRest){
+ this.code = apiRest.getCode();
+ this.msg = apiRest.getMsg();
+ }
+
+ /**
+ * 从枚举中获取参数
+ * @param apiError
+ */
+ public ServiceException(ApiError apiError){
+ this.code = apiError.getCode();
+ this.msg = apiError.msg;
+ }
+
+ /**
+ * 异常构造
+ * @param msg
+ */
+ public ServiceException(String msg){
+ this.code = 1;
+ this.msg = msg;
+ }
+
+}
diff --git a/src/main/java/org/example/core/exception/ServiceExceptionHandler.java b/src/main/java/org/example/core/exception/ServiceExceptionHandler.java
new file mode 100644
index 0000000..9a23eef
--- /dev/null
+++ b/src/main/java/org/example/core/exception/ServiceExceptionHandler.java
@@ -0,0 +1,50 @@
+package org.example.core.exception;
+
+import org.example.core.api.ApiRest;
+import org.springframework.http.HttpStatus;
+import org.springframework.ui.Model;
+import org.springframework.web.bind.WebDataBinder;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.InitBinder;
+import org.springframework.web.bind.annotation.ModelAttribute;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+/**
+ * 统一异常处理类
+ * @author bool
+ * @date 2019-06-21 19:27
+ */
+@RestControllerAdvice
+public class ServiceExceptionHandler {
+
+ /**
+ * 应用到所有@RequestMapping注解方法,在其执行之前初始化数据绑定器
+ * @param binder
+ */
+ @InitBinder
+ public void initWebBinder(WebDataBinder binder){
+
+ }
+
+ /**
+ * 把值绑定到Model中,使全局@RequestMapping可以获取到该值
+ * @param model
+ */
+ @ModelAttribute
+ public void addAttribute(Model model) {
+
+ }
+
+ /**
+ * 捕获ServiceException
+ * @param e
+ * @return
+ */
+ @ExceptionHandler({org.example.core.exception.ServiceException.class})
+ @ResponseStatus(HttpStatus.OK)
+ public ApiRest serviceExceptionHandler(ServiceException e) {
+ return new ApiRest(e);
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/example/core/utils/BeanMapper.java b/src/main/java/org/example/core/utils/BeanMapper.java
new file mode 100644
index 0000000..79ab6b9
--- /dev/null
+++ b/src/main/java/org/example/core/utils/BeanMapper.java
@@ -0,0 +1,58 @@
+package org.example.core.utils;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import org.dozer.DozerBeanMapper;
+
+
+/**
+ * 简单封装Dozer, 实现深度转换Bean<->Bean的Mapper.实现:
+ *
+ * 1. 持有Mapper的单例.
+ * 2. 返回值类型转换.
+ * 3. 批量转换Collection中的所有对象.
+ * 4. 区分创建新的B对象与将对象A值复制到已存在的B对象两种函数.
+ *
+ */
+public class BeanMapper {
+
+ /**
+ * 持有Dozer单例, 避免重复创建DozerMapper消耗资源.
+ */
+ private static DozerBeanMapper dozerBeanMapper = new DozerBeanMapper();
+
+ /**
+ * 基于Dozer转换对象的类型.
+ */
+ public static T map(Object source, Class destinationClass) {
+ return dozerBeanMapper.map(source, destinationClass);
+ }
+
+ /**
+ * 基于Dozer转换Collection中对象的类型.
+ */
+ public static List mapList(Iterable> sourceList, Class destinationClass) {
+ List destinationList = new ArrayList();
+ for (Object sourceObject : sourceList) {
+ T destinationObject = dozerBeanMapper.map(sourceObject, destinationClass);
+ destinationList.add(destinationObject);
+ }
+ return destinationList;
+ }
+
+ /**
+ * 基于Dozer将对象A的值拷贝到对象B中.
+ */
+ public static void copy(Object source, Object destinationObject) {
+ if(source!=null) {
+ dozerBeanMapper.map(source, destinationObject);
+ }
+ }
+
+ public static List mapList(Collection source, Function super S, ? extends T> mapper) {
+ return source.stream().map(mapper).collect(Collectors.toList());
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/example/core/utils/CronUtils.java b/src/main/java/org/example/core/utils/CronUtils.java
new file mode 100644
index 0000000..f99b21d
--- /dev/null
+++ b/src/main/java/org/example/core/utils/CronUtils.java
@@ -0,0 +1,31 @@
+package org.example.core.utils;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * 时间转换quartz表达式
+ * @author bool
+ * @date 2020/11/29 下午3:00
+ */
+public class CronUtils {
+
+ /**
+ * 格式化数据
+ */
+ private static final String DATE_FORMAT = "ss mm HH dd MM ? yyyy";
+
+ /**
+ * 准确的时间点到表达式
+ * @param date
+ * @return
+ */
+ public static String dateToCron(final Date date){
+ SimpleDateFormat fmt = new SimpleDateFormat(DATE_FORMAT);
+ String formatTimeStr = "";
+ if (date != null) {
+ formatTimeStr = fmt.format(date);
+ }
+ return formatTimeStr;
+ }
+}
diff --git a/src/main/java/org/example/core/utils/DateUtils.java b/src/main/java/org/example/core/utils/DateUtils.java
new file mode 100644
index 0000000..6439dab
--- /dev/null
+++ b/src/main/java/org/example/core/utils/DateUtils.java
@@ -0,0 +1,103 @@
+package org.example.core.utils;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+
+/**
+ * 日期处理工具类
+ * ClassName: DateUtils
+ * date: 2018年12月13日 下午6:34:02
+ *
+ * @author Bool
+ * @version
+ */
+public class DateUtils {
+
+ /**
+ *
+ * calcExpDays:计算某个日期与当前日期相差的天数,如果计算的日期大于现在时间,将返回负数;否则返回正数
+ * @author Bool
+ * @param userCreateTime
+ * @return
+ * @since JDK 1.6
+ */
+ public static int calcExpDays(Date userCreateTime){
+
+ Calendar start = Calendar.getInstance();
+ start.setTime(userCreateTime);
+
+ Calendar now = Calendar.getInstance();
+ now.setTime(new Date());
+
+ long l = now.getTimeInMillis() - start.getTimeInMillis();
+ int days = new Long(l / (1000 * 60 * 60 * 24)).intValue();
+ return days;
+ }
+
+
+ /**
+ *
+ * dateNow:获取当前时间的字符串格式,根据传入的格式化来展示.
+ * @author Bool
+ * @param format 日期格式化
+ * @return
+ */
+ public static String dateNow(String format) {
+ SimpleDateFormat fmt = new SimpleDateFormat(format);
+ Calendar c = new GregorianCalendar();
+ return fmt.format(c.getTime());
+ }
+
+ /**
+ * formatDate:格式化日期,返回指定的格式
+ * @author Bool
+ * @param time
+ * @param format
+ * @return
+ */
+ public static String formatDate(Date time, String format) {
+ SimpleDateFormat fmt = new SimpleDateFormat(format);
+ return fmt.format(time.getTime());
+ }
+
+
+
+ /**
+ * parseDate:将字符串转换成日期,使用:yyyy-MM-dd HH:mm:ss 来格式化
+ * @author Bool
+ * @param date
+ * @return
+ */
+ public static Date parseDate(String date) {
+ return parseDate(date, "yyyy-MM-dd HH:mm:ss");
+ }
+
+
+ /**
+ *
+ * parseDate:将字符串转换成日期,使用指定格式化来格式化
+ * @author Bool
+ * @param date
+ * @param pattern
+ * @return
+ */
+ public static Date parseDate(String date, String pattern) {
+
+ if (pattern==null) {
+ pattern = "yyyy-MM-dd HH:mm:ss";
+ }
+
+ SimpleDateFormat fmt = new SimpleDateFormat(pattern);
+
+ try {
+
+ return fmt.parse(date);
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ return null;
+
+ }
+}
diff --git a/src/main/java/org/example/core/utils/IpUtils.java b/src/main/java/org/example/core/utils/IpUtils.java
new file mode 100644
index 0000000..ce346e3
--- /dev/null
+++ b/src/main/java/org/example/core/utils/IpUtils.java
@@ -0,0 +1,65 @@
+package org.example.core.utils;
+
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * IP获取工具类,用户获取网络请求过来的真实IP
+ * ClassName: IpUtils
+ * date: 2018年2月13日 下午7:27:52
+ *
+ * @author Bool
+ * @version
+ */
+public class IpUtils {
+
+
+ /**
+ *
+ * getClientIp:通过请求获取客户端的真实IP地址
+ * @author Bool
+ * @param request
+ * @return
+ */
+ public static String extractClientIp(HttpServletRequest request) {
+
+ String ip = null;
+
+ //X-Forwarded-For:Squid 服务代理
+ String ipAddresses = request.getHeader("X-Forwarded-For");
+
+ if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
+ //Proxy-Client-IP:apache 服务代理
+ ipAddresses = request.getHeader("Proxy-Client-IP");
+ }
+
+ if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
+ //WL-Proxy-Client-IP:weblogic 服务代理
+ ipAddresses = request.getHeader("WL-Proxy-Client-IP");
+ }
+
+ if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
+ //HTTP_CLIENT_IP:有些代理服务器
+ ipAddresses = request.getHeader("HTTP_CLIENT_IP");
+ }
+
+ if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
+ //X-Real-IP:nginx服务代理
+ ipAddresses = request.getHeader("X-Real-IP");
+ }
+
+ //有些网络通过多层代理,那么获取到的ip就会有多个,一般都是通过逗号(,)分割开来,并且第一个ip为客户端的真实IP
+ if (ipAddresses != null && ipAddresses.length() != 0) {
+ ip = ipAddresses.split(",")[0];
+ }
+
+ //还是不能获取到,最后再通过request.getRemoteAddr();获取
+ if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
+ ip = request.getRemoteAddr();
+ }
+
+ return ip;
+ }
+
+
+}
diff --git a/src/main/java/org/example/core/utils/Reflections.java b/src/main/java/org/example/core/utils/Reflections.java
new file mode 100644
index 0000000..d05d74c
--- /dev/null
+++ b/src/main/java/org/example/core/utils/Reflections.java
@@ -0,0 +1,323 @@
+/**
+ * Copyright (c) 2005-2012 springside.org.cn
+ */
+package org.example.core.utils;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import lombok.extern.log4j.Log4j2;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.Validate;
+import org.springframework.util.Assert;
+
+/**
+ * 反射工具类.
+ * 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数.
+ * @author calvin
+ * @version 2016-01-15
+ */
+@Log4j2
+public class Reflections {
+
+ private static final String SETTER_PREFIX = "set";
+
+ private static final String GETTER_PREFIX = "get";
+
+ private static final String CGLIB_CLASS_SEPARATOR = "$$";
+
+
+ /**
+ * 获取类的所有属性,包括父类
+ *
+ * @param object
+ * @return
+ */
+ public static Field[] getAllFields(Object object) {
+ Class> clazz = object.getClass();
+ List fieldList = new ArrayList<>();
+ while (clazz != null) {
+ fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
+ clazz = clazz.getSuperclass();
+ }
+ Field[] fields = new Field[fieldList.size()];
+ fieldList.toArray(fields);
+ return fields;
+ }
+
+
+ /**
+ * 调用Getter方法.
+ * 支持多级,如:对象名.对象名.方法
+ */
+ public static Object 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 object;
+ }
+
+ /**
+ * 调用Setter方法, 仅匹配方法名。
+ * 支持多级,如:对象名.对象名.方法
+ */
+ public static void invokeSetter(Object obj, String propertyName, Object value) {
+ Object object = obj;
+ String[] names = StringUtils.split(propertyName, ".");
+ for (int i=0; i[] parameterTypes,
+ final Object[] args) {
+ Method method = getAccessibleMethod(obj, methodName, parameterTypes);
+ if (method == null) {
+ throw new IllegalArgumentException("Could not find method [" + methodName + "] on target [" + obj + "]");
+ }
+
+ try {
+ return method.invoke(obj, args);
+ } catch (Exception e) {
+ throw convertReflectionExceptionToUnchecked(e);
+ }
+ }
+
+ /**
+ * 直接调用对象方法, 无视private/protected修饰符,
+ * 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用.
+ * 只匹配函数名,如果有多个同名函数调用第一个。
+ */
+ public static Object invokeMethodByName(final Object obj, final String methodName, final Object[] args) {
+ Method method = getAccessibleMethodByName(obj, methodName);
+ if (method == null) {
+ throw new IllegalArgumentException("Could not find method [" + methodName + "] on target [" + obj + "]");
+ }
+
+ try {
+ return method.invoke(obj, args);
+ } catch (Exception e) {
+ throw convertReflectionExceptionToUnchecked(e);
+ }
+ }
+
+ /**
+ * 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问.
+ *
+ * 如向上转型到Object仍无法找到, 返回null.
+ */
+ public static Field getAccessibleField(final Object obj, final String fieldName) {
+ Validate.notNull(obj, "object can't be 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) {//NOSONAR
+ // Field不在当前类定义,继续向上转型
+ continue;// new add
+ }
+ }
+ return null;
+ }
+
+ /**
+ * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
+ * 如向上转型到Object仍无法找到, 返回null.
+ * 匹配函数名+参数类型。
+ *
+ * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
+ */
+ public static Method getAccessibleMethod(final Object obj, final String methodName,
+ final Class>... parameterTypes) {
+ Validate.notNull(obj, "object can't be 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) {
+ // Method不在当前类定义,继续向上转型
+ continue;// new add
+ }
+ }
+ return null;
+ }
+
+ /**
+ * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
+ * 如向上转型到Object仍无法找到, 返回null.
+ * 只匹配函数名。
+ *
+ * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
+ */
+ public static Method getAccessibleMethodByName(final Object obj, final String methodName) {
+ Validate.notNull(obj, "object can't be 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)) {
+ makeAccessible(method);
+ return method;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
+ */
+ 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抱怨。
+ */
+ 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.
+ * eg.
+ * public UserDao extends HibernateDao
+ *
+ * @param clazz The class to introspect
+ * @return the first generic declaration, or Object.class if cannot be determined
+ */
+ @SuppressWarnings("unchecked")
+ public static Class getClassGenricType(final Class clazz) {
+ return getClassGenricType(clazz, 0);
+ }
+
+ /**
+ * 通过反射, 获得Class定义中声明的父类的泛型参数的类型.
+ * 如无法找到, 返回Object.class.
+ *
+ * 如public UserDao extends HibernateDao
+ *
+ * @param clazz clazz The class to introspect
+ * @param index the Index of the generic ddeclaration,start from 0.
+ * @return the index generic declaration, or Object.class if cannot be determined
+ */
+ public static Class getClassGenricType(final Class clazz, final int index) {
+
+ Type genType = clazz.getGenericSuperclass();
+
+ if (!(genType instanceof ParameterizedType)) {
+ log.warn(clazz.getSimpleName() + "'s superclass not ParameterizedType");
+ return Object.class;
+ }
+
+ Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
+
+ if (index >= params.length || index < 0) {
+ log.warn("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: "
+ + params.length);
+ return Object.class;
+ }
+ if (!(params[index] instanceof Class)) {
+ log.warn(clazz.getSimpleName() + " not set the actual class on superclass generic parameter");
+ return Object.class;
+ }
+
+ return (Class) params[index];
+ }
+
+ public static Class> getUserClass(Object instance) {
+ Assert.notNull(instance, "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.
+ */
+ public static RuntimeException convertReflectionExceptionToUnchecked(Exception e) {
+ if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException
+ || e instanceof NoSuchMethodException) {
+ return new IllegalArgumentException(e);
+ } else if (e instanceof InvocationTargetException) {
+ return new RuntimeException(((InvocationTargetException) e).getTargetException());
+ } else if (e instanceof RuntimeException) {
+ return (RuntimeException) e;
+ }
+ return new RuntimeException("Unexpected Checked Exception.", e);
+ }
+}
diff --git a/src/main/java/org/example/core/utils/SpringUtils.java b/src/main/java/org/example/core/utils/SpringUtils.java
new file mode 100644
index 0000000..b659ecb
--- /dev/null
+++ b/src/main/java/org/example/core/utils/SpringUtils.java
@@ -0,0 +1,32 @@
+package org.example.core.utils;
+
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.stereotype.Component;
+
+/**
+ * Spring获取工具
+ *
+ * @author bool
+ * @date 2019-12-09 15:55
+ */
+@Component
+public class SpringUtils implements ApplicationContextAware {
+
+ private static ApplicationContext applicationContext;
+
+ @Override
+ public void setApplicationContext(ApplicationContext context) throws BeansException {
+ applicationContext = context;
+ }
+
+ public static T getBean(Class tClass) {
+ return applicationContext.getBean(tClass);
+ }
+
+ public static T getBean(String name, Class type) {
+ return applicationContext.getBean(name, type);
+ }
+
+}
diff --git a/src/main/java/org/example/core/utils/StringUtils.java b/src/main/java/org/example/core/utils/StringUtils.java
new file mode 100644
index 0000000..20ec321
--- /dev/null
+++ b/src/main/java/org/example/core/utils/StringUtils.java
@@ -0,0 +1,39 @@
+package org.example.core.utils;
+
+import java.util.Map;
+
+/**
+ * 字符串常用工具类
+ * @author bool
+ * @date 2019-05-15 11:40
+ */
+public class StringUtils {
+
+ /**
+ * 判断是否为空字符
+ * @param str
+ * @return
+ */
+ public static boolean isBlank(String str){
+ return str==null || "".equals(str);
+ }
+
+
+ /**
+ * 将MAP转换成一个xml格式,格式为value...
+ * @param params
+ * @return
+ */
+ public static String mapToXml(Map params){
+ StringBuffer sb = new StringBuffer("");
+ for(String key:params.keySet()){
+ sb.append("<")
+ .append(key).append(">")
+ .append(params.get(key))
+ .append("").append(key).append(">");
+ }
+
+ sb.append("");
+ return sb.toString();
+ }
+}
diff --git a/src/main/java/org/example/core/utils/excel/ExportExcel.java b/src/main/java/org/example/core/utils/excel/ExportExcel.java
new file mode 100644
index 0000000..c2ddba5
--- /dev/null
+++ b/src/main/java/org/example/core/utils/excel/ExportExcel.java
@@ -0,0 +1,401 @@
+/**
+ * Copyright © 2015-2020 JeePlus All rights reserved.
+ */
+package org.example.core.utils.excel;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.net.URLEncoder;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.commons.compress.utils.Lists;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellStyle;
+import org.apache.poi.ss.usermodel.Comment;
+import org.apache.poi.ss.usermodel.DataFormat;
+import org.apache.poi.ss.usermodel.Font;
+import org.apache.poi.ss.usermodel.IndexedColors;
+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.CellRangeAddress;
+import org.apache.poi.xssf.streaming.SXSSFWorkbook;
+import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
+import org.apache.poi.xssf.usermodel.XSSFRichTextString;
+import org.example.core.utils.Reflections;
+import org.example.core.utils.excel.annotation.ExcelField;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 导出Excel文件(导出“XLSX”格式,支持大数据量导出 @see org.apache.poi.ss.SpreadsheetVersion)
+ * @author jeeplus
+ * @version 2016-04-21
+ */
+public class ExportExcel {
+
+ private static Logger log = LoggerFactory.getLogger(ExportExcel.class);
+
+ /**
+ * 工作薄对象
+ */
+ private SXSSFWorkbook wb;
+
+ /**
+ * 工作表对象
+ */
+ private Sheet sheet;
+
+ /**
+ * 样式列表
+ */
+ private Map styles;
+
+ /**
+ * 当前行号
+ */
+ private int rownum;
+
+ /**
+ * 注解列表(Object[]{ ExcelField, Field/Method })
+ */
+ List