Commit 6a898afb authored by 郭腾飞's avatar 郭腾飞

后端框架初始化

parent cdefdd78
......@@ -11,5 +11,8 @@ trim_trailing_whitespace = true
[*.java]
indent_size = 4
[*.py]
indent_size = 4
[*.md]
trim_trailing_whitespace = false
......@@ -35,5 +35,19 @@ coverage/
.eslintcache
.stylelintcache
# Python
__pycache__/
*.py[cod]
.venv/
venv/
.pytest_cache/
.mypy_cache/
.ruff_cache/
*.egg-info/
build/
# Python tooling
.python-version
# Local infrastructure data
infra/docker/data/
......@@ -11,23 +11,40 @@
## 技术约束
- `apps/web-vue/`:Vue 3 + TypeScript 主前端,内嵌 React + TypeScript 子模块。
- `services/api/`:Spring Boot 后端,JDK 21,Maven。
- `services/api/`:Spring Boot 模块化单体主业务后端,JDK 21,Maven。
- `services/agent/`:Python 3.11+ Agent 进程,使用 LangChain 和 LangGraph,由 Java 主业务后端调用。
- Maven 坐标约定:`groupId=mc``artifactId=mcaigc`
- Java 基础包名约定:`mc.mcaigc`
- PostgreSQL 是主数据库,Redis 用于缓存,MinIO 用于对象存储。
- `infra/docker/` 只承载本地开发环境相关文件。
### Java DDD 模块
- `services/api/mcaigc-domain/`:领域模型和领域端口,不依赖 Spring。
- `services/api/mcaigc-application/`:应用服务和用例编排,依赖领域层。
- `services/api/mcaigc-infrastructure/`:数据库、缓存、对象存储等技术适配,依赖领域层。
- `services/api/mcaigc-interfaces/`:HTTP 接口、DTO 和参数适配,依赖应用层。
- `services/api/mcaigc-boot/`:Spring Boot 启动模块,组装各层并提供可运行入口。
具体业务服务出现后,应在此骨架内按业务边界拆分模块,不跨领域直接引用内部实现。
## 修改原则
1. 目录、源代码标识符和配置键使用英文;项目文档使用中文。
2. React 子模块与 Vue 主应用之间通过明确的公开契约交互,不直接依赖彼此的内部实现。
3. 后端模块不得绕过既定分层直接访问其他模块内部实现。
4. 原始资料只追加、不覆盖,按 `resources/raw/YYYY/YYYY-MM-DD/` 归档。
5. 不为尚未确认的需求预建模块、依赖或抽象。
6. 修改项目结构、核心技术选型或跨模块契约时,同步更新 `docs/` 中的相关文档。
3. Java 主业务后端负责认证授权、业务 API、业务规则和持久化;Python Agent 负责智能体编排、工具调用和模型交互。
4. 用户和前端不得直接调用 Agent;请求必须先经过 Java 主业务后端认证和授权,再由 Java 调用 Agent。
5. Agent 不建立独立的用户认证体系,不直接修改主业务数据库;需要读写业务数据时通过 Java 主业务后端的公开 HTTP API 完成。
6. Java 与 Agent 通过内部、版本化的 HTTP/JSON 契约通信;用户和前端不得绕过 Java 直接访问 Agent。
7. 后端模块不得绕过既定分层直接访问其他模块内部实现。
8. 原始资料只追加、不覆盖,按 `resources/raw/YYYY/YYYY-MM-DD/` 归档。
9. 不为尚未确认的需求预建模块、依赖或抽象。
10. 修改项目结构、核心技术选型或跨模块契约时,同步更新 `docs/` 中的相关文档。
## 验证要求
- 只运行与改动范围相匹配的最小验证。
- 新增业务代码后,应为关键分支、数据转换和边界条件补充自动化测试。
- 在工具链尚未初始化前,不编造不可执行的构建或测试命令。
- Java 骨架验证:`cd services/api && mvn verify`
- Agent 骨架验证:安装开发依赖后运行 `cd services/agent && python -m pytest`
# 梦畅AIGC
梦畅AIGC 是一个用于 AIGC 内容生成的 Web 平台。本仓库采用单仓库结构,包含 Vue 3 主前端(内嵌 React 子模块)、Spring Boot 后端、本地开发基础设施,以及项目文档和原始资料。
梦畅AIGC 是一个用于 AIGC 内容生成的 Web 平台。本仓库采用单仓库结构,包含 Vue 3 主前端(内嵌 React 子模块)、Spring Boot 主业务后端、Python Agent 服务、本地开发基础设施,以及项目文档和原始资料。
当前仓库仅完成目录与项目规范初始化,尚未生成任何业务代码或运行配置。
当前仓库已生成 Java 主业务后端和 Python Agent 的项目骨架,尚未实现具体业务功能或生产运行配置。
## 技术基线
- 主前端:Vue 3 + TypeScript
- 内嵌子模块:React + TypeScript,位于 Vue 主应用内部
- 后端:Spring Boot、JDK 21、Maven
- Agent:Python 3.11+、LangChain、LangGraph
- 数据库:PostgreSQL
- 缓存:Redis
- 对象存储:MinIO
......@@ -21,7 +22,8 @@
├── apps/
│ └── web-vue/ # Vue 3 主前端,内嵌 React 子模块
├── services/
│ └── api/ # Spring Boot 后端
│ ├── api/ # Spring Boot 模块化单体主业务后端,DDD 多模块骨架
│ └── agent/ # Python LangChain/LangGraph Agent 进程骨架
├── infra/
│ └── docker/ # 本地开发基础设施
├── resources/
......@@ -44,7 +46,9 @@
- Node.js 版本和前端包管理器
- React 子模块与 Vue 主应用的通信契约
- Java 主业务后端调用 Agent 的内部 HTTP API 契约(认证、版本、错误格式等)
- Python Agent 依赖锁定方式
- 数据库迁移工具
- 开发环境服务端口和容器编排细节
以上事项应在开始对应代码开发前确定,不在本次初始化中预设
以上事项应在实现对应能力前确定;当前骨架只提供本地构建和自检入口
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>金水流域闸站联合调度数据支撑系统</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
{
"name": "jinshui-water-dashboard",
"version": "0.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "jinshui-water-dashboard",
"version": "0.1.0",
"dependencies": {
"@vitejs/plugin-vue": "latest",
"vite": "latest",
"vue": "latest"
},
"devDependencies": {}
},
"node_modules/@babel/helper-string-parser": {
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
"integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-identifier": {
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
"integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/parser": {
"version": "7.29.8",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz",
"integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==",
"license": "MIT",
"dependencies": {
"@babel/types": "^7.29.8"
},
"bin": {
"parser": "bin/babel-parser.js"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@babel/types": {
"version": "7.29.8",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz",
"integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==",
"license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.29.7",
"@babel/helper-validator-identifier": "^7.29.7"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
"license": "MIT"
},
"node_modules/@oxc-project/types": {
"version": "0.144.0",
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.144.0.tgz",
"integrity": "sha512-nuhZIOLuI6TFQ32I/WnUx+SCPY7SdSKwgnFHydAuoS1+Z4BRcaP+RRJmGzl9lw+0OFF7UmaESf7KQRXaNLHypg==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/Boshen"
}
},
"node_modules/@rolldown/binding-android-arm64": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.4.tgz",
"integrity": "sha512-jHC2cnyKz5xU2fhECtFl8OZ83cYNt13GZQD+0uMJ/X3o+ijmd56okHhTUwxVSHPx1IRVIJEZ1/1pPzeLCU6XKA==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-darwin-arm64": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.4.tgz",
"integrity": "sha512-Dc5mPD8F5F/FS8i01syd7FTF6yB2fVthH/TRkjwJkzUK6EpoxHtqvZQP5Zwq80/5z19TWYHIg1KOHboCgVx/aQ==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-darwin-x64": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.4.tgz",
"integrity": "sha512-fpDm4oBo6SqLvWUYCmFhdde3U9KH2fRNNMeAnAPAIwxRL345xutL0EtEUcuoxsoazdJGv/MuDBQHlCDrtbvqOg==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-freebsd-x64": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.4.tgz",
"integrity": "sha512-rSJoreDE/HoIzoaib6MTp5jQtCTdMHKIvItAKT/ImS6Y6Ww76oUaeMyp4Vc/fAgd/ehji068IxetHXAnqUwN9A==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-linux-arm-gnueabihf": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.4.tgz",
"integrity": "sha512-/jm8OGHgn7oGaJu3i/qZI9spUGcJ+y/lk43ttQ/iO1tOd9NissG6o97bighBCiL+BKRngmcDuR6ikfwYdJmVuQ==",
"cpu": [
"arm"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-linux-arm64-gnu": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.4.tgz",
"integrity": "sha512-tIP06BeD9EqvECBrPZ+sqdPlYrT+aYaAiu1wYziVx5elRK/ftm33JxVDy2bXGbr6J0CrtirCkR87/X5a2euEng==",
"cpu": [
"arm64"
],
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-linux-arm64-musl": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.4.tgz",
"integrity": "sha512-Ql1Q0EQqVThvn9VAVlwNzsUvbSFtCMGjLpRRi4pk5i7NZZ4n5ISiLMjHYtus4VQ2PvkSw24zyaCVsiS+sXPj1w==",
"cpu": [
"arm64"
],
"libc": [
"musl"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-linux-ppc64-gnu": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.4.tgz",
"integrity": "sha512-GjbjXD4XXfN19D0LZNbmiCBUoDiRACsYHr0yaIbbn8aFsXjHZifcYqu/W5Er5X2X990WjHXFrxarn5chzItorQ==",
"cpu": [
"ppc64"
],
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-linux-s390x-gnu": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.4.tgz",
"integrity": "sha512-p5WR0NOwaRmJ/B1b6IjEFLLivwEsf3PrdBIhRbhTCQisbo2SvHHpG4ELB/+FgQNnB88LTOF86upmJmbvZdQ2lw==",
"cpu": [
"s390x"
],
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-linux-x64-gnu": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.4.tgz",
"integrity": "sha512-4/GyVjmhR+Tc6HLJvwc1sOhPqAZtySiSMesOZyX6JQ5XBxoTDEMKQzvo07NIK6nTon/SivlZqvhzvuVBNQhObQ==",
"cpu": [
"x64"
],
"libc": [
"glibc"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-linux-x64-musl": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.4.tgz",
"integrity": "sha512-l9eeLsCNvPpmSXUej0etw/J1eqV0Jj1D5G/xG6YTijmE6dkv6E2QezgWbTfQk63v952DPqrjOCoiqxq7Bw0YUQ==",
"cpu": [
"x64"
],
"libc": [
"musl"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-openharmony-arm64": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.4.tgz",
"integrity": "sha512-e0F355MSTMm3+UOqtV3L24gFUp2N5m1f8L/7d56deik6va+AXdrt9F8LbzGpeWGWRbZEDq4m8NVnJDeBtf9DZg==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"openharmony"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-win32-arm64-msvc": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.4.tgz",
"integrity": "sha512-AWLi0uBRYh6QlE7OKhiz+phZC0qwtij2QZmhmOdsLdFn64m7oMpooE9ICE3lhm9xMb4SpDo2WbHcxX1iFLFtqw==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-win32-x64-msvc": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.4.tgz",
"integrity": "sha512-UwSDJOg3dqCAejWdxclJjCsh3Qq4vLYMDxmyHqo1btz3stK2VqgwNd3mm5tuIwzSlGIQ/1H9Hr+Zn09mrezNqQ==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/pluginutils": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz",
"integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==",
"license": "MIT"
},
"node_modules/@vitejs/plugin-vue": {
"version": "6.0.8",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.8.tgz",
"integrity": "sha512-0ZjgOg7oO6farnNGup7yvoM/YXZV84OZxHAwtflItNa/6zzQyVb5LNxyea3FEKEX2XlagIKzrlH7wwxkKgtiew==",
"license": "MIT",
"dependencies": {
"@rolldown/pluginutils": "^1.0.1"
},
"engines": {
"node": "^20.19.0 || >=22.12.0"
},
"peerDependencies": {
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0",
"vue": "^3.2.25"
}
},
"node_modules/@vue/compiler-core": {
"version": "3.5.41",
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.41.tgz",
"integrity": "sha512-q0Xtv/F9w2YO/7htQhtiL+Ev2WCJbe5N2hc+XfgyKkEKqWpSxknmT8QOuGdEKNdjPq0c3F7rNpFkTo3Kfrm7pg==",
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.29.8",
"@vue/shared": "3.5.41",
"entities": "^7.0.1",
"estree-walker": "^2.0.2",
"source-map-js": "^1.2.1"
}
},
"node_modules/@vue/compiler-dom": {
"version": "3.5.41",
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.41.tgz",
"integrity": "sha512-oKacVfNglLvGjnS6BXOlGL7EyG2h8X03pqXCjzotRZUaXGjbrTJUnVAQjrCqUnS+lyu31nwQjZY/d817GmCnfw==",
"license": "MIT",
"dependencies": {
"@vue/compiler-core": "3.5.41",
"@vue/shared": "3.5.41"
}
},
"node_modules/@vue/compiler-sfc": {
"version": "3.5.41",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.41.tgz",
"integrity": "sha512-XJhip7R2wy6vX3knCxdZN4KracFaZUef58s1KYewqluedHIJaPIVfXoYT7MF1F8nCvv6k8bWWxDC8opMkg1VTQ==",
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.29.8",
"@vue/compiler-core": "3.5.41",
"@vue/compiler-dom": "3.5.41",
"@vue/compiler-ssr": "3.5.41",
"@vue/shared": "3.5.41",
"estree-walker": "^2.0.2",
"magic-string": "^0.30.21",
"postcss": "^8.5.19",
"source-map-js": "^1.2.1"
}
},
"node_modules/@vue/compiler-ssr": {
"version": "3.5.41",
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.41.tgz",
"integrity": "sha512-U3v5OejKEGqOI0Wy0+Sz7hGuIFZHA4LSXzrNM3IMIeDyJEBBfTpX26n3SDgToRpP2bLc9FfI2j/kSgcJ8Emq5A==",
"license": "MIT",
"dependencies": {
"@vue/compiler-dom": "3.5.41",
"@vue/shared": "3.5.41"
}
},
"node_modules/@vue/reactivity": {
"version": "3.5.41",
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.41.tgz",
"integrity": "sha512-rznsqKM0np0x18EjzF8x88MpEhdNsffbvFbckLL5+oUKz1BxAImEmO7J1ArRYSyo6aQaVoBDp7jEkT91OOxydA==",
"license": "MIT",
"dependencies": {
"@vue/shared": "3.5.41"
}
},
"node_modules/@vue/runtime-core": {
"version": "3.5.41",
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.41.tgz",
"integrity": "sha512-Vcry58hiAKwGen9Z1jUZE0feFsNArPCMOImYI8el48A9Idf6DuQYD0U05zZIF2Iad1hGhPSvcbBbAOhNr55fhg==",
"license": "MIT",
"dependencies": {
"@vue/reactivity": "3.5.41",
"@vue/shared": "3.5.41"
}
},
"node_modules/@vue/runtime-dom": {
"version": "3.5.41",
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.41.tgz",
"integrity": "sha512-3vVBahVBS9+U6cmXBLyb8nE6/yYo4J/CGI9eVFs3KiMc0YHuudwKyShTD65jtJy/L9PUUxNAFu4cj4LiJ0UFbw==",
"license": "MIT",
"dependencies": {
"@vue/reactivity": "3.5.41",
"@vue/runtime-core": "3.5.41",
"@vue/shared": "3.5.41",
"csstype": "^3.2.3"
}
},
"node_modules/@vue/server-renderer": {
"version": "3.5.41",
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.41.tgz",
"integrity": "sha512-n6hx/pNFfbD6SuyeuMVkvqox8bwf/ET9JlA/kAz/imw8sw++wkqKe2mHX5KutjPpbKE4Z56yTHszoOjGMI9igQ==",
"license": "MIT",
"dependencies": {
"@vue/compiler-ssr": "3.5.41",
"@vue/runtime-dom": "3.5.41",
"@vue/shared": "3.5.41"
}
},
"node_modules/@vue/shared": {
"version": "3.5.41",
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.41.tgz",
"integrity": "sha512-IOnwSCma8j+9xJT6b8H0dEYidC80NsYmNMlZxRsukYcSoGaDBohog5hDxzeUXdFeGWFA++vWvxqOmrr96VlqMA==",
"license": "MIT"
},
"node_modules/csstype": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
"license": "MIT"
},
"node_modules/detect-libc": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
"license": "Apache-2.0",
"engines": {
"node": ">=8"
}
},
"node_modules/entities": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz",
"integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=0.12"
},
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/estree-walker": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
"license": "MIT"
},
"node_modules/fdir": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
"license": "MIT",
"engines": {
"node": ">=12.0.0"
},
"peerDependencies": {
"picomatch": "^3 || ^4"
},
"peerDependenciesMeta": {
"picomatch": {
"optional": true
}
}
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/lightningcss": {
"version": "1.33.0",
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz",
"integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==",
"license": "MPL-2.0",
"dependencies": {
"detect-libc": "^2.0.3"
},
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
},
"optionalDependencies": {
"lightningcss-android-arm64": "1.33.0",
"lightningcss-darwin-arm64": "1.33.0",
"lightningcss-darwin-x64": "1.33.0",
"lightningcss-freebsd-x64": "1.33.0",
"lightningcss-linux-arm-gnueabihf": "1.33.0",
"lightningcss-linux-arm64-gnu": "1.33.0",
"lightningcss-linux-arm64-musl": "1.33.0",
"lightningcss-linux-x64-gnu": "1.33.0",
"lightningcss-linux-x64-musl": "1.33.0",
"lightningcss-win32-arm64-msvc": "1.33.0",
"lightningcss-win32-x64-msvc": "1.33.0"
}
},
"node_modules/lightningcss-android-arm64": {
"version": "1.33.0",
"resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz",
"integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==",
"cpu": [
"arm64"
],
"license": "MPL-2.0",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-darwin-arm64": {
"version": "1.33.0",
"resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz",
"integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==",
"cpu": [
"arm64"
],
"license": "MPL-2.0",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-darwin-x64": {
"version": "1.33.0",
"resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz",
"integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==",
"cpu": [
"x64"
],
"license": "MPL-2.0",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-freebsd-x64": {
"version": "1.33.0",
"resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz",
"integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==",
"cpu": [
"x64"
],
"license": "MPL-2.0",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-linux-arm-gnueabihf": {
"version": "1.33.0",
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz",
"integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==",
"cpu": [
"arm"
],
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-linux-arm64-gnu": {
"version": "1.33.0",
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz",
"integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==",
"cpu": [
"arm64"
],
"libc": [
"glibc"
],
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-linux-arm64-musl": {
"version": "1.33.0",
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz",
"integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==",
"cpu": [
"arm64"
],
"libc": [
"musl"
],
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-linux-x64-gnu": {
"version": "1.33.0",
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz",
"integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==",
"cpu": [
"x64"
],
"libc": [
"glibc"
],
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-linux-x64-musl": {
"version": "1.33.0",
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz",
"integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==",
"cpu": [
"x64"
],
"libc": [
"musl"
],
"license": "MPL-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-win32-arm64-msvc": {
"version": "1.33.0",
"resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz",
"integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==",
"cpu": [
"arm64"
],
"license": "MPL-2.0",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/lightningcss-win32-x64-msvc": {
"version": "1.33.0",
"resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz",
"integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==",
"cpu": [
"x64"
],
"license": "MPL-2.0",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 12.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
}
},
"node_modules/magic-string": {
"version": "0.30.21",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
"integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.5"
}
},
"node_modules/nanoid": {
"version": "3.3.18",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz",
"integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"bin": {
"nanoid": "bin/nanoid.cjs"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"license": "ISC"
},
"node_modules/picomatch": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz",
"integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
"license": "MIT",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/postcss": {
"version": "8.5.26",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz",
"integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==",
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"nanoid": "^3.3.17",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/rolldown": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.4.tgz",
"integrity": "sha512-rSr7irW0K7QRWzjdJXqZowkcRdDtjRduh43rBltnVKd0VFq839l1lJoDvGJb6gl7+4rTTCrPWu+YfujUL8Ug7w==",
"license": "MIT",
"dependencies": {
"@oxc-project/types": "=0.144.0",
"@rolldown/pluginutils": "^1.0.0"
},
"bin": {
"rolldown": "bin/cli.mjs"
},
"engines": {
"node": "^20.19.0 || >=22.12.0"
},
"optionalDependencies": {
"@rolldown/binding-android-arm64": "1.2.4",
"@rolldown/binding-darwin-arm64": "1.2.4",
"@rolldown/binding-darwin-x64": "1.2.4",
"@rolldown/binding-freebsd-x64": "1.2.4",
"@rolldown/binding-linux-arm-gnueabihf": "1.2.4",
"@rolldown/binding-linux-arm64-gnu": "1.2.4",
"@rolldown/binding-linux-arm64-musl": "1.2.4",
"@rolldown/binding-linux-ppc64-gnu": "1.2.4",
"@rolldown/binding-linux-s390x-gnu": "1.2.4",
"@rolldown/binding-linux-x64-gnu": "1.2.4",
"@rolldown/binding-linux-x64-musl": "1.2.4",
"@rolldown/binding-openharmony-arm64": "1.2.4",
"@rolldown/binding-win32-arm64-msvc": "1.2.4",
"@rolldown/binding-win32-x64-msvc": "1.2.4"
}
},
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/tinyglobby": {
"version": "0.2.17",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
"integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
"license": "MIT",
"dependencies": {
"fdir": "^6.5.0",
"picomatch": "^4.0.4"
},
"engines": {
"node": ">=12.0.0"
},
"funding": {
"url": "https://github.com/sponsors/SuperchupuDev"
}
},
"node_modules/vite": {
"version": "8.2.1",
"resolved": "https://registry.npmjs.org/vite/-/vite-8.2.1.tgz",
"integrity": "sha512-EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/lw==",
"license": "MIT",
"dependencies": {
"lightningcss": "^1.33.0",
"picomatch": "^4.0.5",
"postcss": "^8.5.25",
"rolldown": "~1.2.1",
"tinyglobby": "^0.2.17"
},
"bin": {
"vite": "bin/vite.js"
},
"engines": {
"node": "^20.19.0 || >=22.12.0"
},
"funding": {
"url": "https://github.com/vitejs/vite?sponsor=1"
},
"optionalDependencies": {
"fsevents": "~2.3.3"
},
"peerDependencies": {
"@types/node": "^20.19.0 || >=22.12.0",
"@vitejs/devtools": "^0.4.0",
"esbuild": "^0.27.0 || ^0.28.0",
"jiti": ">=1.21.0",
"less": "^4.0.0",
"sass": "^1.70.0",
"sass-embedded": "^1.70.0",
"stylus": ">=0.54.8",
"sugarss": "^5.0.0",
"terser": "^5.16.0",
"tsx": "^4.8.1",
"yaml": "^2.4.2"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
},
"@vitejs/devtools": {
"optional": true
},
"esbuild": {
"optional": true
},
"jiti": {
"optional": true
},
"less": {
"optional": true
},
"sass": {
"optional": true
},
"sass-embedded": {
"optional": true
},
"stylus": {
"optional": true
},
"sugarss": {
"optional": true
},
"terser": {
"optional": true
},
"tsx": {
"optional": true
},
"yaml": {
"optional": true
}
}
},
"node_modules/vue": {
"version": "3.5.41",
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.41.tgz",
"integrity": "sha512-2laE0p+aK+/AOPG/XL/WepOs/GlK755LJ1XECi9kDUrz1FKNw8rb2Xzlw9JS1rqEV55nb0ttsKxVlTCcd+R5cg==",
"license": "MIT",
"dependencies": {
"@vue/compiler-dom": "3.5.41",
"@vue/compiler-sfc": "3.5.41",
"@vue/runtime-dom": "3.5.41",
"@vue/server-renderer": "3.5.41",
"@vue/shared": "3.5.41"
},
"peerDependencies": {
"typescript": "*"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
}
}
}
{
"name": "jinshui-water-dashboard",
"private": true,
"version": "0.1.0",
"type": "module",
"scripts": {
"dev": "vite --host 0.0.0.0",
"build": "vite build",
"preview": "vite preview --host 0.0.0.0"
},
"dependencies": {
"@vitejs/plugin-vue": "latest",
"vite": "latest",
"vue": "latest"
},
"devDependencies": {}
}
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
<script setup lang="ts">
import { computed, onMounted, onUnmounted, ref } from 'vue'
type PageKey = 'home' | 'gate-real' | 'pump-real' | 'video' | 'water' | 'flow' | 'rainfall' | 'rain-excerpt' | 'pump-history' | 'gate-maintain'
const navItems: { key: PageKey; label: string; path: string }[] = [
{ key: 'gate-real', label: '实时数据', path: '#/engrg/gate/real' },
{ key: 'home', label: '首页', path: '#/' },
{ key: 'rain-excerpt', label: '水雨情', path: '#/waterRainfall/rainfallExcerpt' },
{ key: 'pump-history', label: '统计报表', path: '#/report/pump-his' },
{ key: 'gate-maintain', label: '数据维护', path: '#/engrg/gate' },
]
const leftNavItems = [navItems[1], navItems[0]]
const rightNavItems = navItems.slice(2)
const weatherIcon = '/assets/weather/104.png'
const sideItems = [
{ key: 'gate-real' as PageKey, icon: '◈', label: '涵闸实时数据' },
{ key: 'pump-real' as PageKey, icon: '◉', label: '泵站实时数据' },
{ key: 'video' as PageKey, icon: '▣', label: '涵闸视频' },
{ key: 'water' as PageKey, icon: '≋', label: '实时水情' },
{ key: 'flow' as PageKey, icon: '⌁', label: '实时流量' },
{ key: 'rainfall' as PageKey, icon: '☷', label: '实时雨情' },
]
const page = ref<PageKey>('home')
const username = ref('')
const password = ref('')
const captcha = ref('')
const captchaCode = ref('2r8u')
const loggedIn = ref(false)
const loginError = ref('')
const currentTime = ref(new Date())
let clock: number | undefined
const gateRows = ref([
{ id: '1', name: '鲁湖闸', river: '金水河', flow: '--', front: '21.37', frontTrend: '↓', back: '21.69', backTrend: '↔', report: '2026-08-13 14:40:00' },
{ id: '2', name: '法泗闸', river: '金水河', flow: '--', front: '21.72', frontTrend: '↓', back: '21.71', backTrend: '↔', report: '2026-08-13 14:35:00' },
{ id: '3', name: '六贯闸', river: '金水河', flow: '--', front: '22.00', frontTrend: '↓', back: '21.77', backTrend: '↔', report: '2026-08-13 14:35:00' },
{ id: '4', name: '金水闸', river: '金水河', flow: '--', front: '21.68', frontTrend: '↓', back: '--', backTrend: '--', report: '2026-08-13 14:45:00' },
{ id: '5', name: '两湖连通闸', river: '金水河', flow: '--', front: '--', frontTrend: '--', back: '--', backTrend: '--', report: '--' },
])
const maintainRows = ref([
{ id: '1', name: '鲁湖闸', river: '金水河', designFlow: '50.7', front: '0.0%', back: '0.05', correction: '-0.01' },
{ id: '2', name: '法泗闸', river: '金水河', designFlow: '270', front: '13.7%', back: '-', correction: '-' },
{ id: '3', name: '六贯闸', river: '金水河', designFlow: '-', front: '0.0%', back: '0.13', correction: '-' },
{ id: '4', name: '金水闸', river: '金水河', designFlow: '360', front: '0.0%', back: '0.07', correction: '-' },
{ id: '5', name: '两湖连通闸', river: '金水河', designFlow: '-', front: '0.0%', back: '-', correction: '-' },
])
const selectedGate = ref('')
const selectedStation = ref('全部')
const selectedYear = ref('2026')
const startDate = ref('2026-08-12')
const endDate = ref('2026-08-13')
const gateNameQuery = ref('')
const gateIdQuery = ref('')
const pumpRows = Array.from({ length: 20 }, (_, i) => ({
index: i + 1,
time: `2026-08-12 ${String(23 - Math.floor(i / 4)).padStart(2, '0')}:${String(50 - (i % 4) * 5).padStart(2, '0')}:00`,
id: '1', name: '金口泵站', river: '金水河', units: '6', design: '2200', power: i === 0 ? '65' : '0', count: i === 0 ? '--' : '0', flow: '--',
}))
const routeTo = (target: PageKey) => {
page.value = target
const item = navItems.find((n) => n.key === target)
if (item) history.replaceState(null, '', item.path)
}
const readRoute = () => {
const hash = window.location.hash
if (hash.includes('/engrg/gate/real')) page.value = 'gate-real'
else if (hash.includes('/engrg/pump/real')) page.value = 'pump-real'
else if (hash.includes('/engrg/video')) page.value = 'video'
else if (hash.includes('/waterRainfall/water')) page.value = 'water'
else if (hash.includes('/engrg/flow')) page.value = 'flow'
else if (hash.includes('/waterRainfall/rainfall')) page.value = 'rainfall'
else if (hash.includes('/waterRainfall/rainfallExcerpt')) page.value = 'rain-excerpt'
else if (hash.includes('/report/pump-his')) page.value = 'pump-history'
else if (hash.includes('/engrg/gate')) page.value = 'gate-maintain'
else page.value = 'home'
}
const doLogin = () => {
if (!username.value || !password.value || captcha.value.toLowerCase() !== captchaCode.value) {
loginError.value = '请输入正确的用户名、密码和验证码'
return
}
loginError.value = ''
loggedIn.value = true
routeTo('home')
}
const refreshCaptcha = () => {
const values = ['2r8u', 'y2z36', '7m4k', 'p6c9']
captchaCode.value = values[Math.floor(Math.random() * values.length)]
}
const resetFilters = () => {
gateNameQuery.value = ''
gateIdQuery.value = ''
selectedGate.value = ''
selectedStation.value = '全部'
selectedYear.value = '2026'
startDate.value = '2026-08-12'
endDate.value = '2026-08-13'
}
const filteredGateRows = computed(() => gateRows.value.filter((row) => !gateNameQuery.value || row.name.includes(gateNameQuery.value)))
const timeLabel = computed(() => currentTime.value.toLocaleTimeString('zh-CN', { hour12: false }))
const dateLabel = computed(() => currentTime.value.toLocaleDateString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', weekday: 'long' }).replaceAll('/', '-'))
const dateOnly = computed(() => {
const d = currentTime.value
return `${d.getFullYear()}${String(d.getMonth() + 1).padStart(2, '0')}${String(d.getDate()).padStart(2, '0')}日`
})
const weekLabel = computed(() => ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'][currentTime.value.getDay()])
onMounted(() => {
readRoute()
window.addEventListener('hashchange', readRoute)
clock = window.setInterval(() => { currentTime.value = new Date() }, 1000)
})
onUnmounted(() => { window.removeEventListener('hashchange', readRoute); if (clock) window.clearInterval(clock) })
</script>
<template>
<main class="app-shell" :class="{ 'dashboard-mode': loggedIn }">
<section v-if="!loggedIn" class="login-screen">
<div class="login-art"></div>
<form class="login-panel" @submit.prevent="doLogin">
<h1>金水流域闸站联合调度数据支撑系统</h1>
<p v-if="loginError" class="login-error">{{ loginError }}</p>
<label class="login-field"><span></span><input v-model="username" placeholder="用户名" autocomplete="username" /></label>
<label class="login-field"><span></span><input v-model="password" type="password" placeholder="密码" autocomplete="current-password" /></label>
<label class="login-field captcha-field"><span></span><input v-model="captcha" placeholder="验证码" maxlength="4" /><button type="button" class="captcha-code" @click="refreshCaptcha">{{ captchaCode }}</button></label>
<button class="login-submit" type="submit">登 录</button>
</form>
</section>
<template v-else>
<header class="topbar head-b">
<div class="head-time">
<div class="newtime"><i v-for="(digit, index) in timeLabel.replaceAll(':', '')" :key="`${digit}-${index}`">{{ digit }}</i></div>
<div class="newtime2">{{ dateOnly }}</div>
<div class="xingqi">{{ weekLabel }}</div>
</div>
<div class="weather"><img :src="weatherIcon" alt="" /><span><b>25℃</b><p>24~28℃ 阴</p></span></div>
<nav class="head-l">
<button v-for="item in leftNavItems" :key="item.key" :class="{ active: page === item.key }" @click="routeTo(item.key)">{{ item.label }}<i></i></button>
</nav>
<h1 class="head-c"><span>{{ '金水流域闸站联合调度数据支撑系统' }}</span></h1>
<nav class="head-r">
<button v-for="item in rightNavItems" :key="item.key" :class="{ active: page === item.key }" @click="routeTo(item.key)">{{ item.label }}<i></i></button>
</nav>
<div class="user-chip"><span></span> admin</div>
</header>
<section v-if="page === 'home'" class="home-view">
<aside class="home-left panel-stack">
<section class="data-panel overview"><h2>24小时综述</h2><p><b>12日15时至13日14时</b> 24小时共有 <em>0</em> 个降雨测站,<em>5</em> 个闸站,其中有降雨测站为 <em>0</em> 个,最大降雨量 <em>0mm</em>,10mm以下测站数为 <em>0</em> 个,超设防水位站数为 <em>0</em> 个,法泗闸、鲁湖闸、六贯闸、金口闸、金口泵站、金口二站的运行情况正常。</p></section>
<section class="data-panel"><h2>闸站信息</h2><table><thead><tr><th>闸站名称</th><th>闸前水位</th><th>闸后水位</th><th>流量</th><th>开度</th></tr></thead><tbody><tr v-for="row in gateRows.slice(0, 4)" :key="row.id"><td><span class="expand">+</span>{{ row.name }}</td><td>{{ row.front }}</td><td>{{ row.back }}</td><td>{{ row.flow }}</td><td>{{ row.name === '法泗闸' ? '13.7%' : '--' }}</td></tr></tbody></table></section>
<section class="data-panel rain-chart"><h2>实时降雨</h2><div class="bars"><i v-for="n in 14" :key="n" :style="{ height: `${6 + ((n * 17) % 38)}px` }"></i></div><div class="axis">22 23 00 01 02 03 04 05 06 07 08 09 10</div><small>单位:mm</small></section>
</aside>
<section class="map-view">
<div class="map-label">卫星地图</div>
<button class="map-collapse" aria-label="收起左侧面板"></button>
<div class="map-markers">
<span class="marker gate" style="left: 37%; top: 8%"><img src="/assets/map/map-gate-enable.png" /><small>鲁湖闸</small></span>
<span class="marker gate" style="left: 26%; top: 12%"><img src="/assets/map/map-gate-enable.png" /><small>法泗闸</small></span>
<span class="marker gate" style="left: 32%; top: 19%"><img src="/assets/map/map-gate-enable.png" /><small>六贯闸</small></span>
<span class="marker gate" style="left: 49%; top: 46%"><img src="/assets/map/map-gate-enable.png" /><small>金水闸</small></span>
<span class="marker pump" style="left: 42%; top: 28%"><img src="/assets/map/map-pump.png" /><small>金口泵站</small></span>
<span class="marker pump" style="left: 46%; top: 78%"><img src="/assets/map/map-pump.png" /><small>两湖连通闸</small></span>
<span class="marker station" style="left: 23%; top: 59%"><img src="/assets/map/map-river-water-level.png" /><small>水位站</small></span>
<span class="marker station" style="left: 69%; top: 12%"><img src="/assets/map/map-rainfall-station.png" /><small>雨量站</small></span>
</div>
<div class="map-controls"><button aria-label="下载地图"><span></span></button><button aria-label="定位"><span></span></button><button aria-label="全屏"><img src="/assets/map/map1.png" /></button></div>
<div class="map-bottom-tools"><button aria-label="地图首页"><img src="/assets/map/map2.png" /></button><button aria-label="定位站点"><img src="/assets/map/map3.png" /></button><button aria-label="图层"><img src="/assets/map/map3.png" /></button><button aria-label="闪电图层"><span>ϟ</span></button></div>
</section>
<aside class="home-right panel-stack"><section class="data-panel pump-status"><h2>泵站运行情况</h2><div class="pump-tabs"><b>金口泵站</b><b>金口二站</b></div><div class="water-grid"><div><img src="/assets/njsw.png" /><b>0</b><small>内江水位</small></div><div><img src="/assets/njsw.png" /><b>0</b><small>内江水位</small></div><div><img src="/assets/wjsw.png" /><b>0</b><small>外江水位</small></div><div><img src="/assets/wjsw.png" /><b>0</b><small>外江水位</small></div></div><div class="pump-bars"><span>开机台数 <i style="width: 6%"></i><b>/6</b></span><span><i style="width: 2%"></i><b>0/3</b></span><span>开机功率 <i style="width: 18%"></i><b>65/13200</b></span><span><i style="width: 1%"></i><b>0/11250</b></span><span>开机排量 <i style="width: 0%"></i><b>/144</b></span><span><i style="width: 0%"></i><b>/96</b></span></div></section><section class="data-panel line-chart"><h2>鲁湖闸水位过程线</h2><div class="legend"><span v-for="n in ['鲁湖闸','法泗闸','六贯闸','金水闸']" :key="n">{{ n }}</span></div><svg viewBox="0 0 300 100" preserveAspectRatio="none"><polyline points="0,65 15,53 30,57 45,48 60,54 75,42 90,50 105,45 120,59 135,50 150,56 165,48 180,53 195,47 210,50 225,45 240,52 255,49 270,54 285,49 300,53" /></svg><div class="chart-labels">21.8  21.7  21.6  21.5  21.4</div></section><section class="data-panel video-panel"><h2>视频监测 <button>▶ 播放</button></h2><strong>无信号</strong><small>NO SIGNAL</small><footer>0 KB/s ↻</footer></section></aside>
<div class="map-status">海拔:0米 经度:114.189113 纬度:30.041939 级别:10.64</div>
</section>
<section v-else class="content-view">
<aside class="side-menu"><button v-for="item in sideItems" :key="item.key" :class="{ active: page === item.key }" @click="routeTo(item.key)"><span>{{ item.icon }}</span>{{ item.label }}</button></aside>
<section class="page-content">
<template v-if="page === 'gate-real' || page === 'gate-maintain'">
<div class="filters"><label>水系 : <select v-model="selectedGate"><option value="">请选择水系</option><option>金水河</option></select></label><label>{{ page === 'gate-real' ? '闸站编号' : '闸站编号' }} : <input v-model="gateIdQuery" /></label><label>闸站名称 : <input v-model="gateNameQuery" /></label><label>闸站类型 : <select><option>请选择</option></select></label><button class="primary" @click="gateNameQuery = gateNameQuery.trim()">查询</button><button @click="resetFilters">重置</button><button v-if="page === 'gate-maintain'" class="new-btn" @click="maintainRows.push({ id: String(maintainRows.length + 1), name: '新建闸站', river: '金水河', designFlow: '-', front: '0.0%', back: '-', correction: '-' })">+ 新建</button></div>
<h2 class="page-title">{{ page === 'gate-real' ? '涵闸实时数据' : '涵闸' }}</h2>
<table class="data-table" v-if="page === 'gate-real'"><thead><tr><th>闸站编号</th><th>闸站名称</th><th>所属水系</th><th>流量(m³/s)</th><th>闸前水位(m)</th><th>闸前水势</th><th>闸后水位(m)</th><th>闸后水势</th><th>最后上报时间</th></tr></thead><tbody><tr v-for="row in filteredGateRows" :key="row.id"><td><button class="row-plus"></button>{{ row.id }}</td><td>{{ row.name }}</td><td>{{ row.river }}</td><td>{{ row.flow }}</td><td class="linkish">{{ row.front }}</td><td class="trend">{{ row.frontTrend }}</td><td class="linkish">{{ row.back }}</td><td class="trend">{{ row.backTrend }}</td><td>{{ row.report }}</td></tr></tbody></table>
<table class="data-table" v-else><thead><tr><th>闸站编号</th><th>闸站名称</th><th>所属水系</th><th>设计流量(m³/s)</th><th>设计闸前水位(m)</th><th>设计闸后水位(m)</th><th>开度</th><th>闸前水位校正值</th><th>闸后水位校正值</th><th>操作</th></tr></thead><tbody><tr v-for="row in maintainRows" :key="row.id"><td><button class="row-plus"></button>{{ row.id }}</td><td>{{ row.name }}</td><td>{{ row.river }}</td><td>{{ row.designFlow }}</td><td>{{ row.front }}</td><td>{{ row.back }}</td><td>{{ row.front }}</td><td>{{ row.back }}</td><td>{{ row.correction }}</td><td><a>修改</a><a @click="maintainRows = maintainRows.filter((r) => r.id !== row.id)">删除</a><a>新增闸门</a></td></tr></tbody></table>
<div class="pager">{{ page === 'gate-real' ? filteredGateRows.length : maintainRows.length }} 条  <button disabled></button><b>1</b><button disabled></button><select><option>20 条/页</option></select> 跳至 <input /></div>
</template>
<template v-else-if="page === 'rain-excerpt'">
<div class="summary-cards"><div><b></b><strong>最大降雨过程</strong><em>0</em></div><div><b></b><strong>最大降雨过程次数</strong><em>0</em></div><div><b></b><strong>最大降雨过程累计雨量 mm</strong><em>0</em></div><div><b></b><strong>最大降雨过程累计时长 h</strong><em>0</em></div></div><div class="filters"><label>测站名称 : <select v-model="selectedStation"><option>全部</option><option>金口</option></select></label><label>年份 : <input v-model="selectedYear" /></label><button class="primary">查询</button><button @click="resetFilters">重置</button></div><h2 class="page-title">{{ selectedYear }}年度降雨过程累计次数</h2><div class="empty-chart"><div class="empty-bars"><i v-for="n in 14" :key="n" :style="{ height: `${8 + ((n * 11) % 42)}px` }"></i></div><span>累计次数  累计降雨量</span></div><table class="data-table"><thead><tr><th>序号</th><th>测站名称</th><th>降水次序</th><th>降水起</th><th>降水止</th><th>累计雨量(mm)</th><th>累计历时(h)</th></tr></thead><tbody><tr><td colspan="7" class="empty-cell">暂无数据</td></tr></tbody></table>
</template>
<template v-else-if="page === 'pump-history'">
<div class="filters"><label>泵站 : <select><option>金口泵站</option><option>金口二站</option></select></label><label>开始时间 : <input v-model="startDate" /></label><label>结束时间 : <input v-model="endDate" /></label><button class="primary">查询</button><button @click="resetFilters">重置</button></div><h2 class="page-title">{{ startDate }}{{ endDate }} 【金口泵站】功率流量曲线图</h2><div class="power-chart"><svg viewBox="0 0 900 180" preserveAspectRatio="none"><polyline points="0,145 55,145 110,145 165,145 220,145 275,145 330,145 385,145 440,138 495,120 550,145 605,145 660,145 715,145 770,145 825,145 900,145" /><polyline class="blue" points="0,125 55,125 110,125 165,125 220,125 275,125 330,125 385,125 440,110 495,80 550,125 605,125 660,125 715,125 770,125 825,125 900,125" /></svg></div><h2 class="page-title">{{ startDate }}{{ endDate }} 【金口泵站】历史明细表</h2><table class="data-table wide"><thead><tr><th>序号</th><th>时间</th><th>泵站编号</th><th>泵站名称</th><th>所属水系</th><th>机组数</th><th>设计功率</th><th>1#机组(w)</th><th>2#机组(w)</th><th>开机功率</th><th>开机台数</th><th>流量(m³/s)</th></tr></thead><tbody><tr v-for="row in pumpRows" :key="row.index"><td>{{ row.index }}</td><td>{{ row.time }}</td><td>{{ row.id }}</td><td>{{ row.name }}</td><td>{{ row.river }}</td><td>{{ row.units }}</td><td>{{ row.design }}</td><td>{{ row.index === 1 ? '10.000' : '0.000' }}</td><td>{{ row.index === 1 ? '10.000' : '0.000' }}</td><td>{{ row.power }}</td><td>{{ row.count }}</td><td>{{ row.flow }}</td></tr></tbody></table><div class="pager">共 287 条  <button disabled></button><b>1</b><button>2</button><button>3</button><button>4</button><button>5</button><button></button><select><option>20 条/页</option></select></div>
</template>
<template v-else><div class="placeholder-page"><h2>{{ sideItems.find((item) => item.key === page)?.label || '页面' }}</h2><div class="placeholder-grid"><div v-for="n in 6" :key="n"><b>{{ ['实时水情','实时流量','实时雨情','涵闸视频','泵站实时数据','综合监测'][n - 1] }}</b><strong>{{ n === 4 ? '无信号' : n === 5 ? '0 / 6' : '0.00' }}</strong><small>数据更新时间 14:45:00</small></div></div></div></template>
</section>
</section>
</template>
</main>
</template>
import { createApp } from 'vue'
import App from './App.vue'
import './styles.css'
createApp(App).mount('#app')
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;600;700&display=swap');
:root { font-family: 'Noto Sans SC', 'Microsoft YaHei', sans-serif; color: #e6f5ff; background: #071842; font-synthesis: none; }
* { box-sizing: border-box; }
body { margin: 0; min-width: 1100px; background: #071842; }
button, input, select { font: inherit; }
button { cursor: pointer; }
.app-shell { min-height: 100vh; background: #071842; }
.login-screen { min-height: 100vh; display: flex; align-items: center; justify-content: center; position: relative; overflow: hidden; background: #090e4d url('/assets/newlogin_bg.jpg') center / cover no-repeat; }
.login-screen::after { content: ''; position: absolute; inset: 0; background: linear-gradient(90deg, rgba(3, 12, 78, .18), rgba(5, 16, 71, .08)); pointer-events: none; }
.login-art { position: absolute; inset: 0; pointer-events: none; }
.login-panel { position: absolute; z-index: 1; left: 56%; top: 50%; width: 380px; transform: translateY(-50%); padding: 38px 52px 48px; background: rgba(12, 45, 106, .76); box-shadow: 0 18px 50px rgba(0, 8, 55, .24); }
.login-panel h1 { margin: 0 0 31px; color: #fff; font-size: 21px; line-height: 1.15; font-weight: 500; text-align: center; letter-spacing: 1px; }
.login-field { display: flex; align-items: center; height: 46px; margin-bottom: 17px; border: 1px solid rgba(112, 170, 242, .32); color: #bcd7fa; }
.login-field span { width: 31px; padding-left: 12px; font-size: 18px; color: #d3e8ff; }
.login-field input { min-width: 0; flex: 1; height: 100%; border: 0; outline: 0; color: #eaf5ff; background: transparent; font-size: 14px; }
.login-field input::placeholder { color: #d0def2; }
.captcha-field { padding-right: 9px; }
.captcha-code { width: 109px; height: 38px; border: 0; color: #6754ab; background: #f8fbff; font-family: Georgia, serif; font-size: 24px; letter-spacing: 6px; transform: skew(-4deg); }
.login-submit { width: 100%; height: 45px; margin-top: 16px; border: 0; color: #fff; background: linear-gradient(180deg, #18d9cd, #078ce9); font-size: 15px; box-shadow: 0 6px 18px rgba(0, 180, 255, .22); }
.login-error { margin: -12px 0 16px; color: #ff9a9a; text-align: center; font-size: 12px; }
.topbar { height: 86px; display: flex; align-items: center; gap: 0; padding: 0 4px; background: linear-gradient(180deg, #071b55, #06133c); border-bottom: 1px solid #153b75; }
.clock-block { width: 207px; height: 75px; padding: 4px 11px 0; text-align: center; color: #65e7ff; background: linear-gradient(135deg, rgba(14,123,209,.9), rgba(18,50,133,.9)); border: 1px solid #1a7dd0; border-radius: 0 18px 18px 0; box-shadow: inset 0 0 15px rgba(59,210,255,.24); }
.clock-block strong { display: block; font-family: monospace; font-size: 32px; letter-spacing: 4px; line-height: 1; }
.clock-block span { display: block; margin-top: 5px; color: #dcf7ff; font-size: 12px; }
.weather-block { width: 120px; height: 75px; display: flex; align-items: center; gap: 5px; padding-left: 3px; color: #fff; background: linear-gradient(90deg, rgba(18,50,133,.9), rgba(8,25,78,.4)); }
.weather-icon { font-size: 42px; color: #54c9f1; }
.weather-block b, .weather-block small { display: block; font-size: 14px; font-weight: 400; }
.weather-block small { margin-top: 2px; color: #e0f0ff; }
.main-nav { display: flex; align-items: center; gap: 0; }
.main-nav button { height: 54px; padding: 0 17px; border: 0; color: #d4e4f9; background: transparent; font-size: 17px; white-space: nowrap; }
.main-nav button.active { color: #fff; background: linear-gradient(180deg, #1379d5, #0a3c96); box-shadow: inset 0 0 12px rgba(35, 210, 255, .45); }
.nav-left { margin-left: 0; }
.nav-right { margin-right: 4px; }
.topbar h1 { flex: 1; min-width: 310px; margin: 0 12px; padding: 8px 26px; color: #37e5f4; font-size: 28px; font-weight: 500; text-align: center; letter-spacing: 3px; text-shadow: 0 0 12px rgba(20, 223, 255, .42); background: linear-gradient(90deg, transparent, rgba(4,78,158,.65), transparent); }
.user-chip { color: #e4eefc; font-size: 12px; white-space: nowrap; }
.user-chip span { display: inline-block; width: 18px; height: 18px; margin-right: 5px; border-radius: 50%; vertical-align: middle; background: #727f99; }
.home-view { position: relative; height: calc(100vh - 86px); min-height: 625px; display: grid; grid-template-columns: 344px minmax(480px, 1fr) 344px; gap: 0; overflow: hidden; background: #061842; }
.map-view { position: relative; overflow: hidden; background: #071a42 url('/assets/home-map.jpg') center / 100% 100% no-repeat; }
.map-view::after { content: ''; position: absolute; inset: 0; background: linear-gradient(180deg, rgba(0, 17, 59, .03), rgba(0, 8, 38, .08)); pointer-events: none; }
.map-label { position: absolute; z-index: 1; left: 46%; top: 1px; color: #1de9ee; font-size: 18px; font-weight: 700; opacity: 0; }
.map-collapse { position: absolute; z-index: 4; top: 5px; left: 0; width: 24px; height: 24px; border: 0; color: #d7f6ff; background: rgba(4, 31, 84, .75); opacity: 0; }
.map-markers { position: absolute; inset: 0; z-index: 1; pointer-events: none; opacity: 0; }
.map-markers span { position: absolute; display: flex; flex-direction: column; align-items: center; }
.map-markers img { width: 25px; height: 25px; object-fit: contain; }
.map-markers small { margin-top: -2px; color: #dbf1ff; font-size: 10px; font-weight: 400; text-shadow: 1px 1px 2px #172d52; }
.map-controls { position: absolute; right: 4px; bottom: 31px; z-index: 3; display: grid; gap: 5px; }
.map-controls button { display: grid; place-items: center; width: 38px; height: 38px; padding: 0; border: 0; color: #d2f5ff; background: transparent; opacity: 0; }
.map-controls button img { width: 38px; height: 38px; object-fit: contain; }
.map-bottom-tools { position: absolute; z-index: 2; bottom: -2px; left: 27%; display: flex; gap: 4px; }
.map-bottom-tools button { width: 68px; height: 55px; padding: 0; border: 0; border-radius: 12px 12px 0 0; color: #d2f5ff; background: transparent; opacity: 0; }
.map-bottom-tools img { width: 68px; height: 64px; object-fit: contain; }
.map-status { position: absolute; z-index: 4; bottom: 2px; left: 10px; color: #dff6ff; font-size: 10px; }
.panel-stack { display: flex; flex-direction: column; gap: 0; padding: 3px 0 0; background: rgba(4, 20, 62, .88); }
.data-panel { overflow: hidden; border: 1px solid #17477f; border-left-color: #0b6fba; border-right-color: #0b6fba; background: linear-gradient(180deg, rgba(9, 58, 119, .9), rgba(3, 24, 68, .95)); box-shadow: inset 0 0 22px rgba(8, 115, 211, .14); }
.data-panel h2 { height: 31px; margin: 0; padding: 7px 8px; border-bottom: 1px solid #1a588f; color: #e0f7ff; background: linear-gradient(90deg, rgba(10, 71, 135, .95), rgba(7, 47, 99, .9)); font-size: 13px; font-weight: 500; }
.overview { min-height: 245px; }
.overview p { margin: 11px 17px; color: #e4f2ff; font-size: 15px; line-height: 2.02; }
.home-left .data-panel:nth-child(2) { min-height: 278px; }
.overview p b { color: #53e6ff; font-weight: 500; }
.overview em { color: #fff; font-style: normal; }
table { width: 100%; border-collapse: collapse; }
th, td { border-bottom: 1px solid rgba(86, 144, 213, .16); padding: 8px 4px; text-align: center; font-size: 13px; white-space: nowrap; }
th { color: #70dafd; font-weight: 400; }
td { color: #e3f4ff; }
.expand { display: inline-flex; width: 13px; height: 13px; margin-right: 3px; align-items: center; justify-content: center; border: 1px solid #5bbbf4; color: #8de4ff; font-size: 10px; }
.rain-chart { flex: 1; min-height: 170px; position: relative; }
.rain-chart small { position: absolute; top: 45px; left: 10px; color: #e8f8ff; font-size: 9px; }
.bars, .empty-bars { height: 105px; display: flex; align-items: end; gap: 8px; margin: 18px 10px 1px 29px; border-bottom: 1px solid rgba(75, 136, 202, .4); background: repeating-linear-gradient(to top, rgba(50, 109, 176, .16) 0, rgba(50, 109, 176, .16) 1px, transparent 1px, transparent 25px); }
.bars i, .empty-bars i { display: block; flex: 1; min-width: 4px; background: linear-gradient(to top, #0752a7, #25dfff); opacity: .74; }
.axis { padding: 3px 4px 7px 26px; color: #c3e8f8; font-family: monospace; font-size: 8px; white-space: nowrap; }
.pump-status { min-height: 341px; }
.pump-tabs { display: grid; grid-template-columns: 1fr 1fr; border-bottom: 1px solid rgba(74, 156, 233, .2); }
.pump-tabs b { padding: 6px 0; text-align: center; font-size: 10px; font-weight: 500; }
.water-grid { display: grid; grid-template-columns: 1fr 1fr; }
.water-grid > div { display: grid; grid-template-columns: 31px 1fr; align-items: center; padding: 5px 8px; border-bottom: 1px solid rgba(83, 145, 222, .18); }
.water-grid img { width: 24px; grid-row: span 2; }
.water-grid b { color: #56efff; font-size: 14px; font-weight: 400; }.water-grid small { color: #e4f3ff; font-size: 9px; }
.pump-bars { padding: 7px 9px; }.pump-bars span { position: relative; display: block; height: 19px; color: #d8edff; font-size: 9px; }.pump-bars i { position: absolute; left: 52px; top: 4px; width: 120px; height: 8px; border-radius: 6px; background: linear-gradient(90deg, #0753bb, #0f7add); }.pump-bars b { float: right; font-weight: 400; }
.line-chart { min-height: 217px; }.line-chart .legend { display: flex; gap: 12px; padding: 11px; font-size: 12px; }.line-chart svg { width: 100%; height: 106px; padding: 0 8px; background: repeating-linear-gradient(to top, rgba(39, 103, 179, .18) 0, rgba(39, 103, 179, .18) 1px, transparent 1px, transparent 25px); }.line-chart polyline { fill: none; stroke: #5cdbff; stroke-width: 1.7; }.chart-labels { color: #bfe3f7; padding: 0 9px 5px; font-size: 11px; }
.video-panel { flex: 1; min-height: 247px; position: relative; background: #07133c; }.video-panel h2 button { float: right; border: 0; color: #dff9ff; background: transparent; font-size: 14px; }.video-panel::before { content: ''; display: block; height: 159px; background: radial-gradient(circle at center, rgba(49, 75, 122, .18), transparent 45%); }.video-panel strong, .video-panel > small { position: absolute; top: 128px; left: 0; width: 100%; text-align: center; }.video-panel strong { color: #e0e7f4; font-size: 17px; font-weight: 400; }.video-panel > small { top: 164px; color: #aeb5ce; font-size: 13px; }.video-panel footer { padding: 9px 14px; color: #e6f8ff; font-size: 13px; }
.content-view { min-height: calc(100vh - 58px); display: flex; padding: 16px 19px 30px; background: #f2f5fa; color: #233a55; }
.side-menu { width: 185px; flex: 0 0 185px; padding: 5px 10px 0 0; border-right: 1px solid #d8e1ed; }.side-menu button { display: block; width: 100%; height: 42px; border: 0; border-bottom: 1px solid #e4eaf2; color: #60748a; background: transparent; text-align: left; font-size: 13px; }.side-menu button span { display: inline-block; width: 28px; color: #5e8eb6; text-align: center; font-size: 17px; }.side-menu button.active { color: #126ec3; background: #e6f3ff; border-right: 3px solid #1684dc; }
.page-content { min-width: 0; flex: 1; padding-left: 22px; overflow: auto; }.filters { display: flex; align-items: center; gap: 12px; min-height: 48px; padding: 8px 11px; border: 1px solid #d9e3ee; background: #fff; box-shadow: 0 1px 2px rgba(25, 65, 102, .04); }.filters label { color: #4d6177; font-size: 12px; white-space: nowrap; }.filters input, .filters select { height: 29px; min-width: 106px; margin-left: 4px; padding: 0 8px; border: 1px solid #ccd9e8; outline: 0; color: #334c67; background: #fff; }.filters button { height: 29px; padding: 0 16px; border: 1px solid #ced9e7; color: #48627a; background: #fff; }.filters button.primary, .filters .new-btn { color: #fff; border-color: #197dd0; background: #1584d7; }.filters .new-btn { margin-left: auto; }
.page-title { margin: 17px 0 10px; color: #284964; font-size: 16px; font-weight: 500; }.data-table { border: 1px solid #d8e1eb; background: #fff; }.data-table th { padding: 11px 8px; color: #4e6883; background: #f4f7fa; font-size: 12px; border-right: 1px solid #e3eaf1; }.data-table td { padding: 10px 7px; color: #52697e; font-size: 12px; border-right: 1px solid #edf1f5; }.data-table tr:hover td { background: #f7fbff; }.row-plus { margin-right: 5px; border: 0; color: #257dc0; background: transparent; }.linkish, .data-table a { color: #1986d3; cursor: pointer; }.trend { color: #4c9bcc; font-size: 15px !important; }.data-table.wide { min-width: 1100px; }.pager { padding: 14px 0; color: #718397; font-size: 12px; }.pager button, .pager select, .pager input { height: 26px; margin: 0 3px; border: 1px solid #d5e0ea; color: #59738b; background: #fff; }.pager b { display: inline-block; padding: 5px 9px; color: #fff; background: #1685d5; font-weight: 400; }.pager select { padding: 0 5px; }.pager input { width: 38px; }.summary-cards { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; margin-bottom: 15px; }.summary-cards > div { display: grid; grid-template-columns: 35px 1fr; padding: 12px; border: 1px solid #d9e3ee; background: #fff; }.summary-cards b { grid-row: span 2; color: #45a4da; font-size: 26px; }.summary-cards strong { color: #60778c; font-size: 12px; font-weight: 400; }.summary-cards em { color: #2486c8; font-size: 20px; font-style: normal; }.empty-chart { width: 100%; height: 175px; margin: 10px 0 16px; padding: 12px 22px; border: 1px solid #dce4ee; background: #fff; }.empty-bars { height: 120px; margin: 3px 0 2px; background: repeating-linear-gradient(to top, #edf3f8 0, #edf3f8 1px, transparent 1px, transparent 29px); }.empty-bars i { background: linear-gradient(to top, #98d7f1, #5bb8e7); opacity: .55; }.empty-chart span { color: #8194a6; font-size: 11px; }.empty-cell { height: 160px; color: #aebdca !important; }.power-chart { height: 190px; border: 1px solid #dce4ee; background: #fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='60' height='30'%3E%3Cpath d='M0 29h60M0 0h60' stroke='%23eef3f7' fill='none'/%3E%3C/svg%3E") repeat; }.power-chart svg { width: 100%; height: 100%; padding: 25px 22px; }.power-chart polyline { fill: none; stroke: #f4a040; stroke-width: 2; }.power-chart polyline.blue { stroke: #2a9ae3; }
.placeholder-page h2 { margin: 0 0 20px; color: #284964; font-size: 18px; }.placeholder-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; }.placeholder-grid > div { min-height: 125px; padding: 16px; border: 1px solid #d9e4ef; background: #fff; }.placeholder-grid b, .placeholder-grid strong, .placeholder-grid small { display: block; }.placeholder-grid b { color: #4e6b83; font-size: 13px; }.placeholder-grid strong { margin-top: 22px; color: #1784cd; font-size: 23px; font-weight: 500; }.placeholder-grid small { margin-top: 8px; color: #94a7b9; font-size: 11px; }
@media (max-width: 1250px) { .topbar h1 { font-size: 18px; }.main-nav button { padding: 0 9px; font-size: 13px; }.home-view { grid-template-columns: 243px minmax(420px, 1fr) 243px; }.overview { min-height: 195px; }.overview p { font-size: 11px; margin: 9px 13px; }.home-left .data-panel:nth-child(2) { min-height: 198px; }.pump-status { min-height: 242px; }.line-chart { min-height: 153px; }.video-panel { min-height: 175px; }.login-panel { left: 56%; } }
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
base: './',
})
......@@ -6,10 +6,13 @@
```text
Vue 3 主应用
└── React 子模块 ──┐
├─ HTTP/API ─ Spring Boot API ─ PostgreSQL
└── React 子模块 ── HTTP/API ─
├─ Java 主业务后端 ─ PostgreSQL
│ ├─ Redis
│ └─ MinIO
└─ 已认证的内部 HTTP 调用 ─ Python Agent
├─ LangChain
└─ LangGraph
```
当前图仅描述已确认的系统边界,不代表最终部署拓扑。
......@@ -20,26 +23,64 @@ Vue 3 主应用
- React 子模块位于 Vue 主应用内部,随主应用一起构建和部署。
- React 子模块与 Vue 主应用之间通过明确的挂载、输入、输出和事件契约交互。
## 3. 后端
## 3. 服务职责
- `services/api/` 是 Java 模块化单体主业务后端,负责认证授权、业务规则、业务数据持久化,以及对 PostgreSQL、Redis 和 MinIO 的受控访问。
- `services/agent/` 是 Python 3.11+ Agent 进程,使用 LangChain 组织模型、提示词和工具,使用 LangGraph 编排有状态、多步骤或可恢复的 Agent 流程。
- 前端请求必须先进入 `services/api/`;Java 完成用户认证和授权后,才可以通过内部 HTTP API 调用 Agent,并传递经过校验的用户和请求上下文。
- Agent 不拥有主业务数据的最终写入权,不直接连接主业务 PostgreSQL,也不建立独立的用户认证体系;业务数据读写通过 `services/api/` 完成。
- 模型供应商、提示词、工具权限和 Agent 状态属于 Agent 服务边界;业务实体、用户权限和业务审计属于 Java 主业务后端边界。
## 4. Java DDD 主业务后端
- 后端位于 `services/api/`,使用 Spring Boot、JDK 21 和 Maven。
- Maven 坐标使用 `mc:mcaigc`,Java 基础包名使用 `mc.mcaigc`
- API、领域逻辑和基础设施访问应保持清晰边界;具体模块随真实业务出现后再建立。
- 数据库结构变更必须通过迁移管理,但迁移工具尚待选定。
## 4. 数据与基础设施
Maven 多模块职责如下:
```text
mcaigc-boot
├── mcaigc-interfaces ── mcaigc-application ── mcaigc-domain
└── mcaigc-infrastructure ────────────────────┘
```
- `domain` 不依赖 Spring、数据库驱动或 Web 框架。
- `application` 只编排用例和事务边界,通过领域端口访问外部能力。
- `infrastructure` 实现领域端口,封装 PostgreSQL、Redis 和 MinIO 等技术细节。
- `interfaces` 负责 HTTP/DTO 等输入输出适配,不承载领域规则。
- `boot` 只负责启动和依赖组装,不承载业务逻辑。
当前骨架是一个模块化单体主业务服务内部的 DDD 多模块结构,暂不拆分为多个微服务。未来确需按限界上下文拆分时,各服务应复用相同的分层约束;服务发现、配置中心、网关和分布式事务等 Spring Cloud 基础设施不在本次骨架中预设。
Python Agent 已提供最小包骨架和无模型 LangGraph 自检流程;真实模型、内部 HTTP 服务、依赖锁定方式和部署形态仍需按业务场景确定。
### Java 与 Agent 的调用方式
- Java 与 Agent 作为独立进程运行,通过内部网络上的 HTTP API 通信;Agent 不直接暴露给浏览器或公网。
- Agent API 使用版本化路径,例如 `/internal/v1/`,请求和响应使用 JSON;具体资源名称、字段和错误格式在实现对应能力前单独确定。
- Java 负责校验用户身份、权限和业务参数,然后向 Agent 传递请求编号、用户/租户上下文、已授权能力和业务输入。Agent 不重复建立用户登录体系,只校验 Java 的服务间调用凭据和请求格式。
- 短时间内可完成的生成任务使用同步 HTTP 请求;较长任务仍通过 HTTP 创建任务并返回任务编号,状态查询或结果获取由 Java 负责对前端提供统一接口。
- Java 调用 Agent 必须设置连接和读取超时、有限重试和幂等请求编号;Agent 需要返回可区分的业务错误、参数错误和暂时不可用错误。
- 服务间认证方式(例如 mTLS 或内部服务令牌)、超时数值、流式输出方式和具体 API 契约在实现前确定。
## 5. 数据与基础设施
- PostgreSQL:持久化业务数据。
- Redis:缓存和适合其数据模型的短期状态;不得作为唯一持久化来源。
- MinIO:存储上传文件、生成内容等对象数据;数据库只保存必要的对象元数据和引用。
- Docker:用于本地开发依赖,不预设生产部署方式。
## 5. 待决策事项
## 6. 待决策事项
下列事项应在相关实现开始前确定:
1. React 子模块的挂载、状态和事件通信契约。
2. API 契约格式和版本策略。
3. PostgreSQL 数据库迁移工具。
4. MinIO 对象命名、生命周期和访问控制策略。
5. 本地 Docker 服务端口、卷和初始化流程。
2. Java 调用 Agent 的内部认证上下文和 HTTP API 契约。
3. Java 主业务后端的认证机制及 Agent 内部调用的信任方式。
4. Python 版本、Agent 依赖锁定方式和运行时隔离方式。
5. API 契约格式和版本策略。
6. PostgreSQL 数据库迁移工具。
7. MinIO 对象命名、生命周期和访问控制策略。
8. 本地 Docker 服务端口、卷和初始化流程。
......@@ -4,10 +4,11 @@
- Java:JDK 21
- 后端构建:Maven
- Agent:Python 3.11+、LangChain、LangGraph
- 前端:Vue 3 + TypeScript、React + TypeScript
- 本地依赖:Docker 中运行 PostgreSQL、Redis 和 MinIO
Node.js 版本、前端包管理器和具体框架版本尚未确定。初始化前端代码时必须一次性选定并提交锁文件。
Node.js 版本、前端包管理器和 Agent 依赖锁定方式尚未确定。初始化对应工程时必须一次性选定并提交锁文件。
## 2. 分支与提交
......@@ -21,6 +22,10 @@ Node.js 版本、前端包管理器和具体框架版本尚未确定。初始化
- 各应用初始化时建立可执行的格式化、静态检查、测试和构建命令。
- 新增依赖前确认标准库、平台能力或现有依赖不能满足需求。
- 跨模块调用使用公开契约,不依赖另一个模块的内部文件结构。
- 前端只调用 Java 主业务后端;Java 完成认证授权后通过内部 HTTP API 调用 Python Agent,Agent 不直接接受用户请求。
- Agent 不直接访问主业务数据库,也不建立独立的用户认证体系。
- Java 调用 Agent 时必须使用版本化 JSON 契约,并传递请求编号和经过授权的用户上下文;HTTP 超时、有限重试和幂等性应在客户端统一处理。
- 长时间 Agent 任务通过 HTTP 创建任务并异步查询结果,前端仍只访问 Java 提供的业务接口。
- 数据库、缓存和对象存储访问必须处理超时、失败和资源释放。
## 4. 配置管理
......@@ -31,10 +36,18 @@ Node.js 版本、前端包管理器和具体框架版本尚未确定。初始化
## 5. 初始化后的命令维护
当前没有业务工程,因此不提供不可执行的占位命令。后续初始化各应用时,应在根 `README.md` 补充以下真实命令:
当前业务骨架已生成,但前端和具体业务能力尚未初始化。后续扩展各应用时,应在根 `README.md` 补充以下真实命令:
- 安装依赖
- 启动本地基础设施
- 启动前端和后端
- 启动 Agent 服务
- 运行测试与静态检查
- 构建发布产物
当前骨架可执行的最小验证:
```bash
cd services/api && mvn verify
cd services/agent && python -m venv .venv && .venv/bin/python -m pip install -e '.[dev]' && .venv/bin/python -m pytest
```
......@@ -9,7 +9,8 @@
| 路径 | 用途 | 主要内容 |
| --- | --- | --- |
| `apps/web-vue/` | 主 Web 应用 | Vue 3、TypeScript 源码,以及内嵌 React 子模块 |
| `services/api/` | 后端 API | Spring Boot、JDK 21、Maven 工程 |
| `services/api/` | 模块化单体主业务 API | Spring Boot、JDK 21、Maven DDD 多模块工程,负责认证授权、业务规则和持久化 |
| `services/agent/` | Agent 进程 | Python 3.11+、LangChain、LangGraph,负责智能体编排和模型交互,由 Java 主业务后端调用 |
| `infra/docker/` | 本地基础设施 | PostgreSQL、Redis、MinIO 的本地编排与初始化配置 |
| `resources/raw/` | 原始资料 | Excel、Markdown、产品原型源文件及其他输入资料 |
| `docs/product/` | 产品资料 | 已整理、持续维护的需求和产品说明 |
......@@ -30,7 +31,12 @@
- React 子模块随 Vue 主应用一起维护依赖、构建配置和测试。
- React 子模块不得被设计为独立部署的前端应用;其挂载、状态和通信方式应通过 Vue 主应用的公开契约实现。
- 前后端通过版本化 API 契约交互,不共享运行时内部实现。
- Java 主业务后端与 Python Agent 分别维护依赖、运行时和测试;Java 主业务后端当前按模块化单体部署,不拆分为多个微服务。
- Java 主业务后端按 `domain``application``infrastructure``interfaces``boot` 分层;依赖方向由外向内,不得由领域层反向依赖技术实现。
- Java 主业务后端负责认证授权、业务 API、业务规则和数据库写入;Agent 负责智能体流程、工具调用和模型交互。
- 用户和前端不得直接访问 Agent;Java 完成认证授权后,才可调用 Agent 的内部 HTTP API。
- Agent 不建立独立用户认证体系,也不直接访问主业务数据库;业务数据读写通过 Java 主业务后端完成。
- Java 与 Agent 通过公开、版本化的内部 HTTP/JSON 契约交互,不共享运行时内部实现。短任务可同步调用,长任务通过 HTTP 任务接口异步处理。
## 5. 配置与安全
......
[tools]
python = "3.11"
[build-system]
requires = ["setuptools>=69", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "mcaigc-agent"
version = "0.1.0"
description = "梦畅AIGC Python Agent 服务"
requires-python = ">=3.11,<3.14"
dependencies = [
"langchain>=0.3,<2",
"langgraph>=0.2,<2",
]
[project.optional-dependencies]
dev = [
"pytest>=8,<9",
]
[project.scripts]
mcaigc-agent = "mcaigc_agent.__main__:main"
[tool.setuptools.packages.find]
where = ["src"]
[tool.pytest.ini_options]
testpaths = ["tests"]
# Python Agent 服务
该目录是梦畅AIGC 的 Python Agent 进程骨架,使用 Python 3.11+、LangChain 和 LangGraph。它不是用户认证入口,必须由 Java 主业务后端在完成认证授权后调用。
当前仅包含一个无模型的 LangGraph 回显流程,用于验证包结构和图编排入口。真实模型、工具、内部调用认证上下文、HTTP 服务协议和业务流程后续按需求接入。Agent 的 HTTP 接口只接受 Java 主业务后端的内部调用,不直接接受浏览器请求。
## 本地验证
```bash
python -m venv .venv
.venv/bin/python -m pip install -e '.[dev]'
.venv/bin/python -m pytest
.venv/bin/mcaigc-agent
```
"""梦畅AIGC Python Agent 服务。"""
__version__ = "0.1.0"
from .graph import build_graph
def main() -> None:
result = build_graph().invoke({"input": "mcaigc-agent-ready", "output": ""})
print(result["output"])
if __name__ == "__main__":
main()
from typing import TypedDict
from langgraph.graph import END, START, StateGraph
class AgentState(TypedDict):
input: str
output: str
def _echo_node(state: AgentState) -> AgentState:
return {"input": state["input"], "output": state["input"]}
def build_graph():
"""Build the model-free graph used as the initial service self-check."""
graph = StateGraph(AgentState)
graph.add_node("echo", _echo_node)
graph.add_edge(START, "echo")
graph.add_edge("echo", END)
return graph.compile()
from mcaigc_agent.graph import build_graph
def test_graph_echoes_input() -> None:
result = build_graph().invoke({"input": "hello", "output": ""})
assert result["output"] == "hello"
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>mc</groupId>
<artifactId>mcaigc</artifactId>
<version>0.1.0-SNAPSHOT</version>
</parent>
<artifactId>mcaigc-application</artifactId>
<name>mcaigc-application</name>
<dependencies>
<dependency>
<groupId>mc</groupId>
<artifactId>mcaigc-domain</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>
package mc.mcaigc.application.shared;
/**
* Application module marker until the first use case is introduced.
*/
public final class ApplicationMarker {
private ApplicationMarker() {
}
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>mc</groupId>
<artifactId>mcaigc</artifactId>
<version>0.1.0-SNAPSHOT</version>
</parent>
<artifactId>mcaigc-boot</artifactId>
<name>mcaigc-boot</name>
<dependencies>
<dependency>
<groupId>mc</groupId>
<artifactId>mcaigc-interfaces</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>mc</groupId>
<artifactId>mcaigc-infrastructure</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
package mc.mcaigc;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication(scanBasePackages = "mc.mcaigc")
public class ApiApplication {
public static void main(String[] args) {
SpringApplication.run(ApiApplication.class, args);
}
}
package mc.mcaigc;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class ApiApplicationTest {
@Test
void contextLoads() {
}
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>mc</groupId>
<artifactId>mcaigc</artifactId>
<version>0.1.0-SNAPSHOT</version>
</parent>
<artifactId>mcaigc-domain</artifactId>
<name>mcaigc-domain</name>
</project>
package mc.mcaigc.domain.shared;
/**
* Domain module marker until the first bounded context is introduced.
*/
public final class DomainMarker {
private DomainMarker() {
}
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>mc</groupId>
<artifactId>mcaigc</artifactId>
<version>0.1.0-SNAPSHOT</version>
</parent>
<artifactId>mcaigc-infrastructure</artifactId>
<name>mcaigc-infrastructure</name>
<dependencies>
<dependency>
<groupId>mc</groupId>
<artifactId>mcaigc-domain</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
</dependencies>
</project>
package mc.mcaigc.infrastructure.configuration;
import org.springframework.context.annotation.Configuration;
/**
* Technical adapters are registered here as bounded contexts are added.
*/
@Configuration
public class InfrastructureConfiguration {
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>mc</groupId>
<artifactId>mcaigc</artifactId>
<version>0.1.0-SNAPSHOT</version>
</parent>
<artifactId>mcaigc-interfaces</artifactId>
<name>mcaigc-interfaces</name>
<dependencies>
<dependency>
<groupId>mc</groupId>
<artifactId>mcaigc-application</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
</dependencies>
</project>
package mc.mcaigc.interfaces.health;
import java.util.Map;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/internal/health")
public class HealthController {
@GetMapping
public Map<String, String> health() {
return Map.of("status", "UP");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.5</version>
<relativePath/>
</parent>
<groupId>mc</groupId>
<artifactId>mcaigc</artifactId>
<version>0.1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>mcaigc</name>
<description>梦畅AIGC 主业务后端</description>
<properties>
<java.version>21</java.version>
</properties>
<modules>
<module>mcaigc-domain</module>
<module>mcaigc-application</module>
<module>mcaigc-infrastructure</module>
<module>mcaigc-interfaces</module>
<module>mcaigc-boot</module>
</modules>
</project>
# Java 主业务后端
该目录是梦畅AIGC 的 Spring Boot 模块化单体主业务后端,使用 JDK 21 和 Maven 多模块 DDD 骨架。当前不拆分为多个微服务。
## 模块
- `mcaigc-domain/`:领域模型和领域端口
- `mcaigc-application/`:应用服务和用例编排
- `mcaigc-infrastructure/`:技术适配和基础设施实现
- `mcaigc-interfaces/`:HTTP 接口和输入输出适配
- `mcaigc-boot/`:启动入口和模块组装
## 验证
```bash
mvn verify
```
启动类为 `mc.mcaigc.ApiApplication`。当前仅提供 `GET /internal/health` 骨架接口。后续由 Java 调用 Agent 的内部 HTTP API;相关入口必须在 Java 完成用户认证授权并补充服务间认证后开放。
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment