CentOS 7 安装宝塔 Linux 面板(bt.cn)、Flarum

安装宝塔 Linux 面板(bt.cn)

1
yum install -y wget && wget -O install.sh http://download.bt.cn/install/install.sh && sh install.sh

添加站点,用于安装 Flarum

安装 Composer

1
2
wget https://getcomposer.org/composer.phar -O /usr/local/bin/composer
chmod a+x /usr/local/bin/composer

添加 composer bin 到 PATH

1
2
export PATH=$PATH:/root/.config/composer/vendor/bin
source /etc/profile

composer http 改成 https,连接速度会更佳

1
composer config -g repo.packagist composer https://packagist.phpcomposer.com

安装 Flarum

安装 flarum

1
mkdir /tmp/flarum && cd /tmp/flarum && composer create-project flarum/flarum . --stability=beta

解决因PHP版本过高,安装依赖报错的问题

1
composer install --ignore-platform-reqs

拷贝 flarum 文件到 testweb 文件夹

1
rm -rf /www/wwwroot/testweb/* && cp -R /tmp/flarum/* /www/wwwroot/testweb

然后访问刚新建的站点,发现已经能访问 Flarum 了,只不过有几个问题:

问题1:The PHP extension ‘fileinfo’ is required.

解决方法:访问宝塔 Linux 面板,在 PHP 设置中安装 fileinfo 扩展,结果如下图:

问题2:The /www/wwwroot/testweb/assets directory is not writable.

Please chmod this directory and its contents to 0775.

解决方法:

1
chmod -R 0777 /www/wwwroot/testweb/assets

问题3:The /www/wwwroot/testweb/storage directory is not writable.

Please chmod this directory and its contents to 0775.

解决方法:

1
chmod -R 0777 /www/wwwroot/testweb/storage

解决问题后,访问到正常安装页面,填写数据库等相关信息即可安装。

  提示:根据官方文档想要访问正常,必需 URL Rewriting。

URL Rewriting

Apache

Flarum includes a .htaccess file – make sure it’s been uploaded correctly. If you’re using shared hosting, confirm with your hosting provider that mod_rewrite is enabled. You may need to add the following to your Apache configuration:

1
2
3
<Directory "/path/to/your/forum">
AllowOverride All
</Directory>

Nginx

Add the following lines to your server’s configuration block:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
location / { try_files $uri $uri/ /index.php?$query_string; }
location /api { try_files $uri $uri/ /api.php?$query_string; }
location /admin { try_files $uri $uri/ /admin.php?$query_string; }

location ~* ^/(composer\.(jsonlock)config\.phpflarumstoragevendor) {
deny all;
return 404;
}

location ~* \.php$ {
fastcgi_split_path_info ^(.+.php)(/.+)$;
fastcgi_pass unix:/var/run/php5-fpm.sock;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param HTTP_PROXY ""; # Fix for https://httpoxy.org/ vulnerability
fastcgi_index index.php;
}

location ~* \.html$ {
expires -1;
}

location ~* \.(cssjsgifjpe?gpng)$ {
expires 1M;
add_header Pragma public;
add_header Cache-Control "public, must-revalidate, proxy-revalidate";
}

gzip on;
gzip_http_version 1.1;
gzip_vary on;
gzip_comp_level 6;
gzip_proxied any;
gzip_types application/atom+xml
application/javascript
application/json
application/vnd.ms-fontobject
application/x-font-ttf
application/x-web-app-manifest+json
application/xhtml+xml
application/xml
font/opentype
image/svg+xml
image/x-icon
text/css
#text/html -- text/html is gzipped by default by nginx
text/plain
text/xml;
gzip_buffers 16 8k;
gzip_disable "MSIE [1-6]\.(?!.*SV1)";

Lighttpd

Add the following lines to your server’s configuration block:

1
2
3
4
5
url.rewrite-if-not-file = (
"/admin.*" => "/admin.php",
"/api.*" => "/api.php",
"/.*" => "/index.php"
)

参考文档

https://www.bt.cn/bbs/thread-1186-1-1.html https://flarum.org/docs/installation/

一加 5 解锁 Bootloader 、刷写第三方 Recovery、ROOT

解锁 Bootloader 会清楚手机所有用户资料,请先做好数据备份。

第一步:手机连接电脑

安装 adb.exe 然后手机打开 usb 调试连接电脑,在弹出的“是否允许计算机调试”对话框中点击“是”, 打开手机设置-关于手机,拖到底部,快速点按“版本号”选项8次,即可打开“开发者选项”,返回设置界面,找到“开发者选项”。

第二步:解锁 Bootloader

打开 cmd ,输入以下命令 :

1
2
adb reboot-bootloader
fastboot devices

1
fastboot oem unlock

此处选择 YES。 手机解锁成功后重启,再次进入“开发者选项”可以看到“OEM 解锁”已经开启。

第三步:刷写第三方 Recovery

手机重启进入 Bootloader ,然后手机连接电脑,打开 cmd 输入以下命令刷写 Recovery

1
2
fastboot flash recovery <第三方 Recovery 文件名>
fastboot boot <第三方 Recovery 文件名>

然后手机自动重启进入第三方 Recovery ,再刷 Magisk.zip ROOT 手机。

SQL 文件生成实体类、Mybatis Mapper、MyBatis Mapper XML 文件

开源地址:https://github.com/godcheese/com.godcheese.common

  • 项目目录D:\demo
  • D:\demo\tables目录下的api_table.sql文件
  • 目前仅支持 MySQL 数据库,Oracle 之后再适配

SQL 文件:api_table.sql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
DROP TABLE
IF EXISTS `api`;

CREATE TABLE `api` (
`id` BIGINT(20) UNSIGNED AUTO_INCREMENT COMMENT 'id',
`name` VARCHAR(255) NOT NULL COMMENT 'api 名称',
`uri` TEXT COMMENT '请求地址',
`api_category_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'api 目录 id',
`sort` BIGINT(20) UNSIGNED DEFAULT 0 COMMENT '排序',
`remark` VARCHAR(255) DEFAULT '' COMMENT '备注',
`authority` VARCHAR(255) NOT NULL COMMENT '权限(API:UUID)',
`gmt_create` DATETIME DEFAULT NOW() COMMENT '创建时间',
`gmt_modified` DATETIME DEFAULT NOW() COMMENT '更新时间',
PRIMARY KEY `pk_id` (`id`),
UNIQUE KEY `uk_authority` (`authority`)
)
ENGINE = INNODB
DEFAULT CHARACTER
SET = utf8mb4
COLLATE = utf8mb4_general_ci
AUTO_INCREMENT = 1
ROW_FORMAT = DYNAMIC
COMMENT = 'api 表';

批量生成实体类、MyBatis Mapper、MyBatis Mapper XML 文件

1
2
3
4
5
6
7
8
9
10
11
12
// 批量生成实体类、MyBatis Mapper、MyBatis Mapper XML 文件
SqlGenerateProperties properties = new SqlGenerateProperties();
properties.setMyBatisMapperPackage("com.gioov.demo.mapper");
properties.setEntityPackage("com.gioov.demo.entity");
properties.setSqlDirectory("D:\\tables");
properties.setEntityDirectory("D:\\demo\\src\\main\\java\\com\\gioov\\demo\\entity");
properties.setMyBatisMapperDirectory("D:\\demo\\src\\main\\java\\com\\gioov\\demo\\mapper");
properties.setMyBatisMapperXmlDirectory("D:\\demo\\src\\main\\java\\com\\gioov\\demo\\mapper");

DatabaseUtil.generateEntity( properties);
DatabaseUtil.generateMyBatisMapperXml( properties);
DatabaseUtil.generateMyBatisMapper(properties);

单个生成实体类、MyBatis Mapper、MyBatis Mapper XML 文件

1
2
3
4
5
6
7
8
// 指定单个 SQL 文件生成对应的实体类、MyBatis Mapper、MyBatis Mapper XML 文件
SqlGenerateProperties properties = new SqlGenerateProperties();
properties.setMyBatisMapperPackage("com.gioov.demo.mapper");
properties.setEntityPackage("com.gioov.demo.entity");

DatabaseUtil.generateEntity("D:\\demo\\tables\\api_table.sql","D:\\demo\\src\\main\\java\\com\\gioov\\demo\\entity", properties);
DatabaseUtil.generateMyBatisMapper("D:\\demo\\tables\\api_table.sql","D:\\demo\\src\\main\\java\\com\\gioov\\demo\\mapper", properties);
DatabaseUtil.generateMyBatisMapperXml("D:\\demo\\tables\\api_table.sql","D:\\demo\\src\\main\\java\\com\\gioov\\demo\\mapper", properties);

生成的实体类文件:ApiEntity.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package com.gioov.demo.entity;

import java.io.Serializable;
import java.lang.Long;
import java.lang.String;
import java.util.Date;

/**
* @author godcheese [[email protected]]
* @date 2018/4/23 15:58
*/
public class ApiEntity implements Serializable {

/**
* id
*/
private Long id;

/**
* api 名称
*/
private String name;

/**
* 请求地址
*/
private String uri;

/**
* api 目录 id
*/
private Long apiCategoryId;

/**
* 排序
*/
private Long sort;

/**
* 备注
*/
private String remark;

/**
* 权限(API:UUID)
*/
private String authority;

/**
* 创建时间
*/
private Date gmtCreate;

/**
* 更新时间
*/
private Date gmtModified;


public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getUri() {
return uri;
}

public void setUri(String uri) {
this.uri = uri;
}

public Long getApiCategoryId() {
return apiCategoryId;
}

public void setApiCategoryId(Long apiCategoryId) {
this.apiCategoryId = apiCategoryId;
}

public Long getSort() {
return sort;
}

public void setSort(Long sort) {
this.sort = sort;
}

public String getRemark() {
return remark;
}

public void setRemark(String remark) {
this.remark = remark;
}

public String getAuthority() {
return authority;
}

public void setAuthority(String authority) {
this.authority = authority;
}

public Date getGmtCreate() {
return gmtCreate;
}

public void setGmtCreate(Date gmtCreate) {
this.gmtCreate = gmtCreate;
}

public Date getGmtModified() {
return gmtModified;
}

public void setGmtModified(Date gmtModified) {
this.gmtModified = gmtModified;
}

}

