Commit a8d4e5e87284b236acccad2475bc8023738a8a76
1 parent
3ed2ca6f
feat(garden): 添加GIS道路管理和坐标转换功能
- 新增GIS道路管理相关控制器、服务和数据对象 - 实现WGS84到GCJ02坐标转换算法和工具类 - 添加GIS道路关联和取消关联功能 - 实现GIS道路坐标批量转换功能 - 添加GIS道路数据导入导出功能 - 新增问题类型级联查询接口优化前端展示 - 配置本地调试Mock认证支持 - 更新部署脚本和数据库表结构变更SQL
Showing
33 changed files
with
1703 additions
and
29 deletions
deploy-test.sh
0 → 100755
| 1 | +#!/bin/bash | |
| 2 | +set -e | |
| 3 | + | |
| 4 | +# ==================== 配置 ==================== | |
| 5 | +SERVER_HOST="172.17.16.14" | |
| 6 | +SERVER_USER="root" | |
| 7 | +SERVER_PASS="JCSS@2025!@#$" | |
| 8 | +UPLOAD_DIR="/home/user/test_jar" | |
| 9 | +RESTART_SCRIPT="restart.sh" | |
| 10 | +JAR_NAME="urbanops-server.jar" | |
| 11 | +PROJECT_DIR="$(cd "$(dirname "$0")" && pwd)" | |
| 12 | +JAR_PATH="$PROJECT_DIR/urbanops-server/target/$JAR_NAME" | |
| 13 | + | |
| 14 | +# ==================== 函数 ==================== | |
| 15 | + | |
| 16 | +check_sshpass() { | |
| 17 | + if ! command -v sshpass &> /dev/null; then | |
| 18 | + echo "[check] sshpass 未安装,尝试安装..." | |
| 19 | + if command -v brew &> /dev/null; then | |
| 20 | + brew install hudochenkov/sshpass/sshpass | |
| 21 | + else | |
| 22 | + echo "[ERROR] 请手动安装 sshpass: brew install hudochenkov/sshpass/sshpass" | |
| 23 | + exit 1 | |
| 24 | + fi | |
| 25 | + fi | |
| 26 | +} | |
| 27 | + | |
| 28 | +build() { | |
| 29 | + echo "[build] 开始 Maven 打包(跳过测试)..." | |
| 30 | + cd "$PROJECT_DIR" | |
| 31 | + mvn clean package -DskipTests | |
| 32 | + if [ -f "$JAR_PATH" ]; then | |
| 33 | + echo "[build] 打包完成: $JAR_PATH ($(du -sh "$JAR_PATH" | cut -f1))" | |
| 34 | + else | |
| 35 | + echo "[ERROR] 打包失败,未找到 $JAR_PATH" | |
| 36 | + exit 1 | |
| 37 | + fi | |
| 38 | +} | |
| 39 | + | |
| 40 | +upload() { | |
| 41 | + echo "[upload] 上传 JAR 到 $SERVER_HOST:$UPLOAD_DIR/ ..." | |
| 42 | + sshpass -p "$SERVER_PASS" scp -o StrictHostKeyChecking=no "$JAR_PATH" "$SERVER_USER@$SERVER_HOST:$UPLOAD_DIR/" | |
| 43 | + echo "[upload] 上传完成" | |
| 44 | +} | |
| 45 | + | |
| 46 | +restart() { | |
| 47 | + echo "[restart] 调用远程重启脚本..." | |
| 48 | + sshpass -p "$SERVER_PASS" ssh -o StrictHostKeyChecking=no "$SERVER_USER@$SERVER_HOST" \ | |
| 49 | + "cd $UPLOAD_DIR && chmod +x $RESTART_SCRIPT && ./$RESTART_SCRIPT" | |
| 50 | + echo "[restart] 远程重启完成" | |
| 51 | +} | |
| 52 | + | |
| 53 | +# ==================== 主流程 ==================== | |
| 54 | + | |
| 55 | +echo "========================================" | |
| 56 | +echo " UrbanOps 测试环境部署" | |
| 57 | +echo " 目标: $SERVER_USER@$SERVER_HOST:$UPLOAD_DIR" | |
| 58 | +echo "========================================" | |
| 59 | + | |
| 60 | +check_sshpass | |
| 61 | +build | |
| 62 | +upload | |
| 63 | +restart | |
| 64 | + | |
| 65 | +echo "" | |
| 66 | +echo "========================================" | |
| 67 | +echo " 部署完成" | |
| 68 | +echo "========================================" | ... | ... |
pom.xml
| ... | ... | @@ -20,7 +20,7 @@ |
| 20 | 20 | <module>urbanops-module-workorder</module> |
| 21 | 21 | <!-- <module>urbanops-module-member</module>--> |
| 22 | 22 | <module>urbanops-module-bpm</module> |
| 23 | - <module>urbanops-module-report</module> | |
| 23 | +<!-- <module>urbanops-module-report</module>--> | |
| 24 | 24 | <module>urbanops-module-api</module> |
| 25 | 25 | <!-- <module>urbanops-module-mp</module>--> |
| 26 | 26 | <!-- <module>urbanops-module-pay</module>--> | ... | ... |
script/convert_gis_road_coords.py
0 → 100644
| 1 | +#!/usr/bin/env python3 | |
| 2 | +""" | |
| 3 | +WGS84 → GCJ02(高德坐标系)坐标转换脚本 | |
| 4 | +目标表: garden_gis_road | |
| 5 | +转换字段: starting_latitude/longitude, end_latitude/longitude, gis_polygon_coords | |
| 6 | + | |
| 7 | +使用方法: | |
| 8 | + python3 convert_gis_road_coords.py | |
| 9 | + | |
| 10 | +前提: pip install pymysql | |
| 11 | +""" | |
| 12 | +import pymysql | |
| 13 | +import math | |
| 14 | +import re | |
| 15 | + | |
| 16 | +# ========== 数据库配置 ========== | |
| 17 | +DB_CONFIG = { | |
| 18 | + 'host': '172.17.16.15', | |
| 19 | + 'port': 3306, | |
| 20 | + 'user': 'root', | |
| 21 | + 'password': 'mysql2025!', | |
| 22 | + 'database': 'urban_ops_agent', | |
| 23 | + 'charset': 'utf8mb4', | |
| 24 | +} | |
| 25 | + | |
| 26 | +# ========== WGS84 → GCJ02 算法 ========== | |
| 27 | +PI = math.pi | |
| 28 | +A = 6378245.0 # 长半轴 | |
| 29 | +EE = 0.00669342162296594323 # 偏心率平方 | |
| 30 | + | |
| 31 | + | |
| 32 | +def wgs84_to_gcj02(lng, lat): | |
| 33 | + """WGS84 转 GCJ02(高德坐标系)""" | |
| 34 | + if lng < 72.004 or lng > 137.8347 or lat < 0.8293 or lat > 55.8271: | |
| 35 | + return lng, lat | |
| 36 | + | |
| 37 | + x, y = lng - 105.0, lat - 35.0 | |
| 38 | + | |
| 39 | + dlat = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * math.sqrt(abs(x)) | |
| 40 | + dlat += (20.0 * math.sin(6.0 * x * PI) + 20.0 * math.sin(2.0 * x * PI)) * 2.0 / 3.0 | |
| 41 | + dlat += (20.0 * math.sin(y * PI) + 40.0 * math.sin(y / 3.0 * PI)) * 2.0 / 3.0 | |
| 42 | + dlat += (160.0 * math.sin(y / 12.0 * PI) + 320.0 * math.sin(y * PI / 30.0)) * 2.0 / 3.0 | |
| 43 | + | |
| 44 | + dlng = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * math.sqrt(abs(x)) | |
| 45 | + dlng += (20.0 * math.sin(6.0 * x * PI) + 20.0 * math.sin(2.0 * x * PI)) * 2.0 / 3.0 | |
| 46 | + dlng += (20.0 * math.sin(x * PI) + 40.0 * math.sin(x / 3.0 * PI)) * 2.0 / 3.0 | |
| 47 | + dlng += (150.0 * math.sin(x / 12.0 * PI) + 300.0 * math.sin(x / 30.0 * PI)) * 2.0 / 3.0 | |
| 48 | + | |
| 49 | + rad_lat = lat / 180.0 * PI | |
| 50 | + magic = math.sin(rad_lat) | |
| 51 | + magic = 1 - EE * magic * magic | |
| 52 | + sqrt_magic = math.sqrt(magic) | |
| 53 | + | |
| 54 | + dlat = (dlat * 180.0) / ((A * (1 - EE)) / (magic * sqrt_magic) * PI) | |
| 55 | + dlng = (dlng * 180.0) / (A / sqrt_magic * math.cos(rad_lat) * PI) | |
| 56 | + | |
| 57 | + return lng + dlng, lat + dlat | |
| 58 | + | |
| 59 | + | |
| 60 | +def transform_wkt(wkt): | |
| 61 | + """转换 WKT 字符串中的所有坐标对(支持 POLYGON / MULTIPOLYGON)""" | |
| 62 | + if not wkt or not wkt.strip(): | |
| 63 | + return wkt | |
| 64 | + | |
| 65 | + def convert_pair(m): | |
| 66 | + nlng, nlat = wgs84_to_gcj02(float(m.group(1)), float(m.group(2))) | |
| 67 | + return f"{nlng:.6f} {nlat:.6f}" | |
| 68 | + | |
| 69 | + return re.sub(r'(\d+\.?\d*)\s+(\d+\.?\d*)', convert_pair, wkt) | |
| 70 | + | |
| 71 | + | |
| 72 | +def main(): | |
| 73 | + conn = pymysql.connect(**DB_CONFIG) | |
| 74 | + cur = conn.cursor() | |
| 75 | + | |
| 76 | + # 1. 转换起点/终点坐标(如果有的话) | |
| 77 | + cur.execute( | |
| 78 | + "SELECT id, starting_latitude, starting_longitude, end_latitude, end_longitude " | |
| 79 | + "FROM garden_gis_road " | |
| 80 | + "WHERE (starting_latitude IS NOT NULL AND starting_latitude != '') " | |
| 81 | + " OR (end_latitude IS NOT NULL AND end_latitude != '')" | |
| 82 | + ) | |
| 83 | + rows = cur.fetchall() | |
| 84 | + updated = 0 | |
| 85 | + for row in rows: | |
| 86 | + id_, slat, slng, elat, elng = row | |
| 87 | + updates = [] | |
| 88 | + params = [] | |
| 89 | + | |
| 90 | + if slat and slng and slat.strip(): | |
| 91 | + try: | |
| 92 | + nlng, nlat = wgs84_to_gcj02(float(slng), float(slat)) | |
| 93 | + updates.append('starting_longitude = %s') | |
| 94 | + updates.append('starting_latitude = %s') | |
| 95 | + params.append(str(round(nlng, 6))) | |
| 96 | + params.append(str(round(nlat, 6))) | |
| 97 | + except ValueError: | |
| 98 | + pass | |
| 99 | + | |
| 100 | + if elat and elng and elat.strip(): | |
| 101 | + try: | |
| 102 | + nlng, nlat = wgs84_to_gcj02(float(elng), float(elat)) | |
| 103 | + updates.append('end_longitude = %s') | |
| 104 | + updates.append('end_latitude = %s') | |
| 105 | + params.append(str(round(nlng, 6))) | |
| 106 | + params.append(str(round(nlat, 6))) | |
| 107 | + except ValueError: | |
| 108 | + pass | |
| 109 | + | |
| 110 | + if updates: | |
| 111 | + sql = 'UPDATE garden_gis_road SET ' + ', '.join(updates) + ' WHERE id = %s' | |
| 112 | + params.append(id_) | |
| 113 | + cur.execute(sql, params) | |
| 114 | + updated += 1 | |
| 115 | + | |
| 116 | + conn.commit() | |
| 117 | + print(f'起点/终点坐标: 转换 {updated} 条') | |
| 118 | + | |
| 119 | + # 2. 转换围栏坐标 | |
| 120 | + cur.execute( | |
| 121 | + "SELECT COUNT(*) FROM garden_gis_road " | |
| 122 | + "WHERE gis_polygon_coords IS NOT NULL AND gis_polygon_coords != ''" | |
| 123 | + ) | |
| 124 | + total = cur.fetchone()[0] | |
| 125 | + print(f'围栏坐标记录: {total} 条') | |
| 126 | + | |
| 127 | + batch_size = 500 | |
| 128 | + updated = 0 | |
| 129 | + for offset in range(0, total, batch_size): | |
| 130 | + cur.execute( | |
| 131 | + "SELECT id, gis_polygon_coords FROM garden_gis_road " | |
| 132 | + "WHERE gis_polygon_coords IS NOT NULL AND gis_polygon_coords != '' " | |
| 133 | + "LIMIT %s OFFSET %s", | |
| 134 | + (batch_size, offset) | |
| 135 | + ) | |
| 136 | + for id_, wkt in cur.fetchall(): | |
| 137 | + new_wkt = transform_wkt(wkt) | |
| 138 | + if new_wkt != wkt: | |
| 139 | + cur.execute( | |
| 140 | + "UPDATE garden_gis_road SET gis_polygon_coords = %s WHERE id = %s", | |
| 141 | + (new_wkt, id_) | |
| 142 | + ) | |
| 143 | + updated += 1 | |
| 144 | + conn.commit() | |
| 145 | + print(f' 进度: {min(offset + batch_size, total)}/{total}, 已更新: {updated}') | |
| 146 | + | |
| 147 | + print(f'围栏坐标: 转换 {updated} 条') | |
| 148 | + | |
| 149 | + # 3. 验证 | |
| 150 | + cur.execute( | |
| 151 | + "SELECT COUNT(*) FROM garden_gis_road " | |
| 152 | + "WHERE gis_polygon_coords IS NOT NULL AND gis_polygon_coords != ''" | |
| 153 | + ) | |
| 154 | + total = cur.fetchone()[0] | |
| 155 | + unconverted = 0 | |
| 156 | + cur.execute( | |
| 157 | + "SELECT id, gis_polygon_coords FROM garden_gis_road " | |
| 158 | + "WHERE gis_polygon_coords IS NOT NULL AND gis_polygon_coords != ''" | |
| 159 | + ) | |
| 160 | + for id_, wkt in cur.fetchall(): | |
| 161 | + m = re.search(r'(\d+\.?\d*)\s+(\d+\.?\d*)', wkt) | |
| 162 | + if m: | |
| 163 | + wlng, wlat = float(m.group(1)), float(m.group(2)) | |
| 164 | + nlng, nlat = wgs84_to_gcj02(wlng, wlat) | |
| 165 | + if abs(wlng - nlng) < 0.00001: | |
| 166 | + unconverted += 1 | |
| 167 | + | |
| 168 | + print(f'\n验证: {unconverted}/{total} 条未转换') | |
| 169 | + if unconverted == 0: | |
| 170 | + print('所有坐标已成功转换为 GCJ02(高德坐标系)') | |
| 171 | + | |
| 172 | + cur.close() | |
| 173 | + conn.close() | |
| 174 | + | |
| 175 | + | |
| 176 | +if __name__ == '__main__': | |
| 177 | + main() | ... | ... |
script/shell/deploy.sh
| 1 | 1 | #!/bin/bash |
| 2 | -set -e | |
| 2 | +set -eo pipefail | |
| 3 | 3 | |
| 4 | 4 | DATE=$(date +%Y%m%d%H%M) |
| 5 | 5 | # 基础路径 |
| ... | ... | @@ -9,9 +9,9 @@ SOURCE_PATH=$BASE_PATH/build |
| 9 | 9 | # 服务名称。同时约定部署服务的 jar 包名字也为它。 |
| 10 | 10 | SERVER_NAME=urbanops-server |
| 11 | 11 | # 环境 |
| 12 | -PROFILES_ACTIVE=development | |
| 12 | +PROFILES_ACTIVE=dev | |
| 13 | 13 | # 健康检查 URL |
| 14 | -HEALTH_CHECK_URL=http://127.0.0.1:48080/actuator/health/ | |
| 14 | +HEALTH_CHECK_URL=http://127.0.0.1:48081/actuator/health/ | |
| 15 | 15 | |
| 16 | 16 | # heapError 存放路径 |
| 17 | 17 | HEAP_ERROR_PATH=$BASE_PATH/heapError |
| ... | ... | @@ -33,7 +33,8 @@ function backup() { |
| 33 | 33 | # 如果存在,则备份到 backup 目录下,使用时间作为后缀 |
| 34 | 34 | else |
| 35 | 35 | echo "[backup] 开始备份 $SERVER_NAME ..." |
| 36 | - cp $BASE_PATH/$SERVER_NAME.jar $BASE_PATH/backup/$SERVER_NAME-$DATE.jar | |
| 36 | + mkdir -p "$BASE_PATH/backup" | |
| 37 | + cp "$BASE_PATH/$SERVER_NAME.jar" "$BASE_PATH/backup/$SERVER_NAME-$DATE.jar" | |
| 37 | 38 | echo "[backup] 备份 $SERVER_NAME 完成" |
| 38 | 39 | fi |
| 39 | 40 | } |
| ... | ... | @@ -47,12 +48,12 @@ function transfer() { |
| 47 | 48 | echo "[transfer] $BASE_PATH/$SERVER_NAME.jar 不存在,跳过删除" |
| 48 | 49 | else |
| 49 | 50 | echo "[transfer] 移除 $BASE_PATH/$SERVER_NAME.jar 完成" |
| 50 | - rm $BASE_PATH/$SERVER_NAME.jar | |
| 51 | + rm "$BASE_PATH/$SERVER_NAME.jar" | |
| 51 | 52 | fi |
| 52 | 53 | |
| 53 | 54 | # 复制新 jar 包 |
| 54 | 55 | echo "[transfer] 从 $SOURCE_PATH 中获取 $SERVER_NAME.jar 并迁移至 $BASE_PATH ...." |
| 55 | - cp $SOURCE_PATH/$SERVER_NAME.jar $BASE_PATH | |
| 56 | + cp "$SOURCE_PATH/$SERVER_NAME.jar" "$BASE_PATH" | |
| 56 | 57 | |
| 57 | 58 | echo "[transfer] 转移 $SERVER_NAME.jar 完成" |
| 58 | 59 | } |
| ... | ... | @@ -60,7 +61,7 @@ function transfer() { |
| 60 | 61 | # 停止:优雅关闭之前已经启动的服务 |
| 61 | 62 | function stop() { |
| 62 | 63 | echo "[stop] 开始停止 $BASE_PATH/$SERVER_NAME" |
| 63 | - PID=$(ps -ef | grep $BASE_PATH/$SERVER_NAME | grep -v "grep" | awk '{print $2}') | |
| 64 | + PID=$(ps -ef | grep "$BASE_PATH/$SERVER_NAME.jar" | grep -v "grep" | awk '{print $2}') | |
| 64 | 65 | # 如果 Java 服务启动中,则进行关闭 |
| 65 | 66 | if [ -n "$PID" ]; then |
| 66 | 67 | # 正常关闭 |
| ... | ... | @@ -70,7 +71,7 @@ function stop() { |
| 70 | 71 | for ((i = 0; i < 120; i++)) |
| 71 | 72 | do |
| 72 | 73 | sleep 1 |
| 73 | - PID=$(ps -ef | grep $BASE_PATH/$SERVER_NAME | grep -v "grep" | awk '{print $2}') | |
| 74 | + PID=$(ps -ef | grep "$BASE_PATH/$SERVER_NAME.jar" | grep -v "grep" | awk '{print $2}') | |
| 74 | 75 | if [ -n "$PID" ]; then |
| 75 | 76 | echo -e ".\c" |
| 76 | 77 | else |
| ... | ... | @@ -99,7 +100,8 @@ function start() { |
| 99 | 100 | echo "[start] PROFILES: $PROFILES_ACTIVE" |
| 100 | 101 | |
| 101 | 102 | # 开始启动 |
| 102 | - BUILD_ID=dontKillMe nohup java -server $JAVA_OPS $JAVA_AGENT -jar $BASE_PATH/$SERVER_NAME.jar --spring.profiles.active=$PROFILES_ACTIVE & | |
| 103 | + mkdir -p "$HEAP_ERROR_PATH" | |
| 104 | + nohup java -server $JAVA_OPS $JAVA_AGENT -jar "$BASE_PATH/$SERVER_NAME.jar" --spring.profiles.active=$PROFILES_ACTIVE >> "$BASE_PATH/nohup.out" 2>&1 & | |
| 103 | 105 | echo "[start] 启动 $BASE_PATH/$SERVER_NAME 完成" |
| 104 | 106 | } |
| 105 | 107 | |
| ... | ... | @@ -112,7 +114,7 @@ function healthCheck() { |
| 112 | 114 | for ((i = 0; i < 120; i++)) |
| 113 | 115 | do |
| 114 | 116 | # 请求健康检查地址,只获取状态码。 |
| 115 | - result=`curl -I -m 10 -o /dev/null -s -w %{http_code} $HEALTH_CHECK_URL || echo "000"` | |
| 117 | + result=$(curl -I -m 10 -o /dev/null -s -w %{http_code} "$HEALTH_CHECK_URL" || echo "000") | |
| 116 | 118 | # 如果状态码为 200,则说明健康检查通过 |
| 117 | 119 | if [ "$result" == "200" ]; then |
| 118 | 120 | echo "[healthCheck] 健康检查通过"; |
| ... | ... | @@ -127,24 +129,29 @@ function healthCheck() { |
| 127 | 129 | # 健康检查未通过,则异常退出 shell 脚本,不继续部署。 |
| 128 | 130 | if [ ! "$result" == "200" ]; then |
| 129 | 131 | echo "[healthCheck] 健康检查不通过,可能部署失败。查看日志,自行判断是否启动成功"; |
| 130 | - tail -n 10 nohup.out | |
| 132 | + tail -n 10 "$BASE_PATH/nohup.out" | |
| 131 | 133 | exit 1; |
| 132 | 134 | # 健康检查通过,打印最后 10 行日志,可能部署的人想看下日志。 |
| 133 | 135 | else |
| 134 | - tail -n 10 nohup.out | |
| 136 | + tail -n 10 "$BASE_PATH/nohup.out" | |
| 135 | 137 | fi |
| 136 | 138 | # 如果未配置健康检查,则 sleep 120 秒,人工看日志是否部署成功。 |
| 137 | 139 | else |
| 138 | 140 | echo "[healthCheck] HEALTH_CHECK_URL 未配置,开始 sleep 120 秒"; |
| 139 | 141 | sleep 120 |
| 140 | 142 | echo "[healthCheck] sleep 120 秒完成,查看日志,自行判断是否启动成功"; |
| 141 | - tail -n 50 nohup.out | |
| 143 | + tail -n 50 "$BASE_PATH/nohup.out" | |
| 142 | 144 | fi |
| 143 | 145 | } |
| 144 | 146 | |
| 145 | 147 | # 部署 |
| 146 | 148 | function deploy() { |
| 147 | - cd $BASE_PATH | |
| 149 | + cd "$BASE_PATH" || { echo "[deploy] 错误:$BASE_PATH 目录不存在"; exit 1; } | |
| 150 | + if [ ! -d "$SOURCE_PATH" ]; then | |
| 151 | + echo "[deploy] 错误:构建目录 $SOURCE_PATH 不存在" | |
| 152 | + exit 1 | |
| 153 | + fi | |
| 154 | + mkdir -p "$BASE_PATH/backup" "$HEAP_ERROR_PATH" | |
| 148 | 155 | # 备份原 jar |
| 149 | 156 | backup |
| 150 | 157 | # 停止 Java 服务 | ... | ... |
sql/dis-check-menu.sql
0 → 100644
| 1 | +-- ===================================================== | |
| 2 | +-- dis核对 完整部署脚本 | |
| 3 | +-- 数据库: urban_ops_agent | |
| 4 | +-- 包含: 表结构变更 + 菜单创建 + 角色授权 | |
| 5 | +-- 执行方式: 在对应环境的数据库中执行此 SQL | |
| 6 | +-- ===================================================== | |
| 7 | + | |
| 8 | +-- ========================================== | |
| 9 | +-- Part 0: garden_gis_road 表结构变更 | |
| 10 | +-- ========================================== | |
| 11 | +-- 新增多边形围栏边界坐标字段(MySQL 8.x 兼容写法) | |
| 12 | +SET @col_exists = ( | |
| 13 | + SELECT COUNT(*) FROM information_schema.COLUMNS | |
| 14 | + WHERE TABLE_SCHEMA = DATABASE() | |
| 15 | + AND TABLE_NAME = 'garden_gis_road' | |
| 16 | + AND COLUMN_NAME = 'gis_polygon_coords' | |
| 17 | +); | |
| 18 | + | |
| 19 | +SET @sql = IF(@col_exists = 0, | |
| 20 | + 'ALTER TABLE garden_gis_road ADD COLUMN gis_polygon_coords LONGTEXT NOT NULL COMMENT ''多边形/矩形围栏边界坐标,格式:POLYGON((x1 y1, x2 y2, ..., xn yn, x1 y1)) 存储的是WGS84坐标系''', | |
| 21 | + 'SELECT ''COLUMN gis_polygon_coords already exists'' AS info' | |
| 22 | +); | |
| 23 | +PREPARE stmt FROM @sql; | |
| 24 | +EXECUTE stmt; | |
| 25 | +DEALLOCATE PREPARE stmt; | |
| 26 | + | |
| 27 | +-- ========================================== | |
| 28 | +-- Part 1: 菜单创建 | |
| 29 | +-- ========================================== | |
| 30 | + | |
| 31 | +-- 1. 新增「dis核对」子菜单(位于电子围栏下) | |
| 32 | +INSERT INTO system_menu | |
| 33 | +(name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) | |
| 34 | +VALUES | |
| 35 | +('dis核对', '', 2, 1, 5698, 'disCheck', 'ep:view', 'fence/dis-check/index', 'DisCheck', 0, 1, 0, 1, '1', NOW(), '1', NOW(), 0); | |
| 36 | + | |
| 37 | +-- 2. 获取上一步插入的菜单ID,用于后续按钮权限插入 | |
| 38 | +SET @dis_check_menu_id = LAST_INSERT_ID(); | |
| 39 | + | |
| 40 | +-- 3. 新增「导出」按钮权限(挂在新菜单下) | |
| 41 | +INSERT INTO system_menu | |
| 42 | +(name, permission, type, sort, parent_id, path, icon, component, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) | |
| 43 | +VALUES | |
| 44 | +('导出', 'garden:gis-road:export', 3, 0, @dis_check_menu_id, '', '', '', 0, 1, 0, 1, '1', NOW(), '1', NOW(), 0); | |
| 45 | + | |
| 46 | +-- 4. 新增「查询」按钮权限 | |
| 47 | +INSERT INTO system_menu | |
| 48 | +(name, permission, type, sort, parent_id, path, icon, component, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) | |
| 49 | +VALUES | |
| 50 | +('查询', 'garden:gis-road:query', 3, 1, @dis_check_menu_id, '', '', '', 0, 1, 0, 1, '1', NOW(), '1', NOW(), 0); | |
| 51 | + | |
| 52 | +-- 5. 给超级管理员(role_id=1)分配菜单权限 | |
| 53 | +INSERT INTO system_role_menu (role_id, menu_id, creator, create_time, updater, update_time) | |
| 54 | +VALUES | |
| 55 | +(1, @dis_check_menu_id, '1', NOW(), '1', NOW()); | |
| 56 | + | |
| 57 | +INSERT INTO system_role_menu (role_id, menu_id, creator, create_time, updater, update_time) | |
| 58 | +SELECT 1, id, '1', NOW(), '1', NOW() | |
| 59 | +FROM system_menu | |
| 60 | +WHERE parent_id = @dis_check_menu_id AND deleted = 0; | |
| 61 | + | |
| 62 | +-- ===================================================== | |
| 63 | +-- 验证插入结果 | |
| 64 | +-- ===================================================== | |
| 65 | +SELECT id, name, type, parent_id, path, component, permission | |
| 66 | +FROM system_menu | |
| 67 | +WHERE parent_id = 5698 OR id = @dis_check_menu_id | |
| 68 | +ORDER BY parent_id, sort; | |
| 69 | + | |
| 70 | +SELECT rm.role_id, rm.menu_id, m.name, m.permission | |
| 71 | +FROM system_role_menu rm | |
| 72 | +JOIN system_menu m ON rm.menu_id = m.id | |
| 73 | +WHERE rm.menu_id = @dis_check_menu_id | |
| 74 | + OR rm.menu_id IN (SELECT id FROM system_menu WHERE parent_id = @dis_check_menu_id); | ... | ... |
sql/garden_road_add_gis_fields.sql
0 → 100644
sql/sync_land_forest_to_road.sql
0 → 100644
| 1 | +-- ============================================================ | |
| 2 | +-- 将 garden_gis_land_forest_data 数据同步到 garden_gis_road | |
| 3 | +-- 按 greenname + greentype 分组聚合 | |
| 4 | +-- - xbmj 求和 → gis_plot_area | |
| 5 | +-- - geometry 所有 MULTIPOLYGON 环拼接 → gis_polygon_coords (WKT) | |
| 6 | +-- - 其他 gis_ 字段取分组内 MAX 值 | |
| 7 | +-- | |
| 8 | +-- 注意:MySQL 不支持 ST_Union 聚合函数,geometry 并集通过 | |
| 9 | +-- GROUP_CONCAT 拼接各 MULTIPOLYGON 的内部环来实现,不做空间融合。 | |
| 10 | +-- 如需真正融合重叠区域,建议在 Java 层用 JTS 处理。 | |
| 11 | +-- ============================================================ | |
| 12 | + | |
| 13 | +-- 避免 GROUP_CONCAT 默认 1024 字节限制导致 WKT 截断 | |
| 14 | +SET SESSION group_concat_max_len = 10485760; -- 10MB | |
| 15 | + | |
| 16 | +INSERT INTO garden_gis_road ( | |
| 17 | + gis_plot_name, | |
| 18 | + gis_green_type_name, | |
| 19 | + gis_plot_area, | |
| 20 | + gis_polygon_coords, | |
| 21 | + gis_owner_unit, | |
| 22 | + gis_property_unit, | |
| 23 | + gis_manage_unit, | |
| 24 | + gis_street, | |
| 25 | + gis_plot_code, | |
| 26 | + gis_green_type_code, | |
| 27 | + street_id, | |
| 28 | + company_id, | |
| 29 | + dept_id | |
| 30 | +) | |
| 31 | +SELECT | |
| 32 | + greenname AS gis_plot_name, | |
| 33 | + greentype AS gis_green_type_name, | |
| 34 | + ROUND(SUM(xbmj), 2) AS gis_plot_area, | |
| 35 | + CONCAT( | |
| 36 | + 'MULTIPOLYGON(', | |
| 37 | + GROUP_CONCAT( | |
| 38 | + SUBSTR( | |
| 39 | + ST_AsText(geometry), | |
| 40 | + 14, -- 跳过 'MULTIPOLYGON('(13字符) | |
| 41 | + LENGTH(ST_AsText(geometry)) - 14 -- 去掉首尾各1个括号 | |
| 42 | + ) | |
| 43 | + SEPARATOR ',' | |
| 44 | + ), | |
| 45 | + ')' | |
| 46 | + ) AS gis_polygon_coords, | |
| 47 | + MAX(cqdw) AS gis_owner_unit, | |
| 48 | + MAX(cqdw) AS gis_property_unit, | |
| 49 | + MAX(gl_dw) AS gis_manage_unit, | |
| 50 | + MAX(CONCAT_WS('-', sheng, shi, xian, xiang)) AS gis_street, | |
| 51 | + '' AS gis_plot_code, | |
| 52 | + '' AS gis_green_type_code, | |
| 53 | + '' AS street_id, | |
| 54 | + 0 AS company_id, | |
| 55 | + 0 AS dept_id | |
| 56 | +FROM garden_gis_land_forest_data | |
| 57 | +WHERE greenname IS NOT NULL | |
| 58 | + AND greentype IS NOT NULL | |
| 59 | +GROUP BY greenname, greentype; | ... | ... |
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/gis/GisRoadController.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.garden.controller.admin.gis; | |
| 2 | + | |
| 3 | +import com.zteits.urbanops.framework.common.pojo.PageParam; | |
| 4 | +import org.slf4j.Logger; | |
| 5 | +import org.slf4j.LoggerFactory; | |
| 6 | +import org.springframework.web.bind.annotation.*; | |
| 7 | +import jakarta.annotation.Resource; | |
| 8 | +import org.springframework.validation.annotation.Validated; | |
| 9 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 10 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 11 | +import io.swagger.v3.oas.annotations.Parameter; | |
| 12 | +import io.swagger.v3.oas.annotations.Operation; | |
| 13 | + | |
| 14 | +import jakarta.validation.*; | |
| 15 | +import jakarta.servlet.http.*; | |
| 16 | +import java.io.IOException; | |
| 17 | +import java.time.LocalDate; | |
| 18 | +import java.time.format.DateTimeFormatter; | |
| 19 | +import java.util.*; | |
| 20 | + | |
| 21 | +import com.zteits.urbanops.framework.common.pojo.PageResult; | |
| 22 | +import com.zteits.urbanops.framework.common.pojo.CommonResult; | |
| 23 | +import static com.zteits.urbanops.framework.common.pojo.CommonResult.success; | |
| 24 | + | |
| 25 | +import com.zteits.urbanops.framework.excel.core.util.ExcelUtils; | |
| 26 | +import com.zteits.urbanops.framework.apilog.core.annotation.ApiAccessLog; | |
| 27 | +import static com.zteits.urbanops.framework.apilog.core.enums.OperateTypeEnum.*; | |
| 28 | + | |
| 29 | +import com.zteits.urbanops.module.garden.controller.admin.gis.vo.*; | |
| 30 | +import com.zteits.urbanops.module.garden.service.gis.GisRoadService; | |
| 31 | + | |
| 32 | +@Tag(name = "管理后台 - GIS 道路管理") | |
| 33 | +@RestController | |
| 34 | +@RequestMapping("/garden/gis/road") | |
| 35 | +@Validated | |
| 36 | +public class GisRoadController { | |
| 37 | + private static final Logger log = LoggerFactory.getLogger(GisRoadController.class); | |
| 38 | + | |
| 39 | + @Resource | |
| 40 | + private GisRoadService gisRoadService; | |
| 41 | + | |
| 42 | + @GetMapping("/list") | |
| 43 | + @Operation(summary = "获得 GIS 道路分页列表") | |
| 44 | + @PreAuthorize("@ss.hasPermission('garden:gis-road:query')") | |
| 45 | + public CommonResult<PageResult<GisRoadRespVO>> getGisRoadList(@Valid GisRoadPageReqVO pageReqVO) { | |
| 46 | + PageResult<GisRoadRespVO> pageResult = gisRoadService.getGisRoadPage(pageReqVO); | |
| 47 | + return success(pageResult); | |
| 48 | + } | |
| 49 | + | |
| 50 | + @GetMapping("/list-all") | |
| 51 | + @Operation(summary = "获得全部 GIS 道路列表(不分页,用于地图渲染)") | |
| 52 | + @PreAuthorize("@ss.hasPermission('garden:gis-road:query')") | |
| 53 | + public CommonResult<List<GisRoadRespVO>> getGisRoadAllList(GisRoadPageReqVO reqVO) { | |
| 54 | + return success(gisRoadService.getGisRoadAllList(reqVO)); | |
| 55 | + } | |
| 56 | + | |
| 57 | + @GetMapping("/get") | |
| 58 | + @Operation(summary = "获得 GIS 道路详情") | |
| 59 | + @Parameter(name = "id", description = "编号", required = true, example = "1024") | |
| 60 | + @PreAuthorize("@ss.hasPermission('garden:gis-road:query')") | |
| 61 | + public CommonResult<GisRoadRespVO> getGisRoad(@RequestParam("id") Long id) { | |
| 62 | + return success(gisRoadService.getGisRoad(id)); | |
| 63 | + } | |
| 64 | + | |
| 65 | + @GetMapping("/export-excel") | |
| 66 | + @Operation(summary = "导出 GIS 道路 Excel") | |
| 67 | + @PreAuthorize("@ss.hasPermission('garden:gis-road:export')") | |
| 68 | + @ApiAccessLog(operateType = EXPORT) | |
| 69 | + public void exportGisRoadExcel(@Valid GisRoadPageReqVO pageReqVO, | |
| 70 | + HttpServletResponse response) throws IOException { | |
| 71 | + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); | |
| 72 | + List<GisRoadRespVO> list = gisRoadService.getGisRoadPage(pageReqVO).getList(); | |
| 73 | + // 只导出表格展示的 6 列数据 | |
| 74 | + List<GisRoadExportVO> exportList = new ArrayList<>(); | |
| 75 | + for (GisRoadRespVO vo : list) { | |
| 76 | + GisRoadExportVO exportVO = new GisRoadExportVO(); | |
| 77 | + exportVO.setGisPlotName(vo.getGisPlotName()); | |
| 78 | + exportVO.setRoadName(vo.getRoadName()); | |
| 79 | + exportVO.setGisPlotArea(vo.getGisPlotArea()); | |
| 80 | + exportVO.setTotalArea(vo.getTotalArea()); | |
| 81 | + exportVO.setGisIsRelatedStr(vo.getGisIsRelated() != null && vo.getGisIsRelated() == 1 ? "已关联" : "未关联"); | |
| 82 | + exportVO.setGisDiffArea(vo.getGisDiffArea()); | |
| 83 | + exportList.add(exportVO); | |
| 84 | + } | |
| 85 | + String filename = "GIS核对" + LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")) + ".xls"; | |
| 86 | + ExcelUtils.write(response, filename, "数据", GisRoadExportVO.class, exportList); | |
| 87 | + } | |
| 88 | + | |
| 89 | + @PostMapping("/associate") | |
| 90 | + @Operation(summary = "关联道路到 GIS 图斑") | |
| 91 | + @PreAuthorize("@ss.hasPermission('garden:gis-road:update')") | |
| 92 | + public CommonResult<Boolean> associateRoad(@Valid @RequestBody GisRoadAssociateReqVO reqVO) { | |
| 93 | + gisRoadService.associateRoad(reqVO); | |
| 94 | + return success(true); | |
| 95 | + } | |
| 96 | + | |
| 97 | + @PostMapping("/disassociate") | |
| 98 | + @Operation(summary = "取消 GIS 图斑与道路的关联") | |
| 99 | + @PreAuthorize("@ss.hasPermission('garden:gis-road:update')") | |
| 100 | + public CommonResult<Boolean> disassociateRoad(@Valid @RequestBody GisRoadDisassociateReqVO reqVO) { | |
| 101 | + gisRoadService.disassociateRoad(reqVO); | |
| 102 | + return success(true); | |
| 103 | + } | |
| 104 | + | |
| 105 | + @PostMapping("/convert-coordinates") | |
| 106 | + @Operation(summary = "将全部道路坐标从 WGS84 转换为 GCJ02(高德坐标系)") | |
| 107 | + @PreAuthorize("@ss.hasPermission('garden:gis-road:update')") | |
| 108 | + public CommonResult<String> convertCoordinates() { | |
| 109 | + int count = gisRoadService.convertCoordinatesToGcj02(); | |
| 110 | + log.info("坐标转换完成,共转换 {} 条记录", count); | |
| 111 | + return success("坐标转换完成,共转换 " + count + " 条记录"); | |
| 112 | + } | |
| 113 | + | |
| 114 | +} | ... | ... |
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/gis/GisTypeController.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.garden.controller.admin.gis; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 4 | +import io.swagger.v3.oas.annotations.Operation; | |
| 5 | +import org.springframework.web.bind.annotation.*; | |
| 6 | +import jakarta.annotation.Resource; | |
| 7 | +import jakarta.validation.Valid; | |
| 8 | +import java.util.*; | |
| 9 | + | |
| 10 | +import com.zteits.urbanops.framework.common.pojo.CommonResult; | |
| 11 | +import static com.zteits.urbanops.framework.common.pojo.CommonResult.success; | |
| 12 | +import com.zteits.urbanops.module.garden.dal.dataobject.gis.GisTypeDO; | |
| 13 | +import com.zteits.urbanops.module.garden.service.gis.GisTypeService; | |
| 14 | + | |
| 15 | +@Tag(name = "管理后台 - GIS 绿地类型") | |
| 16 | +@RestController | |
| 17 | +@RequestMapping("/garden/gis/type") | |
| 18 | +public class GisTypeController { | |
| 19 | + | |
| 20 | + @Resource | |
| 21 | + private GisTypeService gisTypeService; | |
| 22 | + | |
| 23 | + @GetMapping("/list-by-parent") | |
| 24 | + @Operation(summary = "按父级编码查子类型(级联下拉用)") | |
| 25 | + public CommonResult<List<Map<String, Object>>> listByParentCode( | |
| 26 | + @RequestParam(required = false) String parentCode) { | |
| 27 | + List<GisTypeDO> list = gisTypeService.listByParentCode(parentCode); | |
| 28 | + List<Map<String, Object>> result = new ArrayList<>(); | |
| 29 | + for (GisTypeDO t : list) { | |
| 30 | + Map<String, Object> m = new HashMap<>(); | |
| 31 | + m.put("code", t.getTypeCode()); | |
| 32 | + m.put("name", t.getTypeName()); | |
| 33 | + m.put("level", t.getLevel()); | |
| 34 | + m.put("parentCode", t.getParentCode()); | |
| 35 | + result.add(m); | |
| 36 | + } | |
| 37 | + return success(result); | |
| 38 | + } | |
| 39 | + | |
| 40 | + @GetMapping("/list-by-level") | |
| 41 | + @Operation(summary = "按层级查类型列表") | |
| 42 | + public CommonResult<List<Map<String, Object>>> listByLevel(@RequestParam Integer level) { | |
| 43 | + List<GisTypeDO> list = gisTypeService.listByLevel(level); | |
| 44 | + List<Map<String, Object>> result = new ArrayList<>(); | |
| 45 | + for (GisTypeDO t : list) { | |
| 46 | + Map<String, Object> m = new HashMap<>(); | |
| 47 | + m.put("code", t.getTypeCode()); | |
| 48 | + m.put("name", t.getTypeName()); | |
| 49 | + m.put("level", t.getLevel()); | |
| 50 | + m.put("parentCode", t.getParentCode()); | |
| 51 | + result.add(m); | |
| 52 | + } | |
| 53 | + return success(result); | |
| 54 | + } | |
| 55 | +} | ... | ... |
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/gis/vo/GisRoadAssociateReqVO.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.garden.controller.admin.gis.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | +import jakarta.validation.constraints.NotNull; | |
| 6 | + | |
| 7 | +@Schema(description = "管理后台 - GIS 道路关联 Request VO") | |
| 8 | +@Data | |
| 9 | +public class GisRoadAssociateReqVO { | |
| 10 | + | |
| 11 | + @Schema(description = "GIS道路ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 12 | + @NotNull(message = "GIS道路ID不能为空") | |
| 13 | + private Long id; | |
| 14 | + | |
| 15 | + @Schema(description = "关联的道路ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") | |
| 16 | + @NotNull(message = "道路ID不能为空") | |
| 17 | + private Long roadId; | |
| 18 | + | |
| 19 | +} | ... | ... |
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/gis/vo/GisRoadDisassociateReqVO.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.garden.controller.admin.gis.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | +import jakarta.validation.constraints.NotNull; | |
| 6 | + | |
| 7 | +@Schema(description = "管理后台 - GIS 道路取消关联 Request VO") | |
| 8 | +@Data | |
| 9 | +public class GisRoadDisassociateReqVO { | |
| 10 | + | |
| 11 | + @Schema(description = "GIS道路ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 12 | + @NotNull(message = "GIS道路ID不能为空") | |
| 13 | + private Long id; | |
| 14 | + | |
| 15 | +} | ... | ... |
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/gis/vo/GisRoadExportVO.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.garden.controller.admin.gis.vo; | |
| 2 | + | |
| 3 | +import com.fasterxml.jackson.databind.annotation.JsonSerialize; | |
| 4 | +import com.zteits.urbanops.module.garden.util.TwoDecimalFloatSerializer; | |
| 5 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 6 | +import lombok.Data; | |
| 7 | +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; | |
| 8 | +import cn.idev.excel.annotation.ExcelProperty; | |
| 9 | + | |
| 10 | +@Schema(description = "GIS 道路核对导出 VO") | |
| 11 | +@Data | |
| 12 | +@ExcelIgnoreUnannotated | |
| 13 | +public class GisRoadExportVO { | |
| 14 | + | |
| 15 | + @Schema(description = "图斑名称") | |
| 16 | + @ExcelProperty("图斑名称") | |
| 17 | + private String gisPlotName; | |
| 18 | + | |
| 19 | + @Schema(description = "道路名称") | |
| 20 | + @ExcelProperty("道路名称") | |
| 21 | + private String roadName; | |
| 22 | + | |
| 23 | + @Schema(description = "图斑面积") | |
| 24 | + @ExcelProperty("图斑面积(㎡)") | |
| 25 | + @JsonSerialize(using = TwoDecimalFloatSerializer.class) | |
| 26 | + private Float gisPlotArea; | |
| 27 | + | |
| 28 | + @Schema(description = "道路面积") | |
| 29 | + @ExcelProperty("道路面积(㎡)") | |
| 30 | + @JsonSerialize(using = TwoDecimalFloatSerializer.class) | |
| 31 | + private Float totalArea; | |
| 32 | + | |
| 33 | + @Schema(description = "是否关联") | |
| 34 | + @ExcelProperty("是否关联") | |
| 35 | + private String gisIsRelatedStr; | |
| 36 | + | |
| 37 | + @Schema(description = "差异面积") | |
| 38 | + @ExcelProperty("差异面积(㎡)") | |
| 39 | + @JsonSerialize(using = TwoDecimalFloatSerializer.class) | |
| 40 | + private Float gisDiffArea; | |
| 41 | + | |
| 42 | +} | ... | ... |
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/gis/vo/GisRoadPageReqVO.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.garden.controller.admin.gis.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | +import com.zteits.urbanops.framework.common.pojo.PageParam; | |
| 6 | +import org.springframework.format.annotation.DateTimeFormat; | |
| 7 | +import java.time.LocalDateTime; | |
| 8 | + | |
| 9 | +import static com.zteits.urbanops.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | |
| 10 | + | |
| 11 | +@Schema(description = "管理后台 - GIS 道路核对分页 Request VO") | |
| 12 | +@Data | |
| 13 | +public class GisRoadPageReqVO extends PageParam { | |
| 14 | + | |
| 15 | + @Schema(description = "道路名称") | |
| 16 | + private String roadName; | |
| 17 | + | |
| 18 | + @Schema(description = "业务线") | |
| 19 | + private String busiLine; | |
| 20 | + | |
| 21 | + @Schema(description = "街道ID") | |
| 22 | + private String streetId; | |
| 23 | + | |
| 24 | + @Schema(description = "街道名称") | |
| 25 | + private String streetName; | |
| 26 | + | |
| 27 | + @Schema(description = "养护级别") | |
| 28 | + private Integer levelId; | |
| 29 | + | |
| 30 | + @Schema(description = "道路属性") | |
| 31 | + private String roadAttr; | |
| 32 | + | |
| 33 | + @Schema(description = "道路方位") | |
| 34 | + private String direction; | |
| 35 | + | |
| 36 | + @Schema(description = "GIS 图斑编号") | |
| 37 | + private String gisPlotCode; | |
| 38 | + | |
| 39 | + @Schema(description = "GIS 图斑名称") | |
| 40 | + private String gisPlotName; | |
| 41 | + | |
| 42 | + @Schema(description = "是否已关联 (0=未关联, 1=已关联)") | |
| 43 | + private Integer gisIsRelated; | |
| 44 | + | |
| 45 | + @Schema(description = "归属公司ID") | |
| 46 | + private Long companyId; | |
| 47 | + | |
| 48 | + @Schema(description = "部门ID") | |
| 49 | + private Long deptId; | |
| 50 | + | |
| 51 | + @Schema(description = "GIS绿地类型编码(级联查询用)") | |
| 52 | + private String gisTypeCode; | |
| 53 | + | |
| 54 | + @Schema(description = "创建时间") | |
| 55 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 56 | + private LocalDateTime[] createTime; | |
| 57 | + | |
| 58 | +} | ... | ... |
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/gis/vo/GisRoadRespVO.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.garden.controller.admin.gis.vo; | |
| 2 | + | |
| 3 | +import com.fasterxml.jackson.databind.annotation.JsonSerialize; | |
| 4 | +import com.zteits.urbanops.module.garden.util.TwoDecimalFloatSerializer; | |
| 5 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 6 | +import lombok.*; | |
| 7 | +import org.springframework.format.annotation.DateTimeFormat; | |
| 8 | +import java.time.LocalDateTime; | |
| 9 | +import cn.idev.excel.annotation.*; | |
| 10 | + | |
| 11 | +@Schema(description = "管理后台 - GIS 道路核对 Response VO") | |
| 12 | +@Data | |
| 13 | +@ExcelIgnoreUnannotated | |
| 14 | +public class GisRoadRespVO { | |
| 15 | + | |
| 16 | + @Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 17 | + @ExcelProperty("ID") | |
| 18 | + private Long id; | |
| 19 | + | |
| 20 | + @Schema(description = "关联道路ID") | |
| 21 | + @ExcelProperty("关联道路ID") | |
| 22 | + private Long gisRoadId; | |
| 23 | + | |
| 24 | + @Schema(description = "道路名称") | |
| 25 | + @ExcelProperty("道路名称") | |
| 26 | + private String roadName; | |
| 27 | + | |
| 28 | + @Schema(description = "业务线") | |
| 29 | + @ExcelProperty("业务线") | |
| 30 | + private String busiLine; | |
| 31 | + | |
| 32 | + @Schema(description = "街道ID") | |
| 33 | + @ExcelProperty("街道ID") | |
| 34 | + private String streetId; | |
| 35 | + | |
| 36 | + @Schema(description = "街道名称") | |
| 37 | + @ExcelProperty("街道名称") | |
| 38 | + private String streetName; | |
| 39 | + | |
| 40 | + @Schema(description = "养护级别") | |
| 41 | + @ExcelProperty("养护级别") | |
| 42 | + private Integer levelId; | |
| 43 | + | |
| 44 | + @Schema(description = "道路属性") | |
| 45 | + @ExcelProperty("道路属性") | |
| 46 | + private String roadAttr; | |
| 47 | + | |
| 48 | + @Schema(description = "道路方位") | |
| 49 | + @ExcelProperty("道路方位") | |
| 50 | + private String direction; | |
| 51 | + | |
| 52 | + @Schema(description = "行道树绿化面积") | |
| 53 | + @ExcelProperty("行道树绿化面积") | |
| 54 | + @JsonSerialize(using = TwoDecimalFloatSerializer.class) | |
| 55 | + private Float greenBeltTreeArea; | |
| 56 | + | |
| 57 | + @Schema(description = "绿化带面积") | |
| 58 | + @ExcelProperty("绿化带面积") | |
| 59 | + @JsonSerialize(using = TwoDecimalFloatSerializer.class) | |
| 60 | + private Float greenBeltArea; | |
| 61 | + | |
| 62 | + @Schema(description = "总绿化带面积") | |
| 63 | + @ExcelProperty("总绿化带面积") | |
| 64 | + @JsonSerialize(using = TwoDecimalFloatSerializer.class) | |
| 65 | + private Float totalArea; | |
| 66 | + | |
| 67 | + @Schema(description = "行道树面积") | |
| 68 | + @ExcelProperty("行道树面积") | |
| 69 | + @JsonSerialize(using = TwoDecimalFloatSerializer.class) | |
| 70 | + private Float treeArea; | |
| 71 | + | |
| 72 | + @Schema(description = "起点经度") | |
| 73 | + @ExcelProperty("起点经度") | |
| 74 | + private String startingLatitude; | |
| 75 | + | |
| 76 | + @Schema(description = "起点纬度") | |
| 77 | + @ExcelProperty("起点纬度") | |
| 78 | + private String startingLongitude; | |
| 79 | + | |
| 80 | + @Schema(description = "起点描述") | |
| 81 | + @ExcelProperty("起点描述") | |
| 82 | + private String startingRemark; | |
| 83 | + | |
| 84 | + @Schema(description = "终点经度") | |
| 85 | + @ExcelProperty("终点经度") | |
| 86 | + private String endLatitude; | |
| 87 | + | |
| 88 | + @Schema(description = "终点纬度") | |
| 89 | + @ExcelProperty("终点纬度") | |
| 90 | + private String endLongitude; | |
| 91 | + | |
| 92 | + @Schema(description = "终点描述") | |
| 93 | + @ExcelProperty("终点描述") | |
| 94 | + private String endRemark; | |
| 95 | + | |
| 96 | + @Schema(description = "备注") | |
| 97 | + @ExcelProperty("备注") | |
| 98 | + private String remark; | |
| 99 | + | |
| 100 | + @Schema(description = "归属公司ID") | |
| 101 | + @ExcelProperty("归属公司ID") | |
| 102 | + private Long companyId; | |
| 103 | + | |
| 104 | + @Schema(description = "部门ID") | |
| 105 | + @ExcelProperty("部门ID") | |
| 106 | + private Long deptId; | |
| 107 | + | |
| 108 | + @Schema(description = "创建者名称") | |
| 109 | + @ExcelProperty("创建者") | |
| 110 | + private String creatorName; | |
| 111 | + | |
| 112 | + @Schema(description = "创建时间") | |
| 113 | + @ExcelProperty("创建时间") | |
| 114 | + private LocalDateTime createTime; | |
| 115 | + | |
| 116 | + @Schema(description = "更新者名称") | |
| 117 | + @ExcelProperty("更新者") | |
| 118 | + private String updaterName; | |
| 119 | + | |
| 120 | + // ========== GIS 核对相关字段 ========== | |
| 121 | + | |
| 122 | + @Schema(description = "GIS 图斑编号") | |
| 123 | + @ExcelProperty("GIS图斑编号") | |
| 124 | + private String gisPlotCode; | |
| 125 | + | |
| 126 | + @Schema(description = "GIS 图斑名称") | |
| 127 | + @ExcelProperty("GIS图斑名称") | |
| 128 | + private String gisPlotName; | |
| 129 | + | |
| 130 | + @Schema(description = "GIS 图斑面积") | |
| 131 | + @ExcelProperty("GIS图斑面积") | |
| 132 | + @JsonSerialize(using = TwoDecimalFloatSerializer.class) | |
| 133 | + private Float gisPlotArea; | |
| 134 | + | |
| 135 | + @Schema(description = "GIS 绿地类型编码") | |
| 136 | + @ExcelProperty("GIS绿地类型编码") | |
| 137 | + private String gisGreenTypeCode; | |
| 138 | + | |
| 139 | + @Schema(description = "GIS 绿地类型名称") | |
| 140 | + @ExcelProperty("GIS绿地类型名称") | |
| 141 | + private String gisGreenTypeName; | |
| 142 | + | |
| 143 | + @Schema(description = "GIS 所属街道") | |
| 144 | + @ExcelProperty("GIS所属街道") | |
| 145 | + private String gisStreet; | |
| 146 | + | |
| 147 | + @Schema(description = "GIS 权属单位") | |
| 148 | + @ExcelProperty("GIS权属单位") | |
| 149 | + private String gisOwnerUnit; | |
| 150 | + | |
| 151 | + @Schema(description = "GIS 物业单位") | |
| 152 | + @ExcelProperty("GIS物业单位") | |
| 153 | + private String gisPropertyUnit; | |
| 154 | + | |
| 155 | + @Schema(description = "GIS 养护单位") | |
| 156 | + @ExcelProperty("GIS养护单位") | |
| 157 | + private String gisManageUnit; | |
| 158 | + | |
| 159 | + @Schema(description = "GIS 差异面积") | |
| 160 | + @ExcelProperty("GIS差异面积") | |
| 161 | + @JsonSerialize(using = TwoDecimalFloatSerializer.class) | |
| 162 | + private Float gisDiffArea; | |
| 163 | + | |
| 164 | + @Schema(description = "GIS 差异备注") | |
| 165 | + @ExcelProperty("GIS差异备注") | |
| 166 | + private String gisDiffRemark; | |
| 167 | + | |
| 168 | + @Schema(description = "是否已关联 (0=未关联, 1=已关联)") | |
| 169 | + @ExcelProperty("是否已关联") | |
| 170 | + private Integer gisIsRelated; | |
| 171 | + | |
| 172 | + @Schema(description = "关联人") | |
| 173 | + @ExcelProperty("关联人") | |
| 174 | + private String gisRelatedUser; | |
| 175 | + | |
| 176 | + @Schema(description = "关联时间") | |
| 177 | + @ExcelProperty("关联时间") | |
| 178 | + private LocalDateTime gisRelatedTime; | |
| 179 | + | |
| 180 | + @Schema(description = "多边形围栏边界坐标 WKT格式") | |
| 181 | + @ExcelProperty("围栏边界坐标") | |
| 182 | + private String gisPolygonCoords; | |
| 183 | + | |
| 184 | + // ========== 翻译字段 ========== | |
| 185 | + | |
| 186 | + @Schema(description = "公司名称") | |
| 187 | + private String companyName; | |
| 188 | + | |
| 189 | + @Schema(description = "部门名称") | |
| 190 | + private String deptName; | |
| 191 | + | |
| 192 | +} | ... | ... |
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/road/vo/RoadPageReqVO.java
| ... | ... | @@ -86,6 +86,9 @@ public class RoadPageReqVO extends PageParam { |
| 86 | 86 | @Schema(description = "更新者", example = "芋艿") |
| 87 | 87 | private String updaterName; |
| 88 | 88 | |
| 89 | + @Schema(description = "是否关联GIS (0=未关联, 1=已关联)") | |
| 90 | + private Integer gisIsRelated; | |
| 91 | + | |
| 89 | 92 | //对应前端排序字段 |
| 90 | 93 | public static final Map<String, SFunction<RoadDO, ?>> SORT_FIELD_MAP = Map.of( |
| 91 | 94 | "id", RoadDO::getId, | ... | ... |
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/road/vo/RoadRespVO.java
| ... | ... | @@ -122,4 +122,17 @@ public class RoadRespVO { |
| 122 | 122 | @ExcelProperty("部门(道路负责部门)") |
| 123 | 123 | private String deptName; |
| 124 | 124 | |
| 125 | + @Schema(description = "GIS 图斑名称") | |
| 126 | + @ExcelProperty("GIS图斑名称") | |
| 127 | + private String gisPlotName; | |
| 128 | + | |
| 129 | + @Schema(description = "GIS 图斑面积") | |
| 130 | + @ExcelProperty("GIS图斑面积") | |
| 131 | + @JsonSerialize(using = TwoDecimalFloatSerializer.class) | |
| 132 | + private Float gisPlotArea; | |
| 133 | + | |
| 134 | + @Schema(description = "是否关联GIS (0=未关联, 1=已关联)") | |
| 135 | + @ExcelProperty("是否关联GIS") | |
| 136 | + private Integer gisIsRelated; | |
| 137 | + | |
| 125 | 138 | } |
| 126 | 139 | \ No newline at end of file | ... | ... |
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/app/problemtype/AppProblemTypeController.java
| 1 | 1 | package com.zteits.urbanops.module.garden.controller.app.problemtype; |
| 2 | 2 | |
| 3 | 3 | import com.zteits.urbanops.framework.common.pojo.CommonResult; |
| 4 | -import com.zteits.urbanops.framework.common.util.object.BeanUtils; | |
| 5 | -import com.zteits.urbanops.module.garden.controller.admin.problemtype.vo.ProblemTypeRespVO; | |
| 6 | -import com.zteits.urbanops.module.garden.dal.dataobject.problemtype.GardenProblemTypeDO; | |
| 4 | +import com.zteits.urbanops.module.garden.controller.app.problemtype.vo.ProblemTypeCascadeRespVO; | |
| 7 | 5 | import com.zteits.urbanops.module.garden.service.problemtype.GardenProblemTypeService; |
| 8 | 6 | import io.swagger.v3.oas.annotations.Operation; |
| 9 | 7 | import io.swagger.v3.oas.annotations.tags.Tag; |
| ... | ... | @@ -28,11 +26,9 @@ public class AppProblemTypeController { |
| 28 | 26 | private GardenProblemTypeService gardenProblemTypeService; |
| 29 | 27 | |
| 30 | 28 | @GetMapping("/list-by-parent") |
| 31 | - @Operation(summary = "根据父级编码和层级查询子类型列表(级联下拉用)") | |
| 32 | - public CommonResult<List<ProblemTypeRespVO>> getProblemTypeListByParent( | |
| 33 | - @RequestParam(value = "parentCode", required = false) String parentCode, | |
| 34 | - @RequestParam("level") Integer level) { | |
| 35 | - List<GardenProblemTypeDO> list = gardenProblemTypeService.getProblemTypeByParentCode(parentCode, level); | |
| 36 | - return success(BeanUtils.toBean(list, ProblemTypeRespVO.class)); | |
| 29 | + @Operation(summary = "查询问题类型级联列表(级联下拉用)") | |
| 30 | + public CommonResult<List<ProblemTypeCascadeRespVO>> getProblemTypeListByParent( | |
| 31 | + @RequestParam(value = "parentCode", required = false) String parentCode) { | |
| 32 | + return success(gardenProblemTypeService.getProblemTypeTree(parentCode)); | |
| 37 | 33 | } |
| 38 | 34 | } | ... | ... |
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/app/problemtype/vo/ProblemTypeCascadeRespVO.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.garden.controller.app.problemtype.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +import java.util.ArrayList; | |
| 7 | +import java.util.List; | |
| 8 | + | |
| 9 | +@Schema(description = "APP - 问题类型级联响应 VO") | |
| 10 | +@Data | |
| 11 | +public class ProblemTypeCascadeRespVO { | |
| 12 | + | |
| 13 | + @Schema(description = "问题类型名称", example = "树木倒伏") | |
| 14 | + private String label; | |
| 15 | + | |
| 16 | + @Schema(description = "问题类型编码", example = "PT001") | |
| 17 | + private String value; | |
| 18 | + | |
| 19 | + @Schema(description = "子类型列表") | |
| 20 | + private List<ProblemTypeCascadeRespVO> children = new ArrayList<>(); | |
| 21 | + | |
| 22 | +} | |
| 0 | 23 | \ No newline at end of file | ... | ... |
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/dal/dataobject/gis/GisRoadDO.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.garden.dal.dataobject.gis; | |
| 2 | + | |
| 3 | +import com.fasterxml.jackson.databind.annotation.JsonSerialize; | |
| 4 | +import com.zteits.urbanops.module.garden.util.TwoDecimalFloatSerializer; | |
| 5 | +import lombok.*; | |
| 6 | +import com.baomidou.mybatisplus.annotation.*; | |
| 7 | +import com.zteits.urbanops.framework.mybatis.core.dataobject.BaseDO; | |
| 8 | + | |
| 9 | +/** | |
| 10 | + * GIS 道路核对 DO - garden_gis_road 表 | |
| 11 | + * | |
| 12 | + * @author UrbanOps | |
| 13 | + */ | |
| 14 | +@TableName("garden_gis_road") | |
| 15 | +@KeySequence("garden_gis_road_seq") | |
| 16 | +@Data | |
| 17 | +@EqualsAndHashCode(callSuper = true) | |
| 18 | +@ToString(callSuper = true) | |
| 19 | +@Builder | |
| 20 | +@NoArgsConstructor | |
| 21 | +@AllArgsConstructor | |
| 22 | +public class GisRoadDO extends BaseDO { | |
| 23 | + | |
| 24 | + @TableId | |
| 25 | + private Long id; | |
| 26 | + | |
| 27 | + /** 关联道路ID */ | |
| 28 | + private Long gisRoadId; | |
| 29 | + | |
| 30 | + /** 道路名称 */ | |
| 31 | + private String roadName; | |
| 32 | + | |
| 33 | + /** 业务线:yl-园林;wy-物业;sz-市政 */ | |
| 34 | + private String busiLine; | |
| 35 | + | |
| 36 | + /** 街道ID */ | |
| 37 | + private String streetId; | |
| 38 | + | |
| 39 | + /** 街道名称 */ | |
| 40 | + private String streetName; | |
| 41 | + | |
| 42 | + /** 养护级别: 10:特级;11:一级:12:二级;13:三级 */ | |
| 43 | + private Integer levelId; | |
| 44 | + | |
| 45 | + /** 道路属性 */ | |
| 46 | + private String roadAttr; | |
| 47 | + | |
| 48 | + /** 道路方位 W-西 E-东 N-北 S-南 */ | |
| 49 | + private String direction; | |
| 50 | + | |
| 51 | + /** 行道树绿化面积 */ | |
| 52 | + @JsonSerialize(using = TwoDecimalFloatSerializer.class) | |
| 53 | + private Float greenBeltTreeArea; | |
| 54 | + | |
| 55 | + /** 绿化带面积 */ | |
| 56 | + @JsonSerialize(using = TwoDecimalFloatSerializer.class) | |
| 57 | + private Float greenBeltArea; | |
| 58 | + | |
| 59 | + /** 总绿化带面积 */ | |
| 60 | + @JsonSerialize(using = TwoDecimalFloatSerializer.class) | |
| 61 | + private Float totalArea; | |
| 62 | + | |
| 63 | + /** 行道树(棵)面积 */ | |
| 64 | + @JsonSerialize(using = TwoDecimalFloatSerializer.class) | |
| 65 | + private Float treeArea; | |
| 66 | + | |
| 67 | + /** 起点经度 */ | |
| 68 | + private String startingLatitude; | |
| 69 | + | |
| 70 | + /** 起点纬度 */ | |
| 71 | + private String startingLongitude; | |
| 72 | + | |
| 73 | + /** 起点描述 */ | |
| 74 | + private String startingRemark; | |
| 75 | + | |
| 76 | + /** 终点经度 */ | |
| 77 | + private String endLatitude; | |
| 78 | + | |
| 79 | + /** 终点纬度 */ | |
| 80 | + private String endLongitude; | |
| 81 | + | |
| 82 | + /** 终点描述 */ | |
| 83 | + private String endRemark; | |
| 84 | + | |
| 85 | + /** 备注 */ | |
| 86 | + private String remark; | |
| 87 | + | |
| 88 | + /** 归属公司ID */ | |
| 89 | + private Long companyId; | |
| 90 | + | |
| 91 | + /** 部门ID */ | |
| 92 | + private Long deptId; | |
| 93 | + | |
| 94 | + /** 创建者名称 */ | |
| 95 | + private String creatorName; | |
| 96 | + | |
| 97 | + /** 更新者名称 */ | |
| 98 | + private String updaterName; | |
| 99 | + | |
| 100 | + // ========== GIS 核对相关字段 ========== | |
| 101 | + | |
| 102 | + /** GIS 图斑编号 */ | |
| 103 | + private String gisPlotCode; | |
| 104 | + | |
| 105 | + /** GIS 图斑名称 */ | |
| 106 | + private String gisPlotName; | |
| 107 | + | |
| 108 | + /** GIS 图斑面积 */ | |
| 109 | + @JsonSerialize(using = TwoDecimalFloatSerializer.class) | |
| 110 | + private Float gisPlotArea; | |
| 111 | + | |
| 112 | + /** GIS 绿地类型编码 */ | |
| 113 | + private String gisGreenTypeCode; | |
| 114 | + | |
| 115 | + /** GIS 绿地类型名称 */ | |
| 116 | + private String gisGreenTypeName; | |
| 117 | + | |
| 118 | + /** GIS 所属街道 */ | |
| 119 | + private String gisStreet; | |
| 120 | + | |
| 121 | + /** GIS 权属单位 */ | |
| 122 | + private String gisOwnerUnit; | |
| 123 | + | |
| 124 | + /** GIS 物业单位 */ | |
| 125 | + private String gisPropertyUnit; | |
| 126 | + | |
| 127 | + /** GIS 养护单位 */ | |
| 128 | + private String gisManageUnit; | |
| 129 | + | |
| 130 | + /** GIS 差异面积 */ | |
| 131 | + @JsonSerialize(using = TwoDecimalFloatSerializer.class) | |
| 132 | + private Float gisDiffArea; | |
| 133 | + | |
| 134 | + /** GIS 差异备注 */ | |
| 135 | + private String gisDiffRemark; | |
| 136 | + | |
| 137 | + /** 是否已关联 (0=未关联, 1=已关联) */ | |
| 138 | + private Integer gisIsRelated; | |
| 139 | + | |
| 140 | + /** 关联人 */ | |
| 141 | + private String gisRelatedUser; | |
| 142 | + | |
| 143 | + /** 关联时间 */ | |
| 144 | + private java.time.LocalDateTime gisRelatedTime; | |
| 145 | + | |
| 146 | + /** 多边形围栏边界坐标 WKT格式: POLYGON((x1 y1, x2 y2, ...)) WGS84坐标系 */ | |
| 147 | + private String gisPolygonCoords; | |
| 148 | + | |
| 149 | +} | ... | ... |
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/dal/dataobject/gis/GisTypeDO.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.garden.dal.dataobject.gis; | |
| 2 | + | |
| 3 | +import lombok.*; | |
| 4 | +import com.baomidou.mybatisplus.annotation.*; | |
| 5 | +import com.zteits.urbanops.framework.mybatis.core.dataobject.BaseDO; | |
| 6 | + | |
| 7 | +/** | |
| 8 | + * 公园绿地类型字典 DO | |
| 9 | + */ | |
| 10 | +@TableName("garden_gis_type") | |
| 11 | +@Data | |
| 12 | +@EqualsAndHashCode(callSuper = true) | |
| 13 | +@ToString(callSuper = true) | |
| 14 | +@Builder | |
| 15 | +@NoArgsConstructor | |
| 16 | +@AllArgsConstructor | |
| 17 | +public class GisTypeDO extends BaseDO { | |
| 18 | + | |
| 19 | + @TableId | |
| 20 | + private Long id; | |
| 21 | + | |
| 22 | + /** 类型编码 */ | |
| 23 | + private String typeCode; | |
| 24 | + | |
| 25 | + /** 类型名称 */ | |
| 26 | + private String typeName; | |
| 27 | + | |
| 28 | + /** 父级编码 */ | |
| 29 | + private String parentCode; | |
| 30 | + | |
| 31 | + /** 层级 */ | |
| 32 | + private Integer level; | |
| 33 | + | |
| 34 | + /** 排序 */ | |
| 35 | + private Integer sort; | |
| 36 | + | |
| 37 | +} | ... | ... |
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/dal/dataobject/road/RoadDO.java
| ... | ... | @@ -122,5 +122,16 @@ public class RoadDO extends BaseDO { |
| 122 | 122 | */ |
| 123 | 123 | private String updaterName; |
| 124 | 124 | |
| 125 | + // ========== GIS 关联字段 ========== | |
| 126 | + | |
| 127 | + /** GIS 图斑名称 */ | |
| 128 | + private String gisPlotName; | |
| 129 | + | |
| 130 | + /** GIS 图斑面积 */ | |
| 131 | + @JsonSerialize(using = TwoDecimalFloatSerializer.class) | |
| 132 | + private Float gisPlotArea; | |
| 133 | + | |
| 134 | + /** 是否关联GIS (0=未关联, 1=已关联) */ | |
| 135 | + private Integer gisIsRelated; | |
| 125 | 136 | |
| 126 | 137 | } |
| 127 | 138 | \ No newline at end of file | ... | ... |
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/dal/mysql/gis/GisRoadMapper.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.garden.dal.mysql.gis; | |
| 2 | + | |
| 3 | +import com.zteits.urbanops.framework.common.pojo.PageResult; | |
| 4 | +import com.zteits.urbanops.framework.mybatis.core.query.LambdaQueryWrapperX; | |
| 5 | +import com.zteits.urbanops.framework.mybatis.core.mapper.BaseMapperX; | |
| 6 | +import com.zteits.urbanops.module.garden.dal.dataobject.gis.GisRoadDO; | |
| 7 | +import com.zteits.urbanops.module.garden.controller.admin.gis.vo.*; | |
| 8 | +import org.apache.ibatis.annotations.Mapper; | |
| 9 | +import java.util.*; | |
| 10 | + | |
| 11 | +/** | |
| 12 | + * GIS 道路核对 Mapper | |
| 13 | + */ | |
| 14 | +@Mapper | |
| 15 | +public interface GisRoadMapper extends BaseMapperX<GisRoadDO> { | |
| 16 | + | |
| 17 | + /** XG 大类的子代码(不以 XG 开头,需特殊处理) */ | |
| 18 | + List<String> XG_CHILD_CODES = Arrays.asList("RG", "AG", "BG", "MG", "WG", "SG", "UG"); | |
| 19 | + | |
| 20 | + /** 构建公共查询条件 */ | |
| 21 | + private LambdaQueryWrapperX<GisRoadDO> buildQueryWrapper(GisRoadPageReqVO reqVO) { | |
| 22 | + LambdaQueryWrapperX<GisRoadDO> wrapper = new LambdaQueryWrapperX<GisRoadDO>() | |
| 23 | + .likeIfPresent(GisRoadDO::getRoadName, reqVO.getRoadName()) | |
| 24 | + .eqIfPresent(GisRoadDO::getBusiLine, reqVO.getBusiLine()) | |
| 25 | + .eqIfPresent(GisRoadDO::getStreetId, reqVO.getStreetId()) | |
| 26 | + .likeIfPresent(GisRoadDO::getStreetName, reqVO.getStreetName()) | |
| 27 | + .eqIfPresent(GisRoadDO::getLevelId, reqVO.getLevelId()) | |
| 28 | + .eqIfPresent(GisRoadDO::getRoadAttr, reqVO.getRoadAttr()) | |
| 29 | + .eqIfPresent(GisRoadDO::getDirection, reqVO.getDirection()) | |
| 30 | + .likeIfPresent(GisRoadDO::getGisPlotCode, reqVO.getGisPlotCode()) | |
| 31 | + .likeIfPresent(GisRoadDO::getGisPlotName, reqVO.getGisPlotName()) | |
| 32 | + .eqIfPresent(GisRoadDO::getGisIsRelated, reqVO.getGisIsRelated()) | |
| 33 | + .eqIfPresent(GisRoadDO::getCompanyId, reqVO.getCompanyId()) | |
| 34 | + .eqIfPresent(GisRoadDO::getDeptId, reqVO.getDeptId()); | |
| 35 | + | |
| 36 | + if (reqVO.getGisTypeCode() != null) { | |
| 37 | + if ("XG".equals(reqVO.getGisTypeCode())) { | |
| 38 | + wrapper.in(GisRoadDO::getGisGreenTypeCode, XG_CHILD_CODES); | |
| 39 | + } else { | |
| 40 | + wrapper.likeRight(GisRoadDO::getGisGreenTypeCode, reqVO.getGisTypeCode()); | |
| 41 | + } | |
| 42 | + } | |
| 43 | + return wrapper.orderByDesc(GisRoadDO::getId); | |
| 44 | + } | |
| 45 | + | |
| 46 | + default PageResult<GisRoadDO> selectPage(GisRoadPageReqVO reqVO) { | |
| 47 | + return selectPage(reqVO, buildQueryWrapper(reqVO)); | |
| 48 | + } | |
| 49 | + | |
| 50 | + /** 不分页查询全部记录(用于地图渲染) */ | |
| 51 | + default List<GisRoadDO> selectAllList(GisRoadPageReqVO reqVO) { | |
| 52 | + return selectList(buildQueryWrapper(reqVO)); | |
| 53 | + } | |
| 54 | + | |
| 55 | +} | ... | ... |
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/dal/mysql/gis/GisTypeMapper.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.garden.dal.mysql.gis; | |
| 2 | + | |
| 3 | +import com.zteits.urbanops.framework.mybatis.core.mapper.BaseMapperX; | |
| 4 | +import com.zteits.urbanops.module.garden.dal.dataobject.gis.GisTypeDO; | |
| 5 | +import org.apache.ibatis.annotations.Mapper; | |
| 6 | + | |
| 7 | +@Mapper | |
| 8 | +public interface GisTypeMapper extends BaseMapperX<GisTypeDO> { | |
| 9 | +} | ... | ... |
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/dal/mysql/road/RoadMapper.java
| ... | ... | @@ -42,6 +42,7 @@ public interface RoadMapper extends BaseMapperX<RoadDO> { |
| 42 | 42 | .likeIfPresent(RoadDO::getCreatorName, reqVO.getCreatorName()) |
| 43 | 43 | .betweenIfPresent(RoadDO::getCreateTime, reqVO.getCreateTime()) |
| 44 | 44 | .likeIfPresent(RoadDO::getUpdaterName, reqVO.getUpdaterName()) |
| 45 | + .eqIfPresent(RoadDO::getGisIsRelated, reqVO.getGisIsRelated()) | |
| 45 | 46 | .orderByDesc(RoadDO::getId)); |
| 46 | 47 | } |
| 47 | 48 | ... | ... |
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/gis/GisRoadService.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.garden.service.gis; | |
| 2 | + | |
| 3 | +import java.util.*; | |
| 4 | +import com.zteits.urbanops.module.garden.controller.admin.gis.vo.*; | |
| 5 | +import com.zteits.urbanops.framework.common.pojo.PageResult; | |
| 6 | + | |
| 7 | +/** | |
| 8 | + * GIS 道路 Service 接口 | |
| 9 | + * | |
| 10 | + * @author UrbanOps | |
| 11 | + */ | |
| 12 | +public interface GisRoadService { | |
| 13 | + | |
| 14 | + /** | |
| 15 | + * 获得 GIS 道路分页 | |
| 16 | + * | |
| 17 | + * @param pageReqVO 分页查询 | |
| 18 | + * @return GIS 道路分页 | |
| 19 | + */ | |
| 20 | + PageResult<GisRoadRespVO> getGisRoadPage(GisRoadPageReqVO pageReqVO); | |
| 21 | + | |
| 22 | + /** | |
| 23 | + * 获得 GIS 道路 | |
| 24 | + * | |
| 25 | + * @param id 编号 | |
| 26 | + * @return GIS 道路 | |
| 27 | + */ | |
| 28 | + GisRoadRespVO getGisRoad(Long id); | |
| 29 | + | |
| 30 | + /** | |
| 31 | + * 获得全部 GIS 道路列表(不分页,用于地图渲染) | |
| 32 | + * | |
| 33 | + * @param reqVO 查询条件 | |
| 34 | + * @return GIS 道路列表 | |
| 35 | + */ | |
| 36 | + List<GisRoadRespVO> getGisRoadAllList(GisRoadPageReqVO reqVO); | |
| 37 | + | |
| 38 | + /** | |
| 39 | + * 关联道路到 GIS 图斑 | |
| 40 | + * | |
| 41 | + * @param reqVO 关联请求 | |
| 42 | + */ | |
| 43 | + void associateRoad(GisRoadAssociateReqVO reqVO); | |
| 44 | + | |
| 45 | + /** | |
| 46 | + * 取消 GIS 图斑与道路的关联 | |
| 47 | + * | |
| 48 | + * @param reqVO 取消关联请求 | |
| 49 | + */ | |
| 50 | + void disassociateRoad(GisRoadDisassociateReqVO reqVO); | |
| 51 | + | |
| 52 | + /** | |
| 53 | + * 将 garden_gis_road 表中所有坐标从 WGS84 转换为 GCJ02(高德坐标系) | |
| 54 | + * | |
| 55 | + * @return 转换记录数 | |
| 56 | + */ | |
| 57 | + int convertCoordinatesToGcj02(); | |
| 58 | + | |
| 59 | +} | ... | ... |
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/gis/GisRoadServiceImpl.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.garden.service.gis; | |
| 2 | + | |
| 3 | +import com.zteits.urbanops.module.system.api.dept.DeptApi; | |
| 4 | +import com.zteits.urbanops.module.system.api.dept.dto.DeptRespDTO; | |
| 5 | +import org.springframework.stereotype.Service; | |
| 6 | +import org.springframework.transaction.annotation.Transactional; | |
| 7 | +import jakarta.annotation.Resource; | |
| 8 | +import org.springframework.util.CollectionUtils; | |
| 9 | +import org.springframework.validation.annotation.Validated; | |
| 10 | + | |
| 11 | +import java.time.LocalDateTime; | |
| 12 | +import java.util.*; | |
| 13 | +import java.util.stream.Collectors; | |
| 14 | + | |
| 15 | +import com.zteits.urbanops.module.garden.controller.admin.gis.vo.*; | |
| 16 | +import com.zteits.urbanops.module.garden.dal.dataobject.gis.GisRoadDO; | |
| 17 | +import com.zteits.urbanops.module.garden.dal.dataobject.road.RoadDO; | |
| 18 | +import com.zteits.urbanops.framework.common.pojo.PageResult; | |
| 19 | +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; | |
| 20 | +import com.zteits.urbanops.framework.common.util.object.BeanUtils; | |
| 21 | +import com.zteits.urbanops.framework.security.core.util.SecurityFrameworkUtils; | |
| 22 | +import com.zteits.urbanops.module.garden.dal.mysql.gis.GisRoadMapper; | |
| 23 | +import com.zteits.urbanops.module.garden.dal.mysql.road.RoadMapper; | |
| 24 | +import com.zteits.urbanops.module.garden.util.GisCoordinateUtil; | |
| 25 | + | |
| 26 | +/** | |
| 27 | + * GIS 道路 Service 实现类 | |
| 28 | + * | |
| 29 | + * @author UrbanOps | |
| 30 | + */ | |
| 31 | +@Service | |
| 32 | +@Validated | |
| 33 | +public class GisRoadServiceImpl implements GisRoadService { | |
| 34 | + | |
| 35 | + @Resource | |
| 36 | + private GisRoadMapper gisRoadMapper; | |
| 37 | + | |
| 38 | + @Resource | |
| 39 | + private RoadMapper roadMapper; | |
| 40 | + | |
| 41 | + @Resource | |
| 42 | + private DeptApi deptApi; | |
| 43 | + | |
| 44 | + @Override | |
| 45 | + public PageResult<GisRoadRespVO> getGisRoadPage(GisRoadPageReqVO pageReqVO) { | |
| 46 | + PageResult<GisRoadDO> pageResult = gisRoadMapper.selectPage(pageReqVO); | |
| 47 | + PageResult<GisRoadRespVO> page = BeanUtils.toBean(pageResult, GisRoadRespVO.class); | |
| 48 | + fillDeptAndCompanyName(page.getList()); | |
| 49 | + return page; | |
| 50 | + } | |
| 51 | + | |
| 52 | + @Override | |
| 53 | + public GisRoadRespVO getGisRoad(Long id) { | |
| 54 | + GisRoadDO gisRoadDO = gisRoadMapper.selectById(id); | |
| 55 | + if (gisRoadDO == null) { | |
| 56 | + return null; | |
| 57 | + } | |
| 58 | + GisRoadRespVO respVO = BeanUtils.toBean(gisRoadDO, GisRoadRespVO.class); | |
| 59 | + List<GisRoadRespVO> list = new ArrayList<>(); | |
| 60 | + list.add(respVO); | |
| 61 | + fillDeptAndCompanyName(list); | |
| 62 | + return respVO; | |
| 63 | + } | |
| 64 | + | |
| 65 | + @Override | |
| 66 | + public List<GisRoadRespVO> getGisRoadAllList(GisRoadPageReqVO reqVO) { | |
| 67 | + List<GisRoadDO> list = gisRoadMapper.selectAllList(reqVO); | |
| 68 | + List<GisRoadRespVO> voList = BeanUtils.toBean(list, GisRoadRespVO.class); | |
| 69 | + fillDeptAndCompanyName(voList); | |
| 70 | + return voList; | |
| 71 | + } | |
| 72 | + | |
| 73 | + @Override | |
| 74 | + @Transactional(rollbackFor = Exception.class) | |
| 75 | + public void associateRoad(GisRoadAssociateReqVO reqVO) { | |
| 76 | + GisRoadDO gisRoad = gisRoadMapper.selectById(reqVO.getId()); | |
| 77 | + if (gisRoad == null) { | |
| 78 | + throw new IllegalArgumentException("GIS道路不存在: id=" + reqVO.getId()); | |
| 79 | + } | |
| 80 | + RoadDO road = roadMapper.selectById(reqVO.getRoadId()); | |
| 81 | + if (road == null) { | |
| 82 | + throw new IllegalArgumentException("道路不存在: id=" + reqVO.getRoadId()); | |
| 83 | + } | |
| 84 | + | |
| 85 | + gisRoad.setGisRoadId(road.getId()); | |
| 86 | + gisRoad.setRoadName(road.getRoadName()); | |
| 87 | + gisRoad.setTotalArea(road.getTotalArea()); | |
| 88 | + gisRoad.setCompanyId(road.getCompanyId()); | |
| 89 | + gisRoad.setDeptId(road.getDeptId()); | |
| 90 | + | |
| 91 | + // 差异面积 = GIS面积 - 道路面积 | |
| 92 | + float gisArea = gisRoad.getGisPlotArea() != null ? gisRoad.getGisPlotArea() : 0; | |
| 93 | + float roadArea = road.getTotalArea() != null ? road.getTotalArea() : 0; | |
| 94 | + gisRoad.setGisDiffArea(gisArea - roadArea); | |
| 95 | + | |
| 96 | + gisRoad.setGisIsRelated(1); | |
| 97 | + gisRoad.setGisRelatedUser(SecurityFrameworkUtils.getLoginUserNickname()); | |
| 98 | + gisRoad.setGisRelatedTime(LocalDateTime.now()); | |
| 99 | + | |
| 100 | + gisRoadMapper.updateById(gisRoad); | |
| 101 | + | |
| 102 | + // 同步更新 garden_road 的 GIS 关联字段 | |
| 103 | + road.setGisPlotName(gisRoad.getGisPlotName()); | |
| 104 | + road.setGisPlotArea(gisRoad.getGisPlotArea()); | |
| 105 | + road.setGisIsRelated(1); | |
| 106 | + roadMapper.updateById(road); | |
| 107 | + } | |
| 108 | + | |
| 109 | + @Override | |
| 110 | + @Transactional(rollbackFor = Exception.class) | |
| 111 | + public void disassociateRoad(GisRoadDisassociateReqVO reqVO) { | |
| 112 | + GisRoadDO gisRoad = gisRoadMapper.selectById(reqVO.getId()); | |
| 113 | + if (gisRoad == null) { | |
| 114 | + throw new IllegalArgumentException("GIS道路不存在: id=" + reqVO.getId()); | |
| 115 | + } | |
| 116 | + | |
| 117 | + // 同步清空 garden_road 的 GIS 关联字段(LambdaUpdateWrapper 才能将字段设为 null) | |
| 118 | + if (gisRoad.getGisRoadId() != null) { | |
| 119 | + roadMapper.update(null, | |
| 120 | + new LambdaUpdateWrapper<RoadDO>() | |
| 121 | + .eq(RoadDO::getId, gisRoad.getGisRoadId()) | |
| 122 | + .set(RoadDO::getGisPlotName, null) | |
| 123 | + .set(RoadDO::getGisPlotArea, null) | |
| 124 | + .set(RoadDO::getGisIsRelated, 0)); | |
| 125 | + } | |
| 126 | + | |
| 127 | + // 清空 garden_gis_road 的道路关联信息 | |
| 128 | + gisRoadMapper.update(null, | |
| 129 | + new LambdaUpdateWrapper<GisRoadDO>() | |
| 130 | + .eq(GisRoadDO::getId, gisRoad.getId()) | |
| 131 | + .set(GisRoadDO::getGisRoadId, null) | |
| 132 | + .set(GisRoadDO::getRoadName, null) | |
| 133 | + .set(GisRoadDO::getTotalArea, null) | |
| 134 | + .set(GisRoadDO::getCompanyId, null) | |
| 135 | + .set(GisRoadDO::getDeptId, null) | |
| 136 | + .set(GisRoadDO::getGisDiffArea, null) | |
| 137 | + .set(GisRoadDO::getGisIsRelated, 0) | |
| 138 | + .set(GisRoadDO::getGisRelatedUser, null) | |
| 139 | + .set(GisRoadDO::getGisRelatedTime, null)); | |
| 140 | + } | |
| 141 | + | |
| 142 | + @Override | |
| 143 | + @Transactional(rollbackFor = Exception.class) | |
| 144 | + public int convertCoordinatesToGcj02() { | |
| 145 | + List<GisRoadDO> all = gisRoadMapper.selectList(); | |
| 146 | + if (CollectionUtils.isEmpty(all)) { | |
| 147 | + return 0; | |
| 148 | + } | |
| 149 | + int count = 0; | |
| 150 | + for (GisRoadDO road : all) { | |
| 151 | + boolean updated = false; | |
| 152 | + | |
| 153 | + // 转换起点坐标 | |
| 154 | + if (road.getStartingLatitude() != null && !road.getStartingLatitude().isBlank() | |
| 155 | + && road.getStartingLongitude() != null && !road.getStartingLongitude().isBlank()) { | |
| 156 | + try { | |
| 157 | + double lng = Double.parseDouble(road.getStartingLongitude()); | |
| 158 | + double lat = Double.parseDouble(road.getStartingLatitude()); | |
| 159 | + double[] gcj = GisCoordinateUtil.wgs84ToGcj02(lng, lat); | |
| 160 | + road.setStartingLongitude(String.format("%.6f", gcj[0])); | |
| 161 | + road.setStartingLatitude(String.format("%.6f", gcj[1])); | |
| 162 | + updated = true; | |
| 163 | + } catch (NumberFormatException ignored) { | |
| 164 | + } | |
| 165 | + } | |
| 166 | + | |
| 167 | + // 转换终点坐标 | |
| 168 | + if (road.getEndLatitude() != null && !road.getEndLatitude().isBlank() | |
| 169 | + && road.getEndLongitude() != null && !road.getEndLongitude().isBlank()) { | |
| 170 | + try { | |
| 171 | + double lng = Double.parseDouble(road.getEndLongitude()); | |
| 172 | + double lat = Double.parseDouble(road.getEndLatitude()); | |
| 173 | + double[] gcj = GisCoordinateUtil.wgs84ToGcj02(lng, lat); | |
| 174 | + road.setEndLongitude(String.format("%.6f", gcj[0])); | |
| 175 | + road.setEndLatitude(String.format("%.6f", gcj[1])); | |
| 176 | + updated = true; | |
| 177 | + } catch (NumberFormatException ignored) { | |
| 178 | + } | |
| 179 | + } | |
| 180 | + | |
| 181 | + // 转换多边形围栏坐标 | |
| 182 | + if (road.getGisPolygonCoords() != null && !road.getGisPolygonCoords().isBlank()) { | |
| 183 | + String converted = GisCoordinateUtil.transformPolygonWkt(road.getGisPolygonCoords()); | |
| 184 | + if (!converted.equals(road.getGisPolygonCoords())) { | |
| 185 | + road.setGisPolygonCoords(converted); | |
| 186 | + updated = true; | |
| 187 | + } | |
| 188 | + } | |
| 189 | + | |
| 190 | + if (updated) { | |
| 191 | + gisRoadMapper.updateById(road); | |
| 192 | + count++; | |
| 193 | + } | |
| 194 | + } | |
| 195 | + return count; | |
| 196 | + } | |
| 197 | + | |
| 198 | + /** | |
| 199 | + * 回填部门/公司名称 | |
| 200 | + */ | |
| 201 | + private void fillDeptAndCompanyName(List<GisRoadRespVO> list) { | |
| 202 | + if (CollectionUtils.isEmpty(list)) { | |
| 203 | + return; | |
| 204 | + } | |
| 205 | + // 收集所有公司ID和部门ID | |
| 206 | + Set<Long> companyIds = list.stream() | |
| 207 | + .map(GisRoadRespVO::getCompanyId) | |
| 208 | + .filter(Objects::nonNull) | |
| 209 | + .collect(Collectors.toSet()); | |
| 210 | + Set<Long> deptIds = list.stream() | |
| 211 | + .map(GisRoadRespVO::getDeptId) | |
| 212 | + .filter(Objects::nonNull) | |
| 213 | + .collect(Collectors.toSet()); | |
| 214 | + deptIds.addAll(companyIds); | |
| 215 | + | |
| 216 | + if (deptIds.isEmpty()) { | |
| 217 | + return; | |
| 218 | + } | |
| 219 | + | |
| 220 | + Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(deptIds); | |
| 221 | + | |
| 222 | + list.forEach(vo -> { | |
| 223 | + if (vo.getDeptId() != null) { | |
| 224 | + DeptRespDTO dept = deptMap.get(vo.getDeptId()); | |
| 225 | + if (dept != null) { | |
| 226 | + vo.setDeptName(dept.getName()); | |
| 227 | + } | |
| 228 | + } | |
| 229 | + if (vo.getCompanyId() != null) { | |
| 230 | + DeptRespDTO company = deptMap.get(vo.getCompanyId()); | |
| 231 | + if (company != null) { | |
| 232 | + vo.setCompanyName(company.getName()); | |
| 233 | + } | |
| 234 | + } | |
| 235 | + }); | |
| 236 | + } | |
| 237 | + | |
| 238 | +} | ... | ... |
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/gis/GisTypeService.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.garden.service.gis; | |
| 2 | + | |
| 3 | +import java.util.List; | |
| 4 | +import com.zteits.urbanops.module.garden.dal.dataobject.gis.GisTypeDO; | |
| 5 | + | |
| 6 | +public interface GisTypeService { | |
| 7 | + | |
| 8 | + /** 按父级编码查子类型列表 */ | |
| 9 | + List<GisTypeDO> listByParentCode(String parentCode); | |
| 10 | + | |
| 11 | + /** 按层级查类型列表 */ | |
| 12 | + List<GisTypeDO> listByLevel(Integer level); | |
| 13 | +} | ... | ... |
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/gis/GisTypeServiceImpl.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.garden.service.gis; | |
| 2 | + | |
| 3 | +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | |
| 4 | +import com.zteits.urbanops.module.garden.dal.dataobject.gis.GisTypeDO; | |
| 5 | +import com.zteits.urbanops.module.garden.dal.mysql.gis.GisTypeMapper; | |
| 6 | +import org.springframework.stereotype.Service; | |
| 7 | +import jakarta.annotation.Resource; | |
| 8 | +import java.util.List; | |
| 9 | + | |
| 10 | +@Service | |
| 11 | +public class GisTypeServiceImpl implements GisTypeService { | |
| 12 | + | |
| 13 | + @Resource | |
| 14 | + private GisTypeMapper gisTypeMapper; | |
| 15 | + | |
| 16 | + @Override | |
| 17 | + public List<GisTypeDO> listByParentCode(String parentCode) { | |
| 18 | + return gisTypeMapper.selectList( | |
| 19 | + new LambdaQueryWrapper<GisTypeDO>() | |
| 20 | + .eq(GisTypeDO::getParentCode, parentCode == null ? "" : parentCode) | |
| 21 | + .orderByAsc(GisTypeDO::getSort) | |
| 22 | + ); | |
| 23 | + } | |
| 24 | + | |
| 25 | + @Override | |
| 26 | + public List<GisTypeDO> listByLevel(Integer level) { | |
| 27 | + return gisTypeMapper.selectList( | |
| 28 | + new LambdaQueryWrapper<GisTypeDO>() | |
| 29 | + .eq(GisTypeDO::getLevel, level) | |
| 30 | + .orderByAsc(GisTypeDO::getSort) | |
| 31 | + ); | |
| 32 | + } | |
| 33 | +} | ... | ... |
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/problemtype/GardenProblemTypeService.java
| ... | ... | @@ -2,7 +2,9 @@ package com.zteits.urbanops.module.garden.service.problemtype; |
| 2 | 2 | |
| 3 | 3 | import com.zteits.urbanops.module.garden.controller.admin.problemtype.vo.ProblemTypePageReqVO; |
| 4 | 4 | import com.zteits.urbanops.module.garden.controller.admin.problemtype.vo.ProblemTypeSaveReqVO; |
| 5 | +import com.zteits.urbanops.module.garden.controller.app.problemtype.vo.ProblemTypeCascadeRespVO; | |
| 5 | 6 | import com.zteits.urbanops.module.garden.dal.dataobject.problemtype.GardenProblemTypeDO; |
| 7 | +import com.zteits.urbanops.framework.common.pojo.CommonResult; | |
| 6 | 8 | import com.zteits.urbanops.framework.common.pojo.PageResult; |
| 7 | 9 | import jakarta.validation.Valid; |
| 8 | 10 | |
| ... | ... | @@ -63,6 +65,14 @@ public interface GardenProblemTypeService { |
| 63 | 65 | List<GardenProblemTypeDO> getProblemTypeByParentCode(String parentCode, Integer level); |
| 64 | 66 | |
| 65 | 67 | /** |
| 68 | + * 获取问题类型级联树 | |
| 69 | + * | |
| 70 | + * @param parentCode 父级编码(为空则返回顶级树) | |
| 71 | + * @return 级联树列表 | |
| 72 | + */ | |
| 73 | + List<ProblemTypeCascadeRespVO> getProblemTypeTree(String parentCode); | |
| 74 | + | |
| 75 | + /** | |
| 66 | 76 | * 根据父级编码生成下一个类型编码 |
| 67 | 77 | * |
| 68 | 78 | * @param parentCode 父级编码 | ... | ... |
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/problemtype/GardenProblemTypeServiceImpl.java
| ... | ... | @@ -3,13 +3,17 @@ package com.zteits.urbanops.module.garden.service.problemtype; |
| 3 | 3 | import com.zteits.urbanops.framework.common.enums.CommonStatusEnum; |
| 4 | 4 | import com.zteits.urbanops.module.garden.controller.admin.problemtype.vo.ProblemTypePageReqVO; |
| 5 | 5 | import com.zteits.urbanops.module.garden.controller.admin.problemtype.vo.ProblemTypeSaveReqVO; |
| 6 | +import com.zteits.urbanops.module.garden.controller.app.problemtype.vo.ProblemTypeCascadeRespVO; | |
| 6 | 7 | import com.zteits.urbanops.module.system.dal.dataobject.dict.DictDataDO; |
| 7 | 8 | import com.zteits.urbanops.module.system.service.dict.DictDataService; |
| 8 | 9 | import org.springframework.stereotype.Service; |
| 9 | 10 | import org.springframework.validation.annotation.Validated; |
| 10 | 11 | import jakarta.annotation.Resource; |
| 11 | 12 | |
| 13 | +import java.util.ArrayList; | |
| 12 | 14 | import java.util.List; |
| 15 | +import java.util.Map; | |
| 16 | +import java.util.stream.Collectors; | |
| 13 | 17 | |
| 14 | 18 | import com.zteits.urbanops.module.garden.dal.dataobject.problemtype.GardenProblemTypeDO; |
| 15 | 19 | import com.zteits.urbanops.framework.common.pojo.PageResult; |
| ... | ... | @@ -93,6 +97,53 @@ public class GardenProblemTypeServiceImpl implements GardenProblemTypeService { |
| 93 | 97 | } |
| 94 | 98 | |
| 95 | 99 | @Override |
| 100 | + public List<ProblemTypeCascadeRespVO> getProblemTypeTree(String parentCode) { | |
| 101 | + List<GardenProblemTypeDO> allList = gardenProblemTypeMapper.selectList( | |
| 102 | + new com.zteits.urbanops.framework.mybatis.core.query.LambdaQueryWrapperX<GardenProblemTypeDO>() | |
| 103 | + .eq(GardenProblemTypeDO::getStatus, 1) | |
| 104 | + .eq(GardenProblemTypeDO::getParentCode, parentCode) | |
| 105 | + .orderByAsc(GardenProblemTypeDO::getSort) | |
| 106 | + .orderByAsc(GardenProblemTypeDO::getId)); | |
| 107 | + | |
| 108 | + Map<String, List<GardenProblemTypeDO>> childrenMap = allList.stream() | |
| 109 | + .filter(item -> item.getParentCode() != null && !item.getParentCode().equals(item.getTypeCode())) | |
| 110 | + .collect(Collectors.groupingBy(GardenProblemTypeDO::getParentCode)); | |
| 111 | + | |
| 112 | + List<ProblemTypeCascadeRespVO> result = new ArrayList<>(); | |
| 113 | + if (parentCode == null) { | |
| 114 | + List<GardenProblemTypeDO> rootList = allList.stream() | |
| 115 | + .filter(item -> item.getParentCode() == null || item.getParentCode().equals(item.getTypeCode())) | |
| 116 | + .collect(Collectors.toList()); | |
| 117 | + for (GardenProblemTypeDO root : rootList) { | |
| 118 | + ProblemTypeCascadeRespVO vo = buildTreeNode(root, childrenMap); | |
| 119 | + result.add(vo); | |
| 120 | + } | |
| 121 | + } else { | |
| 122 | + List<GardenProblemTypeDO> parentList = allList.stream() | |
| 123 | + .filter(item -> parentCode.equals(item.getTypeCode())) | |
| 124 | + .collect(Collectors.toList()); | |
| 125 | + for (GardenProblemTypeDO parent : parentList) { | |
| 126 | + ProblemTypeCascadeRespVO vo = buildTreeNode(parent, childrenMap); | |
| 127 | + result.add(vo); | |
| 128 | + } | |
| 129 | + } | |
| 130 | + return result; | |
| 131 | + } | |
| 132 | + | |
| 133 | + private ProblemTypeCascadeRespVO buildTreeNode(GardenProblemTypeDO item, Map<String, List<GardenProblemTypeDO>> childrenMap) { | |
| 134 | + ProblemTypeCascadeRespVO vo = new ProblemTypeCascadeRespVO(); | |
| 135 | + vo.setLabel(item.getTypeName()); | |
| 136 | + vo.setValue(item.getTypeCode()); | |
| 137 | + List<GardenProblemTypeDO> children = childrenMap.get(item.getTypeCode()); | |
| 138 | + if (children != null && !children.isEmpty()) { | |
| 139 | + for (GardenProblemTypeDO child : children) { | |
| 140 | + vo.getChildren().add(buildTreeNode(child, childrenMap)); | |
| 141 | + } | |
| 142 | + } | |
| 143 | + return vo; | |
| 144 | + } | |
| 145 | + | |
| 146 | + @Override | |
| 96 | 147 | public String generateNextCode(String parentCode) { |
| 97 | 148 | return gardenProblemTypeMapper.generateNextCode(parentCode); |
| 98 | 149 | } | ... | ... |
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/util/GisCoordinateUtil.java
0 → 100644
| 1 | +package com.zteits.urbanops.module.garden.util; | |
| 2 | + | |
| 3 | +import java.util.regex.Matcher; | |
| 4 | +import java.util.regex.Pattern; | |
| 5 | + | |
| 6 | +/** | |
| 7 | + * GIS 坐标转换工具 — WGS84 ↔ GCJ02(高德坐标系) | |
| 8 | + */ | |
| 9 | +public class GisCoordinateUtil { | |
| 10 | + | |
| 11 | + private static final double PI = Math.PI; | |
| 12 | + private static final double A = 6378245.0; // 长半轴 | |
| 13 | + private static final double EE = 0.00669342162296594323; // 偏心率平方 | |
| 14 | + | |
| 15 | + /** | |
| 16 | + * WGS84 转 GCJ02(高德) | |
| 17 | + */ | |
| 18 | + public static double[] wgs84ToGcj02(double lng, double lat) { | |
| 19 | + if (outOfChina(lng, lat)) { | |
| 20 | + return new double[]{lng, lat}; | |
| 21 | + } | |
| 22 | + double dLat = transformLat(lng - 105.0, lat - 35.0); | |
| 23 | + double dLng = transformLng(lng - 105.0, lat - 35.0); | |
| 24 | + double radLat = lat / 180.0 * PI; | |
| 25 | + double magic = Math.sin(radLat); | |
| 26 | + magic = 1 - EE * magic * magic; | |
| 27 | + double sqrtMagic = Math.sqrt(magic); | |
| 28 | + dLat = (dLat * 180.0) / ((A * (1 - EE)) / (magic * sqrtMagic) * PI); | |
| 29 | + dLng = (dLng * 180.0) / (A / sqrtMagic * Math.cos(radLat) * PI); | |
| 30 | + return new double[]{lng + dLng, lat + dLat}; | |
| 31 | + } | |
| 32 | + | |
| 33 | + /** | |
| 34 | + * GCJ02 转 WGS84 | |
| 35 | + */ | |
| 36 | + public static double[] gcj02ToWgs84(double lng, double lat) { | |
| 37 | + if (outOfChina(lng, lat)) { | |
| 38 | + return new double[]{lng, lat}; | |
| 39 | + } | |
| 40 | + double[] gcj = wgs84ToGcj02(lng, lat); | |
| 41 | + return new double[]{lng * 2 - gcj[0], lat * 2 - gcj[1]}; | |
| 42 | + } | |
| 43 | + | |
| 44 | + /** 匹配 WKT 中的坐标对:数字 空格 数字 */ | |
| 45 | + private static final Pattern COORD_PAIR = Pattern.compile("(\\d+\\.?\\d*)\\s+(\\d+\\.?\\d*)"); | |
| 46 | + | |
| 47 | + /** | |
| 48 | + * 转换 WKT 字符串中的所有坐标对(WGS84 → GCJ02) | |
| 49 | + * 支持 POLYGON、MULTIPOLYGON 等任意 WKT 格式 | |
| 50 | + */ | |
| 51 | + public static String transformPolygonWkt(String wkt) { | |
| 52 | + if (wkt == null || wkt.isBlank()) { | |
| 53 | + return wkt; | |
| 54 | + } | |
| 55 | + Matcher m = COORD_PAIR.matcher(wkt); | |
| 56 | + StringBuilder sb = new StringBuilder(); | |
| 57 | + while (m.find()) { | |
| 58 | + double lng = Double.parseDouble(m.group(1)); | |
| 59 | + double lat = Double.parseDouble(m.group(2)); | |
| 60 | + double[] gcj = wgs84ToGcj02(lng, lat); | |
| 61 | + m.appendReplacement(sb, String.format("%.6f %.6f", gcj[0], gcj[1])); | |
| 62 | + } | |
| 63 | + m.appendTail(sb); | |
| 64 | + return sb.toString(); | |
| 65 | + } | |
| 66 | + | |
| 67 | + private static boolean outOfChina(double lng, double lat) { | |
| 68 | + return lng < 72.004 || lng > 137.8347 || lat < 0.8293 || lat > 55.8271; | |
| 69 | + } | |
| 70 | + | |
| 71 | + private static double transformLat(double x, double y) { | |
| 72 | + double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x)); | |
| 73 | + ret += (20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0 / 3.0; | |
| 74 | + ret += (20.0 * Math.sin(y * PI) + 40.0 * Math.sin(y / 3.0 * PI)) * 2.0 / 3.0; | |
| 75 | + ret += (160.0 * Math.sin(y / 12.0 * PI) + 320.0 * Math.sin(y * PI / 30.0)) * 2.0 / 3.0; | |
| 76 | + return ret; | |
| 77 | + } | |
| 78 | + | |
| 79 | + private static double transformLng(double x, double y) { | |
| 80 | + double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x)); | |
| 81 | + ret += (20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0 / 3.0; | |
| 82 | + ret += (20.0 * Math.sin(x * PI) + 40.0 * Math.sin(x / 3.0 * PI)) * 2.0 / 3.0; | |
| 83 | + ret += (150.0 * Math.sin(x / 12.0 * PI) + 300.0 * Math.sin(x / 30.0 * PI)) * 2.0 / 3.0; | |
| 84 | + return ret; | |
| 85 | + } | |
| 86 | +} | ... | ... |
urbanops-server/pom.xml
| ... | ... | @@ -58,11 +58,11 @@ |
| 58 | 58 | <!-- </dependency>--> |
| 59 | 59 | |
| 60 | 60 | <!-- 数据报表。默认注释,保证编译速度 --> |
| 61 | - <dependency> | |
| 62 | - <groupId>com.zteits.boot</groupId> | |
| 63 | - <artifactId>urbanops-module-report</artifactId> | |
| 64 | - <version>${revision}</version> | |
| 65 | - </dependency> | |
| 61 | +<!-- <dependency>--> | |
| 62 | +<!-- <groupId>com.zteits.boot</groupId>--> | |
| 63 | +<!-- <artifactId>urbanops-module-report</artifactId>--> | |
| 64 | +<!-- <version>${revision}</version>--> | |
| 65 | +<!-- </dependency>--> | |
| 66 | 66 | <!-- 工作流。默认注释,保证编译速度 --> |
| 67 | 67 | <dependency> |
| 68 | 68 | <groupId>com.zteits.boot</groupId> | ... | ... |
urbanops-server/src/main/resources/application-local.yaml
| ... | ... | @@ -186,6 +186,9 @@ urbanops: |
| 186 | 186 | refund-notify-url: http://yunai.natapp1.cc/admin-api/pay/notify/refund # 支付渠道的【退款】回调地址 |
| 187 | 187 | transfer-notify-url: https://yunai.natapp1.cc/admin-api/pay/notify/transfer # 支付渠道的【转账】回调地址 |
| 188 | 188 | demo: false # 开启演示模式 |
| 189 | + security: | |
| 190 | + mock-enable: true # 本地调试启用 Mock 认证,Token 格式: test{userId}(如 test1 表示用户ID=1) | |
| 191 | + mock-secret: test | |
| 189 | 192 | tencent-lbs-key: TVDBZ-TDILD-4ON4B-PFDZA-RNLKH-VVF6E # QQ 地图的密钥 https://lbs.qq.com/service/staticV2/staticGuide/staticDoc |
| 190 | 193 | |
| 191 | 194 | justauth: | ... | ... |