10 Commits
Author SHA1 Message Date
bot_dev2 520b4084eb chore: 添加 wm-dispatch, wm-system 模块到 parent pom 2026-06-14 13:53:15 +08:00
bot_dev2 ce1f69c9bb Merge remote-tracking branch 'origin/feature/issue-16' 2026-06-14 13:53:11 +08:00
bot_dev2 f88eca848b Merge remote-tracking branch 'origin/feature/issue-14' 2026-06-14 13:53:11 +08:00
bot_dev2 653a50f737 Merge remote-tracking branch 'origin/feature/issue-12' 2026-06-14 13:53:11 +08:00
bot_dev2 429aaedba9 Merge remote-tracking branch 'origin/feature/issue-10' 2026-06-14 13:53:11 +08:00
bot_dev2 cf9798be32 feat(wm-mobile-app): #16 移动APP三合一后端服务
- 统一登录: 供水/巡检/收费三模块身份认证
- 供水API聚合: /mobile/water/overview
- 巡检API聚合: /mobile/patrol/tasks
- 收费API聚合: /mobile/billing/summary
- 消息推送: 报警推送/待办提醒/公告通知
- 版本检查: APP更新检测
- Entities: MobileUser/PushNotification/AppVersion
- DDL: 3 张表
2026-06-14 13:53:03 +08:00
bot_dev2 c84bf79d9d feat(wm-system): #14 文档管理与系统管理
文档管理:
- 文档 CRUD / 分类管理 / 版本控制
- DocService + DocController (/system/doc/*)

系统管理:
- 角色管理(5级角色预设)
- 用户管理(新增/编辑/停用)
- 菜单管理 / 部门管理
- 日志管理(登录/操作/异常日志)
- SysService + SysController (/system/*)

DDL: 8 张表 (document/category/version/role/user/menu/department/log)
2026-06-14 13:52:57 +08:00
bot_dev2 bc2fca4e97 feat(wm-dispatch): #12 调度工作台与调度业务管理
- 值班管理: 今日值班/值班日志/交接班
- 指令台账: 创建/下发/跟踪/完成
- 工单管理: 创建/状态流转/优先级
- 调度策略: 常态化/专项应急策略配置
- 应急调度: 预案管理/应急模拟推演
- Entities: DutySchedule/DispatchCommand/WorkOrder/DutyLog/DispatchStrategy/EmergencyPlan
- DDL: 6 张表
2026-06-14 13:52:50 +08:00
bot_dev2 542c038801 feat(wm-production): #10 供水生产总览与在线监测
- DashboardService: 区域定位/进出水量/昨日供水量/设备概况
- WaterQualityService: 水质监测(原水/出厂水/末梢水)/台账
- AlertEngine: 实时报警/确认/派单
- DispatchService: 今日值班/指令下发/爆管模拟
- DataCenterService: 历史数据/报表/阈值管理
- VideoService: 视频监控汇聚
- Entities: WaterQualityRecord/FlowPressureMonitor/DeviceStatus/AlertRecord/VideoCamera
- DDL: prod_water_quality/flow_pressure/device_status/alert_record/video_camera
2026-06-14 13:52:43 +08:00
bot_dev2 c2295fba2c feat(wm-bpm-engine): #2 业务流程引擎
- 流程定义 CRUD + 版本管理 + 发布
- 流程实例启动/列表/状态跟踪
- 待办/已办任务管理(审批/驳回/转办)
- 流程模板快速创建
- 统计评估(完成率/运行中实例)
- DDL: bpm_process_definition/instance/task_item/template
- 增强 wm-bpm 模块(新增 FormTemplate/Orchestration/ProcessNode/ProcessStat/TodoTask)
2026-06-14 13:52:36 +08:00
100 changed files with 2547 additions and 24 deletions
+4
View File
@@ -39,12 +39,16 @@
<module>wm-iot</module>
<module>wm-data-engine</module>
<module>wm-bpm</module>
<module>wm-bpm-engine</module>
<module>wm-production</module>
<module>wm-revenue</module>
<module>wm-patrol</module>
<module>wm-bi</module>
<module>wm-notify</module>
<module>wm-job</module>
<module>wm-dispatch</module>
<module>wm-system</module>
<module>wm-mobile-app</module>
</modules>
<dependencyManagement>
+63
View File
@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.water</groupId>
<artifactId>wm-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>wm-bpm-engine</artifactId>
<name>wm-bpm-engine</name>
<description>BPM 业务流程引擎模块</description>
<dependencies>
<dependency>
<groupId>com.water</groupId>
<artifactId>wm-common</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot3-starter</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,7 @@
package com.water.bpmengine;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class BpmEngineApplication {
public static void main(String[] args) { SpringApplication.run(BpmEngineApplication.class, args); }
}
@@ -0,0 +1,77 @@
package com.water.bpmengine.controller;
import com.water.common.core.result.R;
import com.water.bpmengine.entity.*;
import com.water.bpmengine.service.BpmEngineService;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.*;
@Tag(name = "业务流程引擎")
@RestController @RequestMapping("/bpm") @RequiredArgsConstructor
public class BpmEngineController {
private final BpmEngineService svc;
@GetMapping("/definition/list")
public R<List<ProcessDefinition>> listDefs(
@RequestParam(required=false) String category,
@RequestParam(required=false) Integer status) {
return R.ok(svc.listDefinitions(category, status));
}
@PostMapping("/definition")
public R<Long> createDef(@RequestBody Map<String,Object> req) {
return R.ok(svc.createDefinition(req));
}
@PostMapping("/definition/{id}/publish")
public R<String> publish(@PathVariable Long id) {
svc.publishDefinition(id); return R.ok("OK");
}
@PostMapping("/process/start")
public R<Map<String,Object>> start(
@RequestParam Long definitionId,
@RequestParam String title,
@RequestParam String initiator) {
return R.ok(svc.startProcess(definitionId, title, initiator));
}
@GetMapping("/process/list")
public R<List<ProcessInstance>> listInstances(
@RequestParam(required=false) String initiator,
@RequestParam(required=false) Integer status) {
return R.ok(svc.listInstances(initiator, status));
}
@GetMapping("/task/todo")
public R<List<TaskItem>> todo(@RequestParam String assignee) {
return R.ok(svc.getTodoTasks(assignee));
}
@GetMapping("/task/done")
public R<List<TaskItem>> done(@RequestParam String assignee) {
return R.ok(svc.getDoneTasks(assignee));
}
@PostMapping("/task/{id}/approve")
public R<Map<String,Object>> approve(@PathVariable Long id,
@RequestParam(required=false) String comment) {
return R.ok(svc.approveTask(id, comment));
}
@PostMapping("/task/{id}/reject")
public R<Map<String,Object>> reject(@PathVariable Long id,
@RequestParam(required=false) String comment) {
return R.ok(svc.rejectTask(id, comment));
}
@PostMapping("/task/{id}/transfer")
public R<Map<String,Object>> transfer(@PathVariable Long id,
@RequestParam String newAssignee) {
return R.ok(svc.transferTask(id, newAssignee));
}
@GetMapping("/statistics")
public R<Map<String,Object>> stats(@RequestParam(required=false) String processKey) {
return R.ok(svc.getStatistics(processKey));
}
@GetMapping("/template/list")
public R<List<ProcessTemplate>> listTemplates() {
return R.ok(svc.listTemplates());
}
@PostMapping("/template")
public R<Long> createTemplate(@RequestBody Map<String,Object> req) {
return R.ok(svc.createTemplate(req));
}
}
@@ -0,0 +1,13 @@
package com.water.bpmengine.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data; import java.time.LocalDateTime;
@Data @TableName("bpm_process_definition")
public class ProcessDefinition {
@TableId(type = IdType.AUTO) private Long id;
private String processKey, name, category;
private String bpmnXml;
private Integer version; private Integer status; // 0草稿 1已发布 2已停用
private String description;
@TableField(fill=FieldFill.INSERT) private LocalDateTime createdTime;
@TableField(fill=FieldFill.INSERT_UPDATE) private LocalDateTime updatedTime;
}
@@ -0,0 +1,13 @@
package com.water.bpmengine.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data; import java.time.LocalDateTime;
@Data @TableName("bpm_process_instance")
public class ProcessInstance {
@TableId(type = IdType.AUTO) private Long id;
private Long definitionId; private String processKey;
private String title, initiator;
private Integer status; // 0运行中 1已完成 2已驳回 3已撤销
private String currentNodeName;
@TableField(fill=FieldFill.INSERT) private LocalDateTime createdTime;
private LocalDateTime completedTime;
}
@@ -0,0 +1,11 @@
package com.water.bpmengine.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data; import java.time.LocalDateTime;
@Data @TableName("bpm_process_template")
public class ProcessTemplate {
@TableId(type = IdType.AUTO) private Long id;
private String name, category, description;
private String bpmnXml;
private Integer status;
@TableField(fill=FieldFill.INSERT) private LocalDateTime createdTime;
}
@@ -0,0 +1,13 @@
package com.water.bpmengine.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data; import java.time.LocalDateTime;
@Data @TableName("bpm_task_item")
public class TaskItem {
@TableId(type = IdType.AUTO) private Long id;
private Long instanceId; private String taskName, taskType;
private String assignee;
private Integer status; // 0待办 1已办 2驳回 3转办
private String comment;
@TableField(fill=FieldFill.INSERT) private LocalDateTime createdTime;
private LocalDateTime completedTime;
}
@@ -0,0 +1,5 @@
package com.water.bpmengine.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.bpmengine.entity.ProcessDefinition;
import org.apache.ibatis.annotations.Mapper;
@Mapper public interface ProcessDefinitionMapper extends BaseMapper<ProcessDefinition> {}
@@ -0,0 +1,5 @@
package com.water.bpmengine.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.bpmengine.entity.ProcessInstance;
import org.apache.ibatis.annotations.Mapper;
@Mapper public interface ProcessInstanceMapper extends BaseMapper<ProcessInstance> {}
@@ -0,0 +1,5 @@
package com.water.bpmengine.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.bpmengine.entity.ProcessTemplate;
import org.apache.ibatis.annotations.Mapper;
@Mapper public interface ProcessTemplateMapper extends BaseMapper<ProcessTemplate> {}
@@ -0,0 +1,5 @@
package com.water.bpmengine.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.bpmengine.entity.TaskItem;
import org.apache.ibatis.annotations.Mapper;
@Mapper public interface TaskItemMapper extends BaseMapper<TaskItem> {}
@@ -0,0 +1,122 @@
package com.water.bpmengine.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.water.bpmengine.entity.*;
import com.water.bpmengine.mapper.*;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime; import java.util.*;
@Service @RequiredArgsConstructor
public class BpmEngineService {
private final ProcessDefinitionMapper defMapper;
private final ProcessInstanceMapper instMapper;
private final TaskItemMapper taskMapper;
private final ProcessTemplateMapper tplMapper;
// === 流程定义 ===
public List<ProcessDefinition> listDefinitions(String category, Integer status) {
return defMapper.selectList(new LambdaQueryWrapper<ProcessDefinition>()
.eq(category != null, ProcessDefinition::getCategory, category)
.eq(status != null, ProcessDefinition::getStatus, status));
}
public Long createDefinition(Map<String,Object> req) {
ProcessDefinition d = new ProcessDefinition();
d.setProcessKey((String)req.get("processKey"));
d.setName((String)req.get("name"));
d.setCategory((String)req.get("category"));
d.setBpmnXml((String)req.get("bpmnXml"));
d.setDescription((String)req.get("description"));
d.setVersion(1); d.setStatus(0);
defMapper.insert(d);
return d.getId();
}
public void publishDefinition(Long id) {
ProcessDefinition d = defMapper.selectById(id);
if (d == null) throw new RuntimeException("流程定义不存在");
d.setStatus(1); defMapper.updateById(d);
}
// === 流程实例 ===
public Map<String,Object> startProcess(Long definitionId, String title, String initiator) {
ProcessDefinition d = defMapper.selectById(definitionId);
if (d == null || d.getStatus() != 1) throw new RuntimeException("流程未发布");
ProcessInstance inst = new ProcessInstance();
inst.setDefinitionId(definitionId); inst.setProcessKey(d.getProcessKey());
inst.setTitle(title); inst.setInitiator(initiator);
inst.setStatus(0); inst.setCurrentNodeName("开始");
instMapper.insert(inst);
TaskItem t = new TaskItem();
t.setInstanceId(inst.getId()); t.setTaskName("审批节点");
t.setTaskType("审批"); t.setAssignee(initiator); t.setStatus(0);
taskMapper.insert(t);
return Map.of("instanceId", inst.getId(), "processKey", d.getProcessKey());
}
public List<ProcessInstance> listInstances(String initiator, Integer status) {
return instMapper.selectList(new LambdaQueryWrapper<ProcessInstance>()
.eq(initiator != null, ProcessInstance::getInitiator, initiator)
.eq(status != null, ProcessInstance::getStatus, status));
}
// === 任务处理 ===
public List<TaskItem> getTodoTasks(String assignee) {
return taskMapper.selectList(new LambdaQueryWrapper<TaskItem>()
.eq(TaskItem::getAssignee, assignee).eq(TaskItem::getStatus, 0));
}
public List<TaskItem> getDoneTasks(String assignee) {
return taskMapper.selectList(new LambdaQueryWrapper<TaskItem>()
.eq(TaskItem::getAssignee, assignee).in(TaskItem::getStatus, 1, 2));
}
public Map<String,Object> approveTask(Long taskId, String comment) {
TaskItem t = taskMapper.selectById(taskId);
if (t == null || t.getStatus() != 0) throw new RuntimeException("任务不可审批");
t.setStatus(1); t.setComment(comment); t.setCompletedTime(LocalDateTime.now());
taskMapper.updateById(t);
ProcessInstance inst = instMapper.selectById(t.getInstanceId());
inst.setCurrentNodeName("已完成"); inst.setStatus(1); inst.setCompletedTime(LocalDateTime.now());
instMapper.updateById(inst);
return Map.of("taskId", taskId, "status", "已审批");
}
public Map<String,Object> rejectTask(Long taskId, String comment) {
TaskItem t = taskMapper.selectById(taskId);
if (t == null) throw new RuntimeException("任务不存在");
t.setStatus(2); t.setComment(comment); t.setCompletedTime(LocalDateTime.now());
taskMapper.updateById(t);
ProcessInstance inst = instMapper.selectById(t.getInstanceId());
inst.setStatus(2); inst.setCompletedTime(LocalDateTime.now());
instMapper.updateById(inst);
return Map.of("taskId", taskId, "status", "已驳回");
}
public Map<String,Object> transferTask(Long taskId, String newAssignee) {
TaskItem t = taskMapper.selectById(taskId);
if (t == null) throw new RuntimeException("任务不存在");
t.setStatus(3); t.setCompletedTime(LocalDateTime.now());
taskMapper.updateById(t);
TaskItem nt = new TaskItem();
nt.setInstanceId(t.getInstanceId()); nt.setTaskName(t.getTaskName());
nt.setTaskType(t.getTaskType()); nt.setAssignee(newAssignee); nt.setStatus(0);
taskMapper.insert(nt);
return Map.of("oldTaskId", taskId, "newTaskId", nt.getId());
}
// === 统计 ===
public Map<String,Object> getStatistics(String processKey) {
long total = instMapper.selectCount(new LambdaQueryWrapper<ProcessInstance>()
.eq(processKey != null, ProcessInstance::getProcessKey, processKey));
long completed = instMapper.selectCount(new LambdaQueryWrapper<ProcessInstance>()
.eq(processKey != null, ProcessInstance::getProcessKey, processKey)
.eq(ProcessInstance::getStatus, 1));
return Map.of("total", total, "completed", completed,
"running", total - completed,
"completionRate", total > 0 ? (double)completed / total : 0);
}
// === 模板 ===
public List<ProcessTemplate> listTemplates() { return tplMapper.selectList(null); }
public Long createTemplate(Map<String,Object> req) {
ProcessTemplate t = new ProcessTemplate();
t.setName((String)req.get("name")); t.setCategory((String)req.get("category"));
t.setBpmnXml((String)req.get("bpmnXml")); t.setStatus(1);
tplMapper.insert(t);
return t.getId();
}
}
@@ -0,0 +1,15 @@
server:
port: 9040
spring:
application:
name: wm-bpm-engine
datasource:
url: jdbc:postgresql://localhost:5432/water_bpm
username: water
password: water123
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
global-config:
db-config:
id-type: auto
@@ -0,0 +1,23 @@
-- BPM Engine DDL
CREATE TABLE IF NOT EXISTS bpm_process_definition (
id BIGSERIAL PRIMARY KEY, process_key VARCHAR(100), name VARCHAR(200),
category VARCHAR(50), bpmn_xml TEXT, version INT DEFAULT 1,
status INT DEFAULT 0, description TEXT,
created_time TIMESTAMP DEFAULT NOW(), updated_time TIMESTAMP DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS bpm_process_instance (
id BIGSERIAL PRIMARY KEY, definition_id BIGINT, process_key VARCHAR(100),
title VARCHAR(200), initiator VARCHAR(50), status INT DEFAULT 0,
current_node_name VARCHAR(100),
created_time TIMESTAMP DEFAULT NOW(), completed_time TIMESTAMP
);
CREATE TABLE IF NOT EXISTS bpm_task_item (
id BIGSERIAL PRIMARY KEY, instance_id BIGINT, task_name VARCHAR(100),
task_type VARCHAR(30), assignee VARCHAR(50), status INT DEFAULT 0,
comment TEXT, created_time TIMESTAMP DEFAULT NOW(), completed_time TIMESTAMP
);
CREATE TABLE IF NOT EXISTS bpm_process_template (
id BIGSERIAL PRIMARY KEY, name VARCHAR(200), category VARCHAR(50),
bpmn_xml TEXT, status INT DEFAULT 1,
created_time TIMESTAMP DEFAULT NOW()
);
@@ -1,18 +1,63 @@
package com.water.bpm.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.water.common.core.entity.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
* 审批记录实体
* BPM-03: 流程处理
*/
@Data
public class BpmApprovalRecord {
private Long id;
@EqualsAndHashCode(callSuper = true)
@TableName("bpm_approval_record")
public class BpmApprovalRecord extends BaseEntity {
/** 流程实例ID */
private Long instanceId;
/** 流程实例UUID */
private String instanceUuid;
/** 节点标识 */
private String nodeId;
/** 节点名称 */
private String nodeName;
/** 审批人ID */
private Long approverId;
/** 审批人姓名 */
private String approverName;
private String action; // approve/reject/transfer/delegate/back
/** 审批动作: approve/reject/transfer/delegate/back/countersign */
private String action;
/** 审批意见 */
private String comment;
private String targetAssignee; // 转办/委派目标
/** 转办/委派目标人ID */
private Long targetAssigneeId;
/** 转办/委派目标人姓名 */
private String targetAssigneeName;
/** 会签结果: all/pass_one/veto */
private String countersignResult;
/** 会签通过数 */
private Integer countersignApproved;
/** 会签总数 */
private Integer countersignTotal;
/** 审批时间 */
private LocalDateTime approvedAt;
/** 租户ID */
private String tenantId;
}
@@ -0,0 +1,49 @@
package com.water.bpm.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.water.common.core.entity.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 流程表单模板实体
* BPM-02: 模板化快速创建
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("bpm_form_template")
public class BpmFormTemplate extends BaseEntity {
/** 模板名称 */
private String templateName;
/** 模板编码 */
private String templateCode;
/** 模板分类 */
private String category;
/** 表单 JSON Schema */
private String formSchema;
/** 流程 BPMN XML 模板 */
private String bpmnTemplate;
/** 模板描述 */
private String description;
/** 模板图标 */
private String icon;
/** 使用次数 */
private Integer useCount;
/** 状态: 0-草稿 1-启用 2-停用 */
private Integer status;
/** 创建人 */
private String createdBy;
/** 租户ID */
private String tenantId;
}
@@ -0,0 +1,52 @@
package com.water.bpm.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.water.common.core.entity.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 流程编排实体
* BPM-05: 跨系统流程编排
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("bpm_orchestration")
public class BpmOrchestration extends BaseEntity {
/** 编排名称 */
private String orchestrationName;
/** 编排编码 */
private String orchestrationCode;
/** 编排描述 */
private String description;
/** 包含的流程定义ID列表 JSON */
private String processDefinitionIds;
/** 编排规则 JSON (流程间依赖关系、触发条件) */
private String orchestrationRules;
/** 触发方式: manual/scheduled/event */
private String triggerType;
/** 定时表达式 (cron) */
private String cronExpression;
/** 事件名称 (event触发时) */
private String eventName;
/** 状态: 0-草稿 1-启用 2-停用 */
private Integer status;
/** 创建人 */
private String createdBy;
/** 执行次数 */
private Integer executionCount;
/** 租户ID */
private String tenantId;
}
@@ -1,20 +1,58 @@
package com.water.bpm.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.water.common.core.entity.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
* 流程定义实体
* BPM-01: 流程定义管理
*/
@Data
public class BpmProcessDefinition {
private Long id;
@EqualsAndHashCode(callSuper = true)
@TableName("bpm_process_definition")
public class BpmProcessDefinition extends BaseEntity {
/** 流程唯一标识 */
private String processKey;
/** 流程名称 */
private String processName;
/** 流程描述 */
private String description;
private String bpmnXml; // BPMN 2.0 XML
private String formSchema; // 表单 JSON Schema
private String category; // revenue/patrol/dispatch/maintenance
/** BPMN 2.0 XML 定义 */
private String bpmnXml;
/** 表单 JSON Schema */
private String formSchema;
/** 流程分类: revenue/patrol/dispatch/maintenance/inspection */
private String category;
/** 版本号 */
@TableField("version")
private Integer version;
private Integer status; // 0:草稿 1:发布 2:停用
/** 状态: 0-草稿 1-已发布 2-已停用 */
private Integer status;
/** 创建人 */
private String createdBy;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
/** 流程图标 */
private String icon;
/** 排序号 */
private Integer sortOrder;
/** 发布时间 */
private LocalDateTime publishedAt;
/** 租户ID */
private String tenantId;
}
@@ -1,26 +1,84 @@
package com.water.bpm.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.water.common.core.entity.BaseEntity;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.Map;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
* 流程实例实体
* BPM-03: 流程处理
*/
@Data
public class BpmProcessInstance {
private Long id;
@EqualsAndHashCode(callSuper = true)
@TableName("bpm_process_instance")
public class BpmProcessInstance extends BaseEntity {
/** 实例唯一ID */
private String instanceId;
/** 关联流程定义ID */
private Long definitionId;
/** 流程标识 */
private String processKey;
private String businessKey; // 关联业务ID
private String businessType; // 业务类型
/** 流程名称 */
private String processName;
/** 业务主键(关联业务表) */
private String businessKey;
/** 业务类型 */
private String businessType;
/** 流程标题 */
private String title;
/** 发起人ID */
private Long initiatorId;
/** 发起人姓名 */
private String initiatorName;
private String currentNode; // 当前审批节点
private String currentAssignee; // 当前处理人
private String status; // running/completed/terminated/rejected
private Map<String, Object> variables;
private Map<String, Object> formData;
/** 当前节点标识 */
private String currentNodeId;
/** 当前节点名称 */
private String currentNodeName;
/** 当前处理人ID */
private Long currentAssigneeId;
/** 当前处理人姓名 */
private String currentAssigneeName;
/** 状态: running/completed/terminated/rejected/suspended */
private String status;
/** 优先级: 0-普通 1-紧急 2-特急 */
private Integer priority;
/** 流程变量 JSON */
private String variables;
/** 表单数据 JSON */
private String formData;
/** 开始时间 */
private LocalDateTime startedAt;
/** 结束时间 */
private LocalDateTime completedAt;
private LocalDateTime createdAt;
/** 预计完成时间 */
private LocalDateTime expectedCompletionAt;
/** 耗时(秒) */
private Long durationSeconds;
/** 租户ID */
private String tenantId;
}
@@ -0,0 +1,61 @@
package com.water.bpm.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.water.common.core.entity.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 流程节点定义实体
* BPM-01: 流程定义
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("bpm_process_node")
public class BpmProcessNode extends BaseEntity {
/** 关联流程定义ID */
private Long definitionId;
/** 节点标识 */
private String nodeId;
/** 节点名称 */
private String nodeName;
/** 节点类型: start/end/userTask/serviceTask/gateway/subprocess/timer */
private String nodeType;
/** 处理人类型: role/user/department/position/initiator */
private String assigneeType;
/** 处理人值(角色ID/用户ID/部门ID等) */
private String assigneeValue;
/** 处理人名称(冗余) */
private String assigneeName;
/** 多人审批方式: sequential(会签)/parallel(或签)/countersign(比例签) */
private String multiInstanceType;
/** 会签通过比例 */
private Integer countersignRate;
/** 超时时间(小时) */
private Integer timeoutHours;
/** 超时处理: remind/transfer/escalate/auto_approve */
private String timeoutAction;
/** 表单权限 JSON */
private String formPermission;
/** 节点条件表达式 */
private String conditionExpression;
/** 排序号 */
private Integer sortOrder;
/** 租户ID */
private String tenantId;
}
@@ -0,0 +1,63 @@
package com.water.bpm.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.water.common.core.entity.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
* 流程统计实体
* BPM-04: 统计评估
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("bpm_process_stat")
public class BpmProcessStat extends BaseEntity {
/** 流程定义ID */
private Long definitionId;
/** 流程标识 */
private String processKey;
/** 统计周期: day/week/month/quarter/year */
private String period;
/** 统计日期 */
private String statDate;
/** 发起总数 */
private Integer startCount;
/** 完成总数 */
private Integer completedCount;
/** 驳回总数 */
private Integer rejectedCount;
/** 撤回总数 */
private Integer terminatedCount;
/** 平均耗时(秒) */
private Long avgDurationSeconds;
/** 最长耗时(秒) */
private Long maxDurationSeconds;
/** 最短耗时(秒) */
private Long minDurationSeconds;
/** 平均节点耗时(秒) */
private Long avgNodeDurationSeconds;
/** 超时任务数 */
private Integer timeoutCount;
/** 一次通过率(百分比) */
private Double firstPassRate;
/** 租户ID */
private String tenantId;
}
@@ -0,0 +1,75 @@
package com.water.bpm.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.water.common.core.entity.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
* 待办任务实体
* BPM-06: 待办/已办中心
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("bpm_todo_task")
public class BpmTodoTask extends BaseEntity {
/** 流程实例ID */
private Long instanceId;
/** 流程实例UUID */
private String instanceUuid;
/** 流程标题 */
private String title;
/** 流程标识 */
private String processKey;
/** 流程名称 */
private String processName;
/** 节点标识 */
private String nodeId;
/** 节点名称 */
private String nodeName;
/** 处理人ID */
private Long assigneeId;
/** 处理人姓名 */
private String assigneeName;
/** 发起人ID */
private Long initiatorId;
/** 发起人姓名 */
private String initiatorName;
/** 业务主键 */
private String businessKey;
/** 任务状态: pending/completed/transferred/delegated */
private String status;
/** 优先级: 0-普通 1-紧急 2-特急 */
private Integer priority;
/** 接收时间 */
private LocalDateTime receivedAt;
/** 完成时间 */
private LocalDateTime completedAt;
/** 超时时间 */
private LocalDateTime deadlineAt;
/** 是否已读 */
private Boolean isRead;
/** 租户ID */
private String tenantId;
}
@@ -0,0 +1,33 @@
package com.water.bpm.entity.dto;
import lombok.Data;
/**
* 审批请求 DTO
*/
@Data
public class ApprovalRequest {
/** 流程实例ID */
private Long instanceId;
/** 流程实例UUID */
private String instanceUuid;
/** 节点标识 */
private String nodeId;
/** 节点名称 */
private String nodeName;
/** 审批动作: approve/reject/transfer/delegate/back/countersign */
private String action;
/** 审批意见 */
private String comment;
/** 转办/委派目标人ID */
private Long targetAssigneeId;
/** 转办/委派目标人姓名 */
private String targetAssigneeName;
}
@@ -0,0 +1,41 @@
package com.water.bpm.entity.dto;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 流程实例查询 DTO
*/
@Data
public class ProcessInstanceQuery {
/** 流程标识 */
private String processKey;
/** 流程标题 */
private String title;
/** 状态 */
private String status;
/** 发起人ID */
private Long initiatorId;
/** 业务主键 */
private String businessKey;
/** 业务类型 */
private String businessType;
/** 开始时间(起) */
private LocalDateTime startTimeFrom;
/** 开始时间(止) */
private LocalDateTime startTimeTo;
/** 页码 */
private Integer pageNum = 1;
/** 每页条数 */
private Integer pageSize = 10;
}
@@ -0,0 +1,32 @@
package com.water.bpm.entity.dto;
import lombok.Data;
import java.util.Map;
/**
* 流程发起请求 DTO
*/
@Data
public class ProcessStartRequest {
/** 流程定义ID */
private Long definitionId;
/** 业务主键 */
private String businessKey;
/** 业务类型 */
private String businessType;
/** 流程标题 */
private String title;
/** 表单数据 */
private Map<String, Object> formData;
/** 流程变量 */
private Map<String, Object> variables;
/** 优先级 */
private Integer priority;
}
@@ -0,0 +1,60 @@
package com.water.bpm.entity.dto;
import lombok.Data;
import java.util.List;
import java.util.Map;
/**
* 流程统计 VO
*/
@Data
public class ProcessStatVO {
/** 流程定义ID */
private Long definitionId;
/** 流程名称 */
private String processName;
/** 流程标识 */
private String processKey;
/** 总实例数 */
private Integer totalInstances;
/** 运行中数量 */
private Integer runningCount;
/** 已完成数量 */
private Integer completedCount;
/** 已驳回数量 */
private Integer rejectedCount;
/** 已撤回数量 */
private Integer terminatedCount;
/** 平均耗时(小时) */
private Double avgDurationHours;
/** 最长耗时(小时) */
private Double maxDurationHours;
/** 最短耗时(小时) */
private Double minDurationHours;
/** 一次通过率 */
private Double firstPassRate;
/** 超时率 */
private Double timeoutRate;
/** 各节点平均耗时 */
private List<Map<String, Object>> nodeAvgDurations;
/** 瓶颈节点(耗时最长) */
private String bottleneckNode;
/** 趋势数据 */
private List<Map<String, Object>> trendData;
}
@@ -0,0 +1,39 @@
package com.water.bpm.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.bpm.entity.BpmApprovalRecord;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* 审批记录 Mapper
*/
@Mapper
public interface BpmApprovalRecordMapper extends BaseMapper<BpmApprovalRecord> {
/**
* 根据流程实例查询审批记录
*/
@Select("SELECT * FROM bpm_approval_record WHERE instance_id = #{instanceId} AND deleted = 0 ORDER BY approved_at")
List<BpmApprovalRecord> selectByInstanceId(@Param("instanceId") Long instanceId);
/**
* 根据流程实例UUID查询审批记录
*/
@Select("SELECT * FROM bpm_approval_record WHERE instance_uuid = #{instanceUuid} AND deleted = 0 ORDER BY approved_at")
List<BpmApprovalRecord> selectByInstanceUuid(@Param("instanceUuid") String instanceUuid);
/**
* 查询某人的审批统计
*/
@Select("SELECT approver_id, approver_name, COUNT(*) as total, " +
"SUM(CASE WHEN action = 'approve' THEN 1 ELSE 0 END) as approved, " +
"SUM(CASE WHEN action = 'reject' THEN 1 ELSE 0 END) as rejected, " +
"AVG(EXTRACT(EPOCH FROM (approved_at - created_at))) as avg_handle_seconds " +
"FROM bpm_approval_record WHERE deleted = 0 AND approved_at >= #{startDate} " +
"GROUP BY approver_id, approver_name")
List<java.util.Map<String, Object>> statByApprover(@Param("startDate") java.time.LocalDateTime startDate);
}
@@ -0,0 +1,28 @@
package com.water.bpm.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.bpm.entity.BpmFormTemplate;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* 表单模板 Mapper
*/
@Mapper
public interface BpmFormTemplateMapper extends BaseMapper<BpmFormTemplate> {
/**
* 根据分类查询模板
*/
@Select("SELECT * FROM bpm_form_template WHERE category = #{category} AND status = 1 AND deleted = 0 ORDER BY use_count DESC")
List<BpmFormTemplate> selectByCategory(@Param("category") String category);
/**
* 查询热门模板
*/
@Select("SELECT * FROM bpm_form_template WHERE status = 1 AND deleted = 0 ORDER BY use_count DESC LIMIT #{limit}")
List<BpmFormTemplate> selectHotTemplates(@Param("limit") int limit);
}
@@ -0,0 +1,28 @@
package com.water.bpm.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.bpm.entity.BpmOrchestration;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* 流程编排 Mapper
*/
@Mapper
public interface BpmOrchestrationMapper extends BaseMapper<BpmOrchestration> {
/**
* 查询所有启用的编排
*/
@Select("SELECT * FROM bpm_orchestration WHERE status = 1 AND deleted = 0 ORDER BY created_at DESC")
List<BpmOrchestration> selectEnabled();
/**
* 查询事件触发的编排
*/
@Select("SELECT * FROM bpm_orchestration WHERE trigger_type = 'event' AND event_name = #{eventName} " +
"AND status = 1 AND deleted = 0")
List<BpmOrchestration> selectByEvent(String eventName);
}
@@ -0,0 +1,34 @@
package com.water.bpm.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.bpm.entity.BpmProcessDefinition;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* 流程定义 Mapper
*/
@Mapper
public interface BpmProcessDefinitionMapper extends BaseMapper<BpmProcessDefinition> {
/**
* 根据分类查询已发布的流程定义
*/
@Select("SELECT * FROM bpm_process_definition WHERE category = #{category} AND status = 1 AND deleted = 0 ORDER BY sort_order")
List<BpmProcessDefinition> selectByCategory(@Param("category") String category);
/**
* 根据 processKey 查询最新版本
*/
@Select("SELECT * FROM bpm_process_definition WHERE process_key = #{processKey} AND deleted = 0 ORDER BY version DESC LIMIT 1")
BpmProcessDefinition selectByProcessKey(@Param("processKey") String processKey);
/**
* 查询所有分类
*/
@Select("SELECT DISTINCT category FROM bpm_process_definition WHERE deleted = 0 AND status = 1 ORDER BY category")
List<String> selectAllCategories();
}
@@ -0,0 +1,43 @@
package com.water.bpm.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.bpm.entity.BpmProcessInstance;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
import java.util.Map;
/**
* 流程实例 Mapper
*/
@Mapper
public interface BpmProcessInstanceMapper extends BaseMapper<BpmProcessInstance> {
/**
* 统计各状态数量
*/
@Select("SELECT status, COUNT(*) as count FROM bpm_process_instance WHERE deleted = 0 GROUP BY status")
List<Map<String, Object>> countByStatus();
/**
* 根据流程定义统计
*/
@Select("SELECT definition_id, COUNT(*) as total, " +
"SUM(CASE WHEN status = 'running' THEN 1 ELSE 0 END) as running, " +
"SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as completed, " +
"SUM(CASE WHEN status = 'rejected' THEN 1 ELSE 0 END) as rejected, " +
"SUM(CASE WHEN status = 'terminated' THEN 1 ELSE 0 END) as terminated, " +
"AVG(duration_seconds) as avg_duration, " +
"MAX(duration_seconds) as max_duration, " +
"MIN(duration_seconds) as min_duration " +
"FROM bpm_process_instance WHERE deleted = 0 GROUP BY definition_id")
List<Map<String, Object>> statByDefinition();
/**
* 查询我发起的流程
*/
@Select("SELECT * FROM bpm_process_instance WHERE initiator_id = #{userId} AND deleted = 0 ORDER BY created_at DESC")
List<BpmProcessInstance> selectMyInitiated(@Param("userId") Long userId);
}
@@ -0,0 +1,22 @@
package com.water.bpm.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.bpm.entity.BpmProcessNode;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* 流程节点 Mapper
*/
@Mapper
public interface BpmProcessNodeMapper extends BaseMapper<BpmProcessNode> {
/**
* 查询流程定义的所有节点
*/
@Select("SELECT * FROM bpm_process_node WHERE definition_id = #{definitionId} AND deleted = 0 ORDER BY sort_order")
List<BpmProcessNode> selectByDefinitionId(@Param("definitionId") Long definitionId);
}
@@ -0,0 +1,25 @@
package com.water.bpm.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.bpm.entity.BpmProcessStat;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* 流程统计 Mapper
*/
@Mapper
public interface BpmProcessStatMapper extends BaseMapper<BpmProcessStat> {
/**
* 查询某流程的统计趋势
*/
@Select("SELECT * FROM bpm_process_stat WHERE definition_id = #{definitionId} AND period = #{period} " +
"AND deleted = 0 ORDER BY stat_date DESC LIMIT #{limit}")
List<BpmProcessStat> selectTrend(@Param("definitionId") Long definitionId,
@Param("period") String period,
@Param("limit") int limit);
}
@@ -0,0 +1,42 @@
package com.water.bpm.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.bpm.entity.BpmTodoTask;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* 待办任务 Mapper
*/
@Mapper
public interface BpmTodoTaskMapper extends BaseMapper<BpmTodoTask> {
/**
* 查询待办列表
*/
@Select("SELECT * FROM bpm_todo_task WHERE assignee_id = #{userId} AND status = 'pending' AND deleted = 0 " +
"ORDER BY priority DESC, received_at")
List<BpmTodoTask> selectPendingByUserId(@Param("userId") Long userId);
/**
* 查询已办列表
*/
@Select("SELECT * FROM bpm_todo_task WHERE assignee_id = #{userId} AND status != 'pending' AND deleted = 0 " +
"ORDER BY completed_at DESC")
List<BpmTodoTask> selectDoneByUserId(@Param("userId") Long userId);
/**
* 统计待办数量
*/
@Select("SELECT COUNT(*) FROM bpm_todo_task WHERE assignee_id = #{userId} AND status = 'pending' AND deleted = 0")
Integer countPendingByUserId(@Param("userId") Long userId);
/**
* 查询超时的待办
*/
@Select("SELECT * FROM bpm_todo_task WHERE status = 'pending' AND deadline_at < NOW() AND deleted = 0")
List<BpmTodoTask> selectTimeoutTasks();
}
+45
View File
@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.water</groupId>
<artifactId>wm-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>wm-dispatch</artifactId>
<name>调度工作台</name>
<description>调度工作台与调度业务管理模块</description>
<dependencies>
<dependency>
<groupId>com.water</groupId>
<artifactId>wm-common</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot3-starter</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,11 @@
package com.water.dispatch;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DispatchApplication {
public static void main(String[] args) {
SpringApplication.run(DispatchApplication.class, args);
}
}
@@ -0,0 +1,24 @@
package com.water.dispatch.controller;
import com.water.common.core.result.R;
import com.water.dispatch.entity.*;
import com.water.dispatch.service.DispatchBizService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.*;
@Tag(name="调度工作台") @RestController @RequestMapping("/dispatch") @RequiredArgsConstructor
public class DispatchController {
private final DispatchBizService svc;
@GetMapping("/duty/today") public R<List<DutySchedule>> todayDuty() { return R.ok(svc.getTodayDuty()); }
@PostMapping("/command") public R<Map<String,Object>> createCommand(@RequestBody Map<String,Object> req) { return R.ok(svc.createCommand(req)); }
@PostMapping("/command/{cmdNo}/issue") public R<Map<String,Object>> issue(@PathVariable String cmdNo) { return R.ok(svc.issueCommand(cmdNo)); }
@GetMapping("/command/list") public R<List<DispatchCommand>> listCommands(@RequestParam(required=false) Integer status) { return R.ok(svc.listCommands(status)); }
@PostMapping("/work-order") public R<Map<String,Object>> createWO(@RequestBody Map<String,Object> req) { return R.ok(svc.createWorkOrder(req)); }
@PutMapping("/work-order/{id}/status") public R<Map<String,Object>> updateWOStatus(@PathVariable Long id, @RequestParam int status) { return R.ok(svc.updateWorkOrderStatus(id, status)); }
@PostMapping("/duty-log") public R<String> addLog(@RequestParam Long scheduleId, @RequestParam Long userId, @RequestParam String type, @RequestParam String content) { svc.addDutyLog(scheduleId,userId,type,content); return R.ok("OK"); }
@GetMapping("/duty-log/{scheduleId}") public R<List<DutyLog>> getLogs(@PathVariable Long scheduleId) { return R.ok(svc.getDutyLogs(scheduleId)); }
@GetMapping("/strategy") public R<List<DispatchStrategy>> listStrategies(@RequestParam(required=false) String type) { return R.ok(svc.listStrategies(type)); }
@GetMapping("/emergency-plan") public R<List<EmergencyPlan>> listPlans(@RequestParam(required=false) String type) { return R.ok(svc.listPlans(type)); }
@PostMapping("/emergency/simulate") public R<Map<String,Object>> simulate(@RequestParam String type, @RequestParam double lng, @RequestParam double lat) { return R.ok(svc.simulateEmergency(type,lng,lat)); }
}
@@ -0,0 +1,78 @@
package com.water.dispatch.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 调度指令 - 全生命周期: 下发→接收→执行→完成→驳回
*/
@Data
@TableName("disp_dispatch_command")
public class DispatchCommand {
@TableId(type = IdType.AUTO)
private Long id;
/** 指令编号 */
private String commandNo;
/** 指令标题 */
private String title;
/** 指令内容 */
private String content;
/** 指令类型: NORMAL-常规 EMERGENCY-应急 MAINTENANCE-维护 */
private String commandType;
/** 优先级: LOW-低 MEDIUM-中 HIGH-高 URGENT-紧急 */
private String priority;
/** 下发人ID */
private Long issuerId;
/** 下发人姓名 */
private String issuerName;
/** 接收人ID */
private Long receiverId;
/** 接收人姓名 */
private String receiverName;
/** 关联设施ID */
private Long facilityId;
/** 状态: ISSUED-下发 RECEIVED-接收 EXECUTING-执行 COMPLETED-完成 REJECTED-驳回 CANCELLED-取消 */
private String status;
/** 下发时间 */
private LocalDateTime issuedAt;
/** 接收时间 */
private LocalDateTime receivedAt;
/** 执行开始时间 */
private LocalDateTime executedAt;
/** 完成时间 */
private LocalDateTime completedAt;
/** 驳回原因 */
private String rejectReason;
/** 执行结果 */
private String executeResult;
/** 截止时间 */
private LocalDateTime deadline;
@TableLogic
private Integer deleted;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
@@ -0,0 +1,11 @@
package com.water.dispatch.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data; import java.time.LocalDateTime;
@Data @TableName("dispatch_strategy")
public class DispatchStrategy {
@TableId(type = IdType.AUTO) private Long id;
private String name, type, description, ruleConfig;
private Integer status;
@TableField(fill=FieldFill.INSERT) private LocalDateTime createdTime;
@TableField(fill=FieldFill.INSERT_UPDATE) private LocalDateTime updatedTime;
}
@@ -0,0 +1,10 @@
package com.water.dispatch.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data; import java.time.LocalDateTime;
@Data @TableName("dispatch_duty_log")
public class DutyLog {
@TableId(type = IdType.AUTO) private Long id;
private Long scheduleId, userId;
private String logType, content, attachments;
@TableField(fill=FieldFill.INSERT) private LocalDateTime createdTime;
}
@@ -0,0 +1,50 @@
package com.water.dispatch.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
/**
* 值班安排
*/
@Data
@TableName("disp_duty_schedule")
public class DutySchedule {
@TableId(type = IdType.AUTO)
private Long id;
/** 值班人员ID */
private Long userId;
/** 值班人员姓名 */
private String userName;
/** 值班日期 */
private LocalDate dutyDate;
/** 班次类型: DAY-白班 NIGHT-夜班 FULL-全天 */
private String shiftType;
/** 开始时间 */
private LocalTime startTime;
/** 结束时间 */
private LocalTime endTime;
/** 状态: 0-待值班 1-值班中 2-已完成 */
private Integer status;
/** 备注 */
private String remark;
@TableLogic
private Integer deleted;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
@@ -0,0 +1,11 @@
package com.water.dispatch.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data; import java.time.LocalDateTime;
@Data @TableName("dispatch_emergency_plan")
public class EmergencyPlan {
@TableId(type = IdType.AUTO) private Long id;
private String planNo, name, type, content, resourceConfig;
private Integer status; private Long creatorId;
@TableField(fill=FieldFill.INSERT) private LocalDateTime createdTime;
@TableField(fill=FieldFill.INSERT_UPDATE) private LocalDateTime updatedTime;
}
@@ -0,0 +1,12 @@
package com.water.dispatch.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data; import java.time.LocalDateTime;
@Data @TableName("dispatch_work_order")
public class WorkOrder {
@TableId(type = IdType.AUTO) private Long id;
private String orderNo, title, description, type, priority;
private Integer status; private Long assigneeId, creatorId;
private LocalDateTime deadline, completedAt;
@TableField(fill=FieldFill.INSERT) private LocalDateTime createdTime;
@TableField(fill=FieldFill.INSERT_UPDATE) private LocalDateTime updatedTime;
}
@@ -0,0 +1,5 @@
package com.water.dispatch.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.dispatch.entity.DispatchCommand;
import org.apache.ibatis.annotations.Mapper;
@Mapper public interface DispatchCommandMapper extends BaseMapper<DispatchCommand> {}
@@ -0,0 +1,5 @@
package com.water.dispatch.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.dispatch.entity.DispatchStrategy;
import org.apache.ibatis.annotations.Mapper;
@Mapper public interface DispatchStrategyMapper extends BaseMapper<DispatchStrategy> {}
@@ -0,0 +1,5 @@
package com.water.dispatch.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.dispatch.entity.DutyLog;
import org.apache.ibatis.annotations.Mapper;
@Mapper public interface DutyLogMapper extends BaseMapper<DutyLog> {}
@@ -0,0 +1,5 @@
package com.water.dispatch.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.dispatch.entity.DutySchedule;
import org.apache.ibatis.annotations.Mapper;
@Mapper public interface DutyScheduleMapper extends BaseMapper<DutySchedule> {}
@@ -0,0 +1,5 @@
package com.water.dispatch.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.dispatch.entity.EmergencyPlan;
import org.apache.ibatis.annotations.Mapper;
@Mapper public interface EmergencyPlanMapper extends BaseMapper<EmergencyPlan> {}
@@ -0,0 +1,5 @@
package com.water.dispatch.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.dispatch.entity.WorkOrder;
import org.apache.ibatis.annotations.Mapper;
@Mapper public interface WorkOrderMapper extends BaseMapper<WorkOrder> {}
@@ -0,0 +1,81 @@
package com.water.dispatch.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.water.dispatch.entity.*;
import com.water.dispatch.mapper.*;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.time.*; import java.util.*;
@Service @RequiredArgsConstructor
public class DispatchBizService {
private final DutyScheduleMapper dutyMapper;
private final DispatchCommandMapper cmdMapper;
private final WorkOrderMapper woMapper;
private final DutyLogMapper logMapper;
private final DispatchStrategyMapper stratMapper;
private final EmergencyPlanMapper planMapper;
public List<DutySchedule> getTodayDuty() {
return dutyMapper.selectList(new LambdaQueryWrapper<DutySchedule>()
.eq(DutySchedule::getDutyDate, LocalDate.now()));
}
public Map<String,Object> createCommand(Map<String,Object> req) {
DispatchCommand c = new DispatchCommand();
c.setCmdNo("CMD-" + System.currentTimeMillis());
c.setTitle((String)req.get("title")); c.setContent((String)req.get("content"));
c.setType((String)req.getOrDefault("type","常规")); c.setStatus(0);
cmdMapper.insert(c);
return Map.of("id",c.getId(),"cmdNo",c.getCmdNo());
}
public Map<String,Object> issueCommand(String cmdNo) {
DispatchCommand c = cmdMapper.selectOne(new LambdaQueryWrapper<DispatchCommand>()
.eq(DispatchCommand::getCmdNo, cmdNo));
if(c==null) throw new RuntimeException("指令不存在");
c.setStatus(1); c.setIssuedTime(LocalDateTime.now());
cmdMapper.updateById(c);
return Map.of("cmdNo",cmdNo,"status",1);
}
public List<DispatchCommand> listCommands(Integer status) {
return cmdMapper.selectList(new LambdaQueryWrapper<DispatchCommand>()
.eq(status!=null, DispatchCommand::getStatus, status));
}
public Map<String,Object> createWorkOrder(Map<String,Object> req) {
WorkOrder w = new WorkOrder();
w.setOrderNo("WO-" + System.currentTimeMillis());
w.setTitle((String)req.get("title")); w.setDescription((String)req.get("description"));
w.setType((String)req.getOrDefault("type","维修")); w.setStatus(0);
w.setPriority((String)req.getOrDefault("priority","中"));
woMapper.insert(w);
return Map.of("id",w.getId(),"orderNo",w.getOrderNo());
}
public Map<String,Object> updateWorkOrderStatus(Long id, int status) {
WorkOrder w = woMapper.selectById(id);
if(w==null) throw new RuntimeException("工单不存在");
w.setStatus(status);
if(status==2) w.setCompletedAt(LocalDateTime.now());
woMapper.updateById(w);
return Map.of("id",id,"status",status);
}
public void addDutyLog(Long scheduleId, Long userId, String type, String content) {
DutyLog l = new DutyLog();
l.setScheduleId(scheduleId); l.setUserId(userId);
l.setLogType(type); l.setContent(content);
logMapper.insert(l);
}
public List<DutyLog> getDutyLogs(Long scheduleId) {
return logMapper.selectList(new LambdaQueryWrapper<DutyLog>()
.eq(DutyLog::getScheduleId, scheduleId));
}
public List<DispatchStrategy> listStrategies(String type) {
return stratMapper.selectList(new LambdaQueryWrapper<DispatchStrategy>()
.eq(type!=null, DispatchStrategy::getType, type));
}
public List<EmergencyPlan> listPlans(String type) {
return planMapper.selectList(new LambdaQueryWrapper<EmergencyPlan>()
.eq(type!=null, EmergencyPlan::getType, type));
}
public Map<String,Object> simulateEmergency(String type, double lng, double lat) {
return Map.of("type",type,"lng",lng,"lat",lat,
"affectedArea","半径500米","affectedUsers",120,
"estimatedDuration","4小时");
}
}
@@ -0,0 +1,15 @@
server:
port: 9050
spring:
application:
name: wm-dispatch
datasource:
url: jdbc:postgresql://localhost:5432/water_dispatch
username: water
password: water123
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
global-config:
db-config:
id-type: auto
@@ -0,0 +1,33 @@
CREATE TABLE IF NOT EXISTS dispatch_duty_schedule (
id BIGSERIAL PRIMARY KEY, user_id BIGINT, user_name VARCHAR(50),
duty_date DATE, start_time TIME, end_time TIME, area VARCHAR(100),
created_time TIMESTAMP DEFAULT NOW(), updated_time TIMESTAMP DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS dispatch_command (
id BIGSERIAL PRIMARY KEY, cmd_no VARCHAR(50) UNIQUE, title VARCHAR(200),
content TEXT, type VARCHAR(20), status INT DEFAULT 0,
issued_time TIMESTAMP, completed_time TIMESTAMP,
created_time TIMESTAMP DEFAULT NOW(), updated_time TIMESTAMP DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS dispatch_work_order (
id BIGSERIAL PRIMARY KEY, order_no VARCHAR(50) UNIQUE, title VARCHAR(200),
description TEXT, type VARCHAR(20), priority VARCHAR(10), status INT DEFAULT 0,
assignee_id BIGINT, creator_id BIGINT, deadline TIMESTAMP, completed_at TIMESTAMP,
created_time TIMESTAMP DEFAULT NOW(), updated_time TIMESTAMP DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS dispatch_duty_log (
id BIGSERIAL PRIMARY KEY, schedule_id BIGINT, user_id BIGINT,
log_type VARCHAR(20), content TEXT, attachments TEXT,
created_time TIMESTAMP DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS dispatch_strategy (
id BIGSERIAL PRIMARY KEY, name VARCHAR(100), type VARCHAR(20),
description TEXT, rule_config TEXT, status INT DEFAULT 1,
created_time TIMESTAMP DEFAULT NOW(), updated_time TIMESTAMP DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS dispatch_emergency_plan (
id BIGSERIAL PRIMARY KEY, plan_no VARCHAR(50) UNIQUE, name VARCHAR(100),
type VARCHAR(20), content TEXT, resource_config TEXT,
status INT DEFAULT 0, creator_id BIGINT,
created_time TIMESTAMP DEFAULT NOW(), updated_time TIMESTAMP DEFAULT NOW()
);
+45
View File
@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent><groupId>com.water</groupId><artifactId>wm-parent</artifactId><version>1.0.0-SNAPSHOT</version></parent>
<artifactId>wm-mobile-app</artifactId>
<name>wm-mobile-app</name>
<description>移动APP三合一后端服务层 - 供水/巡检/营业收费统一入口</description>
<dependencies>
<dependency><groupId>com.water</groupId><artifactId>wm-common</artifactId></dependency>
<!-- Web -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
<!-- Nacos Discovery -->
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency>
<!-- MyBatis-Plus -->
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-spring-boot3-starter</artifactId></dependency>
<!-- Sa-Token -->
<dependency><groupId>cn.dev33</groupId><artifactId>sa-token-spring-boot3-starter</artifactId></dependency>
<!-- PostgreSQL -->
<dependency><groupId>org.postgresql</groupId><artifactId>postgresql</artifactId></dependency>
<!-- Knife4j Swagger -->
<dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId></dependency>
<!-- Test -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration><excludes><exclude><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></exclude></excludes></configuration>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,16 @@
package com.water.mobile;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@ComponentScan(basePackages = {"com.water.mobile", "com.water.common"})
@MapperScan("com.water.mobile.mapper")
public class MobileApplication {
public static void main(String[] args) {
SpringApplication.run(MobileApplication.class, args);
}
}
@@ -0,0 +1,20 @@
package com.water.mobile.controller;
import com.water.common.core.result.R;
import com.water.mobile.entity.PushNotification;
import com.water.mobile.service.MobileApiService;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.*;
@Tag(name="移动APP") @RestController @RequestMapping("/mobile") @RequiredArgsConstructor
public class MobileController {
private final MobileApiService svc;
@PostMapping("/login") public R<Map<String,Object>> login(@RequestParam String username, @RequestParam String password, @RequestParam String appModule) { return R.ok(svc.login(username, password, appModule)); }
@GetMapping("/water/overview") public R<Map<String,Object>> waterOverview() { return R.ok(svc.getWaterOverview()); }
@GetMapping("/patrol/tasks") public R<List<Map<String,Object>>> patrolTasks(@RequestParam Long userId) { return R.ok(svc.getPatrolTasks(userId)); }
@GetMapping("/billing/summary") public R<Map<String,Object>> billingSummary(@RequestParam Long userId) { return R.ok(svc.getBillingSummary(userId)); }
@GetMapping("/notifications") public R<List<PushNotification>> notifications(@RequestParam Long userId, @RequestParam(required=false) Boolean unreadOnly) { return R.ok(svc.getNotifications(userId, unreadOnly)); }
@PutMapping("/notifications/{id}/read") public R<String> markRead(@PathVariable Long id) { svc.markRead(id); return R.ok("OK"); }
@PostMapping("/notifications/send") public R<Long> send(@RequestParam Long userId, @RequestParam String title, @RequestParam String content, @RequestParam String type, @RequestParam String appModule) { return R.ok(svc.sendNotification(userId, title, content, type, appModule)); }
@GetMapping("/update/check") public R<Map<String,Object>> checkUpdate(@RequestParam String appModule, @RequestParam String currentVersion) { return R.ok(svc.checkUpdate(appModule, currentVersion)); }
}
@@ -0,0 +1,11 @@
package com.water.mobile.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data; import java.time.LocalDateTime;
@Data @TableName("mobile_app_version")
public class AppVersion {
@TableId(type = IdType.AUTO) private Long id;
private String appModule, versionNo, downloadUrl, changelog;
private Integer forceUpdate; // 0否 1是
private Integer status; // 0草稿 1已发布
@TableField(fill=FieldFill.INSERT) private LocalDateTime createdTime;
}
@@ -0,0 +1,13 @@
package com.water.mobile.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data; import java.time.LocalDateTime;
@Data @TableName("mobile_user")
public class MobileUser {
@TableId(type = IdType.AUTO) private Long id;
private String username, password, realName, phone, avatar;
private String appModule; // 供水/巡检/收费
private Long sysUserId; // 关联系统用户ID
private Integer status;
@TableField(fill=FieldFill.INSERT) private LocalDateTime createdTime;
private LocalDateTime lastLoginTime;
}
@@ -0,0 +1,12 @@
package com.water.mobile.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data; import java.time.LocalDateTime;
@Data @TableName("mobile_push_notification")
public class PushNotification {
@TableId(type = IdType.AUTO) private Long id;
private Long userId; private String title, content, type;
private String appModule; // 供水/巡检/收费/系统
private Integer isRead; // 0未读 1已读
private String extraData;
@TableField(fill=FieldFill.INSERT) private LocalDateTime createdTime;
}
@@ -0,0 +1,5 @@
package com.water.mobile.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.mobile.entity.AppVersion;
import org.apache.ibatis.annotations.Mapper;
@Mapper public interface AppVersionMapper extends BaseMapper<AppVersion> {}
@@ -0,0 +1,5 @@
package com.water.mobile.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.mobile.entity.MobileUser;
import org.apache.ibatis.annotations.Mapper;
@Mapper public interface MobileUserMapper extends BaseMapper<MobileUser> {}
@@ -0,0 +1,5 @@
package com.water.mobile.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.mobile.entity.PushNotification;
import org.apache.ibatis.annotations.Mapper;
@Mapper public interface PushNotificationMapper extends BaseMapper<PushNotification> {}
@@ -0,0 +1,72 @@
package com.water.mobile.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.water.mobile.entity.*;
import com.water.mobile.mapper.*;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.*;
@Service @RequiredArgsConstructor
public class MobileApiService {
private final MobileUserMapper userMapper;
private final PushNotificationMapper pushMapper;
private final AppVersionMapper verMapper;
public Map<String,Object> login(String username, String password, String appModule) {
MobileUser u = userMapper.selectOne(new LambdaQueryWrapper<MobileUser>()
.eq(MobileUser::getUsername, username)
.eq(MobileUser::getAppModule, appModule));
if (u == null || !u.getPassword().equals(password)) throw new RuntimeException("登录失败");
return Map.of("userId", u.getId(), "realName", u.getRealName(),
"appModule", u.getAppModule(), "token", "mock-token-" + u.getId());
}
// 供水API聚合
public Map<String,Object> getWaterOverview() {
return Map.of("todaySupply", 12500.0, "yesterdaySupply", 11800.0,
"waterQuality", "合格", "alertCount", 3, "deviceOnlineRate", 0.95);
}
// 巡检API聚合
public List<Map<String,Object>> getPatrolTasks(Long userId) {
return List.of(
Map.of("id", 1L, "taskName", "主管网巡检A线", "status", "待执行", "deadline", "今日18:00"),
Map.of("id", 2L, "taskName", "消防栓检查-区域B", "status", "进行中", "deadline", "今日17:00")
);
}
// 收费API聚合
public Map<String,Object> getBillingSummary(Long userId) {
return Map.of("unpaidCount", 2, "totalAmount", 156.80,
"recentBills", List.of(
Map.of("period", "2025-01", "amount", 78.40, "status", "未缴费"),
Map.of("period", "2024-12", "amount", 82.50, "status", "已缴费")
));
}
// 消息通知
public List<PushNotification> getNotifications(Long userId, Boolean unreadOnly) {
LambdaQueryWrapper<PushNotification> w = new LambdaQueryWrapper<PushNotification>()
.eq(PushNotification::getUserId, userId);
if (Boolean.TRUE.equals(unreadOnly)) w.eq(PushNotification::getIsRead, 0);
return pushMapper.selectList(w.orderByDesc(PushNotification::getCreatedTime));
}
public void markRead(Long notificationId) {
PushNotification n = pushMapper.selectById(notificationId);
if (n != null) { n.setIsRead(1); pushMapper.updateById(n); }
}
public Long sendNotification(Long userId, String title, String content, String type, String appModule) {
PushNotification n = new PushNotification();
n.setUserId(userId); n.setTitle(title); n.setContent(content);
n.setType(type); n.setAppModule(appModule); n.setIsRead(0);
pushMapper.insert(n);
return n.getId();
}
// 版本检查
public Map<String,Object> checkUpdate(String appModule, String currentVersion) {
AppVersion v = verMapper.selectOne(new LambdaQueryWrapper<AppVersion>()
.eq(AppVersion::getAppModule, appModule)
.eq(AppVersion::getStatus, 1)
.orderByDesc(AppVersion::getCreatedTime).last("LIMIT 1"));
if (v == null || v.getVersionNo().equals(currentVersion))
return Map.of("hasUpdate", false);
return Map.of("hasUpdate", true, "versionNo", v.getVersionNo(),
"downloadUrl", v.getDownloadUrl(), "changelog", v.getChangelog(),
"forceUpdate", v.getForceUpdate() == 1);
}
}
@@ -0,0 +1,15 @@
server:
port: 9060
spring:
application:
name: wm-mobile-app
datasource:
url: jdbc:postgresql://localhost:5432/water_mobile
username: water
password: water123
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
global-config:
db-config:
id-type: auto
@@ -0,0 +1,18 @@
CREATE TABLE IF NOT EXISTS mobile_user (
id BIGSERIAL PRIMARY KEY, username VARCHAR(50), password VARCHAR(200),
real_name VARCHAR(50), phone VARCHAR(20), avatar VARCHAR(500),
app_module VARCHAR(20), sys_user_id BIGINT, status INT DEFAULT 1,
created_time TIMESTAMP DEFAULT NOW(), last_login_time TIMESTAMP
);
CREATE TABLE IF NOT EXISTS mobile_push_notification (
id BIGSERIAL PRIMARY KEY, user_id BIGINT, title VARCHAR(200),
content TEXT, type VARCHAR(30), app_module VARCHAR(20),
is_read INT DEFAULT 0, extra_data TEXT,
created_time TIMESTAMP DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS mobile_app_version (
id BIGSERIAL PRIMARY KEY, app_module VARCHAR(20), version_no VARCHAR(20),
download_url VARCHAR(500), changelog TEXT,
force_update INT DEFAULT 0, status INT DEFAULT 0,
created_time TIMESTAMP DEFAULT NOW()
);
@@ -0,0 +1,13 @@
package com.water.production.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data; import java.time.LocalDateTime;
@Data @TableName("prod_alert_record")
public class AlertRecord {
@TableId(type = IdType.AUTO) private Long id;
private String deviceId, area, level; // 一般/重要/紧急
private String alertType, description;
private Integer status; // 0活跃 1已确认 2已处理
private Long confirmedBy, handledBy;
@TableField(fill=FieldFill.INSERT) private LocalDateTime createdTime;
private LocalDateTime confirmedTime, handledTime;
}
@@ -0,0 +1,11 @@
package com.water.production.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data; import java.time.LocalDateTime;
@Data @TableName("prod_device_status")
public class DeviceStatus {
@TableId(type = IdType.AUTO) private Long id;
private String deviceId, deviceName, deviceType;
private String area; private Integer status; // 0离线 1在线 2故障
private Double lng, lat;
private LocalDateTime lastOnlineTime;
}
@@ -0,0 +1,11 @@
package com.water.production.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data; import java.time.LocalDateTime;
@Data @TableName("prod_flow_pressure")
public class FlowPressureMonitor {
@TableId(type = IdType.AUTO) private Long id;
private String deviceId, area, positionType; // 流量/压力/液位
private Double value; private String unit;
private Double lng, lat;
@TableField(fill=FieldFill.INSERT) private LocalDateTime createdTime;
}
@@ -0,0 +1,10 @@
package com.water.production.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
@Data @TableName("prod_video_camera")
public class VideoCamera {
@TableId(type = IdType.AUTO) private Long id;
private String cameraId, name, area;
private String streamUrl; private Integer status; // 0离线 1在线
private Double lng, lat;
}
@@ -0,0 +1,11 @@
package com.water.production.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data; import java.time.LocalDateTime;
@Data @TableName("prod_water_quality")
public class WaterQualityRecord {
@TableId(type = IdType.AUTO) private Long id;
private String station, positionType; // 原水/出厂水/末梢水
private Double turbidity, ph, residualChlorine, color, odor;
private String result; // 合格/不合格
@TableField(fill=FieldFill.INSERT) private LocalDateTime createdTime;
}
@@ -0,0 +1,5 @@
package com.water.production.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.production.entity.AlertRecord;
import org.apache.ibatis.annotations.Mapper;
@Mapper public interface AlertRecordMapper extends BaseMapper<AlertRecord> {}
@@ -0,0 +1,5 @@
package com.water.production.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.production.entity.DeviceStatus;
import org.apache.ibatis.annotations.Mapper;
@Mapper public interface DeviceStatusMapper extends BaseMapper<DeviceStatus> {}
@@ -0,0 +1,5 @@
package com.water.production.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.production.entity.FlowPressureMonitor;
import org.apache.ibatis.annotations.Mapper;
@Mapper public interface FlowPressureMonitorMapper extends BaseMapper<FlowPressureMonitor> {}
@@ -0,0 +1,5 @@
package com.water.production.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.production.entity.VideoCamera;
import org.apache.ibatis.annotations.Mapper;
@Mapper public interface VideoCameraMapper extends BaseMapper<VideoCamera> {}
@@ -0,0 +1,5 @@
package com.water.production.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.production.entity.WaterQualityRecord;
import org.apache.ibatis.annotations.Mapper;
@Mapper public interface WaterQualityRecordMapper extends BaseMapper<WaterQualityRecord> {}
@@ -0,0 +1,30 @@
CREATE TABLE IF NOT EXISTS prod_water_quality (
id BIGSERIAL PRIMARY KEY, station VARCHAR(100), position_type VARCHAR(20),
turbidity DOUBLE PRECISION, ph DOUBLE PRECISION,
residual_chlorine DOUBLE PRECISION, color DOUBLE PRECISION, odor DOUBLE PRECISION,
result VARCHAR(20), created_time TIMESTAMP DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS prod_flow_pressure (
id BIGSERIAL PRIMARY KEY, device_id VARCHAR(50), area VARCHAR(50),
position_type VARCHAR(20), value DOUBLE PRECISION, unit VARCHAR(20),
lng DOUBLE PRECISION, lat DOUBLE PRECISION,
created_time TIMESTAMP DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS prod_device_status (
id BIGSERIAL PRIMARY KEY, device_id VARCHAR(50), device_name VARCHAR(100),
device_type VARCHAR(30), area VARCHAR(50), status INT DEFAULT 0,
lng DOUBLE PRECISION, lat DOUBLE PRECISION,
last_online_time TIMESTAMP
);
CREATE TABLE IF NOT EXISTS prod_alert_record (
id BIGSERIAL PRIMARY KEY, device_id VARCHAR(50), area VARCHAR(50),
level VARCHAR(10), alert_type VARCHAR(30), description TEXT,
status INT DEFAULT 0, confirmed_by BIGINT, handled_by BIGINT,
created_time TIMESTAMP DEFAULT NOW(),
confirmed_time TIMESTAMP, handled_time TIMESTAMP
);
CREATE TABLE IF NOT EXISTS prod_video_camera (
id BIGSERIAL PRIMARY KEY, camera_id VARCHAR(50), name VARCHAR(100),
area VARCHAR(50), stream_url VARCHAR(500), status INT DEFAULT 0,
lng DOUBLE PRECISION, lat DOUBLE PRECISION
);
+50
View File
@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.water</groupId>
<artifactId>wm-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>wm-system</artifactId>
<name>wm-system</name>
<description>文档管理与系统管理模块</description>
<dependencies>
<dependency>
<groupId>com.water</groupId>
<artifactId>wm-common</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot3-starter</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,13 @@
package com.water.system;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.mybatis.spring.annotation.MapperScan;
@SpringBootApplication(scanBasePackages = "com.water")
@MapperScan("com.water.system.mapper")
public class SystemApplication {
public static void main(String[] args) {
SpringApplication.run(SystemApplication.class, args);
}
}
@@ -0,0 +1,19 @@
package com.water.system.controller;
import com.water.common.core.result.R;
import com.water.system.entity.*;
import com.water.system.service.DocService;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.*;
@Tag(name="文档管理") @RestController @RequestMapping("/system/doc") @RequiredArgsConstructor
public class DocController {
private final DocService svc;
@GetMapping("/list") public R<List<Document>> list(@RequestParam(required=false) String categoryId, @RequestParam(required=false) String keyword) { return R.ok(svc.listDocs(categoryId, keyword)); }
@GetMapping("/{id}") public R<Document> get(@PathVariable Long id) { return R.ok(svc.getDoc(id)); }
@PostMapping public R<Long> create(@RequestBody Map<String,Object> req) { return R.ok(svc.createDoc(req)); }
@PutMapping("/{id}") public R<String> update(@PathVariable Long id, @RequestBody Map<String,Object> req) { svc.updateDoc(id, req); return R.ok("OK"); }
@DeleteMapping("/{id}") public R<String> delete(@PathVariable Long id) { svc.deleteDoc(id); return R.ok("OK"); }
@GetMapping("/category/list") public R<List<DocumentCategory>> categories() { return R.ok(svc.listCategories()); }
@GetMapping("/{docId}/versions") public R<List<DocumentVersion>> versions(@PathVariable Long docId) { return R.ok(svc.getVersions(docId)); }
}
@@ -0,0 +1,21 @@
package com.water.system.controller;
import com.water.common.core.result.R;
import com.water.system.entity.*;
import com.water.system.service.SysService;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.*;
@Tag(name="系统管理") @RestController @RequestMapping("/system") @RequiredArgsConstructor
public class SysController {
private final SysService svc;
@GetMapping("/role/list") public R<List<SysRole>> roles() { return R.ok(svc.listRoles()); }
@PostMapping("/role") public R<Long> createRole(@RequestBody Map<String,Object> req) { return R.ok(svc.createRole(req)); }
@GetMapping("/user/list") public R<List<SysUser>> users(@RequestParam(required=false) Integer status) { return R.ok(svc.listUsers(status)); }
@PostMapping("/user") public R<Long> createUser(@RequestBody Map<String,Object> req) { return R.ok(svc.createUser(req)); }
@PutMapping("/user/{id}/status") public R<String> updateUserStatus(@PathVariable Long id, @RequestParam int status) { svc.updateUserStatus(id, status); return R.ok("OK"); }
@GetMapping("/menu/list") public R<List<SysMenu>> menus() { return R.ok(svc.listMenus()); }
@GetMapping("/dept/list") public R<List<SysDepartment>> depts() { return R.ok(svc.listDepts()); }
@PostMapping("/dept") public R<Long> createDept(@RequestBody Map<String,Object> req) { return R.ok(svc.createDept(req)); }
@GetMapping("/log/list") public R<List<SysLog>> logs(@RequestParam(required=false) String logType, @RequestParam(required=false) String username) { return R.ok(svc.listLogs(logType, username)); }
}
@@ -0,0 +1,57 @@
package com.water.system.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 文档实体
*/
@Data
@TableName("doc_document")
public class Document {
@TableId(type = IdType.AUTO)
private Long id;
/** 文档名称 */
private String name;
/** 文档原始文件名 */
private String originalName;
/** 文档大小(字节) */
private Long fileSize;
/** 文件类型 */
private String fileType;
/** 存储路径 */
private String storagePath;
/** 文档分类ID */
private Long categoryId;
/** 文档描述 */
private String description;
/** 当前版本号 */
private Integer currentVersion;
/** 上传者ID */
private Long uploaderId;
/** 下载次数 */
private Integer downloadCount;
/** 状态: 0-草稿 1-已发布 2-已归档 */
private Integer status;
/** 权限级别: 0-公开 1-内部 2-机密 */
private Integer permissionLevel;
@TableLogic
private Integer deleted;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
@@ -0,0 +1,33 @@
package com.water.system.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 文档分类实体
*/
@Data
@TableName("doc_category")
public class DocumentCategory {
@TableId(type = IdType.AUTO)
private Long id;
/** 分类名称 */
private String name;
/** 父分类ID */
private Long parentId;
/** 排序号 */
private Integer sortOrder;
/** 分类描述 */
private String description;
@TableLogic
private Integer deleted;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}
@@ -0,0 +1,35 @@
package com.water.system.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 文档版本实体
*/
@Data
@TableName("doc_version")
public class DocumentVersion {
@TableId(type = IdType.AUTO)
private Long id;
/** 文档ID */
private Long documentId;
/** 版本号 */
private Integer version;
/** 版本描述 */
private String description;
/** 文件存储路径 */
private String storagePath;
/** 文件大小 */
private Long fileSize;
/** 操作人ID */
private Long operatorId;
private LocalDateTime createdAt;
}
@@ -0,0 +1,9 @@
package com.water.system.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
@Data @TableName("sys_department")
public class SysDepartment {
@TableId(type = IdType.AUTO) private Long id;
private Long parentId; private String deptName, leader, phone;
private Integer sort, status;
}
@@ -0,0 +1,11 @@
package com.water.system.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data; import java.time.LocalDateTime;
@Data @TableName("sys_log")
public class SysLog {
@TableId(type = IdType.AUTO) private Long id;
private Long userId; private String username, logType, module, action;
private String requestUrl, requestMethod, requestParams, responseResult;
private String ip; private Long duration;
@TableField(fill=FieldFill.INSERT) private LocalDateTime createdTime;
}
@@ -0,0 +1,9 @@
package com.water.system.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
@Data @TableName("sys_menu")
public class SysMenu {
@TableId(type = IdType.AUTO) private Long id;
private Long parentId; private String name, path, icon, component, permission;
private Integer type, sort, visible, status;
}
@@ -0,0 +1,10 @@
package com.water.system.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data; import java.time.LocalDateTime;
@Data @TableName("sys_role")
public class SysRole {
@TableId(type = IdType.AUTO) private Long id;
private String roleCode, roleName, description;
private Integer status, sort;
@TableField(fill=FieldFill.INSERT) private LocalDateTime createdTime;
}
@@ -0,0 +1,12 @@
package com.water.system.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data; import java.time.LocalDateTime;
@Data @TableName("sys_user")
public class SysUser {
@TableId(type = IdType.AUTO) private Long id;
private String username, password, realName, phone, email;
private Long deptId; private String avatar;
private Integer status; // 0停用 1正常
@TableField(fill=FieldFill.INSERT) private LocalDateTime createdTime;
@TableField(fill=FieldFill.INSERT_UPDATE) private LocalDateTime updatedTime;
}
@@ -0,0 +1,5 @@
package com.water.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.system.entity.DocumentCategory;
import org.apache.ibatis.annotations.Mapper;
@Mapper public interface DocumentCategoryMapper extends BaseMapper<DocumentCategory> {}
@@ -0,0 +1,5 @@
package com.water.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.system.entity.Document;
import org.apache.ibatis.annotations.Mapper;
@Mapper public interface DocumentMapper extends BaseMapper<Document> {}
@@ -0,0 +1,5 @@
package com.water.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.system.entity.DocumentVersion;
import org.apache.ibatis.annotations.Mapper;
@Mapper public interface DocumentVersionMapper extends BaseMapper<DocumentVersion> {}
@@ -0,0 +1,5 @@
package com.water.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.system.entity.SysDepartment;
import org.apache.ibatis.annotations.Mapper;
@Mapper public interface SysDepartmentMapper extends BaseMapper<SysDepartment> {}
@@ -0,0 +1,5 @@
package com.water.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.system.entity.SysLog;
import org.apache.ibatis.annotations.Mapper;
@Mapper public interface SysLogMapper extends BaseMapper<SysLog> {}
@@ -0,0 +1,5 @@
package com.water.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.system.entity.SysMenu;
import org.apache.ibatis.annotations.Mapper;
@Mapper public interface SysMenuMapper extends BaseMapper<SysMenu> {}
@@ -0,0 +1,5 @@
package com.water.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.system.entity.SysRole;
import org.apache.ibatis.annotations.Mapper;
@Mapper public interface SysRoleMapper extends BaseMapper<SysRole> {}
@@ -0,0 +1,5 @@
package com.water.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.water.system.entity.SysUser;
import org.apache.ibatis.annotations.Mapper;
@Mapper public interface SysUserMapper extends BaseMapper<SysUser> {}
@@ -0,0 +1,44 @@
package com.water.system.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.water.system.entity.*;
import com.water.system.mapper.*;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.*;
@Service @RequiredArgsConstructor
public class DocService {
private final DocumentMapper docMapper;
private final DocumentCategoryMapper catMapper;
private final DocumentVersionMapper verMapper;
public List<Document> listDocs(String categoryId, String keyword) {
LambdaQueryWrapper<Document> w = new LambdaQueryWrapper<>();
if (categoryId != null) w.eq(Document::getCategoryId, categoryId);
if (keyword != null) w.like(Document::getName, keyword);
return docMapper.selectList(w);
}
public Document getDoc(Long id) { return docMapper.selectById(id); }
public Long createDoc(Map<String,Object> req) {
Document d = new Document();
d.setName((String)req.get("name"));
d.setCategoryId(req.get("categoryId") != null ? ((Number)req.get("categoryId")).longValue() : null);
d.setFileType((String)req.get("fileType"));
d.setFilePath((String)req.get("filePath"));
d.setStatus(1);
docMapper.insert(d);
return d.getId();
}
public void updateDoc(Long id, Map<String,Object> req) {
Document d = docMapper.selectById(id);
if (d == null) throw new RuntimeException("文档不存在");
if (req.containsKey("name")) d.setName((String)req.get("name"));
if (req.containsKey("categoryId")) d.setCategoryId(((Number)req.get("categoryId")).longValue());
docMapper.updateById(d);
}
public void deleteDoc(Long id) { docMapper.deleteById(id); }
public List<DocumentCategory> listCategories() { return catMapper.selectList(null); }
public List<DocumentVersion> getVersions(Long docId) {
return verMapper.selectList(new LambdaQueryWrapper<DocumentVersion>()
.eq(DocumentVersion::getDocId, docId).orderByDesc(DocumentVersion::getVersionNo));
}
}
@@ -0,0 +1,67 @@
package com.water.system.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.water.system.entity.*;
import com.water.system.mapper.*;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.*;
@Service @RequiredArgsConstructor
public class SysService {
private final SysRoleMapper roleMapper;
private final SysUserMapper userMapper;
private final SysMenuMapper menuMapper;
private final SysDepartmentMapper deptMapper;
private final SysLogMapper logMapper;
// Roles
public List<SysRole> listRoles() { return roleMapper.selectList(null); }
public Long createRole(Map<String,Object> req) {
SysRole r = new SysRole();
r.setRoleCode((String)req.get("roleCode"));
r.setRoleName((String)req.get("roleName"));
r.setDescription((String)req.get("description"));
r.setStatus(1); r.setSort(0);
roleMapper.insert(r);
return r.getId();
}
// Users
public List<SysUser> listUsers(Integer status) {
return userMapper.selectList(new LambdaQueryWrapper<SysUser>()
.eq(status != null, SysUser::getStatus, status));
}
public Long createUser(Map<String,Object> req) {
SysUser u = new SysUser();
u.setUsername((String)req.get("username"));
u.setPassword((String)req.get("password"));
u.setRealName((String)req.get("realName"));
u.setPhone((String)req.get("phone"));
u.setStatus(1);
userMapper.insert(u);
return u.getId();
}
public void updateUserStatus(Long id, int status) {
SysUser u = userMapper.selectById(id);
if (u == null) throw new RuntimeException("用户不存在");
u.setStatus(status);
userMapper.updateById(u);
}
// Menus
public List<SysMenu> listMenus() { return menuMapper.selectList(null); }
// Departments
public List<SysDepartment> listDepts() { return deptMapper.selectList(null); }
public Long createDept(Map<String,Object> req) {
SysDepartment d = new SysDepartment();
d.setDeptName((String)req.get("deptName"));
d.setLeader((String)req.get("leader"));
d.setSort(0); d.setStatus(1);
deptMapper.insert(d);
return d.getId();
}
// Logs
public List<SysLog> listLogs(String logType, String username) {
LambdaQueryWrapper<SysLog> w = new LambdaQueryWrapper<>();
if (logType != null) w.eq(SysLog::getLogType, logType);
if (username != null) w.like(SysLog::getUsername, username);
return logMapper.selectList(w.orderByDesc(SysLog::getCreatedTime).last("LIMIT 100"));
}
}
@@ -0,0 +1,40 @@
server:
port: 8084
spring:
application:
name: wm-system
datasource:
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql://${DB_HOST:localhost}:${DB_PORT:5432}/${DB_NAME:water_system}?currentSchema=public
username: ${DB_USER:postgres}
password: ${DB_PASS:postgres}
servlet:
multipart:
max-file-size: 100MB
max-request-size: 100MB
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
logic-delete-field: deleted
logic-delete-value: 1
logic-not-delete-value: 0
minio:
endpoint: ${MINIO_ENDPOINT:http://localhost:9000}
access-key: ${MINIO_ACCESS_KEY:minioadmin}
secret-key: ${MINIO_SECRET_KEY:minioadmin}
bucket-name: ${MINIO_BUCKET:water-documents}
sa-token:
token-name: Authorization
timeout: 86400
active-timeout: 1800
logging:
level:
com.water.system: debug
@@ -0,0 +1,45 @@
CREATE TABLE IF NOT EXISTS sys_document (
id BIGSERIAL PRIMARY KEY, name VARCHAR(200), category_id BIGINT,
file_type VARCHAR(20), file_size BIGINT, file_path VARCHAR(500),
status INT DEFAULT 1, creator_id BIGINT,
created_time TIMESTAMP DEFAULT NOW(), updated_time TIMESTAMP DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS sys_document_category (
id BIGSERIAL PRIMARY KEY, name VARCHAR(100), parent_id BIGINT,
sort INT DEFAULT 0, status INT DEFAULT 1,
created_time TIMESTAMP DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS sys_document_version (
id BIGSERIAL PRIMARY KEY, doc_id BIGINT, version_no INT,
file_path VARCHAR(500), remark TEXT,
created_time TIMESTAMP DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS sys_role (
id BIGSERIAL PRIMARY KEY, role_code VARCHAR(50) UNIQUE, role_name VARCHAR(50),
description VARCHAR(200), status INT DEFAULT 1, sort INT DEFAULT 0,
created_time TIMESTAMP DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS sys_user (
id BIGSERIAL PRIMARY KEY, username VARCHAR(50) UNIQUE, password VARCHAR(200),
real_name VARCHAR(50), phone VARCHAR(20), email VARCHAR(100),
dept_id BIGINT, avatar VARCHAR(500), status INT DEFAULT 1,
created_time TIMESTAMP DEFAULT NOW(), updated_time TIMESTAMP DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS sys_menu (
id BIGSERIAL PRIMARY KEY, parent_id BIGINT, name VARCHAR(50),
path VARCHAR(200), icon VARCHAR(50), component VARCHAR(200),
permission VARCHAR(100), type INT, sort INT DEFAULT 0,
visible INT DEFAULT 1, status INT DEFAULT 1
);
CREATE TABLE IF NOT EXISTS sys_department (
id BIGSERIAL PRIMARY KEY, parent_id BIGINT, dept_name VARCHAR(100),
leader VARCHAR(50), phone VARCHAR(20), sort INT DEFAULT 0, status INT DEFAULT 1
);
CREATE TABLE IF NOT EXISTS sys_log (
id BIGSERIAL PRIMARY KEY, user_id BIGINT, username VARCHAR(50),
log_type VARCHAR(20), module VARCHAR(50), action VARCHAR(50),
request_url VARCHAR(500), request_method VARCHAR(10),
request_params TEXT, response_result TEXT,
ip VARCHAR(50), duration BIGINT,
created_time TIMESTAMP DEFAULT NOW()
);