生成的 MyBatis Mapper XML 文件:ApiMapper.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gioov.demo.mapper.ApiMapper">
<resultMap id="BaseResultMap" type="com.gioov.demo.entity.ApiEntity">
<id column="id" property="id" jdbcType="BIGINT"/> <!-- id -->
<result column="name" property="name" jdbcType="VARCHAR"/> <!-- api 名称 -->
<result column="uri" property="uri" jdbcType="null"/> <!-- 请求地址 -->
<result column="api_category_id" property="apiCategoryId" jdbcType="BIGINT"/> <!-- api 目录 id -->
<result column="sort" property="sort" jdbcType="BIGINT"/> <!-- 排序 -->
<result column="remark" property="remark" jdbcType="VARCHAR"/> <!-- 备注 -->
<result column="authority" property="authority" jdbcType="VARCHAR"/> <!-- 权限(API:UUID) -->
<result column="gmt_create" property="gmtCreate" jdbcType="DATE"/> <!-- 创建时间 -->
<result column="gmt_modified" property="gmtModified" jdbcType="DATE"/> <!-- 更新时间 -->
</resultMap>

<sql id="TableName">
`api`
</sql>

<sql id="BaseColumnList">
`id`, `name`, `uri`, `api_category_id`, `sort`, `remark`, `authority`, `gmt_create`, `gmt_modified`
</sql>

<insert id="insertOne" useGeneratedKeys="true" keyProperty="id" parameterType="com.gioov.demo.entity.ApiEntity">
INSERT INTO
<include refid="TableName"/>
(`id`, `name`, `uri`, `api_category_id`, `sort`, `remark`, `authority`, `gmt_create`, `gmt_modified`)
VALUES (#{id}, #{name}, #{uri}, #{apiCategoryId}, #{sort}, #{remark}, #{authority}, #{gmtCreate}, #{gmtModified}) </insert>

<update id="updateOne" keyProperty="id" parameterType="com.gioov.demo.entity.ApiEntity">
UPDATE
<include refid="TableName"/>
SET `name` = #{name}, `uri` = #{uri}, `api_category_id` = #{apiCategoryId}, `sort` = #{sort}, `remark` = #{remark}, `authority` = #{authority}, `gmt_create` = #{gmtCreate}, `gmt_modified` = #{gmtModified}
WHERE `id`= #{id}
</update>

<delete id="deleteOne" parameterType="long">
DELETE FROM
<include refid="TableName"/>
WHERE id = #{id}
</delete>

<select id="getOne" resultMap="BaseResultMap">
SELECT
<include refid="BaseColumnList"/>
FROM <include refid="TableName"/> WHERE id = #{id}
</select>

<select id="listAll" resultMap="BaseResultMap">
SELECT
<include refid="BaseColumnList"/>
FROM <include refid="TableName"/>
</select>

</mapper>

Server Manager 基于 JavaFX 开发的 一款桌面服务器管理软件

Server Manager

Server Manager 基于 JavaFX 开发的 一款桌面服务器管理软件。

目前版本还不可靠,不适合在生产环境中部署使用,仅供参考学习。

目前实现功能

  • 备份项目应用数据、项目数据库数据,仅支持 MySQL 数据库

计划实现功能

  • 更可靠的运行
  • 支持 Oracle 数据库
  • 定时备份项目数据

构建 jar

  1. src/main/java/META-INF 下创建 MAINFEST.MF

    文件内容(最后一行为空行):

    • Manifest-Version: 1.0
    • Main-Class: com.gioov.MainWindow
  2. Build -> Build Artifacts -> Build Or Rebuild

  3. cmd 运行 java -jar servermanager.jar,若报错,请将 data 文件夹及文件置于运行目录。

构建 exe(不推荐,测试报错)

  1. src/main/java/META-INF 下创建 MAINFEST.MF

    文件内容(最后一行为空行):

    • Manifest-Version: 1.0
    • Main-Class: com.gioov.MainWindow
  2. Build -> Build Artifacts -> Build Or Rebuild

  3. 运行 scripts 文件夹下的 build_exe.cmd

开发环境(jar 已测试正常,exe 测试报错)

  • Windows 10 x64
  • MySQL 5.7
  • Java 8
  • Maven 3.5
  • SQLite 3

Snapshots

servermanager_1servermanager_2servermanager_3servermanager_4 servermanager_5

开源地址:http://github.com/godcheese/servermanager

一加 5 手机更新系统后安装 Xposed 出现 cp : write error: No space left no device Error 1 occurred 解决方法

昨天我更新一加5手机到最新系统时,在重新安装 Xposed 时报错:cp : write error: No space left no device Error 1 occurred. 玩 Linux/Unix 的可能就一眼看出应该是空间不足了,那为什么我128G的手机竟然爆出空间不足的问题?原因是因为 Xposed 默认安装在 system 分区,而 Android 内部是默认将 system 设为单独分区的。

检查

这是可能是手机的 system 分区快满了,打开 Termux 输入sh命令:

1
df -k

1
df -h

可以看到 system 分区已经用了99%的存储空间。

解决方法

解决方法就是打开 Root Explorer 文件管理器,进入根目录,然后进入

1
/system/app

1
/system/priv-app

1
/system/reserve #(预装应用)

等目录,删除没用的 文件或 app。 我这里是选择进入 /system/reserve 目录,删除几乎所有没用的 app,因为这个目录所有的都是预装应用,用于出厂恢复时恢复用的,所以我也建议删除此目录下的app。 此处我选择了6个没用的预装 app, 删除成功后300M空间可用。

Spring Boot 1.x 整合 Spring Security 实现角色/权限控制实例

说明

Spring Boot 1.x 整合 Spring Security 实现角色/权限控制实例

官方实现:http://spring.io/guides/gs/securing-web/

测试账号
Username Password Role & Authority
system_admin 123456 ROLE_USER,ROLE_ADMIN,ROLE_SYSTEM_ADMIN
admin 123456 ROLE_USER,ROLE_ADMIN,/user/delete/{id}
user 123456 ROLE_USER

表单登录、注销、后台管理、删除用户等。 主要配置文件:WebSecurityConfiguration.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
package com.gioov.springboot1springsecurity.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

/**
* @author godcheese [[email protected]]
* @date 2018/4/7 19:00
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

@Autowired
private SessionRegistry sessionRegistry;

@Bean
public SessionRegistry sessionRegistry(){
return new SessionRegistryImpl();
}

@Bean
public SimpleUserDetailsService simpleUserDetailsService(){
return new SimpleUserDetailsService();
}

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(simpleUserDetailsService()).passwordEncoder(new BCryptPasswordEncoder());
}

@Override
protected void configure(HttpSecurity http) throws Exception {

http
// 禁用 csrf
// .csrf().disable()

.authorizeRequests()

// 匹配url,无需登录认证权限
.antMatchers("/css/**","/js/**","img/**","/vendor/**").permitAll()

.antMatchers("/","/index").permitAll()

// 其它请求均需要认证
.anyRequest().authenticated()

// .antMatchers("/**").authenticated()

.and()

// 开启表单登录,设置登录 url
.formLogin().loginPage("/login")

// 自定义登录表单提交 url
.loginProcessingUrl("/login/form")

// 设置登录成功跳转到的 url
.defaultSuccessUrl("/userCenter",true)

// 设置登录失败跳转到的 url
.failureUrl("/login?error").permitAll()

.and()

// 开启记住我功能, cookie 保存登录数据
.rememberMe().rememberMeParameter("rememberMe")

// 设置 cookie 有效期,7天内有效
.tokenValiditySeconds(60 * 60 * 60 * 24 * 7)

// 设置cookie的私钥
.key("user")

.and()

.logout()

// 设置注销 url
.logoutUrl("/logout")

//设置注销成功后跳转页面,默认是跳转到登录页面
.logoutSuccessUrl("/login?logout").permitAll()

.and()

.sessionManagement()

// 限制单个用户最大session,只允许一个用户登录
.maximumSessions(1)


// session 过期后跳转到登录 url
.expiredUrl("/login")

.sessionRegistry(sessionRegistry)

.and()

.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)

.and()


// 无权限,访问指定 url
.exceptionHandling().accessDeniedPage("/403");
}


}

Screenshots

源码

https://github.com/godcheese/springboot-example/tree/master/springboot1springsecurity

Java Apache POI 读取 Excel 数值数据时避免发生科学计数法问题的解决方法

第一种

1
2
DecimalFormat decimalFormat = new DecimalFormat("0");
cellValue = decimalFormat.format(cell.getNumericCellValue());

第二种

1
2
3
4
5
NumberFormat numberFormat = NumberFormat.getInstance();
cellValue = numberFormat.format(cell.getNumericCellValue());
if (cellValue.indexOf(",") >= 0) {
cellValue = cellValue.replace(",", "");
}

第三种

在写入Excel 时不想出现科学技术法数值前加上英文半角字符“’”,如下图:

Spring Boot 自定义全局异常报错处理,返回 JSON/HTML

新建一个叫 GlobalErrorController 然后继承 AbstractErrorController,代码的写法可以参考 BasicErrorController 的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
package com.gioov.multifunction.controller.system;

import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.web.AbstractErrorController;
import org.springframework.boot.autoconfigure.web.ErrorAttributes;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;


/**
* @author godcheese
*
*/
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class DefaultErrorController extends AbstractErrorController {


public DefaultErrorController(ErrorAttributes errorAttributes) {
super(errorAttributes);
}

@Value("${server.error.path:${error.path:/error}}")
public static final String ERROR_PATH = "/error";

@Override
public String getErrorPath() {
return ERROR_PATH;
}

@RequestMapping(produces = "text/html")
public ModelAndView errorHtml(HttpServletRequest request,
HttpServletResponse response) {

Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
statusCode = statusCode == null ? HttpStatus.INTERNAL_SERVER_ERROR.value() : statusCode;
response.setStatus(getStatus(request).value());
Map<String, Object> model = new HashMap<>();
switch (statusCode){
case 404:
break;
case 500:
break;
default:
statusCode = 500;
break;
}
return new ModelAndView(""+statusCode, model);
}

@RequestMapping
@ResponseBody
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request,Exception exception) throws Exception {

Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
statusCode = statusCode == null ? HttpStatus.INTERNAL_SERVER_ERROR.value() : statusCode;
Map<String, Object> body = new HashMap<>();
HttpStatus status = getStatus(request);
body.put("code",statusCode);
body.put("message",HttpStatus.valueOf(statusCode));
body.put("data",null);
body.put("exception",exception.getMessage());
return new ResponseEntity<>(body, status);
}

}

然后在 resources/templates 目录下新建 404.html、500.html、error.html 等几个文件,然后访问不存在的链接就可以访问到自定义的报错页面了,如果是json请求还可以返回自定义的json内容。

x.y.z 软件版本发布命名规范 - 语义化版本 2.0.0

语义化版本 2.0.0

摘要

版本格式:主版本号.次版本号.修订号,版本号递增规则如下: 主版本号:当你做了不兼容的 API 修改, 次版本号:当你做了向下兼容的功能性新增, 修订号:当你做了向下兼容的问题修正。 先行版本号及版本编译信息可以加到“主版本号.次版本号.修订号”的后面,作为延伸。

简介

在软件管理的领域里存在着被称作“依赖地狱”的死亡之谷,系统规模越大,加入的套件越多,你就越有可能在未来的某一天发现自己已深陷绝望之中。 在依赖高的系统中发布新版本套件可能很快会成为恶梦。如果依赖关系过高,可能面临版本控制被锁死的风险(必须对每一个相依套件改版才能完成某次升级)。而如果依赖关系过于松散,又将无法避免版本的混乱(假设兼容于未来的多个版本已超出了合理数量)。当你专案的进展因为版本相依被锁死或版本混乱变得不够简便和可靠,就意味着你正处于依赖地狱之中。 作为这个问题的解决方案之一,我提议用一组简单的规则及条件来约束版本号的配置和增长。这些规则是根据(但不局限于)已经被各种封闭、开放源码软件所广泛使用的惯例所设计。为了让这套理论运作,你必须先有定义好的公共 API 。这可以透过文件定义或代码强制要求来实现。无论如何,这套 API 的清楚明了是十分重要的。一旦你定义了公共 API,你就可以透过修改相应的版本号来向大家说明你的修改。考虑使用这样的版本号格式:XYZ (主版本号.次版本号.修订号)修复问题但不影响API 时,递增修订号;API 保持向下兼容的新增及修改时,递增次版本号;进行不向下兼容的修改时,递增主版本号。 我称这套系统为“语义化的版本控制”,在这套约定下,版本号及其更新方式包含了相邻版本间的底层代码和修改内容的信息。

