Commit a8d4e5e87284b236acccad2475bc8023738a8a76

Authored by 王彪总
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 "========================================"
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
20 <module>urbanops-module-workorder</module> 20 <module>urbanops-module-workorder</module>
21 <!-- <module>urbanops-module-member</module>--> 21 <!-- <module>urbanops-module-member</module>-->
22 <module>urbanops-module-bpm</module> 22 <module>urbanops-module-bpm</module>
23 - <module>urbanops-module-report</module> 23 +<!-- <module>urbanops-module-report</module>-->
24 <module>urbanops-module-api</module> 24 <module>urbanops-module-api</module>
25 <!-- <module>urbanops-module-mp</module>--> 25 <!-- <module>urbanops-module-mp</module>-->
26 <!-- <module>urbanops-module-pay</module>--> 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 #!/bin/bash 1 #!/bin/bash
2 -set -e 2 +set -eo pipefail
3 3
4 DATE=$(date +%Y%m%d%H%M) 4 DATE=$(date +%Y%m%d%H%M)
5 # 基础路径 5 # 基础路径
@@ -9,9 +9,9 @@ SOURCE_PATH=$BASE_PATH/build @@ -9,9 +9,9 @@ SOURCE_PATH=$BASE_PATH/build
9 # 服务名称。同时约定部署服务的 jar 包名字也为它。 9 # 服务名称。同时约定部署服务的 jar 包名字也为它。
10 SERVER_NAME=urbanops-server 10 SERVER_NAME=urbanops-server
11 # 环境 11 # 环境
12 -PROFILES_ACTIVE=development 12 +PROFILES_ACTIVE=dev
13 # 健康检查 URL 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 # heapError 存放路径 16 # heapError 存放路径
17 HEAP_ERROR_PATH=$BASE_PATH/heapError 17 HEAP_ERROR_PATH=$BASE_PATH/heapError
@@ -33,7 +33,8 @@ function backup() { @@ -33,7 +33,8 @@ function backup() {
33 # 如果存在,则备份到 backup 目录下,使用时间作为后缀 33 # 如果存在,则备份到 backup 目录下,使用时间作为后缀
34 else 34 else
35 echo "[backup] 开始备份 $SERVER_NAME ..." 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 echo "[backup] 备份 $SERVER_NAME 完成" 38 echo "[backup] 备份 $SERVER_NAME 完成"
38 fi 39 fi
39 } 40 }
@@ -47,12 +48,12 @@ function transfer() { @@ -47,12 +48,12 @@ function transfer() {
47 echo "[transfer] $BASE_PATH/$SERVER_NAME.jar 不存在,跳过删除" 48 echo "[transfer] $BASE_PATH/$SERVER_NAME.jar 不存在,跳过删除"
48 else 49 else
49 echo "[transfer] 移除 $BASE_PATH/$SERVER_NAME.jar 完成" 50 echo "[transfer] 移除 $BASE_PATH/$SERVER_NAME.jar 完成"
50 - rm $BASE_PATH/$SERVER_NAME.jar 51 + rm "$BASE_PATH/$SERVER_NAME.jar"
51 fi 52 fi
52 53
53 # 复制新 jar 包 54 # 复制新 jar 包
54 echo "[transfer] 从 $SOURCE_PATH 中获取 $SERVER_NAME.jar 并迁移至 $BASE_PATH ...." 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 echo "[transfer] 转移 $SERVER_NAME.jar 完成" 58 echo "[transfer] 转移 $SERVER_NAME.jar 完成"
58 } 59 }
@@ -60,7 +61,7 @@ function transfer() { @@ -60,7 +61,7 @@ function transfer() {
60 # 停止:优雅关闭之前已经启动的服务 61 # 停止:优雅关闭之前已经启动的服务
61 function stop() { 62 function stop() {
62 echo "[stop] 开始停止 $BASE_PATH/$SERVER_NAME" 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 # 如果 Java 服务启动中,则进行关闭 65 # 如果 Java 服务启动中,则进行关闭
65 if [ -n "$PID" ]; then 66 if [ -n "$PID" ]; then
66 # 正常关闭 67 # 正常关闭
@@ -70,7 +71,7 @@ function stop() { @@ -70,7 +71,7 @@ function stop() {
70 for ((i = 0; i < 120; i++)) 71 for ((i = 0; i < 120; i++))
71 do 72 do
72 sleep 1 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 if [ -n "$PID" ]; then 75 if [ -n "$PID" ]; then
75 echo -e ".\c" 76 echo -e ".\c"
76 else 77 else
@@ -99,7 +100,8 @@ function start() { @@ -99,7 +100,8 @@ function start() {
99 echo "[start] PROFILES: $PROFILES_ACTIVE" 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 echo "[start] 启动 $BASE_PATH/$SERVER_NAME 完成" 105 echo "[start] 启动 $BASE_PATH/$SERVER_NAME 完成"
104 } 106 }
105 107
@@ -112,7 +114,7 @@ function healthCheck() { @@ -112,7 +114,7 @@ function healthCheck() {
112 for ((i = 0; i < 120; i++)) 114 for ((i = 0; i < 120; i++))
113 do 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 # 如果状态码为 200,则说明健康检查通过 118 # 如果状态码为 200,则说明健康检查通过
117 if [ "$result" == "200" ]; then 119 if [ "$result" == "200" ]; then
118 echo "[healthCheck] 健康检查通过"; 120 echo "[healthCheck] 健康检查通过";
@@ -127,24 +129,29 @@ function healthCheck() { @@ -127,24 +129,29 @@ function healthCheck() {
127 # 健康检查未通过,则异常退出 shell 脚本,不继续部署。 129 # 健康检查未通过,则异常退出 shell 脚本,不继续部署。
128 if [ ! "$result" == "200" ]; then 130 if [ ! "$result" == "200" ]; then
129 echo "[healthCheck] 健康检查不通过,可能部署失败。查看日志,自行判断是否启动成功"; 131 echo "[healthCheck] 健康检查不通过,可能部署失败。查看日志,自行判断是否启动成功";
130 - tail -n 10 nohup.out 132 + tail -n 10 "$BASE_PATH/nohup.out"
131 exit 1; 133 exit 1;
132 # 健康检查通过,打印最后 10 行日志,可能部署的人想看下日志。 134 # 健康检查通过,打印最后 10 行日志,可能部署的人想看下日志。
133 else 135 else
134 - tail -n 10 nohup.out 136 + tail -n 10 "$BASE_PATH/nohup.out"
135 fi 137 fi
136 # 如果未配置健康检查,则 sleep 120 秒,人工看日志是否部署成功。 138 # 如果未配置健康检查,则 sleep 120 秒,人工看日志是否部署成功。
137 else 139 else
138 echo "[healthCheck] HEALTH_CHECK_URL 未配置,开始 sleep 120 秒"; 140 echo "[healthCheck] HEALTH_CHECK_URL 未配置,开始 sleep 120 秒";
139 sleep 120 141 sleep 120
140 echo "[healthCheck] sleep 120 秒完成,查看日志,自行判断是否启动成功"; 142 echo "[healthCheck] sleep 120 秒完成,查看日志,自行判断是否启动成功";
141 - tail -n 50 nohup.out 143 + tail -n 50 "$BASE_PATH/nohup.out"
142 fi 144 fi
143 } 145 }
144 146
145 # 部署 147 # 部署
146 function deploy() { 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 # 备份原 jar 155 # 备份原 jar
149 backup 156 backup
150 # 停止 Java 服务 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
  1 +-- garden_road 表新增 GIS 关联字段
  2 +ALTER TABLE garden_road
  3 + ADD COLUMN gis_plot_name VARCHAR(200) DEFAULT NULL COMMENT 'GIS 图斑名称',
  4 + ADD COLUMN gis_plot_area FLOAT DEFAULT NULL COMMENT 'GIS 图斑面积',
  5 + ADD COLUMN gis_is_related TINYINT DEFAULT 0 COMMENT '是否关联GIS (0=未关联, 1=已关联)';
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,6 +86,9 @@ public class RoadPageReqVO extends PageParam {
86 @Schema(description = "更新者", example = "芋艿") 86 @Schema(description = "更新者", example = "芋艿")
87 private String updaterName; 87 private String updaterName;
88 88
  89 + @Schema(description = "是否关联GIS (0=未关联, 1=已关联)")
  90 + private Integer gisIsRelated;
  91 +
89 //对应前端排序字段 92 //对应前端排序字段
90 public static final Map<String, SFunction<RoadDO, ?>> SORT_FIELD_MAP = Map.of( 93 public static final Map<String, SFunction<RoadDO, ?>> SORT_FIELD_MAP = Map.of(
91 "id", RoadDO::getId, 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,4 +122,17 @@ public class RoadRespVO {
122 @ExcelProperty("部门(道路负责部门)") 122 @ExcelProperty("部门(道路负责部门)")
123 private String deptName; 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 \ No newline at end of file 139 \ No newline at end of file
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/app/problemtype/AppProblemTypeController.java
1 package com.zteits.urbanops.module.garden.controller.app.problemtype; 1 package com.zteits.urbanops.module.garden.controller.app.problemtype;
2 2
3 import com.zteits.urbanops.framework.common.pojo.CommonResult; 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 import com.zteits.urbanops.module.garden.service.problemtype.GardenProblemTypeService; 5 import com.zteits.urbanops.module.garden.service.problemtype.GardenProblemTypeService;
8 import io.swagger.v3.oas.annotations.Operation; 6 import io.swagger.v3.oas.annotations.Operation;
9 import io.swagger.v3.oas.annotations.tags.Tag; 7 import io.swagger.v3.oas.annotations.tags.Tag;
@@ -28,11 +26,9 @@ public class AppProblemTypeController { @@ -28,11 +26,9 @@ public class AppProblemTypeController {
28 private GardenProblemTypeService gardenProblemTypeService; 26 private GardenProblemTypeService gardenProblemTypeService;
29 27
30 @GetMapping("/list-by-parent") 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 \ No newline at end of file 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,5 +122,16 @@ public class RoadDO extends BaseDO {
122 */ 122 */
123 private String updaterName; 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 \ No newline at end of file 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&lt;RoadDO&gt; { @@ -42,6 +42,7 @@ public interface RoadMapper extends BaseMapperX&lt;RoadDO&gt; {
42 .likeIfPresent(RoadDO::getCreatorName, reqVO.getCreatorName()) 42 .likeIfPresent(RoadDO::getCreatorName, reqVO.getCreatorName())
43 .betweenIfPresent(RoadDO::getCreateTime, reqVO.getCreateTime()) 43 .betweenIfPresent(RoadDO::getCreateTime, reqVO.getCreateTime())
44 .likeIfPresent(RoadDO::getUpdaterName, reqVO.getUpdaterName()) 44 .likeIfPresent(RoadDO::getUpdaterName, reqVO.getUpdaterName())
  45 + .eqIfPresent(RoadDO::getGisIsRelated, reqVO.getGisIsRelated())
45 .orderByDesc(RoadDO::getId)); 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,7 +2,9 @@ package com.zteits.urbanops.module.garden.service.problemtype;
2 2
3 import com.zteits.urbanops.module.garden.controller.admin.problemtype.vo.ProblemTypePageReqVO; 3 import com.zteits.urbanops.module.garden.controller.admin.problemtype.vo.ProblemTypePageReqVO;
4 import com.zteits.urbanops.module.garden.controller.admin.problemtype.vo.ProblemTypeSaveReqVO; 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 import com.zteits.urbanops.module.garden.dal.dataobject.problemtype.GardenProblemTypeDO; 6 import com.zteits.urbanops.module.garden.dal.dataobject.problemtype.GardenProblemTypeDO;
  7 +import com.zteits.urbanops.framework.common.pojo.CommonResult;
6 import com.zteits.urbanops.framework.common.pojo.PageResult; 8 import com.zteits.urbanops.framework.common.pojo.PageResult;
7 import jakarta.validation.Valid; 9 import jakarta.validation.Valid;
8 10
@@ -63,6 +65,14 @@ public interface GardenProblemTypeService { @@ -63,6 +65,14 @@ public interface GardenProblemTypeService {
63 List<GardenProblemTypeDO> getProblemTypeByParentCode(String parentCode, Integer level); 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 * @param parentCode 父级编码 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,13 +3,17 @@ package com.zteits.urbanops.module.garden.service.problemtype;
3 import com.zteits.urbanops.framework.common.enums.CommonStatusEnum; 3 import com.zteits.urbanops.framework.common.enums.CommonStatusEnum;
4 import com.zteits.urbanops.module.garden.controller.admin.problemtype.vo.ProblemTypePageReqVO; 4 import com.zteits.urbanops.module.garden.controller.admin.problemtype.vo.ProblemTypePageReqVO;
5 import com.zteits.urbanops.module.garden.controller.admin.problemtype.vo.ProblemTypeSaveReqVO; 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 import com.zteits.urbanops.module.system.dal.dataobject.dict.DictDataDO; 7 import com.zteits.urbanops.module.system.dal.dataobject.dict.DictDataDO;
7 import com.zteits.urbanops.module.system.service.dict.DictDataService; 8 import com.zteits.urbanops.module.system.service.dict.DictDataService;
8 import org.springframework.stereotype.Service; 9 import org.springframework.stereotype.Service;
9 import org.springframework.validation.annotation.Validated; 10 import org.springframework.validation.annotation.Validated;
10 import jakarta.annotation.Resource; 11 import jakarta.annotation.Resource;
11 12
  13 +import java.util.ArrayList;
12 import java.util.List; 14 import java.util.List;
  15 +import java.util.Map;
  16 +import java.util.stream.Collectors;
13 17
14 import com.zteits.urbanops.module.garden.dal.dataobject.problemtype.GardenProblemTypeDO; 18 import com.zteits.urbanops.module.garden.dal.dataobject.problemtype.GardenProblemTypeDO;
15 import com.zteits.urbanops.framework.common.pojo.PageResult; 19 import com.zteits.urbanops.framework.common.pojo.PageResult;
@@ -93,6 +97,53 @@ public class GardenProblemTypeServiceImpl implements GardenProblemTypeService { @@ -93,6 +97,53 @@ public class GardenProblemTypeServiceImpl implements GardenProblemTypeService {
93 } 97 }
94 98
95 @Override 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 public String generateNextCode(String parentCode) { 147 public String generateNextCode(String parentCode) {
97 return gardenProblemTypeMapper.generateNextCode(parentCode); 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,11 +58,11 @@
58 <!-- </dependency>--> 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 <dependency> 67 <dependency>
68 <groupId>com.zteits.boot</groupId> 68 <groupId>com.zteits.boot</groupId>
urbanops-server/src/main/resources/application-local.yaml
@@ -186,6 +186,9 @@ urbanops: @@ -186,6 +186,9 @@ urbanops:
186 refund-notify-url: http://yunai.natapp1.cc/admin-api/pay/notify/refund # 支付渠道的【退款】回调地址 186 refund-notify-url: http://yunai.natapp1.cc/admin-api/pay/notify/refund # 支付渠道的【退款】回调地址
187 transfer-notify-url: https://yunai.natapp1.cc/admin-api/pay/notify/transfer # 支付渠道的【转账】回调地址 187 transfer-notify-url: https://yunai.natapp1.cc/admin-api/pay/notify/transfer # 支付渠道的【转账】回调地址
188 demo: false # 开启演示模式 188 demo: false # 开启演示模式
  189 + security:
  190 + mock-enable: true # 本地调试启用 Mock 认证,Token 格式: test{userId}(如 test1 表示用户ID=1)
  191 + mock-secret: test
189 tencent-lbs-key: TVDBZ-TDILD-4ON4B-PFDZA-RNLKH-VVF6E # QQ 地图的密钥 https://lbs.qq.com/service/staticV2/staticGuide/staticDoc 192 tencent-lbs-key: TVDBZ-TDILD-4ON4B-PFDZA-RNLKH-VVF6E # QQ 地图的密钥 https://lbs.qq.com/service/staticV2/staticGuide/staticDoc
190 193
191 justauth: 194 justauth: