Repository: xuexiangjys/XUpdateService
Branch: master
Commit: 698d68260017
Files: 70
Total size: 36.0 MB
Directory structure:
gitextract_7e1osrry/
├── .gitignore
├── LICENSE
├── README.md
├── build.gradle
├── db/
│ └── generatorConfig.xml
├── generator.gradle
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── libs/
│ └── mysql-connector-java-5.1.35.jar
├── package/
│ └── xupdateservice-1.0.0.jar
├── settings.gradle
├── sql/
│ └── xupdate.sql
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── xuexiang/
│ │ │ └── xupdateservice/
│ │ │ ├── XUpdateServiceApplication.java
│ │ │ ├── api/
│ │ │ │ ├── request/
│ │ │ │ │ ├── ApiRequest.java
│ │ │ │ │ └── PageQuery.java
│ │ │ │ └── response/
│ │ │ │ ├── ApiResult.java
│ │ │ │ ├── LoginInfo.java
│ │ │ │ ├── PageData.java
│ │ │ │ └── UploadFileResponse.java
│ │ │ ├── component/
│ │ │ │ ├── annotation/
│ │ │ │ │ ├── CurrentAccount.java
│ │ │ │ │ ├── LimitedRequest.java
│ │ │ │ │ ├── LoginRequired.java
│ │ │ │ │ └── QuickRequest.java
│ │ │ │ ├── aspect/
│ │ │ │ │ ├── LimitedRequestAspect.java
│ │ │ │ │ └── RestControllerAspect.java
│ │ │ │ ├── interceptor/
│ │ │ │ │ ├── CorsInterceptor.java
│ │ │ │ │ └── QuickRequestInterceptor.java
│ │ │ │ └── token/
│ │ │ │ ├── AuthenticationInterceptor.java
│ │ │ │ └── CurrentAccountMethodArgumentResolver.java
│ │ │ ├── config/
│ │ │ │ ├── Constants.java
│ │ │ │ ├── FileStorageProperties.java
│ │ │ │ └── WebAppConfigurer.java
│ │ │ ├── controller/
│ │ │ │ ├── AccountController.java
│ │ │ │ ├── HomeController.java
│ │ │ │ └── UpdateController.java
│ │ │ ├── exception/
│ │ │ │ ├── ApiException.java
│ │ │ │ ├── ApiExceptionHandler.java
│ │ │ │ ├── FileNotFoundException.java
│ │ │ │ └── FileStorageException.java
│ │ │ ├── mapper/
│ │ │ │ ├── AccountMapper.java
│ │ │ │ └── AppVersionInfoMapper.java
│ │ │ ├── model/
│ │ │ │ ├── Account.java
│ │ │ │ └── AppVersionInfo.java
│ │ │ ├── service/
│ │ │ │ ├── AccountService.java
│ │ │ │ ├── FileStorageService.java
│ │ │ │ ├── UpdateService.java
│ │ │ │ └── impl/
│ │ │ │ ├── AccountServiceImpl.java
│ │ │ │ ├── FileStorageServiceImpl.java
│ │ │ │ └── UpdateServiceImpl.java
│ │ │ └── utils/
│ │ │ ├── AspectJUtils.java
│ │ │ ├── DateUtils.java
│ │ │ ├── FileUtils.java
│ │ │ ├── IpUtils.java
│ │ │ ├── Md5Utils.java
│ │ │ ├── QuickRequestUtils.java
│ │ │ ├── RandomGUID.java
│ │ │ └── TokenUtils.java
│ │ └── resources/
│ │ ├── application.yml
│ │ ├── db-mysql.properties
│ │ ├── mybatis_mapper/
│ │ │ ├── AccountMapper.xml
│ │ │ └── AppVersionInfoMapper.xml
│ │ ├── static/
│ │ │ ├── css/
│ │ │ │ └── main.css
│ │ │ └── js/
│ │ │ └── upload.js
│ │ └── templates/
│ │ └── index.html
│ └── test/
│ └── java/
│ └── com/
│ └── xuexiang/
│ └── xupdateservice/
│ └── XUpdateServiceApplicationTests.java
└── uploads/
├── xupdate_demo_1.0.2.apk
└── xupdate_demo_1.0.apk
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
.gradle
/build/
!gradle/wrapper/gradle-wrapper.jar
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
/out/
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction, and
distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by the copyright
owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all other entities
that control, are controlled by, or are under common control with that entity.
For the purposes of this definition, "control" means (i) the power, direct or
indirect, to cause the direction or management of such entity, whether by
contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity exercising
permissions granted by this License.
"Source" form shall mean the preferred form for making modifications, including
but not limited to software source code, documentation source, and configuration
files.
"Object" form shall mean any form resulting from mechanical transformation or
translation of a Source form, including but not limited to compiled object code,
generated documentation, and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or Object form, made
available under the License, as indicated by a copyright notice that is included
in or attached to the work (an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object form, that
is based on (or derived from) the Work and for which the editorial revisions,
annotations, elaborations, or other modifications represent, as a whole, an
original work of authorship. For the purposes of this License, Derivative Works
shall not include works that remain separable from, or merely link (or bind by
name) to the interfaces of, the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including the original version
of the Work and any modifications or additions to that Work or Derivative Works
thereof, that is intentionally submitted to Licensor for inclusion in the Work
by the copyright owner or by an individual or Legal Entity authorized to submit
on behalf of the copyright owner. For the purposes of this definition,
"submitted" means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems, and
issue tracking systems that are managed by, or on behalf of, the Licensor for
the purpose of discussing and improving the Work, but excluding communication
that is conspicuously marked or otherwise designated in writing by the copyright
owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
of whom a Contribution has been received by Licensor and subsequently
incorporated within the Work.
2. Grant of Copyright License.
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the Work and such
Derivative Works in Source or Object form.
3. Grant of Patent License.
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable (except as stated in this section) patent license to make, have
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
such license applies only to those patent claims licensable by such Contributor
that are necessarily infringed by their Contribution(s) alone or by combination
of their Contribution(s) with the Work to which such Contribution(s) was
submitted. If You institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
Contribution incorporated within the Work constitutes direct or contributory
patent infringement, then any patent licenses granted to You under this License
for that Work shall terminate as of the date such litigation is filed.
4. Redistribution.
You may reproduce and distribute copies of the Work or Derivative Works thereof
in any medium, with or without modifications, and in Source or Object form,
provided that You meet the following conditions:
You must give any other recipients of the Work or Derivative Works a copy of
this License; and
You must cause any modified files to carry prominent notices stating that You
changed the files; and
You must retain, in the Source form of any Derivative Works that You distribute,
all copyright, patent, trademark, and attribution notices from the Source form
of the Work, excluding those notices that do not pertain to any part of the
Derivative Works; and
If the Work includes a "NOTICE" text file as part of its distribution, then any
Derivative Works that You distribute must include a readable copy of the
attribution notices contained within such NOTICE file, excluding those notices
that do not pertain to any part of the Derivative Works, in at least one of the
following places: within a NOTICE text file distributed as part of the
Derivative Works; within the Source form or documentation, if provided along
with the Derivative Works; or, within a display generated by the Derivative
Works, if and wherever such third-party notices normally appear. The contents of
the NOTICE file are for informational purposes only and do not modify the
License. You may add Your own attribution notices within Derivative Works that
You distribute, alongside or as an addendum to the NOTICE text from the Work,
provided that such additional attribution notices cannot be construed as
modifying the License.
You may add Your own copyright statement to Your modifications and may provide
additional or different license terms and conditions for use, reproduction, or
distribution of Your modifications, or for any such Derivative Works as a whole,
provided Your use, reproduction, and distribution of the Work otherwise complies
with the conditions stated in this License.
5. Submission of Contributions.
Unless You explicitly state otherwise, any Contribution intentionally submitted
for inclusion in the Work by You to the Licensor shall be under the terms and
conditions of this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify the terms of
any separate license agreement you may have executed with Licensor regarding
such Contributions.
6. Trademarks.
This License does not grant permission to use the trade names, trademarks,
service marks, or product names of the Licensor, except as required for
reasonable and customary use in describing the origin of the Work and
reproducing the content of the NOTICE file.
7. Disclaimer of Warranty.
Unless required by applicable law or agreed to in writing, Licensor provides the
Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
including, without limitation, any warranties or conditions of TITLE,
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
solely responsible for determining the appropriateness of using or
redistributing the Work and assume any risks associated with Your exercise of
permissions under this License.
8. Limitation of Liability.
In no event and under no legal theory, whether in tort (including negligence),
contract, or otherwise, unless required by applicable law (such as deliberate
and grossly negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special, incidental,
or consequential damages of any character arising as a result of this License or
out of the use or inability to use the Work (including but not limited to
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
any and all other commercial damages or losses), even if such Contributor has
been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability.
While redistributing the Work or Derivative Works thereof, You may choose to
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
other liability obligations and/or rights consistent with this License. However,
in accepting such obligations, You may act only on Your own behalf and on Your
sole responsibility, not on behalf of any other Contributor, and only if You
agree to indemnify, defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason of your
accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work
To apply the Apache License to your work, attach the following boilerplate
notice, with the fields enclosed by brackets "{}" replaced with your own
identifying information. (Don't include the brackets!) The text should be
enclosed in the appropriate comment syntax for the file format. We also
recommend that a file or class name and description of purpose be included on
the same "printed page" as the copyright notice for easier identification within
third-party archives.
Copyright 2018 xuexiangjys
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: README.md
================================================
# XUpdateService
使用Spring Boot简易搭建,Gradle构建,为XUpdate提供的更新服务。
## 内容
* 使用spring boot快速搭建,并使用Gradle进行构建【区别Maven】。
* 使用阿里的druid数据库连接池和tk.mybatis进行数据库的连接。
* 使用MySql作为数据库。
* 提供了应用版本后台管理所需的API服务,使用Html和ajax简单实现了应用版本管理界面。
* 使用了AOP对api请求进行动态日志记录。
* 实现通用的文件上传(包括多文件上传)和下载功能。
* 增加了请求流量以及请求权限的控制。
* 支持浏览器跨域请求。
## 搭建方法
1. clone项目到本地
```
git clone https://github.com/xuexiangjys/XUpdateService.git
```
2.使用IntelliJ IDEA 导入该项目。
3.进行本地数据库的配置。
因为使用的是MySql数据库,如果你电脑上没有安装MySql的话,请先[点击安装](https://www.mysql.com/)。你可以安装`MySQL Community Server`和`MySQL Workbench`,建议下载的MySql版本是5.7。
* MySql安装完成后,请执行根目录下`sql`文件夹下的脚本,创建数据库表和内容。
* 配置`src/main/resources/application.yml`文件,包括服务端口、数据库配置、mybatis配置、文件上传配置等。
* 如果你需要使用mybatis的自动生成代码脚本`generator`,请配置`src/main/resources/db-mysql.properties`文件,然后执行`./gradlew mybatisGenerate`或者在Gradle的Task列表中选择`mybatisGenerate`双击即可。
4.直接运行`XUpdateServiceApplication`即可运行服务。
## 程序打包
1.执行`./gradlew bootJar`或点击Gradle任务栏点击`Tasks` -> `Build` -> `bootJar`.
2.打包后的是jar文件,打包路径:`build/libs/` 下,如下图:

3.最后执行jar包即可.
```
java -jar build/libs/xxxxx.jar
```
4.目前最新的已打包好的jar在项目的`Package`下[xupdateservice-1.0.0.jar](./package/xupdateservice-1.0.0.jar), 运行前请保证你的数据库连接正常。
## 版本更新管理后台
由于使用Java编写web管理后台不是很好看,因此我特地去学习了最近比较火的Vue.js编写了一个简洁优美的管理后台供大家参考。
项目地址: https://github.com/xuexiangjys/xupdate-management
### 项目预览

----------------
## API构成
### 管理接口
#### 1、注册APK的版本信息
* 请求类型: post
* url : /update/addVersionInfo
* 参数 :
```
{
"updateStatus":2,
"modifyContent":"1、优化api接口。\r\n2、添加使用demo演示。\r\n3、新增自定义更新服务API接口。\r\n4、优化更新提示界面。",
"appKey":"test",
"versionName":"1.0.3",
"versionCode":4
}
```
* 响应 :
```
{
"Msg":"",
"Code":0,
"Data":{
"versionId":12,
"updateStatus":2,
"modifyContent":"1、优化api接口。\r\n2、添加使用demo演示。\r\n3、新增自定义更新服务API接口。\r\n4、优化更新提示界面。",
"appKey":"test",
"versionName":"1.0.3",
"versionCode":4
}
}
```
#### 2、上传APK
* 请求类型: post【multipart/form-data】
* url : /update/uploadApk
* 参数 :
```
file=[文件]
versionId=12
```
* 响应 :
```
{
"Code":0,
"Msg":"",
"Data":true
}
```
#### 3、添加版本信息
* 请求类型: post【multipart/form-data】
* url : /update/addAppVersion
* 参数 :
```
file=[文件]
appVersionInfo= {
"updateStatus":2,
"modifyContent":"1、优化api接口。\r\n2、添加使用demo演示。\r\n3、新增自定义更新服务API接口。\r\n4、优化更新提示界面。",
"appKey":"test",
"versionName":"1.0.3",
"versionCode":4
}
```
* 响应 :
```
{
"Code":0,
"Msg":"",
"Data":true
}
```
### 版本更新接口
#### 1、版本信息检查
* 请求类型: post
* url : /update/checkVersion
* 参数 :
```
versionCode=1,
appKey=com.xuexiang.xupdatedemo
```
* 响应 :
```
{
"Msg":"",
"Code":0,
"Data":{
"apkMd5":"E4B79A36EFB9F17DF7E3BB161F9BCFD8",
"versionId":11,
"updateStatus":1,
"downloadUrl":"xupdate_demo_1.0.2.apk",
"modifyContent":"1、优化api接口。\r\n2、添加使用demo演示。\r\n3、新增自定义更新服务API接口。\r\n4、优化更新提示界面。",
"appKey":"com.xuexiang.xupdatedemo",
"apkSize":1649,
"uploadTime":"2018-07-30 09:47:25",
"versionName":"1.23.4",
"versionCode":34
}
}
```
#### 2、最新版本下载
* 请求类型: get
* url : /update/apk/{fileName:.+}
* 响应 : 文件流
================================================
FILE: build.gradle
================================================
plugins {
id 'org.springframework.boot' version '2.4.1'
id 'io.spring.dependency-management' version '1.0.10.RELEASE'
id 'java'
}
group = 'com.xuexiang'
version = '1.0.0'
sourceCompatibility = 1.8
repositories {
maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
}
configurations {
mybatisGenerator
}
dependencies {
//web服务
implementation('org.springframework.boot:spring-boot-starter-web')
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.4'
testImplementation('org.springframework.boot:spring-boot-starter-test')
//物理分页
implementation 'com.github.pagehelper:pagehelper-spring-boot-starter:1.3.0'
//数据库连接
implementation('org.springframework.boot:spring-boot-starter-jdbc')
implementation 'mysql:mysql-connector-java:8.0.22'
implementation 'com.alibaba:fastjson:1.2.75'
implementation 'com.alibaba:druid:1.2.4'
implementation 'com.alibaba:druid-spring-boot-starter:1.2.4'
// token令牌
implementation 'io.jsonwebtoken:jjwt:0.9.1'
//AOP
implementation 'org.aspectj:aspectjweaver:1.9.6'
implementation('org.assertj:assertj-core')
implementation 'org.apache.commons:commons-lang3:3.4'
implementation 'org.apache.commons:commons-collections4:4.1'
//mybatis自动生成代码
mybatisGenerator 'org.mybatis.generator:mybatis-generator-core:1.3.7'
mybatisGenerator 'tk.mybatis:mapper:4.1.5'
implementation 'tk.mybatis:mapper-spring-boot-starter:2.1.5'
//网页加载
implementation('org.springframework.boot:spring-boot-starter-thymeleaf')
}
jar {
enabled = true
}
apply from: './generator.gradle'
================================================
FILE: db/generatorConfig.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!-- 数据库驱动:选择你的本地硬盘上面的数据库驱动包-->
<classPathEntry location="${classPath}"/>
<context id="Mysql" targetRuntime="MyBatis3Simple" defaultModelType="flat">
<plugin type="tk.mybatis.mapper.generator.MapperPlugin">
<property name="mappers" value="tk.mybatis.mapper.common.Mapper"/>
<!-- caseSensitive默认false,当数据库表名区分大小写时,可以将该属性设置为true -->
<property name="caseSensitive" value="true"/>
</plugin>
<commentGenerator>
<property name="suppressAllComments" value="true"/>
</commentGenerator>
<jdbcConnection driverClass="${driverClass}"
connectionURL="${connectionURL}"
userId="${userId}"
password="${password}">
</jdbcConnection>
<javaModelGenerator targetPackage="${modelPackage}" targetProject="${src_main_java}"/>
<sqlMapGenerator targetPackage="${sqlMapperPackage}" targetProject="${src_main_resources}"/>
<javaClientGenerator targetPackage="${mapperPackage}" targetProject="${src_main_java}" type="XMLMAPPER"/>
<!-- sql占位符,表示所有的表 -->
<table tableName="%"/>
</context>
</generatorConfiguration>
================================================
FILE: generator.gradle
================================================
def getDbProperties = {
def properties = new Properties()
file("src/main/resources/db-mysql.properties").withInputStream { inputStream ->
properties.load(inputStream)
}
properties;
}
task mybatisGenerate {
def properties = getDbProperties()
ant.properties['targetProject'] = projectDir.path
ant.properties['classPath'] = properties.getProperty("classPath")
ant.properties['driverClass'] = properties.getProperty("jdbc.driverClassName")
ant.properties['connectionURL'] = properties.getProperty("jdbc.url")
ant.properties['userId'] = properties.getProperty("jdbc.user")
ant.properties['password'] = properties.getProperty("jdbc.pass")
ant.properties['src_main_java'] = sourceSets.main.java.srcDirs[0].path
ant.properties['src_main_resources'] = sourceSets.main.resources.srcDirs[0].path
ant.properties['modelPackage'] = this.modelPackage
ant.properties['mapperPackage'] = this.mapperPackage
ant.properties['sqlMapperPackage'] = this.sqlMapperPackage
ant.taskdef(
name: 'mbgenerator',
classname: 'org.mybatis.generator.ant.GeneratorAntTask',
classpath: configurations.mybatisGenerator.asPath
)
ant.mbgenerator(overwrite: true,
configfile: 'db/generatorConfig.xml', verbose: true) {
propertyset {
propertyref(name: 'targetProject')
propertyref(name: 'classPath')
propertyref(name: 'driverClass')
propertyref(name: 'connectionURL')
propertyref(name: 'userId')
propertyref(name: 'password')
propertyref(name: 'src_main_java')
propertyref(name: 'src_main_resources')
propertyref(name: 'modelPackage')
propertyref(name: 'mapperPackage')
propertyref(name: 'sqlMapperPackage')
}
}
}
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
================================================
FILE: gradle.properties
================================================
modelPackage=com.xuexiang.xupdateservice.model
#生成的mapper接口类所在包
mapperPackage=com.xuexiang.xupdateservice.mapper
#生成的mapper xml文件所在包,默认存储在resources目录下
sqlMapperPackage=mybatis_mapper
================================================
FILE: gradlew
================================================
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save ( ) {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"
================================================
FILE: gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: package/xupdateservice-1.0.0.jar
================================================
[File too large to display: 35.8 MB]
================================================
FILE: settings.gradle
================================================
rootProject.name = 'xupdateservice'
================================================
FILE: sql/xupdate.sql
================================================
-- MySQL dump 10.13 Distrib 5.7.17, for Win64 (x86_64)
--
-- Host: localhost Database: xupdate
-- ------------------------------------------------------
-- Server version 5.7.25-log
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
--
-- Table structure for table `account`
--
DROP TABLE IF EXISTS `account`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `account` (
`account_id` int(11) NOT NULL AUTO_INCREMENT,
`login_name` varchar(45) NOT NULL DEFAULT 'admin',
`password` varchar(45) NOT NULL DEFAULT '123456',
`nick` varchar(45) NOT NULL DEFAULT 'admin',
`authority` varchar(45) NOT NULL DEFAULT 'admin',
`avatar` varchar(200) DEFAULT NULL,
`phone` char(11) DEFAULT NULL,
`address` varchar(60) DEFAULT NULL,
`register_time` datetime DEFAULT NULL,
PRIMARY KEY (`account_id`),
UNIQUE KEY `account_id_UNIQUE` (`account_id`),
UNIQUE KEY `login_name_UNIQUE` (`login_name`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `account`
--
LOCK TABLES `account` WRITE;
/*!40000 ALTER TABLE `account` DISABLE KEYS */;
INSERT INTO `account` VALUES (1,'admin','123456','admin','admin','https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif','13513957542','南京市江宁区','2018-05-06 00:00:00'),(2,'xuexiang','123456','薛翔','admin','https://raw.githubusercontent.com/xuexiangjys/Resource/master/img/avatar/avatar_github.jpg','13913845875','南京市江宁区','2018-12-11 00:00:00');
/*!40000 ALTER TABLE `account` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `app_version_info`
--
DROP TABLE IF EXISTS `app_version_info`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `app_version_info` (
`version_id` int(11) NOT NULL AUTO_INCREMENT,
`update_status` int(11) NOT NULL,
`version_code` int(11) NOT NULL,
`version_name` varchar(45) NOT NULL,
`upload_time` varchar(45) DEFAULT NULL,
`modify_content` longtext,
`download_url` longtext,
`apk_size` int(11) DEFAULT NULL,
`apk_md5` longtext,
`app_key` varchar(45) NOT NULL,
PRIMARY KEY (`version_id`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb4;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `app_version_info`
--
LOCK TABLES `app_version_info` WRITE;
/*!40000 ALTER TABLE `app_version_info` DISABLE KEYS */;
INSERT INTO `app_version_info` VALUES (10,2,24,'1.0.4','2018-07-30 09:36:39','1、优化api接口。\\r\\n2、添加使用demo演示。\\r\\n3、新增自定义更新服务API接口。\\r\\n4、优化更新提示界面。','xupdate_demo_1.0.2.apk',1697,'E4B79A36EFB9F17DF7E3BB161F9BCFD8','test3'),(11,1,34,'1.23.4','2018-07-30 09:47:25','1、优化api接口。\\r\\n2、添加使用demo演示。\\r\\n3、新增自定义更新服务API接口。\\r\\n4、优化更新提示界面。','xupdate_demo_1.0.2.apk',1649,'E4B79A36EFB9F17DF7E3BB161F9BCFD8','com.xuexiang.xupdatedemo'),(12,1,4,'1.0.3','2018-07-30 10:52:53','1、优化api接口。\\r\\n2、添加使用demo演示。\\r\\n3、新增自定义更新服务API接口。\\r\\n4、优化更新提示界面。','xupdate_demo_1.0.2.apk',1649,'E4B79A36EFB9F17DF7E3BB161F9BCFD8','com.xuexiang.xupdatedemo'),(13,1,23,'1.2.34',NULL,'........',NULL,NULL,NULL,'test1');
/*!40000 ALTER TABLE `app_version_info` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-- Dump completed on 2019-04-23 9:19:39
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/XUpdateServiceApplication.java
================================================
package com.xuexiang.xupdateservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import tk.mybatis.spring.annotation.MapperScan;
@SpringBootApplication
@MapperScan("com.xuexiang.xupdateservice.mapper")
public class XUpdateServiceApplication {
public static void main(String[] args) {
SpringApplication.run(XUpdateServiceApplication.class, args);
}
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/api/request/ApiRequest.java
================================================
package com.xuexiang.xupdateservice.api.request;
/**
* 基础请求包装类
*
* @author xuexiang
* @since 2018/7/16 下午7:00
*/
public class ApiRequest<T> {
public T request;
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/api/request/PageQuery.java
================================================
package com.xuexiang.xupdateservice.api.request;
/**
* @author xuexiang
* @since 2018/8/15 上午12:26
*/
public class PageQuery {
/**
* 第几页数
*/
public int pageNum;
/**
* 每页的数量
*/
public int pageSize;
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/api/response/ApiResult.java
================================================
/*
* Copyright (C) 2018 xuexiangjys(xuexiangjys@163.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.xuexiang.xupdateservice.api.response;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.xuexiang.xupdateservice.exception.ApiException;
/**
* 提供的默认的标注返回api
*
* @author xuexiang
* @since 2018/5/22 下午4:22
*/
public class ApiResult<T> {
@JsonProperty(value = "code")
private int Code = 0;
@JsonProperty(value = "msg")
private String Msg = "";
@JsonProperty(value = "data")
private T Data;
@JsonIgnore
public int getCode() {
return Code;
}
@JsonIgnore
public ApiResult<T> setCode(int code) {
Code = code;
return this;
}
@JsonIgnore
public String getMsg() {
return Msg;
}
@JsonIgnore
public ApiResult<T> setMsg(String msg) {
Msg = msg;
return this;
}
@JsonIgnore
public T getData() {
return Data;
}
@JsonIgnore
public ApiResult<T> setData(T data) {
Data = data;
return this;
}
@JsonIgnore
public ApiResult setError(int code, String msg) {
Code = code;
Msg = msg;
return this;
}
@Override
public String toString() {
return "ApiResult{" +
"Code='" + Code + '\'' +
", Msg='" + Msg + '\'' +
", Data=" + Data +
'}';
}
/**
* 获取出错返回
*
* @param ex
* @return
*/
public static ApiResult error(ApiException ex) {
ApiResult apiResult = new ApiResult();
apiResult.setError(ex.getCode(), ex.getMessage());
return apiResult;
}
/**
* 获取出错返回
*
* @param code
* @param msg
* @return
*/
public static ApiResult error(int code, String msg) {
ApiResult apiResult = new ApiResult();
apiResult.setError(code, msg);
return apiResult;
}
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/api/response/LoginInfo.java
================================================
package com.xuexiang.xupdateservice.api.response;
import com.xuexiang.xupdateservice.model.Account;
/**
* @author xuexiang
* @since 2018/8/6 下午6:14
*/
public class LoginInfo {
private Account account;
private String token;
public Account getUser() {
return account;
}
public LoginInfo setUser(Account account) {
this.account = account;
return this;
}
public String getToken() {
return token;
}
public LoginInfo setToken(String token) {
this.token = token;
return this;
}
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/api/response/PageData.java
================================================
package com.xuexiang.xupdateservice.api.response;
import java.util.List;
/**
* 分页请求的响应数据
*
* @author xuexiang
* @since 2019/4/25 下午11:52
*/
public class PageData<T> {
private List<T> array;
private long total;
private int pageNum;
private int pageSize;
public PageData() {
}
public PageData(int pageNum, int pageSize) {
this.pageNum = pageNum;
this.pageSize = pageSize;
}
public List<T> getArray() {
return array;
}
public PageData<T> setArray(List<T> array) {
this.array = array;
return this;
}
public long getTotal() {
return total;
}
public PageData<T> setTotal(long total) {
this.total = total;
return this;
}
public int getPageNum() {
return pageNum;
}
public PageData<T> setPageNum(int pageNum) {
this.pageNum = pageNum;
return this;
}
public int getPageSize() {
return pageSize;
}
public PageData<T> setPageSize(int pageSize) {
this.pageSize = pageSize;
return this;
}
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/api/response/UploadFileResponse.java
================================================
package com.xuexiang.xupdateservice.api.response;
/**
* 文件上传返回结果
*
* @author xuexiang
* @since 2018/7/18 下午3:55
*/
public class UploadFileResponse {
private String fileName;
private String fileDownloadUri;
private String fileType;
private long size;
public UploadFileResponse(String fileName, String fileDownloadUri, String fileType, long size) {
this.fileName = fileName;
this.fileDownloadUri = fileDownloadUri;
this.fileType = fileType;
this.size = size;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public String getFileDownloadUri() {
return fileDownloadUri;
}
public void setFileDownloadUri(String fileDownloadUri) {
this.fileDownloadUri = fileDownloadUri;
}
public String getFileType() {
return fileType;
}
public void setFileType(String fileType) {
this.fileType = fileType;
}
public long getSize() {
return size;
}
public void setSize(long size) {
this.size = size;
}
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/component/annotation/CurrentAccount.java
================================================
package com.xuexiang.xupdateservice.component.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 在Controller的方法参数中使用此注解,该方法在映射时会注入当前登录的Account对象
*
* @author xuexiang
* @since 2018/8/6 下午4:36
*/
@Target(ElementType.PARAMETER) // 可用在方法的参数上
@Retention(RetentionPolicy.RUNTIME) // 运行时有效
public @interface CurrentAccount {
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/component/annotation/LimitedRequest.java
================================================
package com.xuexiang.xupdateservice.component.annotation;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import java.lang.annotation.*;
/**
* 受控的请求【控制一段时间内请求的次数】
*
* @author xuexiang
* @since 2018/8/7 下午1:59
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
//最高优先级
@Order(Ordered.HIGHEST_PRECEDENCE)
public @interface LimitedRequest {
int DEFAULT_COUNT = 3;
int DEFAULT_INTERVAL = 30 * 1000;
/**
* 允许访问的次数,默认值3
*/
int count() default DEFAULT_COUNT;
/**
*
* 时间段,单位为毫秒,默认值30s
*/
long interval() default DEFAULT_INTERVAL;
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/component/annotation/LoginRequired.java
================================================
package com.xuexiang.xupdateservice.component.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 在需要登录验证的Controller的方法上使用此注解
*
* @author xuexiang
* @since 2018/8/6 下午4:37
*/
@Target({ElementType.METHOD})// 可用在方法名上
@Retention(RetentionPolicy.RUNTIME)// 运行时有效
public @interface LoginRequired {
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/component/annotation/QuickRequest.java
================================================
package com.xuexiang.xupdateservice.component.annotation;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author xuexiang
* @since 2018/8/7 下午3:39
*/
@Target({ElementType.METHOD})// 可用在方法名上
@Retention(RetentionPolicy.RUNTIME)// 运行时有效
//最高优先级
@Order(Ordered.HIGHEST_PRECEDENCE)
public @interface QuickRequest {
/**
* 默认请求间隔为5s
*/
int DEFAULT_INTERVAL = 5 * 1000;
/**
*
* 请求之间的间隔,默认值5s
*/
long interval() default DEFAULT_INTERVAL;
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/component/aspect/LimitedRequestAspect.java
================================================
package com.xuexiang.xupdateservice.component.aspect;
import com.xuexiang.xupdateservice.component.annotation.LimitedRequest;
import com.xuexiang.xupdateservice.exception.ApiException;
import com.xuexiang.xupdateservice.utils.AspectJUtils;
import com.xuexiang.xupdateservice.utils.IpUtils;
import com.xuexiang.xupdateservice.utils.QuickRequestUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import static com.xuexiang.xupdateservice.exception.ApiException.ERROR.REQUEST_BEYOND_LIMIT;
/**
* 请求次数控制拦截器(对{@link LimitedRequest}进行拦截)
*
* @author xuexiang
* @since 2018/8/7 下午2:06
*/
@Aspect
@Component
public class LimitedRequestAspect {
@Before("@within(org.springframework.web.bind.annotation.RestController) && @annotation(limit)")
public void requestLimit(final JoinPoint joinPoint, LimitedRequest limit) throws Exception {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String ip = IpUtils.getRealIp(request);
String url = request.getRequestURL().toString();
String methodName = AspectJUtils.getMethodName(joinPoint);
String key = "requestLimit_".concat(url).concat(ip).concat(methodName);
if (QuickRequestUtils.isQuickRequest(key, limit)) {
throw new ApiException("请求过于频繁!", REQUEST_BEYOND_LIMIT);
}
}
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/component/aspect/RestControllerAspect.java
================================================
package com.xuexiang.xupdateservice.component.aspect;
import com.xuexiang.xupdateservice.utils.AspectJUtils;
import com.xuexiang.xupdateservice.utils.IpUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
/**
* 请求api日志记录(对{@link RestController}进行拦截)
*
* @author xuexiang
* @since 2018/7/17 上午10:42
*/
@Aspect
@Component
public class RestControllerAspect {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* 环绕通知
* @param joinPoint 连接点
* @return 切入点返回值
* @throws Throwable 异常信息
*/
@Around("@within(org.springframework.web.bind.annotation.RestController) || @annotation(org.springframework.web.bind.annotation.RestController)")
public Object apiLog(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
boolean logFlag = this.needToLog(method);
if (!logFlag) {
return joinPoint.proceed();
}
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String userAgent = request.getHeader("user-agent");
String ip = IpUtils.getRealIp(request);
String methodName = AspectJUtils.getMethodName(joinPoint);
String params = AspectJUtils.getMethodParams(joinPoint);
logger.info("\n\r" +
"---------->|开始请求方法:{} \n\r" +
" |请求参数:{} \n\r" +
" |IP:{} \n\r" +
" |userAgent:{}", methodName, params, ip, userAgent);
long start = System.currentTimeMillis();
Object result = joinPoint.proceed();
long end = System.currentTimeMillis();
String deleteSensitiveContent = AspectJUtils.deleteSensitiveContent(result);
logger.info("\n\r" +
"<----------|结束请求方法:{}\n\r" +
" |返回结果{} \n\r" +
" |耗时:{}毫秒 ", methodName, deleteSensitiveContent, end - start);
return result;
}
/**
* 判断是否需要记录日志
*/
private boolean needToLog(Method method) {
return method.getAnnotation(GetMapping.class) == null; //不打印Get请求
}
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/component/interceptor/CorsInterceptor.java
================================================
package com.xuexiang.xupdateservice.component.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 跨域支持拦截器
*
* @author xuexiang
* @since 2019/4/21 上午1:46
*/
public class CorsInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
System.out.println("----------【跨域支持拦截器】-----------");
httpServletResponse.setHeader("Access-Control-Allow-Origin","*");
httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true");
httpServletResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, PATCH, DELETE, PUT");
httpServletResponse.setHeader("Access-Control-Max-Age", "3600");
httpServletResponse.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/component/interceptor/QuickRequestInterceptor.java
================================================
package com.xuexiang.xupdateservice.component.interceptor;
import com.xuexiang.xupdateservice.component.annotation.LoginRequired;
import com.xuexiang.xupdateservice.component.annotation.QuickRequest;
import com.xuexiang.xupdateservice.exception.ApiException;
import com.xuexiang.xupdateservice.utils.IpUtils;
import com.xuexiang.xupdateservice.utils.QuickRequestUtils;
import com.xuexiang.xupdateservice.utils.TokenUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import static com.xuexiang.xupdateservice.exception.ApiException.ERROR.REQUEST_BEYOND_LIMIT;
/**
* 快速请求拦截器【根据请求传入的时间戳来判断】
*
* @author xuexiang
* @since 2018/8/7 下午3:36
*/
public class QuickRequestInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse httpServletResponse, Object handler) throws Exception {
System.out.println("----------【快速请求拦截器】-----------");
// 如果不是映射到方法直接通过
if (!(handler instanceof HandlerMethod)) {
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
// 判断接口是否需要登录
QuickRequest quickRequest = method.getAnnotation(QuickRequest.class);
if (quickRequest == null) { //没有 @QuickRequest 注解,无需要校验
return true;
}
// 获取客户端请求携带的时间戳
String timeStamp = request.getHeader("X-TimeStamp");
if (StringUtils.isEmpty(timeStamp)) {
timeStamp = request.getParameter("timeStamp");
if (StringUtils.isEmpty(timeStamp)) { //如果没有携带时间戳,也无需校验
return true;
}
}
String identity = IpUtils.getRealIp(request); //身份默认使用请求的ip地址
//如果注释有需要登录验证,就使用token作为身份
LoginRequired loginRequired = method.getAnnotation(LoginRequired.class);
if (loginRequired != null) { //没有 @LoginRequired 注解,无需认证
String accessToken = TokenUtils.parseToken(request);
if (!StringUtils.isEmpty(accessToken)) {
identity = accessToken;
}
}
String url = request.getRequestURL().toString();
String methodName = method.getName();
String key = "QuickRequest_".concat(url).concat(methodName).concat(identity);
if (QuickRequestUtils.isQuickRequest(key, quickRequest, timeStamp)) {
throw new ApiException("请求过于频繁,请稍后再试!", REQUEST_BEYOND_LIMIT);
}
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/component/token/AuthenticationInterceptor.java
================================================
package com.xuexiang.xupdateservice.component.token;
import com.xuexiang.xupdateservice.component.annotation.LoginRequired;
import com.xuexiang.xupdateservice.config.Constants;
import com.xuexiang.xupdateservice.exception.ApiException;
import com.xuexiang.xupdateservice.model.Account;
import com.xuexiang.xupdateservice.service.AccountService;
import com.xuexiang.xupdateservice.utils.TokenUtils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.SignatureException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import static com.xuexiang.xupdateservice.exception.ApiException.ERROR.*;
/**
* 用户认证拦截器
*
* @author xuexiang
* @since 2018/8/6 下午4:40
*/
public class AuthenticationInterceptor implements HandlerInterceptor {
@Autowired
private AccountService accountService;
// 在业务处理器处理请求之前被调用
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("----------【用户认证拦截器】-----------");
// 如果不是映射到方法直接通过
if (!(handler instanceof HandlerMethod)) {
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
// 判断接口是否需要登录
LoginRequired loginRequired = method.getAnnotation(LoginRequired.class);
if (loginRequired == null) { //没有 @LoginRequired 注解,无需认证
return true;
}
// 判断是否存在令牌信息,如果存在,则允许登录
String accessToken = TokenUtils.parseToken(request);
if (StringUtils.isEmpty(accessToken)) {
throw new ApiException("未携带token,请先进行登录", TOKEN_MISSING);
}
// 从Redis 中查看 token 是否过期
Claims claims;
try {
claims = TokenUtils.parseJWT(accessToken);
} catch (ExpiredJwtException e) {
throw new ApiException("token失效,请重新登录", TOKEN_INVALID);
} catch (SignatureException se) {
throw new ApiException("token令牌错误", AUTH_ERROR);
}
String loginName = claims.getId();
Account account = accountService.checkAccount(loginName);
if (account == null) {
throw new ApiException("用户不存在,请重新登录", TOKEN_INVALID);
}
// 当前登录用户@CurrentAccount
request.setAttribute(Constants.CURRENT_ACCOUNT, account);
return true;
}
// 请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
// 在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/component/token/CurrentAccountMethodArgumentResolver.java
================================================
package com.xuexiang.xupdateservice.component.token;
import com.xuexiang.xupdateservice.component.annotation.CurrentAccount;
import com.xuexiang.xupdateservice.config.Constants;
import com.xuexiang.xupdateservice.model.Account;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.multipart.support.MissingServletRequestPartException;
/**
* 已登录账户自定义参数解析器
*
* @author xuexiang
* @since 2018/8/6 下午5:09
*/
public class CurrentAccountMethodArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterType().isAssignableFrom(Account.class)//判断是否能转成Account类型
&& parameter.hasParameterAnnotation(CurrentAccount.class);//是否有CurrentAccount注解
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
Account account = (Account) nativeWebRequest.getAttribute(Constants.CURRENT_ACCOUNT, RequestAttributes.SCOPE_REQUEST);
if (account != null) {
return account;
}
throw new MissingServletRequestPartException(Constants.CURRENT_ACCOUNT);
}
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/config/Constants.java
================================================
package com.xuexiang.xupdateservice.config;
/**
* @author xuexiang
* @since 2018/8/6 下午5:07
*/
public class Constants {
/**
* 当前用户参数名
*/
public final static String CURRENT_ACCOUNT = "CurrentAccount";
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/config/FileStorageProperties.java
================================================
package com.xuexiang.xupdateservice.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @author xuexiang
* @since 2018/7/18 下午2:15
*/
@Component
@ConfigurationProperties(prefix = "upload")
public class FileStorageProperties {
private String fileDirectory;
private boolean keepName;
public boolean isKeepName() {
return keepName;
}
public void setKeepName(boolean keepName) {
this.keepName = keepName;
}
public String getFileDirectory() {
return fileDirectory;
}
public void setFileDirectory(String fileDirectory) {
this.fileDirectory = fileDirectory;
}
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/config/WebAppConfigurer.java
================================================
package com.xuexiang.xupdateservice.config;
import com.xuexiang.xupdateservice.component.interceptor.QuickRequestInterceptor;
import com.xuexiang.xupdateservice.component.token.AuthenticationInterceptor;
import com.xuexiang.xupdateservice.component.token.CurrentAccountMethodArgumentResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.*;
import java.util.List;
/**
* 配置URLInterceptor拦截器,以及拦截路径
*
* @author xuexiang
* @since 2018/8/6 下午5:50
*/
@EnableWebMvc
@Configuration
public class WebAppConfigurer extends WebMvcConfigurerAdapter {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authenticationInterceptor());
registry.addInterceptor(quickRequestInterceptor());
super.addInterceptors(registry);
}
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(currentAccountMethodArgumentResolver());
super.addArgumentResolvers(argumentResolvers);
}
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*")
.allowCredentials(true)
.allowedHeaders("Origin", "X-Requested-With", "Content-Type", "Accept", "X-Token", "X-TimeStamp")
.allowedMethods("GET", "POST", "PATCH", "DELETE", "PUT")
.maxAge(3600);
super.addCorsMappings(registry);
}
@Bean
public CurrentAccountMethodArgumentResolver currentAccountMethodArgumentResolver() {
return new CurrentAccountMethodArgumentResolver();
}
@Bean
public AuthenticationInterceptor authenticationInterceptor() {
return new AuthenticationInterceptor();
}
@Bean
public QuickRequestInterceptor quickRequestInterceptor() {
return new QuickRequestInterceptor();
}
/**
* 资源列表
*/
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
"classpath:/META-INF/resources/", "classpath:/resources/",
"classpath:/static/", "classpath:/public/"};
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!registry.hasMappingForPattern("/webjars/**")) {
registry.addResourceHandler("/webjars/**").addResourceLocations(
"classpath:/META-INF/resources/webjars/");
}
if (!registry.hasMappingForPattern("/**")) {
registry.addResourceHandler("/**").addResourceLocations(
CLASSPATH_RESOURCE_LOCATIONS);
}
}
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/controller/AccountController.java
================================================
package com.xuexiang.xupdateservice.controller;
import com.xuexiang.xupdateservice.api.request.PageQuery;
import com.xuexiang.xupdateservice.api.response.ApiResult;
import com.xuexiang.xupdateservice.api.response.LoginInfo;
import com.xuexiang.xupdateservice.component.annotation.CurrentAccount;
import com.xuexiang.xupdateservice.component.annotation.LoginRequired;
import com.xuexiang.xupdateservice.exception.ApiException;
import com.xuexiang.xupdateservice.model.Account;
import com.xuexiang.xupdateservice.service.AccountService;
import com.xuexiang.xupdateservice.utils.TokenUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Date;
import static com.xuexiang.xupdateservice.exception.ApiException.ERROR.COMMON_BUSINESS_ERROR;
/**
* 账户管理服务api
*
* @author xuexiang
* @since 2019/4/21 上午12:24
*/
@RestController
@RequestMapping(value = "/account")
public class AccountController {
@Autowired
private AccountService accountService;
@ResponseBody
@RequestMapping(value = "/accountPageQuery", method = RequestMethod.POST)
public ApiResult pageQueryAccounts(@RequestBody PageQuery pageQuery) throws Exception {
return new ApiResult<>().setData(accountService.getAllAccount(pageQuery.pageNum, pageQuery.pageSize));
}
@ResponseBody
@RequestMapping(value = "/accounts", method = RequestMethod.GET)
public ApiResult getAllAccount() throws Exception {
return new ApiResult<>().setData(accountService.getAllAccount());
}
@ResponseBody
@RequestMapping(value = "/login", method = RequestMethod.POST)
public ApiResult login(@RequestBody Account account) throws Exception {
if (accountService.checkAccount(account.getLoginName()) == null) {
throw new ApiException("账号不存在!", COMMON_BUSINESS_ERROR);
}
Account user = accountService.loginAccount(account.getLoginName(), account.getPassword());
if (user != null) {
ApiResult<LoginInfo> apiResult = new ApiResult<>();
apiResult.setData(new LoginInfo()
.setUser(user)
.setToken(TokenUtils.createJwtToken(user.getLoginName())));
return apiResult;
} else {
throw new ApiException("用户名或密码错误!", COMMON_BUSINESS_ERROR);
}
}
@LoginRequired
@ResponseBody
@RequestMapping(value = "/info", method = RequestMethod.GET)
public ApiResult getCurrentAccount(@CurrentAccount Account account) throws Exception {
return new ApiResult<Account>().setData(account);
}
@LoginRequired
@ResponseBody
@RequestMapping(value = "/logout", method = RequestMethod.POST)
public ApiResult logout() throws Exception {
//清理用户数据
return new ApiResult<Boolean>().setData(true);
}
@ResponseBody
@RequestMapping(value = "/checkExist", method = RequestMethod.POST)
public ApiResult checkAccountExist(String loginName) throws Exception {
return new ApiResult<Boolean>().setData(accountService.checkAccount(loginName) != null);
}
@ResponseBody
@RequestMapping(value = "/register", method = RequestMethod.POST)
public ApiResult register(@RequestBody Account account) throws Exception {
if (accountService.checkAccount(account.getLoginName()) != null) {
throw new ApiException("账号已存在!", COMMON_BUSINESS_ERROR);
}
account.setRegisterTime(new Date());
return new ApiResult<Boolean>().setData(accountService.registerAccount(account));
}
@ResponseBody
@RequestMapping(value = "/delete")
public ApiResult deleteAccount(@RequestBody Account account) throws Exception {
return new ApiResult<Boolean>().setData(accountService.deleteAccount(account.getAccountId()));
}
@ResponseBody
@RequestMapping(value = "/updateInfo")
public ApiResult updateAccountInfo(@RequestBody Account account) throws Exception {
return new ApiResult<Boolean>().setData(accountService.updateAccount(account));
}
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/controller/HomeController.java
================================================
package com.xuexiang.xupdateservice.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class HomeController {
@GetMapping(value = {"/", "/index"})
public String index() {
return "index";
}
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/controller/UpdateController.java
================================================
package com.xuexiang.xupdateservice.controller;
import com.xuexiang.xupdateservice.api.request.PageQuery;
import com.xuexiang.xupdateservice.api.response.ApiResult;
import com.xuexiang.xupdateservice.model.AppVersionInfo;
import com.xuexiang.xupdateservice.service.FileStorageService;
import com.xuexiang.xupdateservice.service.UpdateService;
import com.xuexiang.xupdateservice.utils.DateUtils;
import com.xuexiang.xupdateservice.utils.FileUtils;
import com.xuexiang.xupdateservice.utils.Md5Utils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
/**
* 版本更新api
*
* @author xuexiang
* @since 2018/7/23 下午6:21
*/
@RestController
@RequestMapping(value = "/update")
public class UpdateController {
private static final int SERVICE_ERROR_CODE = 5000;
private static final Logger logger = LoggerFactory.getLogger(UpdateController.class);
@Autowired
private UpdateService updateService;
@Autowired
private FileStorageService fileService;
@ResponseBody
@RequestMapping(value = "/checkVersion", method = RequestMethod.POST)
public ApiResult doCheckVersion(int versionCode, String appKey) {
return new ApiResult<AppVersionInfo>().setData(updateService.getLatestAppVersionInfo(versionCode, appKey));
}
@ResponseBody
@RequestMapping(value = "/versionPageQuery", method = RequestMethod.POST)
public ApiResult pageQueryVersions(@RequestBody PageQuery pageQuery) {
return new ApiResult<>().setData(updateService.getAllAppVersionInfo(pageQuery.pageNum, pageQuery.pageSize));
}
@ResponseBody
@RequestMapping(value = "/versions", method = RequestMethod.GET)
public ApiResult getAllVersions() {
return new ApiResult<>().setData(updateService.getAllAppVersionInfo());
}
@ResponseBody
@RequestMapping(value = "/newVersion", method = RequestMethod.POST)
public ApiResult register(@RequestBody AppVersionInfo appVersionInfo) throws Exception {
return addNewAppVersion(appVersionInfo);
}
@ResponseBody
@RequestMapping(value = "/delete")
public ApiResult deleteAppVersionInfo(@RequestBody AppVersionInfo appVersionInfo) throws Exception {
return new ApiResult<Boolean>().setData(updateService.deleteAppVersionInfo(appVersionInfo.getVersionId()));
}
@ResponseBody
@RequestMapping(value = "/updateInfo")
public ApiResult updateAppVersionInfo(@RequestBody AppVersionInfo appVersionInfo) throws Exception {
return new ApiResult<Boolean>().setData(updateService.updateAppVersionInfo(appVersionInfo));
}
@ResponseBody
@RequestMapping(value = "/addVersionInfo", method = RequestMethod.POST)
public ApiResult addAppVersionInfo(AppVersionInfo appVersionInfo) {
return addNewAppVersion(appVersionInfo);
}
/**
* 添加新版本
* @param appVersionInfo
* @return
*/
private ApiResult addNewAppVersion(AppVersionInfo appVersionInfo) {
ApiResult<AppVersionInfo> result = new ApiResult<>();
if (updateService.getAppVersionInfo(appVersionInfo.getVersionCode(), appVersionInfo.getAppKey()) != null) {
return getOnErrorApiResult(result, "该版本信息已存在!");
} else {
if (updateService.addAppVersionInfo(appVersionInfo)) {
return result.setData(updateService.getAppVersionInfo(appVersionInfo.getVersionCode(), appVersionInfo.getAppKey()));
} else {
return getOnErrorApiResult(result, "版本信息添加失败!");
}
}
}
/**
* 上传apk文件
*
* @param file apk文件
* @param versionId apk的版本id
* @return
*/
@PostMapping("/uploadApk")
public ApiResult uploadApkFile(MultipartFile file, int versionId) {
ApiResult<Boolean> result = new ApiResult<>();
try {
String fileName = fileService.storeFile(file);
if (!StringUtils.isEmpty(fileName)) { //更新apk信息
AppVersionInfo appVersionInfo = new AppVersionInfo();
appVersionInfo.setVersionId(versionId);
updateVersionInfo(fileName, appVersionInfo);
result.setData(updateService.updateAppVersionInfo(appVersionInfo));
} else {
result.setCode(SERVICE_ERROR_CODE)
.setMsg("APK上传失败")
.setData(false);
}
} catch (Exception e) {
e.printStackTrace();
result.setCode(SERVICE_ERROR_CODE)
.setMsg(e.getMessage())
.setData(false);
}
return result;
}
private void updateVersionInfo(String fileName, AppVersionInfo appVersionInfo) throws Exception {
File apkFile = fileService.loadFileAsResource(fileName).getFile();
appVersionInfo.setApkMd5(Md5Utils.getFileMD5(apkFile));
appVersionInfo.setApkSize(FileUtils.getApkFileSize(apkFile));
appVersionInfo.setUploadTime(DateUtils.getNowString(DateUtils.yyyyMMddHHmmss.get()));
appVersionInfo.setDownloadUrl(fileName);
}
@ResponseBody
@RequestMapping(value = "/addAppVersion", method = RequestMethod.POST)
public ApiResult addAppVersion(MultipartFile file, AppVersionInfo appVersionInfo) {
ApiResult<String> apiResult = new ApiResult<>();
if (updateService.getAppVersionInfo(appVersionInfo.getVersionCode(), appVersionInfo.getAppKey()) != null) {
return getOnErrorApiResult(apiResult, "该版本信息已存在!");
} else {
boolean result = updateService.addAppVersionInfo(appVersionInfo);
if (result) {
AppVersionInfo newVersion = updateService.getAppVersionInfo(appVersionInfo.getVersionCode(), appVersionInfo.getAppKey());
try {
String fileName = fileService.storeFile(file);
if (!StringUtils.isEmpty(fileName)) { //更新apk信息
updateVersionInfo(fileName, newVersion);
if (updateService.updateAppVersionInfo(newVersion)) {
return apiResult.setData("版本信息添加成功!" );
} else {
return getOnErrorApiResult(apiResult, "Apk信息添加失败!");
}
} else {
return getOnErrorApiResult(apiResult, "APK上传失败:");
}
} catch (Exception e) {
e.printStackTrace();
return getOnErrorApiResult(apiResult, "APK上传失败:" + e.getMessage());
}
} else {
return getOnErrorApiResult(apiResult, "版本信息添加失败!");
}
}
}
private <T> ApiResult<T> getOnErrorApiResult(ApiResult<T> apiResult, String errorMsg) {
return apiResult.setCode(SERVICE_ERROR_CODE).setMsg(errorMsg);
}
@GetMapping("/apk/{fileName:.+}")
public ResponseEntity<Resource> downloadFile(@PathVariable String fileName, HttpServletRequest request) throws Exception {
// Load file as Resource
Resource resource = fileService.loadFileAsResource(fileName);
// Try to determine file's content type
String contentType = null;
try {
contentType = request.getServletContext().getMimeType(resource.getFile().getAbsolutePath());
} catch (IOException ex) {
logger.info("Could not determine file type.");
}
// Fallback to the default content type if type could not be determined
if (contentType == null) {
contentType = "application/octet-stream";
}
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType(contentType))
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"")
.body(resource);
}
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/exception/ApiException.java
================================================
package com.xuexiang.xupdateservice.exception;
/**
* @author xuexiang
* @since 2018/8/6 下午3:11
*/
public class ApiException extends Exception {
/**
* 错误的code码
*/
private int mCode;
public ApiException(String message, int code) {
super(message);
mCode = code;
}
public ApiException(Throwable e, int code) {
super(e);
mCode = code;
}
public int getCode() {
return mCode;
}
/**
* 约定异常
*/
public static class ERROR {
/**
* Token失效,需要重新获取token的code码
*/
public static final int TOKEN_INVALID = 100;
/**
* 缺少Token
*/
public static final int TOKEN_MISSING = TOKEN_INVALID + 1;
/**
* 认证失败
*/
public static final int AUTH_ERROR = TOKEN_MISSING + 1;
/**
* 未知错误
*/
public static final int UNKNOWN = 5000;
/**
* 一般性业务错误
*/
public static final int COMMON_BUSINESS_ERROR = UNKNOWN + 1;
/**
* 文件存储失败
*/
public static final int FILE_STORE_ERROR = COMMON_BUSINESS_ERROR + 1;
/**
* 请求超出限制
*/
public static final int REQUEST_BEYOND_LIMIT = FILE_STORE_ERROR + 1;
}
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/exception/ApiExceptionHandler.java
================================================
package com.xuexiang.xupdateservice.exception;
import com.xuexiang.xupdateservice.api.response.ApiResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.servlet.http.HttpServletRequest;
/**
* @author xuexiang
* @since 2018/8/6 下午3:07
*/
@RestControllerAdvice
public class ApiExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(ApiExceptionHandler.class);
/**
* 拦截捕捉自定义异常
*
* @param ex
* @return
*/
@ExceptionHandler(value = Exception.class)
@ResponseBody
public ApiResult apiErrorHandler(HttpServletRequest req, Exception ex) {
if (ex instanceof ApiException) {
logger.error("请求:{}, 发生业务异常:{}", req.getRequestURL(), ex.getMessage(), ex);
return ApiResult.error((ApiException) ex);
} else {
logger.error("请求:{}, 发生系统异常:{}", req.getRequestURL(), ex.getMessage(), ex);
return ApiResult.error(-1, ex.getMessage()); //系统异常错误
}
}
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/exception/FileNotFoundException.java
================================================
package com.xuexiang.xupdateservice.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(HttpStatus.NOT_FOUND)
public class FileNotFoundException extends RuntimeException {
public FileNotFoundException(String message) {
super(message);
}
public FileNotFoundException(String message, Throwable cause) {
super(message, cause);
}
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/exception/FileStorageException.java
================================================
package com.xuexiang.xupdateservice.exception;
public class FileStorageException extends RuntimeException {
public FileStorageException(String message) {
super(message);
}
public FileStorageException(String message, Throwable cause) {
super(message, cause);
}
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/mapper/AccountMapper.java
================================================
package com.xuexiang.xupdateservice.mapper;
import com.xuexiang.xupdateservice.model.Account;
import tk.mybatis.mapper.common.Mapper;
public interface AccountMapper extends Mapper<Account> {
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/mapper/AppVersionInfoMapper.java
================================================
package com.xuexiang.xupdateservice.mapper;
import com.xuexiang.xupdateservice.model.AppVersionInfo;
import tk.mybatis.mapper.common.Mapper;
public interface AppVersionInfoMapper extends Mapper<AppVersionInfo> {
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/model/Account.java
================================================
package com.xuexiang.xupdateservice.model;
import java.util.Date;
import javax.persistence.*;
@Table(name = "account")
public class Account {
@Id
@Column(name = "account_id")
private Integer accountId;
@Column(name = "login_name")
private String loginName;
private String password;
private String nick;
private String authority;
private String avatar;
private String phone;
private String address;
@Column(name = "register_time")
private Date registerTime;
/**
* @return account_id
*/
public Integer getAccountId() {
return accountId;
}
/**
* @param accountId
*/
public void setAccountId(Integer accountId) {
this.accountId = accountId;
}
/**
* @return login_name
*/
public String getLoginName() {
return loginName;
}
/**
* @param loginName
*/
public void setLoginName(String loginName) {
this.loginName = loginName;
}
/**
* @return password
*/
public String getPassword() {
return password;
}
/**
* @param password
*/
public void setPassword(String password) {
this.password = password;
}
/**
* @return nick
*/
public String getNick() {
return nick;
}
/**
* @param nick
*/
public void setNick(String nick) {
this.nick = nick;
}
/**
* @return authority
*/
public String getAuthority() {
return authority;
}
/**
* @param authority
*/
public void setAuthority(String authority) {
this.authority = authority;
}
/**
* @return avatar
*/
public String getAvatar() {
return avatar;
}
/**
* @param avatar
*/
public void setAvatar(String avatar) {
this.avatar = avatar;
}
/**
* @return phone
*/
public String getPhone() {
return phone;
}
/**
* @param phone
*/
public void setPhone(String phone) {
this.phone = phone;
}
/**
* @return address
*/
public String getAddress() {
return address;
}
/**
* @param address
*/
public void setAddress(String address) {
this.address = address;
}
/**
* @return register_time
*/
public Date getRegisterTime() {
return registerTime;
}
/**
* @param registerTime
*/
public void setRegisterTime(Date registerTime) {
this.registerTime = registerTime;
}
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/model/AppVersionInfo.java
================================================
package com.xuexiang.xupdateservice.model;
import javax.persistence.*;
@Table(name = "app_version_info")
public class AppVersionInfo {
@Id
@Column(name = "version_id")
private Integer versionId;
@Column(name = "update_status")
private Integer updateStatus;
@Column(name = "version_code")
private Integer versionCode;
@Column(name = "version_name")
private String versionName;
@Column(name = "upload_time")
private String uploadTime;
@Column(name = "apk_size")
private Integer apkSize;
@Column(name = "app_key")
private String appKey;
@Column(name = "modify_content")
private String modifyContent;
@Column(name = "download_url")
private String downloadUrl;
@Column(name = "apk_md5")
private String apkMd5;
/**
* @return version_id
*/
public Integer getVersionId() {
return versionId;
}
/**
* @param versionId
*/
public void setVersionId(Integer versionId) {
this.versionId = versionId;
}
/**
* @return update_status
*/
public Integer getUpdateStatus() {
return updateStatus;
}
/**
* @param updateStatus
*/
public void setUpdateStatus(Integer updateStatus) {
this.updateStatus = updateStatus;
}
/**
* @return version_code
*/
public Integer getVersionCode() {
return versionCode;
}
/**
* @param versionCode
*/
public void setVersionCode(Integer versionCode) {
this.versionCode = versionCode;
}
/**
* @return version_name
*/
public String getVersionName() {
return versionName;
}
/**
* @param versionName
*/
public void setVersionName(String versionName) {
this.versionName = versionName;
}
/**
* @return upload_time
*/
public String getUploadTime() {
return uploadTime;
}
/**
* @param uploadTime
*/
public void setUploadTime(String uploadTime) {
this.uploadTime = uploadTime;
}
/**
* @return apk_size
*/
public Integer getApkSize() {
return apkSize;
}
/**
* @param apkSize
*/
public void setApkSize(Integer apkSize) {
this.apkSize = apkSize;
}
/**
* @return app_key
*/
public String getAppKey() {
return appKey;
}
/**
* @param appKey
*/
public void setAppKey(String appKey) {
this.appKey = appKey;
}
/**
* @return modify_content
*/
public String getModifyContent() {
return modifyContent;
}
/**
* @param modifyContent
*/
public void setModifyContent(String modifyContent) {
this.modifyContent = modifyContent;
}
/**
* @return download_url
*/
public String getDownloadUrl() {
return downloadUrl;
}
/**
* @param downloadUrl
*/
public void setDownloadUrl(String downloadUrl) {
this.downloadUrl = downloadUrl;
}
/**
* @return apk_md5
*/
public String getApkMd5() {
return apkMd5;
}
/**
* @param apkMd5
*/
public void setApkMd5(String apkMd5) {
this.apkMd5 = apkMd5;
}
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/service/AccountService.java
================================================
package com.xuexiang.xupdateservice.service;
import com.xuexiang.xupdateservice.api.response.PageData;
import com.xuexiang.xupdateservice.model.Account;
import java.util.List;
/**
* 账户管理服务
*
* @author xuexiang
* @since 2019/4/20 下午11:55
*/
public interface AccountService {
/**
* 分页查询查询所有账户信息
*
* @param pageNum 开始的页数
* @param pageSize 每页的数量
* @return
*/
PageData<Account> getAllAccount(int pageNum, int pageSize);
/**
* 查询所有账户信息
*
* @return
*/
List<Account> getAllAccount();
/**
* 更新账户信息
*
* @param account
* @return
*/
boolean updateAccount(Account account);
/**
* 删除账户信息
*
* @param accountId
* @return
*/
boolean deleteAccount(int accountId);
/**
* 注册账户
*
* @param account
* @return
*/
boolean registerAccount(Account account);
/**
* 检测账户是否存在
* @param loginName
* @return
*/
Account checkAccount(String loginName);
/**
* 登陆账户
* @param loginName
* @param password
* @return
*/
Account loginAccount(String loginName, String password);
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/service/FileStorageService.java
================================================
package com.xuexiang.xupdateservice.service;
import org.springframework.core.io.Resource;
import org.springframework.web.multipart.MultipartFile;
/**
* 文件存储服务
*
* @author xuexiang
* @since 2018/7/18 下午3:36
*/
public interface FileStorageService {
/**
* 存储文件
*
* @param file
* @return 存储文件的文件名
*/
String storeFile(MultipartFile file) throws Exception;
/**
* 读取文件
* @param fileName
* @return
*/
Resource loadFileAsResource(String fileName) throws Exception;
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/service/UpdateService.java
================================================
package com.xuexiang.xupdateservice.service;
import com.xuexiang.xupdateservice.api.response.PageData;
import com.xuexiang.xupdateservice.model.AppVersionInfo;
import java.util.List;
/**
* @author xuexiang
* @since 2018/7/26 上午11:04
*/
public interface UpdateService {
/**
* 无版本更新
*/
int NO_NEW_VERSION = 0; // 0:无版本更新
/**
* 有版本更新,不需要强制升级
*/
int HAVE_NEW_VERSION = 1; // 1:有版本更新,不需要强制升级
/**
* 有版本更新,需要强制升级
*/
int HAVE_NEW_VERSION_FORCED_UPLOAD = 2; // 2:有版本更新,需要强制升级
/**
* 获取最新的apk版本信息
*
* @param versionCode
* @param appKey
* @return
*/
AppVersionInfo getLatestAppVersionInfo(int versionCode, String appKey);
/**
* 根据appKey获取唯一apk的所有版本信息
*
* @param appKey
* @return
*/
List<AppVersionInfo> getAllAppVersionInfo(String appKey);
/**
* 获取所有应用的版本信息
*
* @return
*/
List<AppVersionInfo> getAllAppVersionInfo();
/**
* 分页查询所有应用的版本信息
*
* @param pageNum
* @param pageSize
* @return
*/
PageData<AppVersionInfo> getAllAppVersionInfo(int pageNum, int pageSize);
/**
* 根据appKey和versionCode获取唯一的版本信息
*
* @param appKey
* @return
*/
AppVersionInfo getAppVersionInfo(int versionCode, String appKey);
/**
* 添加app版本信息
*
* @param appVersionInfo
* @return
*/
boolean addAppVersionInfo(AppVersionInfo appVersionInfo);
/**
* 更新app版本信息
*
* @param appVersionInfo
* @return
*/
boolean updateAppVersionInfo(AppVersionInfo appVersionInfo);
/**
* 删除版本信息
*
* @param versionId
* @return
*/
boolean deleteAppVersionInfo(int versionId);
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/service/impl/AccountServiceImpl.java
================================================
package com.xuexiang.xupdateservice.service.impl;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.xuexiang.xupdateservice.api.response.PageData;
import com.xuexiang.xupdateservice.mapper.AccountMapper;
import com.xuexiang.xupdateservice.model.Account;
import com.xuexiang.xupdateservice.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import tk.mybatis.mapper.entity.Condition;
import java.util.List;
/**
*
*
* @author xuexiang
* @since 2019/4/21 上午12:11
*/
@Service(value = "accountService")
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountMapper accountMapper;
@Override
public PageData<Account> getAllAccount(int pageNum, int pageSize) {
PageData<Account> pageData = new PageData<>();
Page<Account> page = PageHelper.startPage(pageNum, pageSize);
pageData.setArray(accountMapper.selectAll());
pageData.setPageNum(page.getPageNum())
.setPageSize(page.getPageSize())
.setTotal(page.getTotal());
return pageData;
}
@Override
public List<Account> getAllAccount() {
return accountMapper.selectAll();
}
@Override
public boolean updateAccount(Account account) {
return accountMapper.updateByPrimaryKeySelective(account) > 0;
}
@Override
public boolean deleteAccount(int accountId) {
return accountMapper.deleteByPrimaryKey(accountId) > 0;
}
@Override
public boolean registerAccount(Account account) {
return accountMapper.insert(account) > 0;
}
@Override
public Account checkAccount(String loginName) {
Condition condition = new Condition(Account.class);
condition.createCriteria().andEqualTo("loginName", loginName);
return selectFirstAccountByCondition(condition);
}
@Override
public Account loginAccount(String loginName, String password) {
Condition condition = new Condition(Account.class);
condition.createCriteria().andEqualTo("loginName", loginName)
.andEqualTo("password", password);
return selectFirstAccountByCondition(condition);
}
private Account selectFirstAccountByCondition(Condition condition) {
List<Account> list = accountMapper.selectByExample(condition);
if (list != null && list.size() > 0) {
return list.get(0);
} else {
return null;
}
}
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/service/impl/FileStorageServiceImpl.java
================================================
package com.xuexiang.xupdateservice.service.impl;
import com.xuexiang.xupdateservice.config.FileStorageProperties;
import com.xuexiang.xupdateservice.exception.FileNotFoundException;
import com.xuexiang.xupdateservice.exception.FileStorageException;
import com.xuexiang.xupdateservice.service.FileStorageService;
import com.xuexiang.xupdateservice.utils.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
@Service(value = "fileService")
public class FileStorageServiceImpl implements FileStorageService {
private final Path fileStorageLocation;
private final FileStorageProperties fileStorageProperties;
@Autowired
public FileStorageServiceImpl(FileStorageProperties fileStorageProperties) {
this.fileStorageProperties = fileStorageProperties;
this.fileStorageLocation = Paths.get(fileStorageProperties.getFileDirectory())
.toAbsolutePath().normalize();
try {
Files.createDirectories(this.fileStorageLocation);
} catch (Exception ex) {
throw new FileStorageException("Could not create the directory where the uploaded files will be stored.", ex);
}
}
@Override
public String storeFile(MultipartFile file) throws Exception {
// Normalize file name
String fileName = StringUtils.cleanPath(file.getOriginalFilename());
if (!fileStorageProperties.isKeepName()){ //不保持文件名
fileName = FileUtils.randomFileName(fileName);
}
try {
// Check if the file's name contains invalid characters
if(fileName.contains("..")) {
throw new FileStorageException("Sorry! Filename contains invalid path sequence " + fileName);
}
// Copy file to the target location (Replacing existing file with the same name)
Path targetLocation = this.fileStorageLocation.resolve(fileName);
Files.copy(file.getInputStream(), targetLocation, StandardCopyOption.REPLACE_EXISTING);
return fileName;
} catch (IOException ex) {
throw new FileStorageException("Could not store file " + fileName + ". Please try again!", ex);
}
}
@Override
public Resource loadFileAsResource(String fileName) throws Exception {
try {
Path filePath = this.fileStorageLocation.resolve(fileName).normalize();
Resource resource = new UrlResource(filePath.toUri());
if(resource.exists()) {
return resource;
} else {
throw new FileNotFoundException("File not found " + fileName);
}
} catch (MalformedURLException ex) {
throw new FileNotFoundException("File not found " + fileName, ex);
}
}
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/service/impl/UpdateServiceImpl.java
================================================
package com.xuexiang.xupdateservice.service.impl;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.xuexiang.xupdateservice.api.response.PageData;
import com.xuexiang.xupdateservice.mapper.AppVersionInfoMapper;
import com.xuexiang.xupdateservice.model.AppVersionInfo;
import com.xuexiang.xupdateservice.service.UpdateService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import tk.mybatis.mapper.entity.Condition;
import java.util.List;
/**
* 版本更新服务
*
* @author xuexiang
* @since 2018/7/26 上午11:58
*/
@Service(value = "updateService")
public class UpdateServiceImpl implements UpdateService {
@Autowired
private AppVersionInfoMapper appVersionInfoMapper;//这里会报错,但是并不会影响
@Override
public AppVersionInfo getLatestAppVersionInfo(int versionCode, String appKey) {
List<AppVersionInfo> appInfos = getAllAppVersionInfo(appKey);
if (appInfos.size() > 0) {
AppVersionInfo appVersionInfo = appInfos.get(0); //获取到最新的版本
if (appVersionInfo.getVersionCode() > versionCode) { //最新版本大,需要更新
return appVersionInfo;
} else {
AppVersionInfo appInfo = new AppVersionInfo();
appInfo.setUpdateStatus(UpdateService.NO_NEW_VERSION);
return appInfo;
}
} else {
AppVersionInfo appInfo = new AppVersionInfo();
appInfo.setUpdateStatus(UpdateService.NO_NEW_VERSION);
return appInfo;
}
}
@Override
public List<AppVersionInfo> getAllAppVersionInfo(String appKey) {
Condition condition = new Condition(AppVersionInfo.class);
condition.createCriteria().andEqualTo("appKey", appKey);
condition.setOrderByClause("version_code desc"); //根据版本号降序
return appVersionInfoMapper.selectByExample(condition);
}
@Override
public List<AppVersionInfo> getAllAppVersionInfo() {
return appVersionInfoMapper.selectAll();
}
/*
* 这个方法中用到了我们开头配置依赖的分页插件PageHelper
* 很简单,只需要在service层传入参数,然后将参数传递给一个插件的一个静态方法即可;
* pageNum 开始页数
* pageSize 每页显示的数据条数
* */
@Override
public PageData<AppVersionInfo> getAllAppVersionInfo(int pageNum, int pageSize) {
//将参数传给这个方法就可以实现物理分页了,非常简单。
PageData<AppVersionInfo> pageData = new PageData<>();
Page<AppVersionInfo> page = PageHelper.startPage(pageNum, pageSize);
pageData.setArray(appVersionInfoMapper.selectAll());
pageData.setPageNum(page.getPageNum())
.setPageSize(page.getPageSize())
.setTotal(page.getTotal());
return pageData;
}
@Override
public AppVersionInfo getAppVersionInfo(int versionCode, String appKey) {
Condition condition = new Condition(AppVersionInfo.class);
condition.createCriteria().andEqualTo("appKey", appKey)
.andEqualTo("versionCode", versionCode);
condition.setOrderByClause("version_code desc"); //根据版本号降序
List<AppVersionInfo> list = appVersionInfoMapper.selectByExample(condition);
if (list != null && list.size() > 0) {
return list.get(0);
} else {
return null;
}
}
@Override
public boolean addAppVersionInfo(AppVersionInfo appVersionInfo) {
return appVersionInfoMapper.insert(appVersionInfo) > 0;
}
@Override
public boolean updateAppVersionInfo(AppVersionInfo appVersionInfo) {
return appVersionInfoMapper.updateByPrimaryKeySelective(appVersionInfo) > 0;
}
@Override
public boolean deleteAppVersionInfo(int versionId) {
return appVersionInfoMapper.deleteByPrimaryKey(versionId) > 0;
}
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/utils/AspectJUtils.java
================================================
package com.xuexiang.xupdateservice.utils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.assertj.core.util.Lists;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
* AspectJ 工具类
*
* @author xuexiang
* @since 2018/7/17 上午10:34
*/
public final class AspectJUtils {
private AspectJUtils() {
throw new UnsupportedOperationException("u can't instantiate me...");
}
/**
* 获取方法名
*
* @param joinPoint
* @return
*/
public static String getMethodName(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().toShortString();
String shortMethodNameSuffix = "(..)";
if (methodName.endsWith(shortMethodNameSuffix)) {
methodName = methodName.substring(0, methodName.length() - shortMethodNameSuffix.length());
}
return methodName;
}
/**
* 获取需要记录日志方法的参数,敏感参数用*代替
*
* @param joinPoint 切点
* @return 去除敏感参数后的Json字符串
*/
public static String getMethodParams(ProceedingJoinPoint joinPoint) {
Object[] arguments = joinPoint.getArgs();
StringBuilder sb = new StringBuilder();
if (arguments == null || arguments.length <= 0) {
return sb.toString();
}
for (Object arg : arguments) {
//移除敏感内容
String paramStr;
if (arg instanceof HttpServletResponse) {
paramStr = HttpServletResponse.class.getSimpleName();
} else if (arg instanceof HttpServletRequest) {
paramStr = HttpServletRequest.class.getSimpleName();
} else if (arg instanceof MultipartFile) {
long size = ((MultipartFile) arg).getSize();
paramStr = MultipartFile.class.getSimpleName() + " size:" + size;
} else {
paramStr = deleteSensitiveContent(arg);
}
sb.append(paramStr).append(",");
}
return sb.deleteCharAt(sb.length() - 1).toString();
}
/**
* 删除参数中的敏感内容
*
* @param obj 参数对象
* @return 去除敏感内容后的参数对象
*/
public static String deleteSensitiveContent(Object obj) {
JSONObject jsonObject = new JSONObject();
if (obj == null || obj instanceof Exception) {
return jsonObject.toJSONString();
}
String param = JSON.toJSONString(obj);
try {
jsonObject = JSONObject.parseObject(param);
} catch (Exception e) {
return String.valueOf(obj);
}
List<String> sensitiveFieldList = getSensitiveFieldList();
for (String sensitiveField : sensitiveFieldList) {
if (jsonObject.containsKey(sensitiveField)) {
jsonObject.put(sensitiveField, "******");
}
}
return jsonObject.toJSONString();
}
/**
* 敏感字段列表(当然这里你可以更改为可配置的)
*/
private static List<String> getSensitiveFieldList() {
List<String> sensitiveFieldList = Lists.newArrayList();
sensitiveFieldList.add("pwd");
sensitiveFieldList.add("password");
return sensitiveFieldList;
}
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/utils/DateUtils.java
================================================
/*
* Copyright (C) 2018 nl-xx(xx@aecg.com.cn)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.xuexiang.xupdateservice.utils;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import static org.apache.commons.lang3.StringUtils.EMPTY;
/**
* 日期工具
*
* @author xuexiang
* @since 2018/7/26 下午4:07
*/
public final class DateUtils {
/**
* yyyy-MM-dd
*/
public static final ThreadLocal<DateFormat> yyyyMMdd = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
/**
* yyyyMMdd
*/
public static final ThreadLocal<DateFormat> yyyyMMddNoSep = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyyMMdd"));
/**
* HH:mm:ss
*/
public static final ThreadLocal<DateFormat> HHmmss = ThreadLocal.withInitial(() -> new SimpleDateFormat("HH:mm:ss"));
/**
* HH:mm
*/
public static final ThreadLocal<DateFormat> HHmm = ThreadLocal.withInitial(() -> new SimpleDateFormat("HH:mm"));
/**
* yyyy-MM-dd HH:mm:ss
*/
public static final ThreadLocal<DateFormat> yyyyMMddHHmmss = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
/**
* yyyyMMddHHmmss
*/
public static final ThreadLocal<DateFormat> yyyyMMddHHmmssNoSep = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyyMMddHHmmss"));
/**
* yyyy-MM-dd HH:mm
*/
public static final ThreadLocal<DateFormat> yyyyMMddHHmm = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm"));
/**
* yyyy-MM-dd HH:mm:ss:SSS
*/
public static final ThreadLocal<DateFormat> yyyyMMddHHmmssSSS = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS"));
//============================时间类型转化================================//
/**
* Don't let anyone instantiate this class.
*/
private DateUtils() {
throw new UnsupportedOperationException("u can't instantiate me...");
}
/**
* 将时间戳转为时间字符串
* <p>格式为 format</p>
*
* @param millis 毫秒时间戳
* @param format 时间格式
* @return 时间字符串
*/
public static String millis2String(final long millis, final DateFormat format) {
return date2String(new Date(millis), format);
}
/**
* 将 Date 类型转为时间字符串
* <p>格式为 format</p>
*
* @param date Date 类型时间
* @param format 时间格式
* @return 时间字符串
*/
public static String date2String(final Date date, final DateFormat format) {
if (format != null) {
return format.format(date);
} else {
return EMPTY;
}
}
/**
* 将时间字符串转为时间戳
* <p>time 格式为 format</p>
*
* @param time 时间字符串
* @param format 时间格式
* @return 毫秒时间戳
*/
public static long string2Millis(final String time, final DateFormat format) {
try {
if (format != null) {
return format.parse(time).getTime();
}
} catch (ParseException e) {
e.printStackTrace();
}
return -1;
}
/**
* 将时间字符串转为 Date 类型
* <p>time 格式为 format</p>
*
* @param time 时间字符串
* @param format 时间格式
* @return Date 类型
*/
public static Date string2Date(final String time, final DateFormat format) {
try {
if (format != null) {
return format.parse(time);
}
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
/**
* 将 Date 类型转为时间戳
*
* @param date Date 类型时间
* @return 毫秒时间戳
*/
public static long date2Millis(final Date date) {
return date != null ? date.getTime() : -1;
}
/**
* 将时间戳转为 Date 类型
*
* @param millis 毫秒时间戳
* @return Date 类型时间
*/
public static Date millis2Date(final long millis) {
return new Date(millis);
}
/**
* 生日期获取年龄
*
* @param birthDay 出生日期字符串
* @param format 日期格式
* @return 计算出的年龄
* @throws IllegalArgumentException
*/
public static int getAgeByBirthDay(final String birthDay, final DateFormat format) throws IllegalArgumentException {
return getAgeByBirthDay(DateUtils.string2Date(birthDay, format));
}
/**
* 根据出生日期获取年龄
*
* @param birthDay 出生日期
* @return 计算出的年龄
* @throws IllegalArgumentException
*/
public static int getAgeByBirthDay(Date birthDay) throws IllegalArgumentException {
Calendar cal = Calendar.getInstance();
if (cal.before(birthDay)) {
throw new IllegalArgumentException("The birthDay is before Now.It's unbelievable!");
}
int yearNow = cal.get(Calendar.YEAR);
int monthNow = cal.get(Calendar.MONTH);
int dayNow = cal.get(Calendar.DAY_OF_MONTH);
cal.setTime(birthDay);
int yearBirth = cal.get(Calendar.YEAR);
int monthBirth = cal.get(Calendar.MONTH);
int dayBirth = cal.get(Calendar.DAY_OF_MONTH);
int age = yearNow - yearBirth;
if (monthNow <= monthBirth) {
if (monthNow == monthBirth) {
if (dayNow < dayBirth) {
age--;
}
} else {
age--;
}
}
return age;
}
//==============================时间获取===============================//
/**
* 获取当前毫秒时间戳
*
* @return 毫秒时间戳
*/
public static long getNowMills() {
return System.currentTimeMillis();
}
/**
* 获取当前时间字符串
* <p>格式为 format</p>
*
* @param format 时间格式
* @return 时间字符串
*/
public static String getNowString(final DateFormat format) {
return millis2String(System.currentTimeMillis(), format);
}
/**
* 获取当前 Date
*
* @return Date 类型时间
*/
public static Date getNowDate() {
return new Date();
}
/**
* 获取星期索引
* <p>注意:周日的 Index 才是 1,周六为 7</p>
* <p>time 格式为 format</p>
*
* @param time 时间字符串
* @param format 时间格式
* @return 1...7
* @see Calendar#SUNDAY
* @see Calendar#MONDAY
* @see Calendar#TUESDAY
* @see Calendar#WEDNESDAY
* @see Calendar#THURSDAY
* @see Calendar#FRIDAY
* @see Calendar#SATURDAY
*/
public static int getWeekIndex(final String time, final DateFormat format) {
return getWeekIndex(string2Date(time, format));
}
/**
* 得到年
*
* @param date Date对象
* @return 年
*/
public static int getYear(final Date date) {
Calendar c = Calendar.getInstance();
c.setTime(date);
return c.get(Calendar.YEAR);
}
/**
* 得到月
*
* @param date Date对象
* @return 月
*/
public static int getMonth(final Date date) {
Calendar c = Calendar.getInstance();
c.setTime(date);
return c.get(Calendar.MONTH) + 1;
}
/**
* 得到日
*
* @param date Date对象
* @return 日
*/
public static int getDay(final Date date) {
Calendar c = Calendar.getInstance();
c.setTime(date);
return c.get(Calendar.DAY_OF_MONTH);
}
/**
* 获取星期索引
* <p>注意:周日的 Index 才是 1,周六为 7</p>
*
* @param date Date 类型时间
* @return 1...7
* @see Calendar#SUNDAY
* @see Calendar#MONDAY
* @see Calendar#TUESDAY
* @see Calendar#WEDNESDAY
* @see Calendar#THURSDAY
* @see Calendar#FRIDAY
* @see Calendar#SATURDAY
*/
public static int getWeekIndex(final Date date) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
return cal.get(Calendar.DAY_OF_WEEK);
}
/**
* 获取星期索引
* <p>注意:周日的 Index 才是 1,周六为 7</p>
*
* @param millis 毫秒时间戳
* @return 1...7
* @see Calendar#SUNDAY
* @see Calendar#MONDAY
* @see Calendar#TUESDAY
* @see Calendar#WEDNESDAY
* @see Calendar#THURSDAY
* @see Calendar#FRIDAY
* @see Calendar#SATURDAY
*/
public static int getWeekIndex(final long millis) {
return getWeekIndex(millis2Date(millis));
}
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/utils/FileUtils.java
================================================
package com.xuexiang.xupdateservice.utils;
import org.springframework.util.StringUtils;
import java.io.File;
/**
* @author xuexiang
* @since 2018/7/18 下午4:30
*/
public final class FileUtils {
private FileUtils() {
throw new UnsupportedOperationException("u can't instantiate me...");
}
/**
* 生成随机的文件名
*
* @return
*/
public static String randomFileName(String filePath) {
String fileExtension = getFileExtension(filePath);
return RandomGUID.getRandomGUID() + "." + fileExtension;
}
/**
* 获取全路径中的文件拓展名
*
* @param filePath 文件路径
* @return 文件拓展名
*/
public static String getFileExtension(final String filePath) {
if (StringUtils.isEmpty(filePath)) return filePath;
int lastPoi = filePath.lastIndexOf('.');
int lastSep = filePath.lastIndexOf(File.separator);
if (lastPoi == -1 || lastSep >= lastPoi) return "";
return filePath.substring(lastPoi + 1);
}
/**
* 获取apk的大小【单位:KB】
*
* @param file 文件
* @return 文件长度
*/
public static int getApkFileSize(final File file) {
long fileLength = getFileLength(file);
return (int) (fileLength / 1024);
}
/**
* 获取文件长度
*
* @param file 文件
* @return 文件长度
*/
private static long getFileLength(final File file) {
if (!isFile(file)) return -1;
return file.length();
}
/**
* 判断是否是文件
*
* @param file 文件
* @return {@code true}: 是<br>{@code false}: 否
*/
private static boolean isFile(final File file) {
return file != null && file.exists() && file.isFile();
}
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/utils/IpUtils.java
================================================
package com.xuexiang.xupdateservice.utils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.UnknownHostException;
import java.util.Enumeration;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* IP操作工具类
*
* @author xuexiang
* @since 2018/7/17 上午10:34
*/
public final class IpUtils {
private IpUtils() {
throw new UnsupportedOperationException("u can't instantiate me...");
}
private static final Logger logger = LoggerFactory.getLogger(IpUtils.class);
private static final String IP_PATTERN = "^(?:(?:[01]?\\d{1,2}|2[0-4]\\d|25[0-5])\\.){3}(?:[01]?\\d{1,2}|2[0-4]\\d|25[0-5])\\b";
private static final String UNKNOWN = "unknown";
private static final String LOCAL_IP = "127.0.0.1";
/**
* 获取请求中的ip地址:过了多级反向代理,获取的ip不是唯一的,二是包含中间代理层ip
* @param request
* @return 可能有多个,例如:192.168.1.110, 192.168.1.120
*/
public static String getIpAddr(HttpServletRequest request) {
String ip = LOCAL_IP;
if (request != null) {
ip = request.getHeader("x-forwarded-for");
if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
}
return ip;
}
/**
* 获取客户端请求中的真实的ip地址
* 获取客户端的IP地址的方法是:request.getRemoteAddr(),这种方法在大部分情况下都是有效的。
* 但是在通过了Apache,Squid等反向代理软件就不能获取到客户端的真实IP地址。而且,如果通过了多级反向代理的话,X-Forwarded-For的值并不止一个,
* 而是一串ip值,例如:192.168.1.110, 192.168.1.120, 192.168.1.130, 192.168.1.100。其中第一个192.168.1.110才是用户真实的ip
* @param request
* @return
*/
public static String getRealIp(HttpServletRequest request) {
String ip = LOCAL_IP;
if (request == null) {
return ip;
}
ip = request.getHeader("x-forwarded-for");
ip = getIp(request,ip);
if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
if (LOCAL_IP.equals(ip) || "0:0:0:0:0:0:0:1".equals(ip)) {
//根据网卡取本机配置的IP
InetAddress inet = null;
try {
inet = InetAddress.getLocalHost();
ip = inet.getHostAddress();
} catch (UnknownHostException e) {
logger.error("getRealIp occurs error, caused by: ", e);
}
}
}
String ch = ",";
if (ip != null && ip.contains(ch)) {
ip = ip.substring(0, ip.indexOf(ch));
}
return ip;
}
/**
* 通过各种方式获取IP
* @param request
* @param ip
* @return
*/
private static String getIp(HttpServletRequest request, String ip){
if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
return ip;
}
/**
* 获取服务器IP
*/
public static String getServiceIp() {
Enumeration<NetworkInterface> netInterfaces = null;
String ipsStr = "";
try {
netInterfaces = NetworkInterface.getNetworkInterfaces();
while (netInterfaces.hasMoreElements()) {
NetworkInterface ni = netInterfaces.nextElement();
Enumeration<InetAddress> ips = ni.getInetAddresses();
Pattern pattern = Pattern.compile(IP_PATTERN);
while (ips.hasMoreElements()) {
String ip = ips.nextElement().getHostAddress();
Matcher matcher = pattern.matcher(ip);
if (matcher.matches() && !LOCAL_IP.equals(ip)) {
ipsStr = ip;
}
}
}
} catch (Exception e) {
logger.error("getServiceIp occurs error, caused by: ", e);
}
return ipsStr;
}
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/utils/Md5Utils.java
================================================
/*
* Copyright (C) 2018 xuexiangjys(xuexiangjys@163.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.xuexiang.xupdateservice.utils;
import org.apache.commons.lang3.StringUtils;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* MD5加密工具类
*
* @author xuexiang
* @since 2018/7/2 下午3:14
*/
public final class Md5Utils {
private Md5Utils() {
throw new UnsupportedOperationException("cannot be instantiated");
}
public static String getFileMD5(File file) {
if (!file.exists()) {
return "";
}
FileInputStream in = null;
try {
in = new FileInputStream(file);
FileChannel channel = in.getChannel();
MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, file.length());
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(buffer);
return bytes2Hex(md.digest());
} catch (NoSuchAlgorithmException | IOException e) {
e.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException ignored) {
}
}
}
return "";
}
/**
* 验证文件是否有效【通过MD5值比较】
*
* @param md5 如果md5值为空,则不进行校验,直接为true
* @param file 需要校验的文件
* @return 文件是否有效
*/
public static boolean isFileValid(String md5, File file) {
return StringUtils.isEmpty(md5) || md5.equals(Md5Utils.getFileMD5(file));
}
/**
* 一个byte转为2个hex字符
*
* @param src byte数组
* @return 16进制大写字符串
*/
private static String bytes2Hex(byte[] src) {
char[] res = new char[src.length << 1];
final char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
for (int i = 0, j = 0; i < src.length; i++) {
res[j++] = hexDigits[src[i] >>> 4 & 0x0f];
res[j++] = hexDigits[src[i] & 0x0f];
}
return new String(res);
}
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/utils/QuickRequestUtils.java
================================================
package com.xuexiang.xupdateservice.utils;
import com.xuexiang.xupdateservice.component.annotation.LimitedRequest;
import com.xuexiang.xupdateservice.component.annotation.QuickRequest;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
/**
* 快速请求工具类
*
* @author xuexiang
* @since 2018/8/7 下午2:27
*/
public final class QuickRequestUtils {
private QuickRequestUtils() {
throw new UnsupportedOperationException("u can't instantiate me...");
}
/**
* 存放请求的次数
*/
private static Map<String, Integer> sRequestCount = new ConcurrentHashMap<>();
/**
* 存放请求的时间戳
*/
private static Map<String, Long> sRequestTimeStamp = new ConcurrentHashMap<>();
/**
* 是否是快速请求
*
* @param key
* @param quickRequest
* @param requestTimeStamp 请求携带的时间戳
* @return
*/
public static boolean isQuickRequest(String key, QuickRequest quickRequest, String requestTimeStamp) {
long now = Long.valueOf(requestTimeStamp);
long lastRequestTimeStamp = sRequestTimeStamp.getOrDefault(key, 0L); //获取最后一次请求的时间戳
long timeD = now - lastRequestTimeStamp; //计算出间隔
if (0 < timeD && timeD < quickRequest.interval()) {
return true;
} else {
sRequestTimeStamp.put(key, now);
return false;
}
}
/**
* 是否是快速请求
*
* @param key
* @param limit
* @return
*/
public static boolean isQuickRequest(String key, LimitedRequest limit) {
int count = sRequestCount.getOrDefault(key, 0) + 1;
sRequestCount.put(key, count);
if (count > 0) {
Timer timer = new Timer();
TimerTask task = new TimerTask() { //创建一个新的计时器任务。
@Override
public void run() {
int number = sRequestCount.getOrDefault(key, 0) - 1;
if (number > 0) {
sRequestCount.put(key, number);
} else {
sRequestCount.remove(key);
}
}
};
timer.schedule(task, limit.interval()); //安排在指定延迟后执行指定的任务。task : 所要安排的任务。: 执行任务前的延迟时间,单位是毫秒。
}
return count > limit.count();
}
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/utils/RandomGUID.java
================================================
package com.xuexiang.xupdateservice.utils;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Random;
/**
* 生成唯一的GUID
*
* @author XUE
* @since 2019/4/22 13:30
*/
public final class RandomGUID {
public String valueBeforeMD5 = "";
public String valueAfterMD5 = "";
private static Random myRand;
private static SecureRandom mySecureRand;
private static String s_id;
private static final int PAD_BELOW = 0x10;
private static final int TWO_BYTES = 0xFF;
static {
mySecureRand = new SecureRandom();
long secureInitializer = mySecureRand.nextLong();
myRand = new Random(secureInitializer);
new Thread(new Runnable() {
public void run() {
try {
s_id = InetAddress.getLocalHost().toString();
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}).start();
}
/**
* 获取随机生成的GUID
* @return
*/
public static String getRandomGUID() {
return new RandomGUID().toString();
}
public RandomGUID() {
getRandomGUID(false);
}
public RandomGUID(boolean secure) {
getRandomGUID(secure);
}
/*
* Method to generate the random GUID
*/
private void getRandomGUID(boolean secure) {
MessageDigest md5 = null;
StringBuffer sbValueBeforeMD5 = new StringBuffer(128);
try {
md5 = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
}
try {
long time = System.currentTimeMillis();
long rand = 0;
if (secure) {
rand = mySecureRand.nextLong();
} else {
rand = myRand.nextLong();
}
sbValueBeforeMD5.append(s_id);
sbValueBeforeMD5.append(":");
sbValueBeforeMD5.append(time);
sbValueBeforeMD5.append(":");
sbValueBeforeMD5.append(rand);
valueBeforeMD5 = sbValueBeforeMD5.toString();
md5.update(valueBeforeMD5.getBytes());
byte[] array = md5.digest();
StringBuffer sb = new StringBuffer(32);
for (byte b1 : array) {
int b = b1 & TWO_BYTES;
if (b < PAD_BELOW)
sb.append('0');
sb.append(Integer.toHexString(b));
}
valueAfterMD5 = sb.toString();
} catch (Exception e) {
}
}
public String toString() {
String raw = valueAfterMD5.toUpperCase();
StringBuffer sb = new StringBuffer(64);
sb.append(raw, 0, 8);
sb.append("-");
sb.append(raw, 8, 12);
sb.append("-");
sb.append(raw, 12, 16);
sb.append("-");
sb.append(raw, 16, 20);
sb.append("-");
sb.append(raw.substring(20));
return sb.toString();
}
}
================================================
FILE: src/main/java/com/xuexiang/xupdateservice/utils/TokenUtils.java
================================================
package com.xuexiang.xupdateservice.utils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.commons.lang3.StringUtils;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.xml.bind.DatatypeConverter;
import java.security.Key;
import java.util.Date;
/**
* token生成工具
* @author xuexiang
* @since 2018/8/6 下午4:27
*/
public final class TokenUtils {
/**
* 签名秘钥
*/
public static final String SECRET = "xuexiangjys";
/**
* 生成token
*
* @param id 一般传入userName
* @return
*/
public static String createJwtToken(String id) {
String issuer = "www.github.com";
String subject = "xuexiangjys@163.com";
long ttlMillis = 60 * 60 * 1000; //有效期一小时
return createJwtToken(id, issuer, subject, ttlMillis);
}
/**
* 生成Token
*
* @param id 编号
* @param issuer 该JWT的签发者,是否使用是可选的
* @param subject 该JWT所面向的用户,是否使用是可选的;
* @param ttlMillis 签发时间 (有效时间,过期会报错)
* @return token String
*/
public static String createJwtToken(String id, String issuer, String subject, long ttlMillis) {
// 签名算法 ,将对token进行签名
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
// 生成签发时间
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
// 通过秘钥签名JWT
byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(SECRET);
Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
// Let's set the JWT Claims
JwtBuilder builder = Jwts.builder().setId(id)
.setIssuedAt(now)
.setSubject(subject)
.setIssuer(issuer)
.signWith(signatureAlgorithm, signingKey);
// if it has been specified, let's add the expiration
if (ttlMillis >= 0) {
long expMillis = nowMillis + ttlMillis;
Date exp = new Date(expMillis);
builder.setExpiration(exp);
}
// Builds the JWT and serializes it to a compact, URL-safe string
return builder.compact();
}
// Sample method to validate and read the JWT
public static Claims parseJWT(String jwt) {
// This line will throw an exception if it is not a signed JWS (as expected)
Claims claims = Jwts.parser()
.setSigningKey(DatatypeConverter.parseBase64Binary(SECRET))
.parseClaimsJws(jwt).getBody();
return claims;
}
/**
* 从HttpServletRequest中解析出token
*
* @param request
* @return
*/
public static String parseToken(HttpServletRequest request) {
String accessToken = request.getHeader("X-Token");
if (StringUtils.isEmpty(accessToken)) {
accessToken = request.getParameter("token");
}
return accessToken;
}
public static void main(String[] args) {
System.out.println(TokenUtils.createJwtToken("11111"));
}
}
================================================
FILE: src/main/resources/application.yml
================================================
server:
port: 1111
spring:
datasource:
name: XUpdateService
url: jdbc:mysql://localhost:3306/xupdate?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
username: root
password: 123456
# 使用druid数据源
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
filters: stat
maxActive: 20
initialSize: 1
maxWait: 60000
minIdle: 1
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: select 'x'
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
maxOpenPreparedStatements: 20
http:
multipart:
max-file-size: 100MB
max-request-size: 1000MB
servlet:
multipart:
enabled: true
file-size-threshold: 2KB
max-file-size: 10MB
max-request-size: 100MB
mvc:
view:
prefix: classpath:/templates/
suffix: .html
mybatis:
mapper-locations: classpath:mapping/*.xml
type-aliases-package: com.xuexiang.xupdateservice.model
#pagehelper
pagehelper:
helperDialect: mysql
reasonable: true
supportMethodsArguments: true
params: count=countSql
upload:
file-directory: ./uploads
keep-name: true
================================================
FILE: src/main/resources/db-mysql.properties
================================================
#Mybatis Generator configuration
##这边请换成你电脑上的绝对路径
## Windows 配置
## classPath=C:/Users/XUE/Documents/GitHub/XUpdateService/libs/mysql-connector-java-5.1.35.jar
## Mac 配置
classPath=/Users/xuexiang/Documents/MyGitHub/XUpdateService/libs/mysql-connector-java-5.1.35.jar
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/xupdate?useUnicode=true&characterEncoding=UTF-8
jdbc.user=root
jdbc.pass=123456
================================================
FILE: src/main/resources/mybatis_mapper/AccountMapper.xml
================================================
<?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="com.xuexiang.xupdateservice.mapper.AccountMapper">
<resultMap id="BaseResultMap" type="com.xuexiang.xupdateservice.model.Account">
<!--
WARNING - @mbg.generated
-->
<id column="account_id" jdbcType="INTEGER" property="accountId" />
<result column="login_name" jdbcType="VARCHAR" property="loginName" />
<result column="password" jdbcType="VARCHAR" property="password" />
<result column="nick" jdbcType="VARCHAR" property="nick" />
<result column="authority" jdbcType="VARCHAR" property="authority" />
<result column="avatar" jdbcType="VARCHAR" property="avatar" />
<result column="phone" jdbcType="CHAR" property="phone" />
<result column="address" jdbcType="VARCHAR" property="address" />
<result column="register_time" jdbcType="TIMESTAMP" property="registerTime" />
</resultMap>
</mapper>
================================================
FILE: src/main/resources/mybatis_mapper/AppVersionInfoMapper.xml
================================================
<?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="com.xuexiang.xupdateservice.mapper.AppVersionInfoMapper">
<resultMap id="BaseResultMap" type="com.xuexiang.xupdateservice.model.AppVersionInfo">
<!--
WARNING - @mbg.generated
-->
<id column="version_id" jdbcType="INTEGER" property="versionId" />
<result column="update_status" jdbcType="INTEGER" property="updateStatus" />
<result column="version_code" jdbcType="INTEGER" property="versionCode" />
<result column="version_name" jdbcType="VARCHAR" property="versionName" />
<result column="upload_time" jdbcType="VARCHAR" property="uploadTime" />
<result column="apk_size" jdbcType="INTEGER" property="apkSize" />
<result column="app_key" jdbcType="VARCHAR" property="appKey" />
<result column="modify_content" jdbcType="LONGVARCHAR" property="modifyContent" />
<result column="download_url" jdbcType="LONGVARCHAR" property="downloadUrl" />
<result column="apk_md5" jdbcType="LONGVARCHAR" property="apkMd5" />
</resultMap>
</mapper>
================================================
FILE: src/main/resources/static/css/main.css
================================================
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
body {
margin: 0;
padding: 0;
font-weight: 400;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 1rem;
line-height: 1.58;
color: #333;
background-color: #f4f4f4;
}
body:before {
height: 50%;
width: 100%;
position: absolute;
top: 0;
left: 0;
background: #128ff2;
content: "";
z-index: 0;
}
.clearfix:after {
display: block;
content: "";
clear: both;
}
h1, h2, h3, h4, h5, h6 {
margin-top: 20px;
margin-bottom: 20px;
}
h1 {
font-size: 1.7em;
}
a {
color: #128ff2;
}
button {
box-shadow: none;
border: 1px solid transparent;
font-size: 14px;
outline: none;
line-height: 100%;
white-space: nowrap;
vertical-align: middle;
padding: 0.6rem 1rem;
border-radius: 2px;
transition: all 0.2s ease-in-out;
cursor: pointer;
min-height: 38px;
}
input {
font-size: 1rem;
}
input[type="file"] {
border: 1px solid #128ff2;
padding: 6px;
max-width: 100%;
}
.file-input {
width: 100%;
}
.version-input {
width: 100%;
}
.submit-btn {
background-color: #128ff2;
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.12);
color: #fff;
display: inline-block;
margin-top: 15px;
min-width: 100px;
}
.reset-btn {
background-color: gray;
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.12);
color: #fff;
display: inline-block;
margin-top: 15px;
min-width: 100px;
}
.update-container {
max-width: 750px;
margin-left: auto;
margin-right: auto;
background-color: #fff;
box-shadow: 0 1px 11px rgba(0, 0, 0, 0.27);
margin-top: 60px;
min-height: 400px;
position: relative;
padding: 20px;
}
.update-header {
border-bottom: 1px solid #ececec;
}
.upload-header h2 {
font-weight: 500;
}
.apk-upload {
padding-bottom: 20px;
margin-bottom: 20px;
border-bottom: 1px solid #e8e8e8;
}
.update-response {
overflow-x: hidden;
word-break: break-all;
}
================================================
FILE: src/main/resources/static/js/upload.js
================================================
var versionId = 0; //保存的临时变量
function onAddInfo() {
var form = document.getElementById('infoForm');
var fd = new FormData(form);
$.ajax({
url: "/update/addVersionInfo",
type: "POST",
data: fd,
dataType: "json",
processData: false, // 告诉jQuery不要去处理发送的数据
contentType: false, // 告诉jQuery不要去设置Content-Type请求头
success: function (data) {
if (data["Code"] === 0) {
$('#result').html("版本信息保存成功!");
versionId = data['Data']['versionId'];
} else {
$('#result').html("版本信息保存失败:" + data['Msg']);
}
},
error: function (error) {
$('#result').html("请求错误:" + JSON.stringify(error));
}
});
return false;
}
function onUpload() {
if (versionId === 0) {
alert('请先填写应用版本信息!')
return false;
}
var fd = new FormData();
var files = document.querySelector('input[type=file]').files;
fd.append("versionId", versionId);
fd.append("file", files[0]);
$.ajax({
url: "/update/uploadApk",
type: "POST",
data: fd,
dataType: "json",
processData: false, // 告诉jQuery不要去处理发送的数据
contentType: false, // 告诉jQuery不要去设置Content-Type请求头
success: function (data) {
if (data["Code"] === 0) {
$('#result').html("APK文件上传成功!");
versionId = data['Data']['versionId'];
} else {
$('#result').html("APK文件上传失败:" + data['Msg']);
}
},
error: function (error) {
$('#result').html("请求错误:" + error.toString());
}
});
return false;
}
================================================
FILE: src/main/resources/templates/index.html
================================================
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>XUpdate Service</title>
<link rel="stylesheet" href="../static/css/main.css"/>
</head>
<body>
<noscript>
<h2>Sorry! Your browser doesn't support Javascript</h2>
</noscript>
<div class="update-container">
<div class="update-header">
<h2>APP版本管理</h2>
</div>
<div class="app-version-info">
<form id="infoForm" name="infoForm" target="nm_iframe">
<label>应用唯一号(App Key): </label><br>
<input id="appKey" type="text" name="appKey" class="version-input" required/><br>
<label>版本名: [例如:1.0.24]</label><br>
<input id="versionName" type="text" name="versionName" class="version-input" required/><br>
<label>版本号: [例如:24]</label><br>
<input id="versionCode" type="number" name="versionCode" class="version-input" required/><br>
<label>是否强制更新: </label>
<input id="updateStatus1" type="radio" value="1" name="updateStatus">否</input>
<input id="updateStatus2" type="radio" value="2" name="updateStatus">是</input><br>
<label>更新内容: </label><br>
<textarea id="modifyContent" name="modifyContent" rows="5" cols="100" class="version-input"
placeholder="请输入更新日志..."></textarea>
<div>
<button type="submit" class="submit-btn" onclick="onAddInfo()">保存</button>
<button type="reset" class="reset-btn">重置</button>
</div>
</form>
<br>
<form id="uploadForm" name="uploadForm" target="nm_iframe">
<label>APK文件: </label><br>
<input id="file" type="file" name="file" class="file-input" required/>
<button type="submit" class="submit-btn" onclick="onUpload()">保存</button>
</form><br>
<iframe id="id_iframe" name="nm_iframe" style="display:none;"></iframe>
<div class="update-response">
<div id="result"></div>
</div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<script src="../static/js/upload.js" ></script>
</body>
</html>
================================================
FILE: src/test/java/com/xuexiang/xupdateservice/XUpdateServiceApplicationTests.java
================================================
package com.xuexiang.xupdateservice;
import com.xuexiang.xupdateservice.mapper.AppVersionInfoMapper;
import com.xuexiang.xupdateservice.model.AppVersionInfo;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import tk.mybatis.mapper.entity.Condition;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest
public class XUpdateServiceApplicationTests {
@Autowired
private AppVersionInfoMapper appVersionInfoMapper;//这里会报错,但是并不会影响
@Test
public void contextLoads() {
}
@Test
public void SelectByConditionMapper(){
Condition condition = new Condition(AppVersionInfo.class);
condition.createCriteria().andEqualTo("appKey", "test");
condition.setOrderByClause("version_code desc");
List<AppVersionInfo> list = appVersionInfoMapper.selectByExample(condition);
System.out.println(list.size());
}
}
gitextract_7e1osrry/
├── .gitignore
├── LICENSE
├── README.md
├── build.gradle
├── db/
│ └── generatorConfig.xml
├── generator.gradle
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── libs/
│ └── mysql-connector-java-5.1.35.jar
├── package/
│ └── xupdateservice-1.0.0.jar
├── settings.gradle
├── sql/
│ └── xupdate.sql
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── xuexiang/
│ │ │ └── xupdateservice/
│ │ │ ├── XUpdateServiceApplication.java
│ │ │ ├── api/
│ │ │ │ ├── request/
│ │ │ │ │ ├── ApiRequest.java
│ │ │ │ │ └── PageQuery.java
│ │ │ │ └── response/
│ │ │ │ ├── ApiResult.java
│ │ │ │ ├── LoginInfo.java
│ │ │ │ ├── PageData.java
│ │ │ │ └── UploadFileResponse.java
│ │ │ ├── component/
│ │ │ │ ├── annotation/
│ │ │ │ │ ├── CurrentAccount.java
│ │ │ │ │ ├── LimitedRequest.java
│ │ │ │ │ ├── LoginRequired.java
│ │ │ │ │ └── QuickRequest.java
│ │ │ │ ├── aspect/
│ │ │ │ │ ├── LimitedRequestAspect.java
│ │ │ │ │ └── RestControllerAspect.java
│ │ │ │ ├── interceptor/
│ │ │ │ │ ├── CorsInterceptor.java
│ │ │ │ │ └── QuickRequestInterceptor.java
│ │ │ │ └── token/
│ │ │ │ ├── AuthenticationInterceptor.java
│ │ │ │ └── CurrentAccountMethodArgumentResolver.java
│ │ │ ├── config/
│ │ │ │ ├── Constants.java
│ │ │ │ ├── FileStorageProperties.java
│ │ │ │ └── WebAppConfigurer.java
│ │ │ ├── controller/
│ │ │ │ ├── AccountController.java
│ │ │ │ ├── HomeController.java
│ │ │ │ └── UpdateController.java
│ │ │ ├── exception/
│ │ │ │ ├── ApiException.java
│ │ │ │ ├── ApiExceptionHandler.java
│ │ │ │ ├── FileNotFoundException.java
│ │ │ │ └── FileStorageException.java
│ │ │ ├── mapper/
│ │ │ │ ├── AccountMapper.java
│ │ │ │ └── AppVersionInfoMapper.java
│ │ │ ├── model/
│ │ │ │ ├── Account.java
│ │ │ │ └── AppVersionInfo.java
│ │ │ ├── service/
│ │ │ │ ├── AccountService.java
│ │ │ │ ├── FileStorageService.java
│ │ │ │ ├── UpdateService.java
│ │ │ │ └── impl/
│ │ │ │ ├── AccountServiceImpl.java
│ │ │ │ ├── FileStorageServiceImpl.java
│ │ │ │ └── UpdateServiceImpl.java
│ │ │ └── utils/
│ │ │ ├── AspectJUtils.java
│ │ │ ├── DateUtils.java
│ │ │ ├── FileUtils.java
│ │ │ ├── IpUtils.java
│ │ │ ├── Md5Utils.java
│ │ │ ├── QuickRequestUtils.java
│ │ │ ├── RandomGUID.java
│ │ │ └── TokenUtils.java
│ │ └── resources/
│ │ ├── application.yml
│ │ ├── db-mysql.properties
│ │ ├── mybatis_mapper/
│ │ │ ├── AccountMapper.xml
│ │ │ └── AppVersionInfoMapper.xml
│ │ ├── static/
│ │ │ ├── css/
│ │ │ │ └── main.css
│ │ │ └── js/
│ │ │ └── upload.js
│ │ └── templates/
│ │ └── index.html
│ └── test/
│ └── java/
│ └── com/
│ └── xuexiang/
│ └── xupdateservice/
│ └── XUpdateServiceApplicationTests.java
└── uploads/
├── xupdate_demo_1.0.2.apk
└── xupdate_demo_1.0.apk
SYMBOL INDEX (265 symbols across 44 files)
FILE: sql/xupdate.sql
type `account` (line 25) | CREATE TABLE `account` (
type `app_version_info` (line 58) | CREATE TABLE `app_version_info` (
FILE: src/main/java/com/xuexiang/xupdateservice/XUpdateServiceApplication.java
class XUpdateServiceApplication (line 7) | @SpringBootApplication
method main (line 11) | public static void main(String[] args) {
FILE: src/main/java/com/xuexiang/xupdateservice/api/request/ApiRequest.java
class ApiRequest (line 9) | public class ApiRequest<T> {
FILE: src/main/java/com/xuexiang/xupdateservice/api/request/PageQuery.java
class PageQuery (line 7) | public class PageQuery {
FILE: src/main/java/com/xuexiang/xupdateservice/api/response/ApiResult.java
class ApiResult (line 29) | public class ApiResult<T> {
method getCode (line 38) | @JsonIgnore
method setCode (line 43) | @JsonIgnore
method getMsg (line 49) | @JsonIgnore
method setMsg (line 54) | @JsonIgnore
method getData (line 60) | @JsonIgnore
method setData (line 65) | @JsonIgnore
method setError (line 71) | @JsonIgnore
method toString (line 78) | @Override
method error (line 95) | public static ApiResult error(ApiException ex) {
method error (line 108) | public static ApiResult error(int code, String msg) {
FILE: src/main/java/com/xuexiang/xupdateservice/api/response/LoginInfo.java
class LoginInfo (line 9) | public class LoginInfo {
method getUser (line 15) | public Account getUser() {
method setUser (line 19) | public LoginInfo setUser(Account account) {
method getToken (line 24) | public String getToken() {
method setToken (line 28) | public LoginInfo setToken(String token) {
FILE: src/main/java/com/xuexiang/xupdateservice/api/response/PageData.java
class PageData (line 11) | public class PageData<T> {
method PageData (line 21) | public PageData() {
method PageData (line 24) | public PageData(int pageNum, int pageSize) {
method getArray (line 29) | public List<T> getArray() {
method setArray (line 33) | public PageData<T> setArray(List<T> array) {
method getTotal (line 38) | public long getTotal() {
method setTotal (line 42) | public PageData<T> setTotal(long total) {
method getPageNum (line 47) | public int getPageNum() {
method setPageNum (line 51) | public PageData<T> setPageNum(int pageNum) {
method getPageSize (line 56) | public int getPageSize() {
method setPageSize (line 60) | public PageData<T> setPageSize(int pageSize) {
FILE: src/main/java/com/xuexiang/xupdateservice/api/response/UploadFileResponse.java
class UploadFileResponse (line 10) | public class UploadFileResponse {
method UploadFileResponse (line 16) | public UploadFileResponse(String fileName, String fileDownloadUri, Str...
method getFileName (line 23) | public String getFileName() {
method setFileName (line 27) | public void setFileName(String fileName) {
method getFileDownloadUri (line 31) | public String getFileDownloadUri() {
method setFileDownloadUri (line 35) | public void setFileDownloadUri(String fileDownloadUri) {
method getFileType (line 39) | public String getFileType() {
method setFileType (line 43) | public void setFileType(String fileType) {
method getSize (line 47) | public long getSize() {
method setSize (line 51) | public void setSize(long size) {
FILE: src/main/java/com/xuexiang/xupdateservice/component/aspect/LimitedRequestAspect.java
class LimitedRequestAspect (line 25) | @Aspect
method requestLimit (line 29) | @Before("@within(org.springframework.web.bind.annotation.RestControlle...
FILE: src/main/java/com/xuexiang/xupdateservice/component/aspect/RestControllerAspect.java
class RestControllerAspect (line 26) | @Aspect
method apiLog (line 38) | @Around("@within(org.springframework.web.bind.annotation.RestControlle...
method needToLog (line 73) | private boolean needToLog(Method method) {
FILE: src/main/java/com/xuexiang/xupdateservice/component/interceptor/CorsInterceptor.java
class CorsInterceptor (line 15) | public class CorsInterceptor implements HandlerInterceptor {
method preHandle (line 16) | @Override
method postHandle (line 28) | @Override
method afterCompletion (line 33) | @Override
FILE: src/main/java/com/xuexiang/xupdateservice/component/interceptor/QuickRequestInterceptor.java
class QuickRequestInterceptor (line 26) | public class QuickRequestInterceptor implements HandlerInterceptor {
method preHandle (line 28) | @Override
method postHandle (line 76) | @Override
method afterCompletion (line 81) | @Override
FILE: src/main/java/com/xuexiang/xupdateservice/component/token/AuthenticationInterceptor.java
class AuthenticationInterceptor (line 30) | public class AuthenticationInterceptor implements HandlerInterceptor {
method preHandle (line 36) | @Override
method postHandle (line 83) | @Override
method afterCompletion (line 89) | @Override
FILE: src/main/java/com/xuexiang/xupdateservice/component/token/CurrentAccountMethodArgumentResolver.java
class CurrentAccountMethodArgumentResolver (line 20) | public class CurrentAccountMethodArgumentResolver implements HandlerMeth...
method supportsParameter (line 22) | @Override
method resolveArgument (line 28) | @Override
FILE: src/main/java/com/xuexiang/xupdateservice/config/Constants.java
class Constants (line 7) | public class Constants {
FILE: src/main/java/com/xuexiang/xupdateservice/config/FileStorageProperties.java
class FileStorageProperties (line 10) | @Component
method isKeepName (line 17) | public boolean isKeepName() {
method setKeepName (line 21) | public void setKeepName(boolean keepName) {
method getFileDirectory (line 25) | public String getFileDirectory() {
method setFileDirectory (line 29) | public void setFileDirectory(String fileDirectory) {
FILE: src/main/java/com/xuexiang/xupdateservice/config/WebAppConfigurer.java
class WebAppConfigurer (line 19) | @EnableWebMvc
method addInterceptors (line 23) | @Override
method addArgumentResolvers (line 30) | @Override
method addCorsMappings (line 36) | @Override
method currentAccountMethodArgumentResolver (line 47) | @Bean
method authenticationInterceptor (line 52) | @Bean
method quickRequestInterceptor (line 57) | @Bean
method addResourceHandlers (line 69) | @Override
FILE: src/main/java/com/xuexiang/xupdateservice/controller/AccountController.java
class AccountController (line 25) | @RestController
method pageQueryAccounts (line 32) | @ResponseBody
method getAllAccount (line 38) | @ResponseBody
method login (line 44) | @ResponseBody
method getCurrentAccount (line 62) | @LoginRequired
method logout (line 69) | @LoginRequired
method checkAccountExist (line 77) | @ResponseBody
method register (line 83) | @ResponseBody
method deleteAccount (line 93) | @ResponseBody
method updateAccountInfo (line 99) | @ResponseBody
FILE: src/main/java/com/xuexiang/xupdateservice/controller/HomeController.java
class HomeController (line 7) | @Controller
method index (line 10) | @GetMapping(value = {"/", "/index"})
FILE: src/main/java/com/xuexiang/xupdateservice/controller/UpdateController.java
class UpdateController (line 32) | @RestController
method doCheckVersion (line 45) | @ResponseBody
method pageQueryVersions (line 51) | @ResponseBody
method getAllVersions (line 57) | @ResponseBody
method register (line 63) | @ResponseBody
method deleteAppVersionInfo (line 69) | @ResponseBody
method updateAppVersionInfo (line 75) | @ResponseBody
method addAppVersionInfo (line 81) | @ResponseBody
method addNewAppVersion (line 92) | private ApiResult addNewAppVersion(AppVersionInfo appVersionInfo) {
method uploadApkFile (line 112) | @PostMapping("/uploadApk")
method updateVersionInfo (line 137) | private void updateVersionInfo(String fileName, AppVersionInfo appVers...
method addAppVersion (line 145) | @ResponseBody
method getOnErrorApiResult (line 178) | private <T> ApiResult<T> getOnErrorApiResult(ApiResult<T> apiResult, S...
method downloadFile (line 183) | @GetMapping("/apk/{fileName:.+}")
FILE: src/main/java/com/xuexiang/xupdateservice/exception/ApiException.java
class ApiException (line 7) | public class ApiException extends Exception {
method ApiException (line 14) | public ApiException(String message, int code) {
method ApiException (line 19) | public ApiException(Throwable e, int code) {
method getCode (line 24) | public int getCode() {
class ERROR (line 31) | public static class ERROR {
FILE: src/main/java/com/xuexiang/xupdateservice/exception/ApiExceptionHandler.java
class ApiExceptionHandler (line 16) | @RestControllerAdvice
method apiErrorHandler (line 27) | @ExceptionHandler(value = Exception.class)
FILE: src/main/java/com/xuexiang/xupdateservice/exception/FileNotFoundException.java
class FileNotFoundException (line 6) | @ResponseStatus(HttpStatus.NOT_FOUND)
method FileNotFoundException (line 9) | public FileNotFoundException(String message) {
method FileNotFoundException (line 13) | public FileNotFoundException(String message, Throwable cause) {
FILE: src/main/java/com/xuexiang/xupdateservice/exception/FileStorageException.java
class FileStorageException (line 3) | public class FileStorageException extends RuntimeException {
method FileStorageException (line 4) | public FileStorageException(String message) {
method FileStorageException (line 8) | public FileStorageException(String message, Throwable cause) {
FILE: src/main/java/com/xuexiang/xupdateservice/mapper/AccountMapper.java
type AccountMapper (line 6) | public interface AccountMapper extends Mapper<Account> {
FILE: src/main/java/com/xuexiang/xupdateservice/mapper/AppVersionInfoMapper.java
type AppVersionInfoMapper (line 6) | public interface AppVersionInfoMapper extends Mapper<AppVersionInfo> {
FILE: src/main/java/com/xuexiang/xupdateservice/model/Account.java
class Account (line 6) | @Table(name = "account")
method getAccountId (line 33) | public Integer getAccountId() {
method setAccountId (line 40) | public void setAccountId(Integer accountId) {
method getLoginName (line 47) | public String getLoginName() {
method setLoginName (line 54) | public void setLoginName(String loginName) {
method getPassword (line 61) | public String getPassword() {
method setPassword (line 68) | public void setPassword(String password) {
method getNick (line 75) | public String getNick() {
method setNick (line 82) | public void setNick(String nick) {
method getAuthority (line 89) | public String getAuthority() {
method setAuthority (line 96) | public void setAuthority(String authority) {
method getAvatar (line 103) | public String getAvatar() {
method setAvatar (line 110) | public void setAvatar(String avatar) {
method getPhone (line 117) | public String getPhone() {
method setPhone (line 124) | public void setPhone(String phone) {
method getAddress (line 131) | public String getAddress() {
method setAddress (line 138) | public void setAddress(String address) {
method getRegisterTime (line 145) | public Date getRegisterTime() {
method setRegisterTime (line 152) | public void setRegisterTime(Date registerTime) {
FILE: src/main/java/com/xuexiang/xupdateservice/model/AppVersionInfo.java
class AppVersionInfo (line 5) | @Table(name = "app_version_info")
method getVersionId (line 41) | public Integer getVersionId() {
method setVersionId (line 48) | public void setVersionId(Integer versionId) {
method getUpdateStatus (line 55) | public Integer getUpdateStatus() {
method setUpdateStatus (line 62) | public void setUpdateStatus(Integer updateStatus) {
method getVersionCode (line 69) | public Integer getVersionCode() {
method setVersionCode (line 76) | public void setVersionCode(Integer versionCode) {
method getVersionName (line 83) | public String getVersionName() {
method setVersionName (line 90) | public void setVersionName(String versionName) {
method getUploadTime (line 97) | public String getUploadTime() {
method setUploadTime (line 104) | public void setUploadTime(String uploadTime) {
method getApkSize (line 111) | public Integer getApkSize() {
method setApkSize (line 118) | public void setApkSize(Integer apkSize) {
method getAppKey (line 125) | public String getAppKey() {
method setAppKey (line 132) | public void setAppKey(String appKey) {
method getModifyContent (line 139) | public String getModifyContent() {
method setModifyContent (line 146) | public void setModifyContent(String modifyContent) {
method getDownloadUrl (line 153) | public String getDownloadUrl() {
method setDownloadUrl (line 160) | public void setDownloadUrl(String downloadUrl) {
method getApkMd5 (line 167) | public String getApkMd5() {
method setApkMd5 (line 174) | public void setApkMd5(String apkMd5) {
FILE: src/main/java/com/xuexiang/xupdateservice/service/AccountService.java
type AccountService (line 14) | public interface AccountService {
method getAllAccount (line 23) | PageData<Account> getAllAccount(int pageNum, int pageSize);
method getAllAccount (line 30) | List<Account> getAllAccount();
method updateAccount (line 39) | boolean updateAccount(Account account);
method deleteAccount (line 47) | boolean deleteAccount(int accountId);
method registerAccount (line 55) | boolean registerAccount(Account account);
method checkAccount (line 62) | Account checkAccount(String loginName);
method loginAccount (line 70) | Account loginAccount(String loginName, String password);
FILE: src/main/java/com/xuexiang/xupdateservice/service/FileStorageService.java
type FileStorageService (line 12) | public interface FileStorageService {
method storeFile (line 21) | String storeFile(MultipartFile file) throws Exception;
method loadFileAsResource (line 29) | Resource loadFileAsResource(String fileName) throws Exception;
FILE: src/main/java/com/xuexiang/xupdateservice/service/UpdateService.java
type UpdateService (line 12) | public interface UpdateService {
method getLatestAppVersionInfo (line 34) | AppVersionInfo getLatestAppVersionInfo(int versionCode, String appKey);
method getAllAppVersionInfo (line 42) | List<AppVersionInfo> getAllAppVersionInfo(String appKey);
method getAllAppVersionInfo (line 49) | List<AppVersionInfo> getAllAppVersionInfo();
method getAllAppVersionInfo (line 58) | PageData<AppVersionInfo> getAllAppVersionInfo(int pageNum, int pageSize);
method getAppVersionInfo (line 66) | AppVersionInfo getAppVersionInfo(int versionCode, String appKey);
method addAppVersionInfo (line 74) | boolean addAppVersionInfo(AppVersionInfo appVersionInfo);
method updateAppVersionInfo (line 82) | boolean updateAppVersionInfo(AppVersionInfo appVersionInfo);
method deleteAppVersionInfo (line 91) | boolean deleteAppVersionInfo(int versionId);
FILE: src/main/java/com/xuexiang/xupdateservice/service/impl/AccountServiceImpl.java
class AccountServiceImpl (line 20) | @Service(value = "accountService")
method getAllAccount (line 26) | @Override
method getAllAccount (line 37) | @Override
method updateAccount (line 42) | @Override
method deleteAccount (line 47) | @Override
method registerAccount (line 52) | @Override
method checkAccount (line 57) | @Override
method loginAccount (line 64) | @Override
method selectFirstAccountByCondition (line 72) | private Account selectFirstAccountByCondition(Condition condition) {
FILE: src/main/java/com/xuexiang/xupdateservice/service/impl/FileStorageServiceImpl.java
class FileStorageServiceImpl (line 22) | @Service(value = "fileService")
method FileStorageServiceImpl (line 29) | @Autowired
method storeFile (line 41) | @Override
method loadFileAsResource (line 67) | @Override
FILE: src/main/java/com/xuexiang/xupdateservice/service/impl/UpdateServiceImpl.java
class UpdateServiceImpl (line 21) | @Service(value = "updateService")
method getLatestAppVersionInfo (line 27) | @Override
method getAllAppVersionInfo (line 47) | @Override
method getAllAppVersionInfo (line 55) | @Override
method getAllAppVersionInfo (line 66) | @Override
method getAppVersionInfo (line 78) | @Override
method addAppVersionInfo (line 92) | @Override
method updateAppVersionInfo (line 97) | @Override
method deleteAppVersionInfo (line 102) | @Override
FILE: src/main/java/com/xuexiang/xupdateservice/utils/AspectJUtils.java
class AspectJUtils (line 20) | public final class AspectJUtils {
method AspectJUtils (line 22) | private AspectJUtils() {
method getMethodName (line 32) | public static String getMethodName(JoinPoint joinPoint) {
method getMethodParams (line 47) | public static String getMethodParams(ProceedingJoinPoint joinPoint) {
method deleteSensitiveContent (line 77) | public static String deleteSensitiveContent(Object obj) {
method getSensitiveFieldList (line 100) | private static List<String> getSensitiveFieldList() {
FILE: src/main/java/com/xuexiang/xupdateservice/utils/DateUtils.java
class DateUtils (line 33) | public final class DateUtils {
method DateUtils (line 74) | private DateUtils() {
method millis2String (line 87) | public static String millis2String(final long millis, final DateFormat...
method date2String (line 99) | public static String date2String(final Date date, final DateFormat for...
method string2Millis (line 115) | public static long string2Millis(final String time, final DateFormat f...
method string2Date (line 134) | public static Date string2Date(final String time, final DateFormat for...
method date2Millis (line 151) | public static long date2Millis(final Date date) {
method millis2Date (line 161) | public static Date millis2Date(final long millis) {
method getAgeByBirthDay (line 174) | public static int getAgeByBirthDay(final String birthDay, final DateFo...
method getAgeByBirthDay (line 185) | public static int getAgeByBirthDay(Date birthDay) throws IllegalArgume...
method getNowMills (line 221) | public static long getNowMills() {
method getNowString (line 232) | public static String getNowString(final DateFormat format) {
method getNowDate (line 241) | public static Date getNowDate() {
method getWeekIndex (line 261) | public static int getWeekIndex(final String time, final DateFormat for...
method getYear (line 271) | public static int getYear(final Date date) {
method getMonth (line 283) | public static int getMonth(final Date date) {
method getDay (line 295) | public static int getDay(final Date date) {
method getWeekIndex (line 315) | public static int getWeekIndex(final Date date) {
method getWeekIndex (line 335) | public static int getWeekIndex(final long millis) {
FILE: src/main/java/com/xuexiang/xupdateservice/utils/FileUtils.java
class FileUtils (line 11) | public final class FileUtils {
method FileUtils (line 13) | private FileUtils() {
method randomFileName (line 22) | public static String randomFileName(String filePath) {
method getFileExtension (line 34) | public static String getFileExtension(final String filePath) {
method getApkFileSize (line 48) | public static int getApkFileSize(final File file) {
method getFileLength (line 59) | private static long getFileLength(final File file) {
method isFile (line 70) | private static boolean isFile(final File file) {
FILE: src/main/java/com/xuexiang/xupdateservice/utils/IpUtils.java
class IpUtils (line 21) | public final class IpUtils {
method IpUtils (line 23) | private IpUtils() {
method getIpAddr (line 40) | public static String getIpAddr(HttpServletRequest request) {
method getRealIp (line 68) | public static String getRealIp(HttpServletRequest request) {
method getIp (line 101) | private static String getIp(HttpServletRequest request, String ip){
method getServiceIp (line 120) | public static String getServiceIp() {
FILE: src/main/java/com/xuexiang/xupdateservice/utils/Md5Utils.java
class Md5Utils (line 35) | public final class Md5Utils {
method Md5Utils (line 37) | private Md5Utils() {
method getFileMD5 (line 41) | public static String getFileMD5(File file) {
method isFileValid (line 73) | public static boolean isFileValid(String md5, File file) {
method bytes2Hex (line 83) | private static String bytes2Hex(byte[] src) {
FILE: src/main/java/com/xuexiang/xupdateservice/utils/QuickRequestUtils.java
class QuickRequestUtils (line 16) | public final class QuickRequestUtils {
method QuickRequestUtils (line 18) | private QuickRequestUtils() {
method isQuickRequest (line 39) | public static boolean isQuickRequest(String key, QuickRequest quickReq...
method isQuickRequest (line 59) | public static boolean isQuickRequest(String key, LimitedRequest limit) {
FILE: src/main/java/com/xuexiang/xupdateservice/utils/RandomGUID.java
class RandomGUID (line 16) | public final class RandomGUID {
method run (line 32) | public void run() {
method getRandomGUID (line 46) | public static String getRandomGUID() {
method RandomGUID (line 50) | public RandomGUID() {
method RandomGUID (line 53) | public RandomGUID(boolean secure) {
method getRandomGUID (line 60) | private void getRandomGUID(boolean secure) {
method toString (line 103) | public String toString() {
FILE: src/main/java/com/xuexiang/xupdateservice/utils/TokenUtils.java
class TokenUtils (line 20) | public final class TokenUtils {
method createJwtToken (line 33) | public static String createJwtToken(String id) {
method createJwtToken (line 49) | public static String createJwtToken(String id, String issuer, String s...
method parseJWT (line 82) | public static Claims parseJWT(String jwt) {
method parseToken (line 97) | public static String parseToken(HttpServletRequest request) {
method main (line 105) | public static void main(String[] args) {
FILE: src/main/resources/static/js/upload.js
function onAddInfo (line 3) | function onAddInfo() {
function onUpload (line 29) | function onUpload() {
FILE: src/test/java/com/xuexiang/xupdateservice/XUpdateServiceApplicationTests.java
class XUpdateServiceApplicationTests (line 14) | @RunWith(SpringRunner.class)
method contextLoads (line 21) | @Test
method SelectByConditionMapper (line 26) | @Test
Condensed preview — 70 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (152K chars).
[
{
"path": ".gitignore",
"chars": 276,
"preview": ".gradle\n/build/\n!gradle/wrapper/gradle-wrapper.jar\n\n### STS ###\n.apt_generated\n.classpath\n.factorypath\n.project\n.setting"
},
{
"path": "LICENSE",
"chars": 10256,
"preview": "Apache License\nVersion 2.0, January 2004\nhttp://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AN"
},
{
"path": "README.md",
"chars": 3302,
"preview": "# XUpdateService\n\n使用Spring Boot简易搭建,Gradle构建,为XUpdate提供的更新服务。\n\n## 内容\n\n* 使用spring boot快速搭建,并使用Gradle进行构建【区别Maven】。\n\n* 使用阿"
},
{
"path": "build.gradle",
"chars": 1645,
"preview": "plugins {\n id 'org.springframework.boot' version '2.4.1'\n id 'io.spring.dependency-management' version '1.0.10.REL"
},
{
"path": "db/generatorConfig.xml",
"chars": 1452,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE generatorConfiguration\n PUBLIC \"-//mybatis.org//DTD MyBatis Gene"
},
{
"path": "generator.gradle",
"chars": 1857,
"preview": "def getDbProperties = {\n def properties = new Properties()\n file(\"src/main/resources/db-mysql.properties\").withInp"
},
{
"path": "gradle/wrapper/gradle-wrapper.properties",
"chars": 202,
"preview": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributi"
},
{
"path": "gradle.properties",
"chars": 184,
"preview": "\n\nmodelPackage=com.xuexiang.xupdateservice.model\n#生成的mapper接口类所在包\nmapperPackage=com.xuexiang.xupdateservice.mapper\n#生成的m"
},
{
"path": "gradlew",
"chars": 5299,
"preview": "#!/usr/bin/env sh\n\n##############################################################################\n##\n## Gradle start up"
},
{
"path": "gradlew.bat",
"chars": 2176,
"preview": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem "
},
{
"path": "settings.gradle",
"chars": 36,
"preview": "rootProject.name = 'xupdateservice'\n"
},
{
"path": "sql/xupdate.sql",
"chars": 4288,
"preview": "-- MySQL dump 10.13 Distrib 5.7.17, for Win64 (x86_64)\n--\n-- Host: localhost Database: xupdate\n-- ------------------"
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/XUpdateServiceApplication.java",
"chars": 433,
"preview": "package com.xuexiang.xupdateservice;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot"
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/api/request/ApiRequest.java",
"chars": 173,
"preview": "package com.xuexiang.xupdateservice.api.request;\n\n/**\n * 基础请求包装类\n *\n * @author xuexiang\n * @since 2018/7/16 下午7:00\n */\np"
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/api/request/PageQuery.java",
"chars": 242,
"preview": "package com.xuexiang.xupdateservice.api.request;\n\n/**\n * @author xuexiang\n * @since 2018/8/15 上午12:26\n */\npublic class P"
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/api/response/ApiResult.java",
"chars": 2545,
"preview": "/*\n * Copyright (C) 2018 xuexiangjys(xuexiangjys@163.com)\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/api/response/LoginInfo.java",
"chars": 572,
"preview": "package com.xuexiang.xupdateservice.api.response;\n\nimport com.xuexiang.xupdateservice.model.Account;\n\n/**\n * @author xue"
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/api/response/PageData.java",
"chars": 1107,
"preview": "package com.xuexiang.xupdateservice.api.response;\n\nimport java.util.List;\n\n/**\n * 分页请求的响应数据\n *\n * @author xuexiang\n * @s"
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/api/response/UploadFileResponse.java",
"chars": 1155,
"preview": "package com.xuexiang.xupdateservice.api.response;\n\n\n/**\n * 文件上传返回结果\n *\n * @author xuexiang\n * @since 2018/7/18 下午3:55\n *"
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/component/annotation/CurrentAccount.java",
"chars": 468,
"preview": "package com.xuexiang.xupdateservice.component.annotation;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.ann"
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/component/annotation/LimitedRequest.java",
"chars": 657,
"preview": "package com.xuexiang.xupdateservice.component.annotation;\n\nimport org.springframework.core.Ordered;\nimport org.springfra"
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/component/annotation/LoginRequired.java",
"chars": 429,
"preview": "package com.xuexiang.xupdateservice.component.annotation;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.ann"
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/component/annotation/QuickRequest.java",
"chars": 689,
"preview": "package com.xuexiang.xupdateservice.component.annotation;\n\nimport org.springframework.core.Ordered;\nimport org.springfra"
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/component/aspect/LimitedRequestAspect.java",
"chars": 1682,
"preview": "package com.xuexiang.xupdateservice.component.aspect;\n\nimport com.xuexiang.xupdateservice.component.annotation.LimitedRe"
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/component/aspect/RestControllerAspect.java",
"chars": 2850,
"preview": "package com.xuexiang.xupdateservice.component.aspect;\n\nimport com.xuexiang.xupdateservice.utils.AspectJUtils;\nimport com"
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/component/interceptor/CorsInterceptor.java",
"chars": 1457,
"preview": "package com.xuexiang.xupdateservice.component.interceptor;\n\nimport org.springframework.web.servlet.HandlerInterceptor;\ni"
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/component/interceptor/QuickRequestInterceptor.java",
"chars": 3136,
"preview": "package com.xuexiang.xupdateservice.component.interceptor;\n\nimport com.xuexiang.xupdateservice.component.annotation.Logi"
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/component/token/AuthenticationInterceptor.java",
"chars": 3241,
"preview": "package com.xuexiang.xupdateservice.component.token;\n\nimport com.xuexiang.xupdateservice.component.annotation.LoginRequi"
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/component/token/CurrentAccountMethodArgumentResolver.java",
"chars": 1654,
"preview": "package com.xuexiang.xupdateservice.component.token;\n\nimport com.xuexiang.xupdateservice.component.annotation.CurrentAcc"
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/config/Constants.java",
"chars": 226,
"preview": "package com.xuexiang.xupdateservice.config;\n\n/**\n * @author xuexiang\n * @since 2018/8/6 下午5:07\n */\npublic class Constant"
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/config/FileStorageProperties.java",
"chars": 724,
"preview": "package com.xuexiang.xupdateservice.config;\n\nimport org.springframework.boot.context.properties.ConfigurationProperties;"
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/config/WebAppConfigurer.java",
"chars": 2831,
"preview": "package com.xuexiang.xupdateservice.config;\n\nimport com.xuexiang.xupdateservice.component.interceptor.QuickRequestInterc"
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/controller/AccountController.java",
"chars": 4094,
"preview": "package com.xuexiang.xupdateservice.controller;\n\nimport com.xuexiang.xupdateservice.api.request.PageQuery;\nimport com.xu"
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/controller/HomeController.java",
"chars": 304,
"preview": "package com.xuexiang.xupdateservice.controller;\n\n\nimport org.springframework.stereotype.Controller;\nimport org.springfra"
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/controller/UpdateController.java",
"chars": 8347,
"preview": "package com.xuexiang.xupdateservice.controller;\n\nimport com.xuexiang.xupdateservice.api.request.PageQuery;\nimport com.xu"
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/exception/ApiException.java",
"chars": 1302,
"preview": "package com.xuexiang.xupdateservice.exception;\n\n/**\n * @author xuexiang\n * @since 2018/8/6 下午3:11\n */\npublic class ApiEx"
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/exception/ApiExceptionHandler.java",
"chars": 1202,
"preview": "package com.xuexiang.xupdateservice.exception;\n\nimport com.xuexiang.xupdateservice.api.response.ApiResult;\nimport org.sl"
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/exception/FileNotFoundException.java",
"chars": 446,
"preview": "package com.xuexiang.xupdateservice.exception;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.w"
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/exception/FileStorageException.java",
"chars": 296,
"preview": "package com.xuexiang.xupdateservice.exception;\n\npublic class FileStorageException extends RuntimeException {\n public "
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/mapper/AccountMapper.java",
"chars": 194,
"preview": "package com.xuexiang.xupdateservice.mapper;\n\nimport com.xuexiang.xupdateservice.model.Account;\nimport tk.mybatis.mapper."
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/mapper/AppVersionInfoMapper.java",
"chars": 215,
"preview": "package com.xuexiang.xupdateservice.mapper;\n\nimport com.xuexiang.xupdateservice.model.AppVersionInfo;\nimport tk.mybatis."
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/model/Account.java",
"chars": 2594,
"preview": "package com.xuexiang.xupdateservice.model;\n\nimport java.util.Date;\nimport javax.persistence.*;\n\n@Table(name = \"account\")"
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/model/AppVersionInfo.java",
"chars": 3277,
"preview": "package com.xuexiang.xupdateservice.model;\n\nimport javax.persistence.*;\n\n@Table(name = \"app_version_info\")\npublic class "
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/service/AccountService.java",
"chars": 1181,
"preview": "package com.xuexiang.xupdateservice.service;\n\nimport com.xuexiang.xupdateservice.api.response.PageData;\nimport com.xuexi"
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/service/FileStorageService.java",
"chars": 530,
"preview": "package com.xuexiang.xupdateservice.service;\n\nimport org.springframework.core.io.Resource;\nimport org.springframework.we"
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/service/UpdateService.java",
"chars": 1747,
"preview": "package com.xuexiang.xupdateservice.service;\n\nimport com.xuexiang.xupdateservice.api.response.PageData;\nimport com.xuexi"
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/service/impl/AccountServiceImpl.java",
"chars": 2562,
"preview": "package com.xuexiang.xupdateservice.service.impl;\n\nimport com.github.pagehelper.Page;\nimport com.github.pagehelper.PageH"
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/service/impl/FileStorageServiceImpl.java",
"chars": 3224,
"preview": "package com.xuexiang.xupdateservice.service.impl;\n\nimport com.xuexiang.xupdateservice.config.FileStorageProperties;\nimpo"
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/service/impl/UpdateServiceImpl.java",
"chars": 3768,
"preview": "package com.xuexiang.xupdateservice.service.impl;\n\nimport com.github.pagehelper.Page;\nimport com.github.pagehelper.PageH"
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/utils/AspectJUtils.java",
"chars": 3352,
"preview": "package com.xuexiang.xupdateservice.utils;\n\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.fastjson.JSONObject;\nim"
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/utils/DateUtils.java",
"chars": 8745,
"preview": "/*\n * Copyright (C) 2018 nl-xx(xx@aecg.com.cn)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * "
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/utils/FileUtils.java",
"chars": 1684,
"preview": "package com.xuexiang.xupdateservice.utils;\n\nimport org.springframework.util.StringUtils;\n\nimport java.io.File;\n\n/**\n * @"
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/utils/IpUtils.java",
"chars": 4760,
"preview": "package com.xuexiang.xupdateservice.utils;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.slf4j.Logger;\nimport"
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/utils/Md5Utils.java",
"chars": 2777,
"preview": "/*\n * Copyright (C) 2018 xuexiangjys(xuexiangjys@163.com)\n *\n * Licensed under the Apache License, Version 2.0 (the \"Lic"
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/utils/QuickRequestUtils.java",
"chars": 2324,
"preview": "package com.xuexiang.xupdateservice.utils;\n\nimport com.xuexiang.xupdateservice.component.annotation.LimitedRequest;\nimpo"
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/utils/RandomGUID.java",
"chars": 2550,
"preview": "package com.xuexiang.xupdateservice.utils;\n\nimport java.net.InetAddress;\nimport java.net.UnknownHostException;\nimport ja"
},
{
"path": "src/main/java/com/xuexiang/xupdateservice/utils/TokenUtils.java",
"chars": 3148,
"preview": "package com.xuexiang.xupdateservice.utils;\n\nimport io.jsonwebtoken.Claims;\nimport io.jsonwebtoken.JwtBuilder;\nimport io."
},
{
"path": "src/main/resources/application.yml",
"chars": 1402,
"preview": "server:\n port: 1111\n\nspring:\n datasource:\n name: XUpdateService\n url: jdbc:mysql://localhost:3306/xupd"
},
{
"path": "src/main/resources/db-mysql.properties",
"chars": 425,
"preview": "#Mybatis Generator configuration\n##这边请换成你电脑上的绝对路径\n## Windows 配置\n## classPath=C:/Users/XUE/Documents/GitHub/XUpdateServic"
},
{
"path": "src/main/resources/mybatis_mapper/AccountMapper.xml",
"chars": 1011,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/"
},
{
"path": "src/main/resources/mybatis_mapper/AppVersionInfoMapper.xml",
"chars": 1155,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/"
},
{
"path": "src/main/resources/static/css/main.css",
"chars": 2127,
"preview": "* {\n -webkit-box-sizing: border-box;\n -moz-box-sizing: border-box;\n box-sizing: border-box;\n}\n\nbody {\n margi"
},
{
"path": "src/main/resources/static/js/upload.js",
"chars": 1709,
"preview": "var versionId = 0; //保存的临时变量\n\nfunction onAddInfo() {\n var form = document.getElementById('infoForm');\n var fd = ne"
},
{
"path": "src/main/resources/templates/index.html",
"chars": 2200,
"preview": "<!DOCTYPE html>\n<html>\n<head>\n <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n <title>XUpdate"
},
{
"path": "src/test/java/com/xuexiang/xupdateservice/XUpdateServiceApplicationTests.java",
"chars": 1020,
"preview": "package com.xuexiang.xupdateservice;\n\nimport com.xuexiang.xupdateservice.mapper.AppVersionInfoMapper;\nimport com.xuexian"
}
]
// ... and 5 more files (download for full content)
About this extraction
This page contains the full source code of the xuexiangjys/XUpdateService GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 70 files (36.0 MB), approximately 35.5k tokens, and a symbol index with 265 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.