语义化版本控制规范(SemVer)

以下关键词 MUST、MUST NOT、REQUIRED、SHALL、SHALL NOT、SHOULD、SHOULD NOT、 RECOMMENDED、MAY、OPTIONAL 依照 RFC 2119 的叙述解读。(译注:为了保持语句顺畅, 以下文件遇到的关键词将依照整句语义进行翻译,在此先不进行个别翻译。) 使用语义化版本控制的软件“必须 MUST ”定义公共 API。该 API 可以在代码中被定义或出现于严谨的文件内。无论何种形式都应该力求精确且完整。 标准的版本号“必须 MUST ”采用 XYZ 的格式,其中 X、Y 和 Z 为非负的整数,且“禁止 MUST NOT”在数字前方补零。X 是主版本号、Y 是次版本号、而 Z 为修订号。每个元素“必须 MUST ”以数值来递增。例如:1.9.1 -> 1.10.0 -> 1.11.0。 标记版本号的软件发行后,“禁止 MUST NOT ”改变该版本软件的内容。任何修改都“必须 MUST ”以新版本发行。 主版本号为零(0.y.z)的软件处于开发初始阶段,一切都可能随时被改变。这样的公共 API 不应该被视为稳定版。 1.0.0 的版本号用于界定公共 API 的形成。这一版本之后所有的版本号更新都基于公共 API 及其修改内容。 修订号 Z(x.y.Z x > 0)“必须 MUST ”在只做了向下兼容的修正时才递增。这里的修正指的是针对不正确结果而进行的内部修改。 次版本号 Y(x.Y.z x > 0)“必须 MUST ”在有向下兼容的新功能出现时递增。在任何公共 API 的功能被标记为弃用时也“必须 MUST ”递增。也“可以 MAY ”在内部程序有大量新功能或改进被加入时递增,其中“可以 MAY ”包括修订级别的改变。每当次版本号递增时,修订号“必须 MUST ”归零。 主版本号 X(X.y.z X > 0)“必须 MUST ”在有任何不兼容的修改被加入公共 API 时递增。其中“可以 MAY ”包括次版本号及修订级别的改变。每当主版本号递增时,次版本号和修订号“必须 MUST ”归零。 先行版本号“可以 MAY ”被标注在修订版之后,先加上一个连接号再加上一连串以句点分隔的标识符号来修饰。标识符号“必须 MUST ”由 ASCII 码的英数字和连接号 [0-9A-Za-z-] 组成,且“禁止 MUST NOT ”留白。数字型的标识符号“禁止 MUST NOT ”在前方补零。先行版的优先级低于相关联的标准版本。被标上先行版本号则表示这个版本并非稳定而且可能无法达到兼容的需求。范例:1.0.0-alpha、1.0.0-alpha.1、1.0.0-0.3.7、1.0.0-x.7.z.92。 版本编译信息“可以 MAY ”被标注在修订版或先行版本号之后,先加上一个加号再加上一连串以句点分隔的标识符号来修饰。标识符号“必须 MUST ”由 ASCII 的英数字和连接号 [0-9A-Za-z-] 组成,且“禁止 MUST NOT ”留白。当判断版本的优先层级时,版本编译信息“可 SHOULD ”被忽略。因此当两个版本只有在版本编译信息有差别时,属于相同的优先层级。范例:1.0.0-alpha+001、1.0.0+20130313144700、1.0.0-beta+exp.sha.5114f85。 版本的优先层级指的是不同版本在排序时如何比较。判断优先层级时,“必须 MUST ”把版本依序拆分为主版本号、次版本号、修订号及先行版本号后进行比较(版本编译信息不在这份比较的列表中)。由左到右依序比较每个标识符号,第一个差异值用来决定优先层级:主版本号、次版本号及修订号以数值比较,例如:1.0.0 < 2.0.0 < 2.1.0 < 2.1.1。当主版本号、次版本号及修订号都相同时,改以优先层级比较低的先行版本号决定。例如:1.0.0-alpha < 1.0.0。有相同主版本号、次版本号及修订号的两个先行版本号,其优先层级“必须 MUST ”透过由左到右的每个被句点分隔的标识符号来比较,直到找到一个差异值后决定:只有数字的标识符号以数值高低比较,有字母或连接号时则逐字以 ASCII 的排序来比较。数字的标识符号比非数字的标识符号优先层级低。若开头的标识符号都相同时,栏位比较多的先行版本号优先层级比较高。范例:1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0- rc.1 < 1.0.0。

