diff --git a/.gitignore b/.gitignore index ff82742..631d03f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -# ---> Java # Compiled class file *.class @@ -10,28 +9,17 @@ # Mobile Tools for Java (J2ME) .mtj.tmp/ +target # Package Files # *.jar *.war -*.nar *.ear *.zip *.tar.gz *.rar +*.iml +.idea # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml -hs_err_pid* -replay_pid* - -# ---> Vue -# gitignore template for Vue.js projects -# -# Recommended template: Node.gitignore - -# TODO: where does this rule come from? -docs/_book - -# TODO: where does this rule come from? -test/ - +hs_err_pid* \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..2ba55ff --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,67 @@ +# AGENTS.md + +## Repo + +Fork of [renren-security](https://gitee.com/renrenio/renren-security) customized as a weather-data management system. Spring Boot 3.5.x multi-module Maven project, Java 17. + +## Quick start + +```bash +mvn clean install -DskipTests +``` + +Run `AdminApplication.java` (`system-admin/`), context path `/system-admin`, port 8080. DB = `weather_data_system` (MySQL). Init from `system-admin/db/mysql.sql`. Default admin: `admin` / `admin` (pw encoded w/ BCrypt). + +Tests are **skipped by default** (`true` in pom). If running tests, pass `-DskipTests=false`. + +## Modules + +| Module | Purpose | +|---|---| +| `system-admin` | Main admin backend. Entrypoint: `com.weather.AdminApplication` | +| `system-api` | API service (separate process). Entrypoint: `com.weather.ApiApplication` | +| `system-common` | Shared lib: base entities, utils, Redis, XSS, validators | +| `system-dynamic-datasource` | Multi-DS support (stub, not populated) | +| `renren-generator` | Code generator. Entrypoint: `com.weather.GeneratorApplication` | + +All sub-modules depend on `system-common`. Maven repo: Aliyun mirror. + +## Key framework choices + +- **Security**: Apache Shiro 1.12 (Jakarta classifier) with OAuth2 token auth, not Spring Security. +- **ORM**: MyBatis-Plus 3.5.8. Mapper XMLs in `src/main/resources/mapper/**/*.xml`. +- **Scheduled tasks**: Quartz via `schedule_job` table. All tasks implement `ITask` interface and are `@Component`-named beans. Jobs auto-register at startup via `JobCommandLineRunner`. +- **Data permissions**: `@DataFilter` annotation + `DataFilterAspect`. Dept-based filtering injected into MyBatis via `DataFilterInterceptor`. +- **Auto-fill**: `FieldMetaObjectHandler` fills `creator`/`createDate`/`updater`/`updateDate`. +- **API docs**: Knife4j (Swagger UI) at `/doc.html` when enabled (disabled by default). +- **Redis**: Optional. Toggle via `project-options.redis.open: false`. +- **Validation**: Hibernate Validator. XSS filter via custom `XssFilter`. +- **Excel**: EasyExcel. Async import with progress tracking (`WeatherDataImportManager`). + +## Custom weather module (`modules/weather/`) + +- `dailydata/` — daily weather observations. Supports Excel batch import (async, via `EasyExcel` listener pattern). +- `station/` — weather station CRUD. Stations are linked to departments (`dept_id`). +- `filescan/` — file scanning module (receive/display/archive). WatchService primary + Quartz fallback. Files served via `FileDownloadController` (`/filescan/file/display/{id}`). + +## Database + +- Scripts in `system-admin/db/` (mysql.sql + Oracle/SQLServer/PostgreSQL/Dameng variants). +- Custom tables: `weather_daily_data`, `weather_station`. + +## Conventions + +- `Result` wrapper class for all API responses (in `system-common`). +- `CrudService`/`BaseService` pattern for service layer. +- DTO/Entity/Excel VO separation per module. +- PK strategy: `ASSIGN_ID` (Snowflake via `IdUtil.getSnowflakeNextId()`). +- Lombok everywhere (`@Data`, `@AllArgsConstructor`, `@Slf4j`). + +## Gotchas + +- Do **not** introduce Spring Security dependencies — project uses Shiro exclusively. +- Swagger (Knife4j) is **disabled by default** (`knife4j.enable: false`). Enable in dev profile. +- `schedule_job` table seed includes a test task `testTask` (paused by default). New tasks must be `@Component("beanName")` implementing `ITask`. +- The `system-dynamic-datasource` module is a placeholder. Multi-DS config in `application-dev.yml` is commented out. +- No frontend sources in this repo (separate `renren-ui` Vue3 project). +- `.gitignore` excludes `.idea/` but `.idea/` is tracked (committed IDE config). diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..e85fae6 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,107 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +> **See also** `AGENTS.md` for module breakdown, framework choices, conventions, and gotchas. This file supplements it with build commands and architectural patterns. + +## Build & Run + +```bash +# Full build (tests skipped by default per pom.xml true) +mvn clean install -DskipTests + +# Build with tests +mvn clean install -DskipTests=false + +# Run single test class +mvn -pl system-admin -DskipTests=false -Dtest=YourTestClass test +``` + +**Run applications** from IntelliJ: +- **Admin backend**: `com.weather.AdminApplication` (`system-admin/`) — port 8080, context path `/system-admin` +- **API service**: `com.weather.ApiApplication` (`system-api/`) — port 8081 +- **Code generator**: `com.weather.GeneratorApplication` (`renren-generator/`) + +**Database**: `weather_data_system` (MySQL). Init from `system-admin/db/mysql.sql`. Default admin: `admin` / `admin`. + +## Architecture + +### Multi-module Maven project (Java 17, Spring Boot 3.5.x) + +``` +weather-data (pom) +├── system-common → shared lib (all modules depend on this) +├── system-admin → main admin backend +├── system-api → external API service +├── system-dynamic-datasource → multi-DS support (placeholder) +└── renren-generator → code generator +``` + +### Service layer pattern + +All services extend one of two base classes from `system-common`: +- **`CrudService`** — generic CRUD with `page()`, `get()`, `save()`, `update()`, `delete()`. The DTO type param is used for query criteria wrapping. +- **`BaseService`** — lighter base without DTO generic. + +New modules follow this convention: +``` +modules// +├── controller/ → @RestController, returns Result +├── dao/ → extends BaseMapper (MyBatis-Plus) +├── dto/ → request/query DTOs (often extends BaseEntity for auto-fill) +├── entity/ → @TableName JPA entity +├── service/ → interface extends CrudService/BaseService +│ └── impl/ → @Service implementation +├── excel/ → EasyExcel VO classes (optional) +└── vo/ → response VO classes (optional) +``` + +Mapper XMLs: `src/main/resources/mapper//**/*.xml` + +### Key cross-cutting mechanisms + +| Mechanism | How it works | +|---|---| +| **Data permissions** | `@DataFilter` annotation on controller → `DataFilterAspect` → `DataFilterInterceptor` injects dept-based SQL filtering into MyBatis | +| **Auto-fill** | `FieldMetaObjectHandler` fills `creator`/`createDate`/`updater`/`updateDate` via MyBatis-Plus meta-object handler | +| **Scheduled jobs** | Quartz. Jobs in `schedule_job` table, implement `ITask`, annotated `@Component("beanName")`. `JobCommandLineRunner` auto-registers at startup | +| **File scanning** | Three-part weather module: `WatchService` (primary, `FileWatchServiceManager`) + Quartz fallback (`FileScanTask`) + startup scan (`FileScanStartupRunner`). Files served via `FileDownloadController` | +| **Excel import** | EasyExcel with async progress tracking via `WeatherDataImportManager` | +| **API responses** | Always wrapped in `Result` class (`system-common`) | +| **Validation** | Hibernate Validator on DTOs. XSS filter via `XssFilter` | + +### PK strategy + +`ASSIGN_ID` (Snowflake via `IdUtil.getSnowflakeNextId()`), set globally in MyBatis-Plus config. All entities extend `BaseEntity` which declares the `id` field. + +### Auth flow + +- Apache Shiro 1.12 (Jakarta classifier) with OAuth2 token auth +- Login → get token → pass `token` header on subsequent requests +- API module (`system-api`) uses `@Login` annotation + `AuthorizationInterceptor` + +### Redis + +Optional, controlled by `project-options.redis.open` (default `false` in dev). Cache aspect: `RedisAspect`. + +### API docs + +Knife4j (Swagger UI) at `/doc.html`. **Disabled by default** (`knife4j.enable: false`). Enable only in dev profile. + +## Custom Weather Domain + +Three sub-modules under `system-admin/.../modules/weather/`: + +| Sub-module | Purpose | Key detail | +|---|---|---| +| `dailydata/` | Daily weather observations | Excel batch import (async), EasyExcel listener pattern | +| `station/` | Weather station CRUD | Linked to dept via `dept_id`, data-permission aware | +| `filescan/` | File monitoring & serving | WatchService → record → serve via `FileDownloadController` | + +File format convention (from `需求文档.md`): `<地区>地区-<指标>.png` for charts, `<地区>地区631信息.txt` for text data. `FileNameParser` extracts region/indicator keywords. + +## Database + +Custom tables: `weather_daily_data`, `weather_station`, `weather_file_scan_record`. +Seed scripts in `system-admin/db/` (mysql.sql + Oracle/SQLServer/PostgreSQL/Dameng variants). +Parameter `scan_root_path` in `sys_params` controls the file-scan base directory. diff --git a/LICENSE b/LICENSE index dd82623..15625aa 100644 --- a/LICENSE +++ b/LICENSE @@ -1,9 +1,191 @@ -MIT License +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ -Copyright (c) 2026 sansenhoshi +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +1. Definitions. -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "{}" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + + Copyright 2018 人人开源 + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/README.md b/README.md index 2e537d2..c4572a4 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,126 @@ -# weather-data +### 项目说明 +- renren-security是一个轻量级的,前后端分离的Java快速开发平台,能快速开发项目并交付【接私活利器】 +- 采用SpringBoot3.x、Shiro、MyBatis-Plus、Vue3、TypeScript、Element Plus、Vue Router、Pinia、Axios、Vite框架,开发的一套权限系统,极低门槛,拿来即用。设计之初,就非常注重安全性,为企业系统保驾护航,让一切都变得如此简单。 +- 提供了代码生成器,只需编写30%左右代码,其余的代码交给系统自动生成,可快速完成开发任务 +- 支持MySQL、达梦、Oracle、SQL Server、PostgreSQL等主流数据库 +- 演示地址:http://demo.open.renren.io/renren-security (账号密码:admin/admin) + +
+ +### 微信交流群 +我们提供了微信交流群,扫码下面的二维码,关注【人人开源】公众号,回复【加群】,即可根据提示加入微信群! +

+![输入图片说明](renren-admin/src/main/resources/public/wechat.jpg) + +
+ +### 具有如下特点 +- 友好的代码结构及注释,便于阅读及二次开发 +- 实现前后端分离,通过token进行数据交互,前端再也不用关注后端技术 +- 灵活的权限控制,可控制到页面或按钮,满足绝大部分的权限需求 +- 提供CrudService接口,对增删改查进行封装,代码更简洁 +- 页面交互使用Vue3.x,极大的提高了开发效率 +- 完善的部门管理及数据权限,通过注解实现数据权限的控制 +- 完善的XSS防范及脚本过滤,彻底杜绝XSS攻击 +- 完善的代码生成机制,可在线生成entity、xml、dao、service、vue、sql代码,减少70%以上的开发任务 +- 引入quartz定时任务,可动态完成任务的添加、修改、删除、暂停、恢复及日志查看等功能 +- 引入Hibernate Validator校验框架,轻松实现后端校验 +- 引入云存储服务,已支持:七牛云、阿里云、腾讯云等 +- 引入swagger文档支持,方便编写API接口文档 + +
+ +### 数据权限设计思想 +- 用户管理、角色管理、部门管理,可操作本部门及子部门数据 +- 菜单管理、定时任务、参数管理、字典管理、系统日志,没有数据权限 +- 业务功能,按照用户数据权限,查询、操作数据【没有本部门数据权限,也能查询本人数据】 + +
+ + +**项目结构** +``` +renren-security +├─renren-common 公共模块 +│ +├─renren-admin 管理后台 +│ ├─db 数据库SQL脚本 +│ │ +│ ├─modules 模块 +│ │ ├─job 定时任务 +│ │ ├─log 日志管理 +│ │ ├─oss 文件存储 +│ │ ├─security 安全模块 +│ │ └─sys 系统管理(核心) +│ │ +│ └─resources +│ ├─mapper MyBatis文件 +│ ├─public 静态资源 +│ └─application.yml 全局配置文件 +│ +│ +├─renren-api API服务 +│ +├─renren-generator 代码生成器 +│ └─resources +│ ├─mapper MyBatis文件 +│ ├─template 代码生成器模板(可增加或修改相应模板) +│ ├─application.yml 全局配置文件 +│ └─generator.properties 代码生成器,配置文件 +│ +├─renren-ui Vue3前端工程 +``` + +
+ +**技术选型:** +- 核心框架:Spring Boot 3.x +- 安全框架:Apache Shiro 1.12 +- 持久层框架:MyBatis 3.5 +- 定时器:Quartz 2.3 +- 数据库连接池:Druid 1.2 +- 日志管理:Logback +- 页面交互:Vue3.x + +
+ +**软件需求** +- JDK17+ +- Maven3.6+ +- MySQL8.0+ +- Oracle 11g+ +- SQLServer 2012+ +- PostgreSQL 9.4+ +- 达梦8 +
+ + +**本地部署** +- 通过git下载源码 +- 创建数据库renren_security,数据库编码为UTF-8 +- 执行db/mysql.sql文件,初始化数据 +- 修改application-dev.yml文件,更新MySQL账号和密码 +- 在renren-security目录下,执行mvn clean install +- IDEA运行AdminApplication.java,则可启动项目【renren-admin】 +- renren-admin访问路径:http://localhost:8080/renren-admin +- swagger文档路径:http://localhost:8080/renren-admin/doc.html +- 再启动前端工程 +- 账号密码:admin/admin +
+ +![输入图片说明](renren-admin/src/main/resources/public/1.png) + +
+ +![输入图片说明](renren-admin/src/main/resources/public/2.png) + +
+ +### 如何交流、反馈、参与贡献? +- 开发文档:https://www.renren.io/guide/security +- Gitee仓库:https://gitee.com/renrenio/renren-security +- GitCode仓库:https://gitcode.com/renrenio/renren-security +- [人人开源](https://www.renren.io):https://www.renren.io +- 如需关注项目最新动态,请Watch、Star项目,同时也是对项目最好的支持 +
-气象管理系统 \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..f01e567 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,14 @@ +version: '2' +services: + renren-admin: + image: renren/renren-admin + ports: + - "8080:8080" + environment: + - spring.profiles.active=dev + renren-api: + image: renren/renren-api + ports: + - "8081:8081" + environment: + - spring.profiles.active=dev \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..5dc50b2 --- /dev/null +++ b/pom.xml @@ -0,0 +1,162 @@ + + 4.0.0 + com.weather + weather-data + 5.5.0 + pom + + weather-data + 气象数据管理系统 + + + org.springframework.boot + spring-boot-starter-parent + 3.5.11 + + + + system-common + system-dynamic-datasource + system-admin + system-api + + + + + UTF-8 + UTF-8 + 17 + 1.2.23 + 3.5.8 + 4.0 + 11.2.0.3 + 8.1.2.141 + 5.8.29 + 1.15.3 + 4.5.0 + 2.8.4 + + + + + org.projectlombok + lombok + true + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-aop + + + org.springframework.boot + spring-boot-starter-validation + + + org.springframework + spring-context-support + + + org.springframework.boot + spring-boot-starter-data-redis + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + com.mysql + mysql-connector-j + + + + com.oracle + ojdbc6 + ${oracle.version} + + + + com.microsoft.sqlserver + sqljdbc4 + ${sqlserver.version} + + + + org.postgresql + postgresql + + + + com.dameng + DmJdbcDriver18 + ${dameng.version} + + + com.alibaba + druid-spring-boot-3-starter + ${druid.version} + + + com.baomidou + mybatis-plus-spring-boot3-starter + ${mybatisplus.version} + + + cn.hutool + hutool-all + ${hutool.version} + + + org.jsoup + jsoup + ${jsoup.version} + + + com.github.xiaoymin + knife4j-openapi3-jakarta-spring-boot-starter + ${knife4j.version} + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + ${springdoc.version} + + + + + + + + public + aliyun nexus + https://maven.aliyun.com/repository/public/ + + true + + + + + + public + aliyun nexus + https://maven.aliyun.com/repository/public/ + + true + + + false + + + + diff --git a/system-admin/Dockerfile b/system-admin/Dockerfile new file mode 100644 index 0000000..9db81f6 --- /dev/null +++ b/system-admin/Dockerfile @@ -0,0 +1,7 @@ +FROM java:8 +EXPOSE 8080 + +VOLUME /tmp +ADD renren-admin.jar /app.jar +RUN bash -c 'touch /app.jar' +ENTRYPOINT ["java","-jar","/app.jar"] diff --git a/system-admin/db/dm8.sql b/system-admin/db/dm8.sql new file mode 100644 index 0000000..f409819 --- /dev/null +++ b/system-admin/db/dm8.sql @@ -0,0 +1,674 @@ +CREATE TABLE sys_user ( + id bigint NOT NULL, + username varchar(50) NOT NULL, + password varchar(100), + real_name varchar(50), + head_url varchar(200), + gender int, + email varchar(100), + mobile varchar(100), + dept_id bigint, + super_admin int, + status int, + creator bigint, + create_date datetime, + updater bigint, + update_date datetime, + primary key (id) +); + +CREATE UNIQUE INDEX uk_sys_user_username on sys_user(username); +CREATE INDEX idx_sys_user_create_date on sys_user(create_date); + +COMMENT ON TABLE sys_user IS '用户管理'; +COMMENT ON COLUMN sys_user.id IS 'id'; +COMMENT ON COLUMN sys_user.username IS '用户名'; +COMMENT ON COLUMN sys_user.password IS '密码'; +COMMENT ON COLUMN sys_user.real_name IS '姓名'; +COMMENT ON COLUMN sys_user.head_url IS '头像'; +COMMENT ON COLUMN sys_user.gender IS '性别 0:男 1:女 2:保密'; +COMMENT ON COLUMN sys_user.email IS '邮箱'; +COMMENT ON COLUMN sys_user.mobile IS '手机号'; +COMMENT ON COLUMN sys_user.dept_id IS '部门ID'; +COMMENT ON COLUMN sys_user.super_admin IS '超级管理员 0:否 1:是'; +COMMENT ON COLUMN sys_user.status IS '状态 0:停用 1:正常'; +COMMENT ON COLUMN sys_user.creator IS '创建者'; +COMMENT ON COLUMN sys_user.create_date IS '创建时间'; +COMMENT ON COLUMN sys_user.updater IS '更新者'; +COMMENT ON COLUMN sys_user.update_date IS '更新时间'; + + +CREATE TABLE sys_dept ( + id bigint NOT NULL, + pid bigint, + pids varchar(500), + name varchar(50), + sort int, + creator bigint, + create_date datetime, + updater bigint, + update_date datetime, + primary key (id) +); +CREATE INDEX idx_sys_dept_pid on sys_dept(pid); +CREATE INDEX idx_sys_dept_idx_sort on sys_dept(sort); + +COMMENT ON TABLE sys_dept IS '部门管理'; +COMMENT ON COLUMN sys_dept.id IS 'id'; +COMMENT ON COLUMN sys_dept.pid IS '上级ID'; +COMMENT ON COLUMN sys_dept.pids IS '所有上级ID,用逗号分开'; +COMMENT ON COLUMN sys_dept.name IS '部门名称'; +COMMENT ON COLUMN sys_dept.sort IS '排序'; +COMMENT ON COLUMN sys_dept.creator IS '创建者'; +COMMENT ON COLUMN sys_dept.create_date IS '创建时间'; +COMMENT ON COLUMN sys_dept.updater IS '更新者'; +COMMENT ON COLUMN sys_dept.update_date IS '更新时间'; + + +create table sys_role +( + id bigint NOT NULL, + name varchar(50), + remark varchar(100), + dept_id bigint, + creator bigint, + create_date datetime, + updater bigint, + update_date datetime, + primary key (id) +); + +CREATE INDEX idx_sys_role_dept_id on sys_role(dept_id); + +COMMENT ON TABLE sys_role IS '角色管理'; +COMMENT ON COLUMN sys_role.id IS 'id'; +COMMENT ON COLUMN sys_role.name IS '角色名称'; +COMMENT ON COLUMN sys_role.remark IS '备注'; +COMMENT ON COLUMN sys_role.dept_id IS '部门ID'; +COMMENT ON COLUMN sys_role.creator IS '创建者'; +COMMENT ON COLUMN sys_role.create_date IS '创建时间'; +COMMENT ON COLUMN sys_role.updater IS '更新者'; +COMMENT ON COLUMN sys_role.update_date IS '更新时间'; + + +create table sys_menu +( + id bigint NOT NULL, + pid bigint, + name varchar(200), + url varchar(200), + permissions varchar(500), + menu_type int, + icon varchar(50), + sort int, + creator bigint, + create_date datetime, + updater bigint, + update_date datetime, + primary key (id) +); + +CREATE INDEX idx_sys_menu_pid on sys_menu(pid); +CREATE INDEX idx_sys_menu_sort on sys_menu(sort); + +COMMENT ON TABLE sys_menu IS '菜单管理'; +COMMENT ON COLUMN sys_menu.id IS 'id'; +COMMENT ON COLUMN sys_menu.pid IS '上级ID,一级菜单为0'; +COMMENT ON COLUMN sys_menu.name IS '名称'; +COMMENT ON COLUMN sys_menu.url IS '菜单URL'; +COMMENT ON COLUMN sys_menu.permissions IS '授权(多个用逗号分隔,如:sys:user:list,sys:user:save)'; +COMMENT ON COLUMN sys_menu.menu_type IS '类型 0:菜单 1:按钮'; +COMMENT ON COLUMN sys_menu.icon IS '菜单图标'; +COMMENT ON COLUMN sys_menu.sort IS '排序'; +COMMENT ON COLUMN sys_menu.creator IS '创建者'; +COMMENT ON COLUMN sys_menu.create_date IS '创建时间'; +COMMENT ON COLUMN sys_menu.updater IS '更新者'; +COMMENT ON COLUMN sys_menu.update_date IS '更新时间'; + + +create table sys_role_user +( + id bigint NOT NULL, + role_id bigint, + user_id bigint, + creator bigint, + create_date datetime, + primary key (id) +); + +CREATE INDEX idx_sys_role_user_role_id on sys_role_user(role_id); +CREATE INDEX idx_sys_role_user_user_id on sys_role_user(user_id); + +COMMENT ON TABLE sys_role_user IS '角色用户关系'; +COMMENT ON COLUMN sys_role_user.role_id IS '角色ID'; +COMMENT ON COLUMN sys_role_user.user_id IS '用户ID'; +COMMENT ON COLUMN sys_role_user.creator IS '创建者'; +COMMENT ON COLUMN sys_role_user.create_date IS '创建时间'; + + +create table sys_role_menu +( + id bigint NOT NULL, + role_id bigint, + menu_id bigint, + creator bigint, + create_date datetime, + primary key (id) +); + +CREATE INDEX idx_sys_role_menu_role_id on sys_role_menu(role_id); +CREATE INDEX idx_sys_role_menu_menu_id on sys_role_menu(menu_id); + +COMMENT ON TABLE sys_role_menu IS '角色菜单关系'; +COMMENT ON COLUMN sys_role_menu.role_id IS '角色ID'; +COMMENT ON COLUMN sys_role_menu.menu_id IS '菜单ID'; +COMMENT ON COLUMN sys_role_menu.creator IS '创建者'; +COMMENT ON COLUMN sys_role_menu.create_date IS '创建时间'; + + +create table sys_role_data_scope +( + id bigint NOT NULL, + role_id bigint, + dept_id bigint, + creator bigint, + create_date datetime, + primary key (id) +); +CREATE INDEX idx_data_scope_role_id on sys_role_data_scope(role_id); + +COMMENT ON TABLE sys_role_data_scope IS '角色数据权限'; +COMMENT ON COLUMN sys_role_data_scope.role_id IS '角色ID'; +COMMENT ON COLUMN sys_role_data_scope.dept_id IS '部门ID'; +COMMENT ON COLUMN sys_role_data_scope.creator IS '创建者'; +COMMENT ON COLUMN sys_role_data_scope.create_date IS '创建时间'; + + +create table sys_params +( + id bigint NOT NULL, + param_code varchar(32), + param_value varchar(2000), + param_type int DEFAULT 1 NOT NULL, + remark varchar(200), + creator bigint, + create_date datetime, + updater bigint, + update_date datetime, + primary key (id) +); +CREATE UNIQUE INDEX uk_sys_params_param_code on sys_params(param_code); +CREATE INDEX idx_sys_params_create_date on sys_params(create_date); + +COMMENT ON TABLE sys_params IS '参数管理'; +COMMENT ON COLUMN sys_params.param_code IS '参数编码'; +COMMENT ON COLUMN sys_params.param_value IS '参数值'; +COMMENT ON COLUMN sys_params.param_type IS '类型 0:系统参数 1:非系统参数'; +COMMENT ON COLUMN sys_params.remark IS '备注'; +COMMENT ON COLUMN sys_params.creator IS '创建者'; +COMMENT ON COLUMN sys_params.create_date IS '创建时间'; +COMMENT ON COLUMN sys_params.updater IS '更新者'; +COMMENT ON COLUMN sys_params.update_date IS '更新时间'; + + +create table sys_dict_type +( + id bigint NOT NULL, + dict_type varchar(100), + dict_name varchar(255), + remark varchar(255), + sort int, + creator bigint, + create_date datetime, + updater bigint, + update_date datetime, + primary key (id) +); +CREATE UNIQUE INDEX uk_sys_dict_type_dict_type on sys_dict_type(dict_type); + +COMMENT ON TABLE sys_dict_type IS '字典类型'; +COMMENT ON COLUMN sys_dict_type.id IS 'id'; +COMMENT ON COLUMN sys_dict_type.dict_type IS '字典类型'; +COMMENT ON COLUMN sys_dict_type.dict_name IS '字典名称'; +COMMENT ON COLUMN sys_dict_type.remark IS '备注'; +COMMENT ON COLUMN sys_dict_type.sort IS '排序'; +COMMENT ON COLUMN sys_dict_type.creator IS '创建者'; +COMMENT ON COLUMN sys_dict_type.create_date IS '创建时间'; +COMMENT ON COLUMN sys_dict_type.updater IS '更新者'; +COMMENT ON COLUMN sys_dict_type.update_date IS '更新时间'; + +create table sys_dict_data +( + id bigint NOT NULL, + dict_type_id bigint NOT NULL, + dict_label varchar(255), + dict_value varchar(255), + remark varchar(255), + sort int, + creator bigint, + create_date datetime, + updater bigint, + update_date datetime, + primary key (id) +); +CREATE INDEX idx_sys_dict_data_sort on sys_dict_data(sort); +CREATE UNIQUE INDEX uk_dict_type_value on sys_dict_data(dict_type_id, dict_value); + +COMMENT ON TABLE sys_dict_data IS '字典数据'; +COMMENT ON COLUMN sys_dict_data.id IS 'id'; +COMMENT ON COLUMN sys_dict_data.dict_type_id IS '字典类型ID'; +COMMENT ON COLUMN sys_dict_data.dict_label IS '字典标签'; +COMMENT ON COLUMN sys_dict_data.dict_value IS '字典值'; +COMMENT ON COLUMN sys_dict_data.remark IS '备注'; +COMMENT ON COLUMN sys_dict_data.sort IS '排序'; +COMMENT ON COLUMN sys_dict_data.creator IS '创建者'; +COMMENT ON COLUMN sys_dict_data.create_date IS '创建时间'; +COMMENT ON COLUMN sys_dict_data.updater IS '更新者'; +COMMENT ON COLUMN sys_dict_data.update_date IS '更新时间'; + + +create table sys_log_login +( + id bigint NOT NULL, + operation int, + status int, + user_agent varchar(500), + ip varchar(32), + creator_name varchar(50), + creator bigint, + create_date datetime, + primary key (id) +); +CREATE INDEX idx_login_status on sys_log_login(status); +CREATE INDEX idx_login_create_date on sys_log_login(create_date); + +COMMENT ON TABLE sys_log_login IS '登录日志'; +COMMENT ON COLUMN sys_log_login.id IS 'id'; +COMMENT ON COLUMN sys_log_login.operation IS '用户操作 0:用户登录 1:用户退出'; +COMMENT ON COLUMN sys_log_login.status IS '状态 0:失败 1:成功 2:账号已锁定'; +COMMENT ON COLUMN sys_log_login.user_agent IS '用户代理'; +COMMENT ON COLUMN sys_log_login.ip IS '操作IP'; +COMMENT ON COLUMN sys_log_login.creator_name IS '用户名'; +COMMENT ON COLUMN sys_log_login.creator IS '创建者'; +COMMENT ON COLUMN sys_log_login.create_date IS '创建时间'; + + +create table sys_log_operation +( + id bigint NOT NULL, + operation varchar(50), + request_uri varchar(200), + request_method varchar(20), + request_params text, + request_time int, + user_agent varchar(500), + ip varchar(32), + status int, + creator_name varchar(50), + creator bigint, + create_date datetime, + primary key (id) +); +CREATE INDEX idx_operation_create_date on sys_log_operation(create_date); + +COMMENT ON TABLE sys_log_operation IS '操作日志'; +COMMENT ON COLUMN sys_log_operation.id IS 'id'; +COMMENT ON COLUMN sys_log_operation.operation IS '用户操作'; +COMMENT ON COLUMN sys_log_operation.request_uri IS '请求URI'; +COMMENT ON COLUMN sys_log_operation.request_method IS '请求方式'; +COMMENT ON COLUMN sys_log_operation.request_params IS '请求参数'; +COMMENT ON COLUMN sys_log_operation.request_time IS '请求时长(毫秒)'; +COMMENT ON COLUMN sys_log_operation.user_agent IS '用户代理'; +COMMENT ON COLUMN sys_log_operation.ip IS '操作IP'; +COMMENT ON COLUMN sys_log_operation.status IS '状态 0:失败 1:成功'; +COMMENT ON COLUMN sys_log_operation.creator_name IS '用户名'; +COMMENT ON COLUMN sys_log_operation.creator IS '创建者'; +COMMENT ON COLUMN sys_log_operation.create_date IS '创建时间'; + + +create table sys_log_error +( + id bigint NOT NULL, + request_uri varchar(200), + request_method varchar(20), + request_params text, + user_agent varchar(500), + ip varchar(32), + error_info text, + creator bigint, + create_date datetime, + primary key (id) +); +CREATE INDEX idx_error_create_date on sys_log_error(create_date); + +COMMENT ON TABLE sys_log_error IS '异常日志'; +COMMENT ON COLUMN sys_log_error.id IS 'id'; +COMMENT ON COLUMN sys_log_error.request_uri IS '请求URI'; +COMMENT ON COLUMN sys_log_error.request_method IS '请求方式'; +COMMENT ON COLUMN sys_log_error.request_params IS '请求参数'; +COMMENT ON COLUMN sys_log_error.user_agent IS '用户代理'; +COMMENT ON COLUMN sys_log_error.ip IS '操作IP'; +COMMENT ON COLUMN sys_log_error.error_info IS '异常信息'; +COMMENT ON COLUMN sys_log_error.creator IS '创建者'; +COMMENT ON COLUMN sys_log_error.create_date IS '创建时间'; + + + +CREATE TABLE sys_oss ( + id bigint NOT NULL, + url varchar(200), + creator bigint, + create_date datetime, + PRIMARY KEY (id) +); +CREATE INDEX idx_sys_oss_create_date on sys_oss(create_date); + +COMMENT ON TABLE sys_oss IS '文件上传'; +COMMENT ON COLUMN sys_oss.id IS 'id'; +COMMENT ON COLUMN sys_oss.url IS 'URL地址'; +COMMENT ON COLUMN sys_oss.creator IS '创建者'; +COMMENT ON COLUMN sys_oss.create_date IS '创建时间'; + + +CREATE TABLE schedule_job ( + id bigint NOT NULL, + bean_name varchar(200), + params varchar(2000), + cron_expression varchar(100), + status int, + remark varchar(255), + creator bigint, + create_date datetime, + updater bigint, + update_date datetime, + PRIMARY KEY (id) +); + +CREATE INDEX idx_schedule_job_create_date on schedule_job(create_date); + +COMMENT ON TABLE schedule_job IS '定时任务'; +COMMENT ON COLUMN schedule_job.id IS 'id'; +COMMENT ON COLUMN schedule_job.bean_name IS 'spring bean名称'; +COMMENT ON COLUMN schedule_job.params IS '参数'; +COMMENT ON COLUMN schedule_job.cron_expression IS 'cron表达式'; +COMMENT ON COLUMN schedule_job.status IS '任务状态 0:暂停 1:正常'; +COMMENT ON COLUMN schedule_job.remark IS '备注'; +COMMENT ON COLUMN schedule_job.creator IS '创建者'; +COMMENT ON COLUMN schedule_job.create_date IS '创建时间'; +COMMENT ON COLUMN schedule_job.updater IS '更新者'; +COMMENT ON COLUMN schedule_job.update_date IS '更新时间'; + + +CREATE TABLE schedule_job_log ( + id bigint NOT NULL, + job_id bigint NOT NULL, + bean_name varchar(200), + params varchar(2000), + status int, + error varchar(2000), + times int, + create_date datetime, + PRIMARY KEY (id) +); + +CREATE INDEX idx_job_log_job_id on schedule_job_log(job_id); +CREATE INDEX idx_job_log_create_date on schedule_job_log(create_date); + +COMMENT ON TABLE schedule_job_log IS '定时任务日志'; +COMMENT ON COLUMN schedule_job_log.id IS 'id'; +COMMENT ON COLUMN schedule_job_log.bean_name IS 'spring bean名称'; +COMMENT ON COLUMN schedule_job_log.params IS '参数'; +COMMENT ON COLUMN schedule_job_log.status IS '任务状态 0:失败 1:成功'; +COMMENT ON COLUMN schedule_job_log.error IS '失败信息'; +COMMENT ON COLUMN schedule_job_log.times IS '耗时(单位:毫秒)'; +COMMENT ON COLUMN schedule_job.create_date IS '创建时间'; + + +CREATE TABLE sys_user_token ( + id bigint NOT NULL, + user_id bigint, + token varchar(100), + expire_date datetime, + update_date datetime, + create_date datetime, + PRIMARY KEY (id) +); + +CREATE UNIQUE INDEX uk_sys_user_token_user_id on sys_user_token(user_id); +CREATE UNIQUE INDEX uk_sys_user_token on sys_user_token(token); + +COMMENT ON TABLE sys_user_token IS '系统用户Token'; +COMMENT ON COLUMN sys_user_token.id IS 'id'; +COMMENT ON COLUMN sys_user_token.user_id IS '用户id'; +COMMENT ON COLUMN sys_user_token.token IS '用户token'; +COMMENT ON COLUMN sys_user_token.expire_date IS '过期时间'; +COMMENT ON COLUMN sys_user_token.update_date IS '更新时间'; +COMMENT ON COLUMN sys_user_token.create_date IS '创建时间'; + + + +-- 初始数据 +INSERT INTO sys_user(id, username, password, real_name, gender, email, mobile, status, dept_id, super_admin, creator, create_date, updater, update_date) VALUES (1067246875800000001, 'admin', '$2a$10$012Kx2ba5jzqr9gLlG4MX.bnQJTD9UWqF57XDo2N3.fPtLne02u/m', '管理员', 0, 'root@weather.io', '13612345678', 1, null, 1, 1067246875800000001, now(), 1067246875800000001, now()); + +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000002, 0, '权限管理', NULL, NULL, 0, 'icon-safetycertificate', 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000003, 1067246875800000055, '新增', NULL, 'sys:user:save,sys:dept:list,sys:role:list', 1, NULL, 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000004, 1067246875800000055, '修改', NULL, 'sys:user:update,sys:dept:list,sys:role:list', 1, NULL, 2, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000005, 1067246875800000055, '删除', NULL, 'sys:user:delete', 1, NULL, 3, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000006, 1067246875800000055, '导出', NULL, 'sys:user:export', 1, NULL, 4, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000007, 1067246875800000002, '角色管理', 'sys/role', NULL, 0, 'icon-team', 2, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000008, 1067246875800000007, '查看', NULL, 'sys:role:page,sys:role:info', 1, NULL, 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000009, 1067246875800000007, '新增', NULL, 'sys:role:save,sys:menu:select,sys:dept:list', 1, NULL, 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000010, 1067246875800000007, '修改', NULL, 'sys:role:update,sys:menu:select,sys:dept:list', 1, NULL, 2, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000011, 1067246875800000007, '删除', NULL, 'sys:role:delete', 1, NULL, 3, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000012, 1067246875800000002, '部门管理', 'sys/dept', NULL, 0, 'icon-apartment', 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000014, 1067246875800000012, '查看', NULL, 'sys:dept:list,sys:dept:info', 1, NULL, 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000015, 1067246875800000012, '新增', NULL, 'sys:dept:save', 1, NULL, 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000016, 1067246875800000012, '修改', NULL, 'sys:dept:update', 1, NULL, 2, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000017, 1067246875800000012, '删除', NULL, 'sys:dept:delete', 1, NULL, 3, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000025, 1067246875800000035, '菜单管理', 'sys/menu', NULL, 0, 'icon-unorderedlist', 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000026, 1067246875800000025, '查看', NULL, 'sys:menu:list,sys:menu:info', 1, NULL, 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000027, 1067246875800000025, '新增', NULL, 'sys:menu:save', 1, NULL, 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000028, 1067246875800000025, '修改', NULL, 'sys:menu:update', 1, NULL, 2, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000029, 1067246875800000025, '删除', NULL, 'sys:menu:delete', 1, NULL, 3, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000030, 1067246875800000035, '定时任务', 'job/schedule', NULL, 0, 'icon-dashboard', 3, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000031, 1067246875800000030, '查看', NULL, 'sys:schedule:page,sys:schedule:info', 1, NULL, 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000032, 1067246875800000030, '新增', NULL, 'sys:schedule:save', 1, NULL, 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000033, 1067246875800000030, '修改', NULL, 'sys:schedule:update', 1, NULL, 2, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000034, 1067246875800000030, '删除', NULL, 'sys:schedule:delete', 1, NULL, 3, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000035, 0, '系统设置', NULL, NULL, 0, 'icon-setting', 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000036, 1067246875800000030, '暂停', NULL, 'sys:schedule:pause', 1, NULL, 4, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000037, 1067246875800000030, '恢复', NULL, 'sys:schedule:resume', 1, NULL, 5, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000038, 1067246875800000030, '立即执行', NULL, 'sys:schedule:run', 1, NULL, 6, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000039, 1067246875800000030, '日志列表', NULL, 'sys:schedule:log', 1, NULL, 7, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000040, 1067246875800000035, '参数管理', 'sys/params', '', 0, 'icon-fileprotect', 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000041, 1067246875800000035, '字典管理', 'sys/dict-type', NULL, 0, 'icon-golden-fill', 2, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000042, 1067246875800000041, '查看', NULL, 'sys:dict:page,sys:dict:info', 1, NULL, 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000043, 1067246875800000041, '新增', NULL, 'sys:dict:save', 1, NULL, 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000044, 1067246875800000041, '修改', NULL, 'sys:dict:update', 1, NULL, 2, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000045, 1067246875800000041, '删除', NULL, 'sys:dict:delete', 1, NULL, 3, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000046, 0, '日志管理', NULL, NULL, 0, 'icon-container', 2, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000047, 1067246875800000035, '文件上传', 'oss/oss', 'sys:oss:all', 0, 'icon-upload', 4, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000048, 1067246875800000046, '登录日志', 'sys/log-login', 'sys:log:login', 0, 'icon-filedone', 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000049, 1067246875800000046, '操作日志', 'sys/log-operation', 'sys:log:operation', 0, 'icon-solution', 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000050, 1067246875800000046, '异常日志', 'sys/log-error', 'sys:log:error', 0, 'icon-file-exception', 2, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000051, 1067246875800000053, 'SQL监控', '{{ApiUrl}}/druid/sql.html', NULL, 0, 'icon-database', 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000053, 0, '系统监控', NULL, NULL, 0, 'icon-desktop', 3, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000055, 1067246875800000002, '用户管理', 'sys/user', NULL, 0, 'icon-user', 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000056, 1067246875800000055, '查看', NULL, 'sys:user:page,sys:user:info', 1, NULL, 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000057, 1067246875800000040, '新增', NULL, 'sys:params:save', 1, NULL, 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000058, 1067246875800000040, '导出', NULL, 'sys:params:export', 1, NULL, 4, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000059, 1067246875800000040, '查看', '', 'sys:params:page,sys:params:info', 1, NULL, 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000060, 1067246875800000040, '修改', NULL, 'sys:params:update', 1, NULL, 2, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000061, 1067246875800000040, '删除', '', 'sys:params:delete', 1, '', 3, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1156748733921165314, 1067246875800000053, '接口文档', '{{ApiUrl}}/doc.html', '', 0, 'icon-file-word', 1, 1067246875800000001, now(), 1067246875800000001, now()); + + +INSERT INTO sys_dept(id, pid, pids, name, sort, creator, create_date, updater, update_date) VALUES (1067246875800000062, 1067246875800000063, '1067246875800000066,1067246875800000063', '技术部', 2, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_dept(id, pid, pids, name, sort, creator, create_date, updater, update_date) VALUES (1067246875800000063, 1067246875800000066, '1067246875800000066', '长沙分公司', 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_dept(id, pid, pids, name, sort, creator, create_date, updater, update_date) VALUES (1067246875800000064, 1067246875800000066, '1067246875800000066', '上海分公司', 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_dept(id, pid, pids, name, sort, creator, create_date, updater, update_date) VALUES (1067246875800000065, 1067246875800000064, '1067246875800000066,1067246875800000064', '市场部', 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_dept(id, pid, pids, name, sort, creator, create_date, updater, update_date) VALUES (1067246875800000066, 0, '0', '人人开源集团', 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_dept(id, pid, pids, name, sort, creator, create_date, updater, update_date) VALUES (1067246875800000067, 1067246875800000064, '1067246875800000066,1067246875800000064', '销售部', 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_dept(id, pid, pids, name, sort, creator, create_date, updater, update_date) VALUES (1067246875800000068, 1067246875800000063, '1067246875800000066,1067246875800000063', '产品部', 1, 1067246875800000001, now(), 1067246875800000001, now()); + +INSERT INTO sys_dict_type(id, dict_type, dict_name, remark, sort, creator, create_date, updater, update_date) VALUES (1160061077912858625, 'gender', '性别', '', 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_dict_data(id, dict_type_id, dict_label, dict_value, remark, sort, creator, create_date, updater, update_date) VALUES (1160061112075464705, 1160061077912858625, '男', '0', '', 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_dict_data(id, dict_type_id, dict_label, dict_value, remark, sort, creator, create_date, updater, update_date) VALUES (1160061146967879681, 1160061077912858625, '女', '1', '', 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_dict_data(id, dict_type_id, dict_label, dict_value, remark, sort, creator, create_date, updater, update_date) VALUES (1160061190127267841, 1160061077912858625, '保密', '2', '', 2, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_dict_type(id, dict_type, dict_name, remark, sort, creator, create_date, updater, update_date) VALUES (1225813644059140097, 'notice_type', '站内通知-类型', '', 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_dict_data(id, dict_type_id, dict_label, dict_value, remark, sort, creator, create_date, updater, update_date) VALUES (1225814069634195457, 1225813644059140097, '公告', '0', '', 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_dict_data(id, dict_type_id, dict_label, dict_value, remark, sort, creator, create_date, updater, update_date) VALUES (1225814107559092225, 1225813644059140097, '会议', '1', '', 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_dict_data(id, dict_type_id, dict_label, dict_value, remark, sort, creator, create_date, updater, update_date) VALUES (1225814271879340034, 1225813644059140097, '其他', '2', '', 2, 1067246875800000001, now(), 1067246875800000001, now()); + +INSERT INTO sys_params(id, param_code, param_value, param_type, remark, creator, create_date, updater, update_date) VALUES (1067246875800000073, 'CLOUD_STORAGE_CONFIG_KEY', '{"type":1,"qiniuDomain":"http://test.oss.weather.io","qiniuPrefix":"upload","qiniuAccessKey":"NrgMfABZxWLo5B-YYSjoE8-AZ1EISdi1Z3ubLOeZ","qiniuSecretKey":"uIwJHevMRWU0VLxFvgy0tAcOdGqasdtVlJkdy6vV","qiniuBucketName":"weather-oss","aliyunDomain":"","aliyunPrefix":"","aliyunEndPoint":"","aliyunAccessKeyId":"","aliyunAccessKeySecret":"","aliyunBucketName":"","qcloudDomain":"","qcloudPrefix":"","qcloudSecretId":"","qcloudSecretKey":"","qcloudBucketName":""}', '0', '云存储配置信息', 1067246875800000001, now(), 1067246875800000001, now()); + +INSERT INTO schedule_job (id, bean_name, params, cron_expression, status, remark, creator, create_date, updater, update_date) VALUES (1067246875800000076, 'testTask', 'weather', '0 0/30 * * * ?', 0, '有参测试,多个参数使用json', 1067246875800000001, now(), 1067246875800000001, now()); + + + + +-- quartz自带表结构 +DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS; +DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS; +DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE; +DROP TABLE IF EXISTS QRTZ_LOCKS; +DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS; +DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS; +DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS; +DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS; +DROP TABLE IF EXISTS QRTZ_TRIGGERS; +DROP TABLE IF EXISTS QRTZ_JOB_DETAILS; +DROP TABLE IF EXISTS QRTZ_CALENDARS; + +create table QRTZ_JOB_DETAILS ( + sched_name varchar(120) not null, + job_name varchar(200) not null, + job_group varchar(200) not null, + description varchar(250) null, + job_class_name varchar(250) not null, + is_durable varchar(1) not null, + is_nonconcurrent varchar(1) not null, + is_update_data varchar(1) not null, + requests_recovery varchar(1) not null, + job_data blob null, + primary key (sched_name, job_name, job_group) +); + +create table QRTZ_TRIGGERS ( + sched_name varchar(120) not null, + trigger_name varchar(200) not null, + trigger_group varchar(200) not null, + job_name varchar(200) not null, + job_group varchar(200) not null, + description varchar(250) null, + next_fire_time bigint null, + prev_fire_time bigint null, + priority int null, + trigger_state varchar(16) not null, + trigger_type varchar(8) not null, + start_time bigint not null, + end_time bigint null, + calendar_name varchar(200) null, + misfire_instr int null, + job_data blob null, + primary key (sched_name, trigger_name, trigger_group), + foreign key (sched_name, job_name, job_group) + references QRTZ_JOB_DETAILS(sched_name, job_name, job_group) +); + +create table QRTZ_SIMPLE_TRIGGERS ( + sched_name varchar(120) not null, + trigger_name varchar(200) not null, + trigger_group varchar(200) not null, + repeat_count bigint not null, + repeat_interval bigint not null, + times_triggered bigint not null, + primary key (sched_name, trigger_name, trigger_group), + foreign key (sched_name, trigger_name, trigger_group) + references QRTZ_TRIGGERS(sched_name, trigger_name, trigger_group) +); + +create table QRTZ_CRON_TRIGGERS ( + sched_name varchar(120) not null, + trigger_name varchar(200) not null, + trigger_group varchar(200) not null, + cron_expression varchar(200) not null, + time_zone_id varchar(80), + primary key (sched_name, trigger_name, trigger_group), + foreign key (sched_name, trigger_name, trigger_group) + references QRTZ_TRIGGERS(sched_name, trigger_name, trigger_group) +); + +create table QRTZ_BLOB_TRIGGERS ( + sched_name varchar(120) not null, + trigger_name varchar(200) not null, + trigger_group varchar(200) not null, + blob_data blob null, + primary key (sched_name, trigger_name, trigger_group), + foreign key (sched_name, trigger_name, trigger_group) + references QRTZ_TRIGGERS(sched_name, trigger_name, trigger_group) +); + +create table QRTZ_CALENDARS ( + sched_name varchar(120) not null, + calendar_name varchar(200) not null, + calendar blob not null, + primary key (sched_name, calendar_name) +); + +create table QRTZ_PAUSED_TRIGGER_GRPS ( + sched_name varchar(120) not null, + trigger_group varchar(200) not null, + primary key (sched_name, trigger_group) +); + +create table QRTZ_FIRED_TRIGGERS ( + sched_name varchar(120) not null, + entry_id varchar(95) not null, + trigger_name varchar(200) not null, + trigger_group varchar(200) not null, + instance_name varchar(200) not null, + fired_time bigint not null, + sched_time bigint not null, + priority int not null, + state varchar(16) not null, + job_name varchar(200) null, + job_group varchar(200) null, + is_nonconcurrent varchar(1) null, + requests_recovery varchar(1) null, + primary key (sched_name, entry_id) +); + +create table QRTZ_SCHEDULER_STATE ( + sched_name varchar(120) not null, + instance_name varchar(200) not null, + last_checkin_time bigint not null, + checkin_interval bigint not null, + primary key (sched_name, instance_name) +); + +create table QRTZ_LOCKS ( + sched_name varchar(120) not null, + lock_name varchar(40) not null, + primary key (sched_name, lock_name) +); + +create table QRTZ_SIMPROP_TRIGGERS ( + sched_name varchar(120) not null, + trigger_name varchar(200) not null, + trigger_group varchar(200) not null, + str_prop_1 varchar(512) null, + str_prop_2 varchar(512) null, + str_prop_3 varchar(512) null, + int_prop_1 int null, + int_prop_2 int null, + long_prop_1 bigint null, + long_prop_2 bigint null, + dec_prop_1 numeric(13,4) null, + dec_prop_2 numeric(13,4) null, + bool_prop_1 varchar(1) null, + bool_prop_2 varchar(1) null, + primary key (sched_name, trigger_name, trigger_group), + foreign key (sched_name, trigger_name, trigger_group) + references QRTZ_TRIGGERS(sched_name, trigger_name, trigger_group) +); \ No newline at end of file diff --git a/system-admin/db/mysql.sql b/system-admin/db/mysql.sql new file mode 100644 index 0000000..836c54f --- /dev/null +++ b/system-admin/db/mysql.sql @@ -0,0 +1,504 @@ +-- 系统用户 +CREATE TABLE sys_user ( + id bigint NOT NULL COMMENT 'id', + username varchar(50) NOT NULL COMMENT '用户名', + password varchar(100) COMMENT '密码', + real_name varchar(50) COMMENT '姓名', + head_url varchar(200) COMMENT '头像', + gender tinyint unsigned COMMENT '性别 0:男 1:女 2:保密', + email varchar(100) COMMENT '邮箱', + mobile varchar(100) COMMENT '手机号', + dept_id bigint COMMENT '部门ID', + super_admin tinyint unsigned COMMENT '超级管理员 0:否 1:是', + status tinyint COMMENT '状态 0:停用 1:正常', + creator bigint COMMENT '创建者', + create_date datetime COMMENT '创建时间', + updater bigint COMMENT '更新者', + update_date datetime COMMENT '更新时间', + primary key (id), + unique key uk_username (username), + key idx_create_date (create_date) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统用户'; + +-- 部门 +CREATE TABLE sys_dept ( + id bigint NOT NULL COMMENT 'id', + pid bigint COMMENT '上级ID', + pids varchar(500) COMMENT '所有上级ID,用逗号分开', + name varchar(50) COMMENT '部门名称', + sort int unsigned COMMENT '排序', + creator bigint COMMENT '创建者', + create_date datetime COMMENT '创建时间', + updater bigint COMMENT '更新者', + update_date datetime COMMENT '更新时间', + primary key (id), + key idx_pid (pid), + key idx_sort (sort) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='部门管理'; + +-- 角色管理 +create table sys_role +( + id bigint NOT NULL COMMENT 'id', + name varchar(50) COMMENT '角色名称', + remark varchar(100) COMMENT '备注', + dept_id bigint COMMENT '部门ID', + creator bigint COMMENT '创建者', + create_date datetime COMMENT '创建时间', + updater bigint COMMENT '更新者', + update_date datetime COMMENT '更新时间', + primary key (id), + key idx_dept_id (dept_id) +)ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COMMENT='角色管理'; + +-- 菜单管理 +create table sys_menu +( + id bigint NOT NULL COMMENT 'id', + pid bigint COMMENT '上级ID,一级菜单为0', + name varchar(200) COMMENT '名称', + url varchar(200) COMMENT '菜单URL', + permissions varchar(500) COMMENT '授权(多个用逗号分隔,如:sys:user:list,sys:user:save)', + menu_type tinyint unsigned COMMENT '类型 0:菜单 1:按钮', + icon varchar(50) COMMENT '菜单图标', + sort int COMMENT '排序', + creator bigint COMMENT '创建者', + create_date datetime COMMENT '创建时间', + updater bigint COMMENT '更新者', + update_date datetime COMMENT '更新时间', + primary key (id), + key idx_pid (pid), + key idx_sort (sort) +)ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COMMENT='菜单管理'; + +-- 角色用户关系 +create table sys_role_user +( + id bigint NOT NULL COMMENT 'id', + role_id bigint COMMENT '角色ID', + user_id bigint COMMENT '用户ID', + creator bigint COMMENT '创建者', + create_date datetime COMMENT '创建时间', + primary key (id), + key idx_role_id (role_id), + key idx_user_id (user_id) +)ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COMMENT='角色用户关系'; + +-- 角色菜单关系 +create table sys_role_menu +( + id bigint NOT NULL COMMENT 'id', + role_id bigint COMMENT '角色ID', + menu_id bigint COMMENT '菜单ID', + creator bigint COMMENT '创建者', + create_date datetime COMMENT '创建时间', + primary key (id), + key idx_role_id (role_id), + key idx_menu_id (menu_id) +)ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COMMENT='角色菜单关系'; + +-- 角色数据权限 +create table sys_role_data_scope +( + id bigint NOT NULL COMMENT 'id', + role_id bigint COMMENT '角色ID', + dept_id bigint COMMENT '部门ID', + creator bigint COMMENT '创建者', + create_date datetime COMMENT '创建时间', + primary key (id), + key idx_role_id (role_id) +)ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COMMENT='角色数据权限'; + +-- 参数管理 +create table sys_params +( + id bigint NOT NULL COMMENT 'id', + param_code varchar(32) COMMENT '参数编码', + param_value varchar(2000) COMMENT '参数值', + param_type tinyint unsigned default 1 COMMENT '类型 0:系统参数 1:非系统参数', + remark varchar(200) COMMENT '备注', + creator bigint COMMENT '创建者', + create_date datetime COMMENT '创建时间', + updater bigint COMMENT '更新者', + update_date datetime COMMENT '更新时间', + primary key (id), + unique key uk_param_code (param_code), + key idx_create_date (create_date) +)ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COMMENT='参数管理'; + +-- 字典类型 +create table sys_dict_type +( + id bigint NOT NULL COMMENT 'id', + dict_type varchar(100) NOT NULL COMMENT '字典类型', + dict_name varchar(255) NOT NULL COMMENT '字典名称', + remark varchar(255) COMMENT '备注', + sort int unsigned COMMENT '排序', + creator bigint COMMENT '创建者', + create_date datetime COMMENT '创建时间', + updater bigint COMMENT '更新者', + update_date datetime COMMENT '更新时间', + primary key (id), + UNIQUE KEY(dict_type) +)ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COMMENT='字典类型'; + +-- 字典数据 +create table sys_dict_data +( + id bigint NOT NULL COMMENT 'id', + dict_type_id bigint NOT NULL COMMENT '字典类型ID', + dict_label varchar(255) NOT NULL COMMENT '字典标签', + dict_value varchar(255) COMMENT '字典值', + remark varchar(255) COMMENT '备注', + sort int unsigned COMMENT '排序', + creator bigint COMMENT '创建者', + create_date datetime COMMENT '创建时间', + updater bigint COMMENT '更新者', + update_date datetime COMMENT '更新时间', + primary key (id), + unique key uk_dict_type_value (dict_type_id, dict_value), + key idx_sort (sort) +)ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COMMENT='字典数据'; + +-- 登录日志 +create table sys_log_login +( + id bigint NOT NULL COMMENT 'id', + operation tinyint unsigned COMMENT '用户操作 0:用户登录 1:用户退出', + status tinyint unsigned NOT NULL COMMENT '状态 0:失败 1:成功 2:账号已锁定', + user_agent varchar(500) COMMENT '用户代理', + ip varchar(32) COMMENT '操作IP', + creator_name varchar(50) COMMENT '用户名', + creator bigint COMMENT '创建者', + create_date datetime COMMENT '创建时间', + primary key (id), + key idx_status (status), + key idx_create_date (create_date) +)ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COMMENT='登录日志'; + +-- 操作日志 +create table sys_log_operation +( + id bigint NOT NULL COMMENT 'id', + operation varchar(50) COMMENT '用户操作', + request_uri varchar(200) COMMENT '请求URI', + request_method varchar(20) COMMENT '请求方式', + request_params text COMMENT '请求参数', + request_time int unsigned NOT NULL COMMENT '请求时长(毫秒)', + user_agent varchar(500) COMMENT '用户代理', + ip varchar(32) COMMENT '操作IP', + status tinyint unsigned NOT NULL COMMENT '状态 0:失败 1:成功', + creator_name varchar(50) COMMENT '用户名', + creator bigint COMMENT '创建者', + create_date datetime COMMENT '创建时间', + primary key (id), + key idx_create_date (create_date) +)ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COMMENT='操作日志'; + +-- 异常日志 +create table sys_log_error +( + id bigint NOT NULL COMMENT 'id', + request_uri varchar(200) COMMENT '请求URI', + request_method varchar(20) COMMENT '请求方式', + request_params text COMMENT '请求参数', + user_agent varchar(500) COMMENT '用户代理', + ip varchar(32) COMMENT '操作IP', + error_info text COMMENT '异常信息', + creator bigint COMMENT '创建者', + create_date datetime COMMENT '创建时间', + primary key (id), + key idx_create_date (create_date) +)ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COMMENT='异常日志'; + + +-- 文件上传 +CREATE TABLE sys_oss ( + id bigint NOT NULL COMMENT 'id', + url varchar(200) COMMENT 'URL地址', + creator bigint COMMENT '创建者', + create_date datetime COMMENT '创建时间', + PRIMARY KEY (id), + key idx_create_date (create_date) +) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COMMENT='文件上传'; + +-- 定时任务 +CREATE TABLE schedule_job ( + id bigint NOT NULL COMMENT 'id', + bean_name varchar(200) DEFAULT NULL COMMENT 'spring bean名称', + params varchar(2000) DEFAULT NULL COMMENT '参数', + cron_expression varchar(100) DEFAULT NULL COMMENT 'cron表达式', + status tinyint unsigned COMMENT '任务状态 0:暂停 1:正常', + remark varchar(255) DEFAULT NULL COMMENT '备注', + creator bigint COMMENT '创建者', + create_date datetime COMMENT '创建时间', + updater bigint COMMENT '更新者', + update_date datetime COMMENT '更新时间', + PRIMARY KEY (id), + key idx_create_date (create_date) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='定时任务'; + +-- 定时任务日志 +CREATE TABLE schedule_job_log ( + id bigint NOT NULL COMMENT 'id', + job_id bigint NOT NULL COMMENT '任务id', + bean_name varchar(200) DEFAULT NULL COMMENT 'spring bean名称', + params varchar(2000) DEFAULT NULL COMMENT '参数', + status tinyint unsigned NOT NULL COMMENT '任务状态 0:失败 1:成功', + error varchar(2000) DEFAULT NULL COMMENT '失败信息', + times int NOT NULL COMMENT '耗时(单位:毫秒)', + create_date datetime COMMENT '创建时间', + PRIMARY KEY (id), + key idx_job_id (job_id), + key idx_create_date (create_date) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='定时任务日志'; + +-- 系统用户Token +CREATE TABLE sys_user_token ( + id bigint NOT NULL COMMENT 'id', + user_id bigint NOT NULL COMMENT '用户id', + token varchar(100) NOT NULL COMMENT '用户token', + expire_date datetime COMMENT '过期时间', + update_date datetime COMMENT '更新时间', + create_date datetime COMMENT '创建时间', + PRIMARY KEY (id), + UNIQUE KEY user_id (user_id), + UNIQUE KEY token (token) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统用户Token'; + +-- 初始数据 +INSERT INTO sys_user(id, username, password, real_name, gender, email, mobile, status, dept_id, super_admin, creator, create_date, updater, update_date) VALUES (1067246875800000001, 'admin', '$2a$10$012Kx2ba5jzqr9gLlG4MX.bnQJTD9UWqF57XDo2N3.fPtLne02u/m', '管理员', 0, 'root@weather.io', '13612345678', 1, null, 1, 1067246875800000001, now(), 1067246875800000001, now()); + +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000002, 0, '权限管理', NULL, NULL, 0, 'icon-safetycertificate', 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000003, 1067246875800000055, '新增', NULL, 'sys:user:save,sys:dept:list,sys:role:list', 1, NULL, 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000004, 1067246875800000055, '修改', NULL, 'sys:user:update,sys:dept:list,sys:role:list', 1, NULL, 2, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000005, 1067246875800000055, '删除', NULL, 'sys:user:delete', 1, NULL, 3, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000006, 1067246875800000055, '导出', NULL, 'sys:user:export', 1, NULL, 4, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000007, 1067246875800000002, '角色管理', 'sys/role', NULL, 0, 'icon-team', 2, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000008, 1067246875800000007, '查看', NULL, 'sys:role:page,sys:role:info', 1, NULL, 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000009, 1067246875800000007, '新增', NULL, 'sys:role:save,sys:menu:select,sys:dept:list', 1, NULL, 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000010, 1067246875800000007, '修改', NULL, 'sys:role:update,sys:menu:select,sys:dept:list', 1, NULL, 2, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000011, 1067246875800000007, '删除', NULL, 'sys:role:delete', 1, NULL, 3, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000012, 1067246875800000002, '部门管理', 'sys/dept', NULL, 0, 'icon-apartment', 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000014, 1067246875800000012, '查看', NULL, 'sys:dept:list,sys:dept:info', 1, NULL, 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000015, 1067246875800000012, '新增', NULL, 'sys:dept:save', 1, NULL, 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000016, 1067246875800000012, '修改', NULL, 'sys:dept:update', 1, NULL, 2, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000017, 1067246875800000012, '删除', NULL, 'sys:dept:delete', 1, NULL, 3, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000025, 1067246875800000035, '菜单管理', 'sys/menu', NULL, 0, 'icon-unorderedlist', 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000026, 1067246875800000025, '查看', NULL, 'sys:menu:list,sys:menu:info', 1, NULL, 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000027, 1067246875800000025, '新增', NULL, 'sys:menu:save', 1, NULL, 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000028, 1067246875800000025, '修改', NULL, 'sys:menu:update', 1, NULL, 2, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000029, 1067246875800000025, '删除', NULL, 'sys:menu:delete', 1, NULL, 3, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000030, 1067246875800000035, '定时任务', 'job/schedule', NULL, 0, 'icon-dashboard', 3, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000031, 1067246875800000030, '查看', NULL, 'sys:schedule:page,sys:schedule:info', 1, NULL, 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000032, 1067246875800000030, '新增', NULL, 'sys:schedule:save', 1, NULL, 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000033, 1067246875800000030, '修改', NULL, 'sys:schedule:update', 1, NULL, 2, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000034, 1067246875800000030, '删除', NULL, 'sys:schedule:delete', 1, NULL, 3, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000035, 0, '系统设置', NULL, NULL, 0, 'icon-setting', 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000036, 1067246875800000030, '暂停', NULL, 'sys:schedule:pause', 1, NULL, 4, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000037, 1067246875800000030, '恢复', NULL, 'sys:schedule:resume', 1, NULL, 5, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000038, 1067246875800000030, '立即执行', NULL, 'sys:schedule:run', 1, NULL, 6, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000039, 1067246875800000030, '日志列表', NULL, 'sys:schedule:log', 1, NULL, 7, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000040, 1067246875800000035, '参数管理', 'sys/params', '', 0, 'icon-fileprotect', 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000041, 1067246875800000035, '字典管理', 'sys/dict-type', NULL, 0, 'icon-golden-fill', 2, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000042, 1067246875800000041, '查看', NULL, 'sys:dict:page,sys:dict:info', 1, NULL, 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000043, 1067246875800000041, '新增', NULL, 'sys:dict:save', 1, NULL, 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000044, 1067246875800000041, '修改', NULL, 'sys:dict:update', 1, NULL, 2, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000045, 1067246875800000041, '删除', NULL, 'sys:dict:delete', 1, NULL, 3, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000046, 0, '日志管理', NULL, NULL, 0, 'icon-container', 2, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000047, 1067246875800000035, '文件上传', 'oss/oss', 'sys:oss:all', 0, 'icon-upload', 4, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000048, 1067246875800000046, '登录日志', 'sys/log-login', 'sys:log:login', 0, 'icon-filedone', 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000049, 1067246875800000046, '操作日志', 'sys/log-operation', 'sys:log:operation', 0, 'icon-solution', 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000050, 1067246875800000046, '异常日志', 'sys/log-error', 'sys:log:error', 0, 'icon-file-exception', 2, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000051, 1067246875800000053, 'SQL监控', '{{ApiUrl}}/druid/sql.html', NULL, 0, 'icon-database', 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000053, 0, '系统监控', NULL, NULL, 0, 'icon-desktop', 3, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000055, 1067246875800000002, '用户管理', 'sys/user', NULL, 0, 'icon-user', 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000056, 1067246875800000055, '查看', NULL, 'sys:user:page,sys:user:info', 1, NULL, 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000057, 1067246875800000040, '新增', NULL, 'sys:params:save', 1, NULL, 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000058, 1067246875800000040, '导出', NULL, 'sys:params:export', 1, NULL, 4, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000059, 1067246875800000040, '查看', '', 'sys:params:page,sys:params:info', 1, NULL, 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000060, 1067246875800000040, '修改', NULL, 'sys:params:update', 1, NULL, 2, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000061, 1067246875800000040, '删除', '', 'sys:params:delete', 1, '', 3, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1156748733921165314, 1067246875800000053, '接口文档', '{{ApiUrl}}/doc.html', '', 0, 'icon-file-word', 1, 1067246875800000001, now(), 1067246875800000001, now()); + + +INSERT INTO sys_dept(id, pid, pids, name, sort, creator, create_date, updater, update_date) VALUES (1067246875800000062, 1067246875800000063, '1067246875800000066,1067246875800000063', '技术部', 2, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_dept(id, pid, pids, name, sort, creator, create_date, updater, update_date) VALUES (1067246875800000063, 1067246875800000066, '1067246875800000066', '长沙分公司', 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_dept(id, pid, pids, name, sort, creator, create_date, updater, update_date) VALUES (1067246875800000064, 1067246875800000066, '1067246875800000066', '上海分公司', 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_dept(id, pid, pids, name, sort, creator, create_date, updater, update_date) VALUES (1067246875800000065, 1067246875800000064, '1067246875800000066,1067246875800000064', '市场部', 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_dept(id, pid, pids, name, sort, creator, create_date, updater, update_date) VALUES (1067246875800000066, 0, '0', '人人开源集团', 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_dept(id, pid, pids, name, sort, creator, create_date, updater, update_date) VALUES (1067246875800000067, 1067246875800000064, '1067246875800000066,1067246875800000064', '销售部', 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_dept(id, pid, pids, name, sort, creator, create_date, updater, update_date) VALUES (1067246875800000068, 1067246875800000063, '1067246875800000066,1067246875800000063', '产品部', 1, 1067246875800000001, now(), 1067246875800000001, now()); + +INSERT INTO sys_dict_type(id, dict_type, dict_name, remark, sort, creator, create_date, updater, update_date) VALUES (1160061077912858625, 'gender', '性别', '', 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_dict_data(id, dict_type_id, dict_label, dict_value, remark, sort, creator, create_date, updater, update_date) VALUES (1160061112075464705, 1160061077912858625, '男', '0', '', 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_dict_data(id, dict_type_id, dict_label, dict_value, remark, sort, creator, create_date, updater, update_date) VALUES (1160061146967879681, 1160061077912858625, '女', '1', '', 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_dict_data(id, dict_type_id, dict_label, dict_value, remark, sort, creator, create_date, updater, update_date) VALUES (1160061190127267841, 1160061077912858625, '保密', '2', '', 2, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_dict_type(id, dict_type, dict_name, remark, sort, creator, create_date, updater, update_date) VALUES (1225813644059140097, 'notice_type', '站内通知-类型', '', 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_dict_data(id, dict_type_id, dict_label, dict_value, remark, sort, creator, create_date, updater, update_date) VALUES (1225814069634195457, 1225813644059140097, '公告', '0', '', 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_dict_data(id, dict_type_id, dict_label, dict_value, remark, sort, creator, create_date, updater, update_date) VALUES (1225814107559092225, 1225813644059140097, '会议', '1', '', 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_dict_data(id, dict_type_id, dict_label, dict_value, remark, sort, creator, create_date, updater, update_date) VALUES (1225814271879340034, 1225813644059140097, '其他', '2', '', 2, 1067246875800000001, now(), 1067246875800000001, now()); + +INSERT INTO sys_params(id, param_code, param_value, param_type, remark, creator, create_date, updater, update_date) VALUES (1067246875800000073, 'CLOUD_STORAGE_CONFIG_KEY', '{"type":1,"qiniuDomain":"http://test.oss.weather.io","qiniuPrefix":"upload","qiniuAccessKey":"NrgMfABZxWLo5B-YYSjoE8-AZ1EISdi1Z3ubLOeZ","qiniuSecretKey":"uIwJHevMRWU0VLxFvgy0tAcOdGqasdtVlJkdy6vV","qiniuBucketName":"weather-oss","aliyunDomain":"","aliyunPrefix":"","aliyunEndPoint":"","aliyunAccessKeyId":"","aliyunAccessKeySecret":"","aliyunBucketName":"","qcloudDomain":"","qcloudPrefix":"","qcloudSecretId":"","qcloudSecretKey":"","qcloudBucketName":""}', '0', '云存储配置信息', 1067246875800000001, now(), 1067246875800000001, now()); + +INSERT INTO schedule_job (id, bean_name, params, cron_expression, status, remark, creator, create_date, updater, update_date) VALUES (1067246875800000076, 'testTask', 'weather', '0 0/30 * * * ?', 0, '有参测试,多个参数使用json', 1067246875800000001, now(), 1067246875800000001, now()); + + +-- quartz自带表结构 +CREATE TABLE QRTZ_JOB_DETAILS( + SCHED_NAME VARCHAR(120) NOT NULL, + JOB_NAME VARCHAR(200) NOT NULL, + JOB_GROUP VARCHAR(200) NOT NULL, + DESCRIPTION VARCHAR(250) NULL, + JOB_CLASS_NAME VARCHAR(250) NOT NULL, + IS_DURABLE VARCHAR(1) NOT NULL, + IS_NONCONCURRENT VARCHAR(1) NOT NULL, + IS_UPDATE_DATA VARCHAR(1) NOT NULL, + REQUESTS_RECOVERY VARCHAR(1) NOT NULL, + JOB_DATA BLOB NULL, + PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP)) + ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE QRTZ_TRIGGERS ( + SCHED_NAME VARCHAR(120) NOT NULL, + TRIGGER_NAME VARCHAR(200) NOT NULL, + TRIGGER_GROUP VARCHAR(200) NOT NULL, + JOB_NAME VARCHAR(200) NOT NULL, + JOB_GROUP VARCHAR(200) NOT NULL, + DESCRIPTION VARCHAR(250) NULL, + NEXT_FIRE_TIME BIGINT(13) NULL, + PREV_FIRE_TIME BIGINT(13) NULL, + PRIORITY INTEGER NULL, + TRIGGER_STATE VARCHAR(16) NOT NULL, + TRIGGER_TYPE VARCHAR(8) NOT NULL, + START_TIME BIGINT(13) NOT NULL, + END_TIME BIGINT(13) NULL, + CALENDAR_NAME VARCHAR(200) NULL, + MISFIRE_INSTR SMALLINT(2) NULL, + JOB_DATA BLOB NULL, + PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), + FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP) + REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP)) + ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE QRTZ_SIMPLE_TRIGGERS ( + SCHED_NAME VARCHAR(120) NOT NULL, + TRIGGER_NAME VARCHAR(200) NOT NULL, + TRIGGER_GROUP VARCHAR(200) NOT NULL, + REPEAT_COUNT BIGINT(7) NOT NULL, + REPEAT_INTERVAL BIGINT(12) NOT NULL, + TIMES_TRIGGERED BIGINT(10) NOT NULL, + PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), + FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) + REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)) + ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE QRTZ_CRON_TRIGGERS ( + SCHED_NAME VARCHAR(120) NOT NULL, + TRIGGER_NAME VARCHAR(200) NOT NULL, + TRIGGER_GROUP VARCHAR(200) NOT NULL, + CRON_EXPRESSION VARCHAR(120) NOT NULL, + TIME_ZONE_ID VARCHAR(80), + PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), + FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) + REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)) + ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE QRTZ_SIMPROP_TRIGGERS +( + SCHED_NAME VARCHAR(120) NOT NULL, + TRIGGER_NAME VARCHAR(200) NOT NULL, + TRIGGER_GROUP VARCHAR(200) NOT NULL, + STR_PROP_1 VARCHAR(512) NULL, + STR_PROP_2 VARCHAR(512) NULL, + STR_PROP_3 VARCHAR(512) NULL, + INT_PROP_1 INT NULL, + INT_PROP_2 INT NULL, + LONG_PROP_1 BIGINT NULL, + LONG_PROP_2 BIGINT NULL, + DEC_PROP_1 NUMERIC(13,4) NULL, + DEC_PROP_2 NUMERIC(13,4) NULL, + BOOL_PROP_1 VARCHAR(1) NULL, + BOOL_PROP_2 VARCHAR(1) NULL, + PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), + FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) + REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)) + ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE QRTZ_BLOB_TRIGGERS ( + SCHED_NAME VARCHAR(120) NOT NULL, + TRIGGER_NAME VARCHAR(200) NOT NULL, + TRIGGER_GROUP VARCHAR(200) NOT NULL, + BLOB_DATA BLOB NULL, + PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), + INDEX (SCHED_NAME,TRIGGER_NAME, TRIGGER_GROUP), + FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) + REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP)) + ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE QRTZ_CALENDARS ( + SCHED_NAME VARCHAR(120) NOT NULL, + CALENDAR_NAME VARCHAR(200) NOT NULL, + CALENDAR BLOB NOT NULL, + PRIMARY KEY (SCHED_NAME,CALENDAR_NAME)) + ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS ( + SCHED_NAME VARCHAR(120) NOT NULL, + TRIGGER_GROUP VARCHAR(200) NOT NULL, + PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP)) + ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE QRTZ_FIRED_TRIGGERS ( + SCHED_NAME VARCHAR(120) NOT NULL, + ENTRY_ID VARCHAR(95) NOT NULL, + TRIGGER_NAME VARCHAR(200) NOT NULL, + TRIGGER_GROUP VARCHAR(200) NOT NULL, + INSTANCE_NAME VARCHAR(200) NOT NULL, + FIRED_TIME BIGINT(13) NOT NULL, + SCHED_TIME BIGINT(13) NOT NULL, + PRIORITY INTEGER NOT NULL, + STATE VARCHAR(16) NOT NULL, + JOB_NAME VARCHAR(200) NULL, + JOB_GROUP VARCHAR(200) NULL, + IS_NONCONCURRENT VARCHAR(1) NULL, + REQUESTS_RECOVERY VARCHAR(1) NULL, + PRIMARY KEY (SCHED_NAME,ENTRY_ID)) + ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE QRTZ_SCHEDULER_STATE ( + SCHED_NAME VARCHAR(120) NOT NULL, + INSTANCE_NAME VARCHAR(200) NOT NULL, + LAST_CHECKIN_TIME BIGINT(13) NOT NULL, + CHECKIN_INTERVAL BIGINT(13) NOT NULL, + PRIMARY KEY (SCHED_NAME,INSTANCE_NAME)) + ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE QRTZ_LOCKS ( + SCHED_NAME VARCHAR(120) NOT NULL, + LOCK_NAME VARCHAR(40) NOT NULL, + PRIMARY KEY (SCHED_NAME,LOCK_NAME)) + ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE INDEX IDX_QRTZ_J_REQ_RECOVERY ON QRTZ_JOB_DETAILS(SCHED_NAME,REQUESTS_RECOVERY); +CREATE INDEX IDX_QRTZ_J_GRP ON QRTZ_JOB_DETAILS(SCHED_NAME,JOB_GROUP); + +CREATE INDEX IDX_QRTZ_T_J ON QRTZ_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP); +CREATE INDEX IDX_QRTZ_T_JG ON QRTZ_TRIGGERS(SCHED_NAME,JOB_GROUP); +CREATE INDEX IDX_QRTZ_T_C ON QRTZ_TRIGGERS(SCHED_NAME,CALENDAR_NAME); +CREATE INDEX IDX_QRTZ_T_G ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP); +CREATE INDEX IDX_QRTZ_T_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE); +CREATE INDEX IDX_QRTZ_T_N_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE); +CREATE INDEX IDX_QRTZ_T_N_G_STATE ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE); +CREATE INDEX IDX_QRTZ_T_NEXT_FIRE_TIME ON QRTZ_TRIGGERS(SCHED_NAME,NEXT_FIRE_TIME); +CREATE INDEX IDX_QRTZ_T_NFT_ST ON QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME); +CREATE INDEX IDX_QRTZ_T_NFT_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME); +CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE); +CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE_GRP ON QRTZ_TRIGGERS(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE); + +CREATE INDEX IDX_QRTZ_FT_TRIG_INST_NAME ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME); +CREATE INDEX IDX_QRTZ_FT_INST_JOB_REQ_RCVRY ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY); +CREATE INDEX IDX_QRTZ_FT_J_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_NAME,JOB_GROUP); +CREATE INDEX IDX_QRTZ_FT_JG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,JOB_GROUP); +CREATE INDEX IDX_QRTZ_FT_T_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP); +CREATE INDEX IDX_QRTZ_FT_TG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_GROUP); \ No newline at end of file diff --git a/system-admin/db/oracle.sql b/system-admin/db/oracle.sql new file mode 100644 index 0000000..9a33b19 --- /dev/null +++ b/system-admin/db/oracle.sql @@ -0,0 +1,684 @@ +CREATE TABLE sys_user ( + id NUMBER(20, 0) NOT NULL, + username varchar2(50) NOT NULL, + password varchar2(100), + real_name varchar2(50), + head_url varchar2(200), + gender NUMBER(2, 0), + email varchar2(100), + mobile varchar2(100), + dept_id NUMBER(20, 0), + super_admin NUMBER(2, 0), + status NUMBER(2, 0), + creator NUMBER(20, 0), + create_date date, + updater NUMBER(20, 0), + update_date date, + primary key (id) +); + +CREATE UNIQUE INDEX uk_sys_user_username on sys_user(username); +CREATE INDEX idx_sys_user_create_date on sys_user(create_date); + +COMMENT ON TABLE sys_user IS '用户管理'; +COMMENT ON COLUMN sys_user.id IS 'id'; +COMMENT ON COLUMN sys_user.username IS '用户名'; +COMMENT ON COLUMN sys_user.password IS '密码'; +COMMENT ON COLUMN sys_user.real_name IS '姓名'; +COMMENT ON COLUMN sys_user.head_url IS '头像'; +COMMENT ON COLUMN sys_user.gender IS '性别 0:男 1:女 2:保密'; +COMMENT ON COLUMN sys_user.email IS '邮箱'; +COMMENT ON COLUMN sys_user.mobile IS '手机号'; +COMMENT ON COLUMN sys_user.dept_id IS '部门ID'; +COMMENT ON COLUMN sys_user.super_admin IS '超级管理员 0:否 1:是'; +COMMENT ON COLUMN sys_user.status IS '状态 0:停用 1:正常'; +COMMENT ON COLUMN sys_user.creator IS '创建者'; +COMMENT ON COLUMN sys_user.create_date IS '创建时间'; +COMMENT ON COLUMN sys_user.updater IS '更新者'; +COMMENT ON COLUMN sys_user.update_date IS '更新时间'; + + +CREATE TABLE sys_dept ( + id NUMBER(20, 0) NOT NULL, + pid NUMBER(20, 0), + pids varchar2(500), + name varchar2(50), + sort NUMBER(10, 0), + creator NUMBER(20, 0), + create_date date, + updater NUMBER(20, 0), + update_date date, + primary key (id) +); +CREATE INDEX idx_sys_dept_pid on sys_dept(pid); +CREATE INDEX idx_sys_dept_idx_sort on sys_dept(sort); + +COMMENT ON TABLE sys_dept IS '部门管理'; +COMMENT ON COLUMN sys_dept.id IS 'id'; +COMMENT ON COLUMN sys_dept.pid IS '上级ID'; +COMMENT ON COLUMN sys_dept.pids IS '所有上级ID,用逗号分开'; +COMMENT ON COLUMN sys_dept.name IS '部门名称'; +COMMENT ON COLUMN sys_dept.sort IS '排序'; +COMMENT ON COLUMN sys_dept.creator IS '创建者'; +COMMENT ON COLUMN sys_dept.create_date IS '创建时间'; +COMMENT ON COLUMN sys_dept.updater IS '更新者'; +COMMENT ON COLUMN sys_dept.update_date IS '更新时间'; + + +create table sys_role +( + id NUMBER(20, 0) NOT NULL, + name varchar2(32), + remark varchar2(100), + dept_id NUMBER(20, 0), + creator NUMBER(20, 0), + create_date date, + updater NUMBER(20, 0), + update_date date, + primary key (id) +); + +CREATE INDEX idx_sys_role_dept_id on sys_role(dept_id); + +COMMENT ON TABLE sys_role IS '角色管理'; +COMMENT ON COLUMN sys_role.id IS 'id'; +COMMENT ON COLUMN sys_role.name IS '角色名称'; +COMMENT ON COLUMN sys_role.remark IS '备注'; +COMMENT ON COLUMN sys_role.dept_id IS '部门ID'; +COMMENT ON COLUMN sys_role.creator IS '创建者'; +COMMENT ON COLUMN sys_role.create_date IS '创建时间'; +COMMENT ON COLUMN sys_role.updater IS '更新者'; +COMMENT ON COLUMN sys_role.update_date IS '更新时间'; + + +create table sys_menu +( + id NUMBER(20, 0) NOT NULL, + pid NUMBER(20, 0), + name varchar2(200), + url varchar2(200), + permissions varchar2(500), + menu_type NUMBER(2, 0), + icon varchar2(50), + sort NUMBER(10, 0), + creator NUMBER(20, 0), + create_date date, + updater NUMBER(20, 0), + update_date date, + primary key (id) +); + +CREATE INDEX idx_sys_menu_pid on sys_menu(pid); +CREATE INDEX idx_sys_menu_sort on sys_menu(sort); + +COMMENT ON TABLE sys_menu IS '菜单管理'; +COMMENT ON COLUMN sys_menu.id IS 'id'; +COMMENT ON COLUMN sys_menu.pid IS '上级ID,一级菜单为0'; +COMMENT ON COLUMN sys_menu.name IS '名称'; +COMMENT ON COLUMN sys_menu.url IS '菜单URL'; +COMMENT ON COLUMN sys_menu.permissions IS '授权(多个用逗号分隔,如:sys:user:list,sys:user:save)'; +COMMENT ON COLUMN sys_menu.menu_type IS '类型 0:菜单 1:按钮'; +COMMENT ON COLUMN sys_menu.icon IS '菜单图标'; +COMMENT ON COLUMN sys_menu.sort IS '排序'; +COMMENT ON COLUMN sys_menu.creator IS '创建者'; +COMMENT ON COLUMN sys_menu.create_date IS '创建时间'; +COMMENT ON COLUMN sys_menu.updater IS '更新者'; +COMMENT ON COLUMN sys_menu.update_date IS '更新时间'; + + +create table sys_role_user +( + id varchar2(32) NOT NULL, + role_id varchar2(32), + user_id varchar2(32), + creator varchar2(32), + create_date date, + primary key (id) +); + +CREATE INDEX idx_sys_role_user_role_id on sys_role_user(role_id); +CREATE INDEX idx_sys_role_user_user_id on sys_role_user(user_id); + +COMMENT ON TABLE sys_role_user IS '角色用户关系'; +COMMENT ON COLUMN sys_role_user.role_id IS '角色ID'; +COMMENT ON COLUMN sys_role_user.user_id IS '用户ID'; +COMMENT ON COLUMN sys_role_user.creator IS '创建者'; +COMMENT ON COLUMN sys_role_user.create_date IS '创建时间'; + + +create table sys_role_menu +( + id NUMBER(20, 0) NOT NULL, + role_id NUMBER(20, 0), + menu_id NUMBER(20, 0), + creator NUMBER(20, 0), + create_date date, + primary key (id) +); + +CREATE INDEX idx_sys_role_menu_role_id on sys_role_menu(role_id); +CREATE INDEX idx_sys_role_menu_menu_id on sys_role_menu(menu_id); + +COMMENT ON TABLE sys_role_menu IS '角色菜单关系'; +COMMENT ON COLUMN sys_role_menu.role_id IS '角色ID'; +COMMENT ON COLUMN sys_role_menu.menu_id IS '菜单ID'; +COMMENT ON COLUMN sys_role_menu.creator IS '创建者'; +COMMENT ON COLUMN sys_role_menu.create_date IS '创建时间'; + + +create table sys_role_data_scope +( + id NUMBER(20, 0) NOT NULL, + role_id NUMBER(20, 0), + dept_id NUMBER(20, 0), + creator NUMBER(20, 0), + create_date date, + primary key (id) +); +CREATE INDEX idx_data_scope_role_id on sys_role_data_scope(role_id); + +COMMENT ON TABLE sys_role_data_scope IS '角色数据权限'; +COMMENT ON COLUMN sys_role_data_scope.role_id IS '角色ID'; +COMMENT ON COLUMN sys_role_data_scope.dept_id IS '部门ID'; +COMMENT ON COLUMN sys_role_data_scope.creator IS '创建者'; +COMMENT ON COLUMN sys_role_data_scope.create_date IS '创建时间'; + + +create table sys_params +( + id NUMBER(20, 0) NOT NULL, + param_code varchar2(32), + param_value varchar2(2000), + param_type NUMBER(2, 0) DEFAULT 1 NOT NULL, + remark varchar2(200), + creator NUMBER(20, 0), + create_date date, + updater NUMBER(20, 0), + update_date date, + primary key (id) +); +CREATE UNIQUE INDEX uk_sys_params_param_code on sys_params(param_code); +CREATE INDEX idx_sys_params_create_date on sys_params(create_date); + +COMMENT ON TABLE sys_params IS '参数管理'; +COMMENT ON COLUMN sys_params.param_code IS '参数编码'; +COMMENT ON COLUMN sys_params.param_value IS '参数值'; +COMMENT ON COLUMN sys_params.param_type IS '类型 0:系统参数 1:非系统参数'; +COMMENT ON COLUMN sys_params.remark IS '备注'; +COMMENT ON COLUMN sys_params.creator IS '创建者'; +COMMENT ON COLUMN sys_params.create_date IS '创建时间'; +COMMENT ON COLUMN sys_params.updater IS '更新者'; +COMMENT ON COLUMN sys_params.update_date IS '更新时间'; + + +create table sys_dict_type +( + id NUMBER(20, 0) NOT NULL, + dict_type varchar2(100), + dict_name varchar2(255), + remark varchar2(255), + sort NUMBER(10, 0), + creator NUMBER(20, 0), + create_date date, + updater NUMBER(20, 0), + update_date date, + primary key (id) +); +CREATE UNIQUE INDEX uk_sys_dict_type_dict_type on sys_dict_type(dict_type); + +COMMENT ON TABLE sys_dict_type IS '字典类型'; +COMMENT ON COLUMN sys_dict_type.id IS 'id'; +COMMENT ON COLUMN sys_dict_type.dict_type IS '字典类型'; +COMMENT ON COLUMN sys_dict_type.dict_name IS '字典名称'; +COMMENT ON COLUMN sys_dict_type.remark IS '备注'; +COMMENT ON COLUMN sys_dict_type.sort IS '排序'; +COMMENT ON COLUMN sys_dict_type.creator IS '创建者'; +COMMENT ON COLUMN sys_dict_type.create_date IS '创建时间'; +COMMENT ON COLUMN sys_dict_type.updater IS '更新者'; +COMMENT ON COLUMN sys_dict_type.update_date IS '更新时间'; + +create table sys_dict_data +( + id NUMBER(20, 0) NOT NULL, + dict_type_id NUMBER(20, 0) NOT NULL, + dict_label varchar2(255), + dict_value varchar2(255), + remark varchar2(255), + sort NUMBER(10, 0), + creator NUMBER(20, 0), + create_date date, + updater NUMBER(20, 0), + update_date date, + primary key (id) +); +CREATE INDEX idx_sys_dict_data_sort on sys_dict_data(sort); +CREATE UNIQUE INDEX uk_dict_type_value on sys_dict_data(dict_type_id, dict_value); + +COMMENT ON TABLE sys_dict_data IS '字典数据'; +COMMENT ON COLUMN sys_dict_data.id IS 'id'; +COMMENT ON COLUMN sys_dict_data.dict_type_id IS '字典类型ID'; +COMMENT ON COLUMN sys_dict_data.dict_label IS '字典标签'; +COMMENT ON COLUMN sys_dict_data.dict_value IS '字典值'; +COMMENT ON COLUMN sys_dict_data.remark IS '备注'; +COMMENT ON COLUMN sys_dict_data.sort IS '排序'; +COMMENT ON COLUMN sys_dict_data.creator IS '创建者'; +COMMENT ON COLUMN sys_dict_data.create_date IS '创建时间'; +COMMENT ON COLUMN sys_dict_data.updater IS '更新者'; +COMMENT ON COLUMN sys_dict_data.update_date IS '更新时间'; + +create table sys_log_login +( + id NUMBER(20, 0) NOT NULL, + operation NUMBER(2, 0), + status NUMBER(2, 0), + user_agent varchar2(500), + ip varchar2(32), + creator_name varchar2(50), + creator NUMBER(20, 0), + create_date date, + primary key (id) +); +CREATE INDEX idx_login_status on sys_log_login(status); +CREATE INDEX idx_login_create_date on sys_log_login(create_date); + +COMMENT ON TABLE sys_log_login IS '登录日志'; +COMMENT ON COLUMN sys_log_login.id IS 'id'; +COMMENT ON COLUMN sys_log_login.operation IS '用户操作 0:用户登录 1:用户退出'; +COMMENT ON COLUMN sys_log_login.status IS '状态 0:失败 1:成功 2:账号已锁定'; +COMMENT ON COLUMN sys_log_login.user_agent IS '用户代理'; +COMMENT ON COLUMN sys_log_login.ip IS '操作IP'; +COMMENT ON COLUMN sys_log_login.creator_name IS '用户名'; +COMMENT ON COLUMN sys_log_login.creator IS '创建者'; +COMMENT ON COLUMN sys_log_login.create_date IS '创建时间'; + + +create table sys_log_operation +( + id NUMBER(20, 0) NOT NULL, + operation varchar2(50), + request_uri varchar2(200), + request_method varchar2(20), + request_params clob, + request_time NUMBER(10, 0), + user_agent varchar2(500), + ip varchar2(32), + status NUMBER(2, 0), + creator_name varchar2(50), + creator NUMBER(20, 0), + create_date date, + primary key (id) +); +CREATE INDEX idx_operation_create_date on sys_log_operation(create_date); + +COMMENT ON TABLE sys_log_operation IS '操作日志'; +COMMENT ON COLUMN sys_log_operation.id IS 'id'; +COMMENT ON COLUMN sys_log_operation.operation IS '用户操作'; +COMMENT ON COLUMN sys_log_operation.request_uri IS '请求URI'; +COMMENT ON COLUMN sys_log_operation.request_method IS '请求方式'; +COMMENT ON COLUMN sys_log_operation.request_params IS '请求参数'; +COMMENT ON COLUMN sys_log_operation.request_time IS '请求时长(毫秒)'; +COMMENT ON COLUMN sys_log_operation.user_agent IS '用户代理'; +COMMENT ON COLUMN sys_log_operation.ip IS '操作IP'; +COMMENT ON COLUMN sys_log_operation.status IS '状态 0:失败 1:成功'; +COMMENT ON COLUMN sys_log_operation.creator_name IS '用户名'; +COMMENT ON COLUMN sys_log_operation.creator IS '创建者'; +COMMENT ON COLUMN sys_log_operation.create_date IS '创建时间'; + + +create table sys_log_error +( + id NUMBER(20, 0) NOT NULL, + request_uri varchar2(200), + request_method varchar2(20), + request_params clob, + user_agent varchar2(500), + ip varchar2(32), + error_info clob, + creator NUMBER(20, 0), + create_date date, + primary key (id) +); +CREATE INDEX idx_error_create_date on sys_log_error(create_date); + +COMMENT ON TABLE sys_log_error IS '异常日志'; +COMMENT ON COLUMN sys_log_error.id IS 'id'; +COMMENT ON COLUMN sys_log_error.request_uri IS '请求URI'; +COMMENT ON COLUMN sys_log_error.request_method IS '请求方式'; +COMMENT ON COLUMN sys_log_error.request_params IS '请求参数'; +COMMENT ON COLUMN sys_log_error.user_agent IS '用户代理'; +COMMENT ON COLUMN sys_log_error.ip IS '操作IP'; +COMMENT ON COLUMN sys_log_error.error_info IS '异常信息'; +COMMENT ON COLUMN sys_log_error.creator IS '创建者'; +COMMENT ON COLUMN sys_log_error.create_date IS '创建时间'; + + +CREATE TABLE sys_oss ( + id NUMBER(20, 0) NOT NULL, + url varchar2(200), + creator NUMBER(20, 0), + create_date date, + PRIMARY KEY (id) +); +CREATE INDEX idx_sys_oss_create_date on sys_oss(create_date); + +COMMENT ON TABLE sys_oss IS '文件上传'; +COMMENT ON COLUMN sys_oss.id IS 'id'; +COMMENT ON COLUMN sys_oss.url IS 'URL地址'; +COMMENT ON COLUMN sys_mail_log.creator IS '创建者'; +COMMENT ON COLUMN sys_mail_log.create_date IS '创建时间'; + + +CREATE TABLE schedule_job ( + id NUMBER(20, 0) NOT NULL, + bean_name varchar2(200), + params varchar2(2000), + cron_expression varchar2(100), + status NUMBER(2, 0), + remark varchar2(255), + creator NUMBER(20, 0), + create_date date, + updater NUMBER(20, 0), + update_date date, + PRIMARY KEY (id) +); + +CREATE INDEX idx_schedule_job_create_date on schedule_job(create_date); + +COMMENT ON TABLE schedule_job IS '定时任务'; +COMMENT ON COLUMN schedule_job.id IS 'id'; +COMMENT ON COLUMN schedule_job.bean_name IS 'spring bean名称'; +COMMENT ON COLUMN schedule_job.params IS '参数'; +COMMENT ON COLUMN schedule_job.cron_expression IS 'cron表达式'; +COMMENT ON COLUMN schedule_job.status IS '任务状态 0:暂停 1:正常'; +COMMENT ON COLUMN schedule_job.remark IS '备注'; +COMMENT ON COLUMN schedule_job.creator IS '创建者'; +COMMENT ON COLUMN schedule_job.create_date IS '创建时间'; +COMMENT ON COLUMN schedule_job.updater IS '更新者'; +COMMENT ON COLUMN schedule_job.update_date IS '更新时间'; + + +CREATE TABLE schedule_job_log ( + id NUMBER(20, 0) NOT NULL, + job_id NUMBER(20, 0) NOT NULL, + bean_name varchar2(200), + params varchar2(2000), + status NUMBER(2, 0), + error varchar2(2000), + times NUMBER(10, 0), + create_date date, + PRIMARY KEY (id) +); + +CREATE INDEX idx_job_log_job_id on schedule_job_log(job_id); +CREATE INDEX idx_job_log_create_date on schedule_job_log(create_date); + +COMMENT ON TABLE schedule_job_log IS '定时任务日志'; +COMMENT ON COLUMN schedule_job_log.id IS 'id'; +COMMENT ON COLUMN schedule_job_log.bean_name IS 'spring bean名称'; +COMMENT ON COLUMN schedule_job_log.params IS '参数'; +COMMENT ON COLUMN schedule_job_log.status IS '任务状态 0:失败 1:成功'; +COMMENT ON COLUMN schedule_job_log.error IS '失败信息'; +COMMENT ON COLUMN schedule_job_log.times IS '耗时(单位:毫秒)'; +COMMENT ON COLUMN schedule_job.create_date IS '创建时间'; + + +CREATE TABLE sys_user_token ( + id NUMBER(20, 0) NOT NULL, + user_id NUMBER(20, 0), + token varchar2(100), + expire_date date, + update_date date, + create_date date, + PRIMARY KEY (id) +); + +CREATE UNIQUE INDEX uk_sys_user_token_user_id on sys_user_token(user_id); +CREATE UNIQUE INDEX uk_sys_user_token on sys_user_token(token); + +COMMENT ON TABLE sys_user_token IS '系统用户Token'; +COMMENT ON COLUMN sys_user_token.id IS 'id'; +COMMENT ON COLUMN sys_user_token.user_id IS '用户id'; +COMMENT ON COLUMN sys_user_token.token IS '用户token'; +COMMENT ON COLUMN sys_user_token.expire_date IS '过期时间'; +COMMENT ON COLUMN sys_user_token.update_date IS '更新时间'; +COMMENT ON COLUMN sys_user_token.create_date IS '创建时间'; + + + +-- 初始数据 +INSERT INTO sys_user(id, username, password, real_name, gender, email, mobile, status, dept_id, super_admin, creator, create_date, updater, update_date) VALUES (1067246875800000001, 'admin', '$2a$10$012Kx2ba5jzqr9gLlG4MX.bnQJTD9UWqF57XDo2N3.fPtLne02u/m', '管理员', 0, 'root@weather.io', '13612345678', 1, null, 1, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); + +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000002, 0, '权限管理', NULL, NULL, 0, 'icon-safetycertificate', 0, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000003, 1067246875800000055, '新增', NULL, 'sys:user:save,sys:dept:list,sys:role:list', 1, NULL, 1, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000004, 1067246875800000055, '修改', NULL, 'sys:user:update,sys:dept:list,sys:role:list', 1, NULL, 2, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000005, 1067246875800000055, '删除', NULL, 'sys:user:delete', 1, NULL, 3, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000006, 1067246875800000055, '导出', NULL, 'sys:user:export', 1, NULL, 4, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000007, 1067246875800000002, '角色管理', 'sys/role', NULL, 0, 'icon-team', 2, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000008, 1067246875800000007, '查看', NULL, 'sys:role:page,sys:role:info', 1, NULL, 0, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000009, 1067246875800000007, '新增', NULL, 'sys:role:save,sys:menu:select,sys:dept:list', 1, NULL, 1, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000010, 1067246875800000007, '修改', NULL, 'sys:role:update,sys:menu:select,sys:dept:list', 1, NULL, 2, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000011, 1067246875800000007, '删除', NULL, 'sys:role:delete', 1, NULL, 3, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000012, 1067246875800000002, '部门管理', 'sys/dept', NULL, 0, 'icon-apartment', 1, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000014, 1067246875800000012, '查看', NULL, 'sys:dept:list,sys:dept:info', 1, NULL, 0, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000015, 1067246875800000012, '新增', NULL, 'sys:dept:save', 1, NULL, 1, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000016, 1067246875800000012, '修改', NULL, 'sys:dept:update', 1, NULL, 2, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000017, 1067246875800000012, '删除', NULL, 'sys:dept:delete', 1, NULL, 3, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000025, 1067246875800000035, '菜单管理', 'sys/menu', NULL, 0, 'icon-unorderedlist', 0, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000026, 1067246875800000025, '查看', NULL, 'sys:menu:list,sys:menu:info', 1, NULL, 0, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000027, 1067246875800000025, '新增', NULL, 'sys:menu:save', 1, NULL, 1, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000028, 1067246875800000025, '修改', NULL, 'sys:menu:update', 1, NULL, 2, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000029, 1067246875800000025, '删除', NULL, 'sys:menu:delete', 1, NULL, 3, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000030, 1067246875800000035, '定时任务', 'job/schedule', NULL, 0, 'icon-dashboard', 3, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000031, 1067246875800000030, '查看', NULL, 'sys:schedule:page,sys:schedule:info', 1, NULL, 0, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000032, 1067246875800000030, '新增', NULL, 'sys:schedule:save', 1, NULL, 1, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000033, 1067246875800000030, '修改', NULL, 'sys:schedule:update', 1, NULL, 2, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000034, 1067246875800000030, '删除', NULL, 'sys:schedule:delete', 1, NULL, 3, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000035, 0, '系统设置', NULL, NULL, 0, 'icon-setting', 1, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000036, 1067246875800000030, '暂停', NULL, 'sys:schedule:pause', 1, NULL, 4, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000037, 1067246875800000030, '恢复', NULL, 'sys:schedule:resume', 1, NULL, 5, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000038, 1067246875800000030, '立即执行', NULL, 'sys:schedule:run', 1, NULL, 6, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000039, 1067246875800000030, '日志列表', NULL, 'sys:schedule:log', 1, NULL, 7, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000040, 1067246875800000035, '参数管理', 'sys/params', '', 0, 'icon-fileprotect', 1, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000041, 1067246875800000035, '字典管理', 'sys/dict-type', NULL, 0, 'icon-golden-fill', 2, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000042, 1067246875800000041, '查看', NULL, 'sys:dict:page,sys:dict:info', 1, NULL, 0, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000043, 1067246875800000041, '新增', NULL, 'sys:dict:save', 1, NULL, 1, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000044, 1067246875800000041, '修改', NULL, 'sys:dict:update', 1, NULL, 2, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000045, 1067246875800000041, '删除', NULL, 'sys:dict:delete', 1, NULL, 3, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000046, 0, '日志管理', NULL, NULL, 0, 'icon-container', 2, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000047, 1067246875800000035, '文件上传', 'oss/oss', 'sys:oss:all', 0, 'icon-upload', 4, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000048, 1067246875800000046, '登录日志', 'sys/log-login', 'sys:log:login', 0, 'icon-filedone', 0, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000049, 1067246875800000046, '操作日志', 'sys/log-operation', 'sys:log:operation', 0, 'icon-solution', 1, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000050, 1067246875800000046, '异常日志', 'sys/log-error', 'sys:log:error', 0, 'icon-file-exception', 2, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000051, 1067246875800000053, 'SQL监控', '{{ApiUrl}}/druid/sql.html', NULL, 0, 'icon-database', 0, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000053, 0, '系统监控', NULL, NULL, 0, 'icon-desktop', 3, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000055, 1067246875800000002, '用户管理', 'sys/user', NULL, 0, 'icon-user', 0, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000056, 1067246875800000055, '查看', NULL, 'sys:user:page,sys:user:info', 1, NULL, 0, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000057, 1067246875800000040, '新增', NULL, 'sys:params:save', 1, NULL, 1, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000058, 1067246875800000040, '导出', NULL, 'sys:params:export', 1, NULL, 4, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000059, 1067246875800000040, '查看', '', 'sys:params:page,sys:params:info', 1, NULL, 0, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000060, 1067246875800000040, '修改', NULL, 'sys:params:update', 1, NULL, 2, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000061, 1067246875800000040, '删除', '', 'sys:params:delete', 1, '', 3, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1156748733921165314, 1067246875800000053, '接口文档', '{{ApiUrl}}/doc.html', '', 0, 'icon-file-word', 1, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); + + +INSERT INTO sys_dept(id, pid, pids, name, sort, creator, create_date, updater, update_date) VALUES (1067246875800000062, 1067246875800000063, '1067246875800000066,1067246875800000063', '技术部', 2, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_dept(id, pid, pids, name, sort, creator, create_date, updater, update_date) VALUES (1067246875800000063, 1067246875800000066, '1067246875800000066', '长沙分公司', 1, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_dept(id, pid, pids, name, sort, creator, create_date, updater, update_date) VALUES (1067246875800000064, 1067246875800000066, '1067246875800000066', '上海分公司', 0, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_dept(id, pid, pids, name, sort, creator, create_date, updater, update_date) VALUES (1067246875800000065, 1067246875800000064, '1067246875800000066,1067246875800000064', '市场部', 0, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_dept(id, pid, pids, name, sort, creator, create_date, updater, update_date) VALUES (1067246875800000066, 0, '0', '人人开源集团', 0, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_dept(id, pid, pids, name, sort, creator, create_date, updater, update_date) VALUES (1067246875800000067, 1067246875800000064, '1067246875800000066,1067246875800000064', '销售部', 0, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_dept(id, pid, pids, name, sort, creator, create_date, updater, update_date) VALUES (1067246875800000068, 1067246875800000063, '1067246875800000066,1067246875800000063', '产品部', 1, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); + +INSERT INTO sys_dict_type(id, dict_type, dict_name, remark, sort, creator, create_date, updater, update_date) VALUES (1160061077912858625, 'gender', '性别', '', 0, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_dict_data(id, dict_type_id, dict_label, dict_value, remark, sort, creator, create_date, updater, update_date) VALUES (1160061112075464705, 1160061077912858625, '男', '0', '', 0, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_dict_data(id, dict_type_id, dict_label, dict_value, remark, sort, creator, create_date, updater, update_date) VALUES (1160061146967879681, 1160061077912858625, '女', '1', '', 1, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_dict_data(id, dict_type_id, dict_label, dict_value, remark, sort, creator, create_date, updater, update_date) VALUES (1160061190127267841, 1160061077912858625, '保密', '2', '', 2, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_dict_type(id, dict_type, dict_name, remark, sort, creator, create_date, updater, update_date) VALUES (1225813644059140097, 'notice_type', '站内通知-类型', '', 1, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_dict_data(id, dict_type_id, dict_label, dict_value, remark, sort, creator, create_date, updater, update_date) VALUES (1225814069634195457, 1225813644059140097, '公告', '0', '', 0, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_dict_data(id, dict_type_id, dict_label, dict_value, remark, sort, creator, create_date, updater, update_date) VALUES (1225814107559092225, 1225813644059140097, '会议', '1', '', 1, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); +INSERT INTO sys_dict_data(id, dict_type_id, dict_label, dict_value, remark, sort, creator, create_date, updater, update_date) VALUES (1225814271879340034, 1225813644059140097, '其他', '2', '', 2, 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); + +INSERT INTO sys_params(id, param_code, param_value, param_type, remark, creator, create_date, updater, update_date) VALUES (1067246875800000073, 'CLOUD_STORAGE_CONFIG_KEY', '{"type":1,"qiniuDomain":"http://test.oss.weather.io","qiniuPrefix":"upload","qiniuAccessKey":"NrgMfABZxWLo5B-YYSjoE8-AZ1EISdi1Z3ubLOeZ","qiniuSecretKey":"uIwJHevMRWU0VLxFvgy0tAcOdGqasdtVlJkdy6vV","qiniuBucketName":"weather-oss","aliyunDomain":"","aliyunPrefix":"","aliyunEndPoint":"","aliyunAccessKeyId":"","aliyunAccessKeySecret":"","aliyunBucketName":"","qcloudDomain":"","qcloudPrefix":"","qcloudSecretId":"","qcloudSecretKey":"","qcloudBucketName":""}', '0', '云存储配置信息', 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); + +INSERT INTO schedule_job (id, bean_name, params, cron_expression, status, remark, creator, create_date, updater, update_date) VALUES (1067246875800000076, 'testTask', 'weather', '0 0/30 * * * ?', 0, '有参测试,多个参数使用json', 1067246875800000001, CURRENT_DATE, 1067246875800000001, CURRENT_DATE); + + +-- quartz自带表结构 +CREATE TABLE qrtz_job_details +( + SCHED_NAME VARCHAR2(120) NOT NULL, + JOB_NAME VARCHAR2(200) NOT NULL, + JOB_GROUP VARCHAR2(200) NOT NULL, + DESCRIPTION VARCHAR2(250) NULL, + JOB_CLASS_NAME VARCHAR2(250) NOT NULL, + IS_DURABLE VARCHAR2(1) NOT NULL, + IS_NONCONCURRENT VARCHAR2(1) NOT NULL, + IS_UPDATE_DATA VARCHAR2(1) NOT NULL, + REQUESTS_RECOVERY VARCHAR2(1) NOT NULL, + JOB_DATA BLOB NULL, + CONSTRAINT QRTZ_JOB_DETAILS_PK PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP) +); +CREATE TABLE qrtz_triggers +( + SCHED_NAME VARCHAR2(120) NOT NULL, + TRIGGER_NAME VARCHAR2(200) NOT NULL, + TRIGGER_GROUP VARCHAR2(200) NOT NULL, + JOB_NAME VARCHAR2(200) NOT NULL, + JOB_GROUP VARCHAR2(200) NOT NULL, + DESCRIPTION VARCHAR2(250) NULL, + NEXT_FIRE_TIME NUMBER(13) NULL, + PREV_FIRE_TIME NUMBER(13) NULL, + PRIORITY NUMBER(13) NULL, + TRIGGER_STATE VARCHAR2(16) NOT NULL, + TRIGGER_TYPE VARCHAR2(8) NOT NULL, + START_TIME NUMBER(13) NOT NULL, + END_TIME NUMBER(13) NULL, + CALENDAR_NAME VARCHAR2(200) NULL, + MISFIRE_INSTR NUMBER(2) NULL, + JOB_DATA BLOB NULL, + CONSTRAINT QRTZ_TRIGGERS_PK PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), + CONSTRAINT QRTZ_TRIGGER_TO_JOBS_FK FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP) + REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP) +); +CREATE TABLE qrtz_simple_triggers +( + SCHED_NAME VARCHAR2(120) NOT NULL, + TRIGGER_NAME VARCHAR2(200) NOT NULL, + TRIGGER_GROUP VARCHAR2(200) NOT NULL, + REPEAT_COUNT NUMBER(7) NOT NULL, + REPEAT_INTERVAL NUMBER(12) NOT NULL, + TIMES_TRIGGERED NUMBER(10) NOT NULL, + CONSTRAINT QRTZ_SIMPLE_TRIG_PK PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), + CONSTRAINT QRTZ_SIMPLE_TRIG_TO_TRIG_FK FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) + REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) +); +CREATE TABLE qrtz_cron_triggers +( + SCHED_NAME VARCHAR2(120) NOT NULL, + TRIGGER_NAME VARCHAR2(200) NOT NULL, + TRIGGER_GROUP VARCHAR2(200) NOT NULL, + CRON_EXPRESSION VARCHAR2(120) NOT NULL, + TIME_ZONE_ID VARCHAR2(80), + CONSTRAINT QRTZ_CRON_TRIG_PK PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), + CONSTRAINT QRTZ_CRON_TRIG_TO_TRIG_FK FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) + REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) +); +CREATE TABLE qrtz_simprop_triggers +( + SCHED_NAME VARCHAR2(120) NOT NULL, + TRIGGER_NAME VARCHAR2(200) NOT NULL, + TRIGGER_GROUP VARCHAR2(200) NOT NULL, + STR_PROP_1 VARCHAR2(512) NULL, + STR_PROP_2 VARCHAR2(512) NULL, + STR_PROP_3 VARCHAR2(512) NULL, + INT_PROP_1 NUMBER(10) NULL, + INT_PROP_2 NUMBER(10) NULL, + LONG_PROP_1 NUMBER(13) NULL, + LONG_PROP_2 NUMBER(13) NULL, + DEC_PROP_1 NUMERIC(13,4) NULL, + DEC_PROP_2 NUMERIC(13,4) NULL, + BOOL_PROP_1 VARCHAR2(1) NULL, + BOOL_PROP_2 VARCHAR2(1) NULL, + CONSTRAINT QRTZ_SIMPROP_TRIG_PK PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), + CONSTRAINT QRTZ_SIMPROP_TRIG_TO_TRIG_FK FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) + REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) +); +CREATE TABLE qrtz_blob_triggers +( + SCHED_NAME VARCHAR2(120) NOT NULL, + TRIGGER_NAME VARCHAR2(200) NOT NULL, + TRIGGER_GROUP VARCHAR2(200) NOT NULL, + BLOB_DATA BLOB NULL, + CONSTRAINT QRTZ_BLOB_TRIG_PK PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), + CONSTRAINT QRTZ_BLOB_TRIG_TO_TRIG_FK FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) + REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) +); +CREATE TABLE qrtz_calendars +( + SCHED_NAME VARCHAR2(120) NOT NULL, + CALENDAR_NAME VARCHAR2(200) NOT NULL, + CALENDAR BLOB NOT NULL, + CONSTRAINT QRTZ_CALENDARS_PK PRIMARY KEY (SCHED_NAME,CALENDAR_NAME) +); +CREATE TABLE qrtz_paused_trigger_grps +( + SCHED_NAME VARCHAR2(120) NOT NULL, + TRIGGER_GROUP VARCHAR2(200) NOT NULL, + CONSTRAINT QRTZ_PAUSED_TRIG_GRPS_PK PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP) +); +CREATE TABLE qrtz_fired_triggers +( + SCHED_NAME VARCHAR2(120) NOT NULL, + ENTRY_ID VARCHAR2(95) NOT NULL, + TRIGGER_NAME VARCHAR2(200) NOT NULL, + TRIGGER_GROUP VARCHAR2(200) NOT NULL, + INSTANCE_NAME VARCHAR2(200) NOT NULL, + FIRED_TIME NUMBER(13) NOT NULL, + SCHED_TIME NUMBER(13) NOT NULL, + PRIORITY NUMBER(13) NOT NULL, + STATE VARCHAR2(16) NOT NULL, + JOB_NAME VARCHAR2(200) NULL, + JOB_GROUP VARCHAR2(200) NULL, + IS_NONCONCURRENT VARCHAR2(1) NULL, + REQUESTS_RECOVERY VARCHAR2(1) NULL, + CONSTRAINT QRTZ_FIRED_TRIGGER_PK PRIMARY KEY (SCHED_NAME,ENTRY_ID) +); +CREATE TABLE qrtz_scheduler_state +( + SCHED_NAME VARCHAR2(120) NOT NULL, + INSTANCE_NAME VARCHAR2(200) NOT NULL, + LAST_CHECKIN_TIME NUMBER(13) NOT NULL, + CHECKIN_INTERVAL NUMBER(13) NOT NULL, + CONSTRAINT QRTZ_SCHEDULER_STATE_PK PRIMARY KEY (SCHED_NAME,INSTANCE_NAME) +); +CREATE TABLE qrtz_locks +( + SCHED_NAME VARCHAR2(120) NOT NULL, + LOCK_NAME VARCHAR2(40) NOT NULL, + CONSTRAINT QRTZ_LOCKS_PK PRIMARY KEY (SCHED_NAME,LOCK_NAME) +); + +create index idx_qrtz_j_req_recovery on qrtz_job_details(SCHED_NAME,REQUESTS_RECOVERY); +create index idx_qrtz_j_grp on qrtz_job_details(SCHED_NAME,JOB_GROUP); + +create index idx_qrtz_t_j on qrtz_triggers(SCHED_NAME,JOB_NAME,JOB_GROUP); +create index idx_qrtz_t_jg on qrtz_triggers(SCHED_NAME,JOB_GROUP); +create index idx_qrtz_t_c on qrtz_triggers(SCHED_NAME,CALENDAR_NAME); +create index idx_qrtz_t_g on qrtz_triggers(SCHED_NAME,TRIGGER_GROUP); +create index idx_qrtz_t_state on qrtz_triggers(SCHED_NAME,TRIGGER_STATE); +create index idx_qrtz_t_n_state on qrtz_triggers(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE); +create index idx_qrtz_t_n_g_state on qrtz_triggers(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE); +create index idx_qrtz_t_next_fire_time on qrtz_triggers(SCHED_NAME,NEXT_FIRE_TIME); +create index idx_qrtz_t_nft_st on qrtz_triggers(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME); +create index idx_qrtz_t_nft_misfire on qrtz_triggers(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME); +create index idx_qrtz_t_nft_st_misfire on qrtz_triggers(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE); +create index idx_qrtz_t_nft_st_misfire_grp on qrtz_triggers(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE); + +create index idx_qrtz_ft_trig_inst_name on qrtz_fired_triggers(SCHED_NAME,INSTANCE_NAME); +create index idx_qrtz_ft_inst_job_req_rcvry on qrtz_fired_triggers(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY); +create index idx_qrtz_ft_j_g on qrtz_fired_triggers(SCHED_NAME,JOB_NAME,JOB_GROUP); +create index idx_qrtz_ft_jg on qrtz_fired_triggers(SCHED_NAME,JOB_GROUP); +create index idx_qrtz_ft_t_g on qrtz_fired_triggers(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP); +create index idx_qrtz_ft_tg on qrtz_fired_triggers(SCHED_NAME,TRIGGER_GROUP); + + diff --git a/system-admin/db/postgresql.sql b/system-admin/db/postgresql.sql new file mode 100644 index 0000000..1122e9a --- /dev/null +++ b/system-admin/db/postgresql.sql @@ -0,0 +1,699 @@ +CREATE TABLE sys_user ( + id int8 NOT NULL, + username varchar(50) NOT NULL, + password varchar(100), + real_name varchar(50), + head_url varchar(200), + gender int, + email varchar(100), + mobile varchar(100), + dept_id int8, + super_admin int, + status int, + creator int8, + create_date timestamp, + updater int8, + update_date timestamp, + primary key (id) +); + +CREATE UNIQUE INDEX uk_sys_user_username on sys_user(username); +CREATE INDEX idx_sys_user_create_date on sys_user(create_date); + +COMMENT ON TABLE sys_user IS '用户管理'; +COMMENT ON COLUMN sys_user.id IS 'id'; +COMMENT ON COLUMN sys_user.username IS '用户名'; +COMMENT ON COLUMN sys_user.password IS '密码'; +COMMENT ON COLUMN sys_user.real_name IS '姓名'; +COMMENT ON COLUMN sys_user.head_url IS '头像'; +COMMENT ON COLUMN sys_user.gender IS '性别 0:男 1:女 2:保密'; +COMMENT ON COLUMN sys_user.email IS '邮箱'; +COMMENT ON COLUMN sys_user.mobile IS '手机号'; +COMMENT ON COLUMN sys_user.dept_id IS '部门ID'; +COMMENT ON COLUMN sys_user.super_admin IS '超级管理员 0:否 1:是'; +COMMENT ON COLUMN sys_user.status IS '状态 0:停用 1:正常'; +COMMENT ON COLUMN sys_user.creator IS '创建者'; +COMMENT ON COLUMN sys_user.create_date IS '创建时间'; +COMMENT ON COLUMN sys_user.updater IS '更新者'; +COMMENT ON COLUMN sys_user.update_date IS '更新时间'; + + +CREATE TABLE sys_dept ( + id int8 NOT NULL, + pid int8, + pids varchar(500), + name varchar(50), + sort int, + creator int8, + create_date timestamp, + updater int8, + update_date timestamp, + primary key (id) +); +CREATE INDEX idx_sys_dept_pid on sys_dept(pid); +CREATE INDEX idx_sys_dept_idx_sort on sys_dept(sort); + +COMMENT ON TABLE sys_dept IS '部门管理'; +COMMENT ON COLUMN sys_dept.id IS 'id'; +COMMENT ON COLUMN sys_dept.pid IS '上级ID'; +COMMENT ON COLUMN sys_dept.pids IS '所有上级ID,用逗号分开'; +COMMENT ON COLUMN sys_dept.name IS '部门名称'; +COMMENT ON COLUMN sys_dept.sort IS '排序'; +COMMENT ON COLUMN sys_dept.creator IS '创建者'; +COMMENT ON COLUMN sys_dept.create_date IS '创建时间'; +COMMENT ON COLUMN sys_dept.updater IS '更新者'; +COMMENT ON COLUMN sys_dept.update_date IS '更新时间'; + + +create table sys_role +( + id int8 NOT NULL, + name varchar(50), + remark varchar(100), + dept_id int8, + creator int8, + create_date timestamp, + updater int8, + update_date timestamp, + primary key (id) +); + +CREATE INDEX idx_sys_role_dept_id on sys_role(dept_id); + +COMMENT ON TABLE sys_role IS '角色管理'; +COMMENT ON COLUMN sys_role.id IS 'id'; +COMMENT ON COLUMN sys_role.name IS '角色名称'; +COMMENT ON COLUMN sys_role.remark IS '备注'; +COMMENT ON COLUMN sys_role.dept_id IS '部门ID'; +COMMENT ON COLUMN sys_role.creator IS '创建者'; +COMMENT ON COLUMN sys_role.create_date IS '创建时间'; +COMMENT ON COLUMN sys_role.updater IS '更新者'; +COMMENT ON COLUMN sys_role.update_date IS '更新时间'; + + +create table sys_menu +( + id int8 NOT NULL, + pid int8, + name varchar(200), + url varchar(200), + permissions varchar(500), + menu_type int, + icon varchar(50), + sort int, + creator int8, + create_date timestamp, + updater int8, + update_date timestamp, + primary key (id) +); + +CREATE INDEX idx_sys_menu_pid on sys_menu(pid); +CREATE INDEX idx_sys_menu_sort on sys_menu(sort); + +COMMENT ON TABLE sys_menu IS '菜单管理'; +COMMENT ON COLUMN sys_menu.id IS 'id'; +COMMENT ON COLUMN sys_menu.pid IS '上级ID,一级菜单为0'; +COMMENT ON COLUMN sys_menu.name IS '名称'; +COMMENT ON COLUMN sys_menu.url IS '菜单URL'; +COMMENT ON COLUMN sys_menu.permissions IS '授权(多个用逗号分隔,如:sys:user:list,sys:user:save)'; +COMMENT ON COLUMN sys_menu.menu_type IS '类型 0:菜单 1:按钮'; +COMMENT ON COLUMN sys_menu.icon IS '菜单图标'; +COMMENT ON COLUMN sys_menu.sort IS '排序'; +COMMENT ON COLUMN sys_menu.creator IS '创建者'; +COMMENT ON COLUMN sys_menu.create_date IS '创建时间'; +COMMENT ON COLUMN sys_menu.updater IS '更新者'; +COMMENT ON COLUMN sys_menu.update_date IS '更新时间'; + + +create table sys_role_user +( + id int8 NOT NULL, + role_id int8, + user_id int8, + creator int8, + create_date timestamp, + primary key (id) +); + +CREATE INDEX idx_sys_role_user_role_id on sys_role_user(role_id); +CREATE INDEX idx_sys_role_user_user_id on sys_role_user(user_id); + +COMMENT ON TABLE sys_role_user IS '角色用户关系'; +COMMENT ON COLUMN sys_role_user.role_id IS '角色ID'; +COMMENT ON COLUMN sys_role_user.user_id IS '用户ID'; +COMMENT ON COLUMN sys_role_user.creator IS '创建者'; +COMMENT ON COLUMN sys_role_user.create_date IS '创建时间'; + + +create table sys_role_menu +( + id int8 NOT NULL, + role_id int8, + menu_id int8, + creator int8, + create_date timestamp, + primary key (id) +); + +CREATE INDEX idx_sys_role_menu_role_id on sys_role_menu(role_id); +CREATE INDEX idx_sys_role_menu_menu_id on sys_role_menu(menu_id); + +COMMENT ON TABLE sys_role_menu IS '角色菜单关系'; +COMMENT ON COLUMN sys_role_menu.role_id IS '角色ID'; +COMMENT ON COLUMN sys_role_menu.menu_id IS '菜单ID'; +COMMENT ON COLUMN sys_role_menu.creator IS '创建者'; +COMMENT ON COLUMN sys_role_menu.create_date IS '创建时间'; + + +create table sys_role_data_scope +( + id int8 NOT NULL, + role_id int8, + dept_id int8, + creator int8, + create_date timestamp, + primary key (id) +); +CREATE INDEX idx_data_scope_role_id on sys_role_data_scope(role_id); + +COMMENT ON TABLE sys_role_data_scope IS '角色数据权限'; +COMMENT ON COLUMN sys_role_data_scope.role_id IS '角色ID'; +COMMENT ON COLUMN sys_role_data_scope.dept_id IS '部门ID'; +COMMENT ON COLUMN sys_role_data_scope.creator IS '创建者'; +COMMENT ON COLUMN sys_role_data_scope.create_date IS '创建时间'; + + +create table sys_params +( + id int8 NOT NULL, + param_code varchar(32), + param_value varchar(2000), + param_type int DEFAULT 1 NOT NULL, + remark varchar(200), + creator int8, + create_date timestamp, + updater int8, + update_date timestamp, + primary key (id) +); +CREATE UNIQUE INDEX uk_sys_params_param_code on sys_params(param_code); +CREATE INDEX idx_sys_params_create_date on sys_params(create_date); + +COMMENT ON TABLE sys_params IS '参数管理'; +COMMENT ON COLUMN sys_params.param_code IS '参数编码'; +COMMENT ON COLUMN sys_params.param_value IS '参数值'; +COMMENT ON COLUMN sys_params.param_type IS '类型 0:系统参数 1:非系统参数'; +COMMENT ON COLUMN sys_params.remark IS '备注'; +COMMENT ON COLUMN sys_params.creator IS '创建者'; +COMMENT ON COLUMN sys_params.create_date IS '创建时间'; +COMMENT ON COLUMN sys_params.updater IS '更新者'; +COMMENT ON COLUMN sys_params.update_date IS '更新时间'; + + +create table sys_dict_type +( + id int8 NOT NULL, + dict_type varchar(100), + dict_name varchar(255), + remark varchar(255), + sort int, + creator int8, + create_date timestamp, + updater int8, + update_date timestamp, + primary key (id) +); +CREATE UNIQUE INDEX uk_sys_dict_type_dict_type on sys_dict_type(dict_type); + +COMMENT ON TABLE sys_dict_type IS '字典类型'; +COMMENT ON COLUMN sys_dict_type.id IS 'id'; +COMMENT ON COLUMN sys_dict_type.dict_type IS '字典类型'; +COMMENT ON COLUMN sys_dict_type.dict_name IS '字典名称'; +COMMENT ON COLUMN sys_dict_type.remark IS '备注'; +COMMENT ON COLUMN sys_dict_type.sort IS '排序'; +COMMENT ON COLUMN sys_dict_type.creator IS '创建者'; +COMMENT ON COLUMN sys_dict_type.create_date IS '创建时间'; +COMMENT ON COLUMN sys_dict_type.updater IS '更新者'; +COMMENT ON COLUMN sys_dict_type.update_date IS '更新时间'; + +create table sys_dict_data +( + id int8 NOT NULL, + dict_type_id int8 NOT NULL, + dict_label varchar(255), + dict_value varchar(255), + remark varchar(255), + sort int, + creator int8, + create_date timestamp, + updater int8, + update_date timestamp, + primary key (id) +); +CREATE INDEX idx_sys_dict_data_sort on sys_dict_data(sort); +CREATE UNIQUE INDEX uk_dict_type_value on sys_dict_data(dict_type_id, dict_value); + +COMMENT ON TABLE sys_dict_data IS '字典数据'; +COMMENT ON COLUMN sys_dict_data.id IS 'id'; +COMMENT ON COLUMN sys_dict_data.dict_type_id IS '字典类型ID'; +COMMENT ON COLUMN sys_dict_data.dict_label IS '字典标签'; +COMMENT ON COLUMN sys_dict_data.dict_value IS '字典值'; +COMMENT ON COLUMN sys_dict_data.remark IS '备注'; +COMMENT ON COLUMN sys_dict_data.sort IS '排序'; +COMMENT ON COLUMN sys_dict_data.creator IS '创建者'; +COMMENT ON COLUMN sys_dict_data.create_date IS '创建时间'; +COMMENT ON COLUMN sys_dict_data.updater IS '更新者'; +COMMENT ON COLUMN sys_dict_data.update_date IS '更新时间'; + + +create table sys_log_login +( + id int8 NOT NULL, + operation int, + status int, + user_agent varchar(500), + ip varchar(32), + creator_name varchar(50), + creator int8, + create_date timestamp, + primary key (id) +); +CREATE INDEX idx_login_status on sys_log_login(status); +CREATE INDEX idx_login_create_date on sys_log_login(create_date); + +COMMENT ON TABLE sys_log_login IS '登录日志'; +COMMENT ON COLUMN sys_log_login.id IS 'id'; +COMMENT ON COLUMN sys_log_login.operation IS '用户操作 0:用户登录 1:用户退出'; +COMMENT ON COLUMN sys_log_login.status IS '状态 0:失败 1:成功 2:账号已锁定'; +COMMENT ON COLUMN sys_log_login.user_agent IS '用户代理'; +COMMENT ON COLUMN sys_log_login.ip IS '操作IP'; +COMMENT ON COLUMN sys_log_login.creator_name IS '用户名'; +COMMENT ON COLUMN sys_log_login.creator IS '创建者'; +COMMENT ON COLUMN sys_log_login.create_date IS '创建时间'; + + +create table sys_log_operation +( + id int8 NOT NULL, + operation varchar(50), + request_uri varchar(200), + request_method varchar(20), + request_params text, + request_time int, + user_agent varchar(500), + ip varchar(32), + status int, + creator_name varchar(50), + creator int8, + create_date timestamp, + primary key (id) +); +CREATE INDEX idx_operation_create_date on sys_log_operation(create_date); + +COMMENT ON TABLE sys_log_operation IS '操作日志'; +COMMENT ON COLUMN sys_log_operation.id IS 'id'; +COMMENT ON COLUMN sys_log_operation.operation IS '用户操作'; +COMMENT ON COLUMN sys_log_operation.request_uri IS '请求URI'; +COMMENT ON COLUMN sys_log_operation.request_method IS '请求方式'; +COMMENT ON COLUMN sys_log_operation.request_params IS '请求参数'; +COMMENT ON COLUMN sys_log_operation.request_time IS '请求时长(毫秒)'; +COMMENT ON COLUMN sys_log_operation.user_agent IS '用户代理'; +COMMENT ON COLUMN sys_log_operation.ip IS '操作IP'; +COMMENT ON COLUMN sys_log_operation.status IS '状态 0:失败 1:成功'; +COMMENT ON COLUMN sys_log_operation.creator_name IS '用户名'; +COMMENT ON COLUMN sys_log_operation.creator IS '创建者'; +COMMENT ON COLUMN sys_log_operation.create_date IS '创建时间'; + + +create table sys_log_error +( + id int8 NOT NULL, + request_uri varchar(200), + request_method varchar(20), + request_params text, + user_agent varchar(500), + ip varchar(32), + error_info text, + creator int8, + create_date timestamp, + primary key (id) +); +CREATE INDEX idx_error_create_date on sys_log_error(create_date); + +COMMENT ON TABLE sys_log_error IS '异常日志'; +COMMENT ON COLUMN sys_log_error.id IS 'id'; +COMMENT ON COLUMN sys_log_error.request_uri IS '请求URI'; +COMMENT ON COLUMN sys_log_error.request_method IS '请求方式'; +COMMENT ON COLUMN sys_log_error.request_params IS '请求参数'; +COMMENT ON COLUMN sys_log_error.user_agent IS '用户代理'; +COMMENT ON COLUMN sys_log_error.ip IS '操作IP'; +COMMENT ON COLUMN sys_log_error.error_info IS '异常信息'; +COMMENT ON COLUMN sys_log_error.creator IS '创建者'; +COMMENT ON COLUMN sys_log_error.create_date IS '创建时间'; + + + +CREATE TABLE sys_oss ( + id int8 NOT NULL, + url varchar(200), + creator int8, + create_date timestamp, + PRIMARY KEY (id) +); +CREATE INDEX idx_sys_oss_create_date on sys_oss(create_date); + +COMMENT ON TABLE sys_oss IS '文件上传'; +COMMENT ON COLUMN sys_oss.id IS 'id'; +COMMENT ON COLUMN sys_oss.url IS 'URL地址'; +COMMENT ON COLUMN sys_oss.creator IS '创建者'; +COMMENT ON COLUMN sys_oss.create_date IS '创建时间'; + + +CREATE TABLE schedule_job ( + id int8 NOT NULL, + bean_name varchar(200), + params varchar(2000), + cron_expression varchar(100), + status int, + remark varchar(255), + creator int8, + create_date timestamp, + updater int8, + update_date timestamp, + PRIMARY KEY (id) +); + +CREATE INDEX idx_schedule_job_create_date on schedule_job(create_date); + +COMMENT ON TABLE schedule_job IS '定时任务'; +COMMENT ON COLUMN schedule_job.id IS 'id'; +COMMENT ON COLUMN schedule_job.bean_name IS 'spring bean名称'; +COMMENT ON COLUMN schedule_job.params IS '参数'; +COMMENT ON COLUMN schedule_job.cron_expression IS 'cron表达式'; +COMMENT ON COLUMN schedule_job.status IS '任务状态 0:暂停 1:正常'; +COMMENT ON COLUMN schedule_job.remark IS '备注'; +COMMENT ON COLUMN schedule_job.creator IS '创建者'; +COMMENT ON COLUMN schedule_job.create_date IS '创建时间'; +COMMENT ON COLUMN schedule_job.updater IS '更新者'; +COMMENT ON COLUMN schedule_job.update_date IS '更新时间'; + + +CREATE TABLE schedule_job_log ( + id int8 NOT NULL, + job_id int8 NOT NULL, + bean_name varchar(200), + params varchar(2000), + status int, + error varchar(2000), + times int, + create_date timestamp, + PRIMARY KEY (id) +); + +CREATE INDEX idx_job_log_job_id on schedule_job_log(job_id); +CREATE INDEX idx_job_log_create_date on schedule_job_log(create_date); + +COMMENT ON TABLE schedule_job_log IS '定时任务日志'; +COMMENT ON COLUMN schedule_job_log.id IS 'id'; +COMMENT ON COLUMN schedule_job_log.bean_name IS 'spring bean名称'; +COMMENT ON COLUMN schedule_job_log.params IS '参数'; +COMMENT ON COLUMN schedule_job_log.status IS '任务状态 0:失败 1:成功'; +COMMENT ON COLUMN schedule_job_log.error IS '失败信息'; +COMMENT ON COLUMN schedule_job_log.times IS '耗时(单位:毫秒)'; +COMMENT ON COLUMN schedule_job.create_date IS '创建时间'; + + +CREATE TABLE sys_user_token ( + id int8 NOT NULL, + user_id int8, + token varchar(100), + expire_date timestamp, + update_date timestamp, + create_date timestamp, + PRIMARY KEY (id) +); + +CREATE UNIQUE INDEX uk_sys_user_token_user_id on sys_user_token(user_id); +CREATE UNIQUE INDEX uk_sys_user_token on sys_user_token(token); + +COMMENT ON TABLE sys_user_token IS '系统用户Token'; +COMMENT ON COLUMN sys_user_token.id IS 'id'; +COMMENT ON COLUMN sys_user_token.user_id IS '用户id'; +COMMENT ON COLUMN sys_user_token.token IS '用户token'; +COMMENT ON COLUMN sys_user_token.expire_date IS '过期时间'; +COMMENT ON COLUMN sys_user_token.update_date IS '更新时间'; +COMMENT ON COLUMN sys_user_token.create_date IS '创建时间'; + + + +-- 初始数据 +INSERT INTO sys_user(id, username, password, real_name, gender, email, mobile, status, dept_id, super_admin, creator, create_date, updater, update_date) VALUES (1067246875800000001, 'admin', '$2a$10$012Kx2ba5jzqr9gLlG4MX.bnQJTD9UWqF57XDo2N3.fPtLne02u/m', '管理员', 0, 'root@weather.io', '13612345678', 1, null, 1, 1067246875800000001, now(), 1067246875800000001, now()); + +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000002, 0, '权限管理', NULL, NULL, 0, 'icon-safetycertificate', 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000003, 1067246875800000055, '新增', NULL, 'sys:user:save,sys:dept:list,sys:role:list', 1, NULL, 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000004, 1067246875800000055, '修改', NULL, 'sys:user:update,sys:dept:list,sys:role:list', 1, NULL, 2, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000005, 1067246875800000055, '删除', NULL, 'sys:user:delete', 1, NULL, 3, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000006, 1067246875800000055, '导出', NULL, 'sys:user:export', 1, NULL, 4, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000007, 1067246875800000002, '角色管理', 'sys/role', NULL, 0, 'icon-team', 2, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000008, 1067246875800000007, '查看', NULL, 'sys:role:page,sys:role:info', 1, NULL, 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000009, 1067246875800000007, '新增', NULL, 'sys:role:save,sys:menu:select,sys:dept:list', 1, NULL, 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000010, 1067246875800000007, '修改', NULL, 'sys:role:update,sys:menu:select,sys:dept:list', 1, NULL, 2, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000011, 1067246875800000007, '删除', NULL, 'sys:role:delete', 1, NULL, 3, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000012, 1067246875800000002, '部门管理', 'sys/dept', NULL, 0, 'icon-apartment', 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000014, 1067246875800000012, '查看', NULL, 'sys:dept:list,sys:dept:info', 1, NULL, 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000015, 1067246875800000012, '新增', NULL, 'sys:dept:save', 1, NULL, 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000016, 1067246875800000012, '修改', NULL, 'sys:dept:update', 1, NULL, 2, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000017, 1067246875800000012, '删除', NULL, 'sys:dept:delete', 1, NULL, 3, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000025, 1067246875800000035, '菜单管理', 'sys/menu', NULL, 0, 'icon-unorderedlist', 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000026, 1067246875800000025, '查看', NULL, 'sys:menu:list,sys:menu:info', 1, NULL, 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000027, 1067246875800000025, '新增', NULL, 'sys:menu:save', 1, NULL, 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000028, 1067246875800000025, '修改', NULL, 'sys:menu:update', 1, NULL, 2, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000029, 1067246875800000025, '删除', NULL, 'sys:menu:delete', 1, NULL, 3, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000030, 1067246875800000035, '定时任务', 'job/schedule', NULL, 0, 'icon-dashboard', 3, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000031, 1067246875800000030, '查看', NULL, 'sys:schedule:page,sys:schedule:info', 1, NULL, 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000032, 1067246875800000030, '新增', NULL, 'sys:schedule:save', 1, NULL, 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000033, 1067246875800000030, '修改', NULL, 'sys:schedule:update', 1, NULL, 2, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000034, 1067246875800000030, '删除', NULL, 'sys:schedule:delete', 1, NULL, 3, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000035, 0, '系统设置', NULL, NULL, 0, 'icon-setting', 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000036, 1067246875800000030, '暂停', NULL, 'sys:schedule:pause', 1, NULL, 4, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000037, 1067246875800000030, '恢复', NULL, 'sys:schedule:resume', 1, NULL, 5, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000038, 1067246875800000030, '立即执行', NULL, 'sys:schedule:run', 1, NULL, 6, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000039, 1067246875800000030, '日志列表', NULL, 'sys:schedule:log', 1, NULL, 7, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000040, 1067246875800000035, '参数管理', 'sys/params', '', 0, 'icon-fileprotect', 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000041, 1067246875800000035, '字典管理', 'sys/dict-type', NULL, 0, 'icon-golden-fill', 2, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000042, 1067246875800000041, '查看', NULL, 'sys:dict:page,sys:dict:info', 1, NULL, 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000043, 1067246875800000041, '新增', NULL, 'sys:dict:save', 1, NULL, 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000044, 1067246875800000041, '修改', NULL, 'sys:dict:update', 1, NULL, 2, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000045, 1067246875800000041, '删除', NULL, 'sys:dict:delete', 1, NULL, 3, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000046, 0, '日志管理', NULL, NULL, 0, 'icon-container', 2, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000047, 1067246875800000035, '文件上传', 'oss/oss', 'sys:oss:all', 0, 'icon-upload', 4, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000048, 1067246875800000046, '登录日志', 'sys/log-login', 'sys:log:login', 0, 'icon-filedone', 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000049, 1067246875800000046, '操作日志', 'sys/log-operation', 'sys:log:operation', 0, 'icon-solution', 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000050, 1067246875800000046, '异常日志', 'sys/log-error', 'sys:log:error', 0, 'icon-file-exception', 2, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000051, 1067246875800000053, 'SQL监控', '{{ApiUrl}}/druid/sql.html', NULL, 0, 'icon-database', 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000053, 0, '系统监控', NULL, NULL, 0, 'icon-desktop', 3, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000055, 1067246875800000002, '用户管理', 'sys/user', NULL, 0, 'icon-user', 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000056, 1067246875800000055, '查看', NULL, 'sys:user:page,sys:user:info', 1, NULL, 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000057, 1067246875800000040, '新增', NULL, 'sys:params:save', 1, NULL, 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000058, 1067246875800000040, '导出', NULL, 'sys:params:export', 1, NULL, 4, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000059, 1067246875800000040, '查看', '', 'sys:params:page,sys:params:info', 1, NULL, 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000060, 1067246875800000040, '修改', NULL, 'sys:params:update', 1, NULL, 2, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000061, 1067246875800000040, '删除', '', 'sys:params:delete', 1, '', 3, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1156748733921165314, 1067246875800000053, '接口文档', '{{ApiUrl}}/doc.html', '', 0, 'icon-file-word', 1, 1067246875800000001, now(), 1067246875800000001, now()); + + +INSERT INTO sys_dept(id, pid, pids, name, sort, creator, create_date, updater, update_date) VALUES (1067246875800000062, 1067246875800000063, '1067246875800000066,1067246875800000063', '技术部', 2, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_dept(id, pid, pids, name, sort, creator, create_date, updater, update_date) VALUES (1067246875800000063, 1067246875800000066, '1067246875800000066', '长沙分公司', 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_dept(id, pid, pids, name, sort, creator, create_date, updater, update_date) VALUES (1067246875800000064, 1067246875800000066, '1067246875800000066', '上海分公司', 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_dept(id, pid, pids, name, sort, creator, create_date, updater, update_date) VALUES (1067246875800000065, 1067246875800000064, '1067246875800000066,1067246875800000064', '市场部', 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_dept(id, pid, pids, name, sort, creator, create_date, updater, update_date) VALUES (1067246875800000066, 0, '0', '人人开源集团', 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_dept(id, pid, pids, name, sort, creator, create_date, updater, update_date) VALUES (1067246875800000067, 1067246875800000064, '1067246875800000066,1067246875800000064', '销售部', 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_dept(id, pid, pids, name, sort, creator, create_date, updater, update_date) VALUES (1067246875800000068, 1067246875800000063, '1067246875800000066,1067246875800000063', '产品部', 1, 1067246875800000001, now(), 1067246875800000001, now()); + +INSERT INTO sys_dict_type(id, dict_type, dict_name, remark, sort, creator, create_date, updater, update_date) VALUES (1160061077912858625, 'gender', '性别', '', 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_dict_data(id, dict_type_id, dict_label, dict_value, remark, sort, creator, create_date, updater, update_date) VALUES (1160061112075464705, 1160061077912858625, '男', '0', '', 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_dict_data(id, dict_type_id, dict_label, dict_value, remark, sort, creator, create_date, updater, update_date) VALUES (1160061146967879681, 1160061077912858625, '女', '1', '', 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_dict_data(id, dict_type_id, dict_label, dict_value, remark, sort, creator, create_date, updater, update_date) VALUES (1160061190127267841, 1160061077912858625, '保密', '2', '', 2, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_dict_type(id, dict_type, dict_name, remark, sort, creator, create_date, updater, update_date) VALUES (1225813644059140097, 'notice_type', '站内通知-类型', '', 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_dict_data(id, dict_type_id, dict_label, dict_value, remark, sort, creator, create_date, updater, update_date) VALUES (1225814069634195457, 1225813644059140097, '公告', '0', '', 0, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_dict_data(id, dict_type_id, dict_label, dict_value, remark, sort, creator, create_date, updater, update_date) VALUES (1225814107559092225, 1225813644059140097, '会议', '1', '', 1, 1067246875800000001, now(), 1067246875800000001, now()); +INSERT INTO sys_dict_data(id, dict_type_id, dict_label, dict_value, remark, sort, creator, create_date, updater, update_date) VALUES (1225814271879340034, 1225813644059140097, '其他', '2', '', 2, 1067246875800000001, now(), 1067246875800000001, now()); + +INSERT INTO sys_params(id, param_code, param_value, param_type, remark, creator, create_date, updater, update_date) VALUES (1067246875800000073, 'CLOUD_STORAGE_CONFIG_KEY', '{"type":1,"qiniuDomain":"http://test.oss.weather.io","qiniuPrefix":"upload","qiniuAccessKey":"NrgMfABZxWLo5B-YYSjoE8-AZ1EISdi1Z3ubLOeZ","qiniuSecretKey":"uIwJHevMRWU0VLxFvgy0tAcOdGqasdtVlJkdy6vV","qiniuBucketName":"weather-oss","aliyunDomain":"","aliyunPrefix":"","aliyunEndPoint":"","aliyunAccessKeyId":"","aliyunAccessKeySecret":"","aliyunBucketName":"","qcloudDomain":"","qcloudPrefix":"","qcloudSecretId":"","qcloudSecretKey":"","qcloudBucketName":""}', '0', '云存储配置信息', 1067246875800000001, now(), 1067246875800000001, now()); + +INSERT INTO schedule_job (id, bean_name, params, cron_expression, status, remark, creator, create_date, updater, update_date) VALUES (1067246875800000076, 'testTask', 'weather', '0 0/30 * * * ?', 0, '有参测试,多个参数使用json', 1067246875800000001, now(), 1067246875800000001, now()); + + + +-- quartz自带表结构 +CREATE TABLE qrtz_job_details +( + SCHED_NAME VARCHAR(120) NOT NULL, + JOB_NAME VARCHAR(200) NOT NULL, + JOB_GROUP VARCHAR(200) NOT NULL, + DESCRIPTION VARCHAR(250) NULL, + JOB_CLASS_NAME VARCHAR(250) NOT NULL, + IS_DURABLE BOOL NOT NULL, + IS_NONCONCURRENT BOOL NOT NULL, + IS_UPDATE_DATA BOOL NOT NULL, + REQUESTS_RECOVERY BOOL NOT NULL, + JOB_DATA BYTEA NULL, + PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP) +); + +CREATE TABLE qrtz_triggers +( + SCHED_NAME VARCHAR(120) NOT NULL, + TRIGGER_NAME VARCHAR(200) NOT NULL, + TRIGGER_GROUP VARCHAR(200) NOT NULL, + JOB_NAME VARCHAR(200) NOT NULL, + JOB_GROUP VARCHAR(200) NOT NULL, + DESCRIPTION VARCHAR(250) NULL, + NEXT_FIRE_TIME BIGINT NULL, + PREV_FIRE_TIME BIGINT NULL, + PRIORITY INTEGER NULL, + TRIGGER_STATE VARCHAR(16) NOT NULL, + TRIGGER_TYPE VARCHAR(8) NOT NULL, + START_TIME BIGINT NOT NULL, + END_TIME BIGINT NULL, + CALENDAR_NAME VARCHAR(200) NULL, + MISFIRE_INSTR SMALLINT NULL, + JOB_DATA BYTEA NULL, + PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), + FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP) + REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP) +); + +CREATE TABLE qrtz_simple_triggers +( + SCHED_NAME VARCHAR(120) NOT NULL, + TRIGGER_NAME VARCHAR(200) NOT NULL, + TRIGGER_GROUP VARCHAR(200) NOT NULL, + REPEAT_COUNT BIGINT NOT NULL, + REPEAT_INTERVAL BIGINT NOT NULL, + TIMES_TRIGGERED BIGINT NOT NULL, + PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), + FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) + REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) +); + +CREATE TABLE qrtz_cron_triggers +( + SCHED_NAME VARCHAR(120) NOT NULL, + TRIGGER_NAME VARCHAR(200) NOT NULL, + TRIGGER_GROUP VARCHAR(200) NOT NULL, + CRON_EXPRESSION VARCHAR(120) NOT NULL, + TIME_ZONE_ID VARCHAR(80), + PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), + FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) + REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) +); + +CREATE TABLE qrtz_simprop_triggers +( + SCHED_NAME VARCHAR(120) NOT NULL, + TRIGGER_NAME VARCHAR(200) NOT NULL, + TRIGGER_GROUP VARCHAR(200) NOT NULL, + STR_PROP_1 VARCHAR(512) NULL, + STR_PROP_2 VARCHAR(512) NULL, + STR_PROP_3 VARCHAR(512) NULL, + INT_PROP_1 INT NULL, + INT_PROP_2 INT NULL, + LONG_PROP_1 BIGINT NULL, + LONG_PROP_2 BIGINT NULL, + DEC_PROP_1 NUMERIC(13,4) NULL, + DEC_PROP_2 NUMERIC(13,4) NULL, + BOOL_PROP_1 BOOL NULL, + BOOL_PROP_2 BOOL NULL, + PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), + FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) + REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) +); + +CREATE TABLE qrtz_blob_triggers +( + SCHED_NAME VARCHAR(120) NOT NULL, + TRIGGER_NAME VARCHAR(200) NOT NULL, + TRIGGER_GROUP VARCHAR(200) NOT NULL, + BLOB_DATA BYTEA NULL, + PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), + FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) + REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) +); + +CREATE TABLE qrtz_calendars +( + SCHED_NAME VARCHAR(120) NOT NULL, + CALENDAR_NAME VARCHAR(200) NOT NULL, + CALENDAR BYTEA NOT NULL, + PRIMARY KEY (SCHED_NAME,CALENDAR_NAME) +); + + +CREATE TABLE qrtz_paused_trigger_grps +( + SCHED_NAME VARCHAR(120) NOT NULL, + TRIGGER_GROUP VARCHAR(200) NOT NULL, + PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP) +); + +CREATE TABLE qrtz_fired_triggers +( + SCHED_NAME VARCHAR(120) NOT NULL, + ENTRY_ID VARCHAR(95) NOT NULL, + TRIGGER_NAME VARCHAR(200) NOT NULL, + TRIGGER_GROUP VARCHAR(200) NOT NULL, + INSTANCE_NAME VARCHAR(200) NOT NULL, + FIRED_TIME BIGINT NOT NULL, + SCHED_TIME BIGINT NOT NULL, + PRIORITY INTEGER NOT NULL, + STATE VARCHAR(16) NOT NULL, + JOB_NAME VARCHAR(200) NULL, + JOB_GROUP VARCHAR(200) NULL, + IS_NONCONCURRENT BOOL NULL, + REQUESTS_RECOVERY BOOL NULL, + PRIMARY KEY (SCHED_NAME,ENTRY_ID) +); + +CREATE TABLE qrtz_scheduler_state +( + SCHED_NAME VARCHAR(120) NOT NULL, + INSTANCE_NAME VARCHAR(200) NOT NULL, + LAST_CHECKIN_TIME BIGINT NOT NULL, + CHECKIN_INTERVAL BIGINT NOT NULL, + PRIMARY KEY (SCHED_NAME,INSTANCE_NAME) +); + +CREATE TABLE qrtz_locks +( + SCHED_NAME VARCHAR(120) NOT NULL, + LOCK_NAME VARCHAR(40) NOT NULL, + PRIMARY KEY (SCHED_NAME,LOCK_NAME) +); + +create index idx_qrtz_j_req_recovery on qrtz_job_details(SCHED_NAME,REQUESTS_RECOVERY); +create index idx_qrtz_j_grp on qrtz_job_details(SCHED_NAME,JOB_GROUP); + +create index idx_qrtz_t_j on qrtz_triggers(SCHED_NAME,JOB_NAME,JOB_GROUP); +create index idx_qrtz_t_jg on qrtz_triggers(SCHED_NAME,JOB_GROUP); +create index idx_qrtz_t_c on qrtz_triggers(SCHED_NAME,CALENDAR_NAME); +create index idx_qrtz_t_g on qrtz_triggers(SCHED_NAME,TRIGGER_GROUP); +create index idx_qrtz_t_state on qrtz_triggers(SCHED_NAME,TRIGGER_STATE); +create index idx_qrtz_t_n_state on qrtz_triggers(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE); +create index idx_qrtz_t_n_g_state on qrtz_triggers(SCHED_NAME,TRIGGER_GROUP,TRIGGER_STATE); +create index idx_qrtz_t_next_fire_time on qrtz_triggers(SCHED_NAME,NEXT_FIRE_TIME); +create index idx_qrtz_t_nft_st on qrtz_triggers(SCHED_NAME,TRIGGER_STATE,NEXT_FIRE_TIME); +create index idx_qrtz_t_nft_misfire on qrtz_triggers(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME); +create index idx_qrtz_t_nft_st_misfire on qrtz_triggers(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_STATE); +create index idx_qrtz_t_nft_st_misfire_grp on qrtz_triggers(SCHED_NAME,MISFIRE_INSTR,NEXT_FIRE_TIME,TRIGGER_GROUP,TRIGGER_STATE); + +create index idx_qrtz_ft_trig_inst_name on qrtz_fired_triggers(SCHED_NAME,INSTANCE_NAME); +create index idx_qrtz_ft_inst_job_req_rcvry on qrtz_fired_triggers(SCHED_NAME,INSTANCE_NAME,REQUESTS_RECOVERY); +create index idx_qrtz_ft_j_g on qrtz_fired_triggers(SCHED_NAME,JOB_NAME,JOB_GROUP); +create index idx_qrtz_ft_jg on qrtz_fired_triggers(SCHED_NAME,JOB_GROUP); +create index idx_qrtz_ft_t_g on qrtz_fired_triggers(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP); +create index idx_qrtz_ft_tg on qrtz_fired_triggers(SCHED_NAME,TRIGGER_GROUP); + + +commit; diff --git a/system-admin/db/sqlserver.sql b/system-admin/db/sqlserver.sql new file mode 100644 index 0000000..ad77565 --- /dev/null +++ b/system-admin/db/sqlserver.sql @@ -0,0 +1,674 @@ +CREATE TABLE sys_user ( + id bigint NOT NULL, + username varchar(50) NOT NULL, + password varchar(100), + real_name varchar(50), + head_url varchar(200), + gender int, + email varchar(100), + mobile varchar(100), + dept_id bigint, + super_admin int, + status int, + creator bigint, + create_date datetime, + updater bigint, + update_date datetime, + primary key (id) +); + +CREATE UNIQUE INDEX uk_sys_user_username on sys_user(username); +CREATE INDEX idx_sys_user_create_date on sys_user(create_date); + + +CREATE TABLE sys_dept ( + id bigint NOT NULL, + pid bigint, + pids varchar(500), + name varchar(50), + sort int, + creator bigint, + create_date datetime, + updater bigint, + update_date datetime, + primary key (id) +); +CREATE INDEX idx_sys_dept_pid on sys_dept(pid); +CREATE INDEX idx_sys_dept_idx_sort on sys_dept(sort); + + +create table sys_role +( + id bigint NOT NULL, + name varchar(50), + remark varchar(100), + dept_id bigint, + creator bigint, + create_date datetime, + updater bigint, + update_date datetime, + primary key (id) +); + +CREATE INDEX idx_sys_role_dept_id on sys_role(dept_id); + + +create table sys_menu +( + id bigint NOT NULL, + pid bigint, + name varchar(200), + url varchar(200), + permissions varchar(500), + menu_type int, + icon varchar(50), + sort int, + creator bigint, + create_date datetime, + updater bigint, + update_date datetime, + primary key (id) +); + +CREATE INDEX idx_sys_menu_pid on sys_menu(pid); +CREATE INDEX idx_sys_menu_sort on sys_menu(sort); + + +create table sys_role_user +( + id bigint NOT NULL, + role_id bigint, + user_id bigint, + creator bigint, + create_date datetime, + primary key (id) +); + +CREATE INDEX idx_sys_role_user_role_id on sys_role_user(role_id); +CREATE INDEX idx_sys_role_user_user_id on sys_role_user(user_id); + + +create table sys_role_menu +( + id bigint NOT NULL, + role_id bigint, + menu_id bigint, + creator bigint, + create_date datetime, + primary key (id) +); + +CREATE INDEX idx_sys_role_menu_role_id on sys_role_menu(role_id); +CREATE INDEX idx_sys_role_menu_menu_id on sys_role_menu(menu_id); + + +create table sys_role_data_scope +( + id bigint NOT NULL, + role_id bigint, + dept_id bigint, + creator bigint, + create_date datetime, + primary key (id) +); +CREATE INDEX idx_data_scope_role_id on sys_role_data_scope(role_id); + + +create table sys_params +( + id bigint NOT NULL, + param_code varchar(32), + param_value varchar(2000), + param_type int DEFAULT 1 NOT NULL, + remark varchar(200), + creator bigint, + create_date datetime, + updater bigint, + update_date datetime, + primary key (id) +); +CREATE UNIQUE INDEX uk_sys_params_param_code on sys_params(param_code); +CREATE INDEX idx_sys_params_create_date on sys_params(create_date); + + +create table sys_dict_type +( + id bigint NOT NULL, + dict_type varchar(100), + dict_name varchar(255), + remark varchar(255), + sort int, + creator bigint, + create_date datetime, + updater bigint, + update_date datetime, + primary key (id) +); +CREATE UNIQUE INDEX uk_sys_dict_type_dict_type on sys_dict_type(dict_type); + + +create table sys_dict_data +( + id bigint NOT NULL, + dict_type_id bigint NOT NULL, + dict_label varchar(255), + dict_value varchar(255), + remark varchar(255), + sort int, + creator bigint, + create_date datetime, + updater bigint, + update_date datetime, + primary key (id) +); +CREATE INDEX idx_sys_dict_data_sort on sys_dict_data(sort); +CREATE UNIQUE INDEX uk_dict_type_value on sys_dict_data(dict_type_id, dict_value); + + +create table sys_log_login +( + id bigint NOT NULL, + operation int, + status int, + user_agent varchar(500), + ip varchar(32), + creator_name varchar(50), + creator bigint, + create_date datetime, + primary key (id) +); +CREATE INDEX idx_login_status on sys_log_login(status); +CREATE INDEX idx_login_create_date on sys_log_login(create_date); + + +create table sys_log_operation +( + id bigint NOT NULL, + operation varchar(50), + request_uri varchar(200), + request_method varchar(20), + request_params text, + request_time int, + user_agent varchar(500), + ip varchar(32), + status int, + creator_name varchar(50), + creator bigint, + create_date datetime, + primary key (id) +); +CREATE INDEX idx_operation_create_date on sys_log_operation(create_date); + + +create table sys_log_error +( + id bigint NOT NULL, + request_uri varchar(200), + request_method varchar(20), + request_params text, + user_agent varchar(500), + ip varchar(32), + error_info text, + creator bigint, + create_date datetime, + primary key (id) +); +CREATE INDEX idx_error_create_date on sys_log_error(create_date); + + +CREATE TABLE sys_oss ( + id bigint NOT NULL, + url varchar(200), + creator bigint, + create_date datetime, + PRIMARY KEY (id) +); +CREATE INDEX idx_sys_oss_create_date on sys_oss(create_date); + + +CREATE TABLE schedule_job ( + id bigint NOT NULL, + bean_name varchar(200), + params varchar(2000), + cron_expression varchar(100), + status int, + remark varchar(255), + creator bigint, + create_date datetime, + updater bigint, + update_date datetime, + PRIMARY KEY (id) +); + +CREATE INDEX idx_schedule_job_create_date on schedule_job(create_date); + + +CREATE TABLE schedule_job_log ( + id bigint NOT NULL, + job_id bigint NOT NULL, + bean_name varchar(200), + params varchar(2000), + status int, + error varchar(2000), + times int, + create_date datetime, + PRIMARY KEY (id) +); + +CREATE INDEX idx_job_log_job_id on schedule_job_log(job_id); +CREATE INDEX idx_job_log_create_date on schedule_job_log(create_date); + + +CREATE TABLE sys_user_token ( + id bigint NOT NULL, + user_id bigint, + token varchar(100), + expire_date datetime, + update_date datetime, + create_date datetime, + PRIMARY KEY (id) +); + +CREATE UNIQUE INDEX uk_sys_user_token_user_id on sys_user_token(user_id); +CREATE UNIQUE INDEX uk_sys_user_token on sys_user_token(token); + + + +-- 初始数据 +INSERT INTO sys_user(id, username, password, real_name, gender, email, mobile, status, dept_id, super_admin, creator, create_date, updater, update_date) VALUES (1067246875800000001, 'admin', '$2a$10$012Kx2ba5jzqr9gLlG4MX.bnQJTD9UWqF57XDo2N3.fPtLne02u/m', '管理员', 0, 'root@weather.io', '13612345678', 1, null, 1, 1067246875800000001, getdate(), 1067246875800000001, getdate()); + +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000002, 0, '权限管理', NULL, NULL, 0, 'icon-safetycertificate', 0, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000003, 1067246875800000055, '新增', NULL, 'sys:user:save,sys:dept:list,sys:role:list', 1, NULL, 1, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000004, 1067246875800000055, '修改', NULL, 'sys:user:update,sys:dept:list,sys:role:list', 1, NULL, 2, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000005, 1067246875800000055, '删除', NULL, 'sys:user:delete', 1, NULL, 3, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000006, 1067246875800000055, '导出', NULL, 'sys:user:export', 1, NULL, 4, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000007, 1067246875800000002, '角色管理', 'sys/role', NULL, 0, 'icon-team', 2, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000008, 1067246875800000007, '查看', NULL, 'sys:role:page,sys:role:info', 1, NULL, 0, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000009, 1067246875800000007, '新增', NULL, 'sys:role:save,sys:menu:select,sys:dept:list', 1, NULL, 1, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000010, 1067246875800000007, '修改', NULL, 'sys:role:update,sys:menu:select,sys:dept:list', 1, NULL, 2, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000011, 1067246875800000007, '删除', NULL, 'sys:role:delete', 1, NULL, 3, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000012, 1067246875800000002, '部门管理', 'sys/dept', NULL, 0, 'icon-apartment', 1, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000014, 1067246875800000012, '查看', NULL, 'sys:dept:list,sys:dept:info', 1, NULL, 0, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000015, 1067246875800000012, '新增', NULL, 'sys:dept:save', 1, NULL, 1, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000016, 1067246875800000012, '修改', NULL, 'sys:dept:update', 1, NULL, 2, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000017, 1067246875800000012, '删除', NULL, 'sys:dept:delete', 1, NULL, 3, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000025, 1067246875800000035, '菜单管理', 'sys/menu', NULL, 0, 'icon-unorderedlist', 0, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000026, 1067246875800000025, '查看', NULL, 'sys:menu:list,sys:menu:info', 1, NULL, 0, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000027, 1067246875800000025, '新增', NULL, 'sys:menu:save', 1, NULL, 1, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000028, 1067246875800000025, '修改', NULL, 'sys:menu:update', 1, NULL, 2, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000029, 1067246875800000025, '删除', NULL, 'sys:menu:delete', 1, NULL, 3, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000030, 1067246875800000035, '定时任务', 'job/schedule', NULL, 0, 'icon-dashboard', 3, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000031, 1067246875800000030, '查看', NULL, 'sys:schedule:page,sys:schedule:info', 1, NULL, 0, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000032, 1067246875800000030, '新增', NULL, 'sys:schedule:save', 1, NULL, 1, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000033, 1067246875800000030, '修改', NULL, 'sys:schedule:update', 1, NULL, 2, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000034, 1067246875800000030, '删除', NULL, 'sys:schedule:delete', 1, NULL, 3, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000035, 0, '系统设置', NULL, NULL, 0, 'icon-setting', 1, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000036, 1067246875800000030, '暂停', NULL, 'sys:schedule:pause', 1, NULL, 4, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000037, 1067246875800000030, '恢复', NULL, 'sys:schedule:resume', 1, NULL, 5, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000038, 1067246875800000030, '立即执行', NULL, 'sys:schedule:run', 1, NULL, 6, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000039, 1067246875800000030, '日志列表', NULL, 'sys:schedule:log', 1, NULL, 7, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000040, 1067246875800000035, '参数管理', 'sys/params', '', 0, 'icon-fileprotect', 1, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000041, 1067246875800000035, '字典管理', 'sys/dict-type', NULL, 0, 'icon-golden-fill', 2, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000042, 1067246875800000041, '查看', NULL, 'sys:dict:page,sys:dict:info', 1, NULL, 0, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000043, 1067246875800000041, '新增', NULL, 'sys:dict:save', 1, NULL, 1, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000044, 1067246875800000041, '修改', NULL, 'sys:dict:update', 1, NULL, 2, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000045, 1067246875800000041, '删除', NULL, 'sys:dict:delete', 1, NULL, 3, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000046, 0, '日志管理', NULL, NULL, 0, 'icon-container', 2, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000047, 1067246875800000035, '文件上传', 'oss/oss', 'sys:oss:all', 0, 'icon-upload', 4, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000048, 1067246875800000046, '登录日志', 'sys/log-login', 'sys:log:login', 0, 'icon-filedone', 0, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000049, 1067246875800000046, '操作日志', 'sys/log-operation', 'sys:log:operation', 0, 'icon-solution', 1, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000050, 1067246875800000046, '异常日志', 'sys/log-error', 'sys:log:error', 0, 'icon-file-exception', 2, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000051, 1067246875800000053, 'SQL监控', '{{ApiUrl}}/druid/sql.html', NULL, 0, 'icon-database', 0, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000053, 0, '系统监控', NULL, NULL, 0, 'icon-desktop', 3, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000055, 1067246875800000002, '用户管理', 'sys/user', NULL, 0, 'icon-user', 0, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000056, 1067246875800000055, '查看', NULL, 'sys:user:page,sys:user:info', 1, NULL, 0, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000057, 1067246875800000040, '新增', NULL, 'sys:params:save', 1, NULL, 1, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000058, 1067246875800000040, '导出', NULL, 'sys:params:export', 1, NULL, 4, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000059, 1067246875800000040, '查看', '', 'sys:params:page,sys:params:info', 1, NULL, 0, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000060, 1067246875800000040, '修改', NULL, 'sys:params:update', 1, NULL, 2, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1067246875800000061, 1067246875800000040, '删除', '', 'sys:params:delete', 1, '', 3, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_menu (id, pid, name, url, permissions, menu_type, icon, sort, creator, create_date, updater, update_date) VALUES (1156748733921165314, 1067246875800000053, '接口文档', '{{ApiUrl}}/doc.html', '', 0, 'icon-file-word', 1, 1067246875800000001, getdate(), 1067246875800000001, getdate()); + + +INSERT INTO sys_dept(id, pid, pids, name, sort, creator, create_date, updater, update_date) VALUES (1067246875800000062, 1067246875800000063, '1067246875800000066,1067246875800000063', '技术部', 2, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_dept(id, pid, pids, name, sort, creator, create_date, updater, update_date) VALUES (1067246875800000063, 1067246875800000066, '1067246875800000066', '长沙分公司', 1, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_dept(id, pid, pids, name, sort, creator, create_date, updater, update_date) VALUES (1067246875800000064, 1067246875800000066, '1067246875800000066', '上海分公司', 0, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_dept(id, pid, pids, name, sort, creator, create_date, updater, update_date) VALUES (1067246875800000065, 1067246875800000064, '1067246875800000066,1067246875800000064', '市场部', 0, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_dept(id, pid, pids, name, sort, creator, create_date, updater, update_date) VALUES (1067246875800000066, 0, '0', '人人开源集团', 0, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_dept(id, pid, pids, name, sort, creator, create_date, updater, update_date) VALUES (1067246875800000067, 1067246875800000064, '1067246875800000066,1067246875800000064', '销售部', 0, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_dept(id, pid, pids, name, sort, creator, create_date, updater, update_date) VALUES (1067246875800000068, 1067246875800000063, '1067246875800000066,1067246875800000063', '产品部', 1, 1067246875800000001, getdate(), 1067246875800000001, getdate()); + +INSERT INTO sys_dict_type(id, dict_type, dict_name, remark, sort, creator, create_date, updater, update_date) VALUES (1160061077912858625, 'gender', '性别', '', 0, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_dict_data(id, dict_type_id, dict_label, dict_value, remark, sort, creator, create_date, updater, update_date) VALUES (1160061112075464705, 1160061077912858625, '男', '0', '', 0, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_dict_data(id, dict_type_id, dict_label, dict_value, remark, sort, creator, create_date, updater, update_date) VALUES (1160061146967879681, 1160061077912858625, '女', '1', '', 1, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_dict_data(id, dict_type_id, dict_label, dict_value, remark, sort, creator, create_date, updater, update_date) VALUES (1160061190127267841, 1160061077912858625, '保密', '2', '', 2, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_dict_type(id, dict_type, dict_name, remark, sort, creator, create_date, updater, update_date) VALUES (1225813644059140097, 'notice_type', '站内通知-类型', '', 1, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_dict_data(id, dict_type_id, dict_label, dict_value, remark, sort, creator, create_date, updater, update_date) VALUES (1225814069634195457, 1225813644059140097, '公告', '0', '', 0, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_dict_data(id, dict_type_id, dict_label, dict_value, remark, sort, creator, create_date, updater, update_date) VALUES (1225814107559092225, 1225813644059140097, '会议', '1', '', 1, 1067246875800000001, getdate(), 1067246875800000001, getdate()); +INSERT INTO sys_dict_data(id, dict_type_id, dict_label, dict_value, remark, sort, creator, create_date, updater, update_date) VALUES (1225814271879340034, 1225813644059140097, '其他', '2', '', 2, 1067246875800000001, getdate(), 1067246875800000001, getdate()); + +INSERT INTO sys_params(id, param_code, param_value, param_type, remark, creator, create_date, updater, update_date) VALUES (1067246875800000073, 'CLOUD_STORAGE_CONFIG_KEY', '{"type":1,"qiniuDomain":"http://test.oss.weather.io","qiniuPrefix":"upload","qiniuAccessKey":"NrgMfABZxWLo5B-YYSjoE8-AZ1EISdi1Z3ubLOeZ","qiniuSecretKey":"uIwJHevMRWU0VLxFvgy0tAcOdGqasdtVlJkdy6vV","qiniuBucketName":"weather-oss","aliyunDomain":"","aliyunPrefix":"","aliyunEndPoint":"","aliyunAccessKeyId":"","aliyunAccessKeySecret":"","aliyunBucketName":"","qcloudDomain":"","qcloudPrefix":"","qcloudSecretId":"","qcloudSecretKey":"","qcloudBucketName":""}', '0', '云存储配置信息', 1067246875800000001, getdate(), 1067246875800000001, getdate()); + +INSERT INTO schedule_job (id, bean_name, params, cron_expression, status, remark, creator, create_date, updater, update_date) VALUES (1067246875800000076, 'testTask', 'weather', '0 0/30 * * * ?', 0, '有参测试,多个参数使用json', 1067246875800000001, getdate(), 1067246875800000001, getdate()); + + +-- quartz自带表结构 +IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[FK_QRTZ_TRIGGERS_QRTZ_JOB_DETAILS]') AND OBJECTPROPERTY(id, N'ISFOREIGNKEY') = 1) + ALTER TABLE [dbo].[QRTZ_TRIGGERS] DROP CONSTRAINT FK_QRTZ_TRIGGERS_QRTZ_JOB_DETAILS +GO + +IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[FK_QRTZ_CRON_TRIGGERS_QRTZ_TRIGGERS]') AND OBJECTPROPERTY(id, N'ISFOREIGNKEY') = 1) + ALTER TABLE [dbo].[QRTZ_CRON_TRIGGERS] DROP CONSTRAINT FK_QRTZ_CRON_TRIGGERS_QRTZ_TRIGGERS +GO + +IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[FK_QRTZ_SIMPLE_TRIGGERS_QRTZ_TRIGGERS]') AND OBJECTPROPERTY(id, N'ISFOREIGNKEY') = 1) + ALTER TABLE [dbo].[QRTZ_SIMPLE_TRIGGERS] DROP CONSTRAINT FK_QRTZ_SIMPLE_TRIGGERS_QRTZ_TRIGGERS +GO + +IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[FK_QRTZ_SIMPROP_TRIGGERS_QRTZ_TRIGGERS]') AND OBJECTPROPERTY(id, N'ISFOREIGNKEY') = 1) + ALTER TABLE [dbo].[QRTZ_SIMPROP_TRIGGERS] DROP CONSTRAINT FK_QRTZ_SIMPROP_TRIGGERS_QRTZ_TRIGGERS +GO + +IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[QRTZ_CALENDARS]') AND OBJECTPROPERTY(id, N'ISUSERTABLE') = 1) + DROP TABLE [dbo].[QRTZ_CALENDARS] +GO + +IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[QRTZ_CRON_TRIGGERS]') AND OBJECTPROPERTY(id, N'ISUSERTABLE') = 1) + DROP TABLE [dbo].[QRTZ_CRON_TRIGGERS] +GO + +IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[QRTZ_BLOB_TRIGGERS]') AND OBJECTPROPERTY(id, N'ISUSERTABLE') = 1) + DROP TABLE [dbo].[QRTZ_BLOB_TRIGGERS] +GO + +IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[QRTZ_FIRED_TRIGGERS]') AND OBJECTPROPERTY(id, N'ISUSERTABLE') = 1) + DROP TABLE [dbo].[QRTZ_FIRED_TRIGGERS] +GO + +IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[QRTZ_PAUSED_TRIGGER_GRPS]') AND OBJECTPROPERTY(id, N'ISUSERTABLE') = 1) + DROP TABLE [dbo].[QRTZ_PAUSED_TRIGGER_GRPS] +GO + +IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[QRTZ_SCHEDULER_STATE]') AND OBJECTPROPERTY(id, N'ISUSERTABLE') = 1) + DROP TABLE [dbo].[QRTZ_SCHEDULER_STATE] +GO + +IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[QRTZ_LOCKS]') AND OBJECTPROPERTY(id, N'ISUSERTABLE') = 1) + DROP TABLE [dbo].[QRTZ_LOCKS] +GO + +IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[QRTZ_JOB_DETAILS]') AND OBJECTPROPERTY(id, N'ISUSERTABLE') = 1) + DROP TABLE [dbo].[QRTZ_JOB_DETAILS] +GO + +IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[QRTZ_SIMPLE_TRIGGERS]') AND OBJECTPROPERTY(id, N'ISUSERTABLE') = 1) + DROP TABLE [dbo].[QRTZ_SIMPLE_TRIGGERS] +GO + +IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[QRTZ_SIMPROP_TRIGGERS]') AND OBJECTPROPERTY(id, N'ISUSERTABLE') = 1) + DROP TABLE [dbo].[QRTZ_SIMPROP_TRIGGERS] +GO + +IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[QRTZ_TRIGGERS]') AND OBJECTPROPERTY(id, N'ISUSERTABLE') = 1) + DROP TABLE [dbo].[QRTZ_TRIGGERS] +GO + +CREATE TABLE [dbo].[QRTZ_CALENDARS] ( + [SCHED_NAME] [VARCHAR] (120) NOT NULL , + [CALENDAR_NAME] [VARCHAR] (200) NOT NULL , + [CALENDAR] [IMAGE] NOT NULL +) ON [PRIMARY] +GO + +CREATE TABLE [dbo].[QRTZ_CRON_TRIGGERS] ( + [SCHED_NAME] [VARCHAR] (120) NOT NULL , + [TRIGGER_NAME] [VARCHAR] (200) NOT NULL , + [TRIGGER_GROUP] [VARCHAR] (200) NOT NULL , + [CRON_EXPRESSION] [VARCHAR] (120) NOT NULL , + [TIME_ZONE_ID] [VARCHAR] (80) +) ON [PRIMARY] +GO + +CREATE TABLE [dbo].[QRTZ_FIRED_TRIGGERS] ( + [SCHED_NAME] [VARCHAR] (120) NOT NULL , + [ENTRY_ID] [VARCHAR] (95) NOT NULL , + [TRIGGER_NAME] [VARCHAR] (200) NOT NULL , + [TRIGGER_GROUP] [VARCHAR] (200) NOT NULL , + [INSTANCE_NAME] [VARCHAR] (200) NOT NULL , + [FIRED_TIME] [BIGINT] NOT NULL , + [SCHED_TIME] [BIGINT] NOT NULL , + [PRIORITY] [INTEGER] NOT NULL , + [STATE] [VARCHAR] (16) NOT NULL, + [JOB_NAME] [VARCHAR] (200) NULL , + [JOB_GROUP] [VARCHAR] (200) NULL , + [IS_NONCONCURRENT] [VARCHAR] (1) NULL , + [REQUESTS_RECOVERY] [VARCHAR] (1) NULL +) ON [PRIMARY] +GO + +CREATE TABLE [dbo].[QRTZ_PAUSED_TRIGGER_GRPS] ( + [SCHED_NAME] [VARCHAR] (120) NOT NULL , + [TRIGGER_GROUP] [VARCHAR] (200) NOT NULL +) ON [PRIMARY] +GO + +CREATE TABLE [dbo].[QRTZ_SCHEDULER_STATE] ( + [SCHED_NAME] [VARCHAR] (120) NOT NULL , + [INSTANCE_NAME] [VARCHAR] (200) NOT NULL , + [LAST_CHECKIN_TIME] [BIGINT] NOT NULL , + [CHECKIN_INTERVAL] [BIGINT] NOT NULL +) ON [PRIMARY] +GO + +CREATE TABLE [dbo].[QRTZ_LOCKS] ( + [SCHED_NAME] [VARCHAR] (120) NOT NULL , + [LOCK_NAME] [VARCHAR] (40) NOT NULL +) ON [PRIMARY] +GO + +CREATE TABLE [dbo].[QRTZ_JOB_DETAILS] ( + [SCHED_NAME] [VARCHAR] (120) NOT NULL , + [JOB_NAME] [VARCHAR] (200) NOT NULL , + [JOB_GROUP] [VARCHAR] (200) NOT NULL , + [DESCRIPTION] [VARCHAR] (250) NULL , + [JOB_CLASS_NAME] [VARCHAR] (250) NOT NULL , + [IS_DURABLE] [VARCHAR] (1) NOT NULL , + [IS_NONCONCURRENT] [VARCHAR] (1) NOT NULL , + [IS_UPDATE_DATA] [VARCHAR] (1) NOT NULL , + [REQUESTS_RECOVERY] [VARCHAR] (1) NOT NULL , + [JOB_DATA] [IMAGE] NULL +) ON [PRIMARY] +GO + +CREATE TABLE [dbo].[QRTZ_SIMPLE_TRIGGERS] ( + [SCHED_NAME] [VARCHAR] (120) NOT NULL , + [TRIGGER_NAME] [VARCHAR] (200) NOT NULL , + [TRIGGER_GROUP] [VARCHAR] (200) NOT NULL , + [REPEAT_COUNT] [BIGINT] NOT NULL , + [REPEAT_INTERVAL] [BIGINT] NOT NULL , + [TIMES_TRIGGERED] [BIGINT] NOT NULL +) ON [PRIMARY] +GO + +CREATE TABLE [dbo].[QRTZ_SIMPROP_TRIGGERS] ( + [SCHED_NAME] [VARCHAR] (120) NOT NULL , + [TRIGGER_NAME] [VARCHAR] (200) NOT NULL , + [TRIGGER_GROUP] [VARCHAR] (200) NOT NULL , + [STR_PROP_1] [VARCHAR] (512) NULL, + [STR_PROP_2] [VARCHAR] (512) NULL, + [STR_PROP_3] [VARCHAR] (512) NULL, + [INT_PROP_1] [INT] NULL, + [INT_PROP_2] [INT] NULL, + [LONG_PROP_1] [BIGINT] NULL, + [LONG_PROP_2] [BIGINT] NULL, + [DEC_PROP_1] [NUMERIC] (13,4) NULL, + [DEC_PROP_2] [NUMERIC] (13,4) NULL, + [BOOL_PROP_1] [VARCHAR] (1) NULL, + [BOOL_PROP_2] [VARCHAR] (1) NULL, +) ON [PRIMARY] +GO + +CREATE TABLE [dbo].[QRTZ_BLOB_TRIGGERS] ( + [SCHED_NAME] [VARCHAR] (120) NOT NULL , + [TRIGGER_NAME] [VARCHAR] (200) NOT NULL , + [TRIGGER_GROUP] [VARCHAR] (200) NOT NULL , + [BLOB_DATA] [IMAGE] NULL +) ON [PRIMARY] +GO + +CREATE TABLE [dbo].[QRTZ_TRIGGERS] ( + [SCHED_NAME] [VARCHAR] (120) NOT NULL , + [TRIGGER_NAME] [VARCHAR] (200) NOT NULL , + [TRIGGER_GROUP] [VARCHAR] (200) NOT NULL , + [JOB_NAME] [VARCHAR] (200) NOT NULL , + [JOB_GROUP] [VARCHAR] (200) NOT NULL , + [DESCRIPTION] [VARCHAR] (250) NULL , + [NEXT_FIRE_TIME] [BIGINT] NULL , + [PREV_FIRE_TIME] [BIGINT] NULL , + [PRIORITY] [INTEGER] NULL , + [TRIGGER_STATE] [VARCHAR] (16) NOT NULL , + [TRIGGER_TYPE] [VARCHAR] (8) NOT NULL , + [START_TIME] [BIGINT] NOT NULL , + [END_TIME] [BIGINT] NULL , + [CALENDAR_NAME] [VARCHAR] (200) NULL , + [MISFIRE_INSTR] [SMALLINT] NULL , + [JOB_DATA] [IMAGE] NULL +) ON [PRIMARY] +GO + +ALTER TABLE [dbo].[QRTZ_CALENDARS] WITH NOCHECK ADD + CONSTRAINT [PK_QRTZ_CALENDARS] PRIMARY KEY CLUSTERED + ( + [SCHED_NAME], + [CALENDAR_NAME] + ) ON [PRIMARY] +GO + +ALTER TABLE [dbo].[QRTZ_CRON_TRIGGERS] WITH NOCHECK ADD + CONSTRAINT [PK_QRTZ_CRON_TRIGGERS] PRIMARY KEY CLUSTERED + ( + [SCHED_NAME], + [TRIGGER_NAME], + [TRIGGER_GROUP] + ) ON [PRIMARY] +GO + +ALTER TABLE [dbo].[QRTZ_FIRED_TRIGGERS] WITH NOCHECK ADD + CONSTRAINT [PK_QRTZ_FIRED_TRIGGERS] PRIMARY KEY CLUSTERED + ( + [SCHED_NAME], + [ENTRY_ID] + ) ON [PRIMARY] +GO + +ALTER TABLE [dbo].[QRTZ_PAUSED_TRIGGER_GRPS] WITH NOCHECK ADD + CONSTRAINT [PK_QRTZ_PAUSED_TRIGGER_GRPS] PRIMARY KEY CLUSTERED + ( + [SCHED_NAME], + [TRIGGER_GROUP] + ) ON [PRIMARY] +GO + +ALTER TABLE [dbo].[QRTZ_SCHEDULER_STATE] WITH NOCHECK ADD + CONSTRAINT [PK_QRTZ_SCHEDULER_STATE] PRIMARY KEY CLUSTERED + ( + [SCHED_NAME], + [INSTANCE_NAME] + ) ON [PRIMARY] +GO + +ALTER TABLE [dbo].[QRTZ_LOCKS] WITH NOCHECK ADD + CONSTRAINT [PK_QRTZ_LOCKS] PRIMARY KEY CLUSTERED + ( + [SCHED_NAME], + [LOCK_NAME] + ) ON [PRIMARY] +GO + +ALTER TABLE [dbo].[QRTZ_JOB_DETAILS] WITH NOCHECK ADD + CONSTRAINT [PK_QRTZ_JOB_DETAILS] PRIMARY KEY CLUSTERED + ( + [SCHED_NAME], + [JOB_NAME], + [JOB_GROUP] + ) ON [PRIMARY] +GO + +ALTER TABLE [dbo].[QRTZ_SIMPLE_TRIGGERS] WITH NOCHECK ADD + CONSTRAINT [PK_QRTZ_SIMPLE_TRIGGERS] PRIMARY KEY CLUSTERED + ( + [SCHED_NAME], + [TRIGGER_NAME], + [TRIGGER_GROUP] + ) ON [PRIMARY] +GO + +ALTER TABLE [dbo].[QRTZ_SIMPROP_TRIGGERS] WITH NOCHECK ADD + CONSTRAINT [PK_QRTZ_SIMPROP_TRIGGERS] PRIMARY KEY CLUSTERED + ( + [SCHED_NAME], + [TRIGGER_NAME], + [TRIGGER_GROUP] + ) ON [PRIMARY] +GO + +ALTER TABLE [dbo].[QRTZ_TRIGGERS] WITH NOCHECK ADD + CONSTRAINT [PK_QRTZ_TRIGGERS] PRIMARY KEY CLUSTERED + ( + [SCHED_NAME], + [TRIGGER_NAME], + [TRIGGER_GROUP] + ) ON [PRIMARY] +GO + +ALTER TABLE [dbo].[QRTZ_CRON_TRIGGERS] ADD + CONSTRAINT [FK_QRTZ_CRON_TRIGGERS_QRTZ_TRIGGERS] FOREIGN KEY + ( + [SCHED_NAME], + [TRIGGER_NAME], + [TRIGGER_GROUP] + ) REFERENCES [dbo].[QRTZ_TRIGGERS] ( + [SCHED_NAME], + [TRIGGER_NAME], + [TRIGGER_GROUP] + ) ON DELETE CASCADE +GO + +ALTER TABLE [dbo].[QRTZ_SIMPLE_TRIGGERS] ADD + CONSTRAINT [FK_QRTZ_SIMPLE_TRIGGERS_QRTZ_TRIGGERS] FOREIGN KEY + ( + [SCHED_NAME], + [TRIGGER_NAME], + [TRIGGER_GROUP] + ) REFERENCES [dbo].[QRTZ_TRIGGERS] ( + [SCHED_NAME], + [TRIGGER_NAME], + [TRIGGER_GROUP] + ) ON DELETE CASCADE +GO + +ALTER TABLE [dbo].[QRTZ_SIMPROP_TRIGGERS] ADD + CONSTRAINT [FK_QRTZ_SIMPROP_TRIGGERS_QRTZ_TRIGGERS] FOREIGN KEY + ( + [SCHED_NAME], + [TRIGGER_NAME], + [TRIGGER_GROUP] + ) REFERENCES [dbo].[QRTZ_TRIGGERS] ( + [SCHED_NAME], + [TRIGGER_NAME], + [TRIGGER_GROUP] + ) ON DELETE CASCADE +GO + +ALTER TABLE [dbo].[QRTZ_TRIGGERS] ADD + CONSTRAINT [FK_QRTZ_TRIGGERS_QRTZ_JOB_DETAILS] FOREIGN KEY + ( + [SCHED_NAME], + [JOB_NAME], + [JOB_GROUP] + ) REFERENCES [dbo].[QRTZ_JOB_DETAILS] ( + [SCHED_NAME], + [JOB_NAME], + [JOB_GROUP] + ) +GO \ No newline at end of file diff --git a/system-admin/pom.xml b/system-admin/pom.xml new file mode 100644 index 0000000..08ff42b --- /dev/null +++ b/system-admin/pom.xml @@ -0,0 +1,139 @@ + + + com.weather + weather-data + 5.5.0 + + 4.0.0 + system-admin + jar + system-admin + + + 2.3.2 + 1.12.0 + 1.6.2 + 3.2.1 + 7.2.27 + 2.8.3 + 3.2.2 + 5.4.4 + + + + + com.weather + system-common + 5.5.0 + + + com.weather + system-dynamic-datasource + 5.5.0 + + + org.quartz-scheduler + quartz + ${quartz.version} + + + com.mchange + c3p0 + + + com.zaxxer + HikariCP-java6 + + + + + org.apache.shiro + shiro-spring + jakarta + ${shiro.version} + + + + org.apache.shiro + shiro-core + + + org.apache.shiro + shiro-web + + + + + + org.apache.shiro + shiro-core + jakarta + ${shiro.version} + + + org.apache.shiro + shiro-web + jakarta + ${shiro.version} + + + org.apache.shiro + shiro-core + + + + + com.github.whvcse + easy-captcha + ${captcha.version} + + + com.alibaba + easyexcel + ${easyexcel.version} + + + com.qiniu + qiniu-java-sdk + ${qiniu.version} + + + com.aliyun.oss + aliyun-sdk-oss + ${aliyun.oss.version} + + + com.aliyun + aliyun-java-sdk-core + ${aliyun.core.version} + + + com.qcloud + cos_api + ${qcloud.cos.version} + + + org.slf4j + slf4j-log4j12 + + + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + true + + + + + diff --git a/system-admin/src/main/java/com/weather/AdminApplication.java b/system-admin/src/main/java/com/weather/AdminApplication.java new file mode 100644 index 0000000..400422f --- /dev/null +++ b/system-admin/src/main/java/com/weather/AdminApplication.java @@ -0,0 +1,27 @@ +package com.weather; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; +import org.springframework.scheduling.annotation.EnableAsync; + + +/** + * weather-admin + * + * @author 123 + */ +@SpringBootApplication +@EnableAsync +public class AdminApplication extends SpringBootServletInitializer { + + public static void main(String[] args) { + SpringApplication.run(AdminApplication.class, args); + } + + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(AdminApplication.class); + } +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/common/annotation/DataFilter.java b/system-admin/src/main/java/com/weather/common/annotation/DataFilter.java new file mode 100644 index 0000000..f669b38 --- /dev/null +++ b/system-admin/src/main/java/com/weather/common/annotation/DataFilter.java @@ -0,0 +1,31 @@ + + +package com.weather.common.annotation; + +import java.lang.annotation.*; + +/** + * 数据过滤注解 + * + * @author 123 + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface DataFilter { + /** + * 表的别名 + */ + String tableAlias() default ""; + + /** + * 用户ID + */ + String userId() default "creator"; + + /** + * 部门ID + */ + String deptId() default "dept_id"; + +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/common/annotation/LogOperation.java b/system-admin/src/main/java/com/weather/common/annotation/LogOperation.java new file mode 100644 index 0000000..4ff9d91 --- /dev/null +++ b/system-admin/src/main/java/com/weather/common/annotation/LogOperation.java @@ -0,0 +1,22 @@ + + +package com.weather.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 操作日志注解 + * + * @author 123 + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface LogOperation { + + String value() default ""; +} diff --git a/system-admin/src/main/java/com/weather/common/aspect/DataFilterAspect.java b/system-admin/src/main/java/com/weather/common/aspect/DataFilterAspect.java new file mode 100644 index 0000000..334f5f2 --- /dev/null +++ b/system-admin/src/main/java/com/weather/common/aspect/DataFilterAspect.java @@ -0,0 +1,101 @@ + +package com.weather.common.aspect; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.qiniu.util.StringUtils; +import com.weather.common.annotation.DataFilter; +import com.weather.common.constant.Constant; +import com.weather.common.exception.ErrorCode; +import com.weather.common.exception.CommonException; +import com.weather.common.interceptor.DataScope; +import com.weather.modules.security.user.SecurityUser; +import com.weather.modules.security.user.UserDetail; +import com.weather.modules.sys.enums.SuperAdminEnum; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Method; +import java.util.List; +import java.util.Map; + +/** + * 数据过滤,切面处理类 + * + * @author 123 + */ +@Aspect +@Component +public class DataFilterAspect { + + @Pointcut("@annotation(com.weather.common.annotation.DataFilter)") + public void dataFilterCut() { + + } + + @Before("dataFilterCut()") + public void dataFilter(JoinPoint point) { + Object params = point.getArgs()[0]; + if (params != null && params instanceof Map) { + UserDetail user = SecurityUser.getUser(); + + //如果是超级管理员,则不进行数据过滤 + if (user.getSuperAdmin() == SuperAdminEnum.YES.value()) { + return; + } + + try { + //否则进行数据过滤 + Map map = (Map) params; + String sqlFilter = getSqlFilter(user, point); + map.put(Constant.SQL_FILTER, new DataScope(sqlFilter)); + } catch (Exception e) { + + } + + return; + } + + throw new CommonException(ErrorCode.DATA_SCOPE_PARAMS_ERROR); + } + + /** + * 获取数据过滤的SQL + */ + private String getSqlFilter(UserDetail user, JoinPoint point) throws Exception { + MethodSignature signature = (MethodSignature) point.getSignature(); + Method method = point.getTarget().getClass().getDeclaredMethod(signature.getName(), signature.getParameterTypes()); + DataFilter dataFilter = method.getAnnotation(DataFilter.class); + + //获取表的别名 + String tableAlias = dataFilter.tableAlias(); + if (StrUtil.isNotBlank(tableAlias)) { + tableAlias += "."; + } + + StringBuilder sqlFilter = new StringBuilder(); + sqlFilter.append(" ("); + + //部门ID列表 + List deptIdList = user.getDeptIdList(); + if (CollUtil.isNotEmpty(deptIdList)) { + sqlFilter.append(tableAlias).append(dataFilter.deptId()); + + sqlFilter.append(" in(").append(StringUtils.join(deptIdList, ",")).append(")"); + } + + //查询本人数据 + if (CollUtil.isNotEmpty(deptIdList)) { + sqlFilter.append(" or "); + } + sqlFilter.append(tableAlias).append(dataFilter.userId()).append("=").append(user.getId()); + + sqlFilter.append(")"); + + return sqlFilter.toString(); + } +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/common/aspect/LogOperationAspect.java b/system-admin/src/main/java/com/weather/common/aspect/LogOperationAspect.java new file mode 100644 index 0000000..59e9e57 --- /dev/null +++ b/system-admin/src/main/java/com/weather/common/aspect/LogOperationAspect.java @@ -0,0 +1,103 @@ + +package com.weather.common.aspect; + +import com.weather.common.annotation.LogOperation; +import com.weather.common.utils.HttpContextUtils; +import com.weather.common.utils.IpUtils; +import com.weather.common.utils.JsonUtils; +import com.weather.modules.log.entity.SysLogOperationEntity; +import com.weather.modules.log.enums.OperationStatusEnum; +import com.weather.modules.log.service.SysLogOperationService; +import com.weather.modules.security.user.SecurityUser; +import com.weather.modules.security.user.UserDetail; +import jakarta.servlet.http.HttpServletRequest; +import lombok.AllArgsConstructor; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.http.HttpHeaders; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Method; + +/** + * 操作日志,切面处理类 + * + * @author 123 + */ +@Aspect +@Component +@AllArgsConstructor +public class LogOperationAspect { + private final SysLogOperationService sysLogOperationService; + + @Pointcut("@annotation(com.weather.common.annotation.LogOperation)") + public void logPointCut() { + + } + + @Around("logPointCut()") + public Object around(ProceedingJoinPoint point) throws Throwable { + long beginTime = System.currentTimeMillis(); + try { + //执行方法 + Object result = point.proceed(); + + //执行时长(毫秒) + long time = System.currentTimeMillis() - beginTime; + //保存日志 + saveLog(point, time, OperationStatusEnum.SUCCESS.value()); + + return result; + } catch (Exception e) { + //执行时长(毫秒) + long time = System.currentTimeMillis() - beginTime; + //保存日志 + saveLog(point, time, OperationStatusEnum.FAIL.value()); + + throw e; + } + } + + private void saveLog(ProceedingJoinPoint joinPoint, long time, Integer status) throws Exception { + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + Method method = joinPoint.getTarget().getClass().getDeclaredMethod(signature.getName(), signature.getParameterTypes()); + LogOperation annotation = method.getAnnotation(LogOperation.class); + + SysLogOperationEntity log = new SysLogOperationEntity(); + if (annotation != null) { + //注解上的描述 + log.setOperation(annotation.value()); + } + + //登录用户信息 + UserDetail user = SecurityUser.getUser(); + if (user != null) { + log.setCreatorName(user.getUsername()); + } + + log.setStatus(status); + log.setRequestTime((int) time); + + //请求相关信息 + HttpServletRequest request = HttpContextUtils.getHttpServletRequest(); + log.setIp(IpUtils.getIpAddr(request)); + log.setUserAgent(request.getHeader(HttpHeaders.USER_AGENT)); + log.setRequestUri(request.getRequestURI()); + log.setRequestMethod(request.getMethod()); + + //请求参数 + Object[] args = joinPoint.getArgs(); + try { + String params = JsonUtils.toJsonString(args[0]); + log.setRequestParams(params); + } catch (Exception e) { + + } + + //保存到DB + sysLogOperationService.save(log); + } +} diff --git a/system-admin/src/main/java/com/weather/common/config/FileServerConfig.java b/system-admin/src/main/java/com/weather/common/config/FileServerConfig.java new file mode 100644 index 0000000..35cc475 --- /dev/null +++ b/system-admin/src/main/java/com/weather/common/config/FileServerConfig.java @@ -0,0 +1,15 @@ +package com.weather.common.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class FileServerConfig implements WebMvcConfigurer { + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + // 文件访问通过 FileDownloadController 提供,支持 Shiro 权限控制 + // 如需直接静态资源映射,可在此配置 + } +} diff --git a/system-admin/src/main/java/com/weather/common/config/JacksonConfig.java b/system-admin/src/main/java/com/weather/common/config/JacksonConfig.java new file mode 100644 index 0000000..8e54e39 --- /dev/null +++ b/system-admin/src/main/java/com/weather/common/config/JacksonConfig.java @@ -0,0 +1,26 @@ +package com.weather.common.config; + +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Jackson 配置 + *

+ * 全局 Long → String 序列化,解决 Snowflake 19位ID 在 JavaScript 端精度丢失问题。 + * JS Number 安全整数上限为 2^53-1 (约16位),Snowflake ASSIGN_ID 为19位,超出精度范围。 + * + * @author 123 + */ +@Configuration +public class JacksonConfig { + + @Bean + public Jackson2ObjectMapperBuilderCustomizer jacksonLongToStringCustomizer() { + return builder -> { + builder.serializerByType(Long.class, ToStringSerializer.instance); + builder.serializerByType(long.class, ToStringSerializer.instance); + }; + } +} diff --git a/system-admin/src/main/java/com/weather/common/config/MybatisPlusConfig.java b/system-admin/src/main/java/com/weather/common/config/MybatisPlusConfig.java new file mode 100644 index 0000000..c36b4b7 --- /dev/null +++ b/system-admin/src/main/java/com/weather/common/config/MybatisPlusConfig.java @@ -0,0 +1,37 @@ + + +package com.weather.common.config; + +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import com.weather.common.interceptor.DataFilterInterceptor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * mybatis-plus配置 + * + * @author 123 + * @since 1.0.0 + */ +@Configuration +public class MybatisPlusConfig { + + @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor() { + MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); + // 数据权限 + mybatisPlusInterceptor.addInnerInterceptor(new DataFilterInterceptor()); + // 分页插件 + mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor()); + // 乐观锁 + mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); + // 防止全表更新与删除 + mybatisPlusInterceptor.addInnerInterceptor(new BlockAttackInnerInterceptor()); + + return mybatisPlusInterceptor; + } + +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/common/config/SwaggerConfig.java b/system-admin/src/main/java/com/weather/common/config/SwaggerConfig.java new file mode 100644 index 0000000..2465cd3 --- /dev/null +++ b/system-admin/src/main/java/com/weather/common/config/SwaggerConfig.java @@ -0,0 +1,43 @@ + +package com.weather.common.config; + +import com.weather.common.constant.Constant; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.security.SecurityRequirement; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.ArrayList; +import java.util.List; + +/** + * Swagger配置 + * + * @author 123 + */ +@Configuration +public class SwaggerConfig { + @Bean + public OpenAPI createRestApi() { + return new OpenAPI() + .info(apiInfo()) + .security(security()); + } + + private Info apiInfo() { + return new Info() + .title("weather") + .description("weather-admin文档") + .version("5.x"); + } + + private List security() { + SecurityRequirement key = new SecurityRequirement(); + key.addList(Constant.TOKEN_HEADER, Constant.TOKEN_HEADER); + + List list = new ArrayList<>(); + list.add(key); + return list; + } +} diff --git a/system-admin/src/main/java/com/weather/common/exception/CustomExceptionHandler.java b/system-admin/src/main/java/com/weather/common/exception/CustomExceptionHandler.java new file mode 100644 index 0000000..ab4d09f --- /dev/null +++ b/system-admin/src/main/java/com/weather/common/exception/CustomExceptionHandler.java @@ -0,0 +1,91 @@ + +package com.weather.common.exception; + +import cn.hutool.core.map.MapUtil; +import com.weather.common.utils.HttpContextUtils; +import com.weather.common.utils.IpUtils; +import com.weather.common.utils.JsonUtils; +import com.weather.common.utils.Result; +import com.weather.modules.log.entity.SysLogErrorEntity; +import com.weather.modules.log.service.SysLogErrorService; +import jakarta.servlet.http.HttpServletRequest; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.shiro.authz.UnauthorizedException; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.http.HttpHeaders; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import java.util.Map; + + +/** + * 异常处理器 + * + * @author 123 + * @since 1.0.0 + */ +@Slf4j +@RestControllerAdvice +@AllArgsConstructor +public class CustomExceptionHandler { + private final SysLogErrorService sysLogErrorService; + + /** + * 处理自定义异常 + */ + @ExceptionHandler(CommonException.class) + public Result handleRenException(CommonException ex) { + Result result = new Result(); + result.error(ex.getCode(), ex.getMsg()); + + return result; + } + + @ExceptionHandler(DuplicateKeyException.class) + public Result handleDuplicateKeyException(DuplicateKeyException ex) { + Result result = new Result(); + result.error(ErrorCode.DB_RECORD_EXISTS); + + return result; + } + + @ExceptionHandler(UnauthorizedException.class) + public Result handleUnauthorizedException(UnauthorizedException ex) { + return new Result().error(ErrorCode.UNAUTHORIZED); + } + + @ExceptionHandler(Exception.class) + public Result handleException(Exception ex) { + log.error(ex.getMessage(), ex); + + saveLog(ex); + + return new Result().error(); + } + + /** + * 保存异常日志 + */ + private void saveLog(Exception ex) { + SysLogErrorEntity log = new SysLogErrorEntity(); + + //请求相关信息 + HttpServletRequest request = HttpContextUtils.getHttpServletRequest(); + log.setIp(IpUtils.getIpAddr(request)); + log.setUserAgent(request.getHeader(HttpHeaders.USER_AGENT)); + log.setRequestUri(request.getRequestURI()); + log.setRequestMethod(request.getMethod()); + Map params = HttpContextUtils.getParameterMap(request); + if (MapUtil.isNotEmpty(params)) { + log.setRequestParams(JsonUtils.toJsonString(params)); + } + + //异常信息 + log.setErrorInfo(ExceptionUtils.getErrorStackTrace(ex)); + + //保存 + sysLogErrorService.save(log); + } +} diff --git a/system-admin/src/main/java/com/weather/common/handler/FieldMetaObjectHandler.java b/system-admin/src/main/java/com/weather/common/handler/FieldMetaObjectHandler.java new file mode 100644 index 0000000..422a64a --- /dev/null +++ b/system-admin/src/main/java/com/weather/common/handler/FieldMetaObjectHandler.java @@ -0,0 +1,52 @@ + + +package com.weather.common.handler; + +import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import com.weather.modules.security.user.SecurityUser; +import com.weather.modules.security.user.UserDetail; +import org.apache.ibatis.reflection.MetaObject; +import org.springframework.stereotype.Component; + +import java.util.Date; + +/** + * 公共字段,自动填充值 + * + * @author 123 + */ +@Component +public class FieldMetaObjectHandler implements MetaObjectHandler { + private final static String CREATE_DATE = "createDate"; + private final static String CREATOR = "creator"; + private final static String UPDATE_DATE = "updateDate"; + private final static String UPDATER = "updater"; + private final static String DEPT_ID = "deptId"; + + @Override + public void insertFill(MetaObject metaObject) { + UserDetail user = SecurityUser.getUser(); + Date date = new Date(); + + //创建者 + strictInsertFill(metaObject, CREATOR, Long.class, user.getId()); + //创建时间 + strictInsertFill(metaObject, CREATE_DATE, Date.class, date); + + //创建者所属部门 + strictInsertFill(metaObject, DEPT_ID, Long.class, user.getDeptId()); + + //更新者 + strictInsertFill(metaObject, UPDATER, Long.class, user.getId()); + //更新时间 + strictInsertFill(metaObject, UPDATE_DATE, Date.class, date); + } + + @Override + public void updateFill(MetaObject metaObject) { + //更新者 + strictUpdateFill(metaObject, UPDATER, Long.class, SecurityUser.getUserId()); + //更新时间 + strictUpdateFill(metaObject, UPDATE_DATE, Date.class, new Date()); + } +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/common/interceptor/DataFilterInterceptor.java b/system-admin/src/main/java/com/weather/common/interceptor/DataFilterInterceptor.java new file mode 100644 index 0000000..0cabe84 --- /dev/null +++ b/system-admin/src/main/java/com/weather/common/interceptor/DataFilterInterceptor.java @@ -0,0 +1,83 @@ + + +package com.weather.common.interceptor; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.toolkit.PluginUtils; +import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor; +import net.sf.jsqlparser.JSQLParserException; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.StringValue; +import net.sf.jsqlparser.expression.operators.conditional.AndExpression; +import net.sf.jsqlparser.parser.CCJSqlParserUtil; +import net.sf.jsqlparser.statement.select.PlainSelect; +import net.sf.jsqlparser.statement.select.Select; +import org.apache.ibatis.executor.Executor; +import org.apache.ibatis.mapping.BoundSql; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.session.ResultHandler; +import org.apache.ibatis.session.RowBounds; + +import java.util.Map; + +/** + * 数据过滤 + * + * @author 123 + */ +public class DataFilterInterceptor implements InnerInterceptor { + + @Override + public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { + DataScope scope = getDataScope(parameter); + // 不进行数据过滤 + if(scope == null || StrUtil.isBlank(scope.getSqlFilter())){ + return; + } + + // 拼接新SQL + String buildSql = getSelect(boundSql.getSql(), scope); + + // 重写SQL + PluginUtils.mpBoundSql(boundSql).sql(buildSql); + } + + private DataScope getDataScope(Object parameter){ + if (parameter == null){ + return null; + } + + // 判断参数里是否有DataScope对象 + if (parameter instanceof Map) { + Map parameterMap = (Map) parameter; + for (Map.Entry entry : parameterMap.entrySet()) { + if (entry.getValue() != null && entry.getValue() instanceof DataScope) { + return (DataScope) entry.getValue(); + } + } + } else if (parameter instanceof DataScope) { + return (DataScope) parameter; + } + + return null; + } + + private String getSelect(String buildSql, DataScope scope){ + try { + Select select = (Select) CCJSqlParserUtil.parse(buildSql); + PlainSelect plainSelect = (PlainSelect) select.getSelectBody(); + + Expression expression = plainSelect.getWhere(); + if(expression == null){ + plainSelect.setWhere(new StringValue(scope.getSqlFilter())); + }else{ + AndExpression andExpression = new AndExpression(expression, new StringValue(scope.getSqlFilter())); + plainSelect.setWhere(andExpression); + } + + return select.toString().replaceAll("'", ""); + }catch (JSQLParserException e){ + return buildSql; + } + } +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/common/interceptor/DataScope.java b/system-admin/src/main/java/com/weather/common/interceptor/DataScope.java new file mode 100644 index 0000000..5638e31 --- /dev/null +++ b/system-admin/src/main/java/com/weather/common/interceptor/DataScope.java @@ -0,0 +1,30 @@ + + +package com.weather.common.interceptor; + +/** + * 数据范围 + * + * @author 123 + * @since 1.0.0 + */ +public class DataScope { + private String sqlFilter; + + public DataScope(String sqlFilter) { + this.sqlFilter = sqlFilter; + } + + public String getSqlFilter() { + return sqlFilter; + } + + public void setSqlFilter(String sqlFilter) { + this.sqlFilter = sqlFilter; + } + + @Override + public String toString() { + return this.sqlFilter; + } +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/common/utils/ExcelUtils.java b/system-admin/src/main/java/com/weather/common/utils/ExcelUtils.java new file mode 100644 index 0000000..c9d915e --- /dev/null +++ b/system-admin/src/main/java/com/weather/common/utils/ExcelUtils.java @@ -0,0 +1,68 @@ + +package com.weather.common.utils; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.core.util.URLUtil; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.converters.longconverter.LongStringConverter; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.BeanUtils; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * excel工具类 + * + * @author 123 + */ +public class ExcelUtils { + + /** + * Excel导出 + * + * @param response response + * @param fileName 文件名 + * @param sheetName sheetName + * @param list 数据List + * @param pojoClass 对象Class + */ + public static void exportExcel(HttpServletResponse response, String fileName, String sheetName, List list, + Class pojoClass) throws IOException { + if (StrUtil.isBlank(fileName)) { + //当前日期 + fileName = DateUtils.format(new Date()); + } + + response.setContentType("application/vnd.ms-excel"); + response.setCharacterEncoding("UTF-8"); + fileName = URLUtil.encode(fileName, StandardCharsets.UTF_8); + response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx"); + EasyExcel.write(response.getOutputStream(), pojoClass).registerConverter(new LongStringConverter()).sheet(sheetName).doWrite(list); + } + + /** + * Excel导出,先sourceList转换成List,再导出 + * + * @param response response + * @param fileName 文件名 + * @param sheetName sheetName + * @param sourceList 原数据List + * @param targetClass 目标对象Class + */ + public static void exportExcelToTarget(HttpServletResponse response, String fileName, String sheetName, List sourceList, + Class targetClass) throws Exception { + List targetList = new ArrayList<>(sourceList.size()); + for (Object source : sourceList) { + Object target = targetClass.newInstance(); + BeanUtils.copyProperties(source, target); + targetList.add(target); + } + + exportExcel(response, fileName, sheetName, targetList, targetClass); + } + +} diff --git a/system-admin/src/main/java/com/weather/common/utils/TimeUtils.java b/system-admin/src/main/java/com/weather/common/utils/TimeUtils.java new file mode 100644 index 0000000..9baabd5 --- /dev/null +++ b/system-admin/src/main/java/com/weather/common/utils/TimeUtils.java @@ -0,0 +1,14 @@ +package com.weather.common.utils; + +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.Date; + +public class TimeUtils { + + public static LocalDate convertToLocalDateTime(Date date) { + return date.toInstant() + .atZone(ZoneId.systemDefault()) + .toLocalDate(); + } +} diff --git a/system-admin/src/main/java/com/weather/common/validator/group/AliyunGroup.java b/system-admin/src/main/java/com/weather/common/validator/group/AliyunGroup.java new file mode 100644 index 0000000..cf29693 --- /dev/null +++ b/system-admin/src/main/java/com/weather/common/validator/group/AliyunGroup.java @@ -0,0 +1,11 @@ + + +package com.weather.common.validator.group; + +/** + * 阿里云 + * + * @author 123 + */ +public interface AliyunGroup { +} diff --git a/system-admin/src/main/java/com/weather/common/validator/group/QcloudGroup.java b/system-admin/src/main/java/com/weather/common/validator/group/QcloudGroup.java new file mode 100644 index 0000000..0b328a8 --- /dev/null +++ b/system-admin/src/main/java/com/weather/common/validator/group/QcloudGroup.java @@ -0,0 +1,11 @@ + + +package com.weather.common.validator.group; + +/** + * 腾讯云 + * + * @author 123 + */ +public interface QcloudGroup { +} diff --git a/system-admin/src/main/java/com/weather/common/validator/group/QiniuGroup.java b/system-admin/src/main/java/com/weather/common/validator/group/QiniuGroup.java new file mode 100644 index 0000000..1558375 --- /dev/null +++ b/system-admin/src/main/java/com/weather/common/validator/group/QiniuGroup.java @@ -0,0 +1,11 @@ + + +package com.weather.common.validator.group; + +/** + * 七牛 + * + * @author 123 + */ +public interface QiniuGroup { +} diff --git a/system-admin/src/main/java/com/weather/modules/job/config/ScheduleConfig.java b/system-admin/src/main/java/com/weather/modules/job/config/ScheduleConfig.java new file mode 100644 index 0000000..8cb1a0b --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/job/config/ScheduleConfig.java @@ -0,0 +1,66 @@ +///** +// * Copyright (c) 2018 人人开源 All rights reserved. +// * +// * https://www.renren.io +// * +// * 版权所有,侵权必究! +// */ +// +//package io.weather.modules.job.config; +// +//import org.springframework.context.annotation.Bean; +//import org.springframework.context.annotation.Configuration; +//import org.springframework.scheduling.quartz.SchedulerFactoryBean; +// +//import javax.sql.DataSource; +//import java.util.Properties; +// +///** +// * 定时任务配置(备注:集群需要打开注释) +// * +// * @author 123 +// */ +//@Configuration +//public class ScheduleConfig { +// +// @Bean +// public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource) { +// SchedulerFactoryBean factory = new SchedulerFactoryBean(); +// factory.setDataSource(dataSource); +// +// //quartz参数 +// Properties prop = new Properties(); +// prop.put("org.quartz.scheduler.instanceName", "RenrenScheduler"); +// prop.put("org.quartz.scheduler.instanceId", "AUTO"); +// //线程池配置 +// prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool"); +// prop.put("org.quartz.threadPool.threadCount", "20"); +// prop.put("org.quartz.threadPool.threadPriority", "5"); +// //JobStore配置 +// prop.put("org.quartz.jobStore.class", "org.springframework.scheduling.quartz.LocalDataSourceJobStore"); +// //集群配置 +// prop.put("org.quartz.jobStore.isClustered", "true"); +// prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000"); +// prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1"); +// +// prop.put("org.quartz.jobStore.misfireThreshold", "12000"); +// prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_"); +// prop.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?"); +// +// //PostgreSQL数据库,需要打开此注释 +// //prop.put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.PostgreSQLDelegate"); +// +// factory.setQuartzProperties(prop); +// +// factory.setSchedulerName("RenrenScheduler"); +// //延时启动 +// factory.setStartupDelay(30); +// factory.setApplicationContextSchedulerContextKey("applicationContextKey"); +// //可选,QuartzScheduler 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了 +// factory.setOverwriteExistingJobs(true); +// //设置自动启动,默认为true +// factory.setAutoStartup(true); +// +// return factory; +// } +//} diff --git a/system-admin/src/main/java/com/weather/modules/job/controller/ScheduleJobController.java b/system-admin/src/main/java/com/weather/modules/job/controller/ScheduleJobController.java new file mode 100644 index 0000000..57c5cb7 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/job/controller/ScheduleJobController.java @@ -0,0 +1,126 @@ + +package com.weather.modules.job.controller; + +import com.weather.common.annotation.LogOperation; +import com.weather.common.constant.Constant; +import com.weather.common.page.PageData; +import com.weather.common.utils.Result; +import com.weather.common.validator.ValidatorUtils; +import com.weather.common.validator.group.AddGroup; +import com.weather.common.validator.group.DefaultGroup; +import com.weather.common.validator.group.UpdateGroup; +import com.weather.modules.job.dto.ScheduleJobDTO; +import com.weather.modules.job.service.ScheduleJobService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +/** + * 定时任务 + * + * @author 123 + */ +@RestController +@RequestMapping("/sys/schedule") +@Tag(name = "定时任务") +@AllArgsConstructor +public class ScheduleJobController { + private final ScheduleJobService scheduleJobService; + + @GetMapping("page") + @Operation(summary = "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", in = ParameterIn.QUERY, required = true, ref = "int"), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", in = ParameterIn.QUERY, required = true, ref = "int"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段", in = ParameterIn.QUERY, ref = "String"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)", in = ParameterIn.QUERY, ref = "String"), + @Parameter(name = "beanName", description = "beanName", in = ParameterIn.QUERY, ref = "String") + }) + @RequiresPermissions("sys:schedule:page") + public Result> page(@Parameter(hidden = true) @RequestParam Map params) { + PageData page = scheduleJobService.page(params); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary = "信息") + @RequiresPermissions("sys:schedule:info") + public Result info(@PathVariable("id") Long id) { + ScheduleJobDTO schedule = scheduleJobService.get(id); + + return new Result().ok(schedule); + } + + @PostMapping + @Operation(summary = "保存") + @LogOperation("保存") + @RequiresPermissions("sys:schedule:save") + public Result save(@RequestBody ScheduleJobDTO dto) { + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + scheduleJobService.save(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary = "修改") + @LogOperation("修改") + @RequiresPermissions("sys:schedule:update") + public Result update(@RequestBody ScheduleJobDTO dto) { + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + scheduleJobService.update(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary = "删除") + @LogOperation("删除") + @RequiresPermissions("sys:schedule:delete") + public Result delete(@RequestBody Long[] ids) { + scheduleJobService.deleteBatch(ids); + + return new Result(); + } + + @PutMapping("/run") + @Operation(summary = "立即执行") + @LogOperation("立即执行") + @RequiresPermissions("sys:schedule:run") + public Result run(@RequestBody Long[] ids) { + scheduleJobService.run(ids); + + return new Result(); + } + + @PutMapping("/pause") + @Operation(summary = "暂停") + @LogOperation("暂停") + @RequiresPermissions("sys:schedule:pause") + public Result pause(@RequestBody Long[] ids) { + scheduleJobService.pause(ids); + + return new Result(); + } + + @PutMapping("/resume") + @Operation(summary = "恢复") + @LogOperation("恢复") + @RequiresPermissions("sys:schedule:resume") + public Result resume(@RequestBody Long[] ids) { + scheduleJobService.resume(ids); + + return new Result(); + } + +} diff --git a/system-admin/src/main/java/com/weather/modules/job/controller/ScheduleJobLogController.java b/system-admin/src/main/java/com/weather/modules/job/controller/ScheduleJobLogController.java new file mode 100644 index 0000000..c915431 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/job/controller/ScheduleJobLogController.java @@ -0,0 +1,56 @@ + +package com.weather.modules.job.controller; + +import com.weather.common.constant.Constant; +import com.weather.common.page.PageData; +import com.weather.common.utils.Result; +import com.weather.modules.job.dto.ScheduleJobLogDTO; +import com.weather.modules.job.service.ScheduleJobLogService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +/** + * 定时任务日志 + * + * @author 123 + */ +@RestController +@RequestMapping("/sys/scheduleLog") +@Tag(name = "定时任务日志") +@AllArgsConstructor +public class ScheduleJobLogController { + private final ScheduleJobLogService scheduleJobLogService; + + @GetMapping("page") + @Operation(summary = "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", in = ParameterIn.QUERY, required = true, ref = "int"), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", in = ParameterIn.QUERY, required = true, ref = "int"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段", in = ParameterIn.QUERY, ref = "String"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)", in = ParameterIn.QUERY, ref = "String"), + @Parameter(name = "jobId", description = "jobId", in = ParameterIn.QUERY, ref = "String") + }) + @RequiresPermissions("sys:schedule:log") + public Result> page(@Parameter(hidden = true) @RequestParam Map params) { + PageData page = scheduleJobLogService.page(params); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary = "信息") + @RequiresPermissions("sys:schedule:log") + public Result info(@PathVariable("id") Long id) { + ScheduleJobLogDTO log = scheduleJobLogService.get(id); + + return new Result().ok(log); + } +} diff --git a/system-admin/src/main/java/com/weather/modules/job/dao/ScheduleJobDao.java b/system-admin/src/main/java/com/weather/modules/job/dao/ScheduleJobDao.java new file mode 100644 index 0000000..4a74239 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/job/dao/ScheduleJobDao.java @@ -0,0 +1,23 @@ + + +package com.weather.modules.job.dao; + +import com.weather.common.dao.BaseDao; +import com.weather.modules.job.entity.ScheduleJobEntity; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Map; + +/** + * 定时任务 + * + * @author 123 + */ +@Mapper +public interface ScheduleJobDao extends BaseDao { + + /** + * 批量更新状态 + */ + int updateBatch(Map map); +} diff --git a/system-admin/src/main/java/com/weather/modules/job/dao/ScheduleJobLogDao.java b/system-admin/src/main/java/com/weather/modules/job/dao/ScheduleJobLogDao.java new file mode 100644 index 0000000..e5faf6c --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/job/dao/ScheduleJobLogDao.java @@ -0,0 +1,17 @@ + + +package com.weather.modules.job.dao; + +import com.weather.common.dao.BaseDao; +import com.weather.modules.job.entity.ScheduleJobLogEntity; +import org.apache.ibatis.annotations.Mapper; + +/** + * 定时任务日志 + * + * @author 123 + */ +@Mapper +public interface ScheduleJobLogDao extends BaseDao { + +} diff --git a/system-admin/src/main/java/com/weather/modules/job/dto/ScheduleJobDTO.java b/system-admin/src/main/java/com/weather/modules/job/dto/ScheduleJobDTO.java new file mode 100644 index 0000000..484209a --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/job/dto/ScheduleJobDTO.java @@ -0,0 +1,57 @@ + + +package com.weather.modules.job.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.weather.common.validator.group.AddGroup; +import com.weather.common.validator.group.DefaultGroup; +import com.weather.common.validator.group.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Null; +import lombok.Data; +import org.hibernate.validator.constraints.Range; + +import java.io.Serializable; +import java.util.Date; + +/** + * 定时任务 + * + * @author 123 + * @since 1.0.0 + */ +@Data +@Schema(title = "定时任务") +public class ScheduleJobDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(title = "id") + @Null(message="{id.null}", groups = AddGroup.class) + @NotNull(message="{id.require}", groups = UpdateGroup.class) + private Long id; + + @Schema(title = "spring bean名称") + @NotBlank(message = "{schedule.bean.require}", groups = DefaultGroup.class) + private String beanName; + + @Schema(title = "参数") + private String params; + + @Schema(title = "cron表达式") + @NotBlank(message = "{schedule.cron.require}", groups = DefaultGroup.class) + private String cronExpression; + + @Schema(title = "任务状态 0:暂停 1:正常") + @Range(min=0, max=1, message = "{schedule.status.range}", groups = DefaultGroup.class) + private Integer status; + + @Schema(title = "备注") + private String remark; + + @Schema(title = "创建时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private Date createDate; + +} diff --git a/system-admin/src/main/java/com/weather/modules/job/dto/ScheduleJobLogDTO.java b/system-admin/src/main/java/com/weather/modules/job/dto/ScheduleJobLogDTO.java new file mode 100644 index 0000000..9f53090 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/job/dto/ScheduleJobLogDTO.java @@ -0,0 +1,46 @@ + + +package com.weather.modules.job.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * 定时任务日志 + * + * @author 123 + * @since 1.0.0 + */ +@Data +@Schema(title = "定时任务日志") +public class ScheduleJobLogDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(title = "id") + private Long id; + + @Schema(title = "任务id") + private Long jobId; + + @Schema(title = "spring bean名称") + private String beanName; + + @Schema(title = "参数") + private String params; + + @Schema(title = "任务状态 0:失败 1:成功") + private Integer status; + + @Schema(title = "失败信息") + private String error; + + @Schema(title = "耗时(单位:毫秒)") + private Integer times; + + @Schema(title = "创建时间") + private Date createDate; + +} diff --git a/system-admin/src/main/java/com/weather/modules/job/entity/ScheduleJobEntity.java b/system-admin/src/main/java/com/weather/modules/job/entity/ScheduleJobEntity.java new file mode 100644 index 0000000..1369941 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/job/entity/ScheduleJobEntity.java @@ -0,0 +1,55 @@ + + +package com.weather.modules.job.entity; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.weather.common.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * 定时任务 + * + * @author 123 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@TableName("schedule_job") +public class ScheduleJobEntity extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** + * spring bean名称 + */ + private String beanName; + /** + * 参数 + */ + private String params; + /** + * cron表达式 + */ + private String cronExpression; + /** + * 任务状态 0:暂停 1:正常 + */ + private Integer status; + /** + * 备注 + */ + private String remark; + /** + * 更新者 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private Long updater; + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private Date updateDate; +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/job/entity/ScheduleJobLogEntity.java b/system-admin/src/main/java/com/weather/modules/job/entity/ScheduleJobLogEntity.java new file mode 100644 index 0000000..b889a8a --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/job/entity/ScheduleJobLogEntity.java @@ -0,0 +1,56 @@ + + +package com.weather.modules.job.entity; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * 定时任务日志 + * + * @author 123 + */ +@Data +@TableName("schedule_job_log") +public class ScheduleJobLogEntity implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * id + */ + @TableId + private Long id; + /** + * 任务id + */ + private Long jobId; + /** + * spring bean名称 + */ + private String beanName; + /** + * 参数 + */ + private String params; + /** + * 任务状态 0:失败 1:成功 + */ + private Integer status; + /** + * 失败信息 + */ + private String error; + /** + * 耗时(单位:毫秒) + */ + private Integer times; + /** + * 创建时间 + */ + private Date createDate; + +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/job/init/JobCommandLineRunner.java b/system-admin/src/main/java/com/weather/modules/job/init/JobCommandLineRunner.java new file mode 100644 index 0000000..b4887bf --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/job/init/JobCommandLineRunner.java @@ -0,0 +1,39 @@ + +package com.weather.modules.job.init; + +import com.weather.modules.job.dao.ScheduleJobDao; +import com.weather.modules.job.entity.ScheduleJobEntity; +import com.weather.modules.job.utils.ScheduleUtils; +import lombok.AllArgsConstructor; +import org.quartz.CronTrigger; +import org.quartz.Scheduler; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * 初始化定时任务数据 + * + * @author 123 + */ +@Component +@AllArgsConstructor +public class JobCommandLineRunner implements CommandLineRunner { + private final Scheduler scheduler; + private final ScheduleJobDao scheduleJobDao; + + @Override + public void run(String... args) { + List scheduleJobList = scheduleJobDao.selectList(null); + for (ScheduleJobEntity scheduleJob : scheduleJobList) { + CronTrigger cronTrigger = ScheduleUtils.getCronTrigger(scheduler, scheduleJob.getId()); + //如果不存在,则创建 + if (cronTrigger == null) { + ScheduleUtils.createScheduleJob(scheduler, scheduleJob); + } else { + ScheduleUtils.updateScheduleJob(scheduler, scheduleJob); + } + } + } +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/job/service/ScheduleJobLogService.java b/system-admin/src/main/java/com/weather/modules/job/service/ScheduleJobLogService.java new file mode 100644 index 0000000..8e453d5 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/job/service/ScheduleJobLogService.java @@ -0,0 +1,22 @@ + + +package com.weather.modules.job.service; + +import com.weather.common.page.PageData; +import com.weather.common.service.BaseService; +import com.weather.modules.job.dto.ScheduleJobLogDTO; +import com.weather.modules.job.entity.ScheduleJobLogEntity; + +import java.util.Map; + +/** + * 定时任务日志 + * + * @author 123 + */ +public interface ScheduleJobLogService extends BaseService { + + PageData page(Map params); + + ScheduleJobLogDTO get(Long id); +} diff --git a/system-admin/src/main/java/com/weather/modules/job/service/ScheduleJobService.java b/system-admin/src/main/java/com/weather/modules/job/service/ScheduleJobService.java new file mode 100644 index 0000000..e042e5a --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/job/service/ScheduleJobService.java @@ -0,0 +1,57 @@ + + +package com.weather.modules.job.service; + +import com.weather.common.page.PageData; +import com.weather.common.service.BaseService; +import com.weather.modules.job.dto.ScheduleJobDTO; +import com.weather.modules.job.entity.ScheduleJobEntity; + +import java.util.Map; + +/** + * 定时任务 + * + * @author 123 + */ +public interface ScheduleJobService extends BaseService { + + PageData page(Map params); + + ScheduleJobDTO get(Long id); + + /** + * 保存定时任务 + */ + void save(ScheduleJobDTO dto); + + /** + * 更新定时任务 + */ + void update(ScheduleJobDTO dto); + + /** + * 批量删除定时任务 + */ + void deleteBatch(Long[] ids); + + /** + * 批量更新定时任务状态 + */ + int updateBatch(Long[] ids, int status); + + /** + * 立即执行 + */ + void run(Long[] ids); + + /** + * 暂停运行 + */ + void pause(Long[] ids); + + /** + * 恢复运行 + */ + void resume(Long[] ids); +} diff --git a/system-admin/src/main/java/com/weather/modules/job/service/impl/ScheduleJobLogServiceImpl.java b/system-admin/src/main/java/com/weather/modules/job/service/impl/ScheduleJobLogServiceImpl.java new file mode 100644 index 0000000..6e74160 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/job/service/impl/ScheduleJobLogServiceImpl.java @@ -0,0 +1,48 @@ + + +package com.weather.modules.job.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.weather.common.constant.Constant; +import com.weather.common.page.PageData; +import com.weather.common.service.impl.BaseServiceImpl; +import com.weather.common.utils.ConvertUtils; +import com.weather.modules.job.dao.ScheduleJobLogDao; +import com.weather.modules.job.dto.ScheduleJobLogDTO; +import com.weather.modules.job.entity.ScheduleJobLogEntity; +import com.weather.modules.job.service.ScheduleJobLogService; +import cn.hutool.core.util.StrUtil; +import org.springframework.stereotype.Service; + +import java.util.Map; + +@Service +public class ScheduleJobLogServiceImpl extends BaseServiceImpl implements ScheduleJobLogService { + + @Override + public PageData page(Map params) { + IPage page = baseDao.selectPage( + getPage(params, Constant.CREATE_DATE, false), + getWrapper(params) + ); + return getPageData(page, ScheduleJobLogDTO.class); + } + + private QueryWrapper getWrapper(Map params){ + String jobId = (String)params.get("jobId"); + + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.eq(StrUtil.isNotBlank(jobId), "job_id", jobId); + + return wrapper; + } + + @Override + public ScheduleJobLogDTO get(Long id) { + ScheduleJobLogEntity entity = baseDao.selectById(id); + + return ConvertUtils.sourceToTarget(entity, ScheduleJobLogDTO.class); + } + +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/job/service/impl/ScheduleJobServiceImpl.java b/system-admin/src/main/java/com/weather/modules/job/service/impl/ScheduleJobServiceImpl.java new file mode 100644 index 0000000..bd88429 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/job/service/impl/ScheduleJobServiceImpl.java @@ -0,0 +1,123 @@ + +package com.weather.modules.job.service.impl; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.weather.common.constant.Constant; +import com.weather.common.page.PageData; +import com.weather.common.service.impl.BaseServiceImpl; +import com.weather.common.utils.ConvertUtils; +import com.weather.modules.job.dao.ScheduleJobDao; +import com.weather.modules.job.dto.ScheduleJobDTO; +import com.weather.modules.job.entity.ScheduleJobEntity; +import com.weather.modules.job.service.ScheduleJobService; +import com.weather.modules.job.utils.ScheduleUtils; +import lombok.AllArgsConstructor; +import org.quartz.Scheduler; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +@Service +@AllArgsConstructor +public class ScheduleJobServiceImpl extends BaseServiceImpl implements ScheduleJobService { + private final Scheduler scheduler; + + @Override + public PageData page(Map params) { + IPage page = baseDao.selectPage( + getPage(params, Constant.CREATE_DATE, false), + getWrapper(params) + ); + return getPageData(page, ScheduleJobDTO.class); + } + + @Override + public ScheduleJobDTO get(Long id) { + ScheduleJobEntity entity = baseDao.selectById(id); + + return ConvertUtils.sourceToTarget(entity, ScheduleJobDTO.class); + } + + private QueryWrapper getWrapper(Map params) { + String beanName = (String) params.get("beanName"); + + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.like(StrUtil.isNotBlank(beanName), "bean_name", beanName); + + return wrapper; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void save(ScheduleJobDTO dto) { + ScheduleJobEntity entity = ConvertUtils.sourceToTarget(dto, ScheduleJobEntity.class); + + entity.setStatus(Constant.ScheduleStatus.NORMAL.getValue()); + this.insert(entity); + + ScheduleUtils.createScheduleJob(scheduler, entity); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(ScheduleJobDTO dto) { + ScheduleJobEntity entity = ConvertUtils.sourceToTarget(dto, ScheduleJobEntity.class); + + ScheduleUtils.updateScheduleJob(scheduler, entity); + + this.updateById(entity); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteBatch(Long[] ids) { + for (Long id : ids) { + ScheduleUtils.deleteScheduleJob(scheduler, id); + } + + //删除数据 + this.deleteBatchIds(Arrays.asList(ids)); + } + + @Override + public int updateBatch(Long[] ids, int status) { + Map map = new HashMap<>(2); + map.put("ids", ids); + map.put("status", status); + return baseDao.updateBatch(map); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void run(Long[] ids) { + for (Long id : ids) { + ScheduleUtils.run(scheduler, this.selectById(id)); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void pause(Long[] ids) { + for (Long id : ids) { + ScheduleUtils.pauseJob(scheduler, id); + } + + updateBatch(ids, Constant.ScheduleStatus.PAUSE.getValue()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void resume(Long[] ids) { + for (Long id : ids) { + ScheduleUtils.resumeJob(scheduler, id); + } + + updateBatch(ids, Constant.ScheduleStatus.NORMAL.getValue()); + } + +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/job/task/ITask.java b/system-admin/src/main/java/com/weather/modules/job/task/ITask.java new file mode 100644 index 0000000..0a8e961 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/job/task/ITask.java @@ -0,0 +1,19 @@ + + +package com.weather.modules.job.task; + +/** + * 定时任务接口,所有定时任务都要实现该接口 + * + * @author 123 + * @since 1.0.0 + */ +public interface ITask { + + /** + * 执行定时任务接口 + * + * @param params 参数,多参数使用JSON数据 + */ + void run(String params); +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/job/task/TestTask.java b/system-admin/src/main/java/com/weather/modules/job/task/TestTask.java new file mode 100644 index 0000000..94e6c3e --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/job/task/TestTask.java @@ -0,0 +1,24 @@ + + +package com.weather.modules.job.task; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +/** + * 测试定时任务(演示Demo,可删除) + * + * testTask为spring bean的名称 + * + * @author 123 + */ +@Component("testTask") +public class TestTask implements ITask{ + private Logger logger = LoggerFactory.getLogger(getClass()); + + @Override + public void run(String params){ + logger.debug("TestTask定时任务正在执行,参数为:{}", params); + } +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/job/utils/ScheduleJob.java b/system-admin/src/main/java/com/weather/modules/job/utils/ScheduleJob.java new file mode 100644 index 0000000..7a4614d --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/job/utils/ScheduleJob.java @@ -0,0 +1,73 @@ + + +package com.weather.modules.job.utils; + +import com.weather.common.constant.Constant; +import com.weather.common.exception.ExceptionUtils; +import com.weather.common.utils.SpringContextUtils; +import com.weather.modules.job.entity.ScheduleJobEntity; +import com.weather.modules.job.entity.ScheduleJobLogEntity; +import com.weather.modules.job.service.ScheduleJobLogService; +import org.quartz.JobExecutionContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.scheduling.quartz.QuartzJobBean; + +import java.lang.reflect.Method; +import java.util.Date; + + +/** + * 定时任务 + * + * @author 123 + */ +public class ScheduleJob extends QuartzJobBean { + private Logger logger = LoggerFactory.getLogger(getClass()); + + @Override + protected void executeInternal(JobExecutionContext context) { + ScheduleJobEntity scheduleJob = (ScheduleJobEntity) context.getMergedJobDataMap(). + get(ScheduleUtils.JOB_PARAM_KEY); + + //数据库保存执行记录 + ScheduleJobLogEntity log = new ScheduleJobLogEntity(); + log.setJobId(scheduleJob.getId()); + log.setBeanName(scheduleJob.getBeanName()); + log.setParams(scheduleJob.getParams()); + log.setCreateDate(new Date()); + + //任务开始时间 + long startTime = System.currentTimeMillis(); + + try { + //执行任务 + logger.info("任务准备执行,任务ID:{}", scheduleJob.getId()); + Object target = SpringContextUtils.getBean(scheduleJob.getBeanName()); + Method method = target.getClass().getDeclaredMethod("run", String.class); + method.invoke(target, scheduleJob.getParams()); + + //任务执行总时长 + long times = System.currentTimeMillis() - startTime; + log.setTimes((int)times); + //任务状态 + log.setStatus(Constant.SUCCESS); + + logger.info("任务执行完毕,任务ID:{} 总共耗时:{} 毫秒", scheduleJob.getId(), times); + } catch (Exception e) { + logger.error("任务执行失败,任务ID:{}", scheduleJob.getId(), e); + + //任务执行总时长 + long times = System.currentTimeMillis() - startTime; + log.setTimes((int)times); + + //任务状态 + log.setStatus(Constant.FAIL); + log.setError(ExceptionUtils.getErrorStackTrace(e)); + }finally { + //获取spring bean + ScheduleJobLogService scheduleJobLogService = SpringContextUtils.getBean(ScheduleJobLogService.class); + scheduleJobLogService.insert(log); + } + } +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/job/utils/ScheduleUtils.java b/system-admin/src/main/java/com/weather/modules/job/utils/ScheduleUtils.java new file mode 100644 index 0000000..148ee59 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/job/utils/ScheduleUtils.java @@ -0,0 +1,155 @@ + + +package com.weather.modules.job.utils; + +import com.weather.common.constant.Constant; +import com.weather.common.exception.ErrorCode; +import com.weather.common.exception.CommonException; +import com.weather.modules.job.entity.ScheduleJobEntity; +import org.quartz.*; + +/** + * 定时任务工具类 + * + * @author 123 + */ +public class ScheduleUtils { + private final static String JOB_NAME = "TASK_"; + /** + * 任务调度参数key + */ + public static final String JOB_PARAM_KEY = "JOB_PARAM_KEY"; + + /** + * 获取触发器key + */ + public static TriggerKey getTriggerKey(Long jobId) { + return TriggerKey.triggerKey(JOB_NAME + jobId); + } + + /** + * 获取jobKey + */ + public static JobKey getJobKey(Long jobId) { + return JobKey.jobKey(JOB_NAME + jobId); + } + + /** + * 获取表达式触发器 + */ + public static CronTrigger getCronTrigger(Scheduler scheduler, Long jobId) { + try { + return (CronTrigger) scheduler.getTrigger(getTriggerKey(jobId)); + } catch (SchedulerException e) { + throw new CommonException(ErrorCode.JOB_ERROR, e); + } + } + + /** + * 创建定时任务 + */ + public static void createScheduleJob(Scheduler scheduler, ScheduleJobEntity scheduleJob) { + try { + //构建job信息 + JobDetail jobDetail = JobBuilder.newJob(ScheduleJob.class).withIdentity(getJobKey(scheduleJob.getId())).build(); + + //表达式调度构建器 + CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression()) + .withMisfireHandlingInstructionDoNothing(); + + //按新的cronExpression表达式构建一个新的trigger + CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(scheduleJob.getId())).withSchedule(scheduleBuilder).build(); + + //放入参数,运行时的方法可以获取 + jobDetail.getJobDataMap().put(JOB_PARAM_KEY, scheduleJob); + + scheduler.scheduleJob(jobDetail, trigger); + + //暂停任务 + if(scheduleJob.getStatus() == Constant.ScheduleStatus.PAUSE.getValue()){ + pauseJob(scheduler, scheduleJob.getId()); + } + } catch (SchedulerException e) { + throw new CommonException(ErrorCode.JOB_ERROR, e); + } + } + + /** + * 更新定时任务 + */ + public static void updateScheduleJob(Scheduler scheduler, ScheduleJobEntity scheduleJob) { + try { + TriggerKey triggerKey = getTriggerKey(scheduleJob.getId()); + + //表达式调度构建器 + CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression()) + .withMisfireHandlingInstructionDoNothing(); + + CronTrigger trigger = getCronTrigger(scheduler, scheduleJob.getId()); + + //按新的cronExpression表达式重新构建trigger + trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build(); + + //参数 + trigger.getJobDataMap().put(JOB_PARAM_KEY, scheduleJob); + + scheduler.rescheduleJob(triggerKey, trigger); + + //暂停任务 + if(scheduleJob.getStatus() == Constant.ScheduleStatus.PAUSE.getValue()){ + pauseJob(scheduler, scheduleJob.getId()); + } + + } catch (SchedulerException e) { + throw new CommonException(ErrorCode.JOB_ERROR, e); + } + } + + /** + * 立即执行任务 + */ + public static void run(Scheduler scheduler, ScheduleJobEntity scheduleJob) { + try { + //参数 + JobDataMap dataMap = new JobDataMap(); + dataMap.put(JOB_PARAM_KEY, scheduleJob); + + scheduler.triggerJob(getJobKey(scheduleJob.getId()), dataMap); + } catch (SchedulerException e) { + throw new CommonException(ErrorCode.JOB_ERROR, e); + } + } + + /** + * 暂停任务 + */ + public static void pauseJob(Scheduler scheduler, Long jobId) { + try { + scheduler.pauseJob(getJobKey(jobId)); + } catch (SchedulerException e) { + throw new CommonException(ErrorCode.JOB_ERROR, e); + } + } + + /** + * 恢复任务 + */ + public static void resumeJob(Scheduler scheduler, Long jobId) { + try { + scheduler.resumeJob(getJobKey(jobId)); + } catch (SchedulerException e) { + throw new CommonException(ErrorCode.JOB_ERROR, e); + } + } + + /** + * 删除定时任务 + */ + public static void deleteScheduleJob(Scheduler scheduler, Long jobId) { + try { + scheduler.deleteJob(getJobKey(jobId)); + } catch (SchedulerException e) { + throw new CommonException(ErrorCode.JOB_ERROR, e); + } + } +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/log/controller/SysLogErrorController.java b/system-admin/src/main/java/com/weather/modules/log/controller/SysLogErrorController.java new file mode 100644 index 0000000..d4e99e2 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/log/controller/SysLogErrorController.java @@ -0,0 +1,67 @@ + +package com.weather.modules.log.controller; + +import com.weather.common.annotation.LogOperation; +import com.weather.common.constant.Constant; +import com.weather.common.page.PageData; +import com.weather.common.utils.ExcelUtils; +import com.weather.common.utils.Result; +import com.weather.modules.log.dto.SysLogErrorDTO; +import com.weather.modules.log.excel.SysLogErrorExcel; +import com.weather.modules.log.service.SysLogErrorService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import lombok.AllArgsConstructor; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; +import java.util.Map; + + +/** + * 异常日志 + * + * @author 123 + * @since 1.0.0 + */ +@RestController +@RequestMapping("sys/log/error") +@Tag(name = "异常日志") +@AllArgsConstructor +public class SysLogErrorController { + private final SysLogErrorService sysLogErrorService; + + @GetMapping("page") + @Operation(summary = "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", in = ParameterIn.QUERY, required = true, ref = "int"), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", in = ParameterIn.QUERY, required = true, ref = "int"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段", in = ParameterIn.QUERY, ref = "String"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)", in = ParameterIn.QUERY, ref = "String") + }) + @RequiresPermissions("sys:log:error") + public Result> page(@Parameter(hidden = true) @RequestParam Map params) { + PageData page = sysLogErrorService.page(params); + + return new Result>().ok(page); + } + + @GetMapping("export") + @Operation(summary = "导出") + @LogOperation("导出") + @RequiresPermissions("sys:log:error") + public void export(@Parameter(hidden = true) @RequestParam Map params, HttpServletResponse response) throws Exception { + List list = sysLogErrorService.list(params); + + ExcelUtils.exportExcelToTarget(response, null, "异常日志", list, SysLogErrorExcel.class); + } + +} diff --git a/system-admin/src/main/java/com/weather/modules/log/controller/SysLogLoginController.java b/system-admin/src/main/java/com/weather/modules/log/controller/SysLogLoginController.java new file mode 100644 index 0000000..695e953 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/log/controller/SysLogLoginController.java @@ -0,0 +1,74 @@ + +package com.weather.modules.log.controller; + +import com.weather.common.annotation.LogOperation; +import com.weather.common.constant.Constant; +import com.weather.common.page.PageData; +import com.weather.common.utils.ExcelUtils; +import com.weather.common.utils.Result; +import com.weather.modules.log.dto.SysLogLoginDTO; +import com.weather.modules.log.excel.SysLogLoginExcel; +import com.weather.modules.log.service.SysLogLoginService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import lombok.AllArgsConstructor; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; +import java.util.Map; + + +/** + * 登录日志 + * + * @author 123 + * @since 1.0.0 + */ +@RestController +@RequestMapping("sys/log/login") +@Tag(name = "登录日志") +@AllArgsConstructor +public class SysLogLoginController { + private final SysLogLoginService sysLogLoginService; + + @GetMapping("page") + @Operation(summary = "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", in = ParameterIn.QUERY, required = true, ref = "int"), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", in = ParameterIn.QUERY, required = true, ref = "int"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段", in = ParameterIn.QUERY, ref = "String"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)", in = ParameterIn.QUERY, ref = "String"), + @Parameter(name = "status", description = "状态 0:失败 1:成功 2:账号已锁定", in = ParameterIn.QUERY, ref = "int"), + @Parameter(name = "creatorName", description = "用户名", in = ParameterIn.QUERY, ref = "String") + }) + @RequiresPermissions("sys:log:login") + public Result> page(@Parameter(hidden = true) @RequestParam Map params) { + PageData page = sysLogLoginService.page(params); + + return new Result>().ok(page); + } + + @GetMapping("export") + @Operation(summary = "导出") + @LogOperation("导出") + @Parameters({ + @Parameter(name = "status", description = "状态 0:失败 1:成功 2:账号已锁定", in = ParameterIn.QUERY, ref = "int"), + @Parameter(name = "creatorName", description = "用户名", in = ParameterIn.QUERY, ref = "String") + }) + @RequiresPermissions("sys:log:login") + public void export(@Parameter(hidden = true) @RequestParam Map params, HttpServletResponse response) throws Exception { + List list = sysLogLoginService.list(params); + + ExcelUtils.exportExcelToTarget(response, null, "登录日志", list, SysLogLoginExcel.class); + + } + +} diff --git a/system-admin/src/main/java/com/weather/modules/log/controller/SysLogOperationController.java b/system-admin/src/main/java/com/weather/modules/log/controller/SysLogOperationController.java new file mode 100644 index 0000000..3290888 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/log/controller/SysLogOperationController.java @@ -0,0 +1,68 @@ + +package com.weather.modules.log.controller; + +import com.weather.common.annotation.LogOperation; +import com.weather.common.constant.Constant; +import com.weather.common.page.PageData; +import com.weather.common.utils.ExcelUtils; +import com.weather.common.utils.Result; +import com.weather.modules.log.dto.SysLogOperationDTO; +import com.weather.modules.log.excel.SysLogOperationExcel; +import com.weather.modules.log.service.SysLogOperationService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import lombok.AllArgsConstructor; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; +import java.util.Map; + + +/** + * 操作日志 + * + * @author 123 + * @since 1.0.0 + */ +@RestController +@RequestMapping("sys/log/operation") +@Tag(name = "操作日志") +@AllArgsConstructor +public class SysLogOperationController { + private final SysLogOperationService sysLogOperationService; + + @GetMapping("page") + @Operation(summary = "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", in = ParameterIn.QUERY, required = true, ref = "int"), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", in = ParameterIn.QUERY, required = true, ref = "int"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段", in = ParameterIn.QUERY, ref = "String"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)", in = ParameterIn.QUERY, ref = "String"), + @Parameter(name = "status", description = "状态 0:失败 1:成功", in = ParameterIn.QUERY, ref = "int") + }) + @RequiresPermissions("sys:log:operation") + public Result> page(@Parameter(hidden = true) @RequestParam Map params) { + PageData page = sysLogOperationService.page(params); + + return new Result>().ok(page); + } + + @GetMapping("export") + @Operation(summary = "导出") + @LogOperation("导出") + @RequiresPermissions("sys:log:operation") + public void export(@Parameter(hidden = true) @RequestParam Map params, HttpServletResponse response) throws Exception { + List list = sysLogOperationService.list(params); + + ExcelUtils.exportExcelToTarget(response, null, "操作日志", list, SysLogOperationExcel.class); + } + +} diff --git a/system-admin/src/main/java/com/weather/modules/log/dao/SysLogErrorDao.java b/system-admin/src/main/java/com/weather/modules/log/dao/SysLogErrorDao.java new file mode 100644 index 0000000..1c77599 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/log/dao/SysLogErrorDao.java @@ -0,0 +1,18 @@ + + +package com.weather.modules.log.dao; + +import com.weather.common.dao.BaseDao; +import com.weather.modules.log.entity.SysLogErrorEntity; +import org.apache.ibatis.annotations.Mapper; + +/** + * 异常日志 + * + * @author 123 + * @since 1.0.0 + */ +@Mapper +public interface SysLogErrorDao extends BaseDao { + +} diff --git a/system-admin/src/main/java/com/weather/modules/log/dao/SysLogLoginDao.java b/system-admin/src/main/java/com/weather/modules/log/dao/SysLogLoginDao.java new file mode 100644 index 0000000..ef73f79 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/log/dao/SysLogLoginDao.java @@ -0,0 +1,18 @@ + + +package com.weather.modules.log.dao; + +import com.weather.common.dao.BaseDao; +import com.weather.modules.log.entity.SysLogLoginEntity; +import org.apache.ibatis.annotations.Mapper; + +/** + * 登录日志 + * + * @author 123 + * @since 1.0.0 + */ +@Mapper +public interface SysLogLoginDao extends BaseDao { + +} diff --git a/system-admin/src/main/java/com/weather/modules/log/dao/SysLogOperationDao.java b/system-admin/src/main/java/com/weather/modules/log/dao/SysLogOperationDao.java new file mode 100644 index 0000000..80c3920 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/log/dao/SysLogOperationDao.java @@ -0,0 +1,18 @@ + + +package com.weather.modules.log.dao; + +import com.weather.common.dao.BaseDao; +import com.weather.modules.log.entity.SysLogOperationEntity; +import org.apache.ibatis.annotations.Mapper; + +/** + * 操作日志 + * + * @author 123 + * @since 1.0.0 + */ +@Mapper +public interface SysLogOperationDao extends BaseDao { + +} diff --git a/system-admin/src/main/java/com/weather/modules/log/dto/SysLogErrorDTO.java b/system-admin/src/main/java/com/weather/modules/log/dto/SysLogErrorDTO.java new file mode 100644 index 0000000..0d0594f --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/log/dto/SysLogErrorDTO.java @@ -0,0 +1,39 @@ + + +package com.weather.modules.log.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * 异常日志 + * + * @author 123 + * @since 1.0.0 + */ +@Data +@Schema(title = "异常日志") +public class SysLogErrorDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(title = "id") + private Long id; + @Schema(title = "请求URI") + private String requestUri; + @Schema(title = "请求方式") + private String requestMethod; + @Schema(title = "请求参数") + private String requestParams; + @Schema(title = "用户代理") + private String userAgent; + @Schema(title = "操作IP") + private String ip; + @Schema(title = "异常信息") + private String errorInfo; + @Schema(title = "创建时间") + private Date createDate; + +} diff --git a/system-admin/src/main/java/com/weather/modules/log/dto/SysLogLoginDTO.java b/system-admin/src/main/java/com/weather/modules/log/dto/SysLogLoginDTO.java new file mode 100644 index 0000000..1620caa --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/log/dto/SysLogLoginDTO.java @@ -0,0 +1,43 @@ + + +package com.weather.modules.log.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * 登录日志 + * + * @author 123 + * @since 1.0.0 + */ +@Data +@Schema(title = "登录日志") +public class SysLogLoginDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(title = "id") + private Long id; + + @Schema(title = "用户操作 0:用户登录 1:用户退出") + private Integer operation; + + @Schema(title = "状态 0:失败 1:成功 2:账号已锁定") + private Integer status; + + @Schema(title = "用户代理") + private String userAgent; + + @Schema(title = "操作IP") + private String ip; + + @Schema(title = "用户名") + private String creatorName; + + @Schema(title = "创建时间") + private Date createDate; + +} diff --git a/system-admin/src/main/java/com/weather/modules/log/dto/SysLogOperationDTO.java b/system-admin/src/main/java/com/weather/modules/log/dto/SysLogOperationDTO.java new file mode 100644 index 0000000..c4c14d0 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/log/dto/SysLogOperationDTO.java @@ -0,0 +1,55 @@ + + +package com.weather.modules.log.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * 操作日志 + * + * @author 123 + * @since 1.0.0 + */ +@Data +@Schema(title = "操作日志") +public class SysLogOperationDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(title = "id") + private Long id; + + @Schema(title = "用户操作") + private String operation; + + @Schema(title = "请求URI") + private String requestUri; + + @Schema(title = "请求方式") + private String requestMethod; + + @Schema(title = "请求参数") + private String requestParams; + + @Schema(title = "请求时长(毫秒)") + private Integer requestTime; + + @Schema(title = "用户代理") + private String userAgent; + + @Schema(title = "操作IP") + private String ip; + + @Schema(title = "状态 0:失败 1:成功") + private Integer status; + + @Schema(title = "用户名") + private String creatorName; + + @Schema(title = "创建时间") + private Date createDate; + +} diff --git a/system-admin/src/main/java/com/weather/modules/log/entity/SysLogErrorEntity.java b/system-admin/src/main/java/com/weather/modules/log/entity/SysLogErrorEntity.java new file mode 100644 index 0000000..237306e --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/log/entity/SysLogErrorEntity.java @@ -0,0 +1,47 @@ + + +package com.weather.modules.log.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.weather.common.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 异常日志 + * + * @author 123 + * @since 1.0.0 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@TableName("sys_log_error") +public class SysLogErrorEntity extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** + * 请求URI + */ + private String requestUri; + /** + * 请求方式 + */ + private String requestMethod; + /** + * 请求参数 + */ + private String requestParams; + /** + * 用户代理 + */ + private String userAgent; + /** + * 操作IP + */ + private String ip; + /** + * 异常信息 + */ + private String errorInfo; + +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/log/entity/SysLogLoginEntity.java b/system-admin/src/main/java/com/weather/modules/log/entity/SysLogLoginEntity.java new file mode 100644 index 0000000..5df1ab4 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/log/entity/SysLogLoginEntity.java @@ -0,0 +1,43 @@ + + +package com.weather.modules.log.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.weather.common.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 登录日志 + * + * @author 123 + * @since 1.0.0 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@TableName("sys_log_login") +public class SysLogLoginEntity extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** + * 用户操作 0:用户登录 1:用户退出 + */ + private Integer operation; + /** + * 状态 0:失败 1:成功 2:账号已锁定 + */ + private Integer status; + /** + * 用户代理 + */ + private String userAgent; + /** + * 操作IP + */ + private String ip; + /** + * 用户名 + */ + private String creatorName; + +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/log/entity/SysLogOperationEntity.java b/system-admin/src/main/java/com/weather/modules/log/entity/SysLogOperationEntity.java new file mode 100644 index 0000000..e1d1b61 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/log/entity/SysLogOperationEntity.java @@ -0,0 +1,58 @@ + + +package com.weather.modules.log.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.weather.common.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 操作日志 + * + * @author 123 + * @since 1.0.0 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@TableName("sys_log_operation") +public class SysLogOperationEntity extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** + * 用户操作 + */ + private String operation; + /** + * 请求URI + */ + private String requestUri; + /** + * 请求方式 + */ + private String requestMethod; + /** + * 请求参数 + */ + private String requestParams; + /** + * 请求时长(毫秒) + */ + private Integer requestTime; + /** + * 用户代理 + */ + private String userAgent; + /** + * 操作IP + */ + private String ip; + /** + * 状态 0:失败 1:成功 + */ + private Integer status; + /** + * 用户名 + */ + private String creatorName; +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/log/enums/LoginOperationEnum.java b/system-admin/src/main/java/com/weather/modules/log/enums/LoginOperationEnum.java new file mode 100644 index 0000000..1c4161c --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/log/enums/LoginOperationEnum.java @@ -0,0 +1,30 @@ + + +package com.weather.modules.log.enums; + +/** + * 登录操作枚举 + * + * @author 123 + * @since 1.0.0 + */ +public enum LoginOperationEnum { + /** + * 用户登录 + */ + LOGIN(0), + /** + * 用户退出 + */ + LOGOUT(1); + + private int value; + + LoginOperationEnum(int value) { + this.value = value; + } + + public int value() { + return this.value; + } +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/log/enums/LoginStatusEnum.java b/system-admin/src/main/java/com/weather/modules/log/enums/LoginStatusEnum.java new file mode 100644 index 0000000..1bb0664 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/log/enums/LoginStatusEnum.java @@ -0,0 +1,34 @@ + + +package com.weather.modules.log.enums; + +/** + * 登录状态枚举 + * + * @author 123 + * @since 1.0.0 + */ +public enum LoginStatusEnum { + /** + * 失败 + */ + FAIL(0), + /** + * 成功 + */ + SUCCESS(1), + /** + * 账号已锁定 + */ + LOCK(2); + + private int value; + + LoginStatusEnum(int value) { + this.value = value; + } + + public int value() { + return this.value; + } +} diff --git a/system-admin/src/main/java/com/weather/modules/log/enums/OperationStatusEnum.java b/system-admin/src/main/java/com/weather/modules/log/enums/OperationStatusEnum.java new file mode 100644 index 0000000..fc90ffc --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/log/enums/OperationStatusEnum.java @@ -0,0 +1,30 @@ + + +package com.weather.modules.log.enums; + +/** + * 操作状态枚举 + * + * @author 123 + * @since 1.0.0 + */ +public enum OperationStatusEnum { + /** + * 失败 + */ + FAIL(0), + /** + * 成功 + */ + SUCCESS(1); + + private int value; + + OperationStatusEnum(int value) { + this.value = value; + } + + public int value() { + return this.value; + } +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/log/excel/SysLogErrorExcel.java b/system-admin/src/main/java/com/weather/modules/log/excel/SysLogErrorExcel.java new file mode 100644 index 0000000..7781ae9 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/log/excel/SysLogErrorExcel.java @@ -0,0 +1,43 @@ + + +package com.weather.modules.log.excel; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.format.DateTimeFormat; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.util.Date; + +/** + * 异常日志 + * + * @author 123 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(20) +@ColumnWidth(25) +public class SysLogErrorExcel { + @ExcelProperty("请求URI") + private String requestUri; + + @ExcelProperty("请求方式") + private String requestMethod; + + @ExcelProperty("请求参数") + private String requestParams; + + @ExcelProperty("User-Agent") + private String userAgent; + + @ExcelProperty("操作IP") + private String ip; + + @DateTimeFormat("yyyy-MM-dd HH:mm:ss") + @ExcelProperty("创建时间") + private Date createDate; + +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/log/excel/SysLogLoginExcel.java b/system-admin/src/main/java/com/weather/modules/log/excel/SysLogLoginExcel.java new file mode 100644 index 0000000..e9e6952 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/log/excel/SysLogLoginExcel.java @@ -0,0 +1,45 @@ + + +package com.weather.modules.log.excel; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.format.DateTimeFormat; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import com.weather.modules.log.excel.converter.SysLogOperationConverter; +import com.weather.modules.log.excel.converter.SysLogStatusConverter; +import lombok.Data; + +import java.util.Date; + +/** + * 登录日志 + * + * @author 123 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(20) +@ColumnWidth(25) +public class SysLogLoginExcel { + @ExcelProperty(value = "操作类型", converter = SysLogOperationConverter.class) + private Integer operation; + + @ExcelProperty(value = "状态", converter = SysLogStatusConverter.class) + private Integer status; + + @ExcelProperty("User-Agent") + private String userAgent; + + @ExcelProperty("操作IP") + private String ip; + + @ExcelProperty("用户名") + private String creatorName; + + @DateTimeFormat("yyyy-MM-dd HH:mm:ss") + @ExcelProperty("创建时间") + private Date createDate; + +} diff --git a/system-admin/src/main/java/com/weather/modules/log/excel/SysLogOperationExcel.java b/system-admin/src/main/java/com/weather/modules/log/excel/SysLogOperationExcel.java new file mode 100644 index 0000000..ad81a27 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/log/excel/SysLogOperationExcel.java @@ -0,0 +1,54 @@ + +package com.weather.modules.log.excel; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.format.DateTimeFormat; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.util.Date; + +/** + * 操作日志 + * + * @author 123 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(20) +@ColumnWidth(25) +public class SysLogOperationExcel { + @ExcelProperty("用户操作") + private String operation; + + @ExcelProperty("请求URI") + private String requestUri; + + @ExcelProperty("请求方式") + private String requestMethod; + + @ExcelProperty("请求参数") + private String requestParams; + + @ExcelProperty("请求时长(毫秒)") + private Integer requestTime; + + @ExcelProperty("User-Agent") + private String userAgent; + + @ExcelProperty("操作IP") + private String ip; + + @ExcelProperty("状态") + private Integer status; + + @ExcelProperty("用户名") + private String creatorName; + + @DateTimeFormat("yyyy-MM-dd HH:mm:ss") + @ExcelProperty("创建时间") + private Date createDate; + +} diff --git a/system-admin/src/main/java/com/weather/modules/log/excel/converter/SysLogOperationConverter.java b/system-admin/src/main/java/com/weather/modules/log/excel/converter/SysLogOperationConverter.java new file mode 100644 index 0000000..9ea621a --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/log/excel/converter/SysLogOperationConverter.java @@ -0,0 +1,40 @@ +package com.weather.modules.log.excel.converter; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; + +public class SysLogOperationConverter implements Converter { + + @Override + public Class supportJavaTypeKey() { + return Integer.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.STRING; + } + + @Override + public Integer convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + if(cellData.getStringValue().equals("用户登录")){ + return 0; + }else{ + return 1; + } + } + + @Override + public WriteCellData convertToExcelData(Integer value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + if(value == 0){ + return new WriteCellData<>("用户登录"); + }else{ + return new WriteCellData<>("用户退出"); + } + } + +} diff --git a/system-admin/src/main/java/com/weather/modules/log/excel/converter/SysLogStatusConverter.java b/system-admin/src/main/java/com/weather/modules/log/excel/converter/SysLogStatusConverter.java new file mode 100644 index 0000000..b9afa90 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/log/excel/converter/SysLogStatusConverter.java @@ -0,0 +1,48 @@ +package com.weather.modules.log.excel.converter; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; + +public class SysLogStatusConverter implements Converter { + + @Override + public Class supportJavaTypeKey() { + return Integer.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.STRING; + } + + @Override + public Integer convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + if(cellData.getStringValue().equals("失败")){ + return 0; + }else if(cellData.getStringValue().equals("成功")){ + return 1; + }else if(cellData.getStringValue().equals("账号已锁定")){ + return 2; + }else{ + return -1; + } + } + + @Override + public WriteCellData convertToExcelData(Integer value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + if(value == 0){ + return new WriteCellData<>("失败"); + }else if(value == 1){ + return new WriteCellData<>("成功"); + }else if(value == 2){ + return new WriteCellData<>("账号已锁定"); + }else{ + return new WriteCellData<>("未知"); + } + } + +} diff --git a/system-admin/src/main/java/com/weather/modules/log/service/SysLogErrorService.java b/system-admin/src/main/java/com/weather/modules/log/service/SysLogErrorService.java new file mode 100644 index 0000000..9fc8b71 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/log/service/SysLogErrorService.java @@ -0,0 +1,28 @@ + + +package com.weather.modules.log.service; + + +import com.weather.common.page.PageData; +import com.weather.common.service.BaseService; +import com.weather.modules.log.dto.SysLogErrorDTO; +import com.weather.modules.log.entity.SysLogErrorEntity; + +import java.util.List; +import java.util.Map; + +/** + * 异常日志 + * + * @author 123 + * @since 1.0.0 + */ +public interface SysLogErrorService extends BaseService { + + PageData page(Map params); + + List list(Map params); + + void save(SysLogErrorEntity entity); + +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/log/service/SysLogLoginService.java b/system-admin/src/main/java/com/weather/modules/log/service/SysLogLoginService.java new file mode 100644 index 0000000..18f42fc --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/log/service/SysLogLoginService.java @@ -0,0 +1,26 @@ + + +package com.weather.modules.log.service; + +import com.weather.common.page.PageData; +import com.weather.common.service.BaseService; +import com.weather.modules.log.dto.SysLogLoginDTO; +import com.weather.modules.log.entity.SysLogLoginEntity; + +import java.util.List; +import java.util.Map; + +/** + * 登录日志 + * + * @author 123 + * @since 1.0.0 + */ +public interface SysLogLoginService extends BaseService { + + PageData page(Map params); + + List list(Map params); + + void save(SysLogLoginEntity entity); +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/log/service/SysLogOperationService.java b/system-admin/src/main/java/com/weather/modules/log/service/SysLogOperationService.java new file mode 100644 index 0000000..f0e1f53 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/log/service/SysLogOperationService.java @@ -0,0 +1,26 @@ + + +package com.weather.modules.log.service; + +import com.weather.common.page.PageData; +import com.weather.common.service.BaseService; +import com.weather.modules.log.dto.SysLogOperationDTO; +import com.weather.modules.log.entity.SysLogOperationEntity; + +import java.util.List; +import java.util.Map; + +/** + * 操作日志 + * + * @author 123 + * @since 1.0.0 + */ +public interface SysLogOperationService extends BaseService { + + PageData page(Map params); + + List list(Map params); + + void save(SysLogOperationEntity entity); +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/log/service/impl/SysLogErrorServiceImpl.java b/system-admin/src/main/java/com/weather/modules/log/service/impl/SysLogErrorServiceImpl.java new file mode 100644 index 0000000..16c03b2 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/log/service/impl/SysLogErrorServiceImpl.java @@ -0,0 +1,58 @@ + + +package com.weather.modules.log.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.weather.common.constant.Constant; +import com.weather.common.page.PageData; +import com.weather.common.service.impl.BaseServiceImpl; +import com.weather.common.utils.ConvertUtils; +import com.weather.modules.log.dao.SysLogErrorDao; +import com.weather.modules.log.dto.SysLogErrorDTO; +import com.weather.modules.log.entity.SysLogErrorEntity; +import com.weather.modules.log.service.SysLogErrorService; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Map; + +/** + * 异常日志 + * + * @author 123 + * @since 1.0.0 + */ +@Service +public class SysLogErrorServiceImpl extends BaseServiceImpl implements SysLogErrorService { + + @Override + public PageData page(Map params) { + IPage page = baseDao.selectPage( + getPage(params, Constant.CREATE_DATE, false), + getWrapper(params) + ); + + return getPageData(page, SysLogErrorDTO.class); + } + + @Override + public List list(Map params) { + List entityList = baseDao.selectList(getWrapper(params)); + + return ConvertUtils.sourceToTarget(entityList, SysLogErrorDTO.class); + } + + private QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper<>(); + return wrapper; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void save(SysLogErrorEntity entity) { + insert(entity); + } + +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/log/service/impl/SysLogLoginServiceImpl.java b/system-admin/src/main/java/com/weather/modules/log/service/impl/SysLogLoginServiceImpl.java new file mode 100644 index 0000000..90452f9 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/log/service/impl/SysLogLoginServiceImpl.java @@ -0,0 +1,65 @@ + + +package com.weather.modules.log.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.weather.common.constant.Constant; +import com.weather.common.page.PageData; +import com.weather.common.service.impl.BaseServiceImpl; +import com.weather.common.utils.ConvertUtils; +import com.weather.modules.log.dao.SysLogLoginDao; +import com.weather.modules.log.dto.SysLogLoginDTO; +import com.weather.modules.log.entity.SysLogLoginEntity; +import com.weather.modules.log.service.SysLogLoginService; +import cn.hutool.core.util.StrUtil; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Map; + +/** + * 登录日志 + * + * @author 123 + * @since 1.0.0 + */ +@Service +public class SysLogLoginServiceImpl extends BaseServiceImpl implements SysLogLoginService { + + @Override + public PageData page(Map params) { + IPage page = baseDao.selectPage( + getPage(params, Constant.CREATE_DATE, false), + getWrapper(params) + ); + + return getPageData(page, SysLogLoginDTO.class); + } + + @Override + public List list(Map params) { + List entityList = baseDao.selectList(getWrapper(params)); + + return ConvertUtils.sourceToTarget(entityList, SysLogLoginDTO.class); + } + + private QueryWrapper getWrapper(Map params){ + String status = (String) params.get("status"); + String creatorName = (String) params.get("creatorName"); + + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.eq(StrUtil.isNotBlank(status), "status", status); + wrapper.like(StrUtil.isNotBlank(creatorName), "creator_name", creatorName); + + return wrapper; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void save(SysLogLoginEntity entity) { + insert(entity); + } + +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/log/service/impl/SysLogOperationServiceImpl.java b/system-admin/src/main/java/com/weather/modules/log/service/impl/SysLogOperationServiceImpl.java new file mode 100644 index 0000000..54ef636 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/log/service/impl/SysLogOperationServiceImpl.java @@ -0,0 +1,63 @@ + + +package com.weather.modules.log.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.weather.common.constant.Constant; +import com.weather.common.page.PageData; +import com.weather.common.service.impl.BaseServiceImpl; +import com.weather.common.utils.ConvertUtils; +import com.weather.modules.log.dao.SysLogOperationDao; +import com.weather.modules.log.dto.SysLogOperationDTO; +import com.weather.modules.log.entity.SysLogOperationEntity; +import com.weather.modules.log.service.SysLogOperationService; +import cn.hutool.core.util.StrUtil; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Map; + +/** + * 操作日志 + * + * @author 123 + * @since 1.0.0 + */ +@Service +public class SysLogOperationServiceImpl extends BaseServiceImpl implements SysLogOperationService { + + @Override + public PageData page(Map params) { + IPage page = baseDao.selectPage( + getPage(params, Constant.CREATE_DATE, false), + getWrapper(params) + ); + + return getPageData(page, SysLogOperationDTO.class); + } + + @Override + public List list(Map params) { + List entityList = baseDao.selectList(getWrapper(params)); + + return ConvertUtils.sourceToTarget(entityList, SysLogOperationDTO.class); + } + + private QueryWrapper getWrapper(Map params){ + String status = (String) params.get("status"); + + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.eq(StrUtil.isNotBlank(status), "status", status); + + return wrapper; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void save(SysLogOperationEntity entity) { + insert(entity); + } + +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/oss/cloud/AbstractCloudStorageService.java b/system-admin/src/main/java/com/weather/modules/oss/cloud/AbstractCloudStorageService.java new file mode 100644 index 0000000..db9f25e --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/oss/cloud/AbstractCloudStorageService.java @@ -0,0 +1,72 @@ + + +package com.weather.modules.oss.cloud; + +import com.weather.common.utils.DateUtils; +import cn.hutool.core.util.StrUtil; + +import java.io.InputStream; +import java.util.Date; +import java.util.UUID; + +/** + * 云存储(支持七牛、阿里云、腾讯云) + * + * @author 123 + */ +public abstract class AbstractCloudStorageService { + /** 云存储配置信息 */ + CloudStorageConfig config; + + /** + * 文件路径 + * @param prefix 前缀 + * @param suffix 后缀 + * @return 返回上传路径 + */ + public String getPath(String prefix, String suffix) { + //生成uuid + String uuid = UUID.randomUUID().toString().replaceAll("-", ""); + //文件路径 + String path = DateUtils.format(new Date(), "yyyyMMdd") + "/" + uuid; + + if(StrUtil.isNotBlank(prefix)){ + path = prefix + "/" + path; + } + + return path + "." + suffix; + } + + /** + * 文件上传 + * @param data 文件字节数组 + * @param path 文件路径,包含文件名 + * @return 返回http地址 + */ + public abstract String upload(byte[] data, String path); + + /** + * 文件上传 + * @param data 文件字节数组 + * @param suffix 后缀 + * @return 返回http地址 + */ + public abstract String uploadSuffix(byte[] data, String suffix); + + /** + * 文件上传 + * @param inputStream 字节流 + * @param path 文件路径,包含文件名 + * @return 返回http地址 + */ + public abstract String upload(InputStream inputStream, String path); + + /** + * 文件上传 + * @param inputStream 字节流 + * @param suffix 后缀 + * @return 返回http地址 + */ + public abstract String uploadSuffix(InputStream inputStream, String suffix); + +} diff --git a/system-admin/src/main/java/com/weather/modules/oss/cloud/AliyunCloudStorageService.java b/system-admin/src/main/java/com/weather/modules/oss/cloud/AliyunCloudStorageService.java new file mode 100644 index 0000000..a3b228b --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/oss/cloud/AliyunCloudStorageService.java @@ -0,0 +1,51 @@ + + +package com.weather.modules.oss.cloud; + +import com.aliyun.oss.OSSClient; +import com.weather.common.exception.ErrorCode; +import com.weather.common.exception.CommonException; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +/** + * 阿里云存储 + * + * @author 123 + */ +public class AliyunCloudStorageService extends AbstractCloudStorageService { + + public AliyunCloudStorageService(CloudStorageConfig config){ + this.config = config; + } + + @Override + public String upload(byte[] data, String path) { + return upload(new ByteArrayInputStream(data), path); + } + + @Override + public String upload(InputStream inputStream, String path) { + OSSClient client = new OSSClient(config.getAliyunEndPoint(), config.getAliyunAccessKeyId(), + config.getAliyunAccessKeySecret()); + try { + client.putObject(config.getAliyunBucketName(), path, inputStream); + client.shutdown(); + } catch (Exception e){ + throw new CommonException(ErrorCode.OSS_UPLOAD_FILE_ERROR, e, ""); + } + + return config.getAliyunDomain() + "/" + path; + } + + @Override + public String uploadSuffix(byte[] data, String suffix) { + return upload(data, getPath(config.getAliyunPrefix(), suffix)); + } + + @Override + public String uploadSuffix(InputStream inputStream, String suffix) { + return upload(inputStream, getPath(config.getAliyunPrefix(), suffix)); + } +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/oss/cloud/CloudStorageConfig.java b/system-admin/src/main/java/com/weather/modules/oss/cloud/CloudStorageConfig.java new file mode 100644 index 0000000..1c1e87a --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/oss/cloud/CloudStorageConfig.java @@ -0,0 +1,102 @@ + +package com.weather.modules.oss.cloud; + +import com.weather.common.validator.group.AliyunGroup; +import com.weather.common.validator.group.QcloudGroup; +import com.weather.common.validator.group.QiniuGroup; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.hibernate.validator.constraints.Range; +import org.hibernate.validator.constraints.URL; + +import java.io.Serializable; + +/** + * 云存储配置信息 + * + * @author 123 + */ +@Data +@Schema(title = "云存储配置信息") +public class CloudStorageConfig implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(title = "类型 1:七牛 2:阿里云 3:腾讯云 ") + @Range(min = 1, max = 6, message = "{oss.type.range}") + private Integer type; + + @Schema(title = "七牛绑定的域名") + @NotBlank(message = "{qiniu.domain.require}", groups = QiniuGroup.class) + @URL(message = "{qiniu.domain.url}", groups = QiniuGroup.class) + private String qiniuDomain; + + @Schema(title = "七牛路径前缀") + private String qiniuPrefix; + + @Schema(title = "七牛ACCESS_KEY") + @NotBlank(message = "{qiniu.accesskey.require}", groups = QiniuGroup.class) + private String qiniuAccessKey; + + @Schema(title = "七牛SECRET_KEY") + @NotBlank(message = "{qiniu.secretkey.require}", groups = QiniuGroup.class) + private String qiniuSecretKey; + + @Schema(title = "七牛存储空间名") + @NotBlank(message = "{qiniu.bucketName.require}", groups = QiniuGroup.class) + private String qiniuBucketName; + + @Schema(title = "阿里云绑定的域名") + @NotBlank(message = "{aliyun.domain.require}", groups = AliyunGroup.class) + @URL(message = "{aliyun.domain.url}", groups = AliyunGroup.class) + private String aliyunDomain; + + @Schema(title = "阿里云路径前缀") + private String aliyunPrefix; + + @Schema(title = "阿里云EndPoint") + @NotBlank(message = "{aliyun.endPoint.require}", groups = AliyunGroup.class) + private String aliyunEndPoint; + + @Schema(title = "阿里云AccessKeyId") + @NotBlank(message = "{aliyun.accesskeyid.require}", groups = AliyunGroup.class) + private String aliyunAccessKeyId; + + @Schema(title = "阿里云AccessKeySecret") + @NotBlank(message = "{aliyun.accesskeysecret.require}", groups = AliyunGroup.class) + private String aliyunAccessKeySecret; + + @Schema(title = "阿里云BucketName") + @NotBlank(message = "{aliyun.bucketName.require}", groups = AliyunGroup.class) + private String aliyunBucketName; + + @Schema(title = "腾讯云绑定的域名") + @NotBlank(message = "{qcloud.domain.require}", groups = QcloudGroup.class) + @URL(message = "{qcloud.domain.url}", groups = QcloudGroup.class) + private String qcloudDomain; + + @Schema(title = "腾讯云路径前缀") + private String qcloudPrefix; + + @Schema(title = "腾讯云AppId") + @NotNull(message = "{qcloud.appid.require}", groups = QcloudGroup.class) + private Integer qcloudAppId; + + @Schema(title = "腾讯云SecretId") + @NotBlank(message = "{qcloud.secretId.require}", groups = QcloudGroup.class) + private String qcloudSecretId; + + @Schema(title = "腾讯云SecretKey") + @NotBlank(message = "{qcloud.secretkey.require}", groups = QcloudGroup.class) + private String qcloudSecretKey; + + @Schema(title = "腾讯云BucketName") + @NotBlank(message = "{qcloud.bucketName.require}", groups = QcloudGroup.class) + private String qcloudBucketName; + + @Schema(title = "腾讯云COS所属地区") + @NotBlank(message = "{qcloud.region.require}", groups = QcloudGroup.class) + private String qcloudRegion; + +} diff --git a/system-admin/src/main/java/com/weather/modules/oss/cloud/OSSFactory.java b/system-admin/src/main/java/com/weather/modules/oss/cloud/OSSFactory.java new file mode 100644 index 0000000..37a6891 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/oss/cloud/OSSFactory.java @@ -0,0 +1,35 @@ + + +package com.weather.modules.oss.cloud; + +import com.weather.common.constant.Constant; +import com.weather.common.utils.SpringContextUtils; +import com.weather.modules.sys.service.SysParamsService; + +/** + * 文件上传Factory + * @author 123 + */ +public final class OSSFactory { + private static SysParamsService sysParamsService; + + static { + OSSFactory.sysParamsService = SpringContextUtils.getBean(SysParamsService.class); + } + + public static AbstractCloudStorageService build(){ + //获取云存储配置信息 + CloudStorageConfig config = sysParamsService.getValueObject(Constant.CLOUD_STORAGE_CONFIG_KEY, CloudStorageConfig.class); + + if(config.getType() == Constant.CloudService.QINIU.getValue()){ + return new QiniuCloudStorageService(config); + }else if(config.getType() == Constant.CloudService.ALIYUN.getValue()){ + return new AliyunCloudStorageService(config); + }else if(config.getType() == Constant.CloudService.QCLOUD.getValue()){ + return new QcloudCloudStorageService(config); + } + + return null; + } + +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/oss/cloud/QcloudCloudStorageService.java b/system-admin/src/main/java/com/weather/modules/oss/cloud/QcloudCloudStorageService.java new file mode 100644 index 0000000..916943e --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/oss/cloud/QcloudCloudStorageService.java @@ -0,0 +1,80 @@ + + +package com.weather.modules.oss.cloud; + +import com.qcloud.cos.COSClient; +import com.qcloud.cos.ClientConfig; +import com.qcloud.cos.auth.BasicCOSCredentials; +import com.qcloud.cos.auth.COSCredentials; +import com.qcloud.cos.model.ObjectMetadata; +import com.qcloud.cos.model.PutObjectRequest; +import com.qcloud.cos.model.PutObjectResult; +import com.qcloud.cos.region.Region; +import com.weather.common.exception.ErrorCode; +import com.weather.common.exception.CommonException; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * 腾讯云存储 + * + * @author 123 + */ +public class QcloudCloudStorageService extends AbstractCloudStorageService { + private COSCredentials credentials; + private ClientConfig clientConfig; + + public QcloudCloudStorageService(CloudStorageConfig config){ + this.config = config; + + //初始化 + init(); + } + + private void init(){ + //1、初始化用户身份信息(secretId, secretKey) + credentials = new BasicCOSCredentials(config.getQcloudSecretId(), config.getQcloudSecretKey()); + + //2、设置bucket的区域, COS地域的简称请参照 https://cloud.tencent.com/document/product/436/6224 + clientConfig = new ClientConfig(new Region(config.getQcloudRegion())); + } + + @Override + public String upload(byte[] data, String path) { + return upload(new ByteArrayInputStream(data), path); + } + + @Override + public String upload(InputStream inputStream, String path) { + try { + COSClient client = new COSClient(credentials, clientConfig); + + ObjectMetadata metadata = new ObjectMetadata(); + metadata.setContentLength(inputStream.available()); + String bucketName = config.getQcloudBucketName() +"-"+ config.getQcloudAppId(); + PutObjectRequest request = new PutObjectRequest(bucketName, path, inputStream, metadata); + PutObjectResult result = client.putObject(request); + + client.shutdown(); + if(result.getETag() == null){ + throw new CommonException(ErrorCode.OSS_UPLOAD_FILE_ERROR, ""); + } + } catch (IOException e) { + throw new CommonException(ErrorCode.OSS_UPLOAD_FILE_ERROR, e, ""); + } + + return config.getQcloudDomain() + "/" + path; + } + + @Override + public String uploadSuffix(byte[] data, String suffix) { + return upload(data, getPath(config.getQcloudPrefix(), suffix)); + } + + @Override + public String uploadSuffix(InputStream inputStream, String suffix) { + return upload(inputStream, getPath(config.getQcloudPrefix(), suffix)); + } +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/oss/cloud/QiniuCloudStorageService.java b/system-admin/src/main/java/com/weather/modules/oss/cloud/QiniuCloudStorageService.java new file mode 100644 index 0000000..21e1e6e --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/oss/cloud/QiniuCloudStorageService.java @@ -0,0 +1,72 @@ + +package com.weather.modules.oss.cloud; + +import com.qiniu.http.Response; +import com.qiniu.storage.Configuration; +import com.qiniu.storage.Region; +import com.qiniu.storage.UploadManager; +import com.qiniu.util.Auth; +import com.qiniu.util.IOUtils; +import com.weather.common.exception.ErrorCode; +import com.weather.common.exception.CommonException; + +import java.io.IOException; +import java.io.InputStream; + +/** + * 七牛云存储 + * + * @author 123 + */ +public class QiniuCloudStorageService extends AbstractCloudStorageService { + private UploadManager uploadManager; + private String token; + + public QiniuCloudStorageService(CloudStorageConfig config) { + this.config = config; + + //初始化 + init(); + } + + private void init() { + uploadManager = new UploadManager(new Configuration(Region.autoRegion())); + token = Auth.create(config.getQiniuAccessKey(), config.getQiniuSecretKey()). + uploadToken(config.getQiniuBucketName()); + + } + + @Override + public String upload(byte[] data, String path) { + try { + Response res = uploadManager.put(data, path, token); + if (!res.isOK()) { + throw new CommonException(ErrorCode.OSS_UPLOAD_FILE_ERROR, res.toString()); + } + } catch (Exception e) { + throw new CommonException(ErrorCode.OSS_UPLOAD_FILE_ERROR, e, ""); + } + + return config.getQiniuDomain() + "/" + path; + } + + @Override + public String upload(InputStream inputStream, String path) { + try { + byte[] data = IOUtils.toByteArray(inputStream); + return this.upload(data, path); + } catch (IOException e) { + throw new CommonException(ErrorCode.OSS_UPLOAD_FILE_ERROR, e, ""); + } + } + + @Override + public String uploadSuffix(byte[] data, String suffix) { + return upload(data, getPath(config.getQiniuPrefix(), suffix)); + } + + @Override + public String uploadSuffix(InputStream inputStream, String suffix) { + return upload(inputStream, getPath(config.getQiniuPrefix(), suffix)); + } +} diff --git a/system-admin/src/main/java/com/weather/modules/oss/controller/SysOssController.java b/system-admin/src/main/java/com/weather/modules/oss/controller/SysOssController.java new file mode 100644 index 0000000..a5a165e --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/oss/controller/SysOssController.java @@ -0,0 +1,124 @@ + +package com.weather.modules.oss.controller; + +import cn.hutool.core.io.file.FileNameUtil; +import com.weather.common.annotation.LogOperation; +import com.weather.common.constant.Constant; +import com.weather.common.exception.ErrorCode; +import com.weather.common.page.PageData; +import com.weather.common.utils.JsonUtils; +import com.weather.common.utils.Result; +import com.weather.common.validator.ValidatorUtils; +import com.weather.common.validator.group.AliyunGroup; +import com.weather.common.validator.group.QcloudGroup; +import com.weather.common.validator.group.QiniuGroup; +import com.weather.modules.oss.cloud.CloudStorageConfig; +import com.weather.modules.oss.cloud.OSSFactory; +import com.weather.modules.oss.entity.SysOssEntity; +import com.weather.modules.oss.service.SysOssService; +import com.weather.modules.sys.service.SysParamsService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * 文件上传 + * + * @author 123 + */ +@RestController +@RequestMapping("sys/oss") +@Tag(name = "文件上传") +@AllArgsConstructor +public class SysOssController { + private final SysOssService sysOssService; + private final SysParamsService sysParamsService; + + private final static String KEY = Constant.CLOUD_STORAGE_CONFIG_KEY; + + @GetMapping("page") + @Operation(summary = "分页") + @RequiresPermissions("sys:oss:all") + public Result> page(@Parameter(hidden = true) @RequestParam Map params) { + PageData page = sysOssService.page(params); + + return new Result>().ok(page); + } + + @GetMapping("info") + @Operation(summary = "云存储配置信息") + @RequiresPermissions("sys:oss:all") + public Result info() { + CloudStorageConfig config = sysParamsService.getValueObject(KEY, CloudStorageConfig.class); + + return new Result().ok(config); + } + + @PostMapping + @Operation(summary = "保存云存储配置信息") + @LogOperation("保存云存储配置信息") + @RequiresPermissions("sys:oss:all") + public Result saveConfig(@RequestBody CloudStorageConfig config) { + //校验类型 + ValidatorUtils.validateEntity(config); + + if (config.getType() == Constant.CloudService.QINIU.getValue()) { + //校验七牛数据 + ValidatorUtils.validateEntity(config, QiniuGroup.class); + } else if (config.getType() == Constant.CloudService.ALIYUN.getValue()) { + //校验阿里云数据 + ValidatorUtils.validateEntity(config, AliyunGroup.class); + } else if (config.getType() == Constant.CloudService.QCLOUD.getValue()) { + //校验腾讯云数据 + ValidatorUtils.validateEntity(config, QcloudGroup.class); + } + + sysParamsService.updateValueByCode(KEY, JsonUtils.toJsonString(config)); + + return new Result(); + } + + @PostMapping("upload") + @Operation(summary = "上传文件") + @RequiresPermissions("sys:oss:all") + public Result> upload(@RequestParam("file") MultipartFile file) throws Exception { + if (file.isEmpty()) { + return new Result>().error(ErrorCode.UPLOAD_FILE_EMPTY); + } + + //上传文件 + String suffix = FileNameUtil.getSuffix(file.getOriginalFilename()); + String url = OSSFactory.build().uploadSuffix(file.getBytes(), suffix); + + //保存文件信息 + SysOssEntity ossEntity = new SysOssEntity(); + ossEntity.setUrl(url); + ossEntity.setCreateDate(new Date()); + sysOssService.insert(ossEntity); + + Map data = new HashMap<>(1); + data.put("src", url); + + return new Result>().ok(data); + } + + @DeleteMapping + @Operation(summary = "删除") + @LogOperation("删除") + @RequiresPermissions("sys:oss:all") + public Result delete(@RequestBody Long[] ids) { + sysOssService.deleteBatchIds(Arrays.asList(ids)); + + return new Result(); + } + +} diff --git a/system-admin/src/main/java/com/weather/modules/oss/dao/SysOssDao.java b/system-admin/src/main/java/com/weather/modules/oss/dao/SysOssDao.java new file mode 100644 index 0000000..5a7df6b --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/oss/dao/SysOssDao.java @@ -0,0 +1,17 @@ + + +package com.weather.modules.oss.dao; + +import com.weather.common.dao.BaseDao; +import com.weather.modules.oss.entity.SysOssEntity; +import org.apache.ibatis.annotations.Mapper; + +/** + * 文件上传 + * + * @author 123 + */ +@Mapper +public interface SysOssDao extends BaseDao { + +} diff --git a/system-admin/src/main/java/com/weather/modules/oss/entity/SysOssEntity.java b/system-admin/src/main/java/com/weather/modules/oss/entity/SysOssEntity.java new file mode 100644 index 0000000..3a241f8 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/oss/entity/SysOssEntity.java @@ -0,0 +1,26 @@ + + +package com.weather.modules.oss.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.weather.common.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 文件上传 + * + * @author 123 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@TableName("sys_oss") +public class SysOssEntity extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** + * URL地址 + */ + private String url; + +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/oss/service/SysOssService.java b/system-admin/src/main/java/com/weather/modules/oss/service/SysOssService.java new file mode 100644 index 0000000..7d4e7c5 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/oss/service/SysOssService.java @@ -0,0 +1,19 @@ + + +package com.weather.modules.oss.service; + +import com.weather.common.page.PageData; +import com.weather.common.service.BaseService; +import com.weather.modules.oss.entity.SysOssEntity; + +import java.util.Map; + +/** + * 文件上传 + * + * @author 123 + */ +public interface SysOssService extends BaseService { + + PageData page(Map params); +} diff --git a/system-admin/src/main/java/com/weather/modules/oss/service/impl/SysOssServiceImpl.java b/system-admin/src/main/java/com/weather/modules/oss/service/impl/SysOssServiceImpl.java new file mode 100644 index 0000000..e67b366 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/oss/service/impl/SysOssServiceImpl.java @@ -0,0 +1,29 @@ + + +package com.weather.modules.oss.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.weather.common.constant.Constant; +import com.weather.common.page.PageData; +import com.weather.common.service.impl.BaseServiceImpl; +import com.weather.modules.oss.dao.SysOssDao; +import com.weather.modules.oss.entity.SysOssEntity; +import com.weather.modules.oss.service.SysOssService; +import org.springframework.stereotype.Service; + +import java.util.Map; + + +@Service +public class SysOssServiceImpl extends BaseServiceImpl implements SysOssService { + + @Override + public PageData page(Map params) { + IPage page = baseDao.selectPage( + getPage(params, Constant.CREATE_DATE, false), + new QueryWrapper<>() + ); + return getPageData(page, SysOssEntity.class); + } +} diff --git a/system-admin/src/main/java/com/weather/modules/region/controller/SysRegionController.java b/system-admin/src/main/java/com/weather/modules/region/controller/SysRegionController.java new file mode 100644 index 0000000..493d863 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/region/controller/SysRegionController.java @@ -0,0 +1,61 @@ +package com.weather.modules.region.controller; + +import com.weather.common.annotation.LogOperation; +import com.weather.common.constant.Constant; +import com.weather.common.page.PageData; +import com.weather.common.utils.ExcelUtils; +import com.weather.common.utils.Result; +import com.weather.common.validator.AssertUtils; +import com.weather.common.validator.ValidatorUtils; +import com.weather.common.validator.group.AddGroup; +import com.weather.common.validator.group.DefaultGroup; +import com.weather.common.validator.group.UpdateGroup; +import com.weather.modules.region.dto.SysRegionDTO; +import com.weather.modules.region.excel.SysRegionExcel; +import com.weather.modules.region.service.SysRegionService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import jakarta.servlet.http.HttpServletResponse; +import java.util.List; +import java.util.Map; + + +/** + * 行政区划表 + * + * @author Mark 123 + * @since 1.0.0 2026-03-05 + */ +@RestController +@RequestMapping("region/sysregion") +@Tag(name="行政区划表") +public class SysRegionController { + + @Autowired + private SysRegionService sysRegionService; + + @GetMapping("parent/{id}") + @Operation(summary = "信息") + @RequiresPermissions("region:sysregion:info") + public Result> get(@PathVariable Long id){ + List data = sysRegionService.getByParentId(id); + return new Result>().ok(data); + } + + @GetMapping("export") + @Operation(summary = "导出") + @LogOperation("导出") + @RequiresPermissions("region:sysregion:export") + public void export(@Parameter(hidden = true) @RequestParam Map params, HttpServletResponse response) throws Exception { + List list = sysRegionService.list(params); + + ExcelUtils.exportExcelToTarget(response, null, "行政区划表", list, SysRegionExcel.class); + } + +} diff --git a/system-admin/src/main/java/com/weather/modules/region/dao/SysRegionDao.java b/system-admin/src/main/java/com/weather/modules/region/dao/SysRegionDao.java new file mode 100644 index 0000000..43c25bb --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/region/dao/SysRegionDao.java @@ -0,0 +1,16 @@ +package com.weather.modules.region.dao; + +import com.weather.common.dao.BaseDao; +import com.weather.modules.region.entity.SysRegionEntity; +import org.apache.ibatis.annotations.Mapper; + +/** + * 行政区划表 + * + * @author Mark 123 + * @since 1.0.0 2026-03-05 + */ +@Mapper +public interface SysRegionDao extends BaseDao { + +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/region/dto/SysRegionDTO.java b/system-admin/src/main/java/com/weather/modules/region/dto/SysRegionDTO.java new file mode 100644 index 0000000..b15c480 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/region/dto/SysRegionDTO.java @@ -0,0 +1,42 @@ +package com.weather.modules.region.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.media.SchemaProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +import java.math.BigDecimal; + +/** + * 行政区划表 + * + * @author Mark 123 + * @since 1.0.0 2026-03-05 + */ +@Data +@Schema(name = "行政区划表") +public class SysRegionDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @SchemaProperty(name = "行政区编码") + private Long id; + + @SchemaProperty(name = "名称") + private String name; + + @SchemaProperty(name = "层级 0国家 1省 2市 3区县") + private Integer level; + + @SchemaProperty(name = "父级行政区") + private Long parentId; + + @SchemaProperty(name = "经度") + private BigDecimal lon; + + @SchemaProperty(name = "纬度") + private BigDecimal lat; + + +} diff --git a/system-admin/src/main/java/com/weather/modules/region/entity/SysRegionEntity.java b/system-admin/src/main/java/com/weather/modules/region/entity/SysRegionEntity.java new file mode 100644 index 0000000..9aef98b --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/region/entity/SysRegionEntity.java @@ -0,0 +1,43 @@ +package com.weather.modules.region.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 行政区划表 + * + * @author Mark 123 + * @since 1.0.0 2026-03-05 + */ +@Data +@TableName("sys_region") +public class SysRegionEntity { + + /** + * 行政区编码 + */ + private Long id; + /** + * 名称 + */ + private String name; + /** + * 层级 0国家 1省 2市 3区县 + */ + private Integer level; + /** + * 父级行政区 + */ + private Long parentId; + /** + * 经度 + */ + private BigDecimal lon; + /** + * 纬度 + */ + private BigDecimal lat; +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/region/excel/SysRegionExcel.java b/system-admin/src/main/java/com/weather/modules/region/excel/SysRegionExcel.java new file mode 100644 index 0000000..d91623c --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/region/excel/SysRegionExcel.java @@ -0,0 +1,33 @@ +package com.weather.modules.region.excel; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 行政区划表 + * + * @author Mark 123 + * @since 1.0.0 2026-03-05 + */ +@Data +public class SysRegionExcel { + @ExcelProperty(value = "行政区编码") + private Long id; + @ExcelProperty(value = "名称") + private String name; + @ExcelProperty(value = "层级 0国家 1省 2市 3区县") + private Integer level; + @ExcelProperty(value = "父级行政区") + private Long parentId; + @ExcelProperty(value = "经度") + private BigDecimal lon; + @ExcelProperty(value = "纬度") + private BigDecimal lat; + +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/region/service/SysRegionService.java b/system-admin/src/main/java/com/weather/modules/region/service/SysRegionService.java new file mode 100644 index 0000000..1f8fe86 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/region/service/SysRegionService.java @@ -0,0 +1,23 @@ +package com.weather.modules.region.service; + +import com.weather.common.service.CrudService; +import com.weather.modules.region.dto.SysRegionDTO; +import com.weather.modules.region.entity.SysRegionEntity; + +import java.util.List; + +/** + * 行政区划表 + * + * @author Mark 123 + * @since 1.0.0 2026-03-05 + */ +public interface SysRegionService extends CrudService { + /** + * 获取子一级地区信息 + * @param id 地区 id + * @return 地区信息 + */ + List getByParentId(Long id); + +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/region/service/impl/SysRegionServiceImpl.java b/system-admin/src/main/java/com/weather/modules/region/service/impl/SysRegionServiceImpl.java new file mode 100644 index 0000000..b94ceea --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/region/service/impl/SysRegionServiceImpl.java @@ -0,0 +1,49 @@ +package com.weather.modules.region.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.weather.common.service.impl.CrudServiceImpl; +import com.weather.common.utils.ConvertUtils; +import com.weather.modules.region.dao.SysRegionDao; +import com.weather.modules.region.dto.SysRegionDTO; +import com.weather.modules.region.entity.SysRegionEntity; +import com.weather.modules.region.service.SysRegionService; +import cn.hutool.core.util.StrUtil; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * 行政区划表 + * + * @author Mark 123 + * @since 1.0.0 2026-03-05 + */ +@Service +public class SysRegionServiceImpl extends CrudServiceImpl implements SysRegionService { + + @Override + public QueryWrapper getWrapper(Map params) { + String id = (String) params.get("id"); + + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.eq(StrUtil.isNotBlank(id), "id", id); + + return wrapper; + } + + + /** + * 获取子一级地区信息 + * + * @param id 地区 id + * @return 地区信息 + */ + @Override + public List getByParentId(Long id) { + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.eq("parent_id", id); + List sysRegionEntities = baseDao.selectList(wrapper); + return ConvertUtils.sourceToTarget(sysRegionEntities, SysRegionDTO.class); + } +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/security/config/FilterConfig.java b/system-admin/src/main/java/com/weather/modules/security/config/FilterConfig.java new file mode 100644 index 0000000..dcb3a9e --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/security/config/FilterConfig.java @@ -0,0 +1,43 @@ + + +package com.weather.modules.security.config; + +import com.weather.common.xss.XssFilter; +import jakarta.servlet.DispatcherType; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.filter.DelegatingFilterProxy; + + +/** + * Filter配置 + * + * @author 123 + */ +@Configuration +public class FilterConfig { + + @Bean + public FilterRegistrationBean shiroFilterRegistration() { + FilterRegistrationBean registration = new FilterRegistrationBean(); + registration.setFilter(new DelegatingFilterProxy("shiroFilter")); + //该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理 + registration.addInitParameter("targetFilterLifecycle", "true"); + registration.setEnabled(true); + registration.setOrder(Integer.MAX_VALUE - 1); + registration.addUrlPatterns("/*"); + return registration; + } + + @Bean + public FilterRegistrationBean xssFilterRegistration() { + FilterRegistrationBean registration = new FilterRegistrationBean(); + registration.setDispatcherTypes(DispatcherType.REQUEST); + registration.setFilter(new XssFilter()); + registration.addUrlPatterns("/*"); + registration.setName("xssFilter"); + registration.setOrder(Integer.MAX_VALUE); + return registration; + } +} diff --git a/system-admin/src/main/java/com/weather/modules/security/config/ShiroConfig.java b/system-admin/src/main/java/com/weather/modules/security/config/ShiroConfig.java new file mode 100644 index 0000000..787b8c9 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/security/config/ShiroConfig.java @@ -0,0 +1,85 @@ + + +package com.weather.modules.security.config; + +import com.weather.modules.security.oauth2.Oauth2Filter; +import com.weather.modules.security.oauth2.Oauth2Realm; +import org.apache.shiro.mgt.SecurityManager; +import org.apache.shiro.session.mgt.SessionManager; +import org.apache.shiro.spring.LifecycleBeanPostProcessor; +import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; +import org.apache.shiro.spring.web.ShiroFilterFactoryBean; +import org.apache.shiro.web.mgt.DefaultWebSecurityManager; +import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import jakarta.servlet.Filter; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * Shiro的配置文件 + * + * @author 123 + */ +@Configuration +public class ShiroConfig { + + @Bean + public DefaultWebSessionManager sessionManager(){ + DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); + sessionManager.setSessionValidationSchedulerEnabled(false); + sessionManager.setSessionIdUrlRewritingEnabled(false); + + return sessionManager; + } + + @Bean("securityManager") + public SecurityManager securityManager(Oauth2Realm oAuth2Realm, SessionManager sessionManager) { + DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); + securityManager.setRealm(oAuth2Realm); + securityManager.setSessionManager(sessionManager); + securityManager.setRememberMeManager(null); + return securityManager; + } + + @Bean("shiroFilter") + public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) { + ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean(); + shiroFilter.setSecurityManager(securityManager); + + //oauth过滤 + Map filters = new HashMap<>(); + filters.put("oauth2", new Oauth2Filter()); + shiroFilter.setFilters(filters); + + Map filterMap = new LinkedHashMap<>(); + filterMap.put("/webjars/**", "anon"); + filterMap.put("/druid/**", "anon"); + filterMap.put("/login", "anon"); + filterMap.put("/swagger/**", "anon"); + filterMap.put("/v3/api-docs/**", "anon"); + filterMap.put("/doc.html", "anon"); + filterMap.put("/swagger-resources/**", "anon"); + filterMap.put("/captcha", "anon"); + filterMap.put("/favicon.ico", "anon"); + filterMap.put("/", "anon"); + filterMap.put("/**", "oauth2"); + shiroFilter.setFilterChainDefinitionMap(filterMap); + + return shiroFilter; + } + + @Bean("lifecycleBeanPostProcessor") + public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { + return new LifecycleBeanPostProcessor(); + } + + @Bean + public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { + AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor(); + advisor.setSecurityManager(securityManager); + return advisor; + } +} diff --git a/system-admin/src/main/java/com/weather/modules/security/config/WebMvcConfig.java b/system-admin/src/main/java/com/weather/modules/security/config/WebMvcConfig.java new file mode 100644 index 0000000..fe0f88b --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/security/config/WebMvcConfig.java @@ -0,0 +1,67 @@ + + +package com.weather.modules.security.config; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import com.weather.common.utils.DateUtils; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.converter.ByteArrayHttpMessageConverter; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.ResourceHttpMessageConverter; +import org.springframework.http.converter.StringHttpMessageConverter; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.text.SimpleDateFormat; +import java.util.List; +import java.util.TimeZone; + +@Configuration +public class WebMvcConfig implements WebMvcConfigurer { + + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**") + .allowedOriginPatterns("*") + .allowCredentials(true) + .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") + .maxAge(3600); + } + + @Override + public void configureMessageConverters(List> converters) { + converters.add(new ByteArrayHttpMessageConverter()); + converters.add(new StringHttpMessageConverter()); + converters.add(new ResourceHttpMessageConverter()); + converters.add(new AllEncompassingFormHttpMessageConverter()); + converters.add(new StringHttpMessageConverter()); + converters.add(jackson2HttpMessageConverter()); + } + + @Bean + public MappingJackson2HttpMessageConverter jackson2HttpMessageConverter() { + MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); + ObjectMapper mapper = new ObjectMapper(); + + //日期格式转换 + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + mapper.setDateFormat(new SimpleDateFormat(DateUtils.DATE_TIME_PATTERN)); + mapper.setTimeZone(TimeZone.getTimeZone("GMT+8")); + + //Long类型转String类型 + SimpleModule simpleModule = new SimpleModule(); + simpleModule.addSerializer(Long.class, ToStringSerializer.instance); + simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance); + mapper.registerModule(simpleModule); + + converter.setObjectMapper(mapper); + return converter; + } + +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/security/controller/LoginController.java b/system-admin/src/main/java/com/weather/modules/security/controller/LoginController.java new file mode 100644 index 0000000..dbadb5a --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/security/controller/LoginController.java @@ -0,0 +1,128 @@ + +package com.weather.modules.security.controller; + +import com.weather.common.exception.ErrorCode; +import com.weather.common.exception.CommonException; +import com.weather.common.utils.IpUtils; +import com.weather.common.utils.Result; +import com.weather.common.validator.AssertUtils; +import com.weather.common.validator.ValidatorUtils; +import com.weather.modules.log.entity.SysLogLoginEntity; +import com.weather.modules.log.enums.LoginOperationEnum; +import com.weather.modules.log.enums.LoginStatusEnum; +import com.weather.modules.log.service.SysLogLoginService; +import com.weather.modules.security.dto.LoginDTO; +import com.weather.modules.security.password.PasswordUtils; +import com.weather.modules.security.service.CaptchaService; +import com.weather.modules.security.service.SysUserTokenService; +import com.weather.modules.security.user.SecurityUser; +import com.weather.modules.security.user.UserDetail; +import com.weather.modules.sys.dto.SysUserDTO; +import com.weather.modules.sys.enums.UserStatusEnum; +import com.weather.modules.sys.service.SysUserService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.AllArgsConstructor; +import org.springframework.http.HttpHeaders; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +import java.io.IOException; +import java.util.Date; + +/** + * 登录 + * + * @author 123 + */ +@RestController +@Tag(name = "登录管理") +@AllArgsConstructor +public class LoginController { + private final SysUserService sysUserService; + private final SysUserTokenService sysUserTokenService; + private final CaptchaService captchaService; + private final SysLogLoginService sysLogLoginService; + + @GetMapping("captcha") + @Operation(summary = "验证码") + @Parameter(in = ParameterIn.QUERY, ref = "string", name = "uuid", required = true) + public void captcha(HttpServletResponse response, String uuid) throws IOException { + //uuid不能为空 + AssertUtils.isBlank(uuid, ErrorCode.IDENTIFIER_NOT_NULL); + + //生成验证码 + captchaService.create(response, uuid); + } + + @PostMapping("login") + @Operation(summary = "登录") + public Result login(HttpServletRequest request, + @RequestBody LoginDTO login) { + // 参数校验 + ValidatorUtils.validateEntity(login); + if (!captchaService.validate(login.getUuid(), login.getCaptcha())) { + return new Result().error(ErrorCode.CAPTCHA_ERROR); + } + SysUserDTO user = sysUserService.getByUsername(login.getUsername()); + SysLogLoginEntity log = new SysLogLoginEntity(); + log.setOperation(LoginOperationEnum.LOGIN.value()); + log.setCreateDate(new Date()); + log.setIp(IpUtils.getIpAddr(request)); + log.setUserAgent(request.getHeader(HttpHeaders.USER_AGENT)); + // 用户真实性 + if (user != null) { + log.setCreator(user.getId()); + log.setCreatorName(user.getUsername()); + } else { + log.setCreatorName(login.getUsername()); + } + LoginStatusEnum status = null; + Integer error = 0; + // 判断密码和账户有效性 + if (user == null ||!PasswordUtils.matches(login.getPassword(), user.getPassword())) { + status = LoginStatusEnum.FAIL; + error = ErrorCode.ACCOUNT_PASSWORD_ERROR; + } else if (user.getStatus() == UserStatusEnum.DISABLE.value()) { + status = LoginStatusEnum.LOCK; + error = ErrorCode.ACCOUNT_DISABLE; + } + // 统一设置异常信息 + log.setStatus(status == null? LoginStatusEnum.SUCCESS.value(): status.value()); + sysLogLoginService.save(log); + if (error > 0) { + throw new CommonException(error); + } + return sysUserTokenService.createToken(user.getId()); + } + + @PostMapping("logout") + @Operation(summary = "退出") + public Result logout(HttpServletRequest request) { + UserDetail user = SecurityUser.getUser(); + + //退出 + sysUserTokenService.logout(user.getId()); + + //用户信息 + SysLogLoginEntity log = new SysLogLoginEntity(); + log.setOperation(LoginOperationEnum.LOGOUT.value()); + log.setIp(IpUtils.getIpAddr(request)); + log.setUserAgent(request.getHeader(HttpHeaders.USER_AGENT)); + log.setIp(IpUtils.getIpAddr(request)); + log.setStatus(LoginStatusEnum.SUCCESS.value()); + log.setCreator(user.getId()); + log.setCreatorName(user.getUsername()); + log.setCreateDate(new Date()); + sysLogLoginService.save(log); + + return new Result(); + } + +} diff --git a/system-admin/src/main/java/com/weather/modules/security/dao/SysUserTokenDao.java b/system-admin/src/main/java/com/weather/modules/security/dao/SysUserTokenDao.java new file mode 100644 index 0000000..4b003f8 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/security/dao/SysUserTokenDao.java @@ -0,0 +1,23 @@ + + +package com.weather.modules.security.dao; + +import com.weather.common.dao.BaseDao; +import com.weather.modules.security.entity.SysUserTokenEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +/** + * 系统用户Token + * + * @author 123 + */ +@Mapper +public interface SysUserTokenDao extends BaseDao { + + SysUserTokenEntity getByToken(String token); + + SysUserTokenEntity getByUserId(Long userId); + + void updateToken(@Param("userId") Long userId, @Param("token") String token); +} diff --git a/system-admin/src/main/java/com/weather/modules/security/dto/LoginDTO.java b/system-admin/src/main/java/com/weather/modules/security/dto/LoginDTO.java new file mode 100644 index 0000000..0558a17 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/security/dto/LoginDTO.java @@ -0,0 +1,37 @@ + + +package com.weather.modules.security.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import java.io.Serializable; + +/** + * 登录表单 + * + * @author 123 + */ +@Data +@Schema(title = "登录表单") +public class LoginDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(title = "用户名", required = true) + @NotBlank(message="{sysuser.username.require}") + private String username; + + @Schema(title = "密码") + @NotBlank(message="{sysuser.password.require}") + private String password; + + @Schema(title = "验证码") + @NotBlank(message="{sysuser.captcha.require}") + private String captcha; + + @Schema(title = "唯一标识") + @NotBlank(message="{sysuser.uuid.require}") + private String uuid; + +} diff --git a/system-admin/src/main/java/com/weather/modules/security/entity/SysUserTokenEntity.java b/system-admin/src/main/java/com/weather/modules/security/entity/SysUserTokenEntity.java new file mode 100644 index 0000000..e47a97a --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/security/entity/SysUserTokenEntity.java @@ -0,0 +1,48 @@ + + +package com.weather.modules.security.entity; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * 系统用户Token + */ +@Data +@TableName("sys_user_token") +public class SysUserTokenEntity implements Serializable { + private static final long serialVersionUID = 1L; + /** + * id + */ + @TableId + private Long id; + /** + * 用户ID + */ + private Long userId; + /** + * 用户token + */ + private String token; + /** + * 过期时间 + */ + private Date expireDate; + /** + * 更新时间 + */ + private Date updateDate; + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + private Date createDate; + +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/security/oauth2/Oauth2Filter.java b/system-admin/src/main/java/com/weather/modules/security/oauth2/Oauth2Filter.java new file mode 100644 index 0000000..742a1e7 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/security/oauth2/Oauth2Filter.java @@ -0,0 +1,105 @@ + +package com.weather.modules.security.oauth2; + +import cn.hutool.core.util.StrUtil; +import com.weather.common.constant.Constant; +import com.weather.common.exception.ErrorCode; +import com.weather.common.utils.HttpContextUtils; +import com.weather.common.utils.JsonUtils; +import com.weather.common.utils.Result; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.shiro.authc.AuthenticationException; +import org.apache.shiro.authc.AuthenticationToken; +import org.apache.shiro.web.filter.authc.AuthenticatingFilter; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.RequestMethod; + +import java.io.IOException; + +/** + * oauth2过滤器 + * + * @author 123 + */ +public class Oauth2Filter extends AuthenticatingFilter { + + @Override + protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception { + //获取请求token + String token = getRequestToken((HttpServletRequest) request); + + if (StrUtil.isBlank(token)) { + return null; + } + + return new Oauth2Token(token); + } + + @Override + protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) { + if (((HttpServletRequest) request).getMethod().equals(RequestMethod.OPTIONS.name())) { + return true; + } + + return false; + } + + @Override + protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { + //获取请求token,如果token不存在,直接返回401 + String token = getRequestToken((HttpServletRequest) request); + if (StrUtil.isBlank(token)) { + HttpServletResponse httpResponse = (HttpServletResponse) response; + httpResponse.setContentType("application/json;charset=utf-8"); + httpResponse.setHeader("Access-Control-Allow-Credentials", "true"); + httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtils.getOrigin()); + + String json = JsonUtils.toJsonString(new Result().error(ErrorCode.UNAUTHORIZED)); + + httpResponse.getWriter().print(json); + + return false; + } + + return executeLogin(request, response); + } + + @Override + protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) { + HttpServletResponse httpResponse = (HttpServletResponse) response; + httpResponse.setContentType("application/json;charset=utf-8"); + httpResponse.setHeader("Access-Control-Allow-Credentials", "true"); + httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtils.getOrigin()); + try { + //处理登录失败的异常 + Throwable throwable = e.getCause() == null ? e : e.getCause(); + Result r = new Result().error(HttpStatus.UNAUTHORIZED.value(), throwable.getMessage()); + + String json = JsonUtils.toJsonString(r); + httpResponse.getWriter().print(json); + } catch (IOException e1) { + + } + + return false; + } + + /** + * 获取请求的token + */ + private String getRequestToken(HttpServletRequest httpRequest) { + //从header中获取token + String token = httpRequest.getHeader(Constant.TOKEN_HEADER); + + //如果header中不存在token,则从参数中获取token + if (StrUtil.isBlank(token)) { + token = httpRequest.getParameter(Constant.TOKEN_HEADER); + } + + return token; + } + +} diff --git a/system-admin/src/main/java/com/weather/modules/security/oauth2/Oauth2Realm.java b/system-admin/src/main/java/com/weather/modules/security/oauth2/Oauth2Realm.java new file mode 100644 index 0000000..6e9b637 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/security/oauth2/Oauth2Realm.java @@ -0,0 +1,85 @@ + +package com.weather.modules.security.oauth2; + +import com.weather.common.exception.ErrorCode; +import com.weather.common.utils.ConvertUtils; +import com.weather.common.utils.MessageUtils; +import com.weather.modules.security.entity.SysUserTokenEntity; +import com.weather.modules.security.service.ShiroService; +import com.weather.modules.security.user.UserDetail; +import com.weather.modules.sys.entity.SysUserEntity; +import lombok.AllArgsConstructor; +import org.apache.shiro.authc.*; +import org.apache.shiro.authz.AuthorizationInfo; +import org.apache.shiro.authz.SimpleAuthorizationInfo; +import org.apache.shiro.realm.AuthorizingRealm; +import org.apache.shiro.subject.PrincipalCollection; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Set; + +/** + * 认证 + * + * @author 123 + */ +@Component +@AllArgsConstructor +public class Oauth2Realm extends AuthorizingRealm { + private final ShiroService shiroService; + + @Override + public boolean supports(AuthenticationToken token) { + return token instanceof Oauth2Token; + } + + /** + * 授权(验证权限时调用) + */ + @Override + protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { + UserDetail user = (UserDetail) principals.getPrimaryPrincipal(); + + //用户权限列表 + Set permsSet = shiroService.getUserPermissions(user); + + SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); + info.setStringPermissions(permsSet); + return info; + } + + /** + * 认证(登录时调用) + */ + @Override + protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { + String accessToken = (String) token.getPrincipal(); + + //根据accessToken,查询用户信息 + SysUserTokenEntity tokenEntity = shiroService.getByToken(accessToken); + //token失效 + if (tokenEntity == null || tokenEntity.getExpireDate().getTime() < System.currentTimeMillis()) { + throw new IncorrectCredentialsException(MessageUtils.getMessage(ErrorCode.TOKEN_INVALID)); + } + + //查询用户信息 + SysUserEntity userEntity = shiroService.getUser(tokenEntity.getUserId()); + + //转换成UserDetail对象 + UserDetail userDetail = ConvertUtils.sourceToTarget(userEntity, UserDetail.class); + + //获取用户对应的部门数据权限 + List deptIdList = shiroService.getDataScopeList(userDetail.getId()); + userDetail.setDeptIdList(deptIdList); + + //账号锁定 + if (userDetail.getStatus() == 0) { + throw new LockedAccountException(MessageUtils.getMessage(ErrorCode.ACCOUNT_LOCK)); + } + + SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(userDetail, accessToken, getName()); + return info; + } + +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/security/oauth2/Oauth2Token.java b/system-admin/src/main/java/com/weather/modules/security/oauth2/Oauth2Token.java new file mode 100644 index 0000000..dea4b91 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/security/oauth2/Oauth2Token.java @@ -0,0 +1,28 @@ + + +package com.weather.modules.security.oauth2; + +import org.apache.shiro.authc.AuthenticationToken; + +/** + * token + * + * @author 123 + */ +public class Oauth2Token implements AuthenticationToken { + private String token; + + public Oauth2Token(String token){ + this.token = token; + } + + @Override + public String getPrincipal() { + return token; + } + + @Override + public Object getCredentials() { + return token; + } +} diff --git a/system-admin/src/main/java/com/weather/modules/security/oauth2/TokenGenerator.java b/system-admin/src/main/java/com/weather/modules/security/oauth2/TokenGenerator.java new file mode 100644 index 0000000..6098e1a --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/security/oauth2/TokenGenerator.java @@ -0,0 +1,46 @@ + + +package com.weather.modules.security.oauth2; + +import com.weather.common.exception.CommonException; + +import java.security.MessageDigest; +import java.util.UUID; + +/** + * 生成token + * + * @author 123 + */ +public class TokenGenerator { + + public static String generateValue() { + return generateValue(UUID.randomUUID().toString()); + } + + private static final char[] HEX_CODE = "0123456789abcdef".toCharArray(); + + public static String toHexString(byte[] data) { + if(data == null) { + return null; + } + StringBuilder r = new StringBuilder(data.length*2); + for ( byte b : data) { + r.append(HEX_CODE[(b >> 4) & 0xF]); + r.append(HEX_CODE[(b & 0xF)]); + } + return r.toString(); + } + + public static String generateValue(String param) { + try { + MessageDigest algorithm = MessageDigest.getInstance("MD5"); + algorithm.reset(); + algorithm.update(param.getBytes()); + byte[] messageDigest = algorithm.digest(); + return toHexString(messageDigest); + } catch (Exception e) { + throw new CommonException("token invalid", e); + } + } +} diff --git a/system-admin/src/main/java/com/weather/modules/security/password/BCrypt.java b/system-admin/src/main/java/com/weather/modules/security/password/BCrypt.java new file mode 100644 index 0000000..c998831 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/security/password/BCrypt.java @@ -0,0 +1,662 @@ +package com.weather.modules.security.password; + +import java.io.ByteArrayOutputStream; +import java.io.UnsupportedEncodingException; +import java.security.SecureRandom; + +/** + * BCrypt implements OpenBSD-style Blowfish password hashing using the scheme described in + * "A Future-Adaptable Password Scheme" by Niels Provos and David Mazieres. + *

+ * This password hashing system tries to thwart off-line password cracking using a + * computationally-intensive hashing algorithm, based on Bruce Schneier's Blowfish cipher. + * The work factor of the algorithm is parameterised, so it can be increased as computers + * get faster. + *

+ * Usage is really simple. To hash a password for the first time, call the hashpw method + * with a random salt, like this: + *

+ * + * String pw_hash = BCrypt.hashpw(plain_password, BCrypt.gensalt());
+ *
+ *

+ * To check whether a plaintext password matches one that has been hashed previously, use + * the checkpw method: + *

+ * + * if (BCrypt.checkpw(candidate_password, stored_hash))
+ *     System.out.println("It matches");
+ * else
+ *     System.out.println("It does not match");
+ *
+ *

+ * The gensalt() method takes an optional parameter (log_rounds) that determines the + * computational complexity of the hashing: + *

+ * + * String strong_salt = BCrypt.gensalt(10)
+ * String stronger_salt = BCrypt.gensalt(12)
+ *
+ *

+ * The amount of work increases exponentially (2**log_rounds), so each increment is twice + * as much work. The default log_rounds is 10, and the valid range is 4 to 31. + * + * @author Damien Miller + */ +public class BCrypt { + // BCrypt parameters + + private static final int GENSALT_DEFAULT_LOG2_ROUNDS = 10; + private static final int BCRYPT_SALT_LEN = 16; + // Blowfish parameters + private static final int BLOWFISH_NUM_ROUNDS = 16; + // Initial contents of key schedule + private static final int P_orig[] = { 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, + 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, + 0xbe5466cf, 0x34e90c6c, 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, + 0x9216d5d9, 0x8979fb1b }; + private static final int S_orig[] = { 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, + 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, + 0x0801f2e2, 0x858efc16, 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, + 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, + 0x9c30d539, 0x2af26013, 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, + 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, 0xbd314b27, + 0x78af2fda, 0x55605c60, 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, + 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, + 0xb3ee1411, 0x636fbc2a, 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, + 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, 0x3b8f4898, 0x6b4bb9af, + 0xc4bfe81b, 0x66282193, 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, + 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, + 0x0f6d6ff3, 0x83f44239, 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, + 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, 0x6a51a0d2, 0xd8542f68, + 0x960fa728, 0xab5133a3, 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, + 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, + 0x7d84a5c3, 0x3b8b5ebe, 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, + 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, 0x37d0d724, 0xd00a1248, + 0xdb0fead3, 0x49f1c09b, 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, + 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, + 0x5e5c9ec2, 0x196a2463, 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, + 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd, + 0x660f2807, 0x192e4bb3, 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, + 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, + 0x8ea5e9f8, 0xdb3222f8, 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, + 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, + 0x1a87562e, 0xdf1769db, 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, + 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, + 0x4afcb56c, 0x2dd1d35b, 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, + 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, 0xef20cada, 0x36774c01, + 0xd07e9efe, 0x2bf11fb4, 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, + 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, + 0x8888b812, 0x900df01c, 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, + 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, 0xe5a0cc0f, 0xb56f74e8, + 0x18acf3d6, 0xce89e299, 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, + 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, + 0xc75442f5, 0xfb9d35cf, 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, + 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, 0x2464369b, 0xf009b91e, + 0x5563911d, 0x59dfa6aa, 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, + 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, + 0x1b510052, 0x9a532915, 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, + 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6, + 0xff34052e, 0xc5855664, 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a, + 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d, + 0x9cee60b8, 0x8fedb266, 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, + 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65, + 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, + 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9, + 0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, + 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, 0xb03ada37, 0xf0500c0d, + 0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, + 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc, + 0xc8b57634, 0x9af3dda7, 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, + 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, 0x4e548b38, 0x4f6db908, + 0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, + 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, + 0x501adde6, 0x9f84cd87, 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, + 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 0xef1c1847, 0x3215d908, + 0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, + 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b, + 0x3c11183b, 0x5924a509, 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, + 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa, + 0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, + 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d, + 0x1939260f, 0x19c27960, 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, + 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, 0xc332ddef, 0xbe6c5aa5, + 0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, + 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96, + 0x0334fe1e, 0xaa0363cf, 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, + 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, 0x648b1eaf, 0x19bdf0ca, + 0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, + 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77, + 0x11ed935f, 0x16681281, 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, + 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, 0xcdb30aeb, 0x532e3054, + 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, + 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea, + 0xdb6c4f15, 0xfacb4fd0, 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, + 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 0xcf62a1f2, 0x5b8d2646, + 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, + 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea, + 0x1dadf43e, 0x233f7061, 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, + 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e, + 0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, + 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd, + 0x675fda79, 0xe3674340, 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, + 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7, 0xe93d5a68, 0x948140f7, + 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, + 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, 0x1e39f62e, 0x97244546, + 0x14214f74, 0xbf8b8840, 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, + 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, 0x96eb27b3, 0x55fd3941, + 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, + 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, 0x4f3ffea2, 0xe887ad8c, + 0xb58ce006, 0x7af4d6b6, 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, + 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, 0x1dc9faf7, 0x4b6d1856, + 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, + 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, 0x55533a3a, 0x20838d87, + 0xfe6ba9b7, 0xd096954b, 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, + 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, 0xfdf8e802, 0x04272f70, + 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, + 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, 0x325f51eb, 0xd59bc0d1, + 0xf2bcc18f, 0x41113564, 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, + 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, 0x6b2395e0, 0x333e92e1, + 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, + 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, 0x5449a36f, 0x877d48fa, + 0xc39dfd27, 0xf33e8d1e, 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, + 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, 0xc67b5510, 0x6d672c37, + 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, + 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, 0xbb132f88, 0x515bad24, + 0x7b9479bf, 0x763bd6eb, 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, + 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, 0x6a124237, 0xb79251e7, + 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, + 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, 0x64af674e, 0xda86a85f, + 0xbebfe988, 0x64e4c3fe, 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, + 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, 0x83426b33, 0xf01eab71, + 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, + 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, 0x5366f9c3, 0xc8b38e74, + 0xb475f255, 0x46fcd9b9, 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, + 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, 0xb90bace1, 0xbb8205d0, + 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, + 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, 0x1ab93d1d, 0x0ba5a4df, + 0xa186f20f, 0x2868f169, 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, + 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, 0x9af88c27, 0x773f8641, + 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, + 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, 0xbbcbee56, 0x90bcb6de, + 0xebfc7da1, 0xce591d76, 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, + 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, 0xed545578, 0x08fca5b5, + 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, + 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, 0xd79a3234, 0x92638212, + 0x670efa8e, 0x406000e0, 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, + 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, + 0xd62d1c7e, 0xc700c47b, 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, + 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, + 0xd5730a1d, 0x4cd04dc6, 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, + 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, 0xc089c2b8, 0x43242ef6, + 0xa51e03aa, 0x9cf2d0a4, 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, + 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, + 0x3f046f69, 0x77fa0a59, 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, + 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, 0x96d5ac3a, 0x017da67d, + 0xd1cf3ed6, 0x7c7d2d28, 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, + 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, + 0xf8d56629, 0x79132e28, 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, + 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2, + 0x97271aec, 0xa93a072a, 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, + 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, + 0xabcc5167, 0xccad925f, 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, + 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46, + 0x48de5369, 0x6413e680, 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, + 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, + 0xccd2017f, 0x6bb4e3bb, 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, + 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, 0xd29be463, 0x542f5d9e, + 0xaec2771b, 0xf64e6370, 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, + 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, + 0x06b89fb4, 0xce6ea048, 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, + 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, 0xa08839e1, 0x51ce794b, + 0x2f32c9b7, 0xa01fbac9, 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, + 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, + 0x8df9317c, 0xe0b12b4f, 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, + 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, 0xfae59361, 0xceb69ceb, + 0xc2a86459, 0x12baa8d1, 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, + 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, + 0xd3a0342b, 0x8971f21e, 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, + 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, 0xe54cda54, 0x1edad891, + 0xce6279cf, 0xcd3e7e6f, 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, + 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, + 0x6e163697, 0x88d273cc, 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, + 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00, + 0xbb25bfe2, 0x35bdd2f6, 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, + 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, + 0x77afa1c5, 0x20756060, 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, + 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, + 0x3f09252d, 0xc208e69f, 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6 }; + // bcrypt IV: "OrpheanBeholderScryDoubt" + static private final int bf_crypt_ciphertext[] = { 0x4f727068, 0x65616e42, + 0x65686f6c, 0x64657253, 0x63727944, 0x6f756274 }; + // Table for Base64 encoding + static private final char base64_code[] = { '.', '/', 'A', 'B', 'C', 'D', 'E', 'F', + 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', + 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', + 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', + 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; + // Table for Base64 decoding + static private final byte index_64[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, -1, -1, -1, -1, -1, -1, -1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, + -1, -1, -1, -1, -1, -1, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, -1, -1, -1, -1, -1 }; + static final int MIN_LOG_ROUNDS = 4; + static final int MAX_LOG_ROUNDS = 31; + // Expanded Blowfish key + private int P[]; + private int S[]; + + /** + * Encode a byte array using bcrypt's slightly-modified base64 encoding scheme. Note + * that this is not compatible with the standard MIME-base64 + * encoding. + * + * @param d the byte array to encode + * @param len the number of bytes to encode + * @param rs the destination buffer for the base64-encoded string + * @exception IllegalArgumentException if the length is invalid + */ + static void encode_base64(byte d[], int len, StringBuilder rs) + throws IllegalArgumentException { + int off = 0; + int c1, c2; + + if (len <= 0 || len > d.length) { + throw new IllegalArgumentException("Invalid len"); + } + + while (off < len) { + c1 = d[off++] & 0xff; + rs.append(base64_code[(c1 >> 2) & 0x3f]); + c1 = (c1 & 0x03) << 4; + if (off >= len) { + rs.append(base64_code[c1 & 0x3f]); + break; + } + c2 = d[off++] & 0xff; + c1 |= (c2 >> 4) & 0x0f; + rs.append(base64_code[c1 & 0x3f]); + c1 = (c2 & 0x0f) << 2; + if (off >= len) { + rs.append(base64_code[c1 & 0x3f]); + break; + } + c2 = d[off++] & 0xff; + c1 |= (c2 >> 6) & 0x03; + rs.append(base64_code[c1 & 0x3f]); + rs.append(base64_code[c2 & 0x3f]); + } + } + + /** + * Look up the 3 bits base64-encoded by the specified character, range-checking + * against conversion table + * @param x the base64-encoded value + * @return the decoded value of x + */ + private static byte char64(char x) { + if (x > index_64.length) { + return -1; + } + return index_64[x]; + } + + /** + * Decode a string encoded using bcrypt's base64 scheme to a byte array. Note that + * this is *not* compatible with the standard MIME-base64 encoding. + * @param s the string to decode + * @param maxolen the maximum number of bytes to decode + * @return an array containing the decoded bytes + * @throws IllegalArgumentException if maxolen is invalid + */ + static byte[] decode_base64(String s, int maxolen) throws IllegalArgumentException { + ByteArrayOutputStream out = new ByteArrayOutputStream(maxolen); + int off = 0, slen = s.length(), olen = 0; + byte c1, c2, c3, c4, o; + + if (maxolen <= 0) { + throw new IllegalArgumentException("Invalid maxolen"); + } + + while (off < slen - 1 && olen < maxolen) { + c1 = char64(s.charAt(off++)); + c2 = char64(s.charAt(off++)); + if (c1 == -1 || c2 == -1) { + break; + } + o = (byte) (c1 << 2); + o |= (c2 & 0x30) >> 4; + out.write(o); + if (++olen >= maxolen || off >= slen) { + break; + } + c3 = char64(s.charAt(off++)); + if (c3 == -1) { + break; + } + o = (byte) ((c2 & 0x0f) << 4); + o |= (c3 & 0x3c) >> 2; + out.write(o); + if (++olen >= maxolen || off >= slen) { + break; + } + c4 = char64(s.charAt(off++)); + o = (byte) ((c3 & 0x03) << 6); + o |= c4; + out.write(o); + ++olen; + } + + return out.toByteArray(); + } + + /** + * Blowfish encipher a single 64-bit block encoded as two 32-bit halves + * @param lr an array containing the two 32-bit half blocks + * @param off the position in the array of the blocks + */ + private final void encipher(int lr[], int off) { + int i, n, l = lr[off], r = lr[off + 1]; + + l ^= P[0]; + for (i = 0; i <= BLOWFISH_NUM_ROUNDS - 2;) { + // Feistel substitution on left word + n = S[(l >> 24) & 0xff]; + n += S[0x100 | ((l >> 16) & 0xff)]; + n ^= S[0x200 | ((l >> 8) & 0xff)]; + n += S[0x300 | (l & 0xff)]; + r ^= n ^ P[++i]; + + // Feistel substitution on right word + n = S[(r >> 24) & 0xff]; + n += S[0x100 | ((r >> 16) & 0xff)]; + n ^= S[0x200 | ((r >> 8) & 0xff)]; + n += S[0x300 | (r & 0xff)]; + l ^= n ^ P[++i]; + } + lr[off] = r ^ P[BLOWFISH_NUM_ROUNDS + 1]; + lr[off + 1] = l; + } + + /** + * Cycically extract a word of key material + * @param data the string to extract the data from + * @param offp a "pointer" (as a one-entry array) to the current offset into data + * @return the next word of material from data + */ + private static int streamtoword(byte data[], int offp[]) { + int i; + int word = 0; + int off = offp[0]; + + for (i = 0; i < 4; i++) { + word = (word << 8) | (data[off] & 0xff); + off = (off + 1) % data.length; + } + + offp[0] = off; + return word; + } + + /** + * Initialise the Blowfish key schedule + */ + private void init_key() { + P = (int[]) P_orig.clone(); + S = (int[]) S_orig.clone(); + } + + /** + * Key the Blowfish cipher + * @param key an array containing the key + */ + private void key(byte key[]) { + int i; + int koffp[] = { 0 }; + int lr[] = { 0, 0 }; + int plen = P.length, slen = S.length; + + for (i = 0; i < plen; i++) { + P[i] = P[i] ^ streamtoword(key, koffp); + } + + for (i = 0; i < plen; i += 2) { + encipher(lr, 0); + P[i] = lr[0]; + P[i + 1] = lr[1]; + } + + for (i = 0; i < slen; i += 2) { + encipher(lr, 0); + S[i] = lr[0]; + S[i + 1] = lr[1]; + } + } + + /** + * Perform the "enhanced key schedule" step described by Provos and Mazieres in + * "A Future-Adaptable Password Scheme" http://www.openbsd.org/papers/bcrypt-paper.ps + * @param data salt information + * @param key password information + */ + private void ekskey(byte data[], byte key[]) { + int i; + int koffp[] = { 0 }, doffp[] = { 0 }; + int lr[] = { 0, 0 }; + int plen = P.length, slen = S.length; + + for (i = 0; i < plen; i++) { + P[i] = P[i] ^ streamtoword(key, koffp); + } + + for (i = 0; i < plen; i += 2) { + lr[0] ^= streamtoword(data, doffp); + lr[1] ^= streamtoword(data, doffp); + encipher(lr, 0); + P[i] = lr[0]; + P[i + 1] = lr[1]; + } + + for (i = 0; i < slen; i += 2) { + lr[0] ^= streamtoword(data, doffp); + lr[1] ^= streamtoword(data, doffp); + encipher(lr, 0); + S[i] = lr[0]; + S[i + 1] = lr[1]; + } + } + + static long roundsForLogRounds(int log_rounds) { + if (log_rounds < 4 || log_rounds > 31) { + throw new IllegalArgumentException("Bad number of rounds"); + } + return 1L << log_rounds; + } + + /** + * Perform the central password hashing step in the bcrypt scheme + * @param password the password to hash + * @param salt the binary salt to hash with the password + * @param log_rounds the binary logarithm of the number of rounds of hashing to apply + * @return an array containing the binary hashed password + */ + private byte[] crypt_raw(byte password[], byte salt[], int log_rounds) { + int cdata[] = (int[]) bf_crypt_ciphertext.clone(); + int clen = cdata.length; + byte ret[]; + + long rounds = roundsForLogRounds(log_rounds); + + init_key(); + ekskey(salt, password); + for (long i = 0; i < rounds; i++) { + key(password); + key(salt); + } + + for (int i = 0; i < 64; i++) { + for (int j = 0; j < (clen >> 1); j++) { + encipher(cdata, j << 1); + } + } + + ret = new byte[clen * 4]; + for (int i = 0, j = 0; i < clen; i++) { + ret[j++] = (byte) ((cdata[i] >> 24) & 0xff); + ret[j++] = (byte) ((cdata[i] >> 16) & 0xff); + ret[j++] = (byte) ((cdata[i] >> 8) & 0xff); + ret[j++] = (byte) (cdata[i] & 0xff); + } + return ret; + } + + /** + * Hash a password using the OpenBSD bcrypt scheme + * @param password the password to hash + * @param salt the salt to hash with (perhaps generated using BCrypt.gensalt) + * @return the hashed password + * @throws IllegalArgumentException if invalid salt is passed + */ + public static String hashpw(String password, String salt) throws IllegalArgumentException { + BCrypt B; + String real_salt; + byte passwordb[], saltb[], hashed[]; + char minor = (char) 0; + int rounds, off = 0; + StringBuilder rs = new StringBuilder(); + + if (salt == null) { + throw new IllegalArgumentException("salt cannot be null"); + } + + int saltLength = salt.length(); + + if (saltLength < 28) { + throw new IllegalArgumentException("Invalid salt"); + } + + if (salt.charAt(0) != '$' || salt.charAt(1) != '2') { + throw new IllegalArgumentException("Invalid salt version"); + } + if (salt.charAt(2) == '$') { + off = 3; + } + else { + minor = salt.charAt(2); + if (minor != 'a' || salt.charAt(3) != '$') { + throw new IllegalArgumentException("Invalid salt revision"); + } + off = 4; + } + + if (saltLength - off < 25) { + throw new IllegalArgumentException("Invalid salt"); + } + + // Extract number of rounds + if (salt.charAt(off + 2) > '$') { + throw new IllegalArgumentException("Missing salt rounds"); + } + rounds = Integer.parseInt(salt.substring(off, off + 2)); + + real_salt = salt.substring(off + 3, off + 25); + try { + passwordb = (password + (minor >= 'a' ? "\000" : "")).getBytes("UTF-8"); + } + catch (UnsupportedEncodingException uee) { + throw new AssertionError("UTF-8 is not supported"); + } + + saltb = decode_base64(real_salt, BCRYPT_SALT_LEN); + + B = new BCrypt(); + hashed = B.crypt_raw(passwordb, saltb, rounds); + + rs.append("$2"); + if (minor >= 'a') { + rs.append(minor); + } + rs.append("$"); + if (rounds < 10) { + rs.append("0"); + } + rs.append(rounds); + rs.append("$"); + encode_base64(saltb, saltb.length, rs); + encode_base64(hashed, bf_crypt_ciphertext.length * 4 - 1, rs); + return rs.toString(); + } + + /** + * Generate a salt for use with the BCrypt.hashpw() method + * @param log_rounds the log2 of the number of rounds of hashing to apply - the work + * factor therefore increases as 2**log_rounds. Minimum 4, maximum 31. + * @param random an instance of SecureRandom to use + * @return an encoded salt value + */ + public static String gensalt(int log_rounds, SecureRandom random) { + if (log_rounds < MIN_LOG_ROUNDS || log_rounds > MAX_LOG_ROUNDS) { + throw new IllegalArgumentException("Bad number of rounds"); + } + StringBuilder rs = new StringBuilder(); + byte rnd[] = new byte[BCRYPT_SALT_LEN]; + + random.nextBytes(rnd); + + rs.append("$2a$"); + if (log_rounds < 10) { + rs.append("0"); + } + rs.append(log_rounds); + rs.append("$"); + encode_base64(rnd, rnd.length, rs); + return rs.toString(); + } + + /** + * Generate a salt for use with the BCrypt.hashpw() method + * @param log_rounds the log2 of the number of rounds of hashing to apply - the work + * factor therefore increases as 2**log_rounds. Minimum 4, maximum 31. + * @return an encoded salt value + */ + public static String gensalt(int log_rounds) { + return gensalt(log_rounds, new SecureRandom()); + } + + /** + * Generate a salt for use with the BCrypt.hashpw() method, selecting a reasonable + * default for the number of hashing rounds to apply + * @return an encoded salt value + */ + public static String gensalt() { + return gensalt(GENSALT_DEFAULT_LOG2_ROUNDS); + } + + /** + * Check that a plaintext password matches a previously hashed one + * @param plaintext the plaintext password to verify + * @param hashed the previously-hashed password + * @return true if the passwords match, false otherwise + */ + public static boolean checkpw(String plaintext, String hashed) { + return equalsNoEarlyReturn(hashed, hashpw(plaintext, hashed)); + } + + static boolean equalsNoEarlyReturn(String a, String b) { + char[] caa = a.toCharArray(); + char[] cab = b.toCharArray(); + + if (caa.length != cab.length) { + return false; + } + + byte ret = 0; + for (int i = 0; i < caa.length; i++) { + ret |= caa[i] ^ cab[i]; + } + return ret == 0; + } +} diff --git a/system-admin/src/main/java/com/weather/modules/security/password/BCryptPasswordEncoder.java b/system-admin/src/main/java/com/weather/modules/security/password/BCryptPasswordEncoder.java new file mode 100644 index 0000000..9914fe3 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/security/password/BCryptPasswordEncoder.java @@ -0,0 +1,82 @@ +package com.weather.modules.security.password; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.security.SecureRandom; +import java.util.regex.Pattern; + +/** + * Implementation of PasswordEncoder that uses the BCrypt strong hashing function. Clients + * can optionally supply a "strength" (a.k.a. log rounds in BCrypt) and a SecureRandom + * instance. The larger the strength parameter the more work will have to be done + * (exponentially) to hash the passwords. The default value is 10. + * + * @author Dave Syer + * + */ +public class BCryptPasswordEncoder implements PasswordEncoder { + private Pattern BCRYPT_PATTERN = Pattern + .compile("\\A\\$2a?\\$\\d\\d\\$[./0-9A-Za-z]{53}"); + private final Log logger = LogFactory.getLog(getClass()); + + private final int strength; + + private final SecureRandom random; + + public BCryptPasswordEncoder() { + this(-1); + } + + /** + * @param strength the log rounds to use, between 4 and 31 + */ + public BCryptPasswordEncoder(int strength) { + this(strength, null); + } + + /** + * @param strength the log rounds to use, between 4 and 31 + * @param random the secure random instance to use + * + */ + public BCryptPasswordEncoder(int strength, SecureRandom random) { + if (strength != -1 && (strength < BCrypt.MIN_LOG_ROUNDS || strength > BCrypt.MAX_LOG_ROUNDS)) { + throw new IllegalArgumentException("Bad strength"); + } + this.strength = strength; + this.random = random; + } + + @Override + public String encode(CharSequence rawPassword) { + String salt; + if (strength > 0) { + if (random != null) { + salt = BCrypt.gensalt(strength, random); + } + else { + salt = BCrypt.gensalt(strength); + } + } + else { + salt = BCrypt.gensalt(); + } + return BCrypt.hashpw(rawPassword.toString(), salt); + } + + @Override + public boolean matches(CharSequence rawPassword, String encodedPassword) { + if (encodedPassword == null || encodedPassword.length() == 0) { + logger.warn("Empty encoded password"); + return false; + } + + if (!BCRYPT_PATTERN.matcher(encodedPassword).matches()) { + logger.warn("Encoded password does not look like BCrypt"); + return false; + } + + return BCrypt.checkpw(rawPassword.toString(), encodedPassword); + } +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/security/password/PasswordEncoder.java b/system-admin/src/main/java/com/weather/modules/security/password/PasswordEncoder.java new file mode 100644 index 0000000..fec3d76 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/security/password/PasswordEncoder.java @@ -0,0 +1,30 @@ +package com.weather.modules.security.password; + +/** + * Service interface for encoding passwords. + * + * The preferred implementation is {@code BCryptPasswordEncoder}. + * + * @author Keith Donald + */ +public interface PasswordEncoder { + + /** + * Encode the raw password. Generally, a good encoding algorithm applies a SHA-1 or + * greater hash combined with an 8-byte or greater randomly generated salt. + */ + String encode(CharSequence rawPassword); + + /** + * Verify the encoded password obtained from storage matches the submitted raw + * password after it too is encoded. Returns true if the passwords match, false if + * they do not. The stored password itself is never decoded. + * + * @param rawPassword the raw password to encode and match + * @param encodedPassword the encoded password from storage to compare with + * @return true if the raw password, after encoding, matches the encoded password from + * storage + */ + boolean matches(CharSequence rawPassword, String encodedPassword); + +} diff --git a/system-admin/src/main/java/com/weather/modules/security/password/PasswordUtils.java b/system-admin/src/main/java/com/weather/modules/security/password/PasswordUtils.java new file mode 100644 index 0000000..51dd111 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/security/password/PasswordUtils.java @@ -0,0 +1,58 @@ +/** + * Copyright 2018 人人开源 https://www.renren.io + *

+ * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + *

+ * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *

+ * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.weather.modules.security.password; + +/** + * 密码工具类 + * + * @author 123 + * @since 1.0.0 + */ +public class PasswordUtils { + private static PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + + /** + * 加密 + * @param str 字符串 + * @return 返回加密字符串 + */ + public static String encode(String str){ + return passwordEncoder.encode(str); + } + + + /** + * 比较密码是否相等 + * @param str 明文密码 + * @param password 加密后密码 + * @return true:成功 false:失败 + */ + public static boolean matches(String str, String password){ + return passwordEncoder.matches(str, password); + } + + + public static void main(String[] args) { + String str = "admin"; + String password = encode(str); + + System.out.println(password); + System.out.println(matches(str, password)); + } + +} diff --git a/system-admin/src/main/java/com/weather/modules/security/service/CaptchaService.java b/system-admin/src/main/java/com/weather/modules/security/service/CaptchaService.java new file mode 100644 index 0000000..da4557d --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/security/service/CaptchaService.java @@ -0,0 +1,28 @@ + + +package com.weather.modules.security.service; + +import jakarta.servlet.http.HttpServletResponse; + +import java.io.IOException; + +/** + * 验证码 + * + * @author 123 + */ +public interface CaptchaService { + + /** + * 图片验证码 + */ + void create(HttpServletResponse response, String uuid) throws IOException; + + /** + * 验证码效验 + * @param uuid uuid + * @param code 验证码 + * @return true:成功 false:失败 + */ + boolean validate(String uuid, String code); +} diff --git a/system-admin/src/main/java/com/weather/modules/security/service/ShiroService.java b/system-admin/src/main/java/com/weather/modules/security/service/ShiroService.java new file mode 100644 index 0000000..6c9ae1e --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/security/service/ShiroService.java @@ -0,0 +1,37 @@ + + +package com.weather.modules.security.service; + +import com.weather.modules.security.user.UserDetail; +import com.weather.modules.sys.entity.SysUserEntity; +import com.weather.modules.security.entity.SysUserTokenEntity; + +import java.util.List; +import java.util.Set; + +/** + * shiro相关接口 + * + * @author 123 + */ +public interface ShiroService { + /** + * 获取用户权限列表 + */ + Set getUserPermissions(UserDetail user); + + SysUserTokenEntity getByToken(String token); + + /** + * 根据用户ID,查询用户 + * @param userId + */ + SysUserEntity getUser(Long userId); + + /** + * 获取用户对应的部门数据权限 + * @param userId 用户ID + * @return 返回部门ID列表 + */ + List getDataScopeList(Long userId); +} diff --git a/system-admin/src/main/java/com/weather/modules/security/service/SysUserTokenService.java b/system-admin/src/main/java/com/weather/modules/security/service/SysUserTokenService.java new file mode 100644 index 0000000..582cdb1 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/security/service/SysUserTokenService.java @@ -0,0 +1,28 @@ + + +package com.weather.modules.security.service; + +import com.weather.common.service.BaseService; +import com.weather.common.utils.Result; +import com.weather.modules.security.entity.SysUserTokenEntity; + +/** + * 用户Token + * + * @author 123 + */ +public interface SysUserTokenService extends BaseService { + + /** + * 生成token + * @param userId 用户ID + */ + Result createToken(Long userId); + + /** + * 退出,修改token值 + * @param userId 用户ID + */ + void logout(Long userId); + +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/security/service/impl/CaptchaServiceImpl.java b/system-admin/src/main/java/com/weather/modules/security/service/impl/CaptchaServiceImpl.java new file mode 100644 index 0000000..2b26d2a --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/security/service/impl/CaptchaServiceImpl.java @@ -0,0 +1,92 @@ + +package com.weather.modules.security.service.impl; + +import cn.hutool.cache.Cache; +import cn.hutool.cache.CacheUtil; +import com.wf.captcha.SpecCaptcha; +import com.wf.captcha.base.Captcha; +import com.weather.common.redis.RedisKeys; +import com.weather.common.redis.RedisUtils; +import com.weather.modules.security.service.CaptchaService; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.io.IOException; + +/** + * 验证码 + * + * @author 123 + */ +@Service +public class CaptchaServiceImpl implements CaptchaService { + @Resource + private RedisUtils redisUtils; + @Value("${project-options.redis.open: false}") + private boolean open; + /** + * Local Cache 5分钟过期 + */ + Cache localCache = CacheUtil.newLRUCache(1000, 1000 * 60 * 5); + + @Override + public void create(HttpServletResponse response, String uuid) throws IOException { + response.setContentType("image/gif"); + response.setHeader("Pragma", "No-cache"); + response.setHeader("Cache-Control", "no-cache"); + response.setDateHeader("Expires", 0); + + //生成验证码 + SpecCaptcha captcha = new SpecCaptcha(150, 40); + captcha.setLen(5); + captcha.setCharType(Captcha.TYPE_DEFAULT); + captcha.out(response.getOutputStream()); + + //保存到缓存 + setCache(uuid, captcha.text()); + } + + @Override + public boolean validate(String uuid, String code) { + //获取验证码 + String captcha = getCache(uuid); + + //效验成功 + if (code.equalsIgnoreCase(captcha)) { + return true; + } + + return false; + } + + private void setCache(String key, String value) { + if (open) { + key = RedisKeys.getCaptchaKey(key); + redisUtils.set(key, value, 300); + } else { + localCache.put(key, value); + } + } + + private String getCache(String key) { + if (open) { + key = RedisKeys.getCaptchaKey(key); + String captcha = (String) redisUtils.get(key); + //删除验证码 + if (captcha != null) { + redisUtils.delete(key); + } + + return captcha; + } + + String captcha = localCache.get(key); + //删除验证码 + if (captcha != null) { + localCache.remove(key); + } + return captcha; + } +} diff --git a/system-admin/src/main/java/com/weather/modules/security/service/impl/ShiroServiceImpl.java b/system-admin/src/main/java/com/weather/modules/security/service/impl/ShiroServiceImpl.java new file mode 100644 index 0000000..fb1db26 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/security/service/impl/ShiroServiceImpl.java @@ -0,0 +1,66 @@ + +package com.weather.modules.security.service.impl; + +import cn.hutool.core.util.StrUtil; +import com.weather.modules.security.dao.SysUserTokenDao; +import com.weather.modules.security.entity.SysUserTokenEntity; +import com.weather.modules.security.service.ShiroService; +import com.weather.modules.security.user.UserDetail; +import com.weather.modules.sys.dao.SysMenuDao; +import com.weather.modules.sys.dao.SysRoleDataScopeDao; +import com.weather.modules.sys.dao.SysUserDao; +import com.weather.modules.sys.entity.SysUserEntity; +import com.weather.modules.sys.enums.SuperAdminEnum; +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +@Service +@AllArgsConstructor +public class ShiroServiceImpl implements ShiroService { + private final SysMenuDao sysMenuDao; + private final SysUserDao sysUserDao; + private final SysUserTokenDao sysUserTokenDao; + private final SysRoleDataScopeDao sysRoleDataScopeDao; + + @Override + public Set getUserPermissions(UserDetail user) { + //系统管理员,拥有最高权限 + List permissionsList; + if (user.getSuperAdmin() == SuperAdminEnum.YES.value()) { + permissionsList = sysMenuDao.getPermissionsList(); + } else { + permissionsList = sysMenuDao.getUserPermissionsList(user.getId()); + } + + //用户权限列表 + Set permsSet = new HashSet<>(); + for (String permissions : permissionsList) { + if (StrUtil.isBlank(permissions)) { + continue; + } + permsSet.addAll(Arrays.asList(permissions.trim().split(","))); + } + + return permsSet; + } + + @Override + public SysUserTokenEntity getByToken(String token) { + return sysUserTokenDao.getByToken(token); + } + + @Override + public SysUserEntity getUser(Long userId) { + return sysUserDao.selectById(userId); + } + + @Override + public List getDataScopeList(Long userId) { + return sysRoleDataScopeDao.getDataScopeList(userId); + } +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/security/service/impl/SysUserTokenServiceImpl.java b/system-admin/src/main/java/com/weather/modules/security/service/impl/SysUserTokenServiceImpl.java new file mode 100644 index 0000000..7785164 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/security/service/impl/SysUserTokenServiceImpl.java @@ -0,0 +1,80 @@ + + +package com.weather.modules.security.service.impl; + +import com.weather.common.constant.Constant; +import com.weather.common.service.impl.BaseServiceImpl; +import com.weather.modules.security.oauth2.TokenGenerator; +import com.weather.common.utils.Result; +import com.weather.modules.security.dao.SysUserTokenDao; +import com.weather.modules.security.entity.SysUserTokenEntity; +import com.weather.modules.security.service.SysUserTokenService; +import org.springframework.stereotype.Service; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +@Service +public class SysUserTokenServiceImpl extends BaseServiceImpl implements SysUserTokenService { + /** + * 12小时后过期 + */ + private final static int EXPIRE = 3600 * 12; + + @Override + public Result createToken(Long userId) { + //用户token + String token; + + //当前时间 + Date now = new Date(); + //过期时间 + Date expireTime = new Date(now.getTime() + EXPIRE * 1000); + + //判断是否生成过token + SysUserTokenEntity tokenEntity = baseDao.getByUserId(userId); + if(tokenEntity == null){ + //生成一个token + token = TokenGenerator.generateValue(); + + tokenEntity = new SysUserTokenEntity(); + tokenEntity.setUserId(userId); + tokenEntity.setToken(token); + tokenEntity.setUpdateDate(now); + tokenEntity.setExpireDate(expireTime); + + //保存token + this.insert(tokenEntity); + }else{ + //判断token是否过期 + if(tokenEntity.getExpireDate().getTime() < System.currentTimeMillis()){ + //token过期,重新生成token + token = TokenGenerator.generateValue(); + }else { + token = tokenEntity.getToken(); + } + + tokenEntity.setToken(token); + tokenEntity.setUpdateDate(now); + tokenEntity.setExpireDate(expireTime); + + //更新token + this.updateById(tokenEntity); + } + + Map map = new HashMap<>(2); + map.put(Constant.TOKEN_HEADER, token); + map.put("expire", EXPIRE); + return new Result().ok(map); + } + + @Override + public void logout(Long userId) { + //生成一个token + String token = TokenGenerator.generateValue(); + + //修改token + baseDao.updateToken(userId, token); + } +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/security/user/SecurityUser.java b/system-admin/src/main/java/com/weather/modules/security/user/SecurityUser.java new file mode 100644 index 0000000..6a06c83 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/security/user/SecurityUser.java @@ -0,0 +1,51 @@ + + +package com.weather.modules.security.user; + +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.subject.Subject; + +/** + * 用户 + * + * @author 123 + */ +public class SecurityUser { + + public static Subject getSubject() { + try { + return SecurityUtils.getSubject(); + }catch (Exception e){ + return null; + } + } + + /** + * 获取用户信息 + */ + public static UserDetail getUser() { + Subject subject = getSubject(); + if (subject != null) { + UserDetail user = (UserDetail) subject.getPrincipal(); + if (user != null) { + return user; + } + } + UserDetail fallback = UserContextHolder.get(); + return fallback != null ? fallback : new UserDetail(); + } + + /** + * 获取用户ID + */ + public static Long getUserId() { + return getUser().getId(); + } + + /** + * 获取部门ID + */ + public static Long getDeptId() { + return getUser().getDeptId(); + } +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/security/user/UserContextHolder.java b/system-admin/src/main/java/com/weather/modules/security/user/UserContextHolder.java new file mode 100644 index 0000000..240bcb6 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/security/user/UserContextHolder.java @@ -0,0 +1,17 @@ +package com.weather.modules.security.user; + +public class UserContextHolder { + private static final ThreadLocal holder = new ThreadLocal<>(); + + public static void set(UserDetail user) { + holder.set(user); + } + + public static UserDetail get() { + return holder.get(); + } + + public static void remove() { + holder.remove(); + } +} diff --git a/system-admin/src/main/java/com/weather/modules/security/user/UserDetail.java b/system-admin/src/main/java/com/weather/modules/security/user/UserDetail.java new file mode 100644 index 0000000..4636366 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/security/user/UserDetail.java @@ -0,0 +1,35 @@ + + +package com.weather.modules.security.user; + +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 登录用户信息 + * + * @author 123 + */ +@Data +public class UserDetail implements Serializable { + private static final long serialVersionUID = 1L; + + private Long id; + private String username; + private String realName; + private String headUrl; + private Integer gender; + private String email; + private String mobile; + private Long deptId; + private String password; + private Integer status; + private Integer superAdmin; + /** + * 部门数据权限 + */ + private List deptIdList; + +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/sys/controller/IndexController.java b/system-admin/src/main/java/com/weather/modules/sys/controller/IndexController.java new file mode 100644 index 0000000..4b9c3f2 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/controller/IndexController.java @@ -0,0 +1,20 @@ +package com.weather.modules.sys.controller; + +import com.weather.common.utils.Result; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 首页提示 + * + * @author 123 + */ +@RestController +public class IndexController { + + @GetMapping("/") + public Result index(){ + String tips = "你好,weather-admin已启动,请启动weather-ui,才能访问页面!"; + return new Result().ok(tips); + } +} diff --git a/system-admin/src/main/java/com/weather/modules/sys/controller/SysDeptController.java b/system-admin/src/main/java/com/weather/modules/sys/controller/SysDeptController.java new file mode 100644 index 0000000..3c62dc0 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/controller/SysDeptController.java @@ -0,0 +1,91 @@ + +package com.weather.modules.sys.controller; + +import com.weather.common.annotation.LogOperation; +import com.weather.common.utils.Result; +import com.weather.common.validator.AssertUtils; +import com.weather.common.validator.ValidatorUtils; +import com.weather.common.validator.group.AddGroup; +import com.weather.common.validator.group.DefaultGroup; +import com.weather.common.validator.group.UpdateGroup; +import com.weather.modules.sys.dto.SysDeptDTO; +import com.weather.modules.sys.service.SysDeptService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.List; + +/** + * 部门管理 + * + * @author 123 + */ +@RestController +@RequestMapping("/sys/dept") +@Tag(name = "部门管理") +@AllArgsConstructor +public class SysDeptController { + private final SysDeptService sysDeptService; + + @GetMapping("list") + @Operation(summary = "列表") + @RequiresPermissions("sys:dept:list") + public Result> list() { + List list = sysDeptService.list(new HashMap<>(1)); + + return new Result>().ok(list); + } + + @GetMapping("{id}") + @Operation(summary = "信息") + @RequiresPermissions("sys:dept:info") + public Result get(@PathVariable("id") Long id) { + SysDeptDTO data = sysDeptService.get(id); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary = "保存") + @LogOperation("保存") + @RequiresPermissions("sys:dept:save") + public Result save(@RequestBody SysDeptDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + sysDeptService.save(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary = "修改") + @LogOperation("修改") + @RequiresPermissions("sys:dept:update") + public Result update(@RequestBody SysDeptDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + sysDeptService.update(dto); + + return new Result(); + } + + @DeleteMapping("{id}") + @Operation(summary = "删除") + @LogOperation("删除") + @RequiresPermissions("sys:dept:delete") + public Result delete(@PathVariable("id") Long id) { + //效验数据 + AssertUtils.isNull(id, "id"); + + sysDeptService.delete(id); + + return new Result(); + } + +} diff --git a/system-admin/src/main/java/com/weather/modules/sys/controller/SysDictDataController.java b/system-admin/src/main/java/com/weather/modules/sys/controller/SysDictDataController.java new file mode 100644 index 0000000..1bcdfc3 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/controller/SysDictDataController.java @@ -0,0 +1,103 @@ + +package com.weather.modules.sys.controller; + +import com.weather.common.annotation.LogOperation; +import com.weather.common.constant.Constant; +import com.weather.common.page.PageData; +import com.weather.common.utils.Result; +import com.weather.common.validator.AssertUtils; +import com.weather.common.validator.ValidatorUtils; +import com.weather.common.validator.group.DefaultGroup; +import com.weather.common.validator.group.UpdateGroup; +import com.weather.modules.sys.dto.SysDictDataDTO; +import com.weather.modules.sys.service.SysDictDataService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +/** + * 字典数据 + * + * @author 123 + */ +@RestController +@RequestMapping("sys/dict/data") +@Tag(name = "字典数据") +@AllArgsConstructor +public class SysDictDataController { + private final SysDictDataService sysDictDataService; + + @GetMapping("page") + @Operation(summary = "字典数据") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", in = ParameterIn.QUERY, required = true, ref = "int"), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", in = ParameterIn.QUERY, required = true, ref = "int"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段", in = ParameterIn.QUERY, ref = "String"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)", in = ParameterIn.QUERY, ref = "String"), + @Parameter(name = "dictLabel", description = "字典标签", in = ParameterIn.QUERY, ref = "String"), + @Parameter(name = "dictValue", description = "字典值", in = ParameterIn.QUERY, ref = "String") + }) + @RequiresPermissions("sys:dict:page") + public Result> page(@Parameter(hidden = true) @RequestParam Map params) { + //字典类型 + PageData page = sysDictDataService.page(params); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary = "信息") + @RequiresPermissions("sys:dict:info") + public Result get(@PathVariable("id") Long id) { + SysDictDataDTO data = sysDictDataService.get(id); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary = "保存") + @LogOperation("保存") + @RequiresPermissions("sys:dict:save") + public Result save(@RequestBody SysDictDataDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, DefaultGroup.class); + + sysDictDataService.save(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary = "修改") + @LogOperation("修改") + @RequiresPermissions("sys:dict:update") + public Result update(@RequestBody SysDictDataDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + sysDictDataService.update(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary = "删除") + @LogOperation("删除") + @RequiresPermissions("sys:dict:delete") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + sysDictDataService.delete(ids); + + return new Result(); + } + +} diff --git a/system-admin/src/main/java/com/weather/modules/sys/controller/SysDictTypeController.java b/system-admin/src/main/java/com/weather/modules/sys/controller/SysDictTypeController.java new file mode 100644 index 0000000..9e6e5e3 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/controller/SysDictTypeController.java @@ -0,0 +1,113 @@ + +package com.weather.modules.sys.controller; + +import com.weather.common.annotation.LogOperation; +import com.weather.common.constant.Constant; +import com.weather.common.page.PageData; +import com.weather.common.utils.Result; +import com.weather.common.validator.AssertUtils; +import com.weather.common.validator.ValidatorUtils; +import com.weather.common.validator.group.DefaultGroup; +import com.weather.common.validator.group.UpdateGroup; +import com.weather.modules.sys.dto.SysDictTypeDTO; +import com.weather.modules.sys.entity.DictType; +import com.weather.modules.sys.service.SysDictTypeService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +/** + * 字典类型 + * + * @author 123 + */ +@RestController +@RequestMapping("sys/dict/type") +@Tag(name = "字典类型") +@AllArgsConstructor +public class SysDictTypeController { + private final SysDictTypeService sysDictTypeService; + + @GetMapping("page") + @Operation(summary = "字典类型") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", in = ParameterIn.QUERY, required = true, ref = "int"), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", in = ParameterIn.QUERY, required = true, ref = "int"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段", in = ParameterIn.QUERY, ref = "String"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)", in = ParameterIn.QUERY, ref = "String"), + @Parameter(name = "dictType", description = "字典类型", in = ParameterIn.QUERY, ref = "String"), + @Parameter(name = "dictName", description = "字典名称", in = ParameterIn.QUERY, ref = "String") + }) + @RequiresPermissions("sys:dict:page") + public Result> page(@Parameter(hidden = true) @RequestParam Map params) { + //字典类型 + PageData page = sysDictTypeService.page(params); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary = "信息") + @RequiresPermissions("sys:dict:info") + public Result get(@PathVariable("id") Long id) { + SysDictTypeDTO data = sysDictTypeService.get(id); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary = "保存") + @LogOperation("保存") + @RequiresPermissions("sys:dict:save") + public Result save(@RequestBody SysDictTypeDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, DefaultGroup.class); + + sysDictTypeService.save(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary = "修改") + @LogOperation("修改") + @RequiresPermissions("sys:dict:update") + public Result update(@RequestBody SysDictTypeDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + sysDictTypeService.update(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary = "删除") + @LogOperation("删除") + @RequiresPermissions("sys:dict:delete") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + sysDictTypeService.delete(ids); + + return new Result(); + } + + @GetMapping("all") + @Operation(summary = "所有字典数据") + public Result> all() { + List list = sysDictTypeService.getAllList(); + + return new Result>().ok(list); + } + +} diff --git a/system-admin/src/main/java/com/weather/modules/sys/controller/SysMenuController.java b/system-admin/src/main/java/com/weather/modules/sys/controller/SysMenuController.java new file mode 100644 index 0000000..452fa04 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/controller/SysMenuController.java @@ -0,0 +1,131 @@ + +package com.weather.modules.sys.controller; + +import com.weather.common.annotation.LogOperation; +import com.weather.common.exception.ErrorCode; +import com.weather.common.utils.Result; +import com.weather.common.validator.AssertUtils; +import com.weather.common.validator.ValidatorUtils; +import com.weather.common.validator.group.DefaultGroup; +import com.weather.modules.security.service.ShiroService; +import com.weather.modules.security.user.SecurityUser; +import com.weather.modules.security.user.UserDetail; +import com.weather.modules.sys.dto.SysMenuDTO; +import com.weather.modules.sys.enums.MenuTypeEnum; +import com.weather.modules.sys.service.SysMenuService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Set; + +/** + * 菜单管理 + * + * @author 123 + */ +@RestController +@RequestMapping("/sys/menu") +@Tag(name = "菜单管理") +@AllArgsConstructor +public class SysMenuController { + private final SysMenuService sysMenuService; + private final ShiroService shiroService; + + @GetMapping("nav") + @Operation(summary = "导航") + public Result> nav() { + UserDetail user = SecurityUser.getUser(); + List list = sysMenuService.getUserMenuList(user, MenuTypeEnum.MENU.value()); + + return new Result>().ok(list); + } + + @GetMapping("permissions") + @Operation(summary = "权限标识") + public Result> permissions() { + UserDetail user = SecurityUser.getUser(); + Set set = shiroService.getUserPermissions(user); + + return new Result>().ok(set); + } + + @GetMapping("list") + @Operation(summary = "列表") + @Parameter(name = "type", description = "菜单类型 0:菜单 1:按钮 null:全部", in = ParameterIn.QUERY, ref = "int") + @RequiresPermissions("sys:menu:list") + public Result> list(Integer type) { + List list = sysMenuService.getAllMenuList(type); + + return new Result>().ok(list); + } + + @GetMapping("{id}") + @Operation(summary = "信息") + @RequiresPermissions("sys:menu:info") + public Result get(@PathVariable("id") Long id) { + SysMenuDTO data = sysMenuService.get(id); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary = "保存") + @LogOperation("保存") + @RequiresPermissions("sys:menu:save") + public Result save(@RequestBody SysMenuDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, DefaultGroup.class); + + sysMenuService.save(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary = "修改") + @LogOperation("修改") + @RequiresPermissions("sys:menu:update") + public Result update(@RequestBody SysMenuDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, DefaultGroup.class); + + sysMenuService.update(dto); + + return new Result(); + } + + @DeleteMapping("{id}") + @Operation(summary = "删除") + @LogOperation("删除") + @RequiresPermissions("sys:menu:delete") + public Result delete(@PathVariable("id") Long id) { + //效验数据 + AssertUtils.isNull(id, "id"); + + //判断是否有子菜单或按钮 + List list = sysMenuService.getListPid(id); + if (list.size() > 0) { + return new Result().error(ErrorCode.SUB_MENU_EXIST); + } + + sysMenuService.delete(id); + + return new Result(); + } + + @GetMapping("select") + @Operation(summary = "角色菜单权限") + @RequiresPermissions("sys:menu:select") + public Result> select() { + UserDetail user = SecurityUser.getUser(); + List list = sysMenuService.getUserMenuList(user, null); + + return new Result>().ok(list); + } +} diff --git a/system-admin/src/main/java/com/weather/modules/sys/controller/SysParamsController.java b/system-admin/src/main/java/com/weather/modules/sys/controller/SysParamsController.java new file mode 100644 index 0000000..672f2a2 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/controller/SysParamsController.java @@ -0,0 +1,119 @@ + +package com.weather.modules.sys.controller; + +import com.weather.common.annotation.LogOperation; +import com.weather.common.constant.Constant; +import com.weather.common.page.PageData; +import com.weather.common.utils.ExcelUtils; +import com.weather.common.utils.Result; +import com.weather.common.validator.AssertUtils; +import com.weather.common.validator.ValidatorUtils; +import com.weather.common.validator.group.AddGroup; +import com.weather.common.validator.group.DefaultGroup; +import com.weather.common.validator.group.UpdateGroup; +import com.weather.modules.sys.dto.SysParamsDTO; +import com.weather.modules.sys.excel.SysParamsExcel; +import com.weather.modules.sys.service.SysParamsService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import lombok.AllArgsConstructor; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + + +/** + * 参数管理 + * + * @author 123 + * @since 1.0.0 + */ +@RestController +@RequestMapping("sys/params") +@Tag(name = "参数管理") +@AllArgsConstructor +public class SysParamsController { + private final SysParamsService sysParamsService; + + @GetMapping("page") + @Operation(summary = "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", in = ParameterIn.QUERY, required = true, ref = "int"), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", in = ParameterIn.QUERY, required = true, ref = "int"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段", in = ParameterIn.QUERY, ref = "String"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)", in = ParameterIn.QUERY, ref = "String"), + @Parameter(name = "paramCode", description = "参数编码", in = ParameterIn.QUERY, ref = "String") + }) + @RequiresPermissions("sys:params:page") + public Result> page(@Parameter(hidden = true) @RequestParam Map params) { + PageData page = sysParamsService.page(params); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary = "信息") + @RequiresPermissions("sys:params:info") + public Result get(@PathVariable("id") Long id) { + SysParamsDTO data = sysParamsService.get(id); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary = "保存") + @LogOperation("保存") + @RequiresPermissions("sys:params:save") + public Result save(@RequestBody SysParamsDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + sysParamsService.save(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary = "修改") + @LogOperation("修改") + @RequiresPermissions("sys:params:update") + public Result update(@RequestBody SysParamsDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + sysParamsService.update(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary = "删除") + @LogOperation("删除") + @RequiresPermissions("sys:params:delete") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + sysParamsService.delete(ids); + + return new Result(); + } + + @GetMapping("export") + @Operation(summary = "导出") + @LogOperation("导出") + @RequiresPermissions("sys:params:export") + @Parameter(name = "paramCode", description = "参数编码", in = ParameterIn.QUERY, ref = "String") + public void export(@Parameter(hidden = true) @RequestParam Map params, HttpServletResponse response) throws Exception { + List list = sysParamsService.list(params); + + ExcelUtils.exportExcelToTarget(response, null, "参数管理", list, SysParamsExcel.class); + } + +} diff --git a/system-admin/src/main/java/com/weather/modules/sys/controller/SysRoleController.java b/system-admin/src/main/java/com/weather/modules/sys/controller/SysRoleController.java new file mode 100644 index 0000000..f36b73a --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/controller/SysRoleController.java @@ -0,0 +1,124 @@ + +package com.weather.modules.sys.controller; + +import com.weather.common.annotation.LogOperation; +import com.weather.common.constant.Constant; +import com.weather.common.page.PageData; +import com.weather.common.utils.Result; +import com.weather.common.validator.AssertUtils; +import com.weather.common.validator.ValidatorUtils; +import com.weather.common.validator.group.AddGroup; +import com.weather.common.validator.group.DefaultGroup; +import com.weather.common.validator.group.UpdateGroup; +import com.weather.modules.sys.dto.SysRoleDTO; +import com.weather.modules.sys.service.SysRoleDataScopeService; +import com.weather.modules.sys.service.SysRoleMenuService; +import com.weather.modules.sys.service.SysRoleService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 角色管理 + * + * @author 123 + */ +@RestController +@RequestMapping("/sys/role") +@Tag(name = "角色管理") +@AllArgsConstructor +public class SysRoleController { + private final SysRoleService sysRoleService; + private final SysRoleMenuService sysRoleMenuService; + private final SysRoleDataScopeService sysRoleDataScopeService; + + @GetMapping("page") + @Operation(summary = "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", in = ParameterIn.QUERY, required = true, ref = "int"), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", in = ParameterIn.QUERY, required = true, ref = "int"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段", in = ParameterIn.QUERY, ref = "String"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)", in = ParameterIn.QUERY, ref = "String"), + @Parameter(name = "name", description = "角色名", in = ParameterIn.QUERY, ref = "String") + }) + @RequiresPermissions("sys:role:page") + public Result> page(@Parameter(hidden = true) @RequestParam Map params) { + PageData page = sysRoleService.page(params); + + return new Result>().ok(page); + } + + @GetMapping("list") + @Operation(summary = "列表") + @RequiresPermissions("sys:role:list") + public Result> list() { + List data = sysRoleService.list(new HashMap<>(1)); + + return new Result>().ok(data); + } + + @GetMapping("{id}") + @Operation(summary = "信息") + @RequiresPermissions("sys:role:info") + public Result get(@PathVariable("id") Long id) { + SysRoleDTO data = sysRoleService.get(id); + + //查询角色对应的菜单 + List menuIdList = sysRoleMenuService.getMenuIdList(id); + data.setMenuIdList(menuIdList); + + //查询角色对应的数据权限 + List deptIdList = sysRoleDataScopeService.getDeptIdList(id); + data.setDeptIdList(deptIdList); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary = "保存") + @LogOperation("保存") + @RequiresPermissions("sys:role:save") + public Result save(@RequestBody SysRoleDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + sysRoleService.save(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary = "修改") + @LogOperation("修改") + @RequiresPermissions("sys:role:update") + public Result update(@RequestBody SysRoleDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + sysRoleService.update(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary = "删除") + @LogOperation("删除") + @RequiresPermissions("sys:role:delete") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + sysRoleService.delete(ids); + + return new Result(); + } +} diff --git a/system-admin/src/main/java/com/weather/modules/sys/controller/SysUserController.java b/system-admin/src/main/java/com/weather/modules/sys/controller/SysUserController.java new file mode 100644 index 0000000..a59487d --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/controller/SysUserController.java @@ -0,0 +1,157 @@ + +package com.weather.modules.sys.controller; + +import com.weather.common.annotation.LogOperation; +import com.weather.common.constant.Constant; +import com.weather.common.exception.ErrorCode; +import com.weather.common.page.PageData; +import com.weather.common.utils.ConvertUtils; +import com.weather.common.utils.ExcelUtils; +import com.weather.common.utils.Result; +import com.weather.common.validator.AssertUtils; +import com.weather.common.validator.ValidatorUtils; +import com.weather.common.validator.group.AddGroup; +import com.weather.common.validator.group.DefaultGroup; +import com.weather.common.validator.group.UpdateGroup; +import com.weather.modules.security.password.PasswordUtils; +import com.weather.modules.security.user.SecurityUser; +import com.weather.modules.security.user.UserDetail; +import com.weather.modules.sys.dto.PasswordDTO; +import com.weather.modules.sys.dto.SysUserDTO; +import com.weather.modules.sys.excel.SysUserExcel; +import com.weather.modules.sys.service.SysRoleUserService; +import com.weather.modules.sys.service.SysUserService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import lombok.AllArgsConstructor; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * 用户管理 + * + * @author 123 + */ +@RestController +@RequestMapping("/sys/user") +@Tag(name = "用户管理") +@AllArgsConstructor +public class SysUserController { + private final SysUserService sysUserService; + private final SysRoleUserService sysRoleUserService; + + @GetMapping("page") + @Operation(summary = "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", in = ParameterIn.QUERY, required = true, ref = "int"), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", in = ParameterIn.QUERY, required = true, ref = "int"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段", in = ParameterIn.QUERY, ref = "String"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)", in = ParameterIn.QUERY, ref = "String"), + @Parameter(name = "username", description = "用户名", in = ParameterIn.QUERY, ref = "String"), + @Parameter(name = "gender", description = "性别", in = ParameterIn.QUERY, ref = "String"), + @Parameter(name = "deptId", description = "部门ID", in = ParameterIn.QUERY, ref = "String") + }) + @RequiresPermissions("sys:user:page") + public Result> page(@Parameter(hidden = true) @RequestParam Map params) { + PageData page = sysUserService.page(params); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary = "信息") + @RequiresPermissions("sys:user:info") + public Result get(@PathVariable("id") Long id) { + SysUserDTO data = sysUserService.get(id); + + //用户角色列表 + List roleIdList = sysRoleUserService.getRoleIdList(id); + data.setRoleIdList(roleIdList); + + return new Result().ok(data); + } + + @GetMapping("info") + @Operation(summary = "登录用户信息") + public Result info() { + SysUserDTO data = ConvertUtils.sourceToTarget(SecurityUser.getUser(), SysUserDTO.class); + return new Result().ok(data); + } + + @PutMapping("password") + @Operation(summary = "修改密码") + @LogOperation("修改密码") + public Result password(@RequestBody PasswordDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto); + + UserDetail user = SecurityUser.getUser(); + + //原密码不正确 + if (!PasswordUtils.matches(dto.getPassword(), user.getPassword())) { + return new Result().error(ErrorCode.PASSWORD_ERROR); + } + + sysUserService.updatePassword(user.getId(), dto.getNewPassword()); + + return new Result(); + } + + @PostMapping + @Operation(summary = "保存") + @LogOperation("保存") + @RequiresPermissions("sys:user:save") + public Result save(@RequestBody SysUserDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + sysUserService.save(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary = "修改") + @LogOperation("修改") + @RequiresPermissions("sys:user:update") + public Result update(@RequestBody SysUserDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + sysUserService.update(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary = "删除") + @LogOperation("删除") + @RequiresPermissions("sys:user:delete") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + sysUserService.deleteBatchIds(Arrays.asList(ids)); + + return new Result(); + } + + @GetMapping("export") + @Operation(summary = "导出") + @LogOperation("导出") + @RequiresPermissions("sys:user:export") + @Parameter(name = "username", description = "用户名", in = ParameterIn.QUERY, ref = "String") + public void export(@Parameter(hidden = true) @RequestParam Map params, HttpServletResponse response) throws Exception { + List list = sysUserService.list(params); + + ExcelUtils.exportExcelToTarget(response, null, "用户管理", list, SysUserExcel.class); + } +} diff --git a/system-admin/src/main/java/com/weather/modules/sys/dao/SysDeptDao.java b/system-admin/src/main/java/com/weather/modules/sys/dao/SysDeptDao.java new file mode 100644 index 0000000..69e89ee --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/dao/SysDeptDao.java @@ -0,0 +1,35 @@ + + +package com.weather.modules.sys.dao; + +import com.weather.common.dao.BaseDao; +import com.weather.modules.sys.entity.SysDeptEntity; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; +import java.util.Map; + +/** + * 部门管理 + * + * @author 123 + */ +@Mapper +public interface SysDeptDao extends BaseDao { + + List getList(Map params); + + SysDeptEntity getById(Long id); + + /** + * 获取所有部门的id、pid列表 + */ + List getIdAndPidList(); + + /** + * 根据部门ID,获取所有子部门ID列表 + * @param id 部门ID + */ + List getSubDeptIdList(String id); + +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/sys/dao/SysDictDataDao.java b/system-admin/src/main/java/com/weather/modules/sys/dao/SysDictDataDao.java new file mode 100644 index 0000000..031e6c0 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/dao/SysDictDataDao.java @@ -0,0 +1,24 @@ + + +package com.weather.modules.sys.dao; + +import com.weather.common.dao.BaseDao; +import com.weather.modules.sys.entity.DictData; +import com.weather.modules.sys.entity.SysDictDataEntity; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * 字典数据 + * + * @author 123 + */ +@Mapper +public interface SysDictDataDao extends BaseDao { + + /** + * 字典数据列表 + */ + List getDictDataList(); +} diff --git a/system-admin/src/main/java/com/weather/modules/sys/dao/SysDictTypeDao.java b/system-admin/src/main/java/com/weather/modules/sys/dao/SysDictTypeDao.java new file mode 100644 index 0000000..28a365d --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/dao/SysDictTypeDao.java @@ -0,0 +1,25 @@ + + +package com.weather.modules.sys.dao; + +import com.weather.common.dao.BaseDao; +import com.weather.modules.sys.entity.DictType; +import com.weather.modules.sys.entity.SysDictTypeEntity; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * 字典类型 + * + * @author 123 + */ +@Mapper +public interface SysDictTypeDao extends BaseDao { + + /** + * 字典类型列表 + */ + List getDictTypeList(); + +} diff --git a/system-admin/src/main/java/com/weather/modules/sys/dao/SysMenuDao.java b/system-admin/src/main/java/com/weather/modules/sys/dao/SysMenuDao.java new file mode 100644 index 0000000..37dcfa3 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/dao/SysMenuDao.java @@ -0,0 +1,54 @@ + + +package com.weather.modules.sys.dao; + +import com.weather.common.dao.BaseDao; +import com.weather.modules.sys.entity.SysMenuEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 菜单管理 + * + * @author 123 + */ +@Mapper +public interface SysMenuDao extends BaseDao { + + SysMenuEntity getById(@Param("id") Long id); + + /** + * 查询所有菜单列表 + * + * @param menuType 菜单类型 + */ + List getMenuList(@Param("menuType") Integer menuType); + + /** + * 查询用户菜单列表 + * + * @param userId 用户ID + * @param menuType 菜单类型 + */ + List getUserMenuList(@Param("userId") Long userId, @Param("menuType") Integer menuType); + + /** + * 查询用户权限列表 + * @param userId 用户ID + */ + List getUserPermissionsList(Long userId); + + /** + * 查询所有权限列表 + */ + List getPermissionsList(); + + /** + * 根据父菜单,查询子菜单 + * @param pid 父菜单ID + */ + List getListPid(Long pid); + +} diff --git a/system-admin/src/main/java/com/weather/modules/sys/dao/SysParamsDao.java b/system-admin/src/main/java/com/weather/modules/sys/dao/SysParamsDao.java new file mode 100644 index 0000000..5c0aa22 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/dao/SysParamsDao.java @@ -0,0 +1,40 @@ + + +package com.weather.modules.sys.dao; + +import com.weather.common.dao.BaseDao; +import com.weather.modules.sys.entity.SysParamsEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 参数管理 + * + * @author 123 + * @since 1.0.0 + */ +@Mapper +public interface SysParamsDao extends BaseDao { + /** + * 根据参数编码,查询value + * @param paramCode 参数编码 + * @return 参数值 + */ + String getValueByCode(String paramCode); + + /** + * 获取参数编码列表 + * @param ids ids + * @return 返回参数编码列表 + */ + List getParamCodeList(Long[] ids); + + /** + * 根据参数编码,更新value + * @param paramCode 参数编码 + * @param paramValue 参数值 + */ + int updateValueByCode(@Param("paramCode") String paramCode, @Param("paramValue") String paramValue); +} diff --git a/system-admin/src/main/java/com/weather/modules/sys/dao/SysRoleDao.java b/system-admin/src/main/java/com/weather/modules/sys/dao/SysRoleDao.java new file mode 100644 index 0000000..fa4846a --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/dao/SysRoleDao.java @@ -0,0 +1,18 @@ + + +package com.weather.modules.sys.dao; + +import com.weather.common.dao.BaseDao; +import com.weather.modules.sys.entity.SysRoleEntity; +import org.apache.ibatis.annotations.Mapper; + +/** + * 角色管理 + * + * @author 123 + */ +@Mapper +public interface SysRoleDao extends BaseDao { + + +} diff --git a/system-admin/src/main/java/com/weather/modules/sys/dao/SysRoleDataScopeDao.java b/system-admin/src/main/java/com/weather/modules/sys/dao/SysRoleDataScopeDao.java new file mode 100644 index 0000000..37f497b --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/dao/SysRoleDataScopeDao.java @@ -0,0 +1,35 @@ + + +package com.weather.modules.sys.dao; + +import com.weather.common.dao.BaseDao; +import com.weather.modules.sys.entity.SysRoleDataScopeEntity; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * 角色数据权限 + * + * @author 123 + * @since 1.0.0 + */ +@Mapper +public interface SysRoleDataScopeDao extends BaseDao { + + /** + * 根据角色ID,获取部门ID列表 + */ + List getDeptIdList(Long roleId); + + /** + * 获取用户的部门数据权限列表 + */ + List getDataScopeList(Long userId); + + /** + * 根据角色id,删除角色数据权限关系 + * @param roleIds 角色ids + */ + void deleteByRoleIds(Long[] roleIds); +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/sys/dao/SysRoleMenuDao.java b/system-admin/src/main/java/com/weather/modules/sys/dao/SysRoleMenuDao.java new file mode 100644 index 0000000..0e2658a --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/dao/SysRoleMenuDao.java @@ -0,0 +1,35 @@ + + +package com.weather.modules.sys.dao; + +import com.weather.common.dao.BaseDao; +import com.weather.modules.sys.entity.SysRoleMenuEntity; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * 角色与菜单对应关系 + * + * @author 123 + */ +@Mapper +public interface SysRoleMenuDao extends BaseDao { + + /** + * 根据角色ID,获取菜单ID列表 + */ + List getMenuIdList(Long roleId); + + /** + * 根据角色id,删除角色菜单关系 + * @param roleIds 角色ids + */ + void deleteByRoleIds(Long[] roleIds); + + /** + * 根据菜单id,删除角色菜单关系 + * @param menuId 菜单id + */ + void deleteByMenuId(Long menuId); +} diff --git a/system-admin/src/main/java/com/weather/modules/sys/dao/SysRoleUserDao.java b/system-admin/src/main/java/com/weather/modules/sys/dao/SysRoleUserDao.java new file mode 100644 index 0000000..c3ee89b --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/dao/SysRoleUserDao.java @@ -0,0 +1,39 @@ + + +package com.weather.modules.sys.dao; + +import com.weather.common.dao.BaseDao; +import com.weather.modules.sys.entity.SysRoleUserEntity; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * 角色用户关系 + * + * @author 123 + * @since 1.0.0 + */ +@Mapper +public interface SysRoleUserDao extends BaseDao { + + /** + * 根据角色ids,删除角色用户关系 + * @param roleIds 角色ids + */ + void deleteByRoleIds(Long[] roleIds); + + /** + * 根据用户id,删除角色用户关系 + * @param userIds 用户ids + */ + void deleteByUserIds(Long[] userIds); + + /** + * 角色ID列表 + * @param userId 用户ID + * + * @return + */ + List getRoleIdList(Long userId); +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/sys/dao/SysUserDao.java b/system-admin/src/main/java/com/weather/modules/sys/dao/SysUserDao.java new file mode 100644 index 0000000..698259e --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/dao/SysUserDao.java @@ -0,0 +1,38 @@ + + +package com.weather.modules.sys.dao; + +import com.weather.common.dao.BaseDao; +import com.weather.modules.sys.entity.SysUserEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** + * 系统用户 + * + * @author 123 + */ +@Mapper +public interface SysUserDao extends BaseDao { + + List getList(Map params); + + SysUserEntity getById(Long id); + + SysUserEntity getByUsername(String username); + + int updatePassword(@Param("id") Long id, @Param("newPassword") String newPassword); + + /** + * 根据部门ID,查询用户数 + */ + int getCountByDeptId(Long deptId); + + /** + * 根据部门ID,查询用户ID列表 + */ + List getUserIdListByDeptId(List deptIdList); +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/sys/dto/PasswordDTO.java b/system-admin/src/main/java/com/weather/modules/sys/dto/PasswordDTO.java new file mode 100644 index 0000000..5fe7564 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/dto/PasswordDTO.java @@ -0,0 +1,30 @@ + + +package com.weather.modules.sys.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import java.io.Serializable; + +/** + * 修改密码 + * + * @author 123 + * @since 1.0.0 + */ +@Data +@Schema(title = "修改密码") +public class PasswordDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(title = "原密码") + @NotBlank(message="{sysuser.password.require}") + private String password; + + @Schema(title = "新密码") + @NotBlank(message="{sysuser.password.require}") + private String newPassword; + +} diff --git a/system-admin/src/main/java/com/weather/modules/sys/dto/SysDeptDTO.java b/system-admin/src/main/java/com/weather/modules/sys/dto/SysDeptDTO.java new file mode 100644 index 0000000..46b990e --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/dto/SysDeptDTO.java @@ -0,0 +1,76 @@ + + +package com.weather.modules.sys.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.weather.common.utils.TreeNode; +import com.weather.common.validator.group.AddGroup; +import com.weather.common.validator.group.DefaultGroup; +import com.weather.common.validator.group.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Null; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; +import java.util.Date; + +/** + * 部门管理 + * + * @author 123 + * @since 1.0.0 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Schema(title = "部门管理") +public class SysDeptDTO extends TreeNode implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(title = "id") + @Null(message="{id.null}", groups = AddGroup.class) + @NotNull(message="{id.require}", groups = UpdateGroup.class) + private Long id; + + @Schema(title = "上级ID") + @NotNull(message="{sysdept.pid.require}", groups = DefaultGroup.class) + private Long pid; + + @Schema(title = "部门名称") + @NotBlank(message="{sysdept.name.require}", groups = DefaultGroup.class) + private String name; + + @Schema(title = "排序") + @Min(value = 0, message = "{sort.number}", groups = DefaultGroup.class) + private Integer sort; + + @Schema(title = "创建时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private Date createDate; + + @Schema(title = "上级部门名称") + private String parentName; + + @Override + public Long getId() { + return id; + } + + @Override + public void setId(Long id) { + this.id = id; + } + + @Override + public Long getPid() { + return pid; + } + + @Override + public void setPid(Long pid) { + this.pid = pid; + } +} diff --git a/system-admin/src/main/java/com/weather/modules/sys/dto/SysDictDataDTO.java b/system-admin/src/main/java/com/weather/modules/sys/dto/SysDictDataDTO.java new file mode 100644 index 0000000..088d160 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/dto/SysDictDataDTO.java @@ -0,0 +1,59 @@ + + +package com.weather.modules.sys.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.weather.common.validator.group.AddGroup; +import com.weather.common.validator.group.DefaultGroup; +import com.weather.common.validator.group.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Null; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * 字典数据 + * + * @author 123 + */ +@Data +@Schema(title = "字典数据") +public class SysDictDataDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(title = "id") + @Null(message="{id.null}", groups = AddGroup.class) + @NotNull(message="{id.require}", groups = UpdateGroup.class) + private Long id; + + @Schema(title = "字典类型ID") + @NotNull(message="{sysdict.type.require}", groups = DefaultGroup.class) + private Long dictTypeId; + + @Schema(title = "字典标签") + @NotBlank(message="{sysdict.label.require}", groups = DefaultGroup.class) + private String dictLabel; + + @Schema(title = "字典值") + private String dictValue; + + @Schema(title = "备注") + private String remark; + + @Schema(title = "排序") + @Min(value = 0, message = "{sort.number}", groups = DefaultGroup.class) + private Integer sort; + + @Schema(title = "创建时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private Date createDate; + + @Schema(title = "更新时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private Date updateDate; +} diff --git a/system-admin/src/main/java/com/weather/modules/sys/dto/SysDictTypeDTO.java b/system-admin/src/main/java/com/weather/modules/sys/dto/SysDictTypeDTO.java new file mode 100644 index 0000000..a153447 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/dto/SysDictTypeDTO.java @@ -0,0 +1,56 @@ + + +package com.weather.modules.sys.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.weather.common.validator.group.AddGroup; +import com.weather.common.validator.group.DefaultGroup; +import com.weather.common.validator.group.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Null; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * 字典类型 + * + * @author 123 + */ +@Data +@Schema(title = "字典类型") +public class SysDictTypeDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(title = "id") + @Null(message="{id.null}", groups = AddGroup.class) + @NotNull(message="{id.require}", groups = UpdateGroup.class) + private Long id; + + @Schema(title = "字典类型") + @NotBlank(message="{sysdict.type.require}", groups = DefaultGroup.class) + private String dictType; + + @Schema(title = "字典名称") + @NotBlank(message="{sysdict.name.require}", groups = DefaultGroup.class) + private String dictName; + + @Schema(title = "备注") + private String remark; + + @Schema(title = "排序") + @Min(value = 0, message = "{sort.number}", groups = DefaultGroup.class) + private Integer sort; + + @Schema(title = "创建时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private Date createDate; + + @Schema(title = "更新时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private Date updateDate; +} diff --git a/system-admin/src/main/java/com/weather/modules/sys/dto/SysMenuDTO.java b/system-admin/src/main/java/com/weather/modules/sys/dto/SysMenuDTO.java new file mode 100644 index 0000000..96b1cc1 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/dto/SysMenuDTO.java @@ -0,0 +1,91 @@ + + +package com.weather.modules.sys.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.weather.common.utils.TreeNode; +import com.weather.common.validator.group.AddGroup; +import com.weather.common.validator.group.DefaultGroup; +import com.weather.common.validator.group.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Null; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.hibernate.validator.constraints.Range; + +import java.io.Serializable; +import java.util.Date; + +/** + * 菜单管理 + * + * @author 123 + * @since 1.0.0 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Schema(title = "菜单管理") +public class SysMenuDTO extends TreeNode implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(title = "id") + @Null(message="{id.null}", groups = AddGroup.class) + @NotNull(message="{id.require}", groups = UpdateGroup.class) + private Long id; + + @Schema(title = "上级ID") + @NotNull(message="{sysmenu.pid.require}", groups = DefaultGroup.class) + private Long pid; + + @Schema(title = "菜单名称") + @NotBlank(message="sysmenu.name.require", groups = DefaultGroup.class) + private String name; + + @Schema(title = "菜单URL") + private String url; + + @Schema(title = "类型 0:菜单 1:按钮") + @Range(min=0, max=1, message = "{sysmenu.type.range}", groups = DefaultGroup.class) + private Integer menuType; + + @Schema(title = "菜单图标") + private String icon; + + @Schema(title = "授权(多个用逗号分隔,如:sys:user:list,sys:user:save)") + private String permissions; + + @Schema(title = "排序") + @Min(value = 0, message = "{sort.number}", groups = DefaultGroup.class) + private Integer sort; + + @Schema(title = "创建时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private Date createDate; + + @Schema(title = "上级菜单名称") + private String parentName; + + @Override + public Long getId() { + return id; + } + + @Override + public void setId(Long id) { + this.id = id; + } + + @Override + public Long getPid() { + return pid; + } + + @Override + public void setPid(Long pid) { + this.pid = pid; + } + +} diff --git a/system-admin/src/main/java/com/weather/modules/sys/dto/SysParamsDTO.java b/system-admin/src/main/java/com/weather/modules/sys/dto/SysParamsDTO.java new file mode 100644 index 0000000..bc7b7f5 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/dto/SysParamsDTO.java @@ -0,0 +1,53 @@ + + +package com.weather.modules.sys.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.weather.common.validator.group.AddGroup; +import com.weather.common.validator.group.DefaultGroup; +import com.weather.common.validator.group.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Null; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * 参数管理 + * + * @author 123 + * @since 1.0.0 + */ +@Data +@Schema(title = "参数管理") +public class SysParamsDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(title = "id") + @Null(message="{id.null}", groups = AddGroup.class) + @NotNull(message="{id.require}", groups = UpdateGroup.class) + private Long id; + + @Schema(title = "参数编码") + @NotBlank(message="{sysparams.paramcode.require}", groups = DefaultGroup.class) + private String paramCode; + + @Schema(title = "参数值") + @NotBlank(message="{sysparams.paramvalue.require}", groups = DefaultGroup.class) + private String paramValue; + + @Schema(title = "备注") + private String remark; + + @Schema(title = "创建时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private Date createDate; + + @Schema(title = "更新时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private Date updateDate; + +} diff --git a/system-admin/src/main/java/com/weather/modules/sys/dto/SysRoleDTO.java b/system-admin/src/main/java/com/weather/modules/sys/dto/SysRoleDTO.java new file mode 100644 index 0000000..7ed586e --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/dto/SysRoleDTO.java @@ -0,0 +1,52 @@ + + +package com.weather.modules.sys.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.weather.common.validator.group.AddGroup; +import com.weather.common.validator.group.DefaultGroup; +import com.weather.common.validator.group.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Null; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** + * 角色管理 + * + * @author 123 + * @since 1.0.0 + */ +@Data +@Schema(title = "角色管理") +public class SysRoleDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(title = "id") + @Null(message="{id.null}", groups = AddGroup.class) + @NotNull(message="{id.require}", groups = UpdateGroup.class) + private Long id; + + @Schema(title = "角色名称") + @NotBlank(message="{sysrole.name.require}", groups = DefaultGroup.class) + private String name; + + @Schema(title = "备注") + private String remark; + + @Schema(title = "创建时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private Date createDate; + + @Schema(title = "菜单ID列表") + private List menuIdList; + + @Schema(title = "部门ID列表") + private List deptIdList; + +} diff --git a/system-admin/src/main/java/com/weather/modules/sys/dto/SysUserDTO.java b/system-admin/src/main/java/com/weather/modules/sys/dto/SysUserDTO.java new file mode 100644 index 0000000..d663a98 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/dto/SysUserDTO.java @@ -0,0 +1,86 @@ + + +package com.weather.modules.sys.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.weather.common.validator.group.AddGroup; +import com.weather.common.validator.group.DefaultGroup; +import com.weather.common.validator.group.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Null; +import lombok.Data; +import org.hibernate.validator.constraints.Range; + +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** + * 用户管理 + * + * @author 123 + * @since 1.0.0 + */ +@Data +@Schema(title = "用户管理") +public class SysUserDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(title = "id") + @Null(message="{id.null}", groups = AddGroup.class) + @NotNull(message="{id.require}", groups = UpdateGroup.class) + private Long id; + + @Schema(title = "用户名", required = true) + @NotBlank(message="{sysuser.username.require}", groups = DefaultGroup.class) + private String username; + + @Schema(title = "密码") + @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) + @NotBlank(message="{sysuser.password.require}", groups = AddGroup.class) + private String password; + + @Schema(title = "姓名", requiredMode = Schema.RequiredMode.REQUIRED) + @NotBlank(message="{sysuser.realName.require}", groups = DefaultGroup.class) + private String realName; + + @Schema(title = "头像") + private String headUrl; + + @Schema(title = "性别 0:男 1:女 2:保密", required = true) + @Range(min=0, max=2, message = "{sysuser.gender.range}", groups = DefaultGroup.class) + private Integer gender; + + @Schema(title = "邮箱") + @Email(message="{sysuser.email.error}", groups = DefaultGroup.class) + private String email; + + @Schema(title = "手机号") + private String mobile; + + @Schema(title = "部门ID", required = true) + @NotNull(message="{sysuser.deptId.require}", groups = DefaultGroup.class) + private Long deptId; + + @Schema(title = "状态 0:停用 1:正常", required = true) + @Range(min=0, max=1, message = "{sysuser.status.range}", groups = DefaultGroup.class) + private Integer status; + + @Schema(title = "创建时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private Date createDate; + + @Schema(title = "超级管理员 0:否 1:是") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private Integer superAdmin; + + @Schema(title = "角色ID列表") + private List roleIdList; + + @Schema(title = "部门名称") + private String deptName; + +} diff --git a/system-admin/src/main/java/com/weather/modules/sys/dto/SystemDTO.java b/system-admin/src/main/java/com/weather/modules/sys/dto/SystemDTO.java new file mode 100644 index 0000000..0a87028 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/dto/SystemDTO.java @@ -0,0 +1,42 @@ + + +package com.weather.modules.sys.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * 系统数据 + * + * @author 123 + * @since 1.0.0 + */ +@Data +@Schema(title = "系统数据") +public class SystemDTO implements Serializable { + private static final long serialVersionUID = 1L; + + private Long sysTime; + private String osName; + private String osArch; + private String osVersion; + private String userLanguage; + private String userDir; + private Long totalPhysical; + private Long freePhysical; + private BigDecimal memoryRate; + private Integer processors; + private String jvmName; + private String javaVersion; + private String javaHome; + private Long javaTotalMemory; + private Long javaFreeMemory; + private Long javaMaxMemory; + private String userName; + private BigDecimal systemCpuLoad; + private String userTimezone; + +} diff --git a/system-admin/src/main/java/com/weather/modules/sys/entity/DictData.java b/system-admin/src/main/java/com/weather/modules/sys/entity/DictData.java new file mode 100644 index 0000000..1909008 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/entity/DictData.java @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2019 人人开源 All rights reserved. + *

+ * https://www.renren.io + *

+ * 版权所有,侵权必究! + */ + +package com.weather.modules.sys.entity; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Data; + +/** + * 字典数据 + * + * @author 123 + */ +@Data +public class DictData { + @JsonIgnore + private Long dictTypeId; + private String dictLabel; + private String dictValue; +} diff --git a/system-admin/src/main/java/com/weather/modules/sys/entity/DictType.java b/system-admin/src/main/java/com/weather/modules/sys/entity/DictType.java new file mode 100644 index 0000000..b46e4e4 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/entity/DictType.java @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2019 人人开源 All rights reserved. + *

+ * https://www.renren.io + *

+ * 版权所有,侵权必究! + */ + +package com.weather.modules.sys.entity; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +/** + * 字典类型 + * + * @author 123 + */ +@Data +public class DictType { + @JsonIgnore + private Long id; + private String dictType; + private List dataList = new ArrayList<>(); +} diff --git a/system-admin/src/main/java/com/weather/modules/sys/entity/SysDeptEntity.java b/system-admin/src/main/java/com/weather/modules/sys/entity/SysDeptEntity.java new file mode 100644 index 0000000..d69625c --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/entity/SysDeptEntity.java @@ -0,0 +1,55 @@ + + +package com.weather.modules.sys.entity; + +import com.baomidou.mybatisplus.annotation.*; +import com.weather.common.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * 部门管理 + * + * @author 123 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@TableName("sys_dept") +public class SysDeptEntity extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** + * 上级ID + */ + private Long pid; + /** + * 所有上级ID,用逗号分开 + */ + private String pids; + /** + * 部门名称 + */ + private String name; + /** + * 排序 + */ + private Integer sort; + /** + * 更新者 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private Long updater; + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private Date updateDate; + /** + * 上级部门名称 + */ + @TableField(exist = false) + private String parentName; + +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/sys/entity/SysDictDataEntity.java b/system-admin/src/main/java/com/weather/modules/sys/entity/SysDictDataEntity.java new file mode 100644 index 0000000..a732c7a --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/entity/SysDictDataEntity.java @@ -0,0 +1,54 @@ + + +package com.weather.modules.sys.entity; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.weather.common.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * 数据字典 + * + * @author 123 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@TableName("sys_dict_data") +public class SysDictDataEntity extends BaseEntity { + private static final long serialVersionUID = 1L; + /** + * 字典类型ID + */ + private Long dictTypeId; + /** + * 字典标签 + */ + private String dictLabel; + /** + * 字典值 + */ + private String dictValue; + /** + * 备注 + */ + private String remark; + /** + * 排序 + */ + private Integer sort; + /** + * 更新者 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private Long updater; + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private Date updateDate; +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/sys/entity/SysDictTypeEntity.java b/system-admin/src/main/java/com/weather/modules/sys/entity/SysDictTypeEntity.java new file mode 100644 index 0000000..f427ca1 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/entity/SysDictTypeEntity.java @@ -0,0 +1,53 @@ + + +package com.weather.modules.sys.entity; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.weather.common.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.util.Date; + +/** + * 字典类型 + * + * @author 123 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@TableName("sys_dict_type") +public class SysDictTypeEntity extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + /** + * 字典类型 + */ + private String dictType; + /** + * 字典名称 + */ + private String dictName; + /** + * 备注 + */ + private String remark; + /** + * 排序 + */ + private Integer sort; + /** + * 更新者 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private Long updater; + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private Date updateDate; +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/sys/entity/SysMenuEntity.java b/system-admin/src/main/java/com/weather/modules/sys/entity/SysMenuEntity.java new file mode 100644 index 0000000..aeea377 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/entity/SysMenuEntity.java @@ -0,0 +1,69 @@ + + +package com.weather.modules.sys.entity; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.weather.common.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * 菜单管理 + * + * @author 123 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@TableName("sys_menu") +public class SysMenuEntity extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** + * 父菜单ID,一级菜单为0 + */ + private Long pid; + /** + * 菜单名称 + */ + private String name; + /** + * 菜单URL + */ + private String url; + /** + * 授权(多个用逗号分隔,如:sys:user:list,sys:user:save) + */ + private String permissions; + /** + * 类型 0:菜单 1:按钮 + */ + private Integer menuType; + /** + * 菜单图标 + */ + private String icon; + /** + * 排序 + */ + private Integer sort; + /** + * 更新者 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private Long updater; + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private Date updateDate; + /** + * 上级菜单名称 + */ + @TableField(exist = false) + private String parentName; + +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/sys/entity/SysParamsEntity.java b/system-admin/src/main/java/com/weather/modules/sys/entity/SysParamsEntity.java new file mode 100644 index 0000000..7d4f302 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/entity/SysParamsEntity.java @@ -0,0 +1,53 @@ + + +package com.weather.modules.sys.entity; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.weather.common.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * 参数管理 + * + * @author 123 + * @since 1.0.0 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@TableName("sys_params") +public class SysParamsEntity extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** + * 参数编码 + */ + private String paramCode; + /** + * 参数值 + */ + private String paramValue; + /** + * 类型 0:系统参数 1:非系统参数 + */ + private Integer paramType; + /** + * 备注 + */ + private String remark; + /** + * 更新者 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private Long updater; + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private Date updateDate; + +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/sys/entity/SysRoleDataScopeEntity.java b/system-admin/src/main/java/com/weather/modules/sys/entity/SysRoleDataScopeEntity.java new file mode 100644 index 0000000..bba677c --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/entity/SysRoleDataScopeEntity.java @@ -0,0 +1,31 @@ + + +package com.weather.modules.sys.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.weather.common.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 角色数据权限 + * + * @author 123 + * @since 1.0.0 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@TableName("sys_role_data_scope") +public class SysRoleDataScopeEntity extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** + * 角色ID + */ + private Long roleId; + /** + * 部门ID + */ + private Long deptId; + +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/sys/entity/SysRoleEntity.java b/system-admin/src/main/java/com/weather/modules/sys/entity/SysRoleEntity.java new file mode 100644 index 0000000..12f1623 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/entity/SysRoleEntity.java @@ -0,0 +1,48 @@ + + +package com.weather.modules.sys.entity; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.weather.common.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * 角色 + * + * @author 123 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@TableName("sys_role") +public class SysRoleEntity extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** + * 角色名称 + */ + private String name; + /** + * 备注 + */ + private String remark; + /** + * 部门ID + */ + @TableField(fill = FieldFill.INSERT) + private Long deptId; + /** + * 更新者 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private Long updater; + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private Date updateDate; +} diff --git a/system-admin/src/main/java/com/weather/modules/sys/entity/SysRoleMenuEntity.java b/system-admin/src/main/java/com/weather/modules/sys/entity/SysRoleMenuEntity.java new file mode 100644 index 0000000..9ce8ab0 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/entity/SysRoleMenuEntity.java @@ -0,0 +1,30 @@ + + +package com.weather.modules.sys.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.weather.common.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 角色菜单关系 + * + * @author 123 + * @since 1.0.0 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@TableName("sys_role_menu") +public class SysRoleMenuEntity extends BaseEntity { + private static final long serialVersionUID = 1L; + /** + * 角色ID + */ + private Long roleId; + /** + * 菜单ID + */ + private Long menuId; + +} diff --git a/system-admin/src/main/java/com/weather/modules/sys/entity/SysRoleUserEntity.java b/system-admin/src/main/java/com/weather/modules/sys/entity/SysRoleUserEntity.java new file mode 100644 index 0000000..9ed2a00 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/entity/SysRoleUserEntity.java @@ -0,0 +1,31 @@ + + +package com.weather.modules.sys.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.weather.common.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 角色用户关系 + * + * @author 123 + * @since 1.0.0 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@TableName("sys_role_user") +public class SysRoleUserEntity extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** + * 角色ID + */ + private Long roleId; + /** + * 用户ID + */ + private Long userId; + +} diff --git a/system-admin/src/main/java/com/weather/modules/sys/entity/SysUserEntity.java b/system-admin/src/main/java/com/weather/modules/sys/entity/SysUserEntity.java new file mode 100644 index 0000000..1fa2ad8 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/entity/SysUserEntity.java @@ -0,0 +1,80 @@ + + +package com.weather.modules.sys.entity; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.weather.common.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * 系统用户 + * + * @author 123 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@TableName("sys_user") +public class SysUserEntity extends BaseEntity { + private static final long serialVersionUID = 1L; + /** + * 用户名 + */ + private String username; + /** + * 密码 + */ + private String password; + /** + * 姓名 + */ + private String realName; + /** + * 头像 + */ + private String headUrl; + /** + * 性别 0:男 1:女 2:保密 + */ + private Integer gender; + /** + * 邮箱 + */ + private String email; + /** + * 手机号 + */ + private String mobile; + /** + * 部门ID + */ + private Long deptId; + /** + * 超级管理员 0:否 1:是 + */ + private Integer superAdmin; + /** + * 状态 0:停用 1:正常 + */ + private Integer status; + /** + * 更新者 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private Long updater; + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private Date updateDate; + /** + * 部门名称 + */ + @TableField(exist=false) + private String deptName; + +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/sys/enums/MenuTypeEnum.java b/system-admin/src/main/java/com/weather/modules/sys/enums/MenuTypeEnum.java new file mode 100644 index 0000000..b1a82e4 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/enums/MenuTypeEnum.java @@ -0,0 +1,30 @@ + + +package com.weather.modules.sys.enums; + +/** + * 菜单类型枚举 + * + * @author 123 + * @since 1.0.0 + */ +public enum MenuTypeEnum { + /** + * 菜单 + */ + MENU(0), + /** + * 按钮 + */ + BUTTON(1); + + private int value; + + MenuTypeEnum(int value) { + this.value = value; + } + + public int value() { + return this.value; + } +} diff --git a/system-admin/src/main/java/com/weather/modules/sys/enums/SuperAdminEnum.java b/system-admin/src/main/java/com/weather/modules/sys/enums/SuperAdminEnum.java new file mode 100644 index 0000000..e3cfb34 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/enums/SuperAdminEnum.java @@ -0,0 +1,24 @@ + + +package com.weather.modules.sys.enums; + +/** + * 超级管理员枚举 + * + * @author 123 + * @since 1.0.0 + */ +public enum SuperAdminEnum { + YES(1), + NO(0); + + private int value; + + SuperAdminEnum(int value) { + this.value = value; + } + + public int value() { + return this.value; + } +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/sys/enums/UserStatusEnum.java b/system-admin/src/main/java/com/weather/modules/sys/enums/UserStatusEnum.java new file mode 100644 index 0000000..1027f93 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/enums/UserStatusEnum.java @@ -0,0 +1,24 @@ + + +package com.weather.modules.sys.enums; + +/** + * 用户状态 + * + * @author 123 + * @since 1.0.0 + */ +public enum UserStatusEnum { + DISABLE(0), + ENABLED(1); + + private int value; + + UserStatusEnum(int value) { + this.value = value; + } + + public int value() { + return this.value; + } +} diff --git a/system-admin/src/main/java/com/weather/modules/sys/excel/SysParamsExcel.java b/system-admin/src/main/java/com/weather/modules/sys/excel/SysParamsExcel.java new file mode 100644 index 0000000..a6f7fdf --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/excel/SysParamsExcel.java @@ -0,0 +1,22 @@ + +package com.weather.modules.sys.excel; + +import com.alibaba.excel.annotation.ExcelProperty; +import lombok.Data; + +/** + * 参数管理 + * + * @author 123 + * @since 1.0.0 + */ +@Data +public class SysParamsExcel { + @ExcelProperty("参数编码") + private String paramCode; + @ExcelProperty("参数值") + private String paramValue; + @ExcelProperty("备注") + private String remark; + +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/sys/excel/SysUserExcel.java b/system-admin/src/main/java/com/weather/modules/sys/excel/SysUserExcel.java new file mode 100644 index 0000000..5e072c3 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/excel/SysUserExcel.java @@ -0,0 +1,40 @@ + +package com.weather.modules.sys.excel; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.format.DateTimeFormat; +import com.weather.modules.sys.excel.converter.GenderConverter; +import com.weather.modules.sys.excel.converter.StatusConverter; +import lombok.Data; + +import java.util.Date; + +/** + * 用户管理 + * + * @author 123 + * @since 1.0.0 + */ +@Data +public class SysUserExcel { + @ExcelProperty("用户名") + private String username; + @ExcelProperty("姓名") + private String realName; + @ExcelProperty(value = "性别", converter = GenderConverter.class) + private Integer gender; + @ExcelProperty("邮箱") + private String email; + @ExcelProperty("手机号") + private String mobile; + @ExcelProperty("部门名称") + private String deptName; + @ExcelProperty(value = "状态", converter = StatusConverter.class) + private Integer status; + @ExcelProperty("备注") + private String remark; + @DateTimeFormat("yyyy-MM-dd HH:mm:ss") + @ExcelProperty("创建时间") + private Date createDate; + +} diff --git a/system-admin/src/main/java/com/weather/modules/sys/excel/converter/GenderConverter.java b/system-admin/src/main/java/com/weather/modules/sys/excel/converter/GenderConverter.java new file mode 100644 index 0000000..3fd9ffb --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/excel/converter/GenderConverter.java @@ -0,0 +1,45 @@ +package com.weather.modules.sys.excel.converter; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; + + +public class GenderConverter implements Converter { + + + @Override + public Class supportJavaTypeKey() { + return Integer.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.STRING; + } + + @Override + public Integer convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + if(cellData.getStringValue().equals("男")){ + return 0; + }else if(cellData.getStringValue().equals("女")){ + return 1; + }else { + return 2; + } + } + + @Override + public WriteCellData convertToExcelData(Integer value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + if(value == 0){ + return new WriteCellData<>("男"); + }else if(value == 1){ + return new WriteCellData<>("女"); + }else { + return new WriteCellData<>("保密"); + } + } +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/sys/excel/converter/StatusConverter.java b/system-admin/src/main/java/com/weather/modules/sys/excel/converter/StatusConverter.java new file mode 100644 index 0000000..756be4c --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/excel/converter/StatusConverter.java @@ -0,0 +1,30 @@ +package com.weather.modules.sys.excel.converter; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; + +public class StatusConverter implements Converter { + @Override + public Class supportJavaTypeKey() { + return Integer.class; + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.STRING; + } + + @Override + public Integer convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + return cellData.getStringValue().equals("正常") ? 1 : 0; + } + + @Override + public WriteCellData convertToExcelData(Integer value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + return new WriteCellData<>(value == 1 ? "正常" : "停用"); + } +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/sys/redis/SysParamsRedis.java b/system-admin/src/main/java/com/weather/modules/sys/redis/SysParamsRedis.java new file mode 100644 index 0000000..52e92af --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/redis/SysParamsRedis.java @@ -0,0 +1,38 @@ + +package com.weather.modules.sys.redis; + +import com.weather.common.redis.RedisKeys; +import com.weather.common.redis.RedisUtils; +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Component; + +/** + * 参数管理 + * + * @author 123 + * @since 1.0.0 + */ +@Component +@AllArgsConstructor +public class SysParamsRedis { + private final RedisUtils redisUtils; + + public void delete(Object[] paramCodes) { + String key = RedisKeys.getSysParamsKey(); + redisUtils.hDel(key, paramCodes); + } + + public void set(String paramCode, String paramValue) { + if (paramValue == null) { + return; + } + String key = RedisKeys.getSysParamsKey(); + redisUtils.hSet(key, paramCode, paramValue); + } + + public String get(String paramCode) { + String key = RedisKeys.getSysParamsKey(); + return (String) redisUtils.hGet(key, paramCode); + } + +} diff --git a/system-admin/src/main/java/com/weather/modules/sys/service/SysDeptService.java b/system-admin/src/main/java/com/weather/modules/sys/service/SysDeptService.java new file mode 100644 index 0000000..1e4dec6 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/service/SysDeptService.java @@ -0,0 +1,43 @@ + + +package com.weather.modules.sys.service; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.weather.common.service.BaseService; +import com.weather.modules.sys.dto.SysDeptDTO; +import com.weather.modules.sys.entity.SysDeptEntity; +import org.apache.poi.ss.formula.functions.T; + +import java.util.List; +import java.util.Map; + +/** + * 部门管理 + * + * @author 123 + */ +public interface SysDeptService extends BaseService { + + List list(Map params); + + SysDeptDTO get(Long id); + + void save(SysDeptDTO dto); + + void update(SysDeptDTO dto); + + void delete(Long id); + + /** + * 根据部门ID,获取本部门及子部门ID列表 + * @param id 部门ID + */ + List getSubDeptIdList(Long id); + + QueryWrapper limitedQueryScope(QueryWrapper queryWrapper); + + /** + * 获取当前用户可见的所有部门ID(含自身、子部门、所有上级部门) + */ + List getAccessibleDeptIdList(); +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/sys/service/SysDictDataService.java b/system-admin/src/main/java/com/weather/modules/sys/service/SysDictDataService.java new file mode 100644 index 0000000..b6f2994 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/service/SysDictDataService.java @@ -0,0 +1,29 @@ + + +package com.weather.modules.sys.service; + +import com.weather.common.page.PageData; +import com.weather.common.service.BaseService; +import com.weather.modules.sys.dto.SysDictDataDTO; +import com.weather.modules.sys.entity.SysDictDataEntity; + +import java.util.Map; + +/** + * 数据字典 + * + * @author 123 + */ +public interface SysDictDataService extends BaseService { + + PageData page(Map params); + + SysDictDataDTO get(Long id); + + void save(SysDictDataDTO dto); + + void update(SysDictDataDTO dto); + + void delete(Long[] ids); + +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/sys/service/SysDictTypeService.java b/system-admin/src/main/java/com/weather/modules/sys/service/SysDictTypeService.java new file mode 100644 index 0000000..72f26c8 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/service/SysDictTypeService.java @@ -0,0 +1,36 @@ + + +package com.weather.modules.sys.service; + +import com.weather.common.page.PageData; +import com.weather.common.service.BaseService; +import com.weather.modules.sys.dto.SysDictTypeDTO; +import com.weather.modules.sys.entity.DictType; +import com.weather.modules.sys.entity.SysDictTypeEntity; + +import java.util.List; +import java.util.Map; + +/** + * 数据字典 + * + * @author 123 + */ +public interface SysDictTypeService extends BaseService { + + PageData page(Map params); + + SysDictTypeDTO get(Long id); + + void save(SysDictTypeDTO dto); + + void update(SysDictTypeDTO dto); + + void delete(Long[] ids); + + /** + * 获取所有字典 + */ + List getAllList(); + +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/sys/service/SysMenuService.java b/system-admin/src/main/java/com/weather/modules/sys/service/SysMenuService.java new file mode 100644 index 0000000..322c7ee --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/service/SysMenuService.java @@ -0,0 +1,48 @@ + + +package com.weather.modules.sys.service; + +import com.weather.common.service.BaseService; +import com.weather.modules.security.user.UserDetail; +import com.weather.modules.sys.dto.SysMenuDTO; +import com.weather.modules.sys.entity.SysMenuEntity; + +import java.util.List; + + +/** + * 菜单管理 + * + * @author 123 + */ +public interface SysMenuService extends BaseService { + + SysMenuDTO get(Long id); + + void save(SysMenuDTO dto); + + void update(SysMenuDTO dto); + + void delete(Long id); + + /** + * 菜单列表 + * + * @param menuType 菜单类型 + */ + List getAllMenuList(Integer menuType); + + /** + * 用户菜单列表 + * + * @param user 用户 + * @param menuType 菜单类型 + */ + List getUserMenuList(UserDetail user, Integer menuType); + + /** + * 根据父菜单,查询子菜单 + * @param pid 父菜单ID + */ + List getListPid(Long pid); +} diff --git a/system-admin/src/main/java/com/weather/modules/sys/service/SysParamsService.java b/system-admin/src/main/java/com/weather/modules/sys/service/SysParamsService.java new file mode 100644 index 0000000..1d351b9 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/service/SysParamsService.java @@ -0,0 +1,54 @@ + + +package com.weather.modules.sys.service; + + +import com.weather.common.page.PageData; +import com.weather.common.service.BaseService; +import com.weather.modules.sys.dto.SysParamsDTO; +import com.weather.modules.sys.entity.SysParamsEntity; + +import java.util.List; +import java.util.Map; + +/** + * 参数管理 + * + * @author 123 + * @since 1.0.0 + */ +public interface SysParamsService extends BaseService { + + PageData page(Map params); + + List list(Map params); + + SysParamsDTO get(Long id); + + void save(SysParamsDTO dto); + + void update(SysParamsDTO dto); + + void delete(Long[] ids); + + /** + * 根据参数编码,获取参数的value值 + * + * @param paramCode 参数编码 + */ + String getValue(String paramCode); + + /** + * 根据参数编码,获取value的Object对象 + * @param paramCode 参数编码 + * @param clazz Object对象 + */ + T getValueObject(String paramCode, Class clazz); + + /** + * 根据参数编码,更新value + * @param paramCode 参数编码 + * @param paramValue 参数值 + */ + int updateValueByCode(String paramCode, String paramValue); +} diff --git a/system-admin/src/main/java/com/weather/modules/sys/service/SysRoleDataScopeService.java b/system-admin/src/main/java/com/weather/modules/sys/service/SysRoleDataScopeService.java new file mode 100644 index 0000000..547f124 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/service/SysRoleDataScopeService.java @@ -0,0 +1,35 @@ + + +package com.weather.modules.sys.service; + +import com.weather.common.service.BaseService; +import com.weather.modules.sys.entity.SysRoleDataScopeEntity; + +import java.util.List; + +/** + * 角色数据权限 + * + * @author 123 + * @since 1.0.0 + */ +public interface SysRoleDataScopeService extends BaseService { + + /** + * 根据角色ID,获取部门ID列表 + */ + List getDeptIdList(Long roleId); + + /** + * 保存或修改 + * @param roleId 角色ID + * @param deptIdList 部门ID列表 + */ + void saveOrUpdate(Long roleId, List deptIdList); + + /** + * 根据角色id,删除角色数据权限关系 + * @param roleId 角色ids + */ + void deleteByRoleIds(Long[] roleId); +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/sys/service/SysRoleMenuService.java b/system-admin/src/main/java/com/weather/modules/sys/service/SysRoleMenuService.java new file mode 100644 index 0000000..e1b1097 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/service/SysRoleMenuService.java @@ -0,0 +1,41 @@ + + +package com.weather.modules.sys.service; + +import com.weather.common.service.BaseService; +import com.weather.modules.sys.entity.SysRoleMenuEntity; + +import java.util.List; + + +/** + * 角色与菜单对应关系 + * + * @author 123 + */ +public interface SysRoleMenuService extends BaseService { + + /** + * 根据角色ID,获取菜单ID列表 + */ + List getMenuIdList(Long roleId); + + /** + * 保存或修改 + * @param roleId 角色ID + * @param menuIdList 菜单ID列表 + */ + void saveOrUpdate(Long roleId, List menuIdList); + + /** + * 根据角色id,删除角色菜单关系 + * @param roleIds 角色ids + */ + void deleteByRoleIds(Long[] roleIds); + + /** + * 根据菜单id,删除角色菜单关系 + * @param menuId 菜单id + */ + void deleteByMenuId(Long menuId); +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/sys/service/SysRoleService.java b/system-admin/src/main/java/com/weather/modules/sys/service/SysRoleService.java new file mode 100644 index 0000000..02f1218 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/service/SysRoleService.java @@ -0,0 +1,34 @@ + + +package com.weather.modules.sys.service; + + +import com.weather.common.page.PageData; +import com.weather.common.service.BaseService; +import com.weather.modules.sys.dto.SysRoleDTO; +import com.weather.modules.sys.entity.SysRoleEntity; + +import java.util.List; +import java.util.Map; + + +/** + * 角色 + * + * @author 123 + */ +public interface SysRoleService extends BaseService { + + PageData page(Map params); + + List list(Map params); + + SysRoleDTO get(Long id); + + void save(SysRoleDTO dto); + + void update(SysRoleDTO dto); + + void delete(Long[] ids); + +} diff --git a/system-admin/src/main/java/com/weather/modules/sys/service/SysRoleUserService.java b/system-admin/src/main/java/com/weather/modules/sys/service/SysRoleUserService.java new file mode 100644 index 0000000..0e87646 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/service/SysRoleUserService.java @@ -0,0 +1,42 @@ + + +package com.weather.modules.sys.service; + +import com.weather.common.service.BaseService; +import com.weather.modules.sys.entity.SysRoleUserEntity; + +import java.util.List; + +/** + * 角色用户关系 + * + * @author 123 + * @since 1.0.0 + */ +public interface SysRoleUserService extends BaseService { + + /** + * 保存或修改 + * @param userId 用户ID + * @param roleIdList 角色ID列表 + */ + void saveOrUpdate(Long userId, List roleIdList); + + /** + * 根据角色ids,删除角色用户关系 + * @param roleIds 角色ids + */ + void deleteByRoleIds(Long[] roleIds); + + /** + * 根据用户id,删除角色用户关系 + * @param userIds 用户ids + */ + void deleteByUserIds(Long[] userIds); + + /** + * 角色ID列表 + * @param userId 用户ID + */ + List getRoleIdList(Long userId); +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/sys/service/SysUserService.java b/system-admin/src/main/java/com/weather/modules/sys/service/SysUserService.java new file mode 100644 index 0000000..a16707e --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/service/SysUserService.java @@ -0,0 +1,52 @@ + + +package com.weather.modules.sys.service; + +import com.weather.common.page.PageData; +import com.weather.common.service.BaseService; +import com.weather.modules.sys.dto.SysUserDTO; +import com.weather.modules.sys.entity.SysUserEntity; + +import java.util.List; +import java.util.Map; + + +/** + * 系统用户 + * + * @author 123 + */ +public interface SysUserService extends BaseService { + + PageData page(Map params); + + List list(Map params); + + SysUserDTO get(Long id); + + SysUserDTO getByUsername(String username); + + void save(SysUserDTO dto); + + void update(SysUserDTO dto); + + void delete(Long[] ids); + + /** + * 修改密码 + * @param id 用户ID + * @param newPassword 新密码 + */ + void updatePassword(Long id, String newPassword); + + /** + * 根据部门ID,查询用户数 + */ + int getCountByDeptId(Long deptId); + + /** + * 根据部门ID,查询用户Id列表 + */ + List getUserIdListByDeptId(List deptIdList); + +} diff --git a/system-admin/src/main/java/com/weather/modules/sys/service/impl/SysDeptServiceImpl.java b/system-admin/src/main/java/com/weather/modules/sys/service/impl/SysDeptServiceImpl.java new file mode 100644 index 0000000..4036565 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/service/impl/SysDeptServiceImpl.java @@ -0,0 +1,197 @@ + +package com.weather.modules.sys.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.qiniu.util.StringUtils; +import com.weather.common.constant.Constant; +import com.weather.common.exception.ErrorCode; +import com.weather.common.exception.CommonException; +import com.weather.common.service.impl.BaseServiceImpl; +import com.weather.common.utils.ConvertUtils; +import com.weather.common.utils.TreeUtils; +import com.weather.modules.security.user.SecurityUser; +import com.weather.modules.security.user.UserDetail; +import com.weather.modules.sys.dao.SysDeptDao; +import com.weather.modules.sys.dao.SysUserDao; +import com.weather.modules.sys.dto.SysDeptDTO; +import com.weather.modules.sys.entity.SysDeptEntity; +import com.weather.modules.sys.enums.SuperAdminEnum; +import com.weather.modules.sys.service.SysDeptService; +import lombok.AllArgsConstructor; +import org.apache.poi.ss.formula.functions.T; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +@Service +@AllArgsConstructor +public class SysDeptServiceImpl extends BaseServiceImpl implements SysDeptService { + private final SysUserDao sysUserDao; + + @Override + public List list(Map params) { + //普通管理员,只能查询所属部门及子部门的数据 + UserDetail user = SecurityUser.getUser(); + if (user.getSuperAdmin() == SuperAdminEnum.NO.value()) { + params.put("deptIdList", getSubDeptIdList(user.getDeptId())); + } + + //查询部门列表 + List entityList = baseDao.getList(params); + + List dtoList = ConvertUtils.sourceToTarget(entityList, SysDeptDTO.class); + + return TreeUtils.build(dtoList); + } + + @Override + public SysDeptDTO get(Long id) { + //超级管理员,部门ID为null + if (id == null) { + return null; + } + + SysDeptEntity entity = baseDao.getById(id); + + return ConvertUtils.sourceToTarget(entity, SysDeptDTO.class); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void save(SysDeptDTO dto) { + SysDeptEntity entity = ConvertUtils.sourceToTarget(dto, SysDeptEntity.class); + + entity.setPids(getPidList(entity.getPid())); + insert(entity); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(SysDeptDTO dto) { + SysDeptEntity entity = ConvertUtils.sourceToTarget(dto, SysDeptEntity.class); + + //上级部门不能为自身 + if (entity.getId().equals(entity.getPid())) { + throw new CommonException(ErrorCode.SUPERIOR_DEPT_ERROR); + } + + //上级部门不能为下级部门 + List subDeptList = getSubDeptIdList(entity.getId()); + if (subDeptList.contains(entity.getPid())) { + throw new CommonException(ErrorCode.SUPERIOR_DEPT_ERROR); + } + + entity.setPids(getPidList(entity.getPid())); + updateById(entity); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(Long id) { + //判断是否有子部门 + List subList = getSubDeptIdList(id); + if (subList.size() > 1) { + throw new CommonException(ErrorCode.DEPT_SUB_DELETE_ERROR); + } + + //判断部门下面是否有用户 + int count = sysUserDao.getCountByDeptId(id); + if (count > 0) { + throw new CommonException(ErrorCode.DEPT_USER_DELETE_ERROR); + } + + //删除 + baseDao.deleteById(id); + } + + @Override + public List getSubDeptIdList(Long id) { + List deptIdList = baseDao.getSubDeptIdList("%" + id + "%"); + deptIdList.add(id); + + return deptIdList; + } + + @Override + public List getAccessibleDeptIdList() { + UserDetail user = SecurityUser.getUser(); + if (user.getSuperAdmin() == SuperAdminEnum.YES.value()) { + return List.of(); + } + + List deptIdList = getSubDeptIdList(user.getDeptId()); + + SysDeptEntity dept = baseDao.selectById(user.getDeptId()); + if (dept != null && dept.getPids() != null && !dept.getPids().isEmpty()) { + String[] pidArr = dept.getPids().split(","); + for (String pid : pidArr) { + pid = pid.trim(); + if (!pid.isEmpty() && !"0".equals(pid)) { + Long pidLong = Long.parseLong(pid); + if (!deptIdList.contains(pidLong)) { + deptIdList.add(pidLong); + } + } + } + } + return deptIdList; + } + + @Override + public QueryWrapper limitedQueryScope(QueryWrapper queryWrapper) { + UserDetail user = SecurityUser.getUser(); + if (user.getSuperAdmin() == SuperAdminEnum.NO.value()) { + List deptIdList = getSubDeptIdList(user.getDeptId()); + // 注意:只有当实体类中有 "dept_id" 字段时才有意义 + queryWrapper.in(deptIdList != null && !deptIdList.isEmpty(), "dept_id", deptIdList); + } + return queryWrapper; + } + + /** + * 获取所有上级部门ID + * + * @param pid 上级ID + */ + private String getPidList(Long pid) { + //顶级部门,无上级部门 + if (Constant.DEPT_ROOT.equals(pid)) { + return Constant.DEPT_ROOT + ""; + } + + //所有部门的id、pid列表 + List deptList = baseDao.getIdAndPidList(); + + //list转map + Map map = new HashMap<>(deptList.size()); + for (SysDeptEntity entity : deptList) { + map.put(entity.getId(), entity); + } + + //递归查询所有上级部门ID列表 + List pidList = new ArrayList<>(); + getPidTree(pid, map, pidList); + + return StringUtils.join(pidList, ","); + } + + private void getPidTree(Long pid, Map map, List pidList) { + //顶级部门,无上级部门 + if (Constant.DEPT_ROOT.equals(pid)) { + return; + } + + //上级部门存在 + SysDeptEntity parent = map.get(pid); + if (parent != null) { + getPidTree(parent.getPid(), map, pidList); + } + + pidList.add(pid); + } +} diff --git a/system-admin/src/main/java/com/weather/modules/sys/service/impl/SysDictDataServiceImpl.java b/system-admin/src/main/java/com/weather/modules/sys/service/impl/SysDictDataServiceImpl.java new file mode 100644 index 0000000..437878e --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/service/impl/SysDictDataServiceImpl.java @@ -0,0 +1,82 @@ + + +package com.weather.modules.sys.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.weather.common.page.PageData; +import com.weather.common.service.impl.BaseServiceImpl; +import com.weather.common.utils.ConvertUtils; +import com.weather.modules.sys.dao.SysDictDataDao; +import com.weather.modules.sys.dto.SysDictDataDTO; +import com.weather.modules.sys.entity.SysDictDataEntity; +import com.weather.modules.sys.service.SysDictDataService; +import cn.hutool.core.util.StrUtil; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Arrays; +import java.util.Map; + +/** + * 字典类型 + * + * @author 123 + */ +@Service +public class SysDictDataServiceImpl extends BaseServiceImpl implements SysDictDataService { + + @Override + public PageData page(Map params) { + IPage page = baseDao.selectPage( + getPage(params, "sort", true), + getWrapper(params) + ); + + return getPageData(page, SysDictDataDTO.class); + } + + private QueryWrapper getWrapper(Map params){ + Long dictTypeId = Long.parseLong((String) params.get("dictTypeId")); + String dictLabel = (String) params.get("dictLabel"); + String dictValue = (String) params.get("dictValue"); + + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.eq("dict_type_id", dictTypeId); + wrapper.like(StrUtil.isNotBlank(dictLabel), "dict_label", dictLabel); + wrapper.like(StrUtil.isNotBlank(dictValue), "dict_value", dictValue); + + return wrapper; + } + + @Override + public SysDictDataDTO get(Long id) { + SysDictDataEntity entity = baseDao.selectById(id); + + return ConvertUtils.sourceToTarget(entity, SysDictDataDTO.class); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void save(SysDictDataDTO dto) { + SysDictDataEntity entity = ConvertUtils.sourceToTarget(dto, SysDictDataEntity.class); + + insert(entity); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(SysDictDataDTO dto) { + SysDictDataEntity entity = ConvertUtils.sourceToTarget(dto, SysDictDataEntity.class); + + updateById(entity); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(Long[] ids) { + //删除 + deleteBatchIds(Arrays.asList(ids)); + } + +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/sys/service/impl/SysDictTypeServiceImpl.java b/system-admin/src/main/java/com/weather/modules/sys/service/impl/SysDictTypeServiceImpl.java new file mode 100644 index 0000000..5d7791f --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/service/impl/SysDictTypeServiceImpl.java @@ -0,0 +1,100 @@ + +package com.weather.modules.sys.service.impl; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.weather.common.page.PageData; +import com.weather.common.service.impl.BaseServiceImpl; +import com.weather.common.utils.ConvertUtils; +import com.weather.modules.sys.dao.SysDictDataDao; +import com.weather.modules.sys.dao.SysDictTypeDao; +import com.weather.modules.sys.dto.SysDictTypeDTO; +import com.weather.modules.sys.entity.DictData; +import com.weather.modules.sys.entity.DictType; +import com.weather.modules.sys.entity.SysDictTypeEntity; +import com.weather.modules.sys.service.SysDictTypeService; +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * 字典类型 + * + * @author 123 + */ +@Service +@AllArgsConstructor +public class SysDictTypeServiceImpl extends BaseServiceImpl implements SysDictTypeService { + private final SysDictDataDao sysDictDataDao; + + @Override + public PageData page(Map params) { + IPage page = baseDao.selectPage( + getPage(params, "sort", true), + getWrapper(params) + ); + + return getPageData(page, SysDictTypeDTO.class); + } + + private QueryWrapper getWrapper(Map params) { + String dictType = (String) params.get("dictType"); + String dictName = (String) params.get("dictName"); + + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.like(StrUtil.isNotBlank(dictType), "dict_type", dictType); + wrapper.like(StrUtil.isNotBlank(dictName), "dict_name", dictName); + + return wrapper; + } + + @Override + public SysDictTypeDTO get(Long id) { + SysDictTypeEntity entity = baseDao.selectById(id); + + return ConvertUtils.sourceToTarget(entity, SysDictTypeDTO.class); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void save(SysDictTypeDTO dto) { + SysDictTypeEntity entity = ConvertUtils.sourceToTarget(dto, SysDictTypeEntity.class); + + insert(entity); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(SysDictTypeDTO dto) { + SysDictTypeEntity entity = ConvertUtils.sourceToTarget(dto, SysDictTypeEntity.class); + + updateById(entity); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(Long[] ids) { + //删除 + deleteBatchIds(Arrays.asList(ids)); + } + + @Override + public List getAllList() { + List typeList = baseDao.getDictTypeList(); + List dataList = sysDictDataDao.getDictDataList(); + for (DictType type : typeList) { + for (DictData data : dataList) { + if (type.getId().equals(data.getDictTypeId())) { + type.getDataList().add(data); + } + } + } + return typeList; + } + +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/sys/service/impl/SysMenuServiceImpl.java b/system-admin/src/main/java/com/weather/modules/sys/service/impl/SysMenuServiceImpl.java new file mode 100644 index 0000000..e15047c --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/service/impl/SysMenuServiceImpl.java @@ -0,0 +1,102 @@ + +package com.weather.modules.sys.service.impl; + +import com.weather.common.constant.Constant; +import com.weather.common.exception.ErrorCode; +import com.weather.common.exception.CommonException; +import com.weather.common.service.impl.BaseServiceImpl; +import com.weather.common.utils.ConvertUtils; +import com.weather.common.utils.TreeUtils; +import com.weather.modules.security.user.UserDetail; +import com.weather.modules.sys.dao.SysMenuDao; +import com.weather.modules.sys.dto.SysMenuDTO; +import com.weather.modules.sys.entity.SysMenuEntity; +import com.weather.modules.sys.enums.SuperAdminEnum; +import com.weather.modules.sys.service.SysMenuService; +import com.weather.modules.sys.service.SysRoleMenuService; +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@AllArgsConstructor +public class SysMenuServiceImpl extends BaseServiceImpl implements SysMenuService { + private final SysRoleMenuService sysRoleMenuService; + + @Override + public SysMenuDTO get(Long id) { + SysMenuEntity entity = baseDao.getById(id); + + SysMenuDTO dto = ConvertUtils.sourceToTarget(entity, SysMenuDTO.class); + + return dto; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void save(SysMenuDTO dto) { + SysMenuEntity entity = ConvertUtils.sourceToTarget(dto, SysMenuEntity.class); + + //保存菜单 + insert(entity); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(SysMenuDTO dto) { + SysMenuEntity entity = ConvertUtils.sourceToTarget(dto, SysMenuEntity.class); + + //上级菜单不能为自身 + if (entity.getId().equals(entity.getPid())) { + throw new CommonException(ErrorCode.SUPERIOR_MENU_ERROR); + } + + //更新菜单 + updateById(entity); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(Long id) { + //删除菜单 + deleteById(id); + + //删除角色菜单关系 + sysRoleMenuService.deleteByMenuId(id); + } + + @Override + public List getAllMenuList(Integer menuType) { + List menuList = baseDao.getMenuList(menuType); + + List dtoList = ConvertUtils.sourceToTarget(menuList, SysMenuDTO.class); + + return TreeUtils.build(dtoList, Constant.MENU_ROOT); + } + + @Override + public List getUserMenuList(UserDetail user, Integer menuType) { + List menuList; + + //系统管理员,拥有最高权限 + if (user.getSuperAdmin() == SuperAdminEnum.YES.value()) { + menuList = baseDao.getMenuList(menuType); + } else { + menuList = baseDao.getUserMenuList(user.getId(), menuType); + } + + List dtoList = ConvertUtils.sourceToTarget(menuList, SysMenuDTO.class); + + return TreeUtils.build(dtoList); + } + + @Override + public List getListPid(Long pid) { + List menuList = baseDao.getListPid(pid); + + return ConvertUtils.sourceToTarget(menuList, SysMenuDTO.class); + } + +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/sys/service/impl/SysParamsServiceImpl.java b/system-admin/src/main/java/com/weather/modules/sys/service/impl/SysParamsServiceImpl.java new file mode 100644 index 0000000..8448ba2 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/service/impl/SysParamsServiceImpl.java @@ -0,0 +1,135 @@ + +package com.weather.modules.sys.service.impl; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.weather.common.constant.Constant; +import com.weather.common.exception.ErrorCode; +import com.weather.common.exception.CommonException; +import com.weather.common.page.PageData; +import com.weather.common.service.impl.BaseServiceImpl; +import com.weather.common.utils.ConvertUtils; +import com.weather.common.utils.JsonUtils; +import com.weather.modules.sys.dao.SysParamsDao; +import com.weather.modules.sys.dto.SysParamsDTO; +import com.weather.modules.sys.entity.SysParamsEntity; +import com.weather.modules.sys.redis.SysParamsRedis; +import com.weather.modules.sys.service.SysParamsService; +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * 参数管理 + * + * @author 123 + * @since 1.0.0 + */ +@Service +@AllArgsConstructor +public class SysParamsServiceImpl extends BaseServiceImpl implements SysParamsService { + private final SysParamsRedis sysParamsRedis; + + @Override + public PageData page(Map params) { + IPage page = baseDao.selectPage( + getPage(params, Constant.CREATE_DATE, false), + getWrapper(params) + ); + + return getPageData(page, SysParamsDTO.class); + } + + @Override + public List list(Map params) { + List entityList = baseDao.selectList(getWrapper(params)); + + return ConvertUtils.sourceToTarget(entityList, SysParamsDTO.class); + } + + private QueryWrapper getWrapper(Map params) { + String paramCode = (String) params.get("paramCode"); + + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.eq("param_type", 1); + wrapper.like(StrUtil.isNotBlank(paramCode), "param_code", paramCode); + + return wrapper; + } + + @Override + public SysParamsDTO get(Long id) { + SysParamsEntity entity = baseDao.selectById(id); + + return ConvertUtils.sourceToTarget(entity, SysParamsDTO.class); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void save(SysParamsDTO dto) { + SysParamsEntity entity = ConvertUtils.sourceToTarget(dto, SysParamsEntity.class); + insert(entity); + + sysParamsRedis.set(entity.getParamCode(), entity.getParamValue()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(SysParamsDTO dto) { + SysParamsEntity entity = ConvertUtils.sourceToTarget(dto, SysParamsEntity.class); + updateById(entity); + + sysParamsRedis.set(entity.getParamCode(), entity.getParamValue()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(Long[] ids) { + //删除Redis数据 + List paramCodeList = baseDao.getParamCodeList(ids); + String[] paramCodes = paramCodeList.toArray(new String[paramCodeList.size()]); + sysParamsRedis.delete(paramCodes); + + //删除 + deleteBatchIds(Arrays.asList(ids)); + } + + @Override + public String getValue(String paramCode) { + String paramValue = sysParamsRedis.get(paramCode); + if (paramValue == null) { + paramValue = baseDao.getValueByCode(paramCode); + + sysParamsRedis.set(paramCode, paramValue); + } + return paramValue; + } + + @Override + public T getValueObject(String paramCode, Class clazz) { + String paramValue = getValue(paramCode); + if (StrUtil.isNotBlank(paramValue)) { + return JsonUtils.parseObject(paramValue, clazz); + } + + try { + return clazz.newInstance(); + } catch (Exception e) { + throw new CommonException(ErrorCode.PARAMS_GET_ERROR); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public int updateValueByCode(String paramCode, String paramValue) { + int count = baseDao.updateValueByCode(paramCode, paramValue); + sysParamsRedis.set(paramCode, paramValue); + return count; + } + +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/sys/service/impl/SysRoleDataScopeServiceImpl.java b/system-admin/src/main/java/com/weather/modules/sys/service/impl/SysRoleDataScopeServiceImpl.java new file mode 100644 index 0000000..d9fd7ce --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/service/impl/SysRoleDataScopeServiceImpl.java @@ -0,0 +1,56 @@ + + +package com.weather.modules.sys.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.weather.common.service.impl.BaseServiceImpl; +import com.weather.modules.sys.dao.SysRoleDataScopeDao; +import com.weather.modules.sys.entity.SysRoleDataScopeEntity; +import com.weather.modules.sys.service.SysRoleDataScopeService; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +/** + * 角色数据权限 + * + * @author 123 + * @since 1.0.0 + */ +@Service +public class SysRoleDataScopeServiceImpl extends BaseServiceImpl + implements SysRoleDataScopeService { + + @Override + public List getDeptIdList(Long roleId) { + return baseDao.getDeptIdList(roleId); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void saveOrUpdate(Long roleId, List deptIdList) { + //先删除角色数据权限关系 + deleteByRoleIds(new Long[]{roleId}); + + //角色没有一个数据权限的情况 + if(CollUtil.isEmpty(deptIdList)){ + return ; + } + + //保存角色数据权限关系 + for(Long deptId : deptIdList){ + SysRoleDataScopeEntity sysRoleDataScopeEntity = new SysRoleDataScopeEntity(); + sysRoleDataScopeEntity.setDeptId(deptId); + sysRoleDataScopeEntity.setRoleId(roleId); + + //保存 + insert(sysRoleDataScopeEntity); + } + } + + @Override + public void deleteByRoleIds(Long[] roleIds) { + baseDao.deleteByRoleIds(roleIds); + } +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/sys/service/impl/SysRoleMenuServiceImpl.java b/system-admin/src/main/java/com/weather/modules/sys/service/impl/SysRoleMenuServiceImpl.java new file mode 100644 index 0000000..2bbcacd --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/service/impl/SysRoleMenuServiceImpl.java @@ -0,0 +1,63 @@ + + +package com.weather.modules.sys.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.weather.common.service.impl.BaseServiceImpl; +import com.weather.modules.sys.dao.SysRoleMenuDao; +import com.weather.modules.sys.entity.SysRoleMenuEntity; +import com.weather.modules.sys.service.SysRoleMenuService; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + + +/** + * 角色与菜单对应关系 + * + * @author 123 + */ +@Service +public class SysRoleMenuServiceImpl extends BaseServiceImpl implements SysRoleMenuService { + + @Override + @Transactional(rollbackFor = Exception.class) + public void saveOrUpdate(Long roleId, List menuIdList) { + //先删除角色菜单关系 + deleteByRoleIds(new Long[]{roleId}); + + //角色没有一个菜单权限的情况 + if(CollUtil.isEmpty(menuIdList)){ + return ; + } + + //保存角色菜单关系 + for(Long menuId : menuIdList){ + SysRoleMenuEntity sysRoleMenuEntity = new SysRoleMenuEntity(); + sysRoleMenuEntity.setMenuId(menuId); + sysRoleMenuEntity.setRoleId(roleId); + + //保存 + insert(sysRoleMenuEntity); + } + } + + @Override + public List getMenuIdList(Long roleId){ + return baseDao.getMenuIdList(roleId); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteByRoleIds(Long[] roleIds) { + baseDao.deleteByRoleIds(roleIds); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteByMenuId(Long menuId) { + baseDao.deleteByMenuId(menuId); + } + +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/sys/service/impl/SysRoleServiceImpl.java b/system-admin/src/main/java/com/weather/modules/sys/service/impl/SysRoleServiceImpl.java new file mode 100644 index 0000000..1254253 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/service/impl/SysRoleServiceImpl.java @@ -0,0 +1,126 @@ + +package com.weather.modules.sys.service.impl; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.weather.common.constant.Constant; +import com.weather.common.page.PageData; +import com.weather.common.service.impl.BaseServiceImpl; +import com.weather.common.utils.ConvertUtils; +import com.weather.modules.security.user.SecurityUser; +import com.weather.modules.security.user.UserDetail; +import com.weather.modules.sys.dao.SysRoleDao; +import com.weather.modules.sys.dto.SysRoleDTO; +import com.weather.modules.sys.entity.SysRoleEntity; +import com.weather.modules.sys.enums.SuperAdminEnum; +import com.weather.modules.sys.service.*; +import com.weather.modules.sys.service.*; +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * 角色 + * + * @author 123 + */ +@Service +@AllArgsConstructor +public class SysRoleServiceImpl extends BaseServiceImpl implements SysRoleService { + private final SysRoleMenuService sysRoleMenuService; + private final SysRoleDataScopeService sysRoleDataScopeService; + private final SysRoleUserService sysRoleUserService; + private final SysDeptService sysDeptService; + + @Override + public PageData page(Map params) { + IPage page = baseDao.selectPage( + getPage(params, Constant.CREATE_DATE, false), + getWrapper(params) + ); + + return getPageData(page, SysRoleDTO.class); + } + + @Override + public List list(Map params) { + List entityList = baseDao.selectList(getWrapper(params)); + + return ConvertUtils.sourceToTarget(entityList, SysRoleDTO.class); + } + + private QueryWrapper getWrapper(Map params) { + String name = (String) params.get("name"); + + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.like(StrUtil.isNotBlank(name), "name", name); + + //普通管理员,只能查询所属部门及子部门的数据 + UserDetail user = SecurityUser.getUser(); + if (user.getSuperAdmin() == SuperAdminEnum.NO.value()) { + List deptIdList = sysDeptService.getSubDeptIdList(user.getDeptId()); + wrapper.in(deptIdList != null, "dept_id", deptIdList); + } + + return wrapper; + } + + @Override + public SysRoleDTO get(Long id) { + SysRoleEntity entity = baseDao.selectById(id); + + return ConvertUtils.sourceToTarget(entity, SysRoleDTO.class); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void save(SysRoleDTO dto) { + SysRoleEntity entity = ConvertUtils.sourceToTarget(dto, SysRoleEntity.class); + + //保存角色 + insert(entity); + + //保存角色菜单关系 + sysRoleMenuService.saveOrUpdate(entity.getId(), dto.getMenuIdList()); + + //保存角色数据权限关系 + sysRoleDataScopeService.saveOrUpdate(entity.getId(), dto.getDeptIdList()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(SysRoleDTO dto) { + SysRoleEntity entity = ConvertUtils.sourceToTarget(dto, SysRoleEntity.class); + + //更新角色 + updateById(entity); + + //更新角色菜单关系 + sysRoleMenuService.saveOrUpdate(entity.getId(), dto.getMenuIdList()); + + //更新角色数据权限关系 + sysRoleDataScopeService.saveOrUpdate(entity.getId(), dto.getDeptIdList()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(Long[] ids) { + //删除角色 + baseDao.deleteBatchIds(Arrays.asList(ids)); + + //删除角色用户关系 + sysRoleUserService.deleteByRoleIds(ids); + + //删除角色菜单关系 + sysRoleMenuService.deleteByRoleIds(ids); + + //删除角色数据权限关系 + sysRoleDataScopeService.deleteByRoleIds(ids); + } + +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/sys/service/impl/SysRoleUserServiceImpl.java b/system-admin/src/main/java/com/weather/modules/sys/service/impl/SysRoleUserServiceImpl.java new file mode 100644 index 0000000..a1cd621 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/service/impl/SysRoleUserServiceImpl.java @@ -0,0 +1,59 @@ + + +package com.weather.modules.sys.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.weather.common.service.impl.BaseServiceImpl; +import com.weather.modules.sys.dao.SysRoleUserDao; +import com.weather.modules.sys.entity.SysRoleUserEntity; +import com.weather.modules.sys.service.SysRoleUserService; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 角色用户关系 + * + * @author 123 + * @since 1.0.0 + */ +@Service +public class SysRoleUserServiceImpl extends BaseServiceImpl implements SysRoleUserService { + + @Override + public void saveOrUpdate(Long userId, List roleIdList) { + //先删除角色用户关系 + deleteByUserIds(new Long[]{userId}); + + //用户没有一个角色权限的情况 + if(CollUtil.isEmpty(roleIdList)){ + return ; + } + + //保存角色用户关系 + for(Long roleId : roleIdList){ + SysRoleUserEntity sysRoleUserEntity = new SysRoleUserEntity(); + sysRoleUserEntity.setUserId(userId); + sysRoleUserEntity.setRoleId(roleId); + + //保存 + insert(sysRoleUserEntity); + } + } + + @Override + public void deleteByRoleIds(Long[] roleIds) { + baseDao.deleteByRoleIds(roleIds); + } + + @Override + public void deleteByUserIds(Long[] userIds) { + baseDao.deleteByUserIds(userIds); + } + + @Override + public List getRoleIdList(Long userId) { + + return baseDao.getRoleIdList(userId); + } +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/sys/service/impl/SysUserServiceImpl.java b/system-admin/src/main/java/com/weather/modules/sys/service/impl/SysUserServiceImpl.java new file mode 100644 index 0000000..0541923 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/sys/service/impl/SysUserServiceImpl.java @@ -0,0 +1,150 @@ + +package com.weather.modules.sys.service.impl; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.weather.common.constant.Constant; +import com.weather.common.page.PageData; +import com.weather.common.service.impl.BaseServiceImpl; +import com.weather.common.utils.ConvertUtils; +import com.weather.modules.security.password.PasswordUtils; +import com.weather.modules.security.user.SecurityUser; +import com.weather.modules.security.user.UserDetail; +import com.weather.modules.sys.dao.SysUserDao; +import com.weather.modules.sys.dto.SysUserDTO; +import com.weather.modules.sys.entity.SysUserEntity; +import com.weather.modules.sys.enums.SuperAdminEnum; +import com.weather.modules.sys.service.SysDeptService; +import com.weather.modules.sys.service.SysRoleUserService; +import com.weather.modules.sys.service.SysUserService; +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + + +/** + * 系统用户 + * + * @author 123 + */ +@Service +@AllArgsConstructor +public class SysUserServiceImpl extends BaseServiceImpl implements SysUserService { + private final SysRoleUserService sysRoleUserService; + private final SysDeptService sysDeptService; + + @Override + public PageData page(Map params) { + //转换成like + paramsToLike(params, "username"); + + //分页 + IPage page = getPage(params, Constant.CREATE_DATE, false); + + //普通管理员,只能查询所属部门及子部门的数据 + UserDetail user = SecurityUser.getUser(); + if (user.getSuperAdmin() == SuperAdminEnum.NO.value()) { + params.put("deptIdList", sysDeptService.getSubDeptIdList(user.getDeptId())); + } + + //查询 + List list = baseDao.getList(params); + + return getPageData(list, page.getTotal(), SysUserDTO.class); + } + + @Override + public List list(Map params) { + //普通管理员,只能查询所属部门及子部门的数据 + UserDetail user = SecurityUser.getUser(); + if (user.getSuperAdmin() == SuperAdminEnum.NO.value()) { + params.put("deptIdList", sysDeptService.getSubDeptIdList(user.getDeptId())); + } + + List entityList = baseDao.getList(params); + + return ConvertUtils.sourceToTarget(entityList, SysUserDTO.class); + } + + @Override + public SysUserDTO get(Long id) { + SysUserEntity entity = baseDao.getById(id); + + return ConvertUtils.sourceToTarget(entity, SysUserDTO.class); + } + + @Override + public SysUserDTO getByUsername(String username) { + SysUserEntity entity = baseDao.getByUsername(username); + return ConvertUtils.sourceToTarget(entity, SysUserDTO.class); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void save(SysUserDTO dto) { + SysUserEntity entity = ConvertUtils.sourceToTarget(dto, SysUserEntity.class); + + //密码加密 + String password = PasswordUtils.encode(entity.getPassword()); + entity.setPassword(password); + + //保存用户 + entity.setSuperAdmin(SuperAdminEnum.NO.value()); + insert(entity); + + //保存角色用户关系 + sysRoleUserService.saveOrUpdate(entity.getId(), dto.getRoleIdList()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(SysUserDTO dto) { + SysUserEntity entity = ConvertUtils.sourceToTarget(dto, SysUserEntity.class); + + //密码加密 + if (StrUtil.isBlank(dto.getPassword())) { + entity.setPassword(null); + } else { + String password = PasswordUtils.encode(entity.getPassword()); + entity.setPassword(password); + } + + //更新用户 + updateById(entity); + + //更新角色用户关系 + sysRoleUserService.saveOrUpdate(entity.getId(), dto.getRoleIdList()); + } + + @Override + public void delete(Long[] ids) { + //删除用户 + baseDao.deleteBatchIds(Arrays.asList(ids)); + + //删除角色用户关系 + sysRoleUserService.deleteByUserIds(ids); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updatePassword(Long id, String newPassword) { + newPassword = PasswordUtils.encode(newPassword); + + baseDao.updatePassword(id, newPassword); + } + + @Override + public int getCountByDeptId(Long deptId) { + return baseDao.getCountByDeptId(deptId); + } + + @Override + public List getUserIdListByDeptId(List deptIdList) { + return baseDao.getUserIdListByDeptId(deptIdList); + } + +} diff --git a/system-admin/src/main/java/com/weather/modules/weather/dailydata/WeatherDataImportManager.java b/system-admin/src/main/java/com/weather/modules/weather/dailydata/WeatherDataImportManager.java new file mode 100644 index 0000000..0311410 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/weather/dailydata/WeatherDataImportManager.java @@ -0,0 +1,90 @@ +package com.weather.modules.weather.dailydata; + +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.weather.modules.weather.dailydata.dto.ImportProgress; +import com.weather.modules.weather.dailydata.service.WeatherDailyDataService; +import com.weather.modules.weather.dailydata.vo.WeatherExcelVO; +import com.weather.modules.security.user.SecurityUser; +import com.weather.modules.security.user.UserContextHolder; +import com.weather.modules.security.user.UserDetail; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.IOException; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; + +@Slf4j +@Component +@AllArgsConstructor +public class WeatherDataImportManager { + + private final WeatherDailyDataService weatherDailyDataService; + private final Map tasks = new ConcurrentHashMap<>(); + + public String submitImport(MultipartFile file) throws IOException { + File tempFile = File.createTempFile("import_", ".xlsx"); + file.transferTo(tempFile); + + String taskId = UUID.randomUUID().toString(); + ImportProgress progress = new ImportProgress(taskId); + progress.setStatus("PROCESSING"); + tasks.put(taskId, progress); + + UserDetail user = SecurityUser.getUser(); + CompletableFuture.runAsync(() -> { + UserContextHolder.set(user); + try { + processImport(taskId, tempFile); + } catch (Exception e) { + log.error("导入任务 {} 失败", taskId, e); + ImportProgress p = tasks.get(taskId); + if (p != null) { + p.setStatus("FAILED"); + p.setErrorMessage(e.getMessage()); + } + } finally { + UserContextHolder.remove(); + if (!tempFile.delete()) { + tempFile.deleteOnExit(); + } + } + }); + + return taskId; + } + + private void processImport(String taskId, File tempFile) { + ImportProgress progress = tasks.get(taskId); + + AnalysisEventListener countListener = new AnalysisEventListener<>() { + @Override + public void invoke(WeatherExcelVO data, AnalysisContext context) { + progress.setTotalRows(progress.getTotalRows() + 1); + } + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + log.info("任务 {} 共 {} 行", taskId, progress.getTotalRows()); + } + }; + EasyExcel.read(tempFile, WeatherExcelVO.class, countListener).sheet().doRead(); + + EasyExcel.read(tempFile, WeatherExcelVO.class, new WeatherDataListener(weatherDailyDataService, progress)) + .sheet() + .doRead(); + + progress.setStatus("COMPLETED"); + log.info("导入任务 {} 完成,共导入 {} 行", taskId, progress.getProcessedRows().get()); + } + + public ImportProgress getProgress(String taskId) { + return tasks.get(taskId); + } +} diff --git a/system-admin/src/main/java/com/weather/modules/weather/dailydata/WeatherDataListener.java b/system-admin/src/main/java/com/weather/modules/weather/dailydata/WeatherDataListener.java new file mode 100644 index 0000000..e32fd5a --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/weather/dailydata/WeatherDataListener.java @@ -0,0 +1,71 @@ +package com.weather.modules.weather.dailydata; + +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.weather.common.utils.ConvertUtils; +import com.weather.modules.weather.dailydata.dto.ImportProgress; +import com.weather.modules.weather.dailydata.entity.WeatherDailyDataEntity; +import com.weather.modules.weather.dailydata.service.WeatherDailyDataService; +import com.weather.modules.weather.dailydata.vo.WeatherExcelVO; +import lombok.extern.slf4j.Slf4j; + +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +@Slf4j +public class WeatherDataListener extends AnalysisEventListener { + private final WeatherDailyDataService weatherService; + private final ImportProgress progress; + private final List list = new ArrayList<>(); + + private static final int BATCH_COUNT = 2000; + + public WeatherDataListener(WeatherDailyDataService weatherService) { + this.weatherService = weatherService; + this.progress = null; + } + + public WeatherDataListener(WeatherDailyDataService weatherService, ImportProgress progress) { + this.weatherService = weatherService; + this.progress = progress; + } + + @Override + public void invoke(WeatherExcelVO data, AnalysisContext context) { + WeatherDailyDataEntity entity = ConvertUtils.sourceToTarget(data, WeatherDailyDataEntity.class); + + try { + LocalDate localDate = LocalDate.of(data.getYear(), data.getMonth(), data.getDay()); + entity.setObserveDate(Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant())); + } catch (Exception e) { + log.warn("第{}行日期转换失败: {}年{}月{}日,已跳过", context.readRowHolder().getRowIndex(), + data.getYear(), data.getMonth(), data.getDay()); + return; + } + + list.add(entity); + + if (list.size() >= BATCH_COUNT) { + saveData(); + list.clear(); + } + } + + @Override + public void doAfterAllAnalysed(AnalysisContext context) { + saveData(); + log.info("Excel解析完成,全部数据已入库"); + } + + private void saveData() { + if (!list.isEmpty()) { + weatherService.insertBatch(list); + if (progress != null) { + progress.getProcessedRows().addAndGet(list.size()); + } + } + } +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/weather/dailydata/controller/WeatherDailyDataController.java b/system-admin/src/main/java/com/weather/modules/weather/dailydata/controller/WeatherDailyDataController.java new file mode 100644 index 0000000..ca211f8 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/weather/dailydata/controller/WeatherDailyDataController.java @@ -0,0 +1,170 @@ +package com.weather.modules.weather.dailydata.controller; + +import com.weather.common.annotation.LogOperation; +import com.weather.common.constant.Constant; +import com.weather.common.page.PageData; +import com.weather.common.utils.ExcelUtils; +import com.weather.common.utils.Result; +import com.weather.common.validator.AssertUtils; +import com.weather.common.validator.ValidatorUtils; +import com.weather.common.validator.group.AddGroup; +import com.weather.common.validator.group.DefaultGroup; +import com.weather.common.validator.group.UpdateGroup; +import com.weather.modules.weather.dailydata.WeatherDataImportManager; +import com.weather.modules.weather.dailydata.dto.DailyWeatherSummarizeDto; +import com.weather.modules.weather.dailydata.dto.ImportProgress; +import com.weather.modules.weather.dailydata.dto.WeatherDailyDataDto; +import com.weather.modules.weather.dailydata.dto.WeatherDailyDataQueryDto; +import com.weather.modules.weather.dailydata.excel.WeatherDailyDataExcel; +import com.weather.modules.weather.dailydata.service.WeatherDailyDataService; +import com.weather.modules.weather.station.dto.WeatherStationDTO; +import com.weather.modules.weather.station.service.WeatherStationService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.web.bind.annotation.*; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.web.multipart.MultipartFile; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +/** + * 每日气象监测数据表 + * + * @author 2333 123 + * @since 1.0.0 2026-03-05 + */ +@RestController +@RequestMapping("dailyweather/weatherdailydata") +@Tag(name="气象数据") +public class WeatherDailyDataController { + + @Resource + private WeatherDailyDataService weatherDailyDataService; + + @Resource + private WeatherStationService weatherStationService; + + @Resource + private WeatherDataImportManager weatherDataImportManager; + + @GetMapping("page") + @Operation(summary = "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", in = ParameterIn.QUERY, required = true, ref="int") , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", in = ParameterIn.QUERY,required = true, ref="int") , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段", in = ParameterIn.QUERY, ref="String") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)", in = ParameterIn.QUERY, ref="String") + }) + @RequiresPermissions("dailyweather:weatherdailydata:page") + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = weatherDailyDataService.page(params); + page.getList().forEach(item -> { + Long stationId = item.getStationId(); + WeatherStationDTO station = weatherStationService.getByStationCode(stationId); + if (station != null) { + item.setStationName(station.getStationName()); + } + }); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary = "信息") + @RequiresPermissions("dailyweather:weatherdailydata:info") + public Result get(@PathVariable("id") Long id){ + WeatherDailyDataDto data = weatherDailyDataService.get(id); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary = "保存") + @LogOperation("保存") + @RequiresPermissions("dailyweather:weatherdailydata:save") + public Result save(@RequestBody WeatherDailyDataDto dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + weatherDailyDataService.save(dto); + + return new Result(); + } + + @PostMapping("import") + @Operation(summary = "Excel导入") + @LogOperation("Excel导入") + @RequiresPermissions("dailyweather:weatherdailydata:save") + public Result> importExcel(@RequestParam("file") MultipartFile file) throws Exception { + if (file.isEmpty()) { + return new Result>().error("上传文件不能为空"); + } + String taskId = weatherDataImportManager.submitImport(file); + Map data = new HashMap<>(); + data.put("taskId", taskId); + return new Result>().ok(data); + } + + @GetMapping("import/progress/{taskId}") + @Operation(summary = "导入进度") + public Result importProgress(@PathVariable("taskId") String taskId) { + ImportProgress progress = weatherDataImportManager.getProgress(taskId); + return new Result().ok(progress); + } + + @PutMapping + @Operation(summary = "修改") + @LogOperation("修改") + @RequiresPermissions("dailyweather:weatherdailydata:update") + public Result update(@RequestBody WeatherDailyDataDto dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + weatherDailyDataService.update(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary = "删除") + @LogOperation("删除") + @RequiresPermissions("dailyweather:weatherdailydata:delete") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + weatherDailyDataService.delete(ids); + + return new Result(); + } + + @GetMapping("export") + @Operation(summary = "导出") + @LogOperation("导出") + @RequiresPermissions("dailyweather:weatherdailydata:export") + public void export(@Parameter(hidden = true) @RequestParam Map params, HttpServletResponse response) throws Exception { + List list = weatherDailyDataService.list(params); + + ExcelUtils.exportExcelToTarget(response, null, "每日气象监测数据表", list, WeatherDailyDataExcel.class); + } + + @PostMapping("export-sum") + @Operation(summary = "天气汇总") + @LogOperation("天气汇总") + @RequiresPermissions("dailyweather:weatherdailydata:query") + public Result> dailyWeatherSummarize(@RequestBody WeatherDailyDataQueryDto dto) { + + List list = weatherDailyDataService.dailyWeatherSummarize(dto); + + return new Result>().ok(list); + } + +} diff --git a/system-admin/src/main/java/com/weather/modules/weather/dailydata/dao/WeatherDailyDataDao.java b/system-admin/src/main/java/com/weather/modules/weather/dailydata/dao/WeatherDailyDataDao.java new file mode 100644 index 0000000..7bb57dd --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/weather/dailydata/dao/WeatherDailyDataDao.java @@ -0,0 +1,24 @@ +package com.weather.modules.weather.dailydata.dao; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.weather.common.dao.BaseDao; +import com.weather.modules.weather.dailydata.entity.WeatherDailyDataEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 每日气象监测数据表 + * + * @author 2333 123 + * @since 1.0.0 2026-03-05 + */ +@Mapper +public interface WeatherDailyDataDao extends BaseDao { + + List selectSummarizeList(@Param(Constants.WRAPPER) QueryWrapper wrapper); + + int insertBatchMultiRow(@Param("list") List list); +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/weather/dailydata/dto/DailyWeatherSummarizeDto.java b/system-admin/src/main/java/com/weather/modules/weather/dailydata/dto/DailyWeatherSummarizeDto.java new file mode 100644 index 0000000..c46852e --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/weather/dailydata/dto/DailyWeatherSummarizeDto.java @@ -0,0 +1,99 @@ +package com.weather.modules.weather.dailydata.dto; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.media.SchemaProperty; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +@Data +@Schema(name = "气象汇总数据") +public class DailyWeatherSummarizeDto implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @SchemaProperty(name = "编号") + private Long id; + + @SchemaProperty(name = "区站号") + private Long stationId; + + @SchemaProperty(name = "观测日期") + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + private Date observeDate; + + @SchemaProperty(name = "平均气温") + private BigDecimal avgTemp; + + @SchemaProperty(name = "最高气温") + private BigDecimal maxTemp; + + @SchemaProperty(name = "高气温出现时间(HHmm)") + private Integer maxTempTime; + + @SchemaProperty(name = "最低气温") + private BigDecimal minTemp; + + + @SchemaProperty(name = "最低气温出现时间(HHmm)") + private Integer minTempTime; + + @SchemaProperty(name = "20-20时降水量(mm)") + @TableField("rain_20_20") + private BigDecimal rain2020; + + @SchemaProperty(name = "08-08时降水量(mm)") + @TableField("rain_08_08") + private BigDecimal rain0808; + + @SchemaProperty(name = "相对湿度") + private BigDecimal relativeHumidity; + + @SchemaProperty(name = "气压") + private BigDecimal atmospheres; + + @SchemaProperty(name = "日平均风速") + private BigDecimal dayAvgWindSpeed; + + @SchemaProperty(name = "日平均风向(角度)") + private Integer dayAvgWindDirection; + + @SchemaProperty(name = "最大风速(m/s)") + private BigDecimal maxWindSpeed; + + @SchemaProperty(name = "最大风速风向(角度)") + private Integer maxWindDirection; + + @SchemaProperty(name = "最大风速出现时间(HHmm)") + private Integer maxWindTime; + + @SchemaProperty(name = "极大风速(m/s)") + private BigDecimal extremeWindSpeed; + + @SchemaProperty(name = "极大风速风向(角度)") + private Integer extremeWindDirection; + + @SchemaProperty(name = "极大风速出现时间(HHmm)") + private Integer extremeWindTime; + + @SchemaProperty(name = "部门id") + private Long deptId; + + @SchemaProperty(name = "更新者") + private Long updater; + + @SchemaProperty(name = "更新时间") + private Date updateDate; + + + + + +} diff --git a/system-admin/src/main/java/com/weather/modules/weather/dailydata/dto/ImportProgress.java b/system-admin/src/main/java/com/weather/modules/weather/dailydata/dto/ImportProgress.java new file mode 100644 index 0000000..ecffb73 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/weather/dailydata/dto/ImportProgress.java @@ -0,0 +1,18 @@ +package com.weather.modules.weather.dailydata.dto; + +import lombok.Data; + +import java.util.concurrent.atomic.AtomicInteger; + +@Data +public class ImportProgress { + private String taskId; + private volatile int totalRows; + private final AtomicInteger processedRows = new AtomicInteger(0); + private volatile String status; + private volatile String errorMessage; + + public ImportProgress(String taskId) { + this.taskId = taskId; + } +} diff --git a/system-admin/src/main/java/com/weather/modules/weather/dailydata/dto/WeatherDailyDataDto.java b/system-admin/src/main/java/com/weather/modules/weather/dailydata/dto/WeatherDailyDataDto.java new file mode 100644 index 0000000..269de3e --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/weather/dailydata/dto/WeatherDailyDataDto.java @@ -0,0 +1,92 @@ +package com.weather.modules.weather.dailydata.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.media.SchemaProperty; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +import java.math.BigDecimal; + +/** + * 每日气象监测数据表 + * + * @author 2333 123 + * @since 1.0.0 2026-03-05 + */ +@Data +@Schema(name = "每日气象监测数据表") +public class WeatherDailyDataDto implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @SchemaProperty(name = "主键ID") + private Long id; + + @SchemaProperty(name = "区站号") + private Long stationId; + + @SchemaProperty(name = "站名") + private String stationName; + + @SchemaProperty(name = "观测日期") + private Date observeDate; + + @SchemaProperty(name = "平均气温") + private BigDecimal avgTemp; + + @SchemaProperty(name = "最高气温") + private BigDecimal maxTemp; + + @SchemaProperty(name = "最高气温出现时间(HHmm)") + private Integer maxTempTime; + + @SchemaProperty(name = "最低气温") + private BigDecimal minTemp; + + @SchemaProperty(name = "最低气温出现时间(HHmm)") + private Integer minTempTime; + + @SchemaProperty(name = "20-20时降水量(mm)") + private BigDecimal rain2020; + + @SchemaProperty(name = "08-08时降水量(mm)") + private BigDecimal rain0808; + + @SchemaProperty(name = "相对湿度") + private BigDecimal relativeHumidity; + + @SchemaProperty(name = "气压") + private BigDecimal atmospheres; + + @SchemaProperty(name = "日平均风速") + private BigDecimal dayAvgWindSpeed; + + @SchemaProperty(name = "日平均风向(角度)") + private Integer dayAvgWindDirection; + + @SchemaProperty(name = "最大风速(m/s)") + private BigDecimal maxWindSpeed; + + @SchemaProperty(name = "最大风速风向(角度)") + private Integer maxWindDirection; + + @SchemaProperty(name = "最大风速出现时间(HHmm)") + private Integer maxWindTime; + + @SchemaProperty(name = "极大风速(m/s)") + private BigDecimal extremeWindSpeed; + + @SchemaProperty(name = "极大风速风向(角度)") + private Integer extremeWindDirection; + + @SchemaProperty(name = "极大风速出现时间(HHmm)") + private Integer extremeWindTime; + + @SchemaProperty(name = "创建时间") + private Date createDate; + + +} diff --git a/system-admin/src/main/java/com/weather/modules/weather/dailydata/dto/WeatherDailyDataQueryDto.java b/system-admin/src/main/java/com/weather/modules/weather/dailydata/dto/WeatherDailyDataQueryDto.java new file mode 100644 index 0000000..0420815 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/weather/dailydata/dto/WeatherDailyDataQueryDto.java @@ -0,0 +1,48 @@ +package com.weather.modules.weather.dailydata.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.media.SchemaProperty; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + +/** + * 每日气象监测数据表 + * + * @author 2333 123 + * @since 1.0.0 2026-03-05 + */ +@Data +@Schema(name = "每日气象监测数据") +public class WeatherDailyDataQueryDto implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @SchemaProperty(name = "区站号") + private List stationIds; + + @SchemaProperty(name = "站名") + private String stationName; + + @SchemaProperty(name = "开始时间") + @DateTimeFormat(pattern = "yyyy-MM-dd") + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + private Date startDate; + + @SchemaProperty(name = "结束时间") + @DateTimeFormat(pattern = "yyyy-MM-dd") + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + private Date endtDate; + + @SchemaProperty(name = "时间跨度") + private Integer spanTime; + + + +} diff --git a/system-admin/src/main/java/com/weather/modules/weather/dailydata/dto/WeatherDailyDataReqDto.java b/system-admin/src/main/java/com/weather/modules/weather/dailydata/dto/WeatherDailyDataReqDto.java new file mode 100644 index 0000000..9a338c4 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/weather/dailydata/dto/WeatherDailyDataReqDto.java @@ -0,0 +1,10 @@ +package com.weather.modules.weather.dailydata.dto; + +import io.swagger.v3.oas.annotations.media.SchemaProperty; + +public class WeatherDailyDataReqDto { + + @SchemaProperty(name = "区站号") + private String stationId; + +} diff --git a/system-admin/src/main/java/com/weather/modules/weather/dailydata/entity/WeatherDailyDataEntity.java b/system-admin/src/main/java/com/weather/modules/weather/dailydata/entity/WeatherDailyDataEntity.java new file mode 100644 index 0000000..ad71e1d --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/weather/dailydata/entity/WeatherDailyDataEntity.java @@ -0,0 +1,126 @@ +package com.weather.modules.weather.dailydata.entity; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.weather.common.entity.BaseEntity; +import io.swagger.v3.oas.annotations.media.SchemaProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 每日气象监测数据表 + * + * @author 2333 123 + * @since 1.0.0 2026-03-05 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@TableName("weather_daily_data") +public class WeatherDailyDataEntity extends BaseEntity { + + /** + * 站点ID + */ + private Long stationId; + /** + * 观测日期 + */ + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + private Date observeDate; + /** + * 平均气温 + */ + private BigDecimal avgTemp; + /** + * 最高气温 + */ + private BigDecimal maxTemp; + /** + * 最高气温出现时间(HHmm) + */ + private Integer maxTempTime; + /** + * 最低气温 + */ + private BigDecimal minTemp; + /** + * 最低气温出现时间(HHmm) + */ + private Integer minTempTime; + /** + * 20-20时降水量(mm) + */ + @TableField("rain_20_20") + private BigDecimal rain2020; + /** + * 08-08时降水量(mm) + */ + @TableField("rain_08_08") + private BigDecimal rain0808; + + /** + * 相对湿度 + */ + private BigDecimal relativeHumidity; + + /** + * 气压 + */ + private BigDecimal atmospheres; + + /** + * 日平均风速 + */ + private BigDecimal dayAvgWindSpeed; + + /** + * 日平均风向(角度) + */ + private Integer dayAvgWindDirection; + + /** + * 最大风速(m/s) + */ + private BigDecimal maxWindSpeed; + /** + * 最大风速风向(角度) + */ + private Integer maxWindDirection; + /** + * 最大风速出现时间(HHmm) + */ + private Integer maxWindTime; + /** + * 极大风速(m/s) + */ + private BigDecimal extremeWindSpeed; + /** + * 极大风速风向(角度) + */ + private Integer extremeWindDirection; + /** + * 极大风速出现时间(HHmm) + */ + private Integer extremeWindTime; + + /** + * 部门ID + */ + @TableField(fill = FieldFill.INSERT) + private Long deptId; + /** + * 更新者 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private Long updater; + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private Date updateDate; +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/weather/dailydata/excel/WeatherDailyDataExcel.java b/system-admin/src/main/java/com/weather/modules/weather/dailydata/excel/WeatherDailyDataExcel.java new file mode 100644 index 0000000..623e444 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/weather/dailydata/excel/WeatherDailyDataExcel.java @@ -0,0 +1,52 @@ +package com.weather.modules.weather.dailydata.excel; + +import com.alibaba.excel.annotation.ExcelProperty; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 每日气象监测数据表 + * + * @author 2333 123 + * @since 1.0.0 2026-03-05 + */ +@Data +public class WeatherDailyDataExcel { + @ExcelProperty(value = "主键ID") + private Long id; + @ExcelProperty(value = "站点ID") + private Long stationId; + @ExcelProperty(value = "观测日期") + private Date observeDate; + @ExcelProperty(value = "平均气温") + private BigDecimal avgTemp; + @ExcelProperty(value = "最高气温") + private BigDecimal maxTemp; + @ExcelProperty(value = "最高气温出现时间(HHmm)") + private Integer maxTempTime; + @ExcelProperty(value = "最低气温") + private BigDecimal minTemp; + @ExcelProperty(value = "最低气温出现时间(HHmm)") + private Integer minTempTime; + @ExcelProperty(value = "20-20时降水量(mm)") + private BigDecimal rain2020; + @ExcelProperty(value = "08-08时降水量(mm)") + private BigDecimal rain0808; + @ExcelProperty(value = "最大风速(m/s)") + private BigDecimal maxWindSpeed; + @ExcelProperty(value = "最大风速风向(角度)") + private Integer maxWindDirection; + @ExcelProperty(value = "最大风速出现时间(HHmm)") + private Integer maxWindTime; + @ExcelProperty(value = "极大风速(m/s)") + private BigDecimal extremeWindSpeed; + @ExcelProperty(value = "极大风速风向(角度)") + private Integer extremeWindDirection; + @ExcelProperty(value = "极大风速出现时间(HHmm)") + private Integer extremeWindTime; + @ExcelProperty(value = "创建时间") + private Date createDate; + +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/weather/dailydata/service/WeatherDailyDataService.java b/system-admin/src/main/java/com/weather/modules/weather/dailydata/service/WeatherDailyDataService.java new file mode 100644 index 0000000..9f39b08 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/weather/dailydata/service/WeatherDailyDataService.java @@ -0,0 +1,34 @@ +package com.weather.modules.weather.dailydata.service; + +import com.weather.common.page.PageData; +import com.weather.common.service.CrudService; +import com.weather.modules.weather.dailydata.dto.DailyWeatherSummarizeDto; +import com.weather.modules.weather.dailydata.dto.WeatherDailyDataDto; +import com.weather.modules.weather.dailydata.dto.WeatherDailyDataQueryDto; +import com.weather.modules.weather.dailydata.entity.WeatherDailyDataEntity; + +import java.util.List; +import java.util.Map; + +/** + * 每日气象监测数据表 + * + * @author 2333 123 + * @since 1.0.0 2026-03-05 + */ +public interface WeatherDailyDataService extends CrudService { + + /** + * 分页查询 + * @param params 查询参数 + * @return 分页数据 + */ + PageData page(Map params); + + /** + * 获取汇总气象数据 + * @param dto 参数 + * @return 响应数据 + */ + List dailyWeatherSummarize(WeatherDailyDataQueryDto dto); +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/weather/dailydata/service/impl/WeatherDailyDataServiceImpl.java b/system-admin/src/main/java/com/weather/modules/weather/dailydata/service/impl/WeatherDailyDataServiceImpl.java new file mode 100644 index 0000000..6e433d1 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/weather/dailydata/service/impl/WeatherDailyDataServiceImpl.java @@ -0,0 +1,180 @@ +package com.weather.modules.weather.dailydata.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.weather.common.constant.Constant; +import com.weather.common.page.PageData; +import com.weather.common.service.impl.CrudServiceImpl; +import com.weather.common.utils.ConvertUtils; +import com.weather.common.utils.TimeUtils; +import com.weather.modules.security.user.SecurityUser; +import com.weather.modules.security.user.UserDetail; +import com.weather.modules.sys.service.SysDeptService; +import com.weather.modules.weather.dailydata.dao.WeatherDailyDataDao; +import com.weather.modules.weather.dailydata.dto.DailyWeatherSummarizeDto; +import com.weather.modules.weather.dailydata.dto.WeatherDailyDataDto; +import com.weather.modules.weather.dailydata.dto.WeatherDailyDataQueryDto; +import com.weather.modules.weather.dailydata.entity.WeatherDailyDataEntity; +import com.weather.modules.weather.dailydata.service.WeatherDailyDataService; +import com.weather.modules.weather.station.dao.WeatherStationDao; +import com.weather.modules.weather.station.entity.WeatherStationEntity; +import lombok.AllArgsConstructor; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDate; +import java.util.*; +import java.util.stream.Collectors; + +@Service +@AllArgsConstructor +public class WeatherDailyDataServiceImpl extends CrudServiceImpl implements WeatherDailyDataService { + + private final SysDeptService sysDeptService; + private final WeatherStationDao weatherStationDao; + private final WeatherDailyDataDao weatherDailyDataDao; + + @Override + public PageData page(Map params) { + QueryWrapper wrapper = getWrapper(params); + + List prioritizedIds = getPrioritizedStationIds(); + if (!prioritizedIds.isEmpty()) { + wrapper.orderByAsc("CASE WHEN station_id IN (" + + prioritizedIds.stream().map(String::valueOf).collect(Collectors.joining(",")) + + ") THEN 0 ELSE 1 END"); + } + + wrapper.orderByDesc(Constant.CREATE_DATE); + + IPage page = baseDao.selectPage( + getPage(params, null, true), + wrapper + ); + return getPageData(page, WeatherDailyDataDto.class); + } + + private List getPrioritizedStationIds() { + UserDetail user = SecurityUser.getUser(); + if (user == null || user.getDeptId() == null) return Collections.emptyList(); + List deptIds = sysDeptService.getSubDeptIdList(user.getDeptId()); + if (deptIds.isEmpty()) return Collections.emptyList(); + List stations = weatherStationDao.selectList( + new QueryWrapper() + .select("station_code") + .in("dept_id", deptIds)); + return stations.stream() + .map(s -> { + try { return Long.valueOf(s.getStationCode()); } + catch (NumberFormatException e) { return null; } + }) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } + + @Override + public QueryWrapper getWrapper(Map params) { + String stationCode = (String) params.get("stationId"); + QueryWrapper wrapper = new QueryWrapper<>(); + if (StrUtil.isNotEmpty(stationCode)) { + List split = StrUtil.split(stationCode, ","); + wrapper.in(CollectionUtil.isNotEmpty(split), "station_id", split); + } + return wrapper; + } + + @Override + public void save(WeatherDailyDataDto dto) { + WeatherDailyDataEntity entity = ConvertUtils.sourceToTarget(dto, currentModelClass()); + fillStationDeptId(entity); + insert(entity); + BeanUtils.copyProperties(entity, dto); + } + + @Override + public void update(WeatherDailyDataDto dto) { + WeatherDailyDataEntity entity = ConvertUtils.sourceToTarget(dto, currentModelClass()); + fillStationDeptId(entity); + updateById(entity); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean insertBatch(Collection entityList) { + List stationIds = entityList.stream() + .map(WeatherDailyDataEntity::getStationId) + .filter(Objects::nonNull) + .distinct() + .collect(Collectors.toList()); + Map stationDeptMap = new HashMap<>(); + if (!stationIds.isEmpty()) { + List stationCodeList = stationIds.stream().map(String::valueOf).collect(Collectors.toList()); + List stations = weatherStationDao.selectList( + new QueryWrapper().in("station_code", stationCodeList)); + stationDeptMap = stations.stream() + .filter(s -> s.getDeptId() != null) + .collect(Collectors.toMap(s -> Long.valueOf(s.getStationCode()), WeatherStationEntity::getDeptId)); + } + + UserDetail user = SecurityUser.getUser(); + Date now = new Date(); + List list = (List) entityList; + for (WeatherDailyDataEntity entity : list) { + entity.setId(IdUtil.getSnowflakeNextId()); + Long deptId = stationDeptMap.get(entity.getStationId()); + entity.setDeptId(deptId != null ? deptId : user.getDeptId()); + entity.setCreator(user.getId()); + entity.setCreateDate(now); + entity.setUpdater(user.getId()); + entity.setUpdateDate(now); + } + + weatherDailyDataDao.insertBatchMultiRow(list); + return true; + } + + private void fillStationDeptId(WeatherDailyDataEntity entity) { + if (entity.getStationId() != null) { + WeatherStationEntity station = weatherStationDao.selectOne( + new QueryWrapper().eq("station_code", String.valueOf(entity.getStationId()))); + if (station != null && station.getDeptId() != null) { + entity.setDeptId(station.getDeptId()); + } + } + } + + @Override + public List dailyWeatherSummarize( + WeatherDailyDataQueryDto queryDto) { + + List stationIds = queryDto.getStationIds(); + if (CollectionUtil.isEmpty(stationIds)) { + return List.of(); + } + + LocalDate startTime; + LocalDate endTime; + + if (queryDto.getSpanTime() != null) { + LocalDate endDate = TimeUtils.convertToLocalDateTime(queryDto.getEndtDate()); + startTime = endDate.minusYears(queryDto.getSpanTime()); + endTime = endDate; + } else { + startTime = TimeUtils.convertToLocalDateTime(queryDto.getStartDate()); + endTime = TimeUtils.convertToLocalDateTime(queryDto.getEndtDate()); + } + + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.in("station_id", stationIds) + .between("observe_date", startTime, endTime); + + List list = weatherDailyDataDao.selectSummarizeList(wrapper); + + return BeanUtil.copyToList(list, DailyWeatherSummarizeDto.class); + } +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/weather/dailydata/vo/WeatherExcelVO.java b/system-admin/src/main/java/com/weather/modules/weather/dailydata/vo/WeatherExcelVO.java new file mode 100644 index 0000000..5cfcd58 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/weather/dailydata/vo/WeatherExcelVO.java @@ -0,0 +1,72 @@ +package com.weather.modules.weather.dailydata.vo; + +import com.alibaba.excel.annotation.ExcelProperty; +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class WeatherExcelVO { + @ExcelProperty("区站号(字符)") // 对应 Excel 里的列名 + private Long stationId; + + @ExcelProperty("年") + private int year; + + @ExcelProperty("月") + private int month; + + @ExcelProperty("日") + private int day; + + @ExcelProperty("平均气温") + private BigDecimal avgTemp; + + @ExcelProperty("最高气温") + private BigDecimal maxTemp; + + @ExcelProperty("最高气温出现时间") + private Integer maxTempTime; + + @ExcelProperty("最低气温") + private BigDecimal minTemp; + + @ExcelProperty("最低气温出现时间") + private Integer minTempTime; + + @ExcelProperty("20-20时降水量") + private BigDecimal rain2020; + + @ExcelProperty("08-08时降水量") + private BigDecimal rain0808; + + @ExcelProperty("相对湿度") + private BigDecimal relativeHumidity; + + @ExcelProperty("气压") + private BigDecimal atmospheres; + + @ExcelProperty("日平均风速") + private BigDecimal dayAvgWindSpeed; + + @ExcelProperty("日平均风向(角度)") + private Integer dayAvgWindDirection; + + @ExcelProperty("日最大风速的风向(角度)") + private Integer maxWindDirection; + + @ExcelProperty("最大风速") + private BigDecimal maxWindSpeed; + + @ExcelProperty("最大风速出现时间") + private Integer maxWindTime; + + @ExcelProperty("极大风速的风向(角度)") + private BigDecimal extremeWindSpeed; + + @ExcelProperty("极大风速") + private Integer extremeWindDirection; + + @ExcelProperty("极大风速出现时间") + private Integer extremeWindTime; +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/weather/filescan/FileScanStartupRunner.java b/system-admin/src/main/java/com/weather/modules/weather/filescan/FileScanStartupRunner.java new file mode 100644 index 0000000..732c282 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/weather/filescan/FileScanStartupRunner.java @@ -0,0 +1,68 @@ +package com.weather.modules.weather.filescan; + +import cn.hutool.core.util.StrUtil; +import com.weather.common.constant.Constant; +import com.weather.modules.sys.dao.SysDeptDao; +import com.weather.modules.sys.entity.SysDeptEntity; +import com.weather.modules.sys.service.SysParamsService; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +@Slf4j +@Component +@AllArgsConstructor +public class FileScanStartupRunner implements CommandLineRunner { + + private final SysParamsService sysParamsService; + private final SysDeptDao sysDeptDao; + private final FileWatchServiceManager fileWatchServiceManager; + + @Override + public void run(String... args) { + String rootPath = sysParamsService.getValue(Constant.FILE_SCAN_ROOT_PATH); + if (StrUtil.isBlank(rootPath)) { + log.warn("FILE_SCAN_ROOT_PATH 未配置,跳过文件扫描目录初始化"); + return; + } + + // 确保根层级 receive / display / archive 目录存在(用于模型预报等非部门文件) + List rootDirs = List.of("receive", "display", "archive"); + for (String sub : rootDirs) { + Path dir = Paths.get(rootPath, sub); + try { + Files.createDirectories(dir); + log.debug("创建根目录: {}", dir); + } catch (IOException e) { + log.error("创建根目录失败: {}", dir, e); + } + } + + // 创建各部门子目录 + List deptList = sysDeptDao.selectList(null); + for (SysDeptEntity dept : deptList) { + if (StrUtil.isBlank(dept.getName())) continue; + for (String sub : rootDirs) { + Path dir = Paths.get(rootPath, sub, dept.getName()); + try { + Files.createDirectories(dir); + log.debug("创建目录: {}", dir); + } catch (IOException e) { + log.error("创建目录失败: {}", dir, e); + } + } + } + + fileWatchServiceManager.registerDeptDirectories(); + fileWatchServiceManager.scanAllDirectories(); + + log.info("文件扫描目录初始化完成,根路径: {}", rootPath); + } +} diff --git a/system-admin/src/main/java/com/weather/modules/weather/filescan/FileScanTask.java b/system-admin/src/main/java/com/weather/modules/weather/filescan/FileScanTask.java new file mode 100644 index 0000000..4d4ea67 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/weather/filescan/FileScanTask.java @@ -0,0 +1,21 @@ +package com.weather.modules.weather.filescan; + +import com.weather.modules.job.task.ITask; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +@Slf4j +@Component("fileScanTask") +@AllArgsConstructor +public class FileScanTask implements ITask { + + private final FileWatchServiceManager fileWatchServiceManager; + + @Override + public void run(String params) { + log.info("文件扫描定时任务开始执行"); + fileWatchServiceManager.scanAllDirectories(); + log.info("文件扫描定时任务执行完毕"); + } +} diff --git a/system-admin/src/main/java/com/weather/modules/weather/filescan/FileWatchServiceManager.java b/system-admin/src/main/java/com/weather/modules/weather/filescan/FileWatchServiceManager.java new file mode 100644 index 0000000..80bda0e --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/weather/filescan/FileWatchServiceManager.java @@ -0,0 +1,345 @@ +package com.weather.modules.weather.filescan; + +import cn.hutool.crypto.digest.DigestUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import com.weather.common.constant.Constant; +import com.weather.modules.sys.dao.SysDeptDao; +import com.weather.modules.sys.entity.SysDeptEntity; +import com.weather.modules.sys.service.SysParamsService; +import com.weather.modules.weather.filescan.dao.WeatherFileScanRecordDao; +import com.weather.modules.weather.filescan.entity.WeatherFileScanRecordEntity; +import com.weather.modules.weather.filescan.utils.FileNameParser; +import com.weather.modules.weather.filescan.utils.ParseResult; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.channels.FileChannel; +import java.nio.file.*; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.*; + +import static java.nio.file.StandardWatchEventKinds.*; + +@Slf4j +@Component +public class FileWatchServiceManager { + + private final SysParamsService sysParamsService; + private final SysDeptDao sysDeptDao; + private final WeatherFileScanRecordDao weatherFileScanRecordDao; + + public FileWatchServiceManager(SysParamsService sysParamsService, SysDeptDao sysDeptDao, WeatherFileScanRecordDao weatherFileScanRecordDao) { + this.sysParamsService = sysParamsService; + this.sysDeptDao = sysDeptDao; + this.weatherFileScanRecordDao = weatherFileScanRecordDao; + } + + /** 需要跳过的文件扩展名 */ + private static final Set SKIP_EXTENSIONS = Set.of("db"); + + /** 需要跳过的目录名 */ + private static final Set SKIP_DIR_NAMES = Set.of("新建文件夹"); + + private WatchService watchService; + private final Map deptDirMap = new LinkedHashMap<>(); + private String rootReceiveDir; + private volatile boolean running = false; + + @PostConstruct + public void init() { + try { + watchService = FileSystems.getDefault().newWatchService(); + running = true; + new Thread(this::processWatchEvents, "file-watch-service").start(); + log.info("WatchService 启动成功"); + } catch (IOException e) { + log.error("WatchService 启动失败", e); + } + } + + @PreDestroy + public void destroy() { + running = false; + if (watchService != null) { + try { + watchService.close(); + } catch (IOException e) { + log.warn("WatchService 关闭异常", e); + } + } + } + + /** 注册部门目录 + 根目录的 WatchService 监控 */ + public void registerDeptDirectories() { + String rootPath = sysParamsService.getValue(Constant.FILE_SCAN_ROOT_PATH); + if (StrUtil.isBlank(rootPath)) { + log.warn("文件扫描根路径未配置,请检查 sys_params 中的 FILE_SCAN_ROOT_PATH"); + return; + } + + Path receiveRoot = Paths.get(rootPath, "receive"); + List deptList = sysDeptDao.selectList(null); + deptDirMap.clear(); + + // 注册各部门子目录 + for (SysDeptEntity dept : deptList) { + if (StrUtil.isBlank(dept.getName())) continue; + Path dir = receiveRoot.resolve(dept.getName()); + try { + Files.createDirectories(dir); + dir.register(watchService, ENTRY_CREATE, ENTRY_MODIFY); + deptDirMap.put(dir.toString(), dept.getId()); + log.debug("注册 WatchService 监控目录: {}", dir); + } catch (IOException e) { + log.error("创建/注册目录失败: {}", dir, e); + } + } + + // 注册根目录(用于模式预报 gif 等非部门文件) + try { + Files.createDirectories(receiveRoot); + receiveRoot.register(watchService, ENTRY_CREATE, ENTRY_MODIFY); + rootReceiveDir = receiveRoot.toString(); + log.debug("注册 WatchService 监控根目录: {}", receiveRoot); + } catch (IOException e) { + log.error("注册根目录监控失败: {}", receiveRoot, e); + } + } + + /** 全量扫描:部门子目录 + 根目录直接文件 */ + public void scanAllDirectories() { + String rootPath = sysParamsService.getValue(Constant.FILE_SCAN_ROOT_PATH); + if (StrUtil.isBlank(rootPath)) return; + + Path receiveRoot = Paths.get(rootPath, "receive"); + if (!Files.exists(receiveRoot)) return; + + List deptList = sysDeptDao.selectList(null); + Map deptNameIdMap = new HashMap<>(); + for (SysDeptEntity dept : deptList) { + if (StrUtil.isNotBlank(dept.getName())) { + deptNameIdMap.put(dept.getName(), dept.getId()); + } + } + + try (var dirStream = Files.list(receiveRoot)) { + dirStream.forEach(entry -> { + if (Files.isDirectory(entry)) { + // 部门子目录 + String deptName = entry.getFileName().toString(); + if (SKIP_DIR_NAMES.contains(deptName)) { + log.debug("跳过非部门目录: {}", deptName); + return; + } + Long deptId = deptNameIdMap.get(deptName); + if (deptId == null) { + log.debug("跳过未匹配部门的目录: {}", deptName); + return; + } + scanDirectory(entry, deptId); + } else if (Files.isRegularFile(entry)) { + // 根目录直接文件(模型预报图等) + processFile(entry, null); + } + }); + } catch (IOException e) { + log.error("扫描根目录失败: {}", receiveRoot, e); + } + } + + /** 扫描部门目录下的所有文件 */ + private void scanDirectory(Path deptDir, Long deptId) { + try (var fileStream = Files.list(deptDir)) { + fileStream.filter(Files::isRegularFile).forEach(file -> processFile(file, deptId)); + } catch (IOException e) { + log.error("扫描目录失败: {}", deptDir, e); + } + } + + /** WatchService 事件处理循环 */ + private void processWatchEvents() { + while (running) { + try { + WatchKey key = watchService.take(); + Path watchDir = (Path) key.watchable(); + + for (WatchEvent event : key.pollEvents()) { + WatchEvent.Kind kind = event.kind(); + if (kind == OVERFLOW) continue; + + Path fileName = (Path) event.context(); + Path fullPath = watchDir.resolve(fileName); + + if (!Files.isRegularFile(fullPath)) continue; + + Long deptId = deptDirMap.get(watchDir.toString()); + // deptDirMap 中没有的是根目录,deptId 为 null + processFile(fullPath, deptId); + } + + if (!key.reset()) { + log.warn("WatchKey 已失效: {}", watchDir); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + break; + } catch (Exception e) { + log.error("WatchService 事件处理异常", e); + } + } + } + + /** 等待文件写入完成并可读 */ + private boolean waitForFileReady(Path filePath) { + long lastSize = -1; + int stableCount = 0; + int maxRetries = 20; + for (int i = 0; i < maxRetries; i++) { + if (!Files.exists(filePath)) return false; + try { + long currentSize = Files.size(filePath); + if (currentSize == lastSize && currentSize > 0) { + stableCount++; + if (stableCount >= 2) { + try (RandomAccessFile raf = new RandomAccessFile(filePath.toFile(), "rw"); + FileChannel ch = raf.getChannel()) { + java.nio.channels.FileLock lock = ch.lock(0, Long.MAX_VALUE, true); + lock.release(); + return true; + } catch (Exception e) { + log.debug("文件仍被占用,等待中: {} ({})", filePath.getFileName(), e.getMessage()); + } + } + } else { + stableCount = 0; + } + lastSize = currentSize; + } catch (IOException e) { + log.debug("文件暂不可读,等待中: {}", filePath.getFileName()); + } + try { + Thread.sleep(500); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return false; + } + } + log.warn("文件等待超时,跳过: {}", filePath.getFileName()); + return false; + } + + /** 处理单个文件入库(deptId 为 null 表示根目录文件,不绑定部门) */ + private void processFile(Path filePath, Long deptId) { + try { + String fileName = filePath.getFileName().toString(); + + // 跳过不需要的文件类型 + String ext = ""; + int dotIdx = fileName.lastIndexOf('.'); + if (dotIdx > 0) { + ext = fileName.substring(dotIdx + 1).toLowerCase(); + } + if (SKIP_EXTENSIONS.contains(ext)) { + log.debug("跳过忽略的文件类型: {}", fileName); + return; + } + + if (!waitForFileReady(filePath)) return; + + String md5 = DigestUtil.md5Hex(filePath.toFile()); + + long existingCount = weatherFileScanRecordDao.selectCount( + new com.baomidou.mybatisplus.core.conditions.query.QueryWrapper() + .eq("md5_hash", md5) + ); + if (existingCount > 0) { + log.debug("文件已存在,跳过: {} (MD5: {})", fileName, md5); + return; + } + + BasicFileAttributes attrs = Files.readAttributes(filePath, BasicFileAttributes.class); + Date fileCreateTime = new Date(attrs.creationTime().toMillis()); + + String rootPath = sysParamsService.getValue(Constant.FILE_SCAN_ROOT_PATH); + Path displayRoot = Paths.get(rootPath, "display"); + + Path displayTarget; + Path archiveDir; + + if (deptId == null) { + // 根目录文件(模型预报图等):直接放在 display/ 和 archive/ 下 + displayTarget = displayRoot.resolve(fileName); + archiveDir = Paths.get(rootPath, "archive"); + } else { + // 部门文件:放在 display/<部门>/ 和 archive/<部门>/ 下 + SysDeptEntity dept = sysDeptDao.selectById(deptId); + String deptName = dept != null ? dept.getName() : String.valueOf(deptId); + Path displayDeptDir = displayRoot.resolve(deptName); + Files.createDirectories(displayDeptDir); + displayTarget = displayDeptDir.resolve(fileName); + archiveDir = Paths.get(rootPath, "archive", deptName); + } + + Files.createDirectories(displayTarget.getParent()); + + // 如果展示目录已有同名文件,先归档 + Path existingDisplayFile = displayTarget; + if (Files.exists(existingDisplayFile)) { + Files.createDirectories(archiveDir); + Path archiveTarget = archiveDir.resolve(fileName); + + Files.move(existingDisplayFile, archiveTarget, StandardCopyOption.REPLACE_EXISTING); + + weatherFileScanRecordDao.update( + null, + new com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper() + .eq("display_path", existingDisplayFile.toString()) + .set("status", Constant.FileScanStatus.ARCHIVED.getValue()) + .set("archive_path", archiveTarget.toString()) + .set("archived_at", new Date()) + ); + } + + Files.copy(filePath, displayTarget, StandardCopyOption.REPLACE_EXISTING); + + ParseResult parsed = FileNameParser.parse(fileName); + + WeatherFileScanRecordEntity entity = new WeatherFileScanRecordEntity(); + entity.setId(IdUtil.getSnowflakeNextId()); + entity.setFileName(fileName); + entity.setFileExt(ext); + entity.setFileSize(Files.size(filePath)); + entity.setMd5Hash(md5); + + if (deptId == null) { + // 根目录文件:统一归为模式预报类别 + entity.setKeywordCategory("模式预报"); + entity.setKeywordRegion(parsed.getModelName()); // 已映射的有值,未映射的为 null + } else { + entity.setKeywordRegion(parsed.getRegion()); + entity.setKeywordCategory(parsed.getCategory()); + } + entity.setKeywordPeriod(parsed.getPeriod()); + + entity.setSrcPath(filePath.toString()); + entity.setDisplayPath(displayTarget.toString()); + entity.setStatus(Constant.FileScanStatus.DISPLAYED.getValue()); + entity.setFileCreateTime(fileCreateTime); + entity.setScanTime(new Date()); + entity.setDeptId(deptId); + entity.setCreator(0L); + entity.setCreateDate(new Date()); + + weatherFileScanRecordDao.insert(entity); + log.info("文件扫描入库成功: {} (deptId={})", fileName, deptId); + + } catch (Exception e) { + log.error("处理文件失败: {}", filePath, e); + } + } +} diff --git a/system-admin/src/main/java/com/weather/modules/weather/filescan/controller/FileDownloadController.java b/system-admin/src/main/java/com/weather/modules/weather/filescan/controller/FileDownloadController.java new file mode 100644 index 0000000..eeb8cf5 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/weather/filescan/controller/FileDownloadController.java @@ -0,0 +1,63 @@ +package com.weather.modules.weather.filescan.controller; + +import com.weather.common.constant.Constant; +import com.weather.modules.weather.filescan.dto.WeatherFileScanRecordDTO; +import com.weather.modules.weather.filescan.service.WeatherFileScanRecordService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.core.io.FileSystemResource; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.io.File; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + +@RestController +@RequestMapping("filescan/file") +@Tag(name = "文件展示") +public class FileDownloadController { + + @Resource + private WeatherFileScanRecordService weatherFileScanRecordService; + + @GetMapping("display/{id}") + @Operation(summary = "获取展示文件") + @RequiresPermissions("filescan:record:display") + public ResponseEntity displayFile(@PathVariable("id") Long id) { + WeatherFileScanRecordDTO record = weatherFileScanRecordService.get(id); + if (record == null || record.getDisplayPath() == null) { + return ResponseEntity.notFound().build(); + } + + File file = new File(record.getDisplayPath()); + if (!file.exists()) { + return ResponseEntity.notFound().build(); + } + + FileSystemResource resource = new FileSystemResource(file); + String contentType = MediaType.APPLICATION_OCTET_STREAM_VALUE; + String ext = record.getFileExt(); + if (ext == null) { + // fall through to default + } else if ("png".equalsIgnoreCase(ext)) { + contentType = MediaType.IMAGE_PNG_VALUE; + } else if ("gif".equalsIgnoreCase(ext)) { + contentType = MediaType.IMAGE_GIF_VALUE; + } else if ("jpg".equalsIgnoreCase(ext) || "jpeg".equalsIgnoreCase(ext)) { + contentType = MediaType.IMAGE_JPEG_VALUE; + } else if ("txt".equalsIgnoreCase(ext)) { + contentType = MediaType.TEXT_PLAIN_VALUE; + } + + return ResponseEntity.ok() + .contentType(MediaType.parseMediaType(contentType)) + .header(HttpHeaders.CONTENT_DISPOSITION, + "inline; filename=\"" + URLEncoder.encode(record.getFileName(), StandardCharsets.UTF_8) + "\"") + .body(resource); + } +} diff --git a/system-admin/src/main/java/com/weather/modules/weather/filescan/controller/WeatherFileScanRecordController.java b/system-admin/src/main/java/com/weather/modules/weather/filescan/controller/WeatherFileScanRecordController.java new file mode 100644 index 0000000..206afc1 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/weather/filescan/controller/WeatherFileScanRecordController.java @@ -0,0 +1,88 @@ +package com.weather.modules.weather.filescan.controller; + +import com.weather.common.annotation.LogOperation; +import com.weather.common.constant.Constant; +import com.weather.common.page.PageData; +import com.weather.common.utils.Result; +import com.weather.common.validator.AssertUtils; +import com.weather.modules.weather.filescan.dto.DeptFileGroupVO; +import com.weather.modules.weather.filescan.dto.ModelFileGroupVO; +import com.weather.modules.weather.filescan.dto.WeatherFileScanRecordDTO; +import com.weather.modules.weather.filescan.service.WeatherFileScanRecordService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("filescan/record") +@Tag(name = "文件扫描") +public class WeatherFileScanRecordController { + + @Resource + private WeatherFileScanRecordService weatherFileScanRecordService; + + @GetMapping("page") + @Operation(summary = "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", in = ParameterIn.QUERY, required = true, ref = "int"), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", in = ParameterIn.QUERY, required = true, ref = "int"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段", in = ParameterIn.QUERY, ref = "String"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)", in = ParameterIn.QUERY, ref = "String") + }) + @RequiresPermissions("filescan:record:page") + public Result> page(@Parameter(hidden = true) @RequestParam Map params) { + PageData page = weatherFileScanRecordService.page(params); + return new Result>().ok(page); + } + + @GetMapping("tree") + @Operation(summary = "部门文件树") + @RequiresPermissions("filescan:record:tree") + public Result> tree(@RequestParam Long deptId) { + List data = weatherFileScanRecordService.getDeptFileTree(deptId); + return new Result>().ok(data); + } + + @GetMapping("model") + @Operation(summary = "模式预报文件列表") + @RequiresPermissions("filescan:record:model:list") + public Result model() { + ModelFileGroupVO data = weatherFileScanRecordService.getModelFiles(); + return new Result().ok(data); + } + + @GetMapping("{id}") + @Operation(summary = "信息") + @RequiresPermissions("filescan:record:info") + public Result get(@PathVariable("id") Long id) { + WeatherFileScanRecordDTO data = weatherFileScanRecordService.get(id); + return new Result().ok(data); + } + + @PostMapping("archive/{id}") + @Operation(summary = "归档") + @LogOperation("归档") + @RequiresPermissions("filescan:record:archive") + public Result archive(@PathVariable("id") Long id) { + weatherFileScanRecordService.archive(id); + return new Result(); + } + + @DeleteMapping + @Operation(summary = "删除") + @LogOperation("删除") + @RequiresPermissions("filescan:record:delete") + public Result delete(@RequestBody Long[] ids) { + AssertUtils.isArrayEmpty(ids, "id"); + weatherFileScanRecordService.delete(ids); + return new Result(); + } +} diff --git a/system-admin/src/main/java/com/weather/modules/weather/filescan/dao/WeatherFileScanRecordDao.java b/system-admin/src/main/java/com/weather/modules/weather/filescan/dao/WeatherFileScanRecordDao.java new file mode 100644 index 0000000..423191b --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/weather/filescan/dao/WeatherFileScanRecordDao.java @@ -0,0 +1,10 @@ +package com.weather.modules.weather.filescan.dao; + +import com.weather.common.dao.BaseDao; +import com.weather.modules.weather.filescan.entity.WeatherFileScanRecordEntity; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface WeatherFileScanRecordDao extends BaseDao { + +} diff --git a/system-admin/src/main/java/com/weather/modules/weather/filescan/dto/DeptFileGroupVO.java b/system-admin/src/main/java/com/weather/modules/weather/filescan/dto/DeptFileGroupVO.java new file mode 100644 index 0000000..5a043d9 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/weather/filescan/dto/DeptFileGroupVO.java @@ -0,0 +1,12 @@ +package com.weather.modules.weather.filescan.dto; + +import lombok.Data; + +import java.util.List; + +@Data +public class DeptFileGroupVO { + private Long deptId; + private String deptName; + private List fileList; +} diff --git a/system-admin/src/main/java/com/weather/modules/weather/filescan/dto/DeptFileItemVO.java b/system-admin/src/main/java/com/weather/modules/weather/filescan/dto/DeptFileItemVO.java new file mode 100644 index 0000000..f9660e1 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/weather/filescan/dto/DeptFileItemVO.java @@ -0,0 +1,15 @@ +package com.weather.modules.weather.filescan.dto; + +import lombok.Data; + +@Data +public class DeptFileItemVO { + /** 展示名(不含后缀),如 "临武县-日平均气温" */ + private String displayName; + /** 完整文件名(含后缀),如 "临武县-日平均气温.png" */ + private String fileName; + private Long fileId; + private String type; + /** txt 文件的内容,非 txt 文件为 null */ + private String content; +} diff --git a/system-admin/src/main/java/com/weather/modules/weather/filescan/dto/ModelFileGroupVO.java b/system-admin/src/main/java/com/weather/modules/weather/filescan/dto/ModelFileGroupVO.java new file mode 100644 index 0000000..969c478 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/weather/filescan/dto/ModelFileGroupVO.java @@ -0,0 +1,13 @@ +package com.weather.modules.weather.filescan.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.util.List; + +@Data +public class ModelFileGroupVO { + private String type; + @JsonProperty("file_list") + private List fileList; +} diff --git a/system-admin/src/main/java/com/weather/modules/weather/filescan/dto/ModelFileItemVO.java b/system-admin/src/main/java/com/weather/modules/weather/filescan/dto/ModelFileItemVO.java new file mode 100644 index 0000000..ce66779 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/weather/filescan/dto/ModelFileItemVO.java @@ -0,0 +1,16 @@ +package com.weather.modules.weather.filescan.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +@Data +public class ModelFileItemVO { + @JsonProperty("display_name") + private String displayName; + @JsonProperty("file_name") + private String fileName; + @JsonProperty("file_id") + private Long fileId; + /** 文件扩展名 */ + private String type; +} diff --git a/system-admin/src/main/java/com/weather/modules/weather/filescan/dto/WeatherFileScanRecordDTO.java b/system-admin/src/main/java/com/weather/modules/weather/filescan/dto/WeatherFileScanRecordDTO.java new file mode 100644 index 0000000..3eb4d97 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/weather/filescan/dto/WeatherFileScanRecordDTO.java @@ -0,0 +1,71 @@ +package com.weather.modules.weather.filescan.dto; + +import io.swagger.v3.oas.annotations.media.SchemaProperty; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +@Data +public class WeatherFileScanRecordDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @SchemaProperty(name = "主键ID") + private Long id; + + @SchemaProperty(name = "文件名") + private String fileName; + + @SchemaProperty(name = "扩展名") + private String fileExt; + + @SchemaProperty(name = "文件大小(字节)") + private Long fileSize; + + @SchemaProperty(name = "MD5哈希值") + private String md5Hash; + + @SchemaProperty(name = "关键字-地区") + private String keywordRegion; + + @SchemaProperty(name = "关键字-数据类别") + private String keywordCategory; + + @SchemaProperty(name = "关键字-周期") + private String keywordPeriod; + + @SchemaProperty(name = "源路径") + private String srcPath; + + @SchemaProperty(name = "展示路径") + private String displayPath; + + @SchemaProperty(name = "归档路径") + private String archivePath; + + @SchemaProperty(name = "状态 0:已接收 1:展示中 2:已归档") + private Integer status; + + @SchemaProperty(name = "文件创建时间") + private Date fileCreateTime; + + @SchemaProperty(name = "扫描入库时间") + private Date scanTime; + + @SchemaProperty(name = "归档时间") + private Date archivedAt; + + @SchemaProperty(name = "所属部门ID") + private Long deptId; + + @SchemaProperty(name = "所属部门名称") + private String deptName; + + @SchemaProperty(name = "访问链接") + private String accessUrl; + + @SchemaProperty(name = "创建时间") + private Date createDate; +} diff --git a/system-admin/src/main/java/com/weather/modules/weather/filescan/entity/WeatherFileScanRecordEntity.java b/system-admin/src/main/java/com/weather/modules/weather/filescan/entity/WeatherFileScanRecordEntity.java new file mode 100644 index 0000000..826ee52 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/weather/filescan/entity/WeatherFileScanRecordEntity.java @@ -0,0 +1,38 @@ +package com.weather.modules.weather.filescan.entity; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.weather.common.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +@Data +@EqualsAndHashCode(callSuper=false) +@TableName("weather_file_scan_record") +public class WeatherFileScanRecordEntity extends BaseEntity { + + private String fileName; + private String fileExt; + private Long fileSize; + private String md5Hash; + private String keywordRegion; + private String keywordCategory; + private String keywordPeriod; + private String srcPath; + private String displayPath; + private String archivePath; + private Integer status; + private Date fileCreateTime; + private Date scanTime; + private Date archivedAt; + private Long deptId; + + @TableField(fill = FieldFill.INSERT_UPDATE) + private Long updater; + + @TableField(fill = FieldFill.INSERT_UPDATE) + private Date updateDate; +} diff --git a/system-admin/src/main/java/com/weather/modules/weather/filescan/service/WeatherFileScanRecordService.java b/system-admin/src/main/java/com/weather/modules/weather/filescan/service/WeatherFileScanRecordService.java new file mode 100644 index 0000000..cd6c9ff --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/weather/filescan/service/WeatherFileScanRecordService.java @@ -0,0 +1,22 @@ +package com.weather.modules.weather.filescan.service; + +import com.weather.common.page.PageData; +import com.weather.common.service.CrudService; +import com.weather.modules.weather.filescan.dto.DeptFileGroupVO; +import com.weather.modules.weather.filescan.dto.ModelFileGroupVO; +import com.weather.modules.weather.filescan.dto.WeatherFileScanRecordDTO; +import com.weather.modules.weather.filescan.entity.WeatherFileScanRecordEntity; + +import java.util.List; +import java.util.Map; + +public interface WeatherFileScanRecordService extends CrudService { + + PageData page(Map params); + + void archive(Long id); + + List getDeptFileTree(Long deptId); + + ModelFileGroupVO getModelFiles(); +} diff --git a/system-admin/src/main/java/com/weather/modules/weather/filescan/service/impl/WeatherFileScanRecordServiceImpl.java b/system-admin/src/main/java/com/weather/modules/weather/filescan/service/impl/WeatherFileScanRecordServiceImpl.java new file mode 100644 index 0000000..f8ca567 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/weather/filescan/service/impl/WeatherFileScanRecordServiceImpl.java @@ -0,0 +1,200 @@ +package com.weather.modules.weather.filescan.service.impl; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.weather.common.constant.Constant; +import com.weather.common.page.PageData; +import com.weather.common.service.impl.CrudServiceImpl; +import com.weather.common.utils.ConvertUtils; +import com.weather.modules.security.user.SecurityUser; +import com.weather.modules.sys.dao.SysDeptDao; +import com.weather.modules.sys.dto.SysDeptDTO; +import com.weather.modules.sys.entity.SysDeptEntity; +import com.weather.modules.sys.service.SysDeptService; +import com.weather.modules.weather.filescan.dao.WeatherFileScanRecordDao; +import com.weather.modules.weather.filescan.dto.DeptFileGroupVO; +import com.weather.modules.weather.filescan.dto.DeptFileItemVO; +import com.weather.modules.weather.filescan.dto.ModelFileGroupVO; +import com.weather.modules.weather.filescan.dto.ModelFileItemVO; +import com.weather.modules.weather.filescan.dto.WeatherFileScanRecordDTO; +import com.weather.modules.weather.filescan.entity.WeatherFileScanRecordEntity; +import com.weather.modules.weather.filescan.service.WeatherFileScanRecordService; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.*; +import java.util.stream.Collectors; + +@Slf4j +@Service +@AllArgsConstructor +public class WeatherFileScanRecordServiceImpl extends CrudServiceImpl implements WeatherFileScanRecordService { + + private final SysDeptService sysDeptService; + private final SysDeptDao sysDeptDao; + + @Override + public PageData page(Map params) { + IPage page = baseDao.selectPage( + getPage(params, null, false), + getWrapper(params) + ); + PageData pageData = getPageData(page, WeatherFileScanRecordDTO.class); + enrichDeptNames(pageData.getList()); + return pageData; + } + + private void enrichDeptNames(List list) { + List depts = sysDeptService.list(Map.of()); + Map deptMap = depts.stream() + .collect(Collectors.toMap(SysDeptDTO::getId, SysDeptDTO::getName, (a, b) -> a)); + for (WeatherFileScanRecordDTO dto : list) { + if (dto.getDeptId() != null) { + dto.setDeptName(deptMap.get(dto.getDeptId())); + } + } + } + + @Override + public QueryWrapper getWrapper(Map params) { + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.orderByDesc(Constant.CREATE_DATE); + return wrapper; + } + + /** category → 展示顺序,数字越小越靠前 */ + private static final Map CATEGORY_ORDER = Map.of( + "日平均气温", 1, + "日平均降雨量", 3, + "月平均气温图", 5, + "月降雨量图", 6, + "累计降雨量", 7, + "631信息", 9 + ); + + @Override + public List getDeptFileTree(Long deptId) { + List records = baseDao.selectList( + new QueryWrapper() + .eq("status", Constant.FileScanStatus.DISPLAYED.getValue()) + .eq("dept_id", deptId) + ); + + SysDeptEntity dept = sysDeptDao.selectById(deptId); + String deptName = dept != null ? dept.getName() : String.valueOf(deptId); + + List fileList = records.stream() + .sorted((a, b) -> { + int orderA = CATEGORY_ORDER.getOrDefault(a.getKeywordCategory(), Integer.MAX_VALUE); + int orderB = CATEGORY_ORDER.getOrDefault(b.getKeywordCategory(), Integer.MAX_VALUE); + if (orderA != orderB) return Integer.compare(orderA, orderB); + // 同 category 内:非全年排在全年前面 + return Comparator.nullsFirst(String::compareTo) + .compare(a.getKeywordPeriod(), b.getKeywordPeriod()); + }) + .map(r -> { + DeptFileItemVO item = new DeptFileItemVO(); + item.setFileId(r.getId()); + item.setFileName(r.getFileName()); + item.setDisplayName(stripExtension(r.getFileName())); + item.setType(r.getFileExt()); + if ("txt".equalsIgnoreCase(r.getFileExt())) { + item.setContent(readTextContent(r.getDisplayPath())); + } + return item; + }) + .collect(Collectors.toList()); + + DeptFileGroupVO group = new DeptFileGroupVO(); + group.setDeptId(deptId); + group.setDeptName(deptName); + group.setFileList(fileList); + + return List.of(group); + } + + /** 去掉文件名后缀,如 "临武县-日平均气温.png" → "临武县-日平均气温" */ + private String stripExtension(String fileName) { + if (StrUtil.isBlank(fileName)) return fileName; + int dotIdx = fileName.lastIndexOf('.'); + return dotIdx > 0 ? fileName.substring(0, dotIdx) : fileName; + } + + private String readTextContent(String displayPath) { + if (StrUtil.isBlank(displayPath)) return null; + try { + return Files.readString(Path.of(displayPath)); + } catch (IOException e) { + log.warn("读取 txt 文件内容失败: {}", displayPath, e); + return null; + } + } + + @Override + public ModelFileGroupVO getModelFiles() { + List records = baseDao.selectList( + new QueryWrapper() + .isNull("dept_id") + .eq("keyword_category", "模式预报") + .eq("status", Constant.FileScanStatus.DISPLAYED.getValue()) + .orderByAsc("keyword_region") + ); + + ModelFileGroupVO group = new ModelFileGroupVO(); + group.setType("模式预报"); + + List fileList = records.stream() + .map(r -> { + ModelFileItemVO item = new ModelFileItemVO(); + item.setFileName(r.getFileName()); + // 已映射的用模型名,未映射的退回文件名(不带后缀) + item.setDisplayName(StrUtil.isNotBlank(r.getKeywordRegion()) + ? r.getKeywordRegion() + : stripExtension(r.getFileName())); + item.setFileId(r.getId()); + item.setType(r.getFileExt()); + return item; + }) + .collect(Collectors.toList()); + group.setFileList(fileList); + return group; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void archive(Long id) { + WeatherFileScanRecordEntity entity = baseDao.selectById(id); + if (entity == null || entity.getStatus() == Constant.FileScanStatus.ARCHIVED.getValue()) { + return; + } + + if (StrUtil.isNotBlank(entity.getDisplayPath())) { + Path displayPath = Paths.get(entity.getDisplayPath()); + String archiveDir = entity.getDisplayPath() + .replace("\\display\\", "\\archive\\") + .replace("/display/", "/archive/"); + Path archivePath = Paths.get(archiveDir); + try { + Files.createDirectories(archivePath.getParent()); + Files.move(displayPath, archivePath, StandardCopyOption.REPLACE_EXISTING); + entity.setArchivePath(archivePath.toString()); + } catch (IOException e) { + throw new RuntimeException("归档文件移动失败: " + entity.getFileName(), e); + } + } + + entity.setStatus(Constant.FileScanStatus.ARCHIVED.getValue()); + entity.setArchivedAt(new Date()); + entity.setUpdater(SecurityUser.getUser().getId()); + entity.setUpdateDate(new Date()); + updateById(entity); + } +} diff --git a/system-admin/src/main/java/com/weather/modules/weather/filescan/utils/FileNameParser.java b/system-admin/src/main/java/com/weather/modules/weather/filescan/utils/FileNameParser.java new file mode 100644 index 0000000..731182a --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/weather/filescan/utils/FileNameParser.java @@ -0,0 +1,79 @@ +package com.weather.modules.weather.filescan.utils; + +import java.util.Map; + +public class FileNameParser { + + /** 模式预报代码 → 模式名称映射 */ + private static final Map MODEL_CODE_MAP = Map.of( + "2FGrapesCR", "CMA-MESO", + "2FGZ", "CMA-GD", + "2FGZ_RUC", "CMA-GDRUC", + "2FSMB", "CMA-SH9", + "2FSMB_RUC", "CMA-SH3" + ); + + /** + * 解析文件名,支持两种格式: + * 1. 区域文件: {@code <地区>-<指标>.ext} 或 {@code <地区>-<指标>_全年.ext} + * 2. 模式预报: {@code <模式代码>.gif}(如 2FGrapesCR.gif → CMA-MESO) + */ + public static ParseResult parse(String fileName) { + ParseResult result = new ParseResult(); + + int dotIdx = fileName.lastIndexOf('.'); + String ext = dotIdx > 0 ? fileName.substring(dotIdx + 1).toLowerCase() : ""; + String nameWithoutExt = dotIdx > 0 ? fileName.substring(0, dotIdx) : fileName; + result.setExt(ext); + + // 尝试匹配模式预报代码 + String modelName = MODEL_CODE_MAP.get(nameWithoutExt); + if (modelName != null) { + result.setFileType("MODEL"); + result.setModelCode(nameWithoutExt); + result.setModelName(modelName); + result.setRegion(null); + result.setCategory("模式预报"); + result.setPeriod(null); + return result; + } + + // 区域文件解析:<地区>-<指标> 或 <地区>-<指标>_全年 + result.setFileType("REGION"); + result.setModelCode(null); + result.setModelName(null); + + int dashIdx = nameWithoutExt.indexOf('-'); + if (dashIdx > 0) { + result.setRegion(nameWithoutExt.substring(0, dashIdx)); + String afterDash = nameWithoutExt.substring(dashIdx + 1); + + int annualIdx = afterDash.indexOf("_全年"); + if (annualIdx > 0) { + result.setCategory(afterDash.substring(0, annualIdx)); + result.setPeriod("全年"); + } else { + result.setCategory(afterDash); + result.setPeriod(null); + } + } else { + result.setRegion(null); + result.setCategory(nameWithoutExt); + result.setPeriod(null); + } + + return result; + } + + /** 检查文件名是否属于模式预报文件 */ + public static boolean isModelFile(String fileName) { + int dotIdx = fileName.lastIndexOf('.'); + String nameWithoutExt = dotIdx > 0 ? fileName.substring(0, dotIdx) : fileName; + return MODEL_CODE_MAP.containsKey(nameWithoutExt); + } + + /** 根据模式代码获取模式名称 */ + public static String getModelName(String modelCode) { + return MODEL_CODE_MAP.get(modelCode); + } +} diff --git a/system-admin/src/main/java/com/weather/modules/weather/filescan/utils/ParseResult.java b/system-admin/src/main/java/com/weather/modules/weather/filescan/utils/ParseResult.java new file mode 100644 index 0000000..52eec06 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/weather/filescan/utils/ParseResult.java @@ -0,0 +1,21 @@ +package com.weather.modules.weather.filescan.utils; + +import lombok.Data; + +@Data +public class ParseResult { + /** 文件类型: "REGION" 区域文件, "MODEL" 模式预报文件 */ + private String fileType; + /** 区域名(区域文件)或 null(模式文件) */ + private String region; + /** 指标类别(区域文件)或 "模式预报"(模式文件) */ + private String category; + /** "全年" 或 null */ + private String period; + /** 文件扩展名(小写) */ + private String ext; + /** 模式代码,如 2FGrapesCR(仅模式文件) */ + private String modelCode; + /** 模式显示名,如 CMA-MESO(仅模式文件) */ + private String modelName; +} diff --git a/system-admin/src/main/java/com/weather/modules/weather/station/controller/WeatherStationController.java b/system-admin/src/main/java/com/weather/modules/weather/station/controller/WeatherStationController.java new file mode 100644 index 0000000..8f80485 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/weather/station/controller/WeatherStationController.java @@ -0,0 +1,130 @@ +package com.weather.modules.weather.station.controller; + +import cn.hutool.core.bean.BeanUtil; +import com.weather.common.annotation.LogOperation; +import com.weather.common.constant.Constant; +import com.weather.common.page.PageData; +import com.weather.common.utils.ExcelUtils; +import com.weather.common.utils.Result; +import com.weather.common.validator.AssertUtils; +import com.weather.common.validator.ValidatorUtils; +import com.weather.common.validator.group.AddGroup; +import com.weather.common.validator.group.DefaultGroup; +import com.weather.common.validator.group.UpdateGroup; +import com.weather.modules.weather.station.dto.WeatherStationDTO; +import com.weather.modules.weather.station.dto.WeatherStationInfoSimpleDto; +import com.weather.modules.weather.station.entity.WeatherStationEntity; +import com.weather.modules.weather.station.excel.WeatherStationExcel; +import com.weather.modules.weather.station.service.WeatherStationService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import jakarta.servlet.http.HttpServletResponse; + +import java.util.List; +import java.util.Map; + + +/** + * 气象站点表 + * + * @author Mark 123 + * @since 1.0.0 2026-03-05 + */ +@RestController +@RequestMapping("station/weatherstation") +@Tag(name = "站点") +public class WeatherStationController { + + @Resource + private WeatherStationService weatherStationService; + + @GetMapping("page") + @Operation(summary = "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", in = ParameterIn.QUERY, required = true, ref = "int"), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", in = ParameterIn.QUERY, required = true, ref = "int"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段", in = ParameterIn.QUERY, ref = "String"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)", in = ParameterIn.QUERY, ref = "String"), + @Parameter(name = "stationName", description = "区站名称", in = ParameterIn.QUERY, ref = "String"), + @Parameter(name = "stationCode", description = "区站编号", in = ParameterIn.QUERY, ref = "String") + }) + @RequiresPermissions("station:weatherstation:page") + public Result> page(@Parameter(hidden = true) @RequestParam Map params) { + PageData page = weatherStationService.page(params); + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary = "信息") + @RequiresPermissions("station:weatherstation:info") + public Result get(@PathVariable("id") Long id) { + WeatherStationEntity weatherStationEntity = weatherStationService.selectById(id); + WeatherStationDTO data = BeanUtil.copyProperties(weatherStationEntity, WeatherStationDTO.class); + return new Result().ok(data); + } + + @GetMapping("list") + @Operation(summary = "区站列表") + @RequiresPermissions("station:weatherstation:list") + public Result> getList() { + List stations = weatherStationService.getStationByDeptId(); + return new Result>().ok(stations); + } + + + @PostMapping + @Operation(summary = "保存") + @LogOperation("保存") + @RequiresPermissions("station:weatherstation:save") + public Result save(@RequestBody WeatherStationDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + WeatherStationEntity weatherStationEntity = BeanUtil.copyProperties(dto, WeatherStationEntity.class); + weatherStationService.insert(weatherStationEntity); + + return new Result(); + } + + @PutMapping + @Operation(summary = "修改") + @LogOperation("修改") + @RequiresPermissions("station:weatherstation:update") + public Result update(@RequestBody WeatherStationDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + WeatherStationEntity weatherStationEntity = BeanUtil.copyProperties(dto, WeatherStationEntity.class); + weatherStationService.updateById(weatherStationEntity); + return new Result().ok(true); + } + + @DeleteMapping + @Operation(summary = "删除") + @LogOperation("删除") + @RequiresPermissions("station:weatherstation:delete") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + weatherStationService.delete(ids); + + return new Result().ok(true); + } + + @GetMapping("export") + @Operation(summary = "导出") + @LogOperation("导出") + @RequiresPermissions("station:weatherstation:export") + public void export(@Parameter(hidden = true) @RequestParam Map params, HttpServletResponse response) throws Exception { + List list = weatherStationService.list(params); + + ExcelUtils.exportExcelToTarget(response, null, "气象站点表", list, WeatherStationExcel.class); + } + +} diff --git a/system-admin/src/main/java/com/weather/modules/weather/station/dao/WeatherStationDao.java b/system-admin/src/main/java/com/weather/modules/weather/station/dao/WeatherStationDao.java new file mode 100644 index 0000000..ab949d4 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/weather/station/dao/WeatherStationDao.java @@ -0,0 +1,16 @@ +package com.weather.modules.weather.station.dao; + +import com.weather.common.dao.BaseDao; +import com.weather.modules.weather.station.entity.WeatherStationEntity; +import org.apache.ibatis.annotations.Mapper; + +/** + * 气象站点表 + * + * @author Mark 123 + * @since 1.0.0 2026-03-05 + */ +@Mapper +public interface WeatherStationDao extends BaseDao { + +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/weather/station/dto/WeatherStationDTO.java b/system-admin/src/main/java/com/weather/modules/weather/station/dto/WeatherStationDTO.java new file mode 100644 index 0000000..e1e9288 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/weather/station/dto/WeatherStationDTO.java @@ -0,0 +1,65 @@ +package com.weather.modules.weather.station.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.media.SchemaProperty; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +import java.math.BigDecimal; + +/** + * 气象站点表 + * + * @author Mark 123 + * @since 1.0.0 2026-03-05 + */ +@Data +@Schema(name = "气象站点表") +public class WeatherStationDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @SchemaProperty(name = "主键ID") + private Long id; + + @SchemaProperty(name = "气象站号") + private String stationCode; + + @SchemaProperty(name = "站点名称") + private String stationName; + + @SchemaProperty(name = "测站级别") + private Integer stationLevel; + + @SchemaProperty(name = "纬度") + private BigDecimal latitude; + + @SchemaProperty(name = "经度") + private BigDecimal longitude; + + @SchemaProperty(name = "测站海拔高度(米)") + private BigDecimal elevation; + + @SchemaProperty(name = "省名称") + private String provinceName; + + @SchemaProperty(name = "地市名称") + private String cityName; + + @SchemaProperty(name = "区县名称") + private String countyName; + + @SchemaProperty(name = "乡镇名称") + private String townName; + + @SchemaProperty(name = "创建时间") + private Date createDate; + + @SchemaProperty(name = "管理部门ID") + private Long deptId; + + +} diff --git a/system-admin/src/main/java/com/weather/modules/weather/station/dto/WeatherStationInfoSimpleDto.java b/system-admin/src/main/java/com/weather/modules/weather/station/dto/WeatherStationInfoSimpleDto.java new file mode 100644 index 0000000..3e2c20a --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/weather/station/dto/WeatherStationInfoSimpleDto.java @@ -0,0 +1,23 @@ +package com.weather.modules.weather.station.dto; + +import io.swagger.v3.oas.annotations.media.SchemaProperty; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +@Data +public class WeatherStationInfoSimpleDto implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @SchemaProperty(name = "主键ID") + private Long id; + + @SchemaProperty(name = "气象站号") + private String stationCode; + + @SchemaProperty(name = "站点名称") + private String stationName; +} diff --git a/system-admin/src/main/java/com/weather/modules/weather/station/entity/WeatherStationEntity.java b/system-admin/src/main/java/com/weather/modules/weather/station/entity/WeatherStationEntity.java new file mode 100644 index 0000000..6895298 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/weather/station/entity/WeatherStationEntity.java @@ -0,0 +1,88 @@ +package com.weather.modules.weather.station.entity; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.weather.common.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 气象站点表 + * + * @author Mark 123 + * @since 1.0.0 2026-03-05 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@TableName("weather_station") +public class WeatherStationEntity extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键ID + */ + private Long id; + /** + * 气象站号 + */ + private String stationCode; + /** + * 站点名称 + */ + private String stationName; + /** + * 测站级别 + */ + private Integer stationLevel; + /** + * 纬度 + */ + private BigDecimal latitude; + /** + * 经度 + */ + private BigDecimal longitude; + /** + * 测站海拔高度(米) + */ + private BigDecimal elevation; + /** + * 省名称 + */ + private String provinceName; + /** + * 地市名称 + */ + private String cityName; + /** + * 区县名称 + */ + private String countyName; + /** + * 乡镇名称 + */ + private String townName; + + /** + * 管理部门ID(建站时由前端传入) + */ + private Long deptId; + + /** + * 更新者 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private Long updater; + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private Date updateDate; +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/weather/station/excel/WeatherStationExcel.java b/system-admin/src/main/java/com/weather/modules/weather/station/excel/WeatherStationExcel.java new file mode 100644 index 0000000..eb75238 --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/weather/station/excel/WeatherStationExcel.java @@ -0,0 +1,45 @@ +package com.weather.modules.weather.station.excel; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 气象站点表 + * + * @author Mark 123 + * @since 1.0.0 2026-03-05 + */ +@Data +public class WeatherStationExcel { + @ExcelProperty(value = "主键ID") + private Long id; + @ExcelProperty(value = "气象站号") + private String stationCode; + @ExcelProperty(value = "站点名称") + private String stationName; + @ExcelProperty(value = "测站级别") + private Integer stationLevel; + @ExcelProperty(value = "纬度") + private BigDecimal latitude; + @ExcelProperty(value = "经度") + private BigDecimal longitude; + @ExcelProperty(value = "测站海拔高度(米)") + private BigDecimal elevation; + @ExcelProperty(value = "省名称") + private String provinceName; + @ExcelProperty(value = "地市名称") + private String cityName; + @ExcelProperty(value = "区县名称") + private String countyName; + @ExcelProperty(value = "乡镇名称") + private String townName; + @ExcelProperty(value = "创建时间") + private Date createDate; + +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/weather/station/service/WeatherStationService.java b/system-admin/src/main/java/com/weather/modules/weather/station/service/WeatherStationService.java new file mode 100644 index 0000000..977842b --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/weather/station/service/WeatherStationService.java @@ -0,0 +1,31 @@ +package com.weather.modules.weather.station.service; + +import com.weather.common.page.PageData; +import com.weather.common.service.CrudService; +import com.weather.modules.weather.station.dto.WeatherStationDTO; +import com.weather.modules.weather.station.dto.WeatherStationInfoSimpleDto; +import com.weather.modules.weather.station.entity.WeatherStationEntity; + +import java.util.List; +import java.util.Map; + +/** + * 气象站点表 + * + * @author Mark 123 + * @since 1.0.0 2026-03-05 + */ +public interface WeatherStationService extends CrudService { + /** + * 分页查询 站点信息 + * @param params 查询条件 + * @return 站点信息 + */ + PageData page(Map params); + + WeatherStationDTO getByStationCode(Long stationCode); + + List getStationByDeptId(); + + List getStationByStationName(); +} \ No newline at end of file diff --git a/system-admin/src/main/java/com/weather/modules/weather/station/service/impl/WeatherStationServiceImpl.java b/system-admin/src/main/java/com/weather/modules/weather/station/service/impl/WeatherStationServiceImpl.java new file mode 100644 index 0000000..21ee24c --- /dev/null +++ b/system-admin/src/main/java/com/weather/modules/weather/station/service/impl/WeatherStationServiceImpl.java @@ -0,0 +1,75 @@ +package com.weather.modules.weather.station.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.weather.common.constant.Constant; +import com.weather.common.page.PageData; +import com.weather.common.service.impl.CrudServiceImpl; +import com.weather.common.utils.ConvertUtils; +import com.weather.modules.sys.service.SysDeptService; +import com.weather.modules.weather.station.dao.WeatherStationDao; +import com.weather.modules.weather.station.dto.WeatherStationDTO; +import com.weather.modules.weather.station.dto.WeatherStationInfoSimpleDto; +import com.weather.modules.weather.station.entity.WeatherStationEntity; +import com.weather.modules.weather.station.service.WeatherStationService; +import cn.hutool.core.util.StrUtil; +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * 气象站点表 + * + * @author Mark 123 + * @since 1.0.0 2026-03-05 + */ +@Service +@AllArgsConstructor +public class WeatherStationServiceImpl extends CrudServiceImpl implements WeatherStationService { + private final SysDeptService sysDeptService; + @Override + public QueryWrapper getWrapper(Map params) { + String stationName = (String) params.get("stationName"); + String stationCode = (String) params.get("stationCode"); + + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.like(StrUtil.isNotBlank(stationName), "station_name", stationName); + wrapper.like(StrUtil.isNotBlank(stationCode), "station_code", stationCode); + return sysDeptService.limitedQueryScope(wrapper); + } + + @Override + public PageData page(Map params) { + IPage page = baseDao.selectPage( + getPage(params, Constant.CREATE_DATE, false), + getWrapper(params) + ); + + return getPageData(page, WeatherStationDTO.class); + + } + + @Override + public WeatherStationDTO getByStationCode(Long stationCode) { + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.eq("station_code", stationCode); + return ConvertUtils.sourceToTarget(baseDao.selectOne(wrapper),WeatherStationDTO.class); + } + + @Override + public List getStationByDeptId() { + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper = sysDeptService.limitedQueryScope(wrapper); + List entities = baseDao.selectList(wrapper); + return BeanUtil.copyToList(entities, WeatherStationInfoSimpleDto.class); + } + + @Override + public List getStationByStationName() { + return List.of(); + } + +} \ No newline at end of file diff --git a/system-admin/src/main/resources/application-dev.yml b/system-admin/src/main/resources/application-dev.yml new file mode 100644 index 0000000..5ce6347 --- /dev/null +++ b/system-admin/src/main/resources/application-dev.yml @@ -0,0 +1,69 @@ +spring: + datasource: + druid: + #MySQL + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://localhost:3306/weather_data_system?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true + username: root + password: root + #达梦8 +# driver-class-name: dm.jdbc.driver.DmDriver +# url: jdbc:dm://192.168.10.10:5236/renren_security?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true +# username: renren_security +# password: 12345678 + # #Oracle + # driver-class-name: oracle.jdbc.OracleDriver + # url: jdbc:oracle:thin:@192.168.10.10:1521:xe + # username: renren_security + # password: 123456 + # #SQLServer + # driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver + # url: jdbc:sqlserver://localhost:1433;DatabaseName=renren_security + # username: sa + # password: 123456 + # #postgresql + # driver-class-name: org.postgresql.Driver + # url: jdbc:postgresql://192.168.10.10:5432/postgres + # username: postgres + # password: 123456 + initial-size: 10 + max-active: 100 + min-idle: 10 + max-wait: 6000 + pool-prepared-statements: true + max-pool-prepared-statement-per-connection-size: 20 + time-between-eviction-runs-millis: 60000 + min-evictable-idle-time-millis: 300000 + #Oracle需要打开注释 + # validation-query: SELECT 1 FROM DUAL + test-while-idle: true + test-on-borrow: false + test-on-return: false + stat-view-servlet: + enabled: true + url-pattern: /druid/* + #login-username: admin + #login-password: admin + #达梦数据库,需要注释掉,其他数据库可以打开 +# filter: +# stat: +# log-slow-sql: true +# slow-sql-millis: 1000 +# merge-sql: false +# wall: +# config: +# multi-statement-allow: true + +##多数据源的配置,需要引用renren-dynamic-datasource +#dynamic: +# datasource: +# slave1: +# driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver +# url: jdbc:sqlserver://123456:1433;DatabaseName=renren_security +# username: sa +# password: 123456 +# slave2: +# driver-class-name: org.postgresql.Driver +# url: jdbc:postgresql://123456:5432/renren_security +# username: postgres +# password: 123456 diff --git a/system-admin/src/main/resources/application-prod.yml b/system-admin/src/main/resources/application-prod.yml new file mode 100644 index 0000000..9bcf5f6 --- /dev/null +++ b/system-admin/src/main/resources/application-prod.yml @@ -0,0 +1,34 @@ +spring: + datasource: + druid: + #MySQL + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://localhost:3306/weather_data_system?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true + username: weather + password: 123456 + initial-size: 10 + max-active: 100 + min-idle: 10 + max-wait: 60000 + pool-prepared-statements: true + max-pool-prepared-statement-per-connection-size: 20 + time-between-eviction-runs-millis: 60000 + min-evictable-idle-time-millis: 300000 + #Oracle需要打开注释 + #validation-query: SELECT 1 FROM DUAL + test-while-idle: true + test-on-borrow: false + test-on-return: false + stat-view-servlet: + enabled: true + url-pattern: /druid/* + #login-username: admin + #login-password: admin + filter: + stat: + log-slow-sql: true + slow-sql-millis: 1000 + merge-sql: false + wall: + config: + multi-statement-allow: true \ No newline at end of file diff --git a/system-admin/src/main/resources/application-test.yml b/system-admin/src/main/resources/application-test.yml new file mode 100644 index 0000000..9bcf5f6 --- /dev/null +++ b/system-admin/src/main/resources/application-test.yml @@ -0,0 +1,34 @@ +spring: + datasource: + druid: + #MySQL + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://localhost:3306/weather_data_system?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true + username: weather + password: 123456 + initial-size: 10 + max-active: 100 + min-idle: 10 + max-wait: 60000 + pool-prepared-statements: true + max-pool-prepared-statement-per-connection-size: 20 + time-between-eviction-runs-millis: 60000 + min-evictable-idle-time-millis: 300000 + #Oracle需要打开注释 + #validation-query: SELECT 1 FROM DUAL + test-while-idle: true + test-on-borrow: false + test-on-return: false + stat-view-servlet: + enabled: true + url-pattern: /druid/* + #login-username: admin + #login-password: admin + filter: + stat: + log-slow-sql: true + slow-sql-millis: 1000 + merge-sql: false + wall: + config: + multi-statement-allow: true \ No newline at end of file diff --git a/system-admin/src/main/resources/application.yml b/system-admin/src/main/resources/application.yml new file mode 100644 index 0000000..cc42588 --- /dev/null +++ b/system-admin/src/main/resources/application.yml @@ -0,0 +1,80 @@ +# Tomcat +server: + tomcat: + uri-encoding: UTF-8 + threads: + max: 1000 + min-spare: 30 + port: 8080 + servlet: + context-path: /system-admin + session: + cookie: + http-only: true + +knife4j: + enable: false + basic: + enable: false + username: admin + password: admin + setting: + enableFooter: false + +spring: + # 环境 dev|test|prod + profiles: + active: dev + messages: + encoding: UTF-8 + basename: i18n/messages + mvc: + pathmatch: + matching-strategy: ANT_PATH_MATCHER + servlet: + multipart: + max-file-size: 100MB + max-request-size: 100MB + enabled: true + + data: + redis: + database: 0 + host: 192.168.10.10 + port: 6379 + password: # 密码(默认为空) + timeout: 6000ms # 连接超时时长(毫秒) + lettuce: + pool: + max-active: 1000 # 连接池最大连接数(使用负值表示没有限制) + max-wait: -1ms # 连接池最大阻塞等待时间(使用负值表示没有限制) + max-idle: 10 # 连接池中的最大空闲连接 + min-idle: 5 # 连接池中的最小空闲连接 + +# 是否开启redis缓存 true开启 false关闭 +project-options: + redis: + open: false + +#mybatis +mybatis-plus: + mapper-locations: classpath*:/mapper/**/*.xml + #实体扫描,多个package用逗号或者分号分隔 + typeAliasesPackage: com.weather.modules.*.entity + global-config: + #数据库相关配置 + db-config: + #主键类型 + id-type: ASSIGN_ID + banner: false + #原生配置 + configuration: + log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + map-underscore-to-camel-case: true + cache-enabled: false + call-setters-on-nulls: true + jdbc-type-for-null: 'null' + configuration-properties: + prefix: + blobType: BLOB + boolValue: TRUE diff --git a/system-admin/src/main/resources/banner.txt b/system-admin/src/main/resources/banner.txt new file mode 100644 index 0000000..e69de29 diff --git a/system-admin/src/main/resources/images/2FGZ.gif b/system-admin/src/main/resources/images/2FGZ.gif new file mode 100644 index 0000000..8e66066 Binary files /dev/null and b/system-admin/src/main/resources/images/2FGZ.gif differ diff --git a/system-admin/src/main/resources/images/2FGZ_RUC.gif b/system-admin/src/main/resources/images/2FGZ_RUC.gif new file mode 100644 index 0000000..8b21f52 Binary files /dev/null and b/system-admin/src/main/resources/images/2FGZ_RUC.gif differ diff --git a/system-admin/src/main/resources/images/2FGrapesCR.gif b/system-admin/src/main/resources/images/2FGrapesCR.gif new file mode 100644 index 0000000..16faeee Binary files /dev/null and b/system-admin/src/main/resources/images/2FGrapesCR.gif differ diff --git a/system-admin/src/main/resources/images/2FSMB.gif b/system-admin/src/main/resources/images/2FSMB.gif new file mode 100644 index 0000000..f2ab79a Binary files /dev/null and b/system-admin/src/main/resources/images/2FSMB.gif differ diff --git a/system-admin/src/main/resources/images/2FSMB_RUC.gif b/system-admin/src/main/resources/images/2FSMB_RUC.gif new file mode 100644 index 0000000..57e08b8 Binary files /dev/null and b/system-admin/src/main/resources/images/2FSMB_RUC.gif differ diff --git a/system-admin/src/main/resources/images/临武县/Thumbs.db b/system-admin/src/main/resources/images/临武县/Thumbs.db new file mode 100644 index 0000000..2b3d5f6 Binary files /dev/null and b/system-admin/src/main/resources/images/临武县/Thumbs.db differ diff --git a/system-admin/src/main/resources/images/临武县/临武县-日平均气温.png b/system-admin/src/main/resources/images/临武县/临武县-日平均气温.png new file mode 100644 index 0000000..84e8cc4 Binary files /dev/null and b/system-admin/src/main/resources/images/临武县/临武县-日平均气温.png differ diff --git a/system-admin/src/main/resources/images/临武县/临武县-日平均气温_全年.png b/system-admin/src/main/resources/images/临武县/临武县-日平均气温_全年.png new file mode 100644 index 0000000..3cfec97 Binary files /dev/null and b/system-admin/src/main/resources/images/临武县/临武县-日平均气温_全年.png differ diff --git a/system-admin/src/main/resources/images/临武县/临武县-日平均降雨量.png b/system-admin/src/main/resources/images/临武县/临武县-日平均降雨量.png new file mode 100644 index 0000000..bb54fca Binary files /dev/null and b/system-admin/src/main/resources/images/临武县/临武县-日平均降雨量.png differ diff --git a/system-admin/src/main/resources/images/临武县/临武县-日平均降雨量_全年.png b/system-admin/src/main/resources/images/临武县/临武县-日平均降雨量_全年.png new file mode 100644 index 0000000..607c6ab Binary files /dev/null and b/system-admin/src/main/resources/images/临武县/临武县-日平均降雨量_全年.png differ diff --git a/system-admin/src/main/resources/images/临武县/临武县-月平均气温图.png b/system-admin/src/main/resources/images/临武县/临武县-月平均气温图.png new file mode 100644 index 0000000..badb35d Binary files /dev/null and b/system-admin/src/main/resources/images/临武县/临武县-月平均气温图.png differ diff --git a/system-admin/src/main/resources/images/临武县/临武县-月降雨量图.png b/system-admin/src/main/resources/images/临武县/临武县-月降雨量图.png new file mode 100644 index 0000000..94883c3 Binary files /dev/null and b/system-admin/src/main/resources/images/临武县/临武县-月降雨量图.png differ diff --git a/system-admin/src/main/resources/images/临武县/临武县-累计降雨量.png b/system-admin/src/main/resources/images/临武县/临武县-累计降雨量.png new file mode 100644 index 0000000..43d09e4 Binary files /dev/null and b/system-admin/src/main/resources/images/临武县/临武县-累计降雨量.png differ diff --git a/system-admin/src/main/resources/images/临武县/临武县-累计降雨量_全年.png b/system-admin/src/main/resources/images/临武县/临武县-累计降雨量_全年.png new file mode 100644 index 0000000..bcf00e1 Binary files /dev/null and b/system-admin/src/main/resources/images/临武县/临武县-累计降雨量_全年.png differ diff --git a/system-admin/src/main/resources/images/临武县/临武县631信息.txt b/system-admin/src/main/resources/images/临武县/临武县631信息.txt new file mode 100644 index 0000000..a41efe7 --- /dev/null +++ b/system-admin/src/main/resources/images/临武县/临武县631信息.txt @@ -0,0 +1,15 @@ +临武县2026年03月23日14时的降雨信息: +过去01小时,最大小时雨强为0.0毫米,出现在石门,03月23日05-06时; +过去03小时,最大小时雨强为0.0毫米,出现在畔塘,03月23日03-04时; +过去06小时,最大小时雨强为0.2毫米,出现在分水坳,03月23日00-01时; +过去12小时,最大小时雨强为0.2毫米,出现在分水坳,03月23日00-01时; +过去24小时,最大小时雨强为0.2毫米,出现在分水坳,03月23日00-01时; +过去48小时,最大小时雨强为0.2毫米,出现在分水坳,03月23日00-01时; +过去78小时,最大小时雨强为9.9毫米,出现在刘家,03月20日11-12时; +过去01小时,平均降雨量为0.0毫米,最大降雨量为0.0毫米,出现在腊水站;。 +过去03小时,平均降雨量为0.0毫米,最大降雨量为0.0毫米,出现在腊水站;。 +过去06小时,平均降雨量为0.0毫米,最大降雨量为0.3毫米,出现在分水坳站;0-9.9毫米的降雨站点有2站。 +过去12小时,平均降雨量为0.0毫米,最大降雨量为0.4毫米,出现在分水坳站;0-9.9毫米的降雨站点有2站。 +过去24小时,平均降雨量为0.0毫米,最大降雨量为0.4毫米,出现在分水坳站;0-9.9毫米的降雨站点有3站。 +过去48小时,平均降雨量为0.0毫米,最大降雨量为0.4毫米,出现在分水坳站;0-9.9毫米的降雨站点有4站。 +过去78小时,平均降雨量为12.1毫米,最大降雨量为23.4毫米,出现在刘家站;0-9.9毫米的降雨站点有15站、10-24.9毫米的降雨站点有19站。 diff --git a/system-admin/src/main/resources/images/北湖区/Thumbs.db b/system-admin/src/main/resources/images/北湖区/Thumbs.db new file mode 100644 index 0000000..2192a92 Binary files /dev/null and b/system-admin/src/main/resources/images/北湖区/Thumbs.db differ diff --git a/system-admin/src/main/resources/images/北湖区/北湖区-日平均气温.png b/system-admin/src/main/resources/images/北湖区/北湖区-日平均气温.png new file mode 100644 index 0000000..c274f0a Binary files /dev/null and b/system-admin/src/main/resources/images/北湖区/北湖区-日平均气温.png differ diff --git a/system-admin/src/main/resources/images/北湖区/北湖区-日平均气温_全年.png b/system-admin/src/main/resources/images/北湖区/北湖区-日平均气温_全年.png new file mode 100644 index 0000000..5492bad Binary files /dev/null and b/system-admin/src/main/resources/images/北湖区/北湖区-日平均气温_全年.png differ diff --git a/system-admin/src/main/resources/images/北湖区/北湖区-日平均降雨量.png b/system-admin/src/main/resources/images/北湖区/北湖区-日平均降雨量.png new file mode 100644 index 0000000..4ac951e Binary files /dev/null and b/system-admin/src/main/resources/images/北湖区/北湖区-日平均降雨量.png differ diff --git a/system-admin/src/main/resources/images/北湖区/北湖区-日平均降雨量_全年.png b/system-admin/src/main/resources/images/北湖区/北湖区-日平均降雨量_全年.png new file mode 100644 index 0000000..d2bb409 Binary files /dev/null and b/system-admin/src/main/resources/images/北湖区/北湖区-日平均降雨量_全年.png differ diff --git a/system-admin/src/main/resources/images/北湖区/北湖区-月平均气温图.png b/system-admin/src/main/resources/images/北湖区/北湖区-月平均气温图.png new file mode 100644 index 0000000..5567f6e Binary files /dev/null and b/system-admin/src/main/resources/images/北湖区/北湖区-月平均气温图.png differ diff --git a/system-admin/src/main/resources/images/北湖区/北湖区-月降雨量图.png b/system-admin/src/main/resources/images/北湖区/北湖区-月降雨量图.png new file mode 100644 index 0000000..04cb13a Binary files /dev/null and b/system-admin/src/main/resources/images/北湖区/北湖区-月降雨量图.png differ diff --git a/system-admin/src/main/resources/images/北湖区/北湖区-累计降雨量.png b/system-admin/src/main/resources/images/北湖区/北湖区-累计降雨量.png new file mode 100644 index 0000000..de0fabe Binary files /dev/null and b/system-admin/src/main/resources/images/北湖区/北湖区-累计降雨量.png differ diff --git a/system-admin/src/main/resources/images/北湖区/北湖区-累计降雨量_全年.png b/system-admin/src/main/resources/images/北湖区/北湖区-累计降雨量_全年.png new file mode 100644 index 0000000..19042da Binary files /dev/null and b/system-admin/src/main/resources/images/北湖区/北湖区-累计降雨量_全年.png differ diff --git a/system-admin/src/main/resources/images/北湖区/北湖区631信息.txt b/system-admin/src/main/resources/images/北湖区/北湖区631信息.txt new file mode 100644 index 0000000..2107974 --- /dev/null +++ b/system-admin/src/main/resources/images/北湖区/北湖区631信息.txt @@ -0,0 +1,15 @@ +北湖区2026年03月23日14时的降雨信息: +过去01小时,最大小时雨强为999999.0毫米,出现在四亩田,03月23日05-06时; +过去03小时,最大小时雨强为999999.0毫米,出现在四亩田,03月23日03-04时; +过去06小时,最大小时雨强为999999.0毫米,出现在四亩田,03月23日00-01时; +过去12小时,最大小时雨强为999999.0毫米,出现在四亩田,03月22日18-19时; +过去24小时,最大小时雨强为999999.0毫米,出现在四亩田,03月22日06-07时; +过去48小时,最大小时雨强为999999.0毫米,出现在四亩田,03月21日06-07时; +过去78小时,最大小时雨强为999999.0毫米,出现在四亩田,03月20日05-06时; +过去01小时,平均降雨量为43478.2毫米,最大降雨量为999999.0毫米,出现在四亩田站;200毫米以上的降雨站点有1站。 +过去03小时,平均降雨量为130434.7毫米,最大降雨量为2999997.0毫米,出现在四亩田站;200毫米以上的降雨站点有1站。 +过去06小时,平均降雨量为260869.3毫米,最大降雨量为5999994.0毫米,出现在四亩田站;0-9.9毫米的降雨站点有2站、200毫米以上的降雨站点有1站。 +过去12小时,平均降雨量为521738.6毫米,最大降雨量为11999988.0毫米,出现在四亩田站;0-9.9毫米的降雨站点有2站、200毫米以上的降雨站点有1站。 +过去24小时,平均降雨量为1043477.3毫米,最大降雨量为23999976.0毫米,出现在四亩田站;0-9.9毫米的降雨站点有3站、200毫米以上的降雨站点有1站。 +过去48小时,平均降雨量为2086954.5毫米,最大降雨量为47999952.0毫米,出现在四亩田站;0-9.9毫米的降雨站点有4站、200毫米以上的降雨站点有1站。 +过去78小时,平均降雨量为3130442.5毫米,最大降雨量为71999928.0毫米,出现在四亩田站;0-9.9毫米的降雨站点有10站、10-24.9毫米的降雨站点有12站、200毫米以上的降雨站点有1站。 diff --git a/system-admin/src/main/resources/images/嘉禾县/Thumbs.db b/system-admin/src/main/resources/images/嘉禾县/Thumbs.db new file mode 100644 index 0000000..bde444d Binary files /dev/null and b/system-admin/src/main/resources/images/嘉禾县/Thumbs.db differ diff --git a/system-admin/src/main/resources/images/嘉禾县/嘉禾县-日平均气温.png b/system-admin/src/main/resources/images/嘉禾县/嘉禾县-日平均气温.png new file mode 100644 index 0000000..4826094 Binary files /dev/null and b/system-admin/src/main/resources/images/嘉禾县/嘉禾县-日平均气温.png differ diff --git a/system-admin/src/main/resources/images/嘉禾县/嘉禾县-日平均气温_全年.png b/system-admin/src/main/resources/images/嘉禾县/嘉禾县-日平均气温_全年.png new file mode 100644 index 0000000..cfb1527 Binary files /dev/null and b/system-admin/src/main/resources/images/嘉禾县/嘉禾县-日平均气温_全年.png differ diff --git a/system-admin/src/main/resources/images/嘉禾县/嘉禾县-日平均降雨量.png b/system-admin/src/main/resources/images/嘉禾县/嘉禾县-日平均降雨量.png new file mode 100644 index 0000000..bebaf8e Binary files /dev/null and b/system-admin/src/main/resources/images/嘉禾县/嘉禾县-日平均降雨量.png differ diff --git a/system-admin/src/main/resources/images/嘉禾县/嘉禾县-日平均降雨量_全年.png b/system-admin/src/main/resources/images/嘉禾县/嘉禾县-日平均降雨量_全年.png new file mode 100644 index 0000000..f419a15 Binary files /dev/null and b/system-admin/src/main/resources/images/嘉禾县/嘉禾县-日平均降雨量_全年.png differ diff --git a/system-admin/src/main/resources/images/嘉禾县/嘉禾县-月平均气温图.png b/system-admin/src/main/resources/images/嘉禾县/嘉禾县-月平均气温图.png new file mode 100644 index 0000000..a6b92fc Binary files /dev/null and b/system-admin/src/main/resources/images/嘉禾县/嘉禾县-月平均气温图.png differ diff --git a/system-admin/src/main/resources/images/嘉禾县/嘉禾县-月降雨量图.png b/system-admin/src/main/resources/images/嘉禾县/嘉禾县-月降雨量图.png new file mode 100644 index 0000000..53014c4 Binary files /dev/null and b/system-admin/src/main/resources/images/嘉禾县/嘉禾县-月降雨量图.png differ diff --git a/system-admin/src/main/resources/images/嘉禾县/嘉禾县-累计降雨量.png b/system-admin/src/main/resources/images/嘉禾县/嘉禾县-累计降雨量.png new file mode 100644 index 0000000..fb9b607 Binary files /dev/null and b/system-admin/src/main/resources/images/嘉禾县/嘉禾县-累计降雨量.png differ diff --git a/system-admin/src/main/resources/images/嘉禾县/嘉禾县-累计降雨量_全年.png b/system-admin/src/main/resources/images/嘉禾县/嘉禾县-累计降雨量_全年.png new file mode 100644 index 0000000..0f5c80b Binary files /dev/null and b/system-admin/src/main/resources/images/嘉禾县/嘉禾县-累计降雨量_全年.png differ diff --git a/system-admin/src/main/resources/images/嘉禾县/嘉禾县631信息.txt b/system-admin/src/main/resources/images/嘉禾县/嘉禾县631信息.txt new file mode 100644 index 0000000..49d3556 --- /dev/null +++ b/system-admin/src/main/resources/images/嘉禾县/嘉禾县631信息.txt @@ -0,0 +1,15 @@ +嘉禾县2026年03月23日14时的降雨信息: +过去01小时,最大小时雨强为0.1毫米,出现在高宅,03月23日05-06时; +过去03小时,最大小时雨强为0.6毫米,出现在南岭林场,03月23日04-05时; +过去06小时,最大小时雨强为0.6毫米,出现在南岭林场,03月23日04-05时; +过去12小时,最大小时雨强为0.6毫米,出现在南岭林场,03月23日04-05时; +过去24小时,最大小时雨强为0.6毫米,出现在南岭林场,03月23日04-05时; +过去48小时,最大小时雨强为0.6毫米,出现在南岭林场,03月23日04-05时; +过去78小时,最大小时雨强为6.5毫米,出现在茶坞,03月20日10-11时; +过去01小时,平均降雨量为0.0毫米,最大降雨量为0.1毫米,出现在高宅站;0-9.9毫米的降雨站点有2站。 +过去03小时,平均降雨量为0.1毫米,最大降雨量为0.6毫米,出现在南岭林场站;0-9.9毫米的降雨站点有9站。 +过去06小时,平均降雨量为0.1毫米,最大降雨量为0.6毫米,出现在南岭林场站;0-9.9毫米的降雨站点有9站。 +过去12小时,平均降雨量为0.1毫米,最大降雨量为0.6毫米,出现在南岭林场站;0-9.9毫米的降雨站点有9站。 +过去24小时,平均降雨量为0.1毫米,最大降雨量为0.6毫米,出现在南岭林场站;0-9.9毫米的降雨站点有10站。 +过去48小时,平均降雨量为0.1毫米,最大降雨量为0.6毫米,出现在南岭林场站;0-9.9毫米的降雨站点有12站。 +过去78小时,平均降雨量为7.9毫米,最大降雨量为12.9毫米,出现在茶坞站;0-9.9毫米的降雨站点有16站、10-24.9毫米的降雨站点有12站。 diff --git a/system-admin/src/main/resources/images/安仁县/Thumbs.db b/system-admin/src/main/resources/images/安仁县/Thumbs.db new file mode 100644 index 0000000..d21b22b Binary files /dev/null and b/system-admin/src/main/resources/images/安仁县/Thumbs.db differ diff --git a/system-admin/src/main/resources/images/安仁县/安仁县-日平均气温.png b/system-admin/src/main/resources/images/安仁县/安仁县-日平均气温.png new file mode 100644 index 0000000..57ba485 Binary files /dev/null and b/system-admin/src/main/resources/images/安仁县/安仁县-日平均气温.png differ diff --git a/system-admin/src/main/resources/images/安仁县/安仁县-日平均气温_全年.png b/system-admin/src/main/resources/images/安仁县/安仁县-日平均气温_全年.png new file mode 100644 index 0000000..27c9809 Binary files /dev/null and b/system-admin/src/main/resources/images/安仁县/安仁县-日平均气温_全年.png differ diff --git a/system-admin/src/main/resources/images/安仁县/安仁县-日平均降雨量.png b/system-admin/src/main/resources/images/安仁县/安仁县-日平均降雨量.png new file mode 100644 index 0000000..67f3406 Binary files /dev/null and b/system-admin/src/main/resources/images/安仁县/安仁县-日平均降雨量.png differ diff --git a/system-admin/src/main/resources/images/安仁县/安仁县-日平均降雨量_全年.png b/system-admin/src/main/resources/images/安仁县/安仁县-日平均降雨量_全年.png new file mode 100644 index 0000000..3928d68 Binary files /dev/null and b/system-admin/src/main/resources/images/安仁县/安仁县-日平均降雨量_全年.png differ diff --git a/system-admin/src/main/resources/images/安仁县/安仁县-月平均气温图.png b/system-admin/src/main/resources/images/安仁县/安仁县-月平均气温图.png new file mode 100644 index 0000000..adaebe3 Binary files /dev/null and b/system-admin/src/main/resources/images/安仁县/安仁县-月平均气温图.png differ diff --git a/system-admin/src/main/resources/images/安仁县/安仁县-月降雨量图.png b/system-admin/src/main/resources/images/安仁县/安仁县-月降雨量图.png new file mode 100644 index 0000000..bbef8ea Binary files /dev/null and b/system-admin/src/main/resources/images/安仁县/安仁县-月降雨量图.png differ diff --git a/system-admin/src/main/resources/images/安仁县/安仁县-累计降雨量.png b/system-admin/src/main/resources/images/安仁县/安仁县-累计降雨量.png new file mode 100644 index 0000000..1b594e8 Binary files /dev/null and b/system-admin/src/main/resources/images/安仁县/安仁县-累计降雨量.png differ diff --git a/system-admin/src/main/resources/images/安仁县/安仁县-累计降雨量_全年.png b/system-admin/src/main/resources/images/安仁县/安仁县-累计降雨量_全年.png new file mode 100644 index 0000000..b34f80d Binary files /dev/null and b/system-admin/src/main/resources/images/安仁县/安仁县-累计降雨量_全年.png differ diff --git a/system-admin/src/main/resources/images/安仁县/安仁县631信息.txt b/system-admin/src/main/resources/images/安仁县/安仁县631信息.txt new file mode 100644 index 0000000..a14a3ca --- /dev/null +++ b/system-admin/src/main/resources/images/安仁县/安仁县631信息.txt @@ -0,0 +1,15 @@ +安仁县2026年03月23日14时的降雨信息: +过去01小时,最大小时雨强为4.7毫米,出现在渡口坪口,03月23日05-06时; +过去03小时,最大小时雨强为4.7毫米,出现在渡口坪口,03月23日05-06时; +过去06小时,最大小时雨强为4.7毫米,出现在渡口坪口,03月23日05-06时; +过去12小时,最大小时雨强为4.7毫米,出现在渡口坪口,03月23日05-06时; +过去24小时,最大小时雨强为4.7毫米,出现在渡口坪口,03月23日05-06时; +过去48小时,最大小时雨强为4.7毫米,出现在渡口坪口,03月23日05-06时; +过去78小时,最大小时雨强为4.7毫米,出现在渡口坪口,03月23日05-06时; +过去01小时,平均降雨量为0.3毫米,最大降雨量为4.7毫米,出现在渡口坪口站;0-9.9毫米的降雨站点有6站。 +过去03小时,平均降雨量为0.4毫米,最大降雨量为5.9毫米,出现在渡口坪口站;0-9.9毫米的降雨站点有7站。 +过去06小时,平均降雨量为0.4毫米,最大降雨量为5.9毫米,出现在渡口坪口站;0-9.9毫米的降雨站点有9站。 +过去12小时,平均降雨量为0.4毫米,最大降雨量为5.9毫米,出现在渡口坪口站;0-9.9毫米的降雨站点有15站。 +过去24小时,平均降雨量为1.1毫米,最大降雨量为6.2毫米,出现在渡口坪口站;0-9.9毫米的降雨站点有39站。 +过去48小时,平均降雨量为2.3毫米,最大降雨量为10.1毫米,出现在渡口坪口站;0-9.9毫米的降雨站点有41站、10-24.9毫米的降雨站点有2站。 +过去78小时,平均降雨量为8.4毫米,最大降雨量为22.1毫米,出现在熊峰山站;0-9.9毫米的降雨站点有29站、10-24.9毫米的降雨站点有12站。 diff --git a/system-admin/src/main/resources/images/宜章县/宜章县-日平均气温.png b/system-admin/src/main/resources/images/宜章县/宜章县-日平均气温.png new file mode 100644 index 0000000..8f29e6f Binary files /dev/null and b/system-admin/src/main/resources/images/宜章县/宜章县-日平均气温.png differ diff --git a/system-admin/src/main/resources/images/宜章县/宜章县-日平均气温_全年.png b/system-admin/src/main/resources/images/宜章县/宜章县-日平均气温_全年.png new file mode 100644 index 0000000..52fd0f2 Binary files /dev/null and b/system-admin/src/main/resources/images/宜章县/宜章县-日平均气温_全年.png differ diff --git a/system-admin/src/main/resources/images/宜章县/宜章县-日平均降雨量.png b/system-admin/src/main/resources/images/宜章县/宜章县-日平均降雨量.png new file mode 100644 index 0000000..4ecabec Binary files /dev/null and b/system-admin/src/main/resources/images/宜章县/宜章县-日平均降雨量.png differ diff --git a/system-admin/src/main/resources/images/宜章县/宜章县-日平均降雨量_全年.png b/system-admin/src/main/resources/images/宜章县/宜章县-日平均降雨量_全年.png new file mode 100644 index 0000000..2d6ab57 Binary files /dev/null and b/system-admin/src/main/resources/images/宜章县/宜章县-日平均降雨量_全年.png differ diff --git a/system-admin/src/main/resources/images/宜章县/宜章县-月平均气温图.png b/system-admin/src/main/resources/images/宜章县/宜章县-月平均气温图.png new file mode 100644 index 0000000..aaa7012 Binary files /dev/null and b/system-admin/src/main/resources/images/宜章县/宜章县-月平均气温图.png differ diff --git a/system-admin/src/main/resources/images/宜章县/宜章县-月降雨量图.png b/system-admin/src/main/resources/images/宜章县/宜章县-月降雨量图.png new file mode 100644 index 0000000..5646ac9 Binary files /dev/null and b/system-admin/src/main/resources/images/宜章县/宜章县-月降雨量图.png differ diff --git a/system-admin/src/main/resources/images/宜章县/宜章县-累计降雨量.png b/system-admin/src/main/resources/images/宜章县/宜章县-累计降雨量.png new file mode 100644 index 0000000..2c0a3d3 Binary files /dev/null and b/system-admin/src/main/resources/images/宜章县/宜章县-累计降雨量.png differ diff --git a/system-admin/src/main/resources/images/宜章县/宜章县-累计降雨量_全年.png b/system-admin/src/main/resources/images/宜章县/宜章县-累计降雨量_全年.png new file mode 100644 index 0000000..d528271 Binary files /dev/null and b/system-admin/src/main/resources/images/宜章县/宜章县-累计降雨量_全年.png differ diff --git a/system-admin/src/main/resources/images/宜章县/宜章县631信息.txt b/system-admin/src/main/resources/images/宜章县/宜章县631信息.txt new file mode 100644 index 0000000..c6ff021 --- /dev/null +++ b/system-admin/src/main/resources/images/宜章县/宜章县631信息.txt @@ -0,0 +1,15 @@ +宜章县2026年03月23日14时的降雨信息: +过去01小时,最大小时雨强为0.0毫米,出现在岩泉,03月23日05-06时; +过去03小时,最大小时雨强为0.0毫米,出现在宜章,03月23日03-04时; +过去06小时,最大小时雨强为0.1毫米,出现在黄岑水库,03月23日00-01时; +过去12小时,最大小时雨强为0.2毫米,出现在林子坪水库,03月22日22-23时; +过去24小时,最大小时雨强为0.2毫米,出现在林子坪水库,03月22日22-23时; +过去48小时,最大小时雨强为0.8毫米,出现在林子坪水库,03月21日20-21时; +过去78小时,最大小时雨强为10.4毫米,出现在关溪,03月20日09-10时; +过去01小时,平均降雨量为0.0毫米,最大降雨量为0.0毫米,出现在岩泉站;。 +过去03小时,平均降雨量为0.0毫米,最大降雨量为0.0毫米,出现在岩泉站;。 +过去06小时,平均降雨量为0.0毫米,最大降雨量为0.1毫米,出现在宜章一中站;0-9.9毫米的降雨站点有3站。 +过去12小时,平均降雨量为0.0毫米,最大降雨量为0.3毫米,出现在林子坪水库站;0-9.9毫米的降雨站点有3站。 +过去24小时,平均降雨量为0.0毫米,最大降雨量为0.4毫米,出现在林子坪水库站;0-9.9毫米的降雨站点有3站。 +过去48小时,平均降雨量为0.1毫米,最大降雨量为1.4毫米,出现在林子坪水库站;0-9.9毫米的降雨站点有4站。 +过去78小时,平均降雨量为15.5毫米,最大降雨量为24.1毫米,出现在一六站;0-9.9毫米的降雨站点有9站、10-24.9毫米的降雨站点有21站。 diff --git a/system-admin/src/main/resources/images/桂东县/Thumbs.db b/system-admin/src/main/resources/images/桂东县/Thumbs.db new file mode 100644 index 0000000..e667cdc Binary files /dev/null and b/system-admin/src/main/resources/images/桂东县/Thumbs.db differ diff --git a/system-admin/src/main/resources/images/桂东县/桂东县-日平均气温.png b/system-admin/src/main/resources/images/桂东县/桂东县-日平均气温.png new file mode 100644 index 0000000..a0e7292 Binary files /dev/null and b/system-admin/src/main/resources/images/桂东县/桂东县-日平均气温.png differ diff --git a/system-admin/src/main/resources/images/桂东县/桂东县-日平均气温_全年.png b/system-admin/src/main/resources/images/桂东县/桂东县-日平均气温_全年.png new file mode 100644 index 0000000..76c265d Binary files /dev/null and b/system-admin/src/main/resources/images/桂东县/桂东县-日平均气温_全年.png differ diff --git a/system-admin/src/main/resources/images/桂东县/桂东县-日平均降雨量.png b/system-admin/src/main/resources/images/桂东县/桂东县-日平均降雨量.png new file mode 100644 index 0000000..024238c Binary files /dev/null and b/system-admin/src/main/resources/images/桂东县/桂东县-日平均降雨量.png differ diff --git a/system-admin/src/main/resources/images/桂东县/桂东县-日平均降雨量_全年.png b/system-admin/src/main/resources/images/桂东县/桂东县-日平均降雨量_全年.png new file mode 100644 index 0000000..ba613e3 Binary files /dev/null and b/system-admin/src/main/resources/images/桂东县/桂东县-日平均降雨量_全年.png differ diff --git a/system-admin/src/main/resources/images/桂东县/桂东县-月平均气温图.png b/system-admin/src/main/resources/images/桂东县/桂东县-月平均气温图.png new file mode 100644 index 0000000..04e484b Binary files /dev/null and b/system-admin/src/main/resources/images/桂东县/桂东县-月平均气温图.png differ diff --git a/system-admin/src/main/resources/images/桂东县/桂东县-月降雨量图.png b/system-admin/src/main/resources/images/桂东县/桂东县-月降雨量图.png new file mode 100644 index 0000000..758a4e9 Binary files /dev/null and b/system-admin/src/main/resources/images/桂东县/桂东县-月降雨量图.png differ diff --git a/system-admin/src/main/resources/images/桂东县/桂东县-累计降雨量.png b/system-admin/src/main/resources/images/桂东县/桂东县-累计降雨量.png new file mode 100644 index 0000000..315895e Binary files /dev/null and b/system-admin/src/main/resources/images/桂东县/桂东县-累计降雨量.png differ diff --git a/system-admin/src/main/resources/images/桂东县/桂东县-累计降雨量_全年.png b/system-admin/src/main/resources/images/桂东县/桂东县-累计降雨量_全年.png new file mode 100644 index 0000000..34e7df1 Binary files /dev/null and b/system-admin/src/main/resources/images/桂东县/桂东县-累计降雨量_全年.png differ diff --git a/system-admin/src/main/resources/images/桂东县/桂东县631信息.txt b/system-admin/src/main/resources/images/桂东县/桂东县631信息.txt new file mode 100644 index 0000000..1bddb82 --- /dev/null +++ b/system-admin/src/main/resources/images/桂东县/桂东县631信息.txt @@ -0,0 +1,15 @@ +桂东县2026年03月23日14时的降雨信息: +过去01小时,最大小时雨强为1.0毫米,出现在四都,03月23日05-06时; +过去03小时,最大小时雨强为1.0毫米,出现在四都,03月23日05-06时; +过去06小时,最大小时雨强为1.0毫米,出现在四都,03月23日05-06时; +过去12小时,最大小时雨强为1.0毫米,出现在四都,03月23日05-06时; +过去24小时,最大小时雨强为1.0毫米,出现在四都,03月23日05-06时; +过去48小时,最大小时雨强为1.0毫米,出现在四都,03月23日05-06时; +过去78小时,最大小时雨强为7.0毫米,出现在尚义,03月20日05-06时; +过去01小时,平均降雨量为0.1毫米,最大降雨量为1.0毫米,出现在四都站;0-9.9毫米的降雨站点有8站。 +过去03小时,平均降雨量为0.1毫米,最大降雨量为1.0毫米,出现在四都站;0-9.9毫米的降雨站点有8站。 +过去06小时,平均降雨量为0.1毫米,最大降雨量为1.0毫米,出现在四都站;0-9.9毫米的降雨站点有9站。 +过去12小时,平均降雨量为0.1毫米,最大降雨量为1.1毫米,出现在四都站;0-9.9毫米的降雨站点有10站。 +过去24小时,平均降雨量为0.2毫米,最大降雨量为1.4毫米,出现在四都站;0-9.9毫米的降雨站点有15站。 +过去48小时,平均降雨量为0.3毫米,最大降雨量为1.7毫米,出现在八面山站;0-9.9毫米的降雨站点有18站。 +过去78小时,平均降雨量为11.3毫米,最大降雨量为17.4毫米,出现在侠头站;0-9.9毫米的降雨站点有12站、10-24.9毫米的降雨站点有20站。 diff --git a/system-admin/src/main/resources/images/桂阳县/Thumbs.db b/system-admin/src/main/resources/images/桂阳县/Thumbs.db new file mode 100644 index 0000000..f5ba21e Binary files /dev/null and b/system-admin/src/main/resources/images/桂阳县/Thumbs.db differ diff --git a/system-admin/src/main/resources/images/桂阳县/桂阳县-日平均气温.png b/system-admin/src/main/resources/images/桂阳县/桂阳县-日平均气温.png new file mode 100644 index 0000000..3065e0b Binary files /dev/null and b/system-admin/src/main/resources/images/桂阳县/桂阳县-日平均气温.png differ diff --git a/system-admin/src/main/resources/images/桂阳县/桂阳县-日平均气温_全年.png b/system-admin/src/main/resources/images/桂阳县/桂阳县-日平均气温_全年.png new file mode 100644 index 0000000..ca98c89 Binary files /dev/null and b/system-admin/src/main/resources/images/桂阳县/桂阳县-日平均气温_全年.png differ diff --git a/system-admin/src/main/resources/images/桂阳县/桂阳县-日平均降雨量.png b/system-admin/src/main/resources/images/桂阳县/桂阳县-日平均降雨量.png new file mode 100644 index 0000000..0057424 Binary files /dev/null and b/system-admin/src/main/resources/images/桂阳县/桂阳县-日平均降雨量.png differ diff --git a/system-admin/src/main/resources/images/桂阳县/桂阳县-日平均降雨量_全年.png b/system-admin/src/main/resources/images/桂阳县/桂阳县-日平均降雨量_全年.png new file mode 100644 index 0000000..adc609b Binary files /dev/null and b/system-admin/src/main/resources/images/桂阳县/桂阳县-日平均降雨量_全年.png differ diff --git a/system-admin/src/main/resources/images/桂阳县/桂阳县-月平均气温图.png b/system-admin/src/main/resources/images/桂阳县/桂阳县-月平均气温图.png new file mode 100644 index 0000000..b50bd73 Binary files /dev/null and b/system-admin/src/main/resources/images/桂阳县/桂阳县-月平均气温图.png differ diff --git a/system-admin/src/main/resources/images/桂阳县/桂阳县-月降雨量图.png b/system-admin/src/main/resources/images/桂阳县/桂阳县-月降雨量图.png new file mode 100644 index 0000000..9021969 Binary files /dev/null and b/system-admin/src/main/resources/images/桂阳县/桂阳县-月降雨量图.png differ diff --git a/system-admin/src/main/resources/images/桂阳县/桂阳县-累计降雨量.png b/system-admin/src/main/resources/images/桂阳县/桂阳县-累计降雨量.png new file mode 100644 index 0000000..57da99e Binary files /dev/null and b/system-admin/src/main/resources/images/桂阳县/桂阳县-累计降雨量.png differ diff --git a/system-admin/src/main/resources/images/桂阳县/桂阳县-累计降雨量_全年.png b/system-admin/src/main/resources/images/桂阳县/桂阳县-累计降雨量_全年.png new file mode 100644 index 0000000..39ef42d Binary files /dev/null and b/system-admin/src/main/resources/images/桂阳县/桂阳县-累计降雨量_全年.png differ diff --git a/system-admin/src/main/resources/images/桂阳县/桂阳县631信息.txt b/system-admin/src/main/resources/images/桂阳县/桂阳县631信息.txt new file mode 100644 index 0000000..5a2bdd3 --- /dev/null +++ b/system-admin/src/main/resources/images/桂阳县/桂阳县631信息.txt @@ -0,0 +1,15 @@ +桂阳县2026年03月23日14时的降雨信息: +过去01小时,最大小时雨强为0.3毫米,出现在洋市,03月23日05-06时; +过去03小时,最大小时雨强为0.3毫米,出现在洋市,03月23日05-06时; +过去06小时,最大小时雨强为0.3毫米,出现在洋市,03月23日05-06时; +过去12小时,最大小时雨强为0.3毫米,出现在洋市,03月23日05-06时; +过去24小时,最大小时雨强为0.4毫米,出现在杨柳,03月22日11-12时; +过去48小时,最大小时雨强为0.4毫米,出现在杨柳,03月22日11-12时; +过去78小时,最大小时雨强为7.9毫米,出现在全义,03月20日10-11时; +过去01小时,平均降雨量为0.0毫米,最大降雨量为0.3毫米,出现在洋市站;0-9.9毫米的降雨站点有3站。 +过去03小时,平均降雨量为0.0毫米,最大降雨量为0.3毫米,出现在洋市站;0-9.9毫米的降雨站点有7站。 +过去06小时,平均降雨量为0.0毫米,最大降雨量为0.3毫米,出现在洋市站;0-9.9毫米的降雨站点有8站。 +过去12小时,平均降雨量为0.0毫米,最大降雨量为0.3毫米,出现在洋市站;0-9.9毫米的降雨站点有11站。 +过去24小时,平均降雨量为0.0毫米,最大降雨量为0.5毫米,出现在天塘山站;0-9.9毫米的降雨站点有13站。 +过去48小时,平均降雨量为0.0毫米,最大降雨量为0.5毫米,出现在天塘山站;0-9.9毫米的降雨站点有17站。 +过去78小时,平均降雨量为9.4毫米,最大降雨量为18.2毫米,出现在全义站;0-9.9毫米的降雨站点有39站、10-24.9毫米的降雨站点有24站。 diff --git a/system-admin/src/main/resources/images/永兴县/永兴县-日平均气温.png b/system-admin/src/main/resources/images/永兴县/永兴县-日平均气温.png new file mode 100644 index 0000000..cbf9f49 Binary files /dev/null and b/system-admin/src/main/resources/images/永兴县/永兴县-日平均气温.png differ diff --git a/system-admin/src/main/resources/images/永兴县/永兴县-日平均气温_全年.png b/system-admin/src/main/resources/images/永兴县/永兴县-日平均气温_全年.png new file mode 100644 index 0000000..bf6b619 Binary files /dev/null and b/system-admin/src/main/resources/images/永兴县/永兴县-日平均气温_全年.png differ diff --git a/system-admin/src/main/resources/images/永兴县/永兴县-日平均降雨量.png b/system-admin/src/main/resources/images/永兴县/永兴县-日平均降雨量.png new file mode 100644 index 0000000..0fdac1a Binary files /dev/null and b/system-admin/src/main/resources/images/永兴县/永兴县-日平均降雨量.png differ diff --git a/system-admin/src/main/resources/images/永兴县/永兴县-日平均降雨量_全年.png b/system-admin/src/main/resources/images/永兴县/永兴县-日平均降雨量_全年.png new file mode 100644 index 0000000..346eb57 Binary files /dev/null and b/system-admin/src/main/resources/images/永兴县/永兴县-日平均降雨量_全年.png differ diff --git a/system-admin/src/main/resources/images/永兴县/永兴县-月平均气温图.png b/system-admin/src/main/resources/images/永兴县/永兴县-月平均气温图.png new file mode 100644 index 0000000..91eeb05 Binary files /dev/null and b/system-admin/src/main/resources/images/永兴县/永兴县-月平均气温图.png differ diff --git a/system-admin/src/main/resources/images/永兴县/永兴县-月降雨量图.png b/system-admin/src/main/resources/images/永兴县/永兴县-月降雨量图.png new file mode 100644 index 0000000..d88e70d Binary files /dev/null and b/system-admin/src/main/resources/images/永兴县/永兴县-月降雨量图.png differ diff --git a/system-admin/src/main/resources/images/永兴县/永兴县-累计降雨量.png b/system-admin/src/main/resources/images/永兴县/永兴县-累计降雨量.png new file mode 100644 index 0000000..c96c9a8 Binary files /dev/null and b/system-admin/src/main/resources/images/永兴县/永兴县-累计降雨量.png differ diff --git a/system-admin/src/main/resources/images/永兴县/永兴县-累计降雨量_全年.png b/system-admin/src/main/resources/images/永兴县/永兴县-累计降雨量_全年.png new file mode 100644 index 0000000..5f7d2c8 Binary files /dev/null and b/system-admin/src/main/resources/images/永兴县/永兴县-累计降雨量_全年.png differ diff --git a/system-admin/src/main/resources/images/永兴县/永兴县631信息.txt b/system-admin/src/main/resources/images/永兴县/永兴县631信息.txt new file mode 100644 index 0000000..8e4d228 --- /dev/null +++ b/system-admin/src/main/resources/images/永兴县/永兴县631信息.txt @@ -0,0 +1,15 @@ +永兴县2026年03月23日14时的降雨信息: +过去01小时,最大小时雨强为0.0毫米,出现在洞洪,03月23日05-06时; +过去03小时,最大小时雨强为0.1毫米,出现在太和,03月23日03-04时; +过去06小时,最大小时雨强为0.2毫米,出现在胜利,03月23日02-03时; +过去12小时,最大小时雨强为0.2毫米,出现在胜利,03月23日02-03时; +过去24小时,最大小时雨强为0.2毫米,出现在胜利,03月23日02-03时; +过去48小时,最大小时雨强为0.7毫米,出现在江头,03月21日22-23时; +过去78小时,最大小时雨强为7.7毫米,出现在湘永,03月20日05-06时; +过去01小时,平均降雨量为0.0毫米,最大降雨量为0.0毫米,出现在石壁垄水库站;。 +过去03小时,平均降雨量为0.0毫米,最大降雨量为0.1毫米,出现在复合站;0-9.9毫米的降雨站点有4站。 +过去06小时,平均降雨量为0.0毫米,最大降雨量为0.2毫米,出现在胜利站;0-9.9毫米的降雨站点有7站。 +过去12小时,平均降雨量为0.0毫米,最大降雨量为0.2毫米,出现在胜利站;0-9.9毫米的降雨站点有8站。 +过去24小时,平均降雨量为0.0毫米,最大降雨量为0.4毫米,出现在复合站;0-9.9毫米的降雨站点有11站。 +过去48小时,平均降雨量为0.1毫米,最大降雨量为1.1毫米,出现在复合站;0-9.9毫米的降雨站点有16站。 +过去78小时,平均降雨量为8.8毫米,最大降雨量为17.9毫米,出现在湘永站;0-9.9毫米的降雨站点有25站、10-24.9毫米的降雨站点有19站。 diff --git a/system-admin/src/main/resources/images/汝城县/汝城县-日平均气温.png b/system-admin/src/main/resources/images/汝城县/汝城县-日平均气温.png new file mode 100644 index 0000000..7548e66 Binary files /dev/null and b/system-admin/src/main/resources/images/汝城县/汝城县-日平均气温.png differ diff --git a/system-admin/src/main/resources/images/汝城县/汝城县-日平均气温_全年.png b/system-admin/src/main/resources/images/汝城县/汝城县-日平均气温_全年.png new file mode 100644 index 0000000..706eefd Binary files /dev/null and b/system-admin/src/main/resources/images/汝城县/汝城县-日平均气温_全年.png differ diff --git a/system-admin/src/main/resources/images/汝城县/汝城县-日平均降雨量.png b/system-admin/src/main/resources/images/汝城县/汝城县-日平均降雨量.png new file mode 100644 index 0000000..f320980 Binary files /dev/null and b/system-admin/src/main/resources/images/汝城县/汝城县-日平均降雨量.png differ diff --git a/system-admin/src/main/resources/images/汝城县/汝城县-日平均降雨量_全年.png b/system-admin/src/main/resources/images/汝城县/汝城县-日平均降雨量_全年.png new file mode 100644 index 0000000..4aab6e4 Binary files /dev/null and b/system-admin/src/main/resources/images/汝城县/汝城县-日平均降雨量_全年.png differ diff --git a/system-admin/src/main/resources/images/汝城县/汝城县-月平均气温图.png b/system-admin/src/main/resources/images/汝城县/汝城县-月平均气温图.png new file mode 100644 index 0000000..0ee236b Binary files /dev/null and b/system-admin/src/main/resources/images/汝城县/汝城县-月平均气温图.png differ diff --git a/system-admin/src/main/resources/images/汝城县/汝城县-月降雨量图.png b/system-admin/src/main/resources/images/汝城县/汝城县-月降雨量图.png new file mode 100644 index 0000000..ff54324 Binary files /dev/null and b/system-admin/src/main/resources/images/汝城县/汝城县-月降雨量图.png differ diff --git a/system-admin/src/main/resources/images/汝城县/汝城县-累计降雨量.png b/system-admin/src/main/resources/images/汝城县/汝城县-累计降雨量.png new file mode 100644 index 0000000..c90c8ae Binary files /dev/null and b/system-admin/src/main/resources/images/汝城县/汝城县-累计降雨量.png differ diff --git a/system-admin/src/main/resources/images/汝城县/汝城县-累计降雨量_全年.png b/system-admin/src/main/resources/images/汝城县/汝城县-累计降雨量_全年.png new file mode 100644 index 0000000..0eb1ff8 Binary files /dev/null and b/system-admin/src/main/resources/images/汝城县/汝城县-累计降雨量_全年.png differ diff --git a/system-admin/src/main/resources/images/汝城县/汝城县631信息.txt b/system-admin/src/main/resources/images/汝城县/汝城县631信息.txt new file mode 100644 index 0000000..918997b --- /dev/null +++ b/system-admin/src/main/resources/images/汝城县/汝城县631信息.txt @@ -0,0 +1,15 @@ +汝城县2026年03月23日14时的降雨信息: +过去01小时,最大小时雨强为0.0毫米,出现在文泉,03月23日05-06时; +过去03小时,最大小时雨强为0.0毫米,出现在汝城,03月23日03-04时; +过去06小时,最大小时雨强为0.1毫米,出现在坪湾,03月23日00-01时; +过去12小时,最大小时雨强为0.1毫米,出现在小垣,03月22日20-21时; +过去24小时,最大小时雨强为0.1毫米,出现在小垣,03月22日20-21时; +过去48小时,最大小时雨强为0.1毫米,出现在文化广场,03月21日15-16时; +过去78小时,最大小时雨强为11.0毫米,出现在满天星电站,03月20日12-13时; +过去01小时,平均降雨量为0.0毫米,最大降雨量为0.0毫米,出现在文明水库站;。 +过去03小时,平均降雨量为0.0毫米,最大降雨量为0.0毫米,出现在文明水库站;。 +过去06小时,平均降雨量为0.0毫米,最大降雨量为0.1毫米,出现在坪湾站;0-9.9毫米的降雨站点有3站。 +过去12小时,平均降雨量为0.0毫米,最大降雨量为0.2毫米,出现在小垣站;0-9.9毫米的降雨站点有4站。 +过去24小时,平均降雨量为0.0毫米,最大降雨量为0.2毫米,出现在小垣站;0-9.9毫米的降雨站点有4站。 +过去48小时,平均降雨量为0.0毫米,最大降雨量为0.2毫米,出现在坪湾站;0-9.9毫米的降雨站点有6站。 +过去78小时,平均降雨量为13.8毫米,最大降雨量为17.7毫米,出现在满天星电站站;0-9.9毫米的降雨站点有2站、10-24.9毫米的降雨站点有30站。 diff --git a/system-admin/src/main/resources/images/苏仙区/苏仙区631信息.txt b/system-admin/src/main/resources/images/苏仙区/苏仙区631信息.txt new file mode 100644 index 0000000..02e036e --- /dev/null +++ b/system-admin/src/main/resources/images/苏仙区/苏仙区631信息.txt @@ -0,0 +1,15 @@ +苏仙区2026年03月23日14时的降雨信息: +过去01小时,最大小时雨强为0.0毫米,出现在许家洞,03月23日05-06时; +过去03小时,最大小时雨强为0.1毫米,出现在小溪,03月23日04-05时; +过去06小时,最大小时雨强为0.1毫米,出现在廖家湾,03月23日01-02时; +过去12小时,最大小时雨强为0.3毫米,出现在东边岭,03月22日19-20时; +过去24小时,最大小时雨强为0.5毫米,出现在东边岭,03月22日14-15时; +过去48小时,最大小时雨强为0.5毫米,出现在东边岭,03月22日14-15时; +过去78小时,最大小时雨强为6.5毫米,出现在联星,03月20日05-06时; +过去01小时,平均降雨量为0.0毫米,最大降雨量为0.0毫米,出现在观山洞站;。 +过去03小时,平均降雨量为0.0毫米,最大降雨量为0.1毫米,出现在小溪站;0-9.9毫米的降雨站点有1站。 +过去06小时,平均降雨量为0.0毫米,最大降雨量为0.1毫米,出现在廖家湾站;0-9.9毫米的降雨站点有2站。 +过去12小时,平均降雨量为0.0毫米,最大降雨量为0.3毫米,出现在东边岭站;0-9.9毫米的降雨站点有4站。 +过去24小时,平均降雨量为0.0毫米,最大降雨量为0.8毫米,出现在东边岭站;0-9.9毫米的降雨站点有5站。 +过去48小时,平均降雨量为0.0毫米,最大降雨量为0.8毫米,出现在东边岭站;0-9.9毫米的降雨站点有7站。 +过去78小时,平均降雨量为12.1毫米,最大降雨量为20.9毫米,出现在观山洞站;0-9.9毫米的降雨站点有8站、10-24.9毫米的降雨站点有32站。 diff --git a/system-admin/src/main/resources/images/资兴市/资兴市-日平均气温.png b/system-admin/src/main/resources/images/资兴市/资兴市-日平均气温.png new file mode 100644 index 0000000..cca0598 Binary files /dev/null and b/system-admin/src/main/resources/images/资兴市/资兴市-日平均气温.png differ diff --git a/system-admin/src/main/resources/images/资兴市/资兴市-日平均气温_全年.png b/system-admin/src/main/resources/images/资兴市/资兴市-日平均气温_全年.png new file mode 100644 index 0000000..5ca53ee Binary files /dev/null and b/system-admin/src/main/resources/images/资兴市/资兴市-日平均气温_全年.png differ diff --git a/system-admin/src/main/resources/images/资兴市/资兴市-日平均降雨量.png b/system-admin/src/main/resources/images/资兴市/资兴市-日平均降雨量.png new file mode 100644 index 0000000..4b1b2cc Binary files /dev/null and b/system-admin/src/main/resources/images/资兴市/资兴市-日平均降雨量.png differ diff --git a/system-admin/src/main/resources/images/资兴市/资兴市-日平均降雨量_全年.png b/system-admin/src/main/resources/images/资兴市/资兴市-日平均降雨量_全年.png new file mode 100644 index 0000000..2cb8af4 Binary files /dev/null and b/system-admin/src/main/resources/images/资兴市/资兴市-日平均降雨量_全年.png differ diff --git a/system-admin/src/main/resources/images/资兴市/资兴市-月平均气温图.png b/system-admin/src/main/resources/images/资兴市/资兴市-月平均气温图.png new file mode 100644 index 0000000..9ab0322 Binary files /dev/null and b/system-admin/src/main/resources/images/资兴市/资兴市-月平均气温图.png differ diff --git a/system-admin/src/main/resources/images/资兴市/资兴市-月降雨量图.png b/system-admin/src/main/resources/images/资兴市/资兴市-月降雨量图.png new file mode 100644 index 0000000..9d0fa08 Binary files /dev/null and b/system-admin/src/main/resources/images/资兴市/资兴市-月降雨量图.png differ diff --git a/system-admin/src/main/resources/images/资兴市/资兴市-累计降雨量.png b/system-admin/src/main/resources/images/资兴市/资兴市-累计降雨量.png new file mode 100644 index 0000000..23709c2 Binary files /dev/null and b/system-admin/src/main/resources/images/资兴市/资兴市-累计降雨量.png differ diff --git a/system-admin/src/main/resources/images/资兴市/资兴市-累计降雨量_全年.png b/system-admin/src/main/resources/images/资兴市/资兴市-累计降雨量_全年.png new file mode 100644 index 0000000..cac3963 Binary files /dev/null and b/system-admin/src/main/resources/images/资兴市/资兴市-累计降雨量_全年.png differ diff --git a/system-admin/src/main/resources/images/资兴市/资兴市631信息.txt b/system-admin/src/main/resources/images/资兴市/资兴市631信息.txt new file mode 100644 index 0000000..777f9be --- /dev/null +++ b/system-admin/src/main/resources/images/资兴市/资兴市631信息.txt @@ -0,0 +1,15 @@ +资兴市2026年03月23日14时的降雨信息: +过去01小时,最大小时雨强为1.3毫米,出现在连坪,03月23日05-06时; +过去03小时,最大小时雨强为1.3毫米,出现在连坪,03月23日05-06时; +过去06小时,最大小时雨强为1.3毫米,出现在连坪,03月23日05-06时; +过去12小时,最大小时雨强为1.3毫米,出现在连坪,03月23日05-06时; +过去24小时,最大小时雨强为1.3毫米,出现在连坪,03月23日05-06时; +过去48小时,最大小时雨强为1.8毫米,出现在汤溪,03月21日22-23时; +过去78小时,最大小时雨强为9.6毫米,出现在羊兴,03月20日12-13时; +过去01小时,平均降雨量为0.1毫米,最大降雨量为1.3毫米,出现在连坪站;0-9.9毫米的降雨站点有6站。 +过去03小时,平均降雨量为0.1毫米,最大降雨量为1.3毫米,出现在连坪站;0-9.9毫米的降雨站点有8站。 +过去06小时,平均降雨量为0.1毫米,最大降雨量为1.3毫米,出现在连坪站;0-9.9毫米的降雨站点有9站。 +过去12小时,平均降雨量为0.1毫米,最大降雨量为1.3毫米,出现在连坪站;0-9.9毫米的降雨站点有9站。 +过去24小时,平均降雨量为0.1毫米,最大降雨量为1.3毫米,出现在连坪站;0-9.9毫米的降雨站点有10站。 +过去48小时,平均降雨量为0.2毫米,最大降雨量为1.9毫米,出现在汤溪站;0-9.9毫米的降雨站点有12站。 +过去78小时,平均降雨量为10.9毫米,最大降雨量为17.9毫米,出现在东坪站;0-9.9毫米的降雨站点有14站、10-24.9毫米的降雨站点有31站。 diff --git a/system-admin/src/main/resources/images/郴州市/Thumbs.db b/system-admin/src/main/resources/images/郴州市/Thumbs.db new file mode 100644 index 0000000..f4be30b Binary files /dev/null and b/system-admin/src/main/resources/images/郴州市/Thumbs.db differ diff --git a/system-admin/src/main/resources/images/郴州市/郴州市-日平均气温.png b/system-admin/src/main/resources/images/郴州市/郴州市-日平均气温.png new file mode 100644 index 0000000..616ba67 Binary files /dev/null and b/system-admin/src/main/resources/images/郴州市/郴州市-日平均气温.png differ diff --git a/system-admin/src/main/resources/images/郴州市/郴州市-日平均气温_全年.png b/system-admin/src/main/resources/images/郴州市/郴州市-日平均气温_全年.png new file mode 100644 index 0000000..5cb768b Binary files /dev/null and b/system-admin/src/main/resources/images/郴州市/郴州市-日平均气温_全年.png differ diff --git a/system-admin/src/main/resources/images/郴州市/郴州市-日平均降雨量.png b/system-admin/src/main/resources/images/郴州市/郴州市-日平均降雨量.png new file mode 100644 index 0000000..ed1b8ff Binary files /dev/null and b/system-admin/src/main/resources/images/郴州市/郴州市-日平均降雨量.png differ diff --git a/system-admin/src/main/resources/images/郴州市/郴州市-日平均降雨量_全年.png b/system-admin/src/main/resources/images/郴州市/郴州市-日平均降雨量_全年.png new file mode 100644 index 0000000..ca7a970 Binary files /dev/null and b/system-admin/src/main/resources/images/郴州市/郴州市-日平均降雨量_全年.png differ diff --git a/system-admin/src/main/resources/images/郴州市/郴州市-月平均气温图.png b/system-admin/src/main/resources/images/郴州市/郴州市-月平均气温图.png new file mode 100644 index 0000000..edff5f9 Binary files /dev/null and b/system-admin/src/main/resources/images/郴州市/郴州市-月平均气温图.png differ diff --git a/system-admin/src/main/resources/images/郴州市/郴州市-月降雨量图.png b/system-admin/src/main/resources/images/郴州市/郴州市-月降雨量图.png new file mode 100644 index 0000000..379fc77 Binary files /dev/null and b/system-admin/src/main/resources/images/郴州市/郴州市-月降雨量图.png differ diff --git a/system-admin/src/main/resources/images/郴州市/郴州市-累计降雨量.png b/system-admin/src/main/resources/images/郴州市/郴州市-累计降雨量.png new file mode 100644 index 0000000..65d295b Binary files /dev/null and b/system-admin/src/main/resources/images/郴州市/郴州市-累计降雨量.png differ diff --git a/system-admin/src/main/resources/images/郴州市/郴州市-累计降雨量_全年.png b/system-admin/src/main/resources/images/郴州市/郴州市-累计降雨量_全年.png new file mode 100644 index 0000000..95b246b Binary files /dev/null and b/system-admin/src/main/resources/images/郴州市/郴州市-累计降雨量_全年.png differ diff --git a/system-admin/src/main/resources/images/郴州市/郴州市631信息.txt b/system-admin/src/main/resources/images/郴州市/郴州市631信息.txt new file mode 100644 index 0000000..970cb5f --- /dev/null +++ b/system-admin/src/main/resources/images/郴州市/郴州市631信息.txt @@ -0,0 +1,15 @@ +郴州市2026年03月23日14时的降雨信息: +过去01小时,最大小时雨强为999999.0毫米,出现在北湖区四亩田,03月23日05-06时; +过去03小时,最大小时雨强为999999.0毫米,出现在北湖区四亩田,03月23日03-04时; +过去06小时,最大小时雨强为999999.0毫米,出现在北湖区四亩田,03月23日00-01时; +过去12小时,最大小时雨强为999999.0毫米,出现在北湖区四亩田,03月22日18-19时; +过去24小时,最大小时雨强为999999.0毫米,出现在北湖区四亩田,03月22日06-07时; +过去48小时,最大小时雨强为999999.0毫米,出现在北湖区四亩田,03月21日06-07时; +过去78小时,最大小时雨强为999999.0毫米,出现在北湖区四亩田,03月20日05-06时; +过去01小时,平均降雨量为2331.0毫米,最大降雨量为999999.0毫米,出现在北湖区四亩田站;0-9.9毫米的降雨站点有25站、200毫米以上的降雨站点有1站。 +过去03小时,平均降雨量为6993.1毫米,最大降雨量为2999997.0毫米,出现在北湖区四亩田站;0-9.9毫米的降雨站点有44站、200毫米以上的降雨站点有1站。 +过去06小时,平均降雨量为13986.1毫米,最大降雨量为5999994.0毫米,出现在北湖区四亩田站;0-9.9毫米的降雨站点有63站、200毫米以上的降雨站点有1站。 +过去12小时,平均降雨量为27972.1毫米,最大降雨量为11999988.0毫米,出现在北湖区四亩田站;0-9.9毫米的降雨站点有77站、200毫米以上的降雨站点有1站。 +过去24小时,平均降雨量为55944.2毫米,最大降雨量为23999976.0毫米,出现在北湖区四亩田站;0-9.9毫米的降雨站点有116站、200毫米以上的降雨站点有1站。 +过去48小时,平均降雨量为111888.3毫米,最大降雨量为47999952.0毫米,出现在北湖区四亩田站;0-9.9毫米的降雨站点有141站、10-24.9毫米的降雨站点有2站、200毫米以上的降雨站点有1站。 +过去78小时,平均降雨量为167842.8毫米,最大降雨量为71999928.0毫米,出现在北湖区四亩田站;0-9.9毫米的降雨站点有179站、10-24.9毫米的降雨站点有232站、200毫米以上的降雨站点有1站。 diff --git a/system-admin/src/main/resources/logback-spring.xml b/system-admin/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..cda96b5 --- /dev/null +++ b/system-admin/src/main/resources/logback-spring.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/system-admin/src/main/resources/mapper/dailyweather/WeatherDailyDataDao.xml b/system-admin/src/main/resources/mapper/dailyweather/WeatherDailyDataDao.xml new file mode 100644 index 0000000..ee6af68 --- /dev/null +++ b/system-admin/src/main/resources/mapper/dailyweather/WeatherDailyDataDao.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + id, station_id, observe_date, + avg_temp, max_temp, max_temp_time, min_temp, min_temp_time, + rain_20_20, rain_08_08, + relative_humidity, atmospheres, + day_avg_wind_speed, day_avg_wind_direction, + max_wind_speed, max_wind_direction, max_wind_time, + extreme_wind_speed, extreme_wind_direction, extreme_wind_time, + dept_id + + + + + + INSERT INTO weather_daily_data ( + id, station_id, observe_date, + avg_temp, max_temp, max_temp_time, min_temp, min_temp_time, + rain_20_20, rain_08_08, + relative_humidity, atmospheres, + day_avg_wind_speed, day_avg_wind_direction, + max_wind_speed, max_wind_direction, max_wind_time, + extreme_wind_speed, extreme_wind_direction, extreme_wind_time, + dept_id, creator, create_date, updater, update_date + ) VALUES + + (#{item.id}, #{item.stationId}, #{item.observeDate}, + #{item.avgTemp}, #{item.maxTemp}, #{item.maxTempTime}, #{item.minTemp}, #{item.minTempTime}, + #{item.rain2020}, #{item.rain0808}, + #{item.relativeHumidity}, #{item.atmospheres}, + #{item.dayAvgWindSpeed}, #{item.dayAvgWindDirection}, + #{item.maxWindSpeed}, #{item.maxWindDirection}, #{item.maxWindTime}, + #{item.extremeWindSpeed}, #{item.extremeWindDirection}, #{item.extremeWindTime}, + #{item.deptId}, #{item.creator}, #{item.createDate}, #{item.updater}, #{item.updateDate}) + + + + \ No newline at end of file diff --git a/system-admin/src/main/resources/mapper/filescan/WeatherFileScanRecordDao.xml b/system-admin/src/main/resources/mapper/filescan/WeatherFileScanRecordDao.xml new file mode 100644 index 0000000..75b6730 --- /dev/null +++ b/system-admin/src/main/resources/mapper/filescan/WeatherFileScanRecordDao.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/system-admin/src/main/resources/mapper/job/ScheduleJobDao.xml b/system-admin/src/main/resources/mapper/job/ScheduleJobDao.xml new file mode 100644 index 0000000..497098f --- /dev/null +++ b/system-admin/src/main/resources/mapper/job/ScheduleJobDao.xml @@ -0,0 +1,14 @@ + + + + + + + + update schedule_job set status = #{status} where id in + + #{id} + + + + \ No newline at end of file diff --git a/system-admin/src/main/resources/mapper/job/ScheduleJobLogDao.xml b/system-admin/src/main/resources/mapper/job/ScheduleJobLogDao.xml new file mode 100644 index 0000000..d69f308 --- /dev/null +++ b/system-admin/src/main/resources/mapper/job/ScheduleJobLogDao.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/system-admin/src/main/resources/mapper/log/SysLogErrorDao.xml b/system-admin/src/main/resources/mapper/log/SysLogErrorDao.xml new file mode 100644 index 0000000..d50a199 --- /dev/null +++ b/system-admin/src/main/resources/mapper/log/SysLogErrorDao.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/system-admin/src/main/resources/mapper/log/SysLogLoginDao.xml b/system-admin/src/main/resources/mapper/log/SysLogLoginDao.xml new file mode 100644 index 0000000..e554345 --- /dev/null +++ b/system-admin/src/main/resources/mapper/log/SysLogLoginDao.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/system-admin/src/main/resources/mapper/log/SysLogOperationDao.xml b/system-admin/src/main/resources/mapper/log/SysLogOperationDao.xml new file mode 100644 index 0000000..273eb8f --- /dev/null +++ b/system-admin/src/main/resources/mapper/log/SysLogOperationDao.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/system-admin/src/main/resources/mapper/oss/SysOssDao.xml b/system-admin/src/main/resources/mapper/oss/SysOssDao.xml new file mode 100644 index 0000000..b3289d3 --- /dev/null +++ b/system-admin/src/main/resources/mapper/oss/SysOssDao.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/system-admin/src/main/resources/mapper/region/SysRegionDao.xml b/system-admin/src/main/resources/mapper/region/SysRegionDao.xml new file mode 100644 index 0000000..2538772 --- /dev/null +++ b/system-admin/src/main/resources/mapper/region/SysRegionDao.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/system-admin/src/main/resources/mapper/station/WeatherStationDao.xml b/system-admin/src/main/resources/mapper/station/WeatherStationDao.xml new file mode 100644 index 0000000..371049c --- /dev/null +++ b/system-admin/src/main/resources/mapper/station/WeatherStationDao.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/system-admin/src/main/resources/mapper/sys/SysDeptDao.xml b/system-admin/src/main/resources/mapper/sys/SysDeptDao.xml new file mode 100644 index 0000000..1326caf --- /dev/null +++ b/system-admin/src/main/resources/mapper/sys/SysDeptDao.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/system-admin/src/main/resources/mapper/sys/SysDictDataDao.xml b/system-admin/src/main/resources/mapper/sys/SysDictDataDao.xml new file mode 100644 index 0000000..15b21fc --- /dev/null +++ b/system-admin/src/main/resources/mapper/sys/SysDictDataDao.xml @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/system-admin/src/main/resources/mapper/sys/SysDictTypeDao.xml b/system-admin/src/main/resources/mapper/sys/SysDictTypeDao.xml new file mode 100644 index 0000000..a594a8a --- /dev/null +++ b/system-admin/src/main/resources/mapper/sys/SysDictTypeDao.xml @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/system-admin/src/main/resources/mapper/sys/SysMenuDao.xml b/system-admin/src/main/resources/mapper/sys/SysMenuDao.xml new file mode 100644 index 0000000..615d352 --- /dev/null +++ b/system-admin/src/main/resources/mapper/sys/SysMenuDao.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/system-admin/src/main/resources/mapper/sys/SysParamsDao.xml b/system-admin/src/main/resources/mapper/sys/SysParamsDao.xml new file mode 100644 index 0000000..554281d --- /dev/null +++ b/system-admin/src/main/resources/mapper/sys/SysParamsDao.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + update sys_params set param_value = #{paramValue} where param_code = #{paramCode} + + \ No newline at end of file diff --git a/system-admin/src/main/resources/mapper/sys/SysRoleDao.xml b/system-admin/src/main/resources/mapper/sys/SysRoleDao.xml new file mode 100644 index 0000000..f54115f --- /dev/null +++ b/system-admin/src/main/resources/mapper/sys/SysRoleDao.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/system-admin/src/main/resources/mapper/sys/SysRoleDataScopeDao.xml b/system-admin/src/main/resources/mapper/sys/SysRoleDataScopeDao.xml new file mode 100644 index 0000000..d69f34e --- /dev/null +++ b/system-admin/src/main/resources/mapper/sys/SysRoleDataScopeDao.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + delete from sys_role_data_scope where role_id in + + #{roleId} + + + + \ No newline at end of file diff --git a/system-admin/src/main/resources/mapper/sys/SysRoleMenuDao.xml b/system-admin/src/main/resources/mapper/sys/SysRoleMenuDao.xml new file mode 100644 index 0000000..0042fb0 --- /dev/null +++ b/system-admin/src/main/resources/mapper/sys/SysRoleMenuDao.xml @@ -0,0 +1,20 @@ + + + + + + + + + delete from sys_role_menu where role_id in + + #{roleId} + + + + + delete from sys_role_menu where menu_id = #{value} + + \ No newline at end of file diff --git a/system-admin/src/main/resources/mapper/sys/SysRoleUserDao.xml b/system-admin/src/main/resources/mapper/sys/SysRoleUserDao.xml new file mode 100644 index 0000000..b8f7707 --- /dev/null +++ b/system-admin/src/main/resources/mapper/sys/SysRoleUserDao.xml @@ -0,0 +1,24 @@ + + + + + + + delete from sys_role_user where role_id in + + #{roleId} + + + + + delete from sys_role_user where user_id in + + #{userId} + + + + + + \ No newline at end of file diff --git a/system-admin/src/main/resources/mapper/sys/SysUserDao.xml b/system-admin/src/main/resources/mapper/sys/SysUserDao.xml new file mode 100644 index 0000000..c16dae3 --- /dev/null +++ b/system-admin/src/main/resources/mapper/sys/SysUserDao.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + update sys_user set password = #{newPassword} where id = #{id} + + + + + + + \ No newline at end of file diff --git a/system-admin/src/main/resources/mapper/sys/SysUserTokenDao.xml b/system-admin/src/main/resources/mapper/sys/SysUserTokenDao.xml new file mode 100644 index 0000000..e08ddde --- /dev/null +++ b/system-admin/src/main/resources/mapper/sys/SysUserTokenDao.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + update sys_user_token set token = #{token} where user_id = #{userId} + + \ No newline at end of file diff --git a/system-admin/src/test/java/com/weather/DynamicDataSourceTest.java b/system-admin/src/test/java/com/weather/DynamicDataSourceTest.java new file mode 100644 index 0000000..5a05963 --- /dev/null +++ b/system-admin/src/test/java/com/weather/DynamicDataSourceTest.java @@ -0,0 +1,33 @@ + +package com.weather; + +import com.weather.service.DynamicDataSourceTestService; +import jakarta.annotation.Resource; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + + +/** + * 多数据源测试 + * + * @author 123 + */ +@RunWith(SpringRunner.class) +@SpringBootTest +public class DynamicDataSourceTest { + @Resource + private DynamicDataSourceTestService dynamicDataSourceTestService; + + @Test + public void test() { + Long id = 1067246875800000001L; + + dynamicDataSourceTestService.updateUser(id); + dynamicDataSourceTestService.updateUserBySlave1(id); + //dynamicDataSourceTestService.updateUserBySlave2(id); + } + + +} diff --git a/system-admin/src/test/java/com/weather/RedisTest.java b/system-admin/src/test/java/com/weather/RedisTest.java new file mode 100644 index 0000000..574d45f --- /dev/null +++ b/system-admin/src/test/java/com/weather/RedisTest.java @@ -0,0 +1,29 @@ + +package com.weather; + +import cn.hutool.core.util.StrUtil; +import com.weather.common.redis.RedisUtils; +import com.weather.modules.sys.entity.SysUserEntity; +import jakarta.annotation.Resource; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + + +@RunWith(SpringRunner.class) +@SpringBootTest +public class RedisTest { + @Resource + private RedisUtils redisUtils; + + @Test + public void contextLoads() { + SysUserEntity user = new SysUserEntity(); + user.setEmail("123456@qq.com"); + redisUtils.set("user", user); + + System.out.println(StrUtil.toString(redisUtils.get("user"))); + } + +} diff --git a/system-admin/src/test/java/com/weather/service/DynamicDataSourceTestService.java b/system-admin/src/test/java/com/weather/service/DynamicDataSourceTestService.java new file mode 100644 index 0000000..7108035 --- /dev/null +++ b/system-admin/src/test/java/com/weather/service/DynamicDataSourceTestService.java @@ -0,0 +1,53 @@ + +package com.weather.service; + +import com.weather.commons.dynamic.datasource.annotation.DataSource; +import com.weather.modules.sys.dao.SysUserDao; +import com.weather.modules.sys.entity.SysUserEntity; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + + +/** + * 测试多数据源 + * + * @author 123 + */ +@Service +//@DataSource("slave1") +public class DynamicDataSourceTestService { + @Resource + private SysUserDao sysUserDao; + + //@Transactional + public void updateUser(Long id) { + SysUserEntity user = new SysUserEntity(); + user.setId(id); + user.setMobile("13500000000"); + //sysUserDao.updateById(user); + System.out.println(sysUserDao.selectById(id)); + } + + @DataSource("slave1") + @Transactional + public void updateUserBySlave1(Long id) { + SysUserEntity user = new SysUserEntity(); + user.setId(id); + user.setMobile("13500000001"); + //sysUserDao.updateById(user); + System.out.println(sysUserDao.selectById(id)); + } + +// @DataSource("slave2") +// @Transactional +// public void updateUserBySlave2(Long id){ +// SysUserEntity user = new SysUserEntity(); +// user.setId(id); +// user.setMobile("13500000002"); +// sysUserDao.updateById(user); +// +// //测试事物 +// int i = 1/0; +// } +} diff --git a/system-api/Dockerfile b/system-api/Dockerfile new file mode 100644 index 0000000..251b7fc --- /dev/null +++ b/system-api/Dockerfile @@ -0,0 +1,7 @@ +FROM java:8 +EXPOSE 8081 + +VOLUME /tmp +ADD renren-api.jar /app.jar +RUN bash -c 'touch /app.jar' +ENTRYPOINT ["java","-jar","/app.jar"] diff --git a/system-api/db/dm8.sql b/system-api/db/dm8.sql new file mode 100644 index 0000000..7f1bfd0 --- /dev/null +++ b/system-api/db/dm8.sql @@ -0,0 +1,27 @@ +CREATE TABLE tb_user ( + id bigint NOT NULL, + username varchar(50) NOT NULL, + mobile varchar(20) NOT NULL, + password varchar(64), + create_date datetime, + PRIMARY KEY (id) +); +CREATE UNIQUE INDEX idx_user_username on tb_user(username); + +CREATE TABLE tb_token ( + id bigint NOT NULL, + user_id bigint NOT NULL, + token varchar(100) NOT NULL, + expire_date datetime, + update_date datetime, + PRIMARY KEY (id) +); + +CREATE UNIQUE INDEX idx_token_user_id on tb_token(user_id); +CREATE UNIQUE INDEX idx_token on tb_token(token); + + +-- 账号:13612345678 密码:admin +INSERT INTO tb_user (id, username, mobile, password, create_date) VALUES (1067246875800000168, 'mark', '13612345678', '8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918', now()); + +commit; diff --git a/system-api/db/mysql.sql b/system-api/db/mysql.sql new file mode 100644 index 0000000..ba6f058 --- /dev/null +++ b/system-api/db/mysql.sql @@ -0,0 +1,25 @@ +-- 用户表 +CREATE TABLE tb_user ( + id bigint NOT NULL COMMENT 'id', + username varchar(50) NOT NULL COMMENT '用户名', + mobile varchar(20) NOT NULL COMMENT '手机号', + password varchar(64) COMMENT '密码', + create_date datetime COMMENT '创建时间', + PRIMARY KEY (id), + UNIQUE INDEX (username) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户'; + +-- 用户Token表 +CREATE TABLE tb_token ( + id bigint NOT NULL COMMENT 'id', + user_id bigint NOT NULL COMMENT '用户ID', + token varchar(100) NOT NULL COMMENT 'token', + expire_date datetime COMMENT '过期时间', + update_date datetime COMMENT '更新时间', + PRIMARY KEY (id), + UNIQUE INDEX (user_id), + UNIQUE INDEX (token) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户Token'; + +-- 账号:13612345678 密码:admin +INSERT INTO tb_user (id, username, mobile, password, create_date) VALUES (1067246875900000001, 'mark', '13612345678', '8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918', now()); diff --git a/system-api/db/oracle.sql b/system-api/db/oracle.sql new file mode 100644 index 0000000..69b44cb --- /dev/null +++ b/system-api/db/oracle.sql @@ -0,0 +1,24 @@ +CREATE TABLE tb_user ( + id NUMBER(20, 0) NOT NULL, + username varchar(50) NOT NULL, + mobile varchar(20) NOT NULL, + password varchar(64), + create_date date, + PRIMARY KEY (id) +); +CREATE UNIQUE INDEX idx_user_username on tb_user(username); + +CREATE TABLE tb_token ( + id NUMBER(20, 0) NOT NULL, + user_id NUMBER(20, 0) NOT NULL, + token varchar(100) NOT NULL, + expire_date date, + update_date date, + PRIMARY KEY (id) +); +CREATE UNIQUE INDEX idx_token_user_id on tb_token(user_id); +CREATE UNIQUE INDEX idx_token on tb_token(token); + + +-- 账号:13612345678 密码:admin +INSERT INTO tb_user (id, username, mobile, password, create_date) VALUES (1067246875900000001, 'mark', '13612345678', '8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918', CURRENT_DATE); diff --git a/system-api/db/postgresql.sql b/system-api/db/postgresql.sql new file mode 100644 index 0000000..408b32c --- /dev/null +++ b/system-api/db/postgresql.sql @@ -0,0 +1,24 @@ +CREATE TABLE tb_user ( + id int8 NOT NULL, + username varchar(50) NOT NULL, + mobile varchar(20) NOT NULL, + password varchar(64), + create_date timestamp, + PRIMARY KEY (id), + UNIQUE (username) +); + +CREATE TABLE tb_token ( + id int8 NOT NULL, + user_id int8 NOT NULL, + token varchar(100) NOT NULL, + expire_date timestamp, + update_date timestamp, + PRIMARY KEY (id), + UNIQUE (user_id), + UNIQUE (token) +); + + +-- 账号:13612345678 密码:admin +INSERT INTO tb_user (id, username, mobile, password, create_date) VALUES (1067246875900000001, 'mark', '13612345678', '8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918', now()); diff --git a/system-api/db/sqlserver.sql b/system-api/db/sqlserver.sql new file mode 100644 index 0000000..3a600a4 --- /dev/null +++ b/system-api/db/sqlserver.sql @@ -0,0 +1,24 @@ +CREATE TABLE tb_user ( + id bigint NOT NULL, + username varchar(50) NOT NULL, + mobile varchar(20) NOT NULL, + password varchar(64), + create_date datetime, + PRIMARY KEY (id), + UNIQUE (username) +); + +CREATE TABLE tb_token ( + id bigint NOT NULL, + user_id bigint NOT NULL, + token varchar(100) NOT NULL, + expire_date datetime, + update_date datetime, + PRIMARY KEY (id), + UNIQUE (user_id), + UNIQUE (token) +); + + +-- 账号:13612345678 密码:admin +INSERT INTO tb_user (id, username, mobile, password, create_date) VALUES (1067246875900000001, 'mark', '13612345678', '8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918', getdate()); diff --git a/system-api/pom.xml b/system-api/pom.xml new file mode 100644 index 0000000..35894e4 --- /dev/null +++ b/system-api/pom.xml @@ -0,0 +1,41 @@ + + + com.weather + weather-data + 5.5.0 + + 4.0.0 + system-api + jar + system-api + + + + com.weather + system-common + 5.5.0 + + + com.weather + system-dynamic-datasource + 5.5.0 + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + true + + + + + diff --git a/system-api/src/main/java/com/weather/ApiApplication.java b/system-api/src/main/java/com/weather/ApiApplication.java new file mode 100644 index 0000000..c5943ee --- /dev/null +++ b/system-api/src/main/java/com/weather/ApiApplication.java @@ -0,0 +1,26 @@ + + +package com.weather; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + +/** + * weather-api + * + * @author 123 + */ +@SpringBootApplication +public class ApiApplication extends SpringBootServletInitializer { + + public static void main(String[] args) { + SpringApplication.run(ApiApplication.class, args); + } + + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(ApiApplication.class); + } +} diff --git a/system-api/src/main/java/com/weather/annotation/Login.java b/system-api/src/main/java/com/weather/annotation/Login.java new file mode 100644 index 0000000..d56a082 --- /dev/null +++ b/system-api/src/main/java/com/weather/annotation/Login.java @@ -0,0 +1,15 @@ + + +package com.weather.annotation; + +import java.lang.annotation.*; + +/** + * 登录效验 + * @author 123 + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Login { +} diff --git a/system-api/src/main/java/com/weather/annotation/LoginUser.java b/system-api/src/main/java/com/weather/annotation/LoginUser.java new file mode 100644 index 0000000..6eae4bc --- /dev/null +++ b/system-api/src/main/java/com/weather/annotation/LoginUser.java @@ -0,0 +1,19 @@ + + +package com.weather.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 登录用户信息 + * + * @author 123 + */ +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +public @interface LoginUser { + +} diff --git a/system-api/src/main/java/com/weather/config/FilterConfig.java b/system-api/src/main/java/com/weather/config/FilterConfig.java new file mode 100644 index 0000000..cdefc26 --- /dev/null +++ b/system-api/src/main/java/com/weather/config/FilterConfig.java @@ -0,0 +1,29 @@ + + +package com.weather.config; + +import com.weather.common.xss.XssFilter; +import jakarta.servlet.DispatcherType; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + + +/** + * Filter配置 + * + * @author 123 + */ +@Configuration +public class FilterConfig { + + @Bean + public FilterRegistrationBean xssFilterRegistration() { + FilterRegistrationBean registration = new FilterRegistrationBean(); + registration.setDispatcherTypes(DispatcherType.REQUEST); + registration.setFilter(new XssFilter()); + registration.addUrlPatterns("/*"); + registration.setName("xssFilter"); + return registration; + } +} diff --git a/system-api/src/main/java/com/weather/config/MybatisPlusConfig.java b/system-api/src/main/java/com/weather/config/MybatisPlusConfig.java new file mode 100644 index 0000000..fd200a6 --- /dev/null +++ b/system-api/src/main/java/com/weather/config/MybatisPlusConfig.java @@ -0,0 +1,36 @@ + + +package com.weather.config; + +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * mybatis-plus配置 + * + * @author 123 + */ +@Configuration +public class MybatisPlusConfig { + + /** + * 配置分页等 + */ + @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor() { + MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor(); + // 分页插件 + mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor()); + // 乐观锁 + mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); + // 防止全表更新与删除 + mybatisPlusInterceptor.addInnerInterceptor(new BlockAttackInnerInterceptor()); + + return mybatisPlusInterceptor; + } + +} \ No newline at end of file diff --git a/system-api/src/main/java/com/weather/config/SwaggerConfig.java b/system-api/src/main/java/com/weather/config/SwaggerConfig.java new file mode 100644 index 0000000..1bc914d --- /dev/null +++ b/system-api/src/main/java/com/weather/config/SwaggerConfig.java @@ -0,0 +1,40 @@ + +package com.weather.config; + +import com.weather.common.constant.Constant; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.security.SecurityRequirement; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.ArrayList; +import java.util.List; + + +@Configuration +public class SwaggerConfig { + @Bean + public OpenAPI createRestApi() { + return new OpenAPI() + .info(apiInfo()) + .security(security()); + } + + private Info apiInfo() { + return new Info() + .title("人人开源") + .description("weather-api文档") + .version("5.x"); + } + + private List security() { + SecurityRequirement key = new SecurityRequirement(); + key.addList(Constant.TOKEN_HEADER, Constant.TOKEN_HEADER); + + List list = new ArrayList<>(); + list.add(key); + return list; + } + +} diff --git a/system-api/src/main/java/com/weather/config/WebMvcConfig.java b/system-api/src/main/java/com/weather/config/WebMvcConfig.java new file mode 100644 index 0000000..4f0c960 --- /dev/null +++ b/system-api/src/main/java/com/weather/config/WebMvcConfig.java @@ -0,0 +1,79 @@ + +package com.weather.config; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import com.weather.common.utils.DateUtils; +import com.weather.interceptor.AuthorizationInterceptor; +import com.weather.resolver.LoginUserHandlerMethodArgumentResolver; +import jakarta.annotation.Resource; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.converter.ByteArrayHttpMessageConverter; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.ResourceHttpMessageConverter; +import org.springframework.http.converter.StringHttpMessageConverter; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.text.SimpleDateFormat; +import java.util.List; +import java.util.TimeZone; + +/** + * MVC配置 + * + * @author 123 + */ +@Configuration +public class WebMvcConfig implements WebMvcConfigurer { + @Resource + private AuthorizationInterceptor authorizationInterceptor; + @Resource + private LoginUserHandlerMethodArgumentResolver loginUserHandlerMethodArgumentResolver; + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(authorizationInterceptor).addPathPatterns("/api/**"); + } + + @Override + public void addArgumentResolvers(List argumentResolvers) { + argumentResolvers.add(loginUserHandlerMethodArgumentResolver); + } + + @Override + public void configureMessageConverters(List> converters) { + converters.add(new ByteArrayHttpMessageConverter()); + converters.add(new StringHttpMessageConverter()); + converters.add(new ResourceHttpMessageConverter()); + converters.add(new AllEncompassingFormHttpMessageConverter()); + converters.add(new StringHttpMessageConverter()); + converters.add(jackson2HttpMessageConverter()); + } + + @Bean + public MappingJackson2HttpMessageConverter jackson2HttpMessageConverter() { + MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); + ObjectMapper mapper = new ObjectMapper(); + + //日期格式转换 + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + mapper.setDateFormat(new SimpleDateFormat(DateUtils.DATE_TIME_PATTERN)); + mapper.setTimeZone(TimeZone.getTimeZone("GMT+8")); + + //Long类型转String类型 + SimpleModule simpleModule = new SimpleModule(); + simpleModule.addSerializer(Long.class, ToStringSerializer.instance); + simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance); + mapper.registerModule(simpleModule); + + converter.setObjectMapper(mapper); + return converter; + } +} diff --git a/system-api/src/main/java/com/weather/controller/ApiLoginController.java b/system-api/src/main/java/com/weather/controller/ApiLoginController.java new file mode 100644 index 0000000..d9c66b6 --- /dev/null +++ b/system-api/src/main/java/com/weather/controller/ApiLoginController.java @@ -0,0 +1,53 @@ + +package com.weather.controller; + + +import com.weather.annotation.Login; +import com.weather.common.utils.Result; +import com.weather.common.validator.ValidatorUtils; +import com.weather.dto.LoginDTO; +import com.weather.service.TokenService; +import com.weather.service.UserService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +/** + * 登录接口 + * + * @author 123 + */ +@RestController +@RequestMapping("/api") +@Tag(name = "登录接口") +@AllArgsConstructor +public class ApiLoginController { + private final UserService userService; + private final TokenService tokenService; + + + @PostMapping("login") + @Operation(summary = "登录") + public Result> login(@RequestBody LoginDTO dto) { + //表单校验 + ValidatorUtils.validateEntity(dto); + + //用户登录 + Map map = userService.login(dto); + + return new Result().ok(map); + } + + @Login + @PostMapping("logout") + @Operation(summary = "退出") + public Result logout(@Parameter(hidden = true) @RequestAttribute("userId") Long userId) { + tokenService.expireToken(userId); + return new Result(); + } + +} diff --git a/system-api/src/main/java/com/weather/controller/ApiRegisterController.java b/system-api/src/main/java/com/weather/controller/ApiRegisterController.java new file mode 100644 index 0000000..04c809c --- /dev/null +++ b/system-api/src/main/java/com/weather/controller/ApiRegisterController.java @@ -0,0 +1,47 @@ + +package com.weather.controller; + +import cn.hutool.crypto.digest.DigestUtil; +import com.weather.common.utils.Result; +import com.weather.common.validator.ValidatorUtils; +import com.weather.dto.RegisterDTO; +import com.weather.entity.UserEntity; +import com.weather.service.UserService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Date; + +/** + * 注册接口 + * + * @author 123 + */ +@RestController +@RequestMapping("/api") +@Tag(name = "注册接口") +@AllArgsConstructor +public class ApiRegisterController { + private final UserService userService; + + @PostMapping("register") + @Operation(summary = "注册") + public Result register(@RequestBody RegisterDTO dto) { + //表单校验 + ValidatorUtils.validateEntity(dto); + + UserEntity user = new UserEntity(); + user.setMobile(dto.getMobile()); + user.setUsername(dto.getMobile()); + user.setPassword(DigestUtil.sha256Hex(dto.getPassword())); + user.setCreateDate(new Date()); + userService.insert(user); + + return new Result(); + } +} diff --git a/system-api/src/main/java/com/weather/controller/ApiTestController.java b/system-api/src/main/java/com/weather/controller/ApiTestController.java new file mode 100644 index 0000000..ac45829 --- /dev/null +++ b/system-api/src/main/java/com/weather/controller/ApiTestController.java @@ -0,0 +1,46 @@ + +package com.weather.controller; + +import com.weather.annotation.Login; +import com.weather.annotation.LoginUser; +import com.weather.common.utils.Result; +import com.weather.entity.UserEntity; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestAttribute; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 测试接口 + * + * @author 123 + */ +@RestController +@RequestMapping("/api") +@Tag(name = "测试接口") +public class ApiTestController { + + @Login + @GetMapping("userInfo") + @Operation(summary = "获取用户信息") + public Result userInfo(@Parameter(hidden = true) @LoginUser UserEntity user) { + return new Result().ok(user); + } + + @Login + @GetMapping("userId") + @Operation(summary = "获取用户ID") + public Result userInfo(@Parameter(hidden = true) @RequestAttribute("userId") Long userId) { + return new Result().ok(userId); + } + + @GetMapping("notToken") + @Operation(summary = "忽略Token验证测试") + public Result notToken() { + return new Result().ok("无需token也能访问。。。"); + } + +} diff --git a/system-api/src/main/java/com/weather/dao/TokenDao.java b/system-api/src/main/java/com/weather/dao/TokenDao.java new file mode 100644 index 0000000..c3c6a28 --- /dev/null +++ b/system-api/src/main/java/com/weather/dao/TokenDao.java @@ -0,0 +1,19 @@ + + +package com.weather.dao; + +import com.weather.common.dao.BaseDao; +import com.weather.entity.TokenEntity; +import org.apache.ibatis.annotations.Mapper; + +/** + * 用户Token + * + * @author 123 + */ +@Mapper +public interface TokenDao extends BaseDao { + TokenEntity getByToken(String token); + + TokenEntity getByUserId(Long userId); +} diff --git a/system-api/src/main/java/com/weather/dao/UserDao.java b/system-api/src/main/java/com/weather/dao/UserDao.java new file mode 100644 index 0000000..e2a819d --- /dev/null +++ b/system-api/src/main/java/com/weather/dao/UserDao.java @@ -0,0 +1,19 @@ + + +package com.weather.dao; + +import com.weather.common.dao.BaseDao; +import com.weather.entity.UserEntity; +import org.apache.ibatis.annotations.Mapper; + +/** + * 用户 + * + * @author 123 + */ +@Mapper +public interface UserDao extends BaseDao { + UserEntity getUserByMobile(String mobile); + + UserEntity getUserByUserId(Long userId); +} diff --git a/system-api/src/main/java/com/weather/dto/LoginDTO.java b/system-api/src/main/java/com/weather/dto/LoginDTO.java new file mode 100644 index 0000000..2265248 --- /dev/null +++ b/system-api/src/main/java/com/weather/dto/LoginDTO.java @@ -0,0 +1,18 @@ +package com.weather.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +@Data +@Schema(title = "登录表单") +public class LoginDTO { + @Schema(title = "手机号") + @NotBlank(message = "手机号不能为空") + private String mobile; + + @Schema(title = "密码") + @NotBlank(message = "密码不能为空") + private String password; + +} diff --git a/system-api/src/main/java/com/weather/dto/RegisterDTO.java b/system-api/src/main/java/com/weather/dto/RegisterDTO.java new file mode 100644 index 0000000..13ae1e8 --- /dev/null +++ b/system-api/src/main/java/com/weather/dto/RegisterDTO.java @@ -0,0 +1,26 @@ + + +package com.weather.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + + +/** + * 注册表单 + * + * @author 123 + */ +@Data +@Schema(title = "注册表单") +public class RegisterDTO { + @Schema(title = "手机号") + @NotBlank(message="手机号不能为空") + private String mobile; + + @Schema(title = "密码") + @NotBlank(message="密码不能为空") + private String password; + +} diff --git a/system-api/src/main/java/com/weather/entity/TokenEntity.java b/system-api/src/main/java/com/weather/entity/TokenEntity.java new file mode 100644 index 0000000..a755258 --- /dev/null +++ b/system-api/src/main/java/com/weather/entity/TokenEntity.java @@ -0,0 +1,41 @@ + + +package com.weather.entity; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * 用户Token + * + * @author 123 + */ +@Data +@TableName("tb_token") +public class TokenEntity implements Serializable { + private static final long serialVersionUID = 1L; + + @TableId + private Long id; + /** + * 用户ID + */ + private Long userId; + /** + * 用户token + */ + private String token; + /** + * 过期时间 + */ + private Date expireDate; + /** + * 更新时间 + */ + private Date updateDate; + +} \ No newline at end of file diff --git a/system-api/src/main/java/com/weather/entity/UserEntity.java b/system-api/src/main/java/com/weather/entity/UserEntity.java new file mode 100644 index 0000000..f778c13 --- /dev/null +++ b/system-api/src/main/java/com/weather/entity/UserEntity.java @@ -0,0 +1,46 @@ + + +package com.weather.entity; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * 用户 + * + * @author 123 + */ +@Data +@TableName("tb_user") +public class UserEntity implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 用户ID + */ + @TableId + private Long id; + /** + * 用户名 + */ + private String username; + /** + * 手机号 + */ + private String mobile; + /** + * 密码 + */ + @JsonIgnore + private String password; + /** + * 创建时间 + */ + private Date createDate; + +} \ No newline at end of file diff --git a/system-api/src/main/java/com/weather/exception/RenExceptionHandler.java b/system-api/src/main/java/com/weather/exception/RenExceptionHandler.java new file mode 100644 index 0000000..43021f4 --- /dev/null +++ b/system-api/src/main/java/com/weather/exception/RenExceptionHandler.java @@ -0,0 +1,49 @@ + + +package com.weather.exception; + +import com.weather.common.exception.ErrorCode; +import com.weather.common.exception.CommonException; +import com.weather.common.utils.Result; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +/** + * 异常处理器 + * + * @author 123 + * @since 1.0.0 + */ +@RestControllerAdvice +public class RenExceptionHandler { + private static final Logger logger = LoggerFactory.getLogger(RenExceptionHandler.class); + + /** + * 处理自定义异常 + */ + @ExceptionHandler(CommonException.class) + public Result handleRenException(CommonException ex){ + Result result = new Result(); + result.error(ex.getCode(), ex.getMsg()); + + return result; + } + + @ExceptionHandler(DuplicateKeyException.class) + public Result handleDuplicateKeyException(DuplicateKeyException ex){ + Result result = new Result(); + result.error(ErrorCode.DB_RECORD_EXISTS); + + return result; + } + + @ExceptionHandler(Exception.class) + public Result handleException(Exception ex){ + logger.error(ex.getMessage(), ex); + + return new Result().error(); + } +} \ No newline at end of file diff --git a/system-api/src/main/java/com/weather/interceptor/AuthorizationInterceptor.java b/system-api/src/main/java/com/weather/interceptor/AuthorizationInterceptor.java new file mode 100644 index 0000000..96bc831 --- /dev/null +++ b/system-api/src/main/java/com/weather/interceptor/AuthorizationInterceptor.java @@ -0,0 +1,65 @@ + +package com.weather.interceptor; + +import cn.hutool.core.util.StrUtil; +import com.weather.annotation.Login; +import com.weather.common.exception.ErrorCode; +import com.weather.common.exception.CommonException; +import com.weather.entity.TokenEntity; +import com.weather.service.TokenService; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.stereotype.Component; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.HandlerInterceptor; + +/** + * 权限(Token)验证 + * + * @author 123 + */ +@Component +public class AuthorizationInterceptor implements HandlerInterceptor { + @Resource + private TokenService tokenService; + + public static final String USER_KEY = "userId"; + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + Login annotation; + if (handler instanceof HandlerMethod) { + annotation = ((HandlerMethod) handler).getMethodAnnotation(Login.class); + } else { + return true; + } + + if (annotation == null) { + return true; + } + + //从header中获取token + String token = request.getHeader("token"); + //如果header中不存在token,则从参数中获取token + if (StrUtil.isBlank(token)) { + token = request.getParameter("token"); + } + + //token为空 + if (StrUtil.isBlank(token)) { + throw new CommonException(ErrorCode.TOKEN_NOT_EMPTY); + } + + //查询token信息 + TokenEntity tokenEntity = tokenService.getByToken(token); + if (tokenEntity == null || tokenEntity.getExpireDate().getTime() < System.currentTimeMillis()) { + throw new CommonException(ErrorCode.TOKEN_INVALID); + } + + //设置userId到request里,后续根据userId,获取用户信息 + request.setAttribute(USER_KEY, tokenEntity.getUserId()); + + return true; + } +} diff --git a/system-api/src/main/java/com/weather/resolver/LoginUserHandlerMethodArgumentResolver.java b/system-api/src/main/java/com/weather/resolver/LoginUserHandlerMethodArgumentResolver.java new file mode 100644 index 0000000..1c6711a --- /dev/null +++ b/system-api/src/main/java/com/weather/resolver/LoginUserHandlerMethodArgumentResolver.java @@ -0,0 +1,46 @@ + +package com.weather.resolver; + +import com.weather.annotation.LoginUser; +import com.weather.entity.UserEntity; +import com.weather.interceptor.AuthorizationInterceptor; +import com.weather.service.UserService; +import lombok.AllArgsConstructor; +import org.springframework.core.MethodParameter; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; + +/** + * 有@LoginUser注解的方法参数,注入当前登录用户 + * + * @author 123 + */ +@Component +@AllArgsConstructor +public class LoginUserHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver { + private final UserService userService; + + @Override + public boolean supportsParameter(MethodParameter parameter) { + return parameter.getParameterType().isAssignableFrom(UserEntity.class) && parameter.hasParameterAnnotation(LoginUser.class); + } + + @Override + public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer container, + NativeWebRequest request, WebDataBinderFactory factory) throws Exception { + //获取用户ID + Object object = request.getAttribute(AuthorizationInterceptor.USER_KEY, RequestAttributes.SCOPE_REQUEST); + if (object == null) { + return null; + } + + //获取用户信息 + UserEntity user = userService.getUserByUserId((Long) object); + + return user; + } +} diff --git a/system-api/src/main/java/com/weather/service/TokenService.java b/system-api/src/main/java/com/weather/service/TokenService.java new file mode 100644 index 0000000..8177ff6 --- /dev/null +++ b/system-api/src/main/java/com/weather/service/TokenService.java @@ -0,0 +1,30 @@ + + +package com.weather.service; + +import com.weather.common.service.BaseService; +import com.weather.entity.TokenEntity; + +/** + * 用户Token + * + * @author 123 + */ +public interface TokenService extends BaseService { + + TokenEntity getByToken(String token); + + /** + * 生成token + * @param userId 用户ID + * @return 返回token信息 + */ + TokenEntity createToken(Long userId); + + /** + * 设置token过期 + * @param userId 用户ID + */ + void expireToken(Long userId); + +} diff --git a/system-api/src/main/java/com/weather/service/UserService.java b/system-api/src/main/java/com/weather/service/UserService.java new file mode 100644 index 0000000..48048fc --- /dev/null +++ b/system-api/src/main/java/com/weather/service/UserService.java @@ -0,0 +1,28 @@ + + +package com.weather.service; + +import com.weather.common.service.BaseService; +import com.weather.entity.UserEntity; +import com.weather.dto.LoginDTO; + +import java.util.Map; + +/** + * 用户 + * + * @author 123 + */ +public interface UserService extends BaseService { + + UserEntity getByMobile(String mobile); + + UserEntity getUserByUserId(Long userId); + + /** + * 用户登录 + * @param dto 登录表单 + * @return 返回登录信息 + */ + Map login(LoginDTO dto); +} diff --git a/system-api/src/main/java/com/weather/service/impl/TokenServiceImpl.java b/system-api/src/main/java/com/weather/service/impl/TokenServiceImpl.java new file mode 100644 index 0000000..bc2ca9d --- /dev/null +++ b/system-api/src/main/java/com/weather/service/impl/TokenServiceImpl.java @@ -0,0 +1,92 @@ + + +package com.weather.service.impl; + +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.weather.common.service.impl.BaseServiceImpl; +import com.weather.dao.TokenDao; +import com.weather.entity.TokenEntity; +import com.weather.service.TokenService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +import java.util.Date; +import java.util.UUID; + + +@Service +public class TokenServiceImpl extends BaseServiceImpl implements TokenService { + + @Resource + private TokenDao tokenDao; + + /** + * 12小时后过期 + */ + private final static int EXPIRE = 3600 * 12; + + @Override + public TokenEntity getByToken(String token) { + return baseDao.getByToken(token); + } + + @Override + public TokenEntity createToken(Long userId) { + //当前时间 + Date now = new Date(); + //过期时间 + Date expireTime = new Date(now.getTime() + EXPIRE * 1000); + + //用户token + String token; + + //判断是否生成过token + TokenEntity tokenEntity = baseDao.getByUserId(userId); + if(tokenEntity == null){ + //生成一个token + token = generateToken(); + + tokenEntity = new TokenEntity(); + tokenEntity.setUserId(userId); + tokenEntity.setToken(token); + tokenEntity.setUpdateDate(now); + tokenEntity.setExpireDate(expireTime); + + //保存token + this.insert(tokenEntity); + }else{ + //判断token是否过期 + if(tokenEntity.getExpireDate().getTime() < System.currentTimeMillis()){ + //token过期,重新生成token + token = generateToken(); + }else { + token = tokenEntity.getToken(); + } + + tokenEntity.setToken(token); + tokenEntity.setUpdateDate(now); + tokenEntity.setExpireDate(expireTime); + + //更新token + this.updateById(tokenEntity); + } + + return tokenEntity; + } + + @Override + public void expireToken(Long userId){ + Date now = new Date(); + + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(TokenEntity::getUserId, userId); + updateWrapper.set(TokenEntity::getExpireDate, now); + updateWrapper.set(TokenEntity::getUpdateDate, now); + + tokenDao.update(updateWrapper); + } + + private String generateToken(){ + return UUID.randomUUID().toString().replace("-", ""); + } +} diff --git a/system-api/src/main/java/com/weather/service/impl/UserServiceImpl.java b/system-api/src/main/java/com/weather/service/impl/UserServiceImpl.java new file mode 100644 index 0000000..00d9747 --- /dev/null +++ b/system-api/src/main/java/com/weather/service/impl/UserServiceImpl.java @@ -0,0 +1,56 @@ + +package com.weather.service.impl; + +import cn.hutool.crypto.digest.DigestUtil; +import com.weather.common.exception.ErrorCode; +import com.weather.common.exception.CommonException; +import com.weather.common.service.impl.BaseServiceImpl; +import com.weather.common.validator.AssertUtils; +import com.weather.dao.UserDao; +import com.weather.dto.LoginDTO; +import com.weather.entity.TokenEntity; +import com.weather.entity.UserEntity; +import com.weather.service.TokenService; +import com.weather.service.UserService; +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.Map; + +@Service +@AllArgsConstructor +public class UserServiceImpl extends BaseServiceImpl implements UserService { + private final TokenService tokenService; + + @Override + public UserEntity getByMobile(String mobile) { + return baseDao.getUserByMobile(mobile); + } + + @Override + public UserEntity getUserByUserId(Long userId) { + return baseDao.getUserByUserId(userId); + } + + @Override + public Map login(LoginDTO dto) { + UserEntity user = getByMobile(dto.getMobile()); + AssertUtils.isNull(user, ErrorCode.ACCOUNT_PASSWORD_ERROR); + + //密码错误 + if (!user.getPassword().equals(DigestUtil.sha256Hex(dto.getPassword()))) { + throw new CommonException(ErrorCode.ACCOUNT_PASSWORD_ERROR); + } + + //获取登录token + TokenEntity tokenEntity = tokenService.createToken(user.getId()); + + Map map = new HashMap<>(2); + map.put("token", tokenEntity.getToken()); + map.put("expire", tokenEntity.getExpireDate().getTime() - System.currentTimeMillis()); + + return map; + } + +} \ No newline at end of file diff --git a/system-api/src/main/resources/application-dev.yml b/system-api/src/main/resources/application-dev.yml new file mode 100644 index 0000000..f5a3093 --- /dev/null +++ b/system-api/src/main/resources/application-dev.yml @@ -0,0 +1,33 @@ +spring: + datasource: + druid: + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://localhost:3306/weather_data_system?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true + username: root + password: root + initial-size: 10 + max-active: 100 + min-idle: 10 + max-wait: 60000 + pool-prepared-statements: true + max-pool-prepared-statement-per-connection-size: 20 + time-between-eviction-runs-millis: 60000 + min-evictable-idle-time-millis: 300000 + #Oracle需要打开注释 + #validation-query: SELECT 1 FROM DUAL + test-while-idle: true + test-on-borrow: false + test-on-return: false + stat-view-servlet: + enabled: true + url-pattern: /druid/* + #login-username: admin + #login-password: admin + filter: + stat: + log-slow-sql: true + slow-sql-millis: 1000 + merge-sql: false + wall: + config: + multi-statement-allow: true diff --git a/system-api/src/main/resources/application-prod.yml b/system-api/src/main/resources/application-prod.yml new file mode 100644 index 0000000..f5a3093 --- /dev/null +++ b/system-api/src/main/resources/application-prod.yml @@ -0,0 +1,33 @@ +spring: + datasource: + druid: + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://localhost:3306/weather_data_system?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true + username: root + password: root + initial-size: 10 + max-active: 100 + min-idle: 10 + max-wait: 60000 + pool-prepared-statements: true + max-pool-prepared-statement-per-connection-size: 20 + time-between-eviction-runs-millis: 60000 + min-evictable-idle-time-millis: 300000 + #Oracle需要打开注释 + #validation-query: SELECT 1 FROM DUAL + test-while-idle: true + test-on-borrow: false + test-on-return: false + stat-view-servlet: + enabled: true + url-pattern: /druid/* + #login-username: admin + #login-password: admin + filter: + stat: + log-slow-sql: true + slow-sql-millis: 1000 + merge-sql: false + wall: + config: + multi-statement-allow: true diff --git a/system-api/src/main/resources/application-test.yml b/system-api/src/main/resources/application-test.yml new file mode 100644 index 0000000..8e38503 --- /dev/null +++ b/system-api/src/main/resources/application-test.yml @@ -0,0 +1,33 @@ +spring: + datasource: + druid: + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://localhost:3306/weather_data_system?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true + username: weather + password: 123456 + initial-size: 10 + max-active: 100 + min-idle: 10 + max-wait: 60000 + pool-prepared-statements: true + max-pool-prepared-statement-per-connection-size: 20 + time-between-eviction-runs-millis: 60000 + min-evictable-idle-time-millis: 300000 + #Oracle需要打开注释 + #validation-query: SELECT 1 FROM DUAL + test-while-idle: true + test-on-borrow: false + test-on-return: false + stat-view-servlet: + enabled: true + url-pattern: /druid/* + #login-username: admin + #login-password: admin + filter: + stat: + log-slow-sql: true + slow-sql-millis: 1000 + merge-sql: false + wall: + config: + multi-statement-allow: true diff --git a/system-api/src/main/resources/application.yml b/system-api/src/main/resources/application.yml new file mode 100644 index 0000000..02fbf50 --- /dev/null +++ b/system-api/src/main/resources/application.yml @@ -0,0 +1,83 @@ +# Tomcat +server: + tomcat: + uri-encoding: UTF-8 + threads: + max: 1000 + min-spare: 30 + port: 8081 + servlet: + context-path: /renren-api + session: + cookie: + http-only: true + +knife4j: + enable: true + basic: + enable: false + username: admin + password: admin + setting: + enableFooter: false + +spring: + # 环境 dev|test|prod + profiles: + active: dev + messages: + encoding: UTF-8 + basename: i18n/messages + # jackson时间格式化 + jackson: + time-zone: GMT+8 + date-format: yyyy-MM-dd HH:mm:ss + mvc: + pathmatch: + matching-strategy: ANT_PATH_MATCHER + servlet: + multipart: + max-file-size: 100MB + max-request-size: 100MB + enabled: true + + data: + redis: + database: 0 + host: 192.168.10.10 + port: 6379 + password: # 密码(默认为空) + timeout: 6000ms # 连接超时时长(毫秒) + lettuce: + pool: + max-active: 1000 # 连接池最大连接数(使用负值表示没有限制) + max-wait: -1ms # 连接池最大阻塞等待时间(使用负值表示没有限制) + max-idle: 10 # 连接池中的最大空闲连接 + min-idle: 5 # 连接池中的最小空闲连接 + +renren: + redis: + open: false # 是否开启redis缓存 true开启 false关闭 + + +#mybatis +mybatis-plus: + mapper-locations: classpath*:/mapper/**/*.xml + #实体扫描,多个package用逗号或者分号分隔 + typeAliasesPackage: io.renren.entity + global-config: + #数据库相关配置 + db-config: + #主键类型 + id-type: ASSIGN_ID + banner: false + #原生配置 + configuration: + map-underscore-to-camel-case: true + cache-enabled: false + call-setters-on-nulls: true + jdbc-type-for-null: 'null' + configuration-properties: + prefix: + blobType: BLOB + boolValue: TRUE diff --git a/system-api/src/main/resources/banner.txt b/system-api/src/main/resources/banner.txt new file mode 100644 index 0000000..a4028b9 --- /dev/null +++ b/system-api/src/main/resources/banner.txt @@ -0,0 +1,5 @@ +==================================================================================================================== + + 欢迎使用 renren-api - Powered By https://www.renren.io + +==================================================================================================================== \ No newline at end of file diff --git a/system-api/src/main/resources/logback-spring.xml b/system-api/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..5deef21 --- /dev/null +++ b/system-api/src/main/resources/logback-spring.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/system-api/src/main/resources/mapper/TokenDao.xml b/system-api/src/main/resources/mapper/TokenDao.xml new file mode 100644 index 0000000..84f666c --- /dev/null +++ b/system-api/src/main/resources/mapper/TokenDao.xml @@ -0,0 +1,14 @@ + + + + + + + + + + \ No newline at end of file diff --git a/system-api/src/main/resources/mapper/UserDao.xml b/system-api/src/main/resources/mapper/UserDao.xml new file mode 100644 index 0000000..a0eeefe --- /dev/null +++ b/system-api/src/main/resources/mapper/UserDao.xml @@ -0,0 +1,13 @@ + + + + + + + + + \ No newline at end of file diff --git a/system-common/pom.xml b/system-common/pom.xml new file mode 100644 index 0000000..7e7cf8c --- /dev/null +++ b/system-common/pom.xml @@ -0,0 +1,16 @@ + + + com.weather + weather-data + 5.5.0 + + 4.0.0 + system-common + jar + system-common + + + ${project.artifactId} + + + \ No newline at end of file diff --git a/system-common/src/main/java/com/weather/common/aspect/RedisAspect.java b/system-common/src/main/java/com/weather/common/aspect/RedisAspect.java new file mode 100644 index 0000000..1de2bed --- /dev/null +++ b/system-common/src/main/java/com/weather/common/aspect/RedisAspect.java @@ -0,0 +1,42 @@ + +package com.weather.common.aspect; + +import com.weather.common.exception.ErrorCode; +import com.weather.common.exception.CommonException; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +/** + * Redis切面处理类 + * + * @author 123 + */ +@Aspect +@Component +public class RedisAspect { + private final Logger logger = LoggerFactory.getLogger(getClass()); + /** + * 是否开启redis缓存 true开启 false关闭 + */ + @Value("${weather.redis.open: false}") + private boolean open; + + @Around("execution(* com.weather.common.redis.RedisUtils.*(..))") + public Object around(ProceedingJoinPoint point) throws Throwable { + Object result = null; + if (open) { + try { + result = point.proceed(); + } catch (Exception e) { + logger.error("redis error", e); + throw new CommonException(ErrorCode.REDIS_ERROR); + } + } + return result; + } +} diff --git a/system-common/src/main/java/com/weather/common/constant/Constant.java b/system-common/src/main/java/com/weather/common/constant/Constant.java new file mode 100644 index 0000000..2aaef98 --- /dev/null +++ b/system-common/src/main/java/com/weather/common/constant/Constant.java @@ -0,0 +1,154 @@ + + +package com.weather.common.constant; + +/** + * 常量 + * + * @author 123 + */ +public interface Constant { + /** + * 成功 + */ + int SUCCESS = 1; + /** + * 失败 + */ + int FAIL = 0; + /** + * 菜单根节点标识 + */ + Long MENU_ROOT = 0L; + /** + * 部门根节点标识 + */ + Long DEPT_ROOT = 0L; + /** + * 升序 + */ + String ASC = "asc"; + /** + * 降序 + */ + String DESC = "desc"; + /** + * 创建时间字段名 + */ + String CREATE_DATE = "create_date"; + + /** + * 数据权限过滤 + */ + String SQL_FILTER = "sqlFilter"; + /** + * 当前页码 + */ + String PAGE = "page"; + /** + * 每页显示记录数 + */ + String LIMIT = "limit"; + /** + * 排序字段 + */ + String ORDER_FIELD = "orderField"; + /** + * 排序方式 + */ + String ORDER = "order"; + /** + * token header + */ + String TOKEN_HEADER = "token"; + + /** + * 云存储配置KEY + */ + String CLOUD_STORAGE_CONFIG_KEY = "CLOUD_STORAGE_CONFIG_KEY"; + + /** + * 文件扫描根路径KEY + */ + String FILE_SCAN_ROOT_PATH = "FILE_SCAN_ROOT_PATH"; + + /** + * 文件扫描状态 + */ + enum FileScanStatus { + /** + * 已接收 + */ + RECEIVED(0), + /** + * 展示中 + */ + DISPLAYED(1), + /** + * 已归档 + */ + ARCHIVED(2); + + private int value; + + FileScanStatus(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + } + + /** + * 定时任务状态 + */ + enum ScheduleStatus { + /** + * 暂停 + */ + PAUSE(0), + /** + * 正常 + */ + NORMAL(1); + + private int value; + + ScheduleStatus(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + } + + /** + * 云服务商 + */ + enum CloudService { + /** + * 七牛云 + */ + QINIU(1), + /** + * 阿里云 + */ + ALIYUN(2), + /** + * 腾讯云 + */ + QCLOUD(3); + + private int value; + + CloudService(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + } +} \ No newline at end of file diff --git a/system-common/src/main/java/com/weather/common/convert/DateConverter.java b/system-common/src/main/java/com/weather/common/convert/DateConverter.java new file mode 100644 index 0000000..a08f5fa --- /dev/null +++ b/system-common/src/main/java/com/weather/common/convert/DateConverter.java @@ -0,0 +1,73 @@ + +package com.weather.common.convert; + +import cn.hutool.core.util.StrUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.convert.converter.Converter; +import org.springframework.stereotype.Component; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * 日期转换 + * + * @author 123 + */ +@Component +public class DateConverter implements Converter { + private static final Logger logger = LoggerFactory.getLogger(DateConverter.class); + private static final List formatList = new ArrayList<>(5); + + static { + formatList.add("yyyy-MM"); + formatList.add("yyyy-MM-dd"); + formatList.add("yyyy-MM-dd HH:mm"); + formatList.add("yyyy-MM-dd HH:mm:ss"); + formatList.add("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); + } + + @Override + public Date convert(String source) { + String value = source.trim(); + if (StrUtil.isEmpty(value)) { + return null; + } + + if (source.matches("^\\d{4}-\\d{1,2}$")) { + return parseDate(source, formatList.get(0)); + } else if (source.matches("^\\d{4}-\\d{1,2}-\\d{1,2}$")) { + return parseDate(source, formatList.get(1)); + } else if (source.matches("^\\d{4}-\\d{1,2}-\\d{1,2} {1}\\d{1,2}:\\d{1,2}$")) { + return parseDate(source, formatList.get(2)); + } else if (source.matches("^\\d{4}-\\d{1,2}-\\d{1,2} {1}\\d{1,2}:\\d{1,2}:\\d{1,2}$")) { + return parseDate(source, formatList.get(3)); + } else if (source.matches("^\\d{4}-\\d{1,2}-\\d{1,2}.*T.*\\d{1,2}:\\d{1,2}:\\d{1,2}.*..*$")) { + return parseDate(source, formatList.get(4)); + } else { + throw new IllegalArgumentException("Invalid boolean value '" + source + "'"); + } + } + + /** + * 格式化日期 + * @param dateStr String 字符型日期 + * @param format String 格式 + * @return Date 日期 + */ + public Date parseDate(String dateStr, String format) { + Date date = null; + try { + DateFormat dateFormat = new SimpleDateFormat(format); + date = dateFormat.parse(dateStr); + } catch (Exception e) { + logger.error("Formatted date with date: {} and format : {} ", dateStr, format); + } + return date; + } + +} diff --git a/system-common/src/main/java/com/weather/common/dao/BaseDao.java b/system-common/src/main/java/com/weather/common/dao/BaseDao.java new file mode 100644 index 0000000..55d8c28 --- /dev/null +++ b/system-common/src/main/java/com/weather/common/dao/BaseDao.java @@ -0,0 +1,15 @@ + + +package com.weather.common.dao; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * 基础Dao + * + * @author 123 + * @since 1.0.0 + */ +public interface BaseDao extends BaseMapper { + +} diff --git a/system-common/src/main/java/com/weather/common/entity/BaseEntity.java b/system-common/src/main/java/com/weather/common/entity/BaseEntity.java new file mode 100644 index 0000000..9121f4f --- /dev/null +++ b/system-common/src/main/java/com/weather/common/entity/BaseEntity.java @@ -0,0 +1,35 @@ + + +package com.weather.common.entity; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * 基础实体类,所有实体都需要继承 + * + * @author 123 + */ +@Data +public abstract class BaseEntity implements Serializable { + /** + * id + */ + @TableId + private Long id; + /** + * 创建者 + */ + @TableField(fill = FieldFill.INSERT) + private Long creator; + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + private Date createDate; +} \ No newline at end of file diff --git a/system-common/src/main/java/com/weather/common/exception/CommonException.java b/system-common/src/main/java/com/weather/common/exception/CommonException.java new file mode 100644 index 0000000..3b89f92 --- /dev/null +++ b/system-common/src/main/java/com/weather/common/exception/CommonException.java @@ -0,0 +1,69 @@ + + +package com.weather.common.exception; + + +import com.weather.common.utils.MessageUtils; + +/** + * 自定义异常 + * + * @author 123 + */ +public class CommonException extends RuntimeException { + private static final long serialVersionUID = 1L; + + private int code; + private String msg; + + public CommonException(int code) { + this.code = code; + this.msg = MessageUtils.getMessage(code); + } + + public CommonException(int code, String... params) { + this.code = code; + this.msg = MessageUtils.getMessage(code, params); + } + + public CommonException(int code, Throwable e) { + super(e); + this.code = code; + this.msg = MessageUtils.getMessage(code); + } + + public CommonException(int code, Throwable e, String... params) { + super(e); + this.code = code; + this.msg = MessageUtils.getMessage(code, params); + } + + public CommonException(String msg) { + super(msg); + this.code = ErrorCode.INTERNAL_SERVER_ERROR; + this.msg = msg; + } + + public CommonException(String msg, Throwable e) { + super(msg, e); + this.code = ErrorCode.INTERNAL_SERVER_ERROR; + this.msg = msg; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + +} \ No newline at end of file diff --git a/system-common/src/main/java/com/weather/common/exception/ErrorCode.java b/system-common/src/main/java/com/weather/common/exception/ErrorCode.java new file mode 100644 index 0000000..e1a038a --- /dev/null +++ b/system-common/src/main/java/com/weather/common/exception/ErrorCode.java @@ -0,0 +1,40 @@ + + +package com.weather.common.exception; + +/** + * 错误编码,由5位数字组成,前2位为模块编码,后3位为业务编码 + *

+ * 如:10001(10代表系统模块,001代表业务代码) + *

+ * + * @author 123 + * @since 1.0.0 + */ +public interface ErrorCode { + int INTERNAL_SERVER_ERROR = 500; + int UNAUTHORIZED = 401; + + int NOT_NULL = 10001; + int DB_RECORD_EXISTS = 10002; + int PARAMS_GET_ERROR = 10003; + int ACCOUNT_PASSWORD_ERROR = 10004; + int ACCOUNT_DISABLE = 10005; + int IDENTIFIER_NOT_NULL = 10006; + int CAPTCHA_ERROR = 10007; + int SUB_MENU_EXIST = 10008; + int PASSWORD_ERROR = 10009; + int SUPERIOR_DEPT_ERROR = 10011; + int SUPERIOR_MENU_ERROR = 10012; + int DATA_SCOPE_PARAMS_ERROR = 10013; + int DEPT_SUB_DELETE_ERROR = 10014; + int DEPT_USER_DELETE_ERROR = 10015; + int UPLOAD_FILE_EMPTY = 10019; + int TOKEN_NOT_EMPTY = 10020; + int TOKEN_INVALID = 10021; + int ACCOUNT_LOCK = 10022; + int OSS_UPLOAD_FILE_ERROR = 10024; + int REDIS_ERROR = 10027; + int JOB_ERROR = 10028; + int INVALID_SYMBOL = 10029; +} diff --git a/system-common/src/main/java/com/weather/common/exception/ExceptionUtils.java b/system-common/src/main/java/com/weather/common/exception/ExceptionUtils.java new file mode 100644 index 0000000..40d81ca --- /dev/null +++ b/system-common/src/main/java/com/weather/common/exception/ExceptionUtils.java @@ -0,0 +1,47 @@ + + +package com.weather.common.exception; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; + +/** + * Exception工具类 + * + * @author 123 + */ +public class ExceptionUtils { + + /** + * 获取异常信息 + * @param ex 异常 + * @return 返回异常信息 + */ + public static String getErrorStackTrace(Exception ex){ + StringWriter sw = null; + PrintWriter pw = null; + try { + sw = new StringWriter(); + pw = new PrintWriter(sw, true); + ex.printStackTrace(pw); + }finally { + try { + if(pw != null) { + pw.close(); + } + } catch (Exception e) { + + } + try { + if(sw != null) { + sw.close(); + } + } catch (IOException e) { + + } + } + + return sw.toString(); + } +} \ No newline at end of file diff --git a/system-common/src/main/java/com/weather/common/page/PageData.java b/system-common/src/main/java/com/weather/common/page/PageData.java new file mode 100644 index 0000000..e90b481 --- /dev/null +++ b/system-common/src/main/java/com/weather/common/page/PageData.java @@ -0,0 +1,36 @@ + + +package com.weather.common.page; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * 分页工具类 + * + * @author 123 + */ +@Data +@Schema(title = "分页数据") +public class PageData implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(title = "总记录数") + private int total; + + @Schema(title = "列表数据") + private List list; + + /** + * 分页 + * @param list 列表数据 + * @param total 总记录数 + */ + public PageData(List list, long total) { + this.list = list; + this.total = (int)total; + } +} diff --git a/system-common/src/main/java/com/weather/common/redis/RedisConfig.java b/system-common/src/main/java/com/weather/common/redis/RedisConfig.java new file mode 100644 index 0000000..b79a206 --- /dev/null +++ b/system-common/src/main/java/com/weather/common/redis/RedisConfig.java @@ -0,0 +1,49 @@ + + +package com.weather.common.redis; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; +import jakarta.annotation.Resource; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; + + +/** + * Redis配置 + * + * @author 123 + */ +@Configuration +public class RedisConfig { + @Resource + private RedisConnectionFactory factory; + + @Bean + public Jackson2JsonRedisSerializer jackson2JsonRedisSerializer(){ + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL); + + // Spring Boot 3.5 新写法 + return new Jackson2JsonRedisSerializer<>(objectMapper, Object.class); + } + + @Bean + public RedisTemplate redisTemplate() { + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.setValueSerializer(jackson2JsonRedisSerializer()); + redisTemplate.setHashKeySerializer(new StringRedisSerializer()); + redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer()); + redisTemplate.setConnectionFactory(factory); + + return redisTemplate; + } +} diff --git a/system-common/src/main/java/com/weather/common/redis/RedisKeys.java b/system-common/src/main/java/com/weather/common/redis/RedisKeys.java new file mode 100644 index 0000000..b1882e5 --- /dev/null +++ b/system-common/src/main/java/com/weather/common/redis/RedisKeys.java @@ -0,0 +1,58 @@ + + +package com.weather.common.redis; + +/** + * @author 123 + * @since 1.0.0 + */ +public class RedisKeys { + /** + * 系统参数Key + */ + public static String getSysParamsKey(){ + return "sys:params"; + } + + /** + * 验证码Key + */ + public static String getCaptchaKey(String uuid){ + return "sys:captcha:" + uuid; + } + + /** + * 登录用户Key + */ + public static String getSecurityUserKey(Long id){ + return "sys:security:user:" + id; + } + + /** + * 系统日志Key + */ + public static String getSysLogKey(){ + return "sys:log"; + } + + /** + * 系统资源Key + */ + public static String getSysResourceKey(){ + return "sys:resource"; + } + + /** + * 用户菜单导航Key + */ + public static String getUserMenuNavKey(Long userId){ + return "sys:user:nav:" + userId; + } + + /** + * 用户权限标识Key + */ + public static String getUserPermissionsKey(Long userId){ + return "sys:user:permissions:" + userId; + } +} diff --git a/system-common/src/main/java/com/weather/common/redis/RedisUtils.java b/system-common/src/main/java/com/weather/common/redis/RedisUtils.java new file mode 100644 index 0000000..0cb85a1 --- /dev/null +++ b/system-common/src/main/java/com/weather/common/redis/RedisUtils.java @@ -0,0 +1,119 @@ + +package com.weather.common.redis; + +import jakarta.annotation.Resource; +import org.springframework.data.redis.core.HashOperations; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + * Redis工具类 + * + * @author 123 + */ +@Component +public class RedisUtils { + @Resource + private RedisTemplate redisTemplate; + + /** 默认过期时长为24小时,单位:秒 */ + public final static long DEFAULT_EXPIRE = 60 * 60 * 24L; + /** 过期时长为1小时,单位:秒 */ + public final static long HOUR_ONE_EXPIRE = 60 * 60 * 1L; + /** 过期时长为6小时,单位:秒 */ + public final static long HOUR_SIX_EXPIRE = 60 * 60 * 6L; + /** 不设置过期时长 */ + public final static long NOT_EXPIRE = -1L; + + public void set(String key, Object value, long expire) { + redisTemplate.opsForValue().set(key, value); + if (expire != NOT_EXPIRE) { + expire(key, expire); + } + } + + public void set(String key, Object value) { + set(key, value, DEFAULT_EXPIRE); + } + + public Object get(String key, long expire) { + Object value = redisTemplate.opsForValue().get(key); + if (expire != NOT_EXPIRE) { + expire(key, expire); + } + return value; + } + + public Object get(String key) { + return get(key, NOT_EXPIRE); + } + + public void delete(String key) { + redisTemplate.delete(key); + } + + public void delete(Collection keys) { + redisTemplate.delete(keys); + } + + public Object hGet(String key, String field) { + return redisTemplate.opsForHash().get(key, field); + } + + public Map hGetAll(String key) { + HashOperations hashOperations = redisTemplate.opsForHash(); + return hashOperations.entries(key); + } + + public void hMSet(String key, Map map) { + hMSet(key, map, DEFAULT_EXPIRE); + } + + public void hMSet(String key, Map map, long expire) { + redisTemplate.opsForHash().putAll(key, map); + + if (expire != NOT_EXPIRE) { + expire(key, expire); + } + } + + public void hSet(String key, String field, Object value) { + hSet(key, field, value, DEFAULT_EXPIRE); + } + + public void hSet(String key, String field, Object value, long expire) { + redisTemplate.opsForHash().put(key, field, value); + + if (expire != NOT_EXPIRE) { + expire(key, expire); + } + } + + public void expire(String key, long expire) { + redisTemplate.expire(key, expire, TimeUnit.SECONDS); + } + + public void hDel(String key, Object... fields) { + redisTemplate.opsForHash().delete(key, fields); + } + + public void leftPush(String key, Object value) { + leftPush(key, value, DEFAULT_EXPIRE); + } + + public void leftPush(String key, Object value, long expire) { + redisTemplate.opsForList().leftPush(key, value); + + if (expire != NOT_EXPIRE) { + expire(key, expire); + } + } + + public Object rightPop(String key) { + return redisTemplate.opsForList().rightPop(key); + } +} diff --git a/system-common/src/main/java/com/weather/common/service/BaseService.java b/system-common/src/main/java/com/weather/common/service/BaseService.java new file mode 100644 index 0000000..0ef4b85 --- /dev/null +++ b/system-common/src/main/java/com/weather/common/service/BaseService.java @@ -0,0 +1,110 @@ + + +package com.weather.common.service; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; + +import java.io.Serializable; +import java.util.Collection; + +/** + * 基础服务接口,所有Service接口都要继承 + * + * @author 123 + */ +public interface BaseService { + Class currentModelClass(); + + /** + *

+ * 插入一条记录(选择字段,策略插入) + *

+ * + * @param entity 实体对象 + */ + boolean insert(T entity); + + /** + *

+ * 插入(批量),该方法不支持 Oracle、SQL Server + *

+ * + * @param entityList 实体对象集合 + */ + boolean insertBatch(Collection entityList); + + /** + *

+ * 插入(批量),该方法不支持 Oracle、SQL Server + *

+ * + * @param entityList 实体对象集合 + * @param batchSize 插入批次数量 + */ + boolean insertBatch(Collection entityList, int batchSize); + + /** + *

+ * 根据 ID 选择修改 + *

+ * + * @param entity 实体对象 + */ + boolean updateById(T entity); + + /** + *

+ * 根据 whereEntity 条件,更新记录 + *

+ * + * @param entity 实体对象 + * @param updateWrapper 实体对象封装操作类 {@link com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper} + */ + boolean update(T entity, Wrapper updateWrapper); + + /** + *

+ * 根据ID 批量更新 + *

+ * + * @param entityList 实体对象集合 + */ + boolean updateBatchById(Collection entityList); + + /** + *

+ * 根据ID 批量更新 + *

+ * + * @param entityList 实体对象集合 + * @param batchSize 更新批次数量 + */ + boolean updateBatchById(Collection entityList, int batchSize); + + /** + *

+ * 根据 ID 查询 + *

+ * + * @param id 主键ID + */ + T selectById(Serializable id); + + /** + *

+ * 根据 ID 删除 + *

+ * + * @param id 主键ID + */ + boolean deleteById(Serializable id); + + /** + *

+ * 删除(根据ID 批量删除) + *

+ * + * @param idList 主键ID列表 + */ + boolean deleteBatchIds(Collection idList); +} \ No newline at end of file diff --git a/system-common/src/main/java/com/weather/common/service/CrudService.java b/system-common/src/main/java/com/weather/common/service/CrudService.java new file mode 100644 index 0000000..3257fee --- /dev/null +++ b/system-common/src/main/java/com/weather/common/service/CrudService.java @@ -0,0 +1,28 @@ + +package com.weather.common.service; + +import com.weather.common.page.PageData; + +import java.util.List; +import java.util.Map; + +/** + * CRUD基础服务接口 + * + * @author 123 + */ +public interface CrudService extends BaseService { + + PageData page(Map params); + + List list(Map params); + + D get(Long id); + + void save(D dto); + + void update(D dto); + + void delete(Long[] ids); + +} \ No newline at end of file diff --git a/system-common/src/main/java/com/weather/common/service/impl/BaseServiceImpl.java b/system-common/src/main/java/com/weather/common/service/impl/BaseServiceImpl.java new file mode 100644 index 0000000..c6ea661 --- /dev/null +++ b/system-common/src/main/java/com/weather/common/service/impl/BaseServiceImpl.java @@ -0,0 +1,212 @@ + +package com.weather.common.service.impl; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.enums.SqlMethod; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.metadata.OrderItem; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.core.toolkit.ReflectionKit; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.toolkit.SqlHelper; +import com.weather.common.constant.Constant; +import com.weather.common.page.PageData; +import com.weather.common.service.BaseService; +import com.weather.common.utils.ConvertUtils; +import org.apache.ibatis.binding.MapperMethod; +import org.apache.ibatis.logging.Log; +import org.apache.ibatis.logging.LogFactory; +import org.apache.ibatis.session.SqlSession; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +import java.io.Serializable; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; + +/** + * 基础服务类,所有Service都要继承 + * + * @author 123 + */ +public abstract class BaseServiceImpl, T> implements BaseService { + @Autowired + protected M baseDao; + protected Log log = LogFactory.getLog(getClass()); + + /** + * 获取分页对象 + * @param params 分页查询参数 + * @param defaultOrderField 默认排序字段 + * @param isAsc 排序方式 + */ + protected IPage getPage(Map params, String defaultOrderField, boolean isAsc) { + //分页参数 + long curPage = 1; + long limit = 10; + + if (params.get(Constant.PAGE) != null) { + curPage = Long.parseLong((String) params.get(Constant.PAGE)); + } + if (params.get(Constant.LIMIT) != null) { + limit = Long.parseLong((String) params.get(Constant.LIMIT)); + } + + //分页对象 + Page page = new Page<>(curPage, limit); + + //分页参数 + params.put(Constant.PAGE, page); + + //排序字段 + String orderField = (String) params.get(Constant.ORDER_FIELD); + String order = (String) params.get(Constant.ORDER); + + //前端字段排序 + if (StrUtil.isNotBlank(orderField) && StrUtil.isNotBlank(order)) { + if (Constant.ASC.equalsIgnoreCase(order)) { + return page.addOrder(OrderItem.asc(orderField)); + } else { + return page.addOrder(OrderItem.desc(orderField)); + } + } + + //没有排序字段,则不排序 + if (StrUtil.isBlank(defaultOrderField)) { + return page; + } + + //默认排序 + if (isAsc) { + page.addOrder(OrderItem.asc(defaultOrderField)); + } else { + page.addOrder(OrderItem.desc(defaultOrderField)); + } + + return page; + } + + protected PageData getPageData(List list, long total, Class target) { + List targetList = ConvertUtils.sourceToTarget(list, target); + + return new PageData<>(targetList, total); + } + + protected PageData getPageData(IPage page, Class target) { + return getPageData(page.getRecords(), page.getTotal(), target); + } + + protected void paramsToLike(Map params, String... likes) { + for (String like : likes) { + String val = (String) params.get(like); + if (StrUtil.isNotBlank(val)) { + params.put(like, "%" + val + "%"); + } else { + params.put(like, null); + } + } + } + + /** + *

+ * 判断数据库操作是否成功 + *

+ *

+ * 注意!! 该方法为 Integer 判断,不可传入 int 基本类型 + *

+ * + * @param result 数据库操作返回影响条数 + * @return boolean + */ + protected static boolean retBool(Integer result) { + return SqlHelper.retBool(result); + } + + protected Class currentMapperClass() { + return (Class) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseServiceImpl.class, 0); + } + + @Override + public Class currentModelClass() { + return (Class) ReflectionKit.getSuperClassGenericType(this.getClass(), BaseServiceImpl.class, 1); + } + + protected String getSqlStatement(SqlMethod sqlMethod) { + return SqlHelper.getSqlStatement(this.currentMapperClass(), sqlMethod); + } + + @Override + public boolean insert(T entity) { + return BaseServiceImpl.retBool(baseDao.insert(entity)); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean insertBatch(Collection entityList) { + return insertBatch(entityList, 100); + } + + /** + * 批量插入 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean insertBatch(Collection entityList, int batchSize) { + String sqlStatement = getSqlStatement(SqlMethod.INSERT_ONE); + return executeBatch(entityList, batchSize, (sqlSession, entity) -> sqlSession.insert(sqlStatement, entity)); + } + + /** + * 执行批量操作 + */ + protected boolean executeBatch(Collection list, int batchSize, BiConsumer consumer) { + return SqlHelper.executeBatch(this.currentModelClass(), this.log, list, batchSize, consumer); + } + + + @Override + public boolean updateById(T entity) { + return BaseServiceImpl.retBool(baseDao.updateById(entity)); + } + + @Override + public boolean update(T entity, Wrapper updateWrapper) { + return BaseServiceImpl.retBool(baseDao.update(entity, updateWrapper)); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean updateBatchById(Collection entityList) { + return updateBatchById(entityList, 30); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean updateBatchById(Collection entityList, int batchSize) { + String sqlStatement = getSqlStatement(SqlMethod.UPDATE_BY_ID); + return executeBatch(entityList, batchSize, (sqlSession, entity) -> { + MapperMethod.ParamMap param = new MapperMethod.ParamMap<>(); + param.put(Constants.ENTITY, entity); + sqlSession.update(sqlStatement, param); + }); + } + + @Override + public T selectById(Serializable id) { + return baseDao.selectById(id); + } + + @Override + public boolean deleteById(Serializable id) { + return SqlHelper.retBool(baseDao.deleteById(id)); + } + + @Override + public boolean deleteBatchIds(Collection idList) { + return SqlHelper.retBool(baseDao.deleteBatchIds(idList)); + } +} \ No newline at end of file diff --git a/system-common/src/main/java/com/weather/common/service/impl/CrudServiceImpl.java b/system-common/src/main/java/com/weather/common/service/impl/CrudServiceImpl.java new file mode 100644 index 0000000..9772cd7 --- /dev/null +++ b/system-common/src/main/java/com/weather/common/service/impl/CrudServiceImpl.java @@ -0,0 +1,73 @@ + +package com.weather.common.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.ReflectionKit; +import com.weather.common.page.PageData; +import com.weather.common.service.CrudService; +import com.weather.common.utils.ConvertUtils; +import org.springframework.beans.BeanUtils; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * CRUD基础服务类 + * + * @author 123 + */ +public abstract class CrudServiceImpl, T, D> extends BaseServiceImpl implements CrudService { + + protected Class currentDtoClass() { + return (Class)ReflectionKit.getSuperClassGenericType(getClass(), CrudServiceImpl.class, 2); + } + + @Override + public PageData page(Map params) { + IPage page = baseDao.selectPage( + getPage(params, null, false), + getWrapper(params) + ); + + return getPageData(page, currentDtoClass()); + } + + @Override + public List list(Map params) { + List entityList = baseDao.selectList(getWrapper(params)); + + return ConvertUtils.sourceToTarget(entityList, currentDtoClass()); + } + + public abstract QueryWrapper getWrapper(Map params); + + @Override + public D get(Long id) { + T entity = baseDao.selectById(id); + + return ConvertUtils.sourceToTarget(entity, currentDtoClass()); + } + + @Override + public void save(D dto) { + T entity = ConvertUtils.sourceToTarget(dto, currentModelClass()); + insert(entity); + + //copy主键值到dto + BeanUtils.copyProperties(entity, dto); + } + + @Override + public void update(D dto) { + T entity = ConvertUtils.sourceToTarget(dto, currentModelClass()); + updateById(entity); + } + + @Override + public void delete(Long[] ids) { + baseDao.deleteByIds(Arrays.asList(ids)); + } +} \ No newline at end of file diff --git a/system-common/src/main/java/com/weather/common/utils/ConvertUtils.java b/system-common/src/main/java/com/weather/common/utils/ConvertUtils.java new file mode 100644 index 0000000..6ea19a1 --- /dev/null +++ b/system-common/src/main/java/com/weather/common/utils/ConvertUtils.java @@ -0,0 +1,54 @@ + + +package com.weather.common.utils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.BeanUtils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * 转换工具类 + * + * @author 123 + */ +public class ConvertUtils { + private static Logger logger = LoggerFactory.getLogger(ConvertUtils.class); + + public static T sourceToTarget(Object source, Class target){ + if(source == null){ + return null; + } + T targetObject = null; + try { + targetObject = target.newInstance(); + BeanUtils.copyProperties(source, targetObject); + } catch (Exception e) { + logger.error("convert error ", e); + } + + return targetObject; + } + + public static List sourceToTarget(Collection sourceList, Class target){ + if(sourceList == null){ + return null; + } + + List targetList = new ArrayList<>(sourceList.size()); + try { + for(Object source : sourceList){ + T targetObject = target.newInstance(); + BeanUtils.copyProperties(source, targetObject); + targetList.add(targetObject); + } + }catch (Exception e){ + logger.error("convert error ", e); + } + + return targetList; + } +} \ No newline at end of file diff --git a/system-common/src/main/java/com/weather/common/utils/DateUtils.java b/system-common/src/main/java/com/weather/common/utils/DateUtils.java new file mode 100644 index 0000000..c886295 --- /dev/null +++ b/system-common/src/main/java/com/weather/common/utils/DateUtils.java @@ -0,0 +1,56 @@ + +package com.weather.common.utils; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * 日期处理 + * + * @author 123 + */ +public class DateUtils { + /** 时间格式(yyyy-MM-dd) */ + public final static String DATE_PATTERN = "yyyy-MM-dd"; + /** 时间格式(yyyy-MM-dd HH:mm:ss) */ + public final static String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss"; + + /** + * 日期格式化 日期格式为:yyyy-MM-dd + * @param date 日期 + * @return 返回yyyy-MM-dd格式日期 + */ + public static String format(Date date) { + return format(date, DATE_PATTERN); + } + + /** + * 日期格式化 日期格式为:yyyy-MM-dd + * @param date 日期 + * @param pattern 格式,如:DateUtils.DATE_TIME_PATTERN + * @return 返回yyyy-MM-dd格式日期 + */ + public static String format(Date date, String pattern) { + if (date != null) { + SimpleDateFormat df = new SimpleDateFormat(pattern); + return df.format(date); + } + return null; + } + + /** + * 日期解析 + * @param date 日期 + * @param pattern 格式,如:DateUtils.DATE_TIME_PATTERN + * @return 返回Date + */ + public static Date parse(String date, String pattern) { + try { + return new SimpleDateFormat(pattern).parse(date); + } catch (ParseException e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/system-common/src/main/java/com/weather/common/utils/HttpContextUtils.java b/system-common/src/main/java/com/weather/common/utils/HttpContextUtils.java new file mode 100644 index 0000000..1b1127e --- /dev/null +++ b/system-common/src/main/java/com/weather/common/utils/HttpContextUtils.java @@ -0,0 +1,57 @@ + + +package com.weather.common.utils; + +import cn.hutool.core.util.StrUtil; +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.http.HttpHeaders; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; + +/** + * Http + * + * @author 123 + */ +public class HttpContextUtils { + + public static HttpServletRequest getHttpServletRequest() { + RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); + if(requestAttributes == null){ + return null; + } + + return ((ServletRequestAttributes) requestAttributes).getRequest(); + } + + public static Map getParameterMap(HttpServletRequest request) { + Enumeration parameters = request.getParameterNames(); + + Map params = new HashMap<>(); + while (parameters.hasMoreElements()) { + String parameter = parameters.nextElement(); + String value = request.getParameter(parameter); + if (StrUtil.isNotBlank(value)) { + params.put(parameter, value); + } + } + + return params; + } + + public static String getDomain(){ + HttpServletRequest request = getHttpServletRequest(); + StringBuffer url = request.getRequestURL(); + return url.delete(url.length() - request.getRequestURI().length(), url.length()).toString(); + } + + public static String getOrigin(){ + HttpServletRequest request = getHttpServletRequest(); + return request.getHeader(HttpHeaders.ORIGIN); + } +} diff --git a/system-common/src/main/java/com/weather/common/utils/IpUtils.java b/system-common/src/main/java/com/weather/common/utils/IpUtils.java new file mode 100644 index 0000000..f4d638f --- /dev/null +++ b/system-common/src/main/java/com/weather/common/utils/IpUtils.java @@ -0,0 +1,52 @@ + + +package com.weather.common.utils; + +import cn.hutool.core.util.StrUtil; +import jakarta.servlet.http.HttpServletRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * IP地址 + * + * @author 123 + */ +public class IpUtils { + private static Logger logger = LoggerFactory.getLogger(IpUtils.class); + + /** + * 获取IP地址 + * + * 使用Nginx等反向代理软件, 则不能通过request.getRemoteAddr()获取IP地址 + * 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,X-Forwarded-For中第一个非unknown的有效IP字符串,则为真实IP地址 + */ + public static String getIpAddr(HttpServletRequest request) { + String unknown = "unknown"; + String ip = null; + try { + ip = request.getHeader("x-forwarded-for"); + if (StrUtil.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); + } + if (StrUtil.isEmpty(ip) || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (StrUtil.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) { + ip = request.getHeader("HTTP_CLIENT_IP"); + } + if (StrUtil.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) { + ip = request.getHeader("HTTP_X_FORWARDED_FOR"); + } + if (StrUtil.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + } catch (Exception e) { + logger.error("IPUtils ERROR ", e); + } + + return ip; + } + +} diff --git a/system-common/src/main/java/com/weather/common/utils/JsonUtils.java b/system-common/src/main/java/com/weather/common/utils/JsonUtils.java new file mode 100644 index 0000000..7b38187 --- /dev/null +++ b/system-common/src/main/java/com/weather/common/utils/JsonUtils.java @@ -0,0 +1,68 @@ +package com.weather.common.utils; + +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.StrUtil; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.util.ArrayList; +import java.util.List; + +/** + * JSON 工具类 + * + * @author 123 + */ +public class JsonUtils { + private static final ObjectMapper objectMapper = new ObjectMapper(); + + public static String toJsonString(Object object) { + try { + return objectMapper.writeValueAsString(object); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static T parseObject(String text, Class clazz) { + if (StrUtil.isEmpty(text)) { + return null; + } + try { + return objectMapper.readValue(text, clazz); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static T parseObject(byte[] bytes, Class clazz) { + if (ArrayUtil.isEmpty(bytes)) { + return null; + } + try { + return objectMapper.readValue(bytes, clazz); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static T parseObject(String text, TypeReference typeReference) { + try { + return objectMapper.readValue(text, typeReference); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static List parseArray(String text, Class clazz) { + if (StrUtil.isEmpty(text)) { + return new ArrayList<>(); + } + try { + return objectMapper.readValue(text, objectMapper.getTypeFactory().constructCollectionType(List.class, clazz)); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + +} diff --git a/system-common/src/main/java/com/weather/common/utils/MessageUtils.java b/system-common/src/main/java/com/weather/common/utils/MessageUtils.java new file mode 100644 index 0000000..b2d5c18 --- /dev/null +++ b/system-common/src/main/java/com/weather/common/utils/MessageUtils.java @@ -0,0 +1,27 @@ + + +package com.weather.common.utils; + +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; + +/** + * 国际化 + * + * @author 123 + * @since 1.0.0 + */ +public class MessageUtils { + private static MessageSource messageSource; + static { + messageSource = (MessageSource)SpringContextUtils.getBean("messageSource"); + } + + public static String getMessage(int code){ + return getMessage(code, new String[0]); + } + + public static String getMessage(int code, String... params){ + return messageSource.getMessage(code+"", params, LocaleContextHolder.getLocale()); + } +} diff --git a/system-common/src/main/java/com/weather/common/utils/Result.java b/system-common/src/main/java/com/weather/common/utils/Result.java new file mode 100644 index 0000000..7d17dc9 --- /dev/null +++ b/system-common/src/main/java/com/weather/common/utils/Result.java @@ -0,0 +1,91 @@ + + +package com.weather.common.utils; + +import com.weather.common.exception.ErrorCode; +import io.swagger.v3.oas.annotations.media.Schema; + +import java.io.Serializable; + +/** + * 响应数据 + * + * @author 123 + * @since 1.0.0 + */ +@Schema(title = "响应") +public class Result implements Serializable { + private static final long serialVersionUID = 1L; + /** + * 编码:0表示成功,其他值表示失败 + */ + @Schema(title = "编码:0表示成功,其他值表示失败") + private int code = 0; + /** + * 消息内容 + */ + @Schema(title = "消息内容") + private String msg = "success"; + /** + * 响应数据 + */ + @Schema(title = "响应数据") + private T data; + + public Result ok(T data) { + this.setData(data); + return this; + } + + public boolean success(){ + return code == 0; + } + + public Result error() { + this.code = ErrorCode.INTERNAL_SERVER_ERROR; + this.msg = MessageUtils.getMessage(this.code); + return this; + } + + public Result error(int code) { + this.code = code; + this.msg = MessageUtils.getMessage(this.code); + return this; + } + + public Result error(int code, String msg) { + this.code = code; + this.msg = msg; + return this; + } + + public Result error(String msg) { + this.code = ErrorCode.INTERNAL_SERVER_ERROR; + this.msg = msg; + return this; + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } +} diff --git a/system-common/src/main/java/com/weather/common/utils/SpringContextUtils.java b/system-common/src/main/java/com/weather/common/utils/SpringContextUtils.java new file mode 100644 index 0000000..5a1abf1 --- /dev/null +++ b/system-common/src/main/java/com/weather/common/utils/SpringContextUtils.java @@ -0,0 +1,49 @@ + + +package com.weather.common.utils; + +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; + +/** + * Spring Context 工具类 + * + * @author 123 + */ +@Component +public class SpringContextUtils implements ApplicationContextAware { + public static ApplicationContext applicationContext; + + @Override + public void setApplicationContext(ApplicationContext applicationContext) + throws BeansException { + SpringContextUtils.applicationContext = applicationContext; + } + + public static Object getBean(String name) { + return applicationContext.getBean(name); + } + + public static T getBean(Class requiredType) { + return applicationContext.getBean(requiredType); + } + + public static T getBean(String name, Class requiredType) { + return applicationContext.getBean(name, requiredType); + } + + public static boolean containsBean(String name) { + return applicationContext.containsBean(name); + } + + public static boolean isSingleton(String name) { + return applicationContext.isSingleton(name); + } + + public static Class getType(String name) { + return applicationContext.getType(name); + } + +} \ No newline at end of file diff --git a/system-common/src/main/java/com/weather/common/utils/TreeNode.java b/system-common/src/main/java/com/weather/common/utils/TreeNode.java new file mode 100644 index 0000000..04f78a9 --- /dev/null +++ b/system-common/src/main/java/com/weather/common/utils/TreeNode.java @@ -0,0 +1,31 @@ + + +package com.weather.common.utils; + +import lombok.Data; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * 树节点,所有需要实现树节点的,都需要继承该类 + * + * @author 123 + * @since 1.0.0 + */ + @Data +public class TreeNode implements Serializable { + private static final long serialVersionUID = 1L; + /** + * 主键 + */ + private Long id; + /** + * 上级ID + */ + private Long pid; + /** + * 子节点列表 + */ + private List children = new ArrayList<>(); +} \ No newline at end of file diff --git a/system-common/src/main/java/com/weather/common/utils/TreeUtils.java b/system-common/src/main/java/com/weather/common/utils/TreeUtils.java new file mode 100644 index 0000000..6f14c27 --- /dev/null +++ b/system-common/src/main/java/com/weather/common/utils/TreeUtils.java @@ -0,0 +1,74 @@ + + +package com.weather.common.utils; + +import com.weather.common.validator.AssertUtils; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * 树形结构工具类,如:菜单、部门等 + * + * @author 123 + * @since 1.0.0 + */ +public class TreeUtils { + + /** + * 根据pid,构建树节点 + */ + public static List build(List treeNodes, Long pid) { + //pid不能为空 + AssertUtils.isNull(pid, "pid"); + + List treeList = new ArrayList<>(); + for(T treeNode : treeNodes) { + if (pid.equals(treeNode.getPid())) { + treeList.add(findChildren(treeNodes, treeNode)); + } + } + + return treeList; + } + + /** + * 查找子节点 + */ + private static T findChildren(List treeNodes, T rootNode) { + for(T treeNode : treeNodes) { + if(rootNode.getId().equals(treeNode.getPid())) { + rootNode.getChildren().add(findChildren(treeNodes, treeNode)); + } + } + return rootNode; + } + + /** + * 构建树节点 + */ + public static List build(List treeNodes) { + List result = new ArrayList<>(); + + //list转map + Map nodeMap = new LinkedHashMap<>(treeNodes.size()); + for(T treeNode : treeNodes){ + nodeMap.put(treeNode.getId(), treeNode); + } + + for(T node : nodeMap.values()) { + T parent = nodeMap.get(node.getPid()); + if(parent != null && !(node.getId().equals(parent.getId()))){ + parent.getChildren().add(node); + continue; + } + + result.add(node); + } + + return result; + } + +} \ No newline at end of file diff --git a/system-common/src/main/java/com/weather/common/validator/AssertUtils.java b/system-common/src/main/java/com/weather/common/validator/AssertUtils.java new file mode 100644 index 0000000..bb1ee47 --- /dev/null +++ b/system-common/src/main/java/com/weather/common/validator/AssertUtils.java @@ -0,0 +1,92 @@ + + +package com.weather.common.validator; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ArrayUtil; +import com.weather.common.exception.ErrorCode; +import com.weather.common.exception.CommonException; +import cn.hutool.core.util.StrUtil; + +import java.util.List; +import java.util.Map; + +/** + * 校验工具类 + * + * @author 123 + * @since 1.0.0 + */ +public class AssertUtils { + + public static void isBlank(String str, String... params) { + isBlank(str, ErrorCode.NOT_NULL, params); + } + + public static void isBlank(String str, Integer code, String... params) { + if(code == null){ + throw new CommonException(ErrorCode.NOT_NULL, "code"); + } + + if (StrUtil.isBlank(str)) { + throw new CommonException(code, params); + } + } + + public static void isNull(Object object, String... params) { + isNull(object, ErrorCode.NOT_NULL, params); + } + + public static void isNull(Object object, Integer code, String... params) { + if(code == null){ + throw new CommonException(ErrorCode.NOT_NULL, "code"); + } + + if (object == null) { + throw new CommonException(code, params); + } + } + + public static void isArrayEmpty(Object[] array, String... params) { + isArrayEmpty(array, ErrorCode.NOT_NULL, params); + } + + public static void isArrayEmpty(Object[] array, Integer code, String... params) { + if(code == null){ + throw new CommonException(ErrorCode.NOT_NULL, "code"); + } + + if(ArrayUtil.isEmpty(array)){ + throw new CommonException(code, params); + } + } + + public static void isListEmpty(List list, String... params) { + isListEmpty(list, ErrorCode.NOT_NULL, params); + } + + public static void isListEmpty(List list, Integer code, String... params) { + if(code == null){ + throw new CommonException(ErrorCode.NOT_NULL, "code"); + } + + if(CollUtil.isEmpty(list)){ + throw new CommonException(code, params); + } + } + + public static void isMapEmpty(Map map, String... params) { + isMapEmpty(map, ErrorCode.NOT_NULL, params); + } + + public static void isMapEmpty(Map map, Integer code, String... params) { + if(code == null){ + throw new CommonException(ErrorCode.NOT_NULL, "code"); + } + + if(MapUtil.isEmpty(map)){ + throw new CommonException(code, params); + } + } +} \ No newline at end of file diff --git a/system-common/src/main/java/com/weather/common/validator/ValidatorUtils.java b/system-common/src/main/java/com/weather/common/validator/ValidatorUtils.java new file mode 100644 index 0000000..707b0b6 --- /dev/null +++ b/system-common/src/main/java/com/weather/common/validator/ValidatorUtils.java @@ -0,0 +1,51 @@ + + +package com.weather.common.validator; + +import com.weather.common.exception.CommonException; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.Validation; +import jakarta.validation.Validator; +import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator; +import org.springframework.context.i18n.LocaleContextHolder; +import org.springframework.context.support.ResourceBundleMessageSource; +import org.springframework.validation.beanvalidation.MessageSourceResourceBundleLocator; + +import java.util.Locale; +import java.util.Set; + +/** + * hibernate-validator校验工具类 + * 参考文档:http://docs.jboss.org/hibernate/validator/6.0/reference/en-US/html_single/ + * + * @author 123 + * @since 1.0.0 + */ +public class ValidatorUtils { + + private static ResourceBundleMessageSource getMessageSource() { + ResourceBundleMessageSource bundleMessageSource = new ResourceBundleMessageSource(); + bundleMessageSource.setDefaultEncoding("UTF-8"); + bundleMessageSource.setBasenames("i18n/validation"); + return bundleMessageSource; + } + + /** + * 校验对象 + * @param object 待校验对象 + * @param groups 待校验的组 + */ + public static void validateEntity(Object object, Class... groups) + throws CommonException { + Locale.setDefault(LocaleContextHolder.getLocale()); + Validator validator = Validation.byDefaultProvider().configure().messageInterpolator( + new ResourceBundleMessageInterpolator(new MessageSourceResourceBundleLocator(getMessageSource()))) + .buildValidatorFactory().getValidator(); + + Set> constraintViolations = validator.validate(object, groups); + if (!constraintViolations.isEmpty()) { + ConstraintViolation constraint = constraintViolations.iterator().next(); + throw new CommonException(constraint.getMessage()); + } + } +} diff --git a/system-common/src/main/java/com/weather/common/validator/group/AddGroup.java b/system-common/src/main/java/com/weather/common/validator/group/AddGroup.java new file mode 100644 index 0000000..fc1d119 --- /dev/null +++ b/system-common/src/main/java/com/weather/common/validator/group/AddGroup.java @@ -0,0 +1,13 @@ + + +package com.weather.common.validator.group; + +/** + * 新增 Group + * + * @author 123 + * @since 1.0.0 + */ +public interface AddGroup { + +} diff --git a/system-common/src/main/java/com/weather/common/validator/group/DefaultGroup.java b/system-common/src/main/java/com/weather/common/validator/group/DefaultGroup.java new file mode 100644 index 0000000..2fc77d1 --- /dev/null +++ b/system-common/src/main/java/com/weather/common/validator/group/DefaultGroup.java @@ -0,0 +1,13 @@ + + +package com.weather.common.validator.group; + +/** + * 默认 Group + * + * @author 123 + * @since 1.0.0 + */ +public interface DefaultGroup { + +} diff --git a/system-common/src/main/java/com/weather/common/validator/group/Group.java b/system-common/src/main/java/com/weather/common/validator/group/Group.java new file mode 100644 index 0000000..9309900 --- /dev/null +++ b/system-common/src/main/java/com/weather/common/validator/group/Group.java @@ -0,0 +1,17 @@ + + +package com.weather.common.validator.group; + + +import jakarta.validation.GroupSequence; + +/** + * 定义校验顺序,如果AddGroup组失败,则UpdateGroup组不会再校验 + * + * @author 123 + * @since 1.0.0 + */ +@GroupSequence({AddGroup.class, UpdateGroup.class}) +public interface Group { + +} diff --git a/system-common/src/main/java/com/weather/common/validator/group/UpdateGroup.java b/system-common/src/main/java/com/weather/common/validator/group/UpdateGroup.java new file mode 100644 index 0000000..25e5287 --- /dev/null +++ b/system-common/src/main/java/com/weather/common/validator/group/UpdateGroup.java @@ -0,0 +1,13 @@ + + +package com.weather.common.validator.group; + +/** + * 修改 Group + * + * @author 123 + * @since 1.0.0 + */ +public interface UpdateGroup { + +} diff --git a/system-common/src/main/java/com/weather/common/xss/SqlFilter.java b/system-common/src/main/java/com/weather/common/xss/SqlFilter.java new file mode 100644 index 0000000..f187f81 --- /dev/null +++ b/system-common/src/main/java/com/weather/common/xss/SqlFilter.java @@ -0,0 +1,45 @@ + +package com.weather.common.xss; + +import cn.hutool.core.util.StrUtil; +import com.weather.common.exception.ErrorCode; +import com.weather.common.exception.CommonException; + +/** + * SQL过滤 + * + * @author 123 + */ +public class SqlFilter { + + /** + * SQL注入过滤 + * + * @param str 待验证的字符串 + */ + public static String sqlInject(String str) { + if (StrUtil.isBlank(str)) { + return null; + } + //去掉'|"|;|\字符 + str = StrUtil.replace(str, "'", ""); + str = StrUtil.replace(str, "\"", ""); + str = StrUtil.replace(str, ";", ""); + str = StrUtil.replace(str, "\\", ""); + + //转换成小写 + str = str.toLowerCase(); + + //非法字符 + String[] keywords = {"master", "truncate", "insert", "select", "delete", "update", "declare", "alter", "drop"}; + + //判断是否包含非法字符 + for (String keyword : keywords) { + if (str.contains(keyword)) { + throw new CommonException(ErrorCode.INVALID_SYMBOL); + } + } + + return str; + } +} diff --git a/system-common/src/main/java/com/weather/common/xss/XssFilter.java b/system-common/src/main/java/com/weather/common/xss/XssFilter.java new file mode 100644 index 0000000..36ebbfd --- /dev/null +++ b/system-common/src/main/java/com/weather/common/xss/XssFilter.java @@ -0,0 +1,32 @@ + + +package com.weather.common.xss; + +import jakarta.servlet.*; +import jakarta.servlet.http.HttpServletRequest; + +import java.io.IOException; + +/** + * XSS过滤 + * @author 123 + */ +public class XssFilter implements Filter { + + @Override + public void init(FilterConfig config) { + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper( + (HttpServletRequest) request); + chain.doFilter(xssRequest, response); + } + + @Override + public void destroy() { + } + +} diff --git a/system-common/src/main/java/com/weather/common/xss/XssHttpServletRequestWrapper.java b/system-common/src/main/java/com/weather/common/xss/XssHttpServletRequestWrapper.java new file mode 100644 index 0000000..829b51c --- /dev/null +++ b/system-common/src/main/java/com/weather/common/xss/XssHttpServletRequestWrapper.java @@ -0,0 +1,148 @@ + + +package com.weather.common.xss; + +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.util.StrUtil; +import jakarta.servlet.ReadListener; +import jakarta.servlet.ServletInputStream; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequestWrapper; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.LinkedHashMap; +import java.util.Map; + + +/** + * XSS过滤处理 + * + * @author 123 + * @since 1.0.0 + */ +public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { + HttpServletRequest orgRequest; + + public XssHttpServletRequestWrapper(HttpServletRequest request) { + super(request); + orgRequest = request; + } + + @Override + public ServletInputStream getInputStream() throws IOException { + //非json类型,直接返回 + if(!checkContentTypeIsJson()){ + return super.getInputStream(); + } + + //为空,直接返回 + String json = IoUtil.readUtf8(super.getInputStream()); + if (StrUtil.isBlank(json)) { + return super.getInputStream(); + } + + //xss过滤 + json = xssEncode(json); + final ByteArrayInputStream bis = new ByteArrayInputStream(json.getBytes(StandardCharsets.UTF_8)); + return new ServletInputStream() { + @Override + public boolean isFinished() { + return true; + } + + @Override + public boolean isReady() { + return true; + } + + @Override + public void setReadListener(ReadListener readListener) { + + } + + @Override + public int read() { + return bis.read(); + } + }; + } + + @Override + public String getParameter(String name) { + String value = super.getParameter(xssEncode(name)); + if (StrUtil.isNotBlank(value)) { + value = xssEncode(value); + } + return value; + } + + @Override + public String[] getParameterValues(String name) { + String[] parameters = super.getParameterValues(name); + if (parameters == null || parameters.length == 0) { + return null; + } + + for (int i = 0; i < parameters.length; i++) { + parameters[i] = xssEncode(parameters[i]); + } + return parameters; + } + + @Override + public Map getParameterMap() { + Map map = new LinkedHashMap<>(); + Map parameters = super.getParameterMap(); + for (String key : parameters.keySet()) { + String[] values = parameters.get(key); + for (int i = 0; i < values.length; i++) { + values[i] = xssEncode(values[i]); + } + map.put(key, values); + } + return map; + } + + @Override + public String getHeader(String name) { + String value = super.getHeader(xssEncode(name)); + if (StrUtil.isNotBlank(value)) { + value = xssEncode(value); + } + return value; + } + + private String xssEncode(String input) { + return XssUtils.filter(input); + } + + /** + * 获取最原始的request + */ + public HttpServletRequest getOrgRequest() { + return orgRequest; + } + + /** + * 获取最原始的request + */ + public static HttpServletRequest getOrgRequest(HttpServletRequest request) { + if (request instanceof XssHttpServletRequestWrapper) { + return ((XssHttpServletRequestWrapper) request).getOrgRequest(); + } + + return request; + } + /** + * 判断是否是json请求,以前缀的方式 + */ + private boolean checkContentTypeIsJson() + { + String header = super.getHeader(HttpHeaders.CONTENT_TYPE); + return StrUtil.startWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE); + } +} diff --git a/system-common/src/main/java/com/weather/common/xss/XssUtils.java b/system-common/src/main/java/com/weather/common/xss/XssUtils.java new file mode 100644 index 0000000..58b573c --- /dev/null +++ b/system-common/src/main/java/com/weather/common/xss/XssUtils.java @@ -0,0 +1,72 @@ + + +package com.weather.common.xss; + +import org.jsoup.Jsoup; +import org.jsoup.safety.Safelist; + +/** + * XSS过滤工具类 + * + * @author 123 + * @since 1.0.0 + */ +public class XssUtils extends Safelist { + + /** + * XSS过滤 + */ + public static String filter(String html){ + return Jsoup.clean(html, xssWhitelist()); + } + + /** + * XSS过滤白名单 + */ + private static Safelist xssWhitelist(){ + return new Safelist() + //支持的标签 + .addTags("a", "b", "blockquote", "br", "caption", "cite", "code", "col", "colgroup", "dd", "div", "dl", + "dt", "em", "h1", "h2", "h3", "h4", "h5", "h6", "i", "img", "li", "ol", "p", "pre", "q", "small", + "strike", "strong","sub", "sup", "table", "tbody", "td","tfoot", "th", "thead", "tr", "u","ul", + "embed","object","param","span") + + //支持的标签属性 + .addAttributes("a", "href", "class", "style", "target", "rel", "nofollow") + .addAttributes("blockquote", "cite") + .addAttributes("code", "class", "style") + .addAttributes("col", "span", "width") + .addAttributes("colgroup", "span", "width") + .addAttributes("img", "align", "alt", "height", "src", "title", "width", "class", "style") + .addAttributes("ol", "start", "type") + .addAttributes("q", "cite") + .addAttributes("table", "summary", "width", "class", "style") + .addAttributes("tr", "abbr", "axis", "colspan", "rowspan", "width", "style") + .addAttributes("td", "abbr", "axis", "colspan", "rowspan", "width", "style") + .addAttributes("th", "abbr", "axis", "colspan", "rowspan", "scope","width", "style") + .addAttributes("ul", "type", "style") + .addAttributes("pre", "class", "style") + .addAttributes("div", "class", "id", "style") + .addAttributes("embed", "src", "wmode", "flashvars", "pluginspage", "allowFullScreen", "allowfullscreen", + "quality", "width", "height", "align", "allowScriptAccess", "allowscriptaccess", "allownetworking", "type") + .addAttributes("object", "type", "id", "name", "data", "width", "height", "style", "classid", "codebase") + .addAttributes("param", "name", "value") + .addAttributes("span", "class", "style") + + //标签属性对应的协议 + .addProtocols("a", "href", "ftp", "http", "https", "mailto") + .addProtocols("img", "src", "http", "https") + .addProtocols("blockquote", "cite", "http", "https") + .addProtocols("cite", "cite", "http", "https") + .addProtocols("q", "cite", "http", "https") + .addProtocols("embed", "src", "http", "https"); + } + + public static void main(String[] args) { + StringBuilder html = new StringBuilder(); + html.append("人人开源"); + + System.out.println(filter(html.toString())); + } + +} \ No newline at end of file diff --git a/system-common/src/main/resources/i18n/messages.properties b/system-common/src/main/resources/i18n/messages.properties new file mode 100644 index 0000000..e4c723f --- /dev/null +++ b/system-common/src/main/resources/i18n/messages.properties @@ -0,0 +1,26 @@ +#Default +500=\u670D\u52A1\u5668\u5185\u90E8\u5F02\u5E38 +401=\u672A\u6388\u6743 +10001={0}\u4E0D\u80FD\u4E3A\u7A7A +10002=\u6570\u636E\u5E93\u4E2D\u5DF2\u5B58\u5728\u8BE5\u8BB0\u5F55 +10003=\u83B7\u53D6\u53C2\u6570\u5931\u8D25 +10004=\u8D26\u53F7\u6216\u5BC6\u7801\u9519\u8BEF +10005=\u8D26\u53F7\u5DF2\u88AB\u505C\u7528 +10006=\u552F\u4E00\u6807\u8BC6\u4E0D\u80FD\u4E3A\u7A7A +10007=\u9A8C\u8BC1\u7801\u4E0D\u6B63\u786E +10008=\u5148\u5220\u9664\u5B50\u83DC\u5355\u6216\u6309\u94AE +10009=\u539F\u5BC6\u7801\u4E0D\u6B63\u786E +10010=\u8D26\u53F7\u4E0D\u5B58\u5728 +10011=\u4E0A\u7EA7\u90E8\u95E8\u9009\u62E9\u9519\u8BEF +10012=\u4E0A\u7EA7\u83DC\u5355\u4E0D\u80FD\u4E3A\u81EA\u8EAB +10013=\u6570\u636E\u6743\u9650\u63A5\u53E3\uFF0C\u53EA\u80FD\u662FMap\u7C7B\u578B\u53C2\u6570 +10014=\u8BF7\u5148\u5220\u9664\u4E0B\u7EA7\u90E8\u95E8 +10015=\u8BF7\u5148\u5220\u9664\u90E8\u95E8\u4E0B\u7684\u7528\u6237 +10019=\u8BF7\u4E0A\u4F20\u6587\u4EF6 +10020=token\u4E0D\u80FD\u4E3A\u7A7A +10021=token\u5931\u6548\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55 +10022=\u8D26\u53F7\u5DF2\u88AB\u9501\u5B9A +10024=\u4E0A\u4F20\u6587\u4EF6\u5931\u8D25{0} +10027=Redis\u670D\u52A1\u5F02\u5E38 +10028=\u5B9A\u65F6\u4EFB\u52A1\u5931\u8D25 +10029=\u4E0D\u80FD\u5305\u542B\u975E\u6CD5\u5B57\u7B26 \ No newline at end of file diff --git a/system-common/src/main/resources/i18n/validation.properties b/system-common/src/main/resources/i18n/validation.properties new file mode 100644 index 0000000..e2c400d --- /dev/null +++ b/system-common/src/main/resources/i18n/validation.properties @@ -0,0 +1,67 @@ +#\u7B80\u4F53\u4E2D\u6587 +id.require=ID\u4E0D\u80FD\u4E3A\u7A7A +id.null=ID\u5FC5\u987B\u4E3A\u7A7A +pid.require=\u4E0A\u7EA7ID\uFF0C\u4E0D\u80FD\u4E3A\u7A7A +sort.number=\u6392\u5E8F\u503C\u4E0D\u80FD\u5C0F\u4E8E0 + +sysparams.paramcode.require=\u53C2\u6570\u7F16\u7801\u4E0D\u80FD\u4E3A\u7A7A +sysparams.paramvalue.require=\u53C2\u6570\u503C\u4E0D\u80FD\u4E3A\u7A7A + +sysuser.username.require=\u7528\u6237\u540D\u4E0D\u80FD\u4E3A\u7A7A +sysuser.password.require=\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A +sysuser.realname.require=\u59D3\u540D\u4E0D\u80FD\u4E3A\u7A7A +sysuser.gender.range=\u6027\u522B\u53D6\u503C\u8303\u56F40~2 +sysuser.email.require=\u90AE\u7BB1\u4E0D\u80FD\u4E3A\u7A7A +sysuser.email.error=\u90AE\u7BB1\u683C\u5F0F\u4E0D\u6B63\u786E +sysuser.mobile.require=\u624B\u673A\u53F7\u4E0D\u80FD\u4E3A\u7A7A +sysuser.deptId.require=\u90E8\u95E8\u4E0D\u80FD\u4E3A\u7A7A +sysuser.superadmin.range=\u8D85\u7EA7\u7BA1\u7406\u5458\u53D6\u503C\u8303\u56F40~1 +sysuser.status.range=\u72B6\u6001\u53D6\u503C\u8303\u56F40~1 +sysuser.captcha.require=\u9A8C\u8BC1\u7801\u4E0D\u80FD\u4E3A\u7A7A +sysuser.uuid.require=\u552F\u4E00\u6807\u8BC6\u4E0D\u80FD\u4E3A\u7A7A + +sysmenu.pid.require=\u8BF7\u9009\u62E9\u4E0A\u7EA7\u83DC\u5355 +sysmenu.name.require=\u83DC\u5355\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A +sysmenu.type.range=\u83DC\u5355\u7C7B\u578B\u53D6\u503C\u8303\u56F40~1 + +sysdept.pid.require=\u8BF7\u9009\u62E9\u4E0A\u7EA7\u90E8\u95E8 +sysdept.name.require=\u90E8\u95E8\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A + +sysrole.name.require=\u89D2\u8272\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A + +sysdict.type.require=\u5B57\u5178\u7C7B\u578B\u4E0D\u80FD\u4E3A\u7A7A +sysdict.name.require=\u5B57\u5178\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A +sysdict.label.require=\u5B57\u5178\u6807\u7B7E\u4E0D\u80FD\u4E3A\u7A7A + +schedule.status.range=\u72B6\u6001\u53D6\u503C\u8303\u56F40~1 +schedule.cron.require=cron\u8868\u8FBE\u5F0F\u4E0D\u80FD\u4E3A\u7A7A +schedule.bean.require=bean\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A + +oss.type.range=\u7C7B\u578B\u53D6\u503C\u8303\u56F41~3 + +aliyun.accesskeyid.require=\u963F\u91CC\u4E91AccessKeyId\u4E0D\u80FD\u4E3A\u7A7A +aliyun.accesskeysecret.require=\u963F\u91CC\u4E91AccessKeySecret\u4E0D\u80FD\u4E3A\u7A7A +aliyun.signname.require=\u963F\u91CC\u4E91\u77ED\u4FE1\u7B7E\u540D\u4E0D\u80FD\u4E3A\u7A7A +aliyun.templatecode.require=\u963F\u91CC\u4E91\u77ED\u4FE1\u6A21\u677F\u4E0D\u80FD\u4E3A\u7A7A +aliyun.domain.require=\u963F\u91CC\u4E91\u7ED1\u5B9A\u7684\u57DF\u540D\u4E0D\u80FD\u4E3A\u7A7A +aliyun.domain.url=\u963F\u91CC\u4E91\u7ED1\u5B9A\u7684\u57DF\u540D\u683C\u5F0F\u4E0D\u6B63\u786E +aliyun.endPoint.require=\u963F\u91CC\u4E91EndPoint\u4E0D\u80FD\u4E3A\u7A7A +aliyun.bucketname.require=\u963F\u91CC\u4E91BucketName\u4E0D\u80FD\u4E3A\u7A7A + +qcloud.appid.require=\u817E\u8BAF\u4E91AppId\u4E0D\u80FD\u4E3A\u7A7A +qcloud.appkey.require=\u817E\u8BAF\u4E91AppKey\u4E0D\u80FD\u4E3A\u7A7A +qcloud.secretId.require=\u817E\u8BAF\u4E91SecretId\u4E0D\u80FD\u4E3A\u7A7A +qcloud.secretkey.require=\u817E\u8BAF\u4E91SecretKey\u4E0D\u80FD\u4E3A\u7A7A +qcloud.signname.require=\u817E\u8BAF\u4E91\u77ED\u4FE1\u7B7E\u540D\u4E0D\u80FD\u4E3A\u7A7A +qcloud.templateid.require=\u817E\u8BAF\u4E91\u77ED\u4FE1\u6A21\u677FID\u4E0D\u80FD\u4E3A\u7A7A +qcloud.domain.require=\u817E\u8BAF\u4E91\u7ED1\u5B9A\u7684\u57DF\u540D\u4E0D\u80FD\u4E3A\u7A7A +qcloud.domain.url=\u817E\u8BAF\u4E91\u7ED1\u5B9A\u7684\u57DF\u540D\u683C\u5F0F\u4E0D\u6B63\u786E +qcloud.bucketname.require=\u817E\u8BAF\u4E91BucketName\u4E0D\u80FD\u4E3A\u7A7A +qcloud.region.require=\u6240\u5C5E\u5730\u533A\u4E0D\u80FD\u4E3A\u7A7A + +qiniu.domain.require=\u4E03\u725B\u7ED1\u5B9A\u7684\u57DF\u540D\u4E0D\u80FD\u4E3A\u7A7A +qiniu.domain.url=\u4E03\u725B\u7ED1\u5B9A\u7684\u57DF\u540D\u683C\u5F0F\u4E0D\u6B63\u786E +qiniu.accesskey.require=\u4E03\u725BAccessKey\u4E0D\u80FD\u4E3A\u7A7A +qiniu.secretkey.require=\u4E03\u725BSecretKey\u4E0D\u80FD\u4E3A\u7A7A +qiniu.bucketname.require=\u4E03\u725B\u7A7A\u95F4\u540D\u4E0D\u80FD\u4E3A\u7A7A +qiniu.templateId.require=\u4E03\u725B\u6A21\u677FID\u4E0D\u80FD\u4E3A\u7A7A diff --git a/system-dynamic-datasource/pom.xml b/system-dynamic-datasource/pom.xml new file mode 100644 index 0000000..48e3e00 --- /dev/null +++ b/system-dynamic-datasource/pom.xml @@ -0,0 +1,16 @@ + + + com.weather + weather-data + 5.5.0 + + 4.0.0 + system-dynamic-datasource + jar + 多数据源 + + + ${project.artifactId} + + + \ No newline at end of file diff --git a/system-dynamic-datasource/src/main/java/com/weather/commons/dynamic/datasource/annotation/DataSource.java b/system-dynamic-datasource/src/main/java/com/weather/commons/dynamic/datasource/annotation/DataSource.java new file mode 100644 index 0000000..fe2909f --- /dev/null +++ b/system-dynamic-datasource/src/main/java/com/weather/commons/dynamic/datasource/annotation/DataSource.java @@ -0,0 +1,19 @@ + + +package com.weather.commons.dynamic.datasource.annotation; + +import java.lang.annotation.*; + +/** + * 多数据源注解 + * + * @author 123 + * @since 1.0.0 + */ +@Target({ElementType.METHOD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +public @interface DataSource { + String value() default ""; +} diff --git a/system-dynamic-datasource/src/main/java/com/weather/commons/dynamic/datasource/aspect/DataSourceAspect.java b/system-dynamic-datasource/src/main/java/com/weather/commons/dynamic/datasource/aspect/DataSourceAspect.java new file mode 100644 index 0000000..f75d7f7 --- /dev/null +++ b/system-dynamic-datasource/src/main/java/com/weather/commons/dynamic/datasource/aspect/DataSourceAspect.java @@ -0,0 +1,64 @@ + +package com.weather.commons.dynamic.datasource.aspect; + +import com.weather.commons.dynamic.datasource.annotation.DataSource; +import com.weather.commons.dynamic.datasource.config.DynamicContextHolder; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Method; + +/** + * 多数据源,切面处理类 + * + * @author 123 + * @since 1.0.0 + */ +@Aspect +@Component +@Order(Ordered.HIGHEST_PRECEDENCE) +public class DataSourceAspect { + protected Logger logger = LoggerFactory.getLogger(getClass()); + + @Pointcut("@annotation(com.weather.commons.dynamic.datasource.annotation.DataSource) " + + "|| @within(com.weather.commons.dynamic.datasource.annotation.DataSource)") + public void dataSourcePointCut() { + + } + + @Around("dataSourcePointCut()") + public Object around(ProceedingJoinPoint point) throws Throwable { + MethodSignature signature = (MethodSignature) point.getSignature(); + Class targetClass = point.getTarget().getClass(); + Method method = signature.getMethod(); + + DataSource targetDataSource = (DataSource) targetClass.getAnnotation(DataSource.class); + DataSource methodDataSource = method.getAnnotation(DataSource.class); + if (targetDataSource != null || methodDataSource != null) { + String value; + if (methodDataSource != null) { + value = methodDataSource.value(); + } else { + value = targetDataSource.value(); + } + + DynamicContextHolder.push(value); + logger.debug("set datasource is {}", value); + } + + try { + return point.proceed(); + } finally { + DynamicContextHolder.poll(); + logger.debug("clean datasource"); + } + } +} \ No newline at end of file diff --git a/system-dynamic-datasource/src/main/java/com/weather/commons/dynamic/datasource/config/DynamicContextHolder.java b/system-dynamic-datasource/src/main/java/com/weather/commons/dynamic/datasource/config/DynamicContextHolder.java new file mode 100644 index 0000000..7dc9d6e --- /dev/null +++ b/system-dynamic-datasource/src/main/java/com/weather/commons/dynamic/datasource/config/DynamicContextHolder.java @@ -0,0 +1,44 @@ + +package com.weather.commons.dynamic.datasource.config; + +import java.util.ArrayDeque; +import java.util.Deque; + +/** + * 多数据源上下文 + * + * @author 123 + */ +public class DynamicContextHolder { + private static final ThreadLocal> CONTEXT_HOLDER = ThreadLocal.withInitial(ArrayDeque::new); + + /** + * 获得当前线程数据源 + * + * @return 数据源名称 + */ + public static String peek() { + return CONTEXT_HOLDER.get().peek(); + } + + /** + * 设置当前线程数据源 + * + * @param dataSource 数据源名称 + */ + public static void push(String dataSource) { + CONTEXT_HOLDER.get().push(dataSource); + } + + /** + * 清空当前线程数据源 + */ + public static void poll() { + Deque deque = CONTEXT_HOLDER.get(); + deque.poll(); + if (deque.isEmpty()) { + CONTEXT_HOLDER.remove(); + } + } + +} \ No newline at end of file diff --git a/system-dynamic-datasource/src/main/java/com/weather/commons/dynamic/datasource/config/DynamicDataSource.java b/system-dynamic-datasource/src/main/java/com/weather/commons/dynamic/datasource/config/DynamicDataSource.java new file mode 100644 index 0000000..b73d4a4 --- /dev/null +++ b/system-dynamic-datasource/src/main/java/com/weather/commons/dynamic/datasource/config/DynamicDataSource.java @@ -0,0 +1,20 @@ + + +package com.weather.commons.dynamic.datasource.config; + +import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; + +/** + * 多数据源 + * + * @author 123 + * @since 1.0.0 + */ +public class DynamicDataSource extends AbstractRoutingDataSource { + + @Override + protected Object determineCurrentLookupKey() { + return DynamicContextHolder.peek(); + } + +} diff --git a/system-dynamic-datasource/src/main/java/com/weather/commons/dynamic/datasource/config/DynamicDataSourceConfig.java b/system-dynamic-datasource/src/main/java/com/weather/commons/dynamic/datasource/config/DynamicDataSourceConfig.java new file mode 100644 index 0000000..b78d3cb --- /dev/null +++ b/system-dynamic-datasource/src/main/java/com/weather/commons/dynamic/datasource/config/DynamicDataSourceConfig.java @@ -0,0 +1,57 @@ + +package com.weather.commons.dynamic.datasource.config; + +import com.alibaba.druid.pool.DruidDataSource; +import com.weather.commons.dynamic.datasource.properties.DataSourceProperties; +import com.weather.commons.dynamic.datasource.properties.DynamicDataSourceProperties; +import jakarta.annotation.Resource; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.HashMap; +import java.util.Map; + +/** + * 配置多数据源 + * + * @author 123 + * @since 1.0.0 + */ +@Configuration +@EnableConfigurationProperties(DynamicDataSourceProperties.class) +public class DynamicDataSourceConfig { + @Resource + private DynamicDataSourceProperties properties; + + @Bean + @ConfigurationProperties(prefix = "spring.datasource.druid") + public DataSourceProperties dataSourceProperties() { + return new DataSourceProperties(); + } + + @Bean + public DynamicDataSource dynamicDataSource(DataSourceProperties dataSourceProperties) { + DynamicDataSource dynamicDataSource = new DynamicDataSource(); + dynamicDataSource.setTargetDataSources(getDynamicDataSource()); + + //默认数据源 + DruidDataSource defaultDataSource = DynamicDataSourceFactory.buildDruidDataSource(dataSourceProperties); + dynamicDataSource.setDefaultTargetDataSource(defaultDataSource); + + return dynamicDataSource; + } + + private Map getDynamicDataSource() { + Map dataSourcePropertiesMap = properties.getDatasource(); + Map targetDataSources = new HashMap<>(dataSourcePropertiesMap.size()); + dataSourcePropertiesMap.forEach((k, v) -> { + DruidDataSource druidDataSource = DynamicDataSourceFactory.buildDruidDataSource(v); + targetDataSources.put(k, druidDataSource); + }); + + return targetDataSources; + } + +} diff --git a/system-dynamic-datasource/src/main/java/com/weather/commons/dynamic/datasource/config/DynamicDataSourceFactory.java b/system-dynamic-datasource/src/main/java/com/weather/commons/dynamic/datasource/config/DynamicDataSourceFactory.java new file mode 100644 index 0000000..b083b39 --- /dev/null +++ b/system-dynamic-datasource/src/main/java/com/weather/commons/dynamic/datasource/config/DynamicDataSourceFactory.java @@ -0,0 +1,48 @@ + + +package com.weather.commons.dynamic.datasource.config; + +import com.alibaba.druid.pool.DruidDataSource; +import com.weather.commons.dynamic.datasource.properties.DataSourceProperties; + +import java.sql.SQLException; + +/** + * DruidDataSource + * + * @author 123 + * @since 1.0.0 + */ +public class DynamicDataSourceFactory { + + public static DruidDataSource buildDruidDataSource(DataSourceProperties properties) { + DruidDataSource druidDataSource = new DruidDataSource(); + druidDataSource.setDriverClassName(properties.getDriverClassName()); + druidDataSource.setUrl(properties.getUrl()); + druidDataSource.setUsername(properties.getUsername()); + druidDataSource.setPassword(properties.getPassword()); + + druidDataSource.setInitialSize(properties.getInitialSize()); + druidDataSource.setMaxActive(properties.getMaxActive()); + druidDataSource.setMinIdle(properties.getMinIdle()); + druidDataSource.setMaxWait(properties.getMaxWait()); + druidDataSource.setTimeBetweenEvictionRunsMillis(properties.getTimeBetweenEvictionRunsMillis()); + druidDataSource.setMinEvictableIdleTimeMillis(properties.getMinEvictableIdleTimeMillis()); + druidDataSource.setMaxEvictableIdleTimeMillis(properties.getMaxEvictableIdleTimeMillis()); + druidDataSource.setValidationQuery(properties.getValidationQuery()); + druidDataSource.setValidationQueryTimeout(properties.getValidationQueryTimeout()); + druidDataSource.setTestOnBorrow(properties.isTestOnBorrow()); + druidDataSource.setTestOnReturn(properties.isTestOnReturn()); + druidDataSource.setPoolPreparedStatements(properties.isPoolPreparedStatements()); + druidDataSource.setMaxOpenPreparedStatements(properties.getMaxOpenPreparedStatements()); + druidDataSource.setSharePreparedStatements(properties.isSharePreparedStatements()); + + try { + // druidDataSource.setFilters(properties.getFilters()); + druidDataSource.init(); + } catch (SQLException e) { + e.printStackTrace(); + } + return druidDataSource; + } +} \ No newline at end of file diff --git a/system-dynamic-datasource/src/main/java/com/weather/commons/dynamic/datasource/properties/DataSourceProperties.java b/system-dynamic-datasource/src/main/java/com/weather/commons/dynamic/datasource/properties/DataSourceProperties.java new file mode 100644 index 0000000..8f4b336 --- /dev/null +++ b/system-dynamic-datasource/src/main/java/com/weather/commons/dynamic/datasource/properties/DataSourceProperties.java @@ -0,0 +1,196 @@ + + +package com.weather.commons.dynamic.datasource.properties; + +/** + * 多数据源属性 + * + * @author 123 + * @since 1.0.0 + */ +public class DataSourceProperties { + private String driverClassName; + private String url; + private String username; + private String password; + + /** + * Druid默认参数 + */ + private int initialSize = 2; + private int maxActive = 10; + private int minIdle = -1; + private long maxWait = 60 * 1000L; + private long timeBetweenEvictionRunsMillis = 60 * 1000L; + private long minEvictableIdleTimeMillis = 1000L * 60L * 30L; + private long maxEvictableIdleTimeMillis = 1000L * 60L * 60L * 7; + private String validationQuery = "select 1"; + private int validationQueryTimeout = -1; + private boolean testOnBorrow = false; + private boolean testOnReturn = false; + private boolean testWhileIdle = true; + private boolean poolPreparedStatements = false; + private int maxOpenPreparedStatements = -1; + private boolean sharePreparedStatements = false; + private String filters = "stat,wall"; + + public String getDriverClassName() { + return driverClassName; + } + + public void setDriverClassName(String driverClassName) { + this.driverClassName = driverClassName; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public int getInitialSize() { + return initialSize; + } + + public void setInitialSize(int initialSize) { + this.initialSize = initialSize; + } + + public int getMaxActive() { + return maxActive; + } + + public void setMaxActive(int maxActive) { + this.maxActive = maxActive; + } + + public int getMinIdle() { + return minIdle; + } + + public void setMinIdle(int minIdle) { + this.minIdle = minIdle; + } + + public long getMaxWait() { + return maxWait; + } + + public void setMaxWait(long maxWait) { + this.maxWait = maxWait; + } + + public long getTimeBetweenEvictionRunsMillis() { + return timeBetweenEvictionRunsMillis; + } + + public void setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) { + this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis; + } + + public long getMinEvictableIdleTimeMillis() { + return minEvictableIdleTimeMillis; + } + + public void setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis) { + this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis; + } + + public long getMaxEvictableIdleTimeMillis() { + return maxEvictableIdleTimeMillis; + } + + public void setMaxEvictableIdleTimeMillis(long maxEvictableIdleTimeMillis) { + this.maxEvictableIdleTimeMillis = maxEvictableIdleTimeMillis; + } + + public String getValidationQuery() { + return validationQuery; + } + + public void setValidationQuery(String validationQuery) { + this.validationQuery = validationQuery; + } + + public int getValidationQueryTimeout() { + return validationQueryTimeout; + } + + public void setValidationQueryTimeout(int validationQueryTimeout) { + this.validationQueryTimeout = validationQueryTimeout; + } + + public boolean isTestOnBorrow() { + return testOnBorrow; + } + + public void setTestOnBorrow(boolean testOnBorrow) { + this.testOnBorrow = testOnBorrow; + } + + public boolean isTestOnReturn() { + return testOnReturn; + } + + public void setTestOnReturn(boolean testOnReturn) { + this.testOnReturn = testOnReturn; + } + + public boolean isTestWhileIdle() { + return testWhileIdle; + } + + public void setTestWhileIdle(boolean testWhileIdle) { + this.testWhileIdle = testWhileIdle; + } + + public boolean isPoolPreparedStatements() { + return poolPreparedStatements; + } + + public void setPoolPreparedStatements(boolean poolPreparedStatements) { + this.poolPreparedStatements = poolPreparedStatements; + } + + public int getMaxOpenPreparedStatements() { + return maxOpenPreparedStatements; + } + + public void setMaxOpenPreparedStatements(int maxOpenPreparedStatements) { + this.maxOpenPreparedStatements = maxOpenPreparedStatements; + } + + public boolean isSharePreparedStatements() { + return sharePreparedStatements; + } + + public void setSharePreparedStatements(boolean sharePreparedStatements) { + this.sharePreparedStatements = sharePreparedStatements; + } + + public String getFilters() { + return filters; + } + + public void setFilters(String filters) { + this.filters = filters; + } +} \ No newline at end of file diff --git a/system-dynamic-datasource/src/main/java/com/weather/commons/dynamic/datasource/properties/DynamicDataSourceProperties.java b/system-dynamic-datasource/src/main/java/com/weather/commons/dynamic/datasource/properties/DynamicDataSourceProperties.java new file mode 100644 index 0000000..5b30531 --- /dev/null +++ b/system-dynamic-datasource/src/main/java/com/weather/commons/dynamic/datasource/properties/DynamicDataSourceProperties.java @@ -0,0 +1,27 @@ + + +package com.weather.commons.dynamic.datasource.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * 多数据源属性 + * + * @author 123 + * @since 1.0.0 + */ +@ConfigurationProperties(prefix = "dynamic") +public class DynamicDataSourceProperties { + private Map datasource = new LinkedHashMap<>(); + + public Map getDatasource() { + return datasource; + } + + public void setDatasource(Map datasource) { + this.datasource = datasource; + } +} diff --git a/需求文档.md b/需求文档.md new file mode 100644 index 0000000..3e9fb5e --- /dev/null +++ b/需求文档.md @@ -0,0 +1,74 @@ +#### 要求: + +1. 在sys_parms中构建一个参数设置为总的扫描路径 +2. 依据现有部门列表在总扫描路径下构建以部门名命名的子文件夹 +3. 定时任务和Java WatchService读取部门的扫描路径属性来读取对应的文件(Java WatchService可以作为主要手段,定时任务作为辅助手段) +4. 项目运行时候需要判断路径是否存在,如果存在则读取文件,如果不存在则需要创建对应的路径 +5. 构建对应的数据库表来记录文件的信息,摘取文件名,提取关键字等操作 +6. 表中应该记录文件本身创建的时间,扫描进库的时间,md5值等信息 +7. 如何在不构建对象存储的情况下构建文件的访问链接且支持局域网访问(base64会导致响应体积庞大因此不采用) + +#### 文件格式清单(分隔符不会发生变动,且文件严格按照下方格式构建): + +``` +XX地区-累计降雨量.png +XX地区-累计降雨量_全年.png +XX地区-日平均降雨量.png +XX地区-日平均降雨量_全年.png +XX地区-日平均气温.png +XX地区-日平均气温_全年.png +XX地区-月降雨量图.png +XX地区-月平均气温图.png +XX地区631信息.txt +``` +扫描文件夹结构 + +``` +扫描总路径/ + ├── 部门文件夹1 + | ├── 各个文件 + ├── 部门文件夹2 + | ├── 各个文件 + └── 部门文件夹3 + ├── 各个文件 + + +``` + +展示文件夹结构 + +``` +展示路径/ + ├── 部门文件夹1 + | ├── 各个文件 + ├── 部门文件夹2 + | ├── 各个文件 + └── 部门文件夹3 + ├── 各个文件 +``` + +归档文件夹结构 + +``` +展示路径/ + ├── 日期1 + ├── 部门文件夹1/ + | ├── 各个文件 + ├── 部门文件夹2/ + | ├── 各个文件 + └── 部门文件夹3/ + ├── 各个文件 + ├── 日期1 + ├── 部门文件夹1/ + | ├── 各个文件 + ├── 部门文件夹2/ + | ├── 各个文件 + └── 部门文件夹3/ + ├── 各个文件 +``` + + + + + +