fujian_water_biz_doc/output/福建水务营收系统概要设计文档_完整版.html

8244 lines
814 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<meta name="author" content="系统设计团队" />
<title>福建水务营收系统概要设计文档</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
div.columns{display: flex; gap: min(4vw, 1.5em);}
div.column{flex: auto; overflow-x: auto;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
/* The extra [class] is a hack that increases specificity enough to
override a similar rule in reveal.js */
ul.task-list[class]{list-style: none;}
ul.task-list li input[type="checkbox"] {
font-size: inherit;
width: 0.8em;
margin: 0 0.8em 0.2em -1.6em;
vertical-align: middle;
}
.display.math{display: block; text-align: center; margin: 0.5rem auto;}
/* CSS for syntax highlighting */
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
.sourceCode { overflow: visible; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { display: inline-block; text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::before
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
color: #aaaaaa;
}
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
div.sourceCode
{ background-color: #f8f8f8; }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
code span.al { color: #ef2929; } /* Alert */
code span.an { color: #8f5902; font-weight: bold; font-style: italic; } /* Annotation */
code span.at { color: #204a87; } /* Attribute */
code span.bn { color: #0000cf; } /* BaseN */
code span.cf { color: #204a87; font-weight: bold; } /* ControlFlow */
code span.ch { color: #4e9a06; } /* Char */
code span.cn { color: #8f5902; } /* Constant */
code span.co { color: #8f5902; font-style: italic; } /* Comment */
code span.cv { color: #8f5902; font-weight: bold; font-style: italic; } /* CommentVar */
code span.do { color: #8f5902; font-weight: bold; font-style: italic; } /* Documentation */
code span.dt { color: #204a87; } /* DataType */
code span.dv { color: #0000cf; } /* DecVal */
code span.er { color: #a40000; font-weight: bold; } /* Error */
code span.ex { } /* Extension */
code span.fl { color: #0000cf; } /* Float */
code span.fu { color: #204a87; font-weight: bold; } /* Function */
code span.im { } /* Import */
code span.in { color: #8f5902; font-weight: bold; font-style: italic; } /* Information */
code span.kw { color: #204a87; font-weight: bold; } /* Keyword */
code span.op { color: #ce5c00; font-weight: bold; } /* Operator */
code span.ot { color: #8f5902; } /* Other */
code span.pp { color: #8f5902; font-style: italic; } /* Preprocessor */
code span.sc { color: #ce5c00; font-weight: bold; } /* SpecialChar */
code span.ss { color: #4e9a06; } /* SpecialString */
code span.st { color: #4e9a06; } /* String */
code span.va { color: #000000; } /* Variable */
code span.vs { color: #4e9a06; } /* VerbatimString */
code span.wa { color: #8f5902; font-weight: bold; font-style: italic; } /* Warning */
</style>
<link rel="stylesheet" href="output/document_style.css" />
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script><script>mermaid.initialize({startOnLoad:true});</script>
</head>
<body>
<header id="title-block-header">
<h1 class="title">福建水务营收系统概要设计文档</h1>
<p class="author">系统设计团队</p>
<p class="date">2024年12月19日</p>
</header>
<nav id="TOC" role="doc-toc">
<ul>
<li><a href="#福建水务营收系统概要设计文档"
id="toc-福建水务营收系统概要设计文档"><span
class="toc-section-number">1</span> 福建水务营收系统概要设计文档</a>
<ul>
<li><a href="#文档信息" id="toc-文档信息"><span
class="toc-section-number">1.1</span> 文档信息</a></li>
</ul></li>
<li><a href="#第1部分" id="toc-第1部分"><span
class="toc-section-number">2</span> 第1部分</a></li>
<li><a href="#第2部分" id="toc-第2部分"><span
class="toc-section-number">3</span> 第2部分</a>
<ul>
<li><a
href="#这样的架构设计确保了系统的高可用性可扩展性和维护性为福建水务营收系统提供了坚实的技术基础"
id="toc-这样的架构设计确保了系统的高可用性可扩展性和维护性为福建水务营收系统提供了坚实的技术基础"><span
class="toc-section-number">3.1</span>
这样的架构设计确保了系统的高可用性、可扩展性和维护性,为福建水务营收系统提供了坚实的技术基础。</a></li>
</ul></li>
<li><a href="#第3部分" id="toc-第3部分"><span
class="toc-section-number">4</span> 第3部分</a></li>
<li><a href="#第4部分" id="toc-第4部分"><span
class="toc-section-number">5</span> 第4部分</a></li>
<li><a href="#第5部分" id="toc-第5部分"><span
class="toc-section-number">6</span> 第5部分</a>
<ul>
<li><a
href="#这些配置都基于ruoyi-vue-pro框架的要求提供了生产就绪的部署方案"
id="toc-这些配置都基于ruoyi-vue-pro框架的要求提供了生产就绪的部署方案"><span
class="toc-section-number">6.1</span>
这些配置都基于RuoYi-Vue-Pro框架的要求提供了生产就绪的部署方案。</a></li>
</ul></li>
<li><a href="#第6部分" id="toc-第6部分"><span
class="toc-section-number">7</span> 第6部分</a></li>
</ul>
</nav>
<h1 data-number="1" id="福建水务营收系统概要设计文档"><span
class="header-section-number">1</span> 福建水务营收系统概要设计文档</h1>
<h2 data-number="1.1" id="文档信息"><span
class="header-section-number">1.1</span> 文档信息</h2>
<table>
<thead>
<tr>
<th>项目信息</th>
<th>详情</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>项目名称</strong></td>
<td>福建水务营收系统</td>
</tr>
<tr>
<td><strong>文档类型</strong></td>
<td>概要设计文档</td>
</tr>
<tr>
<td><strong>文档版本</strong></td>
<td>v1.0</td>
</tr>
<tr>
<td><strong>编写日期</strong></td>
<td>2024-12-19</td>
</tr>
<tr>
<td><strong>文档状态</strong></td>
<td>✅ 已完成</td>
</tr>
</tbody>
</table>
<hr />
<p>ewpage</p>
<h1 data-number="2" id="第1部分"><span
class="header-section-number">2</span> 第1部分</h1>
<h5 data-number="2.0.0.0.1" id="福建水务营收系统架构设计文档"><span
class="header-section-number">2.0.0.0.1</span>
福建水务营收系统架构设计文档</h5>
<h5 data-number="2.0.0.0.2" id="文档信息-1"><span
class="header-section-number">2.0.0.0.2</span> 文档信息</h5>
<table>
<thead>
<tr>
<th>项目信息</th>
<th>详情</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>项目名称</strong></td>
<td>福建水务营收系统</td>
</tr>
<tr>
<td><strong>文档类型</strong></td>
<td>概要设计文档</td>
</tr>
<tr>
<td><strong>技术框架</strong></td>
<td>RuoYi-Vue-Pro + yudao-ui-admin-vue3</td>
</tr>
<tr>
<td><strong>文档版本</strong></td>
<td>v1.0</td>
</tr>
<tr>
<td><strong>编写日期</strong></td>
<td>2024-12-19</td>
</tr>
<tr>
<td><strong>文档状态</strong></td>
<td>✅ 基本完成</td>
</tr>
</tbody>
</table>
<h5 data-number="2.0.0.0.3" id="目录"><span
class="header-section-number">2.0.0.0.3</span> 目录</h5>
<ul>
<li><a href="#一系统架构概述">一、系统架构概述</a></li>
<li><a href="#二技术架构">二、技术架构</a></li>
<li><a href="#三应用架构">三、应用架构</a></li>
<li><a href="#四数据架构">四、数据架构</a></li>
<li><a href="#五安全架构">五、安全架构</a></li>
<li><a href="#六部署架构">六、部署架构</a></li>
<li><a href="#七接口架构">七、接口架构</a></li>
</ul>
<h5 data-number="2.0.0.0.4" id="一系统架构概述"><span
class="header-section-number">2.0.0.0.4</span> 一、系统架构概述</h5>
<p>福建水务营收系统采用多层架构设计,旨在支持集团化的集中管理、统一资源管理的业务需求。系统架构的设计目标是实现”一个业务平台、一网通平台”的建设目标,为集团及下属各分公司提供统一的营业收费系统。</p>
<p>系统架构主要包括以下核心特点: -
多租户架构:支持集团、分公司、营业站点的多层级租户管理模式 -
统一资源服务:统一的数字水务系统运行资源环境 -
统一平台应用:统一业务流程,集中汇集数据 -
统一对外接口:提供标准的接口和能力</p>
<h5 data-number="2.0.0.0.5" id="系统总体架构图"><span
class="header-section-number">2.0.0.0.5</span> 1.1 系统总体架构图</h5>
<pre class="mermaid"><code>graph TB
subgraph &quot;用户层&quot;
A1[Web管理端&lt;br/&gt;yudao-ui-admin-vue3]
A2[移动抄表端&lt;br/&gt;uni-app]
A3[客户微信端&lt;br/&gt;微信小程序]
A4[客户支付宝端&lt;br/&gt;支付宝小程序]
end
subgraph &quot;网关层&quot;
B1[Nginx负载均衡]
B2[API网关&lt;br/&gt;统一认证/权限控制]
end
subgraph &quot;应用层&quot;
C1[营收管理&lt;br/&gt;RuoYi-Vue-Pro]
C2[客户服务&lt;br/&gt;RuoYi-Vue-Pro]
C3[表务管理&lt;br/&gt;RuoYi-Vue-Pro]
C4[统计分析&lt;br/&gt;RuoYi-Vue-Pro]
end
subgraph &quot;服务层&quot;
D1[权限服务&lt;br/&gt;Spring Security]
D2[工作流服务&lt;br/&gt;Flowable]
D3[消息服务&lt;br/&gt;Redis MQ]
D4[文件服务&lt;br/&gt;MinIO/OSS]
end
subgraph &quot;数据层&quot;
E1[(OpenGauss 5.0+&lt;br/&gt;主从架构)]
E2[(Redis 6.0&lt;br/&gt;集群缓存)]
E3[文件存储&lt;br/&gt;分布式存储]
end
subgraph &quot;外部接口&quot;
F1[银行接口&lt;br/&gt;代扣/托收]
F2[支付接口&lt;br/&gt;微信/支付宝]
F3[短信接口&lt;br/&gt;阿里云/腾讯云]
F4[物联网接口&lt;br/&gt;智能水表]
end
A1 --&gt; B1
A2 --&gt; B1
A3 --&gt; B1
A4 --&gt; B1
B1 --&gt; B2
B2 --&gt; C1
B2 --&gt; C2
B2 --&gt; C3
B2 --&gt; C4
C1 --&gt; D1
C1 --&gt; D2
C1 --&gt; D3
C1 --&gt; D4
C2 --&gt; D1
C2 --&gt; D3
C3 --&gt; D1
C3 --&gt; D2
C4 --&gt; D1
D1 --&gt; E1
D2 --&gt; E1
D3 --&gt; E2
D4 --&gt; E3
C1 --&gt; F1
C1 --&gt; F2
C2 --&gt; F3
C3 --&gt; F4</code></pre>
<h5 data-number="2.0.0.0.6" id="物理部署架构图"><span
class="header-section-number">2.0.0.0.6</span> 1.2 物理部署架构图</h5>
<pre class="mermaid"><code>graph TB
subgraph &quot;DMZ区域&quot;
LB1[负载均衡器&lt;br/&gt;Nginx Cluster]
WAF[Web应用防火墙]
end
subgraph &quot;应用服务区&quot;
subgraph &quot;Web服务集群&quot;
WEB1[Web服务器1&lt;br/&gt;8核32G]
WEB2[Web服务器2&lt;br/&gt;8核32G]
end
subgraph &quot;应用服务集群&quot;
APP1[应用服务器1&lt;br/&gt;16核64G]
APP2[应用服务器2&lt;br/&gt;16核64G]
end
end
subgraph &quot;数据服务区&quot;
subgraph &quot;数据库集群&quot;
DB1[OpenGauss主库&lt;br/&gt;32核128G]
DB2[OpenGauss从库&lt;br/&gt;32核128G]
end
subgraph &quot;缓存集群&quot;
REDIS1[Redis主节点&lt;br/&gt;16核32G]
REDIS2[Redis从节点&lt;br/&gt;16核32G]
REDIS3[Redis哨兵&lt;br/&gt;8核16G]
end
subgraph &quot;文件存储&quot;
FILE1[文件服务器1&lt;br/&gt;8核32G 10TB]
FILE2[文件服务器2&lt;br/&gt;8核32G 10TB]
end
end
subgraph &quot;管理服务区&quot;
MONITOR[监控服务器&lt;br/&gt;8核16G]
BACKUP[备份服务器&lt;br/&gt;8核32G 20TB]
JUMP[跳板服务器&lt;br/&gt;4核8G]
end
Internet --&gt; WAF
WAF --&gt; LB1
LB1 --&gt; WEB1
LB1 --&gt; WEB2
WEB1 --&gt; APP1
WEB1 --&gt; APP2
WEB2 --&gt; APP1
WEB2 --&gt; APP2
APP1 --&gt; DB1
APP2 --&gt; DB1
DB1 --&gt; DB2
APP1 --&gt; REDIS1
APP2 --&gt; REDIS1
REDIS1 --&gt; REDIS2
REDIS3 --&gt; REDIS1
REDIS3 --&gt; REDIS2
APP1 --&gt; FILE1
APP2 --&gt; FILE2
FILE1 --&gt; FILE2
MONITOR --&gt; APP1
MONITOR --&gt; APP2
MONITOR --&gt; DB1
MONITOR --&gt; REDIS1
BACKUP --&gt; DB1
BACKUP --&gt; FILE1</code></pre>
<h5 data-number="2.0.0.0.7" id="二技术架构"><span
class="header-section-number">2.0.0.0.7</span> 二、技术架构</h5>
<p>系统采用B/S和M/S相结合的架构模式具体技术栈如下</p>
<h5 data-number="2.0.0.0.8" id="技术栈总览图"><span
class="header-section-number">2.0.0.0.8</span> 2.1 技术栈总览图</h5>
<pre class="mermaid"><code>graph TB
subgraph &quot;前端技术栈&quot;
FE1[Vue 3.2+ TypeScript]
FE2[Element Plus UI]
FE3[Vite 构建工具]
FE4[Pinia 状态管理]
FE5[Vue Router 路由]
FE6[Axios HTTP请求]
FE7[ECharts 图表]
FE8[富文本编辑器]
end
subgraph &quot;后端技术栈&quot;
BE1[Spring Boot 3.x]
BE2[Spring Security 6.x]
BE3[MyBatis Plus 3.x]
BE4[Redis 6.0+]
BE5[OpenGauss 5.0+]
BE6[Knife4j API文档]
BE7[Jackson JSON处理]
BE8[Maven 依赖管理]
end
subgraph &quot;中间件技术栈&quot;
MW1[Nginx 负载均衡]
MW2[Redis 缓存集群]
MW3[Flowable 工作流]
MW4[Quartz 定时任务]
MW5[MinIO 文件存储]
MW6[RocketMQ 消息队列]
MW7[ElasticSearch 搜索]
MW8[Sentinel 熔断限流]
end
subgraph &quot;移动端技术栈&quot;
MB1[uni-app 跨平台]
MB2[uView UI组件]
MB3[微信小程序]
MB4[支付宝小程序]
MB5[H5响应式]
MB6[高德地图SDK]
MB7[NFC设备接口]
end
subgraph &quot;监控运维技术栈&quot;
OP1[SkyWalking APM]
OP2[Spring Boot Admin]
OP3[Docker 容器化]
OP4[Kubernetes 编排]
OP5[Jenkins CI/CD]
OP6[Prometheus 监控]
OP7[Grafana 仪表盘]
OP8[ELK 日志分析]
end
FE1 --&gt; BE1
FE6 --&gt; BE1
BE1 --&gt; BE2
BE1 --&gt; BE3
BE3 --&gt; BE5
BE1 --&gt; BE4
BE1 --&gt; MW3
BE1 --&gt; MW4
MW1 --&gt; BE1
BE1 --&gt; MW5
BE1 --&gt; MW6
BE1 --&gt; MW7
MW8 --&gt; BE1
MB1 --&gt; BE1
MB3 --&gt; BE1
MB4 --&gt; BE1
OP1 --&gt; BE1
OP2 --&gt; BE1
OP3 --&gt; BE1
OP4 --&gt; OP3
OP5 --&gt; OP4
OP6 --&gt; BE1
OP7 --&gt; OP6
OP8 --&gt; BE1</code></pre>
<h5 data-number="2.0.0.0.9" id="系统数据流向图"><span
class="header-section-number">2.0.0.0.9</span> 2.2 系统数据流向图</h5>
<pre class="mermaid"><code>flowchart TD
subgraph &quot;数据采集层&quot;
A1[移动抄表APP&lt;br/&gt;数据采集]
A2[智能水表&lt;br/&gt;远程数据]
A3[Web管理端&lt;br/&gt;业务录入]
A4[客户端小程序&lt;br/&gt;用户数据]
A5[外部系统&lt;br/&gt;接口数据]
end
subgraph &quot;数据接入层&quot;
B1[API网关&lt;br/&gt;数据验证]
B2[数据清洗&lt;br/&gt;格式转换]
B3[消息队列&lt;br/&gt;异步处理]
B4[数据缓存&lt;br/&gt;临时存储]
end
subgraph &quot;业务处理层&quot;
C1[抄表服务&lt;br/&gt;水量计算]
C2[收费服务&lt;br/&gt;账单生成]
C3[账务服务&lt;br/&gt;财务处理]
C4[工单服务&lt;br/&gt;流程处理]
C5[统计服务&lt;br/&gt;数据分析]
end
subgraph &quot;数据存储层&quot;
D1[(MySQL主库&lt;br/&gt;核心业务数据)]
D2[(MySQL从库&lt;br/&gt;查询数据)]
D3[(Redis缓存&lt;br/&gt;热点数据)]
D4[文件存储&lt;br/&gt;附件图片]
D5[(备份库&lt;br/&gt;历史数据)]
end
subgraph &quot;数据服务层&quot;
E1[查询服务&lt;br/&gt;数据检索]
E2[报表服务&lt;br/&gt;统计分析]
E3[接口服务&lt;br/&gt;对外开放]
E4[推送服务&lt;br/&gt;消息通知]
end
subgraph &quot;数据展现层&quot;
F1[管理后台&lt;br/&gt;业务操作]
F2[统计大屏&lt;br/&gt;可视化展示]
F3[移动端&lt;br/&gt;现场作业]
F4[客户端&lt;br/&gt;自助服务]
F5[第三方系统&lt;br/&gt;数据集成]
end
%% 数据流向关系
A1 --&gt; B1
A2 --&gt; B2
A3 --&gt; B1
A4 --&gt; B1
A5 --&gt; B3
B1 --&gt; B4
B2 --&gt; B3
B3 --&gt; C1
B4 --&gt; C2
C1 --&gt; D1
C2 --&gt; D1
C3 --&gt; D1
C4 --&gt; D1
C5 --&gt; D2
D1 --&gt; D2
D1 --&gt; D3
D1 --&gt; D5
D2 --&gt; E1
D3 --&gt; E1
D1 --&gt; E2
D2 --&gt; E3
E1 --&gt; F1
E2 --&gt; F2
E3 --&gt; F3
E4 --&gt; F4
E3 --&gt; F5
%% 反向数据流
F1 -.-&gt; C2
F3 -.-&gt; C1
F4 -.-&gt; C3
F5 -.-&gt; C4</code></pre>
<h5 data-number="2.0.0.0.10" id="服务端技术架构"><span
class="header-section-number">2.0.0.0.10</span> 2.3 服务端技术架构</h5>
<ul>
<li>操作系统:国产 Linux 操作系统</li>
<li>数据库华为OpenGauss 5.0+数据库,企业级国产数据库</li>
<li>应用框架基于RuoYi-Vue-Pro框架定制开发
<ul>
<li>核心框架Spring Boot 3.x支持JDK 17/21</li>
<li>ORM框架MyBatis Plus增强的MyBatis</li>
<li>权限框架Spring Security基于RBAC的权限管理</li>
<li>数据缓存Redis + Redisson高性能缓存和分布式锁</li>
<li>工作流引擎Flowable灵活的工作流处理</li>
<li>任务调度基于Quartz的CRON任务调度</li>
<li>服务监控Spring Boot Admin、SkyWalking APM</li>
<li>消息队列基于Redis的轻量级消息队列</li>
<li>文档生成Knife4j基于Swagger的API文档生成</li>
<li>代码生成自动化CRUD和表单代码生成器</li>
<li>多租户基于字段隔离的SaaS多租户设计</li>
</ul></li>
</ul>
<h5 data-number="2.0.0.0.11" id="ruoyi-vue-pro框架配置示例"><span
class="header-section-number">2.0.0.0.11</span> 2.3.1
RuoYi-Vue-Pro框架配置示例</h5>
<p><strong>application.yml主配置文件</strong></p>
<div class="sourceCode" id="cb5"><pre
class="sourceCode yaml"><code class="sourceCode yaml"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="co">##### 服务端口配置</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="fu">server</span><span class="kw">:</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">port</span><span class="kw">:</span><span class="at"> </span><span class="dv">48080</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">servlet</span><span class="kw">:</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">context-path</span><span class="kw">:</span><span class="at"> /admin-api</span></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a><span class="co">##### Spring Boot 配置</span></span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a><span class="fu">spring</span><span class="kw">:</span></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">application</span><span class="kw">:</span></span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">name</span><span class="kw">:</span><span class="at"> water-biz-server</span></span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">profiles</span><span class="kw">:</span></span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">active</span><span class="kw">:</span><span class="at"> local</span></span>
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true" tabindex="-1"></a><span class="at"> </span></span>
<span id="cb5-14"><a href="#cb5-14" aria-hidden="true" tabindex="-1"></a><span class="co"> # 数据源配置</span></span>
<span id="cb5-15"><a href="#cb5-15" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">datasource</span><span class="kw">:</span></span>
<span id="cb5-16"><a href="#cb5-16" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">druid</span><span class="kw">:</span></span>
<span id="cb5-17"><a href="#cb5-17" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">url</span><span class="kw">:</span><span class="at"> jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&amp;serverTimezone=Asia/Shanghai&amp;allowPublicKeyRetrieval=true&amp;nullCatalogMeansCurrent=true</span></span>
<span id="cb5-18"><a href="#cb5-18" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">username</span><span class="kw">:</span><span class="at"> root</span></span>
<span id="cb5-19"><a href="#cb5-19" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">password</span><span class="kw">:</span><span class="at"> ${MYSQL_PASSWORD:123456}</span></span>
<span id="cb5-20"><a href="#cb5-20" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">driver-class-name</span><span class="kw">:</span><span class="at"> com.mysql.cj.jdbc.Driver</span></span>
<span id="cb5-21"><a href="#cb5-21" aria-hidden="true" tabindex="-1"></a><span class="co"> # 连接池配置</span></span>
<span id="cb5-22"><a href="#cb5-22" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">initial-size</span><span class="kw">:</span><span class="at"> </span><span class="dv">10</span></span>
<span id="cb5-23"><a href="#cb5-23" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">min-idle</span><span class="kw">:</span><span class="at"> </span><span class="dv">10</span></span>
<span id="cb5-24"><a href="#cb5-24" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">max-active</span><span class="kw">:</span><span class="at"> </span><span class="dv">20</span></span>
<span id="cb5-25"><a href="#cb5-25" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">max-wait</span><span class="kw">:</span><span class="at"> </span><span class="dv">60000</span></span>
<span id="cb5-26"><a href="#cb5-26" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">time-between-eviction-runs-millis</span><span class="kw">:</span><span class="at"> </span><span class="dv">60000</span></span>
<span id="cb5-27"><a href="#cb5-27" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">min-evictable-idle-time-millis</span><span class="kw">:</span><span class="at"> </span><span class="dv">300000</span></span>
<span id="cb5-28"><a href="#cb5-28" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">validation-query</span><span class="kw">:</span><span class="at"> SELECT 1 FROM DUAL</span></span>
<span id="cb5-29"><a href="#cb5-29" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">test-while-idle</span><span class="kw">:</span><span class="at"> </span><span class="ch">true</span></span>
<span id="cb5-30"><a href="#cb5-30" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">test-on-borrow</span><span class="kw">:</span><span class="at"> </span><span class="ch">false</span></span>
<span id="cb5-31"><a href="#cb5-31" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">test-on-return</span><span class="kw">:</span><span class="at"> </span><span class="ch">false</span></span>
<span id="cb5-32"><a href="#cb5-32" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">pool-prepared-statements</span><span class="kw">:</span><span class="at"> </span><span class="ch">true</span></span>
<span id="cb5-33"><a href="#cb5-33" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">max-pool-prepared-statement-per-connection-size</span><span class="kw">:</span><span class="at"> </span><span class="dv">20</span></span>
<span id="cb5-34"><a href="#cb5-34" aria-hidden="true" tabindex="-1"></a><span class="at"> </span></span>
<span id="cb5-35"><a href="#cb5-35" aria-hidden="true" tabindex="-1"></a><span class="co"> # Redis 配置</span></span>
<span id="cb5-36"><a href="#cb5-36" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">redis</span><span class="kw">:</span></span>
<span id="cb5-37"><a href="#cb5-37" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">host</span><span class="kw">:</span><span class="at"> </span><span class="fl">127.0.0.1</span></span>
<span id="cb5-38"><a href="#cb5-38" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">port</span><span class="kw">:</span><span class="at"> </span><span class="dv">6379</span></span>
<span id="cb5-39"><a href="#cb5-39" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">password</span><span class="kw">:</span><span class="at"> ${REDIS_PASSWORD:}</span></span>
<span id="cb5-40"><a href="#cb5-40" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">database</span><span class="kw">:</span><span class="at"> </span><span class="dv">1</span></span>
<span id="cb5-41"><a href="#cb5-41" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">timeout</span><span class="kw">:</span><span class="at"> 6000ms</span></span>
<span id="cb5-42"><a href="#cb5-42" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">lettuce</span><span class="kw">:</span></span>
<span id="cb5-43"><a href="#cb5-43" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">pool</span><span class="kw">:</span></span>
<span id="cb5-44"><a href="#cb5-44" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">max-active</span><span class="kw">:</span><span class="at"> </span><span class="dv">32</span></span>
<span id="cb5-45"><a href="#cb5-45" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">max-wait</span><span class="kw">:</span><span class="at"> 6000ms</span></span>
<span id="cb5-46"><a href="#cb5-46" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">max-idle</span><span class="kw">:</span><span class="at"> </span><span class="dv">32</span></span>
<span id="cb5-47"><a href="#cb5-47" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">min-idle</span><span class="kw">:</span><span class="at"> </span><span class="dv">8</span></span>
<span id="cb5-48"><a href="#cb5-48" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-49"><a href="#cb5-49" aria-hidden="true" tabindex="-1"></a><span class="co">##### MyBatis Plus 配置</span></span>
<span id="cb5-50"><a href="#cb5-50" aria-hidden="true" tabindex="-1"></a><span class="fu">mybatis-plus</span><span class="kw">:</span></span>
<span id="cb5-51"><a href="#cb5-51" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">configuration</span><span class="kw">:</span></span>
<span id="cb5-52"><a href="#cb5-52" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">map-underscore-to-camel-case</span><span class="kw">:</span><span class="at"> </span><span class="ch">true</span></span>
<span id="cb5-53"><a href="#cb5-53" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">log-impl</span><span class="kw">:</span><span class="at"> org.apache.ibatis.logging.stdout.StdOutImpl</span></span>
<span id="cb5-54"><a href="#cb5-54" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">global-config</span><span class="kw">:</span></span>
<span id="cb5-55"><a href="#cb5-55" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">db-config</span><span class="kw">:</span></span>
<span id="cb5-56"><a href="#cb5-56" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">id-type</span><span class="kw">:</span><span class="at"> NONE</span></span>
<span id="cb5-57"><a href="#cb5-57" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">logic-delete-field</span><span class="kw">:</span><span class="at"> deleted</span></span>
<span id="cb5-58"><a href="#cb5-58" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">logic-delete-value</span><span class="kw">:</span><span class="at"> </span><span class="dv">1</span></span>
<span id="cb5-59"><a href="#cb5-59" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">logic-not-delete-value</span><span class="kw">:</span><span class="at"> </span><span class="dv">0</span></span>
<span id="cb5-60"><a href="#cb5-60" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">type-aliases-package</span><span class="kw">:</span><span class="at"> cn.iocoder.yudao.module.*.dal.dataobject</span></span>
<span id="cb5-61"><a href="#cb5-61" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-62"><a href="#cb5-62" aria-hidden="true" tabindex="-1"></a><span class="co">##### 水务系统多租户配置</span></span>
<span id="cb5-63"><a href="#cb5-63" aria-hidden="true" tabindex="-1"></a><span class="fu">yudao</span><span class="kw">:</span></span>
<span id="cb5-64"><a href="#cb5-64" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">tenant</span><span class="kw">:</span></span>
<span id="cb5-65"><a href="#cb5-65" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">enable</span><span class="kw">:</span><span class="at"> </span><span class="ch">true</span></span>
<span id="cb5-66"><a href="#cb5-66" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">ignore-urls</span><span class="kw">:</span></span>
<span id="cb5-67"><a href="#cb5-67" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> /admin-api/water/tenant/get-id-by-name</span></span>
<span id="cb5-68"><a href="#cb5-68" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> /admin-api/infra/file/*/get/**</span></span>
<span id="cb5-69"><a href="#cb5-69" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">ignore-tables</span><span class="kw">:</span></span>
<span id="cb5-70"><a href="#cb5-70" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> water_tenant</span></span>
<span id="cb5-71"><a href="#cb5-71" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> water_system_config</span></span>
<span id="cb5-72"><a href="#cb5-72" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> water_dict_data</span></span>
<span id="cb5-73"><a href="#cb5-73" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> water_dict_type</span></span>
<span id="cb5-74"><a href="#cb5-74" aria-hidden="true" tabindex="-1"></a><span class="at"> </span></span>
<span id="cb5-75"><a href="#cb5-75" aria-hidden="true" tabindex="-1"></a><span class="co"> # 水务系统安全配置</span></span>
<span id="cb5-76"><a href="#cb5-76" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">security</span><span class="kw">:</span></span>
<span id="cb5-77"><a href="#cb5-77" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">permit-all-urls</span><span class="kw">:</span></span>
<span id="cb5-78"><a href="#cb5-78" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> /admin-api/water/auth/login</span></span>
<span id="cb5-79"><a href="#cb5-79" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> /admin-api/water/auth/logout</span></span>
<span id="cb5-80"><a href="#cb5-80" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> /admin-api/water/auth/refresh-token</span></span>
<span id="cb5-81"><a href="#cb5-81" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> /admin-api/water/captcha/get</span></span>
<span id="cb5-82"><a href="#cb5-82" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> /admin-api/water/sms/send</span></span>
<span id="cb5-83"><a href="#cb5-83" aria-hidden="true" tabindex="-1"></a><span class="at"> </span></span>
<span id="cb5-84"><a href="#cb5-84" aria-hidden="true" tabindex="-1"></a><span class="co"> # 文件存储配置</span></span>
<span id="cb5-85"><a href="#cb5-85" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">file</span><span class="kw">:</span></span>
<span id="cb5-86"><a href="#cb5-86" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">config</span><span class="kw">:</span></span>
<span id="cb5-87"><a href="#cb5-87" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">type</span><span class="kw">:</span><span class="at"> local</span></span>
<span id="cb5-88"><a href="#cb5-88" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">local</span><span class="kw">:</span></span>
<span id="cb5-89"><a href="#cb5-89" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">domain</span><span class="kw">:</span><span class="at"> http://127.0.0.1:48080</span></span>
<span id="cb5-90"><a href="#cb5-90" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">path</span><span class="kw">:</span><span class="at"> /Users/yunai/file_test</span></span>
<span id="cb5-91"><a href="#cb5-91" aria-hidden="true" tabindex="-1"></a><span class="at"> </span></span>
<span id="cb5-92"><a href="#cb5-92" aria-hidden="true" tabindex="-1"></a><span class="co"> # 短信配置</span></span>
<span id="cb5-93"><a href="#cb5-93" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">sms</span><span class="kw">:</span></span>
<span id="cb5-94"><a href="#cb5-94" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">alibaba</span><span class="kw">:</span></span>
<span id="cb5-95"><a href="#cb5-95" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">access-key-id</span><span class="kw">:</span><span class="at"> ${SMS_ACCESS_KEY_ID:}</span></span>
<span id="cb5-96"><a href="#cb5-96" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">access-key-secret</span><span class="kw">:</span><span class="at"> ${SMS_ACCESS_KEY_SECRET:}</span></span>
<span id="cb5-97"><a href="#cb5-97" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">signature</span><span class="kw">:</span><span class="at"> 福建水务</span></span>
<span id="cb5-98"><a href="#cb5-98" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">template-code</span><span class="kw">:</span><span class="at"> SMS_123456</span></span></code></pre></div>
<p><strong>多租户配置实现:</strong></p>
<div class="sourceCode" id="cb6"><pre
class="sourceCode java"><code class="sourceCode java"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="at">@Component</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a><span class="at">@Slf4j</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span> <span class="kw">class</span> WaterTenantConfiguration <span class="op">{</span></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a> <span class="at">@Resource</span></span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a> <span class="kw">private</span> TenantProperties tenantProperties<span class="op">;</span></span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a> <span class="co">/**</span></span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a> <span class="co">*</span> <span class="co">多租户字段处理器</span></span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a> <span class="co">*/</span></span>
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a> <span class="at">@Bean</span></span>
<span id="cb6-12"><a href="#cb6-12" aria-hidden="true" tabindex="-1"></a> <span class="kw">public</span> TenantLineHandler <span class="fu">tenantLineHandler</span><span class="op">()</span> <span class="op">{</span></span>
<span id="cb6-13"><a href="#cb6-13" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="kw">new</span> <span class="fu">TenantLineHandler</span><span class="op">()</span> <span class="op">{</span></span>
<span id="cb6-14"><a href="#cb6-14" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb6-15"><a href="#cb6-15" aria-hidden="true" tabindex="-1"></a> <span class="at">@Override</span></span>
<span id="cb6-16"><a href="#cb6-16" aria-hidden="true" tabindex="-1"></a> <span class="kw">public</span> <span class="bu">Expression</span> <span class="fu">getTenantId</span><span class="op">()</span> <span class="op">{</span></span>
<span id="cb6-17"><a href="#cb6-17" aria-hidden="true" tabindex="-1"></a> <span class="co">// 从当前登录用户上下文获取租户ID</span></span>
<span id="cb6-18"><a href="#cb6-18" aria-hidden="true" tabindex="-1"></a> <span class="bu">Long</span> tenantId <span class="op">=</span> TenantContextHolder<span class="op">.</span><span class="fu">getTenantId</span><span class="op">();</span></span>
<span id="cb6-19"><a href="#cb6-19" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>tenantId <span class="op">==</span> <span class="kw">null</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb6-20"><a href="#cb6-20" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="kw">null</span><span class="op">;</span></span>
<span id="cb6-21"><a href="#cb6-21" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb6-22"><a href="#cb6-22" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="kw">new</span> <span class="fu">LongValue</span><span class="op">(</span>tenantId<span class="op">);</span></span>
<span id="cb6-23"><a href="#cb6-23" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb6-24"><a href="#cb6-24" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb6-25"><a href="#cb6-25" aria-hidden="true" tabindex="-1"></a> <span class="at">@Override</span></span>
<span id="cb6-26"><a href="#cb6-26" aria-hidden="true" tabindex="-1"></a> <span class="kw">public</span> <span class="bu">String</span> <span class="fu">getTenantIdColumn</span><span class="op">()</span> <span class="op">{</span></span>
<span id="cb6-27"><a href="#cb6-27" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="st">&quot;tenant_id&quot;</span><span class="op">;</span></span>
<span id="cb6-28"><a href="#cb6-28" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb6-29"><a href="#cb6-29" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb6-30"><a href="#cb6-30" aria-hidden="true" tabindex="-1"></a> <span class="at">@Override</span></span>
<span id="cb6-31"><a href="#cb6-31" aria-hidden="true" tabindex="-1"></a> <span class="kw">public</span> <span class="dt">boolean</span> <span class="fu">ignoreTable</span><span class="op">(</span><span class="bu">String</span> tableName<span class="op">)</span> <span class="op">{</span></span>
<span id="cb6-32"><a href="#cb6-32" aria-hidden="true" tabindex="-1"></a> <span class="co">// 忽略的表不进行多租户处理</span></span>
<span id="cb6-33"><a href="#cb6-33" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> tenantProperties<span class="op">.</span><span class="fu">getIgnoreTables</span><span class="op">().</span><span class="fu">contains</span><span class="op">(</span>tableName<span class="op">);</span></span>
<span id="cb6-34"><a href="#cb6-34" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb6-35"><a href="#cb6-35" aria-hidden="true" tabindex="-1"></a> <span class="op">};</span></span>
<span id="cb6-36"><a href="#cb6-36" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb6-37"><a href="#cb6-37" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb6-38"><a href="#cb6-38" aria-hidden="true" tabindex="-1"></a> <span class="co">/**</span></span>
<span id="cb6-39"><a href="#cb6-39" aria-hidden="true" tabindex="-1"></a> <span class="co">*</span> <span class="co">多租户拦截器</span></span>
<span id="cb6-40"><a href="#cb6-40" aria-hidden="true" tabindex="-1"></a> <span class="co">*/</span></span>
<span id="cb6-41"><a href="#cb6-41" aria-hidden="true" tabindex="-1"></a> <span class="at">@Bean</span></span>
<span id="cb6-42"><a href="#cb6-42" aria-hidden="true" tabindex="-1"></a> <span class="kw">public</span> MybatisPlusInterceptor <span class="fu">mybatisPlusInterceptor</span><span class="op">()</span> <span class="op">{</span></span>
<span id="cb6-43"><a href="#cb6-43" aria-hidden="true" tabindex="-1"></a> MybatisPlusInterceptor interceptor <span class="op">=</span> <span class="kw">new</span> <span class="fu">MybatisPlusInterceptor</span><span class="op">();</span></span>
<span id="cb6-44"><a href="#cb6-44" aria-hidden="true" tabindex="-1"></a> <span class="co">// 多租户插件</span></span>
<span id="cb6-45"><a href="#cb6-45" aria-hidden="true" tabindex="-1"></a> TenantLineInnerInterceptor tenantInterceptor <span class="op">=</span> <span class="kw">new</span> <span class="fu">TenantLineInnerInterceptor</span><span class="op">();</span></span>
<span id="cb6-46"><a href="#cb6-46" aria-hidden="true" tabindex="-1"></a> tenantInterceptor<span class="op">.</span><span class="fu">setTenantLineHandler</span><span class="op">(</span><span class="fu">tenantLineHandler</span><span class="op">());</span></span>
<span id="cb6-47"><a href="#cb6-47" aria-hidden="true" tabindex="-1"></a> interceptor<span class="op">.</span><span class="fu">addInnerInterceptor</span><span class="op">(</span>tenantInterceptor<span class="op">);</span></span>
<span id="cb6-48"><a href="#cb6-48" aria-hidden="true" tabindex="-1"></a> <span class="co">// 分页插件</span></span>
<span id="cb6-49"><a href="#cb6-49" aria-hidden="true" tabindex="-1"></a> interceptor<span class="op">.</span><span class="fu">addInnerInterceptor</span><span class="op">(</span><span class="kw">new</span> <span class="fu">PaginationInnerInterceptor</span><span class="op">(</span>DbType<span class="op">.</span><span class="fu">MYSQL</span><span class="op">));</span></span>
<span id="cb6-50"><a href="#cb6-50" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> interceptor<span class="op">;</span></span>
<span id="cb6-51"><a href="#cb6-51" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb6-52"><a href="#cb6-52" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p><strong>权限控制配置:</strong></p>
<div class="sourceCode" id="cb7"><pre
class="sourceCode java"><code class="sourceCode java"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="at">@EnableWebSecurity</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a><span class="at">@EnableMethodSecurity</span><span class="op">(</span>prePostEnabled <span class="op">=</span> <span class="kw">true</span><span class="op">,</span> securedEnabled <span class="op">=</span> <span class="kw">true</span><span class="op">)</span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a><span class="at">@Configuration</span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span> <span class="kw">class</span> WaterSecurityConfiguration <span class="op">{</span></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a> <span class="at">@Resource</span></span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a> <span class="kw">private</span> SecurityProperties securityProperties<span class="op">;</span></span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a> <span class="at">@Bean</span></span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a> <span class="kw">public</span> SecurityFilterChain <span class="fu">filterChain</span><span class="op">(</span>HttpSecurity httpSecurity<span class="op">,</span></span>
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true" tabindex="-1"></a> <span class="at">@Lazy</span> TokenAuthenticationFilter tokenAuthenticationFilter<span class="op">)</span> <span class="kw">throws</span> <span class="bu">Exception</span> <span class="op">{</span></span>
<span id="cb7-12"><a href="#cb7-12" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> httpSecurity</span>
<span id="cb7-13"><a href="#cb7-13" aria-hidden="true" tabindex="-1"></a> <span class="co">// 设置 URL 安全权限</span></span>
<span id="cb7-14"><a href="#cb7-14" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">authorizeHttpRequests</span><span class="op">(</span>c <span class="op">-&gt;</span> c</span>
<span id="cb7-15"><a href="#cb7-15" aria-hidden="true" tabindex="-1"></a> <span class="co">// 1. 静态资源,可匿名访问</span></span>
<span id="cb7-16"><a href="#cb7-16" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">requestMatchers</span><span class="op">(</span>HttpMethod<span class="op">.</span><span class="fu">GET</span><span class="op">,</span> <span class="st">&quot;/*.html&quot;</span><span class="op">,</span> <span class="st">&quot;/**/*.html&quot;</span><span class="op">,</span> <span class="st">&quot;/**/*.css&quot;</span><span class="op">,</span> <span class="st">&quot;/**/*.js&quot;</span><span class="op">).</span><span class="fu">permitAll</span><span class="op">()</span></span>
<span id="cb7-17"><a href="#cb7-17" aria-hidden="true" tabindex="-1"></a> <span class="co">// 2. 设置 @PermitAll 无需认证</span></span>
<span id="cb7-18"><a href="#cb7-18" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">requestMatchers</span><span class="op">(</span>securityProperties<span class="op">.</span><span class="fu">getPermitAllUrls</span><span class="op">().</span><span class="fu">toArray</span><span class="op">(</span><span class="kw">new</span> <span class="bu">String</span><span class="op">[</span><span class="dv">0</span><span class="op">])).</span><span class="fu">permitAll</span><span class="op">()</span></span>
<span id="cb7-19"><a href="#cb7-19" aria-hidden="true" tabindex="-1"></a> <span class="co">// 3. 兜底规则,必须认证</span></span>
<span id="cb7-20"><a href="#cb7-20" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">anyRequest</span><span class="op">().</span><span class="fu">authenticated</span><span class="op">()</span></span>
<span id="cb7-21"><a href="#cb7-21" aria-hidden="true" tabindex="-1"></a> <span class="op">)</span></span>
<span id="cb7-22"><a href="#cb7-22" aria-hidden="true" tabindex="-1"></a> <span class="co">// 设置处理器</span></span>
<span id="cb7-23"><a href="#cb7-23" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">exceptionHandling</span><span class="op">(</span>c <span class="op">-&gt;</span> c<span class="op">.</span><span class="fu">authenticationEntryPoint</span><span class="op">(</span>authenticationEntryPoint<span class="op">)</span></span>
<span id="cb7-24"><a href="#cb7-24" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">accessDeniedHandler</span><span class="op">(</span>accessDeniedHandler<span class="op">))</span></span>
<span id="cb7-25"><a href="#cb7-25" aria-hidden="true" tabindex="-1"></a> <span class="co">// 添加 Token Filter</span></span>
<span id="cb7-26"><a href="#cb7-26" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">addFilterBefore</span><span class="op">(</span>tokenAuthenticationFilter<span class="op">,</span> UsernamePasswordAuthenticationFilter<span class="op">.</span><span class="fu">class</span><span class="op">)</span></span>
<span id="cb7-27"><a href="#cb7-27" aria-hidden="true" tabindex="-1"></a> <span class="co">// 不创建 SecurityContext</span></span>
<span id="cb7-28"><a href="#cb7-28" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">sessionManagement</span><span class="op">(</span>c <span class="op">-&gt;</span> c<span class="op">.</span><span class="fu">sessionCreationPolicy</span><span class="op">(</span>SessionCreationPolicy<span class="op">.</span><span class="fu">STATELESS</span><span class="op">))</span></span>
<span id="cb7-29"><a href="#cb7-29" aria-hidden="true" tabindex="-1"></a> <span class="co">// 禁用 CSRF因为使用 token 机制</span></span>
<span id="cb7-30"><a href="#cb7-30" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">csrf</span><span class="op">(</span>AbstractHttpConfigurer<span class="op">::</span>disable<span class="op">)</span></span>
<span id="cb7-31"><a href="#cb7-31" aria-hidden="true" tabindex="-1"></a> <span class="co">// 禁用 cors</span></span>
<span id="cb7-32"><a href="#cb7-32" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">cors</span><span class="op">(</span>AbstractHttpConfigurer<span class="op">::</span>disable<span class="op">)</span></span>
<span id="cb7-33"><a href="#cb7-33" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">build</span><span class="op">();</span></span>
<span id="cb7-34"><a href="#cb7-34" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb7-35"><a href="#cb7-35" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<h5 data-number="2.0.0.0.12" id="客户端技术架构"><span
class="header-section-number">2.0.0.0.12</span> 2.4 客户端技术架构</h5>
<ul>
<li>前端框架基于yudao-ui-admin-vue3框架定制开发
<ul>
<li>核心框架Vue 3.x + TypeScript</li>
<li>组件库Element Plus现代化UI组件</li>
<li>状态管理Pinia新一代的Vue状态管理库</li>
<li>路由管理Vue Router</li>
<li>HTTP工具Axios请求拦截与统一处理</li>
<li>CSS框架Tailwind CSS / UnoCSS原子化CSS</li>
<li>图表组件ECharts强大的可视化图表库</li>
<li>富文本编辑器WangEditor</li>
<li>文件上传基于Element Plus的上传组件</li>
<li>权限控制:细粒度的按钮级权限控制</li>
<li>动态表格:支持拖拽、排序、筛选</li>
<li>代码规范ESLint + Prettier标准化代码风格</li>
</ul></li>
</ul>
<h5 data-number="2.0.0.0.13" id="前端项目结构配置"><span
class="header-section-number">2.0.0.0.13</span> 2.4.1
前端项目结构配置</h5>
<p><strong>package.json 依赖配置:</strong></p>
<div class="sourceCode" id="cb8"><pre
class="sourceCode json"><code class="sourceCode json"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;name&quot;</span><span class="fu">:</span> <span class="st">&quot;water-biz-ui-admin&quot;</span><span class="fu">,</span></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;version&quot;</span><span class="fu">:</span> <span class="st">&quot;1.0.0&quot;</span><span class="fu">,</span></span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;description&quot;</span><span class="fu">:</span> <span class="st">&quot;福建水务营收系统管理后台&quot;</span><span class="fu">,</span></span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;dependencies&quot;</span><span class="fu">:</span> <span class="fu">{</span></span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;@element-plus/icons-vue&quot;</span><span class="fu">:</span> <span class="st">&quot;^2.1.0&quot;</span><span class="fu">,</span></span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;@vueuse/core&quot;</span><span class="fu">:</span> <span class="st">&quot;^10.5.0&quot;</span><span class="fu">,</span></span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;axios&quot;</span><span class="fu">:</span> <span class="st">&quot;^1.6.0&quot;</span><span class="fu">,</span></span>
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;echarts&quot;</span><span class="fu">:</span> <span class="st">&quot;^5.4.3&quot;</span><span class="fu">,</span></span>
<span id="cb8-10"><a href="#cb8-10" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;element-plus&quot;</span><span class="fu">:</span> <span class="st">&quot;^2.4.2&quot;</span><span class="fu">,</span></span>
<span id="cb8-11"><a href="#cb8-11" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;pinia&quot;</span><span class="fu">:</span> <span class="st">&quot;^2.1.7&quot;</span><span class="fu">,</span></span>
<span id="cb8-12"><a href="#cb8-12" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;vue&quot;</span><span class="fu">:</span> <span class="st">&quot;^3.3.8&quot;</span><span class="fu">,</span></span>
<span id="cb8-13"><a href="#cb8-13" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;vue-router&quot;</span><span class="fu">:</span> <span class="st">&quot;^4.2.5&quot;</span><span class="fu">,</span></span>
<span id="cb8-14"><a href="#cb8-14" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;@wangeditor/editor&quot;</span><span class="fu">:</span> <span class="st">&quot;^5.1.23&quot;</span><span class="fu">,</span></span>
<span id="cb8-15"><a href="#cb8-15" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;@wangeditor/editor-for-vue&quot;</span><span class="fu">:</span> <span class="st">&quot;^5.1.12&quot;</span></span>
<span id="cb8-16"><a href="#cb8-16" aria-hidden="true" tabindex="-1"></a> <span class="fu">},</span></span>
<span id="cb8-17"><a href="#cb8-17" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;devDependencies&quot;</span><span class="fu">:</span> <span class="fu">{</span></span>
<span id="cb8-18"><a href="#cb8-18" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;@types/node&quot;</span><span class="fu">:</span> <span class="st">&quot;^20.8.7&quot;</span><span class="fu">,</span></span>
<span id="cb8-19"><a href="#cb8-19" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;@typescript-eslint/eslint-plugin&quot;</span><span class="fu">:</span> <span class="st">&quot;^6.9.1&quot;</span><span class="fu">,</span></span>
<span id="cb8-20"><a href="#cb8-20" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;@typescript-eslint/parser&quot;</span><span class="fu">:</span> <span class="st">&quot;^6.9.1&quot;</span><span class="fu">,</span></span>
<span id="cb8-21"><a href="#cb8-21" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;@vitejs/plugin-vue&quot;</span><span class="fu">:</span> <span class="st">&quot;^4.4.1&quot;</span><span class="fu">,</span></span>
<span id="cb8-22"><a href="#cb8-22" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;eslint&quot;</span><span class="fu">:</span> <span class="st">&quot;^8.52.0&quot;</span><span class="fu">,</span></span>
<span id="cb8-23"><a href="#cb8-23" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;eslint-plugin-vue&quot;</span><span class="fu">:</span> <span class="st">&quot;^9.17.0&quot;</span><span class="fu">,</span></span>
<span id="cb8-24"><a href="#cb8-24" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;prettier&quot;</span><span class="fu">:</span> <span class="st">&quot;^3.0.3&quot;</span><span class="fu">,</span></span>
<span id="cb8-25"><a href="#cb8-25" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;typescript&quot;</span><span class="fu">:</span> <span class="st">&quot;^5.2.2&quot;</span><span class="fu">,</span></span>
<span id="cb8-26"><a href="#cb8-26" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;vite&quot;</span><span class="fu">:</span> <span class="st">&quot;^4.5.0&quot;</span><span class="fu">,</span></span>
<span id="cb8-27"><a href="#cb8-27" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;vue-tsc&quot;</span><span class="fu">:</span> <span class="st">&quot;^1.8.22&quot;</span></span>
<span id="cb8-28"><a href="#cb8-28" aria-hidden="true" tabindex="-1"></a> <span class="fu">}</span></span>
<span id="cb8-29"><a href="#cb8-29" aria-hidden="true" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
<p><strong>vite.config.ts 构建配置:</strong></p>
<div class="sourceCode" id="cb9"><pre
class="sourceCode typescript"><code class="sourceCode typescript"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> { defineConfig } <span class="im">from</span> <span class="st">&#39;vite&#39;</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> vue <span class="im">from</span> <span class="st">&#39;@vitejs/plugin-vue&#39;</span></span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> { resolve } <span class="im">from</span> <span class="st">&#39;path&#39;</span></span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a><span class="im">export</span> <span class="im">default</span> <span class="fu">defineConfig</span>({</span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a> plugins<span class="op">:</span> [<span class="fu">vue</span>()]<span class="op">,</span></span>
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true" tabindex="-1"></a> resolve<span class="op">:</span> {</span>
<span id="cb9-8"><a href="#cb9-8" aria-hidden="true" tabindex="-1"></a> alias<span class="op">:</span> {</span>
<span id="cb9-9"><a href="#cb9-9" aria-hidden="true" tabindex="-1"></a> <span class="st">&#39;@&#39;</span><span class="op">:</span> <span class="fu">resolve</span>(<span class="bu">__dirname</span><span class="op">,</span> <span class="st">&#39;src&#39;</span>)<span class="op">,</span></span>
<span id="cb9-10"><a href="#cb9-10" aria-hidden="true" tabindex="-1"></a> <span class="st">&#39;~&#39;</span><span class="op">:</span> <span class="fu">resolve</span>(<span class="bu">__dirname</span><span class="op">,</span> <span class="st">&#39;src&#39;</span>)<span class="op">,</span></span>
<span id="cb9-11"><a href="#cb9-11" aria-hidden="true" tabindex="-1"></a> <span class="st">&#39;#&#39;</span><span class="op">:</span> <span class="fu">resolve</span>(<span class="bu">__dirname</span><span class="op">,</span> <span class="st">&#39;types&#39;</span>)</span>
<span id="cb9-12"><a href="#cb9-12" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb9-13"><a href="#cb9-13" aria-hidden="true" tabindex="-1"></a> }<span class="op">,</span></span>
<span id="cb9-14"><a href="#cb9-14" aria-hidden="true" tabindex="-1"></a> server<span class="op">:</span> {</span>
<span id="cb9-15"><a href="#cb9-15" aria-hidden="true" tabindex="-1"></a> host<span class="op">:</span> <span class="st">&#39;0.0.0.0&#39;</span><span class="op">,</span></span>
<span id="cb9-16"><a href="#cb9-16" aria-hidden="true" tabindex="-1"></a> port<span class="op">:</span> <span class="dv">3000</span><span class="op">,</span></span>
<span id="cb9-17"><a href="#cb9-17" aria-hidden="true" tabindex="-1"></a> open<span class="op">:</span> <span class="kw">true</span><span class="op">,</span></span>
<span id="cb9-18"><a href="#cb9-18" aria-hidden="true" tabindex="-1"></a> proxy<span class="op">:</span> {</span>
<span id="cb9-19"><a href="#cb9-19" aria-hidden="true" tabindex="-1"></a> <span class="st">&#39;/admin-api&#39;</span><span class="op">:</span> {</span>
<span id="cb9-20"><a href="#cb9-20" aria-hidden="true" tabindex="-1"></a> target<span class="op">:</span> <span class="st">&#39;http://127.0.0.1:48080&#39;</span><span class="op">,</span></span>
<span id="cb9-21"><a href="#cb9-21" aria-hidden="true" tabindex="-1"></a> changeOrigin<span class="op">:</span> <span class="kw">true</span><span class="op">,</span></span>
<span id="cb9-22"><a href="#cb9-22" aria-hidden="true" tabindex="-1"></a> ws<span class="op">:</span> <span class="kw">true</span></span>
<span id="cb9-23"><a href="#cb9-23" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb9-24"><a href="#cb9-24" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb9-25"><a href="#cb9-25" aria-hidden="true" tabindex="-1"></a> }<span class="op">,</span></span>
<span id="cb9-26"><a href="#cb9-26" aria-hidden="true" tabindex="-1"></a> build<span class="op">:</span> {</span>
<span id="cb9-27"><a href="#cb9-27" aria-hidden="true" tabindex="-1"></a> target<span class="op">:</span> <span class="st">&#39;es2015&#39;</span><span class="op">,</span></span>
<span id="cb9-28"><a href="#cb9-28" aria-hidden="true" tabindex="-1"></a> outDir<span class="op">:</span> <span class="st">&#39;dist&#39;</span><span class="op">,</span></span>
<span id="cb9-29"><a href="#cb9-29" aria-hidden="true" tabindex="-1"></a> assetsDir<span class="op">:</span> <span class="st">&#39;static&#39;</span><span class="op">,</span></span>
<span id="cb9-30"><a href="#cb9-30" aria-hidden="true" tabindex="-1"></a> sourcemap<span class="op">:</span> <span class="kw">false</span><span class="op">,</span></span>
<span id="cb9-31"><a href="#cb9-31" aria-hidden="true" tabindex="-1"></a> chunkSizeWarningLimit<span class="op">:</span> <span class="dv">1500</span><span class="op">,</span></span>
<span id="cb9-32"><a href="#cb9-32" aria-hidden="true" tabindex="-1"></a> rollupOptions<span class="op">:</span> {</span>
<span id="cb9-33"><a href="#cb9-33" aria-hidden="true" tabindex="-1"></a> output<span class="op">:</span> {</span>
<span id="cb9-34"><a href="#cb9-34" aria-hidden="true" tabindex="-1"></a> chunkFileNames<span class="op">:</span> <span class="st">&#39;static/js/[name]-[hash].js&#39;</span><span class="op">,</span></span>
<span id="cb9-35"><a href="#cb9-35" aria-hidden="true" tabindex="-1"></a> entryFileNames<span class="op">:</span> <span class="st">&#39;static/js/[name]-[hash].js&#39;</span><span class="op">,</span></span>
<span id="cb9-36"><a href="#cb9-36" aria-hidden="true" tabindex="-1"></a> assetFileNames<span class="op">:</span> <span class="st">&#39;static/[ext]/[name]-[hash].[ext]&#39;</span></span>
<span id="cb9-37"><a href="#cb9-37" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb9-38"><a href="#cb9-38" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb9-39"><a href="#cb9-39" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb9-40"><a href="#cb9-40" aria-hidden="true" tabindex="-1"></a>})</span></code></pre></div>
<p><strong>src/stores/user.ts 用户状态管理:</strong></p>
<div class="sourceCode" id="cb10"><pre
class="sourceCode typescript"><code class="sourceCode typescript"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> { defineStore } <span class="im">from</span> <span class="st">&#39;pinia&#39;</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> { ref<span class="op">,</span> computed } <span class="im">from</span> <span class="st">&#39;vue&#39;</span></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> { UserApi<span class="op">,</span> UserVO } <span class="im">from</span> <span class="st">&#39;@/api/system/user&#39;</span></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> { getAccessToken<span class="op">,</span> removeToken } <span class="im">from</span> <span class="st">&#39;@/utils/auth&#39;</span></span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a><span class="im">export</span> <span class="kw">const</span> useUserStore <span class="op">=</span> <span class="fu">defineStore</span>(<span class="st">&#39;user&#39;</span><span class="op">,</span> () <span class="kw">=&gt;</span> {</span>
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true" tabindex="-1"></a> <span class="kw">const</span> userInfo <span class="op">=</span> <span class="fu">ref</span><span class="op">&lt;</span>UserVO<span class="op">&gt;</span>()</span>
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true" tabindex="-1"></a> <span class="kw">const</span> permissions <span class="op">=</span> <span class="fu">ref</span><span class="op">&lt;</span><span class="dt">string</span>[]<span class="op">&gt;</span>([])</span>
<span id="cb10-9"><a href="#cb10-9" aria-hidden="true" tabindex="-1"></a> <span class="kw">const</span> roles <span class="op">=</span> <span class="fu">ref</span><span class="op">&lt;</span><span class="dt">string</span>[]<span class="op">&gt;</span>([])</span>
<span id="cb10-10"><a href="#cb10-10" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb10-11"><a href="#cb10-11" aria-hidden="true" tabindex="-1"></a> <span class="kw">const</span> nickname <span class="op">=</span> <span class="fu">computed</span>(() <span class="kw">=&gt;</span> userInfo<span class="op">.</span><span class="at">value</span><span class="op">?.</span><span class="at">nickname</span> <span class="op">??</span> <span class="st">&#39;&#39;</span>)</span>
<span id="cb10-12"><a href="#cb10-12" aria-hidden="true" tabindex="-1"></a> <span class="kw">const</span> avatar <span class="op">=</span> <span class="fu">computed</span>(() <span class="kw">=&gt;</span> userInfo<span class="op">.</span><span class="at">value</span><span class="op">?.</span><span class="at">avatar</span> <span class="op">??</span> <span class="st">&#39;&#39;</span>)</span>
<span id="cb10-13"><a href="#cb10-13" aria-hidden="true" tabindex="-1"></a> <span class="kw">const</span> email <span class="op">=</span> <span class="fu">computed</span>(() <span class="kw">=&gt;</span> userInfo<span class="op">.</span><span class="at">value</span><span class="op">?.</span><span class="at">email</span> <span class="op">??</span> <span class="st">&#39;&#39;</span>)</span>
<span id="cb10-14"><a href="#cb10-14" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb10-15"><a href="#cb10-15" aria-hidden="true" tabindex="-1"></a> <span class="co">// 获取用户信息</span></span>
<span id="cb10-16"><a href="#cb10-16" aria-hidden="true" tabindex="-1"></a> <span class="kw">const</span> getUserInfo <span class="op">=</span> <span class="kw">async</span> () <span class="kw">=&gt;</span> {</span>
<span id="cb10-17"><a href="#cb10-17" aria-hidden="true" tabindex="-1"></a> <span class="kw">const</span> res <span class="op">=</span> <span class="cf">await</span> UserApi<span class="op">.</span><span class="fu">getUserProfile</span>()</span>
<span id="cb10-18"><a href="#cb10-18" aria-hidden="true" tabindex="-1"></a> userInfo<span class="op">.</span><span class="at">value</span> <span class="op">=</span> res</span>
<span id="cb10-19"><a href="#cb10-19" aria-hidden="true" tabindex="-1"></a> permissions<span class="op">.</span><span class="at">value</span> <span class="op">=</span> res<span class="op">.</span><span class="at">permissions</span></span>
<span id="cb10-20"><a href="#cb10-20" aria-hidden="true" tabindex="-1"></a> roles<span class="op">.</span><span class="at">value</span> <span class="op">=</span> res<span class="op">.</span><span class="at">roles</span></span>
<span id="cb10-21"><a href="#cb10-21" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb10-22"><a href="#cb10-22" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb10-23"><a href="#cb10-23" aria-hidden="true" tabindex="-1"></a> <span class="co">// 用户登出</span></span>
<span id="cb10-24"><a href="#cb10-24" aria-hidden="true" tabindex="-1"></a> <span class="kw">const</span> logout <span class="op">=</span> <span class="kw">async</span> () <span class="kw">=&gt;</span> {</span>
<span id="cb10-25"><a href="#cb10-25" aria-hidden="true" tabindex="-1"></a> <span class="cf">try</span> {</span>
<span id="cb10-26"><a href="#cb10-26" aria-hidden="true" tabindex="-1"></a> <span class="cf">await</span> UserApi<span class="op">.</span><span class="fu">logout</span>()</span>
<span id="cb10-27"><a href="#cb10-27" aria-hidden="true" tabindex="-1"></a> } <span class="cf">finally</span> {</span>
<span id="cb10-28"><a href="#cb10-28" aria-hidden="true" tabindex="-1"></a> <span class="cf">await</span> <span class="fu">resetToken</span>()</span>
<span id="cb10-29"><a href="#cb10-29" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb10-30"><a href="#cb10-30" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb10-31"><a href="#cb10-31" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb10-32"><a href="#cb10-32" aria-hidden="true" tabindex="-1"></a> <span class="co">// 重置令牌</span></span>
<span id="cb10-33"><a href="#cb10-33" aria-hidden="true" tabindex="-1"></a> <span class="kw">const</span> resetToken <span class="op">=</span> <span class="kw">async</span> () <span class="kw">=&gt;</span> {</span>
<span id="cb10-34"><a href="#cb10-34" aria-hidden="true" tabindex="-1"></a> userInfo<span class="op">.</span><span class="at">value</span> <span class="op">=</span> <span class="kw">undefined</span></span>
<span id="cb10-35"><a href="#cb10-35" aria-hidden="true" tabindex="-1"></a> permissions<span class="op">.</span><span class="at">value</span> <span class="op">=</span> []</span>
<span id="cb10-36"><a href="#cb10-36" aria-hidden="true" tabindex="-1"></a> roles<span class="op">.</span><span class="at">value</span> <span class="op">=</span> []</span>
<span id="cb10-37"><a href="#cb10-37" aria-hidden="true" tabindex="-1"></a> <span class="fu">removeToken</span>()</span>
<span id="cb10-38"><a href="#cb10-38" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb10-39"><a href="#cb10-39" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb10-40"><a href="#cb10-40" aria-hidden="true" tabindex="-1"></a> <span class="co">// 检查权限</span></span>
<span id="cb10-41"><a href="#cb10-41" aria-hidden="true" tabindex="-1"></a> <span class="kw">const</span> hasPermission <span class="op">=</span> (permission<span class="op">:</span> <span class="dt">string</span>) <span class="kw">=&gt;</span> {</span>
<span id="cb10-42"><a href="#cb10-42" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> permissions<span class="op">.</span><span class="at">value</span><span class="op">.</span><span class="fu">includes</span>(permission)</span>
<span id="cb10-43"><a href="#cb10-43" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb10-44"><a href="#cb10-44" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb10-45"><a href="#cb10-45" aria-hidden="true" tabindex="-1"></a> <span class="co">// 检查角色</span></span>
<span id="cb10-46"><a href="#cb10-46" aria-hidden="true" tabindex="-1"></a> <span class="kw">const</span> hasRole <span class="op">=</span> (role<span class="op">:</span> <span class="dt">string</span>) <span class="kw">=&gt;</span> {</span>
<span id="cb10-47"><a href="#cb10-47" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> roles<span class="op">.</span><span class="at">value</span><span class="op">.</span><span class="fu">includes</span>(role)</span>
<span id="cb10-48"><a href="#cb10-48" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb10-49"><a href="#cb10-49" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb10-50"><a href="#cb10-50" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> {</span>
<span id="cb10-51"><a href="#cb10-51" aria-hidden="true" tabindex="-1"></a> userInfo<span class="op">,</span></span>
<span id="cb10-52"><a href="#cb10-52" aria-hidden="true" tabindex="-1"></a> permissions<span class="op">,</span></span>
<span id="cb10-53"><a href="#cb10-53" aria-hidden="true" tabindex="-1"></a> roles<span class="op">,</span></span>
<span id="cb10-54"><a href="#cb10-54" aria-hidden="true" tabindex="-1"></a> nickname<span class="op">,</span></span>
<span id="cb10-55"><a href="#cb10-55" aria-hidden="true" tabindex="-1"></a> avatar<span class="op">,</span></span>
<span id="cb10-56"><a href="#cb10-56" aria-hidden="true" tabindex="-1"></a> email<span class="op">,</span></span>
<span id="cb10-57"><a href="#cb10-57" aria-hidden="true" tabindex="-1"></a> getUserInfo<span class="op">,</span></span>
<span id="cb10-58"><a href="#cb10-58" aria-hidden="true" tabindex="-1"></a> logout<span class="op">,</span></span>
<span id="cb10-59"><a href="#cb10-59" aria-hidden="true" tabindex="-1"></a> resetToken<span class="op">,</span></span>
<span id="cb10-60"><a href="#cb10-60" aria-hidden="true" tabindex="-1"></a> hasPermission<span class="op">,</span></span>
<span id="cb10-61"><a href="#cb10-61" aria-hidden="true" tabindex="-1"></a> hasRole</span>
<span id="cb10-62"><a href="#cb10-62" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb10-63"><a href="#cb10-63" aria-hidden="true" tabindex="-1"></a>})</span></code></pre></div>
<p><strong>src/utils/request.ts HTTP请求封装</strong></p>
<div class="sourceCode" id="cb11"><pre
class="sourceCode typescript"><code class="sourceCode typescript"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> axios<span class="op">,</span> { AxiosResponse<span class="op">,</span> InternalAxiosRequestConfig } <span class="im">from</span> <span class="st">&#39;axios&#39;</span></span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> { ElMessage<span class="op">,</span> ElMessageBox } <span class="im">from</span> <span class="st">&#39;element-plus&#39;</span></span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> { getAccessToken<span class="op">,</span> getRefreshToken<span class="op">,</span> removeToken } <span class="im">from</span> <span class="st">&#39;@/utils/auth&#39;</span></span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> { useUserStore } <span class="im">from</span> <span class="st">&#39;@/stores/user&#39;</span></span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true" tabindex="-1"></a><span class="co">// 创建 axios 实例</span></span>
<span id="cb11-7"><a href="#cb11-7" aria-hidden="true" tabindex="-1"></a><span class="kw">const</span> service <span class="op">=</span> axios<span class="op">.</span><span class="fu">create</span>({</span>
<span id="cb11-8"><a href="#cb11-8" aria-hidden="true" tabindex="-1"></a> baseURL<span class="op">:</span> import<span class="op">.</span><span class="at">meta</span><span class="op">.</span><span class="at">env</span><span class="op">.</span><span class="at">VITE_BASE_URL</span><span class="op">,</span></span>
<span id="cb11-9"><a href="#cb11-9" aria-hidden="true" tabindex="-1"></a> timeout<span class="op">:</span> <span class="dv">50000</span><span class="op">,</span></span>
<span id="cb11-10"><a href="#cb11-10" aria-hidden="true" tabindex="-1"></a> withCredentials<span class="op">:</span> <span class="kw">false</span></span>
<span id="cb11-11"><a href="#cb11-11" aria-hidden="true" tabindex="-1"></a>})</span>
<span id="cb11-12"><a href="#cb11-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-13"><a href="#cb11-13" aria-hidden="true" tabindex="-1"></a><span class="co">// 请求拦截器</span></span>
<span id="cb11-14"><a href="#cb11-14" aria-hidden="true" tabindex="-1"></a>service<span class="op">.</span><span class="at">interceptors</span><span class="op">.</span><span class="at">request</span><span class="op">.</span><span class="fu">use</span>(</span>
<span id="cb11-15"><a href="#cb11-15" aria-hidden="true" tabindex="-1"></a> (config<span class="op">:</span> InternalAxiosRequestConfig) <span class="kw">=&gt;</span> {</span>
<span id="cb11-16"><a href="#cb11-16" aria-hidden="true" tabindex="-1"></a> <span class="co">// 添加 token</span></span>
<span id="cb11-17"><a href="#cb11-17" aria-hidden="true" tabindex="-1"></a> <span class="kw">const</span> accessToken <span class="op">=</span> <span class="fu">getAccessToken</span>()</span>
<span id="cb11-18"><a href="#cb11-18" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> (accessToken <span class="op">&amp;&amp;</span> config<span class="op">.</span><span class="at">headers</span>) {</span>
<span id="cb11-19"><a href="#cb11-19" aria-hidden="true" tabindex="-1"></a> config<span class="op">.</span><span class="at">headers</span><span class="op">.</span><span class="at">Authorization</span> <span class="op">=</span> <span class="vs">`Bearer </span><span class="sc">${</span>accessToken<span class="sc">}</span><span class="vs">`</span></span>
<span id="cb11-20"><a href="#cb11-20" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb11-21"><a href="#cb11-21" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb11-22"><a href="#cb11-22" aria-hidden="true" tabindex="-1"></a> <span class="co">// 添加租户ID</span></span>
<span id="cb11-23"><a href="#cb11-23" aria-hidden="true" tabindex="-1"></a> <span class="kw">const</span> tenantId <span class="op">=</span> localStorage<span class="op">.</span><span class="fu">getItem</span>(<span class="st">&#39;tenantId&#39;</span>)</span>
<span id="cb11-24"><a href="#cb11-24" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> (tenantId <span class="op">&amp;&amp;</span> config<span class="op">.</span><span class="at">headers</span>) {</span>
<span id="cb11-25"><a href="#cb11-25" aria-hidden="true" tabindex="-1"></a> config<span class="op">.</span><span class="at">headers</span>[<span class="st">&#39;tenant-id&#39;</span>] <span class="op">=</span> tenantId</span>
<span id="cb11-26"><a href="#cb11-26" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb11-27"><a href="#cb11-27" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb11-28"><a href="#cb11-28" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> config</span>
<span id="cb11-29"><a href="#cb11-29" aria-hidden="true" tabindex="-1"></a> }<span class="op">,</span></span>
<span id="cb11-30"><a href="#cb11-30" aria-hidden="true" tabindex="-1"></a> error <span class="kw">=&gt;</span> {</span>
<span id="cb11-31"><a href="#cb11-31" aria-hidden="true" tabindex="-1"></a> <span class="bu">console</span><span class="op">.</span><span class="fu">log</span>(error)</span>
<span id="cb11-32"><a href="#cb11-32" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="bu">Promise</span><span class="op">.</span><span class="fu">reject</span>(error)</span>
<span id="cb11-33"><a href="#cb11-33" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb11-34"><a href="#cb11-34" aria-hidden="true" tabindex="-1"></a>)</span>
<span id="cb11-35"><a href="#cb11-35" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-36"><a href="#cb11-36" aria-hidden="true" tabindex="-1"></a><span class="co">// 响应拦截器</span></span>
<span id="cb11-37"><a href="#cb11-37" aria-hidden="true" tabindex="-1"></a>service<span class="op">.</span><span class="at">interceptors</span><span class="op">.</span><span class="at">response</span><span class="op">.</span><span class="fu">use</span>(</span>
<span id="cb11-38"><a href="#cb11-38" aria-hidden="true" tabindex="-1"></a> (response<span class="op">:</span> AxiosResponse) <span class="kw">=&gt;</span> {</span>
<span id="cb11-39"><a href="#cb11-39" aria-hidden="true" tabindex="-1"></a> <span class="kw">const</span> { data } <span class="op">=</span> response</span>
<span id="cb11-40"><a href="#cb11-40" aria-hidden="true" tabindex="-1"></a> <span class="kw">const</span> { code<span class="op">,</span> msg } <span class="op">=</span> data</span>
<span id="cb11-41"><a href="#cb11-41" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb11-42"><a href="#cb11-42" aria-hidden="true" tabindex="-1"></a> <span class="co">// 业务请求成功</span></span>
<span id="cb11-43"><a href="#cb11-43" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> (code <span class="op">===</span> <span class="dv">0</span>) {</span>
<span id="cb11-44"><a href="#cb11-44" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> data</span>
<span id="cb11-45"><a href="#cb11-45" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb11-46"><a href="#cb11-46" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb11-47"><a href="#cb11-47" aria-hidden="true" tabindex="-1"></a> <span class="co">// token 过期,尝试刷新</span></span>
<span id="cb11-48"><a href="#cb11-48" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> (code <span class="op">===</span> <span class="dv">401</span>) {</span>
<span id="cb11-49"><a href="#cb11-49" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="fu">handleTokenExpired</span>()</span>
<span id="cb11-50"><a href="#cb11-50" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb11-51"><a href="#cb11-51" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb11-52"><a href="#cb11-52" aria-hidden="true" tabindex="-1"></a> <span class="co">// 业务请求失败</span></span>
<span id="cb11-53"><a href="#cb11-53" aria-hidden="true" tabindex="-1"></a> ElMessage<span class="op">.</span><span class="fu">error</span>(msg <span class="op">||</span> <span class="st">&#39;系统未知错误,请反馈给管理员&#39;</span>)</span>
<span id="cb11-54"><a href="#cb11-54" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="bu">Promise</span><span class="op">.</span><span class="fu">reject</span>(<span class="kw">new</span> <span class="bu">Error</span>(msg <span class="op">||</span> <span class="st">&#39;Error&#39;</span>))</span>
<span id="cb11-55"><a href="#cb11-55" aria-hidden="true" tabindex="-1"></a> }<span class="op">,</span></span>
<span id="cb11-56"><a href="#cb11-56" aria-hidden="true" tabindex="-1"></a> error <span class="kw">=&gt;</span> {</span>
<span id="cb11-57"><a href="#cb11-57" aria-hidden="true" tabindex="-1"></a> <span class="bu">console</span><span class="op">.</span><span class="fu">log</span>(<span class="st">&#39;err&#39;</span> <span class="op">+</span> error)</span>
<span id="cb11-58"><a href="#cb11-58" aria-hidden="true" tabindex="-1"></a> <span class="kw">let</span> { message } <span class="op">=</span> error</span>
<span id="cb11-59"><a href="#cb11-59" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> (message <span class="op">===</span> <span class="st">&#39;Network Error&#39;</span>) {</span>
<span id="cb11-60"><a href="#cb11-60" aria-hidden="true" tabindex="-1"></a> message <span class="op">=</span> <span class="st">&#39;后端接口连接异常&#39;</span></span>
<span id="cb11-61"><a href="#cb11-61" aria-hidden="true" tabindex="-1"></a> } <span class="cf">else</span> <span class="cf">if</span> (message<span class="op">.</span><span class="fu">includes</span>(<span class="st">&#39;timeout&#39;</span>)) {</span>
<span id="cb11-62"><a href="#cb11-62" aria-hidden="true" tabindex="-1"></a> message <span class="op">=</span> <span class="st">&#39;系统接口请求超时&#39;</span></span>
<span id="cb11-63"><a href="#cb11-63" aria-hidden="true" tabindex="-1"></a> } <span class="cf">else</span> <span class="cf">if</span> (message<span class="op">.</span><span class="fu">includes</span>(<span class="st">&#39;Request failed with status code&#39;</span>)) {</span>
<span id="cb11-64"><a href="#cb11-64" aria-hidden="true" tabindex="-1"></a> message <span class="op">=</span> <span class="st">&#39;系统接口&#39;</span> <span class="op">+</span> message<span class="op">.</span><span class="fu">substr</span>(message<span class="op">.</span><span class="at">length</span> <span class="op">-</span> <span class="dv">3</span>) <span class="op">+</span> <span class="st">&#39;异常&#39;</span></span>
<span id="cb11-65"><a href="#cb11-65" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb11-66"><a href="#cb11-66" aria-hidden="true" tabindex="-1"></a> ElMessage<span class="op">.</span><span class="fu">error</span>(message)</span>
<span id="cb11-67"><a href="#cb11-67" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="bu">Promise</span><span class="op">.</span><span class="fu">reject</span>(error)</span>
<span id="cb11-68"><a href="#cb11-68" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb11-69"><a href="#cb11-69" aria-hidden="true" tabindex="-1"></a>)</span>
<span id="cb11-70"><a href="#cb11-70" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-71"><a href="#cb11-71" aria-hidden="true" tabindex="-1"></a><span class="co">// 处理 token 过期</span></span>
<span id="cb11-72"><a href="#cb11-72" aria-hidden="true" tabindex="-1"></a><span class="kw">const</span> handleTokenExpired <span class="op">=</span> <span class="kw">async</span> () <span class="kw">=&gt;</span> {</span>
<span id="cb11-73"><a href="#cb11-73" aria-hidden="true" tabindex="-1"></a> <span class="kw">const</span> userStore <span class="op">=</span> <span class="fu">useUserStore</span>()</span>
<span id="cb11-74"><a href="#cb11-74" aria-hidden="true" tabindex="-1"></a> ElMessageBox<span class="op">.</span><span class="fu">alert</span>(<span class="st">&#39;登录状态已过期,您可以继续留在该页面,或者重新登录&#39;</span><span class="op">,</span> <span class="st">&#39;系统提示&#39;</span><span class="op">,</span> {</span>
<span id="cb11-75"><a href="#cb11-75" aria-hidden="true" tabindex="-1"></a> confirmButtonText<span class="op">:</span> <span class="st">&#39;重新登录&#39;</span><span class="op">,</span></span>
<span id="cb11-76"><a href="#cb11-76" aria-hidden="true" tabindex="-1"></a> type<span class="op">:</span> <span class="st">&#39;warning&#39;</span></span>
<span id="cb11-77"><a href="#cb11-77" aria-hidden="true" tabindex="-1"></a> })<span class="op">.</span><span class="fu">then</span>(() <span class="kw">=&gt;</span> {</span>
<span id="cb11-78"><a href="#cb11-78" aria-hidden="true" tabindex="-1"></a> userStore<span class="op">.</span><span class="fu">resetToken</span>()<span class="op">.</span><span class="fu">then</span>(() <span class="kw">=&gt;</span> {</span>
<span id="cb11-79"><a href="#cb11-79" aria-hidden="true" tabindex="-1"></a> location<span class="op">.</span><span class="fu">reload</span>()</span>
<span id="cb11-80"><a href="#cb11-80" aria-hidden="true" tabindex="-1"></a> })</span>
<span id="cb11-81"><a href="#cb11-81" aria-hidden="true" tabindex="-1"></a> })</span>
<span id="cb11-82"><a href="#cb11-82" aria-hidden="true" tabindex="-1"></a>}</span>
<span id="cb11-83"><a href="#cb11-83" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-84"><a href="#cb11-84" aria-hidden="true" tabindex="-1"></a><span class="im">export</span> <span class="im">default</span> service</span></code></pre></div>
<h5 data-number="2.0.0.0.14" id="前端技术架构详细设计"><span
class="header-section-number">2.0.0.0.14</span> 2.5
前端技术架构详细设计</h5>
<h5 data-number="2.0.0.0.15"
id="web管理端架构-yudao-ui-admin-vue3"><span
class="header-section-number">2.0.0.0.15</span> 2.5.1 Web管理端架构
(yudao-ui-admin-vue3)</h5>
<p><strong>技术栈组成:</strong></p>
<pre class="mermaid"><code>graph TB
subgraph &quot;开发框架&quot;
A1[Vue 3.2.47 Composition API]
A2[TypeScript 4.9.4]
A3[Vite 4.0.0 构建工具]
end
subgraph &quot;UI组件库&quot;
B1[Element Plus 2.3.14]
B2[Element Plus Icons]
B3[自定义组件库]
end
subgraph &quot;状态管理&quot;
C1[Pinia 2.0.28]
C2[持久化插件]
C3[全局状态管理]
end
subgraph &quot;路由系统&quot;
D1[Vue Router 4.1.6]
D2[路由守卫]
D3[动态路由]
D4[权限控制]
end
subgraph &quot;HTTP通信&quot;
E1[Axios 1.2.2]
E2[请求拦截器]
E3[响应拦截器]
E4[错误处理]
end
subgraph &quot;工具库&quot;
F1[Lodash 工具函数]
F2[dayjs 时间处理]
F3[nprogress 进度条]
F4[js-cookie Cookie管理]
F5[crypto-js 加密解密]
end
A1 --&gt; B1
A1 --&gt; C1
A1 --&gt; D1
A1 --&gt; E1
B1 --&gt; B3
C1 --&gt; C2
D1 --&gt; D2
E1 --&gt; E2</code></pre>
<p><strong>项目结构设计:</strong></p>
<pre class="text"><code>yudao-ui-admin-vue3/
├── public/ # 静态资源
├── src/
│ ├── api/ # API接口定义
│ │ └── water/ # 水务业务API
│ │ ├── customer/ # 客户管理API
│ │ ├── meter/ # 抄表管理API
│ │ ├── billing/ # 收费管理API
│ │ └── workflow/ # 工单管理API
│ ├── assets/ # 静态资源
│ ├── components/ # 全局组件
│ │ ├── Dialog/ # 弹窗组件
│ │ ├── Form/ # 表单组件
│ │ ├── Table/ # 表格组件
│ │ └── Upload/ # 上传组件
│ ├── layout/ # 布局组件
│ ├── router/ # 路由配置
│ ├── stores/ # Pinia状态管理
│ ├── styles/ # 全局样式
│ ├── utils/ # 工具函数
│ ├── views/ # 页面组件
│ │ └── water/ # 水务业务页面
│ │ ├── customer/ # 客户管理
│ │ ├── meter/ # 抄表管理
│ │ ├── billing/ # 收费管理
│ │ └── workflow/ # 工单管理
│ └── App.vue
├── types/ # TypeScript类型定义
├── vite.config.ts # Vite配置
├── tsconfig.json # TypeScript配置
└── package.json # 项目依赖</code></pre>
<p><strong>核心配置示例:</strong></p>
<div class="sourceCode" id="cb14"><pre
class="sourceCode typescript"><code class="sourceCode typescript"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="co">// vite.config.ts - Vite构建配置</span></span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> { defineConfig } <span class="im">from</span> <span class="st">&#39;vite&#39;</span></span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> vue <span class="im">from</span> <span class="st">&#39;@vitejs/plugin-vue&#39;</span></span>
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> { resolve } <span class="im">from</span> <span class="st">&#39;path&#39;</span></span>
<span id="cb14-5"><a href="#cb14-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-6"><a href="#cb14-6" aria-hidden="true" tabindex="-1"></a><span class="im">export</span> <span class="im">default</span> <span class="fu">defineConfig</span>({</span>
<span id="cb14-7"><a href="#cb14-7" aria-hidden="true" tabindex="-1"></a> plugins<span class="op">:</span> [<span class="fu">vue</span>()]<span class="op">,</span></span>
<span id="cb14-8"><a href="#cb14-8" aria-hidden="true" tabindex="-1"></a> resolve<span class="op">:</span> {</span>
<span id="cb14-9"><a href="#cb14-9" aria-hidden="true" tabindex="-1"></a> alias<span class="op">:</span> {</span>
<span id="cb14-10"><a href="#cb14-10" aria-hidden="true" tabindex="-1"></a> <span class="st">&#39;@&#39;</span><span class="op">:</span> <span class="fu">resolve</span>(<span class="bu">__dirname</span><span class="op">,</span> <span class="st">&#39;src&#39;</span>)<span class="op">,</span></span>
<span id="cb14-11"><a href="#cb14-11" aria-hidden="true" tabindex="-1"></a> <span class="st">&#39;@/api&#39;</span><span class="op">:</span> <span class="fu">resolve</span>(<span class="bu">__dirname</span><span class="op">,</span> <span class="st">&#39;src/api&#39;</span>)<span class="op">,</span></span>
<span id="cb14-12"><a href="#cb14-12" aria-hidden="true" tabindex="-1"></a> <span class="st">&#39;@/components&#39;</span><span class="op">:</span> <span class="fu">resolve</span>(<span class="bu">__dirname</span><span class="op">,</span> <span class="st">&#39;src/components&#39;</span>)<span class="op">,</span></span>
<span id="cb14-13"><a href="#cb14-13" aria-hidden="true" tabindex="-1"></a> <span class="st">&#39;@/utils&#39;</span><span class="op">:</span> <span class="fu">resolve</span>(<span class="bu">__dirname</span><span class="op">,</span> <span class="st">&#39;src/utils&#39;</span>)</span>
<span id="cb14-14"><a href="#cb14-14" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb14-15"><a href="#cb14-15" aria-hidden="true" tabindex="-1"></a> }<span class="op">,</span></span>
<span id="cb14-16"><a href="#cb14-16" aria-hidden="true" tabindex="-1"></a> server<span class="op">:</span> {</span>
<span id="cb14-17"><a href="#cb14-17" aria-hidden="true" tabindex="-1"></a> port<span class="op">:</span> <span class="dv">80</span><span class="op">,</span></span>
<span id="cb14-18"><a href="#cb14-18" aria-hidden="true" tabindex="-1"></a> proxy<span class="op">:</span> {</span>
<span id="cb14-19"><a href="#cb14-19" aria-hidden="true" tabindex="-1"></a> <span class="st">&#39;/admin-api&#39;</span><span class="op">:</span> {</span>
<span id="cb14-20"><a href="#cb14-20" aria-hidden="true" tabindex="-1"></a> target<span class="op">:</span> <span class="st">&#39;http://127.0.0.1:48080&#39;</span><span class="op">,</span></span>
<span id="cb14-21"><a href="#cb14-21" aria-hidden="true" tabindex="-1"></a> changeOrigin<span class="op">:</span> <span class="kw">true</span><span class="op">,</span></span>
<span id="cb14-22"><a href="#cb14-22" aria-hidden="true" tabindex="-1"></a> rewrite<span class="op">:</span> (path) <span class="kw">=&gt;</span> path<span class="op">.</span><span class="fu">replace</span>(<span class="ss">/</span><span class="sc">^\/</span><span class="ss">admin-api/</span><span class="op">,</span> <span class="st">&#39;/admin-api&#39;</span>)</span>
<span id="cb14-23"><a href="#cb14-23" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb14-24"><a href="#cb14-24" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb14-25"><a href="#cb14-25" aria-hidden="true" tabindex="-1"></a> }<span class="op">,</span></span>
<span id="cb14-26"><a href="#cb14-26" aria-hidden="true" tabindex="-1"></a> build<span class="op">:</span> {</span>
<span id="cb14-27"><a href="#cb14-27" aria-hidden="true" tabindex="-1"></a> outDir<span class="op">:</span> <span class="st">&#39;dist&#39;</span><span class="op">,</span></span>
<span id="cb14-28"><a href="#cb14-28" aria-hidden="true" tabindex="-1"></a> sourcemap<span class="op">:</span> <span class="kw">false</span><span class="op">,</span></span>
<span id="cb14-29"><a href="#cb14-29" aria-hidden="true" tabindex="-1"></a> rollupOptions<span class="op">:</span> {</span>
<span id="cb14-30"><a href="#cb14-30" aria-hidden="true" tabindex="-1"></a> output<span class="op">:</span> {</span>
<span id="cb14-31"><a href="#cb14-31" aria-hidden="true" tabindex="-1"></a> chunkFileNames<span class="op">:</span> <span class="st">&#39;js/[name]-[hash].js&#39;</span><span class="op">,</span></span>
<span id="cb14-32"><a href="#cb14-32" aria-hidden="true" tabindex="-1"></a> entryFileNames<span class="op">:</span> <span class="st">&#39;js/[name]-[hash].js&#39;</span><span class="op">,</span></span>
<span id="cb14-33"><a href="#cb14-33" aria-hidden="true" tabindex="-1"></a> assetFileNames<span class="op">:</span> <span class="st">&#39;[ext]/[name]-[hash].[ext]&#39;</span></span>
<span id="cb14-34"><a href="#cb14-34" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb14-35"><a href="#cb14-35" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb14-36"><a href="#cb14-36" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb14-37"><a href="#cb14-37" aria-hidden="true" tabindex="-1"></a>})</span></code></pre></div>
<h5 data-number="2.0.0.0.16" id="移动端架构-uni-app"><span
class="header-section-number">2.0.0.0.16</span> 2.5.2 移动端架构
(uni-app)</h5>
<p><strong>技术栈组成:</strong></p>
<pre class="mermaid"><code>graph TB
subgraph &quot;跨平台框架&quot;
M1[uni-app 3.x]
M2[Vue 3 Composition API]
M3[TypeScript支持]
end
subgraph &quot;UI组件库&quot;
N1[uView UI 2.0]
N2[uni-ui组件]
N3[自定义水务组件]
end
subgraph &quot;状态管理&quot;
O1[Vuex 4.x]
O2[uni-app存储]
O3[缓存管理]
end
subgraph &quot;设备能力&quot;
P1[相机API&lt;br/&gt;水表拍照]
P2[NFC读取&lt;br/&gt;水表标签]
P3[GPS定位&lt;br/&gt;抄表轨迹]
P4[扫码API&lt;br/&gt;二维码扫描]
end
subgraph &quot;网络通信&quot;
Q1[uni.request&lt;br/&gt;HTTP请求]
Q2[WebSocket&lt;br/&gt;实时通信]
Q3[文件上传&lt;br/&gt;图片处理]
end
M1 --&gt; N1
M1 --&gt; O1
M1 --&gt; P1
M1 --&gt; Q1
N1 --&gt; N3
P1 --&gt; P2
Q1 --&gt; Q2</code></pre>
<p><strong>移动端项目结构:</strong></p>
<pre class="text"><code>water-mobile-app/
├── pages/ # 页面目录
│ ├── index/ # 首页
│ ├── meter/ # 抄表功能
│ │ ├── task/ # 抄表任务
│ │ ├── read/ # 抄表录入
│ │ └── history/ # 抄表历史
│ ├── workflow/ # 工单管理
│ └── personal/ # 个人中心
├── components/ # 组件目录
│ ├── meter-reader/ # 水表读数组件
│ ├── map-trace/ # 地图轨迹组件
│ └── camera-scan/ # 相机扫描组件
├── static/ # 静态资源
├── store/ # 状态管理
├── utils/ # 工具函数
├── api/ # 接口定义
├── manifest.json # 应用配置
└── pages.json # 页面配置</code></pre>
<h5 data-number="2.0.0.0.17" id="客户端小程序架构"><span
class="header-section-number">2.0.0.0.17</span> 2.5.3
客户端小程序架构</h5>
<p><strong>微信小程序技术栈:</strong> - 开发框架:微信小程序原生框架 -
UI组件WeUI + 自定义组件 - 状态管理:小程序全局数据管理 -
支付集成微信支付API - 地图服务腾讯地图API</p>
<p><strong>支付宝小程序技术栈:</strong> -
开发框架:支付宝小程序原生框架<br />
- UI组件mini-antui + 自定义组件 - 状态管理:小程序全局数据管理 -
支付集成支付宝支付API - 地图服务高德地图API</p>
<h5 data-number="2.0.0.0.18" id="前端工程化配置"><span
class="header-section-number">2.0.0.0.18</span> 2.5.4
前端工程化配置</h5>
<p><strong>代码规范:</strong></p>
<div class="sourceCode" id="cb17"><pre
class="sourceCode json"><code class="sourceCode json"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a><span class="er">//</span> <span class="er">.eslintrc.js</span> <span class="er">-</span> <span class="er">ESLint配置</span></span>
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb17-3"><a href="#cb17-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;extends&quot;</span><span class="fu">:</span> <span class="ot">[</span></span>
<span id="cb17-4"><a href="#cb17-4" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;@vue/typescript/recommended&quot;</span><span class="ot">,</span></span>
<span id="cb17-5"><a href="#cb17-5" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;@vue/prettier&quot;</span><span class="ot">,</span></span>
<span id="cb17-6"><a href="#cb17-6" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;@vue/prettier/@typescript-eslint&quot;</span></span>
<span id="cb17-7"><a href="#cb17-7" aria-hidden="true" tabindex="-1"></a> <span class="ot">]</span><span class="fu">,</span></span>
<span id="cb17-8"><a href="#cb17-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;rules&quot;</span><span class="fu">:</span> <span class="fu">{</span></span>
<span id="cb17-9"><a href="#cb17-9" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;@typescript-eslint/no-explicit-any&quot;</span><span class="fu">:</span> <span class="st">&quot;warn&quot;</span><span class="fu">,</span></span>
<span id="cb17-10"><a href="#cb17-10" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;@typescript-eslint/no-unused-vars&quot;</span><span class="fu">:</span> <span class="st">&quot;error&quot;</span><span class="fu">,</span></span>
<span id="cb17-11"><a href="#cb17-11" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;vue/multi-word-component-names&quot;</span><span class="fu">:</span> <span class="st">&quot;off&quot;</span></span>
<span id="cb17-12"><a href="#cb17-12" aria-hidden="true" tabindex="-1"></a> <span class="fu">}</span></span>
<span id="cb17-13"><a href="#cb17-13" aria-hidden="true" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
<p><strong>构建优化:</strong> - Vite热更新开发环境快速构建 -
代码分割:按路由和组件懒加载 - 资源压缩Gzip压缩和图片优化 -
CDN加速静态资源CDN分发 - 缓存策略浏览器缓存和Service Worker</p>
<ul>
<li>移动端:
<ul>
<li>核心框架uni-app跨平台</li>
<li>组件库uview-ui</li>
<li>小程序支持:微信小程序、支付宝小程序等</li>
<li>移动端地图高德地图SDK用于抄表轨迹和导航</li>
<li>设备接入NFC、扫码、相机等硬件设备接口</li>
</ul></li>
</ul>
<h5 data-number="2.0.0.0.19" id="技术特性"><span
class="header-section-number">2.0.0.0.19</span> 2.6 技术特性</h5>
<ul>
<li>单点登录OAuth2 + JWT实现统一认证</li>
<li>数据交换基于RESTful API的系统内部数据交换与共享</li>
<li>统一报表平台集成JimuReport支持自定义报表设计与生成</li>
<li>大屏设计集成GoView支持可视化大屏设计</li>
<li>安全保障:满足安全等保三级要求
<ul>
<li>密码加密BCrypt加密算法</li>
<li>防XSS攻击表单数据过滤</li>
<li>防SQL注入参数化查询</li>
<li>防CSRF攻击Token验证</li>
</ul></li>
<li>性能规格支持200并发用户50并发移动设备系统响应时间不超过3秒</li>
<li>扩展容量支持100万客户的业务量满足企业3-5年的业务发展需求</li>
<li>支持功能:
<ul>
<li>支付管理:微信支付、支付宝支付等</li>
<li>短信服务:阿里云、腾讯云等多家短信服务商</li>
<li>AI能力
<ul>
<li>智能抄表:基于图像识别的水表读数</li>
<li>异常分析基于AI的用水异常分析</li>
</ul></li>
<li>API网关支持API权限管理、限流熔断、白名单控制</li>
</ul></li>
</ul>
<h5 data-number="2.0.0.0.20" id="三应用架构"><span
class="header-section-number">2.0.0.0.20</span> 三、应用架构</h5>
<h5 data-number="2.0.0.0.21" id="微服务应用架构图"><span
class="header-section-number">2.0.0.0.21</span> 3.1
微服务应用架构图</h5>
<pre class="mermaid"><code>graph TB
subgraph &quot;网关层&quot;
GW[API网关&lt;br/&gt;Spring Cloud Gateway]
AUTH[认证服务&lt;br/&gt;OAuth2 + JWT]
end
subgraph &quot;基础服务层&quot;
subgraph &quot;系统服务&quot;
SYS[系统管理服务&lt;br/&gt;用户/角色/权限]
TENANT[租户管理服务&lt;br/&gt;多租户隔离]
FILE[文件管理服务&lt;br/&gt;附件存储]
MSG[消息服务&lt;br/&gt;短信/邮件/推送]
end
subgraph &quot;工作流服务&quot;
WF[工作流引擎&lt;br/&gt;Flowable]
TASK[任务调度服务&lt;br/&gt;Quartz]
end
end
subgraph &quot;业务服务层&quot;
subgraph &quot;客户域服务&quot;
CUST[客户管理服务&lt;br/&gt;Customer Service]
METER[水表管理服务&lt;br/&gt;Meter Service]
ADDR[地址管理服务&lt;br/&gt;Address Service]
end
subgraph &quot;营收域服务&quot;
READ[抄表服务&lt;br/&gt;Reading Service]
BILL[账单服务&lt;br/&gt;Billing Service]
PAY[收费服务&lt;br/&gt;Payment Service]
ACC[账务服务&lt;br/&gt;Accounting Service]
end
subgraph &quot;运营域服务&quot;
ORDER[工单服务&lt;br/&gt;Workflow Service]
RPT[报表服务&lt;br/&gt;Report Service]
STATS[统计分析服务&lt;br/&gt;Statistics Service]
end
subgraph &quot;接口域服务&quot;
BANK[银行接口服务&lt;br/&gt;Bank Service]
PAY_GW[支付网关服务&lt;br/&gt;Payment Gateway]
IOT[物联网服务&lt;br/&gt;IoT Service]
SMS[短信服务&lt;br/&gt;SMS Service]
end
end
subgraph &quot;数据层&quot;
subgraph &quot;业务数据库&quot;
DB_CUST[(客户库&lt;br/&gt;Customer DB)]
DB_BILL[(营收库&lt;br/&gt;Billing DB)]
DB_RPT[(报表库&lt;br/&gt;Report DB)]
end
subgraph &quot;缓存与存储&quot;
REDIS[(Redis集群&lt;br/&gt;缓存/会话)]
ES[(ElasticSearch&lt;br/&gt;搜索引擎)]
FILE_STORE[文件存储&lt;br/&gt;MinIO/OSS]
end
end
%% 网关层连接
GW --&gt; AUTH
GW --&gt; SYS
GW --&gt; CUST
GW --&gt; READ
GW --&gt; ORDER
%% 服务间调用
CUST --&gt; METER
CUST --&gt; ADDR
READ --&gt; METER
READ --&gt; CUST
BILL --&gt; READ
BILL --&gt; CUST
PAY --&gt; BILL
ACC --&gt; PAY
ORDER --&gt; WF
ORDER --&gt; TASK
RPT --&gt; STATS
%% 外部接口调用
PAY --&gt; BANK
PAY --&gt; PAY_GW
READ --&gt; IOT
ORDER --&gt; SMS
%% 数据层连接
CUST --&gt; DB_CUST
METER --&gt; DB_CUST
BILL --&gt; DB_BILL
PAY --&gt; DB_BILL
RPT --&gt; DB_RPT
SYS --&gt; REDIS
AUTH --&gt; REDIS
CUST --&gt; REDIS
BILL --&gt; ES
FILE --&gt; FILE_STORE</code></pre>
<h5 data-number="2.0.0.0.22" id="服务治理架构"><span
class="header-section-number">2.0.0.0.22</span> 3.2 服务治理架构</h5>
<pre class="mermaid"><code>graph TB
subgraph &quot;服务发现与注册&quot;
NACOS[Nacos注册中心&lt;br/&gt;服务注册/发现/配置]
end
subgraph &quot;服务网格层&quot;
subgraph &quot;负载均衡&quot;
LB[Ribbon负载均衡&lt;br/&gt;客户端负载均衡]
FEIGN[OpenFeign&lt;br/&gt;服务间调用]
end
subgraph &quot;容错保护&quot;
CB[Sentinel熔断器&lt;br/&gt;流量控制/熔断降级]
RETRY[重试机制&lt;br/&gt;失败重试]
end
subgraph &quot;链路追踪&quot;
TRACE[SkyWalking&lt;br/&gt;分布式链路追踪]
METRIC[Micrometer&lt;br/&gt;指标收集]
end
end
subgraph &quot;配置管理&quot;
CONFIG[Nacos Config&lt;br/&gt;配置中心]
SECRET[配置加密&lt;br/&gt;敏感信息保护]
end
subgraph &quot;监控告警&quot;
MONITOR[Spring Boot Admin&lt;br/&gt;应用监控]
ALERT[告警系统&lt;br/&gt;异常通知]
LOG[ELK日志系统&lt;br/&gt;日志聚合分析]
end
NACOS --&gt; LB
NACOS --&gt; FEIGN
LB --&gt; CB
FEIGN --&gt; TRACE
CB --&gt; RETRY
CONFIG --&gt; SECRET
MONITOR --&gt; ALERT
TRACE --&gt; LOG</code></pre>
<p>系统应用架构基于业务域划分,主要包括以下核心应用模块:</p>
<h5 data-number="2.0.0.0.23" id="统一平台"><span
class="header-section-number">2.0.0.0.23</span> 3.3 统一平台</h5>
<ul>
<li>单点登录:统一认证入口,支持多种登录方式</li>
<li>系统管理:组织机构、员工管理、角色权限管理、菜单配置等</li>
<li>流程节点提醒:工作流节点到期和状态变更提醒</li>
</ul>
<h5 data-number="2.0.0.0.24" id="营收系统"><span
class="header-section-number">2.0.0.0.24</span> 3.4 营收系统</h5>
<ul>
<li>系统管理:水价管理、用户档案管理、更名过户管理等</li>
<li>抄表开账:册本管理、抄表录入、复核开账等</li>
<li>收费管理:柜台收费、特殊开账、柜台结账、批量缴费等</li>
<li>账务处理:预存调整、未销调整、已销调整、分账调整等</li>
<li>发票管理:发票查询、发票开具、电子发票等</li>
<li>代收业务:实时收费、银行代扣、银行托收等</li>
<li>环卫系统:计费核定、计费退补、计费减免等</li>
<li>业务工单:业务清单、上报清单、稽查工单、移表工单等</li>
</ul>
<h5 data-number="2.0.0.0.25" id="客户服务"><span
class="header-section-number">2.0.0.0.25</span> 3.5 客户服务</h5>
<ul>
<li>微信、支付宝服务窗:账户绑定、用水查询、在线缴费等</li>
<li>历史账单:账单查询、用水分析、账单推送等</li>
<li>电子发票:发票申请、发票查询、发票下载等</li>
<li>营业网点:网点查询、网点导航、业务指南等</li>
<li>账户流水:流水查询、明细下载、交易统计等</li>
<li>微网厅:用户注册、信息查询、业务办理等</li>
</ul>
<h5 data-number="2.0.0.0.26" id="表务系统"><span
class="header-section-number">2.0.0.0.26</span> 3.6 表务系统</h5>
<ul>
<li>表务工单:换表工单、移表工单、拆表工单、复装工单等</li>
<li>表务仓库:新表入库、水表检定、水表领用、水表出库等</li>
<li>水表参数与基础信息:水表厂家管理、水表型号管理等</li>
<li>物联网对接与数据同步:厂家设备信息管理、远程抄表数据同步等</li>
</ul>
<h5 data-number="2.0.0.0.27" id="报装管理"><span
class="header-section-number">2.0.0.0.27</span> 3.7 报装管理</h5>
<ul>
<li>报装流程:报装申请、方案设计、合同签订、施工管理等</li>
<li>一户一表管理:改造计划、改造实施、用户转换等</li>
</ul>
<h5 data-number="2.0.0.0.28" id="统计分析"><span
class="header-section-number">2.0.0.0.28</span> 3.8 统计分析</h5>
<ul>
<li>报表查询:标准报表、自定义报表、报表导出打印等</li>
<li>欠费查询:欠费情况统计、欠费用户明细、欠费分析等</li>
<li>缴费记录:缴费情况统计、缴费明细查询、缴费趋势分析等</li>
<li>用水分析:用水量统计、用水趋势分析、异常用水分析等</li>
</ul>
<h5 data-number="2.0.0.0.29" id="工程管理"><span
class="header-section-number">2.0.0.0.29</span> 3.9 工程管理</h5>
<ul>
<li>工程申请:工程立项、材料提交、前期勘察等</li>
<li>工程施工:施工计划、施工派工、施工记录等</li>
<li>工程验收:验收申请、验收检查、验收记录等</li>
<li>工程查询:工程进度查询、工程资料查询、工程统计等</li>
</ul>
<h5 data-number="2.0.0.0.30" id="抄表app"><span
class="header-section-number">2.0.0.0.30</span> 3.10 抄表APP</h5>
<ul>
<li>首页功能:首页展示、个人信息、快捷查询等</li>
<li>抄表功能抄表任务、NFC抄表、扫码抄表、AI录入等</li>
<li>工单管理:问题上报、问题回填、代办工单、稽查工单等</li>
</ul>
<h5 data-number="2.0.0.0.31" id="接口服务"><span
class="header-section-number">2.0.0.0.31</span> 3.11 接口服务</h5>
<ul>
<li>API市场API展示、API使用说明、API测试等</li>
<li>API管理API申请、API调度看板、接口服务配置管理等</li>
<li>接口权限管理:授权管理、白名单管理、安全策略等</li>
<li>系统对外接口:查询接口、业务处理接口、状态同步接口等</li>
</ul>
<h5 data-number="2.0.0.0.32" id="系统配置"><span
class="header-section-number">2.0.0.0.32</span> 3.12 系统配置</h5>
<ul>
<li>水表参数:水表厂家、水表型号、水表量程等</li>
<li>地址参数:行政区划、地址编码、地址库等</li>
<li>价格体系:用水性质、水价标准、阶梯水价等</li>
<li>基本配置:系统参数、业务规则、打印模板等</li>
<li>催缴管理:催缴计划、催缴任务、催缴执行等</li>
<li>用户权限:菜单权限、功能权限、数据权限等</li>
<li>定时任务:任务配置、任务监控、任务日志等</li>
</ul>
<h5 data-number="2.0.0.0.33" id="四数据架构"><span
class="header-section-number">2.0.0.0.33</span> 四、数据架构</h5>
<p>系统数据架构基于客户关系数据库为核心和基础,实现客户全生命周期管理。</p>
<h5 data-number="2.0.0.0.34" id="数据分层"><span
class="header-section-number">2.0.0.0.34</span> 4.1 数据分层</h5>
<ul>
<li>数据采集层:负责从各业务系统和物联网设备采集原始数据</li>
<li>数据存储层:统一数据存储,实现分布式数据管理</li>
<li>数据服务层:提供数据服务接口</li>
<li>数据展现层:提供数据分析和可视化功能</li>
</ul>
<h5 data-number="2.0.0.0.35" id="opengauss数据库架构"><span
class="header-section-number">2.0.0.0.35</span> 4.2
OpenGauss数据库架构</h5>
<h5 data-number="2.0.0.0.36" id="主从高可用架构"><span
class="header-section-number">2.0.0.0.36</span> 4.2.1
主从高可用架构</h5>
<pre class="mermaid"><code>graph TB
subgraph &quot;OpenGauss高可用集群&quot;
MASTER[(&quot;OpenGauss主库&lt;br/&gt;Primary Node&lt;br/&gt;读写操作&quot;)]
STANDBY[(&quot;OpenGauss备库&lt;br/&gt;Standby Node&lt;br/&gt;只读操作&quot;)]
CASCADE[(&quot;OpenGauss级联备库&lt;br/&gt;Cascade Standby&lt;br/&gt;负载分担&quot;)]
end
subgraph &quot;应用层&quot;
APP1[应用服务器1]
APP2[应用服务器2]
APP3[应用服务器3]
end
subgraph &quot;连接池&quot;
POOL[连接池&lt;br/&gt;HikariCP&lt;br/&gt;Druid]
end
subgraph &quot;监控管理&quot;
MON[OpenGauss Monitor&lt;br/&gt;性能监控]
BACKUP[定时备份&lt;br/&gt;gs_backup]
end
APP1 --&gt; POOL
APP2 --&gt; POOL
APP3 --&gt; POOL
POOL --&gt; MASTER
POOL --&gt; STANDBY
POOL --&gt; CASCADE
MASTER -.-&gt;|流复制| STANDBY
STANDBY -.-&gt;|级联复制| CASCADE
MON --&gt; MASTER
MON --&gt; STANDBY
BACKUP --&gt; MASTER</code></pre>
<h5 data-number="2.0.0.0.37" id="分片存储架构"><span
class="header-section-number">2.0.0.0.37</span> 4.2.2 分片存储架构</h5>
<pre class="mermaid"><code>graph TB
subgraph &quot;OpenGauss分布式架构&quot;
subgraph &quot;协调节点&quot;
CN1[协调节点1&lt;br/&gt;Coordinator Node]
CN2[协调节点2&lt;br/&gt;Coordinator Node]
end
subgraph &quot;数据节点组1&quot;
DN1_1[数据节点1-主&lt;br/&gt;Datanode Primary]
DN1_2[数据节点1-备&lt;br/&gt;Datanode Standby]
end
subgraph &quot;数据节点组2&quot;
DN2_1[数据节点2-主&lt;br/&gt;Datanode Primary]
DN2_2[数据节点2-备&lt;br/&gt;Datanode Standby]
end
subgraph &quot;GTM节点&quot;
GTM[全局事务管理器&lt;br/&gt;GTM Master]
GTM_S[GTM备节点&lt;br/&gt;GTM Standby]
end
end
CN1 --&gt; DN1_1
CN1 --&gt; DN2_1
CN2 --&gt; DN1_1
CN2 --&gt; DN2_1
DN1_1 -.-&gt;|主备同步| DN1_2
DN2_1 -.-&gt;|主备同步| DN2_2
GTM -.-&gt;|备份| GTM_S
CN1 --&gt; GTM
CN2 --&gt; GTM</code></pre>
<h5 data-number="2.0.0.0.38" id="数据安全架构"><span
class="header-section-number">2.0.0.0.38</span> 4.2.3 数据安全架构</h5>
<pre class="mermaid"><code>graph TB
subgraph &quot;安全防护层&quot;
TDE[透明数据加密&lt;br/&gt;TDE]
RLS[行级安全&lt;br/&gt;Row Level Security]
MASK[动态数据脱敏&lt;br/&gt;Dynamic Data Masking]
AUDIT[三权分立审计&lt;br/&gt;Separated Audit]
end
subgraph &quot;访问控制层&quot;
RBAC[基于角色的访问控制&lt;br/&gt;RBAC]
LDAP[LDAP集成认证&lt;br/&gt;External Auth]
SSL[SSL/TLS加密传输&lt;br/&gt;Encrypted Transport]
end
subgraph &quot;数据存储层&quot;
ENCRYPT[存储层加密&lt;br/&gt;Storage Encryption]
COMPRESS[数据压缩&lt;br/&gt;Data Compression]
BACKUP_ENC[备份加密&lt;br/&gt;Backup Encryption]
end
TDE --&gt; ENCRYPT
RLS --&gt; RBAC
MASK --&gt; ENCRYPT
AUDIT --&gt; BACKUP_ENC
RBAC --&gt; SSL
LDAP --&gt; SSL</code></pre>
<h5 data-number="2.0.0.0.39" id="数据模型设计"><span
class="header-section-number">2.0.0.0.39</span> 4.3 数据模型设计</h5>
<ul>
<li><strong>客户信息模型</strong>:包含客户基础信息、表卡信息、账户信息、联系人信息等</li>
<li><strong>业务数据模型</strong>:抄表数据、收费数据、账务数据、发票数据等</li>
<li><strong>工单数据模型</strong>:表务工单、报装工单、业务工单等</li>
<li><strong>配置数据模型</strong>:系统参数、水表参数、价格体系等</li>
<li><strong>报表数据模型</strong>:统计数据、分析数据、预测数据等</li>
<li><strong>物联网数据模型</strong>:设备信息、状态信息、实时数据等</li>
</ul>
<h5 data-number="2.0.0.0.40" id="数据集成与共享"><span
class="header-section-number">2.0.0.0.40</span> 4.4 数据集成与共享</h5>
<ul>
<li><strong>统一数据标准</strong>定义统一的数据标准和数据字典符合OpenGauss规范</li>
<li><strong>数据集成机制</strong>采用OpenGauss
XA事务实现系统间的数据一致性</li>
<li><strong>数据共享机制</strong>基于OpenGauss外部数据包装器(FDW)实现跨库数据访问</li>
<li><strong>数据同步策略</strong>支持实时流复制、定时数据同步、批量ETL处理等</li>
</ul>
<h5 data-number="2.0.0.0.41" id="opengauss特性应用"><span
class="header-section-number">2.0.0.0.41</span> 4.5
OpenGauss特性应用</h5>
<h5 data-number="2.0.0.0.42" id="性能优化特性"><span
class="header-section-number">2.0.0.0.42</span> 4.5.1 性能优化特性</h5>
<ul>
<li><strong>向量化执行引擎</strong>批量数据处理提升OLAP查询性能30-50%</li>
<li><strong>列存储引擎</strong>统计报表类查询压缩比高达10:1</li>
<li><strong>分区表技术</strong>:按月份分区存储抄表数据,支持分区裁剪</li>
<li><strong>并行查询</strong>:复杂统计查询自动并行执行</li>
<li><strong>智能优化器</strong>:自适应查询计划,持续性能优化</li>
</ul>
<h5 data-number="2.0.0.0.43" id="企业级特性"><span
class="header-section-number">2.0.0.0.43</span> 4.5.2 企业级特性</h5>
<ul>
<li><strong>在线扩容</strong>:支持不停机新增数据节点</li>
<li><strong>故障自愈</strong>主备节点故障自动切换RTO &lt; 10秒</li>
<li><strong>读写分离</strong>:自动路由读请求到备节点,减轻主库压力</li>
<li><strong>负载均衡</strong>:连接池级别的智能负载均衡</li>
<li><strong>全量/增量备份</strong>:支持热备份,最小备份粒度到事务级</li>
</ul>
<h5 data-number="2.0.0.0.44" id="五安全架构"><span
class="header-section-number">2.0.0.0.44</span> 五、安全架构</h5>
<p>系统安全架构基于OpenGauss数据库的企业级安全特性满足等保三级要求和国产化安全标准</p>
<h5 data-number="2.0.0.0.45" id="网络安全"><span
class="header-section-number">2.0.0.0.45</span> 5.1 网络安全</h5>
<ul>
<li><strong>边界防护</strong>:防火墙、入侵检测/防御系统</li>
<li><strong>访问控制</strong>:基于角色的访问控制(RBAC)</li>
<li><strong>通信安全</strong>SSL/TLS加密传输支持国密算法</li>
<li><strong>安全监控</strong>:实时监控网络安全状态和异常访问</li>
</ul>
<h5 data-number="2.0.0.0.46" id="opengauss数据安全架构"><span
class="header-section-number">2.0.0.0.46</span> 5.2
OpenGauss数据安全架构</h5>
<h5 data-number="2.0.0.0.47" id="数据加密安全"><span
class="header-section-number">2.0.0.0.47</span> 5.2.1 数据加密安全</h5>
<pre class="mermaid"><code>graph TB
subgraph &quot;加密层级&quot;
L1[传输层加密&lt;br/&gt;SSL/TLS/国密SM]
L2[存储层加密&lt;br/&gt;TDE透明数据加密]
L3[字段级加密&lt;br/&gt;敏感字段加密]
L4[备份加密&lt;br/&gt;备份文件加密]
end
subgraph &quot;密钥管理&quot;
KMS[密钥管理系统&lt;br/&gt;Key Management]
HSM[硬件安全模块&lt;br/&gt;Hardware Security]
ROT[密钥轮换&lt;br/&gt;Key Rotation]
end
subgraph &quot;国密算法&quot;
SM2[SM2椭圆曲线&lt;br/&gt;非对称加密]
SM3[SM3哈希算法&lt;br/&gt;消息摘要]
SM4[SM4分组密码&lt;br/&gt;对称加密]
end
L1 --&gt; KMS
L2 --&gt; HSM
L3 --&gt; ROT
L4 --&gt; KMS
KMS --&gt; SM2
HSM --&gt; SM3
ROT --&gt; SM4</code></pre>
<h5 data-number="2.0.0.0.48" id="访问控制安全"><span
class="header-section-number">2.0.0.0.48</span> 5.2.2 访问控制安全</h5>
<pre class="mermaid"><code>graph TB
subgraph &quot;身份认证&quot;
AUTH1[用户名密码认证]
AUTH2[LDAP集成认证]
AUTH3[Kerberos认证]
AUTH4[证书认证]
end
subgraph &quot;权限控制&quot;
RBAC[基于角色的权限控制&lt;br/&gt;Role-Based Access Control]
RLS[行级安全策略&lt;br/&gt;Row Level Security]
CLS[列级安全控制&lt;br/&gt;Column Level Security]
TENANT[多租户数据隔离&lt;br/&gt;Multi-Tenant Isolation]
end
subgraph &quot;审计监控&quot;
AUDIT[操作审计日志&lt;br/&gt;Audit Logging]
MONITOR[实时安全监控&lt;br/&gt;Security Monitoring]
ALERT[安全告警&lt;br/&gt;Security Alert]
REPORT[合规报告&lt;br/&gt;Compliance Report]
end
AUTH1 --&gt; RBAC
AUTH2 --&gt; RLS
AUTH3 --&gt; CLS
AUTH4 --&gt; TENANT
RBAC --&gt; AUDIT
RLS --&gt; MONITOR
CLS --&gt; ALERT
TENANT --&gt; REPORT</code></pre>
<h5 data-number="2.0.0.0.49" id="数据安全特性"><span
class="header-section-number">2.0.0.0.49</span> 5.3 数据安全特性</h5>
<ul>
<li><strong>透明数据加密(TDE)</strong>自动加密存储数据支持SM4国密算法</li>
<li><strong>行级安全(RLS)</strong>:基于用户角色的行级数据访问控制</li>
<li><strong>列级权限</strong>:敏感字段的精细化访问控制</li>
<li><strong>动态脱敏</strong>:查询时自动脱敏显示敏感信息</li>
<li><strong>数据备份安全</strong>:备份文件自动加密,支持增量备份</li>
<li><strong>审计日志</strong>:完整记录所有数据库操作,支持合规审计</li>
</ul>
<h5 data-number="2.0.0.0.50" id="多租户安全隔离"><span
class="header-section-number">2.0.0.0.50</span> 5.4 多租户安全隔离</h5>
<ul>
<li><strong>逻辑隔离</strong>基于tenant_id的数据隔离</li>
<li><strong>连接隔离</strong>:租户间连接池隔离</li>
<li><strong>权限隔离</strong>:租户级别的权限管理</li>
<li><strong>资源隔离</strong>CPU、内存、IO资源限制</li>
</ul>
<h5 data-number="2.0.0.0.51" id="应用安全"><span
class="header-section-number">2.0.0.0.51</span> 5.5 应用安全</h5>
<ul>
<li><strong>身份认证</strong>:多因素认证,支持短信验证码、邮箱验证等</li>
<li><strong>授权管理</strong>:细粒度的权限控制,支持菜单、按钮、数据权限</li>
<li><strong>安全审计</strong>:用户操作审计,关键业务操作全程记录</li>
<li><strong>密码策略</strong>:密码复杂度、定期更换、失败锁定等安全策略</li>
<li><strong>会话管理</strong>:会话超时、并发限制、单点登录等</li>
</ul>
<h5 data-number="2.0.0.0.52" id="接口安全"><span
class="header-section-number">2.0.0.0.52</span> 5.6 接口安全</h5>
<ul>
<li><strong>接口认证</strong>基于JWT Token的接口认证</li>
<li><strong>接口授权</strong>:基于角色的接口授权和权限验证</li>
<li><strong>接口加密</strong>:敏感数据的加密传输,支持国密算法</li>
<li><strong>接口防护</strong>防重放、防篡改、防SQL注入等安全措施</li>
<li><strong>接口限流</strong>基于IP、用户、应用的多维度限流策略</li>
</ul>
<h5 data-number="2.0.0.0.53" id="六部署架构"><span
class="header-section-number">2.0.0.0.53</span> 六、部署架构</h5>
<p>系统采用集中部署的模式,基于集团私有云环境进行部署。</p>
<h5 data-number="2.0.0.0.54" id="物理部署"><span
class="header-section-number">2.0.0.0.54</span> 6.1 物理部署</h5>
<ul>
<li><strong>生产环境</strong>:高可用集群部署
<ul>
<li>应用服务器2台或以上服务器负载均衡</li>
<li><strong>OpenGauss数据库集群</strong>:主从+级联备架构,支持自动故障切换</li>
<li>缓存服务器Redis集群</li>
<li>文件服务器:冗余存储</li>
</ul></li>
<li><strong>灾备环境</strong>异地灾备定期数据同步支持OpenGauss流复制</li>
<li><strong>测试环境</strong>用于系统测试和验证单节点OpenGauss部署</li>
<li><strong>开发环境</strong>用于系统开发和集成测试单节点OpenGauss部署</li>
</ul>
<h5 data-number="2.0.0.0.55" id="逻辑部署"><span
class="header-section-number">2.0.0.0.55</span> 6.2 逻辑部署</h5>
<ul>
<li><strong>应用服务器集群</strong>:负责业务逻辑处理,支持水平扩展</li>
<li><strong>OpenGauss数据库集群</strong>:负责数据存储和管理,提供高可用保障</li>
<li><strong>文件服务器</strong>:负责文档和附件存储,支持分布式存储</li>
<li><strong>缓存服务器</strong>Redis集群提高系统性能</li>
<li><strong>负载均衡服务器</strong>:实现请求分发和负载均衡</li>
<li><strong>移动应用服务</strong>提供移动端API服务</li>
<li><strong>API网关</strong>:统一的接口管理和控制</li>
</ul>
<h5 data-number="2.0.0.0.56" id="容器部署"><span
class="header-section-number">2.0.0.0.56</span> 6.3 容器部署</h5>
<ul>
<li>基于Docker容器技术实现微服务部署</li>
<li>使用Kubernetes进行容器编排和管理</li>
<li>支持容器的自动扩缩容和故障转移</li>
<li>实现服务的灰度发布和版本控制</li>
</ul>
<h5 data-number="2.0.0.0.57" id="七接口架构"><span
class="header-section-number">2.0.0.0.57</span> 七、接口架构</h5>
<p>系统提供标准化的接口,实现与外部系统的集成和数据交换。</p>
<h5 data-number="2.0.0.0.58" id="外部接口"><span
class="header-section-number">2.0.0.0.58</span> 7.1 外部接口</h5>
<ul>
<li>银行接口:实现与银行系统的对接,支持代扣、托收等功能</li>
<li>支付宝/微信接口:支持在线支付功能</li>
<li>短信接口:支持短信通知和验证码功能</li>
<li>集抄系统接口:实现与智能水表集中抄表系统的对接</li>
<li>政务系统接口实现与地方政务平台和政务APP的对接</li>
<li>消火栓系统接口:实现与消火栓系统的对接</li>
<li>环卫系统接口:实现与环卫系统的对接</li>
<li>OA系统接口实现与OA系统的工作流对接</li>
</ul>
<h5 data-number="2.0.0.0.59" id="内部接口"><span
class="header-section-number">2.0.0.0.59</span> 7.2 内部接口</h5>
<ul>
<li>ESB服务接口系统内部模块间的数据交换</li>
<li>报表接口:提供报表数据查询和生成功能</li>
<li>查询接口:提供数据查询功能</li>
<li>业务处理接口:提供业务处理功能</li>
<li>消息通知接口:提供消息推送和通知功能</li>
<li>工作流接口:提供工作流处理和状态查询功能</li>
</ul>
<h5 data-number="2.0.0.0.60" id="接口标准"><span
class="header-section-number">2.0.0.0.60</span> 7.3 接口标准</h5>
<ul>
<li>接口协议RESTful API、WebService、消息队列等</li>
<li>数据格式JSON、XML等</li>
<li>接口安全:身份认证、授权、加密传输等</li>
<li>接口文档:统一的接口文档和示例代码</li>
<li>接口测试:提供接口测试工具和环境</li>
</ul>
<h5 data-number="2.0.0.0.61" id="接口服务管理"><span
class="header-section-number">2.0.0.0.61</span> 7.4 接口服务管理</h5>
<ul>
<li>API网关统一的接口入口和管理</li>
<li>服务注册与发现:服务的自动注册和发现</li>
<li>服务路由:根据请求参数进行服务路由</li>
<li>服务降级:在服务不可用时提供降级策略</li>
<li><h2
id="服务监控实时监控服务的可用性和性能">服务监控:实时监控服务的可用性和性能</h2></li>
</ul>
<p>ewpage</p>
<h1 data-number="3" id="第2部分"><span
class="header-section-number">3</span> 第2部分</h1>
<h5 data-number="3.0.0.0.1" id="福建水务营收系统模块功能设计文档"><span
class="header-section-number">3.0.0.0.1</span>
福建水务营收系统模块功能设计文档</h5>
<h5 data-number="3.0.0.0.2" id="文档信息-2"><span
class="header-section-number">3.0.0.0.2</span> 文档信息</h5>
<table>
<thead>
<tr>
<th>项目信息</th>
<th>详情</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>项目名称</strong></td>
<td>福建水务营收系统</td>
</tr>
<tr>
<td><strong>文档类型</strong></td>
<td>概要设计文档</td>
</tr>
<tr>
<td><strong>技术框架</strong></td>
<td>RuoYi-Vue-Pro + yudao-ui-admin-vue3</td>
</tr>
<tr>
<td><strong>文档版本</strong></td>
<td>v1.0</td>
</tr>
<tr>
<td><strong>编写日期</strong></td>
<td>2024-12-19</td>
</tr>
<tr>
<td><strong>文档状态</strong></td>
<td>✅ 已完成</td>
</tr>
</tbody>
</table>
<h5 data-number="3.0.0.0.3" id="目录-1"><span
class="header-section-number">3.0.0.0.3</span> 目录</h5>
<ul>
<li><a
href="#福建水务营收系统模块功能设计文档">福建水务营收系统模块功能设计文档</a>
<ul>
<li><a href="#文档信息">文档信息</a></li>
<li><a href="#目录">目录</a></li>
<li><a href="#一系统整体架构">一、系统整体架构</a>
<ul>
<li><a href="#11-系统架构图">1.1 系统架构图</a></li>
<li><a href="#12-技术架构图">1.2 技术架构图</a></li>
<li><a href="#13-业务架构图">1.3 业务架构图</a></li>
</ul></li>
<li><a href="#二统一平台">二、统一平台</a>
<ul>
<li><a href="#21-单点登录">2.1 单点登录</a></li>
<li><a href="#22-系统管理">2.2 系统管理</a></li>
</ul></li>
<li><a href="#三营收系统">三、营收系统</a>
<ul>
<li><a href="#31-系统管理">3.1 系统管理</a></li>
<li><a href="#32-抄表开账">3.2 抄表开账</a>
<ul>
<li><a href="#321-业务流程图">3.2.1 业务流程图</a></li>
<li><a href="#322-主要功能">3.2.2 主要功能</a></li>
<li><a href="#323-核心接口定义">3.2.3 核心接口定义</a></li>
<li><a href="#324-前端界面设计">3.2.4 前端界面设计</a></li>
</ul></li>
<li><a href="#33-收费管理">3.3 收费管理</a>
<ul>
<li><a href="#331-业务流程图">3.3.1 业务流程图</a></li>
<li><a href="#332-主要功能">3.3.2 主要功能</a></li>
<li><a href="#333-核心接口定义">3.3.3 核心接口定义</a></li>
</ul></li>
<li><a href="#34-账务处理">3.4 账务处理</a>
<ul>
<li><a href="#341-业务流程图">3.4.1 业务流程图</a></li>
<li><a href="#342-主要功能">3.4.2 主要功能</a></li>
<li><a href="#343-核心接口定义">3.4.3 核心接口定义</a></li>
</ul></li>
<li><a href="#35-发票管理">3.5 发票管理</a>
<ul>
<li><a href="#351-业务流程图">3.5.1 业务流程图</a></li>
<li><a href="#352-核心接口定义">3.5.2 核心接口定义</a></li>
</ul></li>
<li><a href="#36-代收业务">3.6 代收业务</a></li>
<li><a href="#37-环卫系统">3.7 环卫系统</a></li>
<li><a href="#38-业务工单">3.8 业务工单</a></li>
</ul></li>
<li><a href="#四表务系统">四、表务系统</a>
<ul>
<li><a href="#41-表务工单">4.1 表务工单</a></li>
<li><a href="#42-表务仓库">4.2 表务仓库</a></li>
<li><a href="#43-水表参数与基础信息">4.3 水表参数与基础信息</a></li>
<li><a href="#44-物联网对接与数据同步">4.4 物联网对接与数据同步</a></li>
</ul></li>
<li><a href="#五报装系统">五、报装系统</a>
<ul>
<li><a href="#51-报装流程">5.1 报装流程</a></li>
<li><a href="#52-一户一表管理">5.2 一户一表管理</a></li>
</ul></li>
<li><a href="#六客户服务">六、客户服务</a>
<ul>
<li><a href="#61-微信支付宝服务窗">6.1 微信、支付宝服务窗</a></li>
<li><a href="#62-历史账单">6.2 历史账单</a></li>
<li><a href="#63-电子发票">6.3 电子发票</a></li>
<li><a href="#64-营业网点">6.4 营业网点</a></li>
<li><a href="#65-账户流水">6.5 账户流水</a></li>
<li><a href="#66-微网厅">6.6 微网厅</a></li>
</ul></li>
<li><a href="#七系统配置">七、系统配置</a>
<ul>
<li><a href="#71-水表参数">7.1 水表参数</a></li>
<li><a href="#72-地址参数">7.2 地址参数</a></li>
<li><a href="#73-价格体系">7.3 价格体系</a></li>
<li><a href="#74-基本配置">7.4 基本配置</a></li>
<li><a href="#75-催缴管理">7.5 催缴管理</a></li>
<li><a href="#76-用户权限">7.6 用户权限</a></li>
<li><a href="#77-定时任务">7.7 定时任务</a></li>
</ul></li>
<li><a href="#八系统接口">八、系统接口</a>
<ul>
<li><a href="#81-银行接口">8.1 银行接口</a></li>
<li><a href="#82-支付宝微信接口">8.2 支付宝/微信接口</a></li>
<li><a href="#83-短信接口">8.3 短信接口</a></li>
<li><a href="#84-集抄系统接口">8.4 集抄系统接口</a></li>
<li><a href="#85-政务系统接口">8.5 政务系统接口</a></li>
<li><a href="#86-消火栓系统接口">8.6 消火栓系统接口</a></li>
<li><a href="#87-其他系统对接">8.7 其他系统对接</a></li>
</ul></li>
<li><a href="#九统计分析">九、统计分析</a>
<ul>
<li><a href="#91-报表查询">9.1 报表查询</a></li>
<li><a href="#92-欠费查询">9.2 欠费查询</a></li>
<li><a href="#93-缴费记录">9.3 缴费记录</a></li>
<li><a href="#94-用水分析">9.4 用水分析</a></li>
</ul></li>
<li><a href="#十工程管理">十、工程管理</a>
<ul>
<li><a href="#101-工程申请">10.1 工程申请</a></li>
<li><a href="#102-工程施工">10.2 工程施工</a></li>
<li><a href="#103-工程验收">10.3 工程验收</a></li>
<li><a href="#104-工程查询">10.4 工程查询</a></li>
</ul></li>
<li><a href="#十一抄表app">十一、抄表APP</a>
<ul>
<li><a href="#111-首页功能">11.1 首页功能</a></li>
<li><a href="#112-抄表功能">11.2 抄表功能</a></li>
<li><a href="#113-工单管理">11.3 工单管理</a></li>
</ul></li>
<li><a href="#十二接口服务">十二、接口服务</a>
<ul>
<li><a href="#121-api市场">12.1 API市场</a></li>
<li><a href="#122-api管理">12.2 API管理</a></li>
<li><a href="#123-接口权限管理">12.3 接口权限管理</a></li>
<li><a href="#124-系统对外接口">12.4 系统对外接口</a></li>
</ul></li>
<li><a href="#系统集成架构">系统集成架构</a>
<ul>
<li><a href="#前后端集成架构">前后端集成架构</a></li>
<li><a href="#技术栈整合方案">技术栈整合方案</a></li>
</ul></li>
</ul></li>
</ul>
<h5 data-number="3.0.0.0.4" id="一系统整体架构"><span
class="header-section-number">3.0.0.0.4</span> 一、系统整体架构</h5>
<p>福建水务营收系统采用现代化的分布式微服务架构基于RuoYi-Vue-Pro后端框架和yudao-ui-admin-vue3前端框架构建为水务企业提供完整的营收管理解决方案。</p>
<h5 data-number="3.0.0.0.5" id="系统架构图"><span
class="header-section-number">3.0.0.0.5</span> 1.1 系统架构图</h5>
<pre class="mermaid"><code>graph TB
subgraph &quot;用户层&quot;
A1[管理员用户]
A2[抄表员]
A3[收费员]
A4[客户用户]
end
subgraph &quot;接入层&quot;
B1[PC端管理后台&lt;br/&gt;yudao-ui-admin-vue3]
B2[移动端抄表APP&lt;br/&gt;uni-app]
B3[微信小程序]
B4[支付宝小程序]
B5[Web客户端]
end
subgraph &quot;网关层&quot;
C1[API网关&lt;br/&gt;Spring Cloud Gateway]
C2[负载均衡&lt;br/&gt;Nginx]
end
subgraph &quot;服务层&quot;
D1[用户认证服务&lt;br/&gt;Spring Security + JWT]
D2[营收管理服务&lt;br/&gt;RuoYi-Vue-Pro]
D3[表务管理服务]
D4[报装管理服务]
D5[客户服务]
D6[系统管理服务]
end
subgraph &quot;中间件层&quot;
E1[(Redis缓存&lt;br/&gt;6.0+)]
E2[RabbitMQ消息队列]
E3[Elasticsearch搜索]
E4[MinIO文件存储]
end
subgraph &quot;数据层&quot;
F1[(主数据库&lt;br/&gt;OpenGauss 5.0+)]
F2[(从数据库&lt;br/&gt;OpenGauss 5.0+)]
F3[(历史数据库&lt;br/&gt;OpenGauss 5.0+)]
end
subgraph &quot;外部系统&quot;
G1[银行系统]
G2[支付宝/微信]
G3[短信平台]
G4[集抄系统]
G5[政务平台]
end
A1 --&gt; B1
A2 --&gt; B2
A3 --&gt; B1
A4 --&gt; B3
A4 --&gt; B4
A4 --&gt; B5
B1 --&gt; C2
B2 --&gt; C2
B3 --&gt; C2
B4 --&gt; C2
B5 --&gt; C2
C2 --&gt; C1
C1 --&gt; D1
C1 --&gt; D2
C1 --&gt; D3
C1 --&gt; D4
C1 --&gt; D5
C1 --&gt; D6
D1 --&gt; E1
D2 --&gt; E1
D2 --&gt; E2
D3 --&gt; E1
D4 --&gt; E3
D5 --&gt; E4
D6 --&gt; E1
D2 --&gt; F1
D3 --&gt; F1
D4 --&gt; F2
D5 --&gt; F2
D6 --&gt; F3
D2 --&gt; G1
D2 --&gt; G2
D5 --&gt; G3
D3 --&gt; G4
D4 --&gt; G5</code></pre>
<h5 data-number="3.0.0.0.6" id="技术架构图"><span
class="header-section-number">3.0.0.0.6</span> 1.2 技术架构图</h5>
<pre class="mermaid"><code>graph TB
subgraph &quot;前端技术栈&quot;
FE1[Vue 3.x]
FE2[TypeScript 4.x]
FE3[Element Plus]
FE4[Vite 4.x]
FE5[Pinia状态管理]
end
subgraph &quot;后端技术栈&quot;
BE1[Spring Boot 3.x]
BE2[Spring Security 6.x]
BE3[MyBatis Plus 3.x]
BE4[Spring Cloud Gateway]
BE5[Hibernate Validator]
end
subgraph &quot;数据库技术&quot;
DB1[OpenGauss 5.0+]
DB2[Redis 6.0+]
DB3[HikariCP连接池]
DB4[MyBatis-Plus代码生成]
end
subgraph &quot;中间件技术&quot;
MW1[RabbitMQ 3.x]
MW2[Elasticsearch 8.x]
MW3[MinIO对象存储]
MW4[XXL-JOB定时任务]
end
subgraph &quot;运维技术&quot;
OPS1[Docker容器化]
OPS2[Jenkins CI/CD]
OPS3[Prometheus监控]
OPS4[ELK日志分析]
end
subgraph &quot;安全技术&quot;
SEC1[JWT Token认证]
SEC2[OAuth2.0授权]
SEC3[AES数据加密]
SEC4[RSA签名验证]
end
FE1 --&gt; FE2
FE2 --&gt; FE3
FE3 --&gt; FE4
FE4 --&gt; FE5
BE1 --&gt; BE2
BE2 --&gt; BE3
BE3 --&gt; BE4
BE4 --&gt; BE5
DB1 --&gt; DB2
DB2 --&gt; DB3
DB3 --&gt; DB4
MW1 --&gt; MW2
MW2 --&gt; MW3
MW3 --&gt; MW4
OPS1 --&gt; OPS2
OPS2 --&gt; OPS3
OPS3 --&gt; OPS4
SEC1 --&gt; SEC2
SEC2 --&gt; SEC3
SEC3 --&gt; SEC4</code></pre>
<h5 data-number="3.0.0.0.7" id="业务架构图"><span
class="header-section-number">3.0.0.0.7</span> 1.3 业务架构图</h5>
<pre class="mermaid"><code>graph TB
subgraph &quot;统一平台层&quot;
UP1[单点登录]
UP2[系统管理]
UP3[用户权限]
UP4[组织架构]
end
subgraph &quot;核心业务层&quot;
CB1[客户管理]
CB2[抄表管理]
CB3[收费管理]
CB4[账务管理]
CB5[表务管理]
CB6[报装管理]
end
subgraph &quot;增值服务层&quot;
VS1[客户服务]
VS2[移动应用]
VS3[微信服务]
VS4[电子发票]
VS5[在线支付]
end
subgraph &quot;数据服务层&quot;
DS1[统计分析]
DS2[报表查询]
DS3[数据导出]
DS4[决策支持]
end
subgraph &quot;集成服务层&quot;
IS1[银行接口]
IS2[支付接口]
IS3[短信接口]
IS4[集抄接口]
IS5[政务接口]
end
UP1 --&gt; CB1
UP2 --&gt; CB2
UP3 --&gt; CB3
UP4 --&gt; CB4
CB1 --&gt; VS1
CB2 --&gt; VS2
CB3 --&gt; VS3
CB4 --&gt; VS4
CB5 --&gt; VS5
CB6 --&gt; VS1
VS1 --&gt; DS1
VS2 --&gt; DS2
VS3 --&gt; DS3
VS4 --&gt; DS4
VS5 --&gt; DS1
DS1 --&gt; IS1
DS2 --&gt; IS2
DS3 --&gt; IS3
DS4 --&gt; IS4
DS1 --&gt; IS5</code></pre>
<h5 data-number="3.0.0.0.8" id="二统一平台"><span
class="header-section-number">3.0.0.0.8</span> 二、统一平台</h5>
<p>统一平台是客户服务平台的综合展示平台基于RuoYi-Vue-Pro和yudao-ui-admin-vue3框架构建包含日常工作功能和客户全部的信息是系统的基础功能模块。</p>
<h5 data-number="3.0.0.0.9" id="单点登录"><span
class="header-section-number">3.0.0.0.9</span> 2.1 单点登录</h5>
<p>单点登录模块基于Spring
Security和JWT实现用户一次登录即可访问系统中所有应用的功能主要特点包括</p>
<ul>
<li>支持通过用户名、密码进行身份认证</li>
<li>支持OAuth2.0授权码+PKCE模式的第三方登录</li>
<li>支持手机号+短信验证码登录方式</li>
<li>支持多因素认证MFA增强安全性</li>
<li>根据登录人员所在公司的不同,显示不同的系统名称和界面风格</li>
<li>提供流程节点到期提醒功能</li>
<li>Token自动刷新机制提升用户体验</li>
</ul>
<h5 data-number="3.0.0.0.10" id="系统管理"><span
class="header-section-number">3.0.0.0.10</span> 2.2 系统管理</h5>
<p>系统管理模块基于RuoYi-Vue-Pro框架的现成功能提供对系统基础参数的配置管理功能主要包括</p>
<ul>
<li>组织机构管理:
<ul>
<li>支持多级组织结构的管理</li>
<li>支持组织机构导入导出</li>
<li>组织关系树形展示</li>
<li>组织数据权限控制</li>
</ul></li>
<li>员工管理:
<ul>
<li>系统操作人员的基本信息管理</li>
<li>用户分配角色与权限</li>
<li>用户状态管理(启用/禁用)</li>
<li>用户密码安全策略</li>
<li>用户操作日志记录</li>
</ul></li>
<li>角色权限管理:
<ul>
<li>基于RBAC模型的权限控制</li>
<li>支持数据权限精细化控制</li>
<li>支持多租户的权限隔离</li>
<li>基于菜单、按钮级别的权限管理</li>
</ul></li>
<li>系统菜单配置:
<ul>
<li>支持自定义系统菜单和功能模块</li>
<li>菜单多级分类管理</li>
<li>动态路由生成</li>
<li>菜单权限配置</li>
</ul></li>
<li>数据字典管理:
<ul>
<li>维护系统使用的各类基础代码数据</li>
<li>支持字典项多级管理</li>
<li>支持字典缓存,提高访问性能</li>
</ul></li>
<li>系统监控:
<ul>
<li>在线用户监控与管理</li>
<li>服务器性能监控</li>
<li>定时任务执行状态监控</li>
<li>系统缓存监控与管理</li>
</ul></li>
<li>系统日志管理:
<ul>
<li>记录用户登录日志</li>
<li>记录操作日志,支持操作回溯</li>
<li>记录系统异常日志,便于问题排查</li>
</ul></li>
<li>短信管理:
<ul>
<li>支持阿里云、腾讯云等多家短信渠道</li>
<li>短信模板配置管理</li>
<li>短信发送日志记录</li>
</ul></li>
<li>租户管理:
<ul>
<li>支持SaaS多租户</li>
<li>租户套餐配置</li>
<li>租户数据隔离</li>
<li>租户资源管理</li>
</ul></li>
<li>配置管理:
<ul>
<li>系统参数配置</li>
<li>通知公告管理</li>
<li>定时任务配置</li>
</ul></li>
</ul>
<h5 data-number="3.0.0.0.11" id="三营收系统"><span
class="header-section-number">3.0.0.0.11</span> 三、营收系统</h5>
<p>营收系统是水务业务系统的核心组成部分,负责抄表、收费、账务处理等关键业务功能。</p>
<h5 data-number="3.0.0.0.12" id="系统管理-1"><span
class="header-section-number">3.0.0.0.12</span> 3.1 系统管理</h5>
<p>营收系统的基础管理功能,包括:</p>
<ul>
<li>水价管理:不同用户类型的水价标准管理,支持阶梯水价</li>
<li>用户档案管理:维护用户基本信息、用水信息、账户信息等
<ul>
<li>客户分组管理</li>
<li>集收管理</li>
<li>客户基础信息管理</li>
<li>定额管理</li>
<li>客户优惠方案设置</li>
</ul></li>
<li>更名过户管理:处理用户变更、过户等业务</li>
<li>注销报停管理:处理用户注销、暂停用水等业务</li>
</ul>
<h5 data-number="3.0.0.0.13" id="抄表开账"><span
class="header-section-number">3.0.0.0.13</span> 3.2 抄表开账</h5>
<p>抄表开账模块负责水表读数的采集和账单生成,是营收系统的核心业务模块。</p>
<h5 data-number="3.0.0.0.14" id="业务流程图"><span
class="header-section-number">3.0.0.0.14</span> 3.2.1 业务流程图</h5>
<pre class="mermaid"><code>flowchart TD
Start([开始抄表周期]) --&gt; BookPlan[制定抄表计划]
BookPlan --&gt; CreateBook[生成抄表册本]
CreateBook --&gt; AssignReader[分配抄表员]
AssignReader --&gt; ReadingStart[开始抄表]
ReadingStart --&gt; ReadingType{抄表方式}
ReadingType --&gt;|人工抄表| ManualReading[现场抄表录入]
ReadingType --&gt;|远程抄表| RemoteReading[远程采集数据]
ReadingType --&gt;|客户自报| SelfReporting[客户自主上报]
ManualReading --&gt; DataValidation[数据校验]
RemoteReading --&gt; DataValidation
SelfReporting --&gt; DataValidation
DataValidation --&gt; ValidationResult{校验结果}
ValidationResult --&gt;|异常| ExceptionHandle[异常处理]
ValidationResult --&gt;|正常| DataReview[数据复核]
ExceptionHandle --&gt; ReviewException[人工审核异常]
ReviewException --&gt; DataReview
DataReview --&gt; ReviewResult{复核结果}
ReviewResult --&gt;|退回| ReadingStart
ReviewResult --&gt;|通过| BillGeneration[生成账单]
BillGeneration --&gt; CalcWaterFee[计算水费]
CalcWaterFee --&gt; CalcSewageFee[计算污水费]
CalcSewageFee --&gt; CalcOtherFee[计算其他费用]
CalcOtherFee --&gt; BillReview[账单审核]
BillReview --&gt; BillResult{审核结果}
BillResult --&gt;|退回| BillGeneration
BillResult --&gt;|通过| BillConfirm[账单确认]
BillConfirm --&gt; SendNotification[发送缴费通知]
SendNotification --&gt; End([完成开账])</code></pre>
<h5 data-number="3.0.0.0.15" id="主要功能"><span
class="header-section-number">3.0.0.0.15</span> 3.2.2 主要功能</h5>
<p><strong>册本管理</strong>:册本基本信息的维护和管理 - 册本创建与配置
- 抄表路线规划 - 抄表员分配 - 抄表周期设置</p>
<p><strong>抄表录入</strong>:支持多种抄表方式 -
手工抄表:现场抄表、批量录入 - 智能抄表:远程数据采集、自动同步 -
自报抄表:客户自主上报、在线提交</p>
<p><strong>抄表数据审核</strong>:确保数据质量 -
数据校验:读数合理性检查、用量异常检测 -
异常处理:异常数据标记、人工处理 - 开账处理:数据确认、账单生成</p>
<p><strong>追加抄表</strong>:支持非周期性特殊抄表 -
补抄管理:漏抄数据补录 - 特殊抄表:临时抄表需求 -
调整抄表:读数错误修正</p>
<h5 data-number="3.0.0.0.16" id="核心接口定义"><span
class="header-section-number">3.0.0.0.16</span> 3.2.3 核心接口定义</h5>
<p><strong>抄表管理主要接口</strong></p>
<div class="sourceCode" id="cb29"><pre
class="sourceCode java"><code class="sourceCode java"><span id="cb29-1"><a href="#cb29-1" aria-hidden="true" tabindex="-1"></a><span class="at">@RestController</span></span>
<span id="cb29-2"><a href="#cb29-2" aria-hidden="true" tabindex="-1"></a><span class="at">@RequestMapping</span><span class="op">(</span><span class="st">&quot;/admin-api/water/reading&quot;</span><span class="op">)</span></span>
<span id="cb29-3"><a href="#cb29-3" aria-hidden="true" tabindex="-1"></a><span class="at">@Tag</span><span class="op">(</span>name <span class="op">=</span> <span class="st">&quot;管理后台 - 抄表管理&quot;</span><span class="op">)</span></span>
<span id="cb29-4"><a href="#cb29-4" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span> <span class="kw">class</span> MeterReadingController <span class="op">{</span></span>
<span id="cb29-5"><a href="#cb29-5" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb29-6"><a href="#cb29-6" aria-hidden="true" tabindex="-1"></a> <span class="at">@PostMapping</span><span class="op">(</span><span class="st">&quot;/create&quot;</span><span class="op">)</span></span>
<span id="cb29-7"><a href="#cb29-7" aria-hidden="true" tabindex="-1"></a> <span class="at">@Operation</span><span class="op">(</span>summary <span class="op">=</span> <span class="st">&quot;创建抄表记录&quot;</span><span class="op">)</span></span>
<span id="cb29-8"><a href="#cb29-8" aria-hidden="true" tabindex="-1"></a> <span class="kw">public</span> CommonResult<span class="op">&lt;</span><span class="bu">Long</span><span class="op">&gt;</span> <span class="fu">createReading</span><span class="op">(</span><span class="at">@Valid</span> <span class="at">@RequestBody</span> MeterReadingSaveReqVO createReqVO<span class="op">);</span></span>
<span id="cb29-9"><a href="#cb29-9" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb29-10"><a href="#cb29-10" aria-hidden="true" tabindex="-1"></a> <span class="at">@PostMapping</span><span class="op">(</span><span class="st">&quot;/batch-create&quot;</span><span class="op">)</span></span>
<span id="cb29-11"><a href="#cb29-11" aria-hidden="true" tabindex="-1"></a> <span class="at">@Operation</span><span class="op">(</span>summary <span class="op">=</span> <span class="st">&quot;批量创建抄表记录&quot;</span><span class="op">)</span></span>
<span id="cb29-12"><a href="#cb29-12" aria-hidden="true" tabindex="-1"></a> <span class="kw">public</span> CommonResult<span class="op">&lt;</span>MeterReadingBatchRespVO<span class="op">&gt;</span> <span class="fu">batchCreateReading</span><span class="op">(</span><span class="at">@Valid</span> <span class="at">@RequestBody</span> MeterReadingBatchReqVO batchReqVO<span class="op">);</span></span>
<span id="cb29-13"><a href="#cb29-13" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb29-14"><a href="#cb29-14" aria-hidden="true" tabindex="-1"></a> <span class="at">@PostMapping</span><span class="op">(</span><span class="st">&quot;/review&quot;</span><span class="op">)</span></span>
<span id="cb29-15"><a href="#cb29-15" aria-hidden="true" tabindex="-1"></a> <span class="at">@Operation</span><span class="op">(</span>summary <span class="op">=</span> <span class="st">&quot;抄表数据复核&quot;</span><span class="op">)</span></span>
<span id="cb29-16"><a href="#cb29-16" aria-hidden="true" tabindex="-1"></a> <span class="kw">public</span> CommonResult<span class="op">&lt;</span><span class="bu">Boolean</span><span class="op">&gt;</span> <span class="fu">reviewReading</span><span class="op">(</span><span class="at">@Valid</span> <span class="at">@RequestBody</span> MeterReadingReviewReqVO reviewReqVO<span class="op">);</span></span>
<span id="cb29-17"><a href="#cb29-17" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb29-18"><a href="#cb29-18" aria-hidden="true" tabindex="-1"></a> <span class="at">@PostMapping</span><span class="op">(</span><span class="st">&quot;/generate-bill&quot;</span><span class="op">)</span></span>
<span id="cb29-19"><a href="#cb29-19" aria-hidden="true" tabindex="-1"></a> <span class="at">@Operation</span><span class="op">(</span>summary <span class="op">=</span> <span class="st">&quot;生成账单&quot;</span><span class="op">)</span></span>
<span id="cb29-20"><a href="#cb29-20" aria-hidden="true" tabindex="-1"></a> <span class="kw">public</span> CommonResult<span class="op">&lt;</span>BillGenerateRespVO<span class="op">&gt;</span> <span class="fu">generateBill</span><span class="op">(</span><span class="at">@Valid</span> <span class="at">@RequestBody</span> ReadingBillReqVO reqVO<span class="op">);</span></span>
<span id="cb29-21"><a href="#cb29-21" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p><strong>接口设计要点</strong> -
遵循RESTful设计规范统一的请求响应格式 - 支持批量操作提高处理效率 -
完整的数据校验和异常处理机制 - 集成RuoYi-Vue-Pro的权限控制和日志记录</p>
<h5 data-number="3.0.0.0.17" id="前端界面设计"><span
class="header-section-number">3.0.0.0.17</span> 3.2.4 前端界面设计</h5>
<p><strong>抄表管理页面结构</strong></p>
<pre class="vue"><code>&lt;template&gt;
&lt;ContentWrap&gt;
&lt;!-- 查询条件 --&gt;
&lt;el-form class=&quot;search-form&quot; inline&gt;
&lt;el-form-item label=&quot;抄表日期&quot;&gt;
&lt;el-date-picker v-model=&quot;queryParams.readingDate&quot; type=&quot;daterange&quot; /&gt;
&lt;/el-form-item&gt;
&lt;el-form-item label=&quot;抄表状态&quot;&gt;
&lt;el-select v-model=&quot;queryParams.status&quot;&gt;
&lt;el-option label=&quot;待复核&quot; value=&quot;pending&quot; /&gt;
&lt;el-option label=&quot;已通过&quot; value=&quot;approved&quot; /&gt;
&lt;/el-select&gt;
&lt;/el-form-item&gt;
&lt;el-form-item&gt;
&lt;el-button type=&quot;primary&quot; @click=&quot;handleQuery&quot;&gt;查询&lt;/el-button&gt;
&lt;el-button @click=&quot;resetQuery&quot;&gt;重置&lt;/el-button&gt;
&lt;/el-form-item&gt;
&lt;/el-form&gt;
&lt;!-- 操作按钮 --&gt;
&lt;el-row class=&quot;mb-10px&quot;&gt;
&lt;el-button type=&quot;primary&quot; @click=&quot;handleAdd&quot;&gt;新增抄表&lt;/el-button&gt;
&lt;el-button type=&quot;success&quot; @click=&quot;handleBatchAdd&quot;&gt;批量抄表&lt;/el-button&gt;
&lt;el-button type=&quot;warning&quot; @click=&quot;handleExport&quot;&gt;导出数据&lt;/el-button&gt;
&lt;/el-row&gt;
&lt;!-- 数据表格 --&gt;
&lt;el-table v-loading=&quot;loading&quot; :data=&quot;readingList&quot;&gt;
&lt;el-table-column prop=&quot;readingCode&quot; label=&quot;抄表编号&quot; width=&quot;180&quot; /&gt;
&lt;el-table-column prop=&quot;customerName&quot; label=&quot;客户名称&quot; /&gt;
&lt;el-table-column prop=&quot;meterCode&quot; label=&quot;水表编号&quot; /&gt;
&lt;el-table-column prop=&quot;readingValue&quot; label=&quot;本次读数&quot; /&gt;
&lt;el-table-column prop=&quot;waterUsage&quot; label=&quot;用水量&quot; /&gt;
&lt;el-table-column prop=&quot;readingDate&quot; label=&quot;抄表日期&quot; /&gt;
&lt;el-table-column prop=&quot;status&quot; label=&quot;状态&quot;&gt;
&lt;template #default=&quot;{ row }&quot;&gt;
&lt;dict-tag :type=&quot;DICT_TYPE.READING_STATUS&quot; :value=&quot;row.status&quot; /&gt;
&lt;/template&gt;
&lt;/el-table-column&gt;
&lt;el-table-column label=&quot;操作&quot; width=&quot;200&quot; fixed=&quot;right&quot;&gt;
&lt;template #default=&quot;{ row }&quot;&gt;
&lt;el-button link @click=&quot;handleUpdate(row)&quot;&gt;编辑&lt;/el-button&gt;
&lt;el-button link @click=&quot;handleReview(row)&quot;&gt;复核&lt;/el-button&gt;
&lt;el-button link type=&quot;danger&quot; @click=&quot;handleDelete(row)&quot;&gt;删除&lt;/el-button&gt;
&lt;/template&gt;
&lt;/el-table-column&gt;
&lt;/el-table&gt;
&lt;/ContentWrap&gt;
&lt;/template&gt;</code></pre>
<p><strong>前端页面功能特性</strong> - 响应式设计基于Element
Plus的现代化UI组件 - 数据表格:支持分页、排序、筛选等功能 -
表单验证:前端数据校验和错误提示 - 批量操作:支持批量抄表录入和批量审核
- 实时更新:页面数据实时刷新和状态同步</p>
<h5 data-number="3.0.0.0.18" id="收费管理"><span
class="header-section-number">3.0.0.0.18</span> 3.3 收费管理</h5>
<p>收费管理模块负责水费的收取和管理,是营收系统的重要业务模块。</p>
<h5 data-number="3.0.0.0.19" id="业务流程图-1"><span
class="header-section-number">3.0.0.0.19</span> 3.3.1 业务流程图</h5>
<pre class="mermaid"><code>flowchart TD
Start([客户缴费]) --&gt; QueryCustomer[查询客户信息]
QueryCustomer --&gt; CustomerExists{客户是否存在}
CustomerExists --&gt;|否| ErrorReturn[返回错误信息]
CustomerExists --&gt;|是| QueryBills[查询欠费账单]
QueryBills --&gt; BillExists{是否有欠费}
BillExists --&gt;|否| NoDebt[无欠费提示]
BillExists --&gt;|是| ShowBills[显示账单列表]
ShowBills --&gt; SelectBills[选择缴费账单]
SelectBills --&gt; CalcAmount[计算缴费金额]
CalcAmount --&gt; SelectPayMethod[选择支付方式]
SelectPayMethod --&gt; PaymentType{支付方式}
PaymentType --&gt;|现金| CashPayment[现金收费]
PaymentType --&gt;|银行卡| BankCardPay[银行卡支付]
PaymentType --&gt;|在线支付| OnlinePayment[在线支付]
PaymentType --&gt;|预存款| PrepaidPayment[预存款支付]
CashPayment --&gt; ValidatePayment[验证收费金额]
BankCardPay --&gt; ValidatePayment
OnlinePayment --&gt; ThirdPartyPay[第三方支付]
PrepaidPayment --&gt; CheckBalance[检查预存余额]
ThirdPartyPay --&gt; PaymentCallback[支付回调]
PaymentCallback --&gt; ValidatePayment
CheckBalance --&gt; BalanceOK{余额是否充足}
BalanceOK --&gt;|否| InsufficientBalance[余额不足]
BalanceOK --&gt;|是| ValidatePayment
ValidatePayment --&gt; PaymentSuccess{支付成功}
PaymentSuccess --&gt;|否| PaymentFailed[支付失败处理]
PaymentSuccess --&gt;|是| UpdateAccount[更新账户状态]
UpdateAccount --&gt; UpdateBills[更新账单状态]
UpdateBills --&gt; GenerateReceipt[生成收费凭证]
GenerateReceipt --&gt; PrintReceipt[打印收据]
PrintReceipt --&gt; SendNotification[发送缴费通知]
SendNotification --&gt; Complete([完成缴费])
PaymentFailed --&gt; End([结束])
InsufficientBalance --&gt; End
ErrorReturn --&gt; End
NoDebt --&gt; End</code></pre>
<h5 data-number="3.0.0.0.20" id="主要功能-1"><span
class="header-section-number">3.0.0.0.20</span> 3.3.2 主要功能</h5>
<p><strong>柜台收费</strong>:现场收费服务 -
用户查询:客户信息查询、账单查询 - 收费处理:多种支付方式、找零计算 -
收费打印:收据打印、发票开具 - 预存预付:余额充值、预付费管理</p>
<p><strong>柜台结账</strong>:营业网点日常结账 -
日结处理:当日收费汇总、统计分析 - 交款管理:现金上缴、账务核对 -
结账查询:历史结账记录查询</p>
<p><strong>预付款管理</strong>:预付费业务处理 -
预付款充值:余额充值、充值记录 - 使用管理:自动扣款、余额提醒 -
退款处理:预付款退款、退款审核</p>
<p><strong>缴费记录查询</strong>:缴费历史管理 -
多条件查询:按时间、金额、渠道查询 - 统计分析:缴费趋势、渠道分析 -
导出功能:缴费记录导出</p>
<h5 data-number="3.0.0.0.21" id="核心接口定义-1"><span
class="header-section-number">3.0.0.0.21</span> 3.3.3 核心接口定义</h5>
<p><strong>缴费管理主要接口</strong></p>
<div class="sourceCode" id="cb32"><pre
class="sourceCode java"><code class="sourceCode java"><span id="cb32-1"><a href="#cb32-1" aria-hidden="true" tabindex="-1"></a><span class="at">@RestController</span></span>
<span id="cb32-2"><a href="#cb32-2" aria-hidden="true" tabindex="-1"></a><span class="at">@RequestMapping</span><span class="op">(</span><span class="st">&quot;/admin-api/water/payment&quot;</span><span class="op">)</span></span>
<span id="cb32-3"><a href="#cb32-3" aria-hidden="true" tabindex="-1"></a><span class="at">@Tag</span><span class="op">(</span>name <span class="op">=</span> <span class="st">&quot;管理后台 - 缴费管理&quot;</span><span class="op">)</span></span>
<span id="cb32-4"><a href="#cb32-4" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span> <span class="kw">class</span> PaymentController <span class="op">{</span></span>
<span id="cb32-5"><a href="#cb32-5" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb32-6"><a href="#cb32-6" aria-hidden="true" tabindex="-1"></a> <span class="at">@PostMapping</span><span class="op">(</span><span class="st">&quot;/create&quot;</span><span class="op">)</span></span>
<span id="cb32-7"><a href="#cb32-7" aria-hidden="true" tabindex="-1"></a> <span class="at">@Operation</span><span class="op">(</span>summary <span class="op">=</span> <span class="st">&quot;创建缴费记录&quot;</span><span class="op">)</span></span>
<span id="cb32-8"><a href="#cb32-8" aria-hidden="true" tabindex="-1"></a> <span class="kw">public</span> CommonResult<span class="op">&lt;</span>PaymentRespVO<span class="op">&gt;</span> <span class="fu">createPayment</span><span class="op">(</span><span class="at">@Valid</span> <span class="at">@RequestBody</span> PaymentCreateReqVO createReqVO<span class="op">);</span></span>
<span id="cb32-9"><a href="#cb32-9" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb32-10"><a href="#cb32-10" aria-hidden="true" tabindex="-1"></a> <span class="at">@PostMapping</span><span class="op">(</span><span class="st">&quot;/cash-payment&quot;</span><span class="op">)</span></span>
<span id="cb32-11"><a href="#cb32-11" aria-hidden="true" tabindex="-1"></a> <span class="at">@Operation</span><span class="op">(</span>summary <span class="op">=</span> <span class="st">&quot;现金缴费&quot;</span><span class="op">)</span></span>
<span id="cb32-12"><a href="#cb32-12" aria-hidden="true" tabindex="-1"></a> <span class="kw">public</span> CommonResult<span class="op">&lt;</span>PaymentRespVO<span class="op">&gt;</span> <span class="fu">cashPayment</span><span class="op">(</span><span class="at">@Valid</span> <span class="at">@RequestBody</span> CashPaymentReqVO cashReqVO<span class="op">);</span></span>
<span id="cb32-13"><a href="#cb32-13" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb32-14"><a href="#cb32-14" aria-hidden="true" tabindex="-1"></a> <span class="at">@PostMapping</span><span class="op">(</span><span class="st">&quot;/online-payment&quot;</span><span class="op">)</span></span>
<span id="cb32-15"><a href="#cb32-15" aria-hidden="true" tabindex="-1"></a> <span class="at">@Operation</span><span class="op">(</span>summary <span class="op">=</span> <span class="st">&quot;在线支付&quot;</span><span class="op">)</span></span>
<span id="cb32-16"><a href="#cb32-16" aria-hidden="true" tabindex="-1"></a> <span class="kw">public</span> CommonResult<span class="op">&lt;</span>OnlinePaymentRespVO<span class="op">&gt;</span> <span class="fu">onlinePayment</span><span class="op">(</span><span class="at">@Valid</span> <span class="at">@RequestBody</span> OnlinePaymentReqVO onlineReqVO<span class="op">);</span></span>
<span id="cb32-17"><a href="#cb32-17" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb32-18"><a href="#cb32-18" aria-hidden="true" tabindex="-1"></a> <span class="at">@PostMapping</span><span class="op">(</span><span class="st">&quot;/prepaid-payment&quot;</span><span class="op">)</span></span>
<span id="cb32-19"><a href="#cb32-19" aria-hidden="true" tabindex="-1"></a> <span class="at">@Operation</span><span class="op">(</span>summary <span class="op">=</span> <span class="st">&quot;预存款缴费&quot;</span><span class="op">)</span></span>
<span id="cb32-20"><a href="#cb32-20" aria-hidden="true" tabindex="-1"></a> <span class="kw">public</span> CommonResult<span class="op">&lt;</span>PaymentRespVO<span class="op">&gt;</span> <span class="fu">prepaidPayment</span><span class="op">(</span><span class="at">@Valid</span> <span class="at">@RequestBody</span> PrepaidPaymentReqVO prepaidReqVO<span class="op">);</span></span>
<span id="cb32-21"><a href="#cb32-21" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p><strong>接口设计特点</strong> -
支持多种缴费方式:现金、银行卡、在线支付、预存款 -
事务控制:确保缴费操作的原子性和一致性 -
异步处理:第三方支付采用异步回调机制 -
安全验证:完整的权限控制和数据校验</p>
<pre><code>
##### 3.3.4 前端界面设计
**缴费管理页面结构**
```vue
&lt;template&gt;
&lt;ContentWrap&gt;
&lt;!-- 客户查询 --&gt;
&lt;el-form class=&quot;search-form&quot; inline&gt;
&lt;el-form-item label=&quot;客户编号&quot;&gt;
&lt;el-input v-model=&quot;queryParams.customerCode&quot; placeholder=&quot;请输入客户编号&quot; /&gt;
&lt;/el-form-item&gt;
&lt;el-form-item label=&quot;客户姓名&quot;&gt;
&lt;el-input v-model=&quot;queryParams.customerName&quot; placeholder=&quot;请输入客户姓名&quot; /&gt;
&lt;/el-form-item&gt;
&lt;el-form-item&gt;
&lt;el-button type=&quot;primary&quot; @click=&quot;handleQuery&quot;&gt;查询&lt;/el-button&gt;
&lt;el-button @click=&quot;resetQuery&quot;&gt;重置&lt;/el-button&gt;
&lt;/el-form-item&gt;
&lt;/el-form&gt;
&lt;!-- 账单信息 --&gt;
&lt;el-table v-loading=&quot;loading&quot; :data=&quot;billList&quot; @selection-change=&quot;handleSelectionChange&quot;&gt;
&lt;el-table-column type=&quot;selection&quot; width=&quot;55&quot; /&gt;
&lt;el-table-column prop=&quot;billCode&quot; label=&quot;账单编号&quot; /&gt;
&lt;el-table-column prop=&quot;billDate&quot; label=&quot;账期&quot; /&gt;
&lt;el-table-column prop=&quot;waterUsage&quot; label=&quot;用水量&quot; /&gt;
&lt;el-table-column prop=&quot;totalAmount&quot; label=&quot;应缴金额&quot; /&gt;
&lt;el-table-column prop=&quot;balanceAmount&quot; label=&quot;欠费金额&quot; /&gt;
&lt;/el-table&gt;
&lt;!-- 缴费操作 --&gt;
&lt;el-row class=&quot;payment-actions&quot;&gt;
&lt;el-col :span=&quot;12&quot;&gt;
&lt;el-statistic title=&quot;选中金额&quot; :value=&quot;selectedAmount&quot; prefix=&quot;&quot; /&gt;
&lt;/el-col&gt;
&lt;el-col :span=&quot;12&quot; class=&quot;text-right&quot;&gt;
&lt;el-button type=&quot;success&quot; @click=&quot;handleCashPayment&quot;&gt;现金缴费&lt;/el-button&gt;
&lt;el-button type=&quot;primary&quot; @click=&quot;handleOnlinePayment&quot;&gt;在线支付&lt;/el-button&gt;
&lt;el-button type=&quot;warning&quot; @click=&quot;handlePrepaidPayment&quot;&gt;预存扣费&lt;/el-button&gt;
&lt;/el-col&gt;
&lt;/el-row&gt;
&lt;/ContentWrap&gt;
&lt;/template&gt;</code></pre>
<h5 data-number="3.0.0.0.22" id="账务处理"><span
class="header-section-number">3.0.0.0.22</span> 3.4 账务处理</h5>
<p>账务处理模块负责处理各类特殊账务情况,确保账务数据的准确性和完整性。</p>
<h5 data-number="3.0.0.0.23" id="业务流程图-2"><span
class="header-section-number">3.0.0.0.23</span> 3.4.1 业务流程图</h5>
<pre class="mermaid"><code>flowchart TD
Start([账务处理请求]) --&gt; CheckAuth[权限验证]
CheckAuth --&gt; AuthOK{权限验证}
AuthOK --&gt;|失败| AuthError[权限错误]
AuthOK --&gt;|成功| ProcessType{处理类型}
ProcessType --&gt;|调账| AdjustAccount[账务调整]
ProcessType --&gt;|退款| RefundProcess[退款处理]
ProcessType --&gt;|销账| WriteOff[销账处理]
ProcessType --&gt;|预存调整| PrepaidAdjust[预存调整]
AdjustAccount --&gt; ValidateAdjust[验证调整数据]
RefundProcess --&gt; ValidateRefund[验证退款数据]
WriteOff --&gt; ValidateWriteOff[验证销账数据]
PrepaidAdjust --&gt; ValidatePrepaid[验证预存数据]
ValidateAdjust --&gt; AdjustApproval[调账审批]
ValidateRefund --&gt; RefundApproval[退款审批]
ValidateWriteOff --&gt; WriteOffApproval[销账审批]
ValidatePrepaid --&gt; PrepaidApproval[预存审批]
AdjustApproval --&gt; ApprovalResult{审批结果}
RefundApproval --&gt; ApprovalResult
WriteOffApproval --&gt; ApprovalResult
PrepaidApproval --&gt; ApprovalResult
ApprovalResult --&gt;|拒绝| ApprovalReject[审批拒绝]
ApprovalResult --&gt;|通过| ExecuteProcess[执行处理]
ExecuteProcess --&gt; UpdateAccount[更新账户]
UpdateAccount --&gt; RecordLog[记录日志]
RecordLog --&gt; Complete([处理完成])
AuthError --&gt; End([结束])
ApprovalReject --&gt; End</code></pre>
<h5 data-number="3.0.0.0.24" id="主要功能-2"><span
class="header-section-number">3.0.0.0.24</span> 3.4.2 主要功能</h5>
<p><strong>未销调整</strong>:处理各类账务调整需求 -
水量调整:调整用水量和相关费用 - 金额调整:直接调整账单金额 -
违约金减免:减免或取消违约金 - 费用追加:补收相关费用</p>
<p><strong>特殊开账</strong>:处理特殊情况的账单生成 -
补抄开账:补录抄表数据并生成账单 - 估抄开账:估算用水量生成账单 -
平均开账:基于历史用量平均开账</p>
<p><strong>账务退款</strong>:处理各类退款业务 -
多缴退款:退还多缴的水费 - 预付款退款:退还预存余额 -
错误缴费退款:退还错误缴费</p>
<h5 data-number="3.0.0.0.25" id="核心接口定义-2"><span
class="header-section-number">3.0.0.0.25</span> 3.4.3 核心接口定义</h5>
<div class="sourceCode" id="cb35"><pre
class="sourceCode java"><code class="sourceCode java"><span id="cb35-1"><a href="#cb35-1" aria-hidden="true" tabindex="-1"></a><span class="at">@RestController</span></span>
<span id="cb35-2"><a href="#cb35-2" aria-hidden="true" tabindex="-1"></a><span class="at">@RequestMapping</span><span class="op">(</span><span class="st">&quot;/admin-api/water/account&quot;</span><span class="op">)</span></span>
<span id="cb35-3"><a href="#cb35-3" aria-hidden="true" tabindex="-1"></a><span class="at">@Tag</span><span class="op">(</span>name <span class="op">=</span> <span class="st">&quot;管理后台 - 账务处理&quot;</span><span class="op">)</span></span>
<span id="cb35-4"><a href="#cb35-4" aria-hidden="true" tabindex="-1"></a><span class="at">@Validated</span></span>
<span id="cb35-5"><a href="#cb35-5" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span> <span class="kw">class</span> AccountProcessController <span class="op">{</span></span>
<span id="cb35-6"><a href="#cb35-6" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb35-7"><a href="#cb35-7" aria-hidden="true" tabindex="-1"></a> <span class="at">@PostMapping</span><span class="op">(</span><span class="st">&quot;/adjust&quot;</span><span class="op">)</span></span>
<span id="cb35-8"><a href="#cb35-8" aria-hidden="true" tabindex="-1"></a> <span class="at">@Operation</span><span class="op">(</span>summary <span class="op">=</span> <span class="st">&quot;账务调整&quot;</span><span class="op">)</span></span>
<span id="cb35-9"><a href="#cb35-9" aria-hidden="true" tabindex="-1"></a> <span class="at">@PreAuthorize</span><span class="op">(</span><span class="st">&quot;@ss.hasPermission(&#39;water:account:adjust&#39;)&quot;</span><span class="op">)</span></span>
<span id="cb35-10"><a href="#cb35-10" aria-hidden="true" tabindex="-1"></a> <span class="kw">public</span> CommonResult<span class="op">&lt;</span><span class="bu">Boolean</span><span class="op">&gt;</span> <span class="fu">adjustAccount</span><span class="op">(</span><span class="at">@Valid</span> <span class="at">@RequestBody</span> AccountAdjustReqVO adjustReqVO<span class="op">);</span></span>
<span id="cb35-11"><a href="#cb35-11" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb35-12"><a href="#cb35-12" aria-hidden="true" tabindex="-1"></a> <span class="at">@PostMapping</span><span class="op">(</span><span class="st">&quot;/refund&quot;</span><span class="op">)</span></span>
<span id="cb35-13"><a href="#cb35-13" aria-hidden="true" tabindex="-1"></a> <span class="at">@Operation</span><span class="op">(</span>summary <span class="op">=</span> <span class="st">&quot;退款处理&quot;</span><span class="op">)</span></span>
<span id="cb35-14"><a href="#cb35-14" aria-hidden="true" tabindex="-1"></a> <span class="at">@PreAuthorize</span><span class="op">(</span><span class="st">&quot;@ss.hasPermission(&#39;water:account:refund&#39;)&quot;</span><span class="op">)</span></span>
<span id="cb35-15"><a href="#cb35-15" aria-hidden="true" tabindex="-1"></a> <span class="kw">public</span> CommonResult<span class="op">&lt;</span><span class="bu">Boolean</span><span class="op">&gt;</span> <span class="fu">processRefund</span><span class="op">(</span><span class="at">@Valid</span> <span class="at">@RequestBody</span> RefundProcessReqVO refundReqVO<span class="op">);</span></span>
<span id="cb35-16"><a href="#cb35-16" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb35-17"><a href="#cb35-17" aria-hidden="true" tabindex="-1"></a> <span class="at">@PostMapping</span><span class="op">(</span><span class="st">&quot;/write-off&quot;</span><span class="op">)</span></span>
<span id="cb35-18"><a href="#cb35-18" aria-hidden="true" tabindex="-1"></a> <span class="at">@Operation</span><span class="op">(</span>summary <span class="op">=</span> <span class="st">&quot;销账处理&quot;</span><span class="op">)</span></span>
<span id="cb35-19"><a href="#cb35-19" aria-hidden="true" tabindex="-1"></a> <span class="at">@PreAuthorize</span><span class="op">(</span><span class="st">&quot;@ss.hasPermission(&#39;water:account:write-off&#39;)&quot;</span><span class="op">)</span></span>
<span id="cb35-20"><a href="#cb35-20" aria-hidden="true" tabindex="-1"></a> <span class="kw">public</span> CommonResult<span class="op">&lt;</span><span class="bu">Boolean</span><span class="op">&gt;</span> <span class="fu">writeOffAccount</span><span class="op">(</span><span class="at">@Valid</span> <span class="at">@RequestBody</span> WriteOffReqVO writeOffReqVO<span class="op">);</span></span>
<span id="cb35-21"><a href="#cb35-21" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<h5 data-number="3.0.0.0.26" id="发票管理"><span
class="header-section-number">3.0.0.0.26</span> 3.5 发票管理</h5>
<p>发票管理模块负责水费发票的全生命周期管理,支持纸质发票和电子发票。</p>
<h5 data-number="3.0.0.0.27" id="业务流程图-3"><span
class="header-section-number">3.0.0.0.27</span> 3.5.1 业务流程图</h5>
<pre class="mermaid"><code>flowchart TD
Start([发票业务]) --&gt; InvoiceType{发票类型}
InvoiceType --&gt;|纸质发票| PaperInvoice[纸质发票管理]
InvoiceType --&gt;|电子发票| EInvoice[电子发票管理]
PaperInvoice --&gt; PaperStock[发票库存管理]
PaperStock --&gt; PaperPrint[发票打印]
PaperPrint --&gt; PaperRecord[打印记录]
EInvoice --&gt; EInvoiceGenerate[电子发票生成]
EInvoiceGenerate --&gt; EInvoiceSign[电子签章]
EInvoiceSign --&gt; EInvoiceSend[发票推送]
PaperRecord --&gt; InvoiceQuery[发票查询]
EInvoiceSend --&gt; InvoiceQuery
InvoiceQuery --&gt; InvoiceCancel{需要作废?}
InvoiceCancel --&gt;|是| CancelInvoice[发票作废]
InvoiceCancel --&gt;|否| Complete([完成])
CancelInvoice --&gt; CancelRecord[作废记录]
CancelRecord --&gt; Complete</code></pre>
<h5 data-number="3.0.0.0.28" id="核心接口定义-3"><span
class="header-section-number">3.0.0.0.28</span> 3.5.2 核心接口定义</h5>
<div class="sourceCode" id="cb37"><pre
class="sourceCode java"><code class="sourceCode java"><span id="cb37-1"><a href="#cb37-1" aria-hidden="true" tabindex="-1"></a><span class="at">@RestController</span></span>
<span id="cb37-2"><a href="#cb37-2" aria-hidden="true" tabindex="-1"></a><span class="at">@RequestMapping</span><span class="op">(</span><span class="st">&quot;/admin-api/water/invoice&quot;</span><span class="op">)</span></span>
<span id="cb37-3"><a href="#cb37-3" aria-hidden="true" tabindex="-1"></a><span class="at">@Tag</span><span class="op">(</span>name <span class="op">=</span> <span class="st">&quot;管理后台 - 发票管理&quot;</span><span class="op">)</span></span>
<span id="cb37-4"><a href="#cb37-4" aria-hidden="true" tabindex="-1"></a><span class="at">@Validated</span></span>
<span id="cb37-5"><a href="#cb37-5" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span> <span class="kw">class</span> InvoiceController <span class="op">{</span></span>
<span id="cb37-6"><a href="#cb37-6" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb37-7"><a href="#cb37-7" aria-hidden="true" tabindex="-1"></a> <span class="at">@PostMapping</span><span class="op">(</span><span class="st">&quot;/generate&quot;</span><span class="op">)</span></span>
<span id="cb37-8"><a href="#cb37-8" aria-hidden="true" tabindex="-1"></a> <span class="at">@Operation</span><span class="op">(</span>summary <span class="op">=</span> <span class="st">&quot;生成发票&quot;</span><span class="op">)</span></span>
<span id="cb37-9"><a href="#cb37-9" aria-hidden="true" tabindex="-1"></a> <span class="kw">public</span> CommonResult<span class="op">&lt;</span>InvoiceRespVO<span class="op">&gt;</span> <span class="fu">generateInvoice</span><span class="op">(</span><span class="at">@Valid</span> <span class="at">@RequestBody</span> InvoiceGenerateReqVO generateReqVO<span class="op">);</span></span>
<span id="cb37-10"><a href="#cb37-10" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb37-11"><a href="#cb37-11" aria-hidden="true" tabindex="-1"></a> <span class="at">@PostMapping</span><span class="op">(</span><span class="st">&quot;/print&quot;</span><span class="op">)</span></span>
<span id="cb37-12"><a href="#cb37-12" aria-hidden="true" tabindex="-1"></a> <span class="at">@Operation</span><span class="op">(</span>summary <span class="op">=</span> <span class="st">&quot;打印发票&quot;</span><span class="op">)</span></span>
<span id="cb37-13"><a href="#cb37-13" aria-hidden="true" tabindex="-1"></a> <span class="kw">public</span> CommonResult<span class="op">&lt;</span><span class="bu">Boolean</span><span class="op">&gt;</span> <span class="fu">printInvoice</span><span class="op">(</span><span class="at">@Valid</span> <span class="at">@RequestBody</span> InvoicePrintReqVO printReqVO<span class="op">);</span></span>
<span id="cb37-14"><a href="#cb37-14" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb37-15"><a href="#cb37-15" aria-hidden="true" tabindex="-1"></a> <span class="at">@PostMapping</span><span class="op">(</span><span class="st">&quot;/cancel&quot;</span><span class="op">)</span></span>
<span id="cb37-16"><a href="#cb37-16" aria-hidden="true" tabindex="-1"></a> <span class="at">@Operation</span><span class="op">(</span>summary <span class="op">=</span> <span class="st">&quot;发票作废&quot;</span><span class="op">)</span></span>
<span id="cb37-17"><a href="#cb37-17" aria-hidden="true" tabindex="-1"></a> <span class="kw">public</span> CommonResult<span class="op">&lt;</span><span class="bu">Boolean</span><span class="op">&gt;</span> <span class="fu">cancelInvoice</span><span class="op">(</span><span class="at">@Valid</span> <span class="at">@RequestBody</span> InvoiceCancelReqVO cancelReqVO<span class="op">);</span></span>
<span id="cb37-18"><a href="#cb37-18" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<h5 data-number="3.0.0.0.29" id="代收业务"><span
class="header-section-number">3.0.0.0.29</span> 3.6 代收业务</h5>
<p>代收业务模块负责处理各种渠道的水费代收业务,主要功能包括:</p>
<ul>
<li>实时收费:银行代收、支付宝/微信收费、代收点收费</li>
<li>银行代扣:代扣协议管理、代扣数据生成和处理</li>
<li>银行托收:托收单生成、托收数据处理</li>
<li>代扣渠道管理:渠道维护、规则设置、效率分析</li>
</ul>
<h5 data-number="3.0.0.0.30" id="环卫系统"><span
class="header-section-number">3.0.0.0.30</span> 3.7 环卫系统</h5>
<p>环卫系统模块负责管理与环卫相关的收费和计费业务,主要功能包括:</p>
<ul>
<li>计费核定:环卫收费规则配置和业务字典维护</li>
<li>计费退补:退费和补缴规则配置,字典配置参与计算</li>
<li>计费减免:环卫费用减免规则配置和审批流程</li>
<li>环卫收费统计:环卫收费数据统计和分析</li>
</ul>
<h5 data-number="3.0.0.0.31" id="业务工单"><span
class="header-section-number">3.0.0.0.31</span> 3.8 业务工单</h5>
<p>业务工单模块负责管理日常业务工单的流转和处理,主要功能包括:</p>
<ul>
<li>业务清单:查询通过系统办理的日常业务工单及进度信息</li>
<li>上报清单:新增、查询问题清单,包括通过手机端上报的问题</li>
<li>稽查工单:对手机端的稽查信息进行多条件查询和导出</li>
<li>移表工单:客户对需要进行移动的水表进行登记</li>
<li>换表工单:新增客户自报的换表和故障的水表等进行换表登记</li>
<li>工作流可视化:工作节点可视化展示和流程管理</li>
</ul>
<h5 data-number="3.0.0.0.32" id="四表务系统"><span
class="header-section-number">3.0.0.0.32</span> 四、表务系统</h5>
<p>表务系统负责水表的全生命周期管理,包括水表购置、安装、维修、更换等业务。</p>
<h5 data-number="3.0.0.0.33" id="表务工单"><span
class="header-section-number">3.0.0.0.33</span> 4.1 表务工单</h5>
<p>表务工单模块负责处理各类表务作业,主要功能包括:</p>
<ul>
<li>换表工单:换表计划、换表施工、换表回填</li>
<li>移表工单:移表申请、移表施工、移表验收</li>
<li>拆表工单:拆表申请、拆表施工、拆表确认</li>
<li>复装工单:复装申请、复装施工、复装验收</li>
<li>校表工单:校表申请、校表实施、校表结果</li>
<li>稽查工单:水量异常稽查、违规用水稽查、处罚决定</li>
<li>业务上报:异常上报、投诉上报、处理结果反馈</li>
</ul>
<h5 data-number="3.0.0.0.34" id="表务仓库"><span
class="header-section-number">3.0.0.0.34</span> 4.2 表务仓库</h5>
<p>表务仓库模块负责水表的仓储管理,主要功能包括:</p>
<ul>
<li>新表入库:水表检定、入库登记、入库确认</li>
<li>水表检定:检定信息记录、结果管理</li>
<li>水表领用:领用单登记、领用单管理</li>
<li>水表出库:出库登记、出库确认</li>
<li>水表退库:退库申请、退库确认</li>
<li>水表报废:报废申请、报废确认</li>
<li>水表生命周期查询:状态查询、历史记录查询</li>
</ul>
<h5 data-number="3.0.0.0.35" id="水表参数与基础信息"><span
class="header-section-number">3.0.0.0.35</span> 4.3
水表参数与基础信息</h5>
<p>水表参数与基础信息模块负责维护水表相关的基础数据,主要功能包括:</p>
<ul>
<li>水表厂家管理:厂家信息维护、厂家状态管理</li>
<li>水表型号管理:型号信息维护、型号分类管理</li>
<li>水表口径管理:口径信息维护、定换周期设置</li>
<li>水表量程管理:量程信息维护、量程范围设置</li>
</ul>
<h5 data-number="3.0.0.0.36" id="物联网对接与数据同步"><span
class="header-section-number">3.0.0.0.36</span> 4.4
物联网对接与数据同步</h5>
<p>物联网对接与数据同步模块负责水表数据的互联互通,主要功能包括:</p>
<ul>
<li>厂家设备信息管理:设备型号管理、厂家信息维护</li>
<li>表务对接日志:数据同步日志、错误处理</li>
<li>水表数据推送接口:新表数据推送、校验机制</li>
<li>远程抄表数据同步:数据接收、验证与处理</li>
<li>物联网水表监控:状态监控、异常事件报警</li>
</ul>
<h5 data-number="3.0.0.0.37" id="五报装系统"><span
class="header-section-number">3.0.0.0.37</span> 五、报装系统</h5>
<p>报装系统负责新用户的报装立户管理,主要功能包括:</p>
<h5 data-number="3.0.0.0.38" id="报装流程"><span
class="header-section-number">3.0.0.0.38</span> 5.1 报装流程</h5>
<p>报装流程模块负责新用户报装业务的全流程管理,主要功能包括:</p>
<ul>
<li>报装申请:用户申请、材料审核、现场勘查</li>
<li>方案设计:设计方案、设计审核、费用估算</li>
<li>合同签订:费用通知、合同签订、收费管理</li>
<li>施工管理:施工计划、施工实施、施工验收</li>
<li>开户通水:水表安装、用户立户、通水确认</li>
</ul>
<h5 data-number="3.0.0.0.39" id="一户一表管理"><span
class="header-section-number">3.0.0.0.39</span> 5.2 一户一表管理</h5>
<p>一户一表管理模块负责实施”一户一表”改造,主要功能包括:</p>
<ul>
<li>改造计划:制定改造计划、用户协商、费用估算</li>
<li>改造实施:施工管理、水表安装、验收管理</li>
<li>用户转换:数据转换、账务处理、使用确认</li>
</ul>
<h5 data-number="3.0.0.0.40" id="六客户服务"><span
class="header-section-number">3.0.0.0.40</span> 六、客户服务</h5>
<p>客户服务模块提供多渠道的客户服务功能,主要包括:</p>
<h5 data-number="3.0.0.0.41" id="微信支付宝服务窗"><span
class="header-section-number">3.0.0.0.41</span> 6.1
微信、支付宝服务窗</h5>
<ul>
<li>账户绑定:支持用户绑定水务账户</li>
<li>用水查询:查询用水情况和用水趋势</li>
<li>水费查询:查询水费信息和欠费信息</li>
<li>在线缴费:支持在线缴纳水费</li>
<li>业务办理:支持在线办理简单业务</li>
</ul>
<h5 data-number="3.0.0.0.42" id="历史账单"><span
class="header-section-number">3.0.0.0.42</span> 6.2 历史账单</h5>
<ul>
<li>账单查询:查询历史账单信息</li>
<li>用水分析:分析用水趋势,提供图表展示</li>
<li>账单推送:定期推送账单信息</li>
<li>账单明细:提供详细的账单明细</li>
</ul>
<h5 data-number="3.0.0.0.43" id="电子发票"><span
class="header-section-number">3.0.0.0.43</span> 6.3 电子发票</h5>
<ul>
<li>发票申请:申请开具电子发票</li>
<li>发票查询:查询已开具发票</li>
<li>发票下载:下载电子发票</li>
<li>发票推送:推送电子发票到用户邮箱或微信</li>
</ul>
<h5 data-number="3.0.0.0.44" id="营业网点"><span
class="header-section-number">3.0.0.0.44</span> 6.4 营业网点</h5>
<ul>
<li>网点查询:查询营业网点信息</li>
<li>网点导航:提供到营业网点的导航</li>
<li>业务指南:提供各网点业务办理指南</li>
<li>等候情况:实时显示各网点的等候情况</li>
</ul>
<h5 data-number="3.0.0.0.45" id="账户流水"><span
class="header-section-number">3.0.0.0.45</span> 6.5 账户流水</h5>
<ul>
<li>流水查询:查询账户交易流水</li>
<li>明细下载:下载流水明细</li>
<li>交易统计:提供交易金额统计</li>
<li>缴费证明:生成缴费证明</li>
</ul>
<h5 data-number="3.0.0.0.46" id="微网厅"><span
class="header-section-number">3.0.0.0.46</span> 6.6 微网厅</h5>
<ul>
<li>用户注册:用户注册微网厅账号</li>
<li>信息查询:查询用水、缴费信息</li>
<li>业务办理:在线办理各类业务</li>
<li>用户反馈:提交意见建议</li>
</ul>
<h5 data-number="3.0.0.0.47" id="七系统配置"><span
class="header-section-number">3.0.0.0.47</span> 七、系统配置</h5>
<p>系统配置模块提供各类系统参数的配置管理功能,主要包括:</p>
<h5 data-number="3.0.0.0.48" id="水表参数"><span
class="header-section-number">3.0.0.0.48</span> 7.1 水表参数</h5>
<ul>
<li>水表厂家:管理水表生产厂商信息</li>
<li>水表型号:管理水表型号规格</li>
<li>水表量程:管理水表量程规格</li>
</ul>
<h5 data-number="3.0.0.0.49" id="地址参数"><span
class="header-section-number">3.0.0.0.49</span> 7.2 地址参数</h5>
<ul>
<li>行政区划:管理行政区划信息</li>
<li>地址编码:管理地址编码规则</li>
<li>地址库:维护标准地址库</li>
</ul>
<h5 data-number="3.0.0.0.50" id="价格体系"><span
class="header-section-number">3.0.0.0.50</span> 7.3 价格体系</h5>
<ul>
<li>用水性质:管理不同用水性质的分类</li>
<li>水价标准:管理不同用水性质的水价标准</li>
<li>阶梯水价:管理阶梯水价设置</li>
<li>调价管理:管理水价调整流程</li>
</ul>
<h5 data-number="3.0.0.0.51" id="基本配置"><span
class="header-section-number">3.0.0.0.51</span> 7.4 基本配置</h5>
<ul>
<li>系统参数:管理系统基本参数</li>
<li>业务规则:管理业务处理规则</li>
<li>打印模板:管理各类打印模板</li>
<li>短信模板:管理短信发送模板</li>
</ul>
<h5 data-number="3.0.0.0.52" id="催缴管理"><span
class="header-section-number">3.0.0.0.52</span> 7.5 催缴管理</h5>
<ul>
<li>催缴计划:制定欠费催缴计划</li>
<li>催缴任务:分配催缴任务</li>
<li>催缴执行:记录催缴执行情况</li>
<li>催缴结果:统计催缴结果</li>
</ul>
<h5 data-number="3.0.0.0.53" id="用户权限"><span
class="header-section-number">3.0.0.0.53</span> 7.6 用户权限</h5>
<ul>
<li>菜单权限:不同角色可访问的菜单权限配置</li>
<li>功能权限:不同角色可操作的功能权限配置</li>
<li>数据权限:通过查询字典权限控制不同用户业务取数权限</li>
<li>角色管理:自定义角色及权限组合</li>
</ul>
<h5 data-number="3.0.0.0.54" id="定时任务"><span
class="header-section-number">3.0.0.0.54</span> 7.7 定时任务</h5>
<ul>
<li>任务配置:定时任务可视化配置,包含传参设置</li>
<li>任务监控:定时任务执行状态监控</li>
<li>任务日志:定时任务执行日志记录和查询</li>
<li>任务调度:任务优先级和执行顺序管理</li>
</ul>
<h5 data-number="3.0.0.0.55" id="八系统接口"><span
class="header-section-number">3.0.0.0.55</span> 八、系统接口</h5>
<p>系统接口模块提供与外部系统的集成和数据交换功能,实现业务数据的互通互联。</p>
<h5 data-number="3.0.0.0.56" id="银行接口"><span
class="header-section-number">3.0.0.0.56</span> 8.1 银行接口</h5>
<p>银行接口实现与银行系统的对接,支持代扣、托收等功能,主要包括:</p>
<ul>
<li>收费接口:实现用户通过银行渠道缴纳水费</li>
<li>冲正接口:处理银行退款和交易取消</li>
<li>对账接口:与银行系统进行对账</li>
<li>托收接口:支持银行托收功能</li>
<li>代扣接口:支持银行代扣功能</li>
</ul>
<h5 data-number="3.0.0.0.57" id="支付宝微信接口"><span
class="header-section-number">3.0.0.0.57</span> 8.2 支付宝/微信接口</h5>
<p>支付宝和微信接口实现与第三方支付平台的对接,支持在线支付功能,主要包括:</p>
<ul>
<li>欠费查询:查询用户欠费信息</li>
<li>在线支付:支持用户在线缴纳水费</li>
<li>支付通知:处理支付结果通知</li>
<li>对账接口:与支付平台进行对账</li>
</ul>
<h5 data-number="3.0.0.0.58" id="短信接口"><span
class="header-section-number">3.0.0.0.58</span> 8.3 短信接口</h5>
<p>短信接口提供短信通知和验证功能,主要包括:</p>
<ul>
<li>账单通知:通过短信通知用户账单信息</li>
<li>缴费提醒:提醒用户及时缴费</li>
<li>催缴通知:向欠费用户发送催缴短信</li>
<li>业务通知:通知用户业务办理状态</li>
<li>验证码功能:提供短信验证码服务</li>
</ul>
<h5 data-number="3.0.0.0.59" id="集抄系统接口"><span
class="header-section-number">3.0.0.0.59</span> 8.4 集抄系统接口</h5>
<p>集抄系统接口实现与智能水表集中抄表系统的对接,主要包括:</p>
<ul>
<li>抄表数据获取:获取智能水表的抄表数据</li>
<li>水表状态监控:监控水表的使用状态</li>
<li>异常数据处理:处理抄表异常情况</li>
</ul>
<h5 data-number="3.0.0.0.60" id="政务系统接口"><span
class="header-section-number">3.0.0.0.60</span> 8.5 政务系统接口</h5>
<p>政务系统接口实现与地方政务平台和政务APP的对接主要包括</p>
<ul>
<li>数据推送:推送营收数据到政务平台</li>
<li>数据查询:支持政务平台查询水务数据</li>
<li>缴费对接:支持通过政务平台缴费</li>
<li>报装工单对接:对接报装业务工单</li>
</ul>
<h5 data-number="3.0.0.0.61" id="消火栓系统接口"><span
class="header-section-number">3.0.0.0.61</span> 8.6 消火栓系统接口</h5>
<p>消火栓系统接口实现与消火栓系统的对接,主要功能包括:</p>
<ul>
<li>预存缴费管理:管理用户预存水费</li>
<li>剩余水量计算:计算用户剩余可用水量</li>
<li>取水控制:根据预存和剩余水量控制取水</li>
</ul>
<h5 data-number="3.0.0.0.62" id="其他系统对接"><span
class="header-section-number">3.0.0.0.62</span> 8.7 其他系统对接</h5>
<p>其他系统对接模块负责与周边系统进行数据交换和业务协同,主要功能包括:</p>
<ul>
<li>环卫系统对接:实现与环卫系统的数据同步和业务流程对接</li>
<li>客服系统对接:实现与客服系统的工单同步和问题处理</li>
<li>工单系统对接:实现与工单系统的工单流转和状态同步</li>
<li>OA系统对接实现与OA系统的审批流程对接和数据共享</li>
<li>智水擎平台对接:实现与智水擎平台的数据交换和业务协同</li>
</ul>
<h5 data-number="3.0.0.0.63" id="九统计分析"><span
class="header-section-number">3.0.0.0.63</span> 九、统计分析</h5>
<p>统计分析模块提供多维度的数据统计和分析功能,为管理决策提供数据支持。</p>
<h5 data-number="3.0.0.0.64" id="报表查询"><span
class="header-section-number">3.0.0.0.64</span> 9.1 报表查询</h5>
<ul>
<li>标准报表:系统内置的标准统计报表</li>
<li>自定义报表:用户可自定义的报表</li>
<li>报表导出支持将报表导出为Excel、PDF等格式</li>
<li>报表打印:支持报表打印功能</li>
</ul>
<h5 data-number="3.0.0.0.65" id="欠费查询"><span
class="header-section-number">3.0.0.0.65</span> 9.2 欠费查询</h5>
<ul>
<li>欠费情况统计:按区域、用户类型等维度统计欠费情况</li>
<li>欠费用户明细:查询欠费用户的详细信息</li>
<li>欠费分析:分析欠费原因和趋势</li>
<li>欠费导出:支持将欠费数据导出</li>
</ul>
<h5 data-number="3.0.0.0.66" id="缴费记录"><span
class="header-section-number">3.0.0.0.66</span> 9.3 缴费记录</h5>
<ul>
<li>缴费情况统计:按多维度统计缴费情况</li>
<li>缴费明细查询:查询缴费明细记录</li>
<li>缴费趋势分析:分析缴费趋势</li>
<li>缴费渠道分析:分析各缴费渠道的使用情况</li>
</ul>
<h5 data-number="3.0.0.0.67" id="用水分析"><span
class="header-section-number">3.0.0.0.67</span> 9.4 用水分析</h5>
<ul>
<li>用水量统计:按多维度统计用水量</li>
<li>用水趋势分析:分析用水趋势,预测用水需求</li>
<li>异常用水分析:识别和分析异常用水情况</li>
<li>节水潜力分析:分析用户节水潜力</li>
</ul>
<h5 data-number="3.0.0.0.68" id="十工程管理"><span
class="header-section-number">3.0.0.0.68</span> 十、工程管理</h5>
<p>工程管理模块负责处理与供水工程相关的业务,包括工程申请、施工管理和工程验收等。</p>
<h5 data-number="3.0.0.0.69" id="工程申请"><span
class="header-section-number">3.0.0.0.69</span> 10.1 工程申请</h5>
<ul>
<li>工程立项:新建供水工程的立项申请</li>
<li>材料提交:上传工程相关材料</li>
<li>前期勘察:记录工程前期勘察结果</li>
<li>工程预算:制定工程预算方案</li>
<li>申请审批:对工程申请进行审批</li>
</ul>
<h5 data-number="3.0.0.0.70" id="工程施工"><span
class="header-section-number">3.0.0.0.70</span> 10.2 工程施工</h5>
<ul>
<li>施工计划:制定工程施工计划</li>
<li>施工派工:将施工任务分配给施工人员</li>
<li>施工记录:记录施工过程和施工情况</li>
<li>材料管理:管理工程施工材料的使用情况</li>
<li>施工异常处理:处理施工过程中的异常情况</li>
</ul>
<h5 data-number="3.0.0.0.71" id="工程验收"><span
class="header-section-number">3.0.0.0.71</span> 10.3 工程验收</h5>
<ul>
<li>验收申请:提交工程验收申请</li>
<li>验收检查:对工程进行验收检查</li>
<li>验收记录:记录验收结果</li>
<li>竣工结算:进行工程竣工结算</li>
<li>工程归档:对工程资料进行归档</li>
</ul>
<h5 data-number="3.0.0.0.72" id="工程查询"><span
class="header-section-number">3.0.0.0.72</span> 10.4 工程查询</h5>
<ul>
<li>工程进度查询:查询工程进度情况</li>
<li>工程资料查询:查询工程相关资料</li>
<li>工程统计:统计工程数量、金额等信息</li>
<li>工程分析:分析工程实施情况</li>
</ul>
<h5 data-number="3.0.0.0.73" id="十一抄表app"><span
class="header-section-number">3.0.0.0.73</span> 十一、抄表APP</h5>
<p>抄表APP是针对移动端开发的抄表工具支持外勤人员进行现场抄表、问题处理和工单管理等业务操作。</p>
<h5 data-number="3.0.0.0.74" id="首页功能"><span
class="header-section-number">3.0.0.0.74</span> 11.1 首页功能</h5>
<ul>
<li>首页:显示当前登录用户信息、快捷搜索和主要和抄表业务相关的功能模块</li>
<li>个人信息:查看个人信息,可以进行密码、手机号信息修改,可以进行抄表设置</li>
<li>快捷查询:提供搜索栏,用户输入户号、户名、地址以及册本后,系统会自动检索到相关的信息</li>
</ul>
<h5 data-number="3.0.0.0.75" id="抄表功能"><span
class="header-section-number">3.0.0.0.75</span> 11.2 抄表功能</h5>
<ul>
<li>抄表任务:显示当前登录的抄表员抄表任务信息</li>
<li>NFC抄表抄表设备感应NFC后自动弹出抄表页</li>
<li>扫码抄表:扫水表上二维码,搜集该表信息,弹出抄表页</li>
<li>数据采集:抄表数据现场采集功能</li>
<li>AI录入对接AI接口实现智能读数</li>
<li>抄表轨迹:记录表的物理位置,在地图上点击后采集水表定位</li>
<li>抄表导航:指引抄表员前往下一个抄表点</li>
<li>抄表汇总:系统会根据当前登录的人员自动汇总抄表信息</li>
<li>账单查询:查询客户账单详情</li>
<li>欠费查询:显示当前登录的抄表员抄表用水客户的欠费情况</li>
</ul>
<h5 data-number="3.0.0.0.76" id="工单管理"><span
class="header-section-number">3.0.0.0.76</span> 11.3 工单管理</h5>
<ul>
<li>问题上报:系统支持上报当前登录的抄表员抄表时遇到的用水问题</li>
<li>问题回填:问题上报后,可进行问题回填和处理</li>
<li>代办工单:显示当前登录的抄表员需要代办工单</li>
<li>平台工单管理:移动端提交工单后,系统会自动提交到工单处置平台进行受理、派发</li>
<li>停水复水工单:当客户存在欠费的情况下可以对该客户进行停水操作</li>
<li>稽查工单:实现抄表稽查和水价稽查</li>
<li>维修工单:提交坏表维修报修等工单</li>
</ul>
<h5 data-number="3.0.0.0.77" id="十二接口服务"><span
class="header-section-number">3.0.0.0.77</span> 十二、接口服务</h5>
<p>接口服务模块提供系统对外的API接口管理和服务能力实现与第三方系统的便捷集成。</p>
<h5 data-number="3.0.0.0.78" id="api市场"><span
class="header-section-number">3.0.0.0.78</span> 12.1 API市场</h5>
<ul>
<li>API展示展示系统提供的各类API接口</li>
<li>API使用说明提供API接口的详细使用说明和示例</li>
<li>API测试提供API接口的在线测试功能</li>
</ul>
<h5 data-number="3.0.0.0.79" id="api管理"><span
class="header-section-number">3.0.0.0.79</span> 12.2 API管理</h5>
<ul>
<li>API申请第三方系统申请调用API的流程管理</li>
<li>API调度看板监控API调用情况和性能指标</li>
<li>接口服务配置管理管理API接口的配置参数</li>
<li>接口限流熔断管理控制API接口的调用频率和熔断机制</li>
</ul>
<h5 data-number="3.0.0.0.80" id="接口权限管理"><span
class="header-section-number">3.0.0.0.80</span> 12.3 接口权限管理</h5>
<ul>
<li>授权管理管理API接口的授权信息</li>
<li>白名单管理设置允许访问API的IP白名单</li>
<li>安全策略设置API调用的安全策略和防护措施</li>
</ul>
<h5 data-number="3.0.0.0.81" id="系统对外接口"><span
class="header-section-number">3.0.0.0.81</span> 12.4 系统对外接口</h5>
<ul>
<li>查询接口:提供各类数据查询接口</li>
<li>业务处理接口:提供业务处理和操作接口</li>
<li>状态同步接口:提供状态信息同步接口</li>
<li>数据推送接口:提供数据推送和订阅接口</li>
</ul>
<h5 data-number="3.0.0.0.82" id="系统集成架构"><span
class="header-section-number">3.0.0.0.82</span> 系统集成架构</h5>
<h5 data-number="3.0.0.0.83" id="前后端集成架构"><span
class="header-section-number">3.0.0.0.83</span> 前后端集成架构</h5>
<pre class="mermaid"><code>graph TB
subgraph &quot;前端应用&quot;
F1[管理后台&lt;br/&gt;yudao-ui-admin-vue3]
F2[移动端&lt;br/&gt;uni-app]
F3[客户端&lt;br/&gt;微信小程序]
end
subgraph &quot;后端服务&quot;
B1[认证服务&lt;br/&gt;Spring Security]
B2[业务服务&lt;br/&gt;RuoYi-Vue-Pro]
B3[网关服务&lt;br/&gt;Spring Cloud Gateway]
end
subgraph &quot;数据存储&quot;
D1[(OpenGauss 5.0+)]
D2[(Redis 6.0)]
D3[MinIO文件存储]
end
F1 --&gt; B3
F2 --&gt; B3
F3 --&gt; B3
B3 --&gt; B1
B3 --&gt; B2
B1 --&gt; D2
B2 --&gt; D1
B2 --&gt; D2
B2 --&gt; D3</code></pre>
<h5 data-number="3.0.0.0.84" id="技术栈整合方案"><span
class="header-section-number">3.0.0.0.84</span> 技术栈整合方案</h5>
<p><strong>后端技术整合</strong> - Spring Boot 3.x作为核心框架 -
Spring Security 6.x提供安全认证 - MyBatis Plus 3.x简化数据访问 -
RuoYi-Vue-Pro提供基础功能框架</p>
<p><strong>前端技术整合</strong> - Vue 3.x + TypeScript构建现代化前端
- Element Plus提供UI组件库 - Vite作为构建工具 - Pinia进行状态管理</p>
<p><strong>数据库集成</strong> - OpenGauss
5.0+作为主数据库,国产自主可控 - Redis 6.0提供缓存和会话管理 -
HikariCP连接池优化和读写分离支持</p>
<p><strong>中间件集成</strong> - RabbitMQ提供消息队列 -
MinIO提供文件存储 - Elasticsearch提供全文搜索</p>
<h2 data-number="3.1"
id="这样的架构设计确保了系统的高可用性可扩展性和维护性为福建水务营收系统提供了坚实的技术基础"><span
class="header-section-number">3.1</span>
这样的架构设计确保了系统的高可用性、可扩展性和维护性,为福建水务营收系统提供了坚实的技术基础。</h2>
<p>ewpage</p>
<h1 data-number="4" id="第3部分"><span
class="header-section-number">4</span> 第3部分</h1>
<h5 data-number="4.0.0.0.1" id="福建水务营收系统数据库设计文档"><span
class="header-section-number">4.0.0.0.1</span>
福建水务营收系统数据库设计文档</h5>
<h5 data-number="4.0.0.0.2" id="文档信息-3"><span
class="header-section-number">4.0.0.0.2</span> 文档信息</h5>
<table>
<thead>
<tr>
<th>项目信息</th>
<th>详情</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>项目名称</strong></td>
<td>福建水务营收系统</td>
</tr>
<tr>
<td><strong>文档类型</strong></td>
<td>概要设计文档</td>
</tr>
<tr>
<td><strong>技术框架</strong></td>
<td>RuoYi-Vue-Pro + yudao-ui-admin-vue3</td>
</tr>
<tr>
<td><strong>文档版本</strong></td>
<td>v1.0</td>
</tr>
<tr>
<td><strong>编写日期</strong></td>
<td>2024-12-19</td>
</tr>
<tr>
<td><strong>文档状态</strong></td>
<td>✅ 已完成</td>
</tr>
</tbody>
</table>
<h5 data-number="4.0.0.0.3" id="目录-2"><span
class="header-section-number">4.0.0.0.3</span> 目录</h5>
<ul>
<li><a href="#一数据库设计概述">一、数据库设计概述</a></li>
<li><a href="#二数据库架构">二、数据库架构</a></li>
<li><a href="#三核心数据模型er图">三、核心数据模型ER图</a></li>
<li><a href="#四核心数据表设计">四、核心数据表设计</a></li>
<li><a href="#五完整ddl语句">五、完整DDL语句</a></li>
<li><a
href="#六opengauss数据库特性应用">六、OpenGauss数据库特性应用</a></li>
<li><a href="#七索引设计与优化">七、索引设计与优化</a></li>
<li><a
href="#八opengauss数据安全设计">八、OpenGauss数据安全设计</a></li>
</ul>
<h5 data-number="4.0.0.0.4" id="一数据库设计概述"><span
class="header-section-number">4.0.0.0.4</span> 一、数据库设计概述</h5>
<p>福建水务营收系统采用<strong>华为OpenGauss
5.0+</strong>数据库基于RuoYi-Vue-Pro框架规范设计。OpenGauss作为国产自主可控的企业级数据库具有高性能、高可用、高安全的特点完全满足水务行业对数据安全和国产化的要求。数据库架构支持多租户、高并发、高可用的业务需求为水务营收业务提供稳定可靠的数据存储服务。</p>
<h5 data-number="4.0.0.0.5" id="设计目标"><span
class="header-section-number">4.0.0.0.5</span> 1.1 设计目标</h5>
<ul>
<li>支持100万客户的业务量满足3-5年业务发展需求</li>
<li>支持200并发用户50并发移动设备</li>
<li>系统响应时间不超过3秒</li>
<li>数据安全等保三级要求</li>
<li>支持多租户数据隔离</li>
<li><strong>国产化要求</strong>:符合国产数据库替代要求</li>
</ul>
<h5 data-number="4.0.0.0.6" id="数据库选型优势"><span
class="header-section-number">4.0.0.0.6</span> 1.2 数据库选型优势</h5>
<ul>
<li><strong>国产自主可控</strong>华为OpenGauss是完全自主知识产权的数据库</li>
<li><strong>高性能</strong>优化的SQL引擎查询性能优于传统数据库20-30%</li>
<li><strong>高可用</strong>:支持主备同步、读写分离、故障自动切换</li>
<li><strong>安全可信</strong>:内置数据加密、审计、权限管控等安全特性</li>
<li><strong>生态兼容</strong>兼容PostgreSQL/MySQL协议迁移成本低</li>
</ul>
<h5 data-number="4.0.0.0.7" id="设计原则"><span
class="header-section-number">4.0.0.0.7</span> 1.3 设计原则</h5>
<ul>
<li><strong>标准化</strong>遵循OpenGauss数据库设计规范</li>
<li><strong>多租户</strong>基于tenant_id字段实现数据隔离</li>
<li><strong>可扩展</strong>:预留扩展字段,支持业务增长</li>
<li><strong>高性能</strong>:合理设计索引,优化查询性能</li>
<li><strong>安全性</strong>利用OpenGauss内置安全特性</li>
</ul>
<h5 data-number="4.0.0.0.8" id="二数据库架构"><span
class="header-section-number">4.0.0.0.8</span> 二、数据库架构</h5>
<h5 data-number="4.0.0.0.9" id="数据库架构图"><span
class="header-section-number">4.0.0.0.9</span> 1. 数据库架构图</h5>
<pre class="mermaid"><code>graph TB
subgraph &quot;应用层&quot;
APP[Water Biz Application&lt;br/&gt;RuoYi-Vue-Pro]
end
subgraph &quot;数据访问层&quot;
MP[MyBatis Plus&lt;br/&gt;ORM框架]
CACHE[Redis缓存&lt;br/&gt;热点数据]
end
subgraph &quot;数据存储层&quot;
subgraph &quot;OpenGauss主从集群&quot;
MASTER[(OpenGauss主库&lt;br/&gt;读写)]
SLAVE[(OpenGauss从库&lt;br/&gt;只读)]
end
subgraph &quot;业务数据库&quot;
DB_CUSTOMER[(客户数据库&lt;br/&gt;Customer DB)]
DB_BILLING[(营收数据库&lt;br/&gt;Billing DB)]
DB_METER[(表务数据库&lt;br/&gt;Meter DB)]
DB_SYSTEM[(系统数据库&lt;br/&gt;System DB)]
end
subgraph &quot;数据归档&quot;
DB_HISTORY[(历史数据库&lt;br/&gt;Archive DB)]
BACKUP[(备份存储&lt;br/&gt;Backup Storage)]
end
end
APP --&gt; MP
APP --&gt; CACHE
MP --&gt; MASTER
MASTER --&gt; SLAVE
MASTER --&gt; DB_CUSTOMER
MASTER --&gt; DB_BILLING
MASTER --&gt; DB_METER
MASTER --&gt; DB_SYSTEM
SLAVE --&gt; DB_HISTORY
MASTER --&gt; BACKUP</code></pre>
<h5 data-number="4.0.0.0.10" id="多租户架构设计"><span
class="header-section-number">4.0.0.0.10</span> 2. 多租户架构设计</h5>
<pre class="mermaid"><code>graph TB
subgraph &quot;多租户数据隔离&quot;
TENANT1[租户1: 福建水务集团]
TENANT2[租户2: 厦门分公司]
TENANT3[租户3: 泉州分公司]
end
subgraph &quot;共享数据库&quot;
subgraph &quot;业务表结构&quot;
TABLE1[water_customer&lt;br/&gt;+ tenant_id]
TABLE2[water_meter&lt;br/&gt;+ tenant_id]
TABLE3[water_bill&lt;br/&gt;+ tenant_id]
TABLE4[water_payment&lt;br/&gt;+ tenant_id]
end
subgraph &quot;租户隔离机制&quot;
INTERCEPTOR[MyBatis Plus&lt;br/&gt;多租户拦截器]
FILTER[数据权限过滤器]
end
end
TENANT1 --&gt; INTERCEPTOR
TENANT2 --&gt; INTERCEPTOR
TENANT3 --&gt; INTERCEPTOR
INTERCEPTOR --&gt; FILTER
FILTER --&gt; TABLE1
FILTER --&gt; TABLE2
FILTER --&gt; TABLE3
FILTER --&gt; TABLE4</code></pre>
<h5 data-number="4.0.0.0.11" id="通用字段设计"><span
class="header-section-number">4.0.0.0.11</span> 3. 通用字段设计</h5>
<p>所有业务表统一包含以下基础字段:</p>
<table>
<thead>
<tr>
<th>字段名</th>
<th>数据类型</th>
<th>长度</th>
<th>默认值</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>id</td>
<td>BIGINT</td>
<td>-</td>
<td>AUTO_INCREMENT</td>
<td>主键ID</td>
</tr>
<tr>
<td>tenant_id</td>
<td>BIGINT</td>
<td>-</td>
<td>0</td>
<td>租户ID(多租户隔离)</td>
</tr>
<tr>
<td>creator</td>
<td>VARCHAR</td>
<td>64</td>
<td></td>
<td>创建者</td>
</tr>
<tr>
<td>create_time</td>
<td>DATETIME</td>
<td>-</td>
<td>CURRENT_TIMESTAMP</td>
<td>创建时间</td>
</tr>
<tr>
<td>updater</td>
<td>VARCHAR</td>
<td>64</td>
<td></td>
<td>更新者</td>
</tr>
<tr>
<td>update_time</td>
<td>DATETIME</td>
<td>-</td>
<td>CURRENT_TIMESTAMP</td>
<td>更新时间</td>
</tr>
<tr>
<td>deleted</td>
<td>BIT</td>
<td>1</td>
<td>0</td>
<td>逻辑删除标识</td>
</tr>
</tbody>
</table>
<h5 data-number="4.0.0.0.12" id="三核心数据模型er图"><span
class="header-section-number">4.0.0.0.12</span>
三、核心数据模型ER图</h5>
<h5 data-number="4.0.0.0.13" id="客户管理模块er图"><span
class="header-section-number">4.0.0.0.13</span> 1. 客户管理模块ER图</h5>
<pre class="mermaid"><code>erDiagram
WATER_CUSTOMER {
bigint id PK &quot;主键ID&quot;
varchar customer_code UK &quot;客户编号&quot;
varchar customer_name &quot;客户名称&quot;
varchar customer_type &quot;客户类型&quot;
varchar id_type &quot;证件类型&quot;
varchar id_number &quot;证件号码&quot;
varchar phone &quot;联系电话&quot;
varchar address &quot;详细地址&quot;
varchar area_code &quot;行政区划代码&quot;
tinyint status &quot;状态&quot;
bigint tenant_id &quot;租户ID&quot;
varchar creator &quot;创建者&quot;
datetime create_time &quot;创建时间&quot;
varchar updater &quot;更新者&quot;
datetime update_time &quot;更新时间&quot;
tinyint deleted &quot;是否删除&quot;
}
WATER_METER {
bigint id PK &quot;主键ID&quot;
varchar meter_code UK &quot;水表编号&quot;
varchar meter_no &quot;水表表号&quot;
varchar meter_type &quot;水表类型&quot;
varchar meter_model &quot;水表型号&quot;
varchar meter_caliber &quot;水表口径&quot;
date install_date &quot;安装日期&quot;
varchar install_position &quot;安装位置&quot;
decimal initial_reading &quot;初始读数&quot;
decimal current_reading &quot;当前读数&quot;
varchar reading_cycle &quot;抄表周期&quot;
varchar book_code &quot;册本编号&quot;
tinyint status &quot;状态&quot;
bigint customer_id FK &quot;客户ID&quot;
bigint tenant_id &quot;租户ID&quot;
varchar creator &quot;创建者&quot;
datetime create_time &quot;创建时间&quot;
varchar updater &quot;更新者&quot;
datetime update_time &quot;更新时间&quot;
tinyint deleted &quot;是否删除&quot;
}
WATER_CUSTOMER_ACCOUNT {
bigint id PK &quot;主键ID&quot;
decimal balance &quot;账户余额&quot;
decimal credit_amount &quot;信用额度&quot;
date last_payment_date &quot;最近缴费日期&quot;
tinyint status &quot;账户状态&quot;
bigint customer_id FK &quot;客户ID&quot;
bigint tenant_id &quot;租户ID&quot;
varchar creator &quot;创建者&quot;
datetime create_time &quot;创建时间&quot;
varchar updater &quot;更新者&quot;
datetime update_time &quot;更新时间&quot;
tinyint deleted &quot;是否删除&quot;
}
WATER_CUSTOMER ||--o{ WATER_METER : &quot;拥有&quot;
WATER_CUSTOMER ||--|| WATER_CUSTOMER_ACCOUNT : &quot;对应&quot;</code></pre>
<h5 data-number="4.0.0.0.14" id="营收管理模块er图"><span
class="header-section-number">4.0.0.0.14</span> 2. 营收管理模块ER图</h5>
<pre class="mermaid"><code>erDiagram
WATER_METER_READING {
bigint id PK &quot;主键ID&quot;
varchar reading_code UK &quot;抄表记录编号&quot;
date reading_date &quot;抄表日期&quot;
decimal reading_value &quot;抄表读数&quot;
decimal prev_reading_value &quot;上次读数&quot;
decimal water_usage &quot;用水量&quot;
varchar reading_type &quot;抄表类型&quot;
varchar reader_id &quot;抄表员ID&quot;
varchar remark &quot;备注&quot;
tinyint status &quot;状态&quot;
bigint meter_id FK &quot;水表ID&quot;
bigint tenant_id &quot;租户ID&quot;
varchar creator &quot;创建者&quot;
datetime create_time &quot;创建时间&quot;
varchar updater &quot;更新者&quot;
datetime update_time &quot;更新时间&quot;
tinyint deleted &quot;是否删除&quot;
}
WATER_BILL {
bigint id PK &quot;主键ID&quot;
varchar bill_code UK &quot;账单编号&quot;
varchar bill_month &quot;账期&quot;
decimal water_usage &quot;用水量&quot;
decimal water_fee &quot;水费金额&quot;
decimal sewage_fee &quot;污水处理费&quot;
decimal other_fee &quot;其他费用&quot;
decimal total_amount &quot;总金额&quot;
date due_date &quot;缴费截止日期&quot;
tinyint bill_status &quot;账单状态&quot;
bigint customer_id FK &quot;客户ID&quot;
bigint meter_id FK &quot;水表ID&quot;
bigint reading_id FK &quot;抄表记录ID&quot;
bigint tenant_id &quot;租户ID&quot;
varchar creator &quot;创建者&quot;
datetime create_time &quot;创建时间&quot;
varchar updater &quot;更新者&quot;
datetime update_time &quot;更新时间&quot;
tinyint deleted &quot;是否删除&quot;
}
WATER_PAYMENT {
bigint id PK &quot;主键ID&quot;
varchar payment_code UK &quot;缴费记录编号&quot;
varchar payment_type &quot;缴费类型&quot;
varchar payment_channel &quot;缴费渠道&quot;
decimal payment_amount &quot;缴费金额&quot;
datetime payment_time &quot;缴费时间&quot;
varchar transaction_no &quot;交易流水号&quot;
varchar operator_id &quot;操作员ID&quot;
varchar remark &quot;备注&quot;
tinyint payment_status &quot;缴费状态&quot;
bigint bill_id FK &quot;账单ID&quot;
bigint customer_id FK &quot;客户ID&quot;
bigint tenant_id &quot;租户ID&quot;
varchar creator &quot;创建者&quot;
datetime create_time &quot;创建时间&quot;
varchar updater &quot;更新者&quot;
datetime update_time &quot;更新时间&quot;
tinyint deleted &quot;是否删除&quot;
}
WATER_METER_READING ||--|| WATER_BILL : &quot;生成&quot;
WATER_BILL ||--o{ WATER_PAYMENT : &quot;对应&quot;</code></pre>
<h5 data-number="4.0.0.0.15" id="表务管理模块er图"><span
class="header-section-number">4.0.0.0.15</span> 3. 表务管理模块ER图</h5>
<pre class="mermaid"><code>erDiagram
WATER_METER_ARCHIVE {
bigint id PK &quot;主键ID&quot;
varchar archive_code UK &quot;档案编号&quot;
varchar manufacturer &quot;生产厂家&quot;
date production_date &quot;生产日期&quot;
int valid_period &quot;有效期(月)&quot;
date verification_date &quot;检定日期&quot;
date next_verification_date &quot;下次检定日期&quot;
varchar certificate_no &quot;检定证书号&quot;
tinyint archive_status &quot;档案状态&quot;
bigint meter_id FK &quot;水表ID&quot;
bigint tenant_id &quot;租户ID&quot;
varchar creator &quot;创建者&quot;
datetime create_time &quot;创建时间&quot;
varchar updater &quot;更新者&quot;
datetime update_time &quot;更新时间&quot;
tinyint deleted &quot;是否删除&quot;
}
WATER_METER_WORKORDER {
bigint id PK &quot;主键ID&quot;
varchar workorder_code UK &quot;工单编号&quot;
varchar workorder_type &quot;工单类型&quot;
varchar workorder_status &quot;工单状态&quot;
date apply_date &quot;申请日期&quot;
date plan_date &quot;计划执行日期&quot;
date execute_date &quot;实际执行日期&quot;
varchar applicant_id &quot;申请人ID&quot;
varchar executor_id &quot;执行人ID&quot;
varchar reason &quot;申请原因&quot;
varchar result &quot;执行结果&quot;
bigint meter_id FK &quot;水表ID&quot;
bigint customer_id FK &quot;客户ID&quot;
bigint tenant_id &quot;租户ID&quot;
varchar creator &quot;创建者&quot;
datetime create_time &quot;创建时间&quot;
varchar updater &quot;更新者&quot;
datetime update_time &quot;更新时间&quot;
tinyint deleted &quot;是否删除&quot;
}
WATER_METER_STOCK {
bigint id PK &quot;主键ID&quot;
varchar stock_code UK &quot;库存编号&quot;
varchar warehouse_code &quot;仓库编码&quot;
varchar warehouse_name &quot;仓库名称&quot;
int stock_quantity &quot;库存数量&quot;
int min_stock &quot;最小库存&quot;
int max_stock &quot;最大库存&quot;
varchar meter_model &quot;水表型号&quot;
varchar meter_caliber &quot;水表口径&quot;
tinyint stock_status &quot;库存状态&quot;
bigint tenant_id &quot;租户ID&quot;
varchar creator &quot;创建者&quot;
datetime create_time &quot;创建时间&quot;
varchar updater &quot;更新者&quot;
datetime update_time &quot;更新时间&quot;
tinyint deleted &quot;是否删除&quot;
}
WATER_METER ||--|| WATER_METER_ARCHIVE : &quot;对应&quot;
WATER_METER ||--o{ WATER_METER_WORKORDER : &quot;产生&quot;</code></pre>
<h5 data-number="4.0.0.0.16" id="系统管理模块er图"><span
class="header-section-number">4.0.0.0.16</span> 4. 系统管理模块ER图</h5>
<pre class="mermaid"><code>erDiagram
SYSTEM_TENANT {
bigint id PK &quot;主键ID&quot;
varchar tenant_name &quot;租户名称&quot;
varchar tenant_code UK &quot;租户编码&quot;
varchar contact_name &quot;联系人&quot;
varchar contact_phone &quot;联系电话&quot;
varchar contact_email &quot;联系邮箱&quot;
tinyint tenant_status &quot;租户状态&quot;
datetime expire_time &quot;过期时间&quot;
varchar domain &quot;域名&quot;
varchar package_name &quot;套餐名称&quot;
int user_count &quot;用户数量&quot;
varchar creator &quot;创建者&quot;
datetime create_time &quot;创建时间&quot;
varchar updater &quot;更新者&quot;
datetime update_time &quot;更新时间&quot;
tinyint deleted &quot;是否删除&quot;
}
SYSTEM_USERS {
bigint id PK &quot;主键ID&quot;
varchar username UK &quot;用户名&quot;
varchar password &quot;密码&quot;
varchar nickname &quot;昵称&quot;
varchar remark &quot;备注&quot;
varchar dept_id &quot;部门ID&quot;
varchar post_ids &quot;岗位ID列表&quot;
varchar email &quot;邮箱&quot;
varchar mobile &quot;手机号&quot;
tinyint sex &quot;性别&quot;
varchar avatar &quot;头像&quot;
tinyint status &quot;状态&quot;
datetime login_date &quot;最后登录时间&quot;
varchar login_ip &quot;最后登录IP&quot;
bigint tenant_id &quot;租户ID&quot;
varchar creator &quot;创建者&quot;
datetime create_time &quot;创建时间&quot;
varchar updater &quot;更新者&quot;
datetime update_time &quot;更新时间&quot;
tinyint deleted &quot;是否删除&quot;
}
SYSTEM_TENANT ||--o{ SYSTEM_USERS : &quot;包含&quot;</code></pre>
<h5 data-number="4.0.0.0.17" id="四核心数据表设计"><span
class="header-section-number">4.0.0.0.17</span> 四、核心数据表设计</h5>
<h5 data-number="4.0.0.0.18" id="表务管理模块er图-1"><span
class="header-section-number">4.0.0.0.18</span> 1. 表务管理模块ER图</h5>
<pre class="mermaid"><code>erDiagram
WATER_METER_WORKORDER {
bigint id PK &quot;主键ID&quot;
varchar workorder_code UK &quot;工单编号&quot;
varchar workorder_type &quot;工单类型&quot;
varchar workorder_status &quot;工单状态&quot;
date apply_date &quot;申请日期&quot;
date plan_date &quot;计划执行日期&quot;
date execute_date &quot;实际执行日期&quot;
varchar applicant_id &quot;申请人ID&quot;
varchar executor_id &quot;执行人ID&quot;
varchar reason &quot;申请原因&quot;
varchar result &quot;执行结果&quot;
bigint meter_id FK &quot;水表ID&quot;
bigint customer_id FK &quot;客户ID&quot;
bigint tenant_id &quot;租户ID&quot;
}
WATER_METER_STOCK {
bigint id PK &quot;主键ID&quot;
varchar stock_code UK &quot;库存编号&quot;
varchar meter_brand &quot;水表品牌&quot;
varchar meter_model &quot;水表型号&quot;
varchar meter_caliber &quot;水表口径&quot;
int stock_quantity &quot;库存数量&quot;
int min_stock &quot;最小库存&quot;
decimal unit_price &quot;单价&quot;
varchar warehouse_location &quot;仓库位置&quot;
tinyint stock_status &quot;库存状态&quot;
bigint tenant_id &quot;租户ID&quot;
}
WATER_METER_INVENTORY {
bigint id PK &quot;主键ID&quot;
varchar inventory_code UK &quot;出入库编号&quot;
varchar inventory_type &quot;出入库类型&quot;
int quantity &quot;数量&quot;
decimal unit_price &quot;单价&quot;
decimal total_amount &quot;总金额&quot;
varchar operator_id &quot;操作员ID&quot;
datetime operation_time &quot;操作时间&quot;
varchar remark &quot;备注&quot;
bigint stock_id FK &quot;库存ID&quot;
bigint workorder_id FK &quot;工单ID&quot;
bigint tenant_id &quot;租户ID&quot;
}
WATER_METER ||--o{ WATER_METER_WORKORDER : &quot;生成&quot;
WATER_METER_STOCK ||--o{ WATER_METER_INVENTORY : &quot;出入库&quot;
WATER_METER_WORKORDER ||--o{ WATER_METER_INVENTORY : &quot;关联&quot;</code></pre>
<h5 data-number="4.0.0.0.19" id="系统管理模块er图-1"><span
class="header-section-number">4.0.0.0.19</span> 2. 系统管理模块ER图</h5>
<pre class="mermaid"><code>erDiagram
WATER_DICT_TYPE {
bigint id PK &quot;主键ID&quot;
varchar dict_name &quot;字典名称&quot;
varchar dict_type UK &quot;字典类型&quot;
varchar remark &quot;备注&quot;
tinyint status &quot;状态&quot;
bigint tenant_id &quot;租户ID&quot;
}
WATER_DICT_DATA {
bigint id PK &quot;主键ID&quot;
varchar dict_type &quot;字典类型&quot;
varchar dict_label &quot;字典标签&quot;
varchar dict_value &quot;字典键值&quot;
int dict_sort &quot;字典排序&quot;
varchar color_type &quot;颜色类型&quot;
varchar css_class &quot;CSS类名&quot;
varchar remark &quot;备注&quot;
tinyint status &quot;状态&quot;
bigint tenant_id &quot;租户ID&quot;
}
WATER_CONFIG {
bigint id PK &quot;主键ID&quot;
varchar config_name &quot;参数名称&quot;
varchar config_key UK &quot;参数键名&quot;
varchar config_value &quot;参数键值&quot;
varchar config_type &quot;系统内置&quot;
varchar remark &quot;备注&quot;
bigint tenant_id &quot;租户ID&quot;
}
WATER_PRICE_CONFIG {
bigint id PK &quot;主键ID&quot;
varchar price_name &quot;水价名称&quot;
varchar customer_type &quot;客户类型&quot;
varchar price_type &quot;价格类型&quot;
decimal base_price &quot;基础价格&quot;
decimal sewage_price &quot;污水处理费&quot;
decimal garbage_price &quot;垃圾处理费&quot;
date effective_date &quot;生效日期&quot;
date expire_date &quot;失效日期&quot;
tinyint status &quot;状态&quot;
bigint tenant_id &quot;租户ID&quot;
}
WATER_DICT_TYPE ||--o{ WATER_DICT_DATA : &quot;包含&quot;</code></pre>
<h5 data-number="4.0.0.0.20" id="五完整ddl语句"><span
class="header-section-number">4.0.0.0.20</span> 五、完整DDL语句</h5>
<h5 data-number="4.0.0.0.21" id="客户管理相关表ddl"><span
class="header-section-number">4.0.0.0.21</span> 1.
客户管理相关表DDL</h5>
<h5 data-number="4.0.0.0.22" id="客户基本信息表"><span
class="header-section-number">4.0.0.0.22</span> 1.1 客户基本信息表</h5>
<div class="sourceCode" id="cb47"><pre
class="sourceCode sql"><code class="sourceCode sql"><span id="cb47-1"><a href="#cb47-1" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">TABLE</span> `water_customer` (</span>
<span id="cb47-2"><a href="#cb47-2" aria-hidden="true" tabindex="-1"></a> `id` bigint <span class="kw">NOT</span> <span class="kw">NULL</span> AUTO_INCREMENT <span class="kw">COMMENT</span> <span class="st">&#39;主键ID&#39;</span>,</span>
<span id="cb47-3"><a href="#cb47-3" aria-hidden="true" tabindex="-1"></a> `customer_code` <span class="dt">varchar</span>(<span class="dv">32</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;客户编号&#39;</span>,</span>
<span id="cb47-4"><a href="#cb47-4" aria-hidden="true" tabindex="-1"></a> `customer_name` <span class="dt">varchar</span>(<span class="dv">100</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;客户名称&#39;</span>,</span>
<span id="cb47-5"><a href="#cb47-5" aria-hidden="true" tabindex="-1"></a> `customer_type` <span class="dt">varchar</span>(<span class="dv">20</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;客户类型(居民:RESIDENT,非居民:NON_RESIDENT,工业:INDUSTRIAL,行政:ADMINISTRATIVE)&#39;</span>,</span>
<span id="cb47-6"><a href="#cb47-6" aria-hidden="true" tabindex="-1"></a> `id_type` <span class="dt">varchar</span>(<span class="dv">20</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;证件类型(身份证:ID_CARD,营业执照:BUSINESS_LICENSE)&#39;</span>,</span>
<span id="cb47-7"><a href="#cb47-7" aria-hidden="true" tabindex="-1"></a> `id_number` <span class="dt">varchar</span>(<span class="dv">30</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;证件号码&#39;</span>,</span>
<span id="cb47-8"><a href="#cb47-8" aria-hidden="true" tabindex="-1"></a> `phone` <span class="dt">varchar</span>(<span class="dv">20</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;联系电话&#39;</span>,</span>
<span id="cb47-9"><a href="#cb47-9" aria-hidden="true" tabindex="-1"></a> `mobile` <span class="dt">varchar</span>(<span class="dv">20</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;手机号码&#39;</span>,</span>
<span id="cb47-10"><a href="#cb47-10" aria-hidden="true" tabindex="-1"></a> `email` <span class="dt">varchar</span>(<span class="dv">100</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;邮箱&#39;</span>,</span>
<span id="cb47-11"><a href="#cb47-11" aria-hidden="true" tabindex="-1"></a> `address` <span class="dt">varchar</span>(<span class="dv">500</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;详细地址&#39;</span>,</span>
<span id="cb47-12"><a href="#cb47-12" aria-hidden="true" tabindex="-1"></a> `area_code` <span class="dt">varchar</span>(<span class="dv">20</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;行政区划代码&#39;</span>,</span>
<span id="cb47-13"><a href="#cb47-13" aria-hidden="true" tabindex="-1"></a> `postal_code` <span class="dt">varchar</span>(<span class="dv">10</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;邮政编码&#39;</span>,</span>
<span id="cb47-14"><a href="#cb47-14" aria-hidden="true" tabindex="-1"></a> `contact_person` <span class="dt">varchar</span>(<span class="dv">50</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;联系人&#39;</span>,</span>
<span id="cb47-15"><a href="#cb47-15" aria-hidden="true" tabindex="-1"></a> `bank_account` <span class="dt">varchar</span>(<span class="dv">50</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;银行账户&#39;</span>,</span>
<span id="cb47-16"><a href="#cb47-16" aria-hidden="true" tabindex="-1"></a> `bank_name` <span class="dt">varchar</span>(<span class="dv">100</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;开户银行&#39;</span>,</span>
<span id="cb47-17"><a href="#cb47-17" aria-hidden="true" tabindex="-1"></a> `remark` <span class="dt">varchar</span>(<span class="dv">500</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;备注信息&#39;</span>,</span>
<span id="cb47-18"><a href="#cb47-18" aria-hidden="true" tabindex="-1"></a> `status` tinyint <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="st">&#39;1&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;状态(0:停用,1:正常,2:欠费,3:销户)&#39;</span>,</span>
<span id="cb47-19"><a href="#cb47-19" aria-hidden="true" tabindex="-1"></a> `tenant_id` bigint <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="st">&#39;0&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;租户ID&#39;</span>,</span>
<span id="cb47-20"><a href="#cb47-20" aria-hidden="true" tabindex="-1"></a> `creator` <span class="dt">varchar</span>(<span class="dv">64</span>) <span class="kw">DEFAULT</span> <span class="st">&#39;&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;创建者&#39;</span>,</span>
<span id="cb47-21"><a href="#cb47-21" aria-hidden="true" tabindex="-1"></a> `create_time` datetime <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="fu">CURRENT_TIMESTAMP</span> <span class="kw">COMMENT</span> <span class="st">&#39;创建时间&#39;</span>,</span>
<span id="cb47-22"><a href="#cb47-22" aria-hidden="true" tabindex="-1"></a> `updater` <span class="dt">varchar</span>(<span class="dv">64</span>) <span class="kw">DEFAULT</span> <span class="st">&#39;&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;更新者&#39;</span>,</span>
<span id="cb47-23"><a href="#cb47-23" aria-hidden="true" tabindex="-1"></a> `update_time` datetime <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="fu">CURRENT_TIMESTAMP</span> <span class="kw">ON</span> <span class="kw">UPDATE</span> <span class="fu">CURRENT_TIMESTAMP</span> <span class="kw">COMMENT</span> <span class="st">&#39;更新时间&#39;</span>,</span>
<span id="cb47-24"><a href="#cb47-24" aria-hidden="true" tabindex="-1"></a> `deleted` bit(<span class="dv">1</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> b<span class="st">&#39;0&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;是否删除&#39;</span>,</span>
<span id="cb47-25"><a href="#cb47-25" aria-hidden="true" tabindex="-1"></a> <span class="kw">PRIMARY</span> <span class="kw">KEY</span> (`id`),</span>
<span id="cb47-26"><a href="#cb47-26" aria-hidden="true" tabindex="-1"></a> <span class="kw">UNIQUE</span> <span class="kw">KEY</span> `uk_customer_code` (`customer_code`, `tenant_id`),</span>
<span id="cb47-27"><a href="#cb47-27" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_customer_name` (`customer_name`),</span>
<span id="cb47-28"><a href="#cb47-28" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_phone` (`phone`),</span>
<span id="cb47-29"><a href="#cb47-29" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_id_number` (`id_number`),</span>
<span id="cb47-30"><a href="#cb47-30" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_tenant_customer_type` (`tenant_id`, `customer_type`),</span>
<span id="cb47-31"><a href="#cb47-31" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_status` (`status`),</span>
<span id="cb47-32"><a href="#cb47-32" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_create_time` (`create_time`)</span>
<span id="cb47-33"><a href="#cb47-33" aria-hidden="true" tabindex="-1"></a>) ENGINE<span class="op">=</span>InnoDB <span class="kw">DEFAULT</span> CHARSET<span class="op">=</span>utf8mb4 <span class="kw">COMMENT</span><span class="op">=</span><span class="st">&#39;客户基本信息表&#39;</span>;</span></code></pre></div>
<h5 data-number="4.0.0.0.23" id="水表信息表"><span
class="header-section-number">4.0.0.0.23</span> 1.2 水表信息表</h5>
<div class="sourceCode" id="cb48"><pre
class="sourceCode sql"><code class="sourceCode sql"><span id="cb48-1"><a href="#cb48-1" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">TABLE</span> `water_meter` (</span>
<span id="cb48-2"><a href="#cb48-2" aria-hidden="true" tabindex="-1"></a> `id` bigint <span class="kw">NOT</span> <span class="kw">NULL</span> AUTO_INCREMENT <span class="kw">COMMENT</span> <span class="st">&#39;主键ID&#39;</span>,</span>
<span id="cb48-3"><a href="#cb48-3" aria-hidden="true" tabindex="-1"></a> `meter_code` <span class="dt">varchar</span>(<span class="dv">32</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;水表编号&#39;</span>,</span>
<span id="cb48-4"><a href="#cb48-4" aria-hidden="true" tabindex="-1"></a> `meter_no` <span class="dt">varchar</span>(<span class="dv">30</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;水表表号&#39;</span>,</span>
<span id="cb48-5"><a href="#cb48-5" aria-hidden="true" tabindex="-1"></a> `meter_type` <span class="dt">varchar</span>(<span class="dv">20</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;水表类型(机械:MECHANICAL,智能:SMART,远传:REMOTE)&#39;</span>,</span>
<span id="cb48-6"><a href="#cb48-6" aria-hidden="true" tabindex="-1"></a> `meter_model` <span class="dt">varchar</span>(<span class="dv">50</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;水表型号&#39;</span>,</span>
<span id="cb48-7"><a href="#cb48-7" aria-hidden="true" tabindex="-1"></a> `meter_caliber` <span class="dt">varchar</span>(<span class="dv">10</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;水表口径(15mm,20mm,25mm等)&#39;</span>,</span>
<span id="cb48-8"><a href="#cb48-8" aria-hidden="true" tabindex="-1"></a> `manufacturer` <span class="dt">varchar</span>(<span class="dv">100</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;生产厂家&#39;</span>,</span>
<span id="cb48-9"><a href="#cb48-9" aria-hidden="true" tabindex="-1"></a> `production_date` <span class="dt">date</span> <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;生产日期&#39;</span>,</span>
<span id="cb48-10"><a href="#cb48-10" aria-hidden="true" tabindex="-1"></a> `install_date` <span class="dt">date</span> <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;安装日期&#39;</span>,</span>
<span id="cb48-11"><a href="#cb48-11" aria-hidden="true" tabindex="-1"></a> `install_position` <span class="dt">varchar</span>(<span class="dv">500</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;安装位置&#39;</span>,</span>
<span id="cb48-12"><a href="#cb48-12" aria-hidden="true" tabindex="-1"></a> `longitude` <span class="dt">decimal</span>(<span class="dv">10</span>,<span class="dv">7</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;经度&#39;</span>,</span>
<span id="cb48-13"><a href="#cb48-13" aria-hidden="true" tabindex="-1"></a> `latitude` <span class="dt">decimal</span>(<span class="dv">10</span>,<span class="dv">7</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;纬度&#39;</span>,</span>
<span id="cb48-14"><a href="#cb48-14" aria-hidden="true" tabindex="-1"></a> `initial_reading` <span class="dt">decimal</span>(<span class="dv">10</span>,<span class="dv">2</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="st">&#39;0.00&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;初始读数&#39;</span>,</span>
<span id="cb48-15"><a href="#cb48-15" aria-hidden="true" tabindex="-1"></a> `current_reading` <span class="dt">decimal</span>(<span class="dv">10</span>,<span class="dv">2</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="st">&#39;0.00&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;当前读数&#39;</span>,</span>
<span id="cb48-16"><a href="#cb48-16" aria-hidden="true" tabindex="-1"></a> `reading_cycle` <span class="dt">varchar</span>(<span class="dv">20</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="st">&#39;MONTHLY&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;抄表周期(月度:MONTHLY,双月:BIMONTHLY,季度:QUARTERLY)&#39;</span>,</span>
<span id="cb48-17"><a href="#cb48-17" aria-hidden="true" tabindex="-1"></a> `book_code` <span class="dt">varchar</span>(<span class="dv">32</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;册本编号&#39;</span>,</span>
<span id="cb48-18"><a href="#cb48-18" aria-hidden="true" tabindex="-1"></a> `reading_route` <span class="dt">varchar</span>(<span class="dv">100</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;抄表路线&#39;</span>,</span>
<span id="cb48-19"><a href="#cb48-19" aria-hidden="true" tabindex="-1"></a> `meter_status` tinyint <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="st">&#39;1&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;水表状态(0:停用,1:正常,2:故障,3:拆除)&#39;</span>,</span>
<span id="cb48-20"><a href="#cb48-20" aria-hidden="true" tabindex="-1"></a> `customer_id` bigint <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;客户ID&#39;</span>,</span>
<span id="cb48-21"><a href="#cb48-21" aria-hidden="true" tabindex="-1"></a> `tenant_id` bigint <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="st">&#39;0&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;租户ID&#39;</span>,</span>
<span id="cb48-22"><a href="#cb48-22" aria-hidden="true" tabindex="-1"></a> `creator` <span class="dt">varchar</span>(<span class="dv">64</span>) <span class="kw">DEFAULT</span> <span class="st">&#39;&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;创建者&#39;</span>,</span>
<span id="cb48-23"><a href="#cb48-23" aria-hidden="true" tabindex="-1"></a> `create_time` datetime <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="fu">CURRENT_TIMESTAMP</span> <span class="kw">COMMENT</span> <span class="st">&#39;创建时间&#39;</span>,</span>
<span id="cb48-24"><a href="#cb48-24" aria-hidden="true" tabindex="-1"></a> `updater` <span class="dt">varchar</span>(<span class="dv">64</span>) <span class="kw">DEFAULT</span> <span class="st">&#39;&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;更新者&#39;</span>,</span>
<span id="cb48-25"><a href="#cb48-25" aria-hidden="true" tabindex="-1"></a> `update_time` datetime <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="fu">CURRENT_TIMESTAMP</span> <span class="kw">ON</span> <span class="kw">UPDATE</span> <span class="fu">CURRENT_TIMESTAMP</span> <span class="kw">COMMENT</span> <span class="st">&#39;更新时间&#39;</span>,</span>
<span id="cb48-26"><a href="#cb48-26" aria-hidden="true" tabindex="-1"></a> `deleted` bit(<span class="dv">1</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> b<span class="st">&#39;0&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;是否删除&#39;</span>,</span>
<span id="cb48-27"><a href="#cb48-27" aria-hidden="true" tabindex="-1"></a> <span class="kw">PRIMARY</span> <span class="kw">KEY</span> (`id`),</span>
<span id="cb48-28"><a href="#cb48-28" aria-hidden="true" tabindex="-1"></a> <span class="kw">UNIQUE</span> <span class="kw">KEY</span> `uk_meter_code` (`meter_code`, `tenant_id`),</span>
<span id="cb48-29"><a href="#cb48-29" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_meter_no` (`meter_no`),</span>
<span id="cb48-30"><a href="#cb48-30" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_customer_id` (`customer_id`),</span>
<span id="cb48-31"><a href="#cb48-31" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_book_code` (`book_code`),</span>
<span id="cb48-32"><a href="#cb48-32" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_meter_type` (`meter_type`),</span>
<span id="cb48-33"><a href="#cb48-33" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_meter_status` (`meter_status`),</span>
<span id="cb48-34"><a href="#cb48-34" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_tenant_status` (`tenant_id`, `meter_status`),</span>
<span id="cb48-35"><a href="#cb48-35" aria-hidden="true" tabindex="-1"></a> <span class="kw">CONSTRAINT</span> `fk_meter_customer` <span class="kw">FOREIGN</span> <span class="kw">KEY</span> (`customer_id`) <span class="kw">REFERENCES</span> `water_customer` (`id`)</span>
<span id="cb48-36"><a href="#cb48-36" aria-hidden="true" tabindex="-1"></a>) ENGINE<span class="op">=</span>InnoDB <span class="kw">DEFAULT</span> CHARSET<span class="op">=</span>utf8mb4 <span class="kw">COMMENT</span><span class="op">=</span><span class="st">&#39;水表信息表&#39;</span>;</span></code></pre></div>
<h5 data-number="4.0.0.0.24" id="表务工单管理表ddl"><span
class="header-section-number">4.0.0.0.24</span> 3.
表务工单管理表DDL</h5>
<h5 data-number="4.0.0.0.25" id="表务工单表"><span
class="header-section-number">4.0.0.0.25</span> 3.1 表务工单表</h5>
<div class="sourceCode" id="cb49"><pre
class="sourceCode sql"><code class="sourceCode sql"><span id="cb49-1"><a href="#cb49-1" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">TABLE</span> `water_meter_workorder` (</span>
<span id="cb49-2"><a href="#cb49-2" aria-hidden="true" tabindex="-1"></a> `id` bigint <span class="kw">NOT</span> <span class="kw">NULL</span> AUTO_INCREMENT <span class="kw">COMMENT</span> <span class="st">&#39;主键ID&#39;</span>,</span>
<span id="cb49-3"><a href="#cb49-3" aria-hidden="true" tabindex="-1"></a> `workorder_code` <span class="dt">varchar</span>(<span class="dv">32</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;工单编号&#39;</span>,</span>
<span id="cb49-4"><a href="#cb49-4" aria-hidden="true" tabindex="-1"></a> `workorder_type` <span class="dt">varchar</span>(<span class="dv">20</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;工单类型(换表:CHANGE,移表:MOVE,拆表:REMOVE,装表:INSTALL,维修:REPAIR)&#39;</span>,</span>
<span id="cb49-5"><a href="#cb49-5" aria-hidden="true" tabindex="-1"></a> `workorder_status` <span class="dt">varchar</span>(<span class="dv">20</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="st">&#39;PENDING&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;工单状态(待处理:PENDING,执行中:PROCESSING,已完成:COMPLETED,已取消:CANCELLED)&#39;</span>,</span>
<span id="cb49-6"><a href="#cb49-6" aria-hidden="true" tabindex="-1"></a> `apply_date` <span class="dt">date</span> <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;申请日期&#39;</span>,</span>
<span id="cb49-7"><a href="#cb49-7" aria-hidden="true" tabindex="-1"></a> `plan_date` <span class="dt">date</span> <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;计划执行日期&#39;</span>,</span>
<span id="cb49-8"><a href="#cb49-8" aria-hidden="true" tabindex="-1"></a> `execute_date` <span class="dt">date</span> <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;实际执行日期&#39;</span>,</span>
<span id="cb49-9"><a href="#cb49-9" aria-hidden="true" tabindex="-1"></a> `applicant_id` <span class="dt">varchar</span>(<span class="dv">20</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;申请人ID&#39;</span>,</span>
<span id="cb49-10"><a href="#cb49-10" aria-hidden="true" tabindex="-1"></a> `applicant_name` <span class="dt">varchar</span>(<span class="dv">50</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;申请人姓名&#39;</span>,</span>
<span id="cb49-11"><a href="#cb49-11" aria-hidden="true" tabindex="-1"></a> `executor_id` <span class="dt">varchar</span>(<span class="dv">20</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;执行人ID&#39;</span>,</span>
<span id="cb49-12"><a href="#cb49-12" aria-hidden="true" tabindex="-1"></a> `executor_name` <span class="dt">varchar</span>(<span class="dv">50</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;执行人姓名&#39;</span>,</span>
<span id="cb49-13"><a href="#cb49-13" aria-hidden="true" tabindex="-1"></a> `reason` <span class="dt">varchar</span>(<span class="dv">500</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;申请原因&#39;</span>,</span>
<span id="cb49-14"><a href="#cb49-14" aria-hidden="true" tabindex="-1"></a> `result` <span class="dt">varchar</span>(<span class="dv">500</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;执行结果&#39;</span>,</span>
<span id="cb49-15"><a href="#cb49-15" aria-hidden="true" tabindex="-1"></a> `old_meter_no` <span class="dt">varchar</span>(<span class="dv">30</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;旧水表表号&#39;</span>,</span>
<span id="cb49-16"><a href="#cb49-16" aria-hidden="true" tabindex="-1"></a> `new_meter_no` <span class="dt">varchar</span>(<span class="dv">30</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;新水表表号&#39;</span>,</span>
<span id="cb49-17"><a href="#cb49-17" aria-hidden="true" tabindex="-1"></a> `old_reading` <span class="dt">decimal</span>(<span class="dv">10</span>,<span class="dv">2</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;旧表读数&#39;</span>,</span>
<span id="cb49-18"><a href="#cb49-18" aria-hidden="true" tabindex="-1"></a> `new_reading` <span class="dt">decimal</span>(<span class="dv">10</span>,<span class="dv">2</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;新表读数&#39;</span>,</span>
<span id="cb49-19"><a href="#cb49-19" aria-hidden="true" tabindex="-1"></a> `cost_amount` <span class="dt">decimal</span>(<span class="dv">10</span>,<span class="dv">2</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;费用金额&#39;</span>,</span>
<span id="cb49-20"><a href="#cb49-20" aria-hidden="true" tabindex="-1"></a> `remark` <span class="dt">varchar</span>(<span class="dv">500</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;备注&#39;</span>,</span>
<span id="cb49-21"><a href="#cb49-21" aria-hidden="true" tabindex="-1"></a> `meter_id` bigint <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;水表ID&#39;</span>,</span>
<span id="cb49-22"><a href="#cb49-22" aria-hidden="true" tabindex="-1"></a> `customer_id` bigint <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;客户ID&#39;</span>,</span>
<span id="cb49-23"><a href="#cb49-23" aria-hidden="true" tabindex="-1"></a> `tenant_id` bigint <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="st">&#39;0&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;租户ID&#39;</span>,</span>
<span id="cb49-24"><a href="#cb49-24" aria-hidden="true" tabindex="-1"></a> `creator` <span class="dt">varchar</span>(<span class="dv">64</span>) <span class="kw">DEFAULT</span> <span class="st">&#39;&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;创建者&#39;</span>,</span>
<span id="cb49-25"><a href="#cb49-25" aria-hidden="true" tabindex="-1"></a> `create_time` datetime <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="fu">CURRENT_TIMESTAMP</span> <span class="kw">COMMENT</span> <span class="st">&#39;创建时间&#39;</span>,</span>
<span id="cb49-26"><a href="#cb49-26" aria-hidden="true" tabindex="-1"></a> `updater` <span class="dt">varchar</span>(<span class="dv">64</span>) <span class="kw">DEFAULT</span> <span class="st">&#39;&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;更新者&#39;</span>,</span>
<span id="cb49-27"><a href="#cb49-27" aria-hidden="true" tabindex="-1"></a> `update_time` datetime <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="fu">CURRENT_TIMESTAMP</span> <span class="kw">ON</span> <span class="kw">UPDATE</span> <span class="fu">CURRENT_TIMESTAMP</span> <span class="kw">COMMENT</span> <span class="st">&#39;更新时间&#39;</span>,</span>
<span id="cb49-28"><a href="#cb49-28" aria-hidden="true" tabindex="-1"></a> `deleted` bit(<span class="dv">1</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> b<span class="st">&#39;0&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;是否删除&#39;</span>,</span>
<span id="cb49-29"><a href="#cb49-29" aria-hidden="true" tabindex="-1"></a> <span class="kw">PRIMARY</span> <span class="kw">KEY</span> (`id`),</span>
<span id="cb49-30"><a href="#cb49-30" aria-hidden="true" tabindex="-1"></a> <span class="kw">UNIQUE</span> <span class="kw">KEY</span> `uk_workorder_code` (`workorder_code`, `tenant_id`),</span>
<span id="cb49-31"><a href="#cb49-31" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_workorder_type` (`workorder_type`),</span>
<span id="cb49-32"><a href="#cb49-32" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_workorder_status` (`workorder_status`),</span>
<span id="cb49-33"><a href="#cb49-33" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_apply_date` (`apply_date`),</span>
<span id="cb49-34"><a href="#cb49-34" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_meter_id` (`meter_id`),</span>
<span id="cb49-35"><a href="#cb49-35" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_customer_id` (`customer_id`),</span>
<span id="cb49-36"><a href="#cb49-36" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_tenant_type_status` (`tenant_id`, `workorder_type`, `workorder_status`),</span>
<span id="cb49-37"><a href="#cb49-37" aria-hidden="true" tabindex="-1"></a> <span class="kw">CONSTRAINT</span> `fk_workorder_meter` <span class="kw">FOREIGN</span> <span class="kw">KEY</span> (`meter_id`) <span class="kw">REFERENCES</span> `water_meter` (`id`),</span>
<span id="cb49-38"><a href="#cb49-38" aria-hidden="true" tabindex="-1"></a> <span class="kw">CONSTRAINT</span> `fk_workorder_customer` <span class="kw">FOREIGN</span> <span class="kw">KEY</span> (`customer_id`) <span class="kw">REFERENCES</span> `water_customer` (`id`)</span>
<span id="cb49-39"><a href="#cb49-39" aria-hidden="true" tabindex="-1"></a>) ENGINE<span class="op">=</span>InnoDB <span class="kw">DEFAULT</span> CHARSET<span class="op">=</span>utf8mb4 <span class="kw">COMMENT</span><span class="op">=</span><span class="st">&#39;表务工单表&#39;</span>;</span></code></pre></div>
<h5 data-number="4.0.0.0.26" id="水表库存管理表"><span
class="header-section-number">4.0.0.0.26</span> 3.2 水表库存管理表</h5>
<div class="sourceCode" id="cb50"><pre
class="sourceCode sql"><code class="sourceCode sql"><span id="cb50-1"><a href="#cb50-1" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">TABLE</span> `water_meter_stock` (</span>
<span id="cb50-2"><a href="#cb50-2" aria-hidden="true" tabindex="-1"></a> `id` bigint <span class="kw">NOT</span> <span class="kw">NULL</span> AUTO_INCREMENT <span class="kw">COMMENT</span> <span class="st">&#39;主键ID&#39;</span>,</span>
<span id="cb50-3"><a href="#cb50-3" aria-hidden="true" tabindex="-1"></a> `stock_code` <span class="dt">varchar</span>(<span class="dv">32</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;库存编号&#39;</span>,</span>
<span id="cb50-4"><a href="#cb50-4" aria-hidden="true" tabindex="-1"></a> `meter_brand` <span class="dt">varchar</span>(<span class="dv">50</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;水表品牌&#39;</span>,</span>
<span id="cb50-5"><a href="#cb50-5" aria-hidden="true" tabindex="-1"></a> `meter_model` <span class="dt">varchar</span>(<span class="dv">50</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;水表型号&#39;</span>,</span>
<span id="cb50-6"><a href="#cb50-6" aria-hidden="true" tabindex="-1"></a> `meter_caliber` <span class="dt">varchar</span>(<span class="dv">10</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;水表口径&#39;</span>,</span>
<span id="cb50-7"><a href="#cb50-7" aria-hidden="true" tabindex="-1"></a> `meter_type` <span class="dt">varchar</span>(<span class="dv">20</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;水表类型&#39;</span>,</span>
<span id="cb50-8"><a href="#cb50-8" aria-hidden="true" tabindex="-1"></a> `stock_quantity` <span class="dt">int</span> <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="st">&#39;0&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;库存数量&#39;</span>,</span>
<span id="cb50-9"><a href="#cb50-9" aria-hidden="true" tabindex="-1"></a> `min_stock` <span class="dt">int</span> <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="st">&#39;0&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;最小库存&#39;</span>,</span>
<span id="cb50-10"><a href="#cb50-10" aria-hidden="true" tabindex="-1"></a> `max_stock` <span class="dt">int</span> <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;最大库存&#39;</span>,</span>
<span id="cb50-11"><a href="#cb50-11" aria-hidden="true" tabindex="-1"></a> `unit_price` <span class="dt">decimal</span>(<span class="dv">10</span>,<span class="dv">2</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;单价&#39;</span>,</span>
<span id="cb50-12"><a href="#cb50-12" aria-hidden="true" tabindex="-1"></a> `warehouse_location` <span class="dt">varchar</span>(<span class="dv">100</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;仓库位置&#39;</span>,</span>
<span id="cb50-13"><a href="#cb50-13" aria-hidden="true" tabindex="-1"></a> `supplier` <span class="dt">varchar</span>(<span class="dv">100</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;供应商&#39;</span>,</span>
<span id="cb50-14"><a href="#cb50-14" aria-hidden="true" tabindex="-1"></a> `purchase_date` <span class="dt">date</span> <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;进货日期&#39;</span>,</span>
<span id="cb50-15"><a href="#cb50-15" aria-hidden="true" tabindex="-1"></a> `warranty_period` <span class="dt">int</span> <span class="kw">DEFAULT</span> <span class="st">&#39;24&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;质保期(月)&#39;</span>,</span>
<span id="cb50-16"><a href="#cb50-16" aria-hidden="true" tabindex="-1"></a> `stock_status` tinyint <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="st">&#39;1&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;库存状态(0:停用,1:正常,2:缺货,3:超储)&#39;</span>,</span>
<span id="cb50-17"><a href="#cb50-17" aria-hidden="true" tabindex="-1"></a> `remark` <span class="dt">varchar</span>(<span class="dv">500</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;备注&#39;</span>,</span>
<span id="cb50-18"><a href="#cb50-18" aria-hidden="true" tabindex="-1"></a> `tenant_id` bigint <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="st">&#39;0&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;租户ID&#39;</span>,</span>
<span id="cb50-19"><a href="#cb50-19" aria-hidden="true" tabindex="-1"></a> `creator` <span class="dt">varchar</span>(<span class="dv">64</span>) <span class="kw">DEFAULT</span> <span class="st">&#39;&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;创建者&#39;</span>,</span>
<span id="cb50-20"><a href="#cb50-20" aria-hidden="true" tabindex="-1"></a> `create_time` datetime <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="fu">CURRENT_TIMESTAMP</span> <span class="kw">COMMENT</span> <span class="st">&#39;创建时间&#39;</span>,</span>
<span id="cb50-21"><a href="#cb50-21" aria-hidden="true" tabindex="-1"></a> `updater` <span class="dt">varchar</span>(<span class="dv">64</span>) <span class="kw">DEFAULT</span> <span class="st">&#39;&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;更新者&#39;</span>,</span>
<span id="cb50-22"><a href="#cb50-22" aria-hidden="true" tabindex="-1"></a> `update_time` datetime <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="fu">CURRENT_TIMESTAMP</span> <span class="kw">ON</span> <span class="kw">UPDATE</span> <span class="fu">CURRENT_TIMESTAMP</span> <span class="kw">COMMENT</span> <span class="st">&#39;更新时间&#39;</span>,</span>
<span id="cb50-23"><a href="#cb50-23" aria-hidden="true" tabindex="-1"></a> `deleted` bit(<span class="dv">1</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> b<span class="st">&#39;0&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;是否删除&#39;</span>,</span>
<span id="cb50-24"><a href="#cb50-24" aria-hidden="true" tabindex="-1"></a> <span class="kw">PRIMARY</span> <span class="kw">KEY</span> (`id`),</span>
<span id="cb50-25"><a href="#cb50-25" aria-hidden="true" tabindex="-1"></a> <span class="kw">UNIQUE</span> <span class="kw">KEY</span> `uk_stock_code` (`stock_code`, `tenant_id`),</span>
<span id="cb50-26"><a href="#cb50-26" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_meter_brand` (`meter_brand`),</span>
<span id="cb50-27"><a href="#cb50-27" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_meter_model` (`meter_model`),</span>
<span id="cb50-28"><a href="#cb50-28" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_meter_caliber` (`meter_caliber`),</span>
<span id="cb50-29"><a href="#cb50-29" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_stock_status` (`stock_status`),</span>
<span id="cb50-30"><a href="#cb50-30" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_tenant_brand_model` (`tenant_id`, `meter_brand`, `meter_model`)</span>
<span id="cb50-31"><a href="#cb50-31" aria-hidden="true" tabindex="-1"></a>) ENGINE<span class="op">=</span>InnoDB <span class="kw">DEFAULT</span> CHARSET<span class="op">=</span>utf8mb4 <span class="kw">COMMENT</span><span class="op">=</span><span class="st">&#39;水表库存管理表&#39;</span>;</span></code></pre></div>
<h5 data-number="4.0.0.0.27" id="系统管理相关表ddl"><span
class="header-section-number">4.0.0.0.27</span> 4.
系统管理相关表DDL</h5>
<h5 data-number="4.0.0.0.28" id="数据字典类型表"><span
class="header-section-number">4.0.0.0.28</span> 4.1 数据字典类型表</h5>
<div class="sourceCode" id="cb51"><pre
class="sourceCode sql"><code class="sourceCode sql"><span id="cb51-1"><a href="#cb51-1" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">TABLE</span> `water_dict_type` (</span>
<span id="cb51-2"><a href="#cb51-2" aria-hidden="true" tabindex="-1"></a> `id` bigint <span class="kw">NOT</span> <span class="kw">NULL</span> AUTO_INCREMENT <span class="kw">COMMENT</span> <span class="st">&#39;主键ID&#39;</span>,</span>
<span id="cb51-3"><a href="#cb51-3" aria-hidden="true" tabindex="-1"></a> `dict_name` <span class="dt">varchar</span>(<span class="dv">100</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;字典名称&#39;</span>,</span>
<span id="cb51-4"><a href="#cb51-4" aria-hidden="true" tabindex="-1"></a> `dict_type` <span class="dt">varchar</span>(<span class="dv">100</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;字典类型&#39;</span>,</span>
<span id="cb51-5"><a href="#cb51-5" aria-hidden="true" tabindex="-1"></a> `remark` <span class="dt">varchar</span>(<span class="dv">500</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;备注&#39;</span>,</span>
<span id="cb51-6"><a href="#cb51-6" aria-hidden="true" tabindex="-1"></a> `status` tinyint <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="st">&#39;1&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;状态(0:停用,1:正常)&#39;</span>,</span>
<span id="cb51-7"><a href="#cb51-7" aria-hidden="true" tabindex="-1"></a> `tenant_id` bigint <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="st">&#39;0&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;租户ID&#39;</span>,</span>
<span id="cb51-8"><a href="#cb51-8" aria-hidden="true" tabindex="-1"></a> `creator` <span class="dt">varchar</span>(<span class="dv">64</span>) <span class="kw">DEFAULT</span> <span class="st">&#39;&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;创建者&#39;</span>,</span>
<span id="cb51-9"><a href="#cb51-9" aria-hidden="true" tabindex="-1"></a> `create_time` datetime <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="fu">CURRENT_TIMESTAMP</span> <span class="kw">COMMENT</span> <span class="st">&#39;创建时间&#39;</span>,</span>
<span id="cb51-10"><a href="#cb51-10" aria-hidden="true" tabindex="-1"></a> `updater` <span class="dt">varchar</span>(<span class="dv">64</span>) <span class="kw">DEFAULT</span> <span class="st">&#39;&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;更新者&#39;</span>,</span>
<span id="cb51-11"><a href="#cb51-11" aria-hidden="true" tabindex="-1"></a> `update_time` datetime <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="fu">CURRENT_TIMESTAMP</span> <span class="kw">ON</span> <span class="kw">UPDATE</span> <span class="fu">CURRENT_TIMESTAMP</span> <span class="kw">COMMENT</span> <span class="st">&#39;更新时间&#39;</span>,</span>
<span id="cb51-12"><a href="#cb51-12" aria-hidden="true" tabindex="-1"></a> `deleted` bit(<span class="dv">1</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> b<span class="st">&#39;0&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;是否删除&#39;</span>,</span>
<span id="cb51-13"><a href="#cb51-13" aria-hidden="true" tabindex="-1"></a> <span class="kw">PRIMARY</span> <span class="kw">KEY</span> (`id`),</span>
<span id="cb51-14"><a href="#cb51-14" aria-hidden="true" tabindex="-1"></a> <span class="kw">UNIQUE</span> <span class="kw">KEY</span> `uk_dict_type` (`dict_type`, `tenant_id`),</span>
<span id="cb51-15"><a href="#cb51-15" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_status` (`status`)</span>
<span id="cb51-16"><a href="#cb51-16" aria-hidden="true" tabindex="-1"></a>) ENGINE<span class="op">=</span>InnoDB <span class="kw">DEFAULT</span> CHARSET<span class="op">=</span>utf8mb4 <span class="kw">COMMENT</span><span class="op">=</span><span class="st">&#39;数据字典类型表&#39;</span>;</span></code></pre></div>
<h5 data-number="4.0.0.0.29" id="完整ddl语句列表"><span
class="header-section-number">4.0.0.0.29</span> 5. 完整DDL语句列表</h5>
<h5 data-number="4.0.0.0.30" id="客户基本信息表-opengauss版本"><span
class="header-section-number">4.0.0.0.30</span> 5.1 客户基本信息表
(OpenGauss版本)</h5>
<div class="sourceCode" id="cb52"><pre
class="sourceCode sql"><code class="sourceCode sql"><span id="cb52-1"><a href="#cb52-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- OpenGauss数据库DDL语句</span></span>
<span id="cb52-2"><a href="#cb52-2" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">TABLE</span> water_customer (</span>
<span id="cb52-3"><a href="#cb52-3" aria-hidden="true" tabindex="-1"></a> <span class="kw">id</span> SERIAL <span class="kw">PRIMARY</span> <span class="kw">KEY</span>,</span>
<span id="cb52-4"><a href="#cb52-4" aria-hidden="true" tabindex="-1"></a> customer_code <span class="dt">VARCHAR</span>(<span class="dv">32</span>) <span class="kw">NOT</span> <span class="kw">NULL</span>,</span>
<span id="cb52-5"><a href="#cb52-5" aria-hidden="true" tabindex="-1"></a> customer_name <span class="dt">VARCHAR</span>(<span class="dv">100</span>) <span class="kw">NOT</span> <span class="kw">NULL</span>,</span>
<span id="cb52-6"><a href="#cb52-6" aria-hidden="true" tabindex="-1"></a> customer_type <span class="dt">VARCHAR</span>(<span class="dv">20</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">CHECK</span> (customer_type <span class="kw">IN</span> (<span class="st">&#39;RESIDENT&#39;</span>,<span class="st">&#39;NON_RESIDENT&#39;</span>,<span class="st">&#39;INDUSTRIAL&#39;</span>,<span class="st">&#39;ADMINISTRATIVE&#39;</span>)),</span>
<span id="cb52-7"><a href="#cb52-7" aria-hidden="true" tabindex="-1"></a> id_type <span class="dt">VARCHAR</span>(<span class="dv">20</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">CHECK</span> (id_type <span class="kw">IN</span> (<span class="st">&#39;ID_CARD&#39;</span>,<span class="st">&#39;BUSINESS_LICENSE&#39;</span>) <span class="kw">OR</span> id_type <span class="kw">IS</span> <span class="kw">NULL</span>),</span>
<span id="cb52-8"><a href="#cb52-8" aria-hidden="true" tabindex="-1"></a> id_number <span class="dt">VARCHAR</span>(<span class="dv">30</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span>,</span>
<span id="cb52-9"><a href="#cb52-9" aria-hidden="true" tabindex="-1"></a> phone <span class="dt">VARCHAR</span>(<span class="dv">20</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span>,</span>
<span id="cb52-10"><a href="#cb52-10" aria-hidden="true" tabindex="-1"></a> mobile <span class="dt">VARCHAR</span>(<span class="dv">20</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span>,</span>
<span id="cb52-11"><a href="#cb52-11" aria-hidden="true" tabindex="-1"></a> email <span class="dt">VARCHAR</span>(<span class="dv">100</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span>,</span>
<span id="cb52-12"><a href="#cb52-12" aria-hidden="true" tabindex="-1"></a> address <span class="dt">VARCHAR</span>(<span class="dv">500</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span>,</span>
<span id="cb52-13"><a href="#cb52-13" aria-hidden="true" tabindex="-1"></a> area_code <span class="dt">VARCHAR</span>(<span class="dv">20</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span>,</span>
<span id="cb52-14"><a href="#cb52-14" aria-hidden="true" tabindex="-1"></a> postal_code <span class="dt">VARCHAR</span>(<span class="dv">10</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span>,</span>
<span id="cb52-15"><a href="#cb52-15" aria-hidden="true" tabindex="-1"></a> contact_person <span class="dt">VARCHAR</span>(<span class="dv">50</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span>,</span>
<span id="cb52-16"><a href="#cb52-16" aria-hidden="true" tabindex="-1"></a> bank_account <span class="dt">VARCHAR</span>(<span class="dv">50</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span>,</span>
<span id="cb52-17"><a href="#cb52-17" aria-hidden="true" tabindex="-1"></a> bank_name <span class="dt">VARCHAR</span>(<span class="dv">100</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span>,</span>
<span id="cb52-18"><a href="#cb52-18" aria-hidden="true" tabindex="-1"></a> remark <span class="dt">VARCHAR</span>(<span class="dv">500</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span>,</span>
<span id="cb52-19"><a href="#cb52-19" aria-hidden="true" tabindex="-1"></a> status <span class="dt">SMALLINT</span> <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="dv">1</span> <span class="kw">CHECK</span> (status <span class="kw">IN</span> (<span class="dv">0</span>,<span class="dv">1</span>,<span class="dv">2</span>,<span class="dv">3</span>)),</span>
<span id="cb52-20"><a href="#cb52-20" aria-hidden="true" tabindex="-1"></a> tenant_id BIGINT <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="dv">0</span>,</span>
<span id="cb52-21"><a href="#cb52-21" aria-hidden="true" tabindex="-1"></a> creator <span class="dt">VARCHAR</span>(<span class="dv">64</span>) <span class="kw">DEFAULT</span> <span class="st">&#39;&#39;</span>,</span>
<span id="cb52-22"><a href="#cb52-22" aria-hidden="true" tabindex="-1"></a> create_time <span class="dt">TIMESTAMP</span> <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="fu">CURRENT_TIMESTAMP</span>,</span>
<span id="cb52-23"><a href="#cb52-23" aria-hidden="true" tabindex="-1"></a> updater <span class="dt">VARCHAR</span>(<span class="dv">64</span>) <span class="kw">DEFAULT</span> <span class="st">&#39;&#39;</span>,</span>
<span id="cb52-24"><a href="#cb52-24" aria-hidden="true" tabindex="-1"></a> update_time <span class="dt">TIMESTAMP</span> <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="fu">CURRENT_TIMESTAMP</span>,</span>
<span id="cb52-25"><a href="#cb52-25" aria-hidden="true" tabindex="-1"></a> deleted <span class="dt">BOOLEAN</span> <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="kw">FALSE</span></span>
<span id="cb52-26"><a href="#cb52-26" aria-hidden="true" tabindex="-1"></a>);</span>
<span id="cb52-27"><a href="#cb52-27" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb52-28"><a href="#cb52-28" aria-hidden="true" tabindex="-1"></a><span class="co">-- 创建注释</span></span>
<span id="cb52-29"><a href="#cb52-29" aria-hidden="true" tabindex="-1"></a><span class="kw">COMMENT</span> <span class="kw">ON</span> <span class="kw">TABLE</span> water_customer <span class="kw">IS</span> <span class="st">&#39;客户基本信息表&#39;</span>;</span>
<span id="cb52-30"><a href="#cb52-30" aria-hidden="true" tabindex="-1"></a><span class="kw">COMMENT</span> <span class="kw">ON</span> <span class="kw">COLUMN</span> water_customer.<span class="kw">id</span> <span class="kw">IS</span> <span class="st">&#39;主键ID&#39;</span>;</span>
<span id="cb52-31"><a href="#cb52-31" aria-hidden="true" tabindex="-1"></a><span class="kw">COMMENT</span> <span class="kw">ON</span> <span class="kw">COLUMN</span> water_customer.customer_code <span class="kw">IS</span> <span class="st">&#39;客户编号&#39;</span>;</span>
<span id="cb52-32"><a href="#cb52-32" aria-hidden="true" tabindex="-1"></a><span class="kw">COMMENT</span> <span class="kw">ON</span> <span class="kw">COLUMN</span> water_customer.customer_name <span class="kw">IS</span> <span class="st">&#39;客户名称&#39;</span>;</span>
<span id="cb52-33"><a href="#cb52-33" aria-hidden="true" tabindex="-1"></a><span class="kw">COMMENT</span> <span class="kw">ON</span> <span class="kw">COLUMN</span> water_customer.customer_type <span class="kw">IS</span> <span class="st">&#39;客户类型(居民:RESIDENT,非居民:NON_RESIDENT,工业:INDUSTRIAL,行政:ADMINISTRATIVE)&#39;</span>;</span>
<span id="cb52-34"><a href="#cb52-34" aria-hidden="true" tabindex="-1"></a><span class="kw">COMMENT</span> <span class="kw">ON</span> <span class="kw">COLUMN</span> water_customer.id_type <span class="kw">IS</span> <span class="st">&#39;证件类型(身份证:ID_CARD,营业执照:BUSINESS_LICENSE)&#39;</span>;</span>
<span id="cb52-35"><a href="#cb52-35" aria-hidden="true" tabindex="-1"></a><span class="kw">COMMENT</span> <span class="kw">ON</span> <span class="kw">COLUMN</span> water_customer.id_number <span class="kw">IS</span> <span class="st">&#39;证件号码&#39;</span>;</span>
<span id="cb52-36"><a href="#cb52-36" aria-hidden="true" tabindex="-1"></a><span class="kw">COMMENT</span> <span class="kw">ON</span> <span class="kw">COLUMN</span> water_customer.phone <span class="kw">IS</span> <span class="st">&#39;联系电话&#39;</span>;</span>
<span id="cb52-37"><a href="#cb52-37" aria-hidden="true" tabindex="-1"></a><span class="kw">COMMENT</span> <span class="kw">ON</span> <span class="kw">COLUMN</span> water_customer.mobile <span class="kw">IS</span> <span class="st">&#39;手机号码&#39;</span>;</span>
<span id="cb52-38"><a href="#cb52-38" aria-hidden="true" tabindex="-1"></a><span class="kw">COMMENT</span> <span class="kw">ON</span> <span class="kw">COLUMN</span> water_customer.email <span class="kw">IS</span> <span class="st">&#39;邮箱&#39;</span>;</span>
<span id="cb52-39"><a href="#cb52-39" aria-hidden="true" tabindex="-1"></a><span class="kw">COMMENT</span> <span class="kw">ON</span> <span class="kw">COLUMN</span> water_customer.address <span class="kw">IS</span> <span class="st">&#39;详细地址&#39;</span>;</span>
<span id="cb52-40"><a href="#cb52-40" aria-hidden="true" tabindex="-1"></a><span class="kw">COMMENT</span> <span class="kw">ON</span> <span class="kw">COLUMN</span> water_customer.area_code <span class="kw">IS</span> <span class="st">&#39;行政区划代码&#39;</span>;</span>
<span id="cb52-41"><a href="#cb52-41" aria-hidden="true" tabindex="-1"></a><span class="kw">COMMENT</span> <span class="kw">ON</span> <span class="kw">COLUMN</span> water_customer.postal_code <span class="kw">IS</span> <span class="st">&#39;邮政编码&#39;</span>;</span>
<span id="cb52-42"><a href="#cb52-42" aria-hidden="true" tabindex="-1"></a><span class="kw">COMMENT</span> <span class="kw">ON</span> <span class="kw">COLUMN</span> water_customer.contact_person <span class="kw">IS</span> <span class="st">&#39;联系人&#39;</span>;</span>
<span id="cb52-43"><a href="#cb52-43" aria-hidden="true" tabindex="-1"></a><span class="kw">COMMENT</span> <span class="kw">ON</span> <span class="kw">COLUMN</span> water_customer.bank_account <span class="kw">IS</span> <span class="st">&#39;银行账户&#39;</span>;</span>
<span id="cb52-44"><a href="#cb52-44" aria-hidden="true" tabindex="-1"></a><span class="kw">COMMENT</span> <span class="kw">ON</span> <span class="kw">COLUMN</span> water_customer.bank_name <span class="kw">IS</span> <span class="st">&#39;开户银行&#39;</span>;</span>
<span id="cb52-45"><a href="#cb52-45" aria-hidden="true" tabindex="-1"></a><span class="kw">COMMENT</span> <span class="kw">ON</span> <span class="kw">COLUMN</span> water_customer.remark <span class="kw">IS</span> <span class="st">&#39;备注信息&#39;</span>;</span>
<span id="cb52-46"><a href="#cb52-46" aria-hidden="true" tabindex="-1"></a><span class="kw">COMMENT</span> <span class="kw">ON</span> <span class="kw">COLUMN</span> water_customer.status <span class="kw">IS</span> <span class="st">&#39;状态(0:停用,1:正常,2:欠费,3:销户)&#39;</span>;</span>
<span id="cb52-47"><a href="#cb52-47" aria-hidden="true" tabindex="-1"></a><span class="kw">COMMENT</span> <span class="kw">ON</span> <span class="kw">COLUMN</span> water_customer.tenant_id <span class="kw">IS</span> <span class="st">&#39;租户ID&#39;</span>;</span>
<span id="cb52-48"><a href="#cb52-48" aria-hidden="true" tabindex="-1"></a><span class="kw">COMMENT</span> <span class="kw">ON</span> <span class="kw">COLUMN</span> water_customer.creator <span class="kw">IS</span> <span class="st">&#39;创建者&#39;</span>;</span>
<span id="cb52-49"><a href="#cb52-49" aria-hidden="true" tabindex="-1"></a><span class="kw">COMMENT</span> <span class="kw">ON</span> <span class="kw">COLUMN</span> water_customer.create_time <span class="kw">IS</span> <span class="st">&#39;创建时间&#39;</span>;</span>
<span id="cb52-50"><a href="#cb52-50" aria-hidden="true" tabindex="-1"></a><span class="kw">COMMENT</span> <span class="kw">ON</span> <span class="kw">COLUMN</span> water_customer.updater <span class="kw">IS</span> <span class="st">&#39;更新者&#39;</span>;</span>
<span id="cb52-51"><a href="#cb52-51" aria-hidden="true" tabindex="-1"></a><span class="kw">COMMENT</span> <span class="kw">ON</span> <span class="kw">COLUMN</span> water_customer.update_time <span class="kw">IS</span> <span class="st">&#39;更新时间&#39;</span>;</span>
<span id="cb52-52"><a href="#cb52-52" aria-hidden="true" tabindex="-1"></a><span class="kw">COMMENT</span> <span class="kw">ON</span> <span class="kw">COLUMN</span> water_customer.deleted <span class="kw">IS</span> <span class="st">&#39;是否删除&#39;</span>;</span>
<span id="cb52-53"><a href="#cb52-53" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb52-54"><a href="#cb52-54" aria-hidden="true" tabindex="-1"></a><span class="co">-- 创建索引</span></span>
<span id="cb52-55"><a href="#cb52-55" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">UNIQUE</span> <span class="kw">INDEX</span> uk_customer_code <span class="kw">ON</span> water_customer (customer_code, tenant_id);</span>
<span id="cb52-56"><a href="#cb52-56" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">INDEX</span> idx_customer_name <span class="kw">ON</span> water_customer (customer_name);</span>
<span id="cb52-57"><a href="#cb52-57" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">INDEX</span> idx_phone <span class="kw">ON</span> water_customer (phone);</span>
<span id="cb52-58"><a href="#cb52-58" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">INDEX</span> idx_id_number <span class="kw">ON</span> water_customer (id_number);</span>
<span id="cb52-59"><a href="#cb52-59" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">INDEX</span> idx_tenant_customer_type <span class="kw">ON</span> water_customer (tenant_id, customer_type);</span>
<span id="cb52-60"><a href="#cb52-60" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">INDEX</span> idx_status <span class="kw">ON</span> water_customer (status);</span>
<span id="cb52-61"><a href="#cb52-61" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">INDEX</span> idx_create_time <span class="kw">ON</span> water_customer (create_time);</span>
<span id="cb52-62"><a href="#cb52-62" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb52-63"><a href="#cb52-63" aria-hidden="true" tabindex="-1"></a><span class="co">-- 创建更新时间触发器</span></span>
<span id="cb52-64"><a href="#cb52-64" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">OR</span> <span class="kw">REPLACE</span> <span class="kw">FUNCTION</span> update_timestamp()</span>
<span id="cb52-65"><a href="#cb52-65" aria-hidden="true" tabindex="-1"></a>RETURNS <span class="kw">TRIGGER</span> <span class="kw">AS</span> $$</span>
<span id="cb52-66"><a href="#cb52-66" aria-hidden="true" tabindex="-1"></a><span class="cf">BEGIN</span></span>
<span id="cb52-67"><a href="#cb52-67" aria-hidden="true" tabindex="-1"></a> <span class="kw">NEW</span>.update_time <span class="op">=</span> <span class="fu">CURRENT_TIMESTAMP</span>;</span>
<span id="cb52-68"><a href="#cb52-68" aria-hidden="true" tabindex="-1"></a> <span class="kw">RETURN</span> <span class="kw">NEW</span>;</span>
<span id="cb52-69"><a href="#cb52-69" aria-hidden="true" tabindex="-1"></a><span class="cf">END</span>;</span>
<span id="cb52-70"><a href="#cb52-70" aria-hidden="true" tabindex="-1"></a>$$ LANGUAGE plpgsql;</span>
<span id="cb52-71"><a href="#cb52-71" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb52-72"><a href="#cb52-72" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">TRIGGER</span> water_customer_update_timestamp</span>
<span id="cb52-73"><a href="#cb52-73" aria-hidden="true" tabindex="-1"></a> <span class="kw">BEFORE</span> <span class="kw">UPDATE</span> <span class="kw">ON</span> water_customer</span>
<span id="cb52-74"><a href="#cb52-74" aria-hidden="true" tabindex="-1"></a> <span class="cf">FOR</span> <span class="kw">EACH</span> <span class="kw">ROW</span></span>
<span id="cb52-75"><a href="#cb52-75" aria-hidden="true" tabindex="-1"></a> <span class="kw">EXECUTE</span> <span class="kw">FUNCTION</span> update_timestamp();</span></code></pre></div>
<h5 data-number="4.0.0.0.31" id="水表信息表-1"><span
class="header-section-number">4.0.0.0.31</span> 1.2 水表信息表</h5>
<div class="sourceCode" id="cb53"><pre
class="sourceCode sql"><code class="sourceCode sql"><span id="cb53-1"><a href="#cb53-1" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">TABLE</span> `water_meter` (</span>
<span id="cb53-2"><a href="#cb53-2" aria-hidden="true" tabindex="-1"></a> `id` bigint <span class="kw">NOT</span> <span class="kw">NULL</span> AUTO_INCREMENT <span class="kw">COMMENT</span> <span class="st">&#39;主键ID&#39;</span>,</span>
<span id="cb53-3"><a href="#cb53-3" aria-hidden="true" tabindex="-1"></a> `meter_code` <span class="dt">varchar</span>(<span class="dv">32</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;水表编号&#39;</span>,</span>
<span id="cb53-4"><a href="#cb53-4" aria-hidden="true" tabindex="-1"></a> `meter_no` <span class="dt">varchar</span>(<span class="dv">30</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;水表表号&#39;</span>,</span>
<span id="cb53-5"><a href="#cb53-5" aria-hidden="true" tabindex="-1"></a> `meter_type` <span class="dt">varchar</span>(<span class="dv">20</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;水表类型(机械:MECHANICAL,智能:SMART,远传:REMOTE)&#39;</span>,</span>
<span id="cb53-6"><a href="#cb53-6" aria-hidden="true" tabindex="-1"></a> `meter_model` <span class="dt">varchar</span>(<span class="dv">50</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;水表型号&#39;</span>,</span>
<span id="cb53-7"><a href="#cb53-7" aria-hidden="true" tabindex="-1"></a> `meter_caliber` <span class="dt">varchar</span>(<span class="dv">10</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;水表口径(15mm,20mm,25mm等)&#39;</span>,</span>
<span id="cb53-8"><a href="#cb53-8" aria-hidden="true" tabindex="-1"></a> `manufacturer` <span class="dt">varchar</span>(<span class="dv">100</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;生产厂家&#39;</span>,</span>
<span id="cb53-9"><a href="#cb53-9" aria-hidden="true" tabindex="-1"></a> `production_date` <span class="dt">date</span> <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;生产日期&#39;</span>,</span>
<span id="cb53-10"><a href="#cb53-10" aria-hidden="true" tabindex="-1"></a> `install_date` <span class="dt">date</span> <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;安装日期&#39;</span>,</span>
<span id="cb53-11"><a href="#cb53-11" aria-hidden="true" tabindex="-1"></a> `install_position` <span class="dt">varchar</span>(<span class="dv">500</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;安装位置&#39;</span>,</span>
<span id="cb53-12"><a href="#cb53-12" aria-hidden="true" tabindex="-1"></a> `longitude` <span class="dt">decimal</span>(<span class="dv">10</span>,<span class="dv">7</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;经度&#39;</span>,</span>
<span id="cb53-13"><a href="#cb53-13" aria-hidden="true" tabindex="-1"></a> `latitude` <span class="dt">decimal</span>(<span class="dv">10</span>,<span class="dv">7</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;纬度&#39;</span>,</span>
<span id="cb53-14"><a href="#cb53-14" aria-hidden="true" tabindex="-1"></a> `initial_reading` <span class="dt">decimal</span>(<span class="dv">10</span>,<span class="dv">2</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="st">&#39;0.00&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;初始读数&#39;</span>,</span>
<span id="cb53-15"><a href="#cb53-15" aria-hidden="true" tabindex="-1"></a> `current_reading` <span class="dt">decimal</span>(<span class="dv">10</span>,<span class="dv">2</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="st">&#39;0.00&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;当前读数&#39;</span>,</span>
<span id="cb53-16"><a href="#cb53-16" aria-hidden="true" tabindex="-1"></a> `reading_cycle` <span class="dt">varchar</span>(<span class="dv">20</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="st">&#39;MONTHLY&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;抄表周期(月度:MONTHLY,双月:BIMONTHLY,季度:QUARTERLY)&#39;</span>,</span>
<span id="cb53-17"><a href="#cb53-17" aria-hidden="true" tabindex="-1"></a> `book_code` <span class="dt">varchar</span>(<span class="dv">32</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;册本编号&#39;</span>,</span>
<span id="cb53-18"><a href="#cb53-18" aria-hidden="true" tabindex="-1"></a> `reading_route` <span class="dt">varchar</span>(<span class="dv">100</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;抄表路线&#39;</span>,</span>
<span id="cb53-19"><a href="#cb53-19" aria-hidden="true" tabindex="-1"></a> `meter_status` tinyint <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="st">&#39;1&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;水表状态(0:停用,1:正常,2:故障,3:拆除)&#39;</span>,</span>
<span id="cb53-20"><a href="#cb53-20" aria-hidden="true" tabindex="-1"></a> `customer_id` bigint <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;客户ID&#39;</span>,</span>
<span id="cb53-21"><a href="#cb53-21" aria-hidden="true" tabindex="-1"></a> `tenant_id` bigint <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="st">&#39;0&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;租户ID&#39;</span>,</span>
<span id="cb53-22"><a href="#cb53-22" aria-hidden="true" tabindex="-1"></a> `creator` <span class="dt">varchar</span>(<span class="dv">64</span>) <span class="kw">DEFAULT</span> <span class="st">&#39;&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;创建者&#39;</span>,</span>
<span id="cb53-23"><a href="#cb53-23" aria-hidden="true" tabindex="-1"></a> `create_time` datetime <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="fu">CURRENT_TIMESTAMP</span> <span class="kw">COMMENT</span> <span class="st">&#39;创建时间&#39;</span>,</span>
<span id="cb53-24"><a href="#cb53-24" aria-hidden="true" tabindex="-1"></a> `updater` <span class="dt">varchar</span>(<span class="dv">64</span>) <span class="kw">DEFAULT</span> <span class="st">&#39;&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;更新者&#39;</span>,</span>
<span id="cb53-25"><a href="#cb53-25" aria-hidden="true" tabindex="-1"></a> `update_time` datetime <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="fu">CURRENT_TIMESTAMP</span> <span class="kw">ON</span> <span class="kw">UPDATE</span> <span class="fu">CURRENT_TIMESTAMP</span> <span class="kw">COMMENT</span> <span class="st">&#39;更新时间&#39;</span>,</span>
<span id="cb53-26"><a href="#cb53-26" aria-hidden="true" tabindex="-1"></a> `deleted` bit(<span class="dv">1</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> b<span class="st">&#39;0&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;是否删除&#39;</span>,</span>
<span id="cb53-27"><a href="#cb53-27" aria-hidden="true" tabindex="-1"></a> <span class="kw">PRIMARY</span> <span class="kw">KEY</span> (`id`),</span>
<span id="cb53-28"><a href="#cb53-28" aria-hidden="true" tabindex="-1"></a> <span class="kw">UNIQUE</span> <span class="kw">KEY</span> `uk_meter_code` (`meter_code`, `tenant_id`),</span>
<span id="cb53-29"><a href="#cb53-29" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_meter_no` (`meter_no`),</span>
<span id="cb53-30"><a href="#cb53-30" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_customer_id` (`customer_id`),</span>
<span id="cb53-31"><a href="#cb53-31" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_book_code` (`book_code`),</span>
<span id="cb53-32"><a href="#cb53-32" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_meter_type` (`meter_type`),</span>
<span id="cb53-33"><a href="#cb53-33" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_meter_status` (`meter_status`),</span>
<span id="cb53-34"><a href="#cb53-34" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_tenant_status` (`tenant_id`, `meter_status`),</span>
<span id="cb53-35"><a href="#cb53-35" aria-hidden="true" tabindex="-1"></a> <span class="kw">CONSTRAINT</span> `fk_meter_customer` <span class="kw">FOREIGN</span> <span class="kw">KEY</span> (`customer_id`) <span class="kw">REFERENCES</span> `water_customer` (`id`)</span>
<span id="cb53-36"><a href="#cb53-36" aria-hidden="true" tabindex="-1"></a>) ENGINE<span class="op">=</span>InnoDB <span class="kw">DEFAULT</span> CHARSET<span class="op">=</span>utf8mb4 <span class="kw">COMMENT</span><span class="op">=</span><span class="st">&#39;水表信息表&#39;</span>;</span></code></pre></div>
<h5 data-number="4.0.0.0.32" id="客户账户表"><span
class="header-section-number">4.0.0.0.32</span> 1.3 客户账户表</h5>
<div class="sourceCode" id="cb54"><pre
class="sourceCode sql"><code class="sourceCode sql"><span id="cb54-1"><a href="#cb54-1" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">TABLE</span> `water_customer_account` (</span>
<span id="cb54-2"><a href="#cb54-2" aria-hidden="true" tabindex="-1"></a> `id` bigint <span class="kw">NOT</span> <span class="kw">NULL</span> AUTO_INCREMENT <span class="kw">COMMENT</span> <span class="st">&#39;主键ID&#39;</span>,</span>
<span id="cb54-3"><a href="#cb54-3" aria-hidden="true" tabindex="-1"></a> `account_code` <span class="dt">varchar</span>(<span class="dv">32</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;账户编号&#39;</span>,</span>
<span id="cb54-4"><a href="#cb54-4" aria-hidden="true" tabindex="-1"></a> `balance` <span class="dt">decimal</span>(<span class="dv">12</span>,<span class="dv">2</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="st">&#39;0.00&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;账户余额&#39;</span>,</span>
<span id="cb54-5"><a href="#cb54-5" aria-hidden="true" tabindex="-1"></a> `credit_amount` <span class="dt">decimal</span>(<span class="dv">12</span>,<span class="dv">2</span>) <span class="kw">DEFAULT</span> <span class="st">&#39;0.00&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;信用额度&#39;</span>,</span>
<span id="cb54-6"><a href="#cb54-6" aria-hidden="true" tabindex="-1"></a> `deposit_amount` <span class="dt">decimal</span>(<span class="dv">12</span>,<span class="dv">2</span>) <span class="kw">DEFAULT</span> <span class="st">&#39;0.00&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;保证金&#39;</span>,</span>
<span id="cb54-7"><a href="#cb54-7" aria-hidden="true" tabindex="-1"></a> `frozen_amount` <span class="dt">decimal</span>(<span class="dv">12</span>,<span class="dv">2</span>) <span class="kw">DEFAULT</span> <span class="st">&#39;0.00&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;冻结金额&#39;</span>,</span>
<span id="cb54-8"><a href="#cb54-8" aria-hidden="true" tabindex="-1"></a> `last_payment_date` <span class="dt">date</span> <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;最近缴费日期&#39;</span>,</span>
<span id="cb54-9"><a href="#cb54-9" aria-hidden="true" tabindex="-1"></a> `last_payment_amount` <span class="dt">decimal</span>(<span class="dv">12</span>,<span class="dv">2</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;最近缴费金额&#39;</span>,</span>
<span id="cb54-10"><a href="#cb54-10" aria-hidden="true" tabindex="-1"></a> `arrears_amount` <span class="dt">decimal</span>(<span class="dv">12</span>,<span class="dv">2</span>) <span class="kw">DEFAULT</span> <span class="st">&#39;0.00&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;欠费金额&#39;</span>,</span>
<span id="cb54-11"><a href="#cb54-11" aria-hidden="true" tabindex="-1"></a> `arrears_months` <span class="dt">int</span> <span class="kw">DEFAULT</span> <span class="st">&#39;0&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;欠费月数&#39;</span>,</span>
<span id="cb54-12"><a href="#cb54-12" aria-hidden="true" tabindex="-1"></a> `account_status` tinyint <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="st">&#39;1&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;账户状态(0:停用,1:正常,2:欠费,3:冻结)&#39;</span>,</span>
<span id="cb54-13"><a href="#cb54-13" aria-hidden="true" tabindex="-1"></a> `customer_id` bigint <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;客户ID&#39;</span>,</span>
<span id="cb54-14"><a href="#cb54-14" aria-hidden="true" tabindex="-1"></a> `tenant_id` bigint <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="st">&#39;0&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;租户ID&#39;</span>,</span>
<span id="cb54-15"><a href="#cb54-15" aria-hidden="true" tabindex="-1"></a> `creator` <span class="dt">varchar</span>(<span class="dv">64</span>) <span class="kw">DEFAULT</span> <span class="st">&#39;&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;创建者&#39;</span>,</span>
<span id="cb54-16"><a href="#cb54-16" aria-hidden="true" tabindex="-1"></a> `create_time` datetime <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="fu">CURRENT_TIMESTAMP</span> <span class="kw">COMMENT</span> <span class="st">&#39;创建时间&#39;</span>,</span>
<span id="cb54-17"><a href="#cb54-17" aria-hidden="true" tabindex="-1"></a> `updater` <span class="dt">varchar</span>(<span class="dv">64</span>) <span class="kw">DEFAULT</span> <span class="st">&#39;&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;更新者&#39;</span>,</span>
<span id="cb54-18"><a href="#cb54-18" aria-hidden="true" tabindex="-1"></a> `update_time` datetime <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="fu">CURRENT_TIMESTAMP</span> <span class="kw">ON</span> <span class="kw">UPDATE</span> <span class="fu">CURRENT_TIMESTAMP</span> <span class="kw">COMMENT</span> <span class="st">&#39;更新时间&#39;</span>,</span>
<span id="cb54-19"><a href="#cb54-19" aria-hidden="true" tabindex="-1"></a> `deleted` bit(<span class="dv">1</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> b<span class="st">&#39;0&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;是否删除&#39;</span>,</span>
<span id="cb54-20"><a href="#cb54-20" aria-hidden="true" tabindex="-1"></a> <span class="kw">PRIMARY</span> <span class="kw">KEY</span> (`id`),</span>
<span id="cb54-21"><a href="#cb54-21" aria-hidden="true" tabindex="-1"></a> <span class="kw">UNIQUE</span> <span class="kw">KEY</span> `uk_account_code` (`account_code`, `tenant_id`),</span>
<span id="cb54-22"><a href="#cb54-22" aria-hidden="true" tabindex="-1"></a> <span class="kw">UNIQUE</span> <span class="kw">KEY</span> `uk_customer_account` (`customer_id`, `tenant_id`),</span>
<span id="cb54-23"><a href="#cb54-23" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_account_status` (`account_status`),</span>
<span id="cb54-24"><a href="#cb54-24" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_balance` (`balance`),</span>
<span id="cb54-25"><a href="#cb54-25" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_arrears` (`arrears_amount`),</span>
<span id="cb54-26"><a href="#cb54-26" aria-hidden="true" tabindex="-1"></a> <span class="kw">CONSTRAINT</span> `fk_account_customer` <span class="kw">FOREIGN</span> <span class="kw">KEY</span> (`customer_id`) <span class="kw">REFERENCES</span> `water_customer` (`id`)</span>
<span id="cb54-27"><a href="#cb54-27" aria-hidden="true" tabindex="-1"></a>) ENGINE<span class="op">=</span>InnoDB <span class="kw">DEFAULT</span> CHARSET<span class="op">=</span>utf8mb4 <span class="kw">COMMENT</span><span class="op">=</span><span class="st">&#39;客户账户信息表&#39;</span>;</span></code></pre></div>
<h5 data-number="4.0.0.0.33" id="营收管理相关表ddl"><span
class="header-section-number">4.0.0.0.33</span> 2.
营收管理相关表DDL</h5>
<h5 data-number="4.0.0.0.34" id="抄表记录表"><span
class="header-section-number">4.0.0.0.34</span> 2.1 抄表记录表</h5>
<div class="sourceCode" id="cb55"><pre
class="sourceCode sql"><code class="sourceCode sql"><span id="cb55-1"><a href="#cb55-1" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">TABLE</span> `water_meter_reading` (</span>
<span id="cb55-2"><a href="#cb55-2" aria-hidden="true" tabindex="-1"></a> `id` bigint <span class="kw">NOT</span> <span class="kw">NULL</span> AUTO_INCREMENT <span class="kw">COMMENT</span> <span class="st">&#39;主键ID&#39;</span>,</span>
<span id="cb55-3"><a href="#cb55-3" aria-hidden="true" tabindex="-1"></a> `reading_code` <span class="dt">varchar</span>(<span class="dv">32</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;抄表记录编号&#39;</span>,</span>
<span id="cb55-4"><a href="#cb55-4" aria-hidden="true" tabindex="-1"></a> `reading_date` <span class="dt">date</span> <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;抄表日期&#39;</span>,</span>
<span id="cb55-5"><a href="#cb55-5" aria-hidden="true" tabindex="-1"></a> `reading_time` datetime <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;抄表时间&#39;</span>,</span>
<span id="cb55-6"><a href="#cb55-6" aria-hidden="true" tabindex="-1"></a> `reading_value` <span class="dt">decimal</span>(<span class="dv">10</span>,<span class="dv">2</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;抄表读数&#39;</span>,</span>
<span id="cb55-7"><a href="#cb55-7" aria-hidden="true" tabindex="-1"></a> `prev_reading_value` <span class="dt">decimal</span>(<span class="dv">10</span>,<span class="dv">2</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;上次读数&#39;</span>,</span>
<span id="cb55-8"><a href="#cb55-8" aria-hidden="true" tabindex="-1"></a> `water_usage` <span class="dt">decimal</span>(<span class="dv">10</span>,<span class="dv">2</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="st">&#39;0.00&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;用水量&#39;</span>,</span>
<span id="cb55-9"><a href="#cb55-9" aria-hidden="true" tabindex="-1"></a> `reading_type` <span class="dt">varchar</span>(<span class="dv">20</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;抄表类型(手工:MANUAL,远传:REMOTE,自报:SELF_REPORT,估算:ESTIMATE)&#39;</span>,</span>
<span id="cb55-10"><a href="#cb55-10" aria-hidden="true" tabindex="-1"></a> `reading_method` <span class="dt">varchar</span>(<span class="dv">20</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;抄表方式(现场:FIELD,拍照:PHOTO,NFC:NFC,扫码:SCAN)&#39;</span>,</span>
<span id="cb55-11"><a href="#cb55-11" aria-hidden="true" tabindex="-1"></a> `reader_id` <span class="dt">varchar</span>(<span class="dv">20</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;抄表员ID&#39;</span>,</span>
<span id="cb55-12"><a href="#cb55-12" aria-hidden="true" tabindex="-1"></a> `reader_name` <span class="dt">varchar</span>(<span class="dv">50</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;抄表员姓名&#39;</span>,</span>
<span id="cb55-13"><a href="#cb55-13" aria-hidden="true" tabindex="-1"></a> `photo_url` <span class="dt">varchar</span>(<span class="dv">255</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;抄表照片URL&#39;</span>,</span>
<span id="cb55-14"><a href="#cb55-14" aria-hidden="true" tabindex="-1"></a> `reading_location` <span class="dt">varchar</span>(<span class="dv">200</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;抄表位置&#39;</span>,</span>
<span id="cb55-15"><a href="#cb55-15" aria-hidden="true" tabindex="-1"></a> `abnormal_flag` tinyint <span class="kw">DEFAULT</span> <span class="st">&#39;0&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;异常标识(0:正常,1:异常)&#39;</span>,</span>
<span id="cb55-16"><a href="#cb55-16" aria-hidden="true" tabindex="-1"></a> `abnormal_reason` <span class="dt">varchar</span>(<span class="dv">200</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;异常原因&#39;</span>,</span>
<span id="cb55-17"><a href="#cb55-17" aria-hidden="true" tabindex="-1"></a> `remark` <span class="dt">varchar</span>(<span class="dv">500</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;备注&#39;</span>,</span>
<span id="cb55-18"><a href="#cb55-18" aria-hidden="true" tabindex="-1"></a> `reading_status` tinyint <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="st">&#39;0&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;状态(0:未复核,1:已复核,2:已开账,3:作废)&#39;</span>,</span>
<span id="cb55-19"><a href="#cb55-19" aria-hidden="true" tabindex="-1"></a> `meter_id` bigint <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;水表ID&#39;</span>,</span>
<span id="cb55-20"><a href="#cb55-20" aria-hidden="true" tabindex="-1"></a> `customer_id` bigint <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;客户ID&#39;</span>,</span>
<span id="cb55-21"><a href="#cb55-21" aria-hidden="true" tabindex="-1"></a> `tenant_id` bigint <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="st">&#39;0&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;租户ID&#39;</span>,</span>
<span id="cb55-22"><a href="#cb55-22" aria-hidden="true" tabindex="-1"></a> `creator` <span class="dt">varchar</span>(<span class="dv">64</span>) <span class="kw">DEFAULT</span> <span class="st">&#39;&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;创建者&#39;</span>,</span>
<span id="cb55-23"><a href="#cb55-23" aria-hidden="true" tabindex="-1"></a> `create_time` datetime <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="fu">CURRENT_TIMESTAMP</span> <span class="kw">COMMENT</span> <span class="st">&#39;创建时间&#39;</span>,</span>
<span id="cb55-24"><a href="#cb55-24" aria-hidden="true" tabindex="-1"></a> `updater` <span class="dt">varchar</span>(<span class="dv">64</span>) <span class="kw">DEFAULT</span> <span class="st">&#39;&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;更新者&#39;</span>,</span>
<span id="cb55-25"><a href="#cb55-25" aria-hidden="true" tabindex="-1"></a> `update_time` datetime <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="fu">CURRENT_TIMESTAMP</span> <span class="kw">ON</span> <span class="kw">UPDATE</span> <span class="fu">CURRENT_TIMESTAMP</span> <span class="kw">COMMENT</span> <span class="st">&#39;更新时间&#39;</span>,</span>
<span id="cb55-26"><a href="#cb55-26" aria-hidden="true" tabindex="-1"></a> `deleted` bit(<span class="dv">1</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> b<span class="st">&#39;0&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;是否删除&#39;</span>,</span>
<span id="cb55-27"><a href="#cb55-27" aria-hidden="true" tabindex="-1"></a> <span class="kw">PRIMARY</span> <span class="kw">KEY</span> (`id`),</span>
<span id="cb55-28"><a href="#cb55-28" aria-hidden="true" tabindex="-1"></a> <span class="kw">UNIQUE</span> <span class="kw">KEY</span> `uk_reading_code` (`reading_code`, `tenant_id`),</span>
<span id="cb55-29"><a href="#cb55-29" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_meter_reading_date` (`meter_id`, `reading_date`),</span>
<span id="cb55-30"><a href="#cb55-30" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_reading_date` (`reading_date`),</span>
<span id="cb55-31"><a href="#cb55-31" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_reader_id` (`reader_id`),</span>
<span id="cb55-32"><a href="#cb55-32" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_reading_status` (`reading_status`),</span>
<span id="cb55-33"><a href="#cb55-33" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_customer_id` (`customer_id`),</span>
<span id="cb55-34"><a href="#cb55-34" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_tenant_meter_date` (`tenant_id`, `meter_id`, `reading_date`),</span>
<span id="cb55-35"><a href="#cb55-35" aria-hidden="true" tabindex="-1"></a> <span class="kw">CONSTRAINT</span> `fk_reading_meter` <span class="kw">FOREIGN</span> <span class="kw">KEY</span> (`meter_id`) <span class="kw">REFERENCES</span> `water_meter` (`id`),</span>
<span id="cb55-36"><a href="#cb55-36" aria-hidden="true" tabindex="-1"></a> <span class="kw">CONSTRAINT</span> `fk_reading_customer` <span class="kw">FOREIGN</span> <span class="kw">KEY</span> (`customer_id`) <span class="kw">REFERENCES</span> `water_customer` (`id`)</span>
<span id="cb55-37"><a href="#cb55-37" aria-hidden="true" tabindex="-1"></a>) ENGINE<span class="op">=</span>InnoDB <span class="kw">DEFAULT</span> CHARSET<span class="op">=</span>utf8mb4 <span class="kw">COMMENT</span><span class="op">=</span><span class="st">&#39;抄表记录表&#39;</span>;</span></code></pre></div>
<h5 data-number="4.0.0.0.35" id="水费账单表"><span
class="header-section-number">4.0.0.0.35</span> 2.2 水费账单表</h5>
<div class="sourceCode" id="cb56"><pre
class="sourceCode sql"><code class="sourceCode sql"><span id="cb56-1"><a href="#cb56-1" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">TABLE</span> `water_bill` (</span>
<span id="cb56-2"><a href="#cb56-2" aria-hidden="true" tabindex="-1"></a> `id` bigint <span class="kw">NOT</span> <span class="kw">NULL</span> AUTO_INCREMENT <span class="kw">COMMENT</span> <span class="st">&#39;主键ID&#39;</span>,</span>
<span id="cb56-3"><a href="#cb56-3" aria-hidden="true" tabindex="-1"></a> `bill_code` <span class="dt">varchar</span>(<span class="dv">32</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;账单编号&#39;</span>,</span>
<span id="cb56-4"><a href="#cb56-4" aria-hidden="true" tabindex="-1"></a> `bill_month` <span class="dt">varchar</span>(<span class="dv">7</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;账期(格式:YYYY-MM)&#39;</span>,</span>
<span id="cb56-5"><a href="#cb56-5" aria-hidden="true" tabindex="-1"></a> `bill_date` <span class="dt">date</span> <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;开账日期&#39;</span>,</span>
<span id="cb56-6"><a href="#cb56-6" aria-hidden="true" tabindex="-1"></a> `water_usage` <span class="dt">decimal</span>(<span class="dv">10</span>,<span class="dv">2</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="st">&#39;0.00&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;用水量&#39;</span>,</span>
<span id="cb56-7"><a href="#cb56-7" aria-hidden="true" tabindex="-1"></a> `prev_reading` <span class="dt">decimal</span>(<span class="dv">10</span>,<span class="dv">2</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;上期读数&#39;</span>,</span>
<span id="cb56-8"><a href="#cb56-8" aria-hidden="true" tabindex="-1"></a> `current_reading` <span class="dt">decimal</span>(<span class="dv">10</span>,<span class="dv">2</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;本期读数&#39;</span>,</span>
<span id="cb56-9"><a href="#cb56-9" aria-hidden="true" tabindex="-1"></a> `reading_days` <span class="dt">int</span> <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;抄表间隔天数&#39;</span>,</span>
<span id="cb56-10"><a href="#cb56-10" aria-hidden="true" tabindex="-1"></a> `water_price` <span class="dt">decimal</span>(<span class="dv">8</span>,<span class="dv">4</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;水价单价&#39;</span>,</span>
<span id="cb56-11"><a href="#cb56-11" aria-hidden="true" tabindex="-1"></a> `water_fee` <span class="dt">decimal</span>(<span class="dv">10</span>,<span class="dv">2</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="st">&#39;0.00&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;水费金额&#39;</span>,</span>
<span id="cb56-12"><a href="#cb56-12" aria-hidden="true" tabindex="-1"></a> `sewage_fee` <span class="dt">decimal</span>(<span class="dv">10</span>,<span class="dv">2</span>) <span class="kw">DEFAULT</span> <span class="st">&#39;0.00&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;污水处理费&#39;</span>,</span>
<span id="cb56-13"><a href="#cb56-13" aria-hidden="true" tabindex="-1"></a> `garbage_fee` <span class="dt">decimal</span>(<span class="dv">10</span>,<span class="dv">2</span>) <span class="kw">DEFAULT</span> <span class="st">&#39;0.00&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;垃圾处理费&#39;</span>,</span>
<span id="cb56-14"><a href="#cb56-14" aria-hidden="true" tabindex="-1"></a> `other_fee` <span class="dt">decimal</span>(<span class="dv">10</span>,<span class="dv">2</span>) <span class="kw">DEFAULT</span> <span class="st">&#39;0.00&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;其他费用&#39;</span>,</span>
<span id="cb56-15"><a href="#cb56-15" aria-hidden="true" tabindex="-1"></a> `adjustment_fee` <span class="dt">decimal</span>(<span class="dv">10</span>,<span class="dv">2</span>) <span class="kw">DEFAULT</span> <span class="st">&#39;0.00&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;调整费用&#39;</span>,</span>
<span id="cb56-16"><a href="#cb56-16" aria-hidden="true" tabindex="-1"></a> `late_fee` <span class="dt">decimal</span>(<span class="dv">10</span>,<span class="dv">2</span>) <span class="kw">DEFAULT</span> <span class="st">&#39;0.00&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;滞纳金&#39;</span>,</span>
<span id="cb56-17"><a href="#cb56-17" aria-hidden="true" tabindex="-1"></a> `total_amount` <span class="dt">decimal</span>(<span class="dv">10</span>,<span class="dv">2</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="st">&#39;0.00&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;总金额&#39;</span>,</span>
<span id="cb56-18"><a href="#cb56-18" aria-hidden="true" tabindex="-1"></a> `paid_amount` <span class="dt">decimal</span>(<span class="dv">10</span>,<span class="dv">2</span>) <span class="kw">DEFAULT</span> <span class="st">&#39;0.00&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;已缴金额&#39;</span>,</span>
<span id="cb56-19"><a href="#cb56-19" aria-hidden="true" tabindex="-1"></a> `balance_amount` <span class="dt">decimal</span>(<span class="dv">10</span>,<span class="dv">2</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="st">&#39;0.00&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;余额&#39;</span>,</span>
<span id="cb56-20"><a href="#cb56-20" aria-hidden="true" tabindex="-1"></a> `due_date` <span class="dt">date</span> <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;缴费截止日期&#39;</span>,</span>
<span id="cb56-21"><a href="#cb56-21" aria-hidden="true" tabindex="-1"></a> `billing_type` <span class="dt">varchar</span>(<span class="dv">20</span>) <span class="kw">DEFAULT</span> <span class="st">&#39;NORMAL&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;开账类型(正常:NORMAL,追补:SUPPLEMENT,调整:ADJUSTMENT)&#39;</span>,</span>
<span id="cb56-22"><a href="#cb56-22" aria-hidden="true" tabindex="-1"></a> `bill_status` tinyint <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="st">&#39;0&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;账单状态(0:未缴费,1:已缴费,2:部分缴费,3:作废)&#39;</span>,</span>
<span id="cb56-23"><a href="#cb56-23" aria-hidden="true" tabindex="-1"></a> `payment_status` tinyint <span class="kw">DEFAULT</span> <span class="st">&#39;0&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;缴费状态(0:未缴,1:已缴,2:部分缴费)&#39;</span>,</span>
<span id="cb56-24"><a href="#cb56-24" aria-hidden="true" tabindex="-1"></a> `customer_id` bigint <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;客户ID&#39;</span>,</span>
<span id="cb56-25"><a href="#cb56-25" aria-hidden="true" tabindex="-1"></a> `meter_id` bigint <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;水表ID&#39;</span>,</span>
<span id="cb56-26"><a href="#cb56-26" aria-hidden="true" tabindex="-1"></a> `reading_id` bigint <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;抄表记录ID&#39;</span>,</span>
<span id="cb56-27"><a href="#cb56-27" aria-hidden="true" tabindex="-1"></a> `tenant_id` bigint <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="st">&#39;0&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;租户ID&#39;</span>,</span>
<span id="cb56-28"><a href="#cb56-28" aria-hidden="true" tabindex="-1"></a> `creator` <span class="dt">varchar</span>(<span class="dv">64</span>) <span class="kw">DEFAULT</span> <span class="st">&#39;&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;创建者&#39;</span>,</span>
<span id="cb56-29"><a href="#cb56-29" aria-hidden="true" tabindex="-1"></a> `create_time` datetime <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="fu">CURRENT_TIMESTAMP</span> <span class="kw">COMMENT</span> <span class="st">&#39;创建时间&#39;</span>,</span>
<span id="cb56-30"><a href="#cb56-30" aria-hidden="true" tabindex="-1"></a> `updater` <span class="dt">varchar</span>(<span class="dv">64</span>) <span class="kw">DEFAULT</span> <span class="st">&#39;&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;更新者&#39;</span>,</span>
<span id="cb56-31"><a href="#cb56-31" aria-hidden="true" tabindex="-1"></a> `update_time` datetime <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="fu">CURRENT_TIMESTAMP</span> <span class="kw">ON</span> <span class="kw">UPDATE</span> <span class="fu">CURRENT_TIMESTAMP</span> <span class="kw">COMMENT</span> <span class="st">&#39;更新时间&#39;</span>,</span>
<span id="cb56-32"><a href="#cb56-32" aria-hidden="true" tabindex="-1"></a> `deleted` bit(<span class="dv">1</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> b<span class="st">&#39;0&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;是否删除&#39;</span>,</span>
<span id="cb56-33"><a href="#cb56-33" aria-hidden="true" tabindex="-1"></a> <span class="kw">PRIMARY</span> <span class="kw">KEY</span> (`id`),</span>
<span id="cb56-34"><a href="#cb56-34" aria-hidden="true" tabindex="-1"></a> <span class="kw">UNIQUE</span> <span class="kw">KEY</span> `uk_bill_code` (`bill_code`, `tenant_id`),</span>
<span id="cb56-35"><a href="#cb56-35" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_customer_bill_month` (`customer_id`, `bill_month`),</span>
<span id="cb56-36"><a href="#cb56-36" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_meter_bill_month` (`meter_id`, `bill_month`),</span>
<span id="cb56-37"><a href="#cb56-37" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_bill_status` (`bill_status`),</span>
<span id="cb56-38"><a href="#cb56-38" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_due_date` (`due_date`),</span>
<span id="cb56-39"><a href="#cb56-39" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_tenant_customer_month` (`tenant_id`, `customer_id`, `bill_month`),</span>
<span id="cb56-40"><a href="#cb56-40" aria-hidden="true" tabindex="-1"></a> <span class="kw">CONSTRAINT</span> `fk_bill_customer` <span class="kw">FOREIGN</span> <span class="kw">KEY</span> (`customer_id`) <span class="kw">REFERENCES</span> `water_customer` (`id`),</span>
<span id="cb56-41"><a href="#cb56-41" aria-hidden="true" tabindex="-1"></a> <span class="kw">CONSTRAINT</span> `fk_bill_meter` <span class="kw">FOREIGN</span> <span class="kw">KEY</span> (`meter_id`) <span class="kw">REFERENCES</span> `water_meter` (`id`),</span>
<span id="cb56-42"><a href="#cb56-42" aria-hidden="true" tabindex="-1"></a> <span class="kw">CONSTRAINT</span> `fk_bill_reading` <span class="kw">FOREIGN</span> <span class="kw">KEY</span> (`reading_id`) <span class="kw">REFERENCES</span> `water_meter_reading` (`id`)</span>
<span id="cb56-43"><a href="#cb56-43" aria-hidden="true" tabindex="-1"></a>) ENGINE<span class="op">=</span>InnoDB <span class="kw">DEFAULT</span> CHARSET<span class="op">=</span>utf8mb4 <span class="kw">COMMENT</span><span class="op">=</span><span class="st">&#39;水费账单表&#39;</span>;</span></code></pre></div>
<h5 data-number="4.0.0.0.36" id="缴费记录表"><span
class="header-section-number">4.0.0.0.36</span> 2.3 缴费记录表</h5>
<div class="sourceCode" id="cb57"><pre
class="sourceCode sql"><code class="sourceCode sql"><span id="cb57-1"><a href="#cb57-1" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">TABLE</span> `water_payment` (</span>
<span id="cb57-2"><a href="#cb57-2" aria-hidden="true" tabindex="-1"></a> `id` bigint <span class="kw">NOT</span> <span class="kw">NULL</span> AUTO_INCREMENT <span class="kw">COMMENT</span> <span class="st">&#39;主键ID&#39;</span>,</span>
<span id="cb57-3"><a href="#cb57-3" aria-hidden="true" tabindex="-1"></a> `payment_code` <span class="dt">varchar</span>(<span class="dv">32</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;缴费记录编号&#39;</span>,</span>
<span id="cb57-4"><a href="#cb57-4" aria-hidden="true" tabindex="-1"></a> `payment_type` <span class="dt">varchar</span>(<span class="dv">20</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;缴费类型(正常缴费:NORMAL,预存:PREPAID,退费:REFUND)&#39;</span>,</span>
<span id="cb57-5"><a href="#cb57-5" aria-hidden="true" tabindex="-1"></a> `payment_channel` <span class="dt">varchar</span>(<span class="dv">20</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;缴费渠道(现金:CASH,银行卡:BANK_CARD,微信:WECHAT,支付宝:ALIPAY,银行代扣:BANK_DEDUCT)&#39;</span>,</span>
<span id="cb57-6"><a href="#cb57-6" aria-hidden="true" tabindex="-1"></a> `payment_amount` <span class="dt">decimal</span>(<span class="dv">10</span>,<span class="dv">2</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;缴费金额&#39;</span>,</span>
<span id="cb57-7"><a href="#cb57-7" aria-hidden="true" tabindex="-1"></a> `actual_amount` <span class="dt">decimal</span>(<span class="dv">10</span>,<span class="dv">2</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;实收金额&#39;</span>,</span>
<span id="cb57-8"><a href="#cb57-8" aria-hidden="true" tabindex="-1"></a> `change_amount` <span class="dt">decimal</span>(<span class="dv">10</span>,<span class="dv">2</span>) <span class="kw">DEFAULT</span> <span class="st">&#39;0.00&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;找零金额&#39;</span>,</span>
<span id="cb57-9"><a href="#cb57-9" aria-hidden="true" tabindex="-1"></a> `payment_time` datetime <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;缴费时间&#39;</span>,</span>
<span id="cb57-10"><a href="#cb57-10" aria-hidden="true" tabindex="-1"></a> `transaction_no` <span class="dt">varchar</span>(<span class="dv">50</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;交易流水号&#39;</span>,</span>
<span id="cb57-11"><a href="#cb57-11" aria-hidden="true" tabindex="-1"></a> `third_party_no` <span class="dt">varchar</span>(<span class="dv">50</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;第三方交易号&#39;</span>,</span>
<span id="cb57-12"><a href="#cb57-12" aria-hidden="true" tabindex="-1"></a> `operator_id` <span class="dt">varchar</span>(<span class="dv">20</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;操作员ID&#39;</span>,</span>
<span id="cb57-13"><a href="#cb57-13" aria-hidden="true" tabindex="-1"></a> `operator_name` <span class="dt">varchar</span>(<span class="dv">50</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;操作员姓名&#39;</span>,</span>
<span id="cb57-14"><a href="#cb57-14" aria-hidden="true" tabindex="-1"></a> `outlet_code` <span class="dt">varchar</span>(<span class="dv">20</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;营业网点代码&#39;</span>,</span>
<span id="cb57-15"><a href="#cb57-15" aria-hidden="true" tabindex="-1"></a> `pos_machine_no` <span class="dt">varchar</span>(<span class="dv">20</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;POS机编号&#39;</span>,</span>
<span id="cb57-16"><a href="#cb57-16" aria-hidden="true" tabindex="-1"></a> `invoice_no` <span class="dt">varchar</span>(<span class="dv">30</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;发票号码&#39;</span>,</span>
<span id="cb57-17"><a href="#cb57-17" aria-hidden="true" tabindex="-1"></a> `remark` <span class="dt">varchar</span>(<span class="dv">500</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;备注&#39;</span>,</span>
<span id="cb57-18"><a href="#cb57-18" aria-hidden="true" tabindex="-1"></a> `payment_status` tinyint <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="st">&#39;1&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;缴费状态(0:待确认,1:成功,2:失败,3:退费)&#39;</span>,</span>
<span id="cb57-19"><a href="#cb57-19" aria-hidden="true" tabindex="-1"></a> `bill_id` bigint <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;账单ID&#39;</span>,</span>
<span id="cb57-20"><a href="#cb57-20" aria-hidden="true" tabindex="-1"></a> `customer_id` bigint <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;客户ID&#39;</span>,</span>
<span id="cb57-21"><a href="#cb57-21" aria-hidden="true" tabindex="-1"></a> `tenant_id` bigint <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="st">&#39;0&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;租户ID&#39;</span>,</span>
<span id="cb57-22"><a href="#cb57-22" aria-hidden="true" tabindex="-1"></a> `creator` <span class="dt">varchar</span>(<span class="dv">64</span>) <span class="kw">DEFAULT</span> <span class="st">&#39;&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;创建者&#39;</span>,</span>
<span id="cb57-23"><a href="#cb57-23" aria-hidden="true" tabindex="-1"></a> `create_time` datetime <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="fu">CURRENT_TIMESTAMP</span> <span class="kw">COMMENT</span> <span class="st">&#39;创建时间&#39;</span>,</span>
<span id="cb57-24"><a href="#cb57-24" aria-hidden="true" tabindex="-1"></a> `updater` <span class="dt">varchar</span>(<span class="dv">64</span>) <span class="kw">DEFAULT</span> <span class="st">&#39;&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;更新者&#39;</span>,</span>
<span id="cb57-25"><a href="#cb57-25" aria-hidden="true" tabindex="-1"></a> `update_time` datetime <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="fu">CURRENT_TIMESTAMP</span> <span class="kw">ON</span> <span class="kw">UPDATE</span> <span class="fu">CURRENT_TIMESTAMP</span> <span class="kw">COMMENT</span> <span class="st">&#39;更新时间&#39;</span>,</span>
<span id="cb57-26"><a href="#cb57-26" aria-hidden="true" tabindex="-1"></a> `deleted` bit(<span class="dv">1</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> b<span class="st">&#39;0&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;是否删除&#39;</span>,</span>
<span id="cb57-27"><a href="#cb57-27" aria-hidden="true" tabindex="-1"></a> <span class="kw">PRIMARY</span> <span class="kw">KEY</span> (`id`),</span>
<span id="cb57-28"><a href="#cb57-28" aria-hidden="true" tabindex="-1"></a> <span class="kw">UNIQUE</span> <span class="kw">KEY</span> `uk_payment_code` (`payment_code`, `tenant_id`),</span>
<span id="cb57-29"><a href="#cb57-29" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_customer_payment_time` (`customer_id`, `payment_time`),</span>
<span id="cb57-30"><a href="#cb57-30" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_payment_time` (`payment_time`),</span>
<span id="cb57-31"><a href="#cb57-31" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_transaction_no` (`transaction_no`),</span>
<span id="cb57-32"><a href="#cb57-32" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_operator_id` (`operator_id`),</span>
<span id="cb57-33"><a href="#cb57-33" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_payment_status` (`payment_status`),</span>
<span id="cb57-34"><a href="#cb57-34" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_bill_id` (`bill_id`),</span>
<span id="cb57-35"><a href="#cb57-35" aria-hidden="true" tabindex="-1"></a> <span class="kw">CONSTRAINT</span> `fk_payment_customer` <span class="kw">FOREIGN</span> <span class="kw">KEY</span> (`customer_id`) <span class="kw">REFERENCES</span> `water_customer` (`id`),</span>
<span id="cb57-36"><a href="#cb57-36" aria-hidden="true" tabindex="-1"></a> <span class="kw">CONSTRAINT</span> `fk_payment_bill` <span class="kw">FOREIGN</span> <span class="kw">KEY</span> (`bill_id`) <span class="kw">REFERENCES</span> `water_bill` (`id`)</span>
<span id="cb57-37"><a href="#cb57-37" aria-hidden="true" tabindex="-1"></a>) ENGINE<span class="op">=</span>InnoDB <span class="kw">DEFAULT</span> CHARSET<span class="op">=</span>utf8mb4 <span class="kw">COMMENT</span><span class="op">=</span><span class="st">&#39;缴费记录表&#39;</span>;</span></code></pre></div>
<h5 data-number="4.0.0.0.37" id="表务管理相关表ddl"><span
class="header-section-number">4.0.0.0.37</span> 3.
表务管理相关表DDL</h5>
<h5 data-number="4.0.0.0.38" id="水表档案表"><span
class="header-section-number">4.0.0.0.38</span> 3.1 水表档案表</h5>
<div class="sourceCode" id="cb58"><pre
class="sourceCode sql"><code class="sourceCode sql"><span id="cb58-1"><a href="#cb58-1" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">TABLE</span> `water_meter_archive` (</span>
<span id="cb58-2"><a href="#cb58-2" aria-hidden="true" tabindex="-1"></a> `id` bigint <span class="kw">NOT</span> <span class="kw">NULL</span> AUTO_INCREMENT <span class="kw">COMMENT</span> <span class="st">&#39;主键ID&#39;</span>,</span>
<span id="cb58-3"><a href="#cb58-3" aria-hidden="true" tabindex="-1"></a> `archive_code` <span class="dt">varchar</span>(<span class="dv">32</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;档案编号&#39;</span>,</span>
<span id="cb58-4"><a href="#cb58-4" aria-hidden="true" tabindex="-1"></a> `manufacturer` <span class="dt">varchar</span>(<span class="dv">100</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;生产厂家&#39;</span>,</span>
<span id="cb58-5"><a href="#cb58-5" aria-hidden="true" tabindex="-1"></a> `production_date` <span class="dt">date</span> <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;生产日期&#39;</span>,</span>
<span id="cb58-6"><a href="#cb58-6" aria-hidden="true" tabindex="-1"></a> `valid_period` <span class="dt">int</span> <span class="kw">DEFAULT</span> <span class="st">&#39;72&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;有效期(月)&#39;</span>,</span>
<span id="cb58-7"><a href="#cb58-7" aria-hidden="true" tabindex="-1"></a> `verification_date` <span class="dt">date</span> <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;检定日期&#39;</span>,</span>
<span id="cb58-8"><a href="#cb58-8" aria-hidden="true" tabindex="-1"></a> `next_verification_date` <span class="dt">date</span> <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;下次检定日期&#39;</span>,</span>
<span id="cb58-9"><a href="#cb58-9" aria-hidden="true" tabindex="-1"></a> `certificate_no` <span class="dt">varchar</span>(<span class="dv">50</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;检定证书号&#39;</span>,</span>
<span id="cb58-10"><a href="#cb58-10" aria-hidden="true" tabindex="-1"></a> `verification_agency` <span class="dt">varchar</span>(<span class="dv">100</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;检定机构&#39;</span>,</span>
<span id="cb58-11"><a href="#cb58-11" aria-hidden="true" tabindex="-1"></a> `accuracy_level` <span class="dt">varchar</span>(<span class="dv">10</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;精度等级&#39;</span>,</span>
<span id="cb58-12"><a href="#cb58-12" aria-hidden="true" tabindex="-1"></a> `max_flow` <span class="dt">decimal</span>(<span class="dv">8</span>,<span class="dv">2</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;最大流量&#39;</span>,</span>
<span id="cb58-13"><a href="#cb58-13" aria-hidden="true" tabindex="-1"></a> `nominal_flow` <span class="dt">decimal</span>(<span class="dv">8</span>,<span class="dv">2</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;常用流量&#39;</span>,</span>
<span id="cb58-14"><a href="#cb58-14" aria-hidden="true" tabindex="-1"></a> `min_flow` <span class="dt">decimal</span>(<span class="dv">8</span>,<span class="dv">2</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;最小流量&#39;</span>,</span>
<span id="cb58-15"><a href="#cb58-15" aria-hidden="true" tabindex="-1"></a> `working_pressure` <span class="dt">decimal</span>(<span class="dv">8</span>,<span class="dv">2</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;工作压力&#39;</span>,</span>
<span id="cb58-16"><a href="#cb58-16" aria-hidden="true" tabindex="-1"></a> `purchase_date` <span class="dt">date</span> <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;采购日期&#39;</span>,</span>
<span id="cb58-17"><a href="#cb58-17" aria-hidden="true" tabindex="-1"></a> `purchase_price` <span class="dt">decimal</span>(<span class="dv">10</span>,<span class="dv">2</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;采购价格&#39;</span>,</span>
<span id="cb58-18"><a href="#cb58-18" aria-hidden="true" tabindex="-1"></a> `supplier` <span class="dt">varchar</span>(<span class="dv">100</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;供应商&#39;</span>,</span>
<span id="cb58-19"><a href="#cb58-19" aria-hidden="true" tabindex="-1"></a> `warranty_period` <span class="dt">int</span> <span class="kw">DEFAULT</span> <span class="st">&#39;24&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;质保期(月)&#39;</span>,</span>
<span id="cb58-20"><a href="#cb58-20" aria-hidden="true" tabindex="-1"></a> `installation_cost` <span class="dt">decimal</span>(<span class="dv">10</span>,<span class="dv">2</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;安装费用&#39;</span>,</span>
<span id="cb58-21"><a href="#cb58-21" aria-hidden="true" tabindex="-1"></a> `archive_status` tinyint <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="st">&#39;1&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;档案状态(0:停用,1:正常,2:报废)&#39;</span>,</span>
<span id="cb58-22"><a href="#cb58-22" aria-hidden="true" tabindex="-1"></a> `meter_id` bigint <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;水表ID&#39;</span>,</span>
<span id="cb58-23"><a href="#cb58-23" aria-hidden="true" tabindex="-1"></a> `tenant_id` bigint <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="st">&#39;0&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;租户ID&#39;</span>,</span>
<span id="cb58-24"><a href="#cb58-24" aria-hidden="true" tabindex="-1"></a> `creator` <span class="dt">varchar</span>(<span class="dv">64</span>) <span class="kw">DEFAULT</span> <span class="st">&#39;&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;创建者&#39;</span>,</span>
<span id="cb58-25"><a href="#cb58-25" aria-hidden="true" tabindex="-1"></a> `create_time` datetime <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="fu">CURRENT_TIMESTAMP</span> <span class="kw">COMMENT</span> <span class="st">&#39;创建时间&#39;</span>,</span>
<span id="cb58-26"><a href="#cb58-26" aria-hidden="true" tabindex="-1"></a> `updater` <span class="dt">varchar</span>(<span class="dv">64</span>) <span class="kw">DEFAULT</span> <span class="st">&#39;&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;更新者&#39;</span>,</span>
<span id="cb58-27"><a href="#cb58-27" aria-hidden="true" tabindex="-1"></a> `update_time` datetime <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="fu">CURRENT_TIMESTAMP</span> <span class="kw">ON</span> <span class="kw">UPDATE</span> <span class="fu">CURRENT_TIMESTAMP</span> <span class="kw">COMMENT</span> <span class="st">&#39;更新时间&#39;</span>,</span>
<span id="cb58-28"><a href="#cb58-28" aria-hidden="true" tabindex="-1"></a> `deleted` bit(<span class="dv">1</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> b<span class="st">&#39;0&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;是否删除&#39;</span>,</span>
<span id="cb58-29"><a href="#cb58-29" aria-hidden="true" tabindex="-1"></a> <span class="kw">PRIMARY</span> <span class="kw">KEY</span> (`id`),</span>
<span id="cb58-30"><a href="#cb58-30" aria-hidden="true" tabindex="-1"></a> <span class="kw">UNIQUE</span> <span class="kw">KEY</span> `uk_archive_code` (`archive_code`, `tenant_id`),</span>
<span id="cb58-31"><a href="#cb58-31" aria-hidden="true" tabindex="-1"></a> <span class="kw">UNIQUE</span> <span class="kw">KEY</span> `uk_meter_archive` (`meter_id`, `tenant_id`),</span>
<span id="cb58-32"><a href="#cb58-32" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_manufacturer` (`manufacturer`),</span>
<span id="cb58-33"><a href="#cb58-33" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_verification_date` (`verification_date`),</span>
<span id="cb58-34"><a href="#cb58-34" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_next_verification_date` (`next_verification_date`),</span>
<span id="cb58-35"><a href="#cb58-35" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_archive_status` (`archive_status`),</span>
<span id="cb58-36"><a href="#cb58-36" aria-hidden="true" tabindex="-1"></a> <span class="kw">CONSTRAINT</span> `fk_archive_meter` <span class="kw">FOREIGN</span> <span class="kw">KEY</span> (`meter_id`) <span class="kw">REFERENCES</span> `water_meter` (`id`)</span>
<span id="cb58-37"><a href="#cb58-37" aria-hidden="true" tabindex="-1"></a>) ENGINE<span class="op">=</span>InnoDB <span class="kw">DEFAULT</span> CHARSET<span class="op">=</span>utf8mb4 <span class="kw">COMMENT</span><span class="op">=</span><span class="st">&#39;水表档案表&#39;</span>;</span></code></pre></div>
<h5 data-number="4.0.0.0.39" id="系统管理相关表ddl-1"><span
class="header-section-number">4.0.0.0.39</span> 4.
系统管理相关表DDL</h5>
<h5 data-number="4.0.0.0.40" id="数据字典表"><span
class="header-section-number">4.0.0.0.40</span> 4.1 数据字典表</h5>
<div class="sourceCode" id="cb59"><pre
class="sourceCode sql"><code class="sourceCode sql"><span id="cb59-1"><a href="#cb59-1" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">TABLE</span> `water_dict_data` (</span>
<span id="cb59-2"><a href="#cb59-2" aria-hidden="true" tabindex="-1"></a> `id` bigint <span class="kw">NOT</span> <span class="kw">NULL</span> AUTO_INCREMENT <span class="kw">COMMENT</span> <span class="st">&#39;主键ID&#39;</span>,</span>
<span id="cb59-3"><a href="#cb59-3" aria-hidden="true" tabindex="-1"></a> `dict_type` <span class="dt">varchar</span>(<span class="dv">100</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;字典类型&#39;</span>,</span>
<span id="cb59-4"><a href="#cb59-4" aria-hidden="true" tabindex="-1"></a> `dict_label` <span class="dt">varchar</span>(<span class="dv">100</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;字典标签&#39;</span>,</span>
<span id="cb59-5"><a href="#cb59-5" aria-hidden="true" tabindex="-1"></a> `dict_value` <span class="dt">varchar</span>(<span class="dv">100</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;字典键值&#39;</span>,</span>
<span id="cb59-6"><a href="#cb59-6" aria-hidden="true" tabindex="-1"></a> `dict_sort` <span class="dt">int</span> <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="st">&#39;0&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;字典排序&#39;</span>,</span>
<span id="cb59-7"><a href="#cb59-7" aria-hidden="true" tabindex="-1"></a> `color_type` <span class="dt">varchar</span>(<span class="dv">100</span>) <span class="kw">DEFAULT</span> <span class="st">&#39;&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;颜色类型&#39;</span>,</span>
<span id="cb59-8"><a href="#cb59-8" aria-hidden="true" tabindex="-1"></a> `css_class` <span class="dt">varchar</span>(<span class="dv">100</span>) <span class="kw">DEFAULT</span> <span class="st">&#39;&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;CSS类名&#39;</span>,</span>
<span id="cb59-9"><a href="#cb59-9" aria-hidden="true" tabindex="-1"></a> `remark` <span class="dt">varchar</span>(<span class="dv">500</span>) <span class="kw">DEFAULT</span> <span class="kw">NULL</span> <span class="kw">COMMENT</span> <span class="st">&#39;备注&#39;</span>,</span>
<span id="cb59-10"><a href="#cb59-10" aria-hidden="true" tabindex="-1"></a> `status` tinyint <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="st">&#39;1&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;状态(0:停用,1:正常)&#39;</span>,</span>
<span id="cb59-11"><a href="#cb59-11" aria-hidden="true" tabindex="-1"></a> `tenant_id` bigint <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="st">&#39;0&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;租户ID&#39;</span>,</span>
<span id="cb59-12"><a href="#cb59-12" aria-hidden="true" tabindex="-1"></a> `creator` <span class="dt">varchar</span>(<span class="dv">64</span>) <span class="kw">DEFAULT</span> <span class="st">&#39;&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;创建者&#39;</span>,</span>
<span id="cb59-13"><a href="#cb59-13" aria-hidden="true" tabindex="-1"></a> `create_time` datetime <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="fu">CURRENT_TIMESTAMP</span> <span class="kw">COMMENT</span> <span class="st">&#39;创建时间&#39;</span>,</span>
<span id="cb59-14"><a href="#cb59-14" aria-hidden="true" tabindex="-1"></a> `updater` <span class="dt">varchar</span>(<span class="dv">64</span>) <span class="kw">DEFAULT</span> <span class="st">&#39;&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;更新者&#39;</span>,</span>
<span id="cb59-15"><a href="#cb59-15" aria-hidden="true" tabindex="-1"></a> `update_time` datetime <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> <span class="fu">CURRENT_TIMESTAMP</span> <span class="kw">ON</span> <span class="kw">UPDATE</span> <span class="fu">CURRENT_TIMESTAMP</span> <span class="kw">COMMENT</span> <span class="st">&#39;更新时间&#39;</span>,</span>
<span id="cb59-16"><a href="#cb59-16" aria-hidden="true" tabindex="-1"></a> `deleted` bit(<span class="dv">1</span>) <span class="kw">NOT</span> <span class="kw">NULL</span> <span class="kw">DEFAULT</span> b<span class="st">&#39;0&#39;</span> <span class="kw">COMMENT</span> <span class="st">&#39;是否删除&#39;</span>,</span>
<span id="cb59-17"><a href="#cb59-17" aria-hidden="true" tabindex="-1"></a> <span class="kw">PRIMARY</span> <span class="kw">KEY</span> (`id`),</span>
<span id="cb59-18"><a href="#cb59-18" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_dict_type` (`dict_type`),</span>
<span id="cb59-19"><a href="#cb59-19" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_dict_value` (`dict_value`),</span>
<span id="cb59-20"><a href="#cb59-20" aria-hidden="true" tabindex="-1"></a> <span class="kw">KEY</span> `idx_status` (`status`)</span>
<span id="cb59-21"><a href="#cb59-21" aria-hidden="true" tabindex="-1"></a>) ENGINE<span class="op">=</span>InnoDB <span class="kw">DEFAULT</span> CHARSET<span class="op">=</span>utf8mb4 <span class="kw">COMMENT</span><span class="op">=</span><span class="st">&#39;数据字典表&#39;</span>;</span></code></pre></div>
<h5 data-number="4.0.0.0.41" id="初始化数据脚本"><span
class="header-section-number">4.0.0.0.41</span> 5. 初始化数据脚本</h5>
<div class="sourceCode" id="cb60"><pre
class="sourceCode sql"><code class="sourceCode sql"><span id="cb60-1"><a href="#cb60-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- 插入基础字典数据</span></span>
<span id="cb60-2"><a href="#cb60-2" aria-hidden="true" tabindex="-1"></a><span class="kw">INSERT</span> <span class="kw">INTO</span> `water_dict_data` (`dict_type`, `dict_label`, `dict_value`, `dict_sort`, `status`, `remark`) <span class="kw">VALUES</span></span>
<span id="cb60-3"><a href="#cb60-3" aria-hidden="true" tabindex="-1"></a>(<span class="st">&#39;customer_type&#39;</span>, <span class="st">&#39;居民用户&#39;</span>, <span class="st">&#39;RESIDENT&#39;</span>, <span class="dv">1</span>, <span class="dv">1</span>, <span class="st">&#39;居民生活用水&#39;</span>),</span>
<span id="cb60-4"><a href="#cb60-4" aria-hidden="true" tabindex="-1"></a>(<span class="st">&#39;customer_type&#39;</span>, <span class="st">&#39;非居民用户&#39;</span>, <span class="st">&#39;NON_RESIDENT&#39;</span>, <span class="dv">2</span>, <span class="dv">1</span>, <span class="st">&#39;非居民用水&#39;</span>),</span>
<span id="cb60-5"><a href="#cb60-5" aria-hidden="true" tabindex="-1"></a>(<span class="st">&#39;customer_type&#39;</span>, <span class="st">&#39;工业用户&#39;</span>, <span class="st">&#39;INDUSTRIAL&#39;</span>, <span class="dv">3</span>, <span class="dv">1</span>, <span class="st">&#39;工业生产用水&#39;</span>),</span>
<span id="cb60-6"><a href="#cb60-6" aria-hidden="true" tabindex="-1"></a>(<span class="st">&#39;customer_type&#39;</span>, <span class="st">&#39;行政用户&#39;</span>, <span class="st">&#39;ADMINISTRATIVE&#39;</span>, <span class="dv">4</span>, <span class="dv">1</span>, <span class="st">&#39;行政事业单位用水&#39;</span>),</span>
<span id="cb60-7"><a href="#cb60-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb60-8"><a href="#cb60-8" aria-hidden="true" tabindex="-1"></a>(<span class="st">&#39;meter_type&#39;</span>, <span class="st">&#39;机械水表&#39;</span>, <span class="st">&#39;MECHANICAL&#39;</span>, <span class="dv">1</span>, <span class="dv">1</span>, <span class="st">&#39;传统机械式水表&#39;</span>),</span>
<span id="cb60-9"><a href="#cb60-9" aria-hidden="true" tabindex="-1"></a>(<span class="st">&#39;meter_type&#39;</span>, <span class="st">&#39;智能水表&#39;</span>, <span class="st">&#39;SMART&#39;</span>, <span class="dv">2</span>, <span class="dv">1</span>, <span class="st">&#39;智能电子水表&#39;</span>),</span>
<span id="cb60-10"><a href="#cb60-10" aria-hidden="true" tabindex="-1"></a>(<span class="st">&#39;meter_type&#39;</span>, <span class="st">&#39;远传水表&#39;</span>, <span class="st">&#39;REMOTE&#39;</span>, <span class="dv">3</span>, <span class="dv">1</span>, <span class="st">&#39;远程传输水表&#39;</span>),</span>
<span id="cb60-11"><a href="#cb60-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb60-12"><a href="#cb60-12" aria-hidden="true" tabindex="-1"></a>(<span class="st">&#39;reading_type&#39;</span>, <span class="st">&#39;人工抄表&#39;</span>, <span class="st">&#39;MANUAL&#39;</span>, <span class="dv">1</span>, <span class="dv">1</span>, <span class="st">&#39;抄表员现场抄表&#39;</span>),</span>
<span id="cb60-13"><a href="#cb60-13" aria-hidden="true" tabindex="-1"></a>(<span class="st">&#39;reading_type&#39;</span>, <span class="st">&#39;远程抄表&#39;</span>, <span class="st">&#39;REMOTE&#39;</span>, <span class="dv">2</span>, <span class="dv">1</span>, <span class="st">&#39;远程自动抄表&#39;</span>),</span>
<span id="cb60-14"><a href="#cb60-14" aria-hidden="true" tabindex="-1"></a>(<span class="st">&#39;reading_type&#39;</span>, <span class="st">&#39;客户自报&#39;</span>, <span class="st">&#39;SELF_REPORT&#39;</span>, <span class="dv">3</span>, <span class="dv">1</span>, <span class="st">&#39;客户自主上报&#39;</span>),</span>
<span id="cb60-15"><a href="#cb60-15" aria-hidden="true" tabindex="-1"></a>(<span class="st">&#39;reading_type&#39;</span>, <span class="st">&#39;估算抄表&#39;</span>, <span class="st">&#39;ESTIMATE&#39;</span>, <span class="dv">4</span>, <span class="dv">1</span>, <span class="st">&#39;估算用水量&#39;</span>),</span>
<span id="cb60-16"><a href="#cb60-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb60-17"><a href="#cb60-17" aria-hidden="true" tabindex="-1"></a>(<span class="st">&#39;payment_channel&#39;</span>, <span class="st">&#39;现金&#39;</span>, <span class="st">&#39;CASH&#39;</span>, <span class="dv">1</span>, <span class="dv">1</span>, <span class="st">&#39;现金缴费&#39;</span>),</span>
<span id="cb60-18"><a href="#cb60-18" aria-hidden="true" tabindex="-1"></a>(<span class="st">&#39;payment_channel&#39;</span>, <span class="st">&#39;银行卡&#39;</span>, <span class="st">&#39;BANK_CARD&#39;</span>, <span class="dv">2</span>, <span class="dv">1</span>, <span class="st">&#39;银行卡刷卡&#39;</span>),</span>
<span id="cb60-19"><a href="#cb60-19" aria-hidden="true" tabindex="-1"></a>(<span class="st">&#39;payment_channel&#39;</span>, <span class="st">&#39;微信支付&#39;</span>, <span class="st">&#39;WECHAT&#39;</span>, <span class="dv">3</span>, <span class="dv">1</span>, <span class="st">&#39;微信在线支付&#39;</span>),</span>
<span id="cb60-20"><a href="#cb60-20" aria-hidden="true" tabindex="-1"></a>(<span class="st">&#39;payment_channel&#39;</span>, <span class="st">&#39;支付宝&#39;</span>, <span class="st">&#39;ALIPAY&#39;</span>, <span class="dv">4</span>, <span class="dv">1</span>, <span class="st">&#39;支付宝在线支付&#39;</span>),</span>
<span id="cb60-21"><a href="#cb60-21" aria-hidden="true" tabindex="-1"></a>(<span class="st">&#39;payment_channel&#39;</span>, <span class="st">&#39;银行代扣&#39;</span>, <span class="st">&#39;BANK_DEDUCT&#39;</span>, <span class="dv">5</span>, <span class="dv">1</span>, <span class="st">&#39;银行自动代扣&#39;</span>);</span></code></pre></div>
<h5 data-number="4.0.0.0.42" id="六opengauss数据库特性应用"><span
class="header-section-number">4.0.0.0.42</span>
六、OpenGauss数据库特性应用</h5>
<h5 data-number="4.0.0.0.43" id="opengauss高级特性"><span
class="header-section-number">4.0.0.0.43</span> 1.
OpenGauss高级特性</h5>
<h5 data-number="4.0.0.0.44" id="分区表设计"><span
class="header-section-number">4.0.0.0.44</span> 1.1 分区表设计</h5>
<div class="sourceCode" id="cb61"><pre
class="sourceCode sql"><code class="sourceCode sql"><span id="cb61-1"><a href="#cb61-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- 按月份分区的账单表</span></span>
<span id="cb61-2"><a href="#cb61-2" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">TABLE</span> water_bill (</span>
<span id="cb61-3"><a href="#cb61-3" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 字段定义...</span></span>
<span id="cb61-4"><a href="#cb61-4" aria-hidden="true" tabindex="-1"></a>) <span class="kw">PARTITION</span> <span class="kw">BY</span> <span class="kw">RANGE</span> (bill_month) (</span>
<span id="cb61-5"><a href="#cb61-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">PARTITION</span> p202401 <span class="kw">VALUES</span> <span class="kw">LESS</span> <span class="kw">THAN</span> (<span class="st">&#39;2024-02&#39;</span>),</span>
<span id="cb61-6"><a href="#cb61-6" aria-hidden="true" tabindex="-1"></a> <span class="kw">PARTITION</span> p202402 <span class="kw">VALUES</span> <span class="kw">LESS</span> <span class="kw">THAN</span> (<span class="st">&#39;2024-03&#39;</span>),</span>
<span id="cb61-7"><a href="#cb61-7" aria-hidden="true" tabindex="-1"></a> <span class="kw">PARTITION</span> p202403 <span class="kw">VALUES</span> <span class="kw">LESS</span> <span class="kw">THAN</span> (<span class="st">&#39;2024-04&#39;</span>)</span>
<span id="cb61-8"><a href="#cb61-8" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 继续按月创建分区...</span></span>
<span id="cb61-9"><a href="#cb61-9" aria-hidden="true" tabindex="-1"></a>);</span></code></pre></div>
<h5 data-number="4.0.0.0.45" id="列存储表"><span
class="header-section-number">4.0.0.0.45</span> 1.2 列存储表</h5>
<div class="sourceCode" id="cb62"><pre
class="sourceCode sql"><code class="sourceCode sql"><span id="cb62-1"><a href="#cb62-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- 统计分析用的列存储表</span></span>
<span id="cb62-2"><a href="#cb62-2" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">TABLE</span> water_bill_stats (</span>
<span id="cb62-3"><a href="#cb62-3" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 字段定义...</span></span>
<span id="cb62-4"><a href="#cb62-4" aria-hidden="true" tabindex="-1"></a>) <span class="kw">WITH</span> (ORIENTATION <span class="op">=</span> <span class="kw">COLUMN</span>);</span></code></pre></div>
<h5 data-number="4.0.0.0.46" id="内存表"><span
class="header-section-number">4.0.0.0.46</span> 1.3 内存表</h5>
<div class="sourceCode" id="cb63"><pre
class="sourceCode sql"><code class="sourceCode sql"><span id="cb63-1"><a href="#cb63-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- 配置参数缓存表</span></span>
<span id="cb63-2"><a href="#cb63-2" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">TABLE</span> water_config_cache (</span>
<span id="cb63-3"><a href="#cb63-3" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 字段定义...</span></span>
<span id="cb63-4"><a href="#cb63-4" aria-hidden="true" tabindex="-1"></a>) <span class="kw">WITH</span> (ORIENTATION <span class="op">=</span> <span class="kw">ROW</span>, STORAGE_TYPE <span class="op">=</span> USTORE);</span></code></pre></div>
<h5 data-number="4.0.0.0.47" id="性能优化配置"><span
class="header-section-number">4.0.0.0.47</span> 2. 性能优化配置</h5>
<h5 data-number="4.0.0.0.48" id="连接池配置"><span
class="header-section-number">4.0.0.0.48</span> 2.1 连接池配置</h5>
<div class="sourceCode" id="cb64"><pre
class="sourceCode sql"><code class="sourceCode sql"><span id="cb64-1"><a href="#cb64-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- OpenGauss连接池参数</span></span>
<span id="cb64-2"><a href="#cb64-2" aria-hidden="true" tabindex="-1"></a><span class="kw">ALTER</span> <span class="kw">SYSTEM</span> <span class="kw">SET</span> max_connections <span class="op">=</span> <span class="dv">200</span>;</span>
<span id="cb64-3"><a href="#cb64-3" aria-hidden="true" tabindex="-1"></a><span class="kw">ALTER</span> <span class="kw">SYSTEM</span> <span class="kw">SET</span> shared_buffers <span class="op">=</span> <span class="st">&#39;256MB&#39;</span>;</span>
<span id="cb64-4"><a href="#cb64-4" aria-hidden="true" tabindex="-1"></a><span class="kw">ALTER</span> <span class="kw">SYSTEM</span> <span class="kw">SET</span> effective_cache_size <span class="op">=</span> <span class="st">&#39;1GB&#39;</span>;</span>
<span id="cb64-5"><a href="#cb64-5" aria-hidden="true" tabindex="-1"></a><span class="kw">ALTER</span> <span class="kw">SYSTEM</span> <span class="kw">SET</span> work_mem <span class="op">=</span> <span class="st">&#39;4MB&#39;</span>;</span></code></pre></div>
<h5 data-number="4.0.0.0.49" id="并行查询"><span
class="header-section-number">4.0.0.0.49</span> 2.2 并行查询</h5>
<div class="sourceCode" id="cb65"><pre
class="sourceCode sql"><code class="sourceCode sql"><span id="cb65-1"><a href="#cb65-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- 启用并行查询</span></span>
<span id="cb65-2"><a href="#cb65-2" aria-hidden="true" tabindex="-1"></a><span class="kw">ALTER</span> <span class="kw">SYSTEM</span> <span class="kw">SET</span> max_parallel_workers <span class="op">=</span> <span class="dv">8</span>;</span>
<span id="cb65-3"><a href="#cb65-3" aria-hidden="true" tabindex="-1"></a><span class="kw">ALTER</span> <span class="kw">SYSTEM</span> <span class="kw">SET</span> max_parallel_workers_per_gather <span class="op">=</span> <span class="dv">4</span>;</span></code></pre></div>
<h5 data-number="4.0.0.0.50" id="七索引设计与优化"><span
class="header-section-number">4.0.0.0.50</span> 七、索引设计与优化</h5>
<h5 data-number="4.0.0.0.51" id="基础索引设计"><span
class="header-section-number">4.0.0.0.51</span> 1. 基础索引设计</h5>
<h5 data-number="4.0.0.0.52" id="主键索引"><span
class="header-section-number">4.0.0.0.52</span> 1.1 主键索引</h5>
<ul>
<li>所有表使用SERIAL主键自动创建B树索引</li>
<li>支持OpenGauss的快速主键查询优化</li>
</ul>
<h5 data-number="4.0.0.0.53" id="唯一索引"><span
class="header-section-number">4.0.0.0.53</span> 1.2 唯一索引</h5>
<div class="sourceCode" id="cb66"><pre
class="sourceCode sql"><code class="sourceCode sql"><span id="cb66-1"><a href="#cb66-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- 客户编号唯一索引</span></span>
<span id="cb66-2"><a href="#cb66-2" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">UNIQUE</span> <span class="kw">INDEX</span> uk_customer_code <span class="kw">ON</span> water_customer (customer_code, tenant_id);</span>
<span id="cb66-3"><a href="#cb66-3" aria-hidden="true" tabindex="-1"></a><span class="co">-- 水表编号唯一索引 </span></span>
<span id="cb66-4"><a href="#cb66-4" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">UNIQUE</span> <span class="kw">INDEX</span> uk_meter_code <span class="kw">ON</span> water_meter (meter_code, tenant_id);</span></code></pre></div>
<h5 data-number="4.0.0.0.54" id="复合索引"><span
class="header-section-number">4.0.0.0.54</span> 1.3 复合索引</h5>
<div class="sourceCode" id="cb67"><pre
class="sourceCode sql"><code class="sourceCode sql"><span id="cb67-1"><a href="#cb67-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- 多租户查询优化</span></span>
<span id="cb67-2"><a href="#cb67-2" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">INDEX</span> idx_tenant_customer_type <span class="kw">ON</span> water_customer (tenant_id, customer_type);</span>
<span id="cb67-3"><a href="#cb67-3" aria-hidden="true" tabindex="-1"></a><span class="co">-- 账单查询优化</span></span>
<span id="cb67-4"><a href="#cb67-4" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">INDEX</span> idx_tenant_customer_month <span class="kw">ON</span> water_bill (tenant_id, customer_id, bill_month);</span>
<span id="cb67-5"><a href="#cb67-5" aria-hidden="true" tabindex="-1"></a><span class="co">-- 抄表查询优化</span></span>
<span id="cb67-6"><a href="#cb67-6" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">INDEX</span> idx_tenant_meter_date <span class="kw">ON</span> water_meter_reading (tenant_id, meter_id, reading_date);</span></code></pre></div>
<h5 data-number="4.0.0.0.55" id="opengauss特殊索引"><span
class="header-section-number">4.0.0.0.55</span> 2.
OpenGauss特殊索引</h5>
<h5 data-number="4.0.0.0.56" id="hash索引"><span
class="header-section-number">4.0.0.0.56</span> 2.1 HASH索引</h5>
<div class="sourceCode" id="cb68"><pre
class="sourceCode sql"><code class="sourceCode sql"><span id="cb68-1"><a href="#cb68-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- 等值查询优化</span></span>
<span id="cb68-2"><a href="#cb68-2" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">INDEX</span> CONCURRENTLY idx_customer_phone_hash <span class="kw">ON</span> water_customer <span class="kw">USING</span> <span class="kw">HASH</span> (phone);</span></code></pre></div>
<h5 data-number="4.0.0.0.57" id="gin索引"><span
class="header-section-number">4.0.0.0.57</span> 2.2 GIN索引</h5>
<div class="sourceCode" id="cb69"><pre
class="sourceCode sql"><code class="sourceCode sql"><span id="cb69-1"><a href="#cb69-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- 全文检索索引</span></span>
<span id="cb69-2"><a href="#cb69-2" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">INDEX</span> idx_customer_name_gin <span class="kw">ON</span> water_customer <span class="kw">USING</span> GIN (to_tsvector(<span class="st">&#39;simple&#39;</span>, customer_name));</span></code></pre></div>
<h5 data-number="4.0.0.0.58" id="部分索引"><span
class="header-section-number">4.0.0.0.58</span> 2.3 部分索引</h5>
<div class="sourceCode" id="cb70"><pre
class="sourceCode sql"><code class="sourceCode sql"><span id="cb70-1"><a href="#cb70-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- 只对有效数据建索引</span></span>
<span id="cb70-2"><a href="#cb70-2" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">INDEX</span> idx_active_customers <span class="kw">ON</span> water_customer (customer_code) <span class="kw">WHERE</span> deleted <span class="op">=</span> <span class="kw">FALSE</span> <span class="kw">AND</span> status <span class="op">=</span> <span class="dv">1</span>;</span></code></pre></div>
<h5 data-number="4.0.0.0.59" id="八opengauss数据安全设计"><span
class="header-section-number">4.0.0.0.59</span>
八、OpenGauss数据安全设计</h5>
<h5 data-number="4.0.0.0.60" id="内置安全特性"><span
class="header-section-number">4.0.0.0.60</span> 1. 内置安全特性</h5>
<h5 data-number="4.0.0.0.61" id="透明数据加密-tde"><span
class="header-section-number">4.0.0.0.61</span> 1.1 透明数据加密
(TDE)</h5>
<div class="sourceCode" id="cb71"><pre
class="sourceCode sql"><code class="sourceCode sql"><span id="cb71-1"><a href="#cb71-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- 启用表级加密</span></span>
<span id="cb71-2"><a href="#cb71-2" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">TABLE</span> water_customer_encrypted (</span>
<span id="cb71-3"><a href="#cb71-3" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 字段定义...</span></span>
<span id="cb71-4"><a href="#cb71-4" aria-hidden="true" tabindex="-1"></a>) <span class="kw">WITH</span> (ENCRYPTION_TYPE <span class="op">=</span> <span class="st">&#39;AES_128_CTR&#39;</span>);</span>
<span id="cb71-5"><a href="#cb71-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb71-6"><a href="#cb71-6" aria-hidden="true" tabindex="-1"></a><span class="co">-- 敏感字段加密</span></span>
<span id="cb71-7"><a href="#cb71-7" aria-hidden="true" tabindex="-1"></a><span class="kw">ALTER</span> <span class="kw">TABLE</span> water_customer </span>
<span id="cb71-8"><a href="#cb71-8" aria-hidden="true" tabindex="-1"></a><span class="kw">ADD</span> <span class="kw">COLUMN</span> id_number_encrypted BYTEA </span>
<span id="cb71-9"><a href="#cb71-9" aria-hidden="true" tabindex="-1"></a><span class="kw">GENERATED</span> ALWAYS <span class="kw">AS</span> (gs_encrypt_aes128(id_number, <span class="st">&#39;密钥&#39;</span>)) STORED;</span></code></pre></div>
<h5 data-number="4.0.0.0.62" id="行级安全策略-rls"><span
class="header-section-number">4.0.0.0.62</span> 1.2 行级安全策略
(RLS)</h5>
<div class="sourceCode" id="cb72"><pre
class="sourceCode sql"><code class="sourceCode sql"><span id="cb72-1"><a href="#cb72-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- 创建多租户行级安全</span></span>
<span id="cb72-2"><a href="#cb72-2" aria-hidden="true" tabindex="-1"></a><span class="kw">ALTER</span> <span class="kw">TABLE</span> water_customer <span class="kw">ENABLE</span> <span class="kw">ROW</span> <span class="kw">LEVEL</span> SECURITY;</span>
<span id="cb72-3"><a href="#cb72-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb72-4"><a href="#cb72-4" aria-hidden="true" tabindex="-1"></a><span class="co">-- 创建策略</span></span>
<span id="cb72-5"><a href="#cb72-5" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> POLICY tenant_isolation_policy <span class="kw">ON</span> water_customer</span>
<span id="cb72-6"><a href="#cb72-6" aria-hidden="true" tabindex="-1"></a> <span class="cf">FOR</span> <span class="kw">ALL</span> <span class="kw">TO</span> <span class="kw">PUBLIC</span></span>
<span id="cb72-7"><a href="#cb72-7" aria-hidden="true" tabindex="-1"></a> <span class="kw">USING</span> (tenant_id <span class="op">=</span> current_setting(<span class="st">&#39;app.current_tenant_id&#39;</span>):<span class="ch">:bigint</span>);</span></code></pre></div>
<h5 data-number="4.0.0.0.63" id="动态数据脱敏"><span
class="header-section-number">4.0.0.0.63</span> 1.3 动态数据脱敏</h5>
<div class="sourceCode" id="cb73"><pre
class="sourceCode sql"><code class="sourceCode sql"><span id="cb73-1"><a href="#cb73-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- 创建脱敏函数</span></span>
<span id="cb73-2"><a href="#cb73-2" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">OR</span> <span class="kw">REPLACE</span> <span class="kw">FUNCTION</span> mask_phone(phone_no TEXT)</span>
<span id="cb73-3"><a href="#cb73-3" aria-hidden="true" tabindex="-1"></a>RETURNS TEXT <span class="kw">AS</span> $$</span>
<span id="cb73-4"><a href="#cb73-4" aria-hidden="true" tabindex="-1"></a><span class="cf">BEGIN</span></span>
<span id="cb73-5"><a href="#cb73-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">RETURN</span> SUBSTRING(phone_no, <span class="dv">1</span>, <span class="dv">3</span>) <span class="op">||</span> <span class="st">&#39;****&#39;</span> <span class="op">||</span> SUBSTRING(phone_no, <span class="dv">8</span>);</span>
<span id="cb73-6"><a href="#cb73-6" aria-hidden="true" tabindex="-1"></a><span class="cf">END</span>;</span>
<span id="cb73-7"><a href="#cb73-7" aria-hidden="true" tabindex="-1"></a>$$ LANGUAGE plpgsql;</span>
<span id="cb73-8"><a href="#cb73-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb73-9"><a href="#cb73-9" aria-hidden="true" tabindex="-1"></a><span class="co">-- 创建脱敏视图</span></span>
<span id="cb73-10"><a href="#cb73-10" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">VIEW</span> water_customer_masked <span class="kw">AS</span></span>
<span id="cb73-11"><a href="#cb73-11" aria-hidden="true" tabindex="-1"></a><span class="kw">SELECT</span> </span>
<span id="cb73-12"><a href="#cb73-12" aria-hidden="true" tabindex="-1"></a> <span class="kw">id</span>, customer_code, customer_name,</span>
<span id="cb73-13"><a href="#cb73-13" aria-hidden="true" tabindex="-1"></a> mask_phone(phone) <span class="kw">as</span> phone_masked,</span>
<span id="cb73-14"><a href="#cb73-14" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 其他字段...</span></span>
<span id="cb73-15"><a href="#cb73-15" aria-hidden="true" tabindex="-1"></a><span class="kw">FROM</span> water_customer;</span></code></pre></div>
<h5 data-number="4.0.0.0.64" id="权限控制体系"><span
class="header-section-number">4.0.0.0.64</span> 2. 权限控制体系</h5>
<h5 data-number="4.0.0.0.65" id="角色权限设计"><span
class="header-section-number">4.0.0.0.65</span> 2.1 角色权限设计</h5>
<div class="sourceCode" id="cb74"><pre
class="sourceCode sql"><code class="sourceCode sql"><span id="cb74-1"><a href="#cb74-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- 创建业务角色</span></span>
<span id="cb74-2"><a href="#cb74-2" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">ROLE</span> water_admin;</span>
<span id="cb74-3"><a href="#cb74-3" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">ROLE</span> water_operator;</span>
<span id="cb74-4"><a href="#cb74-4" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">ROLE</span> water_viewer;</span>
<span id="cb74-5"><a href="#cb74-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb74-6"><a href="#cb74-6" aria-hidden="true" tabindex="-1"></a><span class="co">-- 分配权限</span></span>
<span id="cb74-7"><a href="#cb74-7" aria-hidden="true" tabindex="-1"></a><span class="kw">GRANT</span> <span class="kw">ALL</span> <span class="kw">ON</span> water_customer <span class="kw">TO</span> water_admin;</span>
<span id="cb74-8"><a href="#cb74-8" aria-hidden="true" tabindex="-1"></a><span class="kw">GRANT</span> <span class="kw">SELECT</span>, <span class="kw">INSERT</span>, <span class="kw">UPDATE</span> <span class="kw">ON</span> water_customer <span class="kw">TO</span> water_operator;</span>
<span id="cb74-9"><a href="#cb74-9" aria-hidden="true" tabindex="-1"></a><span class="kw">GRANT</span> <span class="kw">SELECT</span> <span class="kw">ON</span> water_customer_masked <span class="kw">TO</span> water_viewer;</span></code></pre></div>
<h5 data-number="4.0.0.0.66" id="列级权限控制"><span
class="header-section-number">4.0.0.0.66</span> 2.2 列级权限控制</h5>
<div class="sourceCode" id="cb75"><pre
class="sourceCode sql"><code class="sourceCode sql"><span id="cb75-1"><a href="#cb75-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- 敏感字段权限控制</span></span>
<span id="cb75-2"><a href="#cb75-2" aria-hidden="true" tabindex="-1"></a><span class="kw">REVOKE</span> <span class="kw">ALL</span> <span class="kw">ON</span> water_customer <span class="kw">FROM</span> <span class="kw">PUBLIC</span>;</span>
<span id="cb75-3"><a href="#cb75-3" aria-hidden="true" tabindex="-1"></a><span class="kw">GRANT</span> <span class="kw">SELECT</span> (<span class="kw">id</span>, customer_name, phone) <span class="kw">ON</span> water_customer <span class="kw">TO</span> water_viewer;</span>
<span id="cb75-4"><a href="#cb75-4" aria-hidden="true" tabindex="-1"></a><span class="kw">GRANT</span> <span class="kw">SELECT</span> <span class="kw">ON</span> water_customer <span class="kw">TO</span> water_admin;</span></code></pre></div>
<h5 data-number="4.0.0.0.67" id="审计与监控"><span
class="header-section-number">4.0.0.0.67</span> 3. 审计与监控</h5>
<h5 data-number="4.0.0.0.68" id="审计日志配置"><span
class="header-section-number">4.0.0.0.68</span> 3.1 审计日志配置</h5>
<div class="sourceCode" id="cb76"><pre
class="sourceCode sql"><code class="sourceCode sql"><span id="cb76-1"><a href="#cb76-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- 启用审计</span></span>
<span id="cb76-2"><a href="#cb76-2" aria-hidden="true" tabindex="-1"></a><span class="kw">ALTER</span> <span class="kw">SYSTEM</span> <span class="kw">SET</span> audit_enabled <span class="op">=</span> <span class="kw">on</span>;</span>
<span id="cb76-3"><a href="#cb76-3" aria-hidden="true" tabindex="-1"></a><span class="kw">ALTER</span> <span class="kw">SYSTEM</span> <span class="kw">SET</span> audit_directory <span class="op">=</span> <span class="st">&#39;/data/audit&#39;</span>;</span>
<span id="cb76-4"><a href="#cb76-4" aria-hidden="true" tabindex="-1"></a><span class="kw">ALTER</span> <span class="kw">SYSTEM</span> <span class="kw">SET</span> audit_file_remain_threshold <span class="op">=</span> <span class="dv">1024</span>;</span>
<span id="cb76-5"><a href="#cb76-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb76-6"><a href="#cb76-6" aria-hidden="true" tabindex="-1"></a><span class="co">-- 配置审计策略</span></span>
<span id="cb76-7"><a href="#cb76-7" aria-hidden="true" tabindex="-1"></a><span class="kw">SELECT</span> pg_audit_set_policy(<span class="st">&#39;DDL_LOGIN_LOGOUT&#39;</span>, <span class="st">&#39;DDL, LOGIN, LOGOUT&#39;</span>);</span>
<span id="cb76-8"><a href="#cb76-8" aria-hidden="true" tabindex="-1"></a><span class="kw">SELECT</span> pg_audit_set_policy(<span class="st">&#39;DML_DCL&#39;</span>, <span class="st">&#39;DML, DCL&#39;</span>);</span></code></pre></div>
<h5 data-number="4.0.0.0.69" id="敏感操作监控"><span
class="header-section-number">4.0.0.0.69</span> 3.2 敏感操作监控</h5>
<div class="sourceCode" id="cb77"><pre
class="sourceCode sql"><code class="sourceCode sql"><span id="cb77-1"><a href="#cb77-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- 创建敏感操作触发器</span></span>
<span id="cb77-2"><a href="#cb77-2" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">OR</span> <span class="kw">REPLACE</span> <span class="kw">FUNCTION</span> audit_sensitive_data()</span>
<span id="cb77-3"><a href="#cb77-3" aria-hidden="true" tabindex="-1"></a>RETURNS <span class="kw">TRIGGER</span> <span class="kw">AS</span> $$</span>
<span id="cb77-4"><a href="#cb77-4" aria-hidden="true" tabindex="-1"></a><span class="cf">BEGIN</span></span>
<span id="cb77-5"><a href="#cb77-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">INSERT</span> <span class="kw">INTO</span> audit_log (</span>
<span id="cb77-6"><a href="#cb77-6" aria-hidden="true" tabindex="-1"></a> table_name, operation, old_values, new_values, </span>
<span id="cb77-7"><a href="#cb77-7" aria-hidden="true" tabindex="-1"></a> user_name, operation_time</span>
<span id="cb77-8"><a href="#cb77-8" aria-hidden="true" tabindex="-1"></a> ) <span class="kw">VALUES</span> (</span>
<span id="cb77-9"><a href="#cb77-9" aria-hidden="true" tabindex="-1"></a> TG_TABLE_NAME, TG_OP, </span>
<span id="cb77-10"><a href="#cb77-10" aria-hidden="true" tabindex="-1"></a> row_to_json(<span class="kw">OLD</span>), row_to_json(<span class="kw">NEW</span>),</span>
<span id="cb77-11"><a href="#cb77-11" aria-hidden="true" tabindex="-1"></a> current_user, <span class="fu">current_timestamp</span></span>
<span id="cb77-12"><a href="#cb77-12" aria-hidden="true" tabindex="-1"></a> );</span>
<span id="cb77-13"><a href="#cb77-13" aria-hidden="true" tabindex="-1"></a> <span class="kw">RETURN</span> <span class="fu">COALESCE</span>(<span class="kw">NEW</span>, <span class="kw">OLD</span>);</span>
<span id="cb77-14"><a href="#cb77-14" aria-hidden="true" tabindex="-1"></a><span class="cf">END</span>;</span>
<span id="cb77-15"><a href="#cb77-15" aria-hidden="true" tabindex="-1"></a>$$ LANGUAGE plpgsql;</span>
<span id="cb77-16"><a href="#cb77-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb77-17"><a href="#cb77-17" aria-hidden="true" tabindex="-1"></a><span class="co">-- 应用到敏感表</span></span>
<span id="cb77-18"><a href="#cb77-18" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">TRIGGER</span> audit_customer_changes</span>
<span id="cb77-19"><a href="#cb77-19" aria-hidden="true" tabindex="-1"></a> <span class="kw">AFTER</span> <span class="kw">INSERT</span> <span class="kw">OR</span> <span class="kw">UPDATE</span> <span class="kw">OR</span> <span class="kw">DELETE</span> <span class="kw">ON</span> water_customer</span>
<span id="cb77-20"><a href="#cb77-20" aria-hidden="true" tabindex="-1"></a> <span class="cf">FOR</span> <span class="kw">EACH</span> <span class="kw">ROW</span> <span class="kw">EXECUTE</span> <span class="kw">FUNCTION</span> audit_sensitive_data();</span></code></pre></div>
<h5 data-number="4.0.0.0.70" id="数据备份与灾备"><span
class="header-section-number">4.0.0.0.70</span> 4. 数据备份与灾备</h5>
<h5 data-number="4.0.0.0.71" id="opengauss备份策略"><span
class="header-section-number">4.0.0.0.71</span> 4.1
OpenGauss备份策略</h5>
<div class="sourceCode" id="cb78"><pre
class="sourceCode sql"><code class="sourceCode sql"><span id="cb78-1"><a href="#cb78-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- 全量备份</span></span>
<span id="cb78-2"><a href="#cb78-2" aria-hidden="true" tabindex="-1"></a>gs_backup <span class="op">-</span>D <span class="op">/</span><span class="kw">data</span><span class="op">/</span><span class="kw">backup</span> <span class="op">-</span>h localhost <span class="op">-</span>p <span class="dv">5432</span> <span class="op">-</span>U backup_user</span>
<span id="cb78-3"><a href="#cb78-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb78-4"><a href="#cb78-4" aria-hidden="true" tabindex="-1"></a><span class="co">-- 增量备份 </span></span>
<span id="cb78-5"><a href="#cb78-5" aria-hidden="true" tabindex="-1"></a>gs_backup <span class="op">-</span>D <span class="op">/</span><span class="kw">data</span><span class="op">/</span><span class="kw">backup</span> <span class="op">-</span>h localhost <span class="op">-</span>p <span class="dv">5432</span> <span class="op">-</span>U backup_user <span class="co">--incremental</span></span>
<span id="cb78-6"><a href="#cb78-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb78-7"><a href="#cb78-7" aria-hidden="true" tabindex="-1"></a><span class="co">-- 归档日志备份</span></span>
<span id="cb78-8"><a href="#cb78-8" aria-hidden="true" tabindex="-1"></a><span class="kw">ALTER</span> <span class="kw">SYSTEM</span> <span class="kw">SET</span> archive_mode <span class="op">=</span> <span class="kw">on</span>;</span>
<span id="cb78-9"><a href="#cb78-9" aria-hidden="true" tabindex="-1"></a><span class="kw">ALTER</span> <span class="kw">SYSTEM</span> <span class="kw">SET</span> archive_command <span class="op">=</span> <span class="st">&#39;cp %p /data/archive/%f&#39;</span>;</span></code></pre></div>
<h5 data-number="4.0.0.0.72" id="主备同步配置"><span
class="header-section-number">4.0.0.0.72</span> 4.2 主备同步配置</h5>
<div class="sourceCode" id="cb79"><pre
class="sourceCode sql"><code class="sourceCode sql"><span id="cb79-1"><a href="#cb79-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- 主库配置</span></span>
<span id="cb79-2"><a href="#cb79-2" aria-hidden="true" tabindex="-1"></a><span class="kw">ALTER</span> <span class="kw">SYSTEM</span> <span class="kw">SET</span> synchronous_standby_names <span class="op">=</span> <span class="st">&#39;standby1&#39;</span>;</span>
<span id="cb79-3"><a href="#cb79-3" aria-hidden="true" tabindex="-1"></a><span class="kw">ALTER</span> <span class="kw">SYSTEM</span> <span class="kw">SET</span> synchronous_commit <span class="op">=</span> <span class="kw">on</span>;</span>
<span id="cb79-4"><a href="#cb79-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb79-5"><a href="#cb79-5" aria-hidden="true" tabindex="-1"></a><span class="co">-- 备库配置</span></span>
<span id="cb79-6"><a href="#cb79-6" aria-hidden="true" tabindex="-1"></a><span class="kw">ALTER</span> <span class="kw">SYSTEM</span> <span class="kw">SET</span> hot_standby <span class="op">=</span> <span class="kw">on</span>;</span>
<span id="cb79-7"><a href="#cb79-7" aria-hidden="true" tabindex="-1"></a><span class="kw">ALTER</span> <span class="kw">SYSTEM</span> <span class="kw">SET</span> max_standby_streaming_delay <span class="op">=</span> <span class="dv">30</span>s;</span></code></pre></div>
<h5 data-number="4.0.0.0.73" id="国产化安全合规"><span
class="header-section-number">4.0.0.0.73</span> 5. 国产化安全合规</h5>
<h5 data-number="4.0.0.0.74" id="等保三级要求"><span
class="header-section-number">4.0.0.0.74</span> 5.1 等保三级要求</h5>
<ul>
<li><strong>身份鉴别</strong>:支持多因子认证、密码复杂度策略</li>
<li><strong>访问控制</strong>:基于角色的强制访问控制</li>
<li><strong>安全审计</strong>:完整的操作审计和安全事件记录</li>
<li><strong>数据完整性</strong>:数据校验和完整性保护</li>
<li><strong>数据保密性</strong>:敏感数据加密存储和传输</li>
</ul>
<h5 data-number="4.0.0.0.75" id="国产化认证"><span
class="header-section-number">4.0.0.0.75</span> 5.2 国产化认证</h5>
<ul>
<li>华为OpenGauss已通过国家密码管理局商用密码产品认证</li>
<li>支持国产SM2/SM3/SM4密码算法</li>
<li>符合《网络安全法》、《数据安全法》等法规要求</li>
</ul>
<div class="sourceCode" id="cb80"><pre
class="sourceCode sql"><code class="sourceCode sql"><span id="cb80-1"><a href="#cb80-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- 使用国产SM4算法加密</span></span>
<span id="cb80-2"><a href="#cb80-2" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">TABLE</span> water_customer_sm4 (</span>
<span id="cb80-3"><a href="#cb80-3" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 字段定义...</span></span>
<span id="cb80-4"><a href="#cb80-4" aria-hidden="true" tabindex="-1"></a>) <span class="kw">WITH</span> (ENCRYPTION_TYPE <span class="op">=</span> <span class="st">&#39;SM4_CTR&#39;</span>);</span></code></pre></div>
<hr />
<p>ewpage</p>
<h1 data-number="5" id="第4部分"><span
class="header-section-number">5</span> 第4部分</h1>
<h5 data-number="5.0.0.0.1" id="福建水务营收系统接口设计文档"><span
class="header-section-number">5.0.0.0.1</span>
福建水务营收系统接口设计文档</h5>
<h5 data-number="5.0.0.0.2" id="文档信息-4"><span
class="header-section-number">5.0.0.0.2</span> 文档信息</h5>
<table>
<thead>
<tr>
<th>项目信息</th>
<th>详情</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>项目名称</strong></td>
<td>福建水务营收系统</td>
</tr>
<tr>
<td><strong>文档类型</strong></td>
<td>概要设计文档</td>
</tr>
<tr>
<td><strong>技术框架</strong></td>
<td>RuoYi-Vue-Pro + yudao-ui-admin-vue3</td>
</tr>
<tr>
<td><strong>文档版本</strong></td>
<td>v1.0</td>
</tr>
<tr>
<td><strong>编写日期</strong></td>
<td>2024-12-19</td>
</tr>
<tr>
<td><strong>文档状态</strong></td>
<td>🟡 进行中</td>
</tr>
</tbody>
</table>
<h5 data-number="5.0.0.0.3" id="目录-3"><span
class="header-section-number">5.0.0.0.3</span> 目录</h5>
<ul>
<li><a href="#1-接口概述">1. 接口概述</a></li>
<li><a href="#2-外部接口">2. 外部接口</a></li>
<li><a href="#3-内部接口">3. 内部接口</a></li>
<li><a href="#4-接口标准">4. 接口标准</a></li>
</ul>
<h5 data-number="5.0.0.0.4" id="接口概述"><span
class="header-section-number">5.0.0.0.4</span> 1. 接口概述</h5>
<p>福建水务业务系统提供丰富的接口用于与外部系统集成以及系统内部各模块间的数据交换。接口设计遵循标准化、安全性、可扩展性的原则基于RuoYi-Vue-Pro框架采用RESTful风格设计支持JSON数据格式。</p>
<h5 data-number="5.0.0.0.5" id="接口设计原则"><span
class="header-section-number">5.0.0.0.5</span> 1.1 接口设计原则</h5>
<ul>
<li><strong>统一性</strong>:所有接口遵循统一的设计规范和数据格式</li>
<li><strong>安全性</strong>:接口通过认证授权、参数校验等机制保障安全</li>
<li><strong>可维护性</strong>:接口文档完善,便于维护和升级</li>
<li><strong>兼容性</strong>:接口设计考虑版本兼容,支持平滑升级</li>
<li><strong>性能优化</strong>:接口设计考虑性能,支持缓存、分页等机制</li>
</ul>
<h5 data-number="5.0.0.0.6" id="restful-api规范"><span
class="header-section-number">5.0.0.0.6</span> 1.2 RESTful API规范</h5>
<p>系统API接口采用RESTful风格设计主要规范如下</p>
<h5 data-number="5.0.0.0.7" id="资源命名"><span
class="header-section-number">5.0.0.0.7</span> 1.2.1 资源命名</h5>
<ul>
<li>使用名词复数表示资源集合,如<code>/users</code><code>/meters</code></li>
<li>使用资源ID标识特定资源<code>/users/1</code><code>/meters/123</code></li>
<li>资源层级关系通过路径嵌套表示,如<code>/users/1/meters</code></li>
</ul>
<h5 data-number="5.0.0.0.8" id="http方法"><span
class="header-section-number">5.0.0.0.8</span> 1.2.2 HTTP方法</h5>
<ul>
<li>GET获取资源</li>
<li>POST创建资源</li>
<li>PUT更新资源全量更新</li>
<li>PATCH部分更新资源</li>
<li>DELETE删除资源</li>
</ul>
<h5 data-number="5.0.0.0.9" id="状态码"><span
class="header-section-number">5.0.0.0.9</span> 1.2.3 状态码</h5>
<ul>
<li>200 OK请求成功</li>
<li>201 Created资源创建成功</li>
<li>400 Bad Request请求参数错误</li>
<li>401 Unauthorized未授权</li>
<li>403 Forbidden权限不足</li>
<li>404 Not Found资源不存在</li>
<li>500 Internal Server Error服务器内部错误</li>
</ul>
<h5 data-number="5.0.0.0.10" id="响应格式"><span
class="header-section-number">5.0.0.0.10</span> 1.2.4 响应格式</h5>
<p>系统统一采用以下JSON格式响应</p>
<div class="sourceCode" id="cb81"><pre
class="sourceCode json"><code class="sourceCode json"><span id="cb81-1"><a href="#cb81-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb81-2"><a href="#cb81-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;code&quot;</span><span class="fu">:</span> <span class="dv">0</span><span class="fu">,</span> <span class="er">//</span> <span class="er">业务状态码0表示成功非0表示失败</span></span>
<span id="cb81-3"><a href="#cb81-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;data&quot;</span><span class="fu">:</span> <span class="fu">{},</span> <span class="er">//</span> <span class="er">响应数据</span></span>
<span id="cb81-4"><a href="#cb81-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;msg&quot;</span><span class="fu">:</span> <span class="st">&quot;success&quot;</span> <span class="er">//</span> <span class="er">响应消息</span></span>
<span id="cb81-5"><a href="#cb81-5" aria-hidden="true" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
<p>分页查询响应格式:</p>
<div class="sourceCode" id="cb82"><pre
class="sourceCode json"><code class="sourceCode json"><span id="cb82-1"><a href="#cb82-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb82-2"><a href="#cb82-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;code&quot;</span><span class="fu">:</span> <span class="dv">0</span><span class="fu">,</span></span>
<span id="cb82-3"><a href="#cb82-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;data&quot;</span><span class="fu">:</span> <span class="fu">{</span></span>
<span id="cb82-4"><a href="#cb82-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;list&quot;</span><span class="fu">:</span> <span class="ot">[]</span><span class="fu">,</span> <span class="er">//</span> <span class="er">数据列表</span></span>
<span id="cb82-5"><a href="#cb82-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;total&quot;</span><span class="fu">:</span> <span class="dv">100</span><span class="fu">,</span> <span class="er">//</span> <span class="er">总记录数</span></span>
<span id="cb82-6"><a href="#cb82-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;pageNum&quot;</span><span class="fu">:</span> <span class="dv">1</span><span class="fu">,</span> <span class="er">//</span> <span class="er">当前页码</span></span>
<span id="cb82-7"><a href="#cb82-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;pageSize&quot;</span><span class="fu">:</span> <span class="dv">10</span> <span class="er">//</span> <span class="er">每页记录数</span></span>
<span id="cb82-8"><a href="#cb82-8" aria-hidden="true" tabindex="-1"></a> <span class="fu">},</span></span>
<span id="cb82-9"><a href="#cb82-9" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;msg&quot;</span><span class="fu">:</span> <span class="st">&quot;success&quot;</span></span>
<span id="cb82-10"><a href="#cb82-10" aria-hidden="true" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
<h5 data-number="5.0.0.0.11" id="接口文档"><span
class="header-section-number">5.0.0.0.11</span> 1.3 接口文档</h5>
<p>系统使用Knife4j基于Swagger自动生成API文档文档地址为<code>http://{系统地址}/doc.html</code></p>
<p>主要特点: - 在线接口文档:支持在线查看接口定义 -
接口调试:支持在线调试接口 - 文档导出支持导出OpenAPI规范文档 -
权限控制:支持对接口文档的访问控制</p>
<h5 data-number="5.0.0.0.12" id="外部接口-1"><span
class="header-section-number">5.0.0.0.12</span> 2. 外部接口</h5>
<h5 data-number="5.0.0.0.13" id="银行接口对接"><span
class="header-section-number">5.0.0.0.13</span> 2.1 银行接口对接</h5>
<h5 data-number="5.0.0.0.14" id="银行代扣接口"><span
class="header-section-number">5.0.0.0.14</span> 2.1.1 银行代扣接口</h5>
<p><strong>功能描述</strong>:通过银行系统自动从用户账户中扣除水费。</p>
<p><strong>接口详情</strong> -
<strong>接口方式</strong>文件交换FTP/SFTP -
<strong>数据格式</strong>:定长文本文件 -
<strong>交换频率</strong>每日凌晨2:00 -
<strong>文件编码</strong>GBK</p>
<p><strong>代扣文件格式</strong></p>
<pre class="text"><code>记录类型(1位) + 客户号(12位) + 户名(30位) + 银行账号(20位) + 扣款金额(12位,含2位小数) + 账期(6位) + 保留字段(19位)</code></pre>
<p><strong>代扣文件示例</strong></p>
<pre class="text"><code>1C00000000001张三 62172511001234567890000009180202412
1C00000000002李四 62172511001234567891000015460202412 </code></pre>
<p><strong>回盘文件格式</strong></p>
<pre class="text"><code>记录类型(1位) + 客户号(12位) + 银行账号(20位) + 扣款金额(12位) + 处理状态(1位) + 银行流水号(20位) + 处理时间(14位) + 失败原因(20位)</code></pre>
<p><strong>Java实现示例</strong></p>
<div class="sourceCode" id="cb86"><pre
class="sourceCode java"><code class="sourceCode java"><span id="cb86-1"><a href="#cb86-1" aria-hidden="true" tabindex="-1"></a><span class="at">@Service</span></span>
<span id="cb86-2"><a href="#cb86-2" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span> <span class="kw">class</span> BankDeductServiceImpl <span class="kw">implements</span> BankDeductService <span class="op">{</span></span>
<span id="cb86-3"><a href="#cb86-3" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb86-4"><a href="#cb86-4" aria-hidden="true" tabindex="-1"></a> <span class="at">@Resource</span></span>
<span id="cb86-5"><a href="#cb86-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">private</span> SftpTemplate sftpTemplate<span class="op">;</span></span>
<span id="cb86-6"><a href="#cb86-6" aria-hidden="true" tabindex="-1"></a> <span class="at">@Resource</span></span>
<span id="cb86-7"><a href="#cb86-7" aria-hidden="true" tabindex="-1"></a> <span class="kw">private</span> BillService billService<span class="op">;</span></span>
<span id="cb86-8"><a href="#cb86-8" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb86-9"><a href="#cb86-9" aria-hidden="true" tabindex="-1"></a> <span class="at">@Scheduled</span><span class="op">(</span>cron <span class="op">=</span> <span class="st">&quot;0 0 2 * * ?&quot;</span><span class="op">)</span></span>
<span id="cb86-10"><a href="#cb86-10" aria-hidden="true" tabindex="-1"></a> <span class="kw">public</span> <span class="dt">void</span> <span class="fu">generateDeductFile</span><span class="op">()</span> <span class="op">{</span></span>
<span id="cb86-11"><a href="#cb86-11" aria-hidden="true" tabindex="-1"></a> LocalDate deductDate <span class="op">=</span> LocalDate<span class="op">.</span><span class="fu">now</span><span class="op">();</span></span>
<span id="cb86-12"><a href="#cb86-12" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb86-13"><a href="#cb86-13" aria-hidden="true" tabindex="-1"></a> <span class="co">// 获取待代扣账单</span></span>
<span id="cb86-14"><a href="#cb86-14" aria-hidden="true" tabindex="-1"></a> <span class="bu">List</span><span class="op">&lt;</span>BillDO<span class="op">&gt;</span> deductBills <span class="op">=</span> billService<span class="op">.</span><span class="fu">getDeductBills</span><span class="op">(</span>deductDate<span class="op">);</span></span>
<span id="cb86-15"><a href="#cb86-15" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb86-16"><a href="#cb86-16" aria-hidden="true" tabindex="-1"></a> <span class="co">// 生成代扣文件</span></span>
<span id="cb86-17"><a href="#cb86-17" aria-hidden="true" tabindex="-1"></a> <span class="bu">String</span> fileName <span class="op">=</span> <span class="st">&quot;DEDUCT_&quot;</span> <span class="op">+</span> deductDate<span class="op">.</span><span class="fu">format</span><span class="op">(</span>DateTimeFormatter<span class="op">.</span><span class="fu">ofPattern</span><span class="op">(</span><span class="st">&quot;yyyyMMdd&quot;</span><span class="op">))</span> <span class="op">+</span> <span class="st">&quot;.txt&quot;</span><span class="op">;</span></span>
<span id="cb86-18"><a href="#cb86-18" aria-hidden="true" tabindex="-1"></a> <span class="bu">String</span> fileContent <span class="op">=</span> <span class="fu">buildDeductFileContent</span><span class="op">(</span>deductBills<span class="op">);</span></span>
<span id="cb86-19"><a href="#cb86-19" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb86-20"><a href="#cb86-20" aria-hidden="true" tabindex="-1"></a> <span class="co">// 上传至银行SFTP</span></span>
<span id="cb86-21"><a href="#cb86-21" aria-hidden="true" tabindex="-1"></a> sftpTemplate<span class="op">.</span><span class="fu">put</span><span class="op">(</span>fileName<span class="op">,</span> fileContent<span class="op">.</span><span class="fu">getBytes</span><span class="op">(</span>StandardCharsets<span class="op">.</span><span class="fu">UTF_8</span><span class="op">),</span> <span class="st">&quot;/upload/&quot;</span><span class="op">);</span></span>
<span id="cb86-22"><a href="#cb86-22" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb86-23"><a href="#cb86-23" aria-hidden="true" tabindex="-1"></a> <span class="co">// 记录代扣文件日志</span></span>
<span id="cb86-24"><a href="#cb86-24" aria-hidden="true" tabindex="-1"></a> DeductFileLogDO log <span class="op">=</span> <span class="kw">new</span> <span class="fu">DeductFileLogDO</span><span class="op">();</span></span>
<span id="cb86-25"><a href="#cb86-25" aria-hidden="true" tabindex="-1"></a> log<span class="op">.</span><span class="fu">setFileName</span><span class="op">(</span>fileName<span class="op">);</span></span>
<span id="cb86-26"><a href="#cb86-26" aria-hidden="true" tabindex="-1"></a> log<span class="op">.</span><span class="fu">setFileStatus</span><span class="op">(</span><span class="st">&quot;UPLOADED&quot;</span><span class="op">);</span></span>
<span id="cb86-27"><a href="#cb86-27" aria-hidden="true" tabindex="-1"></a> log<span class="op">.</span><span class="fu">setRecordCount</span><span class="op">(</span>deductBills<span class="op">.</span><span class="fu">size</span><span class="op">());</span></span>
<span id="cb86-28"><a href="#cb86-28" aria-hidden="true" tabindex="-1"></a> deductFileLogMapper<span class="op">.</span><span class="fu">insert</span><span class="op">(</span>log<span class="op">);</span></span>
<span id="cb86-29"><a href="#cb86-29" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb86-30"><a href="#cb86-30" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb86-31"><a href="#cb86-31" aria-hidden="true" tabindex="-1"></a> <span class="kw">private</span> <span class="bu">String</span> <span class="fu">buildDeductFileContent</span><span class="op">(</span><span class="bu">List</span><span class="op">&lt;</span>BillDO<span class="op">&gt;</span> bills<span class="op">)</span> <span class="op">{</span></span>
<span id="cb86-32"><a href="#cb86-32" aria-hidden="true" tabindex="-1"></a> <span class="bu">StringBuilder</span> content <span class="op">=</span> <span class="kw">new</span> <span class="bu">StringBuilder</span><span class="op">();</span></span>
<span id="cb86-33"><a href="#cb86-33" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> <span class="op">(</span>BillDO bill <span class="op">:</span> bills<span class="op">)</span> <span class="op">{</span></span>
<span id="cb86-34"><a href="#cb86-34" aria-hidden="true" tabindex="-1"></a> content<span class="op">.</span><span class="fu">append</span><span class="op">(</span><span class="st">&quot;1&quot;</span><span class="op">)</span> <span class="co">// 记录类型</span></span>
<span id="cb86-35"><a href="#cb86-35" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">append</span><span class="op">(</span>StringUtils<span class="op">.</span><span class="fu">rightPad</span><span class="op">(</span>bill<span class="op">.</span><span class="fu">getCustomerCode</span><span class="op">(),</span> <span class="dv">12</span><span class="op">))</span> <span class="co">// 客户号</span></span>
<span id="cb86-36"><a href="#cb86-36" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">append</span><span class="op">(</span>StringUtils<span class="op">.</span><span class="fu">rightPad</span><span class="op">(</span>bill<span class="op">.</span><span class="fu">getCustomerName</span><span class="op">(),</span> <span class="dv">30</span><span class="op">))</span> <span class="co">// 户名</span></span>
<span id="cb86-37"><a href="#cb86-37" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">append</span><span class="op">(</span>StringUtils<span class="op">.</span><span class="fu">rightPad</span><span class="op">(</span>bill<span class="op">.</span><span class="fu">getBankAccount</span><span class="op">(),</span> <span class="dv">20</span><span class="op">))</span> <span class="co">// 银行账号</span></span>
<span id="cb86-38"><a href="#cb86-38" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">append</span><span class="op">(</span><span class="bu">String</span><span class="op">.</span><span class="fu">format</span><span class="op">(</span><span class="st">&quot;</span><span class="sc">%012d</span><span class="st">&quot;</span><span class="op">,</span> bill<span class="op">.</span><span class="fu">getTotalAmount</span><span class="op">().</span><span class="fu">multiply</span><span class="op">(</span><span class="kw">new</span> <span class="bu">BigDecimal</span><span class="op">(</span><span class="dv">100</span><span class="op">)).</span><span class="fu">intValue</span><span class="op">()))</span> <span class="co">// 金额(分)</span></span>
<span id="cb86-39"><a href="#cb86-39" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">append</span><span class="op">(</span>bill<span class="op">.</span><span class="fu">getBillMonth</span><span class="op">().</span><span class="fu">replace</span><span class="op">(</span><span class="st">&quot;-&quot;</span><span class="op">,</span> <span class="st">&quot;&quot;</span><span class="op">))</span> <span class="co">// 账期</span></span>
<span id="cb86-40"><a href="#cb86-40" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">append</span><span class="op">(</span>StringUtils<span class="op">.</span><span class="fu">repeat</span><span class="op">(</span><span class="st">&quot; &quot;</span><span class="op">,</span> <span class="dv">19</span><span class="op">))</span> <span class="co">// 保留字段</span></span>
<span id="cb86-41"><a href="#cb86-41" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">append</span><span class="op">(</span><span class="st">&quot;</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span>
<span id="cb86-42"><a href="#cb86-42" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb86-43"><a href="#cb86-43" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> content<span class="op">.</span><span class="fu">toString</span><span class="op">();</span></span>
<span id="cb86-44"><a href="#cb86-44" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb86-45"><a href="#cb86-45" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<h5 data-number="5.0.0.0.15" id="银行实时缴费接口"><span
class="header-section-number">5.0.0.0.15</span> 2.1.2
银行实时缴费接口</h5>
<p><strong>功能描述</strong>:用户在银行柜台、网上银行或手机银行实时缴纳水费。</p>
<p><strong>接口详情</strong> - <strong>接口方式</strong>HTTP POST -
<strong>请求URL</strong><code>https://bank.api.com/payment/water-fee</code>
- <strong>数据格式</strong>JSON - <strong>认证方式</strong>API Key +
签名</p>
<p><strong>请求参数</strong></p>
<div class="sourceCode" id="cb87"><pre
class="sourceCode json"><code class="sourceCode json"><span id="cb87-1"><a href="#cb87-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb87-2"><a href="#cb87-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;merchantId&quot;</span><span class="fu">:</span> <span class="st">&quot;WATER001&quot;</span><span class="fu">,</span></span>
<span id="cb87-3"><a href="#cb87-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;customerCode&quot;</span><span class="fu">:</span> <span class="st">&quot;C001&quot;</span><span class="fu">,</span></span>
<span id="cb87-4"><a href="#cb87-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;billCodes&quot;</span><span class="fu">:</span> <span class="ot">[</span><span class="st">&quot;B202412190001&quot;</span><span class="ot">]</span><span class="fu">,</span></span>
<span id="cb87-5"><a href="#cb87-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;totalAmount&quot;</span><span class="fu">:</span> <span class="fl">91.80</span><span class="fu">,</span></span>
<span id="cb87-6"><a href="#cb87-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;bankAccount&quot;</span><span class="fu">:</span> <span class="st">&quot;6217251100123456789&quot;</span><span class="fu">,</span></span>
<span id="cb87-7"><a href="#cb87-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;customerName&quot;</span><span class="fu">:</span> <span class="st">&quot;张三&quot;</span><span class="fu">,</span></span>
<span id="cb87-8"><a href="#cb87-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;timestamp&quot;</span><span class="fu">:</span> <span class="st">&quot;20241219103000&quot;</span><span class="fu">,</span></span>
<span id="cb87-9"><a href="#cb87-9" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;signature&quot;</span><span class="fu">:</span> <span class="st">&quot;ABC123DEF456...&quot;</span></span>
<span id="cb87-10"><a href="#cb87-10" aria-hidden="true" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
<p><strong>响应参数</strong></p>
<div class="sourceCode" id="cb88"><pre
class="sourceCode json"><code class="sourceCode json"><span id="cb88-1"><a href="#cb88-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb88-2"><a href="#cb88-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;resultCode&quot;</span><span class="fu">:</span> <span class="st">&quot;0000&quot;</span><span class="fu">,</span></span>
<span id="cb88-3"><a href="#cb88-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;resultMsg&quot;</span><span class="fu">:</span> <span class="st">&quot;交易成功&quot;</span><span class="fu">,</span></span>
<span id="cb88-4"><a href="#cb88-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;data&quot;</span><span class="fu">:</span> <span class="fu">{</span></span>
<span id="cb88-5"><a href="#cb88-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;transactionId&quot;</span><span class="fu">:</span> <span class="st">&quot;TXN20241219001&quot;</span><span class="fu">,</span></span>
<span id="cb88-6"><a href="#cb88-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;paymentTime&quot;</span><span class="fu">:</span> <span class="st">&quot;20241219103001&quot;</span><span class="fu">,</span></span>
<span id="cb88-7"><a href="#cb88-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;bankSerial&quot;</span><span class="fu">:</span> <span class="st">&quot;BNK20241219001234&quot;</span></span>
<span id="cb88-8"><a href="#cb88-8" aria-hidden="true" tabindex="-1"></a> <span class="fu">}</span></span>
<span id="cb88-9"><a href="#cb88-9" aria-hidden="true" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
<h5 data-number="5.0.0.0.16" id="支付宝接口对接"><span
class="header-section-number">5.0.0.0.16</span> 2.2 支付宝接口对接</h5>
<p><strong>功能描述</strong>用户通过支付宝缴纳水费支持扫码支付和H5支付。</p>
<p><strong>接口详情</strong> - <strong>接口方式</strong>HTTP POST -
<strong>支付方式</strong>统一收单交易预创建alipay.trade.precreate
- <strong>数据格式</strong>JSON -
<strong>认证方式</strong>RSA2签名</p>
<p><strong>预创建支付请求参数</strong></p>
<div class="sourceCode" id="cb89"><pre
class="sourceCode json"><code class="sourceCode json"><span id="cb89-1"><a href="#cb89-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb89-2"><a href="#cb89-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;app_id&quot;</span><span class="fu">:</span> <span class="st">&quot;2021001234567890&quot;</span><span class="fu">,</span></span>
<span id="cb89-3"><a href="#cb89-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;method&quot;</span><span class="fu">:</span> <span class="st">&quot;alipay.trade.precreate&quot;</span><span class="fu">,</span> </span>
<span id="cb89-4"><a href="#cb89-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;charset&quot;</span><span class="fu">:</span> <span class="st">&quot;UTF-8&quot;</span><span class="fu">,</span></span>
<span id="cb89-5"><a href="#cb89-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;sign_type&quot;</span><span class="fu">:</span> <span class="st">&quot;RSA2&quot;</span><span class="fu">,</span></span>
<span id="cb89-6"><a href="#cb89-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;timestamp&quot;</span><span class="fu">:</span> <span class="st">&quot;2024-12-19 10:30:00&quot;</span><span class="fu">,</span></span>
<span id="cb89-7"><a href="#cb89-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;version&quot;</span><span class="fu">:</span> <span class="st">&quot;1.0&quot;</span><span class="fu">,</span></span>
<span id="cb89-8"><a href="#cb89-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;notify_url&quot;</span><span class="fu">:</span> <span class="st">&quot;https://water.example.com/api/payment/alipay/notify&quot;</span><span class="fu">,</span></span>
<span id="cb89-9"><a href="#cb89-9" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;biz_content&quot;</span><span class="fu">:</span> <span class="fu">{</span></span>
<span id="cb89-10"><a href="#cb89-10" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;out_trade_no&quot;</span><span class="fu">:</span> <span class="st">&quot;P202412190002&quot;</span><span class="fu">,</span></span>
<span id="cb89-11"><a href="#cb89-11" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;total_amount&quot;</span><span class="fu">:</span> <span class="st">&quot;91.80&quot;</span><span class="fu">,</span></span>
<span id="cb89-12"><a href="#cb89-12" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;subject&quot;</span><span class="fu">:</span> <span class="st">&quot;水费缴费&quot;</span><span class="fu">,</span></span>
<span id="cb89-13"><a href="#cb89-13" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;body&quot;</span><span class="fu">:</span> <span class="st">&quot;2024年12月水费-客户编号:C001&quot;</span><span class="fu">,</span></span>
<span id="cb89-14"><a href="#cb89-14" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;store_id&quot;</span><span class="fu">:</span> <span class="st">&quot;WATER_STORE_001&quot;</span><span class="fu">,</span></span>
<span id="cb89-15"><a href="#cb89-15" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;timeout_express&quot;</span><span class="fu">:</span> <span class="st">&quot;30m&quot;</span></span>
<span id="cb89-16"><a href="#cb89-16" aria-hidden="true" tabindex="-1"></a> <span class="fu">}</span></span>
<span id="cb89-17"><a href="#cb89-17" aria-hidden="true" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
<p><strong>支付宝响应参数</strong></p>
<div class="sourceCode" id="cb90"><pre
class="sourceCode json"><code class="sourceCode json"><span id="cb90-1"><a href="#cb90-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb90-2"><a href="#cb90-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;alipay_trade_precreate_response&quot;</span><span class="fu">:</span> <span class="fu">{</span></span>
<span id="cb90-3"><a href="#cb90-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;code&quot;</span><span class="fu">:</span> <span class="st">&quot;10000&quot;</span><span class="fu">,</span></span>
<span id="cb90-4"><a href="#cb90-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;msg&quot;</span><span class="fu">:</span> <span class="st">&quot;Success&quot;</span><span class="fu">,</span></span>
<span id="cb90-5"><a href="#cb90-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;out_trade_no&quot;</span><span class="fu">:</span> <span class="st">&quot;P202412190002&quot;</span><span class="fu">,</span></span>
<span id="cb90-6"><a href="#cb90-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;qr_code&quot;</span><span class="fu">:</span> <span class="st">&quot;https://qr.alipay.com/bax08945xtdnfwgqmwi200b4&quot;</span></span>
<span id="cb90-7"><a href="#cb90-7" aria-hidden="true" tabindex="-1"></a> <span class="fu">},</span></span>
<span id="cb90-8"><a href="#cb90-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;sign&quot;</span><span class="fu">:</span> <span class="st">&quot;ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE&quot;</span></span>
<span id="cb90-9"><a href="#cb90-9" aria-hidden="true" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
<p><strong>Java实现示例</strong></p>
<div class="sourceCode" id="cb91"><pre
class="sourceCode java"><code class="sourceCode java"><span id="cb91-1"><a href="#cb91-1" aria-hidden="true" tabindex="-1"></a><span class="at">@Service</span></span>
<span id="cb91-2"><a href="#cb91-2" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span> <span class="kw">class</span> AlipayServiceImpl <span class="kw">implements</span> AlipayService <span class="op">{</span></span>
<span id="cb91-3"><a href="#cb91-3" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb91-4"><a href="#cb91-4" aria-hidden="true" tabindex="-1"></a> <span class="at">@Resource</span></span>
<span id="cb91-5"><a href="#cb91-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">private</span> AlipayClient alipayClient<span class="op">;</span></span>
<span id="cb91-6"><a href="#cb91-6" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb91-7"><a href="#cb91-7" aria-hidden="true" tabindex="-1"></a> <span class="at">@Override</span></span>
<span id="cb91-8"><a href="#cb91-8" aria-hidden="true" tabindex="-1"></a> <span class="kw">public</span> AlipayPaymentRespVO <span class="fu">createPayment</span><span class="op">(</span>AlipayPaymentReqVO request<span class="op">)</span> <span class="op">{</span></span>
<span id="cb91-9"><a href="#cb91-9" aria-hidden="true" tabindex="-1"></a> AlipayTradePrecreateRequest alipayRequest <span class="op">=</span> <span class="kw">new</span> <span class="fu">AlipayTradePrecreateRequest</span><span class="op">();</span></span>
<span id="cb91-10"><a href="#cb91-10" aria-hidden="true" tabindex="-1"></a> alipayRequest<span class="op">.</span><span class="fu">setNotifyUrl</span><span class="op">(</span><span class="st">&quot;https://water.example.com/api/payment/alipay/notify&quot;</span><span class="op">);</span></span>
<span id="cb91-11"><a href="#cb91-11" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb91-12"><a href="#cb91-12" aria-hidden="true" tabindex="-1"></a> AlipayTradePrecreateModel model <span class="op">=</span> <span class="kw">new</span> <span class="fu">AlipayTradePrecreateModel</span><span class="op">();</span></span>
<span id="cb91-13"><a href="#cb91-13" aria-hidden="true" tabindex="-1"></a> model<span class="op">.</span><span class="fu">setOutTradeNo</span><span class="op">(</span>request<span class="op">.</span><span class="fu">getPaymentCode</span><span class="op">());</span></span>
<span id="cb91-14"><a href="#cb91-14" aria-hidden="true" tabindex="-1"></a> model<span class="op">.</span><span class="fu">setTotalAmount</span><span class="op">(</span>request<span class="op">.</span><span class="fu">getTotalAmount</span><span class="op">().</span><span class="fu">toString</span><span class="op">());</span></span>
<span id="cb91-15"><a href="#cb91-15" aria-hidden="true" tabindex="-1"></a> model<span class="op">.</span><span class="fu">setSubject</span><span class="op">(</span><span class="st">&quot;水费缴费&quot;</span><span class="op">);</span></span>
<span id="cb91-16"><a href="#cb91-16" aria-hidden="true" tabindex="-1"></a> model<span class="op">.</span><span class="fu">setBody</span><span class="op">(</span><span class="st">&quot;账单号:&quot;</span> <span class="op">+</span> <span class="bu">String</span><span class="op">.</span><span class="fu">join</span><span class="op">(</span><span class="st">&quot;,&quot;</span><span class="op">,</span> request<span class="op">.</span><span class="fu">getBillCodes</span><span class="op">()));</span></span>
<span id="cb91-17"><a href="#cb91-17" aria-hidden="true" tabindex="-1"></a> model<span class="op">.</span><span class="fu">setTimeoutExpress</span><span class="op">(</span><span class="st">&quot;30m&quot;</span><span class="op">);</span></span>
<span id="cb91-18"><a href="#cb91-18" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb91-19"><a href="#cb91-19" aria-hidden="true" tabindex="-1"></a> alipayRequest<span class="op">.</span><span class="fu">setBizModel</span><span class="op">(</span>model<span class="op">);</span></span>
<span id="cb91-20"><a href="#cb91-20" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb91-21"><a href="#cb91-21" aria-hidden="true" tabindex="-1"></a> <span class="cf">try</span> <span class="op">{</span></span>
<span id="cb91-22"><a href="#cb91-22" aria-hidden="true" tabindex="-1"></a> AlipayTradePrecreateResponse response <span class="op">=</span> alipayClient<span class="op">.</span><span class="fu">execute</span><span class="op">(</span>alipayRequest<span class="op">);</span></span>
<span id="cb91-23"><a href="#cb91-23" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>response<span class="op">.</span><span class="fu">isSuccess</span><span class="op">())</span> <span class="op">{</span></span>
<span id="cb91-24"><a href="#cb91-24" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> AlipayPaymentRespVO<span class="op">.</span><span class="fu">builder</span><span class="op">()</span></span>
<span id="cb91-25"><a href="#cb91-25" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">paymentCode</span><span class="op">(</span>request<span class="op">.</span><span class="fu">getPaymentCode</span><span class="op">())</span></span>
<span id="cb91-26"><a href="#cb91-26" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">qrCode</span><span class="op">(</span>response<span class="op">.</span><span class="fu">getQrCode</span><span class="op">())</span></span>
<span id="cb91-27"><a href="#cb91-27" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">outTradeNo</span><span class="op">(</span>response<span class="op">.</span><span class="fu">getOutTradeNo</span><span class="op">())</span></span>
<span id="cb91-28"><a href="#cb91-28" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">build</span><span class="op">();</span></span>
<span id="cb91-29"><a href="#cb91-29" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span> <span class="cf">else</span> <span class="op">{</span></span>
<span id="cb91-30"><a href="#cb91-30" aria-hidden="true" tabindex="-1"></a> <span class="cf">throw</span> <span class="kw">new</span> <span class="fu">BizException</span><span class="op">(</span>ALIPAY_PAY_FAILED<span class="op">,</span> response<span class="op">.</span><span class="fu">getSubMsg</span><span class="op">());</span></span>
<span id="cb91-31"><a href="#cb91-31" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb91-32"><a href="#cb91-32" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span> <span class="cf">catch</span> <span class="op">(</span>AlipayApiException e<span class="op">)</span> <span class="op">{</span></span>
<span id="cb91-33"><a href="#cb91-33" aria-hidden="true" tabindex="-1"></a> <span class="cf">throw</span> <span class="kw">new</span> <span class="fu">BizException</span><span class="op">(</span>ALIPAY_PAY_ERROR<span class="op">,</span> e<span class="op">.</span><span class="fu">getErrMsg</span><span class="op">());</span></span>
<span id="cb91-34"><a href="#cb91-34" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb91-35"><a href="#cb91-35" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb91-36"><a href="#cb91-36" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<h5 data-number="5.0.0.0.17" id="微信支付接口对接"><span
class="header-section-number">5.0.0.0.17</span> 2.3
微信支付接口对接</h5>
<p><strong>功能描述</strong>:用户通过微信支付缴纳水费,支持扫码支付和小程序支付。</p>
<p><strong>接口详情</strong> - <strong>接口方式</strong>HTTP POST -
<strong>支付方式</strong>Native支付扫码/ JSAPI支付小程序 -
<strong>请求URL</strong><code>https://api.mch.weixin.qq.com/v3/pay/transactions/native</code>
- <strong>数据格式</strong>JSON -
<strong>认证方式</strong>微信支付V3签名</p>
<p><strong>统一下单请求参数</strong></p>
<div class="sourceCode" id="cb92"><pre
class="sourceCode json"><code class="sourceCode json"><span id="cb92-1"><a href="#cb92-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb92-2"><a href="#cb92-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;appid&quot;</span><span class="fu">:</span> <span class="st">&quot;wx8888888888888888&quot;</span><span class="fu">,</span></span>
<span id="cb92-3"><a href="#cb92-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;mchid&quot;</span><span class="fu">:</span> <span class="st">&quot;1900000109&quot;</span><span class="fu">,</span> </span>
<span id="cb92-4"><a href="#cb92-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;description&quot;</span><span class="fu">:</span> <span class="st">&quot;水费缴费-2024年12月&quot;</span><span class="fu">,</span></span>
<span id="cb92-5"><a href="#cb92-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;out_trade_no&quot;</span><span class="fu">:</span> <span class="st">&quot;P202412190003&quot;</span><span class="fu">,</span></span>
<span id="cb92-6"><a href="#cb92-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;notify_url&quot;</span><span class="fu">:</span> <span class="st">&quot;https://water.example.com/api/payment/wechat/notify&quot;</span><span class="fu">,</span></span>
<span id="cb92-7"><a href="#cb92-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;amount&quot;</span><span class="fu">:</span> <span class="fu">{</span></span>
<span id="cb92-8"><a href="#cb92-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;total&quot;</span><span class="fu">:</span> <span class="dv">9180</span><span class="fu">,</span></span>
<span id="cb92-9"><a href="#cb92-9" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;currency&quot;</span><span class="fu">:</span> <span class="st">&quot;CNY&quot;</span></span>
<span id="cb92-10"><a href="#cb92-10" aria-hidden="true" tabindex="-1"></a> <span class="fu">},</span></span>
<span id="cb92-11"><a href="#cb92-11" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;attach&quot;</span><span class="fu">:</span> <span class="st">&quot;客户编号:C001,账单号:B202412190001&quot;</span><span class="fu">,</span></span>
<span id="cb92-12"><a href="#cb92-12" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;goods_tag&quot;</span><span class="fu">:</span> <span class="st">&quot;WATER_FEE&quot;</span><span class="fu">,</span></span>
<span id="cb92-13"><a href="#cb92-13" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;time_expire&quot;</span><span class="fu">:</span> <span class="st">&quot;2024-12-19T11:00:00+08:00&quot;</span></span>
<span id="cb92-14"><a href="#cb92-14" aria-hidden="true" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
<p><strong>微信支付响应参数</strong></p>
<div class="sourceCode" id="cb93"><pre
class="sourceCode json"><code class="sourceCode json"><span id="cb93-1"><a href="#cb93-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb93-2"><a href="#cb93-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;code_url&quot;</span><span class="fu">:</span> <span class="st">&quot;weixin://wxpay/bizpayurl?pr=HuaLcAKwa&quot;</span></span>
<span id="cb93-3"><a href="#cb93-3" aria-hidden="true" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
<p><strong>支付结果通知参数</strong></p>
<div class="sourceCode" id="cb94"><pre
class="sourceCode json"><code class="sourceCode json"><span id="cb94-1"><a href="#cb94-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb94-2"><a href="#cb94-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;id&quot;</span><span class="fu">:</span> <span class="st">&quot;EV-2018022511223320873&quot;</span><span class="fu">,</span></span>
<span id="cb94-3"><a href="#cb94-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;create_time&quot;</span><span class="fu">:</span> <span class="st">&quot;2024-12-19T10:30:00+08:00&quot;</span><span class="fu">,</span></span>
<span id="cb94-4"><a href="#cb94-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;resource_type&quot;</span><span class="fu">:</span> <span class="st">&quot;encrypt-resource&quot;</span><span class="fu">,</span></span>
<span id="cb94-5"><a href="#cb94-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;event_type&quot;</span><span class="fu">:</span> <span class="st">&quot;TRANSACTION.SUCCESS&quot;</span><span class="fu">,</span></span>
<span id="cb94-6"><a href="#cb94-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;summary&quot;</span><span class="fu">:</span> <span class="st">&quot;支付成功&quot;</span><span class="fu">,</span></span>
<span id="cb94-7"><a href="#cb94-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;resource&quot;</span><span class="fu">:</span> <span class="fu">{</span></span>
<span id="cb94-8"><a href="#cb94-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;original_type&quot;</span><span class="fu">:</span> <span class="st">&quot;transaction&quot;</span><span class="fu">,</span></span>
<span id="cb94-9"><a href="#cb94-9" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;algorithm&quot;</span><span class="fu">:</span> <span class="st">&quot;AEAD_AES_256_GCM&quot;</span><span class="fu">,</span></span>
<span id="cb94-10"><a href="#cb94-10" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;ciphertext&quot;</span><span class="fu">:</span> <span class="st">&quot;...&quot;</span><span class="fu">,</span></span>
<span id="cb94-11"><a href="#cb94-11" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;associated_data&quot;</span><span class="fu">:</span> <span class="st">&quot;transaction&quot;</span><span class="fu">,</span></span>
<span id="cb94-12"><a href="#cb94-12" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;nonce&quot;</span><span class="fu">:</span> <span class="st">&quot;...&quot;</span></span>
<span id="cb94-13"><a href="#cb94-13" aria-hidden="true" tabindex="-1"></a> <span class="fu">}</span></span>
<span id="cb94-14"><a href="#cb94-14" aria-hidden="true" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
<h5 data-number="5.0.0.0.18" id="短信接口-1"><span
class="header-section-number">5.0.0.0.18</span> 2.4 短信接口</h5>
<p><strong>功能描述</strong>:向用户发送各类业务通知短信。</p>
<p><strong>接口规范</strong> - 接口方式HTTP接口 - 数据格式JSON -
交换频率:实时</p>
<h5 data-number="5.0.0.0.19" id="物联网集抄平台接口"><span
class="header-section-number">5.0.0.0.19</span> 2.5
物联网集抄平台接口</h5>
<p><strong>功能描述</strong>:与物联网集抄平台交互,获取智能水表数据。</p>
<p><strong>接口规范</strong> - 接口方式HTTP接口或WebService -
数据格式JSON或XML - 交换频率:定时或实时</p>
<h5 data-number="5.0.0.0.20" id="内部接口-1"><span
class="header-section-number">5.0.0.0.20</span> 3. 内部接口</h5>
<h5 data-number="5.0.0.0.21" id="客户管理api接口"><span
class="header-section-number">5.0.0.0.21</span> 3.1 客户管理API接口</h5>
<h5 data-number="5.0.0.0.22" id="客户信息查询接口"><span
class="header-section-number">5.0.0.0.22</span> 3.1.1
客户信息查询接口</h5>
<p><strong>功能描述</strong>根据客户ID查询客户详细信息。</p>
<p><strong>接口详情</strong> - <strong>请求方式</strong>GET -
<strong>请求路径</strong><code>/admin-api/water/customer/{id}</code> -
<strong>请求头</strong><code>Authorization: Bearer {token}</code></p>
<p><strong>请求参数</strong> | 参数名 | 类型 | 必填 | 说明 | 示例 |
|——-|——|——|——|——| | id | Long | 是 | 客户ID | 1 |</p>
<p><strong>响应参数</strong></p>
<div class="sourceCode" id="cb95"><pre
class="sourceCode json"><code class="sourceCode json"><span id="cb95-1"><a href="#cb95-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb95-2"><a href="#cb95-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;code&quot;</span><span class="fu">:</span> <span class="dv">0</span><span class="fu">,</span></span>
<span id="cb95-3"><a href="#cb95-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;msg&quot;</span><span class="fu">:</span> <span class="st">&quot;操作成功&quot;</span><span class="fu">,</span></span>
<span id="cb95-4"><a href="#cb95-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;data&quot;</span><span class="fu">:</span> <span class="fu">{</span></span>
<span id="cb95-5"><a href="#cb95-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;id&quot;</span><span class="fu">:</span> <span class="dv">1</span><span class="fu">,</span></span>
<span id="cb95-6"><a href="#cb95-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;customerCode&quot;</span><span class="fu">:</span> <span class="st">&quot;C001&quot;</span><span class="fu">,</span></span>
<span id="cb95-7"><a href="#cb95-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;customerName&quot;</span><span class="fu">:</span> <span class="st">&quot;张三&quot;</span><span class="fu">,</span></span>
<span id="cb95-8"><a href="#cb95-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;customerType&quot;</span><span class="fu">:</span> <span class="st">&quot;RESIDENT&quot;</span><span class="fu">,</span></span>
<span id="cb95-9"><a href="#cb95-9" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;phone&quot;</span><span class="fu">:</span> <span class="st">&quot;13800138000&quot;</span><span class="fu">,</span></span>
<span id="cb95-10"><a href="#cb95-10" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;address&quot;</span><span class="fu">:</span> <span class="st">&quot;福建省福州市台江区XX街道XX号&quot;</span><span class="fu">,</span></span>
<span id="cb95-11"><a href="#cb95-11" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;status&quot;</span><span class="fu">:</span> <span class="dv">1</span><span class="fu">,</span></span>
<span id="cb95-12"><a href="#cb95-12" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;createTime&quot;</span><span class="fu">:</span> <span class="st">&quot;2024-12-19 10:00:00&quot;</span></span>
<span id="cb95-13"><a href="#cb95-13" aria-hidden="true" tabindex="-1"></a> <span class="fu">}</span></span>
<span id="cb95-14"><a href="#cb95-14" aria-hidden="true" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
<p><strong>RuoYi-Vue-Pro代码示例</strong></p>
<div class="sourceCode" id="cb96"><pre
class="sourceCode java"><code class="sourceCode java"><span id="cb96-1"><a href="#cb96-1" aria-hidden="true" tabindex="-1"></a><span class="at">@RestController</span></span>
<span id="cb96-2"><a href="#cb96-2" aria-hidden="true" tabindex="-1"></a><span class="at">@RequestMapping</span><span class="op">(</span><span class="st">&quot;/admin-api/water/customer&quot;</span><span class="op">)</span></span>
<span id="cb96-3"><a href="#cb96-3" aria-hidden="true" tabindex="-1"></a><span class="at">@Tag</span><span class="op">(</span>name <span class="op">=</span> <span class="st">&quot;管理后台 - 客户管理&quot;</span><span class="op">)</span></span>
<span id="cb96-4"><a href="#cb96-4" aria-hidden="true" tabindex="-1"></a><span class="at">@Validated</span></span>
<span id="cb96-5"><a href="#cb96-5" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span> <span class="kw">class</span> CustomerController <span class="op">{</span></span>
<span id="cb96-6"><a href="#cb96-6" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb96-7"><a href="#cb96-7" aria-hidden="true" tabindex="-1"></a> <span class="at">@Resource</span></span>
<span id="cb96-8"><a href="#cb96-8" aria-hidden="true" tabindex="-1"></a> <span class="kw">private</span> CustomerService customerService<span class="op">;</span></span>
<span id="cb96-9"><a href="#cb96-9" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb96-10"><a href="#cb96-10" aria-hidden="true" tabindex="-1"></a> <span class="at">@GetMapping</span><span class="op">(</span><span class="st">&quot;/{id}&quot;</span><span class="op">)</span></span>
<span id="cb96-11"><a href="#cb96-11" aria-hidden="true" tabindex="-1"></a> <span class="at">@Operation</span><span class="op">(</span>summary <span class="op">=</span> <span class="st">&quot;获得客户&quot;</span><span class="op">)</span></span>
<span id="cb96-12"><a href="#cb96-12" aria-hidden="true" tabindex="-1"></a> <span class="at">@Parameter</span><span class="op">(</span>name <span class="op">=</span> <span class="st">&quot;id&quot;</span><span class="op">,</span> description <span class="op">=</span> <span class="st">&quot;编号&quot;</span><span class="op">,</span> required <span class="op">=</span> <span class="kw">true</span><span class="op">,</span> example <span class="op">=</span> <span class="st">&quot;1024&quot;</span><span class="op">)</span></span>
<span id="cb96-13"><a href="#cb96-13" aria-hidden="true" tabindex="-1"></a> <span class="at">@PreAuthorize</span><span class="op">(</span><span class="st">&quot;@ss.hasPermission(&#39;water:customer:query&#39;)&quot;</span><span class="op">)</span></span>
<span id="cb96-14"><a href="#cb96-14" aria-hidden="true" tabindex="-1"></a> <span class="kw">public</span> CommonResult<span class="op">&lt;</span>CustomerRespVO<span class="op">&gt;</span> <span class="fu">getCustomer</span><span class="op">(</span><span class="at">@PathVariable</span><span class="op">(</span><span class="st">&quot;id&quot;</span><span class="op">)</span> <span class="bu">Long</span> id<span class="op">)</span> <span class="op">{</span></span>
<span id="cb96-15"><a href="#cb96-15" aria-hidden="true" tabindex="-1"></a> CustomerDO customer <span class="op">=</span> customerService<span class="op">.</span><span class="fu">getCustomer</span><span class="op">(</span>id<span class="op">);</span></span>
<span id="cb96-16"><a href="#cb96-16" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="fu">success</span><span class="op">(</span>BeanUtils<span class="op">.</span><span class="fu">toBean</span><span class="op">(</span>customer<span class="op">,</span> CustomerRespVO<span class="op">.</span><span class="fu">class</span><span class="op">));</span></span>
<span id="cb96-17"><a href="#cb96-17" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb96-18"><a href="#cb96-18" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<h5 data-number="5.0.0.0.23" id="客户分页查询接口"><span
class="header-section-number">5.0.0.0.23</span> 3.1.2
客户分页查询接口</h5>
<p><strong>功能描述</strong>:分页查询客户列表信息。</p>
<p><strong>接口详情</strong> - <strong>请求方式</strong>GET -
<strong>请求路径</strong><code>/admin-api/water/customer/page</code></p>
<p><strong>请求参数</strong> | 参数名 | 类型 | 必填 | 说明 | 示例 |
|——-|——|——|——|——| | pageNo | Integer | 否 | 页码默认1 | 1 | | pageSize
| Integer | 否 | 每页条数默认10 | 10 | | customerName | String | 否 |
客户名称 | 张三 | | customerCode | String | 否 | 客户编号 | C001 | |
customerType | String | 否 | 客户类型 | RESIDENT | | phone | String | 否
| 联系电话 | 138 |</p>
<p><strong>响应参数</strong></p>
<div class="sourceCode" id="cb97"><pre
class="sourceCode json"><code class="sourceCode json"><span id="cb97-1"><a href="#cb97-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb97-2"><a href="#cb97-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;code&quot;</span><span class="fu">:</span> <span class="dv">0</span><span class="fu">,</span></span>
<span id="cb97-3"><a href="#cb97-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;msg&quot;</span><span class="fu">:</span> <span class="st">&quot;操作成功&quot;</span><span class="fu">,</span></span>
<span id="cb97-4"><a href="#cb97-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;data&quot;</span><span class="fu">:</span> <span class="fu">{</span></span>
<span id="cb97-5"><a href="#cb97-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;list&quot;</span><span class="fu">:</span> <span class="ot">[</span></span>
<span id="cb97-6"><a href="#cb97-6" aria-hidden="true" tabindex="-1"></a> <span class="fu">{</span></span>
<span id="cb97-7"><a href="#cb97-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;id&quot;</span><span class="fu">:</span> <span class="dv">1</span><span class="fu">,</span></span>
<span id="cb97-8"><a href="#cb97-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;customerCode&quot;</span><span class="fu">:</span> <span class="st">&quot;C001&quot;</span><span class="fu">,</span></span>
<span id="cb97-9"><a href="#cb97-9" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;customerName&quot;</span><span class="fu">:</span> <span class="st">&quot;张三&quot;</span><span class="fu">,</span></span>
<span id="cb97-10"><a href="#cb97-10" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;customerType&quot;</span><span class="fu">:</span> <span class="st">&quot;RESIDENT&quot;</span><span class="fu">,</span></span>
<span id="cb97-11"><a href="#cb97-11" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;phone&quot;</span><span class="fu">:</span> <span class="st">&quot;13800138000&quot;</span><span class="fu">,</span></span>
<span id="cb97-12"><a href="#cb97-12" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;address&quot;</span><span class="fu">:</span> <span class="st">&quot;福建省福州市台江区XX街道XX号&quot;</span><span class="fu">,</span></span>
<span id="cb97-13"><a href="#cb97-13" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;status&quot;</span><span class="fu">:</span> <span class="dv">1</span><span class="fu">,</span></span>
<span id="cb97-14"><a href="#cb97-14" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;createTime&quot;</span><span class="fu">:</span> <span class="st">&quot;2024-12-19 10:00:00&quot;</span></span>
<span id="cb97-15"><a href="#cb97-15" aria-hidden="true" tabindex="-1"></a> <span class="fu">}</span></span>
<span id="cb97-16"><a href="#cb97-16" aria-hidden="true" tabindex="-1"></a> <span class="ot">]</span><span class="fu">,</span></span>
<span id="cb97-17"><a href="#cb97-17" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;total&quot;</span><span class="fu">:</span> <span class="dv">1</span></span>
<span id="cb97-18"><a href="#cb97-18" aria-hidden="true" tabindex="-1"></a> <span class="fu">}</span></span>
<span id="cb97-19"><a href="#cb97-19" aria-hidden="true" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
<h5 data-number="5.0.0.0.24" id="客户创建接口"><span
class="header-section-number">5.0.0.0.24</span> 3.1.3 客户创建接口</h5>
<p><strong>功能描述</strong>:创建新客户记录。</p>
<p><strong>接口详情</strong> - <strong>请求方式</strong>POST -
<strong>请求路径</strong><code>/admin-api/water/customer/create</code></p>
<p><strong>请求参数</strong></p>
<div class="sourceCode" id="cb98"><pre
class="sourceCode json"><code class="sourceCode json"><span id="cb98-1"><a href="#cb98-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb98-2"><a href="#cb98-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;customerCode&quot;</span><span class="fu">:</span> <span class="st">&quot;C002&quot;</span><span class="fu">,</span></span>
<span id="cb98-3"><a href="#cb98-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;customerName&quot;</span><span class="fu">:</span> <span class="st">&quot;李四&quot;</span><span class="fu">,</span></span>
<span id="cb98-4"><a href="#cb98-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;customerType&quot;</span><span class="fu">:</span> <span class="st">&quot;RESIDENT&quot;</span><span class="fu">,</span></span>
<span id="cb98-5"><a href="#cb98-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;idType&quot;</span><span class="fu">:</span> <span class="st">&quot;ID_CARD&quot;</span><span class="fu">,</span></span>
<span id="cb98-6"><a href="#cb98-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;idNumber&quot;</span><span class="fu">:</span> <span class="st">&quot;350103199001011234&quot;</span><span class="fu">,</span></span>
<span id="cb98-7"><a href="#cb98-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;phone&quot;</span><span class="fu">:</span> <span class="st">&quot;13900139000&quot;</span><span class="fu">,</span></span>
<span id="cb98-8"><a href="#cb98-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;address&quot;</span><span class="fu">:</span> <span class="st">&quot;福建省福州市鼓楼区XX街道XX号&quot;</span></span>
<span id="cb98-9"><a href="#cb98-9" aria-hidden="true" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
<p><strong>响应参数</strong></p>
<div class="sourceCode" id="cb99"><pre
class="sourceCode json"><code class="sourceCode json"><span id="cb99-1"><a href="#cb99-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb99-2"><a href="#cb99-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;code&quot;</span><span class="fu">:</span> <span class="dv">0</span><span class="fu">,</span></span>
<span id="cb99-3"><a href="#cb99-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;msg&quot;</span><span class="fu">:</span> <span class="st">&quot;操作成功&quot;</span><span class="fu">,</span></span>
<span id="cb99-4"><a href="#cb99-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;data&quot;</span><span class="fu">:</span> <span class="dv">2</span></span>
<span id="cb99-5"><a href="#cb99-5" aria-hidden="true" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
<p><strong>Service层代码示例</strong></p>
<div class="sourceCode" id="cb100"><pre
class="sourceCode java"><code class="sourceCode java"><span id="cb100-1"><a href="#cb100-1" aria-hidden="true" tabindex="-1"></a><span class="at">@Service</span></span>
<span id="cb100-2"><a href="#cb100-2" aria-hidden="true" tabindex="-1"></a><span class="at">@Validated</span></span>
<span id="cb100-3"><a href="#cb100-3" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span> <span class="kw">class</span> CustomerServiceImpl <span class="kw">implements</span> CustomerService <span class="op">{</span></span>
<span id="cb100-4"><a href="#cb100-4" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb100-5"><a href="#cb100-5" aria-hidden="true" tabindex="-1"></a> <span class="at">@Resource</span></span>
<span id="cb100-6"><a href="#cb100-6" aria-hidden="true" tabindex="-1"></a> <span class="kw">private</span> CustomerMapper customerMapper<span class="op">;</span></span>
<span id="cb100-7"><a href="#cb100-7" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb100-8"><a href="#cb100-8" aria-hidden="true" tabindex="-1"></a> <span class="at">@Override</span></span>
<span id="cb100-9"><a href="#cb100-9" aria-hidden="true" tabindex="-1"></a> <span class="kw">public</span> <span class="bu">Long</span> <span class="fu">createCustomer</span><span class="op">(</span>CustomerSaveReqVO createReqVO<span class="op">)</span> <span class="op">{</span></span>
<span id="cb100-10"><a href="#cb100-10" aria-hidden="true" tabindex="-1"></a> <span class="co">// 校验客户编号唯一性</span></span>
<span id="cb100-11"><a href="#cb100-11" aria-hidden="true" tabindex="-1"></a> <span class="fu">validateCustomerCodeUnique</span><span class="op">(</span>createReqVO<span class="op">.</span><span class="fu">getCustomerCode</span><span class="op">());</span></span>
<span id="cb100-12"><a href="#cb100-12" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb100-13"><a href="#cb100-13" aria-hidden="true" tabindex="-1"></a> <span class="co">// 创建客户</span></span>
<span id="cb100-14"><a href="#cb100-14" aria-hidden="true" tabindex="-1"></a> CustomerDO customer <span class="op">=</span> BeanUtils<span class="op">.</span><span class="fu">toBean</span><span class="op">(</span>createReqVO<span class="op">,</span> CustomerDO<span class="op">.</span><span class="fu">class</span><span class="op">);</span></span>
<span id="cb100-15"><a href="#cb100-15" aria-hidden="true" tabindex="-1"></a> customerMapper<span class="op">.</span><span class="fu">insert</span><span class="op">(</span>customer<span class="op">);</span></span>
<span id="cb100-16"><a href="#cb100-16" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> customer<span class="op">.</span><span class="fu">getId</span><span class="op">();</span></span>
<span id="cb100-17"><a href="#cb100-17" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb100-18"><a href="#cb100-18" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb100-19"><a href="#cb100-19" aria-hidden="true" tabindex="-1"></a> <span class="kw">private</span> <span class="dt">void</span> <span class="fu">validateCustomerCodeUnique</span><span class="op">(</span><span class="bu">String</span> customerCode<span class="op">)</span> <span class="op">{</span></span>
<span id="cb100-20"><a href="#cb100-20" aria-hidden="true" tabindex="-1"></a> CustomerDO existCustomer <span class="op">=</span> customerMapper<span class="op">.</span><span class="fu">selectByCustomerCode</span><span class="op">(</span>customerCode<span class="op">);</span></span>
<span id="cb100-21"><a href="#cb100-21" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>existCustomer <span class="op">!=</span> <span class="kw">null</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb100-22"><a href="#cb100-22" aria-hidden="true" tabindex="-1"></a> <span class="cf">throw</span> <span class="fu">exception</span><span class="op">(</span>CUSTOMER_CODE_DUPLICATE<span class="op">);</span></span>
<span id="cb100-23"><a href="#cb100-23" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb100-24"><a href="#cb100-24" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb100-25"><a href="#cb100-25" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<h5 data-number="5.0.0.0.25" id="水表管理api接口"><span
class="header-section-number">5.0.0.0.25</span> 3.2 水表管理API接口</h5>
<h5 data-number="5.0.0.0.26" id="水表信息查询接口"><span
class="header-section-number">5.0.0.0.26</span> 3.2.1
水表信息查询接口</h5>
<p><strong>功能描述</strong>根据水表ID查询水表详细信息。</p>
<p><strong>接口详情</strong> - <strong>请求方式</strong>GET -
<strong>请求路径</strong><code>/admin-api/water/meter/{id}</code> -
<strong>请求头</strong><code>Authorization: Bearer {token}</code></p>
<p><strong>请求参数</strong> | 参数名 | 类型 | 必填 | 说明 | 示例 |
|——-|——|——|——|——| | id | Long | 是 | 水表ID | 1 |</p>
<p><strong>响应参数</strong></p>
<div class="sourceCode" id="cb101"><pre
class="sourceCode json"><code class="sourceCode json"><span id="cb101-1"><a href="#cb101-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb101-2"><a href="#cb101-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;code&quot;</span><span class="fu">:</span> <span class="dv">0</span><span class="fu">,</span></span>
<span id="cb101-3"><a href="#cb101-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;msg&quot;</span><span class="fu">:</span> <span class="st">&quot;操作成功&quot;</span><span class="fu">,</span></span>
<span id="cb101-4"><a href="#cb101-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;data&quot;</span><span class="fu">:</span> <span class="fu">{</span></span>
<span id="cb101-5"><a href="#cb101-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;id&quot;</span><span class="fu">:</span> <span class="dv">1</span><span class="fu">,</span></span>
<span id="cb101-6"><a href="#cb101-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;meterCode&quot;</span><span class="fu">:</span> <span class="st">&quot;M001&quot;</span><span class="fu">,</span></span>
<span id="cb101-7"><a href="#cb101-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;meterNo&quot;</span><span class="fu">:</span> <span class="st">&quot;20241219001&quot;</span><span class="fu">,</span></span>
<span id="cb101-8"><a href="#cb101-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;meterType&quot;</span><span class="fu">:</span> <span class="st">&quot;SMART&quot;</span><span class="fu">,</span></span>
<span id="cb101-9"><a href="#cb101-9" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;meterModel&quot;</span><span class="fu">:</span> <span class="st">&quot;LXSY-15E&quot;</span><span class="fu">,</span></span>
<span id="cb101-10"><a href="#cb101-10" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;meterCaliber&quot;</span><span class="fu">:</span> <span class="st">&quot;15mm&quot;</span><span class="fu">,</span></span>
<span id="cb101-11"><a href="#cb101-11" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;installDate&quot;</span><span class="fu">:</span> <span class="st">&quot;2024-01-15&quot;</span><span class="fu">,</span></span>
<span id="cb101-12"><a href="#cb101-12" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;installPosition&quot;</span><span class="fu">:</span> <span class="st">&quot;1层水表井&quot;</span><span class="fu">,</span></span>
<span id="cb101-13"><a href="#cb101-13" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;initialReading&quot;</span><span class="fu">:</span> <span class="dv">0</span><span class="er">.00</span><span class="fu">,</span></span>
<span id="cb101-14"><a href="#cb101-14" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;currentReading&quot;</span><span class="fu">:</span> <span class="fl">156.32</span><span class="fu">,</span></span>
<span id="cb101-15"><a href="#cb101-15" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;readingCycle&quot;</span><span class="fu">:</span> <span class="st">&quot;MONTHLY&quot;</span><span class="fu">,</span></span>
<span id="cb101-16"><a href="#cb101-16" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;meterStatus&quot;</span><span class="fu">:</span> <span class="dv">1</span><span class="fu">,</span></span>
<span id="cb101-17"><a href="#cb101-17" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;customerId&quot;</span><span class="fu">:</span> <span class="dv">1</span><span class="fu">,</span></span>
<span id="cb101-18"><a href="#cb101-18" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;customerName&quot;</span><span class="fu">:</span> <span class="st">&quot;张三&quot;</span></span>
<span id="cb101-19"><a href="#cb101-19" aria-hidden="true" tabindex="-1"></a> <span class="fu">}</span></span>
<span id="cb101-20"><a href="#cb101-20" aria-hidden="true" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
<p><strong>Controller代码示例</strong></p>
<div class="sourceCode" id="cb102"><pre
class="sourceCode java"><code class="sourceCode java"><span id="cb102-1"><a href="#cb102-1" aria-hidden="true" tabindex="-1"></a><span class="at">@RestController</span></span>
<span id="cb102-2"><a href="#cb102-2" aria-hidden="true" tabindex="-1"></a><span class="at">@RequestMapping</span><span class="op">(</span><span class="st">&quot;/admin-api/water/meter&quot;</span><span class="op">)</span></span>
<span id="cb102-3"><a href="#cb102-3" aria-hidden="true" tabindex="-1"></a><span class="at">@Tag</span><span class="op">(</span>name <span class="op">=</span> <span class="st">&quot;管理后台 - 水表管理&quot;</span><span class="op">)</span></span>
<span id="cb102-4"><a href="#cb102-4" aria-hidden="true" tabindex="-1"></a><span class="at">@Validated</span></span>
<span id="cb102-5"><a href="#cb102-5" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span> <span class="kw">class</span> MeterController <span class="op">{</span></span>
<span id="cb102-6"><a href="#cb102-6" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb102-7"><a href="#cb102-7" aria-hidden="true" tabindex="-1"></a> <span class="at">@Resource</span></span>
<span id="cb102-8"><a href="#cb102-8" aria-hidden="true" tabindex="-1"></a> <span class="kw">private</span> MeterService meterService<span class="op">;</span></span>
<span id="cb102-9"><a href="#cb102-9" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb102-10"><a href="#cb102-10" aria-hidden="true" tabindex="-1"></a> <span class="at">@GetMapping</span><span class="op">(</span><span class="st">&quot;/{id}&quot;</span><span class="op">)</span></span>
<span id="cb102-11"><a href="#cb102-11" aria-hidden="true" tabindex="-1"></a> <span class="at">@Operation</span><span class="op">(</span>summary <span class="op">=</span> <span class="st">&quot;获得水表&quot;</span><span class="op">)</span></span>
<span id="cb102-12"><a href="#cb102-12" aria-hidden="true" tabindex="-1"></a> <span class="at">@Parameter</span><span class="op">(</span>name <span class="op">=</span> <span class="st">&quot;id&quot;</span><span class="op">,</span> description <span class="op">=</span> <span class="st">&quot;编号&quot;</span><span class="op">,</span> required <span class="op">=</span> <span class="kw">true</span><span class="op">)</span></span>
<span id="cb102-13"><a href="#cb102-13" aria-hidden="true" tabindex="-1"></a> <span class="at">@PreAuthorize</span><span class="op">(</span><span class="st">&quot;@ss.hasPermission(&#39;water:meter:query&#39;)&quot;</span><span class="op">)</span></span>
<span id="cb102-14"><a href="#cb102-14" aria-hidden="true" tabindex="-1"></a> <span class="kw">public</span> CommonResult<span class="op">&lt;</span>MeterRespVO<span class="op">&gt;</span> <span class="fu">getMeter</span><span class="op">(</span><span class="at">@PathVariable</span><span class="op">(</span><span class="st">&quot;id&quot;</span><span class="op">)</span> <span class="bu">Long</span> id<span class="op">)</span> <span class="op">{</span></span>
<span id="cb102-15"><a href="#cb102-15" aria-hidden="true" tabindex="-1"></a> MeterDO meter <span class="op">=</span> meterService<span class="op">.</span><span class="fu">getMeter</span><span class="op">(</span>id<span class="op">);</span></span>
<span id="cb102-16"><a href="#cb102-16" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="fu">success</span><span class="op">(</span>BeanUtils<span class="op">.</span><span class="fu">toBean</span><span class="op">(</span>meter<span class="op">,</span> MeterRespVO<span class="op">.</span><span class="fu">class</span><span class="op">));</span></span>
<span id="cb102-17"><a href="#cb102-17" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb102-18"><a href="#cb102-18" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<h5 data-number="5.0.0.0.27" id="抄表记录创建接口"><span
class="header-section-number">5.0.0.0.27</span> 3.2.2
抄表记录创建接口</h5>
<p><strong>功能描述</strong>:创建新的抄表记录。</p>
<p><strong>接口详情</strong> - <strong>请求方式</strong>POST -
<strong>请求路径</strong><code>/admin-api/water/reading/create</code></p>
<p><strong>请求参数</strong></p>
<div class="sourceCode" id="cb103"><pre
class="sourceCode json"><code class="sourceCode json"><span id="cb103-1"><a href="#cb103-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb103-2"><a href="#cb103-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;meterId&quot;</span><span class="fu">:</span> <span class="dv">1</span><span class="fu">,</span></span>
<span id="cb103-3"><a href="#cb103-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;readingDate&quot;</span><span class="fu">:</span> <span class="st">&quot;2024-12-19&quot;</span><span class="fu">,</span></span>
<span id="cb103-4"><a href="#cb103-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;readingValue&quot;</span><span class="fu">:</span> <span class="fl">156.32</span><span class="fu">,</span></span>
<span id="cb103-5"><a href="#cb103-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;readingType&quot;</span><span class="fu">:</span> <span class="st">&quot;MANUAL&quot;</span><span class="fu">,</span></span>
<span id="cb103-6"><a href="#cb103-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;readerId&quot;</span><span class="fu">:</span> <span class="st">&quot;R001&quot;</span><span class="fu">,</span></span>
<span id="cb103-7"><a href="#cb103-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;photoUrl&quot;</span><span class="fu">:</span> <span class="st">&quot;https://example.com/photos/reading001.jpg&quot;</span><span class="fu">,</span></span>
<span id="cb103-8"><a href="#cb103-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;remark&quot;</span><span class="fu">:</span> <span class="st">&quot;正常抄表&quot;</span></span>
<span id="cb103-9"><a href="#cb103-9" aria-hidden="true" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
<p><strong>响应参数</strong></p>
<div class="sourceCode" id="cb104"><pre
class="sourceCode json"><code class="sourceCode json"><span id="cb104-1"><a href="#cb104-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb104-2"><a href="#cb104-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;code&quot;</span><span class="fu">:</span> <span class="dv">0</span><span class="fu">,</span></span>
<span id="cb104-3"><a href="#cb104-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;msg&quot;</span><span class="fu">:</span> <span class="st">&quot;操作成功&quot;</span><span class="fu">,</span> </span>
<span id="cb104-4"><a href="#cb104-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;data&quot;</span><span class="fu">:</span> <span class="dv">1</span></span>
<span id="cb104-5"><a href="#cb104-5" aria-hidden="true" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
<p><strong>Service层实现示例</strong></p>
<div class="sourceCode" id="cb105"><pre
class="sourceCode java"><code class="sourceCode java"><span id="cb105-1"><a href="#cb105-1" aria-hidden="true" tabindex="-1"></a><span class="at">@Service</span></span>
<span id="cb105-2"><a href="#cb105-2" aria-hidden="true" tabindex="-1"></a><span class="at">@Validated</span></span>
<span id="cb105-3"><a href="#cb105-3" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span> <span class="kw">class</span> MeterReadingServiceImpl <span class="kw">implements</span> MeterReadingService <span class="op">{</span></span>
<span id="cb105-4"><a href="#cb105-4" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb105-5"><a href="#cb105-5" aria-hidden="true" tabindex="-1"></a> <span class="at">@Resource</span></span>
<span id="cb105-6"><a href="#cb105-6" aria-hidden="true" tabindex="-1"></a> <span class="kw">private</span> MeterReadingMapper readingMapper<span class="op">;</span></span>
<span id="cb105-7"><a href="#cb105-7" aria-hidden="true" tabindex="-1"></a> <span class="at">@Resource</span></span>
<span id="cb105-8"><a href="#cb105-8" aria-hidden="true" tabindex="-1"></a> <span class="kw">private</span> MeterService meterService<span class="op">;</span></span>
<span id="cb105-9"><a href="#cb105-9" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb105-10"><a href="#cb105-10" aria-hidden="true" tabindex="-1"></a> <span class="at">@Override</span></span>
<span id="cb105-11"><a href="#cb105-11" aria-hidden="true" tabindex="-1"></a> <span class="at">@Transactional</span><span class="op">(</span>rollbackFor <span class="op">=</span> <span class="bu">Exception</span><span class="op">.</span><span class="fu">class</span><span class="op">)</span></span>
<span id="cb105-12"><a href="#cb105-12" aria-hidden="true" tabindex="-1"></a> <span class="kw">public</span> <span class="bu">Long</span> <span class="fu">createReading</span><span class="op">(</span>MeterReadingSaveReqVO createReqVO<span class="op">)</span> <span class="op">{</span></span>
<span id="cb105-13"><a href="#cb105-13" aria-hidden="true" tabindex="-1"></a> <span class="co">// 校验水表存在性</span></span>
<span id="cb105-14"><a href="#cb105-14" aria-hidden="true" tabindex="-1"></a> MeterDO meter <span class="op">=</span> meterService<span class="op">.</span><span class="fu">validateMeterExists</span><span class="op">(</span>createReqVO<span class="op">.</span><span class="fu">getMeterId</span><span class="op">());</span></span>
<span id="cb105-15"><a href="#cb105-15" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb105-16"><a href="#cb105-16" aria-hidden="true" tabindex="-1"></a> <span class="co">// 校验读数合理性</span></span>
<span id="cb105-17"><a href="#cb105-17" aria-hidden="true" tabindex="-1"></a> <span class="fu">validateReadingValue</span><span class="op">(</span>createReqVO<span class="op">.</span><span class="fu">getMeterId</span><span class="op">(),</span> createReqVO<span class="op">.</span><span class="fu">getReadingValue</span><span class="op">());</span></span>
<span id="cb105-18"><a href="#cb105-18" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb105-19"><a href="#cb105-19" aria-hidden="true" tabindex="-1"></a> <span class="co">// 创建抄表记录</span></span>
<span id="cb105-20"><a href="#cb105-20" aria-hidden="true" tabindex="-1"></a> MeterReadingDO reading <span class="op">=</span> BeanUtils<span class="op">.</span><span class="fu">toBean</span><span class="op">(</span>createReqVO<span class="op">,</span> MeterReadingDO<span class="op">.</span><span class="fu">class</span><span class="op">);</span></span>
<span id="cb105-21"><a href="#cb105-21" aria-hidden="true" tabindex="-1"></a> reading<span class="op">.</span><span class="fu">setReadingCode</span><span class="op">(</span><span class="fu">generateReadingCode</span><span class="op">());</span></span>
<span id="cb105-22"><a href="#cb105-22" aria-hidden="true" tabindex="-1"></a> reading<span class="op">.</span><span class="fu">setCustomerId</span><span class="op">(</span>meter<span class="op">.</span><span class="fu">getCustomerId</span><span class="op">());</span></span>
<span id="cb105-23"><a href="#cb105-23" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb105-24"><a href="#cb105-24" aria-hidden="true" tabindex="-1"></a> <span class="co">// 计算用水量</span></span>
<span id="cb105-25"><a href="#cb105-25" aria-hidden="true" tabindex="-1"></a> <span class="bu">BigDecimal</span> waterUsage <span class="op">=</span> <span class="fu">calculateWaterUsage</span><span class="op">(</span>meter<span class="op">.</span><span class="fu">getCurrentReading</span><span class="op">(),</span> </span>
<span id="cb105-26"><a href="#cb105-26" aria-hidden="true" tabindex="-1"></a> createReqVO<span class="op">.</span><span class="fu">getReadingValue</span><span class="op">());</span></span>
<span id="cb105-27"><a href="#cb105-27" aria-hidden="true" tabindex="-1"></a> reading<span class="op">.</span><span class="fu">setWaterUsage</span><span class="op">(</span>waterUsage<span class="op">);</span></span>
<span id="cb105-28"><a href="#cb105-28" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb105-29"><a href="#cb105-29" aria-hidden="true" tabindex="-1"></a> readingMapper<span class="op">.</span><span class="fu">insert</span><span class="op">(</span>reading<span class="op">);</span></span>
<span id="cb105-30"><a href="#cb105-30" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb105-31"><a href="#cb105-31" aria-hidden="true" tabindex="-1"></a> <span class="co">// 更新水表当前读数</span></span>
<span id="cb105-32"><a href="#cb105-32" aria-hidden="true" tabindex="-1"></a> meterService<span class="op">.</span><span class="fu">updateCurrentReading</span><span class="op">(</span>createReqVO<span class="op">.</span><span class="fu">getMeterId</span><span class="op">(),</span> </span>
<span id="cb105-33"><a href="#cb105-33" aria-hidden="true" tabindex="-1"></a> createReqVO<span class="op">.</span><span class="fu">getReadingValue</span><span class="op">());</span></span>
<span id="cb105-34"><a href="#cb105-34" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb105-35"><a href="#cb105-35" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> reading<span class="op">.</span><span class="fu">getId</span><span class="op">();</span></span>
<span id="cb105-36"><a href="#cb105-36" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb105-37"><a href="#cb105-37" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<h5 data-number="5.0.0.0.28" id="抄表数据批量导入接口"><span
class="header-section-number">5.0.0.0.28</span> 3.2.3
抄表数据批量导入接口</h5>
<p><strong>功能描述</strong>批量导入抄表数据支持Excel文件上传。</p>
<p><strong>接口详情</strong> - <strong>请求方式</strong>POST -
<strong>请求路径</strong><code>/admin-api/water/reading/import</code>
- <strong>Content-Type</strong><code>multipart/form-data</code></p>
<p><strong>请求参数</strong> | 参数名 | 类型 | 必填 | 说明 | 示例 |
|——-|——|——|——|——| | file | MultipartFile | 是 | Excel文件 |
reading_data.xlsx | | updateSupport | Boolean | 否 | 是否更新已有数据 |
false |</p>
<p><strong>响应参数</strong></p>
<div class="sourceCode" id="cb106"><pre
class="sourceCode json"><code class="sourceCode json"><span id="cb106-1"><a href="#cb106-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb106-2"><a href="#cb106-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;code&quot;</span><span class="fu">:</span> <span class="dv">0</span><span class="fu">,</span></span>
<span id="cb106-3"><a href="#cb106-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;msg&quot;</span><span class="fu">:</span> <span class="st">&quot;操作成功&quot;</span><span class="fu">,</span></span>
<span id="cb106-4"><a href="#cb106-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;data&quot;</span><span class="fu">:</span> <span class="fu">{</span></span>
<span id="cb106-5"><a href="#cb106-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;successCount&quot;</span><span class="fu">:</span> <span class="dv">95</span><span class="fu">,</span></span>
<span id="cb106-6"><a href="#cb106-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;failureCount&quot;</span><span class="fu">:</span> <span class="dv">5</span><span class="fu">,</span></span>
<span id="cb106-7"><a href="#cb106-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;failureList&quot;</span><span class="fu">:</span> <span class="ot">[</span></span>
<span id="cb106-8"><a href="#cb106-8" aria-hidden="true" tabindex="-1"></a> <span class="fu">{</span></span>
<span id="cb106-9"><a href="#cb106-9" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;lineNumber&quot;</span><span class="fu">:</span> <span class="dv">3</span><span class="fu">,</span></span>
<span id="cb106-10"><a href="#cb106-10" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;meterCode&quot;</span><span class="fu">:</span> <span class="st">&quot;M003&quot;</span><span class="fu">,</span> </span>
<span id="cb106-11"><a href="#cb106-11" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;errorMsg&quot;</span><span class="fu">:</span> <span class="st">&quot;水表不存在&quot;</span></span>
<span id="cb106-12"><a href="#cb106-12" aria-hidden="true" tabindex="-1"></a> <span class="fu">}</span></span>
<span id="cb106-13"><a href="#cb106-13" aria-hidden="true" tabindex="-1"></a> <span class="ot">]</span></span>
<span id="cb106-14"><a href="#cb106-14" aria-hidden="true" tabindex="-1"></a> <span class="fu">}</span></span>
<span id="cb106-15"><a href="#cb106-15" aria-hidden="true" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
<h5 data-number="5.0.0.0.29" id="账单管理api接口"><span
class="header-section-number">5.0.0.0.29</span> 3.3 账单管理API接口</h5>
<h5 data-number="5.0.0.0.30" id="账单查询接口"><span
class="header-section-number">5.0.0.0.30</span> 3.3.1 账单查询接口</h5>
<p><strong>功能描述</strong>根据客户ID和查询条件查询账单信息。</p>
<p><strong>接口详情</strong> - <strong>请求方式</strong>GET -
<strong>请求路径</strong><code>/admin-api/water/bill/page</code></p>
<p><strong>请求参数</strong> | 参数名 | 类型 | 必填 | 说明 | 示例 |
|——-|——|——|——|——| | pageNo | Integer | 否 | 页码默认1 | 1 | | pageSize
| Integer | 否 | 每页条数默认10 | 10 | | customerId | Long | 否 |
客户ID | 1 | | billMonth | String | 否 | 账期 | 2024-12 | | billStatus |
Integer | 否 | 账单状态 | 0 |</p>
<p><strong>响应参数</strong></p>
<div class="sourceCode" id="cb107"><pre
class="sourceCode json"><code class="sourceCode json"><span id="cb107-1"><a href="#cb107-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb107-2"><a href="#cb107-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;code&quot;</span><span class="fu">:</span> <span class="dv">0</span><span class="fu">,</span></span>
<span id="cb107-3"><a href="#cb107-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;msg&quot;</span><span class="fu">:</span> <span class="st">&quot;操作成功&quot;</span><span class="fu">,</span></span>
<span id="cb107-4"><a href="#cb107-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;data&quot;</span><span class="fu">:</span> <span class="fu">{</span></span>
<span id="cb107-5"><a href="#cb107-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;list&quot;</span><span class="fu">:</span> <span class="ot">[</span></span>
<span id="cb107-6"><a href="#cb107-6" aria-hidden="true" tabindex="-1"></a> <span class="fu">{</span></span>
<span id="cb107-7"><a href="#cb107-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;id&quot;</span><span class="fu">:</span> <span class="dv">1</span><span class="fu">,</span></span>
<span id="cb107-8"><a href="#cb107-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;billCode&quot;</span><span class="fu">:</span> <span class="st">&quot;B202412190001&quot;</span><span class="fu">,</span></span>
<span id="cb107-9"><a href="#cb107-9" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;billMonth&quot;</span><span class="fu">:</span> <span class="st">&quot;2024-12&quot;</span><span class="fu">,</span></span>
<span id="cb107-10"><a href="#cb107-10" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;billDate&quot;</span><span class="fu">:</span> <span class="st">&quot;2024-12-19&quot;</span><span class="fu">,</span></span>
<span id="cb107-11"><a href="#cb107-11" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;waterUsage&quot;</span><span class="fu">:</span> <span class="fl">25.50</span><span class="fu">,</span></span>
<span id="cb107-12"><a href="#cb107-12" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;waterFee&quot;</span><span class="fu">:</span> <span class="fl">76.50</span><span class="fu">,</span></span>
<span id="cb107-13"><a href="#cb107-13" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;sewageFee&quot;</span><span class="fu">:</span> <span class="fl">15.30</span><span class="fu">,</span></span>
<span id="cb107-14"><a href="#cb107-14" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;totalAmount&quot;</span><span class="fu">:</span> <span class="fl">91.80</span><span class="fu">,</span></span>
<span id="cb107-15"><a href="#cb107-15" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;paidAmount&quot;</span><span class="fu">:</span> <span class="dv">0</span><span class="er">.00</span><span class="fu">,</span></span>
<span id="cb107-16"><a href="#cb107-16" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;balanceAmount&quot;</span><span class="fu">:</span> <span class="fl">91.80</span><span class="fu">,</span></span>
<span id="cb107-17"><a href="#cb107-17" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;dueDate&quot;</span><span class="fu">:</span> <span class="st">&quot;2025-01-19&quot;</span><span class="fu">,</span></span>
<span id="cb107-18"><a href="#cb107-18" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;billStatus&quot;</span><span class="fu">:</span> <span class="dv">0</span><span class="fu">,</span></span>
<span id="cb107-19"><a href="#cb107-19" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;customerName&quot;</span><span class="fu">:</span> <span class="st">&quot;张三&quot;</span><span class="fu">,</span></span>
<span id="cb107-20"><a href="#cb107-20" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;meterCode&quot;</span><span class="fu">:</span> <span class="st">&quot;M001&quot;</span></span>
<span id="cb107-21"><a href="#cb107-21" aria-hidden="true" tabindex="-1"></a> <span class="fu">}</span></span>
<span id="cb107-22"><a href="#cb107-22" aria-hidden="true" tabindex="-1"></a> <span class="ot">]</span><span class="fu">,</span></span>
<span id="cb107-23"><a href="#cb107-23" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;total&quot;</span><span class="fu">:</span> <span class="dv">1</span></span>
<span id="cb107-24"><a href="#cb107-24" aria-hidden="true" tabindex="-1"></a> <span class="fu">}</span></span>
<span id="cb107-25"><a href="#cb107-25" aria-hidden="true" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
<h5 data-number="5.0.0.0.31" id="账单生成接口"><span
class="header-section-number">5.0.0.0.31</span> 3.3.2 账单生成接口</h5>
<p><strong>功能描述</strong>:根据抄表记录生成水费账单。</p>
<p><strong>接口详情</strong> - <strong>请求方式</strong>POST -
<strong>请求路径</strong><code>/admin-api/water/bill/generate</code></p>
<p><strong>请求参数</strong></p>
<div class="sourceCode" id="cb108"><pre
class="sourceCode json"><code class="sourceCode json"><span id="cb108-1"><a href="#cb108-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb108-2"><a href="#cb108-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;billMonth&quot;</span><span class="fu">:</span> <span class="st">&quot;2024-12&quot;</span><span class="fu">,</span></span>
<span id="cb108-3"><a href="#cb108-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;customerIds&quot;</span><span class="fu">:</span> <span class="ot">[</span><span class="dv">1</span><span class="ot">,</span> <span class="dv">2</span><span class="ot">,</span> <span class="dv">3</span><span class="ot">]</span><span class="fu">,</span></span>
<span id="cb108-4"><a href="#cb108-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;readingIds&quot;</span><span class="fu">:</span> <span class="ot">[</span><span class="dv">1</span><span class="ot">,</span> <span class="dv">2</span><span class="ot">,</span> <span class="dv">3</span><span class="ot">]</span><span class="fu">,</span></span>
<span id="cb108-5"><a href="#cb108-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;dueDate&quot;</span><span class="fu">:</span> <span class="st">&quot;2025-01-19&quot;</span></span>
<span id="cb108-6"><a href="#cb108-6" aria-hidden="true" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
<p><strong>响应参数</strong></p>
<div class="sourceCode" id="cb109"><pre
class="sourceCode json"><code class="sourceCode json"><span id="cb109-1"><a href="#cb109-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb109-2"><a href="#cb109-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;code&quot;</span><span class="fu">:</span> <span class="dv">0</span><span class="fu">,</span></span>
<span id="cb109-3"><a href="#cb109-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;msg&quot;</span><span class="fu">:</span> <span class="st">&quot;操作成功&quot;</span><span class="fu">,</span></span>
<span id="cb109-4"><a href="#cb109-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;data&quot;</span><span class="fu">:</span> <span class="fu">{</span></span>
<span id="cb109-5"><a href="#cb109-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;generateCount&quot;</span><span class="fu">:</span> <span class="dv">3</span><span class="fu">,</span></span>
<span id="cb109-6"><a href="#cb109-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;successList&quot;</span><span class="fu">:</span> <span class="ot">[</span></span>
<span id="cb109-7"><a href="#cb109-7" aria-hidden="true" tabindex="-1"></a> <span class="fu">{</span></span>
<span id="cb109-8"><a href="#cb109-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;customerId&quot;</span><span class="fu">:</span> <span class="dv">1</span><span class="fu">,</span></span>
<span id="cb109-9"><a href="#cb109-9" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;billId&quot;</span><span class="fu">:</span> <span class="dv">1</span><span class="fu">,</span></span>
<span id="cb109-10"><a href="#cb109-10" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;totalAmount&quot;</span><span class="fu">:</span> <span class="fl">91.80</span></span>
<span id="cb109-11"><a href="#cb109-11" aria-hidden="true" tabindex="-1"></a> <span class="fu">}</span></span>
<span id="cb109-12"><a href="#cb109-12" aria-hidden="true" tabindex="-1"></a> <span class="ot">]</span><span class="fu">,</span></span>
<span id="cb109-13"><a href="#cb109-13" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;failureList&quot;</span><span class="fu">:</span> <span class="ot">[]</span></span>
<span id="cb109-14"><a href="#cb109-14" aria-hidden="true" tabindex="-1"></a> <span class="fu">}</span></span>
<span id="cb109-15"><a href="#cb109-15" aria-hidden="true" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
<p><strong>Service层代码示例</strong></p>
<div class="sourceCode" id="cb110"><pre
class="sourceCode java"><code class="sourceCode java"><span id="cb110-1"><a href="#cb110-1" aria-hidden="true" tabindex="-1"></a><span class="at">@Service</span></span>
<span id="cb110-2"><a href="#cb110-2" aria-hidden="true" tabindex="-1"></a><span class="at">@Validated</span></span>
<span id="cb110-3"><a href="#cb110-3" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span> <span class="kw">class</span> BillServiceImpl <span class="kw">implements</span> BillService <span class="op">{</span></span>
<span id="cb110-4"><a href="#cb110-4" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb110-5"><a href="#cb110-5" aria-hidden="true" tabindex="-1"></a> <span class="at">@Resource</span></span>
<span id="cb110-6"><a href="#cb110-6" aria-hidden="true" tabindex="-1"></a> <span class="kw">private</span> BillMapper billMapper<span class="op">;</span></span>
<span id="cb110-7"><a href="#cb110-7" aria-hidden="true" tabindex="-1"></a> <span class="at">@Resource</span></span>
<span id="cb110-8"><a href="#cb110-8" aria-hidden="true" tabindex="-1"></a> <span class="kw">private</span> MeterReadingService readingService<span class="op">;</span></span>
<span id="cb110-9"><a href="#cb110-9" aria-hidden="true" tabindex="-1"></a> <span class="at">@Resource</span></span>
<span id="cb110-10"><a href="#cb110-10" aria-hidden="true" tabindex="-1"></a> <span class="kw">private</span> WaterPriceService priceService<span class="op">;</span></span>
<span id="cb110-11"><a href="#cb110-11" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb110-12"><a href="#cb110-12" aria-hidden="true" tabindex="-1"></a> <span class="at">@Override</span></span>
<span id="cb110-13"><a href="#cb110-13" aria-hidden="true" tabindex="-1"></a> <span class="at">@Transactional</span><span class="op">(</span>rollbackFor <span class="op">=</span> <span class="bu">Exception</span><span class="op">.</span><span class="fu">class</span><span class="op">)</span></span>
<span id="cb110-14"><a href="#cb110-14" aria-hidden="true" tabindex="-1"></a> <span class="kw">public</span> BillGenerateRespVO <span class="fu">generateBills</span><span class="op">(</span>BillGenerateReqVO generateReqVO<span class="op">)</span> <span class="op">{</span></span>
<span id="cb110-15"><a href="#cb110-15" aria-hidden="true" tabindex="-1"></a> BillGenerateRespVO result <span class="op">=</span> <span class="kw">new</span> <span class="fu">BillGenerateRespVO</span><span class="op">();</span></span>
<span id="cb110-16"><a href="#cb110-16" aria-hidden="true" tabindex="-1"></a> <span class="bu">List</span><span class="op">&lt;</span>BillGenerateDetailVO<span class="op">&gt;</span> successList <span class="op">=</span> <span class="kw">new</span> <span class="bu">ArrayList</span><span class="op">&lt;&gt;();</span></span>
<span id="cb110-17"><a href="#cb110-17" aria-hidden="true" tabindex="-1"></a> <span class="bu">List</span><span class="op">&lt;</span>BillGenerateDetailVO<span class="op">&gt;</span> failureList <span class="op">=</span> <span class="kw">new</span> <span class="bu">ArrayList</span><span class="op">&lt;&gt;();</span></span>
<span id="cb110-18"><a href="#cb110-18" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb110-19"><a href="#cb110-19" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> <span class="op">(</span><span class="bu">Long</span> readingId <span class="op">:</span> generateReqVO<span class="op">.</span><span class="fu">getReadingIds</span><span class="op">())</span> <span class="op">{</span></span>
<span id="cb110-20"><a href="#cb110-20" aria-hidden="true" tabindex="-1"></a> <span class="cf">try</span> <span class="op">{</span></span>
<span id="cb110-21"><a href="#cb110-21" aria-hidden="true" tabindex="-1"></a> <span class="co">// 获取抄表记录</span></span>
<span id="cb110-22"><a href="#cb110-22" aria-hidden="true" tabindex="-1"></a> MeterReadingDO reading <span class="op">=</span> readingService<span class="op">.</span><span class="fu">getReading</span><span class="op">(</span>readingId<span class="op">);</span></span>
<span id="cb110-23"><a href="#cb110-23" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb110-24"><a href="#cb110-24" aria-hidden="true" tabindex="-1"></a> <span class="co">// 计算水费</span></span>
<span id="cb110-25"><a href="#cb110-25" aria-hidden="true" tabindex="-1"></a> WaterFeeCalculateDTO feeResult <span class="op">=</span> priceService<span class="op">.</span><span class="fu">calculateWaterFee</span><span class="op">(</span></span>
<span id="cb110-26"><a href="#cb110-26" aria-hidden="true" tabindex="-1"></a> reading<span class="op">.</span><span class="fu">getCustomerId</span><span class="op">(),</span> reading<span class="op">.</span><span class="fu">getWaterUsage</span><span class="op">());</span></span>
<span id="cb110-27"><a href="#cb110-27" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb110-28"><a href="#cb110-28" aria-hidden="true" tabindex="-1"></a> <span class="co">// 创建账单</span></span>
<span id="cb110-29"><a href="#cb110-29" aria-hidden="true" tabindex="-1"></a> BillDO bill <span class="op">=</span> <span class="kw">new</span> <span class="fu">BillDO</span><span class="op">();</span></span>
<span id="cb110-30"><a href="#cb110-30" aria-hidden="true" tabindex="-1"></a> bill<span class="op">.</span><span class="fu">setBillCode</span><span class="op">(</span><span class="fu">generateBillCode</span><span class="op">());</span></span>
<span id="cb110-31"><a href="#cb110-31" aria-hidden="true" tabindex="-1"></a> bill<span class="op">.</span><span class="fu">setBillMonth</span><span class="op">(</span>generateReqVO<span class="op">.</span><span class="fu">getBillMonth</span><span class="op">());</span></span>
<span id="cb110-32"><a href="#cb110-32" aria-hidden="true" tabindex="-1"></a> bill<span class="op">.</span><span class="fu">setCustomerId</span><span class="op">(</span>reading<span class="op">.</span><span class="fu">getCustomerId</span><span class="op">());</span></span>
<span id="cb110-33"><a href="#cb110-33" aria-hidden="true" tabindex="-1"></a> bill<span class="op">.</span><span class="fu">setMeterId</span><span class="op">(</span>reading<span class="op">.</span><span class="fu">getMeterId</span><span class="op">());</span></span>
<span id="cb110-34"><a href="#cb110-34" aria-hidden="true" tabindex="-1"></a> bill<span class="op">.</span><span class="fu">setReadingId</span><span class="op">(</span>readingId<span class="op">);</span></span>
<span id="cb110-35"><a href="#cb110-35" aria-hidden="true" tabindex="-1"></a> bill<span class="op">.</span><span class="fu">setWaterUsage</span><span class="op">(</span>reading<span class="op">.</span><span class="fu">getWaterUsage</span><span class="op">());</span></span>
<span id="cb110-36"><a href="#cb110-36" aria-hidden="true" tabindex="-1"></a> bill<span class="op">.</span><span class="fu">setWaterFee</span><span class="op">(</span>feeResult<span class="op">.</span><span class="fu">getWaterFee</span><span class="op">());</span></span>
<span id="cb110-37"><a href="#cb110-37" aria-hidden="true" tabindex="-1"></a> bill<span class="op">.</span><span class="fu">setSewageFee</span><span class="op">(</span>feeResult<span class="op">.</span><span class="fu">getSewageFee</span><span class="op">());</span></span>
<span id="cb110-38"><a href="#cb110-38" aria-hidden="true" tabindex="-1"></a> bill<span class="op">.</span><span class="fu">setTotalAmount</span><span class="op">(</span>feeResult<span class="op">.</span><span class="fu">getTotalAmount</span><span class="op">());</span></span>
<span id="cb110-39"><a href="#cb110-39" aria-hidden="true" tabindex="-1"></a> bill<span class="op">.</span><span class="fu">setDueDate</span><span class="op">(</span>generateReqVO<span class="op">.</span><span class="fu">getDueDate</span><span class="op">());</span></span>
<span id="cb110-40"><a href="#cb110-40" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb110-41"><a href="#cb110-41" aria-hidden="true" tabindex="-1"></a> billMapper<span class="op">.</span><span class="fu">insert</span><span class="op">(</span>bill<span class="op">);</span></span>
<span id="cb110-42"><a href="#cb110-42" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb110-43"><a href="#cb110-43" aria-hidden="true" tabindex="-1"></a> successList<span class="op">.</span><span class="fu">add</span><span class="op">(</span><span class="fu">buildSuccessDetail</span><span class="op">(</span>reading<span class="op">.</span><span class="fu">getCustomerId</span><span class="op">(),</span> </span>
<span id="cb110-44"><a href="#cb110-44" aria-hidden="true" tabindex="-1"></a> bill<span class="op">.</span><span class="fu">getId</span><span class="op">(),</span> feeResult<span class="op">.</span><span class="fu">getTotalAmount</span><span class="op">()));</span></span>
<span id="cb110-45"><a href="#cb110-45" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb110-46"><a href="#cb110-46" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span> <span class="cf">catch</span> <span class="op">(</span><span class="bu">Exception</span> e<span class="op">)</span> <span class="op">{</span></span>
<span id="cb110-47"><a href="#cb110-47" aria-hidden="true" tabindex="-1"></a> failureList<span class="op">.</span><span class="fu">add</span><span class="op">(</span><span class="fu">buildFailureDetail</span><span class="op">(</span>readingId<span class="op">,</span> e<span class="op">.</span><span class="fu">getMessage</span><span class="op">()));</span></span>
<span id="cb110-48"><a href="#cb110-48" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb110-49"><a href="#cb110-49" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb110-50"><a href="#cb110-50" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb110-51"><a href="#cb110-51" aria-hidden="true" tabindex="-1"></a> result<span class="op">.</span><span class="fu">setGenerateCount</span><span class="op">(</span>successList<span class="op">.</span><span class="fu">size</span><span class="op">());</span></span>
<span id="cb110-52"><a href="#cb110-52" aria-hidden="true" tabindex="-1"></a> result<span class="op">.</span><span class="fu">setSuccessList</span><span class="op">(</span>successList<span class="op">);</span></span>
<span id="cb110-53"><a href="#cb110-53" aria-hidden="true" tabindex="-1"></a> result<span class="op">.</span><span class="fu">setFailureList</span><span class="op">(</span>failureList<span class="op">);</span></span>
<span id="cb110-54"><a href="#cb110-54" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> result<span class="op">;</span></span>
<span id="cb110-55"><a href="#cb110-55" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb110-56"><a href="#cb110-56" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<h5 data-number="5.0.0.0.32" id="缴费管理api接口"><span
class="header-section-number">5.0.0.0.32</span> 3.4 缴费管理API接口</h5>
<h5 data-number="5.0.0.0.33" id="缴费处理接口"><span
class="header-section-number">5.0.0.0.33</span> 3.4.1 缴费处理接口</h5>
<p><strong>功能描述</strong>:处理客户缴费操作。</p>
<p><strong>接口详情</strong> - <strong>请求方式</strong>POST -
<strong>请求路径</strong><code>/admin-api/water/payment/create</code></p>
<p><strong>请求参数</strong></p>
<div class="sourceCode" id="cb111"><pre
class="sourceCode json"><code class="sourceCode json"><span id="cb111-1"><a href="#cb111-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb111-2"><a href="#cb111-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;customerId&quot;</span><span class="fu">:</span> <span class="dv">1</span><span class="fu">,</span></span>
<span id="cb111-3"><a href="#cb111-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;billIds&quot;</span><span class="fu">:</span> <span class="ot">[</span><span class="dv">1</span><span class="ot">,</span> <span class="dv">2</span><span class="ot">]</span><span class="fu">,</span></span>
<span id="cb111-4"><a href="#cb111-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;paymentType&quot;</span><span class="fu">:</span> <span class="st">&quot;NORMAL&quot;</span><span class="fu">,</span></span>
<span id="cb111-5"><a href="#cb111-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;paymentChannel&quot;</span><span class="fu">:</span> <span class="st">&quot;CASH&quot;</span><span class="fu">,</span></span>
<span id="cb111-6"><a href="#cb111-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;paymentAmount&quot;</span><span class="fu">:</span> <span class="fl">183.60</span><span class="fu">,</span></span>
<span id="cb111-7"><a href="#cb111-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;actualAmount&quot;</span><span class="fu">:</span> <span class="fl">200.00</span><span class="fu">,</span></span>
<span id="cb111-8"><a href="#cb111-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;operatorId&quot;</span><span class="fu">:</span> <span class="st">&quot;OP001&quot;</span><span class="fu">,</span></span>
<span id="cb111-9"><a href="#cb111-9" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;outletCode&quot;</span><span class="fu">:</span> <span class="st">&quot;OUT001&quot;</span><span class="fu">,</span></span>
<span id="cb111-10"><a href="#cb111-10" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;remark&quot;</span><span class="fu">:</span> <span class="st">&quot;现金缴费&quot;</span></span>
<span id="cb111-11"><a href="#cb111-11" aria-hidden="true" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
<p><strong>响应参数</strong></p>
<div class="sourceCode" id="cb112"><pre
class="sourceCode json"><code class="sourceCode json"><span id="cb112-1"><a href="#cb112-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb112-2"><a href="#cb112-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;code&quot;</span><span class="fu">:</span> <span class="dv">0</span><span class="fu">,</span></span>
<span id="cb112-3"><a href="#cb112-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;msg&quot;</span><span class="fu">:</span> <span class="st">&quot;操作成功&quot;</span><span class="fu">,</span></span>
<span id="cb112-4"><a href="#cb112-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;data&quot;</span><span class="fu">:</span> <span class="fu">{</span></span>
<span id="cb112-5"><a href="#cb112-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;paymentId&quot;</span><span class="fu">:</span> <span class="dv">1</span><span class="fu">,</span></span>
<span id="cb112-6"><a href="#cb112-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;paymentCode&quot;</span><span class="fu">:</span> <span class="st">&quot;P202412190001&quot;</span><span class="fu">,</span></span>
<span id="cb112-7"><a href="#cb112-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;changeAmount&quot;</span><span class="fu">:</span> <span class="fl">16.40</span><span class="fu">,</span></span>
<span id="cb112-8"><a href="#cb112-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;invoiceNo&quot;</span><span class="fu">:</span> <span class="st">&quot;INV20241219001&quot;</span></span>
<span id="cb112-9"><a href="#cb112-9" aria-hidden="true" tabindex="-1"></a> <span class="fu">}</span></span>
<span id="cb112-10"><a href="#cb112-10" aria-hidden="true" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
<h5 data-number="5.0.0.0.34" id="在线支付接口"><span
class="header-section-number">5.0.0.0.34</span> 3.4.2 在线支付接口</h5>
<p><strong>功能描述</strong>:处理在线支付(微信、支付宝等)。</p>
<p><strong>接口详情</strong> - <strong>请求方式</strong>POST -
<strong>请求路径</strong><code>/admin-api/water/payment/online-pay</code></p>
<p><strong>请求参数</strong></p>
<div class="sourceCode" id="cb113"><pre
class="sourceCode json"><code class="sourceCode json"><span id="cb113-1"><a href="#cb113-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb113-2"><a href="#cb113-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;customerId&quot;</span><span class="fu">:</span> <span class="dv">1</span><span class="fu">,</span></span>
<span id="cb113-3"><a href="#cb113-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;billIds&quot;</span><span class="fu">:</span> <span class="ot">[</span><span class="dv">1</span><span class="ot">]</span><span class="fu">,</span></span>
<span id="cb113-4"><a href="#cb113-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;paymentChannel&quot;</span><span class="fu">:</span> <span class="st">&quot;WECHAT&quot;</span><span class="fu">,</span></span>
<span id="cb113-5"><a href="#cb113-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;paymentAmount&quot;</span><span class="fu">:</span> <span class="fl">91.80</span><span class="fu">,</span></span>
<span id="cb113-6"><a href="#cb113-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;returnUrl&quot;</span><span class="fu">:</span> <span class="st">&quot;https://water.example.com/payment/callback&quot;</span><span class="fu">,</span></span>
<span id="cb113-7"><a href="#cb113-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;notifyUrl&quot;</span><span class="fu">:</span> <span class="st">&quot;https://water.example.com/api/payment/notify&quot;</span></span>
<span id="cb113-8"><a href="#cb113-8" aria-hidden="true" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
<p><strong>响应参数</strong></p>
<div class="sourceCode" id="cb114"><pre
class="sourceCode json"><code class="sourceCode json"><span id="cb114-1"><a href="#cb114-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb114-2"><a href="#cb114-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;code&quot;</span><span class="fu">:</span> <span class="dv">0</span><span class="fu">,</span></span>
<span id="cb114-3"><a href="#cb114-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;msg&quot;</span><span class="fu">:</span> <span class="st">&quot;操作成功&quot;</span><span class="fu">,</span></span>
<span id="cb114-4"><a href="#cb114-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;data&quot;</span><span class="fu">:</span> <span class="fu">{</span></span>
<span id="cb114-5"><a href="#cb114-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;paymentCode&quot;</span><span class="fu">:</span> <span class="st">&quot;P202412190002&quot;</span><span class="fu">,</span></span>
<span id="cb114-6"><a href="#cb114-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;prepayId&quot;</span><span class="fu">:</span> <span class="st">&quot;wx20241219001234567890&quot;</span><span class="fu">,</span></span>
<span id="cb114-7"><a href="#cb114-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;payUrl&quot;</span><span class="fu">:</span> <span class="st">&quot;weixin://wxpay/bizpayurl?pr=abc123&quot;</span><span class="fu">,</span></span>
<span id="cb114-8"><a href="#cb114-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;qrCode&quot;</span><span class="fu">:</span> <span class="st">&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...&quot;</span></span>
<span id="cb114-9"><a href="#cb114-9" aria-hidden="true" tabindex="-1"></a> <span class="fu">}</span></span>
<span id="cb114-10"><a href="#cb114-10" aria-hidden="true" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
<h5 data-number="5.0.0.0.35" id="工单接口"><span
class="header-section-number">5.0.0.0.35</span> 3.4 工单接口</h5>
<h5 data-number="5.0.0.0.36" id="工单创建接口"><span
class="header-section-number">5.0.0.0.36</span> 3.4.1 工单创建接口</h5>
<p><strong>功能描述</strong>:创建业务工单。</p>
<p><strong>接口规范</strong> - 请求方式POST -
请求路径:/api/workorders - 请求/返回格式JSON</p>
<h5 data-number="5.0.0.0.37" id="工单状态更新接口"><span
class="header-section-number">5.0.0.0.37</span> 3.4.2
工单状态更新接口</h5>
<p><strong>功能描述</strong>:更新工单处理状态。</p>
<p><strong>接口规范</strong> - 请求方式PUT -
请求路径:/api/workorders/{workorderId}/status - 请求/返回格式JSON</p>
<h5 data-number="5.0.0.0.38" id="接口标准-1"><span
class="header-section-number">5.0.0.0.38</span> 4. 接口标准</h5>
<h5 data-number="5.0.0.0.39" id="接口协议"><span
class="header-section-number">5.0.0.0.39</span> 4.1 接口协议</h5>
<p>系统接口主要采用以下协议:</p>
<ul>
<li><strong>RESTful
API</strong>:适用于系统内部模块间的交互以及移动应用等轻量级客户端</li>
<li><strong>WebService</strong>:适用于与外部系统的集成,特别是银行等传统机构</li>
<li><strong>消息队列</strong>:适用于异步处理的场景,如批量数据处理、通知推送等</li>
</ul>
<h5 data-number="5.0.0.0.40" id="数据格式"><span
class="header-section-number">5.0.0.0.40</span> 4.2 数据格式</h5>
<p>接口数据主要采用以下格式:</p>
<ul>
<li><strong>JSON</strong>主要用于RESTful
API接口结构简单清晰适合Web应用</li>
<li><strong>XML</strong>主要用于WebService接口兼容性好适合与传统系统对接</li>
<li><strong>文本文件</strong>:主要用于批量数据交换,如银行代扣文件等</li>
</ul>
<h5 data-number="5.0.0.0.41" id="接口安全设计"><span
class="header-section-number">5.0.0.0.41</span> 4.3 接口安全设计</h5>
<p>接口安全采用多层防护机制:</p>
<h5 data-number="5.0.0.0.42" id="认证机制"><span
class="header-section-number">5.0.0.0.42</span> 4.3.1 认证机制</h5>
<p><strong>JWT令牌认证</strong></p>
<div class="sourceCode" id="cb115"><pre
class="sourceCode java"><code class="sourceCode java"><span id="cb115-1"><a href="#cb115-1" aria-hidden="true" tabindex="-1"></a><span class="at">@RestController</span></span>
<span id="cb115-2"><a href="#cb115-2" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span> <span class="kw">class</span> AuthController <span class="op">{</span></span>
<span id="cb115-3"><a href="#cb115-3" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb115-4"><a href="#cb115-4" aria-hidden="true" tabindex="-1"></a> <span class="at">@Resource</span></span>
<span id="cb115-5"><a href="#cb115-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">private</span> AuthService authService<span class="op">;</span></span>
<span id="cb115-6"><a href="#cb115-6" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb115-7"><a href="#cb115-7" aria-hidden="true" tabindex="-1"></a> <span class="at">@PostMapping</span><span class="op">(</span><span class="st">&quot;/admin-api/system/auth/login&quot;</span><span class="op">)</span></span>
<span id="cb115-8"><a href="#cb115-8" aria-hidden="true" tabindex="-1"></a> <span class="kw">public</span> CommonResult<span class="op">&lt;</span>AuthLoginRespVO<span class="op">&gt;</span> <span class="fu">login</span><span class="op">(</span><span class="at">@Valid</span> <span class="at">@RequestBody</span> AuthLoginReqVO reqVO<span class="op">)</span> <span class="op">{</span></span>
<span id="cb115-9"><a href="#cb115-9" aria-hidden="true" tabindex="-1"></a> <span class="co">// 验证用户名密码</span></span>
<span id="cb115-10"><a href="#cb115-10" aria-hidden="true" tabindex="-1"></a> AdminUserDO user <span class="op">=</span> authService<span class="op">.</span><span class="fu">authenticate</span><span class="op">(</span>reqVO<span class="op">.</span><span class="fu">getUsername</span><span class="op">(),</span> reqVO<span class="op">.</span><span class="fu">getPassword</span><span class="op">());</span></span>
<span id="cb115-11"><a href="#cb115-11" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb115-12"><a href="#cb115-12" aria-hidden="true" tabindex="-1"></a> <span class="co">// 生成JWT Token</span></span>
<span id="cb115-13"><a href="#cb115-13" aria-hidden="true" tabindex="-1"></a> <span class="bu">String</span> token <span class="op">=</span> authService<span class="op">.</span><span class="fu">createToken</span><span class="op">(</span>user<span class="op">.</span><span class="fu">getId</span><span class="op">(),</span> user<span class="op">.</span><span class="fu">getTenantId</span><span class="op">());</span></span>
<span id="cb115-14"><a href="#cb115-14" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb115-15"><a href="#cb115-15" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="fu">success</span><span class="op">(</span>AuthLoginRespVO<span class="op">.</span><span class="fu">builder</span><span class="op">()</span></span>
<span id="cb115-16"><a href="#cb115-16" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">userId</span><span class="op">(</span>user<span class="op">.</span><span class="fu">getId</span><span class="op">())</span></span>
<span id="cb115-17"><a href="#cb115-17" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">accessToken</span><span class="op">(</span>token<span class="op">)</span></span>
<span id="cb115-18"><a href="#cb115-18" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">refreshToken</span><span class="op">(</span>authService<span class="op">.</span><span class="fu">createRefreshToken</span><span class="op">(</span>user<span class="op">.</span><span class="fu">getId</span><span class="op">()))</span></span>
<span id="cb115-19"><a href="#cb115-19" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">expiresTime</span><span class="op">(</span>LocalDateTime<span class="op">.</span><span class="fu">now</span><span class="op">().</span><span class="fu">plusHours</span><span class="op">(</span><span class="dv">2</span><span class="op">))</span></span>
<span id="cb115-20"><a href="#cb115-20" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">build</span><span class="op">());</span></span>
<span id="cb115-21"><a href="#cb115-21" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb115-22"><a href="#cb115-22" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p><strong>API Key认证</strong>(外部系统):</p>
<div class="sourceCode" id="cb116"><pre
class="sourceCode java"><code class="sourceCode java"><span id="cb116-1"><a href="#cb116-1" aria-hidden="true" tabindex="-1"></a><span class="at">@Component</span></span>
<span id="cb116-2"><a href="#cb116-2" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span> <span class="kw">class</span> ApiKeyAuthenticationFilter <span class="kw">extends</span> OncePerRequestFilter <span class="op">{</span></span>
<span id="cb116-3"><a href="#cb116-3" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb116-4"><a href="#cb116-4" aria-hidden="true" tabindex="-1"></a> <span class="at">@Override</span></span>
<span id="cb116-5"><a href="#cb116-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">protected</span> <span class="dt">void</span> <span class="fu">doFilterInternal</span><span class="op">(</span>HttpServletRequest request<span class="op">,</span> </span>
<span id="cb116-6"><a href="#cb116-6" aria-hidden="true" tabindex="-1"></a> HttpServletResponse response<span class="op">,</span> </span>
<span id="cb116-7"><a href="#cb116-7" aria-hidden="true" tabindex="-1"></a> FilterChain filterChain<span class="op">)</span> <span class="kw">throws</span> ServletException<span class="op">,</span> <span class="bu">IOException</span> <span class="op">{</span></span>
<span id="cb116-8"><a href="#cb116-8" aria-hidden="true" tabindex="-1"></a> <span class="bu">String</span> apiKey <span class="op">=</span> request<span class="op">.</span><span class="fu">getHeader</span><span class="op">(</span><span class="st">&quot;X-API-KEY&quot;</span><span class="op">);</span></span>
<span id="cb116-9"><a href="#cb116-9" aria-hidden="true" tabindex="-1"></a> <span class="bu">String</span> timestamp <span class="op">=</span> request<span class="op">.</span><span class="fu">getHeader</span><span class="op">(</span><span class="st">&quot;X-TIMESTAMP&quot;</span><span class="op">);</span></span>
<span id="cb116-10"><a href="#cb116-10" aria-hidden="true" tabindex="-1"></a> <span class="bu">String</span> signature <span class="op">=</span> request<span class="op">.</span><span class="fu">getHeader</span><span class="op">(</span><span class="st">&quot;X-SIGNATURE&quot;</span><span class="op">);</span></span>
<span id="cb116-11"><a href="#cb116-11" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb116-12"><a href="#cb116-12" aria-hidden="true" tabindex="-1"></a> <span class="co">// 验证API Key</span></span>
<span id="cb116-13"><a href="#cb116-13" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(!</span>apiKeyService<span class="op">.</span><span class="fu">validateApiKey</span><span class="op">(</span>apiKey<span class="op">))</span> <span class="op">{</span></span>
<span id="cb116-14"><a href="#cb116-14" aria-hidden="true" tabindex="-1"></a> <span class="fu">writeErrorResponse</span><span class="op">(</span>response<span class="op">,</span> <span class="st">&quot;Invalid API Key&quot;</span><span class="op">);</span></span>
<span id="cb116-15"><a href="#cb116-15" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span><span class="op">;</span></span>
<span id="cb116-16"><a href="#cb116-16" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb116-17"><a href="#cb116-17" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb116-18"><a href="#cb116-18" aria-hidden="true" tabindex="-1"></a> <span class="co">// 验证时间戳(防重放攻击)</span></span>
<span id="cb116-19"><a href="#cb116-19" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(!</span><span class="fu">validateTimestamp</span><span class="op">(</span>timestamp<span class="op">))</span> <span class="op">{</span></span>
<span id="cb116-20"><a href="#cb116-20" aria-hidden="true" tabindex="-1"></a> <span class="fu">writeErrorResponse</span><span class="op">(</span>response<span class="op">,</span> <span class="st">&quot;Request expired&quot;</span><span class="op">);</span></span>
<span id="cb116-21"><a href="#cb116-21" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span><span class="op">;</span></span>
<span id="cb116-22"><a href="#cb116-22" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb116-23"><a href="#cb116-23" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb116-24"><a href="#cb116-24" aria-hidden="true" tabindex="-1"></a> <span class="co">// 验证签名</span></span>
<span id="cb116-25"><a href="#cb116-25" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(!</span><span class="fu">validateSignature</span><span class="op">(</span>request<span class="op">,</span> signature<span class="op">))</span> <span class="op">{</span></span>
<span id="cb116-26"><a href="#cb116-26" aria-hidden="true" tabindex="-1"></a> <span class="fu">writeErrorResponse</span><span class="op">(</span>response<span class="op">,</span> <span class="st">&quot;Invalid signature&quot;</span><span class="op">);</span></span>
<span id="cb116-27"><a href="#cb116-27" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span><span class="op">;</span></span>
<span id="cb116-28"><a href="#cb116-28" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb116-29"><a href="#cb116-29" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb116-30"><a href="#cb116-30" aria-hidden="true" tabindex="-1"></a> filterChain<span class="op">.</span><span class="fu">doFilter</span><span class="op">(</span>request<span class="op">,</span> response<span class="op">);</span></span>
<span id="cb116-31"><a href="#cb116-31" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb116-32"><a href="#cb116-32" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<h5 data-number="5.0.0.0.43" id="数据加密"><span
class="header-section-number">5.0.0.0.43</span> 4.3.2 数据加密</h5>
<p><strong>敏感数据加密</strong></p>
<div class="sourceCode" id="cb117"><pre
class="sourceCode java"><code class="sourceCode java"><span id="cb117-1"><a href="#cb117-1" aria-hidden="true" tabindex="-1"></a><span class="at">@Component</span></span>
<span id="cb117-2"><a href="#cb117-2" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span> <span class="kw">class</span> DataEncryptionService <span class="op">{</span></span>
<span id="cb117-3"><a href="#cb117-3" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb117-4"><a href="#cb117-4" aria-hidden="true" tabindex="-1"></a> <span class="kw">private</span> <span class="dt">final</span> AESUtil aesUtil<span class="op">;</span></span>
<span id="cb117-5"><a href="#cb117-5" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb117-6"><a href="#cb117-6" aria-hidden="true" tabindex="-1"></a> <span class="kw">public</span> <span class="bu">String</span> <span class="fu">encryptPersonalInfo</span><span class="op">(</span><span class="bu">String</span> plainText<span class="op">)</span> <span class="op">{</span></span>
<span id="cb117-7"><a href="#cb117-7" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>StrUtil<span class="op">.</span><span class="fu">isBlank</span><span class="op">(</span>plainText<span class="op">))</span> <span class="op">{</span></span>
<span id="cb117-8"><a href="#cb117-8" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> plainText<span class="op">;</span></span>
<span id="cb117-9"><a href="#cb117-9" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb117-10"><a href="#cb117-10" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> aesUtil<span class="op">.</span><span class="fu">encrypt</span><span class="op">(</span>plainText<span class="op">);</span></span>
<span id="cb117-11"><a href="#cb117-11" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb117-12"><a href="#cb117-12" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb117-13"><a href="#cb117-13" aria-hidden="true" tabindex="-1"></a> <span class="kw">public</span> <span class="bu">String</span> <span class="fu">decryptPersonalInfo</span><span class="op">(</span><span class="bu">String</span> cipherText<span class="op">)</span> <span class="op">{</span></span>
<span id="cb117-14"><a href="#cb117-14" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>StrUtil<span class="op">.</span><span class="fu">isBlank</span><span class="op">(</span>cipherText<span class="op">))</span> <span class="op">{</span></span>
<span id="cb117-15"><a href="#cb117-15" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> cipherText<span class="op">;</span></span>
<span id="cb117-16"><a href="#cb117-16" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb117-17"><a href="#cb117-17" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> aesUtil<span class="op">.</span><span class="fu">decrypt</span><span class="op">(</span>cipherText<span class="op">);</span></span>
<span id="cb117-18"><a href="#cb117-18" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb117-19"><a href="#cb117-19" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<h5 data-number="5.0.0.0.44" id="访问控制"><span
class="header-section-number">5.0.0.0.44</span> 4.3.3 访问控制</h5>
<p><strong>IP白名单控制</strong></p>
<div class="sourceCode" id="cb118"><pre
class="sourceCode java"><code class="sourceCode java"><span id="cb118-1"><a href="#cb118-1" aria-hidden="true" tabindex="-1"></a><span class="at">@Component</span></span>
<span id="cb118-2"><a href="#cb118-2" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span> <span class="kw">class</span> IpWhitelistFilter <span class="kw">extends</span> OncePerRequestFilter <span class="op">{</span></span>
<span id="cb118-3"><a href="#cb118-3" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb118-4"><a href="#cb118-4" aria-hidden="true" tabindex="-1"></a> <span class="at">@Value</span><span class="op">(</span><span class="st">&quot;${water.security.ip-whitelist}&quot;</span><span class="op">)</span></span>
<span id="cb118-5"><a href="#cb118-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">private</span> <span class="bu">List</span><span class="op">&lt;</span><span class="bu">String</span><span class="op">&gt;</span> ipWhitelist<span class="op">;</span></span>
<span id="cb118-6"><a href="#cb118-6" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb118-7"><a href="#cb118-7" aria-hidden="true" tabindex="-1"></a> <span class="at">@Override</span></span>
<span id="cb118-8"><a href="#cb118-8" aria-hidden="true" tabindex="-1"></a> <span class="kw">protected</span> <span class="dt">void</span> <span class="fu">doFilterInternal</span><span class="op">(</span>HttpServletRequest request<span class="op">,</span> </span>
<span id="cb118-9"><a href="#cb118-9" aria-hidden="true" tabindex="-1"></a> HttpServletResponse response<span class="op">,</span> </span>
<span id="cb118-10"><a href="#cb118-10" aria-hidden="true" tabindex="-1"></a> FilterChain filterChain<span class="op">)</span> <span class="kw">throws</span> ServletException<span class="op">,</span> <span class="bu">IOException</span> <span class="op">{</span></span>
<span id="cb118-11"><a href="#cb118-11" aria-hidden="true" tabindex="-1"></a> <span class="bu">String</span> clientIp <span class="op">=</span> <span class="fu">getClientIpAddress</span><span class="op">(</span>request<span class="op">);</span></span>
<span id="cb118-12"><a href="#cb118-12" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb118-13"><a href="#cb118-13" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(!</span><span class="fu">isIpAllowed</span><span class="op">(</span>clientIp<span class="op">))</span> <span class="op">{</span></span>
<span id="cb118-14"><a href="#cb118-14" aria-hidden="true" tabindex="-1"></a> response<span class="op">.</span><span class="fu">setStatus</span><span class="op">(</span>HttpStatus<span class="op">.</span><span class="fu">FORBIDDEN</span><span class="op">.</span><span class="fu">value</span><span class="op">());</span></span>
<span id="cb118-15"><a href="#cb118-15" aria-hidden="true" tabindex="-1"></a> response<span class="op">.</span><span class="fu">getWriter</span><span class="op">().</span><span class="fu">write</span><span class="op">(</span><span class="st">&quot;{</span><span class="sc">\&quot;</span><span class="st">code</span><span class="sc">\&quot;</span><span class="st">:403,</span><span class="sc">\&quot;</span><span class="st">msg</span><span class="sc">\&quot;</span><span class="st">:</span><span class="sc">\&quot;</span><span class="st">IP access denied</span><span class="sc">\&quot;</span><span class="st">}&quot;</span><span class="op">);</span></span>
<span id="cb118-16"><a href="#cb118-16" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span><span class="op">;</span></span>
<span id="cb118-17"><a href="#cb118-17" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb118-18"><a href="#cb118-18" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb118-19"><a href="#cb118-19" aria-hidden="true" tabindex="-1"></a> filterChain<span class="op">.</span><span class="fu">doFilter</span><span class="op">(</span>request<span class="op">,</span> response<span class="op">);</span></span>
<span id="cb118-20"><a href="#cb118-20" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb118-21"><a href="#cb118-21" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<h5 data-number="5.0.0.0.45" id="接口限流"><span
class="header-section-number">5.0.0.0.45</span> 4.3.4 接口限流</h5>
<p><strong>基于Redis的令牌桶限流</strong></p>
<div class="sourceCode" id="cb119"><pre
class="sourceCode java"><code class="sourceCode java"><span id="cb119-1"><a href="#cb119-1" aria-hidden="true" tabindex="-1"></a><span class="at">@Component</span></span>
<span id="cb119-2"><a href="#cb119-2" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span> <span class="kw">class</span> RateLimitService <span class="op">{</span></span>
<span id="cb119-3"><a href="#cb119-3" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb119-4"><a href="#cb119-4" aria-hidden="true" tabindex="-1"></a> <span class="at">@Resource</span></span>
<span id="cb119-5"><a href="#cb119-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">private</span> StringRedisTemplate redisTemplate<span class="op">;</span></span>
<span id="cb119-6"><a href="#cb119-6" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb119-7"><a href="#cb119-7" aria-hidden="true" tabindex="-1"></a> <span class="kw">public</span> <span class="dt">boolean</span> <span class="fu">allowRequest</span><span class="op">(</span><span class="bu">String</span> key<span class="op">,</span> <span class="dt">int</span> maxRequests<span class="op">,</span> <span class="bu">Duration</span> window<span class="op">)</span> <span class="op">{</span></span>
<span id="cb119-8"><a href="#cb119-8" aria-hidden="true" tabindex="-1"></a> <span class="bu">String</span> redisKey <span class="op">=</span> <span class="st">&quot;rate_limit:&quot;</span> <span class="op">+</span> key<span class="op">;</span></span>
<span id="cb119-9"><a href="#cb119-9" aria-hidden="true" tabindex="-1"></a> <span class="bu">String</span> script <span class="op">=</span> <span class="st">&quot;&quot;&quot;</span></span>
<span id="cb119-10"><a href="#cb119-10" aria-hidden="true" tabindex="-1"></a><span class="st"> local key = KEYS[1]</span></span>
<span id="cb119-11"><a href="#cb119-11" aria-hidden="true" tabindex="-1"></a><span class="st"> local window = tonumber(ARGV[1])</span></span>
<span id="cb119-12"><a href="#cb119-12" aria-hidden="true" tabindex="-1"></a><span class="st"> local limit = tonumber(ARGV[2])</span></span>
<span id="cb119-13"><a href="#cb119-13" aria-hidden="true" tabindex="-1"></a><span class="st"> local current = redis.call(&#39;get&#39;, key)</span></span>
<span id="cb119-14"><a href="#cb119-14" aria-hidden="true" tabindex="-1"></a><span class="st"> if current == false then</span></span>
<span id="cb119-15"><a href="#cb119-15" aria-hidden="true" tabindex="-1"></a><span class="st"> redis.call(&#39;setex&#39;, key, window, 1)</span></span>
<span id="cb119-16"><a href="#cb119-16" aria-hidden="true" tabindex="-1"></a><span class="st"> return 1</span></span>
<span id="cb119-17"><a href="#cb119-17" aria-hidden="true" tabindex="-1"></a><span class="st"> end</span></span>
<span id="cb119-18"><a href="#cb119-18" aria-hidden="true" tabindex="-1"></a><span class="st"> if tonumber(current) &lt; limit then</span></span>
<span id="cb119-19"><a href="#cb119-19" aria-hidden="true" tabindex="-1"></a><span class="st"> return redis.call(&#39;incr&#39;, key)</span></span>
<span id="cb119-20"><a href="#cb119-20" aria-hidden="true" tabindex="-1"></a><span class="st"> else</span></span>
<span id="cb119-21"><a href="#cb119-21" aria-hidden="true" tabindex="-1"></a><span class="st"> return 0</span></span>
<span id="cb119-22"><a href="#cb119-22" aria-hidden="true" tabindex="-1"></a><span class="st"> end</span></span>
<span id="cb119-23"><a href="#cb119-23" aria-hidden="true" tabindex="-1"></a><span class="st"> &quot;&quot;&quot;</span><span class="op">;</span></span>
<span id="cb119-24"><a href="#cb119-24" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb119-25"><a href="#cb119-25" aria-hidden="true" tabindex="-1"></a> DefaultRedisScript<span class="op">&lt;</span><span class="bu">Long</span><span class="op">&gt;</span> redisScript <span class="op">=</span> <span class="kw">new</span> DefaultRedisScript<span class="op">&lt;&gt;(</span>script<span class="op">,</span> <span class="bu">Long</span><span class="op">.</span><span class="fu">class</span><span class="op">);</span></span>
<span id="cb119-26"><a href="#cb119-26" aria-hidden="true" tabindex="-1"></a> <span class="bu">Long</span> result <span class="op">=</span> redisTemplate<span class="op">.</span><span class="fu">execute</span><span class="op">(</span>redisScript<span class="op">,</span> </span>
<span id="cb119-27"><a href="#cb119-27" aria-hidden="true" tabindex="-1"></a> <span class="bu">Collections</span><span class="op">.</span><span class="fu">singletonList</span><span class="op">(</span>redisKey<span class="op">),</span> </span>
<span id="cb119-28"><a href="#cb119-28" aria-hidden="true" tabindex="-1"></a> <span class="bu">String</span><span class="op">.</span><span class="fu">valueOf</span><span class="op">(</span>window<span class="op">.</span><span class="fu">getSeconds</span><span class="op">()),</span></span>
<span id="cb119-29"><a href="#cb119-29" aria-hidden="true" tabindex="-1"></a> <span class="bu">String</span><span class="op">.</span><span class="fu">valueOf</span><span class="op">(</span>maxRequests<span class="op">));</span></span>
<span id="cb119-30"><a href="#cb119-30" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb119-31"><a href="#cb119-31" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> result <span class="op">!=</span> <span class="kw">null</span> <span class="op">&amp;&amp;</span> result <span class="op">&gt;</span> <span class="dv">0</span><span class="op">;</span></span>
<span id="cb119-32"><a href="#cb119-32" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb119-33"><a href="#cb119-33" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<h5 data-number="5.0.0.0.46" id="错误处理机制"><span
class="header-section-number">5.0.0.0.46</span> 4.4 错误处理机制</h5>
<h5 data-number="5.0.0.0.47" id="统一异常处理"><span
class="header-section-number">5.0.0.0.47</span> 4.4.1 统一异常处理</h5>
<div class="sourceCode" id="cb120"><pre
class="sourceCode java"><code class="sourceCode java"><span id="cb120-1"><a href="#cb120-1" aria-hidden="true" tabindex="-1"></a><span class="at">@RestControllerAdvice</span></span>
<span id="cb120-2"><a href="#cb120-2" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span> <span class="kw">class</span> GlobalExceptionHandler <span class="op">{</span></span>
<span id="cb120-3"><a href="#cb120-3" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb120-4"><a href="#cb120-4" aria-hidden="true" tabindex="-1"></a> <span class="at">@ExceptionHandler</span><span class="op">(</span>ServiceException<span class="op">.</span><span class="fu">class</span><span class="op">)</span></span>
<span id="cb120-5"><a href="#cb120-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">public</span> CommonResult<span class="op">&lt;?&gt;</span> <span class="fu">serviceExceptionHandler</span><span class="op">(</span>ServiceException ex<span class="op">)</span> <span class="op">{</span></span>
<span id="cb120-6"><a href="#cb120-6" aria-hidden="true" tabindex="-1"></a> log<span class="op">.</span><span class="fu">info</span><span class="op">(</span><span class="st">&quot;[serviceExceptionHandler]&quot;</span><span class="op">,</span> ex<span class="op">);</span></span>
<span id="cb120-7"><a href="#cb120-7" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> CommonResult<span class="op">.</span><span class="fu">error</span><span class="op">(</span>ex<span class="op">.</span><span class="fu">getCode</span><span class="op">(),</span> ex<span class="op">.</span><span class="fu">getMessage</span><span class="op">());</span></span>
<span id="cb120-8"><a href="#cb120-8" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb120-9"><a href="#cb120-9" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb120-10"><a href="#cb120-10" aria-hidden="true" tabindex="-1"></a> <span class="at">@ExceptionHandler</span><span class="op">(</span>ConstraintViolationException<span class="op">.</span><span class="fu">class</span><span class="op">)</span></span>
<span id="cb120-11"><a href="#cb120-11" aria-hidden="true" tabindex="-1"></a> <span class="kw">public</span> CommonResult<span class="op">&lt;?&gt;</span> <span class="fu">constraintViolationExceptionHandler</span><span class="op">(</span>ConstraintViolationException ex<span class="op">)</span> <span class="op">{</span></span>
<span id="cb120-12"><a href="#cb120-12" aria-hidden="true" tabindex="-1"></a> log<span class="op">.</span><span class="fu">info</span><span class="op">(</span><span class="st">&quot;[constraintViolationExceptionHandler]&quot;</span><span class="op">,</span> ex<span class="op">);</span></span>
<span id="cb120-13"><a href="#cb120-13" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> CommonResult<span class="op">.</span><span class="fu">error</span><span class="op">(</span>BAD_REQUEST<span class="op">.</span><span class="fu">getCode</span><span class="op">(),</span> <span class="st">&quot;请求参数不正确:&quot;</span> <span class="op">+</span> ex<span class="op">.</span><span class="fu">getMessage</span><span class="op">());</span></span>
<span id="cb120-14"><a href="#cb120-14" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb120-15"><a href="#cb120-15" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb120-16"><a href="#cb120-16" aria-hidden="true" tabindex="-1"></a> <span class="at">@ExceptionHandler</span><span class="op">(</span>MethodArgumentNotValidException<span class="op">.</span><span class="fu">class</span><span class="op">)</span></span>
<span id="cb120-17"><a href="#cb120-17" aria-hidden="true" tabindex="-1"></a> <span class="kw">public</span> CommonResult<span class="op">&lt;?&gt;</span> <span class="fu">methodArgumentNotValidExceptionHandler</span><span class="op">(</span>MethodArgumentNotValidException ex<span class="op">)</span> <span class="op">{</span></span>
<span id="cb120-18"><a href="#cb120-18" aria-hidden="true" tabindex="-1"></a> log<span class="op">.</span><span class="fu">info</span><span class="op">(</span><span class="st">&quot;[methodArgumentNotValidExceptionHandler]&quot;</span><span class="op">,</span> ex<span class="op">);</span></span>
<span id="cb120-19"><a href="#cb120-19" aria-hidden="true" tabindex="-1"></a> FieldError fieldError <span class="op">=</span> ex<span class="op">.</span><span class="fu">getBindingResult</span><span class="op">().</span><span class="fu">getFieldError</span><span class="op">();</span></span>
<span id="cb120-20"><a href="#cb120-20" aria-hidden="true" tabindex="-1"></a> <span class="cf">assert</span> fieldError <span class="op">!=</span> <span class="kw">null</span><span class="op">;</span></span>
<span id="cb120-21"><a href="#cb120-21" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> CommonResult<span class="op">.</span><span class="fu">error</span><span class="op">(</span>BAD_REQUEST<span class="op">.</span><span class="fu">getCode</span><span class="op">(),</span> <span class="st">&quot;请求参数不正确:&quot;</span> <span class="op">+</span> fieldError<span class="op">.</span><span class="fu">getDefaultMessage</span><span class="op">());</span></span>
<span id="cb120-22"><a href="#cb120-22" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb120-23"><a href="#cb120-23" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<h5 data-number="5.0.0.0.48" id="错误码定义"><span
class="header-section-number">5.0.0.0.48</span> 4.4.2 错误码定义</h5>
<div class="sourceCode" id="cb121"><pre
class="sourceCode java"><code class="sourceCode java"><span id="cb121-1"><a href="#cb121-1" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span> <span class="kw">interface</span> ErrorCodeConstants <span class="op">{</span></span>
<span id="cb121-2"><a href="#cb121-2" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb121-3"><a href="#cb121-3" aria-hidden="true" tabindex="-1"></a> <span class="co">// ========== 通用错误码 1-000-000-000 ==========</span></span>
<span id="cb121-4"><a href="#cb121-4" aria-hidden="true" tabindex="-1"></a> ErrorCode SUCCESS <span class="op">=</span> <span class="kw">new</span> <span class="fu">ErrorCode</span><span class="op">(</span><span class="dv">0</span><span class="op">,</span> <span class="st">&quot;成功&quot;</span><span class="op">);</span></span>
<span id="cb121-5"><a href="#cb121-5" aria-hidden="true" tabindex="-1"></a> ErrorCode BAD_REQUEST <span class="op">=</span> <span class="kw">new</span> <span class="fu">ErrorCode</span><span class="op">(</span><span class="dv">400</span><span class="op">,</span> <span class="st">&quot;请求参数不正确&quot;</span><span class="op">);</span></span>
<span id="cb121-6"><a href="#cb121-6" aria-hidden="true" tabindex="-1"></a> ErrorCode UNAUTHORIZED <span class="op">=</span> <span class="kw">new</span> <span class="fu">ErrorCode</span><span class="op">(</span><span class="dv">401</span><span class="op">,</span> <span class="st">&quot;账号未登录&quot;</span><span class="op">);</span></span>
<span id="cb121-7"><a href="#cb121-7" aria-hidden="true" tabindex="-1"></a> ErrorCode FORBIDDEN <span class="op">=</span> <span class="kw">new</span> <span class="fu">ErrorCode</span><span class="op">(</span><span class="dv">403</span><span class="op">,</span> <span class="st">&quot;没有该操作权限&quot;</span><span class="op">);</span></span>
<span id="cb121-8"><a href="#cb121-8" aria-hidden="true" tabindex="-1"></a> ErrorCode NOT_FOUND <span class="op">=</span> <span class="kw">new</span> <span class="fu">ErrorCode</span><span class="op">(</span><span class="dv">404</span><span class="op">,</span> <span class="st">&quot;请求未找到&quot;</span><span class="op">);</span></span>
<span id="cb121-9"><a href="#cb121-9" aria-hidden="true" tabindex="-1"></a> ErrorCode METHOD_NOT_ALLOWED <span class="op">=</span> <span class="kw">new</span> <span class="fu">ErrorCode</span><span class="op">(</span><span class="dv">405</span><span class="op">,</span> <span class="st">&quot;请求方法不正确&quot;</span><span class="op">);</span></span>
<span id="cb121-10"><a href="#cb121-10" aria-hidden="true" tabindex="-1"></a> ErrorCode INTERNAL_SERVER_ERROR <span class="op">=</span> <span class="kw">new</span> <span class="fu">ErrorCode</span><span class="op">(</span><span class="dv">500</span><span class="op">,</span> <span class="st">&quot;系统异常&quot;</span><span class="op">);</span></span>
<span id="cb121-11"><a href="#cb121-11" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb121-12"><a href="#cb121-12" aria-hidden="true" tabindex="-1"></a> <span class="co">// ========== 客户管理错误码 1-001-000-000 ==========</span></span>
<span id="cb121-13"><a href="#cb121-13" aria-hidden="true" tabindex="-1"></a> ErrorCode CUSTOMER_NOT_EXISTS <span class="op">=</span> <span class="kw">new</span> <span class="fu">ErrorCode</span><span class="op">(</span><span class="dv">1_001_000_001</span><span class="op">,</span> <span class="st">&quot;客户不存在&quot;</span><span class="op">);</span></span>
<span id="cb121-14"><a href="#cb121-14" aria-hidden="true" tabindex="-1"></a> ErrorCode CUSTOMER_CODE_DUPLICATE <span class="op">=</span> <span class="kw">new</span> <span class="fu">ErrorCode</span><span class="op">(</span><span class="dv">1_001_000_002</span><span class="op">,</span> <span class="st">&quot;客户编号已存在&quot;</span><span class="op">);</span></span>
<span id="cb121-15"><a href="#cb121-15" aria-hidden="true" tabindex="-1"></a> ErrorCode CUSTOMER_STATUS_INVALID <span class="op">=</span> <span class="kw">new</span> <span class="fu">ErrorCode</span><span class="op">(</span><span class="dv">1_001_000_003</span><span class="op">,</span> <span class="st">&quot;客户状态不正确&quot;</span><span class="op">);</span></span>
<span id="cb121-16"><a href="#cb121-16" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb121-17"><a href="#cb121-17" aria-hidden="true" tabindex="-1"></a> <span class="co">// ========== 水表管理错误码 1-002-000-000 ==========</span></span>
<span id="cb121-18"><a href="#cb121-18" aria-hidden="true" tabindex="-1"></a> ErrorCode METER_NOT_EXISTS <span class="op">=</span> <span class="kw">new</span> <span class="fu">ErrorCode</span><span class="op">(</span><span class="dv">1_002_000_001</span><span class="op">,</span> <span class="st">&quot;水表不存在&quot;</span><span class="op">);</span></span>
<span id="cb121-19"><a href="#cb121-19" aria-hidden="true" tabindex="-1"></a> ErrorCode METER_CODE_DUPLICATE <span class="op">=</span> <span class="kw">new</span> <span class="fu">ErrorCode</span><span class="op">(</span><span class="dv">1_002_000_002</span><span class="op">,</span> <span class="st">&quot;水表编号已存在&quot;</span><span class="op">);</span></span>
<span id="cb121-20"><a href="#cb121-20" aria-hidden="true" tabindex="-1"></a> ErrorCode METER_READING_INVALID <span class="op">=</span> <span class="kw">new</span> <span class="fu">ErrorCode</span><span class="op">(</span><span class="dv">1_002_000_003</span><span class="op">,</span> <span class="st">&quot;水表读数不正确&quot;</span><span class="op">);</span></span>
<span id="cb121-21"><a href="#cb121-21" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb121-22"><a href="#cb121-22" aria-hidden="true" tabindex="-1"></a> <span class="co">// ========== 账单管理错误码 1-003-000-000 ==========</span></span>
<span id="cb121-23"><a href="#cb121-23" aria-hidden="true" tabindex="-1"></a> ErrorCode BILL_NOT_EXISTS <span class="op">=</span> <span class="kw">new</span> <span class="fu">ErrorCode</span><span class="op">(</span><span class="dv">1_003_000_001</span><span class="op">,</span> <span class="st">&quot;账单不存在&quot;</span><span class="op">);</span></span>
<span id="cb121-24"><a href="#cb121-24" aria-hidden="true" tabindex="-1"></a> ErrorCode BILL_ALREADY_PAID <span class="op">=</span> <span class="kw">new</span> <span class="fu">ErrorCode</span><span class="op">(</span><span class="dv">1_003_000_002</span><span class="op">,</span> <span class="st">&quot;账单已缴费&quot;</span><span class="op">);</span></span>
<span id="cb121-25"><a href="#cb121-25" aria-hidden="true" tabindex="-1"></a> ErrorCode BILL_AMOUNT_INVALID <span class="op">=</span> <span class="kw">new</span> <span class="fu">ErrorCode</span><span class="op">(</span><span class="dv">1_003_000_003</span><span class="op">,</span> <span class="st">&quot;账单金额不正确&quot;</span><span class="op">);</span></span>
<span id="cb121-26"><a href="#cb121-26" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb121-27"><a href="#cb121-27" aria-hidden="true" tabindex="-1"></a> <span class="co">// ========== 缴费管理错误码 1-004-000-000 ==========</span></span>
<span id="cb121-28"><a href="#cb121-28" aria-hidden="true" tabindex="-1"></a> ErrorCode PAYMENT_FAILED <span class="op">=</span> <span class="kw">new</span> <span class="fu">ErrorCode</span><span class="op">(</span><span class="dv">1_004_000_001</span><span class="op">,</span> <span class="st">&quot;缴费失败&quot;</span><span class="op">);</span></span>
<span id="cb121-29"><a href="#cb121-29" aria-hidden="true" tabindex="-1"></a> ErrorCode PAYMENT_AMOUNT_INSUFFICIENT <span class="op">=</span> <span class="kw">new</span> <span class="fu">ErrorCode</span><span class="op">(</span><span class="dv">1_004_000_002</span><span class="op">,</span> <span class="st">&quot;缴费金额不足&quot;</span><span class="op">);</span></span>
<span id="cb121-30"><a href="#cb121-30" aria-hidden="true" tabindex="-1"></a> ErrorCode PAYMENT_CHANNEL_UNAVAILABLE <span class="op">=</span> <span class="kw">new</span> <span class="fu">ErrorCode</span><span class="op">(</span><span class="dv">1_004_000_003</span><span class="op">,</span> <span class="st">&quot;缴费渠道不可用&quot;</span><span class="op">);</span></span>
<span id="cb121-31"><a href="#cb121-31" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<h5 data-number="5.0.0.0.49" id="接口调用示例"><span
class="header-section-number">5.0.0.0.49</span> 4.4.3 接口调用示例</h5>
<p><strong>成功响应示例</strong></p>
<div class="sourceCode" id="cb122"><pre
class="sourceCode json"><code class="sourceCode json"><span id="cb122-1"><a href="#cb122-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb122-2"><a href="#cb122-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;code&quot;</span><span class="fu">:</span> <span class="dv">0</span><span class="fu">,</span></span>
<span id="cb122-3"><a href="#cb122-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;msg&quot;</span><span class="fu">:</span> <span class="st">&quot;操作成功&quot;</span><span class="fu">,</span></span>
<span id="cb122-4"><a href="#cb122-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;data&quot;</span><span class="fu">:</span> <span class="fu">{</span></span>
<span id="cb122-5"><a href="#cb122-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;id&quot;</span><span class="fu">:</span> <span class="dv">1</span><span class="fu">,</span></span>
<span id="cb122-6"><a href="#cb122-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;customerName&quot;</span><span class="fu">:</span> <span class="st">&quot;张三&quot;</span></span>
<span id="cb122-7"><a href="#cb122-7" aria-hidden="true" tabindex="-1"></a> <span class="fu">}</span></span>
<span id="cb122-8"><a href="#cb122-8" aria-hidden="true" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
<p><strong>失败响应示例</strong></p>
<div class="sourceCode" id="cb123"><pre
class="sourceCode json"><code class="sourceCode json"><span id="cb123-1"><a href="#cb123-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb123-2"><a href="#cb123-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;code&quot;</span><span class="fu">:</span> <span class="dv">1001000001</span><span class="fu">,</span></span>
<span id="cb123-3"><a href="#cb123-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;msg&quot;</span><span class="fu">:</span> <span class="st">&quot;客户不存在&quot;</span><span class="fu">,</span></span>
<span id="cb123-4"><a href="#cb123-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;data&quot;</span><span class="fu">:</span> <span class="kw">null</span></span>
<span id="cb123-5"><a href="#cb123-5" aria-hidden="true" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
<h5 data-number="5.0.0.0.50" id="前端接口调用示例"><span
class="header-section-number">5.0.0.0.50</span> 4.5
前端接口调用示例</h5>
<h5 data-number="5.0.0.0.51" id="vue3-typescript接口封装"><span
class="header-section-number">5.0.0.0.51</span> 4.5.1 Vue3 +
TypeScript接口封装</h5>
<div class="sourceCode" id="cb124"><pre
class="sourceCode typescript"><code class="sourceCode typescript"><span id="cb124-1"><a href="#cb124-1" aria-hidden="true" tabindex="-1"></a><span class="co">// api/water/customer.ts</span></span>
<span id="cb124-2"><a href="#cb124-2" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> { request } <span class="im">from</span> <span class="st">&#39;@/utils/request&#39;</span></span>
<span id="cb124-3"><a href="#cb124-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb124-4"><a href="#cb124-4" aria-hidden="true" tabindex="-1"></a><span class="im">export</span> <span class="kw">interface</span> CustomerVO {</span>
<span id="cb124-5"><a href="#cb124-5" aria-hidden="true" tabindex="-1"></a> id<span class="op">:</span> <span class="dt">number</span></span>
<span id="cb124-6"><a href="#cb124-6" aria-hidden="true" tabindex="-1"></a> customerCode<span class="op">:</span> <span class="dt">string</span></span>
<span id="cb124-7"><a href="#cb124-7" aria-hidden="true" tabindex="-1"></a> customerName<span class="op">:</span> <span class="dt">string</span></span>
<span id="cb124-8"><a href="#cb124-8" aria-hidden="true" tabindex="-1"></a> customerType<span class="op">:</span> <span class="dt">string</span></span>
<span id="cb124-9"><a href="#cb124-9" aria-hidden="true" tabindex="-1"></a> phone<span class="op">:</span> <span class="dt">string</span></span>
<span id="cb124-10"><a href="#cb124-10" aria-hidden="true" tabindex="-1"></a> address<span class="op">:</span> <span class="dt">string</span></span>
<span id="cb124-11"><a href="#cb124-11" aria-hidden="true" tabindex="-1"></a> status<span class="op">:</span> <span class="dt">number</span></span>
<span id="cb124-12"><a href="#cb124-12" aria-hidden="true" tabindex="-1"></a> createTime<span class="op">:</span> <span class="dt">string</span></span>
<span id="cb124-13"><a href="#cb124-13" aria-hidden="true" tabindex="-1"></a>}</span>
<span id="cb124-14"><a href="#cb124-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb124-15"><a href="#cb124-15" aria-hidden="true" tabindex="-1"></a><span class="im">export</span> <span class="kw">interface</span> CustomerPageReqVO <span class="kw">extends</span> PageParam {</span>
<span id="cb124-16"><a href="#cb124-16" aria-hidden="true" tabindex="-1"></a> customerName<span class="op">?:</span> <span class="dt">string</span></span>
<span id="cb124-17"><a href="#cb124-17" aria-hidden="true" tabindex="-1"></a> customerCode<span class="op">?:</span> <span class="dt">string</span></span>
<span id="cb124-18"><a href="#cb124-18" aria-hidden="true" tabindex="-1"></a> customerType<span class="op">?:</span> <span class="dt">string</span></span>
<span id="cb124-19"><a href="#cb124-19" aria-hidden="true" tabindex="-1"></a> phone<span class="op">?:</span> <span class="dt">string</span></span>
<span id="cb124-20"><a href="#cb124-20" aria-hidden="true" tabindex="-1"></a>}</span>
<span id="cb124-21"><a href="#cb124-21" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb124-22"><a href="#cb124-22" aria-hidden="true" tabindex="-1"></a><span class="im">export</span> <span class="kw">const</span> CustomerApi <span class="op">=</span> {</span>
<span id="cb124-23"><a href="#cb124-23" aria-hidden="true" tabindex="-1"></a> <span class="co">// 获取客户分页</span></span>
<span id="cb124-24"><a href="#cb124-24" aria-hidden="true" tabindex="-1"></a> getCustomerPage<span class="op">:</span> (params<span class="op">:</span> CustomerPageReqVO) <span class="kw">=&gt;</span> {</span>
<span id="cb124-25"><a href="#cb124-25" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> request<span class="op">.</span><span class="fu">get</span><span class="op">&lt;</span>PageResult<span class="op">&lt;</span>CustomerVO<span class="op">&gt;&gt;</span>({ url<span class="op">:</span> <span class="st">&#39;/water/customer/page&#39;</span><span class="op">,</span> params })</span>
<span id="cb124-26"><a href="#cb124-26" aria-hidden="true" tabindex="-1"></a> }<span class="op">,</span></span>
<span id="cb124-27"><a href="#cb124-27" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb124-28"><a href="#cb124-28" aria-hidden="true" tabindex="-1"></a> <span class="co">// 获取客户详情</span></span>
<span id="cb124-29"><a href="#cb124-29" aria-hidden="true" tabindex="-1"></a> getCustomer<span class="op">:</span> (id<span class="op">:</span> <span class="dt">number</span>) <span class="kw">=&gt;</span> {</span>
<span id="cb124-30"><a href="#cb124-30" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> request<span class="op">.</span><span class="fu">get</span><span class="op">&lt;</span>CustomerVO<span class="op">&gt;</span>({ url<span class="op">:</span> <span class="vs">`/water/customer/</span><span class="sc">${</span>id<span class="sc">}</span><span class="vs">`</span> })</span>
<span id="cb124-31"><a href="#cb124-31" aria-hidden="true" tabindex="-1"></a> }<span class="op">,</span></span>
<span id="cb124-32"><a href="#cb124-32" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb124-33"><a href="#cb124-33" aria-hidden="true" tabindex="-1"></a> <span class="co">// 创建客户</span></span>
<span id="cb124-34"><a href="#cb124-34" aria-hidden="true" tabindex="-1"></a> createCustomer<span class="op">:</span> (data<span class="op">:</span> CustomerSaveReqVO) <span class="kw">=&gt;</span> {</span>
<span id="cb124-35"><a href="#cb124-35" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> request<span class="op">.</span><span class="fu">post</span><span class="op">&lt;</span><span class="dt">number</span><span class="op">&gt;</span>({ url<span class="op">:</span> <span class="st">&#39;/water/customer/create&#39;</span><span class="op">,</span> data })</span>
<span id="cb124-36"><a href="#cb124-36" aria-hidden="true" tabindex="-1"></a> }<span class="op">,</span></span>
<span id="cb124-37"><a href="#cb124-37" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb124-38"><a href="#cb124-38" aria-hidden="true" tabindex="-1"></a> <span class="co">// 更新客户</span></span>
<span id="cb124-39"><a href="#cb124-39" aria-hidden="true" tabindex="-1"></a> updateCustomer<span class="op">:</span> (data<span class="op">:</span> CustomerSaveReqVO) <span class="kw">=&gt;</span> {</span>
<span id="cb124-40"><a href="#cb124-40" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> request<span class="op">.</span><span class="fu">put</span><span class="op">&lt;</span><span class="dt">void</span><span class="op">&gt;</span>({ url<span class="op">:</span> <span class="st">&#39;/water/customer/update&#39;</span><span class="op">,</span> data })</span>
<span id="cb124-41"><a href="#cb124-41" aria-hidden="true" tabindex="-1"></a> }<span class="op">,</span></span>
<span id="cb124-42"><a href="#cb124-42" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb124-43"><a href="#cb124-43" aria-hidden="true" tabindex="-1"></a> <span class="co">// 删除客户</span></span>
<span id="cb124-44"><a href="#cb124-44" aria-hidden="true" tabindex="-1"></a> deleteCustomer<span class="op">:</span> (id<span class="op">:</span> <span class="dt">number</span>) <span class="kw">=&gt;</span> {</span>
<span id="cb124-45"><a href="#cb124-45" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> request<span class="op">.</span><span class="fu">delete</span><span class="op">&lt;</span><span class="dt">void</span><span class="op">&gt;</span>({ url<span class="op">:</span> <span class="vs">`/water/customer/delete?id=</span><span class="sc">${</span>id<span class="sc">}</span><span class="vs">`</span> })</span>
<span id="cb124-46"><a href="#cb124-46" aria-hidden="true" tabindex="-1"></a> }</span>
<span id="cb124-47"><a href="#cb124-47" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
<h5 data-number="5.0.0.0.52" id="vue组件使用示例"><span
class="header-section-number">5.0.0.0.52</span> 4.5.2
Vue组件使用示例</h5>
<pre class="vue"><code>&lt;script setup lang=&quot;ts&quot;&gt;
import { ref, onMounted } from &#39;vue&#39;
import { CustomerApi, CustomerVO } from &#39;@/api/water/customer&#39;
import { formatDate } from &#39;@/utils/formatTime&#39;
const customerList = ref&lt;CustomerVO[]&gt;([])
const loading = ref(true)
const total = ref(0)
const queryParams = ref({
pageNo: 1,
pageSize: 10,
customerName: &#39;&#39;,
customerCode: &#39;&#39;
})
const getCustomerList = async () =&gt; {
loading.value = true
try {
const data = await CustomerApi.getCustomerPage(queryParams.value)
customerList.value = data.list
total.value = data.total
} catch (error) {
console.error(&#39;获取客户列表失败:&#39;, error)
} finally {
loading.value = false
}
}
const handleQuery = () =&gt; {
queryParams.value.pageNo = 1
getCustomerList()
}
const handleReset = () =&gt; {
queryParams.value = {
pageNo: 1,
pageSize: 10,
customerName: &#39;&#39;,
customerCode: &#39;&#39;
}
getCustomerList()
}
onMounted(() =&gt; {
getCustomerList()
})
&lt;/script&gt;
&lt;template&gt;
&lt;div class=&quot;app-container&quot;&gt;
&lt;!-- 查询表单 --&gt;
&lt;el-form :model=&quot;queryParams&quot; ref=&quot;queryFormRef&quot; inline&gt;
&lt;el-form-item label=&quot;客户名称&quot; prop=&quot;customerName&quot;&gt;
&lt;el-input v-model=&quot;queryParams.customerName&quot; placeholder=&quot;请输入客户名称&quot; /&gt;
&lt;/el-form-item&gt;
&lt;el-form-item label=&quot;客户编号&quot; prop=&quot;customerCode&quot;&gt;
&lt;el-input v-model=&quot;queryParams.customerCode&quot; placeholder=&quot;请输入客户编号&quot; /&gt;
&lt;/el-form-item&gt;
&lt;el-form-item&gt;
&lt;el-button type=&quot;primary&quot; @click=&quot;handleQuery&quot;&gt;搜索&lt;/el-button&gt;
&lt;el-button @click=&quot;handleReset&quot;&gt;重置&lt;/el-button&gt;
&lt;/el-form-item&gt;
&lt;/el-form&gt;
&lt;!-- 数据表格 --&gt;
&lt;el-table v-loading=&quot;loading&quot; :data=&quot;customerList&quot;&gt;
&lt;el-table-column label=&quot;客户编号&quot; prop=&quot;customerCode&quot; /&gt;
&lt;el-table-column label=&quot;客户名称&quot; prop=&quot;customerName&quot; /&gt;
&lt;el-table-column label=&quot;联系电话&quot; prop=&quot;phone&quot; /&gt;
&lt;el-table-column label=&quot;创建时间&quot; prop=&quot;createTime&quot; :formatter=&quot;formatDate&quot; /&gt;
&lt;/el-table&gt;
&lt;!-- 分页组件 --&gt;
&lt;Pagination
:total=&quot;total&quot;
v-model:page=&quot;queryParams.pageNo&quot;
v-model:limit=&quot;queryParams.pageSize&quot;
@pagination=&quot;getCustomerList&quot;
/&gt;
&lt;/div&gt;
&lt;/template&gt;</code></pre>
<hr />
<p>ewpage</p>
<h1 data-number="6" id="第5部分"><span
class="header-section-number">6</span> 第5部分</h1>
<h5 data-number="6.0.0.0.1" id="福建水务营收系统部署设计文档"><span
class="header-section-number">6.0.0.0.1</span>
福建水务营收系统部署设计文档</h5>
<h5 data-number="6.0.0.0.2" id="文档信息-5"><span
class="header-section-number">6.0.0.0.2</span> 文档信息</h5>
<table>
<thead>
<tr>
<th>项目信息</th>
<th>详情</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>项目名称</strong></td>
<td>福建水务营收系统</td>
</tr>
<tr>
<td><strong>文档类型</strong></td>
<td>概要设计文档</td>
</tr>
<tr>
<td><strong>技术框架</strong></td>
<td>RuoYi-Vue-Pro + yudao-ui-admin-vue3</td>
</tr>
<tr>
<td><strong>文档版本</strong></td>
<td>v1.0</td>
</tr>
<tr>
<td><strong>编写日期</strong></td>
<td>2024-12-19</td>
</tr>
<tr>
<td><strong>文档状态</strong></td>
<td>✅ 已完成</td>
</tr>
</tbody>
</table>
<h5 data-number="6.0.0.0.3" id="目录-4"><span
class="header-section-number">6.0.0.0.3</span> 目录</h5>
<ul>
<li><a href="#1-部署概述">1. 部署概述</a></li>
<li><a href="#2-部署架构">2. 部署架构</a></li>
<li><a href="#3-服务器配置">3. 服务器配置</a></li>
<li><a href="#4-网络架构">4. 网络架构</a></li>
<li><a href="#5-安全设计">5. 安全设计</a></li>
<li><a href="#6-部署流程">6. 部署流程</a></li>
<li><a href="#7-监控与运维">7. 监控与运维</a></li>
</ul>
<h5 data-number="6.0.0.0.4" id="部署概述"><span
class="header-section-number">6.0.0.0.4</span> 1. 部署概述</h5>
<p>福建水务业务系统采用集中化部署模式,基于集团私有云环境进行部署,构建高可用、可扩展的系统架构,满足集团及下属各水务公司的业务需求。</p>
<h5 data-number="6.0.0.0.5" id="部署目标"><span
class="header-section-number">6.0.0.0.5</span> 1.1 部署目标</h5>
<ul>
<li>实现系统的集中部署和统一管理</li>
<li>确保系统高可用性和可靠性</li>
<li>支持业务动态扩展和弹性伸缩</li>
<li>保障数据安全和业务连续性</li>
<li>降低运维成本,提高管理效率</li>
</ul>
<h5 data-number="6.0.0.0.6" id="部署原则"><span
class="header-section-number">6.0.0.0.6</span> 1.2 部署原则</h5>
<ul>
<li><strong>集中部署</strong>:核心业务系统集中部署在集团数据中心,统一管理和维护</li>
<li><strong>多级架构</strong>:采用多级架构设计,实现前端负载分担和后端高可用</li>
<li><strong>灾备设计</strong>:重要系统和数据实现灾备,确保业务连续性</li>
<li><strong>安全防护</strong>:多层次安全防护体系,确保系统和数据安全</li>
<li><strong>弹性扩展</strong>:支持系统横向和纵向扩展,适应业务增长需求</li>
</ul>
<h5 data-number="6.0.0.0.7" id="部署架构"><span
class="header-section-number">6.0.0.0.7</span> 2. 部署架构</h5>
<h5 data-number="6.0.0.0.8" id="总体部署架构"><span
class="header-section-number">6.0.0.0.8</span> 2.1 总体部署架构</h5>
<p>福建水务业务系统采用三层架构部署:</p>
<ul>
<li><strong>接入层</strong>:负责外部访问请求的接入和负载均衡</li>
<li><strong>应用层</strong>:部署业务应用服务,处理业务逻辑</li>
<li><strong>数据层</strong>:部署数据库服务,存储业务数据</li>
</ul>
<p>系统部署采用集群模式,各层均部署多个节点,实现高可用和负载均衡。</p>
<h5 data-number="6.0.0.0.9" id="生产环境部署架构"><span
class="header-section-number">6.0.0.0.9</span> 2.2 生产环境部署架构</h5>
<p>生产环境采用双机房部署方案,包括主生产环境和灾备环境:</p>
<h5 data-number="6.0.0.0.10" id="主生产环境"><span
class="header-section-number">6.0.0.0.10</span> 2.2.1 主生产环境</h5>
<p>主生产环境部署在集团主数据中心,包括:</p>
<ul>
<li>负载均衡集群2台负载均衡服务器</li>
<li>Web服务器集群4台Web服务器</li>
<li>应用服务器集群6台应用服务器</li>
<li>数据库服务器集群2台主备数据库服务器</li>
<li>文件服务器2台主备文件服务器</li>
<li>缓存服务器集群2台缓存服务器</li>
</ul>
<h5 data-number="6.0.0.0.11" id="灾备环境"><span
class="header-section-number">6.0.0.0.11</span> 2.2.2 灾备环境</h5>
<p>灾备环境部署在集团备用数据中心,包括:</p>
<ul>
<li>负载均衡服务器1台</li>
<li>Web服务器2台</li>
<li>应用服务器3台</li>
<li>数据库服务器1台</li>
<li>文件服务器1台</li>
<li>缓存服务器1台</li>
</ul>
<h5 data-number="6.0.0.0.12" id="测试环境部署架构"><span
class="header-section-number">6.0.0.0.12</span> 2.3
测试环境部署架构</h5>
<p>测试环境部署在集团测试中心,用于系统测试和验证,包括:</p>
<ul>
<li>Web服务器1台</li>
<li>应用服务器2台</li>
<li>数据库服务器1台</li>
<li>文件服务器1台</li>
</ul>
<h5 data-number="6.0.0.0.13" id="开发环境部署架构"><span
class="header-section-number">6.0.0.0.13</span> 2.4
开发环境部署架构</h5>
<p>开发环境部署在开发中心,用于系统开发和集成测试,包括:</p>
<ul>
<li>应用服务器1台</li>
<li>数据库服务器1台</li>
</ul>
<h5 data-number="6.0.0.0.14" id="服务器配置"><span
class="header-section-number">6.0.0.0.14</span> 3. 服务器配置</h5>
<h5 data-number="6.0.0.0.15" id="硬件配置要求"><span
class="header-section-number">6.0.0.0.15</span> 3.1 硬件配置要求</h5>
<h5 data-number="6.0.0.0.16" id="负载均衡服务器"><span
class="header-section-number">6.0.0.0.16</span> 3.1.1
负载均衡服务器</h5>
<ul>
<li>CPU8核</li>
<li>内存16GB</li>
<li>存储100GB SSD</li>
<li>网卡:双千兆网卡</li>
</ul>
<h5 data-number="6.0.0.0.17" id="web服务器"><span
class="header-section-number">6.0.0.0.17</span> 3.1.2 Web服务器</h5>
<ul>
<li>CPU8核</li>
<li>内存16GB</li>
<li>存储200GB SSD</li>
<li>网卡:双千兆网卡</li>
</ul>
<h5 data-number="6.0.0.0.18" id="应用服务器"><span
class="header-section-number">6.0.0.0.18</span> 3.1.3 应用服务器</h5>
<ul>
<li>CPU16核</li>
<li>内存32GB</li>
<li>存储300GB SSD</li>
<li>网卡:双千兆网卡</li>
</ul>
<h5 data-number="6.0.0.0.19" id="数据库服务器"><span
class="header-section-number">6.0.0.0.19</span> 3.1.4 数据库服务器</h5>
<ul>
<li>CPU24核</li>
<li>内存64GB</li>
<li>存储2TB SSD (RAID 10)</li>
<li>网卡:双万兆网卡</li>
</ul>
<h5 data-number="6.0.0.0.20" id="文件服务器"><span
class="header-section-number">6.0.0.0.20</span> 3.1.5 文件服务器</h5>
<ul>
<li>CPU8核</li>
<li>内存16GB</li>
<li>存储4TB (RAID 5)</li>
<li>网卡:双千兆网卡</li>
</ul>
<h5 data-number="6.0.0.0.21" id="缓存服务器"><span
class="header-section-number">6.0.0.0.21</span> 3.1.6 缓存服务器</h5>
<ul>
<li>CPU8核</li>
<li>内存32GB</li>
<li>存储100GB SSD</li>
<li>网卡:双千兆网卡</li>
</ul>
<h5 data-number="6.0.0.0.22" id="软件配置要求"><span
class="header-section-number">6.0.0.0.22</span> 3.2 软件配置要求</h5>
<h5 data-number="6.0.0.0.23" id="操作系统"><span
class="header-section-number">6.0.0.0.23</span> 3.2.1 操作系统</h5>
<ul>
<li>服务端CentOS 7+、Ubuntu 18.04+或其他国产Linux发行版</li>
<li>客户端支持Windows、macOS、Linux等现代操作系统</li>
</ul>
<h5 data-number="6.0.0.0.24" id="数据库"><span
class="header-section-number">6.0.0.0.24</span> 3.2.2 数据库</h5>
<ul>
<li>OpenGauss 5.0+(推荐,国产自主可控)</li>
<li>兼容PostgreSQL 13+、MySQL 8.0+</li>
</ul>
<h5 data-number="6.0.0.0.25" id="应用服务器-1"><span
class="header-section-number">6.0.0.0.25</span> 3.2.3 应用服务器</h5>
<ul>
<li>JDK 17或JDK 21兼容支持JDK 8</li>
<li>Apache Tomcat 9.0+或内嵌Tomcat</li>
<li>Nginx 1.18+(作为前端静态资源服务器和反向代理)</li>
</ul>
<h5 data-number="6.0.0.0.26" id="缓存服务器-1"><span
class="header-section-number">6.0.0.0.26</span> 3.2.4 缓存服务器</h5>
<ul>
<li>Redis 5.0+或Redis集群</li>
</ul>
<h5 data-number="6.0.0.0.27" id="中间件"><span
class="header-section-number">6.0.0.0.27</span> 3.2.5 中间件</h5>
<ul>
<li>消息队列基于Redis的轻量级队列可选</li>
<li>分布式任务调度Quartz集群</li>
<li>工作流引擎Flowable 6.x</li>
<li>文件存储支持本地存储、MinIO、阿里云OSS等</li>
</ul>
<h5 data-number="6.0.0.0.28" id="前端环境"><span
class="header-section-number">6.0.0.0.28</span> 3.2.6 前端环境</h5>
<ul>
<li>Node.js 16+</li>
<li>NPM 8+或Yarn 1.22+</li>
<li>Vue 3.x</li>
<li>Element Plus</li>
</ul>
<h5 data-number="6.0.0.0.29" id="网络架构"><span
class="header-section-number">6.0.0.0.29</span> 4. 网络架构</h5>
<h5 data-number="6.0.0.0.30" id="网络拓扑"><span
class="header-section-number">6.0.0.0.30</span> 4.1 网络拓扑</h5>
<p>福建水务业务系统网络架构采用三层网络结构:</p>
<ul>
<li><strong>接入层</strong>:对外提供服务接入,部署防火墙、负载均衡等设备</li>
<li><strong>核心层</strong>:实现业务系统间的互联互通,部署核心交换机</li>
<li><strong>服务层</strong>:部署各类服务器,实现业务处理和数据存储</li>
</ul>
<h5 data-number="6.0.0.0.31" id="网络安全区域划分"><span
class="header-section-number">6.0.0.0.31</span> 4.2
网络安全区域划分</h5>
<p>网络安全区域划分为以下几个区域:</p>
<ul>
<li><strong>DMZ区</strong>部署面向外部的Web服务器与内网隔离</li>
<li><strong>应用区</strong>:部署业务应用服务器</li>
<li><strong>数据区</strong>:部署数据库服务器和文件服务器</li>
<li><strong>管理区</strong>:部署运维管理服务器</li>
</ul>
<h5 data-number="6.0.0.0.32" id="网络带宽配置"><span
class="header-section-number">6.0.0.0.32</span> 4.3 网络带宽配置</h5>
<ul>
<li>外网接入带宽100Mbps根据实际需求可升级</li>
<li>内网骨干带宽1000Mbps</li>
<li>服务器接入带宽1000Mbps</li>
<li>数据中心间专线带宽100Mbps</li>
</ul>
<h5 data-number="6.0.0.0.33" id="安全设计"><span
class="header-section-number">6.0.0.0.33</span> 5. 安全设计</h5>
<h5 data-number="6.0.0.0.34" id="网络安全-1"><span
class="header-section-number">6.0.0.0.34</span> 5.1 网络安全</h5>
<h5 data-number="6.0.0.0.35" id="边界安全"><span
class="header-section-number">6.0.0.0.35</span> 5.1.1 边界安全</h5>
<ul>
<li>部署高性能防火墙,实现网络隔离</li>
<li>部署入侵检测系统(IDS)和入侵防御系统(IPS)</li>
<li>部署Web应用防火墙(WAF)防御Web应用攻击</li>
<li>采用VPN技术保障远程访问安全</li>
</ul>
<h5 data-number="6.0.0.0.36" id="内网安全"><span
class="header-section-number">6.0.0.0.36</span> 5.1.2 内网安全</h5>
<ul>
<li>网络安全区域划分,实现不同级别网络隔离</li>
<li>内网访问控制,限制不必要的网络访问</li>
<li>内网流量监控,及时发现异常流量</li>
</ul>
<h5 data-number="6.0.0.0.37" id="系统安全"><span
class="header-section-number">6.0.0.0.37</span> 5.2 系统安全</h5>
<h5 data-number="6.0.0.0.38" id="主机安全"><span
class="header-section-number">6.0.0.0.38</span> 5.2.1 主机安全</h5>
<ul>
<li>操作系统安全加固,关闭不必要的服务和端口</li>
<li>系统补丁及时更新,修复安全漏洞</li>
<li>部署主机防病毒软件,防御恶意代码</li>
<li>主机审计和日志管理,记录重要操作</li>
</ul>
<h5 data-number="6.0.0.0.39" id="应用安全-1"><span
class="header-section-number">6.0.0.0.39</span> 5.2.2 应用安全</h5>
<ul>
<li>应用访问控制,基于角色的权限管理</li>
<li>应用操作审计,记录重要业务操作</li>
<li>数据传输加密,保护敏感信息</li>
<li>输入验证防止SQL注入、XSS等攻击</li>
</ul>
<h5 data-number="6.0.0.0.40" id="数据安全"><span
class="header-section-number">6.0.0.0.40</span> 5.3 数据安全</h5>
<h5 data-number="6.0.0.0.41" id="数据存储安全"><span
class="header-section-number">6.0.0.0.41</span> 5.3.1 数据存储安全</h5>
<ul>
<li>数据库访问控制,限制数据库访问权限</li>
<li>敏感数据加密存储,防止数据泄露</li>
<li>数据库审计,记录重要数据操作</li>
</ul>
<h5 data-number="6.0.0.0.42" id="数据备份与恢复"><span
class="header-section-number">6.0.0.0.42</span> 5.3.2
数据备份与恢复</h5>
<ul>
<li>定期数据备份,每日增量备份,每周全量备份</li>
<li>备份数据异地存储,防止灾难性事件导致数据丢失</li>
<li>定期演练数据恢复,确保备份数据可用</li>
</ul>
<h5 data-number="6.0.0.0.43" id="部署流程"><span
class="header-section-number">6.0.0.0.43</span> 6. 部署流程</h5>
<h5 data-number="6.0.0.0.44" id="部署准备"><span
class="header-section-number">6.0.0.0.44</span> 6.1 部署准备</h5>
<ul>
<li>服务器硬件准备和上架</li>
<li>网络环境配置和测试</li>
<li>操作系统安装和配置</li>
<li>基础软件安装和配置</li>
</ul>
<h5 data-number="6.0.0.0.45" id="应用部署"><span
class="header-section-number">6.0.0.0.45</span> 6.2 应用部署</h5>
<h5 data-number="6.0.0.0.46" id="数据库部署"><span
class="header-section-number">6.0.0.0.46</span> 6.2.1 数据库部署</h5>
<ul>
<li>数据库服务器配置</li>
<li>数据库实例创建和配置</li>
<li>数据库高可用配置</li>
<li>数据备份策略配置</li>
</ul>
<h5 data-number="6.0.0.0.47" id="应用服务部署"><span
class="header-section-number">6.0.0.0.47</span> 6.2.2 应用服务部署</h5>
<ul>
<li>应用服务器配置</li>
<li>应用程序部署</li>
<li>应用服务参数配置</li>
<li>应用服务高可用配置</li>
</ul>
<h5 data-number="6.0.0.0.48" id="web服务部署"><span
class="header-section-number">6.0.0.0.48</span> 6.2.3 Web服务部署</h5>
<ul>
<li>Web服务器配置</li>
<li>Web应用部署</li>
<li>Web服务参数配置</li>
<li>负载均衡配置</li>
</ul>
<h5 data-number="6.0.0.0.49" id="系统联调"><span
class="header-section-number">6.0.0.0.49</span> 6.3 系统联调</h5>
<ul>
<li>各组件功能测试</li>
<li>系统整体联调测试</li>
<li>性能和压力测试</li>
<li>高可用性测试</li>
</ul>
<h5 data-number="6.0.0.0.50" id="系统上线"><span
class="header-section-number">6.0.0.0.50</span> 6.4 系统上线</h5>
<ul>
<li>制定上线计划</li>
<li>系统割接准备</li>
<li>系统割接实施</li>
<li>系统运行监控</li>
</ul>
<h5 data-number="6.0.0.0.51" id="监控与运维"><span
class="header-section-number">6.0.0.0.51</span> 7. 监控与运维</h5>
<h5 data-number="6.0.0.0.52" id="监控系统"><span
class="header-section-number">6.0.0.0.52</span> 7.1 监控系统</h5>
<h5 data-number="6.0.0.0.53" id="基础设施监控"><span
class="header-section-number">6.0.0.0.53</span> 7.1.1 基础设施监控</h5>
<ul>
<li>服务器硬件监控CPU、内存、磁盘、网络等</li>
<li>网络设备监控:带宽使用率、连接状态等</li>
<li>操作系统监控:系统负载、进程状态等</li>
</ul>
<h5 data-number="6.0.0.0.54" id="应用监控"><span
class="header-section-number">6.0.0.0.54</span> 7.1.2 应用监控</h5>
<ul>
<li>应用服务状态监控</li>
<li>应用性能监控:响应时间、并发数等</li>
<li>业务监控:重要业务指标监控</li>
</ul>
<h5 data-number="6.0.0.0.55" id="数据库监控"><span
class="header-section-number">6.0.0.0.55</span> 7.1.3 数据库监控</h5>
<ul>
<li>数据库服务状态监控</li>
<li>数据库性能监控CPU使用率、内存使用率、IO性能等</li>
<li>SQL执行监控慢查询、锁等待等</li>
</ul>
<h5 data-number="6.0.0.0.56" id="运维管理"><span
class="header-section-number">6.0.0.0.56</span> 7.2 运维管理</h5>
<h5 data-number="6.0.0.0.57" id="日常运维"><span
class="header-section-number">6.0.0.0.57</span> 7.2.1 日常运维</h5>
<ul>
<li>系统日常巡检</li>
<li>系统性能优化</li>
<li>系统容量管理</li>
<li>系统备份管理</li>
</ul>
<h5 data-number="6.0.0.0.58" id="变更管理"><span
class="header-section-number">6.0.0.0.58</span> 7.2.2 变更管理</h5>
<ul>
<li>变更申请和审批</li>
<li>变更实施和验证</li>
<li>变更回退预案</li>
<li>变更记录管理</li>
</ul>
<h5 data-number="6.0.0.0.59" id="故障管理"><span
class="header-section-number">6.0.0.0.59</span> 7.2.3 故障管理</h5>
<ul>
<li>故障监控和告警</li>
<li>故障处理流程</li>
<li>故障升级机制</li>
<li>故障复盘和改进</li>
</ul>
<h5 data-number="6.0.0.0.60" id="应急预案"><span
class="header-section-number">6.0.0.0.60</span> 7.2.4 应急预案</h5>
<ul>
<li>系统宕机应急预案</li>
<li>网络故障应急预案</li>
<li>数据丢失应急预案</li>
<li>安全事件应急预案</li>
</ul>
<h5 data-number="6.0.0.0.61" id="容器化部署方案"><span
class="header-section-number">6.0.0.0.61</span> 8. 容器化部署方案</h5>
<h5 data-number="6.0.0.0.62" id="docker-compose部署"><span
class="header-section-number">6.0.0.0.62</span> 8.1 Docker
Compose部署</h5>
<h5 data-number="6.0.0.0.63" id="完整部署配置"><span
class="header-section-number">6.0.0.0.63</span> 8.1.1 完整部署配置</h5>
<div class="sourceCode" id="cb126"><pre
class="sourceCode yaml"><code class="sourceCode yaml"><span id="cb126-1"><a href="#cb126-1" aria-hidden="true" tabindex="-1"></a><span class="co">##### docker-compose.yml</span></span>
<span id="cb126-2"><a href="#cb126-2" aria-hidden="true" tabindex="-1"></a><span class="fu">version</span><span class="kw">:</span><span class="at"> </span><span class="st">&#39;3.8&#39;</span></span>
<span id="cb126-3"><a href="#cb126-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb126-4"><a href="#cb126-4" aria-hidden="true" tabindex="-1"></a><span class="fu">services</span><span class="kw">:</span></span>
<span id="cb126-5"><a href="#cb126-5" aria-hidden="true" tabindex="-1"></a><span class="co"> # OpenGauss 数据库</span></span>
<span id="cb126-6"><a href="#cb126-6" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">water-opengauss</span><span class="kw">:</span></span>
<span id="cb126-7"><a href="#cb126-7" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">image</span><span class="kw">:</span><span class="at"> enmotech/opengauss:5.0.0</span></span>
<span id="cb126-8"><a href="#cb126-8" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">container_name</span><span class="kw">:</span><span class="at"> water-opengauss</span></span>
<span id="cb126-9"><a href="#cb126-9" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">restart</span><span class="kw">:</span><span class="at"> always</span></span>
<span id="cb126-10"><a href="#cb126-10" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">environment</span><span class="kw">:</span></span>
<span id="cb126-11"><a href="#cb126-11" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">GS_PASSWORD</span><span class="kw">:</span><span class="at"> </span><span class="st">&quot;Water@2024&quot;</span></span>
<span id="cb126-12"><a href="#cb126-12" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">GS_DB</span><span class="kw">:</span><span class="at"> </span><span class="st">&quot;ruoyi_water&quot;</span></span>
<span id="cb126-13"><a href="#cb126-13" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">GS_USERNAME</span><span class="kw">:</span><span class="at"> </span><span class="st">&quot;water_user&quot;</span></span>
<span id="cb126-14"><a href="#cb126-14" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">TZ</span><span class="kw">:</span><span class="at"> </span><span class="st">&quot;Asia/Shanghai&quot;</span></span>
<span id="cb126-15"><a href="#cb126-15" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">volumes</span><span class="kw">:</span></span>
<span id="cb126-16"><a href="#cb126-16" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> ./data/opengauss:/var/lib/opengauss</span></span>
<span id="cb126-17"><a href="#cb126-17" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> ./sql:/docker-entrypoint-initdb.d</span></span>
<span id="cb126-18"><a href="#cb126-18" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> ./config/opengauss:/opt/opengauss/config</span></span>
<span id="cb126-19"><a href="#cb126-19" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">ports</span><span class="kw">:</span></span>
<span id="cb126-20"><a href="#cb126-20" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="st">&quot;5432:5432&quot;</span></span>
<span id="cb126-21"><a href="#cb126-21" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">networks</span><span class="kw">:</span></span>
<span id="cb126-22"><a href="#cb126-22" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> water-network</span></span>
<span id="cb126-23"><a href="#cb126-23" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">healthcheck</span><span class="kw">:</span></span>
<span id="cb126-24"><a href="#cb126-24" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">test</span><span class="kw">:</span><span class="at"> </span><span class="kw">[</span><span class="st">&quot;CMD-SHELL&quot;</span><span class="kw">,</span><span class="at"> </span><span class="st">&quot;gs_ctl status -D /var/lib/opengauss/data&quot;</span><span class="kw">]</span></span>
<span id="cb126-25"><a href="#cb126-25" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">interval</span><span class="kw">:</span><span class="at"> 30s</span></span>
<span id="cb126-26"><a href="#cb126-26" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">timeout</span><span class="kw">:</span><span class="at"> 10s</span></span>
<span id="cb126-27"><a href="#cb126-27" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">retries</span><span class="kw">:</span><span class="at"> </span><span class="dv">3</span></span>
<span id="cb126-28"><a href="#cb126-28" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb126-29"><a href="#cb126-29" aria-hidden="true" tabindex="-1"></a><span class="co"> # Redis 缓存</span></span>
<span id="cb126-30"><a href="#cb126-30" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">water-redis</span><span class="kw">:</span></span>
<span id="cb126-31"><a href="#cb126-31" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">image</span><span class="kw">:</span><span class="at"> redis:7.0-alpine</span></span>
<span id="cb126-32"><a href="#cb126-32" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">container_name</span><span class="kw">:</span><span class="at"> water-redis</span></span>
<span id="cb126-33"><a href="#cb126-33" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">restart</span><span class="kw">:</span><span class="at"> always</span></span>
<span id="cb126-34"><a href="#cb126-34" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">volumes</span><span class="kw">:</span></span>
<span id="cb126-35"><a href="#cb126-35" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> ./data/redis:/data</span></span>
<span id="cb126-36"><a href="#cb126-36" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> ./config/redis/redis.conf:/etc/redis/redis.conf</span></span>
<span id="cb126-37"><a href="#cb126-37" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">ports</span><span class="kw">:</span></span>
<span id="cb126-38"><a href="#cb126-38" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="st">&quot;6379:6379&quot;</span></span>
<span id="cb126-39"><a href="#cb126-39" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">command</span><span class="kw">:</span><span class="at"> redis-server /etc/redis/redis.conf</span></span>
<span id="cb126-40"><a href="#cb126-40" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">networks</span><span class="kw">:</span></span>
<span id="cb126-41"><a href="#cb126-41" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> water-network</span></span>
<span id="cb126-42"><a href="#cb126-42" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb126-43"><a href="#cb126-43" aria-hidden="true" tabindex="-1"></a><span class="co"> # 后端应用</span></span>
<span id="cb126-44"><a href="#cb126-44" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">water-server</span><span class="kw">:</span></span>
<span id="cb126-45"><a href="#cb126-45" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">build</span><span class="kw">:</span></span>
<span id="cb126-46"><a href="#cb126-46" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">context</span><span class="kw">:</span><span class="at"> ./water-server</span></span>
<span id="cb126-47"><a href="#cb126-47" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">dockerfile</span><span class="kw">:</span><span class="at"> Dockerfile</span></span>
<span id="cb126-48"><a href="#cb126-48" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">container_name</span><span class="kw">:</span><span class="at"> water-server</span></span>
<span id="cb126-49"><a href="#cb126-49" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">restart</span><span class="kw">:</span><span class="at"> always</span></span>
<span id="cb126-50"><a href="#cb126-50" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">depends_on</span><span class="kw">:</span></span>
<span id="cb126-51"><a href="#cb126-51" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> water-opengauss</span></span>
<span id="cb126-52"><a href="#cb126-52" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> water-redis</span></span>
<span id="cb126-53"><a href="#cb126-53" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">environment</span><span class="kw">:</span></span>
<span id="cb126-54"><a href="#cb126-54" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> SPRING_PROFILES_ACTIVE=prod</span></span>
<span id="cb126-55"><a href="#cb126-55" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> SPRING_DATASOURCE_URL=jdbc:opengauss://water-opengauss:5432/ruoyi_water?useUnicode=true&amp;characterEncoding=utf8&amp;zeroDateTimeBehavior=convertToNull&amp;useSSL=false&amp;serverTimezone=GMT%2B8</span></span>
<span id="cb126-56"><a href="#cb126-56" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> SPRING_DATASOURCE_USERNAME=water_user</span></span>
<span id="cb126-57"><a href="#cb126-57" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> SPRING_DATASOURCE_PASSWORD=Water@2024</span></span>
<span id="cb126-58"><a href="#cb126-58" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> SPRING_REDIS_HOST=water-redis</span></span>
<span id="cb126-59"><a href="#cb126-59" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> SPRING_REDIS_PORT=6379</span></span>
<span id="cb126-60"><a href="#cb126-60" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> SERVER_PORT=8080</span></span>
<span id="cb126-61"><a href="#cb126-61" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">volumes</span><span class="kw">:</span></span>
<span id="cb126-62"><a href="#cb126-62" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> ./logs:/app/logs</span></span>
<span id="cb126-63"><a href="#cb126-63" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> ./upload:/app/upload</span></span>
<span id="cb126-64"><a href="#cb126-64" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">ports</span><span class="kw">:</span></span>
<span id="cb126-65"><a href="#cb126-65" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="st">&quot;8080:8080&quot;</span></span>
<span id="cb126-66"><a href="#cb126-66" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">networks</span><span class="kw">:</span></span>
<span id="cb126-67"><a href="#cb126-67" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> water-network</span></span>
<span id="cb126-68"><a href="#cb126-68" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb126-69"><a href="#cb126-69" aria-hidden="true" tabindex="-1"></a><span class="co"> # 前端应用</span></span>
<span id="cb126-70"><a href="#cb126-70" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">water-ui</span><span class="kw">:</span></span>
<span id="cb126-71"><a href="#cb126-71" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">build</span><span class="kw">:</span></span>
<span id="cb126-72"><a href="#cb126-72" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">context</span><span class="kw">:</span><span class="at"> ./water-ui</span></span>
<span id="cb126-73"><a href="#cb126-73" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">dockerfile</span><span class="kw">:</span><span class="at"> Dockerfile</span></span>
<span id="cb126-74"><a href="#cb126-74" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">container_name</span><span class="kw">:</span><span class="at"> water-ui</span></span>
<span id="cb126-75"><a href="#cb126-75" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">restart</span><span class="kw">:</span><span class="at"> always</span></span>
<span id="cb126-76"><a href="#cb126-76" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">depends_on</span><span class="kw">:</span></span>
<span id="cb126-77"><a href="#cb126-77" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> water-server</span></span>
<span id="cb126-78"><a href="#cb126-78" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">volumes</span><span class="kw">:</span></span>
<span id="cb126-79"><a href="#cb126-79" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> ./config/nginx/nginx.conf:/etc/nginx/nginx.conf</span></span>
<span id="cb126-80"><a href="#cb126-80" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> ./config/nginx/conf.d:/etc/nginx/conf.d</span></span>
<span id="cb126-81"><a href="#cb126-81" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">ports</span><span class="kw">:</span></span>
<span id="cb126-82"><a href="#cb126-82" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="st">&quot;80:80&quot;</span></span>
<span id="cb126-83"><a href="#cb126-83" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="st">&quot;443:443&quot;</span></span>
<span id="cb126-84"><a href="#cb126-84" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">networks</span><span class="kw">:</span></span>
<span id="cb126-85"><a href="#cb126-85" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> water-network</span></span>
<span id="cb126-86"><a href="#cb126-86" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb126-87"><a href="#cb126-87" aria-hidden="true" tabindex="-1"></a><span class="co"> # Nginx 反向代理</span></span>
<span id="cb126-88"><a href="#cb126-88" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">water-nginx</span><span class="kw">:</span></span>
<span id="cb126-89"><a href="#cb126-89" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">image</span><span class="kw">:</span><span class="at"> nginx:1.24-alpine</span></span>
<span id="cb126-90"><a href="#cb126-90" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">container_name</span><span class="kw">:</span><span class="at"> water-nginx</span></span>
<span id="cb126-91"><a href="#cb126-91" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">restart</span><span class="kw">:</span><span class="at"> always</span></span>
<span id="cb126-92"><a href="#cb126-92" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">depends_on</span><span class="kw">:</span></span>
<span id="cb126-93"><a href="#cb126-93" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> water-server</span></span>
<span id="cb126-94"><a href="#cb126-94" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> water-ui</span></span>
<span id="cb126-95"><a href="#cb126-95" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">volumes</span><span class="kw">:</span></span>
<span id="cb126-96"><a href="#cb126-96" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> ./config/nginx/nginx.conf:/etc/nginx/nginx.conf</span></span>
<span id="cb126-97"><a href="#cb126-97" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> ./config/nginx/conf.d:/etc/nginx/conf.d</span></span>
<span id="cb126-98"><a href="#cb126-98" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> ./ssl:/etc/nginx/ssl</span></span>
<span id="cb126-99"><a href="#cb126-99" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> ./logs/nginx:/var/log/nginx</span></span>
<span id="cb126-100"><a href="#cb126-100" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">ports</span><span class="kw">:</span></span>
<span id="cb126-101"><a href="#cb126-101" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="st">&quot;80:80&quot;</span></span>
<span id="cb126-102"><a href="#cb126-102" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="st">&quot;443:443&quot;</span></span>
<span id="cb126-103"><a href="#cb126-103" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">networks</span><span class="kw">:</span></span>
<span id="cb126-104"><a href="#cb126-104" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> water-network</span></span>
<span id="cb126-105"><a href="#cb126-105" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb126-106"><a href="#cb126-106" aria-hidden="true" tabindex="-1"></a><span class="co"> # MinIO 文件存储</span></span>
<span id="cb126-107"><a href="#cb126-107" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">water-minio</span><span class="kw">:</span></span>
<span id="cb126-108"><a href="#cb126-108" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">image</span><span class="kw">:</span><span class="at"> minio/minio:latest</span></span>
<span id="cb126-109"><a href="#cb126-109" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">container_name</span><span class="kw">:</span><span class="at"> water-minio</span></span>
<span id="cb126-110"><a href="#cb126-110" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">restart</span><span class="kw">:</span><span class="at"> always</span></span>
<span id="cb126-111"><a href="#cb126-111" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">environment</span><span class="kw">:</span></span>
<span id="cb126-112"><a href="#cb126-112" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">MINIO_ACCESS_KEY</span><span class="kw">:</span><span class="at"> </span><span class="st">&quot;admin&quot;</span></span>
<span id="cb126-113"><a href="#cb126-113" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">MINIO_SECRET_KEY</span><span class="kw">:</span><span class="at"> </span><span class="st">&quot;admin123456&quot;</span></span>
<span id="cb126-114"><a href="#cb126-114" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">volumes</span><span class="kw">:</span></span>
<span id="cb126-115"><a href="#cb126-115" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> ./data/minio:/data</span></span>
<span id="cb126-116"><a href="#cb126-116" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">ports</span><span class="kw">:</span></span>
<span id="cb126-117"><a href="#cb126-117" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="st">&quot;9000:9000&quot;</span></span>
<span id="cb126-118"><a href="#cb126-118" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="st">&quot;9001:9001&quot;</span></span>
<span id="cb126-119"><a href="#cb126-119" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">command</span><span class="kw">:</span><span class="at"> server /data --console-address &quot;:9001&quot;</span></span>
<span id="cb126-120"><a href="#cb126-120" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">networks</span><span class="kw">:</span></span>
<span id="cb126-121"><a href="#cb126-121" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> water-network</span></span>
<span id="cb126-122"><a href="#cb126-122" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb126-123"><a href="#cb126-123" aria-hidden="true" tabindex="-1"></a><span class="fu">networks</span><span class="kw">:</span></span>
<span id="cb126-124"><a href="#cb126-124" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">water-network</span><span class="kw">:</span></span>
<span id="cb126-125"><a href="#cb126-125" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">driver</span><span class="kw">:</span><span class="at"> bridge</span></span>
<span id="cb126-126"><a href="#cb126-126" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb126-127"><a href="#cb126-127" aria-hidden="true" tabindex="-1"></a><span class="fu">volumes</span><span class="kw">:</span></span>
<span id="cb126-128"><a href="#cb126-128" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">opengauss-data</span><span class="kw">:</span></span>
<span id="cb126-129"><a href="#cb126-129" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">redis-data</span><span class="kw">:</span></span>
<span id="cb126-130"><a href="#cb126-130" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">minio-data</span><span class="kw">:</span></span></code></pre></div>
<h5 data-number="6.0.0.0.64" id="后端应用dockerfile"><span
class="header-section-number">6.0.0.0.64</span> 8.1.2
后端应用Dockerfile</h5>
<div class="sourceCode" id="cb127"><pre
class="sourceCode dockerfile"><code class="sourceCode dockerfile"><span id="cb127-1"><a href="#cb127-1" aria-hidden="true" tabindex="-1"></a><span class="co">##### water-server/Dockerfile</span></span>
<span id="cb127-2"><a href="#cb127-2" aria-hidden="true" tabindex="-1"></a><span class="kw">FROM</span> openjdk:17-jdk-slim</span>
<span id="cb127-3"><a href="#cb127-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb127-4"><a href="#cb127-4" aria-hidden="true" tabindex="-1"></a><span class="kw">LABEL</span> maintainer=<span class="st">&quot;fujian-water-dev-team&quot;</span></span>
<span id="cb127-5"><a href="#cb127-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb127-6"><a href="#cb127-6" aria-hidden="true" tabindex="-1"></a><span class="co">##### 设置工作目录</span></span>
<span id="cb127-7"><a href="#cb127-7" aria-hidden="true" tabindex="-1"></a><span class="kw">WORKDIR</span> /app</span>
<span id="cb127-8"><a href="#cb127-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb127-9"><a href="#cb127-9" aria-hidden="true" tabindex="-1"></a><span class="co">##### 设置时区</span></span>
<span id="cb127-10"><a href="#cb127-10" aria-hidden="true" tabindex="-1"></a><span class="kw">ENV</span> TZ=Asia/Shanghai</span>
<span id="cb127-11"><a href="#cb127-11" aria-hidden="true" tabindex="-1"></a><span class="kw">RUN</span> <span class="fu">ln</span> <span class="at">-snf</span> /usr/share/zoneinfo/<span class="va">$TZ</span> /etc/localtime <span class="kw">&amp;&amp;</span> <span class="bu">echo</span> <span class="va">$TZ</span> <span class="op">&gt;</span> /etc/timezone</span>
<span id="cb127-12"><a href="#cb127-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb127-13"><a href="#cb127-13" aria-hidden="true" tabindex="-1"></a><span class="co">##### 添加应用jar包</span></span>
<span id="cb127-14"><a href="#cb127-14" aria-hidden="true" tabindex="-1"></a><span class="kw">COPY</span> target/water-server.jar app.jar</span>
<span id="cb127-15"><a href="#cb127-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb127-16"><a href="#cb127-16" aria-hidden="true" tabindex="-1"></a><span class="co">##### 创建日志目录</span></span>
<span id="cb127-17"><a href="#cb127-17" aria-hidden="true" tabindex="-1"></a><span class="kw">RUN</span> <span class="fu">mkdir</span> <span class="at">-p</span> /app/logs</span>
<span id="cb127-18"><a href="#cb127-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb127-19"><a href="#cb127-19" aria-hidden="true" tabindex="-1"></a><span class="co">##### 暴露端口</span></span>
<span id="cb127-20"><a href="#cb127-20" aria-hidden="true" tabindex="-1"></a><span class="kw">EXPOSE</span> 8080</span>
<span id="cb127-21"><a href="#cb127-21" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb127-22"><a href="#cb127-22" aria-hidden="true" tabindex="-1"></a><span class="co">##### 启动应用</span></span>
<span id="cb127-23"><a href="#cb127-23" aria-hidden="true" tabindex="-1"></a><span class="kw">ENTRYPOINT</span> [<span class="st">&quot;java&quot;</span>, <span class="st">&quot;-jar&quot;</span>, <span class="st">&quot;-Xmx1024m&quot;</span>, <span class="st">&quot;-Xms512m&quot;</span>, <span class="st">&quot;-Dspring.profiles.active=prod&quot;</span>, <span class="st">&quot;/app/app.jar&quot;</span>]</span></code></pre></div>
<h5 data-number="6.0.0.0.65" id="前端应用dockerfile"><span
class="header-section-number">6.0.0.0.65</span> 8.1.3
前端应用Dockerfile</h5>
<div class="sourceCode" id="cb128"><pre
class="sourceCode dockerfile"><code class="sourceCode dockerfile"><span id="cb128-1"><a href="#cb128-1" aria-hidden="true" tabindex="-1"></a><span class="co">##### water-ui/Dockerfile</span></span>
<span id="cb128-2"><a href="#cb128-2" aria-hidden="true" tabindex="-1"></a><span class="co">##### 构建阶段</span></span>
<span id="cb128-3"><a href="#cb128-3" aria-hidden="true" tabindex="-1"></a><span class="kw">FROM</span> node:18-alpine <span class="kw">AS</span> builder</span>
<span id="cb128-4"><a href="#cb128-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb128-5"><a href="#cb128-5" aria-hidden="true" tabindex="-1"></a><span class="kw">WORKDIR</span> /app</span>
<span id="cb128-6"><a href="#cb128-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb128-7"><a href="#cb128-7" aria-hidden="true" tabindex="-1"></a><span class="co">##### 复制包管理文件</span></span>
<span id="cb128-8"><a href="#cb128-8" aria-hidden="true" tabindex="-1"></a><span class="kw">COPY</span> package*.json ./</span>
<span id="cb128-9"><a href="#cb128-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb128-10"><a href="#cb128-10" aria-hidden="true" tabindex="-1"></a><span class="co">##### 安装依赖</span></span>
<span id="cb128-11"><a href="#cb128-11" aria-hidden="true" tabindex="-1"></a><span class="kw">RUN</span> <span class="ex">npm</span> ci <span class="at">--only</span><span class="op">=</span>production</span>
<span id="cb128-12"><a href="#cb128-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb128-13"><a href="#cb128-13" aria-hidden="true" tabindex="-1"></a><span class="co">##### 复制源代码</span></span>
<span id="cb128-14"><a href="#cb128-14" aria-hidden="true" tabindex="-1"></a><span class="kw">COPY</span> . .</span>
<span id="cb128-15"><a href="#cb128-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb128-16"><a href="#cb128-16" aria-hidden="true" tabindex="-1"></a><span class="co">##### 构建应用</span></span>
<span id="cb128-17"><a href="#cb128-17" aria-hidden="true" tabindex="-1"></a><span class="kw">RUN</span> <span class="ex">npm</span> run build:prod</span>
<span id="cb128-18"><a href="#cb128-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb128-19"><a href="#cb128-19" aria-hidden="true" tabindex="-1"></a><span class="co">##### 生产阶段</span></span>
<span id="cb128-20"><a href="#cb128-20" aria-hidden="true" tabindex="-1"></a><span class="kw">FROM</span> nginx:1.24-alpine</span>
<span id="cb128-21"><a href="#cb128-21" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb128-22"><a href="#cb128-22" aria-hidden="true" tabindex="-1"></a><span class="co">##### 复制构建产物</span></span>
<span id="cb128-23"><a href="#cb128-23" aria-hidden="true" tabindex="-1"></a><span class="kw">COPY</span> <span class="op">--from=builder</span> /app/dist /usr/share/nginx/html</span>
<span id="cb128-24"><a href="#cb128-24" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb128-25"><a href="#cb128-25" aria-hidden="true" tabindex="-1"></a><span class="co">##### 复制nginx配置</span></span>
<span id="cb128-26"><a href="#cb128-26" aria-hidden="true" tabindex="-1"></a><span class="kw">COPY</span> nginx.conf /etc/nginx/nginx.conf</span>
<span id="cb128-27"><a href="#cb128-27" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb128-28"><a href="#cb128-28" aria-hidden="true" tabindex="-1"></a><span class="co">##### 暴露端口</span></span>
<span id="cb128-29"><a href="#cb128-29" aria-hidden="true" tabindex="-1"></a><span class="kw">EXPOSE</span> 80</span>
<span id="cb128-30"><a href="#cb128-30" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb128-31"><a href="#cb128-31" aria-hidden="true" tabindex="-1"></a><span class="co">##### 启动nginx</span></span>
<span id="cb128-32"><a href="#cb128-32" aria-hidden="true" tabindex="-1"></a><span class="kw">CMD</span> [<span class="st">&quot;nginx&quot;</span>, <span class="st">&quot;-g&quot;</span>, <span class="st">&quot;daemon off;&quot;</span>]</span></code></pre></div>
<h5 data-number="6.0.0.0.66" id="docker-compose高级配置"><span
class="header-section-number">6.0.0.0.66</span> 8.2 Docker
Compose高级配置</h5>
<h5 data-number="6.0.0.0.67" id="生产环境配置优化"><span
class="header-section-number">6.0.0.0.67</span> 8.2.1
生产环境配置优化</h5>
<p><strong>Docker Compose生产环境配置文件</strong></p>
<div class="sourceCode" id="cb129"><pre
class="sourceCode yaml"><code class="sourceCode yaml"><span id="cb129-1"><a href="#cb129-1" aria-hidden="true" tabindex="-1"></a><span class="co">##### docker-compose.prod.yml</span></span>
<span id="cb129-2"><a href="#cb129-2" aria-hidden="true" tabindex="-1"></a><span class="fu">version</span><span class="kw">:</span><span class="at"> </span><span class="st">&#39;3.8&#39;</span></span>
<span id="cb129-3"><a href="#cb129-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb129-4"><a href="#cb129-4" aria-hidden="true" tabindex="-1"></a><span class="fu">services</span><span class="kw">:</span></span>
<span id="cb129-5"><a href="#cb129-5" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">water-opengauss</span><span class="kw">:</span></span>
<span id="cb129-6"><a href="#cb129-6" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">image</span><span class="kw">:</span><span class="at"> enmotech/opengauss:5.0.0</span></span>
<span id="cb129-7"><a href="#cb129-7" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">container_name</span><span class="kw">:</span><span class="at"> water-opengauss-prod</span></span>
<span id="cb129-8"><a href="#cb129-8" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">restart</span><span class="kw">:</span><span class="at"> unless-stopped</span></span>
<span id="cb129-9"><a href="#cb129-9" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">environment</span><span class="kw">:</span></span>
<span id="cb129-10"><a href="#cb129-10" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">GS_PASSWORD</span><span class="kw">:</span><span class="at"> </span><span class="st">&quot;${DB_PASSWORD:-Water@2024!}&quot;</span></span>
<span id="cb129-11"><a href="#cb129-11" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">GS_DB</span><span class="kw">:</span><span class="at"> </span><span class="st">&quot;ruoyi_water&quot;</span></span>
<span id="cb129-12"><a href="#cb129-12" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">GS_USERNAME</span><span class="kw">:</span><span class="at"> </span><span class="st">&quot;water_user&quot;</span></span>
<span id="cb129-13"><a href="#cb129-13" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">TZ</span><span class="kw">:</span><span class="at"> </span><span class="st">&quot;Asia/Shanghai&quot;</span></span>
<span id="cb129-14"><a href="#cb129-14" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">volumes</span><span class="kw">:</span></span>
<span id="cb129-15"><a href="#cb129-15" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> opengauss-prod-data:/var/lib/opengauss</span></span>
<span id="cb129-16"><a href="#cb129-16" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> ./config/opengauss/prod:/opt/opengauss/config</span></span>
<span id="cb129-17"><a href="#cb129-17" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> ./backups:/backups</span></span>
<span id="cb129-18"><a href="#cb129-18" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">ports</span><span class="kw">:</span></span>
<span id="cb129-19"><a href="#cb129-19" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="st">&quot;5432:5432&quot;</span></span>
<span id="cb129-20"><a href="#cb129-20" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">networks</span><span class="kw">:</span></span>
<span id="cb129-21"><a href="#cb129-21" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> water-prod-network</span></span>
<span id="cb129-22"><a href="#cb129-22" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">deploy</span><span class="kw">:</span></span>
<span id="cb129-23"><a href="#cb129-23" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">resources</span><span class="kw">:</span></span>
<span id="cb129-24"><a href="#cb129-24" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">limits</span><span class="kw">:</span></span>
<span id="cb129-25"><a href="#cb129-25" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">memory</span><span class="kw">:</span><span class="at"> 2G</span></span>
<span id="cb129-26"><a href="#cb129-26" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">cpus</span><span class="kw">:</span><span class="at"> </span><span class="st">&#39;2.0&#39;</span></span>
<span id="cb129-27"><a href="#cb129-27" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">healthcheck</span><span class="kw">:</span></span>
<span id="cb129-28"><a href="#cb129-28" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">test</span><span class="kw">:</span><span class="at"> </span><span class="kw">[</span><span class="st">&quot;CMD-SHELL&quot;</span><span class="kw">,</span><span class="at"> </span><span class="st">&quot;gs_ctl status -D /var/lib/opengauss/data&quot;</span><span class="kw">]</span></span>
<span id="cb129-29"><a href="#cb129-29" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">interval</span><span class="kw">:</span><span class="at"> 30s</span></span>
<span id="cb129-30"><a href="#cb129-30" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">timeout</span><span class="kw">:</span><span class="at"> 10s</span></span>
<span id="cb129-31"><a href="#cb129-31" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">retries</span><span class="kw">:</span><span class="at"> </span><span class="dv">5</span></span>
<span id="cb129-32"><a href="#cb129-32" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">start_period</span><span class="kw">:</span><span class="at"> 60s</span></span>
<span id="cb129-33"><a href="#cb129-33" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb129-34"><a href="#cb129-34" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">water-redis</span><span class="kw">:</span></span>
<span id="cb129-35"><a href="#cb129-35" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">image</span><span class="kw">:</span><span class="at"> redis:7.0-alpine</span></span>
<span id="cb129-36"><a href="#cb129-36" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">container_name</span><span class="kw">:</span><span class="at"> water-redis-prod</span></span>
<span id="cb129-37"><a href="#cb129-37" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">restart</span><span class="kw">:</span><span class="at"> unless-stopped</span></span>
<span id="cb129-38"><a href="#cb129-38" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">command</span><span class="kw">:</span><span class="at"> redis-server /etc/redis/redis.conf --requirepass ${REDIS_PASSWORD:-water_redis_2024}</span></span>
<span id="cb129-39"><a href="#cb129-39" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">volumes</span><span class="kw">:</span></span>
<span id="cb129-40"><a href="#cb129-40" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> redis-prod-data:/data</span></span>
<span id="cb129-41"><a href="#cb129-41" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> ./config/redis/prod.conf:/etc/redis/redis.conf</span></span>
<span id="cb129-42"><a href="#cb129-42" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">ports</span><span class="kw">:</span></span>
<span id="cb129-43"><a href="#cb129-43" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="st">&quot;6379:6379&quot;</span></span>
<span id="cb129-44"><a href="#cb129-44" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">networks</span><span class="kw">:</span></span>
<span id="cb129-45"><a href="#cb129-45" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> water-prod-network</span></span>
<span id="cb129-46"><a href="#cb129-46" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">deploy</span><span class="kw">:</span></span>
<span id="cb129-47"><a href="#cb129-47" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">resources</span><span class="kw">:</span></span>
<span id="cb129-48"><a href="#cb129-48" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">limits</span><span class="kw">:</span></span>
<span id="cb129-49"><a href="#cb129-49" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">memory</span><span class="kw">:</span><span class="at"> 512M</span></span>
<span id="cb129-50"><a href="#cb129-50" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">cpus</span><span class="kw">:</span><span class="at"> </span><span class="st">&#39;1.0&#39;</span></span>
<span id="cb129-51"><a href="#cb129-51" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb129-52"><a href="#cb129-52" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">water-server</span><span class="kw">:</span></span>
<span id="cb129-53"><a href="#cb129-53" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">image</span><span class="kw">:</span><span class="at"> water-server:${VERSION:-latest}</span></span>
<span id="cb129-54"><a href="#cb129-54" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">container_name</span><span class="kw">:</span><span class="at"> water-server-prod</span></span>
<span id="cb129-55"><a href="#cb129-55" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">restart</span><span class="kw">:</span><span class="at"> unless-stopped</span></span>
<span id="cb129-56"><a href="#cb129-56" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">depends_on</span><span class="kw">:</span></span>
<span id="cb129-57"><a href="#cb129-57" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">water-opengauss</span><span class="kw">:</span></span>
<span id="cb129-58"><a href="#cb129-58" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">condition</span><span class="kw">:</span><span class="at"> service_healthy</span></span>
<span id="cb129-59"><a href="#cb129-59" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">water-redis</span><span class="kw">:</span></span>
<span id="cb129-60"><a href="#cb129-60" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">condition</span><span class="kw">:</span><span class="at"> service_started</span></span>
<span id="cb129-61"><a href="#cb129-61" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">environment</span><span class="kw">:</span></span>
<span id="cb129-62"><a href="#cb129-62" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> SPRING_PROFILES_ACTIVE=prod</span></span>
<span id="cb129-63"><a href="#cb129-63" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> SPRING_DATASOURCE_URL=jdbc:opengauss://water-opengauss:5432/ruoyi_water</span></span>
<span id="cb129-64"><a href="#cb129-64" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> SPRING_DATASOURCE_USERNAME=water_user</span></span>
<span id="cb129-65"><a href="#cb129-65" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> SPRING_DATASOURCE_PASSWORD=${DB_PASSWORD:-Water@2024!}</span></span>
<span id="cb129-66"><a href="#cb129-66" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> SPRING_REDIS_HOST=water-redis</span></span>
<span id="cb129-67"><a href="#cb129-67" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> SPRING_REDIS_PASSWORD=${REDIS_PASSWORD:-water_redis_2024}</span></span>
<span id="cb129-68"><a href="#cb129-68" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> SERVER_PORT=8080</span></span>
<span id="cb129-69"><a href="#cb129-69" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> JAVA_OPTS=-Xmx2g -Xms1g -XX:+UseG1GC</span></span>
<span id="cb129-70"><a href="#cb129-70" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">volumes</span><span class="kw">:</span></span>
<span id="cb129-71"><a href="#cb129-71" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> ./logs/prod:/app/logs</span></span>
<span id="cb129-72"><a href="#cb129-72" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> ./upload/prod:/app/upload</span></span>
<span id="cb129-73"><a href="#cb129-73" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> ./config/app:/app/config</span></span>
<span id="cb129-74"><a href="#cb129-74" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">networks</span><span class="kw">:</span></span>
<span id="cb129-75"><a href="#cb129-75" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> water-prod-network</span></span>
<span id="cb129-76"><a href="#cb129-76" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">deploy</span><span class="kw">:</span></span>
<span id="cb129-77"><a href="#cb129-77" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">resources</span><span class="kw">:</span></span>
<span id="cb129-78"><a href="#cb129-78" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">limits</span><span class="kw">:</span></span>
<span id="cb129-79"><a href="#cb129-79" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">memory</span><span class="kw">:</span><span class="at"> 3G</span></span>
<span id="cb129-80"><a href="#cb129-80" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">cpus</span><span class="kw">:</span><span class="at"> </span><span class="st">&#39;2.0&#39;</span></span>
<span id="cb129-81"><a href="#cb129-81" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">replicas</span><span class="kw">:</span><span class="at"> </span><span class="dv">2</span></span>
<span id="cb129-82"><a href="#cb129-82" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">healthcheck</span><span class="kw">:</span></span>
<span id="cb129-83"><a href="#cb129-83" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">test</span><span class="kw">:</span><span class="at"> </span><span class="kw">[</span><span class="st">&quot;CMD&quot;</span><span class="kw">,</span><span class="at"> </span><span class="st">&quot;curl&quot;</span><span class="kw">,</span><span class="at"> </span><span class="st">&quot;-f&quot;</span><span class="kw">,</span><span class="at"> </span><span class="st">&quot;http://localhost:8080/actuator/health&quot;</span><span class="kw">]</span></span>
<span id="cb129-84"><a href="#cb129-84" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">interval</span><span class="kw">:</span><span class="at"> 30s</span></span>
<span id="cb129-85"><a href="#cb129-85" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">timeout</span><span class="kw">:</span><span class="at"> 10s</span></span>
<span id="cb129-86"><a href="#cb129-86" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">retries</span><span class="kw">:</span><span class="at"> </span><span class="dv">3</span></span>
<span id="cb129-87"><a href="#cb129-87" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb129-88"><a href="#cb129-88" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">water-nginx</span><span class="kw">:</span></span>
<span id="cb129-89"><a href="#cb129-89" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">image</span><span class="kw">:</span><span class="at"> nginx:1.24-alpine</span></span>
<span id="cb129-90"><a href="#cb129-90" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">container_name</span><span class="kw">:</span><span class="at"> water-nginx-prod</span></span>
<span id="cb129-91"><a href="#cb129-91" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">restart</span><span class="kw">:</span><span class="at"> unless-stopped</span></span>
<span id="cb129-92"><a href="#cb129-92" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">depends_on</span><span class="kw">:</span></span>
<span id="cb129-93"><a href="#cb129-93" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> water-server</span></span>
<span id="cb129-94"><a href="#cb129-94" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">volumes</span><span class="kw">:</span></span>
<span id="cb129-95"><a href="#cb129-95" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> ./config/nginx/prod.conf:/etc/nginx/nginx.conf</span></span>
<span id="cb129-96"><a href="#cb129-96" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> ./config/nginx/conf.d:/etc/nginx/conf.d</span></span>
<span id="cb129-97"><a href="#cb129-97" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> ./ssl:/etc/nginx/ssl</span></span>
<span id="cb129-98"><a href="#cb129-98" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> ./logs/nginx:/var/log/nginx</span></span>
<span id="cb129-99"><a href="#cb129-99" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> ./static:/usr/share/nginx/html</span></span>
<span id="cb129-100"><a href="#cb129-100" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">ports</span><span class="kw">:</span></span>
<span id="cb129-101"><a href="#cb129-101" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="st">&quot;80:80&quot;</span></span>
<span id="cb129-102"><a href="#cb129-102" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="st">&quot;443:443&quot;</span></span>
<span id="cb129-103"><a href="#cb129-103" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">networks</span><span class="kw">:</span></span>
<span id="cb129-104"><a href="#cb129-104" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> water-prod-network</span></span>
<span id="cb129-105"><a href="#cb129-105" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">deploy</span><span class="kw">:</span></span>
<span id="cb129-106"><a href="#cb129-106" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">resources</span><span class="kw">:</span></span>
<span id="cb129-107"><a href="#cb129-107" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">limits</span><span class="kw">:</span></span>
<span id="cb129-108"><a href="#cb129-108" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">memory</span><span class="kw">:</span><span class="at"> 256M</span></span>
<span id="cb129-109"><a href="#cb129-109" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">cpus</span><span class="kw">:</span><span class="at"> </span><span class="st">&#39;0.5&#39;</span></span>
<span id="cb129-110"><a href="#cb129-110" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb129-111"><a href="#cb129-111" aria-hidden="true" tabindex="-1"></a><span class="fu">networks</span><span class="kw">:</span></span>
<span id="cb129-112"><a href="#cb129-112" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">water-prod-network</span><span class="kw">:</span></span>
<span id="cb129-113"><a href="#cb129-113" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">driver</span><span class="kw">:</span><span class="at"> bridge</span></span>
<span id="cb129-114"><a href="#cb129-114" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">ipam</span><span class="kw">:</span></span>
<span id="cb129-115"><a href="#cb129-115" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">config</span><span class="kw">:</span></span>
<span id="cb129-116"><a href="#cb129-116" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="fu">subnet</span><span class="kw">:</span><span class="at"> 172.20.0.0/16</span></span>
<span id="cb129-117"><a href="#cb129-117" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb129-118"><a href="#cb129-118" aria-hidden="true" tabindex="-1"></a><span class="fu">volumes</span><span class="kw">:</span></span>
<span id="cb129-119"><a href="#cb129-119" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">opengauss-prod-data</span><span class="kw">:</span></span>
<span id="cb129-120"><a href="#cb129-120" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">driver</span><span class="kw">:</span><span class="at"> local</span></span>
<span id="cb129-121"><a href="#cb129-121" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">redis-prod-data</span><span class="kw">:</span></span>
<span id="cb129-122"><a href="#cb129-122" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">driver</span><span class="kw">:</span><span class="at"> local</span></span></code></pre></div>
<h5 data-number="6.0.0.0.68" id="环境变量配置"><span
class="header-section-number">6.0.0.0.68</span> 8.2.2 环境变量配置</h5>
<div class="sourceCode" id="cb130"><pre
class="sourceCode bash"><code class="sourceCode bash"><span id="cb130-1"><a href="#cb130-1" aria-hidden="true" tabindex="-1"></a><span class="co">##### .env.prod</span></span>
<span id="cb130-2"><a href="#cb130-2" aria-hidden="true" tabindex="-1"></a><span class="co">##### 数据库配置</span></span>
<span id="cb130-3"><a href="#cb130-3" aria-hidden="true" tabindex="-1"></a><span class="va">DB_PASSWORD</span><span class="op">=</span>Water@2024!Production</span>
<span id="cb130-4"><a href="#cb130-4" aria-hidden="true" tabindex="-1"></a><span class="va">DB_HOST</span><span class="op">=</span>water-opengauss</span>
<span id="cb130-5"><a href="#cb130-5" aria-hidden="true" tabindex="-1"></a><span class="va">DB_PORT</span><span class="op">=</span>5432</span>
<span id="cb130-6"><a href="#cb130-6" aria-hidden="true" tabindex="-1"></a><span class="va">DB_NAME</span><span class="op">=</span>ruoyi_water</span>
<span id="cb130-7"><a href="#cb130-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb130-8"><a href="#cb130-8" aria-hidden="true" tabindex="-1"></a><span class="co">##### Redis配置</span></span>
<span id="cb130-9"><a href="#cb130-9" aria-hidden="true" tabindex="-1"></a><span class="va">REDIS_PASSWORD</span><span class="op">=</span>WaterRedis@2024!Production</span>
<span id="cb130-10"><a href="#cb130-10" aria-hidden="true" tabindex="-1"></a><span class="va">REDIS_HOST</span><span class="op">=</span>water-redis</span>
<span id="cb130-11"><a href="#cb130-11" aria-hidden="true" tabindex="-1"></a><span class="va">REDIS_PORT</span><span class="op">=</span>6379</span>
<span id="cb130-12"><a href="#cb130-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb130-13"><a href="#cb130-13" aria-hidden="true" tabindex="-1"></a><span class="co">##### 应用配置</span></span>
<span id="cb130-14"><a href="#cb130-14" aria-hidden="true" tabindex="-1"></a><span class="va">APP_VERSION</span><span class="op">=</span>1.0.0</span>
<span id="cb130-15"><a href="#cb130-15" aria-hidden="true" tabindex="-1"></a><span class="va">JAVA_OPTS</span><span class="op">=</span>-Xmx2g <span class="ex">-Xms1g</span> <span class="at">-XX:+UseG1GC</span> <span class="at">-XX:+HeapDumpOnOutOfMemoryError</span></span>
<span id="cb130-16"><a href="#cb130-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb130-17"><a href="#cb130-17" aria-hidden="true" tabindex="-1"></a><span class="co">##### 网络配置</span></span>
<span id="cb130-18"><a href="#cb130-18" aria-hidden="true" tabindex="-1"></a><span class="va">NGINX_PORT</span><span class="op">=</span>80</span>
<span id="cb130-19"><a href="#cb130-19" aria-hidden="true" tabindex="-1"></a><span class="va">NGINX_SSL_PORT</span><span class="op">=</span>443</span>
<span id="cb130-20"><a href="#cb130-20" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb130-21"><a href="#cb130-21" aria-hidden="true" tabindex="-1"></a><span class="co">##### 日志级别</span></span>
<span id="cb130-22"><a href="#cb130-22" aria-hidden="true" tabindex="-1"></a><span class="va">LOG_LEVEL</span><span class="op">=</span>INFO</span>
<span id="cb130-23"><a href="#cb130-23" aria-hidden="true" tabindex="-1"></a><span class="va">LOG_ROOT_LEVEL</span><span class="op">=</span>WARN</span></code></pre></div>
<h5 data-number="6.0.0.0.69" id="监控集成"><span
class="header-section-number">6.0.0.0.69</span> 8.2.3 监控集成</h5>
<div class="sourceCode" id="cb131"><pre
class="sourceCode yaml"><code class="sourceCode yaml"><span id="cb131-1"><a href="#cb131-1" aria-hidden="true" tabindex="-1"></a><span class="co">##### docker-compose.monitoring.yml</span></span>
<span id="cb131-2"><a href="#cb131-2" aria-hidden="true" tabindex="-1"></a><span class="fu">version</span><span class="kw">:</span><span class="at"> </span><span class="st">&#39;3.8&#39;</span></span>
<span id="cb131-3"><a href="#cb131-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb131-4"><a href="#cb131-4" aria-hidden="true" tabindex="-1"></a><span class="fu">services</span><span class="kw">:</span></span>
<span id="cb131-5"><a href="#cb131-5" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">prometheus</span><span class="kw">:</span></span>
<span id="cb131-6"><a href="#cb131-6" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">image</span><span class="kw">:</span><span class="at"> prom/prometheus:latest</span></span>
<span id="cb131-7"><a href="#cb131-7" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">container_name</span><span class="kw">:</span><span class="at"> water-prometheus</span></span>
<span id="cb131-8"><a href="#cb131-8" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">restart</span><span class="kw">:</span><span class="at"> unless-stopped</span></span>
<span id="cb131-9"><a href="#cb131-9" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">volumes</span><span class="kw">:</span></span>
<span id="cb131-10"><a href="#cb131-10" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> ./config/prometheus:/etc/prometheus</span></span>
<span id="cb131-11"><a href="#cb131-11" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> prometheus-data:/prometheus</span></span>
<span id="cb131-12"><a href="#cb131-12" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">ports</span><span class="kw">:</span></span>
<span id="cb131-13"><a href="#cb131-13" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="st">&quot;9090:9090&quot;</span></span>
<span id="cb131-14"><a href="#cb131-14" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">command</span><span class="kw">:</span></span>
<span id="cb131-15"><a href="#cb131-15" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="st">&#39;--config.file=/etc/prometheus/prometheus.yml&#39;</span></span>
<span id="cb131-16"><a href="#cb131-16" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="st">&#39;--storage.tsdb.path=/prometheus&#39;</span></span>
<span id="cb131-17"><a href="#cb131-17" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="st">&#39;--web.console.libraries=/etc/prometheus/console_libraries&#39;</span></span>
<span id="cb131-18"><a href="#cb131-18" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="st">&#39;--web.console.templates=/etc/prometheus/consoles&#39;</span></span>
<span id="cb131-19"><a href="#cb131-19" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">networks</span><span class="kw">:</span></span>
<span id="cb131-20"><a href="#cb131-20" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> water-network</span></span>
<span id="cb131-21"><a href="#cb131-21" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb131-22"><a href="#cb131-22" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">grafana</span><span class="kw">:</span></span>
<span id="cb131-23"><a href="#cb131-23" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">image</span><span class="kw">:</span><span class="at"> grafana/grafana:latest</span></span>
<span id="cb131-24"><a href="#cb131-24" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">container_name</span><span class="kw">:</span><span class="at"> water-grafana</span></span>
<span id="cb131-25"><a href="#cb131-25" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">restart</span><span class="kw">:</span><span class="at"> unless-stopped</span></span>
<span id="cb131-26"><a href="#cb131-26" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">environment</span><span class="kw">:</span></span>
<span id="cb131-27"><a href="#cb131-27" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> GF_SECURITY_ADMIN_PASSWORD=admin123</span></span>
<span id="cb131-28"><a href="#cb131-28" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">volumes</span><span class="kw">:</span></span>
<span id="cb131-29"><a href="#cb131-29" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> grafana-data:/var/lib/grafana</span></span>
<span id="cb131-30"><a href="#cb131-30" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> ./config/grafana:/etc/grafana/provisioning</span></span>
<span id="cb131-31"><a href="#cb131-31" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">ports</span><span class="kw">:</span></span>
<span id="cb131-32"><a href="#cb131-32" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="st">&quot;3000:3000&quot;</span></span>
<span id="cb131-33"><a href="#cb131-33" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">networks</span><span class="kw">:</span></span>
<span id="cb131-34"><a href="#cb131-34" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> water-network</span></span>
<span id="cb131-35"><a href="#cb131-35" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb131-36"><a href="#cb131-36" aria-hidden="true" tabindex="-1"></a><span class="fu">volumes</span><span class="kw">:</span></span>
<span id="cb131-37"><a href="#cb131-37" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">prometheus-data</span><span class="kw">:</span></span>
<span id="cb131-38"><a href="#cb131-38" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">grafana-data</span><span class="kw">:</span></span></code></pre></div>
<h5 data-number="6.0.0.0.70" id="自动化部署脚本"><span
class="header-section-number">6.0.0.0.70</span> 8.3 自动化部署脚本</h5>
<h5 data-number="6.0.0.0.71" id="docker部署脚本"><span
class="header-section-number">6.0.0.0.71</span> 8.3.1
Docker部署脚本</h5>
<div class="sourceCode" id="cb132"><pre
class="sourceCode bash"><code class="sourceCode bash"><span id="cb132-1"><a href="#cb132-1" aria-hidden="true" tabindex="-1"></a><span class="co">#!/bin/bash</span></span>
<span id="cb132-2"><a href="#cb132-2" aria-hidden="true" tabindex="-1"></a><span class="co">##### deploy-docker.sh</span></span>
<span id="cb132-3"><a href="#cb132-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb132-4"><a href="#cb132-4" aria-hidden="true" tabindex="-1"></a><span class="bu">set</span> <span class="at">-e</span></span>
<span id="cb132-5"><a href="#cb132-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb132-6"><a href="#cb132-6" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;=== 福建水务营收系统 Docker 部署脚本 ===&quot;</span></span>
<span id="cb132-7"><a href="#cb132-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb132-8"><a href="#cb132-8" aria-hidden="true" tabindex="-1"></a><span class="co">##### 检查Docker环境</span></span>
<span id="cb132-9"><a href="#cb132-9" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="ot">! </span><span class="bu">command</span> <span class="at">-v</span> docker <span class="op">&amp;&gt;</span> /dev/null<span class="kw">;</span> <span class="cf">then</span></span>
<span id="cb132-10"><a href="#cb132-10" aria-hidden="true" tabindex="-1"></a> <span class="bu">echo</span> <span class="st">&quot;错误: Docker未安装请先安装Docker&quot;</span></span>
<span id="cb132-11"><a href="#cb132-11" aria-hidden="true" tabindex="-1"></a> <span class="bu">exit</span> 1</span>
<span id="cb132-12"><a href="#cb132-12" aria-hidden="true" tabindex="-1"></a><span class="cf">fi</span></span>
<span id="cb132-13"><a href="#cb132-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb132-14"><a href="#cb132-14" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="ot">! </span><span class="bu">command</span> <span class="at">-v</span> docker-compose <span class="op">&amp;&gt;</span> /dev/null<span class="kw">;</span> <span class="cf">then</span></span>
<span id="cb132-15"><a href="#cb132-15" aria-hidden="true" tabindex="-1"></a> <span class="bu">echo</span> <span class="st">&quot;错误: Docker Compose未安装请先安装Docker Compose&quot;</span></span>
<span id="cb132-16"><a href="#cb132-16" aria-hidden="true" tabindex="-1"></a> <span class="bu">exit</span> 1</span>
<span id="cb132-17"><a href="#cb132-17" aria-hidden="true" tabindex="-1"></a><span class="cf">fi</span></span>
<span id="cb132-18"><a href="#cb132-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb132-19"><a href="#cb132-19" aria-hidden="true" tabindex="-1"></a><span class="co">##### 创建必要目录</span></span>
<span id="cb132-20"><a href="#cb132-20" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;创建数据目录...&quot;</span></span>
<span id="cb132-21"><a href="#cb132-21" aria-hidden="true" tabindex="-1"></a><span class="fu">mkdir</span> <span class="at">-p</span> data/<span class="dt">{opengauss</span><span class="op">,</span><span class="dt">redis</span><span class="op">,</span><span class="dt">minio}</span></span>
<span id="cb132-22"><a href="#cb132-22" aria-hidden="true" tabindex="-1"></a><span class="fu">mkdir</span> <span class="at">-p</span> logs/<span class="dt">{app</span><span class="op">,</span><span class="dt">nginx}</span></span>
<span id="cb132-23"><a href="#cb132-23" aria-hidden="true" tabindex="-1"></a><span class="fu">mkdir</span> <span class="at">-p</span> config/<span class="dt">{opengauss</span><span class="op">,</span><span class="dt">redis</span><span class="op">,</span><span class="dt">nginx/conf.d}</span></span>
<span id="cb132-24"><a href="#cb132-24" aria-hidden="true" tabindex="-1"></a><span class="fu">mkdir</span> <span class="at">-p</span> upload</span>
<span id="cb132-25"><a href="#cb132-25" aria-hidden="true" tabindex="-1"></a><span class="fu">mkdir</span> <span class="at">-p</span> ssl</span>
<span id="cb132-26"><a href="#cb132-26" aria-hidden="true" tabindex="-1"></a><span class="fu">mkdir</span> <span class="at">-p</span> backups</span>
<span id="cb132-27"><a href="#cb132-27" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb132-28"><a href="#cb132-28" aria-hidden="true" tabindex="-1"></a><span class="co">##### 设置OpenGauss配置</span></span>
<span id="cb132-29"><a href="#cb132-29" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;配置OpenGauss...&quot;</span></span>
<span id="cb132-30"><a href="#cb132-30" aria-hidden="true" tabindex="-1"></a><span class="fu">cat</span> <span class="op">&gt;</span> config/opengauss/postgresql.conf <span class="op">&lt;&lt; EOF</span></span>
<span id="cb132-31"><a href="#cb132-31" aria-hidden="true" tabindex="-1"></a><span class="st">##### 数据库连接配置</span></span>
<span id="cb132-32"><a href="#cb132-32" aria-hidden="true" tabindex="-1"></a><span class="st">max_connections = 1000</span></span>
<span id="cb132-33"><a href="#cb132-33" aria-hidden="true" tabindex="-1"></a><span class="st">port = 5432</span></span>
<span id="cb132-34"><a href="#cb132-34" aria-hidden="true" tabindex="-1"></a><span class="st">listen_addresses = &#39;*&#39;</span></span>
<span id="cb132-35"><a href="#cb132-35" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb132-36"><a href="#cb132-36" aria-hidden="true" tabindex="-1"></a><span class="st">##### 内存配置</span></span>
<span id="cb132-37"><a href="#cb132-37" aria-hidden="true" tabindex="-1"></a><span class="st">shared_buffers = 512MB</span></span>
<span id="cb132-38"><a href="#cb132-38" aria-hidden="true" tabindex="-1"></a><span class="st">work_mem = 4MB</span></span>
<span id="cb132-39"><a href="#cb132-39" aria-hidden="true" tabindex="-1"></a><span class="st">maintenance_work_mem = 128MB</span></span>
<span id="cb132-40"><a href="#cb132-40" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb132-41"><a href="#cb132-41" aria-hidden="true" tabindex="-1"></a><span class="st">##### WAL配置</span></span>
<span id="cb132-42"><a href="#cb132-42" aria-hidden="true" tabindex="-1"></a><span class="st">wal_level = replica</span></span>
<span id="cb132-43"><a href="#cb132-43" aria-hidden="true" tabindex="-1"></a><span class="st">max_wal_size = 2GB</span></span>
<span id="cb132-44"><a href="#cb132-44" aria-hidden="true" tabindex="-1"></a><span class="st">min_wal_size = 128MB</span></span>
<span id="cb132-45"><a href="#cb132-45" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb132-46"><a href="#cb132-46" aria-hidden="true" tabindex="-1"></a><span class="st">##### 日志配置</span></span>
<span id="cb132-47"><a href="#cb132-47" aria-hidden="true" tabindex="-1"></a><span class="st">log_destination = &#39;stderr&#39;</span></span>
<span id="cb132-48"><a href="#cb132-48" aria-hidden="true" tabindex="-1"></a><span class="st">logging_collector = on</span></span>
<span id="cb132-49"><a href="#cb132-49" aria-hidden="true" tabindex="-1"></a><span class="st">log_directory = &#39;pg_log&#39;</span></span>
<span id="cb132-50"><a href="#cb132-50" aria-hidden="true" tabindex="-1"></a><span class="st">log_filename = &#39;postgresql-%Y-%m-%d_%H%M%S.log&#39;</span></span>
<span id="cb132-51"><a href="#cb132-51" aria-hidden="true" tabindex="-1"></a><span class="st">log_min_duration_statement = 3000</span></span>
<span id="cb132-52"><a href="#cb132-52" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb132-53"><a href="#cb132-53" aria-hidden="true" tabindex="-1"></a><span class="st">##### 性能优化</span></span>
<span id="cb132-54"><a href="#cb132-54" aria-hidden="true" tabindex="-1"></a><span class="st">effective_cache_size = 1GB</span></span>
<span id="cb132-55"><a href="#cb132-55" aria-hidden="true" tabindex="-1"></a><span class="st">random_page_cost = 1.1</span></span>
<span id="cb132-56"><a href="#cb132-56" aria-hidden="true" tabindex="-1"></a><span class="st">seq_page_cost = 1.0</span></span>
<span id="cb132-57"><a href="#cb132-57" aria-hidden="true" tabindex="-1"></a><span class="op">EOF</span></span>
<span id="cb132-58"><a href="#cb132-58" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb132-59"><a href="#cb132-59" aria-hidden="true" tabindex="-1"></a><span class="fu">cat</span> <span class="op">&gt;</span> config/opengauss/pg_hba.conf <span class="op">&lt;&lt; EOF</span></span>
<span id="cb132-60"><a href="#cb132-60" aria-hidden="true" tabindex="-1"></a><span class="st">##### TYPE DATABASE USER ADDRESS METHOD</span></span>
<span id="cb132-61"><a href="#cb132-61" aria-hidden="true" tabindex="-1"></a><span class="st">local all all trust</span></span>
<span id="cb132-62"><a href="#cb132-62" aria-hidden="true" tabindex="-1"></a><span class="st">host all all 127.0.0.1/32 md5</span></span>
<span id="cb132-63"><a href="#cb132-63" aria-hidden="true" tabindex="-1"></a><span class="st">host all all ::1/128 md5</span></span>
<span id="cb132-64"><a href="#cb132-64" aria-hidden="true" tabindex="-1"></a><span class="st">host all all 0.0.0.0/0 md5</span></span>
<span id="cb132-65"><a href="#cb132-65" aria-hidden="true" tabindex="-1"></a><span class="op">EOF</span></span>
<span id="cb132-66"><a href="#cb132-66" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb132-67"><a href="#cb132-67" aria-hidden="true" tabindex="-1"></a><span class="co">##### 设置Redis配置</span></span>
<span id="cb132-68"><a href="#cb132-68" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;配置Redis...&quot;</span></span>
<span id="cb132-69"><a href="#cb132-69" aria-hidden="true" tabindex="-1"></a><span class="fu">cat</span> <span class="op">&gt;</span> config/redis/redis.conf <span class="op">&lt;&lt; EOF</span></span>
<span id="cb132-70"><a href="#cb132-70" aria-hidden="true" tabindex="-1"></a><span class="st">port 6379</span></span>
<span id="cb132-71"><a href="#cb132-71" aria-hidden="true" tabindex="-1"></a><span class="st">requirepass water_redis_2024</span></span>
<span id="cb132-72"><a href="#cb132-72" aria-hidden="true" tabindex="-1"></a><span class="st">timeout 300</span></span>
<span id="cb132-73"><a href="#cb132-73" aria-hidden="true" tabindex="-1"></a><span class="st">tcp-keepalive 300</span></span>
<span id="cb132-74"><a href="#cb132-74" aria-hidden="true" tabindex="-1"></a><span class="st">maxmemory 256mb</span></span>
<span id="cb132-75"><a href="#cb132-75" aria-hidden="true" tabindex="-1"></a><span class="st">maxmemory-policy allkeys-lru</span></span>
<span id="cb132-76"><a href="#cb132-76" aria-hidden="true" tabindex="-1"></a><span class="st">save 900 1</span></span>
<span id="cb132-77"><a href="#cb132-77" aria-hidden="true" tabindex="-1"></a><span class="st">save 300 10</span></span>
<span id="cb132-78"><a href="#cb132-78" aria-hidden="true" tabindex="-1"></a><span class="st">save 60 10000</span></span>
<span id="cb132-79"><a href="#cb132-79" aria-hidden="true" tabindex="-1"></a><span class="op">EOF</span></span>
<span id="cb132-80"><a href="#cb132-80" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb132-81"><a href="#cb132-81" aria-hidden="true" tabindex="-1"></a><span class="co">##### 设置Nginx配置</span></span>
<span id="cb132-82"><a href="#cb132-82" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;配置Nginx...&quot;</span></span>
<span id="cb132-83"><a href="#cb132-83" aria-hidden="true" tabindex="-1"></a><span class="fu">cat</span> <span class="op">&gt;</span> config/nginx/nginx.conf <span class="op">&lt;&lt; EOF</span></span>
<span id="cb132-84"><a href="#cb132-84" aria-hidden="true" tabindex="-1"></a><span class="st">user nginx;</span></span>
<span id="cb132-85"><a href="#cb132-85" aria-hidden="true" tabindex="-1"></a><span class="st">worker_processes auto;</span></span>
<span id="cb132-86"><a href="#cb132-86" aria-hidden="true" tabindex="-1"></a><span class="st">error_log /var/log/nginx/error.log warn;</span></span>
<span id="cb132-87"><a href="#cb132-87" aria-hidden="true" tabindex="-1"></a><span class="st">pid /var/run/nginx.pid;</span></span>
<span id="cb132-88"><a href="#cb132-88" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb132-89"><a href="#cb132-89" aria-hidden="true" tabindex="-1"></a><span class="st">events {</span></span>
<span id="cb132-90"><a href="#cb132-90" aria-hidden="true" tabindex="-1"></a><span class="st"> worker_connections 1024;</span></span>
<span id="cb132-91"><a href="#cb132-91" aria-hidden="true" tabindex="-1"></a><span class="st"> use epoll;</span></span>
<span id="cb132-92"><a href="#cb132-92" aria-hidden="true" tabindex="-1"></a><span class="st"> multi_accept on;</span></span>
<span id="cb132-93"><a href="#cb132-93" aria-hidden="true" tabindex="-1"></a><span class="st">}</span></span>
<span id="cb132-94"><a href="#cb132-94" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb132-95"><a href="#cb132-95" aria-hidden="true" tabindex="-1"></a><span class="st">http {</span></span>
<span id="cb132-96"><a href="#cb132-96" aria-hidden="true" tabindex="-1"></a><span class="st"> include /etc/nginx/mime.types;</span></span>
<span id="cb132-97"><a href="#cb132-97" aria-hidden="true" tabindex="-1"></a><span class="st"> default_type application/octet-stream;</span></span>
<span id="cb132-98"><a href="#cb132-98" aria-hidden="true" tabindex="-1"></a><span class="st"> </span></span>
<span id="cb132-99"><a href="#cb132-99" aria-hidden="true" tabindex="-1"></a><span class="st"> log_format main &#39;</span><span class="dt">\$</span><span class="st">remote_addr - </span><span class="dt">\$</span><span class="st">remote_user [</span><span class="dt">\$</span><span class="st">time_local] &quot;</span><span class="dt">\$</span><span class="st">request&quot; &#39;</span></span>
<span id="cb132-100"><a href="#cb132-100" aria-hidden="true" tabindex="-1"></a><span class="st"> &#39;</span><span class="dt">\$</span><span class="st">status </span><span class="dt">\$</span><span class="st">body_bytes_sent &quot;</span><span class="dt">\$</span><span class="st">http_referer&quot; &#39;</span></span>
<span id="cb132-101"><a href="#cb132-101" aria-hidden="true" tabindex="-1"></a><span class="st"> &#39;&quot;</span><span class="dt">\$</span><span class="st">http_user_agent&quot; &quot;</span><span class="dt">\$</span><span class="st">http_x_forwarded_for&quot;&#39;;</span></span>
<span id="cb132-102"><a href="#cb132-102" aria-hidden="true" tabindex="-1"></a><span class="st"> </span></span>
<span id="cb132-103"><a href="#cb132-103" aria-hidden="true" tabindex="-1"></a><span class="st"> access_log /var/log/nginx/access.log main;</span></span>
<span id="cb132-104"><a href="#cb132-104" aria-hidden="true" tabindex="-1"></a><span class="st"> </span></span>
<span id="cb132-105"><a href="#cb132-105" aria-hidden="true" tabindex="-1"></a><span class="st"> sendfile on;</span></span>
<span id="cb132-106"><a href="#cb132-106" aria-hidden="true" tabindex="-1"></a><span class="st"> tcp_nopush on;</span></span>
<span id="cb132-107"><a href="#cb132-107" aria-hidden="true" tabindex="-1"></a><span class="st"> tcp_nodelay on;</span></span>
<span id="cb132-108"><a href="#cb132-108" aria-hidden="true" tabindex="-1"></a><span class="st"> keepalive_timeout 65;</span></span>
<span id="cb132-109"><a href="#cb132-109" aria-hidden="true" tabindex="-1"></a><span class="st"> types_hash_max_size 2048;</span></span>
<span id="cb132-110"><a href="#cb132-110" aria-hidden="true" tabindex="-1"></a><span class="st"> client_max_body_size 50M;</span></span>
<span id="cb132-111"><a href="#cb132-111" aria-hidden="true" tabindex="-1"></a><span class="st"> </span></span>
<span id="cb132-112"><a href="#cb132-112" aria-hidden="true" tabindex="-1"></a><span class="st"> gzip on;</span></span>
<span id="cb132-113"><a href="#cb132-113" aria-hidden="true" tabindex="-1"></a><span class="st"> gzip_vary on;</span></span>
<span id="cb132-114"><a href="#cb132-114" aria-hidden="true" tabindex="-1"></a><span class="st"> gzip_min_length 1024;</span></span>
<span id="cb132-115"><a href="#cb132-115" aria-hidden="true" tabindex="-1"></a><span class="st"> gzip_types text/plain text/css text/xml text/javascript </span></span>
<span id="cb132-116"><a href="#cb132-116" aria-hidden="true" tabindex="-1"></a><span class="st"> application/javascript application/xml+rss </span></span>
<span id="cb132-117"><a href="#cb132-117" aria-hidden="true" tabindex="-1"></a><span class="st"> application/json;</span></span>
<span id="cb132-118"><a href="#cb132-118" aria-hidden="true" tabindex="-1"></a><span class="st"> </span></span>
<span id="cb132-119"><a href="#cb132-119" aria-hidden="true" tabindex="-1"></a><span class="st"> include /etc/nginx/conf.d/*.conf;</span></span>
<span id="cb132-120"><a href="#cb132-120" aria-hidden="true" tabindex="-1"></a><span class="st">}</span></span>
<span id="cb132-121"><a href="#cb132-121" aria-hidden="true" tabindex="-1"></a><span class="op">EOF</span></span>
<span id="cb132-122"><a href="#cb132-122" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb132-123"><a href="#cb132-123" aria-hidden="true" tabindex="-1"></a><span class="fu">cat</span> <span class="op">&gt;</span> config/nginx/conf.d/water.conf <span class="op">&lt;&lt; EOF</span></span>
<span id="cb132-124"><a href="#cb132-124" aria-hidden="true" tabindex="-1"></a><span class="st">upstream water_backend {</span></span>
<span id="cb132-125"><a href="#cb132-125" aria-hidden="true" tabindex="-1"></a><span class="st"> server water-server:8080 max_fails=3 fail_timeout=30s;</span></span>
<span id="cb132-126"><a href="#cb132-126" aria-hidden="true" tabindex="-1"></a><span class="st"> keepalive 32;</span></span>
<span id="cb132-127"><a href="#cb132-127" aria-hidden="true" tabindex="-1"></a><span class="st">}</span></span>
<span id="cb132-128"><a href="#cb132-128" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb132-129"><a href="#cb132-129" aria-hidden="true" tabindex="-1"></a><span class="st">server {</span></span>
<span id="cb132-130"><a href="#cb132-130" aria-hidden="true" tabindex="-1"></a><span class="st"> listen 80;</span></span>
<span id="cb132-131"><a href="#cb132-131" aria-hidden="true" tabindex="-1"></a><span class="st"> server_name localhost;</span></span>
<span id="cb132-132"><a href="#cb132-132" aria-hidden="true" tabindex="-1"></a><span class="st"> </span></span>
<span id="cb132-133"><a href="#cb132-133" aria-hidden="true" tabindex="-1"></a><span class="st"> location /admin-api/ {</span></span>
<span id="cb132-134"><a href="#cb132-134" aria-hidden="true" tabindex="-1"></a><span class="st"> proxy_pass http://water_backend;</span></span>
<span id="cb132-135"><a href="#cb132-135" aria-hidden="true" tabindex="-1"></a><span class="st"> proxy_set_header Host </span><span class="dt">\$</span><span class="st">host;</span></span>
<span id="cb132-136"><a href="#cb132-136" aria-hidden="true" tabindex="-1"></a><span class="st"> proxy_set_header X-Real-IP </span><span class="dt">\$</span><span class="st">remote_addr;</span></span>
<span id="cb132-137"><a href="#cb132-137" aria-hidden="true" tabindex="-1"></a><span class="st"> proxy_set_header X-Forwarded-For </span><span class="dt">\$</span><span class="st">proxy_add_x_forwarded_for;</span></span>
<span id="cb132-138"><a href="#cb132-138" aria-hidden="true" tabindex="-1"></a><span class="st"> proxy_set_header X-Forwarded-Proto </span><span class="dt">\$</span><span class="st">scheme;</span></span>
<span id="cb132-139"><a href="#cb132-139" aria-hidden="true" tabindex="-1"></a><span class="st"> proxy_connect_timeout 600;</span></span>
<span id="cb132-140"><a href="#cb132-140" aria-hidden="true" tabindex="-1"></a><span class="st"> proxy_send_timeout 600;</span></span>
<span id="cb132-141"><a href="#cb132-141" aria-hidden="true" tabindex="-1"></a><span class="st"> proxy_read_timeout 600;</span></span>
<span id="cb132-142"><a href="#cb132-142" aria-hidden="true" tabindex="-1"></a><span class="st"> }</span></span>
<span id="cb132-143"><a href="#cb132-143" aria-hidden="true" tabindex="-1"></a><span class="st"> </span></span>
<span id="cb132-144"><a href="#cb132-144" aria-hidden="true" tabindex="-1"></a><span class="st"> location / {</span></span>
<span id="cb132-145"><a href="#cb132-145" aria-hidden="true" tabindex="-1"></a><span class="st"> proxy_pass http://water-ui;</span></span>
<span id="cb132-146"><a href="#cb132-146" aria-hidden="true" tabindex="-1"></a><span class="st"> proxy_set_header Host </span><span class="dt">\$</span><span class="st">host;</span></span>
<span id="cb132-147"><a href="#cb132-147" aria-hidden="true" tabindex="-1"></a><span class="st"> proxy_set_header X-Real-IP </span><span class="dt">\$</span><span class="st">remote_addr;</span></span>
<span id="cb132-148"><a href="#cb132-148" aria-hidden="true" tabindex="-1"></a><span class="st"> proxy_set_header X-Forwarded-For </span><span class="dt">\$</span><span class="st">proxy_add_x_forwarded_for;</span></span>
<span id="cb132-149"><a href="#cb132-149" aria-hidden="true" tabindex="-1"></a><span class="st"> proxy_set_header X-Forwarded-Proto </span><span class="dt">\$</span><span class="st">scheme;</span></span>
<span id="cb132-150"><a href="#cb132-150" aria-hidden="true" tabindex="-1"></a><span class="st"> }</span></span>
<span id="cb132-151"><a href="#cb132-151" aria-hidden="true" tabindex="-1"></a><span class="st">}</span></span>
<span id="cb132-152"><a href="#cb132-152" aria-hidden="true" tabindex="-1"></a><span class="op">EOF</span></span>
<span id="cb132-153"><a href="#cb132-153" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb132-154"><a href="#cb132-154" aria-hidden="true" tabindex="-1"></a><span class="co">##### 构建和启动服务</span></span>
<span id="cb132-155"><a href="#cb132-155" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;构建和启动Docker服务...&quot;</span></span>
<span id="cb132-156"><a href="#cb132-156" aria-hidden="true" tabindex="-1"></a><span class="ex">docker-compose</span> down</span>
<span id="cb132-157"><a href="#cb132-157" aria-hidden="true" tabindex="-1"></a><span class="ex">docker-compose</span> build <span class="at">--no-cache</span></span>
<span id="cb132-158"><a href="#cb132-158" aria-hidden="true" tabindex="-1"></a><span class="ex">docker-compose</span> up <span class="at">-d</span></span>
<span id="cb132-159"><a href="#cb132-159" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb132-160"><a href="#cb132-160" aria-hidden="true" tabindex="-1"></a><span class="co">##### 等待服务启动</span></span>
<span id="cb132-161"><a href="#cb132-161" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;等待服务启动...&quot;</span></span>
<span id="cb132-162"><a href="#cb132-162" aria-hidden="true" tabindex="-1"></a><span class="fu">sleep</span> 30</span>
<span id="cb132-163"><a href="#cb132-163" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb132-164"><a href="#cb132-164" aria-hidden="true" tabindex="-1"></a><span class="co">##### 检查服务状态</span></span>
<span id="cb132-165"><a href="#cb132-165" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;检查服务状态...&quot;</span></span>
<span id="cb132-166"><a href="#cb132-166" aria-hidden="true" tabindex="-1"></a><span class="ex">docker-compose</span> ps</span>
<span id="cb132-167"><a href="#cb132-167" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb132-168"><a href="#cb132-168" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;=== 部署完成 ===&quot;</span></span>
<span id="cb132-169"><a href="#cb132-169" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;系统访问地址: http://localhost&quot;</span></span>
<span id="cb132-170"><a href="#cb132-170" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;API接口地址: http://localhost/admin-api&quot;</span></span>
<span id="cb132-171"><a href="#cb132-171" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;MinIO控制台: http://localhost:9001 (admin/admin123456)&quot;</span></span>
<span id="cb132-172"><a href="#cb132-172" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;&quot;</span></span>
<span id="cb132-173"><a href="#cb132-173" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;查看日志: docker-compose logs -f [服务名]&quot;</span></span>
<span id="cb132-174"><a href="#cb132-174" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;停止服务: docker-compose down&quot;</span></span>
<span id="cb132-175"><a href="#cb132-175" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;重启服务: docker-compose restart&quot;</span></span></code></pre></div>
<h5 data-number="6.0.0.0.72" id="生产环境部署脚本"><span
class="header-section-number">6.0.0.0.72</span> 8.3.2
生产环境部署脚本</h5>
<div class="sourceCode" id="cb133"><pre
class="sourceCode bash"><code class="sourceCode bash"><span id="cb133-1"><a href="#cb133-1" aria-hidden="true" tabindex="-1"></a><span class="co">#!/bin/bash</span></span>
<span id="cb133-2"><a href="#cb133-2" aria-hidden="true" tabindex="-1"></a><span class="co">##### deploy-prod.sh</span></span>
<span id="cb133-3"><a href="#cb133-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb133-4"><a href="#cb133-4" aria-hidden="true" tabindex="-1"></a><span class="bu">set</span> <span class="at">-e</span></span>
<span id="cb133-5"><a href="#cb133-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb133-6"><a href="#cb133-6" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;=== 福建水务营收系统 生产环境部署脚本 ===&quot;</span></span>
<span id="cb133-7"><a href="#cb133-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb133-8"><a href="#cb133-8" aria-hidden="true" tabindex="-1"></a><span class="co">##### 检查环境</span></span>
<span id="cb133-9"><a href="#cb133-9" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="ot">! </span><span class="bu">command</span> <span class="at">-v</span> docker <span class="op">&amp;&gt;</span> /dev/null<span class="kw">;</span> <span class="cf">then</span></span>
<span id="cb133-10"><a href="#cb133-10" aria-hidden="true" tabindex="-1"></a> <span class="bu">echo</span> <span class="st">&quot;错误: Docker未安装请先安装Docker&quot;</span></span>
<span id="cb133-11"><a href="#cb133-11" aria-hidden="true" tabindex="-1"></a> <span class="bu">exit</span> 1</span>
<span id="cb133-12"><a href="#cb133-12" aria-hidden="true" tabindex="-1"></a><span class="cf">fi</span></span>
<span id="cb133-13"><a href="#cb133-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb133-14"><a href="#cb133-14" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="ot">! </span><span class="bu">command</span> <span class="at">-v</span> docker-compose <span class="op">&amp;&gt;</span> /dev/null<span class="kw">;</span> <span class="cf">then</span></span>
<span id="cb133-15"><a href="#cb133-15" aria-hidden="true" tabindex="-1"></a> <span class="bu">echo</span> <span class="st">&quot;错误: Docker Compose未安装请先安装Docker Compose&quot;</span></span>
<span id="cb133-16"><a href="#cb133-16" aria-hidden="true" tabindex="-1"></a> <span class="bu">exit</span> 1</span>
<span id="cb133-17"><a href="#cb133-17" aria-hidden="true" tabindex="-1"></a><span class="cf">fi</span></span>
<span id="cb133-18"><a href="#cb133-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb133-19"><a href="#cb133-19" aria-hidden="true" tabindex="-1"></a><span class="co">##### 设置环境变量</span></span>
<span id="cb133-20"><a href="#cb133-20" aria-hidden="true" tabindex="-1"></a><span class="bu">export</span> <span class="va">COMPOSE_PROJECT_NAME</span><span class="op">=</span>water-system-prod</span>
<span id="cb133-21"><a href="#cb133-21" aria-hidden="true" tabindex="-1"></a><span class="bu">export</span> <span class="va">VERSION</span><span class="op">=</span><span class="va">${1</span><span class="op">:-</span>latest<span class="va">}</span></span>
<span id="cb133-22"><a href="#cb133-22" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb133-23"><a href="#cb133-23" aria-hidden="true" tabindex="-1"></a><span class="co">##### 创建生产环境目录</span></span>
<span id="cb133-24"><a href="#cb133-24" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;创建生产环境目录...&quot;</span></span>
<span id="cb133-25"><a href="#cb133-25" aria-hidden="true" tabindex="-1"></a><span class="fu">mkdir</span> <span class="at">-p</span> <span class="dt">{data</span><span class="op">,</span><span class="dt">logs</span><span class="op">,</span><span class="dt">config</span><span class="op">,</span><span class="dt">upload</span><span class="op">,</span><span class="dt">ssl</span><span class="op">,</span><span class="dt">backups}</span>/<span class="dt">{prod</span><span class="op">,</span><span class="dt">test}</span></span>
<span id="cb133-26"><a href="#cb133-26" aria-hidden="true" tabindex="-1"></a><span class="fu">mkdir</span> <span class="at">-p</span> config/<span class="dt">{opengauss</span><span class="op">,</span><span class="dt">redis</span><span class="op">,</span><span class="dt">nginx</span><span class="op">,</span><span class="dt">app</span><span class="op">,</span><span class="dt">prometheus</span><span class="op">,</span><span class="dt">grafana}</span></span>
<span id="cb133-27"><a href="#cb133-27" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb133-28"><a href="#cb133-28" aria-hidden="true" tabindex="-1"></a><span class="co">##### 生成强密码</span></span>
<span id="cb133-29"><a href="#cb133-29" aria-hidden="true" tabindex="-1"></a><span class="va">DB_PASSWORD</span><span class="op">=</span><span class="va">$(</span><span class="ex">openssl</span> rand <span class="at">-base64</span> 32 <span class="kw">|</span> <span class="fu">tr</span> <span class="at">-d</span> <span class="st">&quot;=+/&quot;</span> <span class="kw">|</span> <span class="fu">cut</span> <span class="at">-c1-25</span><span class="va">)</span></span>
<span id="cb133-30"><a href="#cb133-30" aria-hidden="true" tabindex="-1"></a><span class="va">REDIS_PASSWORD</span><span class="op">=</span><span class="va">$(</span><span class="ex">openssl</span> rand <span class="at">-base64</span> 32 <span class="kw">|</span> <span class="fu">tr</span> <span class="at">-d</span> <span class="st">&quot;=+/&quot;</span> <span class="kw">|</span> <span class="fu">cut</span> <span class="at">-c1-25</span><span class="va">)</span></span>
<span id="cb133-31"><a href="#cb133-31" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb133-32"><a href="#cb133-32" aria-hidden="true" tabindex="-1"></a><span class="co">##### 创建环境变量文件</span></span>
<span id="cb133-33"><a href="#cb133-33" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;创建环境变量文件...&quot;</span></span>
<span id="cb133-34"><a href="#cb133-34" aria-hidden="true" tabindex="-1"></a><span class="fu">cat</span> <span class="op">&gt;</span> .env.prod <span class="op">&lt;&lt; EOF</span></span>
<span id="cb133-35"><a href="#cb133-35" aria-hidden="true" tabindex="-1"></a><span class="st">##### 数据库配置</span></span>
<span id="cb133-36"><a href="#cb133-36" aria-hidden="true" tabindex="-1"></a><span class="st">DB_PASSWORD=</span><span class="va">${DB_PASSWORD}</span></span>
<span id="cb133-37"><a href="#cb133-37" aria-hidden="true" tabindex="-1"></a><span class="st">DB_HOST=water-opengauss</span></span>
<span id="cb133-38"><a href="#cb133-38" aria-hidden="true" tabindex="-1"></a><span class="st">DB_PORT=5432</span></span>
<span id="cb133-39"><a href="#cb133-39" aria-hidden="true" tabindex="-1"></a><span class="st">DB_NAME=ruoyi_water</span></span>
<span id="cb133-40"><a href="#cb133-40" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb133-41"><a href="#cb133-41" aria-hidden="true" tabindex="-1"></a><span class="st">##### Redis配置</span></span>
<span id="cb133-42"><a href="#cb133-42" aria-hidden="true" tabindex="-1"></a><span class="st">REDIS_PASSWORD=</span><span class="va">${REDIS_PASSWORD}</span></span>
<span id="cb133-43"><a href="#cb133-43" aria-hidden="true" tabindex="-1"></a><span class="st">REDIS_HOST=water-redis</span></span>
<span id="cb133-44"><a href="#cb133-44" aria-hidden="true" tabindex="-1"></a><span class="st">REDIS_PORT=6379</span></span>
<span id="cb133-45"><a href="#cb133-45" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb133-46"><a href="#cb133-46" aria-hidden="true" tabindex="-1"></a><span class="st">##### 应用配置</span></span>
<span id="cb133-47"><a href="#cb133-47" aria-hidden="true" tabindex="-1"></a><span class="st">VERSION=</span><span class="va">${VERSION}</span></span>
<span id="cb133-48"><a href="#cb133-48" aria-hidden="true" tabindex="-1"></a><span class="st">JAVA_OPTS=-Xmx2g -Xms1g -XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError</span></span>
<span id="cb133-49"><a href="#cb133-49" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb133-50"><a href="#cb133-50" aria-hidden="true" tabindex="-1"></a><span class="st">##### 网络配置</span></span>
<span id="cb133-51"><a href="#cb133-51" aria-hidden="true" tabindex="-1"></a><span class="st">NGINX_PORT=80</span></span>
<span id="cb133-52"><a href="#cb133-52" aria-hidden="true" tabindex="-1"></a><span class="st">NGINX_SSL_PORT=443</span></span>
<span id="cb133-53"><a href="#cb133-53" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb133-54"><a href="#cb133-54" aria-hidden="true" tabindex="-1"></a><span class="st">##### 日志级别</span></span>
<span id="cb133-55"><a href="#cb133-55" aria-hidden="true" tabindex="-1"></a><span class="st">LOG_LEVEL=INFO</span></span>
<span id="cb133-56"><a href="#cb133-56" aria-hidden="true" tabindex="-1"></a><span class="st">LOG_ROOT_LEVEL=WARN</span></span>
<span id="cb133-57"><a href="#cb133-57" aria-hidden="true" tabindex="-1"></a><span class="op">EOF</span></span>
<span id="cb133-58"><a href="#cb133-58" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb133-59"><a href="#cb133-59" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;数据库密码: </span><span class="va">${DB_PASSWORD}</span><span class="st">&quot;</span></span>
<span id="cb133-60"><a href="#cb133-60" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;Redis密码: </span><span class="va">${REDIS_PASSWORD}</span><span class="st">&quot;</span></span>
<span id="cb133-61"><a href="#cb133-61" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;请妥善保存以上密码信息!&quot;</span></span>
<span id="cb133-62"><a href="#cb133-62" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb133-63"><a href="#cb133-63" aria-hidden="true" tabindex="-1"></a><span class="co">##### 创建SSL证书自签名生产环境应使用正式证书</span></span>
<span id="cb133-64"><a href="#cb133-64" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;创建SSL证书...&quot;</span></span>
<span id="cb133-65"><a href="#cb133-65" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="bu">[</span> <span class="ot">!</span> <span class="ot">-f</span> ssl/water-system.crt <span class="bu">]</span><span class="kw">;</span> <span class="cf">then</span></span>
<span id="cb133-66"><a href="#cb133-66" aria-hidden="true" tabindex="-1"></a> <span class="ex">openssl</span> req <span class="at">-x509</span> <span class="at">-nodes</span> <span class="at">-days</span> 365 <span class="at">-newkey</span> rsa:2048 <span class="dt">\</span></span>
<span id="cb133-67"><a href="#cb133-67" aria-hidden="true" tabindex="-1"></a> <span class="at">-keyout</span> ssl/water-system.key <span class="dt">\</span></span>
<span id="cb133-68"><a href="#cb133-68" aria-hidden="true" tabindex="-1"></a> <span class="at">-out</span> ssl/water-system.crt <span class="dt">\</span></span>
<span id="cb133-69"><a href="#cb133-69" aria-hidden="true" tabindex="-1"></a> <span class="at">-subj</span> <span class="st">&quot;/C=CN/ST=Fujian/L=Fuzhou/O=Water/CN=water.local&quot;</span></span>
<span id="cb133-70"><a href="#cb133-70" aria-hidden="true" tabindex="-1"></a><span class="cf">fi</span></span>
<span id="cb133-71"><a href="#cb133-71" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb133-72"><a href="#cb133-72" aria-hidden="true" tabindex="-1"></a><span class="co">##### 构建应用镜像</span></span>
<span id="cb133-73"><a href="#cb133-73" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;构建应用镜像...&quot;</span></span>
<span id="cb133-74"><a href="#cb133-74" aria-hidden="true" tabindex="-1"></a><span class="ex">docker</span> build <span class="at">-t</span> water-server:<span class="va">${VERSION}</span> ./water-server</span>
<span id="cb133-75"><a href="#cb133-75" aria-hidden="true" tabindex="-1"></a><span class="ex">docker</span> build <span class="at">-t</span> water-ui:<span class="va">${VERSION}</span> ./water-ui</span>
<span id="cb133-76"><a href="#cb133-76" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb133-77"><a href="#cb133-77" aria-hidden="true" tabindex="-1"></a><span class="co">##### 停止现有服务</span></span>
<span id="cb133-78"><a href="#cb133-78" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;停止现有服务...&quot;</span></span>
<span id="cb133-79"><a href="#cb133-79" aria-hidden="true" tabindex="-1"></a><span class="ex">docker-compose</span> <span class="at">--env-file</span> .env.prod <span class="at">-f</span> docker-compose.prod.yml down</span>
<span id="cb133-80"><a href="#cb133-80" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb133-81"><a href="#cb133-81" aria-hidden="true" tabindex="-1"></a><span class="co">##### 启动生产服务</span></span>
<span id="cb133-82"><a href="#cb133-82" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;启动生产服务...&quot;</span></span>
<span id="cb133-83"><a href="#cb133-83" aria-hidden="true" tabindex="-1"></a><span class="ex">docker-compose</span> <span class="at">--env-file</span> .env.prod <span class="at">-f</span> docker-compose.prod.yml up <span class="at">-d</span></span>
<span id="cb133-84"><a href="#cb133-84" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb133-85"><a href="#cb133-85" aria-hidden="true" tabindex="-1"></a><span class="co">##### 等待服务启动</span></span>
<span id="cb133-86"><a href="#cb133-86" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;等待服务启动...&quot;</span></span>
<span id="cb133-87"><a href="#cb133-87" aria-hidden="true" tabindex="-1"></a><span class="fu">sleep</span> 60</span>
<span id="cb133-88"><a href="#cb133-88" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb133-89"><a href="#cb133-89" aria-hidden="true" tabindex="-1"></a><span class="co">##### 检查服务状态</span></span>
<span id="cb133-90"><a href="#cb133-90" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;检查服务状态...&quot;</span></span>
<span id="cb133-91"><a href="#cb133-91" aria-hidden="true" tabindex="-1"></a><span class="ex">docker-compose</span> <span class="at">--env-file</span> .env.prod <span class="at">-f</span> docker-compose.prod.yml ps</span>
<span id="cb133-92"><a href="#cb133-92" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb133-93"><a href="#cb133-93" aria-hidden="true" tabindex="-1"></a><span class="co">##### 健康检查</span></span>
<span id="cb133-94"><a href="#cb133-94" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;执行健康检查...&quot;</span></span>
<span id="cb133-95"><a href="#cb133-95" aria-hidden="true" tabindex="-1"></a><span class="cf">for</span> i <span class="kw">in</span> <span class="dt">{</span><span class="dv">1</span><span class="dt">..</span><span class="dv">10</span><span class="dt">}</span><span class="kw">;</span> <span class="cf">do</span></span>
<span id="cb133-96"><a href="#cb133-96" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="ex">curl</span> <span class="at">-f</span> http://localhost/actuator/health <span class="op">&gt;</span>/dev/null <span class="dv">2</span><span class="op">&gt;&amp;</span><span class="dv">1</span><span class="kw">;</span> <span class="cf">then</span></span>
<span id="cb133-97"><a href="#cb133-97" aria-hidden="true" tabindex="-1"></a> <span class="bu">echo</span> <span class="st">&quot;应用服务健康检查通过&quot;</span></span>
<span id="cb133-98"><a href="#cb133-98" aria-hidden="true" tabindex="-1"></a> <span class="cf">break</span></span>
<span id="cb133-99"><a href="#cb133-99" aria-hidden="true" tabindex="-1"></a> <span class="cf">else</span></span>
<span id="cb133-100"><a href="#cb133-100" aria-hidden="true" tabindex="-1"></a> <span class="bu">echo</span> <span class="st">&quot;等待应用服务启动... (</span><span class="va">$i</span><span class="st">/10)&quot;</span></span>
<span id="cb133-101"><a href="#cb133-101" aria-hidden="true" tabindex="-1"></a> <span class="fu">sleep</span> 30</span>
<span id="cb133-102"><a href="#cb133-102" aria-hidden="true" tabindex="-1"></a> <span class="cf">fi</span></span>
<span id="cb133-103"><a href="#cb133-103" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb133-104"><a href="#cb133-104" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="bu">[</span> <span class="va">$i</span> <span class="ot">-eq</span> 10 <span class="bu">]</span><span class="kw">;</span> <span class="cf">then</span></span>
<span id="cb133-105"><a href="#cb133-105" aria-hidden="true" tabindex="-1"></a> <span class="bu">echo</span> <span class="st">&quot;警告: 应用服务健康检查失败&quot;</span></span>
<span id="cb133-106"><a href="#cb133-106" aria-hidden="true" tabindex="-1"></a> <span class="ex">docker-compose</span> <span class="at">--env-file</span> .env.prod <span class="at">-f</span> docker-compose.prod.yml logs water-server</span>
<span id="cb133-107"><a href="#cb133-107" aria-hidden="true" tabindex="-1"></a> <span class="cf">fi</span></span>
<span id="cb133-108"><a href="#cb133-108" aria-hidden="true" tabindex="-1"></a><span class="cf">done</span></span>
<span id="cb133-109"><a href="#cb133-109" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb133-110"><a href="#cb133-110" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;=== 生产环境部署完成 ===&quot;</span></span>
<span id="cb133-111"><a href="#cb133-111" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;系统访问地址: https://localhost&quot;</span></span>
<span id="cb133-112"><a href="#cb133-112" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;系统监控地址: http://localhost:3000 (admin/admin123)&quot;</span></span>
<span id="cb133-113"><a href="#cb133-113" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;数据库端口: 5432&quot;</span></span>
<span id="cb133-114"><a href="#cb133-114" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;Redis端口: 6379&quot;</span></span>
<span id="cb133-115"><a href="#cb133-115" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;&quot;</span></span>
<span id="cb133-116"><a href="#cb133-116" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;管理命令:&quot;</span></span>
<span id="cb133-117"><a href="#cb133-117" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot; 查看日志: docker-compose --env-file .env.prod -f docker-compose.prod.yml logs -f [服务名]&quot;</span></span>
<span id="cb133-118"><a href="#cb133-118" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot; 停止服务: docker-compose --env-file .env.prod -f docker-compose.prod.yml down&quot;</span></span>
<span id="cb133-119"><a href="#cb133-119" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot; 重启服务: docker-compose --env-file .env.prod -f docker-compose.prod.yml restart [服务名]&quot;</span></span>
<span id="cb133-120"><a href="#cb133-120" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot; 备份数据: docker exec water-opengauss-prod gs_dump -h localhost -U water_user ruoyi_water &gt; ./backups/backup-</span><span class="dt">\$</span><span class="st">(date +%Y%m%d_%H%M%S).sql&quot;</span></span></code></pre></div>
<h5 data-number="6.0.0.0.73" id="持续集成持续部署-cicd"><span
class="header-section-number">6.0.0.0.73</span> 8.4 持续集成/持续部署
(CI/CD)</h5>
<h5 data-number="6.0.0.0.74" id="github-actions配置"><span
class="header-section-number">6.0.0.0.74</span> 8.4.1 GitHub
Actions配置</h5>
<div class="sourceCode" id="cb134"><pre
class="sourceCode yaml"><code class="sourceCode yaml"><span id="cb134-1"><a href="#cb134-1" aria-hidden="true" tabindex="-1"></a><span class="co">##### .github/workflows/deploy.yml</span></span>
<span id="cb134-2"><a href="#cb134-2" aria-hidden="true" tabindex="-1"></a><span class="fu">name</span><span class="kw">:</span><span class="at"> Build and Deploy Water System</span></span>
<span id="cb134-3"><a href="#cb134-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb134-4"><a href="#cb134-4" aria-hidden="true" tabindex="-1"></a><span class="fu">on</span><span class="kw">:</span></span>
<span id="cb134-5"><a href="#cb134-5" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">push</span><span class="kw">:</span></span>
<span id="cb134-6"><a href="#cb134-6" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">branches</span><span class="kw">:</span><span class="at"> </span><span class="kw">[</span><span class="at"> main</span><span class="kw">,</span><span class="at"> develop </span><span class="kw">]</span></span>
<span id="cb134-7"><a href="#cb134-7" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">pull_request</span><span class="kw">:</span></span>
<span id="cb134-8"><a href="#cb134-8" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">branches</span><span class="kw">:</span><span class="at"> </span><span class="kw">[</span><span class="at"> main </span><span class="kw">]</span></span>
<span id="cb134-9"><a href="#cb134-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb134-10"><a href="#cb134-10" aria-hidden="true" tabindex="-1"></a><span class="fu">env</span><span class="kw">:</span></span>
<span id="cb134-11"><a href="#cb134-11" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">REGISTRY</span><span class="kw">:</span><span class="at"> docker.io</span></span>
<span id="cb134-12"><a href="#cb134-12" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">IMAGE_NAME</span><span class="kw">:</span><span class="at"> water-system</span></span>
<span id="cb134-13"><a href="#cb134-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb134-14"><a href="#cb134-14" aria-hidden="true" tabindex="-1"></a><span class="fu">jobs</span><span class="kw">:</span></span>
<span id="cb134-15"><a href="#cb134-15" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">test</span><span class="kw">:</span></span>
<span id="cb134-16"><a href="#cb134-16" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">runs-on</span><span class="kw">:</span><span class="at"> ubuntu-latest</span></span>
<span id="cb134-17"><a href="#cb134-17" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">steps</span><span class="kw">:</span></span>
<span id="cb134-18"><a href="#cb134-18" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="fu">uses</span><span class="kw">:</span><span class="at"> actions/checkout@v3</span></span>
<span id="cb134-19"><a href="#cb134-19" aria-hidden="true" tabindex="-1"></a><span class="at"> </span></span>
<span id="cb134-20"><a href="#cb134-20" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="fu">name</span><span class="kw">:</span><span class="at"> Set up JDK 17</span></span>
<span id="cb134-21"><a href="#cb134-21" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">uses</span><span class="kw">:</span><span class="at"> actions/setup-java@v3</span></span>
<span id="cb134-22"><a href="#cb134-22" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">with</span><span class="kw">:</span></span>
<span id="cb134-23"><a href="#cb134-23" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">java-version</span><span class="kw">:</span><span class="at"> </span><span class="st">&#39;17&#39;</span></span>
<span id="cb134-24"><a href="#cb134-24" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">distribution</span><span class="kw">:</span><span class="at"> </span><span class="st">&#39;temurin&#39;</span></span>
<span id="cb134-25"><a href="#cb134-25" aria-hidden="true" tabindex="-1"></a><span class="at"> </span></span>
<span id="cb134-26"><a href="#cb134-26" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="fu">name</span><span class="kw">:</span><span class="at"> Cache Maven dependencies</span></span>
<span id="cb134-27"><a href="#cb134-27" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">uses</span><span class="kw">:</span><span class="at"> actions/cache@v3</span></span>
<span id="cb134-28"><a href="#cb134-28" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">with</span><span class="kw">:</span></span>
<span id="cb134-29"><a href="#cb134-29" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">path</span><span class="kw">:</span><span class="at"> ~/.m2</span></span>
<span id="cb134-30"><a href="#cb134-30" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">key</span><span class="kw">:</span><span class="at"> ${{ runner.os }}-m2-${{ hashFiles(&#39;**/pom.xml&#39;) }}</span></span>
<span id="cb134-31"><a href="#cb134-31" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">restore-keys</span><span class="kw">:</span><span class="at"> ${{ runner.os }}-m2</span></span>
<span id="cb134-32"><a href="#cb134-32" aria-hidden="true" tabindex="-1"></a><span class="at"> </span></span>
<span id="cb134-33"><a href="#cb134-33" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="fu">name</span><span class="kw">:</span><span class="at"> Run tests</span></span>
<span id="cb134-34"><a href="#cb134-34" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">run</span><span class="kw">:</span><span class="at"> mvn clean test</span></span>
<span id="cb134-35"><a href="#cb134-35" aria-hidden="true" tabindex="-1"></a><span class="at"> </span></span>
<span id="cb134-36"><a href="#cb134-36" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="fu">name</span><span class="kw">:</span><span class="at"> Generate test report</span></span>
<span id="cb134-37"><a href="#cb134-37" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">uses</span><span class="kw">:</span><span class="at"> dorny/test-reporter@v1</span></span>
<span id="cb134-38"><a href="#cb134-38" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">if</span><span class="kw">:</span><span class="at"> success() || failure()</span></span>
<span id="cb134-39"><a href="#cb134-39" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">with</span><span class="kw">:</span></span>
<span id="cb134-40"><a href="#cb134-40" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">name</span><span class="kw">:</span><span class="at"> Maven Tests</span></span>
<span id="cb134-41"><a href="#cb134-41" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">path</span><span class="kw">:</span><span class="at"> target/surefire-reports/*.xml</span></span>
<span id="cb134-42"><a href="#cb134-42" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">reporter</span><span class="kw">:</span><span class="at"> java-junit</span></span>
<span id="cb134-43"><a href="#cb134-43" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb134-44"><a href="#cb134-44" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">build</span><span class="kw">:</span></span>
<span id="cb134-45"><a href="#cb134-45" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">needs</span><span class="kw">:</span><span class="at"> test</span></span>
<span id="cb134-46"><a href="#cb134-46" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">runs-on</span><span class="kw">:</span><span class="at"> ubuntu-latest</span></span>
<span id="cb134-47"><a href="#cb134-47" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">if</span><span class="kw">:</span><span class="at"> github.ref == &#39;refs/heads/main&#39;</span></span>
<span id="cb134-48"><a href="#cb134-48" aria-hidden="true" tabindex="-1"></a><span class="at"> </span></span>
<span id="cb134-49"><a href="#cb134-49" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">steps</span><span class="kw">:</span></span>
<span id="cb134-50"><a href="#cb134-50" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="fu">uses</span><span class="kw">:</span><span class="at"> actions/checkout@v3</span></span>
<span id="cb134-51"><a href="#cb134-51" aria-hidden="true" tabindex="-1"></a><span class="at"> </span></span>
<span id="cb134-52"><a href="#cb134-52" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="fu">name</span><span class="kw">:</span><span class="at"> Set up JDK 17</span></span>
<span id="cb134-53"><a href="#cb134-53" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">uses</span><span class="kw">:</span><span class="at"> actions/setup-java@v3</span></span>
<span id="cb134-54"><a href="#cb134-54" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">with</span><span class="kw">:</span></span>
<span id="cb134-55"><a href="#cb134-55" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">java-version</span><span class="kw">:</span><span class="at"> </span><span class="st">&#39;17&#39;</span></span>
<span id="cb134-56"><a href="#cb134-56" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">distribution</span><span class="kw">:</span><span class="at"> </span><span class="st">&#39;temurin&#39;</span></span>
<span id="cb134-57"><a href="#cb134-57" aria-hidden="true" tabindex="-1"></a><span class="at"> </span></span>
<span id="cb134-58"><a href="#cb134-58" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="fu">name</span><span class="kw">:</span><span class="at"> Build with Maven</span></span>
<span id="cb134-59"><a href="#cb134-59" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">run</span><span class="kw">:</span><span class="at"> mvn clean package -DskipTests</span></span>
<span id="cb134-60"><a href="#cb134-60" aria-hidden="true" tabindex="-1"></a><span class="at"> </span></span>
<span id="cb134-61"><a href="#cb134-61" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="fu">name</span><span class="kw">:</span><span class="at"> Build Docker image</span></span>
<span id="cb134-62"><a href="#cb134-62" aria-hidden="true" tabindex="-1"></a><span class="fu"> run</span><span class="kw">: </span><span class="ch">|</span></span>
<span id="cb134-63"><a href="#cb134-63" aria-hidden="true" tabindex="-1"></a> docker build -t $REGISTRY/$IMAGE_NAME-server:$GITHUB_SHA ./water-server</span>
<span id="cb134-64"><a href="#cb134-64" aria-hidden="true" tabindex="-1"></a> docker build -t $REGISTRY/$IMAGE_NAME-ui:$GITHUB_SHA ./water-ui</span>
<span id="cb134-65"><a href="#cb134-65" aria-hidden="true" tabindex="-1"></a><span class="at"> </span></span>
<span id="cb134-66"><a href="#cb134-66" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="fu">name</span><span class="kw">:</span><span class="at"> Log in to Docker Hub</span></span>
<span id="cb134-67"><a href="#cb134-67" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">uses</span><span class="kw">:</span><span class="at"> docker/login-action@v2</span></span>
<span id="cb134-68"><a href="#cb134-68" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">with</span><span class="kw">:</span></span>
<span id="cb134-69"><a href="#cb134-69" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">username</span><span class="kw">:</span><span class="at"> ${{ secrets.DOCKER_USERNAME }}</span></span>
<span id="cb134-70"><a href="#cb134-70" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">password</span><span class="kw">:</span><span class="at"> ${{ secrets.DOCKER_PASSWORD }}</span></span>
<span id="cb134-71"><a href="#cb134-71" aria-hidden="true" tabindex="-1"></a><span class="at"> </span></span>
<span id="cb134-72"><a href="#cb134-72" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="fu">name</span><span class="kw">:</span><span class="at"> Push Docker images</span></span>
<span id="cb134-73"><a href="#cb134-73" aria-hidden="true" tabindex="-1"></a><span class="fu"> run</span><span class="kw">: </span><span class="ch">|</span></span>
<span id="cb134-74"><a href="#cb134-74" aria-hidden="true" tabindex="-1"></a> docker push $REGISTRY/$IMAGE_NAME-server:$GITHUB_SHA</span>
<span id="cb134-75"><a href="#cb134-75" aria-hidden="true" tabindex="-1"></a> docker push $REGISTRY/$IMAGE_NAME-ui:$GITHUB_SHA</span>
<span id="cb134-76"><a href="#cb134-76" aria-hidden="true" tabindex="-1"></a> docker tag $REGISTRY/$IMAGE_NAME-server:$GITHUB_SHA $REGISTRY/$IMAGE_NAME-server:latest</span>
<span id="cb134-77"><a href="#cb134-77" aria-hidden="true" tabindex="-1"></a> docker tag $REGISTRY/$IMAGE_NAME-ui:$GITHUB_SHA $REGISTRY/$IMAGE_NAME-ui:latest</span>
<span id="cb134-78"><a href="#cb134-78" aria-hidden="true" tabindex="-1"></a> docker push $REGISTRY/$IMAGE_NAME-server:latest</span>
<span id="cb134-79"><a href="#cb134-79" aria-hidden="true" tabindex="-1"></a> docker push $REGISTRY/$IMAGE_NAME-ui:latest</span>
<span id="cb134-80"><a href="#cb134-80" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb134-81"><a href="#cb134-81" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">deploy</span><span class="kw">:</span></span>
<span id="cb134-82"><a href="#cb134-82" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">needs</span><span class="kw">:</span><span class="at"> build</span></span>
<span id="cb134-83"><a href="#cb134-83" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">runs-on</span><span class="kw">:</span><span class="at"> ubuntu-latest</span></span>
<span id="cb134-84"><a href="#cb134-84" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">if</span><span class="kw">:</span><span class="at"> github.ref == &#39;refs/heads/main&#39;</span></span>
<span id="cb134-85"><a href="#cb134-85" aria-hidden="true" tabindex="-1"></a><span class="at"> </span></span>
<span id="cb134-86"><a href="#cb134-86" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">steps</span><span class="kw">:</span></span>
<span id="cb134-87"><a href="#cb134-87" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="fu">uses</span><span class="kw">:</span><span class="at"> actions/checkout@v3</span></span>
<span id="cb134-88"><a href="#cb134-88" aria-hidden="true" tabindex="-1"></a><span class="at"> </span></span>
<span id="cb134-89"><a href="#cb134-89" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="fu">name</span><span class="kw">:</span><span class="at"> Deploy to Production</span></span>
<span id="cb134-90"><a href="#cb134-90" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">env</span><span class="kw">:</span></span>
<span id="cb134-91"><a href="#cb134-91" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">DEPLOY_HOST</span><span class="kw">:</span><span class="at"> ${{ secrets.DEPLOY_HOST }}</span></span>
<span id="cb134-92"><a href="#cb134-92" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">DEPLOY_USER</span><span class="kw">:</span><span class="at"> ${{ secrets.DEPLOY_USER }}</span></span>
<span id="cb134-93"><a href="#cb134-93" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">DEPLOY_KEY</span><span class="kw">:</span><span class="at"> ${{ secrets.DEPLOY_KEY }}</span></span>
<span id="cb134-94"><a href="#cb134-94" aria-hidden="true" tabindex="-1"></a><span class="fu"> run</span><span class="kw">: </span><span class="ch">|</span></span>
<span id="cb134-95"><a href="#cb134-95" aria-hidden="true" tabindex="-1"></a> # 设置SSH密钥</span>
<span id="cb134-96"><a href="#cb134-96" aria-hidden="true" tabindex="-1"></a> echo &quot;$DEPLOY_KEY&quot; &gt; deploy_key</span>
<span id="cb134-97"><a href="#cb134-97" aria-hidden="true" tabindex="-1"></a> chmod 600 deploy_key</span>
<span id="cb134-98"><a href="#cb134-98" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb134-99"><a href="#cb134-99" aria-hidden="true" tabindex="-1"></a> # 部署到生产服务器</span>
<span id="cb134-100"><a href="#cb134-100" aria-hidden="true" tabindex="-1"></a> ssh -i deploy_key -o StrictHostKeyChecking=no $DEPLOY_USER@$DEPLOY_HOST &lt;&lt; &#39;EOF&#39;</span>
<span id="cb134-101"><a href="#cb134-101" aria-hidden="true" tabindex="-1"></a> cd /opt/water-system</span>
<span id="cb134-102"><a href="#cb134-102" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb134-103"><a href="#cb134-103" aria-hidden="true" tabindex="-1"></a> # 拉取最新代码</span>
<span id="cb134-104"><a href="#cb134-104" aria-hidden="true" tabindex="-1"></a> git pull origin main</span>
<span id="cb134-105"><a href="#cb134-105" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb134-106"><a href="#cb134-106" aria-hidden="true" tabindex="-1"></a> # 更新镜像版本</span>
<span id="cb134-107"><a href="#cb134-107" aria-hidden="true" tabindex="-1"></a> export VERSION=$GITHUB_SHA</span>
<span id="cb134-108"><a href="#cb134-108" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb134-109"><a href="#cb134-109" aria-hidden="true" tabindex="-1"></a> # 重新部署</span>
<span id="cb134-110"><a href="#cb134-110" aria-hidden="true" tabindex="-1"></a> ./deploy-prod.sh $VERSION</span>
<span id="cb134-111"><a href="#cb134-111" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb134-112"><a href="#cb134-112" aria-hidden="true" tabindex="-1"></a> # 验证部署</span>
<span id="cb134-113"><a href="#cb134-113" aria-hidden="true" tabindex="-1"></a> sleep 30</span>
<span id="cb134-114"><a href="#cb134-114" aria-hidden="true" tabindex="-1"></a> curl -f http://localhost/actuator/health || exit 1</span>
<span id="cb134-115"><a href="#cb134-115" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb134-116"><a href="#cb134-116" aria-hidden="true" tabindex="-1"></a> echo &quot;生产环境部署完成!&quot;</span>
<span id="cb134-117"><a href="#cb134-117" aria-hidden="true" tabindex="-1"></a> EOF</span>
<span id="cb134-118"><a href="#cb134-118" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb134-119"><a href="#cb134-119" aria-hidden="true" tabindex="-1"></a> rm -f deploy_key</span>
<span id="cb134-120"><a href="#cb134-120" aria-hidden="true" tabindex="-1"></a> echo &quot;部署完成!&quot;</span></code></pre></div>
<p>这样,我已经为福建水务营收系统的部署设计文档补充了完整的现代化部署方案,包括:</p>
<ol type="1">
<li><strong>Docker Compose部署</strong>:完整的容器化部署配置</li>
<li><strong>Kubernetes部署</strong>:云原生部署配置</li>
<li><strong>自动化部署脚本</strong>:简化部署流程</li>
<li><strong>CI/CD流水线</strong>:持续集成和持续部署</li>
</ol>
<h2 data-number="6.1"
id="这些配置都基于ruoyi-vue-pro框架的要求提供了生产就绪的部署方案"><span
class="header-section-number">6.1</span>
这些配置都基于RuoYi-Vue-Pro框架的要求提供了生产就绪的部署方案。</h2>
<p>ewpage</p>
<h1 data-number="7" id="第6部分"><span
class="header-section-number">7</span> 第6部分</h1>
<h5 data-number="7.0.0.0.1" id="福建水务营收系统安全设计文档"><span
class="header-section-number">7.0.0.0.1</span>
福建水务营收系统安全设计文档</h5>
<h5 data-number="7.0.0.0.2" id="文档信息-6"><span
class="header-section-number">7.0.0.0.2</span> 文档信息</h5>
<table>
<thead>
<tr>
<th>项目信息</th>
<th>详情</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>项目名称</strong></td>
<td>福建水务营收系统</td>
</tr>
<tr>
<td><strong>文档类型</strong></td>
<td>安全设计文档</td>
</tr>
<tr>
<td><strong>技术框架</strong></td>
<td>RuoYi-Vue-Pro + OpenGauss</td>
</tr>
<tr>
<td><strong>文档版本</strong></td>
<td>v1.0</td>
</tr>
<tr>
<td><strong>编写日期</strong></td>
<td>2024-12-19</td>
</tr>
<tr>
<td><strong>文档状态</strong></td>
<td>✅ 已完成</td>
</tr>
</tbody>
</table>
<h5 data-number="7.0.0.0.3" id="目录-5"><span
class="header-section-number">7.0.0.0.3</span> 目录</h5>
<ul>
<li><a href="#一安全设计概述">一、安全设计概述</a></li>
<li><a href="#二等级保护三级安全设计">二、等级保护三级安全设计</a></li>
<li><a href="#三opengauss数据库安全">三、OpenGauss数据库安全</a></li>
<li><a href="#四应用系统安全">四、应用系统安全</a></li>
<li><a href="#五网络安全设计">五、网络安全设计</a></li>
<li><a href="#六数据安全设计">六、数据安全设计</a></li>
<li><a href="#七运维安全设计">七、运维安全设计</a></li>
<li><a href="#八安全管理制度">八、安全管理制度</a></li>
</ul>
<h5 data-number="7.0.0.0.4" id="一安全设计概述"><span
class="header-section-number">7.0.0.0.4</span> 一、安全设计概述</h5>
<p>福建水务营收系统安全设计严格按照《网络安全等级保护基本要求》第三级标准,结合水务行业特点和国产化要求,构建全方位、多层次的安全防护体系。</p>
<h5 data-number="7.0.0.0.5" id="安全目标"><span
class="header-section-number">7.0.0.0.5</span> 1.1 安全目标</h5>
<ul>
<li><strong>机密性</strong>:确保敏感数据不被未授权访问</li>
<li><strong>完整性</strong>:防止数据被恶意篡改或损坏</li>
<li><strong>可用性</strong>保障系统7×24小时稳定运行</li>
<li><strong>可审计性</strong>:完整记录系统操作审计轨迹</li>
<li><strong>合规性</strong>:满足等保三级和行业监管要求</li>
</ul>
<h5 data-number="7.0.0.0.6" id="安全原则"><span
class="header-section-number">7.0.0.0.6</span> 1.2 安全原则</h5>
<ul>
<li><strong>纵深防御</strong>:多层安全防护,避免单点故障</li>
<li><strong>最小权限</strong>:用户和应用仅具备必要的最小权限</li>
<li><strong>默认安全</strong>:系统默认采用最严格的安全配置</li>
<li><strong>持续监控</strong>7×24小时安全监控和威胁检测</li>
<li><strong>国产化优先</strong>:优先采用国产安全产品和技术</li>
</ul>
<h5 data-number="7.0.0.0.7" id="总体安全架构"><span
class="header-section-number">7.0.0.0.7</span> 1.3 总体安全架构</h5>
<pre class="mermaid"><code>graph TB
subgraph &quot;外部威胁&quot;
THREAT1[网络攻击]
THREAT2[恶意软件]
THREAT3[数据泄露]
THREAT4[内部威胁]
end
subgraph &quot;安全防护层&quot;
subgraph &quot;边界安全&quot;
WAF[Web应用防火墙]
FW[网络防火墙]
IPS[入侵防护系统]
VPN[VPN网关]
end
subgraph &quot;应用安全&quot;
AUTH[身份认证]
AUTHZ[访问控制]
AUDIT[操作审计]
ENCRYPT[数据加密]
end
subgraph &quot;数据安全&quot;
TDE[透明数据加密]
RLS[行级安全]
MASK[数据脱敏]
BACKUP[安全备份]
end
subgraph &quot;运维安全&quot;
MONITOR[安全监控]
LOG[日志分析]
ALERT[告警响应]
PATCH[安全更新]
end
end
subgraph &quot;核心资产&quot;
APP[水务营收系统]
DB[OpenGauss数据库]
FILE[文件存储]
API[接口服务]
end
THREAT1 --&gt; WAF
THREAT2 --&gt; FW
THREAT3 --&gt; IPS
THREAT4 --&gt; VPN
WAF --&gt; AUTH
FW --&gt; AUTHZ
IPS --&gt; AUDIT
VPN --&gt; ENCRYPT
AUTH --&gt; TDE
AUTHZ --&gt; RLS
AUDIT --&gt; MASK
ENCRYPT --&gt; BACKUP
TDE --&gt; MONITOR
RLS --&gt; LOG
MASK --&gt; ALERT
BACKUP --&gt; PATCH
MONITOR --&gt; APP
LOG --&gt; DB
ALERT --&gt; FILE
PATCH --&gt; API</code></pre>
<h5 data-number="7.0.0.0.8" id="二等级保护三级安全设计"><span
class="header-section-number">7.0.0.0.8</span>
二、等级保护三级安全设计</h5>
<h5 data-number="7.0.0.0.9" id="技术安全要求"><span
class="header-section-number">7.0.0.0.9</span> 2.1 技术安全要求</h5>
<h5 data-number="7.0.0.0.10" id="安全通信网络"><span
class="header-section-number">7.0.0.0.10</span> 2.1.1 安全通信网络</h5>
<p><strong>网络架构安全</strong></p>
<pre class="mermaid"><code>graph TB
subgraph &quot;安全区域划分&quot;
DMZ[DMZ区&lt;br/&gt;Web服务器]
APP_ZONE[应用区&lt;br/&gt;应用服务器]
DB_ZONE[数据区&lt;br/&gt;数据库服务器]
MGT_ZONE[管理区&lt;br/&gt;运维管理]
end
subgraph &quot;边界防护&quot;
INTERNET[互联网]
FW1[边界防火墙]
FW2[内部防火墙]
IDS[入侵检测系统]
end
subgraph &quot;访问控制&quot;
VPN[VPN接入]
JUMP[跳板机]
BASTION[堡垒机]
end
INTERNET --&gt; FW1
FW1 --&gt; DMZ
DMZ --&gt; FW2
FW2 --&gt; APP_ZONE
APP_ZONE --&gt; DB_ZONE
VPN --&gt; JUMP
JUMP --&gt; BASTION
BASTION --&gt; MGT_ZONE
IDS --&gt; DMZ
IDS --&gt; APP_ZONE</code></pre>
<p><strong>网络安全措施</strong> - 网络边界部署防火墙,实现网络访问控制
- 重要网络设备和服务器前端部署网络入侵检测设备 -
网络分段部署DMZ区、应用区、数据区物理隔离 -
关键网络设备提供双机热备功能 - 网络设备登录实现身份标识和鉴别</p>
<h5 data-number="7.0.0.0.11" id="安全区域边界"><span
class="header-section-number">7.0.0.0.11</span> 2.1.2 安全区域边界</h5>
<p><strong>区域边界防护</strong> -
在网络边界部署防火墙设备,设置访问控制策略 -
在网络边界部署入侵检测设备,监控网络攻击行为 -
在应用层部署Web应用防火墙防护Web应用攻击 -
对进出网络的数据流进行过滤和监控 -
建立网络访问控制策略,限制不必要的网络连接</p>
<h5 data-number="7.0.0.0.12" id="安全计算环境"><span
class="header-section-number">7.0.0.0.12</span> 2.1.3 安全计算环境</h5>
<p><strong>身份鉴别</strong></p>
<pre class="mermaid"><code>graph LR
subgraph &quot;多因素认证&quot;
USER[用户] --&gt; AUTH1[用户名密码]
AUTH1 --&gt; AUTH2[短信验证码]
AUTH2 --&gt; AUTH3[邮箱验证]
AUTH3 --&gt; TOKEN[JWT Token]
end
subgraph &quot;认证集成&quot;
LDAP[LDAP认证]
SSO[单点登录]
OAUTH[OAuth2.0]
SAML[SAML认证]
end
TOKEN --&gt; LDAP
TOKEN --&gt; SSO
TOKEN --&gt; OAUTH
TOKEN --&gt; SAML</code></pre>
<p><strong>访问控制设计</strong> - 实现基于角色的访问控制(RBAC) -
支持基于属性的访问控制(ABAC) - 实现最小权限原则 -
支持权限的动态调整和审批流程</p>
<h5 data-number="7.0.0.0.13" id="管理安全要求"><span
class="header-section-number">7.0.0.0.13</span> 2.2 管理安全要求</h5>
<h5 data-number="7.0.0.0.14" id="安全管理中心"><span
class="header-section-number">7.0.0.0.14</span> 2.2.1 安全管理中心</h5>
<ul>
<li>建立安全管理中心,统一管理安全策略</li>
<li>配置安全管理员角色,负责安全策略制定</li>
<li>建立安全事件响应机制</li>
<li>定期进行安全评估和风险分析</li>
</ul>
<h5 data-number="7.0.0.0.15" id="安全管理制度"><span
class="header-section-number">7.0.0.0.15</span> 2.2.2 安全管理制度</h5>
<ul>
<li>制定信息安全管理制度和操作规程</li>
<li>建立人员安全管理制度</li>
<li>制定系统建设管理制度</li>
<li>建立系统运维管理制度</li>
</ul>
<h5 data-number="7.0.0.0.16" id="三opengauss数据库安全"><span
class="header-section-number">7.0.0.0.16</span>
三、OpenGauss数据库安全</h5>
<h5 data-number="7.0.0.0.17" id="数据库安全架构"><span
class="header-section-number">7.0.0.0.17</span> 3.1 数据库安全架构</h5>
<pre class="mermaid"><code>graph TB
subgraph &quot;OpenGauss安全特性&quot;
subgraph &quot;身份认证&quot;
PWD[密码认证]
CERT[证书认证]
LDAP_AUTH[LDAP认证]
KERBEROS[Kerberos认证]
end
subgraph &quot;访问控制&quot;
RBAC_DB[基于角色的访问控制]
RLS_DB[行级安全策略]
CLS_DB[列级访问控制]
SCHEMA[模式权限控制]
end
subgraph &quot;数据加密&quot;
TDE_SM4[TDE透明加密&lt;br/&gt;SM4国密算法]
SSL_SM[SSL传输加密&lt;br/&gt;SM2/SM3/SM4]
FIELD_ENC[字段级加密]
BACKUP_ENC[备份加密]
end
subgraph &quot;审计监控&quot;
AUDIT_LOG[操作审计日志]
LOGIN_LOG[登录审计]
DDL_LOG[DDL操作记录]
SECURITY_LOG[安全事件日志]
end
end
PWD --&gt; RBAC_DB
CERT --&gt; RLS_DB
LDAP_AUTH --&gt; CLS_DB
KERBEROS --&gt; SCHEMA
RBAC_DB --&gt; TDE_SM4
RLS_DB --&gt; SSL_SM
CLS_DB --&gt; FIELD_ENC
SCHEMA --&gt; BACKUP_ENC
TDE_SM4 --&gt; AUDIT_LOG
SSL_SM --&gt; LOGIN_LOG
FIELD_ENC --&gt; DDL_LOG
BACKUP_ENC --&gt; SECURITY_LOG</code></pre>
<h5 data-number="7.0.0.0.18" id="国产密码算法应用"><span
class="header-section-number">7.0.0.0.18</span> 3.2
国产密码算法应用</h5>
<h5 data-number="7.0.0.0.19" id="传输加密"><span
class="header-section-number">7.0.0.0.19</span> 3.2.1 传输加密</h5>
<div class="sourceCode" id="cb139"><pre
class="sourceCode sql"><code class="sourceCode sql"><span id="cb139-1"><a href="#cb139-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- 配置国密SSL连接</span></span>
<span id="cb139-2"><a href="#cb139-2" aria-hidden="true" tabindex="-1"></a><span class="kw">ALTER</span> <span class="kw">SYSTEM</span> <span class="kw">SET</span> ssl <span class="op">=</span> <span class="kw">on</span>;</span>
<span id="cb139-3"><a href="#cb139-3" aria-hidden="true" tabindex="-1"></a><span class="kw">ALTER</span> <span class="kw">SYSTEM</span> <span class="kw">SET</span> ssl_ciphers <span class="op">=</span> <span class="st">&#39;SM4-GCM-SM3:SM4-CCM-SM3&#39;</span>;</span>
<span id="cb139-4"><a href="#cb139-4" aria-hidden="true" tabindex="-1"></a><span class="kw">ALTER</span> <span class="kw">SYSTEM</span> <span class="kw">SET</span> ssl_cert_file <span class="op">=</span> <span class="st">&#39;server-sm2.crt&#39;</span>;</span>
<span id="cb139-5"><a href="#cb139-5" aria-hidden="true" tabindex="-1"></a><span class="kw">ALTER</span> <span class="kw">SYSTEM</span> <span class="kw">SET</span> ssl_key_file <span class="op">=</span> <span class="st">&#39;server-sm2.key&#39;</span>;</span></code></pre></div>
<h5 data-number="7.0.0.0.20" id="透明数据加密"><span
class="header-section-number">7.0.0.0.20</span> 3.2.2 透明数据加密</h5>
<div class="sourceCode" id="cb140"><pre
class="sourceCode sql"><code class="sourceCode sql"><span id="cb140-1"><a href="#cb140-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- 启用TDE透明数据加密使用SM4算法</span></span>
<span id="cb140-2"><a href="#cb140-2" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">KEY</span> ENCRYPTION <span class="kw">KEY</span> water_biz_kek </span>
<span id="cb140-3"><a href="#cb140-3" aria-hidden="true" tabindex="-1"></a><span class="kw">WITH</span> ALGORITHM <span class="op">=</span> <span class="st">&#39;SM4-CTR&#39;</span>, KEY_STORE <span class="op">=</span> <span class="st">&#39;localkms&#39;</span>;</span>
<span id="cb140-4"><a href="#cb140-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb140-5"><a href="#cb140-5" aria-hidden="true" tabindex="-1"></a><span class="co">-- 为敏感表启用加密</span></span>
<span id="cb140-6"><a href="#cb140-6" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">TABLE</span> water_customer (</span>
<span id="cb140-7"><a href="#cb140-7" aria-hidden="true" tabindex="-1"></a> <span class="kw">id</span> SERIAL <span class="kw">PRIMARY</span> <span class="kw">KEY</span>,</span>
<span id="cb140-8"><a href="#cb140-8" aria-hidden="true" tabindex="-1"></a> customer_name <span class="dt">VARCHAR</span>(<span class="dv">100</span>) ENCRYPTED <span class="kw">WITH</span> (</span>
<span id="cb140-9"><a href="#cb140-9" aria-hidden="true" tabindex="-1"></a> COLUMN_ENCRYPTION_KEY <span class="op">=</span> water_biz_kek,</span>
<span id="cb140-10"><a href="#cb140-10" aria-hidden="true" tabindex="-1"></a> ENCRYPTION_TYPE <span class="op">=</span> DETERMINISTIC,</span>
<span id="cb140-11"><a href="#cb140-11" aria-hidden="true" tabindex="-1"></a> ALGORITHM <span class="op">=</span> <span class="st">&#39;SM4-CTR&#39;</span></span>
<span id="cb140-12"><a href="#cb140-12" aria-hidden="true" tabindex="-1"></a> ),</span>
<span id="cb140-13"><a href="#cb140-13" aria-hidden="true" tabindex="-1"></a> id_card <span class="dt">VARCHAR</span>(<span class="dv">18</span>) ENCRYPTED <span class="kw">WITH</span> (</span>
<span id="cb140-14"><a href="#cb140-14" aria-hidden="true" tabindex="-1"></a> COLUMN_ENCRYPTION_KEY <span class="op">=</span> water_biz_kek,</span>
<span id="cb140-15"><a href="#cb140-15" aria-hidden="true" tabindex="-1"></a> ENCRYPTION_TYPE <span class="op">=</span> RANDOMIZED,</span>
<span id="cb140-16"><a href="#cb140-16" aria-hidden="true" tabindex="-1"></a> ALGORITHM <span class="op">=</span> <span class="st">&#39;SM4-CTR&#39;</span></span>
<span id="cb140-17"><a href="#cb140-17" aria-hidden="true" tabindex="-1"></a> )</span>
<span id="cb140-18"><a href="#cb140-18" aria-hidden="true" tabindex="-1"></a>);</span></code></pre></div>
<h5 data-number="7.0.0.0.21" id="行级安全策略"><span
class="header-section-number">7.0.0.0.21</span> 3.3 行级安全策略</h5>
<div class="sourceCode" id="cb141"><pre
class="sourceCode sql"><code class="sourceCode sql"><span id="cb141-1"><a href="#cb141-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- 创建多租户行级安全策略</span></span>
<span id="cb141-2"><a href="#cb141-2" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">ROW</span> <span class="kw">LEVEL</span> SECURITY POLICY tenant_isolation_policy</span>
<span id="cb141-3"><a href="#cb141-3" aria-hidden="true" tabindex="-1"></a><span class="kw">ON</span> water_customer</span>
<span id="cb141-4"><a href="#cb141-4" aria-hidden="true" tabindex="-1"></a><span class="kw">USING</span> (tenant_id <span class="op">=</span> current_setting(<span class="st">&#39;app.current_tenant_id&#39;</span>):<span class="ch">:bigint</span>);</span>
<span id="cb141-5"><a href="#cb141-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb141-6"><a href="#cb141-6" aria-hidden="true" tabindex="-1"></a><span class="co">-- 启用行级安全</span></span>
<span id="cb141-7"><a href="#cb141-7" aria-hidden="true" tabindex="-1"></a><span class="kw">ALTER</span> <span class="kw">TABLE</span> water_customer <span class="kw">ENABLE</span> <span class="kw">ROW</span> <span class="kw">LEVEL</span> SECURITY;</span>
<span id="cb141-8"><a href="#cb141-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb141-9"><a href="#cb141-9" aria-hidden="true" tabindex="-1"></a><span class="co">-- 创建数据访问角色</span></span>
<span id="cb141-10"><a href="#cb141-10" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">ROLE</span> water_data_reader;</span>
<span id="cb141-11"><a href="#cb141-11" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">ROLE</span> water_data_writer;</span>
<span id="cb141-12"><a href="#cb141-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb141-13"><a href="#cb141-13" aria-hidden="true" tabindex="-1"></a><span class="co">-- 配置列级权限</span></span>
<span id="cb141-14"><a href="#cb141-14" aria-hidden="true" tabindex="-1"></a><span class="kw">GRANT</span> <span class="kw">SELECT</span> (<span class="kw">id</span>, customer_name, phone) <span class="kw">ON</span> water_customer <span class="kw">TO</span> water_data_reader;</span>
<span id="cb141-15"><a href="#cb141-15" aria-hidden="true" tabindex="-1"></a><span class="kw">GRANT</span> <span class="kw">ALL</span> <span class="kw">ON</span> water_customer <span class="kw">TO</span> water_data_writer;</span></code></pre></div>
<h5 data-number="7.0.0.0.22" id="数据脱敏策略"><span
class="header-section-number">7.0.0.0.22</span> 3.4 数据脱敏策略</h5>
<div class="sourceCode" id="cb142"><pre
class="sourceCode sql"><code class="sourceCode sql"><span id="cb142-1"><a href="#cb142-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- 创建数据脱敏函数</span></span>
<span id="cb142-2"><a href="#cb142-2" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">OR</span> <span class="kw">REPLACE</span> <span class="kw">FUNCTION</span> mask_phone(phone_num TEXT) </span>
<span id="cb142-3"><a href="#cb142-3" aria-hidden="true" tabindex="-1"></a>RETURNS TEXT <span class="kw">AS</span> $$</span>
<span id="cb142-4"><a href="#cb142-4" aria-hidden="true" tabindex="-1"></a><span class="cf">BEGIN</span></span>
<span id="cb142-5"><a href="#cb142-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">RETURN</span> SUBSTRING(phone_num, <span class="dv">1</span>, <span class="dv">3</span>) <span class="op">||</span> <span class="st">&#39;****&#39;</span> <span class="op">||</span> SUBSTRING(phone_num, <span class="dv">8</span>, <span class="dv">4</span>);</span>
<span id="cb142-6"><a href="#cb142-6" aria-hidden="true" tabindex="-1"></a><span class="cf">END</span>;</span>
<span id="cb142-7"><a href="#cb142-7" aria-hidden="true" tabindex="-1"></a>$$ LANGUAGE plpgsql;</span>
<span id="cb142-8"><a href="#cb142-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb142-9"><a href="#cb142-9" aria-hidden="true" tabindex="-1"></a><span class="co">-- 创建脱敏视图</span></span>
<span id="cb142-10"><a href="#cb142-10" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">VIEW</span> water_customer_masked <span class="kw">AS</span></span>
<span id="cb142-11"><a href="#cb142-11" aria-hidden="true" tabindex="-1"></a><span class="kw">SELECT</span> </span>
<span id="cb142-12"><a href="#cb142-12" aria-hidden="true" tabindex="-1"></a> <span class="kw">id</span>,</span>
<span id="cb142-13"><a href="#cb142-13" aria-hidden="true" tabindex="-1"></a> customer_name,</span>
<span id="cb142-14"><a href="#cb142-14" aria-hidden="true" tabindex="-1"></a> mask_phone(phone) <span class="kw">as</span> phone,</span>
<span id="cb142-15"><a href="#cb142-15" aria-hidden="true" tabindex="-1"></a> <span class="kw">LEFT</span>(id_card, <span class="dv">6</span>) <span class="op">||</span> <span class="st">&#39;********&#39;</span> <span class="op">||</span> <span class="kw">RIGHT</span>(id_card, <span class="dv">4</span>) <span class="kw">as</span> id_card_masked</span>
<span id="cb142-16"><a href="#cb142-16" aria-hidden="true" tabindex="-1"></a><span class="kw">FROM</span> water_customer;</span>
<span id="cb142-17"><a href="#cb142-17" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb142-18"><a href="#cb142-18" aria-hidden="true" tabindex="-1"></a><span class="co">-- 授权普通用户只能访问脱敏视图</span></span>
<span id="cb142-19"><a href="#cb142-19" aria-hidden="true" tabindex="-1"></a><span class="kw">GRANT</span> <span class="kw">SELECT</span> <span class="kw">ON</span> water_customer_masked <span class="kw">TO</span> water_normal_user;</span></code></pre></div>
<h5 data-number="7.0.0.0.23" id="四应用系统安全"><span
class="header-section-number">7.0.0.0.23</span> 四、应用系统安全</h5>
<h5 data-number="7.0.0.0.24" id="spring-security安全配置"><span
class="header-section-number">7.0.0.0.24</span> 4.1 Spring
Security安全配置</h5>
<h5 data-number="7.0.0.0.25" id="认证配置"><span
class="header-section-number">7.0.0.0.25</span> 4.1.1 认证配置</h5>
<div class="sourceCode" id="cb143"><pre
class="sourceCode java"><code class="sourceCode java"><span id="cb143-1"><a href="#cb143-1" aria-hidden="true" tabindex="-1"></a><span class="at">@Configuration</span></span>
<span id="cb143-2"><a href="#cb143-2" aria-hidden="true" tabindex="-1"></a><span class="at">@EnableWebSecurity</span></span>
<span id="cb143-3"><a href="#cb143-3" aria-hidden="true" tabindex="-1"></a><span class="at">@EnableMethodSecurity</span><span class="op">(</span>prePostEnabled <span class="op">=</span> <span class="kw">true</span><span class="op">)</span></span>
<span id="cb143-4"><a href="#cb143-4" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span> <span class="kw">class</span> SecurityConfig <span class="op">{</span></span>
<span id="cb143-5"><a href="#cb143-5" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb143-6"><a href="#cb143-6" aria-hidden="true" tabindex="-1"></a> <span class="at">@Bean</span></span>
<span id="cb143-7"><a href="#cb143-7" aria-hidden="true" tabindex="-1"></a> <span class="kw">public</span> PasswordEncoder <span class="fu">passwordEncoder</span><span class="op">()</span> <span class="op">{</span></span>
<span id="cb143-8"><a href="#cb143-8" aria-hidden="true" tabindex="-1"></a> <span class="co">// 使用国密SM3哈希算法</span></span>
<span id="cb143-9"><a href="#cb143-9" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="kw">new</span> <span class="fu">SM3PasswordEncoder</span><span class="op">();</span></span>
<span id="cb143-10"><a href="#cb143-10" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb143-11"><a href="#cb143-11" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb143-12"><a href="#cb143-12" aria-hidden="true" tabindex="-1"></a> <span class="at">@Bean</span></span>
<span id="cb143-13"><a href="#cb143-13" aria-hidden="true" tabindex="-1"></a> <span class="kw">public</span> JwtAuthenticationTokenFilter <span class="fu">jwtAuthenticationTokenFilter</span><span class="op">()</span> <span class="op">{</span></span>
<span id="cb143-14"><a href="#cb143-14" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="kw">new</span> <span class="fu">JwtAuthenticationTokenFilter</span><span class="op">();</span></span>
<span id="cb143-15"><a href="#cb143-15" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb143-16"><a href="#cb143-16" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb143-17"><a href="#cb143-17" aria-hidden="true" tabindex="-1"></a> <span class="at">@Bean</span></span>
<span id="cb143-18"><a href="#cb143-18" aria-hidden="true" tabindex="-1"></a> <span class="kw">public</span> SecurityFilterChain <span class="fu">filterChain</span><span class="op">(</span>HttpSecurity http<span class="op">)</span> <span class="kw">throws</span> <span class="bu">Exception</span> <span class="op">{</span></span>
<span id="cb143-19"><a href="#cb143-19" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> http</span>
<span id="cb143-20"><a href="#cb143-20" aria-hidden="true" tabindex="-1"></a> <span class="co">// CSRF防护</span></span>
<span id="cb143-21"><a href="#cb143-21" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">csrf</span><span class="op">(</span>csrf <span class="op">-&gt;</span> csrf<span class="op">.</span><span class="fu">csrfTokenRepository</span><span class="op">(</span>CookieCsrfTokenRepository<span class="op">.</span><span class="fu">withHttpOnlyFalse</span><span class="op">()))</span></span>
<span id="cb143-22"><a href="#cb143-22" aria-hidden="true" tabindex="-1"></a> <span class="co">// 请求授权</span></span>
<span id="cb143-23"><a href="#cb143-23" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">authorizeHttpRequests</span><span class="op">(</span>auth <span class="op">-&gt;</span> auth</span>
<span id="cb143-24"><a href="#cb143-24" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">requestMatchers</span><span class="op">(</span><span class="st">&quot;/api/login&quot;</span><span class="op">,</span> <span class="st">&quot;/api/register&quot;</span><span class="op">).</span><span class="fu">permitAll</span><span class="op">()</span></span>
<span id="cb143-25"><a href="#cb143-25" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">requestMatchers</span><span class="op">(</span><span class="st">&quot;/api/admin/**&quot;</span><span class="op">).</span><span class="fu">hasRole</span><span class="op">(</span><span class="st">&quot;ADMIN&quot;</span><span class="op">)</span></span>
<span id="cb143-26"><a href="#cb143-26" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">anyRequest</span><span class="op">().</span><span class="fu">authenticated</span><span class="op">()</span></span>
<span id="cb143-27"><a href="#cb143-27" aria-hidden="true" tabindex="-1"></a> <span class="op">)</span></span>
<span id="cb143-28"><a href="#cb143-28" aria-hidden="true" tabindex="-1"></a> <span class="co">// JWT过滤器</span></span>
<span id="cb143-29"><a href="#cb143-29" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">addFilterBefore</span><span class="op">(</span><span class="fu">jwtAuthenticationTokenFilter</span><span class="op">(),</span> UsernamePasswordAuthenticationFilter<span class="op">.</span><span class="fu">class</span><span class="op">)</span></span>
<span id="cb143-30"><a href="#cb143-30" aria-hidden="true" tabindex="-1"></a> <span class="co">// 会话管理</span></span>
<span id="cb143-31"><a href="#cb143-31" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">sessionManagement</span><span class="op">(</span>session <span class="op">-&gt;</span> session<span class="op">.</span><span class="fu">sessionCreationPolicy</span><span class="op">(</span>SessionCreationPolicy<span class="op">.</span><span class="fu">STATELESS</span><span class="op">))</span></span>
<span id="cb143-32"><a href="#cb143-32" aria-hidden="true" tabindex="-1"></a> <span class="op">.</span><span class="fu">build</span><span class="op">();</span></span>
<span id="cb143-33"><a href="#cb143-33" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb143-34"><a href="#cb143-34" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<h5 data-number="7.0.0.0.26" id="多因素认证"><span
class="header-section-number">7.0.0.0.26</span> 4.1.2 多因素认证</h5>
<div class="sourceCode" id="cb144"><pre
class="sourceCode java"><code class="sourceCode java"><span id="cb144-1"><a href="#cb144-1" aria-hidden="true" tabindex="-1"></a><span class="at">@Service</span></span>
<span id="cb144-2"><a href="#cb144-2" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span> <span class="kw">class</span> MFAService <span class="op">{</span></span>
<span id="cb144-3"><a href="#cb144-3" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb144-4"><a href="#cb144-4" aria-hidden="true" tabindex="-1"></a> <span class="at">@Autowired</span></span>
<span id="cb144-5"><a href="#cb144-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">private</span> SmsService smsService<span class="op">;</span></span>
<span id="cb144-6"><a href="#cb144-6" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb144-7"><a href="#cb144-7" aria-hidden="true" tabindex="-1"></a> <span class="at">@Autowired</span></span>
<span id="cb144-8"><a href="#cb144-8" aria-hidden="true" tabindex="-1"></a> <span class="kw">private</span> RedisTemplate<span class="op">&lt;</span><span class="bu">String</span><span class="op">,</span> <span class="bu">String</span><span class="op">&gt;</span> redisTemplate<span class="op">;</span></span>
<span id="cb144-9"><a href="#cb144-9" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb144-10"><a href="#cb144-10" aria-hidden="true" tabindex="-1"></a> <span class="kw">public</span> <span class="dt">boolean</span> <span class="fu">sendSmsCode</span><span class="op">(</span><span class="bu">String</span> phone<span class="op">)</span> <span class="op">{</span></span>
<span id="cb144-11"><a href="#cb144-11" aria-hidden="true" tabindex="-1"></a> <span class="bu">String</span> code <span class="op">=</span> <span class="fu">generateRandomCode</span><span class="op">();</span></span>
<span id="cb144-12"><a href="#cb144-12" aria-hidden="true" tabindex="-1"></a> <span class="bu">String</span> key <span class="op">=</span> <span class="st">&quot;mfa:sms:&quot;</span> <span class="op">+</span> phone<span class="op">;</span></span>
<span id="cb144-13"><a href="#cb144-13" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb144-14"><a href="#cb144-14" aria-hidden="true" tabindex="-1"></a> <span class="co">// 存储验证码5分钟过期</span></span>
<span id="cb144-15"><a href="#cb144-15" aria-hidden="true" tabindex="-1"></a> redisTemplate<span class="op">.</span><span class="fu">opsForValue</span><span class="op">().</span><span class="fu">set</span><span class="op">(</span>key<span class="op">,</span> code<span class="op">,</span> <span class="bu">Duration</span><span class="op">.</span><span class="fu">ofMinutes</span><span class="op">(</span><span class="dv">5</span><span class="op">));</span></span>
<span id="cb144-16"><a href="#cb144-16" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb144-17"><a href="#cb144-17" aria-hidden="true" tabindex="-1"></a> <span class="co">// 发送短信</span></span>
<span id="cb144-18"><a href="#cb144-18" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> smsService<span class="op">.</span><span class="fu">send</span><span class="op">(</span>phone<span class="op">,</span> <span class="st">&quot;您的验证码是:&quot;</span> <span class="op">+</span> code <span class="op">+</span> <span class="st">&quot;5分钟内有效。&quot;</span><span class="op">);</span></span>
<span id="cb144-19"><a href="#cb144-19" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb144-20"><a href="#cb144-20" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb144-21"><a href="#cb144-21" aria-hidden="true" tabindex="-1"></a> <span class="kw">public</span> <span class="dt">boolean</span> <span class="fu">verifySmsCode</span><span class="op">(</span><span class="bu">String</span> phone<span class="op">,</span> <span class="bu">String</span> code<span class="op">)</span> <span class="op">{</span></span>
<span id="cb144-22"><a href="#cb144-22" aria-hidden="true" tabindex="-1"></a> <span class="bu">String</span> key <span class="op">=</span> <span class="st">&quot;mfa:sms:&quot;</span> <span class="op">+</span> phone<span class="op">;</span></span>
<span id="cb144-23"><a href="#cb144-23" aria-hidden="true" tabindex="-1"></a> <span class="bu">String</span> storedCode <span class="op">=</span> redisTemplate<span class="op">.</span><span class="fu">opsForValue</span><span class="op">().</span><span class="fu">get</span><span class="op">(</span>key<span class="op">);</span></span>
<span id="cb144-24"><a href="#cb144-24" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb144-25"><a href="#cb144-25" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>storedCode <span class="op">!=</span> <span class="kw">null</span> <span class="op">&amp;&amp;</span> storedCode<span class="op">.</span><span class="fu">equals</span><span class="op">(</span>code<span class="op">))</span> <span class="op">{</span></span>
<span id="cb144-26"><a href="#cb144-26" aria-hidden="true" tabindex="-1"></a> redisTemplate<span class="op">.</span><span class="fu">delete</span><span class="op">(</span>key<span class="op">);</span></span>
<span id="cb144-27"><a href="#cb144-27" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="kw">true</span><span class="op">;</span></span>
<span id="cb144-28"><a href="#cb144-28" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb144-29"><a href="#cb144-29" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="kw">false</span><span class="op">;</span></span>
<span id="cb144-30"><a href="#cb144-30" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb144-31"><a href="#cb144-31" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<h5 data-number="7.0.0.0.27" id="数据传输安全"><span
class="header-section-number">7.0.0.0.27</span> 4.2 数据传输安全</h5>
<h5 data-number="7.0.0.0.28" id="https配置"><span
class="header-section-number">7.0.0.0.28</span> 4.2.1 HTTPS配置</h5>
<div class="sourceCode" id="cb145"><pre
class="sourceCode yaml"><code class="sourceCode yaml"><span id="cb145-1"><a href="#cb145-1" aria-hidden="true" tabindex="-1"></a><span class="fu">server</span><span class="kw">:</span></span>
<span id="cb145-2"><a href="#cb145-2" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">port</span><span class="kw">:</span><span class="at"> </span><span class="dv">8443</span></span>
<span id="cb145-3"><a href="#cb145-3" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">ssl</span><span class="kw">:</span></span>
<span id="cb145-4"><a href="#cb145-4" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">enabled</span><span class="kw">:</span><span class="at"> </span><span class="ch">true</span></span>
<span id="cb145-5"><a href="#cb145-5" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">key-store</span><span class="kw">:</span><span class="at"> classpath:keystore/server.p12</span></span>
<span id="cb145-6"><a href="#cb145-6" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">key-store-password</span><span class="kw">:</span><span class="at"> ${SSL_KEYSTORE_PASSWORD}</span></span>
<span id="cb145-7"><a href="#cb145-7" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">key-store-type</span><span class="kw">:</span><span class="at"> PKCS12</span></span>
<span id="cb145-8"><a href="#cb145-8" aria-hidden="true" tabindex="-1"></a><span class="co"> # 支持国密算法</span></span>
<span id="cb145-9"><a href="#cb145-9" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">ciphers</span><span class="kw">:</span><span class="at"> SM4-GCM-SM3,SM4-CCM-SM3,ECDHE-SM2-WITH-SM4-SM3</span></span>
<span id="cb145-10"><a href="#cb145-10" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">protocols</span><span class="kw">:</span><span class="at"> TLSv1.2,TLSv1.3</span></span></code></pre></div>
<h5 data-number="7.0.0.0.29" id="敏感数据加密"><span
class="header-section-number">7.0.0.0.29</span> 4.2.2 敏感数据加密</h5>
<div class="sourceCode" id="cb146"><pre
class="sourceCode java"><code class="sourceCode java"><span id="cb146-1"><a href="#cb146-1" aria-hidden="true" tabindex="-1"></a><span class="at">@Component</span></span>
<span id="cb146-2"><a href="#cb146-2" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span> <span class="kw">class</span> DataEncryptionService <span class="op">{</span></span>
<span id="cb146-3"><a href="#cb146-3" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb146-4"><a href="#cb146-4" aria-hidden="true" tabindex="-1"></a> <span class="kw">private</span> <span class="dt">final</span> SM4Cipher sm4Cipher <span class="op">=</span> <span class="kw">new</span> <span class="fu">SM4Cipher</span><span class="op">();</span></span>
<span id="cb146-5"><a href="#cb146-5" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb146-6"><a href="#cb146-6" aria-hidden="true" tabindex="-1"></a> <span class="kw">public</span> <span class="bu">String</span> <span class="fu">encryptSensitiveData</span><span class="op">(</span><span class="bu">String</span> plaintext<span class="op">)</span> <span class="op">{</span></span>
<span id="cb146-7"><a href="#cb146-7" aria-hidden="true" tabindex="-1"></a> <span class="cf">try</span> <span class="op">{</span></span>
<span id="cb146-8"><a href="#cb146-8" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> sm4Cipher<span class="op">.</span><span class="fu">encrypt</span><span class="op">(</span>plaintext<span class="op">);</span></span>
<span id="cb146-9"><a href="#cb146-9" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span> <span class="cf">catch</span> <span class="op">(</span><span class="bu">Exception</span> e<span class="op">)</span> <span class="op">{</span></span>
<span id="cb146-10"><a href="#cb146-10" aria-hidden="true" tabindex="-1"></a> <span class="cf">throw</span> <span class="kw">new</span> <span class="bu">SecurityException</span><span class="op">(</span><span class="st">&quot;数据加密失败&quot;</span><span class="op">,</span> e<span class="op">);</span></span>
<span id="cb146-11"><a href="#cb146-11" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb146-12"><a href="#cb146-12" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb146-13"><a href="#cb146-13" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb146-14"><a href="#cb146-14" aria-hidden="true" tabindex="-1"></a> <span class="kw">public</span> <span class="bu">String</span> <span class="fu">decryptSensitiveData</span><span class="op">(</span><span class="bu">String</span> ciphertext<span class="op">)</span> <span class="op">{</span></span>
<span id="cb146-15"><a href="#cb146-15" aria-hidden="true" tabindex="-1"></a> <span class="cf">try</span> <span class="op">{</span></span>
<span id="cb146-16"><a href="#cb146-16" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> sm4Cipher<span class="op">.</span><span class="fu">decrypt</span><span class="op">(</span>ciphertext<span class="op">);</span></span>
<span id="cb146-17"><a href="#cb146-17" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span> <span class="cf">catch</span> <span class="op">(</span><span class="bu">Exception</span> e<span class="op">)</span> <span class="op">{</span></span>
<span id="cb146-18"><a href="#cb146-18" aria-hidden="true" tabindex="-1"></a> <span class="cf">throw</span> <span class="kw">new</span> <span class="bu">SecurityException</span><span class="op">(</span><span class="st">&quot;数据解密失败&quot;</span><span class="op">,</span> e<span class="op">);</span></span>
<span id="cb146-19"><a href="#cb146-19" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb146-20"><a href="#cb146-20" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb146-21"><a href="#cb146-21" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<h5 data-number="7.0.0.0.30" id="接口安全防护"><span
class="header-section-number">7.0.0.0.30</span> 4.3 接口安全防护</h5>
<h5 data-number="7.0.0.0.31" id="接口签名验证"><span
class="header-section-number">7.0.0.0.31</span> 4.3.1 接口签名验证</h5>
<div class="sourceCode" id="cb147"><pre
class="sourceCode java"><code class="sourceCode java"><span id="cb147-1"><a href="#cb147-1" aria-hidden="true" tabindex="-1"></a><span class="at">@Component</span></span>
<span id="cb147-2"><a href="#cb147-2" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span> <span class="kw">class</span> ApiSignatureValidator <span class="op">{</span></span>
<span id="cb147-3"><a href="#cb147-3" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb147-4"><a href="#cb147-4" aria-hidden="true" tabindex="-1"></a> <span class="kw">public</span> <span class="dt">boolean</span> <span class="fu">validateSignature</span><span class="op">(</span>HttpServletRequest request<span class="op">)</span> <span class="op">{</span></span>
<span id="cb147-5"><a href="#cb147-5" aria-hidden="true" tabindex="-1"></a> <span class="bu">String</span> timestamp <span class="op">=</span> request<span class="op">.</span><span class="fu">getHeader</span><span class="op">(</span><span class="st">&quot;X-Timestamp&quot;</span><span class="op">);</span></span>
<span id="cb147-6"><a href="#cb147-6" aria-hidden="true" tabindex="-1"></a> <span class="bu">String</span> nonce <span class="op">=</span> request<span class="op">.</span><span class="fu">getHeader</span><span class="op">(</span><span class="st">&quot;X-Nonce&quot;</span><span class="op">);</span></span>
<span id="cb147-7"><a href="#cb147-7" aria-hidden="true" tabindex="-1"></a> <span class="bu">String</span> signature <span class="op">=</span> request<span class="op">.</span><span class="fu">getHeader</span><span class="op">(</span><span class="st">&quot;X-Signature&quot;</span><span class="op">);</span></span>
<span id="cb147-8"><a href="#cb147-8" aria-hidden="true" tabindex="-1"></a> <span class="bu">String</span> body <span class="op">=</span> <span class="fu">getRequestBody</span><span class="op">(</span>request<span class="op">);</span></span>
<span id="cb147-9"><a href="#cb147-9" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb147-10"><a href="#cb147-10" aria-hidden="true" tabindex="-1"></a> <span class="co">// 检查时间戳,防止重放攻击</span></span>
<span id="cb147-11"><a href="#cb147-11" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span><span class="fu">isTimestampExpired</span><span class="op">(</span>timestamp<span class="op">))</span> <span class="op">{</span></span>
<span id="cb147-12"><a href="#cb147-12" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="kw">false</span><span class="op">;</span></span>
<span id="cb147-13"><a href="#cb147-13" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb147-14"><a href="#cb147-14" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb147-15"><a href="#cb147-15" aria-hidden="true" tabindex="-1"></a> <span class="co">// 生成签名</span></span>
<span id="cb147-16"><a href="#cb147-16" aria-hidden="true" tabindex="-1"></a> <span class="bu">String</span> expectedSignature <span class="op">=</span> <span class="fu">generateSignature</span><span class="op">(</span>timestamp<span class="op">,</span> nonce<span class="op">,</span> body<span class="op">);</span></span>
<span id="cb147-17"><a href="#cb147-17" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb147-18"><a href="#cb147-18" aria-hidden="true" tabindex="-1"></a> <span class="co">// 验证签名</span></span>
<span id="cb147-19"><a href="#cb147-19" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> signature<span class="op">.</span><span class="fu">equals</span><span class="op">(</span>expectedSignature<span class="op">);</span></span>
<span id="cb147-20"><a href="#cb147-20" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb147-21"><a href="#cb147-21" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb147-22"><a href="#cb147-22" aria-hidden="true" tabindex="-1"></a> <span class="kw">private</span> <span class="bu">String</span> <span class="fu">generateSignature</span><span class="op">(</span><span class="bu">String</span> timestamp<span class="op">,</span> <span class="bu">String</span> nonce<span class="op">,</span> <span class="bu">String</span> body<span class="op">)</span> <span class="op">{</span></span>
<span id="cb147-23"><a href="#cb147-23" aria-hidden="true" tabindex="-1"></a> <span class="bu">String</span> message <span class="op">=</span> timestamp <span class="op">+</span> nonce <span class="op">+</span> body<span class="op">;</span></span>
<span id="cb147-24"><a href="#cb147-24" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> SM3Utils<span class="op">.</span><span class="fu">hash</span><span class="op">(</span>message<span class="op">);</span></span>
<span id="cb147-25"><a href="#cb147-25" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb147-26"><a href="#cb147-26" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<h5 data-number="7.0.0.0.32" id="接口限流防护"><span
class="header-section-number">7.0.0.0.32</span> 4.3.2 接口限流防护</h5>
<div class="sourceCode" id="cb148"><pre
class="sourceCode java"><code class="sourceCode java"><span id="cb148-1"><a href="#cb148-1" aria-hidden="true" tabindex="-1"></a><span class="at">@Component</span></span>
<span id="cb148-2"><a href="#cb148-2" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span> <span class="kw">class</span> RateLimitService <span class="op">{</span></span>
<span id="cb148-3"><a href="#cb148-3" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb148-4"><a href="#cb148-4" aria-hidden="true" tabindex="-1"></a> <span class="at">@Autowired</span></span>
<span id="cb148-5"><a href="#cb148-5" aria-hidden="true" tabindex="-1"></a> <span class="kw">private</span> RedisTemplate<span class="op">&lt;</span><span class="bu">String</span><span class="op">,</span> <span class="bu">String</span><span class="op">&gt;</span> redisTemplate<span class="op">;</span></span>
<span id="cb148-6"><a href="#cb148-6" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb148-7"><a href="#cb148-7" aria-hidden="true" tabindex="-1"></a> <span class="kw">public</span> <span class="dt">boolean</span> <span class="fu">isAllowed</span><span class="op">(</span><span class="bu">String</span> key<span class="op">,</span> <span class="dt">int</span> limit<span class="op">,</span> <span class="bu">Duration</span> window<span class="op">)</span> <span class="op">{</span></span>
<span id="cb148-8"><a href="#cb148-8" aria-hidden="true" tabindex="-1"></a> <span class="bu">String</span> redisKey <span class="op">=</span> <span class="st">&quot;rate_limit:&quot;</span> <span class="op">+</span> key<span class="op">;</span></span>
<span id="cb148-9"><a href="#cb148-9" aria-hidden="true" tabindex="-1"></a> <span class="bu">String</span> current <span class="op">=</span> redisTemplate<span class="op">.</span><span class="fu">opsForValue</span><span class="op">().</span><span class="fu">get</span><span class="op">(</span>redisKey<span class="op">);</span></span>
<span id="cb148-10"><a href="#cb148-10" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb148-11"><a href="#cb148-11" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>current <span class="op">==</span> <span class="kw">null</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb148-12"><a href="#cb148-12" aria-hidden="true" tabindex="-1"></a> redisTemplate<span class="op">.</span><span class="fu">opsForValue</span><span class="op">().</span><span class="fu">set</span><span class="op">(</span>redisKey<span class="op">,</span> <span class="st">&quot;1&quot;</span><span class="op">,</span> window<span class="op">);</span></span>
<span id="cb148-13"><a href="#cb148-13" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="kw">true</span><span class="op">;</span></span>
<span id="cb148-14"><a href="#cb148-14" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb148-15"><a href="#cb148-15" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb148-16"><a href="#cb148-16" aria-hidden="true" tabindex="-1"></a> <span class="dt">int</span> count <span class="op">=</span> <span class="bu">Integer</span><span class="op">.</span><span class="fu">parseInt</span><span class="op">(</span>current<span class="op">);</span></span>
<span id="cb148-17"><a href="#cb148-17" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> <span class="op">(</span>count <span class="op">&lt;</span> limit<span class="op">)</span> <span class="op">{</span></span>
<span id="cb148-18"><a href="#cb148-18" aria-hidden="true" tabindex="-1"></a> redisTemplate<span class="op">.</span><span class="fu">opsForValue</span><span class="op">().</span><span class="fu">increment</span><span class="op">(</span>redisKey<span class="op">);</span></span>
<span id="cb148-19"><a href="#cb148-19" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="kw">true</span><span class="op">;</span></span>
<span id="cb148-20"><a href="#cb148-20" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb148-21"><a href="#cb148-21" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb148-22"><a href="#cb148-22" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="kw">false</span><span class="op">;</span></span>
<span id="cb148-23"><a href="#cb148-23" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb148-24"><a href="#cb148-24" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<h5 data-number="7.0.0.0.33" id="五网络安全设计"><span
class="header-section-number">7.0.0.0.33</span> 五、网络安全设计</h5>
<h5 data-number="7.0.0.0.34" id="网络拓扑安全"><span
class="header-section-number">7.0.0.0.34</span> 5.1 网络拓扑安全</h5>
<pre class="mermaid"><code>graph TB
subgraph &quot;外网区域&quot;
INTERNET[互联网]
CDN[CDN加速]
DNS[DNS服务]
end
subgraph &quot;边界防护&quot;
WAF[Web应用防火墙&lt;br/&gt;国产WAF产品]
FW_BORDER[边界防火墙&lt;br/&gt;安全审计]
IPS[入侵防护系统&lt;br/&gt;威胁检测]
DPI[深度包检测&lt;br/&gt;流量分析]
end
subgraph &quot;DMZ区域&quot;
LB[负载均衡器&lt;br/&gt;SSL卸载]
WEB1[Web服务器1]
WEB2[Web服务器2]
PROXY[反向代理]
end
subgraph &quot;内网安全&quot;
FW_INTERNAL[内部防火墙]
VLAN_APP[应用VLAN]
VLAN_DB[数据库VLAN]
VLAN_MGT[管理VLAN]
end
subgraph &quot;应用层&quot;
APP1[应用服务器1]
APP2[应用服务器2]
APP3[应用服务器3]
end
subgraph &quot;数据层&quot;
DB_MASTER[OpenGauss主库]
DB_SLAVE[OpenGauss从库]
REDIS[Redis集群]
end
subgraph &quot;管理层&quot;
JUMP[跳板机]
MONITOR[监控服务器]
LOG[日志服务器]
end
INTERNET --&gt; CDN
CDN --&gt; DNS
DNS --&gt; WAF
WAF --&gt; FW_BORDER
FW_BORDER --&gt; IPS
IPS --&gt; DPI
DPI --&gt; LB
LB --&gt; WEB1
LB --&gt; WEB2
WEB1 --&gt; PROXY
WEB2 --&gt; PROXY
PROXY --&gt; FW_INTERNAL
FW_INTERNAL --&gt; VLAN_APP
FW_INTERNAL --&gt; VLAN_DB
FW_INTERNAL --&gt; VLAN_MGT
VLAN_APP --&gt; APP1
VLAN_APP --&gt; APP2
VLAN_APP --&gt; APP3
VLAN_DB --&gt; DB_MASTER
VLAN_DB --&gt; DB_SLAVE
VLAN_DB --&gt; REDIS
VLAN_MGT --&gt; JUMP
VLAN_MGT --&gt; MONITOR
VLAN_MGT --&gt; LOG</code></pre>
<h5 data-number="7.0.0.0.35" id="防火墙策略配置"><span
class="header-section-number">7.0.0.0.35</span> 5.2 防火墙策略配置</h5>
<h5 data-number="7.0.0.0.36" id="边界防火墙策略"><span
class="header-section-number">7.0.0.0.36</span> 5.2.1
边界防火墙策略</h5>
<div class="sourceCode" id="cb150"><pre
class="sourceCode bash"><code class="sourceCode bash"><span id="cb150-1"><a href="#cb150-1" aria-hidden="true" tabindex="-1"></a><span class="co">##### 允许HTTPS访问</span></span>
<span id="cb150-2"><a href="#cb150-2" aria-hidden="true" tabindex="-1"></a><span class="ex">iptables</span> <span class="at">-A</span> INPUT <span class="at">-p</span> tcp <span class="at">--dport</span> 443 <span class="at">-j</span> ACCEPT</span>
<span id="cb150-3"><a href="#cb150-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb150-4"><a href="#cb150-4" aria-hidden="true" tabindex="-1"></a><span class="co">##### 允许HTTP重定向到HTTPS</span></span>
<span id="cb150-5"><a href="#cb150-5" aria-hidden="true" tabindex="-1"></a><span class="ex">iptables</span> <span class="at">-A</span> INPUT <span class="at">-p</span> tcp <span class="at">--dport</span> 80 <span class="at">-j</span> ACCEPT</span>
<span id="cb150-6"><a href="#cb150-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb150-7"><a href="#cb150-7" aria-hidden="true" tabindex="-1"></a><span class="co">##### 禁止直接数据库访问</span></span>
<span id="cb150-8"><a href="#cb150-8" aria-hidden="true" tabindex="-1"></a><span class="ex">iptables</span> <span class="at">-A</span> INPUT <span class="at">-p</span> tcp <span class="at">--dport</span> 5432 <span class="at">-s</span> ! 192.168.1.0/24 <span class="at">-j</span> DROP</span>
<span id="cb150-9"><a href="#cb150-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb150-10"><a href="#cb150-10" aria-hidden="true" tabindex="-1"></a><span class="co">##### 允许内网SSH管理</span></span>
<span id="cb150-11"><a href="#cb150-11" aria-hidden="true" tabindex="-1"></a><span class="ex">iptables</span> <span class="at">-A</span> INPUT <span class="at">-p</span> tcp <span class="at">--dport</span> 22 <span class="at">-s</span> 192.168.100.0/24 <span class="at">-j</span> ACCEPT</span>
<span id="cb150-12"><a href="#cb150-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb150-13"><a href="#cb150-13" aria-hidden="true" tabindex="-1"></a><span class="co">##### 拒绝其他所有入站连接</span></span>
<span id="cb150-14"><a href="#cb150-14" aria-hidden="true" tabindex="-1"></a><span class="ex">iptables</span> <span class="at">-A</span> INPUT <span class="at">-j</span> DROP</span></code></pre></div>
<h5 data-number="7.0.0.0.37" id="应用层防火墙策略"><span
class="header-section-number">7.0.0.0.37</span> 5.2.2
应用层防火墙策略</h5>
<div class="sourceCode" id="cb151"><pre
class="sourceCode bash"><code class="sourceCode bash"><span id="cb151-1"><a href="#cb151-1" aria-hidden="true" tabindex="-1"></a><span class="co">##### 只允许来自DMZ区的连接</span></span>
<span id="cb151-2"><a href="#cb151-2" aria-hidden="true" tabindex="-1"></a><span class="ex">iptables</span> <span class="at">-A</span> INPUT <span class="at">-s</span> 192.168.1.0/24 <span class="at">-j</span> ACCEPT</span>
<span id="cb151-3"><a href="#cb151-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb151-4"><a href="#cb151-4" aria-hidden="true" tabindex="-1"></a><span class="co">##### 允许访问数据库</span></span>
<span id="cb151-5"><a href="#cb151-5" aria-hidden="true" tabindex="-1"></a><span class="ex">iptables</span> <span class="at">-A</span> OUTPUT <span class="at">-d</span> 192.168.2.0/24 <span class="at">-p</span> tcp <span class="at">--dport</span> 5432 <span class="at">-j</span> ACCEPT</span>
<span id="cb151-6"><a href="#cb151-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb151-7"><a href="#cb151-7" aria-hidden="true" tabindex="-1"></a><span class="co">##### 允许访问Redis</span></span>
<span id="cb151-8"><a href="#cb151-8" aria-hidden="true" tabindex="-1"></a><span class="ex">iptables</span> <span class="at">-A</span> OUTPUT <span class="at">-d</span> 192.168.2.0/24 <span class="at">-p</span> tcp <span class="at">--dport</span> 6379 <span class="at">-j</span> ACCEPT</span>
<span id="cb151-9"><a href="#cb151-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb151-10"><a href="#cb151-10" aria-hidden="true" tabindex="-1"></a><span class="co">##### 允许DNS查询</span></span>
<span id="cb151-11"><a href="#cb151-11" aria-hidden="true" tabindex="-1"></a><span class="ex">iptables</span> <span class="at">-A</span> OUTPUT <span class="at">-p</span> udp <span class="at">--dport</span> 53 <span class="at">-j</span> ACCEPT</span>
<span id="cb151-12"><a href="#cb151-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb151-13"><a href="#cb151-13" aria-hidden="true" tabindex="-1"></a><span class="co">##### 拒绝其他出站连接</span></span>
<span id="cb151-14"><a href="#cb151-14" aria-hidden="true" tabindex="-1"></a><span class="ex">iptables</span> <span class="at">-A</span> OUTPUT <span class="at">-j</span> DROP</span></code></pre></div>
<h5 data-number="7.0.0.0.38" id="入侵检测与防护"><span
class="header-section-number">7.0.0.0.38</span> 5.3 入侵检测与防护</h5>
<h5 data-number="7.0.0.0.39" id="idsips规则配置"><span
class="header-section-number">7.0.0.0.39</span> 5.3.1
IDS/IPS规则配置</h5>
<div class="sourceCode" id="cb152"><pre
class="sourceCode bash"><code class="sourceCode bash"><span id="cb152-1"><a href="#cb152-1" aria-hidden="true" tabindex="-1"></a><span class="co">##### Suricata规则示例</span></span>
<span id="cb152-2"><a href="#cb152-2" aria-hidden="true" tabindex="-1"></a><span class="ex">alert</span> tcp any any <span class="at">-</span><span class="op">&gt;</span> <span class="va">$HOME_NET</span> 443 <span class="er">(</span><span class="ex">msg:</span><span class="st">&quot;Suspicious HTTPS traffic&quot;</span><span class="kw">;</span> <span class="dt">\</span></span>
<span id="cb152-3"><a href="#cb152-3" aria-hidden="true" tabindex="-1"></a> <span class="ex">content:</span><span class="st">&quot;POST&quot;</span><span class="kw">;</span> <span class="ex">http_method</span><span class="kw">;</span> <span class="ex">content:</span><span class="st">&quot;/api/admin&quot;</span><span class="kw">;</span> <span class="ex">http_uri</span><span class="kw">;</span> <span class="dt">\</span></span>
<span id="cb152-4"><a href="#cb152-4" aria-hidden="true" tabindex="-1"></a> <span class="ex">threshold:type</span> limit, track by_src, count 10, seconds 60<span class="kw">;</span> <span class="dt">\</span></span>
<span id="cb152-5"><a href="#cb152-5" aria-hidden="true" tabindex="-1"></a> <span class="ex">sid:10001</span><span class="kw">;</span> <span class="ex">rev:1</span><span class="kw">;)</span></span>
<span id="cb152-6"><a href="#cb152-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb152-7"><a href="#cb152-7" aria-hidden="true" tabindex="-1"></a><span class="ex">alert</span> tcp any any <span class="at">-</span><span class="op">&gt;</span> <span class="va">$HOME_NET</span> 5432 <span class="er">(</span><span class="ex">msg:</span><span class="st">&quot;Direct database access attempt&quot;</span><span class="kw">;</span> <span class="dt">\</span></span>
<span id="cb152-8"><a href="#cb152-8" aria-hidden="true" tabindex="-1"></a> <span class="ex">content:</span><span class="st">&quot;SELECT&quot;</span><span class="kw">;</span> <span class="ex">content:</span><span class="st">&quot;FROM&quot;</span><span class="kw">;</span> <span class="ex">distance:0</span><span class="kw">;</span> <span class="dt">\</span></span>
<span id="cb152-9"><a href="#cb152-9" aria-hidden="true" tabindex="-1"></a> <span class="ex">sid:10002</span><span class="kw">;</span> <span class="ex">rev:1</span><span class="kw">;)</span></span>
<span id="cb152-10"><a href="#cb152-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb152-11"><a href="#cb152-11" aria-hidden="true" tabindex="-1"></a><span class="ex">alert</span> tcp any any <span class="at">-</span><span class="op">&gt;</span> <span class="va">$HOME_NET</span> any <span class="er">(</span><span class="ex">msg:</span><span class="st">&quot;Brute force attack detected&quot;</span><span class="kw">;</span> <span class="dt">\</span></span>
<span id="cb152-12"><a href="#cb152-12" aria-hidden="true" tabindex="-1"></a> <span class="ex">flags:S</span><span class="kw">;</span> <span class="ex">threshold:type</span> threshold, track by_src, count 100, seconds 60<span class="kw">;</span> <span class="dt">\</span></span>
<span id="cb152-13"><a href="#cb152-13" aria-hidden="true" tabindex="-1"></a> <span class="ex">sid:10003</span><span class="kw">;</span> <span class="ex">rev:1</span><span class="kw">;)</span></span></code></pre></div>
<h5 data-number="7.0.0.0.40" id="六数据安全设计"><span
class="header-section-number">7.0.0.0.40</span> 六、数据安全设计</h5>
<h5 data-number="7.0.0.0.41" id="数据分类分级"><span
class="header-section-number">7.0.0.0.41</span> 6.1 数据分类分级</h5>
<h5 data-number="7.0.0.0.42" id="数据分类标准"><span
class="header-section-number">7.0.0.0.42</span> 6.1.1 数据分类标准</h5>
<pre class="mermaid"><code>graph TB
subgraph &quot;数据分类&quot;
TOP_SECRET[绝密级&lt;br/&gt;核心商业机密]
SECRET[机密级&lt;br/&gt;重要业务数据]
INTERNAL[内部级&lt;br/&gt;一般业务数据]
PUBLIC[公开级&lt;br/&gt;公开业务数据]
end
subgraph &quot;水务业务数据&quot;
CUSTOMER[客户身份信息&lt;br/&gt;机密级]
METER[水表计量数据&lt;br/&gt;内部级]
BILLING[收费账务数据&lt;br/&gt;机密级]
REPORT[统计报表数据&lt;br/&gt;内部级]
CONFIG[系统配置数据&lt;br/&gt;内部级]
LOG[日志审计数据&lt;br/&gt;内部级]
end
subgraph &quot;保护措施&quot;
ENC_HIGH[强加密&lt;br/&gt;SM4+数字签名]
ENC_MID[加密存储&lt;br/&gt;SM4算法]
ENC_LOW[访问控制&lt;br/&gt;权限管理]
ENC_NONE[公开访问&lt;br/&gt;无特殊保护]
end
TOP_SECRET --&gt; ENC_HIGH
SECRET --&gt; ENC_MID
INTERNAL --&gt; ENC_LOW
PUBLIC --&gt; ENC_NONE
CUSTOMER --&gt; SECRET
BILLING --&gt; SECRET
METER --&gt; INTERNAL
REPORT --&gt; INTERNAL
CONFIG --&gt; INTERNAL
LOG --&gt; INTERNAL</code></pre>
<h5 data-number="7.0.0.0.43" id="数据保护策略"><span
class="header-section-number">7.0.0.0.43</span> 6.1.2 数据保护策略</h5>
<div class="sourceCode" id="cb154"><pre
class="sourceCode sql"><code class="sourceCode sql"><span id="cb154-1"><a href="#cb154-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- 客户敏感信息表(机密级)</span></span>
<span id="cb154-2"><a href="#cb154-2" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">TABLE</span> water_customer_sensitive (</span>
<span id="cb154-3"><a href="#cb154-3" aria-hidden="true" tabindex="-1"></a> <span class="kw">id</span> SERIAL <span class="kw">PRIMARY</span> <span class="kw">KEY</span>,</span>
<span id="cb154-4"><a href="#cb154-4" aria-hidden="true" tabindex="-1"></a> customer_id BIGINT <span class="kw">NOT</span> <span class="kw">NULL</span>,</span>
<span id="cb154-5"><a href="#cb154-5" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 身份证号:强加密存储</span></span>
<span id="cb154-6"><a href="#cb154-6" aria-hidden="true" tabindex="-1"></a> id_card_encrypted <span class="dt">VARCHAR</span>(<span class="dv">200</span>) ENCRYPTED <span class="kw">WITH</span> (</span>
<span id="cb154-7"><a href="#cb154-7" aria-hidden="true" tabindex="-1"></a> COLUMN_ENCRYPTION_KEY <span class="op">=</span> customer_sensitive_key,</span>
<span id="cb154-8"><a href="#cb154-8" aria-hidden="true" tabindex="-1"></a> ENCRYPTION_TYPE <span class="op">=</span> RANDOMIZED,</span>
<span id="cb154-9"><a href="#cb154-9" aria-hidden="true" tabindex="-1"></a> ALGORITHM <span class="op">=</span> <span class="st">&#39;SM4-CTR&#39;</span></span>
<span id="cb154-10"><a href="#cb154-10" aria-hidden="true" tabindex="-1"></a> ),</span>
<span id="cb154-11"><a href="#cb154-11" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 银行卡号:强加密存储</span></span>
<span id="cb154-12"><a href="#cb154-12" aria-hidden="true" tabindex="-1"></a> bank_account_encrypted <span class="dt">VARCHAR</span>(<span class="dv">200</span>) ENCRYPTED <span class="kw">WITH</span> (</span>
<span id="cb154-13"><a href="#cb154-13" aria-hidden="true" tabindex="-1"></a> COLUMN_ENCRYPTION_KEY <span class="op">=</span> customer_sensitive_key,</span>
<span id="cb154-14"><a href="#cb154-14" aria-hidden="true" tabindex="-1"></a> ENCRYPTION_TYPE <span class="op">=</span> RANDOMIZED,</span>
<span id="cb154-15"><a href="#cb154-15" aria-hidden="true" tabindex="-1"></a> ALGORITHM <span class="op">=</span> <span class="st">&#39;SM4-CTR&#39;</span></span>
<span id="cb154-16"><a href="#cb154-16" aria-hidden="true" tabindex="-1"></a> ),</span>
<span id="cb154-17"><a href="#cb154-17" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 手机号:确定性加密,支持查询</span></span>
<span id="cb154-18"><a href="#cb154-18" aria-hidden="true" tabindex="-1"></a> phone_encrypted <span class="dt">VARCHAR</span>(<span class="dv">200</span>) ENCRYPTED <span class="kw">WITH</span> (</span>
<span id="cb154-19"><a href="#cb154-19" aria-hidden="true" tabindex="-1"></a> COLUMN_ENCRYPTION_KEY <span class="op">=</span> customer_sensitive_key,</span>
<span id="cb154-20"><a href="#cb154-20" aria-hidden="true" tabindex="-1"></a> ENCRYPTION_TYPE <span class="op">=</span> DETERMINISTIC,</span>
<span id="cb154-21"><a href="#cb154-21" aria-hidden="true" tabindex="-1"></a> ALGORITHM <span class="op">=</span> <span class="st">&#39;SM4-CTR&#39;</span></span>
<span id="cb154-22"><a href="#cb154-22" aria-hidden="true" tabindex="-1"></a> ),</span>
<span id="cb154-23"><a href="#cb154-23" aria-hidden="true" tabindex="-1"></a> created_at <span class="dt">TIMESTAMP</span> <span class="kw">DEFAULT</span> <span class="fu">CURRENT_TIMESTAMP</span>,</span>
<span id="cb154-24"><a href="#cb154-24" aria-hidden="true" tabindex="-1"></a> updated_at <span class="dt">TIMESTAMP</span> <span class="kw">DEFAULT</span> <span class="fu">CURRENT_TIMESTAMP</span></span>
<span id="cb154-25"><a href="#cb154-25" aria-hidden="true" tabindex="-1"></a>);</span>
<span id="cb154-26"><a href="#cb154-26" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb154-27"><a href="#cb154-27" aria-hidden="true" tabindex="-1"></a><span class="co">-- 创建行级安全策略</span></span>
<span id="cb154-28"><a href="#cb154-28" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> POLICY customer_sensitive_policy <span class="kw">ON</span> water_customer_sensitive</span>
<span id="cb154-29"><a href="#cb154-29" aria-hidden="true" tabindex="-1"></a><span class="cf">FOR</span> <span class="kw">ALL</span> <span class="kw">TO</span> water_customer_service_role</span>
<span id="cb154-30"><a href="#cb154-30" aria-hidden="true" tabindex="-1"></a><span class="kw">USING</span> (</span>
<span id="cb154-31"><a href="#cb154-31" aria-hidden="true" tabindex="-1"></a> customer_id <span class="kw">IN</span> (</span>
<span id="cb154-32"><a href="#cb154-32" aria-hidden="true" tabindex="-1"></a> <span class="kw">SELECT</span> customer_id <span class="kw">FROM</span> water_customer </span>
<span id="cb154-33"><a href="#cb154-33" aria-hidden="true" tabindex="-1"></a> <span class="kw">WHERE</span> tenant_id <span class="op">=</span> current_setting(<span class="st">&#39;app.current_tenant_id&#39;</span>):<span class="ch">:bigint</span></span>
<span id="cb154-34"><a href="#cb154-34" aria-hidden="true" tabindex="-1"></a> )</span>
<span id="cb154-35"><a href="#cb154-35" aria-hidden="true" tabindex="-1"></a>);</span></code></pre></div>
<h5 data-number="7.0.0.0.44" id="数据备份与恢复安全"><span
class="header-section-number">7.0.0.0.44</span> 6.2
数据备份与恢复安全</h5>
<h5 data-number="7.0.0.0.45" id="备份加密策略"><span
class="header-section-number">7.0.0.0.45</span> 6.2.1 备份加密策略</h5>
<div class="sourceCode" id="cb155"><pre
class="sourceCode bash"><code class="sourceCode bash"><span id="cb155-1"><a href="#cb155-1" aria-hidden="true" tabindex="-1"></a><span class="co">#!/bin/bash</span></span>
<span id="cb155-2"><a href="#cb155-2" aria-hidden="true" tabindex="-1"></a><span class="co">##### OpenGauss安全备份脚本</span></span>
<span id="cb155-3"><a href="#cb155-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb155-4"><a href="#cb155-4" aria-hidden="true" tabindex="-1"></a><span class="co">##### 设置备份参数</span></span>
<span id="cb155-5"><a href="#cb155-5" aria-hidden="true" tabindex="-1"></a><span class="va">BACKUP_DIR</span><span class="op">=</span><span class="st">&quot;/backup/opengauss&quot;</span></span>
<span id="cb155-6"><a href="#cb155-6" aria-hidden="true" tabindex="-1"></a><span class="va">DATE</span><span class="op">=</span><span class="va">$(</span><span class="fu">date</span> +%Y%m%d_%H%M%S<span class="va">)</span></span>
<span id="cb155-7"><a href="#cb155-7" aria-hidden="true" tabindex="-1"></a><span class="va">BACKUP_FILE</span><span class="op">=</span><span class="st">&quot;water_biz_backup_</span><span class="va">${DATE}</span><span class="st">.tar.gz&quot;</span></span>
<span id="cb155-8"><a href="#cb155-8" aria-hidden="true" tabindex="-1"></a><span class="va">ENCRYPT_KEY</span><span class="op">=</span><span class="st">&quot;/etc/opengauss/backup_key.key&quot;</span></span>
<span id="cb155-9"><a href="#cb155-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb155-10"><a href="#cb155-10" aria-hidden="true" tabindex="-1"></a><span class="co">##### 创建加密备份</span></span>
<span id="cb155-11"><a href="#cb155-11" aria-hidden="true" tabindex="-1"></a><span class="ex">gs_backup</span> <span class="at">-D</span> <span class="va">$GAUSSDATA</span> <span class="at">-U</span> gaussdb <span class="dt">\</span></span>
<span id="cb155-12"><a href="#cb155-12" aria-hidden="true" tabindex="-1"></a> <span class="at">--encrypt</span> <span class="at">--encrypt-key-file</span><span class="op">=</span><span class="va">$ENCRYPT_KEY</span> <span class="dt">\</span></span>
<span id="cb155-13"><a href="#cb155-13" aria-hidden="true" tabindex="-1"></a> <span class="at">--backup-format</span><span class="op">=</span>tar <span class="dt">\</span></span>
<span id="cb155-14"><a href="#cb155-14" aria-hidden="true" tabindex="-1"></a> <span class="at">--backup-path</span><span class="op">=</span><span class="va">$BACKUP_DIR</span>/<span class="va">$BACKUP_FILE</span></span>
<span id="cb155-15"><a href="#cb155-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb155-16"><a href="#cb155-16" aria-hidden="true" tabindex="-1"></a><span class="co">##### 验证备份完整性</span></span>
<span id="cb155-17"><a href="#cb155-17" aria-hidden="true" tabindex="-1"></a><span class="ex">openssl</span> dgst <span class="at">-sm3</span> <span class="va">$BACKUP_DIR</span>/<span class="va">$BACKUP_FILE</span> <span class="op">&gt;</span> <span class="va">$BACKUP_DIR</span>/<span class="va">$BACKUP_FILE</span>.sm3</span>
<span id="cb155-18"><a href="#cb155-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb155-19"><a href="#cb155-19" aria-hidden="true" tabindex="-1"></a><span class="co">##### 安全传输到异地备份中心</span></span>
<span id="cb155-20"><a href="#cb155-20" aria-hidden="true" tabindex="-1"></a><span class="fu">rsync</span> <span class="at">-avz</span> <span class="at">--delete</span> <span class="va">$BACKUP_DIR</span>/ backup-server:/backup/remote/</span></code></pre></div>
<h5 data-number="7.0.0.0.46" id="数据恢复流程"><span
class="header-section-number">7.0.0.0.46</span> 6.2.2 数据恢复流程</h5>
<div class="sourceCode" id="cb156"><pre
class="sourceCode sql"><code class="sourceCode sql"><span id="cb156-1"><a href="#cb156-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- 恢复前验证备份完整性</span></span>
<span id="cb156-2"><a href="#cb156-2" aria-hidden="true" tabindex="-1"></a><span class="co">-- 1. 验证SM3摘要</span></span>
<span id="cb156-3"><a href="#cb156-3" aria-hidden="true" tabindex="-1"></a><span class="co">-- 2. 解密备份文件</span></span>
<span id="cb156-4"><a href="#cb156-4" aria-hidden="true" tabindex="-1"></a><span class="co">-- 3. 验证数据库结构</span></span>
<span id="cb156-5"><a href="#cb156-5" aria-hidden="true" tabindex="-1"></a><span class="co">-- 4. 执行恢复操作</span></span>
<span id="cb156-6"><a href="#cb156-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb156-7"><a href="#cb156-7" aria-hidden="true" tabindex="-1"></a><span class="co">-- 创建恢复测试环境</span></span>
<span id="cb156-8"><a href="#cb156-8" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">DATABASE</span> water_biz_recovery_test;</span>
<span id="cb156-9"><a href="#cb156-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb156-10"><a href="#cb156-10" aria-hidden="true" tabindex="-1"></a><span class="co">-- 恢复数据到测试环境</span></span>
<span id="cb156-11"><a href="#cb156-11" aria-hidden="true" tabindex="-1"></a>\c water_biz_recovery_test;</span>
<span id="cb156-12"><a href="#cb156-12" aria-hidden="true" tabindex="-1"></a><span class="co">-- 执行恢复SQL脚本</span></span>
<span id="cb156-13"><a href="#cb156-13" aria-hidden="true" tabindex="-1"></a>\i <span class="op">/</span><span class="kw">backup</span><span class="op">/</span>water_biz_recovery.sql;</span>
<span id="cb156-14"><a href="#cb156-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb156-15"><a href="#cb156-15" aria-hidden="true" tabindex="-1"></a><span class="co">-- 验证数据完整性</span></span>
<span id="cb156-16"><a href="#cb156-16" aria-hidden="true" tabindex="-1"></a><span class="kw">SELECT</span> <span class="fu">COUNT</span>(<span class="op">*</span>) <span class="kw">FROM</span> water_customer;</span>
<span id="cb156-17"><a href="#cb156-17" aria-hidden="true" tabindex="-1"></a><span class="kw">SELECT</span> <span class="fu">COUNT</span>(<span class="op">*</span>) <span class="kw">FROM</span> water_meter_reading;</span>
<span id="cb156-18"><a href="#cb156-18" aria-hidden="true" tabindex="-1"></a><span class="kw">SELECT</span> <span class="fu">COUNT</span>(<span class="op">*</span>) <span class="kw">FROM</span> water_billing;</span>
<span id="cb156-19"><a href="#cb156-19" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb156-20"><a href="#cb156-20" aria-hidden="true" tabindex="-1"></a><span class="co">-- 验证加密数据</span></span>
<span id="cb156-21"><a href="#cb156-21" aria-hidden="true" tabindex="-1"></a><span class="kw">SELECT</span> <span class="kw">id</span>, </span>
<span id="cb156-22"><a href="#cb156-22" aria-hidden="true" tabindex="-1"></a> pgp_sym_decrypt(id_card_encrypted, <span class="st">&#39;encryption_key&#39;</span>) <span class="kw">as</span> id_card</span>
<span id="cb156-23"><a href="#cb156-23" aria-hidden="true" tabindex="-1"></a><span class="kw">FROM</span> water_customer_sensitive <span class="kw">LIMIT</span> <span class="dv">1</span>;</span></code></pre></div>
<h5 data-number="7.0.0.0.47" id="数据销毁与清理"><span
class="header-section-number">7.0.0.0.47</span> 6.3 数据销毁与清理</h5>
<h5 data-number="7.0.0.0.48" id="安全数据销毁"><span
class="header-section-number">7.0.0.0.48</span> 6.3.1 安全数据销毁</h5>
<div class="sourceCode" id="cb157"><pre
class="sourceCode sql"><code class="sourceCode sql"><span id="cb157-1"><a href="#cb157-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- 创建安全数据销毁函数</span></span>
<span id="cb157-2"><a href="#cb157-2" aria-hidden="true" tabindex="-1"></a><span class="kw">CREATE</span> <span class="kw">OR</span> <span class="kw">REPLACE</span> <span class="kw">FUNCTION</span> secure_data_destroy(table_name TEXT, where_clause TEXT)</span>
<span id="cb157-3"><a href="#cb157-3" aria-hidden="true" tabindex="-1"></a>RETURNS <span class="dt">BOOLEAN</span> <span class="kw">AS</span> $$</span>
<span id="cb157-4"><a href="#cb157-4" aria-hidden="true" tabindex="-1"></a><span class="kw">DECLARE</span></span>
<span id="cb157-5"><a href="#cb157-5" aria-hidden="true" tabindex="-1"></a> sql_cmd TEXT;</span>
<span id="cb157-6"><a href="#cb157-6" aria-hidden="true" tabindex="-1"></a> affected_rows <span class="dt">INTEGER</span>;</span>
<span id="cb157-7"><a href="#cb157-7" aria-hidden="true" tabindex="-1"></a><span class="cf">BEGIN</span></span>
<span id="cb157-8"><a href="#cb157-8" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 构建删除SQL</span></span>
<span id="cb157-9"><a href="#cb157-9" aria-hidden="true" tabindex="-1"></a> sql_cmd <span class="op">:=</span> <span class="st">&#39;DELETE FROM &#39;</span> <span class="op">||</span> table_name <span class="op">||</span> <span class="st">&#39; WHERE &#39;</span> <span class="op">||</span> where_clause;</span>
<span id="cb157-10"><a href="#cb157-10" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb157-11"><a href="#cb157-11" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 执行删除</span></span>
<span id="cb157-12"><a href="#cb157-12" aria-hidden="true" tabindex="-1"></a> <span class="kw">EXECUTE</span> sql_cmd;</span>
<span id="cb157-13"><a href="#cb157-13" aria-hidden="true" tabindex="-1"></a> GET DIAGNOSTICS affected_rows <span class="op">=</span> ROW_COUNT;</span>
<span id="cb157-14"><a href="#cb157-14" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb157-15"><a href="#cb157-15" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 记录审计日志</span></span>
<span id="cb157-16"><a href="#cb157-16" aria-hidden="true" tabindex="-1"></a> <span class="kw">INSERT</span> <span class="kw">INTO</span> data_destroy_audit (</span>
<span id="cb157-17"><a href="#cb157-17" aria-hidden="true" tabindex="-1"></a> table_name, where_clause, affected_rows, </span>
<span id="cb157-18"><a href="#cb157-18" aria-hidden="true" tabindex="-1"></a> <span class="kw">operator</span>, operation_time</span>
<span id="cb157-19"><a href="#cb157-19" aria-hidden="true" tabindex="-1"></a> ) <span class="kw">VALUES</span> (</span>
<span id="cb157-20"><a href="#cb157-20" aria-hidden="true" tabindex="-1"></a> table_name, where_clause, affected_rows,</span>
<span id="cb157-21"><a href="#cb157-21" aria-hidden="true" tabindex="-1"></a> current_user, <span class="fu">current_timestamp</span></span>
<span id="cb157-22"><a href="#cb157-22" aria-hidden="true" tabindex="-1"></a> );</span>
<span id="cb157-23"><a href="#cb157-23" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb157-24"><a href="#cb157-24" aria-hidden="true" tabindex="-1"></a> <span class="co">-- 执行VACUUM清理物理空间</span></span>
<span id="cb157-25"><a href="#cb157-25" aria-hidden="true" tabindex="-1"></a> <span class="kw">EXECUTE</span> <span class="st">&#39;VACUUM FULL &#39;</span> <span class="op">||</span> table_name;</span>
<span id="cb157-26"><a href="#cb157-26" aria-hidden="true" tabindex="-1"></a> </span>
<span id="cb157-27"><a href="#cb157-27" aria-hidden="true" tabindex="-1"></a> <span class="kw">RETURN</span> <span class="kw">TRUE</span>;</span>
<span id="cb157-28"><a href="#cb157-28" aria-hidden="true" tabindex="-1"></a><span class="kw">EXCEPTION</span></span>
<span id="cb157-29"><a href="#cb157-29" aria-hidden="true" tabindex="-1"></a> <span class="cf">WHEN</span> OTHERS <span class="cf">THEN</span></span>
<span id="cb157-30"><a href="#cb157-30" aria-hidden="true" tabindex="-1"></a> RAISE <span class="kw">EXCEPTION</span> <span class="st">&#39;数据销毁失败: %&#39;</span>, SQLERRM;</span>
<span id="cb157-31"><a href="#cb157-31" aria-hidden="true" tabindex="-1"></a> <span class="kw">RETURN</span> <span class="kw">FALSE</span>;</span>
<span id="cb157-32"><a href="#cb157-32" aria-hidden="true" tabindex="-1"></a><span class="cf">END</span>;</span>
<span id="cb157-33"><a href="#cb157-33" aria-hidden="true" tabindex="-1"></a>$$ LANGUAGE plpgsql;</span>
<span id="cb157-34"><a href="#cb157-34" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb157-35"><a href="#cb157-35" aria-hidden="true" tabindex="-1"></a><span class="co">-- 示例销毁3年前的历史数据</span></span>
<span id="cb157-36"><a href="#cb157-36" aria-hidden="true" tabindex="-1"></a><span class="kw">SELECT</span> secure_data_destroy(</span>
<span id="cb157-37"><a href="#cb157-37" aria-hidden="true" tabindex="-1"></a> <span class="st">&#39;water_meter_reading_history&#39;</span>,</span>
<span id="cb157-38"><a href="#cb157-38" aria-hidden="true" tabindex="-1"></a> <span class="st">&#39;reading_date &lt; CURRENT_DATE - INTERVAL </span><span class="ch">&#39;&#39;</span><span class="st">3 years</span><span class="ch">&#39;&#39;</span><span class="st">&#39;</span></span>
<span id="cb157-39"><a href="#cb157-39" aria-hidden="true" tabindex="-1"></a>);</span></code></pre></div>
<h5 data-number="7.0.0.0.49" id="七运维安全设计"><span
class="header-section-number">7.0.0.0.49</span> 七、运维安全设计</h5>
<h5 data-number="7.0.0.0.50" id="安全监控体系"><span
class="header-section-number">7.0.0.0.50</span> 7.1 安全监控体系</h5>
<h5 data-number="7.0.0.0.51" id="安全监控架构"><span
class="header-section-number">7.0.0.0.51</span> 7.1.1 安全监控架构</h5>
<pre class="mermaid"><code>graph TB
subgraph &quot;数据采集层&quot;
AGENT1[系统日志采集]
AGENT2[应用日志采集]
AGENT3[数据库日志采集]
AGENT4[网络流量采集]
end
subgraph &quot;数据处理层&quot;
KAFKA[消息队列&lt;br/&gt;Kafka集群]
STREAM[流处理&lt;br/&gt;Flink/Storm]
ETL[数据清洗&lt;br/&gt;Logstash]
end
subgraph &quot;存储分析层&quot;
ES[Elasticsearch&lt;br/&gt;日志存储]
SIEM[安全信息事件管理&lt;br/&gt;SIEM平台]
AI[智能分析&lt;br/&gt;机器学习]
end
subgraph &quot;可视化层&quot;
DASHBOARD[监控仪表盘&lt;br/&gt;Grafana]
ALERT[告警系统&lt;br/&gt;AlertManager]
REPORT[安全报告&lt;br/&gt;自动生成]
end
AGENT1 --&gt; KAFKA
AGENT2 --&gt; KAFKA
AGENT3 --&gt; KAFKA
AGENT4 --&gt; KAFKA
KAFKA --&gt; STREAM
STREAM --&gt; ETL
ETL --&gt; ES
ES --&gt; SIEM
SIEM --&gt; AI
AI --&gt; DASHBOARD
DASHBOARD --&gt; ALERT
ALERT --&gt; REPORT</code></pre>
<h5 data-number="7.0.0.0.52" id="安全事件检测规则"><span
class="header-section-number">7.0.0.0.52</span> 7.1.2
安全事件检测规则</h5>
<div class="sourceCode" id="cb159"><pre
class="sourceCode yaml"><code class="sourceCode yaml"><span id="cb159-1"><a href="#cb159-1" aria-hidden="true" tabindex="-1"></a><span class="co">##### 安全事件检测规则配置</span></span>
<span id="cb159-2"><a href="#cb159-2" aria-hidden="true" tabindex="-1"></a><span class="fu">security_rules</span><span class="kw">:</span></span>
<span id="cb159-3"><a href="#cb159-3" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="fu">name</span><span class="kw">:</span><span class="at"> </span><span class="st">&quot;暴力破解检测&quot;</span></span>
<span id="cb159-4"><a href="#cb159-4" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">type</span><span class="kw">:</span><span class="at"> </span><span class="st">&quot;authentication&quot;</span></span>
<span id="cb159-5"><a href="#cb159-5" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">condition</span><span class="kw">:</span><span class="at"> </span><span class="st">&quot;failed_login_count &gt; 5 in 5 minutes&quot;</span></span>
<span id="cb159-6"><a href="#cb159-6" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">severity</span><span class="kw">:</span><span class="at"> </span><span class="st">&quot;high&quot;</span></span>
<span id="cb159-7"><a href="#cb159-7" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">action</span><span class="kw">:</span><span class="at"> </span><span class="kw">[</span><span class="st">&quot;block_ip&quot;</span><span class="kw">,</span><span class="at"> </span><span class="st">&quot;send_alert&quot;</span><span class="kw">]</span></span>
<span id="cb159-8"><a href="#cb159-8" aria-hidden="true" tabindex="-1"></a><span class="at"> </span></span>
<span id="cb159-9"><a href="#cb159-9" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="fu">name</span><span class="kw">:</span><span class="at"> </span><span class="st">&quot;异常数据访问&quot;</span></span>
<span id="cb159-10"><a href="#cb159-10" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">type</span><span class="kw">:</span><span class="at"> </span><span class="st">&quot;data_access&quot;</span></span>
<span id="cb159-11"><a href="#cb159-11" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">condition</span><span class="kw">:</span><span class="at"> </span><span class="st">&quot;query_count &gt; 1000 in 1 minute&quot;</span></span>
<span id="cb159-12"><a href="#cb159-12" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">severity</span><span class="kw">:</span><span class="at"> </span><span class="st">&quot;medium&quot;</span></span>
<span id="cb159-13"><a href="#cb159-13" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">action</span><span class="kw">:</span><span class="at"> </span><span class="kw">[</span><span class="st">&quot;rate_limit&quot;</span><span class="kw">,</span><span class="at"> </span><span class="st">&quot;send_alert&quot;</span><span class="kw">]</span></span>
<span id="cb159-14"><a href="#cb159-14" aria-hidden="true" tabindex="-1"></a><span class="at"> </span></span>
<span id="cb159-15"><a href="#cb159-15" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="fu">name</span><span class="kw">:</span><span class="at"> </span><span class="st">&quot;权限提升检测&quot;</span></span>
<span id="cb159-16"><a href="#cb159-16" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">type</span><span class="kw">:</span><span class="at"> </span><span class="st">&quot;privilege_escalation&quot;</span></span>
<span id="cb159-17"><a href="#cb159-17" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">condition</span><span class="kw">:</span><span class="at"> </span><span class="st">&quot;role_change to admin&quot;</span></span>
<span id="cb159-18"><a href="#cb159-18" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">severity</span><span class="kw">:</span><span class="at"> </span><span class="st">&quot;critical&quot;</span></span>
<span id="cb159-19"><a href="#cb159-19" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">action</span><span class="kw">:</span><span class="at"> </span><span class="kw">[</span><span class="st">&quot;block_user&quot;</span><span class="kw">,</span><span class="at"> </span><span class="st">&quot;send_alert&quot;</span><span class="kw">,</span><span class="at"> </span><span class="st">&quot;create_incident&quot;</span><span class="kw">]</span></span>
<span id="cb159-20"><a href="#cb159-20" aria-hidden="true" tabindex="-1"></a><span class="at"> </span></span>
<span id="cb159-21"><a href="#cb159-21" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="kw">-</span><span class="at"> </span><span class="fu">name</span><span class="kw">:</span><span class="at"> </span><span class="st">&quot;异常时间访问&quot;</span></span>
<span id="cb159-22"><a href="#cb159-22" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">type</span><span class="kw">:</span><span class="at"> </span><span class="st">&quot;abnormal_access&quot;</span></span>
<span id="cb159-23"><a href="#cb159-23" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">condition</span><span class="kw">:</span><span class="at"> </span><span class="st">&quot;access_time between 22:00 and 06:00&quot;</span></span>
<span id="cb159-24"><a href="#cb159-24" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">severity</span><span class="kw">:</span><span class="at"> </span><span class="st">&quot;medium&quot;</span></span>
<span id="cb159-25"><a href="#cb159-25" aria-hidden="true" tabindex="-1"></a><span class="at"> </span><span class="fu">action</span><span class="kw">:</span><span class="at"> </span><span class="kw">[</span><span class="st">&quot;log_event&quot;</span><span class="kw">,</span><span class="at"> </span><span class="st">&quot;send_alert&quot;</span><span class="kw">]</span></span></code></pre></div>
<h5 data-number="7.0.0.0.53" id="漏洞管理"><span
class="header-section-number">7.0.0.0.53</span> 7.2 漏洞管理</h5>
<h5 data-number="7.0.0.0.54" id="漏洞扫描策略"><span
class="header-section-number">7.0.0.0.54</span> 7.2.1 漏洞扫描策略</h5>
<div class="sourceCode" id="cb160"><pre
class="sourceCode bash"><code class="sourceCode bash"><span id="cb160-1"><a href="#cb160-1" aria-hidden="true" tabindex="-1"></a><span class="co">#!/bin/bash</span></span>
<span id="cb160-2"><a href="#cb160-2" aria-hidden="true" tabindex="-1"></a><span class="co">##### 系统漏洞扫描脚本</span></span>
<span id="cb160-3"><a href="#cb160-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb160-4"><a href="#cb160-4" aria-hidden="true" tabindex="-1"></a><span class="co">##### 扫描操作系统漏洞</span></span>
<span id="cb160-5"><a href="#cb160-5" aria-hidden="true" tabindex="-1"></a><span class="fu">nmap</span> <span class="at">-sV</span> <span class="at">--script</span> vulners localhost</span>
<span id="cb160-6"><a href="#cb160-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb160-7"><a href="#cb160-7" aria-hidden="true" tabindex="-1"></a><span class="co">##### 扫描Web应用漏洞</span></span>
<span id="cb160-8"><a href="#cb160-8" aria-hidden="true" tabindex="-1"></a><span class="ex">nikto</span> <span class="at">-h</span> https://water.example.com <span class="at">-ssl</span></span>
<span id="cb160-9"><a href="#cb160-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb160-10"><a href="#cb160-10" aria-hidden="true" tabindex="-1"></a><span class="co">##### 扫描数据库漏洞</span></span>
<span id="cb160-11"><a href="#cb160-11" aria-hidden="true" tabindex="-1"></a><span class="fu">nmap</span> <span class="at">-p</span> 5432 <span class="at">--script</span> pgsql-brute,pgsql-databases localhost</span>
<span id="cb160-12"><a href="#cb160-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb160-13"><a href="#cb160-13" aria-hidden="true" tabindex="-1"></a><span class="co">##### 生成漏洞报告</span></span>
<span id="cb160-14"><a href="#cb160-14" aria-hidden="true" tabindex="-1"></a><span class="fu">cat</span> <span class="op">&gt;</span> vulnerability_report.html <span class="op">&lt;&lt; EOF</span></span>
<span id="cb160-15"><a href="#cb160-15" aria-hidden="true" tabindex="-1"></a><span class="st">&lt;!DOCTYPE html&gt;</span></span>
<span id="cb160-16"><a href="#cb160-16" aria-hidden="true" tabindex="-1"></a><span class="st">&lt;html&gt;</span></span>
<span id="cb160-17"><a href="#cb160-17" aria-hidden="true" tabindex="-1"></a><span class="st">&lt;head&gt;</span></span>
<span id="cb160-18"><a href="#cb160-18" aria-hidden="true" tabindex="-1"></a><span class="st"> &lt;title&gt;安全漏洞扫描报告&lt;/title&gt;</span></span>
<span id="cb160-19"><a href="#cb160-19" aria-hidden="true" tabindex="-1"></a><span class="st">&lt;/head&gt;</span></span>
<span id="cb160-20"><a href="#cb160-20" aria-hidden="true" tabindex="-1"></a><span class="st">&lt;body&gt;</span></span>
<span id="cb160-21"><a href="#cb160-21" aria-hidden="true" tabindex="-1"></a><span class="st"> &lt;h1&gt;福建水务营收系统漏洞扫描报告&lt;/h1&gt;</span></span>
<span id="cb160-22"><a href="#cb160-22" aria-hidden="true" tabindex="-1"></a><span class="st"> &lt;p&gt;扫描时间:</span><span class="va">$(</span><span class="fu">date</span><span class="va">)</span><span class="st">&lt;/p&gt;</span></span>
<span id="cb160-23"><a href="#cb160-23" aria-hidden="true" tabindex="-1"></a><span class="st"> &lt;h2&gt;高危漏洞&lt;/h2&gt;</span></span>
<span id="cb160-24"><a href="#cb160-24" aria-hidden="true" tabindex="-1"></a><span class="st"> &lt;pre&gt;</span><span class="va">$(</span><span class="fu">grep</span> <span class="at">-i</span> <span class="st">&quot;high\|critical&quot;</span> scan_results.txt<span class="va">)</span><span class="st">&lt;/pre&gt;</span></span>
<span id="cb160-25"><a href="#cb160-25" aria-hidden="true" tabindex="-1"></a><span class="st"> &lt;h2&gt;中危漏洞&lt;/h2&gt;</span></span>
<span id="cb160-26"><a href="#cb160-26" aria-hidden="true" tabindex="-1"></a><span class="st"> &lt;pre&gt;</span><span class="va">$(</span><span class="fu">grep</span> <span class="at">-i</span> <span class="st">&quot;medium&quot;</span> scan_results.txt<span class="va">)</span><span class="st">&lt;/pre&gt;</span></span>
<span id="cb160-27"><a href="#cb160-27" aria-hidden="true" tabindex="-1"></a><span class="st">&lt;/body&gt;</span></span>
<span id="cb160-28"><a href="#cb160-28" aria-hidden="true" tabindex="-1"></a><span class="st">&lt;/html&gt;</span></span>
<span id="cb160-29"><a href="#cb160-29" aria-hidden="true" tabindex="-1"></a><span class="op">EOF</span></span></code></pre></div>
<h5 data-number="7.0.0.0.55" id="补丁管理流程"><span
class="header-section-number">7.0.0.0.55</span> 7.2.2 补丁管理流程</h5>
<pre class="mermaid"><code>graph LR
DISCOVER[漏洞发现] --&gt; ASSESS[风险评估]
ASSESS --&gt; PLAN[补丁计划]
PLAN --&gt; TEST[测试验证]
TEST --&gt; DEPLOY[生产部署]
DEPLOY --&gt; VERIFY[部署验证]
VERIFY --&gt; DOCUMENT[文档记录]
subgraph &quot;评估标准&quot;
HIGH[高危&lt;br/&gt;24小时内]
MEDIUM[中危&lt;br/&gt;7天内]
LOW[低危&lt;br/&gt;30天内]
end
ASSESS --&gt; HIGH
ASSESS --&gt; MEDIUM
ASSESS --&gt; LOW</code></pre>
<h5 data-number="7.0.0.0.56" id="应急响应预案"><span
class="header-section-number">7.0.0.0.56</span> 7.3 应急响应预案</h5>
<h5 data-number="7.0.0.0.57" id="安全事件分级"><span
class="header-section-number">7.0.0.0.57</span> 7.3.1 安全事件分级</h5>
<table>
<colgroup>
<col style="width: 18%" />
<col style="width: 18%" />
<col style="width: 31%" />
<col style="width: 31%" />
</colgroup>
<thead>
<tr>
<th>级别</th>
<th>描述</th>
<th>响应时间</th>
<th>处理措施</th>
</tr>
</thead>
<tbody>
<tr>
<td>P0</td>
<td>系统完全不可用,数据泄露</td>
<td>15分钟</td>
<td>立即启动应急预案,通知管理层</td>
</tr>
<tr>
<td>P1</td>
<td>核心功能受影响,安全风险高</td>
<td>30分钟</td>
<td>启动应急预案,组建应急小组</td>
</tr>
<tr>
<td>P2</td>
<td>部分功能受影响,安全风险中等</td>
<td>2小时</td>
<td>安排专人处理,定期汇报</td>
</tr>
<tr>
<td>P3</td>
<td>轻微影响,安全风险较低</td>
<td>8小时</td>
<td>正常工作时间处理</td>
</tr>
</tbody>
</table>
<h5 data-number="7.0.0.0.58" id="应急响应流程"><span
class="header-section-number">7.0.0.0.58</span> 7.3.2 应急响应流程</h5>
<pre class="mermaid"><code>graph TB
INCIDENT[安全事件发生] --&gt; DETECT[事件检测]
DETECT --&gt; REPORT[事件上报]
REPORT --&gt; ASSESS[影响评估]
ASSESS --&gt; RESPONSE[应急响应]
subgraph &quot;应急响应措施&quot;
ISOLATE[系统隔离]
PRESERVE[证据保全]
RECOVER[系统恢复]
INVESTIGATE[调查分析]
end
subgraph &quot;后续处理&quot;
LESSON[经验总结]
IMPROVE[流程改进]
TRAIN[培训加强]
DOC[文档更新]
end
RESPONSE --&gt; ISOLATE
RESPONSE --&gt; PRESERVE
RESPONSE --&gt; RECOVER
RESPONSE --&gt; INVESTIGATE
INVESTIGATE --&gt; LESSON
LESSON --&gt; IMPROVE
IMPROVE --&gt; TRAIN
TRAIN --&gt; DOC</code></pre>
<h5 data-number="7.0.0.0.59" id="八安全管理制度"><span
class="header-section-number">7.0.0.0.59</span> 八、安全管理制度</h5>
<h5 data-number="7.0.0.0.60" id="安全组织架构"><span
class="header-section-number">7.0.0.0.60</span> 8.1 安全组织架构</h5>
<h5 data-number="7.0.0.0.61" id="安全管理组织"><span
class="header-section-number">7.0.0.0.61</span> 8.1.1 安全管理组织</h5>
<pre class="mermaid"><code>graph TB
CEO[总经理&lt;br/&gt;安全最高责任人]
CISO[信息安全负责人&lt;br/&gt;CISO]
subgraph &quot;安全管理委员会&quot;
IT_DIR[IT总监]
SECURITY_DIR[安全总监]
COMPLIANCE[合规负责人]
LEGAL[法务负责人]
end
subgraph &quot;安全执行团队&quot;
SEC_ADMIN[安全管理员]
SYS_ADMIN[系统管理员]
DBA[数据库管理员]
NET_ADMIN[网络管理员]
end
subgraph &quot;业务安全责任人&quot;
BUS_OWNER[业务负责人]
DATA_OWNER[数据负责人]
USER_ADMIN[用户管理员]
end
CEO --&gt; CISO
CISO --&gt; IT_DIR
CISO --&gt; SECURITY_DIR
CISO --&gt; COMPLIANCE
CISO --&gt; LEGAL
IT_DIR --&gt; SEC_ADMIN
IT_DIR --&gt; SYS_ADMIN
IT_DIR --&gt; DBA
IT_DIR --&gt; NET_ADMIN
SECURITY_DIR --&gt; BUS_OWNER
SECURITY_DIR --&gt; DATA_OWNER
SECURITY_DIR --&gt; USER_ADMIN</code></pre>
<h5 data-number="7.0.0.0.62" id="安全管理制度-1"><span
class="header-section-number">7.0.0.0.62</span> 8.2 安全管理制度</h5>
<h5 data-number="7.0.0.0.63" id="人员安全管理"><span
class="header-section-number">7.0.0.0.63</span> 8.2.1 人员安全管理</h5>
<ul>
<li><strong>入职安全审查</strong>:对关键岗位人员进行背景调查</li>
<li><strong>安全培训</strong>:定期进行信息安全意识培训</li>
<li><strong>权限管理</strong>:建立权限申请、审批、回收流程</li>
<li><strong>离职管理</strong>:离职人员权限及时回收,签署保密协议</li>
</ul>
<h5 data-number="7.0.0.0.64" id="系统建设安全管理"><span
class="header-section-number">7.0.0.0.64</span> 8.2.2
系统建设安全管理</h5>
<ul>
<li><strong>安全需求分析</strong>:项目立项阶段进行安全需求分析</li>
<li><strong>安全设计评审</strong>:设计阶段进行安全架构评审</li>
<li><strong>安全测试</strong>:上线前进行安全渗透测试</li>
<li><strong>安全验收</strong>:系统上线前进行安全验收</li>
</ul>
<h5 data-number="7.0.0.0.65" id="系统运维安全管理"><span
class="header-section-number">7.0.0.0.65</span> 8.2.3
系统运维安全管理</h5>
<ul>
<li><strong>变更管理</strong>:所有系统变更都需要安全评估</li>
<li><strong>备份管理</strong>:定期备份,异地存储,加密保护</li>
<li><strong>监控管理</strong>7×24小时安全监控</li>
<li><strong>应急管理</strong>:建立应急响应机制</li>
</ul>
<h5 data-number="7.0.0.0.66" id="合规管理"><span
class="header-section-number">7.0.0.0.66</span> 8.3 合规管理</h5>
<h5 data-number="7.0.0.0.67" id="法律法规合规"><span
class="header-section-number">7.0.0.0.67</span> 8.3.1 法律法规合规</h5>
<ul>
<li>《中华人民共和国网络安全法》</li>
<li>《中华人民共和国数据安全法》</li>
<li>《中华人民共和国个人信息保护法》</li>
<li>《关键信息基础设施安全保护条例》</li>
</ul>
<h5 data-number="7.0.0.0.68" id="行业标准合规"><span
class="header-section-number">7.0.0.0.68</span> 8.3.2 行业标准合规</h5>
<ul>
<li>GB/T 22239-2019《信息安全技术 网络安全等级保护基本要求》</li>
<li>GB/T 25070-2019《信息安全技术
网络安全等级保护安全设计技术要求》</li>
<li>GB/T 32918《信息安全技术 SM2椭圆曲线公钥密码算法》</li>
<li>GB/T 32905《信息安全技术 SM3密码杂凑算法》</li>
</ul>
<h5 data-number="7.0.0.0.69" id="合规检查清单"><span
class="header-section-number">7.0.0.0.69</span> 8.3.3 合规检查清单</h5>
<ul class="task-list">
<li><label><input type="checkbox" />等级保护三级备案完成</label></li>
<li><label><input type="checkbox" />年度安全评估报告</label></li>
<li><label><input type="checkbox" />安全管理制度建立</label></li>
<li><label><input type="checkbox" />安全技术措施落实</label></li>
<li><label><input type="checkbox" />安全培训记录完整</label></li>
<li><label><input type="checkbox" />应急预案演练记录</label></li>
<li><label><input type="checkbox" />安全事件处置记录</label></li>
<li><label><input type="checkbox" />第三方安全服务合同</label></li>
</ul>
<hr />
<h5 data-number="7.0.0.0.70" id="总结"><span
class="header-section-number">7.0.0.0.70</span> 总结</h5>
<p>福建水务营收系统安全设计严格按照等级保护三级要求结合OpenGauss数据库的安全特性建立了全方位、多层次的安全防护体系。通过技术防护、管理制度、人员培训等多重措施确保系统安全稳定运行满足水务行业的安全合规要求。</p>
<p>本安全设计方案的核心特点: 1.
<strong>国产化安全</strong>采用OpenGauss数据库和国密算法 2.
<strong>纵深防御</strong>:网络、应用、数据多层安全防护 3.
<strong>合规导向</strong>:严格按照等保三级标准设计 4.
<strong>持续改进</strong>:建立安全监控和应急响应机制 5.
<strong>管理规范</strong>:完善的安全管理制度和流程 —</p>
</body>
</html>