为什么要使用语义化的版本控制?

这并不是一个新的或者革命性的想法。实际上,你可能已经在做一些近似的事情了。问题在于只是“近似”还不够。如果没有某个正式的规范可循,版本号对于依赖的管理并无实质意义。将上述的想法命名并给予清楚的定义,让你对软件使用者传达意向变得容易。一旦这些意向变得清楚,弹性(但又不会太弹性)的依赖规范就能达成。 举个简单的例子就可以展示语义化的版本控制如何让依赖地狱成为过去。假设有个名为“救火车”的函式库,它需要另一个名为“梯子”并已经有使用语义化版本控制的套件。当救火车创建时,梯子的版本号为 3.1.0。因为救火车使用了一些版本 3.1.0 所新增的功能, 你可以放心地指定相依于梯子的版本号大等于 3.1.0 但小于 4.0.0。这样,当梯子版本 3.1.1 和 3.2.0 发布时,你可以将直接它们纳入你的套件管理系统,因为它们能与原有相依的软件兼容。 作为一位负责任的开发者,你理当确保每次套件升级的运作与版本号的表述一致。现实世界是复杂的,我们除了提高警觉外能做的不多。你所能做的就是让语义化的版本控制为你提供一个健全的方式来发行以及升级套件,而无需推出新的相依套件,节省你的时间及烦恼。 如果你对此认同,希望立即开始使用语义化版本控制,你只需声明你的函式库正在使用它并遵循这些规则就可以了。请在你的 README 文件中保留此页连结,让别人也知道这些规则并从中受益。

