Java实战:城市公园信息管理系统(2025最新技术实现)

随着智慧公园概念的普及,传统的公园管理方式已无法满足现代城市发展需求。本文将基于2025年最新技术栈,提供一套完整的城市公园信息管理系统实操指南,涵盖从环境搭建到核心功能实现的全过程。

一、技术栈升级与环境配置

1. 最新技术选型

考虑到系统性能、开发效率和未来扩展性,我们采用以下技术栈:

  • 后端框架:Spring Boot 3.2.0(支持虚拟线程,提升并发处理能力)
  • 数据访问:Spring Data JPA + Hibernate 6.4(简化数据操作)
  • 前端框架:Vue 3 + Vite 5(更快的构建速度和更好的TypeScript支持)
  • 数据库:MySQL 8.3(支持JSON数据类型,优化空间数据处理)
  • 安全框架:Spring Security 6.2 + JWT(增强的安全特性)
  • API文档:SpringDoc-OpenAPI 2.3(生成OpenAPI 3.0规范文档)
  • 部署工具:Docker + Docker Compose(简化部署流程)

2. 开发环境搭建

首先确保安装以下工具:

  • JDK 21(必须,支持虚拟线程)
  • Node.js 20.x + npm 10.x
  • Docker 25.x
  • IntelliJ IDEA 2024.2(或其他IDE)

项目初始化命令

# 创建Spring Boot后端项目
spring init --dependencies=web,data-jpa,mysql,security,validation,prometheus,actuator \--language=java --java-version=21 --packaging=jar \--groupId=com.park --artifactId=park-management --name=ParkManagement \--description="City Park Information Management System" --version=1.0.0# 创建Vue前端项目
npm create vite@latest park-frontend -- --template vue-ts
cd park-frontend
npm install axios vue-router pinia element-plus @element-plus/icons-vue

二、数据库设计与实现

采用MySQL 8.3的新特性,优化空间数据存储和查询性能。

1. 核心数据表设计

-- 公园基本信息表(使用新的JSON类型存储额外属性)
CREATE TABLE parks (id BIGINT AUTO_INCREMENT PRIMARY KEY,name VARCHAR(255) NOT NULL,area DECIMAL(10,2) NOT NULL COMMENT '面积(公顷)',location POINT NOT NULL COMMENT '地理位置',opening_hours VARCHAR(255) NOT NULL,contact_phone VARCHAR(20),description TEXT,extra_attributes JSON, -- 存储动态扩展属性created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,INDEX idx_location (location) USING GEO
);-- 设施表
CREATE TABLE facilities (id BIGINT AUTO_INCREMENT PRIMARY KEY,park_id BIGINT NOT NULL,name VARCHAR(100) NOT NULL,type VARCHAR(50) NOT NULL,status ENUM('normal', 'maintenance', 'closed') DEFAULT 'normal',location POINT,last_maintained_at TIMESTAMP,FOREIGN KEY (park_id) REFERENCES parks(id) ON DELETE CASCADE,INDEX idx_park_id (park_id)
);

使用MySQL的空间数据类型POINT存储地理位置,便于后续实现基于位置的查询功能。

2. JPA实体类实现

@Entity
@Table(name = "parks")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Park {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;@NotBlank(message = "公园名称不能为空")private String name;@NotNull(message = "面积不能为空")private BigDecimal area;@NotNull(message = "位置信息不能为空")@Convert(converter = PointConverter.class) // 自定义转换器private Point location;private String openingHours;private String contactPhone;private String description;// 存储额外属性,使用HashMap映射JSON字段@Column(columnDefinition = "JSON")private Map<String, Object> extraAttributes;@CreationTimestampprivate LocalDateTime createdAt;@UpdateTimestampprivate LocalDateTime updatedAt;// 关联设施@OneToMany(mappedBy = "park", cascade = CascadeType.ALL, orphanRemoval = true)private List<Facility> facilities = new ArrayList<>();
}

自定义Point转换器处理空间数据类型:

三、核心功能实现

1. 公园信息管理API

使用Spring Boot 3.2的虚拟线程特性提升并发处理能力:

@RestController
@RequestMapping("/api/v1/parks")
@RequiredArgsConstructor
public class ParkController {private final ParkService parkService;// 使用虚拟线程提高并发处理能力@GetMappingpublic ResponseEntity<Page<ParkDTO>> getAllParks(@RequestParam(defaultValue = "0") int page,@RequestParam(defaultValue = "10") int size,@RequestParam(required = false) String name) {// Spring Boot 3.2+ 自动使用虚拟线程执行控制器方法Page<ParkDTO> parks = parkService.findParks(name, PageRequest.of(page, size));return ResponseEntity.ok(parks);}@GetMapping("/nearby")public ResponseEntity<List<ParkDTO>> getNearbyParks(@RequestParam double latitude,@RequestParam double longitude,@RequestParam(defaultValue = "2000") double radius) {List<ParkDTO> parks = parkService.findNearbyParks(latitude, longitude, radius);return ResponseEntity.ok(parks);}@PostMapping@PreAuthorize("hasRole('ADMIN')")public ResponseEntity<ParkDTO> createPark(@Valid @RequestBody ParkCreateDTO parkDTO) {ParkDTO created = parkService.createPark(parkDTO);return ResponseEntity.status(HttpStatus.CREATED).body(created);}// 其他API方法...
}

服务层实现(使用Spring Data JPA的空间查询):

