百度外呼任务回调接口

This commit is contained in:
haown 2025-12-24 14:56:35 +08:00
parent ba034ef3f5
commit be9c299398
138 changed files with 4840 additions and 0 deletions

8
.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

19
.idea/compiler.xml Normal file
View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<annotationProcessing>
<profile default="true" name="Default" enabled="true" />
<profile name="Maven default annotation processors profile" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<module name="aiobcallback" />
</profile>
</annotationProcessing>
</component>
<component name="JavacSettings">
<option name="ADDITIONAL_OPTIONS_OVERRIDE">
<module name="aiobcallback" options="-parameters" />
</option>
</component>
</project>

7
.idea/encodings.xml Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
</component>
</project>

20
.idea/jarRepositories.xml Normal file
View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="https://repo.maven.apache.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
</component>
</project>

14
.idea/misc.xml Normal file
View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

124
.idea/uiDesigner.xml Normal file
View File

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Palette2">
<group name="Swing">
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
</item>
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
</item>
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.svg" removable="false" auto-create-binding="false" can-attach-label="true">
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
</item>
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
<initial-values>
<property name="text" value="Button" />
</initial-values>
</item>
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="RadioButton" />
</initial-values>
</item>
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="CheckBox" />
</initial-values>
</item>
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
<initial-values>
<property name="text" value="Label" />
</initial-values>
</item>
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
</item>
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
</item>
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
<preferred-size width="-1" height="20" />
</default-constraints>
</item>
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
</item>
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
</item>
</group>
</component>
</project>

6
.idea/vcs.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

236
pom.xml Normal file
View File

@ -0,0 +1,236 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>aiobcallback</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<fastjson.version>2.0.24</fastjson.version>
<oss.version>3.7.0</oss.version>
<aliyun.sdk.version>4.1.1</aliyun.sdk.version>
<swagger.version>2.9.2</swagger.version>
<dozer.version>5.5.1</dozer.version>
<apache.commons.version>3.8</apache.commons.version>
<mysql.driver.version>8.0.11</mysql.driver.version>
<mybatis-plus.version>3.4.1</mybatis-plus.version>
<lombok.version>1.18.4</lombok.version>
<thymeleaf.version>3.0.11.RELEASE</thymeleaf.version>
<alicloud.version>2.1.1.RELEASE</alicloud.version>
<poi.version>3.9</poi.version>
<log4j2.version>2.17.2</log4j2.version>
<project.name>exam-admin</project.name>
<wechatpay-apiv3.version>0.4.4</wechatpay-apiv3.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.14</version>
</parent>
<dependencies>
<!-- WEB支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!--<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>-->
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<dependency>
<groupId>net.sf.dozer</groupId>
<artifactId>dozer</artifactId>
<version>${dozer.version}</version>
<exclusions>
<exclusion>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.driver.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
<version>1.9.3</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.27.1</version> <!-- 推荐使用最新稳定版 -->
</dependency>
<!-- poi office -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>${poi.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>${poi.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
<version>${poi.version}</version>
</dependency>
<!--JWT-->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.7.0</version>
</dependency>
<!-- Shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>1.8.0</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version> <!-- 使用最新版本 -->
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.6</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
<!-- pool 对象池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.11.1</version>
</dependency>
</dependencies>
<build>
<finalName>${project.name}</finalName>
<defaultGoal>compile</defaultGoal>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.1.1.RELEASE</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
<configuration>
<skipTests>false</skipTests>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,32 @@
package org.example;
import java.net.InetAddress;
import java.net.UnknownHostException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.Environment;
@Slf4j
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class})
public class AIOBCallbackApplication {
public static void main(String[] args) throws UnknownHostException {
ConfigurableApplicationContext application = SpringApplication.run(AIOBCallbackApplication.class, args);
Environment env = application.getEnvironment();
String ip = InetAddress.getLocalHost().getHostAddress();
String port = env.getProperty("server.port");
String path = env.getProperty("server.servlet.context-path");
// 未配置默认空白
if(path == null){
path = "";
}
log.info("\n----------------------------------------------------------\n\t" +
"百度外呼回调接口启动成功\n\t" +
"----------------------------------------------------------");
}
}

View File

@ -0,0 +1,28 @@
package org.example.ability.shiro;
import java.util.Map;
import javax.servlet.Filter;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.InvalidRequestFilter;
import org.apache.shiro.web.filter.mgt.DefaultFilter;
import org.apache.shiro.web.filter.mgt.FilterChainManager;
/**
* 自定义过滤器用于处理中文URL问题
* 下载文件中包含中文会返回400错误https://youdomain.com/upload/file/云帆考试系统用户手册.pdf
* @author van
*/
public class CNFilterFactoryBean extends ShiroFilterFactoryBean {
@Override
protected FilterChainManager createFilterChainManager() {
FilterChainManager manager = super.createFilterChainManager();
// URL携带中文400servletPath中文校验bug
Map<String, Filter> filterMap = manager.getFilters();
Filter invalidRequestFilter = filterMap.get(DefaultFilter.invalidRequest.name());
if (invalidRequestFilter instanceof InvalidRequestFilter) {
((InvalidRequestFilter) invalidRequestFilter).setBlockNonAscii(false);
}
return manager;
}
}

View File

@ -0,0 +1,69 @@
package org.example.ability.shiro;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.example.ability.shiro.jwt.JwtToken;
import org.springframework.stereotype.Component;
/**
* 用户登录鉴权和获取用户授权
* @author bool
*/
@Component
@Slf4j
public class ShiroRealm extends AuthorizingRealm {
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof JwtToken;
}
/**
* 详细授权认证
* @param principals
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
log.info("++++++++++校验详细权限完成");
return info;
}
/**
* 校验用户的账号密码是否正确
* @param auth
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
String token = (String) auth.getCredentials();
if (token == null) {
throw new AuthenticationException("token为空!");
}
// 校验token有效性
return new SimpleAuthenticationInfo();
}
/**
* 清除当前用户的权限认证缓存
* @param principals
*/
@Override
public void clearCache(PrincipalCollection principals) {
super.clearCache(principals);
}
}

View File

@ -0,0 +1,49 @@
package org.example.ability.shiro.aop;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.example.aspect.utils.InjectUtils;
/**
* 鉴权登录拦截器
* @author bool
*/
@Slf4j
public class JwtFilter extends BasicHttpAuthenticationFilter {
/**
* 执行登录认证
* @param request
* @param response
* @param mappedValue
* @return
*/
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
try {
executeLogin(request, response);
return true;
} catch (Exception e) {
// 写出统一错误信息
InjectUtils.restError((HttpServletResponse) response);
return false;
}
}
@Override
protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
//String token = httpServletRequest.getHeader(Constants.TOKEN);
//
//JwtToken jwtToken = new JwtToken(token);
//// 提交给realm进行登入如果错误他会抛出异常并被捕获
//getSubject(request, response).login(jwtToken);
// 如果没有抛出异常则代表登入成功返回true
return true;
}
}

View File

@ -0,0 +1,33 @@
package org.example.ability.shiro.jwt;
import lombok.Data;
import org.apache.shiro.authc.AuthenticationToken;
/**
* @author bool
*/
@Data
public class JwtToken implements AuthenticationToken {
private static final long serialVersionUID = 1L;
/**
* JWT的字符token
*/
private String token;
public JwtToken(String token) {
this.token = token;
}
@Override
public Object getPrincipal() {
return token;
}
@Override
public Object getCredentials() {
return token;
}
}

View File

@ -0,0 +1,98 @@
package org.example.ability.shiro.jwt;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.util.Calendar;
import java.util.Date;
import org.example.core.utils.file.Md5Util;
/**
* JWT工具类
* @author bool
*/
public class JwtUtils {
/**
* 有效期24小时
*/
private static final long EXPIRE_TIME = 24 * 60 * 60 * 1000;
/**
* 校验是否正确
* @param token
* @param username
* @return
*/
public static boolean verify(String token, String username) {
try {
// 根据密码生成JWT效验器
Algorithm algorithm = Algorithm.HMAC256(encryptSecret(username));
JWTVerifier verifier = JWT.require(algorithm)
.withClaim("username", username)
.build();
// 效验TOKEN
verifier.verify(token);
return true;
} catch (Exception exception) {
return false;
}
}
/**
* 从Token中解密获得用户名
* @param token
* @return
*/
public static String getUsername(String token) {
try {
DecodedJWT jwt = JWT.decode(token);
return jwt.getClaim("username").asString();
} catch (JWTDecodeException e) {
return null;
}
}
/**
* 生成JWT Token字符串
* @param username
* @return
*/
public static String sign(String username) {
Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
Algorithm algorithm = Algorithm.HMAC256(encryptSecret(username));
// 附带username信息ExpiredJwtException
return JWT.create()
.withClaim("username", username)
.withExpiresAt(date).sign(algorithm);
}
/**
* 根据用户名和秘钥生成一个新的秘钥用于JWT加强一些安全性
* @param userName
* @return
*/
private static String encryptSecret(String userName){
// 一个简单的登录规则用户名+当前月份为加密串意思每个月会变要重新登录
// 可自行修改此规则
Calendar cl = Calendar.getInstance();
cl.setTimeInMillis(System.currentTimeMillis());
StringBuffer sb = new StringBuffer(userName)
.append("&")
.append(cl.get(Calendar.MONTH));
// 获取MD5
String secret = Md5Util.md5(sb.toString());
return Md5Util.md5(userName + "&" + secret);
}
}