FAQ

在 0.y.z 初始开发阶段,我该如何进行版本控制? 最简单的做法是以 0.1.0 作为你的初始化开发版本,并在后续的每次发行时递增次版本号。 如何判断发布 1.0.0 版本的时机? 当你的软件被用于正式环境,它应该已经达到了 1.0.0 版。如果你已经有个稳定的 API 被使用者依赖,也会是 1.0.0 版。如果你很担心向下兼容的问题,也应该算是 1.0.0 版了。 这不会阻碍快速开发和迭代吗? 主版本号为零的时候就是为了做快速开发。如果你每天都在改变 API,那么你应该仍在主版本号为零的阶段(0.y.z),或是正在下个主版本的独立开发分支中。 对于公共 API,若即使是最小但不向下兼容的改变都需要产生新的主版本号,岂不是很快就达到 42.0.0 版? 这是开发的责任感和前瞻性的问题。不兼容的改变不应该轻易被加入到有许多依赖代码的软件中。升级所付出的代价可能是巨大的。要递增主版本号来发行不兼容的改版,意味着你必须为这些改变所带来的影响深思熟虑,并且评估所涉及的成本及效益比。 为整个公共 API 写文件太费事了! 为供他人使用的软件编写适当的文件,是你作为一名专业开发者应尽的职责。保持专案高效一个非常重要的部份是掌控软件的复杂度,如果没有人知道如何使用你的软件或不知道哪些函数的调用是可靠的,要掌控复杂度会是困难的。长远来看,使用语义化版本控制以及对于公共 API 有良好规范的坚持,可以让每个人及每件事都运行顺畅。 万一不小心把一个不兼容的改版当成了次版本号发行了该怎么办? 一旦发现自己破坏了语义化版本控制的规范,就要修正这个问题,并发行一个新的次版本号来更正这个问题并且恢复向下兼容。即使是这种情况,也不能去修改已发行的版本。可以的话,将有问题的版本号记录到文件中,告诉使用者问题所在,让他们能够意识到这是有问题的版本。 如果我更新了自己的依赖但没有改变公共 API 该怎么办? 由于没有影响到公共 API,这可以被认定是兼容的。若某个软件和你的套件有共同依赖,则它会有自己的依赖规范,作者也会告知可能的冲突。要判断改版是属于修订等级或是次版等级,是依据你更新的依赖关系是为了修复问题或是加入新功能。对于后者,我经常会预期伴随着更多的代码,这显然会是一个次版本号级别的递增。 如果我变更了公共 API 但无意中未遵循版本号的改动怎么办呢?(意即在修订等级的发布中,误将重大且不兼容的改变加到代码之中) 自行做最佳的判断。如果你有庞大的使用者群在依照公共 API 的意图而变更行为后会大受影响,那么最好做一次主版本的发布,即使严格来说这个修复仅是修订等级的发布。记住, 语义化的版本控制就是透过版本号的改变来传达意义。若这些改变对你的使用者是重要的,那就透过版本号来向他们说明。 我该如何处理即将弃用的功能? 弃用现存的功能是软件开发中的家常便饭,也通常是向前发展所必须的。当你弃用部份公共 API 时,你应该做两件事:(1)更新你的文件让使用者知道这个改变,(2)在适当的时机将弃用的功能透过新的次版本号发布。在新的主版本完全移除弃用功能前,至少要有一个次版本包含这个弃用信息,这样使用者才能平顺地转移到新版 API。 语义化版本对于版本的字串长度是否有限制呢? 没有,请自行做适当的判断。举例来说,长到 255 个字元的版本已过度夸张。再者,特定的系统对于字串长度可能会有他们自己的限制。