@Service
@RequiredArgsConstructor
@Transactional
public class ParkServiceImpl implements ParkService {private final ParkRepository parkRepository;private final ParkMapper parkMapper;@Override@Transactional(readOnly = true)public List<ParkDTO> findNearbyParks(double latitude, double longitude, double radius) {// 创建查询点Point userLocation = GEOMETRY_FACTORY.createPoint(new Coordinate(longitude, latitude));// 计算距离(米),使用MySQL的空间函数List<Park> parks = parkRepository.findByLocationWithin(userLocation, radius);return parks.stream().map(parkMapper::toDto).collect(Collectors.toList());}// 其他方法实现...
}

2. 前端实现(Vue 3 + TypeScript)

使用Composition API实现公园列表和地图展示:

<template><div class="park-container"><el-row :gutter="20"><el-col :span="8"><el-input v-model="searchQuery" placeholder="搜索公园名称" clearable@clear="handleSearch"@input="handleSearchDebounced"><template #append><el-button @click="handleSearch" icon="Search" /></template></el-input><el-card class="park-list" v-if="parks.length > 0"><el-scrollbar height="calc(100vh - 200px)"><el-row :gutter="10" v-for="park in parks" :key="park.id" class="park-item"><el-col :span="24"><el-card @click="showParkDetails(park)"><h3>{{ park.name }}</h3><p>面积: {{ park.area }} 公顷</p><p>开放时间: {{ park.openingHours }}</p><el-tag :type="getDistanceTagType(park.distance)">{{ formatDistance(park.distance) }}</el-tag></el-card></el-col></el-row></el-scrollbar></el-card></el-col><el-col :span="16"><div class="map-container"><!-- 地图组件 --><park-map :parks="parks" :center="mapCenter" :user-location="userLocation"@park-clicked="showParkDetails"/></div></el-col></el-row></div>
</template>

TypeScript逻辑部分:

<script setup lang="ts">
import { ref, onMounted, computed } from 'vue';
import { useDebounceFn } from '@vueuse/core';
import ParkMap from '@/components/ParkMap.vue';
import { getParks, getNearbyParks } from '@/api/park';
import { ParkDTO } from '@/types/park';// 状态定义
const searchQuery = ref('');
const parks = ref<ParkDTO[]>([]);
const loading = ref(false);
const userLocation = ref<{latitude: number, longitude: number} | null>(null);
const mapCenter = ref<{latitude: number, longitude: number}>({latitude: 39.9042,  // 默认北京坐标longitude: 116.4074
});// 防抖处理搜索
const handleSearchDebounced = useDebounceFn(handleSearch, 500);// 方法定义
async function handleSearch() {loading.value = true;try {if (searchQuery.value) {// 按名称搜索const response = await getParks({ name: searchQuery.value });parks.value = response.data.content;} else if (userLocation.value) {// 按当前位置搜索附近公园const response = await getNearbyParks({latitude: userLocation.value.latitude,longitude: userLocation.value.longitude,radius: 2000});parks.value = response.data;}} catch (error) {console.error('获取公园信息失败', error);ElMessage.error('获取公园信息失败,请稍后重试');} finally {loading.value = false;}
}// 获取用户位置
function getUserLocation() {if (navigator.geolocation) {navigator.geolocation.getCurrentPosition((position) => {userLocation.value = {latitude: position.coords.latitude,longitude: position.coords.longitude};mapCenter.value = { ...userLocation.value };handleSearch();},(error) => {console.warn('无法获取位置信息', error);ElMessage.warning('无法获取位置信息,将显示默认区域公园');handleSearch();});} else {ElMessage.warning('您的浏览器不支持地理位置服务');handleSearch();}
}// 格式化距离显示
function formatDistance(meters: number): string {if (meters < 1000) {return `${Math.round(meters)}米`;} else {return `${(meters / 1000).toFixed(1)}公里`;}
}// 根据距离返回标签类型
function getDistanceTagType(meters: number): string {if (meters < 500) return 'success';if (meters < 1000) return 'warning';return 'info';
}// 生命周期钩子
onMounted(() => {getUserLocation();
});
</script>

3. 安全配置(Spring Security 6.2)

@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {private final JwtAuthenticationFilter jwtAuthFilter;private final AuthenticationProvider authenticationProvider;@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {http.csrf(csrf -> csrf.disable()).authorizeHttpRequests(auth -> auth.requestMatchers("/api/v1/auth/**", "/v3/api-docs/**", "/swagger-ui/**", "/swagger-ui.html").permitAll().requestMatchers(HttpMethod.GET, "/api/v1/parks/**").permitAll().anyRequest().authenticated()).sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)).authenticationProvider(authenticationProvider).addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class).cors(cors -> cors.configurationSource(corsConfigurationSource()));return http.build();}@Beanpublic CorsConfigurationSource corsConfigurationSource() {CorsConfiguration configuration = new CorsConfiguration();configuration.setAllowedOrigins(Arrays.asList("http://localhost:5173"));configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));configuration.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type"));configuration.setExposedHeaders(Arrays.asList("Authorization"));configuration.setAllowCredentials(true);configuration.setMaxAge(3600L);UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();source.registerCorsConfiguration("/**", configuration);return source;}
}

四、容器化部署配置

使用Docker Compose实现一键部署:

后端Dockerfile:

FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
COPY target/park-management-1.0.0.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

前端Dockerfile:

# 构建阶段
FROM node:20-alpine as build-stage
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build# 生产阶段
FROM nginx:stable-alpine as production-stage
COPY --from=build-stage /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

五、系统扩展与优化建议

  1. 性能优化

    • 使用Redis缓存热门公园信息和用户会话
    • 实现数据库读写分离,提高查询性能
    • 对图片等静态资源使用CDN加速
  2. 功能扩展

    • 集成天气API,显示公园实时天气
    • 添加预约管理模块,支持公园内设施预约
    • 实现环境监测数据采集与展示(空气质量、噪音等)
  3. 安全增强

    • 实现API请求限流,防止恶意攻J
    • 添加数据备份与恢复机制
    • 定期进行安全审计和漏D扫描

通过本实操指南,你可以基于最新的Java技术栈构建一个功能完善、性能优异的城市公园信息管理系统。该系统不仅能满足基本的公园信息管理需求,还具备良好的扩展性,可根据实际需求进一步增强功能。


Java 实战,城市公园信息管理系统,2025 最新技术,技术实现指南,管理系统开发,Java 项目实战,公园管理系统,Java 技术应用,系统开发指南,企业级系统搭建,数字化管理方案,Java 实战案例,信息系统开发,2025 Java 框架,公园信息化建设