View File

@ -0,0 +1,75 @@
package org.example.aspect.mybatis;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import java.sql.Connection;
import java.util.Properties;
import lombok.extern.log4j.Log4j2;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
/**
* 查询拦截器用于拦截处理通用的信息如用户ID多租户信息等
* 特别注意此处继承了PaginationInterceptor分页分页必须在拦截数据后执行否则容易出现分页不准确分页计数大于实际数量等问题
* @author bool
*/
@Log4j2
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class}),})
public class QueryInterceptor extends PaginationInterceptor implements Interceptor {
/**
* 客户ID
*/
private static final String USER_FILTER = "{{userId}}";
@Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
MetaObject metaObject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY, SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory());
MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
//sql语句类型
SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
// 只过滤查询的
if (SqlCommandType.SELECT == sqlCommandType) {
// 获得原始SQL
String sql = statementHandler.getBoundSql().getSql();
// 不处理
if(!sql.contains(USER_FILTER)){
return super.intercept(invocation);
}
// 处理SQL语句
String outSql = "";
// 设置SQL
metaObject.setValue("delegate.boundSql.sql", outSql);
// 再分页
return super.intercept(invocation);
}
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
}

View File

@ -0,0 +1,79 @@
package org.example.aspect.mybatis;
import com.baomidou.mybatisplus.extension.handlers.AbstractSqlParserHandler;
import java.lang.reflect.Field;
import java.sql.Timestamp;
import java.util.Objects;
import java.util.Properties;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
/**
* 自动给创建时间个更新时间加值
* @author bool
*/
@Intercepts(value = {@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})})
public class UpdateInterceptor extends AbstractSqlParserHandler implements Interceptor {
/**
* 创建时间
*/
private static final String CREATE_TIME = "createTime";
/**
* 更新时间
*/
private static final String UPDATE_TIME = "updateTime";
@Override
public Object intercept(Invocation invocation) throws Throwable {
MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
// SQL操作命令
SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
// 获取新增或修改的对象参数
Object parameter = invocation.getArgs()[1];
// 获取对象中所有的私有成员变量对应表字段
Field[] declaredFields = parameter.getClass().getDeclaredFields();
if (parameter.getClass().getSuperclass() != null) {
Field[] superField = parameter.getClass().getSuperclass().getDeclaredFields();
declaredFields = ArrayUtils.addAll(declaredFields, superField);
}
String fieldName = null;
for (Field field : declaredFields) {
fieldName = field.getName();
if (Objects.equals(CREATE_TIME, fieldName)) {
if (SqlCommandType.INSERT.equals(sqlCommandType)) {
field.setAccessible(true);
field.set(parameter, new Timestamp(System.currentTimeMillis()));
}
}
if (Objects.equals(UPDATE_TIME, fieldName)) {
if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {
field.setAccessible(true);
field.set(parameter, new Timestamp(System.currentTimeMillis()));
}
}
}
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
if (target instanceof Executor) {
return Plugin.wrap(target, this);
}
return target;
}
@Override
public void setProperties(Properties properties) {
}
}

View File

@ -0,0 +1,98 @@
package org.example.aspect.utils;
import java.io.IOException;
import java.lang.reflect.Field;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.log4j.Log4j2;
import org.example.core.api.ApiError;
import org.example.core.api.ApiRest;
import org.example.core.utils.jackson.JsonHelper;
import org.springframework.stereotype.Component;
/**
* 注入工具类
* @author bool
* @date 2019-07-17 09:32
*/
@Log4j2
@Component
public class InjectUtils {
/**
* 给对象字段赋值
*
* @param object 赋值的对象
* @param value
* @param fields 字段
* @throws Exception 异常
*/
public void setValue(Object object, Object value, String... fields) throws Exception {
//设置同类的属性
for (String fieldName : fields) {
//获取当前
Field field = this.getFiled(object.getClass(), fieldName);
if(field == null){
continue;
}
field.setAccessible(true);
field.set(object, value);
}
}
/**
* 获取字段名对应的字段
*
* @param clazz 目标类
* @param fieldName 字段名
*/
private Field getFiled(Class clazz, String fieldName) {
System.out.println("注入的类:"+clazz.toString());
//是否具有包含关系
try {
//获取当前类的属性
return clazz.getDeclaredField(fieldName);
}catch (Exception e){
log.error(clazz.toString() + ": not exist field, try superclass " + fieldName);
//如果为空且存在父类则往上找
if(clazz.getSuperclass()!=null){
return this.getFiled(clazz.getSuperclass(), fieldName);
}
return null;
}
}
/**
* 打印结果返回
* @param response
* @throws IOException
*/
public static void restError(HttpServletResponse response) {
try {
//固定错误
ApiRest apiRest = new ApiRest(ApiError.ERROR_10010002);
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
response.getWriter().write(JsonHelper.toJson(apiRest));
response.getWriter().close();
}catch (IOException e){
}
}
}

View File

@ -0,0 +1,39 @@
package org.example.config;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
/**
* 网关全局设置允许跨域
* @author bool
* @date 2019-08-13 17:28
*/
@Configuration
public class CorsConfig {
@Bean
public FilterRegistrationBean corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
// 设置访问源地址
config.addAllowedOriginPattern("*");
// 设置访问源请求头
config.addAllowedHeader("*");
// 设置访问源请求方法
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return bean;
}
}

View File

@ -0,0 +1,27 @@
package org.example.config;
import javax.servlet.MultipartConfigElement;
import org.springframework.boot.web.servlet.MultipartConfigFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.unit.DataSize;
/**
* 文件上传配置
* @author bool
* @date 2019-07-29 16:23
*/
@Configuration
public class MultipartConfig {
@Bean
public MultipartConfigElement multipartConfigElement() {
MultipartConfigFactory factory = new MultipartConfigFactory();
// 单个数据大小
factory.setMaxFileSize(DataSize.ofMegabytes(5000L));
/// 总上传数据大小
factory.setMaxRequestSize(DataSize.ofMegabytes(5000L));
return factory.createMultipartConfig();
}
}

View File

@ -0,0 +1,37 @@
package org.example.config;
import org.example.aspect.mybatis.QueryInterceptor;
import org.example.aspect.mybatis.UpdateInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Mybatis过滤器配置
* 注意必须按顺序进行配置否则容易出现业务异常
* @author bool
*/
@Configuration
@MapperScan(basePackages = "org.example.mapper")
public class MybatisConfig {
/**
* 数据查询过滤器
*/
@Bean
public QueryInterceptor queryInterceptor() {
QueryInterceptor query = new QueryInterceptor();
query.setLimit(-1L);
return query;
}
/**
* 插入数据过滤器
*/
@Bean
public UpdateInterceptor updateInterceptor() {
return new UpdateInterceptor();
}
}

View File

@ -0,0 +1,76 @@
package org.example.config;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import lombok.extern.log4j.Log4j2;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
/**
* 任务调度配置
* @author bool
*/
@Log4j2
@Configuration
@EnableScheduling
@EnableAsync
public class ScheduledConfig implements SchedulingConfigurer, AsyncConfigurer {
/**
* 定时任务使用的线程池
* @return
*/
@Bean(destroyMethod = "shutdown", name = "taskScheduler")
public ThreadPoolTaskScheduler taskScheduler(){
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(10);
scheduler.setThreadNamePrefix("task-");
scheduler.setAwaitTerminationSeconds(600);
scheduler.setWaitForTasksToCompleteOnShutdown(true);
return scheduler;
}
/**
* 异步任务执行线程池
* @return
*/
@Bean(name = "asyncExecutor")
public ThreadPoolTaskExecutor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setQueueCapacity(1000);
executor.setKeepAliveSeconds(600);
executor.setMaxPoolSize(20);
executor.setThreadNamePrefix("taskExecutor-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
@Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
ThreadPoolTaskScheduler taskScheduler = taskScheduler();
scheduledTaskRegistrar.setTaskScheduler(taskScheduler);
}
@Override
public Executor getAsyncExecutor() {
return asyncExecutor();
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return (throwable, method, objects) -> {
log.error("异步任务执行出现异常, message {}, emthod {}, params {}", throwable, method, objects);
};
}
}

View File