关于

语义化版本控制的规范是由 Gravatars 创办者兼 GitHub 共同创办者 Tom Preston-Werner 所建立。 如果您有任何建议,请到 GitHub 上提出您的问题。

授权

创用 CC 姓名标示 3.0 Unported 授权条款 http://creativecommons.org/licenses/by/3.0/  

原文:http://semver.org/lang/zh-CN/spec/v2.0.0.html

Oracle 常用运维命令

表空间

1
create tablespace sdt  datafile 'd:\tablespace\demp' size 800m extent management local segment space management auto;

索引表空间

1
create tablespace sdt_Index datafile 'd:\tablespace\demo' size 512m extent management local segment space management auto;

建用户

1
create user demo identified by demo default tablespace sdt;

赋权

1
grant connect,resource to demo; grant create any sequence to demo; grant create any table to demo; grant delete any table to demo; grant insert any table to demo; grant select any table to demo; grant unlimited tablespace to demo; grant execute any procedure to demo; grant update any table to demo; grant create any view to demo;

导入导出命令

1
exp demo/[email protected] file=d:/backup.dmp full=y imp demo/[email protected] file=d:/backup.dmp full=y ignore=y IP远程服务器导出方式: exp demo/[email protected]:1521/orcl file=d:/backup.dmp full=y