全部
This commit is contained in:
59
geo.html
59
geo.html
@ -4,11 +4,12 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
||||
<title>路径规划系统</title>
|
||||
<!-- 仅保留必要CDN -->
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin=""/>
|
||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
|
||||
integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin=""/>
|
||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
|
||||
integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/axios@1.6.8/dist/axios.min.js"></script>
|
||||
|
||||
<!-- Tailwind配置 -->
|
||||
@ -38,25 +39,32 @@
|
||||
.content-auto {
|
||||
content-visibility: auto;
|
||||
}
|
||||
|
||||
.map-height {
|
||||
height: 100vh; /* 调整为占满视口高度 */
|
||||
}
|
||||
|
||||
.sidebar-height {
|
||||
height: 100vh; /* 调整为占满视口高度 */
|
||||
}
|
||||
|
||||
.scrollbar-hide {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
.scrollbar-hide::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.custom-marker .fa {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.input-error {
|
||||
@apply border-danger focus:ring-danger/50 focus:border-danger;
|
||||
}
|
||||
|
||||
.btn-disabled {
|
||||
@apply bg-gray-300 text-gray-500 cursor-not-allowed hover:bg-gray-300;
|
||||
}
|
||||
@ -100,7 +108,8 @@
|
||||
|
||||
<!-- 起点:移除默认value、默认无数据 -->
|
||||
<div class="space-y-1">
|
||||
<label class="block text-sm font-medium text-gray-700">起点 <span class="text-danger">*</span></label>
|
||||
<label class="block text-sm font-medium text-gray-700">起点 <span
|
||||
class="text-danger">*</span></label>
|
||||
<div class="flex space-x-2">
|
||||
<div class="flex-1 space-y-0.5">
|
||||
<input type="text" id="startLat" placeholder="纬度"
|
||||
@ -114,7 +123,8 @@
|
||||
maxlength="10">
|
||||
<span id="startLngError" class="text-danger text-xs hidden">请输入有效经度</span>
|
||||
</div>
|
||||
<button id="setStartBtn" class="p-2 bg-gray-100 hover:bg-gray-200 rounded transition-colors duration-200"
|
||||
<button id="setStartBtn"
|
||||
class="p-2 bg-gray-100 hover:bg-gray-200 rounded transition-colors duration-200"
|
||||
title="在地图上选择起点">
|
||||
<i class="fa fa-map-marker text-danger"></i>
|
||||
</button>
|
||||
@ -123,7 +133,8 @@
|
||||
|
||||
<!-- 终点 -->
|
||||
<div class="space-y-1">
|
||||
<label class="block text-sm font-medium text-gray-700">终点 <span class="text-danger">*</span></label>
|
||||
<label class="block text-sm font-medium text-gray-700">终点 <span
|
||||
class="text-danger">*</span></label>
|
||||
<div class="flex space-x-2">
|
||||
<div class="flex-1 space-y-0.5">
|
||||
<input type="text" id="endLat" placeholder="纬度"
|
||||
@ -137,7 +148,8 @@
|
||||
maxlength="10">
|
||||
<span id="endLngError" class="text-danger text-xs hidden">请输入有效经度</span>
|
||||
</div>
|
||||
<button id="setEndBtn" class="p-2 bg-gray-100 hover:bg-gray-200 rounded transition-colors duration-200"
|
||||
<button id="setEndBtn"
|
||||
class="p-2 bg-gray-100 hover:bg-gray-200 rounded transition-colors duration-200"
|
||||
title="在地图上选择终点">
|
||||
<i class="fa fa-flag text-success"></i>
|
||||
</button>
|
||||
@ -160,7 +172,8 @@
|
||||
|
||||
<!-- 交通方式 -->
|
||||
<div class="space-y-1">
|
||||
<label class="block text-sm font-medium text-gray-700">交通方式 <span class="text-danger">*</span></label>
|
||||
<label class="block text-sm font-medium text-gray-700">交通方式 <span
|
||||
class="text-danger">*</span></label>
|
||||
<select id="profile"
|
||||
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary/50 focus:border-primary text-sm transition-all bg-white">
|
||||
<option value="car">驾车</option>
|
||||
@ -241,14 +254,14 @@
|
||||
<script>
|
||||
// 大整数转字符串处理(保留原逻辑)
|
||||
axios.defaults.transformResponse = [
|
||||
function(data) {
|
||||
function (data) {
|
||||
if (typeof data !== 'string') return data;
|
||||
const bigIntRegex = /(\s*"[^"]*"\s*:\s*)(\d{16,})(\s*)/g;
|
||||
return data.replace(bigIntRegex, (match, keyPart, bigInt, endPart) => {
|
||||
return `${keyPart}"${bigInt}"${endPart}`;
|
||||
});
|
||||
},
|
||||
function(parsedData) {
|
||||
function (parsedData) {
|
||||
try {
|
||||
return JSON.parse(parsedData);
|
||||
} catch (e) {
|
||||
@ -265,7 +278,7 @@
|
||||
let routeLine = null;
|
||||
let waypointCount = 0;
|
||||
const API_BASE_URL = "http://127.0.0.1:8848";
|
||||
const DEFAULT_CENTER = { lat: 30.6570, lng: 104.0650 }; // 成都默认坐标
|
||||
const DEFAULT_CENTER = {lat: 30.6570, lng: 104.0650}; // 成都默认坐标
|
||||
|
||||
// 地图初始化
|
||||
function initMap() {
|
||||
@ -353,7 +366,7 @@
|
||||
}).addTo(map);
|
||||
|
||||
startMarker.on('dragend', (e) => {
|
||||
const { lat, lng } = e.target.getLatLng();
|
||||
const {lat, lng} = e.target.getLatLng();
|
||||
setStartPoint(lat, lng);
|
||||
});
|
||||
}
|
||||
@ -383,7 +396,7 @@
|
||||
}).addTo(map);
|
||||
|
||||
endMarker.on('dragend', (e) => {
|
||||
const { lat, lng } = e.target.getLatLng();
|
||||
const {lat, lng} = e.target.getLatLng();
|
||||
setEndPoint(lat, lng);
|
||||
});
|
||||
}
|
||||
@ -436,7 +449,7 @@
|
||||
waypointDiv.querySelector('.set-waypoint-btn').addEventListener('click', () => {
|
||||
const id = parseInt(waypointDiv.dataset.id);
|
||||
map.once('click', (e) => {
|
||||
const { lat, lng } = e.latlng;
|
||||
const {lat, lng} = e.latlng;
|
||||
latInput.value = lat.toFixed(6);
|
||||
lngInput.value = lng.toFixed(6);
|
||||
latInput.dispatchEvent(new Event('input'));
|
||||
@ -457,7 +470,7 @@
|
||||
}).addTo(map);
|
||||
|
||||
waypointMarkers[id].on('dragend', (e) => {
|
||||
const { lat, lng } = e.target.getLatLng();
|
||||
const {lat, lng} = e.target.getLatLng();
|
||||
latInput.value = lat.toFixed(6);
|
||||
lngInput.value = lng.toFixed(6);
|
||||
latInput.dispatchEvent(new Event('input'));
|
||||
@ -480,7 +493,7 @@
|
||||
|
||||
// 地图点击处理
|
||||
function handleMapClick(e) {
|
||||
const { lat, lng } = e.latlng;
|
||||
const {lat, lng} = e.latlng;
|
||||
const startLat = document.getElementById('startLat').value;
|
||||
const endLat = document.getElementById('endLat').value;
|
||||
|
||||
@ -632,7 +645,7 @@
|
||||
const lat = parseFloat(item.querySelector('.waypoint-lat').value);
|
||||
const lng = parseFloat(item.querySelector('.waypoint-lng').value);
|
||||
if (!isNaN(lat) && !isNaN(lng) && validateCoord(lat, 'lat') && validateCoord(lng, 'lng')) {
|
||||
waypoints.push({ lat, lng });
|
||||
waypoints.push({lat, lng});
|
||||
}
|
||||
});
|
||||
|
||||
@ -644,8 +657,8 @@
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`${API_BASE_URL}/graphhopper/route`,
|
||||
{ startLat, startLng, endLat, endLng, profile, waypoints },
|
||||
{ headers: { 'Content-Type': 'application/json' } }
|
||||
{startLat, startLng, endLat, endLng, profile, waypoints},
|
||||
{headers: {'Content-Type': 'application/json'}}
|
||||
);
|
||||
|
||||
if (response.data.code !== 200 || !response.data.data) {
|
||||
@ -671,16 +684,16 @@
|
||||
if (routeLine) map.removeLayer(routeLine);
|
||||
const latLngs = routeData.pathPoints.map(point => [point.lat, point.lng]);
|
||||
const lineStyles = {
|
||||
car: { color: '#165DFF', weight: 5, opacity: 0.8, dashArray: '' },
|
||||
bike: { color: '#00B42A', weight: 4, opacity: 0.8, dashArray: '5,5' },
|
||||
foot: { color: '#4b0c35', weight: 3, opacity: 0.8, dashArray: '2,2' }
|
||||
car: {color: '#165DFF', weight: 5, opacity: 0.8, dashArray: ''},
|
||||
bike: {color: '#00B42A', weight: 4, opacity: 0.8, dashArray: '5,5'},
|
||||
foot: {color: '#4b0c35', weight: 3, opacity: 0.8, dashArray: '2,2'}
|
||||
};
|
||||
|
||||
routeLine = L.polyline(latLngs, lineStyles[document.getElementById('profile').value])
|
||||
.addTo(map)
|
||||
.bindPopup(`<div class="text-sm"><p>距离:${routeData.distanceKm.toFixed(2)} 公里</p><p>时间:${routeData.timeMinutes} 分钟</p></div>`);
|
||||
|
||||
map.fitBounds(routeLine.getBounds(), { padding: [50, 50], maxZoom: 14 });
|
||||
map.fitBounds(routeLine.getBounds(), {padding: [50, 50], maxZoom: 14});
|
||||
}
|
||||
|
||||
// 事件绑定:移除起点按钮弹窗
|
||||
|
||||
@ -150,10 +150,10 @@ public class GraphHopperController {
|
||||
public ApiResponse calculateRoute(@RequestBody RouteRequest request) {
|
||||
// 区分未加载地图和加载中两种状态
|
||||
if (isLoading.get()) {
|
||||
return ApiResponse.failure("地图正在加载中,请稍后再试");
|
||||
return ApiResponse.failure("地图正在加载中、请稍后再试");
|
||||
}
|
||||
if (!isLoaded.get() || currentHopper == null) {
|
||||
return ApiResponse.failure("地图未加载,请先加载地图");
|
||||
return ApiResponse.failure("地图未加载、请先加载地图");
|
||||
}
|
||||
try {
|
||||
// 构建路径点列表
|
||||
|
||||
@ -33,6 +33,7 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.sql.SQLException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
@ -200,29 +201,16 @@ public class IconLibraryController {
|
||||
|
||||
@Operation(summary = "添加图标文件")
|
||||
@PostMapping("/addIconFile")
|
||||
public ApiResponse addIconFile(@RequestParam("files") MultipartFile[] files, @RequestParam("iconTypeId") @Parameter(description = "图标类型ID") String typeId) throws IOException, SQLException, IllegalAccessException, InstantiationException {
|
||||
// 获取当前启用的图标库
|
||||
public ApiResponse addIconFile(@RequestParam("filePaths") List<String> filePaths, @RequestParam("iconTypeId") String typeId) throws Exception {
|
||||
// 获取当前启用的图标库路径
|
||||
String iconPath = getIconLibrary();
|
||||
if (iconPath == null) {
|
||||
return ApiResponse.failure("请先创建或导入图标库");
|
||||
}
|
||||
|
||||
// 校验类型是否存在
|
||||
if (!isIconTypeExist(iconPath, typeId)) {
|
||||
return ApiResponse.failure("图标类型不存在:" + typeId);
|
||||
}
|
||||
|
||||
// 循环处理每个文件
|
||||
for (MultipartFile file : files) {
|
||||
if (file.isEmpty()) {
|
||||
continue; // 跳过空文件
|
||||
}
|
||||
// 循环处理每个绝对路径对应的文件
|
||||
for (String filePath : filePaths) {
|
||||
File file = new File(filePath);
|
||||
|
||||
// 解析文件名与后缀
|
||||
String originalFileName = file.getOriginalFilename();
|
||||
if (originalFileName == null) {
|
||||
continue;
|
||||
}
|
||||
String originalFileName = file.getName();
|
||||
String fileSuffix = FileUtil.extName(originalFileName);
|
||||
String fileNameWithoutSuffix = FileUtil.mainName(originalFileName);
|
||||
|
||||
@ -235,10 +223,10 @@ public class IconLibraryController {
|
||||
params.add(typeId);
|
||||
params.add(fileNameWithoutSuffix);
|
||||
params.add(fileSuffix);
|
||||
params.add(file.getBytes());
|
||||
params.add(Files.readAllBytes(file.toPath()));
|
||||
params.add(LocalDateTime.now().toString());
|
||||
System.out.println(insertSql);
|
||||
|
||||
// 执行数据库插入
|
||||
SQLiteUtil.executeUpdate(iconPath, insertSql, params);
|
||||
}
|
||||
|
||||
@ -391,7 +379,7 @@ public class IconLibraryController {
|
||||
|
||||
private String getIconLibrary() {
|
||||
LambdaQueryWrapper<IconLibrary> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(IconLibrary::getIsEnable, 1); // 1=启用、0=未启用
|
||||
queryWrapper.eq(IconLibrary::getIsEnable, 1);
|
||||
IconLibrary library = iconLibraryService.getOne(queryWrapper);
|
||||
return library == null ? null : library.getPath();
|
||||
}
|
||||
|
||||
@ -1,21 +1,32 @@
|
||||
package com.yj.earth.business.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.yj.earth.business.domain.Matter;
|
||||
import com.yj.earth.business.domain.Source;
|
||||
import com.yj.earth.business.service.MatterService;
|
||||
import com.yj.earth.business.service.SourceService;
|
||||
import com.yj.earth.common.util.ApiResponse;
|
||||
import com.yj.earth.common.util.JsonUtil;
|
||||
import com.yj.earth.dto.matter.AddMatterDto;
|
||||
import com.yj.earth.dto.matter.UpdateMatterDto;
|
||||
import com.yj.earth.params.Point;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
|
||||
@Tag(name = "物资数据管理")
|
||||
@ -24,6 +35,8 @@ import java.util.List;
|
||||
public class MatterController {
|
||||
@Resource
|
||||
private MatterService matterService;
|
||||
@Resource
|
||||
private SourceService sourceService;
|
||||
|
||||
@PostMapping("/add")
|
||||
@Operation(summary = "添加物资")
|
||||
@ -71,10 +84,122 @@ public class MatterController {
|
||||
public ApiResponse list(@Parameter(description = "分页数量") @RequestParam(value = "pageNum") Integer pageNum,
|
||||
@Parameter(description = "分页大小") @RequestParam(value = "pageSize") Integer pageSize,
|
||||
@Parameter(description = "物资名称") @RequestParam(value = "name", required = false) String name) {
|
||||
LambdaQueryWrapper<Matter> queryWrapper = new LambdaQueryWrapper<>();
|
||||
|
||||
Map<String, Integer> goodsTotalMap = getGoodCountMap();
|
||||
|
||||
// 分页查询物资表的数据
|
||||
LambdaQueryWrapper<Matter> matterWrapper = new LambdaQueryWrapper<>();
|
||||
if (StringUtils.isNotBlank(name)) {
|
||||
queryWrapper.like(Matter::getName, name);
|
||||
matterWrapper.like(Matter::getName, name);
|
||||
}
|
||||
return ApiResponse.success(matterService.page(new Page<>(pageNum, pageSize), queryWrapper));
|
||||
Page<Matter> matterPage = matterService.page(new Page<>(pageNum, pageSize), matterWrapper);
|
||||
|
||||
// 用统计出的数量替换分页结果中的数量
|
||||
if (!CollectionUtils.isEmpty(matterPage.getRecords())) {
|
||||
matterPage.getRecords().forEach(matter -> {
|
||||
String matterName = matter.getName();
|
||||
Integer count = goodsTotalMap.get(matterName);
|
||||
if (count != null) {
|
||||
matter.setNum(count);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return ApiResponse.success(matterPage);
|
||||
}
|
||||
|
||||
@Operation(summary = "物资统计")
|
||||
@PostMapping("/statistics")
|
||||
public ApiResponse statistics(@Parameter(description = "点ID列表") @RequestBody List<String> pointIdList) {
|
||||
// 构建Source表的查询条件
|
||||
LambdaQueryWrapper<Source> sourceWrapper = new LambdaQueryWrapper<>();
|
||||
// 根据ID列表查询
|
||||
sourceWrapper.in(Source::getId, pointIdList);
|
||||
// 执行查询、获取所有符合条件的Source列表
|
||||
List<Source> sourceList = sourceService.list(sourceWrapper);
|
||||
|
||||
// 初始化物资统计Map:用于存储 "物资名称 -> 总数量" 的映射关系
|
||||
Map<String, Integer> goodsTotalMap = new HashMap<>();
|
||||
|
||||
if (!CollectionUtils.isEmpty(sourceList)) {
|
||||
for (Source source : sourceList) {
|
||||
try {
|
||||
Point point = JsonUtil.jsonToObject(source.getParams(), Point.class);
|
||||
if (point == null) continue;
|
||||
Optional.ofNullable(point.getAttribute())
|
||||
.map(Point.Attribute::getGoods)
|
||||
.map(Point.Attribute.Goods::getContent)
|
||||
.ifPresent(contentList -> {
|
||||
// 遍历物资列表中的每个物资项
|
||||
for (Point.Attribute.Goods.GoodsContent goodsContent : contentList) {
|
||||
if (goodsContent == null) continue;
|
||||
|
||||
// 处理物资名称
|
||||
String goodsName = Optional.ofNullable(goodsContent.getName())
|
||||
.orElse("未知物资");
|
||||
|
||||
// 处理物资数量
|
||||
int count = Optional.ofNullable(goodsContent.getCnt())
|
||||
.map(Integer::valueOf)
|
||||
.orElse(0);
|
||||
|
||||
// 累加物资总数量
|
||||
goodsTotalMap.merge(goodsName, count, Integer::sum);
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 返回最终的物资统计Map
|
||||
return ApiResponse.success(goodsTotalMap);
|
||||
}
|
||||
|
||||
|
||||
private Map<String, Integer> getGoodCountMap() {
|
||||
// 构建Source表的查询条件
|
||||
LambdaQueryWrapper<Source> sourceWrapper = new LambdaQueryWrapper<>();
|
||||
sourceWrapper.eq(Source::getSourceType, "point");
|
||||
|
||||
// 执行查询、获取所有符合条件的Source列表
|
||||
List<Source> sourceList = sourceService.list(sourceWrapper);
|
||||
|
||||
// 初始化物资统计Map:用于存储 "物资名称 -> 总数量" 的映射关系
|
||||
Map<String, Integer> goodsTotalMap = new HashMap<>();
|
||||
|
||||
if (!CollectionUtils.isEmpty(sourceList)) {
|
||||
for (Source source : sourceList) {
|
||||
try {
|
||||
Point point = JsonUtil.jsonToObject(source.getParams(), Point.class);
|
||||
if (point == null) continue;
|
||||
Optional.ofNullable(point.getAttribute())
|
||||
.map(Point.Attribute::getGoods)
|
||||
.map(Point.Attribute.Goods::getContent)
|
||||
.ifPresent(contentList -> {
|
||||
// 遍历物资列表中的每个物资项
|
||||
for (Point.Attribute.Goods.GoodsContent goodsContent : contentList) {
|
||||
if (goodsContent == null) continue;
|
||||
|
||||
// 处理物资名称
|
||||
String goodsName = Optional.ofNullable(goodsContent.getName())
|
||||
.orElse("未知物资");
|
||||
|
||||
// 处理物资数量
|
||||
int count = Optional.ofNullable(goodsContent.getCnt())
|
||||
.map(Integer::valueOf)
|
||||
.orElse(0);
|
||||
|
||||
// 累加物资总数量
|
||||
goodsTotalMap.merge(goodsName, count, Integer::sum);
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 返回最终的物资统计Map
|
||||
return goodsTotalMap;
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,6 +33,7 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.sql.SQLException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
@ -200,29 +201,16 @@ public class MilitaryLibraryController {
|
||||
|
||||
@Operation(summary = "添加军标文件")
|
||||
@PostMapping("/addMilitaryFile")
|
||||
public ApiResponse addMilitaryFile(@RequestParam("files") MultipartFile[] files, @RequestParam("militaryTypeId") @Parameter(description = "军标类型ID") String typeId) throws IOException, SQLException, IllegalAccessException, InstantiationException {
|
||||
// 获取当前启用的军标库
|
||||
public ApiResponse addMilitaryFile(@RequestParam("filePaths") List<String> filePaths,
|
||||
@RequestParam("militaryTypeId") @Parameter(description = "军标类型ID") String typeId) throws IOException, SQLException {
|
||||
String militaryPath = getMilitaryLibrary();
|
||||
if (militaryPath == null) {
|
||||
return ApiResponse.failure("请先创建或导入军标库");
|
||||
}
|
||||
|
||||
// 校验类型是否存在
|
||||
if (!isMilitaryTypeExist(militaryPath, typeId)) {
|
||||
return ApiResponse.failure("军标类型不存在:" + typeId);
|
||||
}
|
||||
|
||||
// 循环处理每个文件
|
||||
for (MultipartFile file : files) {
|
||||
if (file.isEmpty()) {
|
||||
continue; // 跳过空文件
|
||||
}
|
||||
// 循环处理每个绝对路径对应的文件
|
||||
for (String filePath : filePaths) {
|
||||
File file = new File(filePath);
|
||||
|
||||
// 解析文件名与后缀
|
||||
String originalFileName = file.getOriginalFilename();
|
||||
if (originalFileName == null) {
|
||||
continue;
|
||||
}
|
||||
String originalFileName = file.getName();
|
||||
String fileSuffix = FileUtil.extName(originalFileName);
|
||||
String fileNameWithoutSuffix = FileUtil.mainName(originalFileName);
|
||||
|
||||
@ -235,7 +223,7 @@ public class MilitaryLibraryController {
|
||||
params.add(typeId);
|
||||
params.add(fileNameWithoutSuffix);
|
||||
params.add(fileSuffix);
|
||||
params.add(file.getBytes());
|
||||
params.add(Files.readAllBytes(file.toPath()));
|
||||
params.add(LocalDateTime.now().toString());
|
||||
|
||||
SQLiteUtil.executeUpdate(militaryPath, insertSql, params);
|
||||
@ -243,7 +231,6 @@ public class MilitaryLibraryController {
|
||||
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
|
||||
@Operation(summary = "获取军标文件数据")
|
||||
@GetMapping("/data/military/{militaryId}/{fileSuffix}")
|
||||
public ResponseEntity<byte[]> getMilitaryData(@PathVariable("militaryId") @Parameter(description = "军标ID") String militaryId, @PathVariable("fileSuffix") @Parameter(description = "军标文件后缀") String fileSuffix) {
|
||||
|
||||
@ -16,6 +16,7 @@ import com.yj.earth.dto.modelLibrary.AddModelTypeDto;
|
||||
import com.yj.earth.dto.modelLibrary.CreateModelLibraryDto;
|
||||
import com.yj.earth.dto.modelLibrary.DragModelTypeDto;
|
||||
import com.yj.earth.dto.modelLibrary.UpdateModelTypeNameDto;
|
||||
import com.yj.earth.vo.IconDataVo;
|
||||
import com.yj.earth.vo.ModelDataVo;
|
||||
import com.yj.earth.vo.ModelTypeVo;
|
||||
import com.yj.earth.vo.ModelVo;
|
||||
@ -36,6 +37,7 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.sql.SQLException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
@ -165,25 +167,24 @@ public class ModelLibraryController {
|
||||
|
||||
@Operation(summary = "添加模型文件")
|
||||
@PostMapping("/addModelFile")
|
||||
public ApiResponse addModelFile(@RequestParam("files") MultipartFile[] files, @Parameter(description = "模型类型ID") @RequestParam("modelTypeId") String modelTypeId) throws IOException, SQLException {
|
||||
public ApiResponse addModelFile(
|
||||
@Parameter(description = "绝对文件路径列表") @RequestParam("filePaths") List<String> filePaths,
|
||||
@Parameter(description = "模型类型ID") @RequestParam("modelTypeId") String modelTypeId) throws IOException, SQLException {
|
||||
|
||||
// 获取最新的模型库路径
|
||||
String modelPath = getModelLibrary();
|
||||
if (modelPath == null) {
|
||||
return ApiResponse.failure("请先创建或导入模型库");
|
||||
}
|
||||
// 循环处理每个上传的文件
|
||||
for (MultipartFile file : files) {
|
||||
// 跳过空文件
|
||||
if (file.isEmpty()) {
|
||||
continue;
|
||||
|
||||
// 简化校验:仅判断列表是否为空
|
||||
if (filePaths == null || filePaths.isEmpty()) {
|
||||
return ApiResponse.failure("文件路径列表不能为空");
|
||||
}
|
||||
// 获取文件信息
|
||||
String fileName = file.getOriginalFilename();
|
||||
if (fileName == null) {
|
||||
continue;
|
||||
}
|
||||
String fileSuffix = FileUtil.extName(fileName);
|
||||
String fileNameWithoutSuffix = FileUtil.mainName(fileName);
|
||||
|
||||
// 循环处理每个文件路径
|
||||
for (String filePath : filePaths) {
|
||||
File file = new File(filePath);
|
||||
|
||||
// 构建插入SQL
|
||||
String sql = "INSERT INTO model " +
|
||||
@ -194,9 +195,9 @@ public class ModelLibraryController {
|
||||
String modelId = UUID.fastUUID().toString(true);
|
||||
params.add(modelId);
|
||||
params.add(modelTypeId);
|
||||
params.add(fileNameWithoutSuffix);
|
||||
params.add(fileSuffix);
|
||||
params.add(file.getBytes());
|
||||
params.add(file.getName().split("\\.")[0]);
|
||||
params.add(file.getName().substring(file.getName().lastIndexOf(".") + 1));
|
||||
params.add(Files.readAllBytes(file.toPath()));
|
||||
params.add(LocalDateTime.now());
|
||||
|
||||
// 执行插入操作
|
||||
|
||||
@ -34,7 +34,7 @@ public class PoiController {
|
||||
Class.forName("org.sqlite.JDBC");
|
||||
String dbPath = System.getProperty("user.dir") + File.separator + "poi" + File.separator + "poi.db";
|
||||
connection = DriverManager.getConnection("jdbc:sqlite:" + dbPath);
|
||||
logger.info("POI数据库连接初始化成功,路径:{}", dbPath);
|
||||
logger.info("POI数据库连接初始化成功、路径:{}", dbPath);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -50,6 +50,7 @@ public class TsSourceController {
|
||||
public ApiResponse update(@RequestBody UpdateTsSourceDto updateTsSourceDto) {
|
||||
TsSource tsSource = new TsSource();
|
||||
BeanUtils.copyProperties(updateTsSourceDto, tsSource);
|
||||
tsSourceService.updateById(tsSource);
|
||||
return ApiResponse.success(null);
|
||||
}
|
||||
|
||||
|
||||
@ -49,14 +49,16 @@ public class JsonUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 JSON 字符串转换为指定类型的对象
|
||||
*/
|
||||
|
||||
public static <T> T jsonToObject(String json, Class<T> clazz) {
|
||||
if (json == null || json.trim().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return objectMapper.convertValue(json, clazz);
|
||||
try {
|
||||
return objectMapper.readValue(json, clazz);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -90,7 +90,7 @@ public class SQLiteUtil {
|
||||
stmt.execute("PRAGMA busy_timeout=2000;"); // 忙等待超时:2秒(避免瞬时并发锁等待)
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException("初始化SQLite数据源失败(路径:" + dbFilePath + ")+ 原因是:" + e.getMessage());
|
||||
throw new RuntimeException("请检查路径是否存在");
|
||||
}
|
||||
|
||||
return dataSource;
|
||||
|
||||
@ -141,11 +141,11 @@ public class DatabaseManager {
|
||||
|
||||
public static String getSqliteDbFilePath() {
|
||||
if (!isPathInitialized || FOLDER_NAME == null) {
|
||||
throw new IllegalStateException("数据库路径尚未初始化,请先调用 initializePath 方法或等待 @PostConstruct 完成");
|
||||
throw new IllegalStateException("数据库路径尚未初始化、请先调用 initializePath 方法或等待 @PostConstruct 完成");
|
||||
}
|
||||
|
||||
if (sqliteDbFilePath == null) {
|
||||
// 如果没有预先创建,则动态构建路径
|
||||
// 如果没有预先创建、则动态构建路径
|
||||
Path appDir = Paths.get(FOLDER_NAME);
|
||||
Path dbFile = appDir.resolve(DB_FILE_NAME);
|
||||
sqliteDbFilePath = dbFile.toAbsolutePath().toString();
|
||||
@ -208,7 +208,7 @@ public class DatabaseManager {
|
||||
|
||||
// 直接使用配置的绝对路径
|
||||
if (FOLDER_NAME == null || FOLDER_NAME.trim().isEmpty()) {
|
||||
throw new IOException("server.path 配置为空,无法创建SQLite数据库文件");
|
||||
throw new IOException("server.path 配置为空、无法创建SQLite数据库文件");
|
||||
}
|
||||
|
||||
Path appDir = Paths.get(FOLDER_NAME);
|
||||
@ -484,10 +484,10 @@ public class DatabaseManager {
|
||||
DatabaseManager.initializePath(serverPath);
|
||||
log.info("已提前初始化数据库路径: {}", serverPath);
|
||||
} else {
|
||||
log.warn("未找到 server.path 配置,将等待 @PostConstruct 初始化");
|
||||
log.warn("未找到 server.path 配置、将等待 @PostConstruct 初始化");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("提前初始化数据库路径失败,将等待 @PostConstruct 初始化: {}", e.getMessage());
|
||||
log.warn("提前初始化数据库路径失败、将等待 @PostConstruct 初始化: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,104 +0,0 @@
|
||||
package com.yj.earth.word;
|
||||
|
||||
import org.apache.poi.xwpf.usermodel.*;
|
||||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTShd;
|
||||
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STShd;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class GenerateDocx {
|
||||
public static void main(String[] args) {
|
||||
// 创建文档对象
|
||||
XWPFDocument document = new XWPFDocument();
|
||||
|
||||
try {
|
||||
// 添加标题段落
|
||||
XWPFParagraph titlePara = document.createParagraph();
|
||||
titlePara.setAlignment(ParagraphAlignment.CENTER);
|
||||
XWPFRun titleRun = titlePara.createRun();
|
||||
titleRun.setText("POI 生成 Docx 示例");
|
||||
// 字体大小
|
||||
titleRun.setFontSize(20);
|
||||
// 加粗
|
||||
titleRun.setBold(true);
|
||||
// 字体
|
||||
titleRun.setFontFamily("宋体");
|
||||
|
||||
// 添加正文段落
|
||||
XWPFParagraph contentPara1 = document.createParagraph();
|
||||
contentPara1.setAlignment(ParagraphAlignment.LEFT);
|
||||
XWPFRun run1 = contentPara1.createRun();
|
||||
run1.setText("这是使用 Apache POI 生成的 docx 文档。");
|
||||
run1.setFontSize(12);
|
||||
run1.setFontFamily("微软雅黑");
|
||||
|
||||
// 换行
|
||||
XWPFParagraph contentPara2 = document.createParagraph();
|
||||
XWPFRun run2 = contentPara2.createRun();
|
||||
run2.setText("支持设置文本样式,如:");
|
||||
run2.setFontSize(12);
|
||||
|
||||
// 文本样式示例(同一行不同样式)
|
||||
XWPFRun run3 = contentPara2.createRun();
|
||||
run3.setText(" 加粗 ");
|
||||
run3.setBold(true);
|
||||
|
||||
XWPFRun run4 = contentPara2.createRun();
|
||||
run4.setText(" 斜体 ");
|
||||
run4.setItalic(true);
|
||||
|
||||
XWPFRun run5 = contentPara2.createRun();
|
||||
run5.setText(" 下划线 ");
|
||||
run5.setUnderline(UnderlinePatterns.SINGLE); // 单下划线
|
||||
|
||||
XWPFRun run6 = contentPara2.createRun();
|
||||
run6.setText(" 红色 ");
|
||||
run6.setColor("FF0000"); // 红色
|
||||
|
||||
// 添加表格
|
||||
int rows = 3; // 3行
|
||||
int cols = 3; // 3列
|
||||
XWPFTable table = document.createTable(rows, cols);
|
||||
table.setWidth("100%"); // 表格宽度
|
||||
|
||||
// 设置表头
|
||||
XWPFTableRow headerRow = table.getRow(0);
|
||||
headerRow.getCell(0).setText("ID");
|
||||
headerRow.getCell(1).setText("名称");
|
||||
headerRow.getCell(2).setText("描述");
|
||||
// 表头背景色(浅灰色)
|
||||
for (XWPFTableCell cell : headerRow.getTableCells()) {
|
||||
CTShd cTShd = cell.getCTTc().addNewTcPr().addNewShd();
|
||||
cTShd.setVal(STShd.CLEAR);
|
||||
cTShd.setFill("D9D9D9"); // 浅灰色
|
||||
}
|
||||
|
||||
// 设置表格内容
|
||||
XWPFTableRow row1 = table.getRow(1);
|
||||
row1.getCell(0).setText("1");
|
||||
row1.getCell(1).setText("POI");
|
||||
row1.getCell(2).setText("处理 Office 文档的 Java 库");
|
||||
|
||||
XWPFTableRow row2 = table.getRow(2);
|
||||
row2.getCell(0).setText("2");
|
||||
row2.getCell(1).setText("Docx");
|
||||
row2.getCell(2).setText("Word 2007+ 格式");
|
||||
|
||||
// 保存文档到本地
|
||||
FileOutputStream out = new FileOutputStream("poi-demo.docx");
|
||||
document.write(out);
|
||||
out.close();
|
||||
System.out.println("docx 生成成功!");
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
document.close(); // 关闭文档,释放资源
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user