mirror of
https://gitee.com/novel_dev_team/novel-plus
synced 2026-07-20 17:54:59 +08:00
Merge branch '5.3.x'
This commit is contained in:
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
<groupId>com.java2nb</groupId>
|
<groupId>com.java2nb</groupId>
|
||||||
<artifactId>novel-admin</artifactId>
|
<artifactId>novel-admin</artifactId>
|
||||||
<version>5.2.6</version>
|
<version>5.3.0</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<name>novel-admin</name>
|
<name>novel-admin</name>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ public class Constant {
|
|||||||
//通知公告阅读状态-已读
|
//通知公告阅读状态-已读
|
||||||
public static int OA_NOTIFY_READ_YES = 1;
|
public static int OA_NOTIFY_READ_YES = 1;
|
||||||
//部门根节点id
|
//部门根节点id
|
||||||
public static Long DEPT_ROOT_ID = 0l;
|
public static Long DEPT_ROOT_ID = 0L;
|
||||||
//缓存方式
|
//缓存方式
|
||||||
public static String CACHE_TYPE_REDIS = "redis";
|
public static String CACHE_TYPE_REDIS = "redis";
|
||||||
|
|
||||||
@@ -23,5 +23,6 @@ public class Constant {
|
|||||||
|
|
||||||
public static final String UPLOAD_FILES_PREFIX = "/files/";
|
public static final String UPLOAD_FILES_PREFIX = "/files/";
|
||||||
|
|
||||||
|
public static final String BOOK_IS_DOWNLOADING_KEY = "bookIsDownloading:";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import java.util.Set;
|
|||||||
public class SortWhitelistUtil {
|
public class SortWhitelistUtil {
|
||||||
|
|
||||||
// 白名单字段
|
// 白名单字段
|
||||||
private static final Set<String> ALLOWED_COLUMNS = Set.of("id", "name", "order_num");
|
private static final Set<String> ALLOWED_COLUMNS = Set.of("id", "name", "order_num","index_num");
|
||||||
|
|
||||||
// 白名单排序方式
|
// 白名单排序方式
|
||||||
private static final Set<String> ALLOWED_ORDERS = Set.of("asc", "desc");
|
private static final Set<String> ALLOWED_ORDERS = Set.of("asc", "desc");
|
||||||
|
|||||||
@@ -1,10 +1,24 @@
|
|||||||
package com.java2nb.novel.controller;
|
package com.java2nb.novel.controller;
|
||||||
|
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import com.java2nb.common.config.Constant;
|
||||||
|
import com.java2nb.novel.domain.BookContentDO;
|
||||||
|
import com.java2nb.novel.domain.BookIndexDO;
|
||||||
|
import com.java2nb.novel.service.BookContentService;
|
||||||
|
import com.java2nb.novel.service.BookIndexService;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.ui.Model;
|
import org.springframework.ui.Model;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
@@ -22,6 +36,9 @@ import com.java2nb.common.utils.PageBean;
|
|||||||
import com.java2nb.common.utils.Query;
|
import com.java2nb.common.utils.Query;
|
||||||
import com.java2nb.common.utils.R;
|
import com.java2nb.common.utils.R;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.servlet.http.HttpSession;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 小说表
|
* 小说表
|
||||||
*
|
*
|
||||||
@@ -30,12 +47,34 @@ import com.java2nb.common.utils.R;
|
|||||||
* @date 2020-12-01 03:49:46
|
* @date 2020-12-01 03:49:46
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
@Controller
|
@Controller
|
||||||
@RequestMapping("/novel/book")
|
@RequestMapping("/novel/book")
|
||||||
public class BookController {
|
public class BookController {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private BookService bookService;
|
private BookService bookService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RedisTemplate<String, String> redisTemplate;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private BookIndexService bookIndexService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private BookContentService bookContentService;
|
||||||
|
|
||||||
|
|
||||||
|
private final Pattern PATTERN_BR = Pattern.compile("<br\\s*/?>", Pattern.CASE_INSENSITIVE);
|
||||||
|
private final Pattern PATTERN_NBSP = Pattern.compile(" ", Pattern.CASE_INSENSITIVE);
|
||||||
|
private final Pattern PATTERN_ANCHOR_OPEN = Pattern.compile("<a[^>]*>", Pattern.CASE_INSENSITIVE);
|
||||||
|
private final Pattern PATTERN_ANCHOR_CLOSE = Pattern.compile("</a>", Pattern.CASE_INSENSITIVE);
|
||||||
|
private final Pattern PATTERN_DIV_OPEN = Pattern.compile("<div[^>]*>", Pattern.CASE_INSENSITIVE);
|
||||||
|
private final Pattern PATTERN_DIV_CLOSE = Pattern.compile("</div>", Pattern.CASE_INSENSITIVE);
|
||||||
|
private final Pattern PATTERN_EMPTY_PARAGRAPH_WITH_LINK = Pattern.compile("<p[^>]*>[^<]*<a[^>]*>[^<]*</a>\\s*</p>", Pattern.CASE_INSENSITIVE);
|
||||||
|
private final Pattern PATTERN_P_OPEN = Pattern.compile("<p[^>]*>", Pattern.CASE_INSENSITIVE);
|
||||||
|
private final Pattern PATTERN_P_CLOSE = Pattern.compile("</p>", Pattern.CASE_INSENSITIVE);
|
||||||
|
|
||||||
@GetMapping()
|
@GetMapping()
|
||||||
@RequiresPermissions("novel:book:book")
|
@RequiresPermissions("novel:book:book")
|
||||||
String Book() {
|
String Book() {
|
||||||
@@ -66,7 +105,7 @@ public class BookController {
|
|||||||
@GetMapping("/edit/{id}")
|
@GetMapping("/edit/{id}")
|
||||||
@RequiresPermissions("novel:book:edit")
|
@RequiresPermissions("novel:book:edit")
|
||||||
String edit(@PathVariable("id") Long id, Model model) {
|
String edit(@PathVariable("id") Long id, Model model) {
|
||||||
BookDO book = bookService.get(id);
|
BookDO book = bookService.get(id);
|
||||||
model.addAttribute("book", book);
|
model.addAttribute("book", book);
|
||||||
return "novel/book/edit";
|
return "novel/book/edit";
|
||||||
}
|
}
|
||||||
@@ -75,7 +114,7 @@ public class BookController {
|
|||||||
@GetMapping("/detail/{id}")
|
@GetMapping("/detail/{id}")
|
||||||
@RequiresPermissions("novel:book:detail")
|
@RequiresPermissions("novel:book:detail")
|
||||||
String detail(@PathVariable("id") Long id, Model model) {
|
String detail(@PathVariable("id") Long id, Model model) {
|
||||||
BookDO book = bookService.get(id);
|
BookDO book = bookService.get(id);
|
||||||
model.addAttribute("book", book);
|
model.addAttribute("book", book);
|
||||||
return "novel/book/detail";
|
return "novel/book/detail";
|
||||||
}
|
}
|
||||||
@@ -87,7 +126,7 @@ public class BookController {
|
|||||||
@ResponseBody
|
@ResponseBody
|
||||||
@PostMapping("/save")
|
@PostMapping("/save")
|
||||||
@RequiresPermissions("novel:book:add")
|
@RequiresPermissions("novel:book:add")
|
||||||
public R save( BookDO book) {
|
public R save(BookDO book) {
|
||||||
if (bookService.save(book) > 0) {
|
if (bookService.save(book) > 0) {
|
||||||
return R.ok();
|
return R.ok();
|
||||||
}
|
}
|
||||||
@@ -101,8 +140,8 @@ public class BookController {
|
|||||||
@ResponseBody
|
@ResponseBody
|
||||||
@RequestMapping("/update")
|
@RequestMapping("/update")
|
||||||
@RequiresPermissions("novel:book:edit")
|
@RequiresPermissions("novel:book:edit")
|
||||||
public R update( BookDO book) {
|
public R update(BookDO book) {
|
||||||
bookService.update(book);
|
bookService.update(book);
|
||||||
return R.ok();
|
return R.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,7 +152,7 @@ public class BookController {
|
|||||||
@PostMapping("/remove")
|
@PostMapping("/remove")
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
@RequiresPermissions("novel:book:remove")
|
@RequiresPermissions("novel:book:remove")
|
||||||
public R remove( Long id) {
|
public R remove(Long id) {
|
||||||
if (bookService.remove(id) > 0) {
|
if (bookService.remove(id) > 0) {
|
||||||
return R.ok();
|
return R.ok();
|
||||||
}
|
}
|
||||||
@@ -128,8 +167,83 @@ public class BookController {
|
|||||||
@ResponseBody
|
@ResponseBody
|
||||||
@RequiresPermissions("novel:book:batchRemove")
|
@RequiresPermissions("novel:book:batchRemove")
|
||||||
public R remove(@RequestParam("ids[]") Long[] ids) {
|
public R remove(@RequestParam("ids[]") Long[] ids) {
|
||||||
bookService.batchRemove(ids);
|
bookService.batchRemove(ids);
|
||||||
return R.ok();
|
return R.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 小说下载
|
||||||
|
*/
|
||||||
|
@RequestMapping(value = "/download")
|
||||||
|
public void download(@RequestParam("bookId") Long bookId, @RequestParam("bookName") String bookName,
|
||||||
|
HttpServletResponse resp) {
|
||||||
|
try {
|
||||||
|
OutputStream out = resp.getOutputStream();
|
||||||
|
Boolean success = redisTemplate
|
||||||
|
.opsForValue()
|
||||||
|
.setIfAbsent(Constant.BOOK_IS_DOWNLOADING_KEY + bookId, "1", 10, TimeUnit.MINUTES);
|
||||||
|
if (Boolean.FALSE.equals(success)) {
|
||||||
|
resp.setContentType("text/html;charset=UTF-8");
|
||||||
|
out.write("该小说正在下载中,请稍后重试!".getBytes(StandardCharsets.UTF_8));
|
||||||
|
out.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//设置响应头,对文件进行url编码
|
||||||
|
bookName = URLEncoder.encode(bookName, StandardCharsets.UTF_8);
|
||||||
|
//解决手机端不能下载附件的问题
|
||||||
|
resp.setContentType("application/octet-stream");
|
||||||
|
resp.setHeader("Content-Disposition", "attachment;filename=" + bookName + ".txt");
|
||||||
|
|
||||||
|
Map<String, Object> params = new HashMap<>();
|
||||||
|
params.put("bookId", bookId);
|
||||||
|
params.put("sort", "index_num");
|
||||||
|
params.put("order", "asc");
|
||||||
|
params.put("limit", 9999);
|
||||||
|
params.put("offset", 0);
|
||||||
|
List<BookIndexDO> bookIndexBigList = bookIndexService.list(params);
|
||||||
|
if (!bookIndexBigList.isEmpty()) {
|
||||||
|
List<List<BookIndexDO>> bookIndexSmallList = bookIndexBigList.stream().collect(
|
||||||
|
Collectors.groupingBy(item -> bookIndexBigList.indexOf(item) / 100)).values().stream().toList();
|
||||||
|
for (List<BookIndexDO> bookIndexList : bookIndexSmallList) {
|
||||||
|
// 获取集合中所有的ID
|
||||||
|
List<Long> bookIndexIds = bookIndexList.stream().map(BookIndexDO::getId).toList();
|
||||||
|
List<BookContentDO> bookContentList = bookContentService.listByIndexIds(bookIndexIds);
|
||||||
|
Map<Long, String> bookContentMap = bookContentList.stream()
|
||||||
|
.collect(Collectors.toMap(BookContentDO::getIndexId, BookContentDO::getContent));
|
||||||
|
for (BookIndexDO bookIndex : bookIndexList) {
|
||||||
|
String indexName = bookIndex.getIndexName();
|
||||||
|
if (indexName != null) {
|
||||||
|
String content = bookContentMap.get(bookIndex.getId());
|
||||||
|
out.write(indexName.getBytes(StandardCharsets.UTF_8));
|
||||||
|
out.write("\r\n".getBytes(StandardCharsets.UTF_8));
|
||||||
|
content = PATTERN_BR.matcher(content).replaceAll("\r\n");
|
||||||
|
content = PATTERN_NBSP.matcher(content).replaceAll(" ");
|
||||||
|
content = PATTERN_ANCHOR_OPEN.matcher(content).replaceAll("");
|
||||||
|
content = PATTERN_ANCHOR_CLOSE.matcher(content).replaceAll("");
|
||||||
|
content = PATTERN_DIV_OPEN.matcher(content).replaceAll("");
|
||||||
|
content = PATTERN_DIV_CLOSE.matcher(content).replaceAll("");
|
||||||
|
content = PATTERN_EMPTY_PARAGRAPH_WITH_LINK.matcher(content).replaceAll("");
|
||||||
|
content = PATTERN_P_OPEN.matcher(content).replaceAll("");
|
||||||
|
content = PATTERN_P_CLOSE.matcher(content).replaceAll("\r\n");
|
||||||
|
out.write(content.getBytes(StandardCharsets.UTF_8));
|
||||||
|
out.write("\r\n".getBytes(StandardCharsets.UTF_8));
|
||||||
|
out.write("\r\n".getBytes(StandardCharsets.UTF_8));
|
||||||
|
out.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
out.close();
|
||||||
|
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
} finally {
|
||||||
|
redisTemplate.delete(Constant.BOOK_IS_DOWNLOADING_KEY + bookId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import com.java2nb.common.annotation.SanitizeMap;
|
|||||||
|
|
||||||
import com.java2nb.novel.domain.BookContentDO;
|
import com.java2nb.novel.domain.BookContentDO;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -33,4 +34,6 @@ public interface BookContentDao {
|
|||||||
int batchRemove(Long[] ids);
|
int batchRemove(Long[] ids);
|
||||||
|
|
||||||
int removeByIndexIds(Long[] indexIds);
|
int removeByIndexIds(Long[] indexIds);
|
||||||
|
|
||||||
|
List<BookContentDO> listByIndexIds(@Param("indexIds") List<Long> indexIds);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,4 +27,6 @@ public interface BookContentService {
|
|||||||
int remove(Long id);
|
int remove(Long id);
|
||||||
|
|
||||||
int batchRemove(Long[] ids);
|
int batchRemove(Long[] ids);
|
||||||
|
|
||||||
|
List<BookContentDO> listByIndexIds(List<Long> indexIds);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.java2nb.novel.service.impl;
|
package com.java2nb.novel.service.impl;
|
||||||
|
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@@ -51,5 +52,10 @@ public class BookContentServiceImpl implements BookContentService {
|
|||||||
public int batchRemove(Long[] ids){
|
public int batchRemove(Long[] ids){
|
||||||
return bookContentDao.batchRemove(ids);
|
return bookContentDao.batchRemove(ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<BookContentDO> listByIndexIds(List<Long> indexIds) {
|
||||||
|
return bookContentDao.listByIndexIds(indexIds);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,17 @@
|
|||||||
</where>
|
</where>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<select id="listByIndexIds" resultType="com.java2nb.novel.domain.BookContentDO">
|
||||||
|
select `id`,`index_id`,`content` from book_content
|
||||||
|
where index_id in (
|
||||||
|
<foreach item="item" index="index" collection="indexIds" separator=",">
|
||||||
|
#{item}
|
||||||
|
</foreach>
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
</select>
|
||||||
|
|
||||||
<insert id="save" parameterType="com.java2nb.novel.domain.BookContentDO">
|
<insert id="save" parameterType="com.java2nb.novel.domain.BookContentDO">
|
||||||
insert into book_content
|
insert into book_content
|
||||||
(`id`,
|
(`id`,
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="count" resultType="int">
|
<select id="count" resultType="int">
|
||||||
select count(*) from book_setting t1 inner join book t2 on t1.book_id = t2.id
|
select count(*) from book_setting t1
|
||||||
<where>
|
<where>
|
||||||
<if test="id != null and id != ''">and t1.id = #{id}</if>
|
<if test="id != null and id != ''">and t1.id = #{id}</if>
|
||||||
<if test="bookId != null and bookId != ''">and t1.book_id = #{bookId}</if>
|
<if test="bookId != null and bookId != ''">and t1.book_id = #{bookId}</if>
|
||||||
|
|||||||
@@ -143,10 +143,14 @@ function load() {
|
|||||||
field: 'id',
|
field: 'id',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
formatter: function (value, row, index) {
|
formatter: function (value, row, index) {
|
||||||
|
// 增加下载按钮
|
||||||
|
var d = '<a class="btn btn-primary btn-sm" href="#" mce_href="#" title="下载TXT" onclick="downloadBook(\''
|
||||||
|
+ row.id
|
||||||
|
+ '\',\'' + row.bookName + '\')"><i class="fa fa-cloud-download"></i></a><br><br> ';
|
||||||
var r = '<a class="btn btn-warning btn-sm ' + s_remove_h + '" href="#" title="删除" mce_href="#" onclick="remove(\''
|
var r = '<a class="btn btn-warning btn-sm ' + s_remove_h + '" href="#" title="删除" mce_href="#" onclick="remove(\''
|
||||||
+ row.id
|
+ row.id
|
||||||
+ '\')"><i class="fa fa-remove"></i></a> ';
|
+ '\')"><i class="fa fa-remove"></i></a> ';
|
||||||
return r;
|
return d + r;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,4 +253,9 @@ function batchRemove() {
|
|||||||
}, function () {
|
}, function () {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function downloadBook(bookId, bookName) {
|
||||||
|
window.open(prefix + '/download?bookId='+bookId+'&bookName='+bookName);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>novel</artifactId>
|
<artifactId>novel</artifactId>
|
||||||
<groupId>com.java2nb</groupId>
|
<groupId>com.java2nb</groupId>
|
||||||
<version>5.2.6</version>
|
<version>5.3.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>novel</artifactId>
|
<artifactId>novel</artifactId>
|
||||||
<groupId>com.java2nb</groupId>
|
<groupId>com.java2nb</groupId>
|
||||||
<version>5.2.6</version>
|
<version>5.3.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
@@ -37,6 +37,10 @@
|
|||||||
<artifactId>jackson-databind</artifactId>
|
<artifactId>jackson-databind</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-mail</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|||||||
@@ -44,4 +44,32 @@ http:
|
|||||||
# 代理用户名
|
# 代理用户名
|
||||||
username: bestproxy_u
|
username: bestproxy_u
|
||||||
# 代理密码
|
# 代理密码
|
||||||
password: bestproxy_p
|
password: bestproxy_p
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#邮箱服务器
|
||||||
|
spring:
|
||||||
|
mail:
|
||||||
|
host: smtp.163.com
|
||||||
|
#发件人昵称
|
||||||
|
nickname: novel-plus
|
||||||
|
#邮箱账户
|
||||||
|
username: xxyopen@163.com
|
||||||
|
#邮箱第三方授权码
|
||||||
|
password: novel123456
|
||||||
|
|
||||||
|
|
||||||
|
# 磁盘监控配置
|
||||||
|
disk-monitor:
|
||||||
|
# 是否开启磁盘监控,true-开启,false-不启用
|
||||||
|
enabled: false
|
||||||
|
# 磁盘监控间隔时间(分钟)
|
||||||
|
interval-minutes: 5
|
||||||
|
# 磁盘使用率阈值
|
||||||
|
warning-threshold: 85.0
|
||||||
|
severe-threshold: 90.0
|
||||||
|
critical-threshold: 95.0
|
||||||
|
# 告警邮件接收人列表(支持多个邮箱)
|
||||||
|
recipients: xxyopen@foxmail.com,12345678@qq.com
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.java2nb.novel.core.bean;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author xiongxiaoyang
|
||||||
|
* @date 2025/10/24
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class DiskInfo {
|
||||||
|
|
||||||
|
private String path;
|
||||||
|
private double totalGB;
|
||||||
|
private double freeGB;
|
||||||
|
private double usage;
|
||||||
|
|
||||||
|
public DiskInfo(String path, long totalBytes, long freeBytes, double usage) {
|
||||||
|
this.path = path;
|
||||||
|
this.totalGB = bytesToGB(totalBytes);
|
||||||
|
this.freeGB = bytesToGB(freeBytes);
|
||||||
|
this.usage = usage;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double bytesToGB(long bytes) {
|
||||||
|
return bytes / (1024.0 * 1024.0 * 1024.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package com.java2nb.novel.core.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author xiongxiaoyang
|
||||||
|
* @date 2025/10/24
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@ConfigurationProperties(prefix = "disk-monitor")
|
||||||
|
@Data
|
||||||
|
public class DiskMonitorProperties {
|
||||||
|
|
||||||
|
private boolean enabled;
|
||||||
|
private int intervalMinutes;
|
||||||
|
private double warningThreshold;
|
||||||
|
private double severeThreshold;
|
||||||
|
private double criticalThreshold;
|
||||||
|
private List<String> recipients;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
package com.java2nb.novel.core.schedule;
|
||||||
|
|
||||||
|
import com.java2nb.novel.core.bean.DiskInfo;
|
||||||
|
import com.java2nb.novel.core.config.DiskMonitorProperties;
|
||||||
|
import com.java2nb.novel.service.EmailService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author xiongxiaoyang
|
||||||
|
* @date 2025/10/24
|
||||||
|
*/
|
||||||
|
@ConditionalOnProperty(prefix = "disk-monitor", name = "enabled", havingValue = "true")
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Slf4j
|
||||||
|
public class DiskMonitorSchedule {
|
||||||
|
|
||||||
|
private final DiskMonitorProperties properties;
|
||||||
|
|
||||||
|
private final EmailService emailService;
|
||||||
|
|
||||||
|
private final AtomicBoolean criticalAlertSent = new AtomicBoolean(false);
|
||||||
|
|
||||||
|
@Scheduled(fixedDelayString = "#{1000 * 60 * ${disk-monitor.interval-minutes}}")
|
||||||
|
public void checkDiskUsage() {
|
||||||
|
log.info("🔍 开始检查磁盘使用情况...");
|
||||||
|
|
||||||
|
File[] roots = File.listRoots();
|
||||||
|
List<DiskInfo> diskInfos = new ArrayList<>();
|
||||||
|
boolean criticalDetected = false;
|
||||||
|
|
||||||
|
for (File root : roots) {
|
||||||
|
String path = root.getAbsolutePath().trim();
|
||||||
|
if (path.isEmpty()) continue;
|
||||||
|
long total = root.getTotalSpace();
|
||||||
|
if (total == 0) continue;
|
||||||
|
long free = root.getFreeSpace();
|
||||||
|
double usage = (double)(total - free) / total * 100;
|
||||||
|
diskInfos.add(new DiskInfo(path, total, free, usage));
|
||||||
|
if (usage >= properties.getCriticalThreshold()) {
|
||||||
|
criticalDetected = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (criticalDetected) {
|
||||||
|
if (criticalAlertSent.compareAndSet(false, true)) {
|
||||||
|
sendAlertEmail("CRITICAL", diskInfos);
|
||||||
|
log.error("🚨 磁盘使用率 ≥ 95%,10秒后终止进程...");
|
||||||
|
try {
|
||||||
|
Thread.sleep(10000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
criticalAlertSent.set(false);
|
||||||
|
double maxUsage = diskInfos.stream().mapToDouble(DiskInfo::getUsage).max().orElse(0);
|
||||||
|
if (maxUsage >= properties.getSevereThreshold() && maxUsage < properties.getCriticalThreshold()) {
|
||||||
|
sendAlertEmail("WARNING", diskInfos);
|
||||||
|
} else if (maxUsage >= properties.getWarningThreshold() && maxUsage < properties.getSevereThreshold()) {
|
||||||
|
sendAlertEmail("INFO", diskInfos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendAlertEmail(String level, List<DiskInfo> diskInfos) {
|
||||||
|
Map<String, Object> model = new HashMap<>();
|
||||||
|
model.put("diskInfos", diskInfos);
|
||||||
|
model.put("alertLevel", getAlertLevelText(level));
|
||||||
|
model.put("icon", getIcon(level));
|
||||||
|
model.put("actionText", getActionText(level));
|
||||||
|
model.put("levelColor", getLevelColor(level));
|
||||||
|
|
||||||
|
String subject = getSubject(level);
|
||||||
|
emailService.sendHtmlEmail(subject, "disk_alert", model, properties.getRecipients());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getAlertLevelText(String level) {
|
||||||
|
return switch (level) {
|
||||||
|
case "CRITICAL" -> "严重";
|
||||||
|
case "WARNING" -> "警告";
|
||||||
|
case "INFO" -> "提示";
|
||||||
|
default -> "通知";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getIcon(String level) {
|
||||||
|
return switch (level) {
|
||||||
|
case "CRITICAL" -> "🚨";
|
||||||
|
case "WARNING" -> "⚠️";
|
||||||
|
case "INFO" -> "💡";
|
||||||
|
default -> "📢";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getActionText(String level) {
|
||||||
|
return switch (level) {
|
||||||
|
case "CRITICAL" ->
|
||||||
|
"⚠️ 检测到任一磁盘使用率 ≥ 95%,为防止系统崩溃,系统已自动<strong style='color:#d32f2f'>关闭爬虫程序</strong>。";
|
||||||
|
case "WARNING" ->
|
||||||
|
"📌 建议:<strong style='color:#f57c00'>请立即暂停爬虫程序</strong>,防止磁盘写满导致服务中断。";
|
||||||
|
case "INFO" ->
|
||||||
|
"📌 提示:磁盘使用率已较高,请关注爬虫数据写入情况。";
|
||||||
|
default -> "";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getLevelColor(String level) {
|
||||||
|
return switch (level) {
|
||||||
|
case "CRITICAL" -> "#d32f2f";
|
||||||
|
case "WARNING" -> "#f57c00";
|
||||||
|
case "INFO" -> "#388e3c";
|
||||||
|
default -> "#1976d2";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getSubject(String level) {
|
||||||
|
return switch (level) {
|
||||||
|
case "CRITICAL" -> "🚨 严重告警:磁盘使用率超 95%,爬虫已关闭";
|
||||||
|
case "WARNING" -> "⚠️ 警告:磁盘使用率超 90%,请暂停爬虫";
|
||||||
|
case "INFO" -> "💡 提示:磁盘使用率超 85%";
|
||||||
|
default -> "磁盘使用率告警";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package com.java2nb.novel.service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author xiongxiaoyang
|
||||||
|
* @date 2025/10/24
|
||||||
|
*/
|
||||||
|
public interface EmailService {
|
||||||
|
|
||||||
|
void sendHtmlEmail(String subject, String templateName, Map<String, Object> templateModel, List<String> to);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package com.java2nb.novel.service.impl;
|
||||||
|
|
||||||
|
import com.java2nb.novel.service.EmailService;
|
||||||
|
import jakarta.mail.internet.InternetAddress;
|
||||||
|
import jakarta.mail.internet.MimeMessage;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.mail.javamail.JavaMailSender;
|
||||||
|
import org.springframework.mail.javamail.MimeMessageHelper;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.thymeleaf.TemplateEngine;
|
||||||
|
import org.thymeleaf.context.Context;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author xiongxiaoyang
|
||||||
|
* @date 2025/10/24
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class EmailServiceImpl implements EmailService {
|
||||||
|
|
||||||
|
private final JavaMailSender javaMailSender;
|
||||||
|
private final TemplateEngine templateEngine;
|
||||||
|
|
||||||
|
@Value("${spring.mail.username}")
|
||||||
|
private String fromEmail;
|
||||||
|
|
||||||
|
@Value("${spring.mail.nickname}")
|
||||||
|
private String nickName;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendHtmlEmail(String subject, String templateName, Map<String, Object> templateModel, List<String> to) {
|
||||||
|
try {
|
||||||
|
MimeMessage message = javaMailSender.createMimeMessage();
|
||||||
|
MimeMessageHelper helper = new MimeMessageHelper(
|
||||||
|
message,
|
||||||
|
MimeMessageHelper.MULTIPART_MODE_MIXED_RELATED,
|
||||||
|
StandardCharsets.UTF_8.name());
|
||||||
|
|
||||||
|
Context context = new Context();
|
||||||
|
context.setVariables(templateModel);
|
||||||
|
|
||||||
|
String htmlBody = templateEngine.process(templateName, context);
|
||||||
|
|
||||||
|
helper.setFrom(new InternetAddress(fromEmail, nickName, "UTF-8"));
|
||||||
|
helper.setTo(to.toArray(new String[0]));
|
||||||
|
helper.setSubject(subject);
|
||||||
|
helper.setText(htmlBody, true);
|
||||||
|
|
||||||
|
javaMailSender.send(message);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("发送邮件失败");
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,6 +28,41 @@ crawl:
|
|||||||
max: 500
|
max: 500
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#邮箱服务器
|
||||||
|
spring:
|
||||||
|
mail:
|
||||||
|
host: smtp.163.com
|
||||||
|
#发件人昵称
|
||||||
|
nickname: novel-plus
|
||||||
|
#邮箱账户
|
||||||
|
username: xxyopen@163.com
|
||||||
|
#邮箱第三方授权码
|
||||||
|
password: novel123456
|
||||||
|
#编码类型
|
||||||
|
default-encoding: UTF-8
|
||||||
|
port: 465
|
||||||
|
properties:
|
||||||
|
mail:
|
||||||
|
smtp:
|
||||||
|
auth: true
|
||||||
|
starttls:
|
||||||
|
enable: true
|
||||||
|
required: rue
|
||||||
|
socketFactory:
|
||||||
|
port: 465
|
||||||
|
class: javax.net.ssl.SSLSocketFactory
|
||||||
|
fallback: false
|
||||||
|
|
||||||
|
# 磁盘监控配置
|
||||||
|
disk-monitor:
|
||||||
|
enabled: false
|
||||||
|
interval-minutes: 5
|
||||||
|
warning-threshold: 85.0
|
||||||
|
severe-threshold: 90.0
|
||||||
|
critical-threshold: 95.0
|
||||||
|
# 告警邮件接收人列表(支持多个邮箱)
|
||||||
|
recipients: xxyopen@foxmail.com,12345678@qq.com
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -118,9 +118,10 @@
|
|||||||
<script language="javascript" type="text/javascript">
|
<script language="javascript" type="text/javascript">
|
||||||
let curr = 1;
|
let curr = 1;
|
||||||
let limit = 10;
|
let limit = 10;
|
||||||
|
let isUpdate = false;
|
||||||
|
|
||||||
search();
|
search();
|
||||||
setInterval(function(){
|
setInterval(function () {
|
||||||
search();
|
search();
|
||||||
}, 10000);
|
}, 10000);
|
||||||
|
|
||||||
@@ -185,6 +186,7 @@
|
|||||||
if (!first) {
|
if (!first) {
|
||||||
curr = obj.curr;
|
curr = obj.curr;
|
||||||
limit = obj.limit;
|
limit = obj.limit;
|
||||||
|
isUpdate = false;
|
||||||
search();
|
search();
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
@@ -196,13 +198,21 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} else {
|
} else if (data.code == 1001) {
|
||||||
|
//未登录
|
||||||
|
location.href = '/user/login.html?originUrl=' + encodeURIComponent(location.href);
|
||||||
|
|
||||||
|
} else if (!isUpdate) {
|
||||||
layer.alert(data.msg);
|
layer.alert(data.msg);
|
||||||
}
|
}
|
||||||
|
isUpdate = true;
|
||||||
|
|
||||||
},
|
},
|
||||||
error: function () {
|
error: function () {
|
||||||
layer.alert('网络异常');
|
if (!isUpdate) {
|
||||||
|
layer.alert('网络异常');
|
||||||
|
}
|
||||||
|
isUpdate = true;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -116,14 +116,13 @@
|
|||||||
<script language="javascript" type="text/javascript">
|
<script language="javascript" type="text/javascript">
|
||||||
let curr = 1;
|
let curr = 1;
|
||||||
let limit = 10;
|
let limit = 10;
|
||||||
|
let isUpdate = false;
|
||||||
|
|
||||||
search();
|
search();
|
||||||
setInterval(function(){
|
setInterval(function () {
|
||||||
search();
|
search();
|
||||||
}, 10000);
|
}, 10000);
|
||||||
|
|
||||||
var pageCrawlSourceList = null;
|
|
||||||
|
|
||||||
function search() {
|
function search() {
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
@@ -134,7 +133,6 @@
|
|||||||
success: function (data) {
|
success: function (data) {
|
||||||
if (data.code == 200) {
|
if (data.code == 200) {
|
||||||
var crawlSourceList = data.data.list;
|
var crawlSourceList = data.data.list;
|
||||||
pageCrawlSourceList = data.data.list;
|
|
||||||
if (crawlSourceList.length > 0) {
|
if (crawlSourceList.length > 0) {
|
||||||
var crawlSourceListHtml = "";
|
var crawlSourceListHtml = "";
|
||||||
for (var i = 0; i < crawlSourceList.length; i++) {
|
for (var i = 0; i < crawlSourceList.length; i++) {
|
||||||
@@ -182,6 +180,7 @@
|
|||||||
if (!first) {
|
if (!first) {
|
||||||
curr = obj.curr;
|
curr = obj.curr;
|
||||||
limit = obj.limit;
|
limit = obj.limit;
|
||||||
|
isUpdate = false;
|
||||||
search();
|
search();
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
@@ -197,13 +196,17 @@
|
|||||||
//未登录
|
//未登录
|
||||||
location.href = '/user/login.html?originUrl=' + encodeURIComponent(location.href);
|
location.href = '/user/login.html?originUrl=' + encodeURIComponent(location.href);
|
||||||
|
|
||||||
} else {
|
} else if (!isUpdate) {
|
||||||
layer.alert(data.msg);
|
layer.alert(data.msg);
|
||||||
}
|
}
|
||||||
|
isUpdate = true;
|
||||||
|
|
||||||
},
|
},
|
||||||
error: function () {
|
error: function () {
|
||||||
layer.alert('网络异常');
|
if (!isUpdate) {
|
||||||
|
layer.alert('网络异常');
|
||||||
|
}
|
||||||
|
isUpdate = true;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -229,11 +232,11 @@
|
|||||||
if (status == 0) {
|
if (status == 0) {
|
||||||
//开启
|
//开启
|
||||||
$("#sourceStatus" + sourceId).html("正在运行");
|
$("#sourceStatus" + sourceId).html("正在运行");
|
||||||
$("#opt" + sourceId).html("<a href='javascript:openOrStopCrawl(" + sourceId + "," + 1 + ")'>关闭 </a>"+"<a href='javascript:updateCrawlSource(" + sourceId + ")'>修改 </a>");
|
$("#opt" + sourceId).html("<a href='javascript:openOrStopCrawl(" + sourceId + "," + 1 + ")'>关闭 </a>" + "<a href='javascript:updateCrawlSource(" + sourceId + ")'>修改 </a>");
|
||||||
} else {
|
} else {
|
||||||
//关闭
|
//关闭
|
||||||
$("#sourceStatus" + sourceId).html("停止运行");
|
$("#sourceStatus" + sourceId).html("停止运行");
|
||||||
$("#opt" + sourceId).html("<a href='javascript:openOrStopCrawl(" + sourceId + "," + 0 + ")'>开启 </a>"+"<a href='javascript:updateCrawlSource(" + sourceId + ")'>修改 </a>");
|
$("#opt" + sourceId).html("<a href='javascript:openOrStopCrawl(" + sourceId + "," + 0 + ")'>开启 </a>" + "<a href='javascript:updateCrawlSource(" + sourceId + ")'>修改 </a>");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
44
novel-crawl/src/main/resources/templates/disk_alert.html
Normal file
44
novel-crawl/src/main/resources/templates/disk_alert.html
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html xmlns:th="http://www.thymeleaf.org">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<style>
|
||||||
|
body { font-family: Arial, sans-serif; color: #333; background: #f4f6f9; }
|
||||||
|
.container { max-width: 800px; margin: 20px auto; padding: 20px; background: #fff; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
|
||||||
|
.header { padding: 15px; border-radius: 6px; text-align: center; color: white; }
|
||||||
|
table { width: 100%; border-collapse: collapse; margin: 20px 0; }
|
||||||
|
th { background: #1976d2; color: white; padding: 12px; text-align: center; }
|
||||||
|
td { padding: 10px; border: 1px solid #ddd; text-align: center; }
|
||||||
|
.action { margin: 20px 0; font-size: 16px; line-height: 1.6; }
|
||||||
|
.footer { margin-top: 30px; font-size: 12px; color: #777; text-align: center; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="header" th:style="'background:' + ${levelColor}">
|
||||||
|
<h2 th:text="${icon} + ' ' + ${alertLevel} + ' 告警:磁盘使用率过高'"></h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p><strong>时间:</strong><span th:text="${#dates.format(#dates.createNow(), 'yyyy-MM-dd HH:mm:ss')}"></span></p>
|
||||||
|
|
||||||
|
<div class="action" th:utext="${actionText}"></div>
|
||||||
|
|
||||||
|
<h3>📊 磁盘使用详情</h3>
|
||||||
|
<table>
|
||||||
|
<tr style="background:#1976d2; color:white;">
|
||||||
|
<th>磁盘路径</th><th>总空间 (GB)</th><th>空闲空间 (GB)</th><th>使用率</th>
|
||||||
|
</tr>
|
||||||
|
<tr th:each="disk : ${diskInfos}" th:style="'background-color:#f9f9f9;'">
|
||||||
|
<td th:text="${disk.path}"></td>
|
||||||
|
<td th:text="${#numbers.formatDecimal(disk.totalGB, 2, 2)}"></td>
|
||||||
|
<td th:text="${#numbers.formatDecimal(disk.freeGB, 2, 2)}"></td>
|
||||||
|
<td th:text="${#numbers.formatDecimal(disk.usage, 2, 2)} + '%'"
|
||||||
|
th:style="'color:' + ${disk.usage >= 95 ? '#d32f2f' : (disk.usage >= 90 ? '#f57c00' : '#388e3c')} + '; font-weight:bold;'">
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div class="footer">此邮件由磁盘监控系统自动发送,请及时处理。</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>novel</artifactId>
|
<artifactId>novel</artifactId>
|
||||||
<groupId>com.java2nb</groupId>
|
<groupId>com.java2nb</groupId>
|
||||||
<version>5.2.6</version>
|
<version>5.3.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ public class IpLocationServiceImpl implements IpLocationService {
|
|||||||
try {
|
try {
|
||||||
// 示例返回:"中国|0|湖北省|武汉市|电信"
|
// 示例返回:"中国|0|湖北省|武汉市|电信"
|
||||||
String region = searcher.search(ip);
|
String region = searcher.search(ip);
|
||||||
|
log.info("IP:{},区域:{}", ip, region);
|
||||||
String[] regions = region.split("\\|");
|
String[] regions = region.split("\\|");
|
||||||
if (regions.length > 0) {
|
if (regions.length > 0) {
|
||||||
// 国家
|
// 国家
|
||||||
@@ -38,9 +39,17 @@ public class IpLocationServiceImpl implements IpLocationService {
|
|||||||
return getLocation(publicIp);
|
return getLocation(publicIp);
|
||||||
}
|
}
|
||||||
} else if ("中国".equals(country)) {
|
} else if ("中国".equals(country)) {
|
||||||
// 是中国,则返回省份(第三个字段)
|
// 若为中国,则尝试返回省份(regions[2])
|
||||||
String province = regions.length > 2 ? regions[2] : "未知地区";
|
if (regions.length <= 2) {
|
||||||
// 去掉最后一个“省”字
|
// 数据不足,直接返回国家
|
||||||
|
return country;
|
||||||
|
}
|
||||||
|
String province = regions[2];
|
||||||
|
if (!StringUtils.hasText(province) || "0".equals(province)) {
|
||||||
|
// 省份字段为空或未知,返回国家
|
||||||
|
return country;
|
||||||
|
}
|
||||||
|
// 去掉末尾的“省”字
|
||||||
return province.endsWith("省") ? province.substring(0, province.length() - 1) : province;
|
return province.endsWith("省") ? province.substring(0, province.length() - 1) : province;
|
||||||
} else {
|
} else {
|
||||||
// 非中国,返回国家名
|
// 非中国,返回国家名
|
||||||
|
|||||||
2
pom.xml
2
pom.xml
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
<groupId>com.java2nb</groupId>
|
<groupId>com.java2nb</groupId>
|
||||||
<artifactId>novel</artifactId>
|
<artifactId>novel</artifactId>
|
||||||
<version>5.2.6</version>
|
<version>5.3.0</version>
|
||||||
<modules>
|
<modules>
|
||||||
<module>novel-common</module>
|
<module>novel-common</module>
|
||||||
<module>novel-front</module>
|
<module>novel-front</module>
|
||||||
|
|||||||
Reference in New Issue
Block a user