@ -0,0 +1,118 @@
package org.example.config;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.servlet.Filter;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.example.ability.shiro.CNFilterFactoryBean;
import org.example.ability.shiro.ShiroRealm;
import org.example.ability.shiro.aop.JwtFilter;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
/**
* Shiro配置类
* @author bool
*/
@Slf4j
@Configuration
public class ShiroConfig {
/**
* Filter Chain定义说明
*
* 1一个URL可以配置多个Filter使用逗号分隔
* 2当设置多个过滤器时全部验证通过才视为通过
* 3部分过滤器可指定参数如permsroles
*/
@Bean("shiroFilterFactoryBean")
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new CNFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 拦截器
Map<String, String> map = new LinkedHashMap<>();
// 需要排除的一些接口
map.put("/api/**", "anon");
map.put("/", "anon");
map.put("/v2/**", "anon");
map.put("/doc.html", "anon");
map.put("/**/*.js", "anon");
map.put("/**/*.css", "anon");
map.put("/**/*.html", "anon");
map.put("/**/*.svg", "anon");
map.put("/**/*.pdf", "anon");
map.put("/**/*.jpg", "anon");
map.put("/**/*.png", "anon");
map.put("/**/*.ico", "anon");
// 字体
map.put("/**/*.ttf", "anon");
map.put("/**/*.woff", "anon");
map.put("/**/*.woff2", "anon");
map.put("/druid/**", "anon");
map.put("/swagger-ui.html", "anon");
map.put("/swagger**/**", "anon");
map.put("/webjars/**", "anon");
// 添加自己的过滤器并且取名为jwt
Map<String, Filter> filterMap = new HashMap<String, Filter>(1);
filterMap.put("jwt", new JwtFilter());
shiroFilterFactoryBean.setFilters(filterMap);
map.put("/**", "jwt");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
@Bean("securityManager")
public DefaultWebSecurityManager securityManager(ShiroRealm myRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myRealm);
DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
securityManager.setSubjectDAO(subjectDAO);
return securityManager;
}
/**
* 下面的代码是添加注解支持
* @return
*/
@Bean
@DependsOn("lifecycleBeanPostProcessor")
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
defaultAdvisorAutoProxyCreator.setUsePrefix(true);
defaultAdvisorAutoProxyCreator.setAdvisorBeanNamePrefix("_no_advisor");
return defaultAdvisorAutoProxyCreator;
}
@Bean
public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
}

View File

@ -0,0 +1,65 @@
package org.example.config;
import com.github.xiaoymin.swaggerbootstrapui.annotations.EnableSwaggerBootstrapUI;
import io.swagger.annotations.ApiOperation;
import java.util.Collections;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.Contact;
import springfox.documentation.service.SecurityScheme;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* Swagger配置
* @author bool
* @date 2020/8/19 20:53
*/
@Configuration
@EnableSwagger2
@EnableSwaggerBootstrapUI
@ConfigurationProperties(prefix = "swagger")
public class SwaggerConfig {
@Bean
public Docket examApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.groupName("考试模块接口")
.select()
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
//.paths(PathSelectors.ant("/exam/api/**"))
.paths(PathSelectors.any())
.build()
.securitySchemes(Collections.singletonList(securityScheme()));
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder().title("考试系统接口")
.description("考试系统接口")
.contact(new Contact("Van", "https://exam.yfhl.net", "18365918@qq.com"))
.version("1.0.0")
.build();
}
/**
* 授权头部
* @return
*/
@Bean
SecurityScheme securityScheme() {
return new ApiKey("token", "token", "header");
}
}

View File

@ -0,0 +1,21 @@
package org.example.config.jackson;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* JSON序列化配置
* @author van
*/
@Configuration
public class JacksonConfig {
@Bean
public SimpleModule xssEscapeModule() {
SimpleModule module = new SimpleModule();
// 注册反序列化器处理输入
module.addDeserializer(String.class, new XssEscapeJsonDeserializer());
return module;
}
}

View File

@ -0,0 +1,15 @@
package org.example.config.jackson;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import java.io.IOException;
import org.owasp.encoder.Encode;
public class XssEscapeJsonDeserializer extends JsonDeserializer<String> {
@Override
public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
String value = p.getText(); // 获取原始字符串
return Encode.forHtml(value); // 转义HTML特殊字符
}
}

View File

@ -0,0 +1,43 @@
package org.example.controller;
import com.alibaba.fastjson2.JSONObject;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import org.example.core.api.ApiRest;
import org.example.core.api.controller.BaseController;
import org.example.dto.TaskCallbackDto;
import org.example.service.IAIOBCallbackService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @description: 百度外呼回调接口
* @author: haown
* @create: 2025-12-22 15:55
**/
@Api(tags={"百度外呼回调"})
@RestController
@RequestMapping("/api")
public class AIOBCallbackController extends BaseController {
@Resource
private IAIOBCallbackService aiobCallbackService;
@ApiOperation("任务回调")
@PostMapping("/taskCallBack")
public JSONObject taskCallBack(@RequestBody TaskCallbackDto taskCallbackDto, HttpServletRequest request) {
return aiobCallbackService.taskCallBack(taskCallbackDto.getCallbackType(), taskCallbackDto.getData());
}
@ApiOperation("创建实时任务")
@GetMapping("/test")
public ApiRest createActualTimeTask() {
System.out.println("1111111111111111111111");
return super.success();
}
}

View File

@ -0,0 +1,21 @@
package org.example.core.annon;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 数据字典注解
* @author bool
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Dict {
String dicCode();
String dicText() default "";
String dictTable() default "";
}

View File

@ -0,0 +1,65 @@
package org.example.core.api;
import java.io.Serializable;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
/**
* 全局错误码定义用于定义接口的响应数据
* 枚举名称全部使用代码命名在系统中调用免去取名难的问题
* @author bool
* @date 2019-06-14 21:15
*/
@NoArgsConstructor
@AllArgsConstructor
public enum ApiError implements Serializable {
/**
* 通用错误接口参数不全
*/
ERROR_10010001("参数不全或类型错误!"),
ERROR_10010002("您还未登录,请先登录!"),
ERROR_10010003("数据不存在!"),
ERROR_10010012("图形验证码错误!"),
ERROR_10010013("短信验证码错误!"),
ERROR_10010014("不允许重复评论!"),
/**
* 考试相关错误
*/
ERROR_20010001("试题被删除,无法继续考试!"),
ERROR_20010002("您有正在进行的考试!"),
ERROR_90010001("账号不存在,请确认!"),
ERROR_90010002("账号或密码错误!"),
ERROR_90010003("至少要包含一个角色!"),
ERROR_90010004("管理员账号无法修改!"),
ERROR_90010005("账号被禁用,请联系管理员!"),
ERROR_90010006("活动用户不足,无法开启竞拍!"),
ERROR_90010007("旧密码不正确,请确认!"),
ERROR_60000001("数据不存在!");
public String msg;
/**
* 生成Markdown格式文档用于更新文档用的
* @param args
*/
public static void main(String[] args) {
for (ApiError e : ApiError.values()) {
System.out.println("'"+e.name().replace("ERROR_", "")+"':'"+e.msg+"',");
}
}
/**
* 获取错误码
* @return
*/
public Integer getCode(){
return Integer.parseInt(this.name().replace("ERROR_", ""));
}
}

View File

@ -0,0 +1,62 @@
package org.example.core.api;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.example.core.exception.ServiceException;
/**
* 数据结果返回的封装
* @author bool
* @date 2018/11/20 09:48
*/
@Data
@NoArgsConstructor
@ApiModel(value="接口响应", description="接口响应")
public class ApiRest<T>{
/**
* 响应消息
*/
@ApiModelProperty(value = "响应消息")
private String msg;
/**
* 响应代码
*/
@ApiModelProperty(value = "响应代码,0为成功,1为失败", required = true)
private Integer code;
/**
* 请求或响应body
*/
@ApiModelProperty(value = "响应内容")
protected T data;
/**
* 是否成功
* @return
*/
public boolean isSuccess(){
return code.equals(0);
}
/**
* 构造函数
* @param error
*/
public ApiRest(ServiceException error){
this.code = error.getCode();
this.msg = error.getMsg();
}
/**
* 构造函数
* @param error
*/
public ApiRest(ApiError error){
this.code = error.getCode();
this.msg = error.msg;
}
}

View File

@ -0,0 +1,164 @@
package org.example.core.api.controller;
import org.example.core.api.ApiError;
import org.example.core.api.ApiRest;
import org.example.core.domain.AjaxResult;
import org.example.core.exception.ServiceException;
/**
* 基础控制器
* @author Dav
*/
public class BaseController {
/**
* 成功默认消息
*/
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 = "请求失败!";
/**
* 完成消息构造
* @param code
* @param message
* @param data
* @param <T>
* @return
*/
protected <T> ApiRest<T> message(Integer code, String message, T data){
ApiRest<T> response = new ApiRest<>();
response.setCode(code);
response.setMsg(message);
if(data!=null) {
response.setData(data);
}
return response;
}
/**
* 请求成功空数据
* @param <T>
* @return
*/
protected <T> ApiRest<T> success(){
return message(0, "请求成功!", null);
}
/**
* 请求成功通用代码
* @param message
* @param data
* @param <T>
* @return
*/
protected <T> ApiRest<T> success(String message, T data){
return message(CODE_SUCCESS, message, data);
}
/**
* 请求成功仅内容
* @param data
* @param <T>
* @return
*/
protected <T> ApiRest<T> success(T data){
return message(CODE_SUCCESS, MSG_SUCCESS, data);
}
/**
* 请求失败完整构造
* @param code
* @param message
* @param data
* @param <T>
* @return
*/
protected <T> ApiRest<T> failure(Integer code, String message, T data){
return message(code, message, data);
}
/**
* 请求失败消息和内容
* @param message
* @param data
* @param <T>
* @return
*/
protected <T> ApiRest<T> failure(String message, T data){
return message(CODE_FAILURE, message, data);
}
/**
* 请求失败消息
* @param message
* @return
*/
protected <T> ApiRest<T> failure(String message){
return message(CODE_FAILURE, message, null);
}
/**
* 请求失败仅内容
* @param data
* @param <T>
* @return
*/
protected <T> ApiRest<T> failure(T data){
return message(CODE_FAILURE, MSG_FAILURE, data);
}
/**
* 请求失败仅内容
* @param <T>
* @return
*/
protected <T> ApiRest<T> failure(){
return message(CODE_FAILURE, MSG_FAILURE, null);
}
/**
* 请求失败仅内容
* @param <T>
* @return
*/
protected <T> ApiRest<T> failure(ApiError error, T data){
return message(error.getCode(), error.msg, data);
}
/**
* 请求失败仅内容
* @param ex
* @param <T>
* @return
*/
protected <T> ApiRest<T> failure(ServiceException ex){
ApiRest<T> apiRest = message(ex.getCode(), ex.getMsg(), null);
return apiRest;
}
/**
* 响应返回结果
*
* @param rows 影响行数
* @return 操作结果
*/
protected AjaxResult toAjax(int rows) {
return rows > 0 ? AjaxResult.success() : AjaxResult.error();
}
}

View File

@ -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 {
}

View File

@ -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;
/**
* <p>
* 主键通用请求类用于根据ID查询
* </p>
*
* @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;
}

View File

@ -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;
/**
* <p>
* 主键通用响应类用于添加后返回内容
* </p>
*
* @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;
}

View File

@ -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<String> ids;
}

View File

@ -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;
/**
* <p>
* 通用状态请求类用于修改状态什么的
* </p>
*
* @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<String> ids;
@ApiModelProperty(value = "通用状态0为正常1为禁用", required=true)
private Integer state;
}

View File

@ -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 <T>
* @author bool
*/
@ApiModel(value="分页参数", description="分页参数")
@Data
public class PagingReqDTO<T> {
@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;
}
}

View File

@ -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 <T>
*/
public class PagingRespDTO<T> extends Page<T> {
/**
* 获取页面总数量
* @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;
}
}
}

View File

@ -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<MediaType> fastMediaTypes = new ArrayList<>();
fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
fastConverter.setSupportedMediaTypes(fastMediaTypes);
// 在convert中添加配置信息
fastConverter.setFastJsonConfig(fastJsonConfig);
return fastConverter;
}
}

View File

@ -0,0 +1,165 @@
package org.example.core.domain;
import java.util.HashMap;
/**
* 操作消息提醒
*
* @author xinyilu
*/
public class AjaxResult extends HashMap<String, Object> {
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;
}
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,18 @@
package org.example.core.enums;
/**
* 开放方式
* @author bool
*/
public interface OpenType {
/**
* 完全开放
*/
Integer OPEN = 1;
/**
* 部门开放
*/
Integer DEPT_OPEN = 2;
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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> T map(Object source, Class<T> destinationClass) {
return dozerBeanMapper.map(source, destinationClass);
}
/**
* 基于Dozer转换Collection中对象的类型.
*/
public static <T> List<T> mapList(Iterable<?> sourceList, Class<T> destinationClass) {
List<T> 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 <T, S> List<T> mapList(Collection<S> source, Function<? super S, ? extends T> mapper) {
return source.stream().map(mapper).collect(Collectors.toList());
}
}

View File

@ -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;
}
}

View File

@ -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 <br/>
* date: 2018年12月13日 下午6:34:02 <br/>
*
* @author Bool
* @version
*/
public class DateUtils {
/**
*
* calcExpDays:计算某个日期与当前日期相差的天数如果计算的日期大于现在时间将返回负数否则返回正数 <br/>
* @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:获取当前时间的字符串格式根据传入的格式化来展示. <br/>
* @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:格式化日期返回指定的格式 <br/>
* @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;
}
}

View File

@ -0,0 +1,65 @@
package org.example.core.utils;
import javax.servlet.http.HttpServletRequest;
/**
* IP获取工具类用户获取网络请求过来的真实IP
* ClassName: IpUtils <br/>
* date: 2018年2月13日 下午7:27:52 <br/>
*
* @author Bool
* @version
*/
public class IpUtils {
/**
*
* getClientIp:通过请求获取客户端的真实IP地址
* @author Bool
* @param request
* @return
*/
public static String extractClientIp(HttpServletRequest request) {
String ip = null;
//X-Forwarded-ForSquid 服务代理
String ipAddresses = request.getHeader("X-Forwarded-For");
if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
//Proxy-Client-IPapache 服务代理
ipAddresses = request.getHeader("Proxy-Client-IP");
}
if (ipAddresses == null || ipAddresses.length() == 0 || "unknown".equalsIgnoreCase(ipAddresses)) {
//WL-Proxy-Client-IPweblogic 服务代理
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-IPnginx服务代理
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;
}
}

View File

@ -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<Field> 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<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函数.
*/
public static Object getFieldValue(final Object obj, final String fieldName) {
Field field = getAccessibleField(obj, fieldName);
if (field == null) {
throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + obj + "]");
}
Object result = null;
try {
result = field.get(obj);
} catch (IllegalAccessException e) {
log.error("不可能抛出的异常{}", e.getMessage());
}
return result;
}
/**
* 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数.
*/
public static void setFieldValue(final Object obj, final String fieldName, final Object value) {
Field field = getAccessibleField(obj, fieldName);
if (field == null) {
throw new IllegalArgumentException("Could not find field [" + fieldName + "] on target [" + obj + "]");
}
try {
field.set(obj, value);
} catch (IllegalAccessException e) {
log.error("不可能抛出的异常:{}", e.getMessage());
}
}
/**
* 直接调用对象方法, 无视private/protected修饰符.
* 用于一次性调用的情况否则应使用getAccessibleMethod()函数获得Method后反复调用.
* 同时匹配方法名+参数类型
*/
public static Object invokeMethod(final Object obj, final String methodName, final Class<?>[] 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<User>
*
* @param clazz The class to introspect
* @return the first generic declaration, or Object.class if cannot be determined
*/
@SuppressWarnings("unchecked")
public static <T> Class<T> getClassGenricType(final Class clazz) {
return getClassGenricType(clazz, 0);
}
/**
* 通过反射, 获得Class定义中声明的父类的泛型参数的类型.
* 如无法找到, 返回Object.class.
*
* 如public UserDao extends HibernateDao<User,Long>
*
* @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);
}
}

View File

@ -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> T getBean(Class<T> tClass) {
return applicationContext.getBean(tClass);
}
public static <T> T getBean(String name, Class<T> type) {
return applicationContext.getBean(name, type);
}
}

View File

@ -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格式格式为<xml><key>value</key>...</xml>
* @param params
* @return
*/
public static String mapToXml(Map<String, String> params){
StringBuffer sb = new StringBuffer("<xml>");
for(String key:params.keySet()){
sb.append("<")
.append(key).append(">")
.append(params.get(key))
.append("</").append(key).append(">");
}
sb.append("</xml>");
return sb.toString();
}
}

View File

@ -0,0 +1,401 @@
/**
* Copyright &copy; 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> 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<String, CellStyle> styles;
/**
* 当前行号
*/
private int rownum;
/**
* 注解列表Object[]{ ExcelField, Field/Method }
*/
List<Object[]> annotationList = Lists.newArrayList();
/**
* 构造函数
* @param title 表格标题空值表示无标题
* @param cls 实体对象通过annotation.ExportField获取标题
*/
public ExportExcel(String title, Class<?> cls){
this(title, cls, 1);
}
/**
* 构造函数
* @param title 表格标题空值表示无标题
* @param cls 实体对象通过annotation.ExportField获取标题
* @param type 导出类型1:导出数据2导出模板
* @param groups 导入分组
*/
public ExportExcel(String title, Class<?> cls, int type, int... groups){
// Get annotation field
Field[] fs = cls.getDeclaredFields();
for (Field f : fs){
ExcelField ef = f.getAnnotation(ExcelField.class);
if (ef != null && (ef.type()==0 || ef.type()==type)){
if (groups!=null && groups.length>0){
boolean inGroup = false;
for (int g : groups){
if (inGroup){
break;
}
for (int efg : ef.groups()){
if (g == efg){
inGroup = true;
annotationList.add(new Object[]{ef, f});
break;
}
}
}
}else{
annotationList.add(new Object[]{ef, f});
}
}
}
// Get annotation method
Method[] ms = cls.getDeclaredMethods();
for (Method m : ms){
ExcelField ef = m.getAnnotation(ExcelField.class);
if (ef != null && (ef.type()==0 || ef.type()==type)){
if (groups!=null && groups.length>0){
boolean inGroup = false;
for (int g : groups){
if (inGroup){
break;
}
for (int efg : ef.groups()){
if (g == efg){
inGroup = true;
annotationList.add(new Object[]{ef, m});
break;
}
}
}
}else{
annotationList.add(new Object[]{ef, m});
}
}
}
// Field sorting
Collections.sort(annotationList, new Comparator<Object[]>() {
@Override
public int compare(Object[] o1, Object[] o2) {
return new Integer(((ExcelField)o1[0]).sort()).compareTo(
new Integer(((ExcelField)o2[0]).sort()));
}
});
// Initialize
List<String> headerList = Lists.newArrayList();
for (Object[] os : annotationList){
String t = ((ExcelField)os[0]).title();
// 如果是导出则去掉注释
if (type==1){
String[] ss = StringUtils.split(t, "**", 2);
if (ss.length==2){
t = ss[0];
}
}
headerList.add(t);
}
initialize(title, headerList);
}
/**
* 初始化函数
* @param title 表格标题空值表示无标题
* @param headerList 表头列表
*/
private void initialize(String title, List<String> headerList) {
this.wb = new SXSSFWorkbook(500);
this.sheet = wb.createSheet("Export");
this.styles = createStyles(wb);
// Create title
if (StringUtils.isNotBlank(title)){
Row titleRow = sheet.createRow(rownum++);
titleRow.setHeightInPoints(30);
Cell titleCell = titleRow.createCell(0);
titleCell.setCellStyle(styles.get("title"));
titleCell.setCellValue(title);
sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(),
titleRow.getRowNum(), titleRow.getRowNum(), headerList.size()-1));
}
// Create header
if (headerList == null){
throw new RuntimeException("headerList not null!");
}
Row headerRow = sheet.createRow(rownum++);
headerRow.setHeightInPoints(16);
for (int i = 0; i < headerList.size(); i++) {
Cell cell = headerRow.createCell(i);
cell.setCellStyle(styles.get("header"));
String[] ss = StringUtils.split(headerList.get(i), "**", 2);
if (ss.length==2){
cell.setCellValue(ss[0]);
Comment comment = this.sheet.createDrawingPatriarch().createCellComment(
new XSSFClientAnchor(0, 0, 0, 0, (short) 3, 3, (short) 5, 6));
comment.setString(new XSSFRichTextString(ss[1]));
cell.setCellComment(comment);
}else{
cell.setCellValue(headerList.get(i));
}
sheet.autoSizeColumn(i);
}
for (int i = 0; i < headerList.size(); i++) {
int colWidth = sheet.getColumnWidth(i)*2;
sheet.setColumnWidth(i, colWidth < 3000 ? 3000 : colWidth);
}
log.debug("Initialize success.");
}
/**
* 创建表格样式
* @param wb 工作薄对象
* @return 样式列表
*/
private Map<String, CellStyle> createStyles(Workbook wb) {
Map<String, CellStyle> styles = new HashMap<>(16);
CellStyle style = wb.createCellStyle();
style.setAlignment(CellStyle.ALIGN_CENTER);
style.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
Font titleFont = wb.createFont();
titleFont.setFontName("Arial");
titleFont.setFontHeightInPoints((short) 16);
titleFont.setBoldweight(Font.BOLDWEIGHT_BOLD);
style.setFont(titleFont);
styles.put("title", style);
style = wb.createCellStyle();
style.setVerticalAlignment(CellStyle.VERTICAL_CENTER);
style.setBorderRight(CellStyle.BORDER_THIN);
style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
style.setBorderLeft(CellStyle.BORDER_THIN);
style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
style.setBorderTop(CellStyle.BORDER_THIN);
style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
style.setBorderBottom(CellStyle.BORDER_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(CellStyle.ALIGN_LEFT);
styles.put("data1", style);
style = wb.createCellStyle();
style.cloneStyleFrom(styles.get("data"));
style.setAlignment(CellStyle.ALIGN_CENTER);
styles.put("data2", style);
style = wb.createCellStyle();
style.cloneStyleFrom(styles.get("data"));
style.setAlignment(CellStyle.ALIGN_RIGHT);
styles.put("data3", style);
style = wb.createCellStyle();
style.cloneStyleFrom(styles.get("data"));
// style.setWrapText(true);
style.setAlignment(CellStyle.ALIGN_CENTER);
style.setFillForegroundColor(IndexedColors.GREY_50_PERCENT.getIndex());
style.setFillPattern(CellStyle.SOLID_FOREGROUND);
Font headerFont = wb.createFont();
headerFont.setFontName("Arial");
headerFont.setFontHeightInPoints((short) 10);
headerFont.setBoldweight(Font.BOLDWEIGHT_BOLD);
headerFont.setColor(IndexedColors.WHITE.getIndex());
style.setFont(headerFont);
styles.put("header", style);
return styles;
}
/**
* 添加一行
* @return 行对象
*/
public Row addRow(){
return sheet.createRow(rownum++);
}
/**
* 添加一个单元格
* @param row 添加的行
* @param column 添加列号
* @param val 添加值
* @return 单元格对象
*/
public Cell addCell(Row row, int column, Object val){
return this.addCell(row, column, val, 0, Class.class);
}
/**
* 添加一个单元格
* @param row 添加的行
* @param column 添加列号
* @param val 添加值
* @param align 对齐方式1靠左2居中3靠右
* @return 单元格对象
*/
public Cell addCell(Row row, int column, Object val, int align, Class<?> fieldType){
Cell cell = row.createCell(column);
CellStyle style = styles.get("data"+(align>=1&&align<=3?align:""));
try {
if (val == null){
cell.setCellValue("");
} else if (val instanceof String) {
cell.setCellValue((String) val);
} else if (val instanceof Integer) {
cell.setCellValue((Integer) val);
} else if (val instanceof Long) {
cell.setCellValue((Long) val);
} else if (val instanceof Double) {
cell.setCellValue((Double) val);
} else if (val instanceof Float) {
cell.setCellValue((Float) val);
} else if (val instanceof Date) {
DataFormat format = wb.createDataFormat();
style.setDataFormat(format.getFormat("yyyy-MM-dd"));
cell.setCellValue((Date) val);
} else {
if (fieldType != Class.class){
cell.setCellValue((String)fieldType.getMethod("setValue", Object.class).invoke(null, val));
}else{
cell.setCellValue((String)Class.forName(this.getClass().getName().replaceAll(this.getClass().getSimpleName(),
"fieldtype."+val.getClass().getSimpleName()+"Type")).getMethod("setValue", Object.class).invoke(null, val));
}
}
} catch (Exception ex) {
log.info("Set cell value ["+row.getRowNum()+","+column+"] error: " + ex.toString());
cell.setCellValue(val.toString());
}
cell.setCellStyle(style);
return cell;
}
/**
* 添加数据通过annotation.ExportField添加数据
* @return list 数据列表
*/
public <E> ExportExcel setDataList(List<E> list){
for (E e : list){
int colunm = 0;
Row row = this.addRow();
StringBuilder sb = new StringBuilder();
for (Object[] os : annotationList){
ExcelField ef = (ExcelField)os[0];
Object val = null;
try{
if (StringUtils.isNotBlank(ef.value())){
val = Reflections.invokeGetter(e, ef.value());
}else{
if (os[1] instanceof Field){
val = Reflections.invokeGetter(e, ((Field)os[1]).getName());
}else if (os[1] instanceof Method){
val = Reflections.invokeMethod(e, ((Method)os[1]).getName(), new Class[] {}, new Object[] {});
}
}
}catch(Exception ex) {
log.info(ex.toString());
val = "";
}
this.addCell(row, colunm++, val, ef.align(), ef.fieldType());
sb.append(val + ", ");
}
log.debug("Write success: ["+row.getRowNum()+"] "+sb.toString());
}
return this;
}
/**
* 输出数据流
* @param os 输出数据流
*/
public ExportExcel write(OutputStream os) throws IOException{
wb.write(os);
return this;
}
/**
* 输出到客户端
* @param fileName 输出文件名
*/
public ExportExcel write(HttpServletResponse response, String fileName) throws IOException{
response.reset();
response.setHeader("Access-Control-Allow-Origin", "*");
response.setContentType("application/octet-stream; charset=utf-8");
response.addHeader("Content-Disposition", "attachment; filename="+ URLEncoder.encode(fileName, "utf-8"));
write(response.getOutputStream());
return this;
}
/**
* 清理临时文件
*/
public ExportExcel dispose(){
wb.dispose();
return this;
}
}

View File

@ -0,0 +1,302 @@
/**
* Copyright &copy; 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
*/
package org.example.core.utils.excel;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.example.core.utils.Reflections;
import org.example.core.utils.excel.annotation.ExcelField;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;
/**
* 导入Excel文件支持XLSXLSX格式
* @author jeeplus
* @version 2016-03-10
*/
public class ImportExcel {
private static Logger log = LoggerFactory.getLogger(ImportExcel.class);
/**
* 工作薄对象
*/
private Workbook wb;
/**
* 工作表对象
*/
private Sheet sheet;
/**
* 标题行号
*/
private int headerNum;
/**
* 构造函数
* @param multipartFile 导入文件对象
* @param headerNum 标题行号数据行号=标题行号+1
* @param sheetIndex 工作表编号
* @throws InvalidFormatException
* @throws IOException
*/
public ImportExcel(MultipartFile multipartFile, int headerNum, int sheetIndex)
throws InvalidFormatException, IOException {
this(multipartFile.getOriginalFilename(), multipartFile.getInputStream(), headerNum, sheetIndex);
}
/**
* 构造函数
* @param is 导入文件对象
* @param headerNum 标题行号数据行号=标题行号+1
* @param sheetIndex 工作表编号
* @throws InvalidFormatException
* @throws IOException
*/
public ImportExcel(String fileName, InputStream is, int headerNum, int sheetIndex)
throws IOException {
if (StringUtils.isBlank(fileName)){
throw new RuntimeException("导入文档为空!");
}else if(fileName.toLowerCase().endsWith("xls")){
this.wb = new HSSFWorkbook(is);
}else if(fileName.toLowerCase().endsWith("xlsx")){
this.wb = new XSSFWorkbook(is);
}else{
throw new RuntimeException("文档格式不正确!");
}
if (this.wb.getNumberOfSheets()<sheetIndex){
throw new RuntimeException("文档中没有工作表!");
}
this.sheet = this.wb.getSheetAt(sheetIndex);
this.headerNum = headerNum;
log.debug("Initialize success.");
}
/**
* 获取行对象
* @param rownum
* @return
*/
public Row getRow(int rownum){
return this.sheet.getRow(rownum);
}
/**
* 获取数据行号
* @return
*/
public int getDataRowNum(){
return headerNum+1;
}
/**
* 获取最后一个数据行号
* @return
*/
public int getLastDataRowNum(){
return this.sheet.getLastRowNum()+headerNum;
}
/**
* 获取单元格值
* @param row 获取的行
* @param column 获取单元格列号
* @return 单元格值
*/
public Object getCellValue(Row row, int column) {
Object val = "";
try {
Cell cell = row.getCell(column);
if (cell != null) {
if (cell.getCellType() == Cell.CELL_TYPE_NUMERIC) {
// 当excel 中的数据为数值或日期是需要特殊处理
if (HSSFDateUtil.isCellDateFormatted(cell)) {
double d = cell.getNumericCellValue();
Date date = HSSFDateUtil.getJavaDate(d);
SimpleDateFormat dformat = new SimpleDateFormat(
"yyyy-MM-dd");
val = dformat.format(date);
} else {
NumberFormat nf = NumberFormat.getInstance();
nf.setGroupingUsed(false);// true时的格式1,234,567,890
val = nf.format(cell.getNumericCellValue());// 数值类型的数据为double所以需要转换一下
}
} else if (cell.getCellType() == Cell.CELL_TYPE_STRING) {
val = cell.getStringCellValue();
} else if (cell.getCellType() == Cell.CELL_TYPE_FORMULA) {
val = cell.getCellFormula();
} else if (cell.getCellType() == Cell.CELL_TYPE_BOOLEAN) {
val = cell.getBooleanCellValue();
} else if (cell.getCellType() == Cell.CELL_TYPE_ERROR) {
val = cell.getErrorCellValue();
}
}
} catch (Exception e) {
return val;
}
return val;
}
/**
* 获取导入数据列表
* @param cls 导入对象类型
* @param groups 导入分组
*/
public <E> List<E> getDataList(Class<E> cls, int... groups) throws InstantiationException, IllegalAccessException{
List<Object[]> annotationList = Lists.newArrayList();
// Get annotation field
Field[] fs = cls.getDeclaredFields();
for (Field f : fs){
ExcelField ef = f.getAnnotation(ExcelField.class);
if (ef != null && (ef.type()==0 || ef.type()==2)){
if (groups!=null && groups.length>0){
boolean inGroup = false;
for (int g : groups){
if (inGroup){
break;
}
for (int efg : ef.groups()){
if (g == efg){
inGroup = true;
annotationList.add(new Object[]{ef, f});
break;
}
}
}
}else{
annotationList.add(new Object[]{ef, f});
}
}
}
// Get annotation method
Method[] ms = cls.getDeclaredMethods();
for (Method m : ms){
ExcelField ef = m.getAnnotation(ExcelField.class);
if (ef != null && (ef.type()==0 || ef.type()==2)){
if (groups!=null && groups.length>0){
boolean inGroup = false;
for (int g : groups){
if (inGroup){
break;
}
for (int efg : ef.groups()){
if (g == efg){
inGroup = true;
annotationList.add(new Object[]{ef, m});
break;
}
}
}
}else{
annotationList.add(new Object[]{ef, m});
}
}
}
// Field sorting
Collections.sort(annotationList, new Comparator<Object[]>() {
@Override
public int compare(Object[] o1, Object[] o2) {
return new Integer(((ExcelField)o1[0]).sort()).compareTo(
new Integer(((ExcelField)o2[0]).sort()));
}
});
// Get excel data
List<E> dataList = Lists.newArrayList();
for (int i = this.getDataRowNum(); i < this.getLastDataRowNum(); i++) {
E e = (E)cls.newInstance();
int column = 0;
Row row = this.getRow(i);
StringBuilder sb = new StringBuilder();
for (Object[] os : annotationList){
Object val = this.getCellValue(row, column++);
if (val != null){
ExcelField ef = (ExcelField)os[0];
// Get param type and type cast
Class<?> valType = Class.class;
if (os[1] instanceof Field){
valType = ((Field)os[1]).getType();
}else if (os[1] instanceof Method){
Method method = ((Method)os[1]);
if ("get".equals(method.getName().substring(0, 3))){
valType = method.getReturnType();
}else if("set".equals(method.getName().substring(0, 3))){
valType = ((Method)os[1]).getParameterTypes()[0];
}
}
//log.debug("Import value type: ["+i+","+column+"] " + valType);
try {
//如果导入的java对象需要在这里自己进行变换
if (valType == String.class){
String s = String.valueOf(val.toString());
if(StringUtils.endsWith(s, ".0")){
val = StringUtils.substringBefore(s, ".0");
}else{
val = String.valueOf(val.toString());
}
}else if (valType == Integer.class){
val = Double.valueOf(val.toString()).intValue();
}else if (valType == Long.class){
val = Double.valueOf(val.toString()).longValue();
}else if (valType == Double.class){
val = Double.valueOf(val.toString());
}else if (valType == Float.class){
val = Float.valueOf(val.toString());
}else if (valType == Date.class){
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
val=sdf.parse(val.toString());
}else{
if (ef.fieldType() != Class.class){
val = ef.fieldType().getMethod("getValue", String.class).invoke(null, val.toString());
}else{
val = Class.forName(this.getClass().getName().replaceAll(this.getClass().getSimpleName(),
"fieldtype."+valType.getSimpleName()+"Type")).getMethod("getValue", String.class).invoke(null, val.toString());
}
}
} catch (Exception ex) {
log.info("Get cell value ["+i+","+column+"] error: " + ex.toString());
val = null;
}
// set entity value
if (os[1] instanceof Field){
Reflections.invokeSetter(e, ((Field)os[1]).getName(), val);
}else if (os[1] instanceof Method){
String mthodName = ((Method)os[1]).getName();
if ("get".equals(mthodName.substring(0, 3))){
mthodName = "set"+StringUtils.substringAfter(mthodName, "get");
}
Reflections.invokeMethod(e, mthodName, new Class[] {valType}, new Object[] {val});
}
}
sb.append(val+", ");
}
dataList.add(e);
log.debug("Read success: ["+i+"] "+sb.toString());
}
return dataList;
}
}

View File

@ -0,0 +1,59 @@
/**
* Copyright &copy; 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
*/
package org.example.core.utils.excel.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Excel注解定义
* @author jeeplus
* @version 2016-03-10
*/
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelField {
/**
* 导出字段名默认调用当前字段的get方法如指定导出字段为对象请填写对象名.对象属性area.nameoffice.name
*/
String value() default "";
/**
* 导出字段标题需要添加批注请用**分隔标题**批注仅对导出模板有效
*/
String title();
/**
* 字段类型0导出导入1仅导出2仅导入
*/
int type() default 0;
/**
* 导出字段对齐方式0自动1靠左2居中3靠右
*/
int align() default 0;
/**
* 导出字段字段排序升序
*/
int sort() default 0;
/**
* 如果是字典类型请设置字典的type值
*/
String dictType() default "";
/**
* 反射类型
*/
Class<?> fieldType() default Class.class;
/**
* 字段归属组根据分组导出导入
*/
int[] groups() default {};
}

View File

