diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f5919d2..9571c00 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,6 +14,8 @@ jobs: runs-on: ubuntu-latest env: KEY: ${{ secrets.WEB_HOOK_ACCESS_KEY }} + QINIU_ACCESSKEY: ${{ secrets.QINIU_ACCESSKEY }} + QINIU_SECRETKEY: ${{ secrets.QINIU_SECRETKEY }} steps: - uses: actions/checkout@v2 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index cb5da84..fb2ff5c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,9 +12,9 @@ jobs: build: runs-on: ubuntu-latest - # env: - # APPLICATION_PROPERTIES_TEST: ${{ secrets.APPLICATION_PROPERTIES_TEST }} - # APPLICATION_PROPERTIES_PROD: ${{ secrets.APPLICATION_PROPERTIES_PROD }} + env: + QINIU_ACCESSKEY: ${{ secrets.QINIU_ACCESSKEY }} + QINIU_SECRETKEY: ${{ secrets.QINIU_SECRETKEY }} steps: - uses: actions/checkout@v2 diff --git a/doc/API.md b/doc/API.md index 68ed876..84e21cb 100644 --- a/doc/API.md +++ b/doc/API.md @@ -1411,6 +1411,78 @@ | 401 | Unauthorized || | 403 | Forbidden || | 404 | Not Found || +## fileUpload + + +**接口描述**: + + +**接口地址**:`/fileUpload` + + +**请求方式**:`POST` + + +**consumes**:`["application/json"]` + + +**produces**:`["*/*"]` + + + +**请求参数**: + +| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | +| ------------ | -------------------------------- |-----------|--------|----|--- | +|file[]| file[] | formData | true |array | file | + +**响应示例**: + +```json +{ + "code": 0, + "msg": "", + "result": [ + {} + ] +} +``` + +**响应参数**: + + +| 参数名称 | 参数说明 | 类型 | schema | +| ------------ | -------------------|-------|----------- | +|code| |integer(int32) | integer(int32) | +|msg| |string | | +|result| |array | Map«string,object» | + + + +**schema属性说明** + + + + +**Map«string,object»** + +| 参数名称 | 参数说明 | 类型 | schema | +| ------------ | ------------------|--------|----------- | + + + + + +**响应状态**: + + +| 状态码 | 说明 | schema | +| ------------ | -------------------------------- |---------------------- | +| 200 | OK |Response«List«Map«string,object»»»| +| 201 | Created || +| 401 | Unauthorized || +| 403 | Forbidden || +| 404 | Not Found || ## headerInfo @@ -1623,6 +1695,231 @@ | 401 | Unauthorized || | 403 | Forbidden || | 404 | Not Found || +# config-controller + +## getConfiguration + + +**接口描述**: + + +**接口地址**:`/admin/config` + + +**请求方式**:`GET` + + +**consumes**:`` + + +**produces**:`["*/*"]` + + + +**请求参数**: +暂无 + + + +**响应示例**: + +```json +{ + "code": 0, + "msg": "", + "result": [ + { + "id": 0, + "name": "", + "value": "" + } + ] +} +``` + +**响应参数**: + + +| 参数名称 | 参数说明 | 类型 | schema | +| ------------ | -------------------|-------|----------- | +|code| |integer(int32) | integer(int32) | +|msg| |string | | +|result| |array | Config | + + + +**schema属性说明** + + + + +**Config** + +| 参数名称 | 参数说明 | 类型 | schema | +| ------------ | ------------------|--------|----------- | +|id | |integer(int32) | | +|name | |string | | +|value | |string | | + +**响应状态**: + + +| 状态码 | 说明 | schema | +| ------------ | -------------------------------- |---------------------- | +| 200 | OK |Response«List«Config»»| +| 401 | Unauthorized || +| 403 | Forbidden || +| 404 | Not Found || +## addConfiguration + + +**接口描述**: + + +**接口地址**:`/admin/config` + + +**请求方式**:`POST` + + +**consumes**:`["application/json"]` + + +**produces**:`["*/*"]` + + + +**请求参数**: + +| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | +| ------------ | -------------------------------- |-----------|--------|----|--- | +|configs| configs | query | false |array | Config | + +**响应示例**: + +```json +{ + "code": 0, + "msg": "", + "result": [ + { + "id": 0, + "name": "", + "value": "" + } + ] +} +``` + +**响应参数**: + + +| 参数名称 | 参数说明 | 类型 | schema | +| ------------ | -------------------|-------|----------- | +|code| |integer(int32) | integer(int32) | +|msg| |string | | +|result| |array | Config | + + + +**schema属性说明** + + + + +**Config** + +| 参数名称 | 参数说明 | 类型 | schema | +| ------------ | ------------------|--------|----------- | +|id | |integer(int32) | | +|name | |string | | +|value | |string | | + +**响应状态**: + + +| 状态码 | 说明 | schema | +| ------------ | -------------------------------- |---------------------- | +| 200 | OK |Response«List«Config»»| +| 201 | Created || +| 401 | Unauthorized || +| 403 | Forbidden || +| 404 | Not Found || +## updateConfiguration + + +**接口描述**: + + +**接口地址**:`/admin/config` + + +**请求方式**:`PUT` + + +**consumes**:`["application/json"]` + + +**produces**:`["*/*"]` + + + +**请求参数**: + +| 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | +| ------------ | -------------------------------- |-----------|--------|----|--- | +|configs| configs | query | false |array | Config | + +**响应示例**: + +```json +{ + "code": 0, + "msg": "", + "result": [ + { + "id": 0, + "name": "", + "value": "" + } + ] +} +``` + +**响应参数**: + + +| 参数名称 | 参数说明 | 类型 | schema | +| ------------ | -------------------|-------|----------- | +|code| |integer(int32) | integer(int32) | +|msg| |string | | +|result| |array | Config | + + + +**schema属性说明** + + + + +**Config** + +| 参数名称 | 参数说明 | 类型 | schema | +| ------------ | ------------------|--------|----------- | +|id | |integer(int32) | | +|name | |string | | +|value | |string | | + +**响应状态**: + + +| 状态码 | 说明 | schema | +| ------------ | -------------------------------- |---------------------- | +| 200 | OK |Response«List«Config»»| +| 201 | Created || +| 401 | Unauthorized || +| 403 | Forbidden || +| 404 | Not Found || # links-controller ## all @@ -1649,8 +1946,8 @@ | 参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema | | ------------ | -------------------------------- |-----------|--------|----|--- | |count| count | query | true |integer | | -|page| page | query | true |integer | | |deleted| deleted | query | false |boolean | | +|page| page | query | true |integer | | **响应示例**: diff --git a/src/main/java/cn/celess/blog/BlogApplication.java b/src/main/java/cn/celess/blog/BlogApplication.java index f65dc22..52c7e0e 100644 --- a/src/main/java/cn/celess/blog/BlogApplication.java +++ b/src/main/java/cn/celess/blog/BlogApplication.java @@ -1,20 +1,34 @@ package cn.celess.blog; import org.mybatis.spring.annotation.MapperScan; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.springframework.boot.ApplicationArguments; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.scheduling.annotation.EnableAsync; +/** + * @author zheng + */ @SpringBootApplication @EnableAsync @MapperScan("cn.celess.blog.mapper") public class BlogApplication { - public static final Logger logger = LoggerFactory.getLogger(BlogApplication.class); + public static ConfigurableApplicationContext context; public static void main(String[] args) { - SpringApplication.run(BlogApplication.class, args); - logger.info("启动完成!"); + context = SpringApplication.run(BlogApplication.class, args); + } + + public static void restart() { + ApplicationArguments args = context.getBean(ApplicationArguments.class); + + Thread thread = new Thread(() -> { + context.close(); + context = SpringApplication.run(BlogApplication.class, args.getSourceArgs()); + }); + thread.setDaemon(false); + thread.start(); } } diff --git a/src/main/java/cn/celess/blog/configuration/ApplicationListener.java b/src/main/java/cn/celess/blog/configuration/ApplicationListener.java new file mode 100644 index 0000000..0305e8b --- /dev/null +++ b/src/main/java/cn/celess/blog/configuration/ApplicationListener.java @@ -0,0 +1,59 @@ +package cn.celess.blog.configuration; + +import cn.celess.blog.enmu.ConfigKeyEnum; +import cn.celess.blog.entity.Config; +import cn.celess.blog.mapper.ConfigMapper; +import cn.celess.blog.service.fileserviceimpl.LocalFileServiceImpl; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.core.env.Environment; +import org.springframework.stereotype.Component; + +import java.io.File; +import java.util.List; +import java.util.stream.Collectors; + +/** + * @author : xiaohai + * @date : 2020/10/16 16:00 + * @desc : + */ +@Component +@Slf4j +public class ApplicationListener implements ApplicationRunner { + + @Autowired + ConfigMapper configMapper; + @Autowired + Environment env; + + @Override + public void run(ApplicationArguments args) throws Exception { + log.info("博客启动!"); + // 设置初始值 + setProperty(ConfigKeyEnum.FILE_QINIU_SECRET_KEY); + setProperty(ConfigKeyEnum.FILE_QINIU_ACCESS_KEY); + setProperty(ConfigKeyEnum.FILE_QINIU_BUCKET); + setProperty(ConfigKeyEnum.BLOG_FILE_PATH); + + List configurations = configMapper.getConfigurations() + .stream() + .filter(config -> config.getValue() != null && !"".equals(config.getValue())) + .collect(Collectors.toList()); + configurations.forEach(config -> System.setProperty(config.getName(), config.getValue())); + log.debug("注入配置成功 {}", configurations.stream().map(Config::getName).collect(Collectors.toList())); + File path = new File(LocalFileServiceImpl.getPath(System.getProperty(ConfigKeyEnum.BLOG_FILE_PATH.getKey()))); + if (!path.exists() && !path.mkdirs()) { + throw new IllegalAccessException("创建数据目录失败==>" + path.getAbsolutePath()); + } + } + + private void setProperty(ConfigKeyEnum e) { + String property = env.getProperty(e.getKey()); + if (property != null) { + System.setProperty(e.getKey(), property); + } + } +} diff --git a/src/main/java/cn/celess/blog/configuration/DruidConfig.java b/src/main/java/cn/celess/blog/configuration/DruidConfig.java index 8bfcacc..1c10e7a 100644 --- a/src/main/java/cn/celess/blog/configuration/DruidConfig.java +++ b/src/main/java/cn/celess/blog/configuration/DruidConfig.java @@ -1,41 +1,81 @@ package cn.celess.blog.configuration; import com.alibaba.druid.pool.DruidDataSource; -import org.springframework.beans.factory.annotation.Value; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.Properties; /** * @author : xiaohai * @date : 2019/03/28 14:26 */ +@Slf4j @Configuration public class DruidConfig { - @Value("${spring.datasource.url}") - private String dbUrl; - @Value("${spring.datasource.username}") - private String username; + @Autowired + Environment env; - @Value("${spring.datasource.password}") - private String password; - - @Value("${spring.datasource.driver-class-name}") - private String driverClassName; + public static final String DB_CONFIG_PATH = System.getProperty("user.home") + "/blog/application.properties"; + public static final String DB_CONFIG_URL_PREFIX = "spring.datasource.url"; + public static final String DB_CONFIG_USERNAME_PREFIX = "spring.datasource.username"; + public static final String DB_CONFIG_PASSWORD_PREFIX = "spring.datasource.password"; + public static final String DB_CONFIG_DRIVER_CLASS_NAME_PREFIX = "spring.datasource.driver-class-name"; + public static final String TEST_PROFILES = "test"; @Bean - public DruidDataSource druidDataSource() { - DruidDataSource dataSource = new DruidDataSource(); - dataSource.setDriverClassName(driverClassName); - // 数据库基本信息 - dataSource.setUrl(dbUrl); - dataSource.setUsername(username); - dataSource.setPassword(password); - - // 数据库连接池配置 + public DruidDataSource initDataSource() throws IOException { + DruidDataSource dataSource; + File file = new File(DB_CONFIG_PATH); + if (file.exists() && !Arrays.asList(env.getActiveProfiles()).contains(TEST_PROFILES)) { + log.debug("从文件中获取数据库配置"); + dataSource = readConfigFromFile(file); + } else { + log.debug("默认数据库配置"); + dataSource = defaultDruidSource(); + } dataSource.setInitialSize(10); dataSource.setMinIdle(10); dataSource.setMaxActive(100); + dataSource.setValidationQuery("select 1"); + return dataSource; + } + + private DruidDataSource readConfigFromFile(File file) throws IOException { + Properties properties = new Properties(); + FileInputStream fis = new FileInputStream(file); + properties.load(fis); + fis.close(); + String url = properties.getProperty(DB_CONFIG_URL_PREFIX, null); + String username = properties.getProperty(DB_CONFIG_USERNAME_PREFIX, null); + String password = properties.getProperty(DB_CONFIG_PASSWORD_PREFIX, null); + String className = properties.getProperty(DB_CONFIG_DRIVER_CLASS_NAME_PREFIX, null); + if (url == null || username == null || password == null || className == null) { + return defaultDruidSource(); + } + DruidDataSource dataSource = new DruidDataSource(); + dataSource.setDriverClassName(className); + dataSource.setUrl(url); + dataSource.setUsername(username); + dataSource.setPassword(password); + return dataSource; + } + + + private DruidDataSource defaultDruidSource() { + DruidDataSource dataSource = new DruidDataSource(); + dataSource.setDriverClassName(env.getProperty(DruidConfig.DB_CONFIG_DRIVER_CLASS_NAME_PREFIX)); + dataSource.setUrl(env.getProperty(DruidConfig.DB_CONFIG_URL_PREFIX)); + dataSource.setUsername(env.getProperty(DruidConfig.DB_CONFIG_USERNAME_PREFIX)); + dataSource.setPassword(env.getProperty(DruidConfig.DB_CONFIG_PASSWORD_PREFIX)); return dataSource; } } diff --git a/src/main/java/cn/celess/blog/controller/CommonController.java b/src/main/java/cn/celess/blog/controller/CommonController.java index 7f7ce96..07df4ee 100644 --- a/src/main/java/cn/celess/blog/controller/CommonController.java +++ b/src/main/java/cn/celess/blog/controller/CommonController.java @@ -2,10 +2,10 @@ package cn.celess.blog.controller; import cn.celess.blog.enmu.ResponseEnum; import cn.celess.blog.entity.Response; -import cn.celess.blog.entity.model.QiniuResponse; +import cn.celess.blog.entity.model.FileResponse; import cn.celess.blog.exception.MyException; import cn.celess.blog.service.CountService; -import cn.celess.blog.service.QiniuService; +import cn.celess.blog.service.FileService; import cn.celess.blog.util.HttpUtil; import cn.celess.blog.util.RedisUserUtil; import cn.celess.blog.util.RedisUtil; @@ -42,7 +42,7 @@ public class CommonController { @Autowired CountService countService; @Autowired - QiniuService qiniuService; + FileService fileService; @Autowired RedisUtil redisUtil; @Autowired @@ -161,10 +161,10 @@ public class CommonController { String mime = fileName.substring(fileName.lastIndexOf(".")); if (".png".equals(mime.toLowerCase()) || ".jpg".equals(mime.toLowerCase()) || ".jpeg".equals(mime.toLowerCase()) || ".bmp".equals(mime.toLowerCase())) { - QiniuResponse qiniuResponse = qiniuService.uploadFile(file.getInputStream(), "img_" + System.currentTimeMillis() + mime); + FileResponse fileResponse = fileService.getFileManager().uploadFile(file.getInputStream(), "img_" + System.currentTimeMillis() + mime); map.put("success", 1); map.put("message", "上传成功"); - map.put("url", "http://cdn.celess.cn/" + qiniuResponse.key); + map.put("url", "http://cdn.celess.cn/" + fileResponse.key); response.getWriter().println(mapper.writeValueAsString(map)); redisUtil.setEx(request.getRemoteAddr() + "-ImgUploadTimes", uploadTimes + 1 + "", 2, TimeUnit.HOURS); return; @@ -208,12 +208,12 @@ public class CommonController { assert fileName != null; String mime = fileName.substring(fileName.lastIndexOf(".")); String name = fileName.replace(mime, "").replaceAll(" ", ""); - QiniuResponse qiniuResponse = qiniuService.uploadFile(file.getInputStream(), "file_" + name + '_' + System.currentTimeMillis() + mime); + FileResponse fileResponse = fileService.getFileManager().uploadFile(file.getInputStream(), "file_" + name + '_' + System.currentTimeMillis() + mime); resp.put("originalFilename", fileName); - resp.put("success", qiniuResponse != null); - if (qiniuResponse != null) { + resp.put("success", fileResponse != null); + if (fileResponse != null) { resp.put("host", "http://cdn.celess.cn/"); - resp.put("path", qiniuResponse.key); + resp.put("path", fileResponse.key); } result.add(resp); } diff --git a/src/main/java/cn/celess/blog/controller/ConfigController.java b/src/main/java/cn/celess/blog/controller/ConfigController.java new file mode 100644 index 0000000..944233a --- /dev/null +++ b/src/main/java/cn/celess/blog/controller/ConfigController.java @@ -0,0 +1,39 @@ +package cn.celess.blog.controller; + +import cn.celess.blog.entity.Config; +import cn.celess.blog.entity.Response; +import cn.celess.blog.mapper.ConfigMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * @author : xiaohai + * @date : 2020/10/16 20:56 + * @desc : 配置管理接口 + */ +@RestController +public class ConfigController { + @Autowired + ConfigMapper configMapper; + + @GetMapping("/admin/config") + public Response> getConfiguration() { + return Response.success(configMapper.getConfigurations()); + } + + @PutMapping("/admin/config") + public Response> updateConfiguration(@RequestBody List configs) { + configs.forEach(config -> configMapper.updateConfiguration(config)); + configs.forEach(config -> System.setProperty(config.getName(), config.getValue())); + return Response.success(configMapper.getConfigurations()); + } + + @PostMapping("/admin/config") + public Response> addConfiguration(@RequestBody List configs) { + configs.forEach(config -> configMapper.addConfiguration(config)); + configs.forEach(config -> System.setProperty(config.getName(), config.getValue())); + return Response.success(configMapper.getConfigurations()); + } +} diff --git a/src/main/java/cn/celess/blog/controller/InstallController.java b/src/main/java/cn/celess/blog/controller/InstallController.java new file mode 100644 index 0000000..6f2b90c --- /dev/null +++ b/src/main/java/cn/celess/blog/controller/InstallController.java @@ -0,0 +1,78 @@ +package cn.celess.blog.controller; + +import cn.celess.blog.BlogApplication; +import cn.celess.blog.enmu.ConfigKeyEnum; +import cn.celess.blog.enmu.ResponseEnum; +import cn.celess.blog.entity.Config; +import cn.celess.blog.entity.InstallParam; +import cn.celess.blog.entity.Response; +import cn.celess.blog.exception.MyException; +import cn.celess.blog.mapper.ConfigMapper; +import cn.celess.blog.service.InstallService; +import cn.celess.blog.util.RegexUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.validation.Valid; +import java.util.Map; + +/** + * @author : xiaohai + * @date : 2020/10/18 15:36 + * @desc : + */ +@Slf4j +@Controller +@Validated +public class InstallController { + + @Autowired + InstallService installService; + @Autowired + ConfigMapper configMapper; + + + @GetMapping("/is_install") + @ResponseBody + public Response> isInstall() { + Map install = installService.isInstall(); + return Response.success(install); + } + + + @PostMapping("/install") + @ResponseBody + public Response install(@Valid @RequestBody InstallParam installParam) { + if (!RegexUtil.emailMatch(installParam.getEmail())) { + throw new MyException(ResponseEnum.PARAMETERS_EMAIL_ERROR); + } + if (!RegexUtil.pwdMatch(installParam.getPassword())) { + throw new MyException(ResponseEnum.PARAMETERS_PWD_ERROR); + } + return Response.success(installService.install(installParam)); + } + + + @GetMapping("/default_config") + @ResponseBody + public Response defaultConfig() { + return null; + } + + + @GetMapping("/install") + public String install() { + Config configuration = configMapper.getConfiguration(ConfigKeyEnum.BLOG_INSTALLED.getKey()); + if (Boolean.parseBoolean(configuration.getValue())) { + return "index.html"; + } + log.info("博客第一次运行,还未安装"); + return "install.html"; + } +} diff --git a/src/main/java/cn/celess/blog/enmu/ConfigKeyEnum.java b/src/main/java/cn/celess/blog/enmu/ConfigKeyEnum.java new file mode 100644 index 0000000..d25e59c --- /dev/null +++ b/src/main/java/cn/celess/blog/enmu/ConfigKeyEnum.java @@ -0,0 +1,37 @@ +package cn.celess.blog.enmu; + +/** + * @author : xiaohai + * @date : 2020/10/16 16:41 + * @desc : + */ +public enum ConfigKeyEnum { + + /** + * 枚举 + */ + FILE_TYPE("file.type"), + FILE_QINIU_ACCESS_KEY("file.qiniu.accessKey"), + FILE_QINIU_SECRET_KEY("file.qiniu.secretKey"), + FILE_QINIU_BUCKET("file.qiniu.bucket"), + FILE_LOCAL_DIRECTORY_PATH("file.local.directoryPath"), + BLOG_FILE_PATH("blog.file.path"), + BLOG_INSTALLED("blog.installed"), + DB_TYPE("db.type"), + DB_URL("spring.datasource.url"), + DB_USERNAME("spring.datasource.username"), + DB_PASSWORD("spring.datasource.password"), + DB_DRIVER_CLASS_NAME("spring.datasource.driver-class-name"), + BLOG_DB_PATH("blog.db.path"); + + + private final String key; + + ConfigKeyEnum(String key) { + this.key = key; + } + + public String getKey() { + return key; + } +} diff --git a/src/main/java/cn/celess/blog/entity/Config.java b/src/main/java/cn/celess/blog/entity/Config.java new file mode 100644 index 0000000..1668e2b --- /dev/null +++ b/src/main/java/cn/celess/blog/entity/Config.java @@ -0,0 +1,26 @@ +package cn.celess.blog.entity; + +import cn.celess.blog.enmu.ConfigKeyEnum; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author : xiaohai + * @date : 2020/10/16 15:24 + * @desc : + */ +@Data +@NoArgsConstructor +public class Config { + private Integer id; + private String name; + private String value; + + public Config(String name) { + this.name = name; + } + + public Config(ConfigKeyEnum e) { + this.name = e.getKey(); + } +} diff --git a/src/main/java/cn/celess/blog/entity/InstallParam.java b/src/main/java/cn/celess/blog/entity/InstallParam.java new file mode 100644 index 0000000..6b5ac5c --- /dev/null +++ b/src/main/java/cn/celess/blog/entity/InstallParam.java @@ -0,0 +1,38 @@ +package cn.celess.blog.entity; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; + +/** + * @author : xiaohai + * @date : 2020/10/18 14:52 + * @desc : + */ +@Data +public class InstallParam { + @NotBlank(message = "数据库类型不可为空") + private String dbType; + + @NotBlank(message = "数据库主机不可为空") + private String dbHost; + + @NotBlank(message = "数据库名称不可为空") + private String dbName; + + @NotBlank(message = "数据库用户名不可为空") + private String dbUsername; + + @NotBlank(message = "数据库密码不可为空") + private String dbPassword; + + + /** + * user 相关 + */ + @NotBlank(message = "用户邮箱地址不可为空") + private String email; + + @NotBlank(message = "用户密码不可为空") + private String password; +} diff --git a/src/main/java/cn/celess/blog/entity/model/FileInfo.java b/src/main/java/cn/celess/blog/entity/model/FileInfo.java new file mode 100644 index 0000000..eebfe02 --- /dev/null +++ b/src/main/java/cn/celess/blog/entity/model/FileInfo.java @@ -0,0 +1,25 @@ +package cn.celess.blog.entity.model; + +/** + * @author : xiaohai + * @date : 2020/10/15 22:16 + * @desc : + */ +public class FileInfo { + /** + * 文件名 + */ + public String key; + /** + * 文件hash值 + */ + public String hash; + /** + * 文件大小,单位:字节 + */ + public long size; + /** + * 文件上传时间,单位为:100纳秒 + */ + public long uploadTime; +} diff --git a/src/main/java/cn/celess/blog/entity/model/QiniuResponse.java b/src/main/java/cn/celess/blog/entity/model/FileResponse.java similarity index 61% rename from src/main/java/cn/celess/blog/entity/model/QiniuResponse.java rename to src/main/java/cn/celess/blog/entity/model/FileResponse.java index 2048f14..13fc33e 100644 --- a/src/main/java/cn/celess/blog/entity/model/QiniuResponse.java +++ b/src/main/java/cn/celess/blog/entity/model/FileResponse.java @@ -1,13 +1,13 @@ -package cn.celess.blog.entity.model; - - -/** - * @author : xiaohai - * @date : 2019/04/21 22:43 - */ -public class QiniuResponse { - public String key; - public String hash; - public String bucket; - public long fsize; -} +package cn.celess.blog.entity.model; + + +/** + * @author : xiaohai + * @date : 2019/04/21 22:43 + */ +public class FileResponse { + public String key; + public String hash; + public String type; + public long size; +} diff --git a/src/main/java/cn/celess/blog/exception/ExceptionHandle.java b/src/main/java/cn/celess/blog/exception/ExceptionHandle.java index 0b85dd8..a8e92c9 100644 --- a/src/main/java/cn/celess/blog/exception/ExceptionHandle.java +++ b/src/main/java/cn/celess/blog/exception/ExceptionHandle.java @@ -10,7 +10,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.mail.SimpleMailMessage; import org.springframework.validation.BindException; +import org.springframework.validation.BindingResult; import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; @@ -18,6 +20,10 @@ import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; import javax.servlet.http.HttpServletRequest; +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; +import javax.validation.ValidationException; +import java.util.Set; /** * @author : xiaohai @@ -41,27 +47,32 @@ public class ExceptionHandle { if (e instanceof MyException) { MyException exception = (MyException) e; logger.debug("返回了自定义的exception,[code={},msg={},result={}]", exception.getCode(), e.getMessage(), exception.getResult()); - return new Response(exception.getCode(), e.getMessage(), exception.getResult()); + return new Response<>(exception.getCode(), e.getMessage(), exception.getResult()); } //请求路径不支持该方法 if (e instanceof HttpRequestMethodNotSupportedException) { logger.debug("遇到请求路径与请求方法不匹配的请求,[msg={},path:{},method:{}]", e.getMessage(), request.getRequestURL(), request.getMethod()); - return new Response(ResponseEnum.ERROR.getCode(), e.getMessage(), null); + return new Response<>(ResponseEnum.ERROR.getCode(), e.getMessage(), null); } //数据输入类型不匹配 if (e instanceof MethodArgumentTypeMismatchException) { logger.debug("输入类型不匹配,[msg={}]", e.getMessage()); - return new Response(ResponseEnum.PARAMETERS_ERROR.getCode(), "数据输入有问题,请修改后再访问", null); + return new Response<>(ResponseEnum.PARAMETERS_ERROR.getCode(), "数据输入有问题,请修改后再访问", null); } //数据验证失败 if (e instanceof BindException) { logger.debug("数据验证失败,[msg={}]", e.getMessage()); - return new Response(ResponseEnum.PARAMETERS_ERROR.getCode(), "数据输入有问题,请修改", null); + return new Response<>(ResponseEnum.PARAMETERS_ERROR.getCode(), "数据输入有问题,请修改", null); } //数据输入不完整 if (e instanceof MissingServletRequestParameterException) { logger.debug("数据输入不完整,[msg={}]", e.getMessage()); - return new Response(ResponseEnum.PARAMETERS_ERROR.getCode(), "数据输入不完整,请检查", null); + return new Response<>(ResponseEnum.PARAMETERS_ERROR.getCode(), "数据输入不完整,请检查", null); + } + if (e instanceof MethodArgumentNotValidException) { + logger.debug("数据验证失败,[msg: {}]", e.getMessage()); + BindingResult violations = ((MethodArgumentNotValidException) e).getBindingResult(); + return new Response<>(ResponseEnum.PARAMETERS_ERROR.getCode(), violations.getAllErrors().get(0).getDefaultMessage(), null); } // 发送错误信息到邮箱 @@ -70,7 +81,7 @@ public class ExceptionHandle { sendMessage(e); } e.printStackTrace(); - return new Response(ResponseEnum.ERROR.getCode(), "服务器出现错误,已记录", null); + return new Response<>(ResponseEnum.ERROR.getCode(), "服务器出现错误,已记录", null); } /** diff --git a/src/main/java/cn/celess/blog/mapper/ConfigMapper.java b/src/main/java/cn/celess/blog/mapper/ConfigMapper.java new file mode 100644 index 0000000..9cda49c --- /dev/null +++ b/src/main/java/cn/celess/blog/mapper/ConfigMapper.java @@ -0,0 +1,55 @@ +package cn.celess.blog.mapper; + +import cn.celess.blog.entity.Config; +import org.apache.ibatis.annotations.Mapper; +import org.springframework.stereotype.Repository; + +import java.util.List; + +/** + * @author : xiaohai + * @date : 2020/10/16 15:24 + * @desc : + */ +@Mapper +@Repository +public interface ConfigMapper { + /** + * 获取单个配置 + * + * @param key 配置名 + * @return 配置 + */ + Config getConfiguration(String key); + + /** + * 更新配置 + * + * @param c 配置 + * @return 改变数据行数 + */ + int updateConfiguration(Config c); + + /** + * 获取所有配置 + * + * @return 所有配置 + */ + List getConfigurations(); + + /** + * 新增配置 + * + * @param c 配置 + * @return 改变行数 + */ + int addConfiguration(Config c); + + /** + * 删除配置 + * + * @param id 主键id + * @return 改变行数 + */ + int deleteConfiguration(int id); +} diff --git a/src/main/java/cn/celess/blog/service/FileManager.java b/src/main/java/cn/celess/blog/service/FileManager.java new file mode 100644 index 0000000..9c355ed --- /dev/null +++ b/src/main/java/cn/celess/blog/service/FileManager.java @@ -0,0 +1,40 @@ +package cn.celess.blog.service; + +import cn.celess.blog.entity.model.FileInfo; +import cn.celess.blog.entity.model.FileResponse; +import org.springframework.stereotype.Service; + +import java.io.InputStream; +import java.util.List; + +/** + * @author : xiaohai + * @date : 2020/10/15 18:19 + * @desc : 文件管理器 定义操作文件的方法 + */ +@Service +public interface FileManager { + /** + * 上传文件到文件存储容器中 + * + * @param is 文件流 + * @param fileName 文件名 + * @return FileResponse + */ + FileResponse uploadFile(InputStream is, String fileName); + + /** + * 获取文件列表 + * + * @return 文件信息 + */ + List getFileList(); + + /** + * 删除文件 + * + * @param fileName 文件名 + * @return 是否删除成功 + */ + boolean deleteFile(String fileName); +} diff --git a/src/main/java/cn/celess/blog/service/FileService.java b/src/main/java/cn/celess/blog/service/FileService.java new file mode 100644 index 0000000..7b2abde --- /dev/null +++ b/src/main/java/cn/celess/blog/service/FileService.java @@ -0,0 +1,18 @@ +package cn.celess.blog.service; + +import org.springframework.stereotype.Service; + +/** + * @author : xiaohai + * @date : 2019/04/25 18:15 + */ +@Service("fileService") +@FunctionalInterface +public interface FileService { + /** + * 获取文件管理器 + * + * @return 文件管理器 可以操作文件 + */ + FileManager getFileManager(); +} diff --git a/src/main/java/cn/celess/blog/service/InstallService.java b/src/main/java/cn/celess/blog/service/InstallService.java new file mode 100644 index 0000000..289e800 --- /dev/null +++ b/src/main/java/cn/celess/blog/service/InstallService.java @@ -0,0 +1,20 @@ +package cn.celess.blog.service; + +import cn.celess.blog.entity.InstallParam; +import org.springframework.stereotype.Service; + +import javax.validation.constraints.NotNull; +import java.util.Map; + +/** + * @author : xiaohai + * @date : 2020/10/23 16:22 + * @desc : + */ +@Service +public interface InstallService { + Map isInstall(); + + Map install(@NotNull InstallParam installParam); + +} diff --git a/src/main/java/cn/celess/blog/service/QiniuService.java b/src/main/java/cn/celess/blog/service/QiniuService.java deleted file mode 100644 index 32bdc2a..0000000 --- a/src/main/java/cn/celess/blog/service/QiniuService.java +++ /dev/null @@ -1,31 +0,0 @@ -package cn.celess.blog.service; - -import cn.celess.blog.entity.model.QiniuResponse; -import com.qiniu.storage.model.FileInfo; -import org.springframework.stereotype.Service; - -import java.io.InputStream; - -/** - * @author : xiaohai - * @date : 2019/04/25 18:15 - */ -@Service -public interface QiniuService { - /** - * 上传文件 - * - * @param is InputStream流 - * @param fileName 文件名 - * @return 响应数据 - */ - QiniuResponse uploadFile(InputStream is, String fileName); - - /** - * 获取文件列表 - * - * @return 文件列表 - */ - FileInfo[] getFileList(); - -} diff --git a/src/main/java/cn/celess/blog/service/fileserviceimpl/FileServiceImpl.java b/src/main/java/cn/celess/blog/service/fileserviceimpl/FileServiceImpl.java new file mode 100644 index 0000000..9e5e918 --- /dev/null +++ b/src/main/java/cn/celess/blog/service/fileserviceimpl/FileServiceImpl.java @@ -0,0 +1,36 @@ +package cn.celess.blog.service.fileserviceimpl; + +import cn.celess.blog.enmu.ConfigKeyEnum; +import cn.celess.blog.service.FileManager; +import cn.celess.blog.service.FileService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; + +/** + * @author : xiaohai + * @date : 2020/10/15 18:52 + * @desc : 提供文件管理器的服务 + */ +@Slf4j +@Service("fileServiceImpl") +public class FileServiceImpl implements FileService { + @Resource(name = "qiniuFileServiceImpl") + FileManager qiniuFileManager; + + @Resource(name = "localFileServiceImpl") + FileManager localFileServiceImpl; + + @Override + public FileManager getFileManager() { + String property = System.getProperty(ConfigKeyEnum.FILE_TYPE.getKey()); + log.info("获取到{}:{}", ConfigKeyEnum.FILE_TYPE.getKey(), property); + if ("qiniu".equals(property)){ + return qiniuFileManager; + }else if ("local".equals(property)){ + return localFileServiceImpl; + } + return localFileServiceImpl; + } +} diff --git a/src/main/java/cn/celess/blog/service/fileserviceimpl/LocalFileServiceImpl.java b/src/main/java/cn/celess/blog/service/fileserviceimpl/LocalFileServiceImpl.java new file mode 100644 index 0000000..b1d6eef --- /dev/null +++ b/src/main/java/cn/celess/blog/service/fileserviceimpl/LocalFileServiceImpl.java @@ -0,0 +1,104 @@ +package cn.celess.blog.service.fileserviceimpl; + +import cn.celess.blog.enmu.ConfigKeyEnum; +import cn.celess.blog.entity.model.FileInfo; +import cn.celess.blog.entity.model.FileResponse; +import cn.celess.blog.service.FileManager; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.net.URLEncoder; +import java.nio.file.Files; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.List; + +/** + * @author : xiaohai + * @date : 2020/10/16 14:39 + * @desc : + */ +@Slf4j +@Service("localFileServiceImpl") +public class LocalFileServiceImpl implements FileManager { + private static String path = null; + + @SneakyThrows + @Override + public FileResponse uploadFile(InputStream is, String fileName) { + if (path == null) { + path = System.getProperty(ConfigKeyEnum.FILE_LOCAL_DIRECTORY_PATH.getKey()); + } + // 判断文件夹是否存在 + File directory = new File(getPath(path)); + if (!directory.exists() && directory.mkdirs()) { + log.info("不存在文件夹,创建文件夹=>{}", directory.getAbsolutePath()); + } + log.info("上传文件 {}", fileName); + // 存储文件 + File file1 = new File(getPath(path) + File.separator + fileName); + FileOutputStream fos = new FileOutputStream(file1); + byte[] cache = new byte[1024 * 100]; + int len = 0; + FileResponse fileResponse = new FileResponse(); + while ((len = is.read(cache)) != -1) { + fileResponse.size += len; + fos.write(cache, 0, len); + } + fos.flush(); + fos.close(); + is.close(); + fileResponse.key = URLEncoder.encode(fileName, "UTF-8"); + fileResponse.type = "local"; + return fileResponse; + } + + @SneakyThrows + @Override + public List getFileList() { + if (path == null) { + path = System.getProperty(ConfigKeyEnum.FILE_LOCAL_DIRECTORY_PATH.getKey()); + } + File file = new File(getPath(path)); + File[] files = file.listFiles(); + List fileInfoList = new ArrayList<>(); + if (files == null) { + return null; + } + for (File file1 : files) { + FileInfo fileInfo = new FileInfo(); + fileInfo.key = URLEncoder.encode(file1.getName(), "UTF-8"); + fileInfo.size = file1.length(); + BasicFileAttributes basicFileAttributes = Files.readAttributes(file1.toPath(), BasicFileAttributes.class); + fileInfo.uploadTime = basicFileAttributes.creationTime().toMillis(); + fileInfoList.add(fileInfo); + } + return fileInfoList; + } + + @Override + public boolean deleteFile(String fileName) { + if (path == null) { + path = System.getProperty(ConfigKeyEnum.FILE_LOCAL_DIRECTORY_PATH.getKey()); + } + File file = new File(getPath(path) + File.separator + fileName); + return file.delete(); + } + + public static String getPath(String path) { + if (path == null) { + return ""; + } + String pathCop = path.replaceAll("//", File.separator); + if (path.startsWith("~")) { + // 家目录 + pathCop = path.replaceFirst("~", ""); + pathCop = System.getProperty("user.home") + pathCop; + } + return pathCop; + } +} diff --git a/src/main/java/cn/celess/blog/service/fileserviceimpl/QiniuFileServiceImpl.java b/src/main/java/cn/celess/blog/service/fileserviceimpl/QiniuFileServiceImpl.java new file mode 100644 index 0000000..f5c6f4f --- /dev/null +++ b/src/main/java/cn/celess/blog/service/fileserviceimpl/QiniuFileServiceImpl.java @@ -0,0 +1,120 @@ +package cn.celess.blog.service.fileserviceimpl; + +import cn.celess.blog.enmu.ConfigKeyEnum; +import cn.celess.blog.entity.model.FileInfo; +import cn.celess.blog.entity.model.FileResponse; +import cn.celess.blog.service.FileManager; +import com.qiniu.common.QiniuException; +import com.qiniu.common.Zone; +import com.qiniu.http.Response; +import com.qiniu.storage.BucketManager; +import com.qiniu.storage.Configuration; +import com.qiniu.storage.UploadManager; +import com.qiniu.util.Auth; +import com.qiniu.util.StringMap; +import lombok.SneakyThrows; +import org.springframework.stereotype.Service; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * @author : xiaohai + * @date : 2019/04/25 18:15 + */ +@Service("qiniuFileServiceImpl") +public class QiniuFileServiceImpl implements FileManager { + private static final Configuration cfg = new Configuration(Zone.zone2()); + private static UploadManager uploadManager; + private static BucketManager bucketManager; + private static Auth auth; + + private String bucket; + + /** + * todo :: 添加reload 方法 配置修改重新创建对象 + */ + private void init() { + String accessKey = System.getProperty(ConfigKeyEnum.FILE_QINIU_ACCESS_KEY.getKey()); + String secretKey = System.getProperty(ConfigKeyEnum.FILE_QINIU_SECRET_KEY.getKey()); + this.bucket = System.getProperty(ConfigKeyEnum.FILE_QINIU_BUCKET.getKey()); + if (auth == null || uploadManager == null || bucketManager == null) { + auth = Auth.create(accessKey, secretKey); + uploadManager = new UploadManager(cfg); + bucketManager = new BucketManager(auth, cfg); + } + } + + @Override + public FileResponse uploadFile(InputStream is, String fileName) { + init(); + //文件存在则删除文件 + if (continueFile(fileName)) { + try { + System.out.println(bucketManager.delete(bucket, fileName).toString()); + } catch (QiniuException e) { + e.printStackTrace(); + } + } + //上传 + try { + Response response = uploadManager.put(is, fileName, auth.uploadToken(bucket), null, null); + FileResponse fileResponse = new FileResponse(); + StringMap stringMap = response.jsonToMap(); + + fileResponse.key = (String) stringMap.get("key"); + fileResponse.type = "qiniu"; + fileResponse.size = 0; + fileResponse.hash = (String) stringMap.get("hash"); + + return fileResponse; + } catch (QiniuException e) { + Response r = e.response; + System.err.println(r.toString()); + return null; + } + } + + @Override + public List getFileList() { + init(); + List infoList = null; + BucketManager.FileListIterator fileListIterator = bucketManager.createFileListIterator(bucket, "", 1000, ""); + while (fileListIterator.hasNext()) { + //处理获取的file list结果 + infoList = new ArrayList(Arrays.asList(fileListIterator.next())) + .stream() + .map(item -> { + FileInfo fileInfo = new FileInfo(); + fileInfo.key = item.key; + fileInfo.hash = item.hash; + fileInfo.size = item.fsize; + fileInfo.uploadTime = item.putTime; + return fileInfo; + }) + .collect(Collectors.toList()); + } + return infoList; + } + + @SneakyThrows + @Override + public boolean deleteFile(String fileName) { + init(); + Response response = bucketManager.delete(bucket, fileName); + return "".equals(response.bodyString()); + } + + private boolean continueFile(String key) { + List fileList = getFileList(); + for (FileInfo fileInfo : fileList) { + if (key.equals(fileInfo.key)) { + return true; + } + } + return false; + } +} diff --git a/src/main/java/cn/celess/blog/service/serviceimpl/InstallServiceImpl.java b/src/main/java/cn/celess/blog/service/serviceimpl/InstallServiceImpl.java new file mode 100644 index 0000000..f1f2199 --- /dev/null +++ b/src/main/java/cn/celess/blog/service/serviceimpl/InstallServiceImpl.java @@ -0,0 +1,118 @@ +package cn.celess.blog.service.serviceimpl; + +import cn.celess.blog.BlogApplication; +import cn.celess.blog.enmu.ConfigKeyEnum; +import cn.celess.blog.enmu.ResponseEnum; +import cn.celess.blog.entity.Config; +import cn.celess.blog.entity.InstallParam; +import cn.celess.blog.entity.User; +import cn.celess.blog.exception.MyException; +import cn.celess.blog.mapper.ConfigMapper; +import cn.celess.blog.mapper.UserMapper; +import cn.celess.blog.service.InstallService; +import cn.celess.blog.service.fileserviceimpl.LocalFileServiceImpl; +import cn.celess.blog.util.MD5Util; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.validation.constraints.NotNull; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import java.util.stream.Collectors; + +/** + * @author : xiaohai + * @date : 2020/10/23 16:23 + * @desc : + */ +@Slf4j +@Service +public class InstallServiceImpl implements InstallService { + + public static final String MYSQL_DRIVER_CLASS_NAME = "com.mysql.cj.jdbc.Driver"; + public static final String H2_DRIVER_CLASS_NAME = "org.h2.Driver"; + + @Autowired + ConfigMapper configMapper; + @Autowired + UserMapper userMapper; + + @Override + public Map isInstall() { + Config installed = configMapper.getConfiguration(ConfigKeyEnum.BLOG_INSTALLED.getKey()); + Boolean isInstall = Boolean.valueOf(installed.getValue()); + Map result = new HashMap<>(3); + result.put("is_install", isInstall); + if (isInstall) { + Map configMap = configMapper.getConfigurations() + .stream() + .filter(config -> config.getValue() != null) + .collect(Collectors.toMap(Config::getName, Config::getValue)); + result.put(ConfigKeyEnum.FILE_TYPE.getKey(), configMap.get(ConfigKeyEnum.FILE_TYPE.getKey())); + result.put(ConfigKeyEnum.DB_TYPE.getKey(), configMap.get(ConfigKeyEnum.DB_TYPE.getKey())); + } + return result; + } + + @SneakyThrows + @Override + @Transactional(rollbackFor = Exception.class) + public Map install(@NotNull InstallParam installParam) { + User user = new User(installParam.getEmail(), MD5Util.getMD5(installParam.getPassword())); + userMapper.addUser(user); + userMapper.setUserRole(user.getId(), "admin"); + + + Config install = configMapper.getConfiguration(ConfigKeyEnum.BLOG_INSTALLED.getKey()); + boolean isInstall = Boolean.parseBoolean(install.getValue()); + if (isInstall) { + throw new MyException(ResponseEnum.FAILURE, "已经安装过了"); + } + + Config config = new Config(ConfigKeyEnum.DB_TYPE); + config.setValue(installParam.getDbType()); + Properties properties = new Properties(); + StringBuilder urlSb = new StringBuilder(); + if ("h2".equals(installParam.getDbType())) { + properties.setProperty(ConfigKeyEnum.DB_DRIVER_CLASS_NAME.getKey(), H2_DRIVER_CLASS_NAME); + urlSb.append("jdbc:h2:"); + } else { + properties.setProperty(ConfigKeyEnum.DB_DRIVER_CLASS_NAME.getKey(), MYSQL_DRIVER_CLASS_NAME); + urlSb.append("jdbc:mysql:"); + } + // TODO :: + urlSb.append(installParam.getDbHost()).append('/').append(installParam.getDbName()); + properties.setProperty(ConfigKeyEnum.DB_URL.getKey(), urlSb.toString()); + properties.setProperty(ConfigKeyEnum.DB_USERNAME.getKey(), installParam.getDbUsername()); + properties.setProperty(ConfigKeyEnum.DB_PASSWORD.getKey(), installParam.getDbPassword()); + + Config configuration = configMapper.getConfiguration(ConfigKeyEnum.BLOG_DB_PATH.getKey()); + + File file = new File(LocalFileServiceImpl.getPath(configuration.getValue())); + if (!file.exists() && file.createNewFile()) { + log.info("创建数据库配置文件: {}", file.getAbsolutePath()); + } + FileInputStream fis = new FileInputStream(file); + FileOutputStream fos = new FileOutputStream(file); + properties.load(fis); + + configMapper.addConfiguration(config); + properties.store(fos, "DB CONFIG"); + + install.setValue(Boolean.valueOf(true).toString()); + configMapper.updateConfiguration(install); + + log.info("重启..."); + + // 重启 + BlogApplication.restart(); + return null; + } +} diff --git a/src/main/java/cn/celess/blog/service/serviceimpl/QiniuServiceImpl.java b/src/main/java/cn/celess/blog/service/serviceimpl/QiniuServiceImpl.java deleted file mode 100644 index e95d202..0000000 --- a/src/main/java/cn/celess/blog/service/serviceimpl/QiniuServiceImpl.java +++ /dev/null @@ -1,87 +0,0 @@ -package cn.celess.blog.service.serviceimpl; - -import cn.celess.blog.entity.model.QiniuResponse; -import cn.celess.blog.service.QiniuService; -import com.qiniu.common.QiniuException; -import com.qiniu.common.Zone; -import com.qiniu.http.Response; -import com.qiniu.storage.BucketManager; -import com.qiniu.storage.Configuration; -import com.qiniu.storage.UploadManager; -import com.qiniu.storage.model.FileInfo; -import com.qiniu.util.Auth; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; - -import java.io.InputStream; - -/** - * @author : xiaohai - * @date : 2019/04/25 18:15 - */ -@Service -public class QiniuServiceImpl implements QiniuService { - private static final Configuration cfg = new Configuration(Zone.zone2()); - private static UploadManager uploadManager; - private static BucketManager bucketManager; - private static Auth auth; - - @Value("${qiniu.accessKey}") - private String accessKey; - @Value("${qiniu.secretKey}") - private String secretKey; - @Value("${qiniu.bucket}") - private String bucket; - - private void init() { - if (auth == null || uploadManager == null || bucketManager == null) { - auth = Auth.create(accessKey, secretKey); - uploadManager = new UploadManager(cfg); - bucketManager = new BucketManager(auth, cfg); - } - } - - @Override - public QiniuResponse uploadFile(InputStream is, String fileName) { - init(); - //文件存在则删除文件 - if (continueFile(fileName)) { - try { - System.out.println(bucketManager.delete(bucket, fileName).toString()); - } catch (QiniuException e) { - e.printStackTrace(); - } - } - //上传 - try { - Response response = uploadManager.put(is, fileName, auth.uploadToken(bucket), null, null); - return response.jsonToObject(QiniuResponse.class); - } catch (QiniuException e) { - Response r = e.response; - System.err.println(r.toString()); - return null; - } - } - - @Override - public FileInfo[] getFileList() { - init(); - BucketManager.FileListIterator fileListIterator = bucketManager.createFileListIterator(bucket, "", 1000, ""); - FileInfo[] items = null; - while (fileListIterator.hasNext()) { - //处理获取的file list结果 - items = fileListIterator.next(); - } - return items; - } - - private boolean continueFile(String key) { - FileInfo[] allFile = getFileList(); - for (FileInfo fileInfo : allFile) { - if (key.equals(fileInfo.key)) { - return true; - } - } - return false; - } -} diff --git a/src/main/java/cn/celess/blog/service/serviceimpl/UserServiceImpl.java b/src/main/java/cn/celess/blog/service/serviceimpl/UserServiceImpl.java index 1cae19c..342984b 100644 --- a/src/main/java/cn/celess/blog/service/serviceimpl/UserServiceImpl.java +++ b/src/main/java/cn/celess/blog/service/serviceimpl/UserServiceImpl.java @@ -5,15 +5,15 @@ import cn.celess.blog.enmu.RoleEnum; import cn.celess.blog.enmu.UserAccountStatusEnum; import cn.celess.blog.entity.Response; import cn.celess.blog.entity.User; +import cn.celess.blog.entity.model.FileResponse; import cn.celess.blog.entity.model.PageData; -import cn.celess.blog.entity.model.QiniuResponse; import cn.celess.blog.entity.model.UserModel; import cn.celess.blog.entity.request.LoginReq; import cn.celess.blog.entity.request.UserReq; import cn.celess.blog.exception.MyException; import cn.celess.blog.mapper.UserMapper; +import cn.celess.blog.service.FileService; import cn.celess.blog.service.MailService; -import cn.celess.blog.service.QiniuService; import cn.celess.blog.service.UserService; import cn.celess.blog.util.*; import com.github.pagehelper.PageHelper; @@ -24,6 +24,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.mail.SimpleMailMessage; import org.springframework.stereotype.Service; +import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.beans.Transient; import java.io.InputStream; @@ -45,8 +46,8 @@ public class UserServiceImpl implements UserService { HttpServletRequest request; @Autowired MailService mailService; - @Autowired - QiniuService qiniuService; + @Resource(name = "fileServiceImpl") + FileService fileService; @Autowired RedisUtil redisUtil; @Autowired @@ -185,7 +186,7 @@ public class UserServiceImpl implements UserService { @Override public Object updateUserAavatarImg(InputStream is, String mime) { User user = redisUserUtil.get(); - QiniuResponse upload = qiniuService.uploadFile(is, user.getEmail() + "_" + user.getId() + mime.toLowerCase()); + FileResponse upload = fileService.getFileManager().uploadFile(is, user.getEmail() + "_" + user.getId() + mime.toLowerCase()); user.setAvatarImgUrl(upload.key); userMapper.updateAvatarImgUrl(upload.key, user.getId()); redisUserUtil.set(user); diff --git a/src/main/resources/application-easyDeploy.properties b/src/main/resources/application-easyDeploy.properties new file mode 100644 index 0000000..5c087bf --- /dev/null +++ b/src/main/resources/application-easyDeploy.properties @@ -0,0 +1,65 @@ +spring.datasource.type=com.alibaba.druid.pool.DruidDataSource +spring.datasource.url=jdbc:h2:~/blog/db +spring.datasource.driver-class-name=org.h2.Driver +spring.datasource.username=sa +spring.datasource.password=sa + +spring.datasource.platform=h2 +spring.datasource.sql-script-encoding=utf-8 +spring.datasource.initialization-mode=never +spring.datasource.schema=classpath:sql/schema-h2.sql +spring.datasource.data=classpath:sql/data.sql + +spring.h2.console.path=/h2-console +spring.h2.console.enabled=true + +# 生成JWT时候的密钥 +jwt.secret=11111222223333444455556667778888 +# sitemap 存放地址 +sitemap.path=/www/wwwroot/celess.cn/sitemap.xml + + +############### email ############## +spring.mail.host=smtp.163.com +spring.mail.username= +spring.mail.password= +spring.mail.properties.mail.smtp.auth=true +spring.mail.properties.mail.smtp.starttls.enable=true +spring.mail.properties.mail.smtp.starttls.required=true +spring.mail.default-encoding=UTF-8 +spring.mail.port=465 +spring.mail.properties.mail.smtp.socketFactory.port=465 +spring.mail.properties.mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory +spring.mail.properties.mail.smtp.socketFactory.fallback=false + +################## mybatis ################## +mybatis.mapper-locations=classpath:mapper/*.xml +mybatis.type-aliases-package=cn.celess.blog.entity + + +pagehelper.helper-dialect=mysql +pagehelper.reasonable=true +pagehelper.support-methods-arguments=true +pagehelper.params=count=countSql + +############### redis ############## +# REDIS (RedisProperties) +# Redis数据库索引(默认为0) +spring.redis.database=1 +# Redis服务器地址 +spring.redis.host=127.0.0.1 +# Redis服务器连接端口 解决端口冲突 防止使用本地的redis +spring.redis.port=6379 +# Redis服务器连接密码(默认为空) +spring.redis.password= +# 连接池最大连接数(使用负值表示没有限制) +spring.redis.jedis.pool.max-active=-1 +# 连接池最大阻塞等待时间(使用负值表示没有限制) +spring.redis.jedis.pool.max-wait=-1 +# 连接池中的最大空闲连接 +spring.redis.jedis.pool.max-idle=8 +# 连接池中的最小空闲连接 +spring.redis.jedis.pool.min-idle=0 +# 连接超时时间(毫秒) +spring.redis.timeout=5000 + diff --git a/src/main/resources/application-openSource.properties b/src/main/resources/application-openSource.properties index 766a8f7..063a1fd 100644 --- a/src/main/resources/application-openSource.properties +++ b/src/main/resources/application-openSource.properties @@ -1,9 +1,9 @@ server.port=8081 # 七牛的密钥配置 -qiniu.accessKey= -qiniu.secretKey= -qiniu.bucket= +file.qiniu.accessKey= +file.qiniu.secretKey= +file.qiniu.bucket= # sitemap 存放地址 sitemap.path= # 生成JWT时候的密钥 @@ -62,12 +62,6 @@ spring.mail.properties.mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFact spring.mail.properties.mail.smtp.socketFactory.fallback=false -#### 用于nginx的代理 获取真实ip -server.use-forward-headers = true -server.tomcat.remote-ip-header = X-Real-IP -server.tomcat.protocol-header = X-Forwarded-Proto - - ############### redis ############## # REDIS (RedisProperties) # Redis数据库索引(默认为0) diff --git a/src/main/resources/application-test.properties b/src/main/resources/application-test.properties index 7f8f605..7634284 100644 --- a/src/main/resources/application-test.properties +++ b/src/main/resources/application-test.properties @@ -1,9 +1,9 @@ server.port=8081 # 七牛的密钥配置 -qiniu.accessKey= -qiniu.secretKey= -qiniu.bucket= +file.qiniu.accessKey=${QINIU_ACCESSKEY:null} +file.qiniu.secretKey=${QINIU_SECRETKEY:null} +file.qiniu.bucket=xiaohai # sitemap 存放地址 sitemap.path= # 生成JWT时候的密钥 @@ -32,10 +32,9 @@ spring.datasource.username=sa spring.datasource.password= -spring.datasource.platform=h2 spring.datasource.sql-script-encoding=utf-8 spring.datasource.initialization-mode=ALWAYS -spring.datasource.schema=classpath:sql/schema_h2.sql +spring.datasource.schema=classpath:sql/schema-h2.sql spring.datasource.data=classpath:sql/data.sql @@ -50,13 +49,6 @@ pagehelper.support-methods-arguments=true pagehelper.params=count=countSql - -#### 用于nginx的代理 获取真实ip -server.use-forward-headers = true -server.tomcat.remote-ip-header = X-Real-IP -server.tomcat.protocol-header = X-Forwarded-Proto - - ############### email ############## spring.mail.host=smtp.163.com spring.mail.username= diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index e0c3787..d06fccc 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,7 +1,10 @@ -spring.profiles.active=prod -####七牛的配置 -####cn.celess.blog.service.serviceimpl.QiniuServiceImpl +spring.profiles.active=easyDeploy logging.level.cn.celess.blog=debug logging.level.cn.celess.blog.mapper=info +#### 用于nginx的代理 获取真实ip +server.use-forward-headers = true +server.tomcat.remote-ip-header = X-Real-IP +server.tomcat.protocol-header = X-Forwarded-Proto + ## 修改openSource 添加-test 文件用于测试 -prod文件用于线上发布 \ No newline at end of file diff --git a/src/main/resources/mapper/ConfigMapper.xml b/src/main/resources/mapper/ConfigMapper.xml new file mode 100644 index 0000000..fc843ed --- /dev/null +++ b/src/main/resources/mapper/ConfigMapper.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + insert into config(conf_name, conf_value) + values (#{name}, #{value}) + + + + + + update config + set conf_value = #{value} + where conf_id = #{id} + + + + + delete + from config + where conf_id = #{id} + + + \ No newline at end of file diff --git a/src/main/resources/public/css/install.css b/src/main/resources/public/css/install.css new file mode 100644 index 0000000..2a0a3ec --- /dev/null +++ b/src/main/resources/public/css/install.css @@ -0,0 +1,110 @@ +* { + margin: 0; + padding: 0; +} + +html, body { + width: 100%; + height: 100%; +} + +.box { + width: 600px; + height: 45vh; + padding: 8px 10px; + border: 1px solid rgba(50, 50, 50, .1); + border-radius: 3px; + position: relative; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +.box-inner { + height: 100%; + width: 100%; + overflow: hidden; +} + +.box-inner select, input, button { + width: 90%; + margin: 12px 5%; + height: 40px; + padding: 5px 10px; + border-radius: 3px; + border: 1px solid rgba(50, 50, 50, .1); + box-sizing: border-box; +} + +input:focus { + border: 1px solid rgba(0, 50, 50, .6); + outline: none; +} + +.box-inner h3 { + text-align: center; + margin-bottom: 20px; +} + +.err-msg { + display: none; + color: red; + width: 100%; + padding-left: 30px; + font-size: small; + font-weight: lighter; +} + + +.show-err-tip { + display: block; +} + +#first-page, #second-page { + height: 100%; + margin: 10px 0; +} + +.hidden { + height: 0 !important; + visibility: hidden; + position: absolute; +} + +.show { + width: 100%; + position: relative; + height: 100%; + visibility: visible; +} + + +#next-step, .button-group { + position: absolute; + left: 0; + right: 0; + bottom: 10px; +} + +.button-group button { + width: 80px; +} + +.button-group #install { + position: absolute; + bottom: 0; + right: 20px; + background: #1890ff; + color: white; +} + +.loading-show { + z-index: 10000; + display: flex !important; + position: fixed; + left: 0; + right: 0; + bottom: 0; + top: 0; +} + diff --git a/src/main/resources/public/css/loading.css b/src/main/resources/public/css/loading.css new file mode 100644 index 0000000..b2d6f5e --- /dev/null +++ b/src/main/resources/public/css/loading.css @@ -0,0 +1,171 @@ +.pacman { + position: relative; +} + + +.loading { + box-sizing: border-box; + flex: 0 1 auto; + display: none; + flex-direction: column; + flex-grow: 1; + flex-shrink: 0; + flex-basis: 100%; + max-width: 100%; + /*height: 200px;*/ + align-items: center; + justify-content: center; + background: #ececec; +} + + +.pacman > div:nth-child(2) { + -webkit-animation: pacman-balls 1s 0s infinite linear; + animation: pacman-balls 1s 0s infinite linear; +} + +.pacman > div:nth-child(3) { + -webkit-animation: pacman-balls 1s 0.33s infinite linear; + animation: pacman-balls 1s 0.33s infinite linear; +} + +.pacman > div:nth-child(4) { + -webkit-animation: pacman-balls 1s 0.66s infinite linear; + animation: pacman-balls 1s 0.66s infinite linear; +} + +.pacman > div:nth-child(5) { + -webkit-animation: pacman-balls 1s 0.99s infinite linear; + animation: pacman-balls 1s 0.99s infinite linear; +} + +.pacman > div:first-of-type { + width: 0px; + height: 0px; + border-right: 25px solid transparent; + border-top: 25px solid #ed5565; + border-left: 25px solid #ed5565; + border-bottom: 25px solid #ed5565; + border-radius: 25px; + -webkit-animation: rotate_pacman_half_up 0.5s 0s infinite; + animation: rotate_pacman_half_up 0.5s 0s infinite; +} + +.pacman > div:nth-child(2) { + width: 0px; + height: 0px; + border-right: 25px solid transparent; + border-top: 25px solid #ed5565; + border-left: 25px solid #ed5565; + border-bottom: 25px solid #ed5565; + border-radius: 25px; + -webkit-animation: rotate_pacman_half_down 0.5s 0s infinite; + animation: rotate_pacman_half_down 0.5s 0s infinite; + margin-top: -50px; +} + +.pacman > div:nth-child(3), .pacman > div:nth-child(4), .pacman > div:nth-child(5), .pacman > div:nth-child(6) { + background-color: #ed5565; + width: 15px; + height: 15px; + border-radius: 100%; + margin: 2px; + width: 10px; + height: 10px; + position: absolute; + -webkit-transform: translate(0, -6.25px); + -ms-transform: translate(0, -6.25px); + transform: translate(0, -6.25px); + top: 25px; + left: 100px; +} + +@-webkit-keyframes rotate_pacman_half_up { + 0% { + -webkit-transform: rotate(270deg); + transform: rotate(270deg); + } + + 50% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } + + 100% { + -webkit-transform: rotate(270deg); + transform: rotate(270deg); + } +} + +@keyframes rotate_pacman_half_up { + 0% { + -webkit-transform: rotate(270deg); + transform: rotate(270deg); + } + + 50% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } + + 100% { + -webkit-transform: rotate(270deg); + transform: rotate(270deg); + } +} + +@-webkit-keyframes rotate_pacman_half_down { + 0% { + -webkit-transform: rotate(90deg); + transform: rotate(90deg); + } + + 50% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + + 100% { + -webkit-transform: rotate(90deg); + transform: rotate(90deg); + } +} + +@keyframes rotate_pacman_half_down { + 0% { + -webkit-transform: rotate(90deg); + transform: rotate(90deg); + } + + 50% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + + 100% { + -webkit-transform: rotate(90deg); + transform: rotate(90deg); + } +} + +@-webkit-keyframes pacman-balls { + 75% { + opacity: 0.7; + } + + 100% { + -webkit-transform: translate(-100px, -6.25px); + transform: translate(-100px, -6.25px); + } +} + +@keyframes pacman-balls { + 75% { + opacity: 0.7; + } + + 100% { + -webkit-transform: translate(-100px, -6.25px); + transform: translate(-100px, -6.25px); + } +} \ No newline at end of file diff --git a/src/main/resources/public/install.html b/src/main/resources/public/install.html new file mode 100644 index 0000000..9a4d579 --- /dev/null +++ b/src/main/resources/public/install.html @@ -0,0 +1,164 @@ + + + + + 博客安装页面 + + + + + +
+
+
+
+
+
+
+
+

+ 加载中.... +

+
+
+
+
+

数据库配置

+ + + + + 数据库的主机地址不可为空 + + + 数据库的地址名称不可为空 + + + 数据库用户名不可为空 + + + 数据库密码不可为空 + + +
+ +
+
+ + + diff --git a/src/main/resources/sql/data.sql b/src/main/resources/sql/data.sql index 1a52c25..0204f52 100644 --- a/src/main/resources/sql/data.sql +++ b/src/main/resources/sql/data.sql @@ -214,3 +214,12 @@ VALUES (1, '1.新增网站更新接口api \n2.新增友链api \n3.优化了文 (16, '登陆处理过程变更,登陆时长修改至5天', '2019-11-22 11:39:03', 0), (17, '界面改版v2.0', '2020-04-06 11:00:53', 0); +INSERT INTO config (conf_id, conf_name, conf_value) +VALUES (1, 'file.type', 'local'), + (2, 'file.qiniu.accessKey', null), + (3, 'file.qiniu.secretKey', null), + (4, 'file.qiniu.bucket', null), + (5, 'blog.file.path', '~/blog/'), + (6, 'file.local.directoryPath', '~/blog/files/'), + (8, 'blog.installed', 'false'), + (9, 'blog.db.path', '~/blog/db.properties') \ No newline at end of file diff --git a/src/main/resources/sql/schema_h2.sql b/src/main/resources/sql/schema-h2.sql similarity index 97% rename from src/main/resources/sql/schema_h2.sql rename to src/main/resources/sql/schema-h2.sql index df185f2..7de1702 100644 --- a/src/main/resources/sql/schema_h2.sql +++ b/src/main/resources/sql/schema-h2.sql @@ -8,6 +8,7 @@ drop table if exists tag_category; drop table if exists links; drop table if exists visitor; drop table if exists web_update; +drop table if exists config; -- 用户表 CREATE TABLE `user` @@ -115,6 +116,13 @@ CREATE TABLE `web_update` `is_delete` boolean not null default false comment '该数据是否被删除' ); +CREATE TABLE `config` +( + `conf_id` int primary key auto_increment, + `conf_name` varchar(255) unique not null comment '配置名', + `conf_value` varchar(255) default null comment '配置值' +); + CREATE VIEW articleView (articleId, title, summary, mdContent, url, isOriginal, readingCount, likeCount, dislikeCount, publishDate, updateDate, isOpen, diff --git a/src/main/resources/sql/schema.sql b/src/main/resources/sql/schema.sql index fe1c62a..ef0ea57 100644 --- a/src/main/resources/sql/schema.sql +++ b/src/main/resources/sql/schema.sql @@ -8,6 +8,7 @@ drop table if exists tag_category; drop table if exists links; drop table if exists visitor; drop table if exists web_update; +drop table if exists config; CREATE TABLE `user` @@ -88,7 +89,7 @@ CREATE TABLE `links` `l_url` varchar(255) unique not null comment '首页地址', `l_icon_path` varchar(255) not null comment '友链的icon地址', `l_desc` varchar(255) COLLATE utf8mb4_unicode_ci not null comment '友链的说明描述', - `is_delete` boolean not null default false comment '该数据是否被删除', + `is_delete` boolean not null default false comment '该数据是否被删除', `l_email` varchar(255) comment '网站管理员的邮箱', `l_notification` boolean default false comment '是否通知了' ) comment '友站表'; @@ -110,6 +111,13 @@ CREATE TABLE `web_update` `is_delete` boolean not null default false comment '该数据是否被删除' ) comment '更新内容表'; +CREATE TABLE `config` +( + `conf_id` int primary key auto_increment, + `conf_name` varchar(255) unique not null comment '配置名', + `conf_value` varchar(255) default null comment '配置值' +); + CREATE VIEW articleView (articleId, title, summary, mdContent, url, isOriginal, readingCount, likeCount, dislikeCount, publishDate, updateDate, isOpen, diff --git a/src/test/java/cn/celess/blog/BaseTest.java b/src/test/java/cn/celess/blog/BaseTest.java index 20a6603..edd13d7 100644 --- a/src/test/java/cn/celess/blog/BaseTest.java +++ b/src/test/java/cn/celess/blog/BaseTest.java @@ -2,15 +2,15 @@ package cn.celess.blog; import cn.celess.blog.entity.Response; -import cn.celess.blog.entity.model.QiniuResponse; +import cn.celess.blog.entity.model.FileInfo; +import cn.celess.blog.entity.model.FileResponse; import cn.celess.blog.entity.model.UserModel; import cn.celess.blog.entity.request.LoginReq; import cn.celess.blog.service.MailService; -import cn.celess.blog.service.QiniuService; +import cn.celess.blog.service.FileManager; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -import com.qiniu.storage.model.FileInfo; import lombok.extern.slf4j.Slf4j; import org.junit.After; import org.junit.Before; @@ -37,6 +37,8 @@ import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.UUID; @@ -325,23 +327,29 @@ public class BaseTest { } @Slf4j - public static class TestQiNiuServiceImpl implements QiniuService { + public static class TestFileManager implements FileManager { @Override - public QiniuResponse uploadFile(InputStream is, String fileName) { - QiniuResponse response = new QiniuResponse(); + public FileResponse uploadFile(InputStream is, String fileName) { + FileResponse response = new FileResponse(); log.debug("上传文件请求,[fileName:{}]", fileName); response.key = "key"; - response.bucket = "bucket"; + response.type = "test"; response.hash = "hash"; - response.fsize = 1; + response.size = 1; return response; } @Override - public FileInfo[] getFileList() { + public List getFileList() { log.debug("获取文件列表请求"); - return new FileInfo[0]; + return new ArrayList<>(); + } + + @Override + public boolean deleteFile(String fileName) { + log.debug("删除[{}]成功", fileName); + return true; } } } diff --git a/src/test/java/cn/celess/blog/configuration/DruidConfigTest.java b/src/test/java/cn/celess/blog/configuration/DruidConfigTest.java new file mode 100644 index 0000000..047fd79 --- /dev/null +++ b/src/test/java/cn/celess/blog/configuration/DruidConfigTest.java @@ -0,0 +1,131 @@ +package cn.celess.blog.configuration; + +import cn.celess.blog.BaseTest; +import cn.celess.blog.enmu.ConfigKeyEnum; +import cn.celess.blog.service.fileserviceimpl.LocalFileServiceImpl; +import com.alibaba.druid.pool.DruidDataSource; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.Properties; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class DruidConfigTest extends BaseTest { + + @Mock + Environment env; + + @Autowired + Environment globalEnvironment; + + + private File configFile; + private File bakConfigFile; + + @Test + public void initDataSource() throws IOException { + + DruidConfig druidConfig = new DruidConfig(); + + druidConfig.env = env; + + System.out.println(Arrays.toString(env.getActiveProfiles())); + + // 无配置文件 + assertTrue(!configFile.exists() || configFile.delete()); + DruidDataSource druidDataSource = druidConfig.initDataSource(); + + // 加载初始化时候配置文件的数据库连接 + assertEquals(env.getProperty(DruidConfig.DB_CONFIG_URL_PREFIX), druidDataSource.getUrl()); + assertEquals(env.getProperty(DruidConfig.DB_CONFIG_DRIVER_CLASS_NAME_PREFIX), druidDataSource.getDriverClassName()); + assertEquals(env.getProperty(DruidConfig.DB_CONFIG_USERNAME_PREFIX), druidDataSource.getUsername()); + assertEquals(env.getProperty(DruidConfig.DB_CONFIG_PASSWORD_PREFIX), druidDataSource.getPassword()); + assertTrue(configFile.createNewFile()); + + // 有配置文件的测试 + Properties properties = new Properties(); + FileInputStream fileInputStream = new FileInputStream(configFile); + properties.load(fileInputStream); + fileInputStream.close(); + properties.setProperty(DruidConfig.DB_CONFIG_URL_PREFIX, "jdbc:mysql://localhost:3306/blog"); + properties.setProperty(DruidConfig.DB_CONFIG_DRIVER_CLASS_NAME_PREFIX, "com.mysql.cj.jdbc.Driver"); + properties.setProperty(DruidConfig.DB_CONFIG_USERNAME_PREFIX, "username"); + properties.setProperty(DruidConfig.DB_CONFIG_PASSWORD_PREFIX, "password"); + // 保存到文件 + FileOutputStream fileOutputStream = new FileOutputStream(configFile); + properties.store(fileOutputStream, "数据库配置"); + fileOutputStream.close(); + druidDataSource = druidConfig.initDataSource(); + assertEquals(properties.getProperty(DruidConfig.DB_CONFIG_URL_PREFIX), druidDataSource.getUrl()); + assertEquals(properties.getProperty(DruidConfig.DB_CONFIG_DRIVER_CLASS_NAME_PREFIX), druidDataSource.getDriverClassName()); + assertEquals(properties.getProperty(DruidConfig.DB_CONFIG_USERNAME_PREFIX), druidDataSource.getUsername()); + assertEquals(properties.getProperty(DruidConfig.DB_CONFIG_PASSWORD_PREFIX), druidDataSource.getPassword()); + } + + @After + public void setConfigBack() { + if (bakConfigFile.exists()) { + System.out.println("恢复配置文件成功"); + copyFile(bakConfigFile, configFile); + assertTrue(bakConfigFile.delete()); + } else { + configFile.deleteOnExit(); + } + } + + @Before + public void recordConfig() { + File path = new File(LocalFileServiceImpl.getPath(System.getProperty(ConfigKeyEnum.BLOG_FILE_PATH.getKey()))); + if (!path.exists() && !path.mkdirs()) { + fail("创建失败"); + } + this.bakConfigFile = new File(DruidConfig.DB_CONFIG_PATH + ".bak"); + this.configFile = new File(DruidConfig.DB_CONFIG_PATH); + if (configFile.exists()) { + System.out.println("备份文件成功"); + copyFile(configFile, bakConfigFile); + } + when(this.env.getActiveProfiles()).thenReturn(new String[]{"dev"}); + when(this.env.getProperty(DruidConfig.DB_CONFIG_PASSWORD_PREFIX)).thenReturn(globalEnvironment.getProperty(DruidConfig.DB_CONFIG_PASSWORD_PREFIX)); + when(this.env.getProperty(DruidConfig.DB_CONFIG_URL_PREFIX)).thenReturn(globalEnvironment.getProperty(DruidConfig.DB_CONFIG_URL_PREFIX)); + when(this.env.getProperty(DruidConfig.DB_CONFIG_USERNAME_PREFIX)).thenReturn(globalEnvironment.getProperty(DruidConfig.DB_CONFIG_USERNAME_PREFIX)); + when(this.env.getProperty(DruidConfig.DB_CONFIG_DRIVER_CLASS_NAME_PREFIX)).thenReturn(globalEnvironment.getProperty(DruidConfig.DB_CONFIG_DRIVER_CLASS_NAME_PREFIX)); + } + + /** + * 复制文件 + * + * @param src + * @param dist + * @return + */ + private boolean copyFile(File src, File dist) { + try { + // 复制备份文件 + FileOutputStream fos = new FileOutputStream(dist); + FileInputStream fis = new FileInputStream(src); + int len = 0; + byte[] bytes = new byte[1024]; + while ((len = fis.read(bytes)) != -1) { + fos.write(bytes, 0, len); + } + fos.close(); + fis.close(); + return true; + } catch (IOException e) { + return false; + } + } +} \ No newline at end of file diff --git a/src/test/java/cn/celess/blog/controller/UserControllerTest.java b/src/test/java/cn/celess/blog/controller/UserControllerTest.java index a6bc88b..1922e7b 100644 --- a/src/test/java/cn/celess/blog/controller/UserControllerTest.java +++ b/src/test/java/cn/celess/blog/controller/UserControllerTest.java @@ -9,6 +9,7 @@ import cn.celess.blog.entity.request.LoginReq; import cn.celess.blog.entity.request.UserReq; import cn.celess.blog.mapper.UserMapper; import cn.celess.blog.service.UserService; +import cn.celess.blog.service.FileService; import cn.celess.blog.util.MD5Util; import cn.celess.blog.util.RedisUtil; import com.fasterxml.jackson.core.type.TypeReference; @@ -115,7 +116,7 @@ public class UserControllerTest extends BaseTest { assertNotNull(inputStream); // mock 实现类 - mockInjectInstance(userService, "qiniuService", new TestQiNiuServiceImpl()); + mockInjectInstance(userService, "fileService", (FileService) TestFileManager::new); MockMultipartFile file = new MockMultipartFile("file", "logo.png", MediaType.IMAGE_PNG_VALUE, inputStream); getMockData(multipart("/user/imgUpload").file(file), userLogin()).andDo(result -> { diff --git a/src/test/java/cn/celess/blog/mapper/ConfigMapperTest.java b/src/test/java/cn/celess/blog/mapper/ConfigMapperTest.java new file mode 100644 index 0000000..3c3264d --- /dev/null +++ b/src/test/java/cn/celess/blog/mapper/ConfigMapperTest.java @@ -0,0 +1,63 @@ +package cn.celess.blog.mapper; + +import cn.celess.blog.BaseTest; +import cn.celess.blog.entity.Config; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import static org.junit.Assert.*; + +public class ConfigMapperTest extends BaseTest { + + @Autowired + ConfigMapper configMapper; + + @Test + public void getConfiguration() { + Config file = configMapper.getConfiguration("file.type"); + assertNotNull(file); + assertNotNull(file.getId()); + assertEquals("file.type", file.getName()); + assertEquals("local", file.getValue()); + } + + @Test + public void updateConfiguration() { + Config config = generateConfig(); + configMapper.addConfiguration(config); + assertNotNull(config.getId()); + + String s = randomStr(); + config.setValue(s); + configMapper.updateConfiguration(config); + assertEquals(s, configMapper.getConfiguration(config.getName()).getValue()); + } + + @Test + public void getConfigurations() { + assertTrue(configMapper.getConfigurations().size() > 0); + } + + @Test + public void addConfiguration() { + Config config = generateConfig(); + configMapper.addConfiguration(config); + assertNotNull(config.getId()); + } + + @Test + public void deleteConfiguration() { + Config config = generateConfig(); + configMapper.addConfiguration(config); + assertNotNull(config.getId()); + assertNotEquals(0, configMapper.deleteConfiguration(config.getId())); + assertNull(configMapper.getConfiguration(config.getName())); + } + + private Config generateConfig() { + Config config = new Config(); + config.setName("test" + randomStr(4)); + config.setValue(randomStr(4)); + return config; + } +} \ No newline at end of file diff --git a/src/test/java/cn/celess/blog/service/fileserviceimpl/FileManagerTest.java b/src/test/java/cn/celess/blog/service/fileserviceimpl/FileManagerTest.java new file mode 100644 index 0000000..82d44a7 --- /dev/null +++ b/src/test/java/cn/celess/blog/service/fileserviceimpl/FileManagerTest.java @@ -0,0 +1,123 @@ +package cn.celess.blog.service.fileserviceimpl; + +import cn.celess.blog.BaseTest; +import cn.celess.blog.enmu.ConfigKeyEnum; +import cn.celess.blog.entity.model.FileInfo; +import cn.celess.blog.entity.model.FileResponse; +import cn.celess.blog.service.FileManager; +import cn.celess.blog.service.FileService; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.util.List; + +import static org.junit.Assert.*; + +@Slf4j +public class FileManagerTest extends BaseTest { + + @Autowired + FileService fileService; + + FileManager fileManager; + + @Test + public void testUploadFile() { + // 测试本地的文件上传 + fileManager = null; + System.setProperty(ConfigKeyEnum.FILE_TYPE.getKey(), "local"); + fileManager = fileService.getFileManager(); + assertTrue(fileManager instanceof LocalFileServiceImpl); + uploadFile(); + + // 测试七牛云的文件上传 + fileManager = null; + System.setProperty(ConfigKeyEnum.FILE_TYPE.getKey(), "qiniu"); + fileManager = fileService.getFileManager(); + assertTrue(fileManager instanceof QiniuFileServiceImpl); + uploadFile(); + } + + @Test + public void testGetFileList() { + // 测试获取本地的文件列表 + fileManager = null; + System.setProperty(ConfigKeyEnum.FILE_TYPE.getKey(), "local"); + fileManager = fileService.getFileManager(); + assertTrue(fileManager instanceof LocalFileServiceImpl); + getFileList(); + + // 测试获取七牛云的文件列表 + fileManager = null; + System.setProperty(ConfigKeyEnum.FILE_TYPE.getKey(), "qiniu"); + fileManager = fileService.getFileManager(); + assertTrue(fileManager instanceof QiniuFileServiceImpl); + getFileList(); + } + + @Test + public void testDeleteFile() { + // 测试删除本地文件 + fileManager = null; + System.setProperty(ConfigKeyEnum.FILE_TYPE.getKey(), "local"); + fileManager = fileService.getFileManager(); + assertTrue(fileManager instanceof LocalFileServiceImpl); + deleteFile(); + + // 测试删除七牛云文件 + fileManager = null; + System.setProperty(ConfigKeyEnum.FILE_TYPE.getKey(), "qiniu"); + fileManager = fileService.getFileManager(); + assertTrue(fileManager instanceof QiniuFileServiceImpl); + deleteFile(); + } + + @SneakyThrows + public void uploadFile() { + String fileName = null; + File file = createFile(); + FileResponse fileResponse = fileManager.uploadFile(new FileInputStream(file), file.getName()); + assertEquals(file.getName(), fileResponse.key); + assertEquals(System.getProperty(ConfigKeyEnum.FILE_TYPE.getKey()), fileResponse.type); + // assertNotNull(fileResponse.hash); + fileName = fileResponse.key; + + fileManager.deleteFile(fileName); + + file.deleteOnExit(); + } + + public void getFileList() { + List fileList = fileManager.getFileList(); + fileList.forEach(fileInfo -> { + assertNotNull(fileInfo.key); + // assertNotNull(fileInfo.hash); + }); + } + + public void deleteFile() { + uploadFile(); + } + + @SneakyThrows + private File createFile() { + String fileName = "test." + randomStr(3); + File file = new File(fileName); + if (!file.exists() && file.createNewFile()) { + // 创建文件 + log.debug("创建文件[{}]", fileName); + FileOutputStream outputStream = new FileOutputStream(file); + for (int i = 0; i < 100; i++) { + outputStream.write(new byte[1024]); + } + outputStream.flush(); + outputStream.close(); + } + return file; + } +} \ No newline at end of file