diff --git a/.gitignore b/.gitignore
index e625a99..4bf7246 100644
--- a/.gitignore
+++ b/.gitignore
@@ -53,3 +53,15 @@ application-my.yaml
/urbanops-ui-app/unpackage/
**/.DS_Store
old/
+/.claudeignore
+/docs/all_test.html
+/docs/ppt-assets/animations.css
+/docs/ppt-assets/base.css
+/CLAUDE.md
+/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/enums/ErrorCodeConstants_手动操作.java
+/docs/ppt-assets/fonts.css
+/docs/ppt-assets/runtime.js
+/.claude/settings.local.json
+/docs/ppt-assets/style.css
+/docs/urbanops-architecture-ppt.html
+/docs/urbanops-tech-sharing.html
diff --git a/pom.xml b/pom.xml
index 5d1fce6..74308ca 100644
--- a/pom.xml
+++ b/pom.xml
@@ -49,6 +49,7 @@
3.5.5
1.6.3
UTF-8
+ yyyyMMdd-HHmm
diff --git a/sql/mysql/garden_problem_type-fix.sql b/sql/mysql/garden_problem_type-fix.sql
new file mode 100644
index 0000000..a7c5d85
--- /dev/null
+++ b/sql/mysql/garden_problem_type-fix.sql
@@ -0,0 +1,35 @@
+-- =================================================
+-- garden_problem_type 表结构补全
+-- 原因:BaseDO 包含 create_time/update_time/creator/updater/deleted
+-- MyBatis Plus @TableLogic 自动 WHERE deleted=0
+-- 缺少这些列会导致 SQL 报错或无数据返回
+-- =================================================
+
+-- 如果表已存在数据,先备份
+-- CREATE TABLE garden_problem_type_bak AS SELECT * FROM garden_problem_type;
+
+-- 方案1:删表重建(无重要数据时)
+DROP TABLE IF EXISTS `garden_problem_type`;
+CREATE TABLE `garden_problem_type` (
+ `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
+ `type_code` varchar(32) NOT NULL COMMENT '问题类型编码',
+ `type_name` varchar(64) NOT NULL COMMENT '问题类型名称',
+ `level` tinyint NOT NULL COMMENT '层级:1一级 2二级 3三级',
+ `parent_code` varchar(32) DEFAULT NULL COMMENT '父级类型编码',
+ `sort` int DEFAULT 0 COMMENT '排序序号',
+ `status` tinyint DEFAULT 1 COMMENT '状态 1启用 0禁用',
+ `creator` varchar(64) DEFAULT '' COMMENT '创建者',
+ `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `updater` varchar(64) DEFAULT '' COMMENT '更新者',
+ `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+ `deleted` tinyint NOT NULL DEFAULT 0 COMMENT '逻辑删除 0正常 1删除',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `uk_type_code` (`type_code`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='问题类型三级分类表';
+
+-- 方案2:已有数据时,用 ALTER 补齐缺失列(按需执行)
+-- ALTER TABLE garden_problem_type ADD COLUMN `creator` varchar(64) DEFAULT '' COMMENT '创建者' AFTER `status`;
+-- ALTER TABLE garden_problem_type ADD COLUMN `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间' AFTER `creator`;
+-- ALTER TABLE garden_problem_type ADD COLUMN `updater` varchar(64) DEFAULT '' COMMENT '更新者' AFTER `create_time`;
+-- ALTER TABLE garden_problem_type ADD COLUMN `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间' AFTER `updater`;
+-- ALTER TABLE garden_problem_type ADD COLUMN `deleted` tinyint NOT NULL DEFAULT 0 COMMENT '逻辑删除' AFTER `update_time`;
diff --git a/sql/mysql/problem-type-menu-auto.sql b/sql/mysql/problem-type-menu-auto.sql
new file mode 100644
index 0000000..05a0766
--- /dev/null
+++ b/sql/mysql/problem-type-menu-auto.sql
@@ -0,0 +1,33 @@
+-- =================================================
+-- 问题类型配置 - 菜单与权限 SQL(自动查找父菜单ID)
+-- 直接执行即可,自动从「工单管理」下挂载菜单
+-- =================================================
+
+-- 查询当前最大 ID 作为起始值
+SET @maxId = (SELECT IFNULL(MAX(id), 5000) FROM system_menu);
+
+-- 查询工单管理 菜单ID
+SET @parentId = (SELECT id FROM system_menu WHERE name = '工单管理' AND type = 1 LIMIT 1);
+
+-- 如果找不到工单管理,尝试匹配 workorder 关键字
+-- SET @parentId = (SELECT id FROM system_menu WHERE (name LIKE '%工单%' OR path LIKE '%workorder%') AND type = 1 LIMIT 1);
+
+-- 1. 二级菜单:问题类型
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+VALUES (@maxId + 1, '问题类型', '', 2, 10, @parentId, 'problem-type', 'ep:list', 'garden/problemType/index', 'ProblemType', 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0');
+
+-- 2. 按钮:查询
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+VALUES (@maxId + 2, '问题类型查询', 'garden:problem-type:query', 3, 1, @maxId + 1, '', '', '', '', 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0');
+
+-- 3. 按钮:创建
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+VALUES (@maxId + 3, '问题类型创建', 'garden:problem-type:create', 3, 2, @maxId + 1, '', '', '', '', 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0');
+
+-- 4. 按钮:更新
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+VALUES (@maxId + 4, '问题类型更新', 'garden:problem-type:update', 3, 3, @maxId + 1, '', '', '', '', 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0');
+
+-- 5. 按钮:删除
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+VALUES (@maxId + 5, '问题类型删除', 'garden:problem-type:delete', 3, 4, @maxId + 1, '', '', '', '', 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0');
diff --git a/sql/mysql/problem-type-menu.sql b/sql/mysql/problem-type-menu.sql
new file mode 100644
index 0000000..c2b59f5
--- /dev/null
+++ b/sql/mysql/problem-type-menu.sql
@@ -0,0 +1,35 @@
+-- =================================================
+-- 问题类型配置 - 菜单与权限 SQL
+-- 说明:
+-- 1. 请先查询「工单管理」的菜单 ID,替换下面 @parentId 为实际值
+-- 查询语句:SELECT id FROM system_menu WHERE name = '工单管理' AND type = 1;
+-- 2. 修改下面 @menuId 为可用的起始 ID(确保不与已有菜单 ID 冲突)
+-- 查询最大ID:SELECT MAX(id) FROM system_menu;
+-- 3. 执行前请备份 system_menu 表
+-- =================================================
+
+-- 请根据实际情况替换以下变量值:
+-- SET @parentId = (SELECT id FROM system_menu WHERE name = '工单管理' AND type = 1);
+-- SET @menuId = (SELECT IFNULL(MAX(id), 0) + 1 FROM system_menu);
+
+-- 示例:假设工单管理 parent_id = 2000,起始 menu_id = 5100
+
+-- 1. 二级菜单:问题类型
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+VALUES (@menuId, '问题类型', '', 2, 10, @parentId, 'problem-type', 'ep:list', 'garden/problemType/index', 'ProblemType', 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0');
+
+-- 2. 按钮权限:查询
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+VALUES (@menuId + 1, '问题类型查询', 'garden:problem-type:query', 3, 1, @menuId, '', '', '', '', 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0');
+
+-- 3. 按钮权限:创建
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+VALUES (@menuId + 2, '问题类型创建', 'garden:problem-type:create', 3, 2, @menuId, '', '', '', '', 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0');
+
+-- 4. 按钮权限:更新
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+VALUES (@menuId + 3, '问题类型更新', 'garden:problem-type:update', 3, 3, @menuId, '', '', '', '', 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0');
+
+-- 5. 按钮权限:删除
+INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+VALUES (@menuId + 4, '问题类型删除', 'garden:problem-type:delete', 3, 4, @menuId, '', '', '', '', 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0');
diff --git a/urbanops-dependencies/pom.xml b/urbanops-dependencies/pom.xml
index 5f0399f..79802ac 100644
--- a/urbanops-dependencies/pom.xml
+++ b/urbanops-dependencies/pom.xml
@@ -76,6 +76,7 @@
2.1.1
2.1.0
4.7.7-20250808.182223
+ yyyyMMdd-HHmm
diff --git a/urbanops-framework/urbanops-common/src/main/java/com/zteits/urbanops/framework/common/pojo/PageParam.java b/urbanops-framework/urbanops-common/src/main/java/com/zteits/urbanops/framework/common/pojo/PageParam.java
index d9d9d8e..60363c8 100644
--- a/urbanops-framework/urbanops-common/src/main/java/com/zteits/urbanops/framework/common/pojo/PageParam.java
+++ b/urbanops-framework/urbanops-common/src/main/java/com/zteits/urbanops/framework/common/pojo/PageParam.java
@@ -72,7 +72,8 @@ public class PageParam implements Serializable {
public void setOrderByColumn(String orderByColumn) {
if (!StringUtils.hasText(orderByColumn)) {
this.orderByColumn = "id";
- return;
+ } else {
+ this.orderByColumn = orderByColumn;
}
}
diff --git a/urbanops-framework/urbanops-common/src/main/java/com/zteits/urbanops/framework/common/util/date/DateUtils.java b/urbanops-framework/urbanops-common/src/main/java/com/zteits/urbanops/framework/common/util/date/DateUtils.java
index 6446d5c..808c8e2 100644
--- a/urbanops-framework/urbanops-common/src/main/java/com/zteits/urbanops/framework/common/util/date/DateUtils.java
+++ b/urbanops-framework/urbanops-common/src/main/java/com/zteits/urbanops/framework/common/util/date/DateUtils.java
@@ -3,6 +3,7 @@ package com.zteits.urbanops.framework.common.util.date;
import cn.hutool.core.date.LocalDateTimeUtil;
import java.time.*;
+import java.time.format.DateTimeFormatter;
import java.util.Calendar;
import java.util.Date;
@@ -18,6 +19,9 @@ public class DateUtils {
*/
public static final String TIME_ZONE_DEFAULT = "GMT+8";
+ public static final ZoneId DEFAULT_ZONE = ZoneId.of("GMT+8"); // 东八区
+
+
/**
* 秒转换成毫秒
*/
@@ -27,6 +31,10 @@ public class DateUtils {
public static final String FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND = "yyyy-MM-dd HH:mm:ss";
+ public static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
+ public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+
+
/**
* 将 LocalDateTime 转换成 Date
*
diff --git a/urbanops-framework/urbanops-common/src/main/java/com/zteits/urbanops/framework/common/util/validation/ValidationUtils.java b/urbanops-framework/urbanops-common/src/main/java/com/zteits/urbanops/framework/common/util/validation/ValidationUtils.java
index 228fd35..f3129fa 100644
--- a/urbanops-framework/urbanops-common/src/main/java/com/zteits/urbanops/framework/common/util/validation/ValidationUtils.java
+++ b/urbanops-framework/urbanops-common/src/main/java/com/zteits/urbanops/framework/common/util/validation/ValidationUtils.java
@@ -18,8 +18,9 @@ import java.util.regex.Pattern;
*/
public class ValidationUtils {
- private static final Pattern PATTERN_MOBILE = Pattern.compile("^(?:(?:\\+|00)86)?1(?:(?:3[\\d])|(?:4[0,1,4-9])|(?:5[0-3,5-9])|(?:6[2,5-7])|(?:7[0-8])|(?:8[\\d])|(?:9[0-3,5-9]))\\d{8}$");
-
+ //private static final Pattern PATTERN_MOBILE = Pattern.compile("^(?:(?:\\+|00)86)?1(?:(?:3[\\d])|(?:4[0,1,4-9])|(?:5[0-3,5-9])|(?:6[2,5-7])|(?:7[0-8])|(?:8[\\d])|(?:9[0-3,5-9]))\\d{8}$");
+ //add by wq 20260327 手机号码校验覆盖全号段
+ private static final Pattern PATTERN_MOBILE = Pattern.compile("^(?:\\+86|0086)?1[3-9]\\d{9}$");
private static final Pattern PATTERN_URL = Pattern.compile("^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]");
private static final Pattern PATTERN_XML_NCNAME = Pattern.compile("[a-zA-Z_][\\-_.0-9_a-zA-Z$]*");
@@ -51,5 +52,4 @@ public class ValidationUtils {
throw new ConstraintViolationException(constraintViolations);
}
}
-
}
diff --git a/urbanops-framework/urbanops-spring-boot-starter-excel/src/main/java/com/zteits/urbanops/framework/excel/core/convert/BusinessLineConvert.java b/urbanops-framework/urbanops-spring-boot-starter-excel/src/main/java/com/zteits/urbanops/framework/excel/core/convert/BusinessLineConvert.java
new file mode 100644
index 0000000..4781cd0
--- /dev/null
+++ b/urbanops-framework/urbanops-spring-boot-starter-excel/src/main/java/com/zteits/urbanops/framework/excel/core/convert/BusinessLineConvert.java
@@ -0,0 +1,112 @@
+package com.zteits.urbanops.framework.excel.core.convert;
+
+import cn.idev.excel.converters.Converter;
+import cn.idev.excel.enums.CellDataTypeEnum;
+import cn.idev.excel.metadata.GlobalConfiguration;
+import cn.idev.excel.metadata.data.ReadCellData;
+import cn.idev.excel.metadata.data.WriteCellData;
+import cn.idev.excel.metadata.property.ExcelContentProperty;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class BusinessLineConvert implements Converter