@ -0,0 +1,55 @@
/**
* Copyright &copy; 2015-2020 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
*/
package org.example.core.utils.excel.fieldtype;
import java.util.List;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.StringUtils;
/**
* 字段类型转换
* @author jeeplus
* @version 2016-5-29
*/
public class ListType {
/**
* 获取对象值导入
*/
public static Object getValue(String val) {
List<String> list = Lists.newArrayList();
if(!StringUtils.isBlank(val)) {
for (String s : val.split(",")) {
list.add(s);
}
}
return list;
}
/**
* 设置对象值导出
*/
public static String setValue(Object val) {
if (val != null){
List<String> list = (List<String>)val;
StringBuffer sb = null;
for (String item: list){
if(StringUtils.isBlank(item)){
continue;
}
if(sb == null){
sb = new StringBuffer(item);
}else{
sb.append(",").append(item);
}
}
if(sb!=null) {
return sb.toString().replace("[]", "");
}
}
return "";
}
}

View File

@ -0,0 +1,37 @@
package org.example.core.utils.file;
import java.security.MessageDigest;
/**
* MD5工具类
* ClassName: MD5Util <br/>
* date: 2018年1月13日 下午6:54:53 <br/>
*
* @author Bool
* @version
*/
public class Md5Util {
/**
* 简单MD5
* @param str
* @return
*/
public static String md5(String str) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] array = md.digest(str.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte item : array) {
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
}
return sb.toString();
}catch(Exception e) {
return null;
}
}
}

View File

@ -0,0 +1,116 @@
package org.example.core.utils.jackson;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import java.io.IOException;
import java.util.TimeZone;
import org.example.core.exception.ServiceException;
/**
* JSON工具类
* @author van
*/
public class JsonHelper {
/**
* 转换为字符串
* @param obj
* @return
*/
public static String toJson(Object obj) {
ObjectMapper mapper = getMapper();
try {
return mapper.writeValueAsString(obj);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
/**
* 将字符转换为java对象
* @param json
* @param clazz
* @return
*/
public static <T> T parseObject(String json, Class<T> clazz) {
ObjectMapper mapper = getMapper();
try {
return mapper.readValue(json, clazz);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 将对象转换为另外一个对象
* @param object
* @param clazz
* @return
*/
public static <T> T parseObject(Object object, Class<T> clazz) {
ObjectMapper mapper = getMapper();
try {
return mapper.readValue(toJson(object), clazz);
} catch (InvalidFormatException e){
throw new ServiceException("数据格式存在错误!");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 复杂对象的转换
* @param object
* @param typeReference
* @return
* @param <T>
*/
public static <T> T parseObject(Object object, TypeReference<T> typeReference) {
ObjectMapper mapper = getMapper();
try {
return mapper.readValue(toJson(object), typeReference);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 复杂对象的转换
* @param json
* @param typeReference
* @return
* @param <T>
*/
public static <T> T parseObject(String json, TypeReference<T> typeReference) {
ObjectMapper mapper = getMapper();
try {
return mapper.readValue(json, typeReference);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 获取转换配置
* @return
*/
public static ObjectMapper getMapper(){
ObjectMapper mapper = new ObjectMapper();
mapper.setDateFormat(new MyDateFormat());
mapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
return mapper;
}
}

View File

@ -0,0 +1,36 @@
package org.example.core.utils.jackson;
import com.fasterxml.jackson.databind.util.StdDateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* JSON时间格式类用于兼容不同类型日期
* @author van
*/
public class MyDateFormat extends SimpleDateFormat {
private StdDateFormat stdDateFormat = new StdDateFormat();
public MyDateFormat() {
// 设置默认的日期格式
super("yyyy-MM-dd HH:mm:ss");
}
@Override
public Date parse(String dateStr) throws ParseException {
if (dateStr != null && !dateStr.contains("T")) {
return super.parse(dateStr);
}
return stdDateFormat.parse(dateStr);
}
@Override
public Object clone() {
MyDateFormat other = (MyDateFormat)super.clone();
other.stdDateFormat = new StdDateFormat();
return other;
}
}

View File

@ -0,0 +1,169 @@
package org.example.dto;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import java.util.List;
import lombok.Data;
/**
* @description: 任务单通电话回调传输data
* @author: haown
* @create: 2024-08-29 09:36
**/
@Data
public class TaskCallbackDataDto {
/**
* 通话唯一标识
*/
private String sessionId;
/**
* 租户唯一标识
*/
private Long tenantId;
/**
* 任务标识
*/
private String taskId;
/**
* 任务名称
*/
private String taskName;
/**
* 机器人ID
*/
private String robotId;
/**
* 外呼机器人名称
*/
private String robotName;
/**
* 号码组唯一标识导入名单后返回的名单号码组对应标识
*/
private Long memberId;
/**
* 被叫号码
*/
private String mobile;
/**
* 拨打次数
*/
private Integer callTimes;
/**
* 主叫号码
*/
private String callerNum;
/**
* 接通状态1-已接通 0-未接通
*/
private Integer endType;
/**
* 呼叫类型0-首次呼叫1-重试2-预约呼叫3-实时呼叫
*/
private Integer callType;
/**
* 未接通原因
*/
private String endTypeReason;
/**
* 生成通话录音唯一标识,可通过该标识获取录音
*/
private String contactUUID;
/**
* 文件id名单导入任务时生成的文件ID
*/
private Long field;
/**
* 信息收集内容
*/
private JSONObject collectInfo;
/**
* 会话还原记录
*/
private JSONArray record;
/**
* 拨号总时长单位为秒
*/
private Integer durationTimeLen;
/**
* 振铃时长单位为秒
*/
private Integer ringingTimeLen;
/**
* 对话时长单位为秒
*/
private Integer talkingTimeLen;
/**
* 呼叫开始时间-Unix时间戳(单位:毫秒)
*/
private Long startTime;
/**
* 振铃开始时间-Unix时间戳(单位:毫秒)
*/
private Long ringStartTime;
/**
* 通话开始时间-Unix时间戳(单位:毫秒)未接通显示'-'已接通会有值
*/
private Long talkingStartTime;
/**
* 呼叫结束时间-Unix时间戳(单位:毫秒)
*/
private Long endTime;
/**
* 意向在外呼机器人-流程节点-信息收集配置key为意向对应的value值
*/
private String intent;
/**
* 动作HUNGUP挂机
*/
private List<String> action;
/**
* 是否机器人主动挂机
*/
private Boolean isRobotHangup;
/**
* 变量导入名单的变量
*/
private JSONObject dialogVar;
/**
* 短信变量
*/
private JSONObject smsVar;
/**
* 来自导入信息
*/
private String extJson;
/**
* 转接结果,字段为空或值为0未发起1成功-1失败
*/
private Integer transResult;
}

View File

@ -0,0 +1,22 @@
package org.example.dto;
import lombok.Data;
/**
* @description: 任务单通电话回调传输对象
* @author: haown
* @create: 2024-08-29 14:06
**/
@Data
public class TaskCallbackDto {
/**
* 回调数据类型 0-任务呼叫单通电话回调 1-号码组终态回调 2-任务状态变更回调 3-实时呼叫单通电话回调
*/
private Integer callbackType;
/**
* 任务单通电话回调传输data
*/
private TaskCallbackDataDto data;
}

View File

@ -0,0 +1,49 @@
package org.example.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import java.util.Date;
import lombok.Data;
/**
* @description: 百度外呼回调传输对象
* @author: haown
* @create: 2025-12-22 15:11
**/
@Data
@TableName("aiob_callback_data")
public class AIOBCallbackEntity extends Model<AIOBCallbackEntity> {
/**
* 主键
*/
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
* 回调数据类型 0-任务呼叫单通电话回调 1-号码组终态回调 2-任务状态变更回调 3-实时呼叫单通电话回调
*/
private Integer callbackType;
/**
* 回调数据
*/
private String callbackData;
/**
* 已读状态0未读1已读
*/
private Integer readState;
/**
* 创建时间
*/
private Date createDate;
/**
* 修改时间
*/
private Date updateDate;
}

View File

@ -0,0 +1,8 @@
package org.example.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.example.entity.AIOBCallbackEntity;
public interface AIOBCallbackMapper extends BaseMapper<AIOBCallbackEntity> {
}

View File

@ -0,0 +1,16 @@
package org.example.service;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.extension.service.IService;
import org.example.dto.TaskCallbackDataDto;
import org.example.entity.AIOBCallbackEntity;
/**
* @description: 百度外呼回调service
* @author: haown
* @create: 2025-12-22 15:11
**/
public interface IAIOBCallbackService extends IService<AIOBCallbackEntity> {
JSONObject taskCallBack(Integer callbackType, TaskCallbackDataDto data);
}

View File

@ -0,0 +1,39 @@
package org.example.service.impl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import java.util.Date;
import javax.annotation.Resource;
import org.example.dto.TaskCallbackDataDto;
import org.example.entity.AIOBCallbackEntity;
import org.example.mapper.AIOBCallbackMapper;
import org.example.service.IAIOBCallbackService;
import org.springframework.stereotype.Service;
/**
* @description: 百度外呼回调service实现类
* @author: haown
* @create: 2025-12-22 15:22
**/
@Service
public class AIOBCallbackServiceImpl extends ServiceImpl<AIOBCallbackMapper, AIOBCallbackEntity> implements IAIOBCallbackService {
@Resource
private AIOBCallbackMapper aiobCallbackMapper;
@Override public JSONObject taskCallBack(Integer callbackType, TaskCallbackDataDto data) {
JSONObject retObj = new JSONObject();
retObj.fluentPut("code", 200).fluentPut("msg", "success");
if (callbackType == 0 || callbackType == 3) {
// 保存
AIOBCallbackEntity aiobCallbackEntity = new AIOBCallbackEntity();
aiobCallbackEntity.setCallbackType(callbackType);
aiobCallbackEntity.setCallbackData(JSON.toJSONString(data));
aiobCallbackEntity.setReadState(0);
aiobCallbackEntity.setCreateDate(new Date());
aiobCallbackMapper.insert(aiobCallbackEntity);
}
return retObj;
}
}

View File

@ -0,0 +1,36 @@
# 开发环境配置文件
spring:
# 数据库配置
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://8.131.93.145:54081/aiob_callback?useSSL=false&serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&allowPublicKeyRetrieval=true
username: root
password: 1qaz!@#$
# druid相关配置
druid:
max-active: 5000
initial-size: 20
min-idle: 5
async-init: true
# 监控统计
filters: stat,wall
filter:
stat:
log-slow-sql: true
slow-sql-millis: 5000
wall:
config:
create-table-allow: false
alter-table-allow: false
drop-table-allow: false
truncate-allow: false
# 开启文档
swagger:
enable: true
logging:
level:
root: debug
path: logs/${spring.application.name}/

View File

@ -0,0 +1,93 @@
# 独立配置文件可以拿到jar外面跑
spring:
application:
name: yf-exam-lite
profiles:
active: dev
main:
allow-bean-definition-overriding: true
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
default-property-inclusion: non_null
deserialization:
fail_on_unknown_properties: false
parser:
# 允许出现特殊字符和转义符
allow_unquoted_control_chars: true
#允许出现单引号
allow_single_quotes: true
serialization:
fail-on-empty-beans: false
mapper:
# 支持类型转换
allow-coercion-of-scalars: true
# 数据库配置
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/yf_exam_lite?useSSL=false&serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&allowPublicKeyRetrieval=true
username: root
password: root
# druid相关配置
druid:
max-active: 5000
initial-size: 20
min-idle: 5
async-init: true
# 监控统计
filters: stat,wall
filter:
stat:
log-slow-sql: true
slow-sql-millis: 5000
wall:
config:
create-table-allow: false
alter-table-allow: false
drop-table-allow: false
truncate-allow: false
server:
port: 8101
# 启用服务端压缩
compression:
enabled: true
min-response-size: 10
mime-types: application/json,application/xml,text/html,text/xml,text/plain,application/javascript,text/css
# 文件上传配置
conf:
upload:
# 物理文件存储位置,以/结束windows已正斜杠d:/exam-upload/
dir: /data/upload/
# 访问地址,注意不要去除/upload/file/,此节点为虚拟标识符
# 如http://localhost:8101/upload/file/exam.jpg对应物理文件为/data/upload/exam.jpg
url: http://8.131.93.145:54012/upload/file/
# 允许上传的文件后缀
allow-extensions: jpg,jpeg,png
folder:
# 身份证正面存储文件夹名称
card_front_url: cardfront/
# 身份证背面存储文件夹名称
card_back_url: cardback/
# 身份证正反面复印件
card_copy_url: cardcopy/
# 证件照
photo_url: photo/
# 学历证明
certificate_url: certificate/
# 体检报告
physical_report_url: physicalreport/
# 签名图片
sign_picture_url: signpicture/
# 开启文档
swagger:
enable: true
logging:
level:
root: debug
path: logs/${spring.application.name}/

View File

@ -0,0 +1,33 @@
spring:
application:
name: aiob-callback
profiles:
active: dev
main:
allow-bean-definition-overriding: true
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
default-property-inclusion: non_null
deserialization:
fail_on_unknown_properties: false
parser:
# 允许出现特殊字符和转义符
allow_unquoted_control_chars: true
#允许出现单引号
allow_single_quotes: true
serialization:
fail-on-empty-beans: false
mapper:
# 支持类型转换
allow-coercion-of-scalars: true
server:
port: 8101
servlet:
# 应用的访问路径
context-path: /
# 启用服务端压缩
compression:
enabled: true
min-response-size: 10
mime-types: application/json,application/xml,text/html,text/xml,text/plain,application/javascript,text/css

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.example.mapper.AIOBCallbackMapper">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="org.example.entity.AIOBCallbackEntity">
<id column="id" property="id" />
<result column="callback_type" property="callbackType" />
<result column="callback_data" property="callbackData" />
<result column="read_state" property="readState" />
<result column="create_date" property="createDate" />
<result column="update_date" property="updateDate" />
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
id, callback_type, callback_data, read_state, create_date, update_date
</sql>
</mapper>

View File

@ -0,0 +1,36 @@
# 开发环境配置文件
spring:
# 数据库配置
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://8.131.93.145:54081/aiob_callback?useSSL=false&serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&allowPublicKeyRetrieval=true
username: root
password: 1qaz!@#$
# druid相关配置
druid:
max-active: 5000
initial-size: 20
min-idle: 5
async-init: true
# 监控统计
filters: stat,wall
filter:
stat:
log-slow-sql: true
slow-sql-millis: 5000
wall:
config:
create-table-allow: false
alter-table-allow: false
drop-table-allow: false
truncate-allow: false
# 开启文档
swagger:
enable: true
logging:
level:
root: debug
path: logs/${spring.application.name}/

View File

@ -0,0 +1,93 @@
# 独立配置文件可以拿到jar外面跑
spring:
application:
name: yf-exam-lite
profiles:
active: dev
main:
allow-bean-definition-overriding: true
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
default-property-inclusion: non_null
deserialization:
fail_on_unknown_properties: false
parser:
# 允许出现特殊字符和转义符
allow_unquoted_control_chars: true
#允许出现单引号
allow_single_quotes: true
serialization:
fail-on-empty-beans: false
mapper:
# 支持类型转换
allow-coercion-of-scalars: true
# 数据库配置
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/yf_exam_lite?useSSL=false&serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&allowPublicKeyRetrieval=true
username: root
password: root
# druid相关配置
druid:
max-active: 5000
initial-size: 20
min-idle: 5
async-init: true
# 监控统计
filters: stat,wall
filter:
stat:
log-slow-sql: true
slow-sql-millis: 5000
wall:
config:
create-table-allow: false
alter-table-allow: false
drop-table-allow: false
truncate-allow: false
server:
port: 8101
# 启用服务端压缩
compression:
enabled: true
min-response-size: 10
mime-types: application/json,application/xml,text/html,text/xml,text/plain,application/javascript,text/css
# 文件上传配置
conf:
upload:
# 物理文件存储位置,以/结束windows已正斜杠d:/exam-upload/
dir: /data/upload/
# 访问地址,注意不要去除/upload/file/,此节点为虚拟标识符
# 如http://localhost:8101/upload/file/exam.jpg对应物理文件为/data/upload/exam.jpg
url: http://8.131.93.145:54012/upload/file/
# 允许上传的文件后缀
allow-extensions: jpg,jpeg,png
folder:
# 身份证正面存储文件夹名称
card_front_url: cardfront/
# 身份证背面存储文件夹名称
card_back_url: cardback/
# 身份证正反面复印件
card_copy_url: cardcopy/
# 证件照
photo_url: photo/
# 学历证明
certificate_url: certificate/
# 体检报告
physical_report_url: physicalreport/
# 签名图片
sign_picture_url: signpicture/
# 开启文档
swagger:
enable: true
logging:
level:
root: debug
path: logs/${spring.application.name}/

View File

@ -0,0 +1,33 @@
spring:
application:
name: aiob-callback
profiles:
active: dev
main:
allow-bean-definition-overriding: true
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
default-property-inclusion: non_null
deserialization:
fail_on_unknown_properties: false
parser:
# 允许出现特殊字符和转义符
allow_unquoted_control_chars: true
#允许出现单引号
allow_single_quotes: true
serialization:
fail-on-empty-beans: false
mapper:
# 支持类型转换
allow-coercion-of-scalars: true
server:
port: 8101
servlet:
# 应用的访问路径
context-path: /
# 启用服务端压缩
compression:
enabled: true
min-response-size: 10
mime-types: application/json,application/xml,text/html,text/xml,text/plain,application/javascript,text/css

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.example.mapper.AIOBCallbackMapper">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="org.example.entity.AIOBCallbackEntity">
<id column="id" property="id" />
<result column="callback_type" property="callbackType" />
<result column="callback_data" property="callbackData" />
<result column="read_state" property="readState" />
<result column="create_date" property="createDate" />
<result column="update_date" property="updateDate" />
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
id, callback_type, callback_data, read_state, create_date, update_date
</sql>
</mapper>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More