businessHandler(BusinessException businessException) {
+ log.warn("业务异常:message={}", businessException.getMessage());
+ if (businessException.getCode() != null) {
+ return ResponseResult.fail(businessException.getCode(), businessException.getMessage());
+ }
+ return ResponseResult.fail(businessException.getMessage());
+ }
+
+}
diff --git a/common-base-starter/src/main/java/com/mosty/common/base/exception/ServiceException.java b/common-base-starter/src/main/java/com/mosty/common/base/exception/ServiceException.java
new file mode 100644
index 0000000..f81734c
--- /dev/null
+++ b/common-base-starter/src/main/java/com/mosty/common/base/exception/ServiceException.java
@@ -0,0 +1,50 @@
+package com.mosty.common.base.exception;
+
+/**
+ * 业务异常
+ *
+ * @author ruoyi
+ */
+public final class ServiceException extends RuntimeException {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 错误提示
+ */
+ private String message;
+
+ /**
+ * 错误明细,内部调试错误
+ *
+ */
+ private String detailMessage;
+
+ /**
+ * 空构造方法,避免反序列化问题
+ */
+ public ServiceException() {
+ }
+
+ public ServiceException(String message) {
+ this.message = message;
+ }
+
+ public String getDetailMessage() {
+ return detailMessage;
+ }
+
+ public ServiceException setDetailMessage(String detailMessage) {
+ this.detailMessage = detailMessage;
+ return this;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public ServiceException setMessage(String message) {
+ this.message = message;
+ return this;
+ }
+}
\ No newline at end of file
diff --git a/common-base-starter/src/main/java/com/mosty/common/base/exception/UtilException.java b/common-base-starter/src/main/java/com/mosty/common/base/exception/UtilException.java
new file mode 100644
index 0000000..c80ca24
--- /dev/null
+++ b/common-base-starter/src/main/java/com/mosty/common/base/exception/UtilException.java
@@ -0,0 +1,26 @@
+package com.mosty.common.base.exception;
+
+/**
+ * 工具类异常
+ *
+ * @author ruoyi
+ */
+public class UtilException extends RuntimeException
+{
+ private static final long serialVersionUID = 8247610319171014183L;
+
+ public UtilException(Throwable e)
+ {
+ super(e.getMessage(), e);
+ }
+
+ public UtilException(String message)
+ {
+ super(message);
+ }
+
+ public UtilException(String message, Throwable throwable)
+ {
+ super(message, throwable);
+ }
+}
diff --git a/common-base-starter/src/main/java/com/mosty/common/base/lock/optimistic/AopTargetUtils.java b/common-base-starter/src/main/java/com/mosty/common/base/lock/optimistic/AopTargetUtils.java
new file mode 100644
index 0000000..ff6405a
--- /dev/null
+++ b/common-base-starter/src/main/java/com/mosty/common/base/lock/optimistic/AopTargetUtils.java
@@ -0,0 +1,48 @@
+package com.mosty.common.base.lock.optimistic;
+
+import org.springframework.aop.framework.AdvisedSupport;
+import org.springframework.aop.framework.AopProxy;
+import org.springframework.aop.support.AopUtils;
+
+import java.lang.reflect.Field;
+
+public class AopTargetUtils {
+
+ /**
+ * 递归获取代理的 目标对象
+ * @param proxy 代理对象
+ * @return 目标对象
+ * @throws Exception
+ */
+ public static Object getTarget(Object proxy) throws Exception {
+ if (!AopUtils.isAopProxy(proxy)) {
+ return proxy;
+ }
+ if (AopUtils.isJdkDynamicProxy(proxy)) {
+ proxy = getJdkDynamicProxyTargetObject(proxy);
+ } else {
+ proxy = getCglibProxyTargetObject(proxy);
+ }
+ return getTarget(proxy);
+ }
+
+ private static Object getCglibProxyTargetObject(Object proxy) throws Exception {
+ Field h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0");
+ h.setAccessible(true);
+ Object dynamicAdvisedInterceptor = h.get(proxy);
+ Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised");
+ advised.setAccessible(true);
+ Object target = ((AdvisedSupport) advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget();
+ return target;
+ }
+
+ private static Object getJdkDynamicProxyTargetObject(Object proxy) throws Exception {
+ Field h = proxy.getClass().getSuperclass().getDeclaredField("h");
+ h.setAccessible(true);
+ AopProxy aopProxy = (AopProxy) h.get(proxy);
+ Field advised = aopProxy.getClass().getDeclaredField("advised");
+ advised.setAccessible(true);
+ Object target = ((AdvisedSupport) advised.get(aopProxy)).getTargetSource().getTarget();
+ return target;
+ }
+ }
diff --git a/common-base-starter/src/main/java/com/mosty/common/base/lock/optimistic/EnableOptimisticLock.java b/common-base-starter/src/main/java/com/mosty/common/base/lock/optimistic/EnableOptimisticLock.java
new file mode 100644
index 0000000..728b059
--- /dev/null
+++ b/common-base-starter/src/main/java/com/mosty/common/base/lock/optimistic/EnableOptimisticLock.java
@@ -0,0 +1,32 @@
+package com.mosty.common.base.lock.optimistic;
+
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.context.support.AbstractApplicationContext;
+
+import java.lang.annotation.*;
+
+
+/**
+ * 允许启动乐观锁自定义插件
+ *
+ * @author kevin
+ * @date 2020/8/11 0:03
+ * @since 1.0.0
+ * @see AbstractApplicationContext#refresh()
+ * @see AbstractApplicationContext {@code invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory) }
+ * @see org.springframework.context.annotation
+ * {@code ConfigurationClassParser#processImports(ConfigurationClass, ConfigurationClassParser.SourceClass, Collection, boolean)}
+ *
+ * @see OptimisticLockException 需要关注跑出的检查异常,后面根据业务给予处理
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Configuration
+//@Import(OptimisticLockConfiguration.class)
+@Deprecated
+public @interface EnableOptimisticLock {
+
+}
diff --git a/common-base-starter/src/main/java/com/mosty/common/base/lock/optimistic/OptimisticLock.java b/common-base-starter/src/main/java/com/mosty/common/base/lock/optimistic/OptimisticLock.java
new file mode 100644
index 0000000..35a6f1a
--- /dev/null
+++ b/common-base-starter/src/main/java/com/mosty/common/base/lock/optimistic/OptimisticLock.java
@@ -0,0 +1,300 @@
+//package com.mosty.common.base.lock.optimistic;
+//
+//import lombok.extern.slf4j.Slf4j;
+//import org.apache.ibatis.binding.MapperMethod;
+//import org.apache.ibatis.executor.Executor;
+//import org.apache.ibatis.executor.parameter.ParameterHandler;
+//import org.apache.ibatis.executor.statement.StatementHandler;
+//import org.apache.ibatis.mapping.BoundSql;
+//import org.apache.ibatis.mapping.MappedStatement;
+//import org.apache.ibatis.mapping.ParameterMapping;
+//import org.apache.ibatis.mapping.SqlCommandType;
+//import org.apache.ibatis.plugin.*;
+//import org.apache.ibatis.reflection.MetaObject;
+//import org.apache.ibatis.reflection.SystemMetaObject;
+//import org.apache.ibatis.session.Configuration;
+//import org.apache.ibatis.session.SqlSessionFactory;
+//import org.apache.ibatis.type.JdbcType;
+//import org.apache.ibatis.type.TypeException;
+//import org.apache.ibatis.type.TypeHandler;
+//
+//import java.lang.reflect.Proxy;
+//import java.sql.Connection;
+//import java.sql.PreparedStatement;
+//import java.sql.SQLException;
+//import java.util.Objects;
+//import java.util.Properties;
+//import java.util.regex.Pattern;
+//
+//
+///**
+// * Mybatis 自定义插件,{@link OptimisticLock} 就是一个{@link Plugin}
+// *
+// * 使用:1、如果某些 Mapper 不想使用乐观锁,可以在Mapper.xml 中不添加 乐观锁字段即可,如:
+// *
+// * UPDATE CONF_ORDER_APPROVE
+// *
+// * ORDER_TYPE_CODE = #{orderTypeCode},
+// * RECEIVE_ORG_CODE = #{receiveOrgCode},
+// *
+// * OBJECT_VERSION_NUMBER = #{objectVersionNumber},
+// *
+// * WHERE ID = #{id}
+// *
+// * 2、业务端自行根据update的条数进行处理(给用户相应的提示等)或不处理(可能update本身就可能不会修改任何数据)
+// * 3、乐观锁的字段类型必须为{@link Long} ,可以对乐观锁的数据库的java pojo字段进行定义,【注意: OptimisticLock只能放在SqlInterceptor和ResultInterceptor之后】如下:
+// *
+// *
+// *
+// *
+// *
+// *
+// *
+// *
+// *
+// *
+// *
+// * 每一个拦截器可以拦截下面的四种,当前只拦截了三种
+// * Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
+// * ParameterHandler (getParameterObject, setParameters)
+// * ResultSetHandler (handleResultSets, handleOutputParameters)
+// * StatementHandler (prepare, parameterize, batch, update, query)
+// *
+// * @author kevin
+// * @date 2020/8/10 22:14
+// * @since 1.0.0
+// * @see org.apache.ibatis.session.Configuration#interceptorChain
+// * @see Plugin#signatureMap 其中:Map, Set> Class 为所有需要实现乐观锁的Mapper,Set集合为需要乐观锁的方法集合【原则上是所有写的方法】
+// * @see Invocation#proceed() 直接执行被代理对象的方法
+// * @see MappedStatement 对应每个mapper中的