diff --git a/db/postgresql/V1__production.sql b/db/postgresql/V1__production.sql index 8d7131b4..98404ab7 100644 --- a/db/postgresql/V1__production.sql +++ b/db/postgresql/V1__production.sql @@ -187,3 +187,115 @@ CREATE TABLE IF NOT EXISTS water_quality_record ( COMMENT ON TABLE water_quality_record IS '水质检测记录表'; CREATE INDEX IF NOT EXISTS idx_wq_record_date ON water_quality_record(test_date); CREATE INDEX IF NOT EXISTS idx_wq_record_area ON water_quality_record(area); +-- ============================================= +-- 智慧水务管理系统 - 巡检问题上报 + 工单管理 DDL +-- 版本: V1 +-- ============================================= + +-- 巡检问题上报表 +CREATE TABLE IF NOT EXISTS patrol_problem ( + id BIGSERIAL PRIMARY KEY, + problem_no VARCHAR(30) UNIQUE NOT NULL, -- 问题编号:WQ-2026-001 + task_id BIGINT REFERENCES patrol_task(id), + point_seq INT, + device_id BIGINT, + device_name VARCHAR(200), + problem_type VARCHAR(50) NOT NULL, -- 设备故障/水质异常/安全隐患/环境卫生/其他 + problem_level VARCHAR(20) DEFAULT 'normal', -- low/normal/high/critical + problem_title VARCHAR(200) NOT NULL, + problem_description TEXT, + location VARCHAR(300), + lng DOUBLE PRECISION, + lat DOUBLE PRECISION, + photo_urls JSONB, -- 现场照片URL数组 + reporter_id BIGINT REFERENCES sys_user(id), + reporter_name VARCHAR(50), + report_time TIMESTAMP DEFAULT NOW(), + status VARCHAR(20) DEFAULT 'reported', -- reported/processing/completed/closed + work_order_id BIGINT, -- 关联工单ID + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW() +); +COMMENT ON TABLE patrol_problem IS '巡检问题上报表'; +CREATE INDEX IF NOT EXISTS idx_problem_task ON patrol_problem(task_id); +CREATE INDEX IF NOT EXISTS idx_problem_status ON patrol_problem(status); +CREATE INDEX IF NOT EXISTS idx_problem_device ON patrol_problem(device_id); +CREATE INDEX IF NOT EXISTS idx_problem_type ON patrol_problem(problem_type); + +-- 工单表 +CREATE TABLE IF NOT EXISTS work_order ( + id BIGSERIAL PRIMARY KEY, + order_no VARCHAR(30) UNIQUE NOT NULL, -- 工单编号:WO-2026-001 + problem_id BIGINT REFERENCES patrol_problem(id), + order_type VARCHAR(50) NOT NULL, -- 设备维修/水质处理/安全隐患处理/清洁/其他 + priority VARCHAR(20) DEFAULT 'normal', -- low/normal/high/critical + title VARCHAR(200) NOT NULL, + description TEXT, + location VARCHAR(300), + contact_person VARCHAR(50), + contact_phone VARCHAR(20), + reporter_id BIGINT REFERENCES sys_user(id), + reporter_name VARCHAR(50), + assignee_id BIGINT REFERENCES sys_user(id), + assignee_name VARCHAR(50), + status VARCHAR(20) DEFAULT 'pending', -- pending/assigned/processing/completed/cancelled + process_status VARCHAR(20) DEFAULT 'created', -- created/accepted/in_progress/completed + estimated_duration INT, -- 预计工时(分钟) + actual_start_time TIMESTAMP, + actual_end_time TIMESTAMP, + completion_time TIMESTAMP, + photos_before JSONB, -- 处理前照片 + photos_after JSONB, -- 处理后照片 + solution_description TEXT, -- 处理方案描述 + solution_result TEXT, -- 处理结果 + customer_feedback TEXT, -- 客户反馈 + created_at TIMESTAMP DEFAULT NOW(), + updated_at TIMESTAMP DEFAULT NOW() +); +COMMENT ON TABLE work_order IS '工单表'; +CREATE INDEX IF NOT EXISTS idx_order_problem ON work_order(problem_id); +CREATE INDEX IF NOT EXISTS idx_order_status ON work_order(status, process_status); +CREATE INDEX IF NOT EXISTS idx_order_assignee ON work_order(assignee_id); + +-- 工单处理记录表 +CREATE TABLE IF NOT EXISTS work_order_process ( + id BIGSERIAL PRIMARY KEY, + work_order_id BIGINT REFERENCES work_order(id), + process_step VARCHAR(50) NOT NULL, -- created/accepted/in_progress/completed + processor_id BIGINT REFERENCES sys_user(id), + processor_name VARCHAR(50), + action VARCHAR(50) NOT NULL, -- create/assign/start/complete/cancel + comment TEXT, + photos JSONB, -- 处理过程照片 + created_at TIMESTAMP DEFAULT NOW() +); +COMMENT ON TABLE work_order_process IS '工单处理记录表'; +CREATE INDEX IF NOT EXISTS idx_process_order ON work_order_process(work_order_id); +CREATE INDEX IF NOT EXISTS idx_process_step ON work_order_process(process_step); + +-- 工单附件表 +CREATE TABLE IF NOT EXISTS work_order_attachment ( + id BIGSERIAL PRIMARY KEY, + work_order_id BIGINT REFERENCES work_order(id), + file_name VARCHAR(200) NOT NULL, + file_path VARCHAR(500) NOT NULL, + file_type VARCHAR(50), -- image/pdf/doc/other + file_size BIGINT, + uploaded_by BIGINT REFERENCES sys_user(id), + uploaded_at TIMESTAMP DEFAULT NOW() +); +COMMENT ON TABLE work_order_attachment IS '工单附件表'; +CREATE INDEX IF NOT EXISTS idx_attachment_order ON work_order_attachment(work_order_id); + +-- 巡检问题与工单关联触发记录 +CREATE TABLE IF NOT EXISTS patrol_work_order_trigger ( + id BIGSERIAL PRIMARY KEY, + patrol_problem_id BIGINT REFERENCES patrol_problem(id), + work_order_id BIGINT REFERENCES work_order(id), + trigger_type VARCHAR(20) NOT NULL, -- auto/manual + trigger_condition JSONB, -- 触发条件 + created_at TIMESTAMP DEFAULT NOW() +); +COMMENT ON TABLE patrol_work_order_trigger IS '巡检问题与工单关联触发记录'; +CREATE INDEX IF NOT EXISTS idx_trigger_problem ON patrol_work_order_trigger(patrol_problem_id); +CREATE INDEX IF NOT EXISTS idx_trigger_order ON patrol_work_order_trigger(work_order_id); \ No newline at end of file diff --git a/frontend/src/views/patrol/WorkOrderManagementView.vue b/frontend/src/views/patrol/WorkOrderManagementView.vue new file mode 100644 index 00000000..02b72cf8 --- /dev/null +++ b/frontend/src/views/patrol/WorkOrderManagementView.vue @@ -0,0 +1,552 @@ + + + + + 工单统计 + + + + + {{ statistics.totalOrders }} + 总工单数 + + + + + {{ statistics.pendingCount }} + 待处理 + + + + + {{ statistics.processingCount }} + 处理中 + + + + + {{ statistics.completedCount }} + 已完成 + + + + + + + + + 工单列表 + + + + + + + + + + 新建工单 + + + + + + + + + + + + {{ getPriorityText(row.priority) }} + + + + + + + {{ getStatusText(row.status) }} + + + + + + + + {{ formatDate(row.createdAt) }} + + + + + {{ row.completionTime ? formatDate(row.completionTime) : '-' }} + + + + + + 查看 + + + 分派 + + + 开始处理 + + + 完成 + + + 取消 + + + + + + + + + + + + + + + {{ currentWorkOrder.orderNo }} + {{ currentWorkOrder.orderType }} + + + {{ getPriorityText(currentWorkOrder.priority) }} + + + + + {{ getStatusText(currentWorkOrder.status) }} + + + {{ currentWorkOrder.assigneeName }} + {{ currentWorkOrder.estimatedDuration }}分钟 + {{ currentWorkOrder.title }} + {{ currentWorkOrder.location }} + {{ currentWorkOrder.description }} + {{ currentWorkOrder.solutionDescription || '-' }} + {{ currentWorkOrder.solutionResult || '-' }} + {{ currentWorkOrder.customerFeedback || '-' }} + + + + + 处理记录 + + + {{ getProcessStepText(record.processStep) }} + {{ record.comment }} + 处理人:{{ record.processorName }} + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 2cb55ca7..210ed8c9 100644 --- a/pom.xml +++ b/pom.xml @@ -122,5 +122,15 @@ spring-boot-starter-test test + + org.apache.poi + poi + 5.2.5 + + + org.apache.poi + poi-ooxml + 5.2.5 + diff --git a/sql/create_sequences.sql b/sql/create_sequences.sql new file mode 100644 index 00000000..9704b430 --- /dev/null +++ b/sql/create_sequences.sql @@ -0,0 +1,11 @@ +-- 创建巡检问题序列 +CREATE SEQUENCE IF NOT EXISTS seq_patrol_problem +INCREMENT 1 +START 1 +NO CYCLE; + +-- 创建工单序列 +CREATE SEQUENCE IF NOT EXISTS seq_work_order +INCREMENT 1 +START 1 +NO CYCLE; \ No newline at end of file diff --git a/wm-common/pom.xml b/wm-common/pom.xml index 6213ec1d..db96a53a 100644 --- a/wm-common/pom.xml +++ b/wm-common/pom.xml @@ -11,7 +11,6 @@ com.github.xiaoyminknife4j-openapi3-jakarta-spring-boot-starter cn.hutoolhutool-all com.alibabaeasyexcel - org.springframework.bootspring-boot-starter-validation - - - + org.springframework.bootspring-boot-starter-validation + + diff --git a/wm-common/src/main/java/com/water/common/handler/JsonListTypeHandler.java b/wm-common/src/main/java/com/water/common/handler/JsonListTypeHandler.java new file mode 100644 index 00000000..92191289 --- /dev/null +++ b/wm-common/src/main/java/com/water/common/handler/JsonListTypeHandler.java @@ -0,0 +1,61 @@ +package com.water.common.handler; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.ibatis.type.JdbcType; +import org.apache.ibatis.type.MappedTypes; +import org.apache.ibatis.type.TypeHandler; + +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; + +@MappedTypes(List.class) +public class JsonListTypeHandler implements TypeHandler> { + + private static final ObjectMapper objectMapper = new ObjectMapper(); + + @Override + public void setParameter(PreparedStatement ps, int i, List parameter, JdbcType jdbcType) throws SQLException { + if (parameter == null) { + ps.setString(i, null); + } else { + try { + ps.setString(i, objectMapper.writeValueAsString(parameter)); + } catch (Exception e) { + throw new SQLException("Error converting list to JSON string", e); + } + } + } + + @Override + public List getResult(ResultSet rs, String columnName) throws SQLException { + String json = rs.getString(columnName); + return parseJson(json); + } + + @Override + public List getResult(ResultSet rs, int columnIndex) throws SQLException { + String json = rs.getString(columnIndex); + return parseJson(json); + } + + @Override + public List getResult(CallableStatement cs, int columnIndex) throws SQLException { + String json = cs.getString(columnIndex); + return parseJson(json); + } + + private List parseJson(String json) { + if (json == null || json.trim().isEmpty()) { + return null; + } + try { + return objectMapper.readValue(json, new TypeReference>() {}); + } catch (Exception e) { + throw new SQLException("Error parsing JSON string to list", e); + } + } +} \ No newline at end of file
{{ record.comment }}
处理人:{{ record.processorName }}