This commit is contained in:
2025-10-30 15:30:14 +08:00
parent fbf1804998
commit 0f69b41c1d
21 changed files with 1046 additions and 62 deletions

View File

@ -171,7 +171,6 @@ public class GraphHopperController {
// 用新实例计算路径
GHResponse response = currentHopper.route(ghRequest);
// 处理错误
if (response.hasErrors()) {
// 检查是否有超出范围的错误
@ -219,7 +218,6 @@ public class GraphHopperController {
// 配置基础参数
hopper.setOSMFile(osmFilePath);
hopper.setGraphHopperLocation(graphHopperProperties.getGraphLocation());
// 配置交通方式 + 权重策略
List<Profile> profileList = new ArrayList<>();
for (String profileName : graphHopperProperties.getProfiles()) {
@ -261,7 +259,7 @@ public class GraphHopperController {
System.out.println("删除旧地图目录: " + graphDir.getAbsolutePath() + "" + (dirDeleted ? "成功" : "失败"));
}
// 重载: 递归删除子目录
// 重载:递归删除子目录
private void deleteOldGraphDir(File subDir) {
File[] files = subDir.listFiles();
if (files != null) {

View File

@ -0,0 +1,73 @@
package com.yj.earth.business.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yj.earth.business.domain.Matter;
import com.yj.earth.business.service.MatterService;
import com.yj.earth.common.util.ApiResponse;
import com.yj.earth.dto.matter.AddMatterDto;
import com.yj.earth.dto.matter.UpdateMatterDto;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
@Tag(name = "物资数据管理")
@RestController
@RequestMapping("/matter")
public class MatterController {
@Resource
private MatterService matterService;
@PostMapping("/add")
@Operation(summary = "添加物资")
public ApiResponse add(@RequestBody AddMatterDto addMatterDto) {
Matter matter = new Matter();
BeanUtils.copyProperties(addMatterDto, matter);
matterService.save(matter);
return ApiResponse.success(null);
}
@Operation(summary = "删除物资")
@PostMapping("/deletes")
public ApiResponse deletes(@Parameter(description = "物资ID列表") @RequestBody List<String> ids) {
matterService.removeByIds(ids);
return ApiResponse.success(null);
}
@Operation(summary = "更新物资")
@PostMapping("/update")
public ApiResponse update(@RequestBody UpdateMatterDto updateMatterDto) {
Matter matter = new Matter();
BeanUtils.copyProperties(updateMatterDto, matter);
matterService.updateById(matter);
return ApiResponse.success(null);
}
@Operation(summary = "物资详情")
@PostMapping("/detail")
public ApiResponse detail(@Parameter(description = "物资ID") @RequestParam(value = "物资ID") String id) {
Matter matter = matterService.getById(id);
return ApiResponse.success(matter);
}
@Operation(summary = "物资列表")
@PostMapping("/list")
public ApiResponse list(@Parameter(description = "分页数量") Integer pageNum,
@Parameter(description = "分页大小") Integer pageSize,
@Parameter(description = "物资名称") String name) {
LambdaQueryWrapper<Matter> queryWrapper = new LambdaQueryWrapper<>();
if (StringUtils.isNotBlank(name)) {
queryWrapper.like(Matter::getName, name);
}
Page<Matter> page = new Page<>(pageNum, pageSize);
List<Matter> matterList = matterService.list(page, queryWrapper);
return ApiResponse.success(matterList);
}
}

View File

@ -35,36 +35,37 @@ public class PoiController {
@GetMapping("/data")
@Operation(summary = "查看数据")
public ApiResponse data(@Parameter(description = "分页页码") Integer pageNum, @Parameter(description = "分页大小") Integer pageSize, @Parameter(description = "名称搜索") String name) {
public ApiResponse data(@Parameter(description = "分页页码") Integer pageNum,
@Parameter(description = "分页大小") Integer pageSize,
@Parameter(description = "名称搜索") String name) {
int offset = (pageNum - 1) * pageSize;
StringBuilder dataSql = new StringBuilder("SELECT id, name, address, lng, lat FROM tbl_pois WHERE 1=1");
StringBuilder dataSql = new StringBuilder("SELECT id, name, lng, lat FROM data WHERE 1=1");
List<Object> params = new ArrayList<>();
// 添加名称搜索条件
if (name != null && !name.trim().isEmpty()) {
dataSql.append(" AND name LIKE ?");
params.add("%" + name.trim() + "%");
}
// 添加分页条件
dataSql.append(" LIMIT ? OFFSET ?");
try {
// 执行数据查询、获取List
List<PoiVo> poiList = new ArrayList<>();
long dataStartTime = System.currentTimeMillis();
try (PreparedStatement dataPs = connection.prepareStatement(dataSql.toString())) {
// 设置参数(搜索条件 + 分页参数)
int paramIndex = 1;
for (Object param : params) {
dataPs.setObject(paramIndex++, param);
}
dataPs.setInt(paramIndex++, pageSize);
dataPs.setInt(paramIndex++, offset);
// 处理结果集、填充List
try (ResultSet dataRs = dataPs.executeQuery()) {
while (dataRs.next()) {
PoiVo poi = new PoiVo();
poi.setId(dataRs.getLong("id"));
poi.setName(dataRs.getString("name"));
poi.setAddress(dataRs.getString("address"));
poi.setLng(dataRs.getString("lng"));
poi.setLat(dataRs.getString("lat"));
poiList.add(poi);

View File

@ -12,11 +12,11 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.*;
import org.sqlite.JDBC;
import javax.annotation.Resource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.io.File;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
@ -96,7 +96,7 @@ public class PoiInfoController {
}
String path = poiInfo.getPath();
// 构建查询SQL
StringBuilder dataSql = new StringBuilder("SELECT id, name, address, lng, lat FROM tbl_pois WHERE 1=1");
StringBuilder dataSql = new StringBuilder("SELECT id, name, lng, lat FROM data WHERE 1=1");
List<Object> params = new ArrayList<>();
// 名称搜索条件
if (name != null && !name.trim().isEmpty()) {
@ -106,7 +106,7 @@ public class PoiInfoController {
// 分页条件
dataSql.append(" LIMIT ? OFFSET ?");
// 执行查询
try (Connection connection = SQLiteUtil.getConnection(path);
try (Connection connection = getConnectionByAbsolutePath(path);
PreparedStatement dataPs = connection.prepareStatement(dataSql.toString())) {
// 设置参数
int paramIndex = 1;
@ -122,7 +122,6 @@ public class PoiInfoController {
PoiVo poi = new PoiVo();
poi.setId(dataRs.getLong("id"));
poi.setName(dataRs.getString("name"));
poi.setAddress(dataRs.getString("address"));
poi.setLng(dataRs.getString("lng"));
poi.setLat(dataRs.getString("lat"));
poiList.add(poi);
@ -134,4 +133,33 @@ public class PoiInfoController {
return ApiResponse.failure("POI数据查询失败" + e.getMessage());
}
}
/**
* 根据绝对路径获取SQLite数据库连接
*/
public static Connection getConnectionByAbsolutePath(String absolutePath) throws SQLException {
if (absolutePath == null || absolutePath.trim().isEmpty()) {
throw new SQLException("数据库文件路径不能为空");
}
File dbFile = new File(absolutePath.trim());
if (!dbFile.exists()) {
throw new SQLException("数据库文件不存在:" + absolutePath);
}
if (!dbFile.isFile()) {
throw new SQLException("路径指向的不是文件:" + absolutePath);
}
if (!dbFile.canRead()) {
throw new SQLException("没有权限读取数据库文件:" + absolutePath);
}
try {
Class.forName(JDBC.class.getName());
} catch (ClassNotFoundException e) {
throw new SQLException("SQLite驱动加载失败、请检查依赖是否正确", e);
}
String jdbcUrl = "jdbc:sqlite:" + absolutePath;
Connection connection = DriverManager.getConnection(jdbcUrl);
return connection;
}
}

View File

@ -1,24 +1,35 @@
package com.yj.earth.business.controller;
import cn.hutool.core.lang.UUID;
import com.yj.earth.business.domain.Source;
import com.yj.earth.business.service.SourceService;
import com.yj.earth.common.util.ApiResponse;
import com.yj.earth.datasource.DatabaseManager;
import com.yj.earth.dto.system.UpdateSystemServiceDto;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import org.sqlite.JDBC;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
import javax.annotation.Resource;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.sql.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.*;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import static com.yj.earth.common.constant.GlobalConstant.SHOW;
@Tag(name = "系统服务管理")
@RestController
@ -27,6 +38,8 @@ public class SystemController {
@Value("${server.port}")
private Integer serverPort;
@Resource
private SourceService sourceService;
private static final String CONFIG_FILE_PATH = "application.yml";
@ -86,48 +99,202 @@ public class SystemController {
}
}
@Operation(summary = "工程导出")
@GetMapping("/export")
public void exportProject(HttpServletResponse response) {
// 获取 SQLite 文件绝对路径
String sqliteFilePath = DatabaseManager.getSqliteDbFilePath();
// 校验路径与文件有效性
if (sqliteFilePath == null || sqliteFilePath.isEmpty()) {
handleError(response, HttpStatus.BAD_REQUEST.value(), "SQLite数据库未初始化");
return;
}
File sqliteFile = new File(sqliteFilePath);
if (!sqliteFile.exists()) {
handleError(response, HttpStatus.NOT_FOUND.value(), "SQLite文件不存在");
return;
}
if (!sqliteFile.canRead()) {
handleError(response, HttpStatus.FORBIDDEN.value(), "无权限读取SQLite文件");
return;
}
// 配置下载响应头(让浏览器触发下载而非打开)
response.setContentType("application/octet-stream");
response.setContentLengthLong(sqliteFile.length());
// 处理文件名编码
String fileName = URLEncoder.encode("app.db", StandardCharsets.UTF_8);
response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
response.setBufferSize(1024 * 8); // 设置响应缓冲区(提升传输效率)
// 读取文件流并写入响应
try (InputStream inputStream = new BufferedInputStream(new FileInputStream(sqliteFile));
OutputStream outputStream = new BufferedOutputStream(response.getOutputStream())) {
byte[] buffer = new byte[1024 * 8];
int len;
// 循环读取文件内容并写入响应流
while ((len = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, len);
@PostMapping("/importProjectConfig")
@Operation(summary = "导入项目配置")
public ApiResponse importProjectConfig(@RequestParam("path") String zipPath) throws SQLException, IOException {
File tempDir = Files.createTempDirectory("project_config_temp").toFile();
File sqliteFile = null;
try {
sqliteFile = unzipAndGetSqlite(zipPath, tempDir.getAbsolutePath());
String sqliteFilePath = sqliteFile.getAbsolutePath();
String fileName = Paths.get(zipPath).getFileName().toString();
// 创建新的总根节点
Source parentSource = new Source();
parentSource.setId(UUID.randomUUID().toString(true));
parentSource.setSourceName(fileName);
parentSource.setTreeIndex(0);
parentSource.setSourceType("folder");
parentSource.setIsShow(SHOW);
sourceService.save(parentSource);
String newParentId = parentSource.getId();
// 连接SQLite并读取原始节点列表
Connection connection = getConnectionByAbsolutePath(sqliteFilePath);
List<Source> originalSourceList = getSourceList(connection, newParentId);
// 过滤不需要的节点
List<Source> filteredList = originalSourceList.stream()
.filter(source -> !fileName.equals(source.getSourceName()))
.collect(Collectors.toList());
// 生成新ID
Map<String, String> originalIdToNewId = new HashMap<>();
for (Source source : filteredList) {
String newId = UUID.randomUUID().toString(true);
originalIdToNewId.put(source.getId(), newId);
source.setId(newId);
}
// 基于映射表重构父子关系
List<Source> processedList = filteredList.stream().map(source -> {
String originalParentId = source.getParentId();
if (originalParentId != null && !originalParentId.trim().isEmpty() && originalIdToNewId.containsKey(originalParentId)) {
// 父节点在导入数据中用新ID关联
source.setParentId(originalIdToNewId.get(originalParentId));
} else {
// 父节点不在导入数据中:挂到总根节点
source.setParentId(newParentId);
}
return source;
}).collect(Collectors.toList());
// 批量保存
sourceService.saveBatch(processedList);
return ApiResponse.success(null);
} finally {
if (tempDir.exists()) {
deleteDirectory(tempDir);
}
// 强制刷新缓冲区、确保所有数据发送到前端
response.flushBuffer();
} catch (IOException e) {
handleError(response, HttpStatus.INTERNAL_SERVER_ERROR.value(), "文件下载失败:" + e.getMessage());
}
}
private List<Source> getSourceList(Connection connection, String unusedParentId) throws SQLException {
List<Source> sourceList = new ArrayList<>();
String sql = "SELECT id, source_name, source_type, source_path, parent_id, " +
"tree_index, is_show, detail, params, created_at, updated_at " +
"FROM source";
DateTimeFormatter timeFormatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
try (Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(sql)) {
while (resultSet.next()) {
Source source = new Source();
source.setId(resultSet.getString("id"));
source.setSourceName(resultSet.getString("source_name"));
source.setSourceType(resultSet.getString("source_type"));
source.setSourcePath(resultSet.getString("source_path"));
// 关键修改读取原始parent_id而非覆盖为新父节点ID
source.setParentId(resultSet.getString("parent_id"));
Integer treeIndex = resultSet.getObject("tree_index") != null
? resultSet.getInt("tree_index")
: null;
source.setTreeIndex(treeIndex);
Integer isShow = resultSet.getObject("is_show") != null
? resultSet.getInt("is_show")
: null;
source.setIsShow(isShow);
source.setDetail(resultSet.getString("detail"));
source.setParams(resultSet.getString("params"));
// 时间处理(保持不变)
String createdAtStr = resultSet.getString("created_at");
if (createdAtStr != null && !createdAtStr.trim().isEmpty()) {
try {
source.setCreatedAt(LocalDateTime.parse(createdAtStr, timeFormatter));
} catch (DateTimeParseException e) {
source.setCreatedAt(null);
}
} else {
source.setCreatedAt(null);
}
String updatedAtStr = resultSet.getString("updated_at");
if (updatedAtStr != null && !updatedAtStr.trim().isEmpty()) {
try {
source.setUpdatedAt(LocalDateTime.parse(updatedAtStr, timeFormatter));
} catch (DateTimeParseException e) {
source.setUpdatedAt(null);
}
} else {
source.setUpdatedAt(null);
}
sourceList.add(source);
}
} finally {
if (connection != null && !connection.isClosed()) {
connection.close();
}
}
return sourceList;
}
/**
* 解压ZIP文件并获取SQLite文件
*/
private File unzipAndGetSqlite(String zipPath, String destDir) throws IOException {
File zipFile = new File(zipPath);
// 校验ZIP文件是否存在
if (!zipFile.exists() || !zipFile.isFile()) {
throw new FileNotFoundException("ZIP文件不存在或不是有效文件: " + zipPath);
}
File sqliteFile = null;
try (ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFile))) {
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
File destFile = new File(destDir, entry.getName());
// 处理目录条目
if (entry.isDirectory()) {
destFile.mkdirs();
continue;
}
// 确保父目录存在
if (!destFile.getParentFile().exists()) {
destFile.getParentFile().mkdirs();
}
// 写入文件内容
try (FileOutputStream fos = new FileOutputStream(destFile)) {
byte[] buffer = new byte[1024];
int len;
while ((len = zis.read(buffer)) > 0) {
fos.write(buffer, 0, len);
}
}
String fileName = destFile.getName().toLowerCase();
if (fileName.endsWith(".db") || fileName.endsWith(".sqlite")) {
if (sqliteFile != null) {
throw new IOException("ZIP文件中包含多个SQLite文件、无法确定目标文件");
}
sqliteFile = destFile;
}
zis.closeEntry();
}
}
if (sqliteFile == null) {
throw new IOException("ZIP文件中未找到SQLite文件.db或.sqlite后缀");
}
return sqliteFile;
}
/**
* 递归删除目录及内部文件
*/
private void deleteDirectory(File dir) {
if (dir.isDirectory()) {
File[] files = dir.listFiles();
if (files != null) {
for (File file : files) {
deleteDirectory(file);
}
}
}
dir.delete();
}
/**
* 返回指定状态码与错误信息
@ -142,4 +309,32 @@ public class SystemController {
e.printStackTrace();
}
}
/**
* 根据绝对路径获取SQLite数据库连接
*/
public static Connection getConnectionByAbsolutePath(String absolutePath) throws SQLException {
if (absolutePath == null || absolutePath.trim().isEmpty()) {
throw new SQLException("数据库文件路径不能为空");
}
File dbFile = new File(absolutePath.trim());
if (!dbFile.exists()) {
throw new SQLException("数据库文件不存在:" + absolutePath);
}
if (!dbFile.isFile()) {
throw new SQLException("路径指向的不是文件:" + absolutePath);
}
if (!dbFile.canRead()) {
throw new SQLException("没有权限读取数据库文件:" + absolutePath);
}
try {
Class.forName(JDBC.class.getName());
} catch (ClassNotFoundException e) {
throw new SQLException("SQLite驱动加载失败、请检查依赖是否正确", e);
}
String jdbcUrl = "jdbc:sqlite:" + absolutePath;
Connection connection = DriverManager.getConnection(jdbcUrl);
return connection;
}
}

View File

@ -0,0 +1,41 @@
package com.yj.earth.business.domain;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import java.io.Serializable;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
/**
* <p>
*
* </p>
*
* @author 周志雄
* @since 2025-10-30
*/
@Getter
@Setter
@Accessors(chain = true)
@Schema(name = "Matter", description = "")
public class Matter implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.ASSIGN_UUID)
private String id;
private String name;
private Integer num;
@TableField(fill = FieldFill.INSERT)
private String createdAt;
@TableField(fill = FieldFill.UPDATE)
private String updatedAt;
}

View File

@ -0,0 +1,18 @@
package com.yj.earth.business.mapper;
import com.yj.earth.business.domain.Matter;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* <p>
* Mapper 接口
* </p>
*
* @author 周志雄
* @since 2025-10-30
*/
@Mapper
public interface MatterMapper extends BaseMapper<Matter> {
}

View File

@ -0,0 +1,16 @@
package com.yj.earth.business.service;
import com.yj.earth.business.domain.Matter;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* <p>
* 服务类
* </p>
*
* @author 周志雄
* @since 2025-10-30
*/
public interface MatterService extends IService<Matter> {
}

View File

@ -0,0 +1,20 @@
package com.yj.earth.business.service.impl;
import com.yj.earth.business.domain.Matter;
import com.yj.earth.business.mapper.MatterMapper;
import com.yj.earth.business.service.MatterService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* <p>
* 服务实现类
* </p>
*
* @author 周志雄
* @since 2025-10-30
*/
@Service
public class MatterServiceImpl extends ServiceImpl<MatterMapper, Matter> implements MatterService {
}

View File

@ -251,7 +251,7 @@ public class SourceServiceImpl extends ServiceImpl<SourceMapper, Source> impleme
// 处理分页和总数
if (pageNum != null && pageSize != null) {
// 分页时查询条件排除directory类型
// 分页时查询条件排除directory类型
sourceQueryWrapper.ne(Source::getSourceType, "directory");
// 分页查询:获取分页数据
Page<Source> page = new Page<>(pageNum, pageSize);

View File

@ -34,7 +34,7 @@ public class CodeUtil {
}
// 传入需要生成代码的表名
Generation("poi_info");
Generation("matter");
}
public static void Generation(String... tableName) {

View File

@ -410,7 +410,7 @@ public class GdalJsonConverter {
}
/**
* 创建空的MultiPolygon几何
* 创建空的 MultiPolygon 几何
*/
private static JSONObject createEmptyMultiPolygon() throws JSONException {
JSONObject geometry = new JSONObject();

View File

@ -0,0 +1,172 @@
package com.yj.earth.common.util;
import java.sql.*;
public class PoiDataUtil {
// 源数据库路径
private static final String SOURCE_DB_PATH = "jdbc:sqlite:E:\\Users\\MSI\\Downloads\\poi.db3";
// 目标数据库路径
private static final String TARGET_DB_PATH = "jdbc:sqlite:F:\\poi.db";
public static void main(String[] args) {
Connection sourceConn = null;
Connection targetConn = null;
try {
// 加载SQLite JDBC驱动
Class.forName("org.sqlite.JDBC");
// 建立数据库连接
sourceConn = DriverManager.getConnection(SOURCE_DB_PATH);
targetConn = DriverManager.getConnection(TARGET_DB_PATH);
System.out.println("数据库连接成功");
// 禁用自动提交、以便事务管理
targetConn.setAutoCommit(false);
// 创建目标表结构
createTargetTables(targetConn);
// 迁移地区表数据
migrateAreaTable(sourceConn, targetConn);
System.out.println("地区表数据迁移完成");
// 迁移全国数据(包括香港和澳门)
migratePoiData(sourceConn, targetConn, "poi_gcj");
migratePoiData(sourceConn, targetConn, "poi_bd_hk");
migratePoiData(sourceConn, targetConn, "poi_bd_mo");
System.out.println("POI数据迁移完成");
// 为name字段创建索引
createNameIndex(targetConn);
System.out.println("索引创建完成");
// 提交事务
targetConn.commit();
System.out.println("所有操作已提交");
} catch (ClassNotFoundException e) {
System.err.println("找不到SQLite JDBC驱动: " + e.getMessage());
} catch (SQLException e) {
System.err.println("数据库操作错误: " + e.getMessage());
try {
if (targetConn != null) {
targetConn.rollback();
System.out.println("事务已回滚");
}
} catch (SQLException ex) {
System.err.println("回滚错误: " + ex.getMessage());
}
} finally {
// 关闭数据库连接
try {
if (sourceConn != null) sourceConn.close();
if (targetConn != null) targetConn.close();
System.out.println("数据库连接已关闭");
} catch (SQLException e) {
System.err.println("关闭连接错误: " + e.getMessage());
}
}
}
/**
* 创建目标数据库的表结构
*/
private static void createTargetTables(Connection conn) throws SQLException {
// 创建地区表
String createAreaTable = "CREATE TABLE IF NOT EXISTS \"poi_area\" (" +
"\"adcode\" INTEGER," +
"\"parentid\" INTEGER," +
"\"citycode\" TEXT," +
"\"level\" TEXT," +
"\"name\" TEXT," +
"\"center\" TEXT," +
"\"islast\" TEXT" +
");";
// 创建数据主表
String createDataTable = "CREATE TABLE IF NOT EXISTS \"data\" (" +
"\"id\" INTEGER," +
"\"lng\" REAL," +
"\"lat\" REAL," +
"\"name\" TEXT," +
"\"areaid\" INTEGER" +
");";
try (Statement stmt = conn.createStatement()) {
stmt.execute(createAreaTable);
stmt.execute(createDataTable);
}
}
/**
* 迁移地区表数据
*/
private static void migrateAreaTable(Connection sourceConn, Connection targetConn) throws SQLException {
String selectSql = "SELECT adcode, parentid, citycode, level, name, center, islast FROM poi_area";
String insertSql = "INSERT INTO poi_area (adcode, parentid, citycode, level, name, center, islast) " +
"VALUES (?, ?, ?, ?, ?, ?, ?)";
try (PreparedStatement selectStmt = sourceConn.prepareStatement(selectSql);
PreparedStatement insertStmt = targetConn.prepareStatement(insertSql);
ResultSet rs = selectStmt.executeQuery()) {
int count = 0;
while (rs.next()) {
insertStmt.setInt(1, rs.getInt("adcode"));
insertStmt.setInt(2, rs.getInt("parentid"));
insertStmt.setString(3, rs.getString("citycode"));
insertStmt.setString(4, rs.getString("level"));
insertStmt.setString(5, rs.getString("name"));
insertStmt.setString(6, rs.getString("center"));
insertStmt.setString(7, rs.getString("islast"));
insertStmt.executeUpdate();
count++;
// 每1000条记录打印一次进度
if (count % 1000 == 0) {
System.out.println("已迁移地区数据: " + count + "");
}
}
System.out.println("地区表共迁移: " + count + " 条记录");
}
}
/**
* 迁移 POI 数据
*/
private static void migratePoiData(Connection sourceConn, Connection targetConn, String sourceTable) throws SQLException {
// 查询源表中需要的字段
String selectSql = "SELECT id, wgslng, wgslat, name, areaid FROM " + sourceTable;
// 插入到目标表
String insertSql = "INSERT INTO data (id, lng, lat, name, areaid) VALUES (?, ?, ?, ?, ?)";
try (PreparedStatement selectStmt = sourceConn.prepareStatement(selectSql);
PreparedStatement insertStmt = targetConn.prepareStatement(insertSql);
ResultSet rs = selectStmt.executeQuery()) {
int count = 0;
while (rs.next()) {
insertStmt.setInt(1, rs.getInt("id"));
insertStmt.setDouble(2, rs.getDouble("wgslng"));
insertStmt.setDouble(3, rs.getDouble("wgslat"));
insertStmt.setString(4, rs.getString("name"));
if (rs.wasNull()) {
insertStmt.setNull(5, Types.INTEGER);
} else {
insertStmt.setInt(5, rs.getInt("areaid"));
}
insertStmt.executeUpdate();
count++;
// 每10000条记录打印一次进度
if (count % 10000 == 0) {
System.out.println("已迁移" + sourceTable + "数据: " + count + "");
}
}
System.out.println(sourceTable + "共迁移: " + count + " 条记录");
}
}
private static void createNameIndex(Connection conn) throws SQLException {
String createIndexSql = "CREATE INDEX IF NOT EXISTS idx_data_name ON data(name)";
try (Statement stmt = conn.createStatement()) {
stmt.execute(createIndexSql);
}
}
}

View File

@ -0,0 +1,184 @@
package com.yj.earth.common.util;
import java.sql.*;
public class PoiExportDataUtil {
// 源数据库路径
private static final String SOURCE_DB_PATH = "jdbc:sqlite:E:\\Users\\MSI\\Downloads\\poi.db3";
// 目标数据库路径
private static final String TARGET_DB_PATH = "jdbc:sqlite:F:\\export.db";
public static void main(String[] args) {
Connection sourceConn = null;
Connection targetConn = null;
try {
// 加载SQLite JDBC驱动
Class.forName("org.sqlite.JDBC");
// 建立数据库连接
sourceConn = DriverManager.getConnection(SOURCE_DB_PATH);
targetConn = DriverManager.getConnection(TARGET_DB_PATH);
System.out.println("数据库连接成功");
// 禁用自动提交、以便事务管理
targetConn.setAutoCommit(false);
// 创建目标表结构
createTargetTables(targetConn);
// 迁移地区表数据
migrateAreaTable(sourceConn, targetConn);
System.out.println("地区表数据迁移完成");
// 迁移全国数据(包括香港和澳门)
migratePoiData(sourceConn, targetConn, "poi_gcj");
migratePoiData(sourceConn, targetConn, "poi_bd_hk");
migratePoiData(sourceConn, targetConn, "poi_bd_mo");
System.out.println("POI数据迁移完成");
// 为areaid字段创建索引此处为修改点
createAreaidIndex(targetConn);
System.out.println("索引创建完成");
// 提交事务
targetConn.commit();
System.out.println("所有操作已提交");
} catch (ClassNotFoundException e) {
System.err.println("找不到SQLite JDBC驱动: " + e.getMessage());
} catch (SQLException e) {
System.err.println("数据库操作错误: " + e.getMessage());
try {
if (targetConn != null) {
targetConn.rollback();
System.out.println("事务已回滚");
}
} catch (SQLException ex) {
System.err.println("回滚错误: " + ex.getMessage());
}
} finally {
// 关闭数据库连接
try {
if (sourceConn != null) sourceConn.close();
if (targetConn != null) targetConn.close();
System.out.println("数据库连接已关闭");
} catch (SQLException e) {
System.err.println("关闭连接错误: " + e.getMessage());
}
}
}
/**
* 创建目标数据库的表结构
*/
private static void createTargetTables(Connection conn) throws SQLException {
// 创建地区表
String createAreaTable = "CREATE TABLE IF NOT EXISTS \"poi_area\" (" +
"\"adcode\" INTEGER," +
"\"parentid\" INTEGER," +
"\"citycode\" TEXT," +
"\"level\" TEXT," +
"\"name\" TEXT," +
"\"center\" TEXT," +
"\"islast\" TEXT" +
");";
// 创建数据主表
String createDataTable = "CREATE TABLE IF NOT EXISTS \"data\" (" +
"\"id\" INTEGER," +
"\"lng\" REAL," +
"\"lat\" REAL," +
"\"name\" TEXT," +
"\"areaid\" INTEGER" +
");";
try (Statement stmt = conn.createStatement()) {
stmt.execute(createAreaTable);
stmt.execute(createDataTable);
}
}
/**
* 迁移地区表数据
*/
private static void migrateAreaTable(Connection sourceConn, Connection targetConn) throws SQLException {
String selectSql = "SELECT adcode, parentid, citycode, level, name, center, islast FROM poi_area";
String insertSql = "INSERT INTO poi_area (adcode, parentid, citycode, level, name, center, islast) " +
"VALUES (?, ?, ?, ?, ?, ?, ?)";
try (PreparedStatement selectStmt = sourceConn.prepareStatement(selectSql);
PreparedStatement insertStmt = targetConn.prepareStatement(insertSql);
ResultSet rs = selectStmt.executeQuery()) {
int count = 0;
while (rs.next()) {
insertStmt.setInt(1, rs.getInt("adcode"));
insertStmt.setInt(2, rs.getInt("parentid"));
insertStmt.setString(3, rs.getString("citycode"));
insertStmt.setString(4, rs.getString("level"));
insertStmt.setString(5, rs.getString("name"));
insertStmt.setString(6, rs.getString("center"));
insertStmt.setString(7, rs.getString("islast"));
insertStmt.executeUpdate();
count++;
// 每1000条记录打印一次进度
if (count % 1000 == 0) {
System.out.println("已迁移地区数据: " + count + "");
}
}
System.out.println("地区表共迁移: " + count + " 条记录");
}
}
/**
* 迁移POI数据包括全国、香港、澳门数据
*/
private static void migratePoiData(Connection sourceConn, Connection targetConn, String sourceTable) throws SQLException {
// 查询源表中需要的字段
String selectSql = "SELECT id, wgslng, wgslat, name, areaid FROM " + sourceTable;
// 插入到目标表
String insertSql = "INSERT INTO data (id, lng, lat, name, areaid) VALUES (?, ?, ?, ?, ?)";
try (PreparedStatement selectStmt = sourceConn.prepareStatement(selectSql);
PreparedStatement insertStmt = targetConn.prepareStatement(insertSql);
ResultSet rs = selectStmt.executeQuery()) {
int count = 0;
while (rs.next()) {
insertStmt.setInt(1, rs.getInt("id"));
insertStmt.setDouble(2, rs.getDouble("wgslng"));
insertStmt.setDouble(3, rs.getDouble("wgslat"));
insertStmt.setString(4, rs.getString("name"));
// 处理可能为NULL的areaid
if (rs.wasNull()) {
insertStmt.setNull(5, Types.INTEGER);
} else {
insertStmt.setInt(5, rs.getInt("areaid"));
}
insertStmt.executeUpdate();
count++;
// 每10000条记录打印一次进度
if (count % 10000 == 0) {
System.out.println("已迁移" + sourceTable + "数据: " + count + "");
}
}
System.out.println(sourceTable + "共迁移: " + count + " 条记录");
}
}
/**
* 为 areaid 字段创建索引
*/
private static void createAreaidIndex(Connection conn) throws SQLException {
String createIndexSql = "CREATE INDEX IF NOT EXISTS idx_data_areaid ON data(areaid)";
try (Statement stmt = conn.createStatement()) {
stmt.execute(createIndexSql);
}
}
}

View File

@ -0,0 +1,173 @@
package com.yj.earth.common.util;
import lombok.extern.slf4j.Slf4j;
import org.sqlite.JDBC;
import java.io.File;
import java.sql.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* POI数据按地区导出工具类
*/
@Slf4j
public class PoiExporter {
/**
* 根据地区名称导出对应区域及子区域的POI数据到新的SQLite文件
*/
public void exportPoiByArea(String areaName) throws SQLException {
log.info("=== 开始导出[{}]的POI数据 ===", areaName);
// 注册SQLite驱动
DriverManager.registerDriver(new JDBC());
String originalDbPath = "jdbc:sqlite:F:\\export.db";
String projectDir = System.getProperty("user.dir");
String newDbUrl = "jdbc:sqlite:" + projectDir + File.separator + areaName + ".poi";
String newDbPath = newDbUrl.replace("jdbc:sqlite:", "");
try {
log.info("正在查询[{}]及其子区域的地区码", areaName);
List<Integer> adcodeList = getAdcodeList(originalDbPath, areaName);
if (adcodeList.isEmpty()) {
log.error("未找到地区[{}]及其子区域的地区码数据、导出终止", areaName);
throw new IllegalArgumentException("未找到地区[" + areaName + "]及其子区域的地区编码数据");
}
log.info("查询完成、共获取到[{}]个地区码(含子区域)", adcodeList.size());
log.info("开始导出数据到新文件:{}", newDbPath);
exportDataToNewDb(originalDbPath, newDbUrl, adcodeList);
log.info("=== [{}]的POI数据导出成功文件路径{} ===", areaName, newDbPath);
} catch (SQLException e) {
log.error("=== [{}]的POI数据导出失败原因{} ===", areaName, e.getMessage(), e);
throw e;
}
}
/**
* 获取指定地区及其所有子区域的地区码列表
*/
private List<Integer> getAdcodeList(String dbUrl, String areaName) throws SQLException {
List<Integer> adcodeList = new ArrayList<>();
try (Connection conn = DriverManager.getConnection(dbUrl)) {
// 查询根地区码
Integer rootAdcode = getRootAdcode(conn, areaName);
if (rootAdcode == null) {
log.warn("未查询到[{}]对应的根地区码", areaName);
return adcodeList;
}
log.info("查询到[{}]的根地区码:{}", areaName, rootAdcode);
// 递归查询所有子adcode
String recursiveSql = "WITH RECURSIVE sub_areas(adcode) AS (" +
" SELECT adcode FROM poi_area WHERE adcode = ?" +
" UNION ALL" +
" SELECT a.adcode FROM poi_area a " +
" JOIN sub_areas s ON a.parentid = s.adcode" +
") SELECT adcode FROM sub_areas";
try (PreparedStatement ps = conn.prepareStatement(recursiveSql)) {
ps.setInt(1, rootAdcode);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
adcodeList.add(rs.getInt("adcode"));
}
}
}
} catch (SQLException e) {
log.error("查询地区码列表失败!原因:{}", e.getMessage(), e);
throw e;
}
return adcodeList;
}
/**
* 获取地区名称对应的根地区码
*/
private Integer getRootAdcode(Connection conn, String areaName) throws SQLException {
String sql = "SELECT adcode FROM poi_area WHERE name = ?";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, areaName);
try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
return rs.getInt("adcode");
}
}
} catch (SQLException e) {
log.error("查询[{}]的根地区码失败!原因:{}", areaName, e.getMessage(), e);
throw e;
}
return null;
}
/**
* 将指定地区码列表的数据导出到新数据库(带数据处理进度日志)
*/
private void exportDataToNewDb(String originalDbUrl, String newDbUrl, List<Integer> adcodeList) throws SQLException {
try (Connection newConn = DriverManager.getConnection(newDbUrl)) {
log.info("正在创建新表[data](若已存在则删除)...");
newConn.createStatement().execute("DROP TABLE IF EXISTS \"data\"");
String createTableSql = "CREATE TABLE \"data\" (" +
"\"id\" INTEGER, " +
"\"lng\" REAL, " +
"\"lat\" REAL, " +
"\"name\" TEXT, " +
"\"areaid\" INTEGER" +
")";
newConn.createStatement().execute(createTableSql);
log.info("新表[data]创建成功");
try (Connection originalConn = DriverManager.getConnection(originalDbUrl)) {
String placeholders = String.join(",", Collections.nCopies(adcodeList.size(), "?"));
String querySql = "SELECT id, lng, lat, name, areaid FROM data WHERE areaid IN (" + placeholders + ")";
try (PreparedStatement queryPs = originalConn.prepareStatement(querySql);
PreparedStatement insertPs = newConn.prepareStatement(
"INSERT INTO data (id, lng, lat, name, areaid) VALUES (?, ?, ?, ?, ?)")) {
// 设置查询参数
for (int i = 0; i < adcodeList.size(); i++) {
queryPs.setInt(i + 1, adcodeList.get(i));
}
// 批量插入(带进度日志)
int batchSize = 5000;
int totalCount = 0; // 总处理数据量
try (ResultSet rs = queryPs.executeQuery()) {
while (rs.next()) {
insertPs.setInt(1, rs.getInt("id"));
insertPs.setDouble(2, rs.getDouble("lng"));
insertPs.setDouble(3, rs.getDouble("lat"));
insertPs.setString(4, rs.getString("name"));
insertPs.setInt(5, rs.getInt("areaid"));
insertPs.addBatch();
totalCount++;
// 每10000条打印一次进度
if (totalCount % batchSize == 0) {
insertPs.executeBatch();
log.info("已处理数据:{}条", totalCount);
}
}
// 处理剩余数据
if (totalCount % batchSize != 0) {
insertPs.executeBatch();
}
log.info("数据插入完成、共处理[{}]条POI数据", totalCount);
}
}
}
} catch (SQLException e) {
log.error("新数据库操作失败!原因:{}", e.getMessage(), e);
throw e;
}
}
public static void main(String[] args) {
try {
new PoiExporter().exportPoiByArea("兰州市");
} catch (SQLException e) {
e.printStackTrace();
}
}
}

View File

@ -55,6 +55,7 @@ public class DatabaseManager {
classes.add(PbfInfo.class);
classes.add(Device.class);
classes.add(PoiInfo.class);
classes.add(Matter.class);
ENTITY_CLASSES = Collections.unmodifiableList(classes);
}

View File

@ -0,0 +1,20 @@
package com.yj.earth.design;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class Matter {
@Schema(description = "主键")
private String id;
@Schema(description = "名称")
private String name;
@Schema(description = "数量")
private Integer num;
@Schema(description = "创建时间")
private LocalDateTime createdAt;
@Schema(description = "更新时间")
private LocalDateTime updatedAt;
}

View File

@ -0,0 +1,12 @@
package com.yj.earth.dto.matter;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
public class AddMatterDto {
@Schema(description = "名称")
private String name;
@Schema(description = "数量")
private Integer num;
}

View File

@ -0,0 +1,14 @@
package com.yj.earth.dto.matter;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
public class UpdateMatterDto {
@Schema(description = "主键")
private String id;
@Schema(description = "名称")
private String name;
@Schema(description = "数量")
private Integer num;
}

View File

@ -6,7 +6,6 @@ import lombok.Data;
public class PoiVo {
private Long id;
private String name;
private String address;
private String lng;
private String lat;
}