Compare commits
186 Commits
zzl
...
14c1d9dff0
| Author | SHA1 | Date | |
|---|---|---|---|
| 14c1d9dff0 | |||
| 5254a46de8 | |||
| 12ef1210bc | |||
| d1821bbfcf | |||
| 2fbbf9b0bb | |||
| 427e4b0c84 | |||
| 2404281bff | |||
| 2dafd44c26 | |||
| cfe36dfc8b | |||
| 6b97685165 | |||
| 0797785176 | |||
| fd6563f53b | |||
| ddfa52a2ac | |||
| 37d0ebc21e | |||
| 993f802111 | |||
| 4c96583701 | |||
| 8485efe00d | |||
| b02aff8ff6 | |||
| 37309b24f9 | |||
| 56c7bc020a | |||
| af1430eaee | |||
| 86adb47392 | |||
| 780cd56ffe | |||
| 8cf9140f56 | |||
| 304061774e | |||
| 98697af6df | |||
| 2f56ca07cb | |||
| 6987a0e790 | |||
| 0bfc9cbc86 | |||
| 9668f6059a | |||
| d5e7787e89 | |||
| aa2084090a | |||
| a138f3b916 | |||
| cc33bc5ef2 | |||
| 380fd117c5 | |||
| a2e23ea264 | |||
| 3b26591f9d | |||
| 8d13978e84 | |||
| a2d9cfec02 | |||
| 01993779a7 | |||
| 0fa0eae43c | |||
| 8207afe04f | |||
| cbc7e16453 | |||
| ad496fdf28 | |||
| b182b700b3 | |||
| 8a79f4d428 | |||
| d1b4f286b2 | |||
| 4d412e90ee | |||
| af3b6982fd | |||
| 0d1f14f703 | |||
| 087d89b31a | |||
| 70e44da789 | |||
| 940f0b9b9f | |||
| 2262c39f60 | |||
| 2da4d824fd | |||
| aa8b7ade99 | |||
| 93c7e6f8f5 | |||
| e7d7e61846 | |||
| 049a46227d | |||
| 63af35a333 | |||
| 0ce701ab90 | |||
| bdafb83583 | |||
| f370f56a92 | |||
| b4a9e30287 | |||
| a3eeb24f4f | |||
| 4e9bb19b23 | |||
| 7afd243a80 | |||
| 4d34525b7b | |||
| 2e7ab6f508 | |||
| 397e9f3b62 | |||
| 14a3c23c9d | |||
| 90afc3c94c | |||
| c725aaf203 | |||
| 809b4eb63a | |||
| 7f87cd8c27 | |||
| a8d866231b | |||
| 88b04b7dac | |||
| 25fee0656d | |||
| e5b8ff5d4c | |||
| 3edfb72d7d | |||
| 1363a526ff | |||
| a4599c48c5 | |||
| d1ef6eb97b | |||
| 35309b32ae | |||
| 27ed35f9dd | |||
| 12996eb739 | |||
| a2b604f3a3 | |||
| a029463f82 | |||
| 3a655e40a1 | |||
| 81f49babd0 | |||
| 15dc0791f1 | |||
| 1be1a18ea8 | |||
| cce9ac0f3d | |||
| 171887811c | |||
| b750f6ecf0 | |||
| 792a988c11 | |||
| 8075d3bf4d | |||
| fa58ce052d | |||
| 4e1a162660 | |||
| b8c210fb6b | |||
| 9a4be16c05 | |||
| 4698ca34fb | |||
| 71114f73dc | |||
| 172098bb59 | |||
| 19949d102a | |||
| 8ad8b2df90 | |||
| 8c5704caba | |||
| 79f4c35235 | |||
| 3e265fc71e | |||
| 7683ad804a | |||
| bd2c53fc15 | |||
| 762d6e4768 | |||
| 35b3d1b0ed | |||
| 8beb542c98 | |||
| df6d417e40 | |||
| 8bb18c6f6a | |||
| e7ca06736c | |||
| 83fb67a142 | |||
| 154cc4a7cb | |||
| c8b8b59c50 | |||
| 5aceb6e893 | |||
| 6c64b32a4a | |||
| cb425028c0 | |||
| 5c4e66edf7 | |||
| b62247e83f | |||
| 2aefc21822 | |||
| 176035ab95 | |||
| 6c74983707 | |||
| 98cd7ba4cf | |||
| 1ca36ae153 | |||
| 2373d62ceb | |||
| 8e06aa3f68 | |||
| 323ce61136 | |||
| 31dbd731dd | |||
| 2441fb3023 | |||
| 4571a7fce0 | |||
| 1109fe777c | |||
| 4ad3108c4d | |||
| 7f31952bff | |||
| 4e7e354349 | |||
| 1029bcd209 | |||
| 4de1d35c2c | |||
| 8602262e16 | |||
| e1fb293c57 | |||
| 570aa77f4e | |||
| 0aae137e7b | |||
| 301cb3b4b1 | |||
| 0b8aecc49b | |||
| 0d247afef3 | |||
| 31f8e07ffe | |||
| f692d8e275 | |||
| 1e1a817081 | |||
| cd82edbaf6 | |||
| 0ec904f68d | |||
| 044b5dc3f8 | |||
| 21b2884bad | |||
| ed0e61563c | |||
| de5cecd0cd | |||
| ceafeba393 | |||
| 5035f75fd7 | |||
| f716b3fc63 | |||
| a1eea5075b | |||
| 28eb484a68 | |||
| 58726a675e | |||
| d47d6b4b23 | |||
| 2079fa5fb0 | |||
| 78b430bed8 | |||
| 62a110b6de | |||
| 30c51ba271 | |||
| 454ca91760 | |||
| e03b554473 | |||
| 887db5c8d0 | |||
| 9419439668 | |||
| 27b1c5543d | |||
| b5246d4e58 | |||
| 221825ebf9 | |||
| 74416dabaf | |||
| 4b6908d3bf | |||
| f0b515c2e0 | |||
| fd98744b46 | |||
| af8c9ac2e6 | |||
| 0970af239d | |||
| 35afec99f6 | |||
| af06f725fa | |||
| be59acd0df | |||
| d4ef46bf97 |
@@ -195,6 +195,7 @@ Entity 基类的选择**直接决定** Service 和 ServiceImpl 的继承方式
|
|||||||
- 简单查询使用 `LambdaQueryWrapper`,复杂查询写在 Mapper XML 中
|
- 简单查询使用 `LambdaQueryWrapper`,复杂查询写在 Mapper XML 中
|
||||||
- Mapper XML 与接口**同包**放置(`src/main/java` 下)
|
- Mapper XML 与接口**同包**放置(`src/main/java` 下)
|
||||||
- 分页统一使用 `Condition.getPage(query)` + `Condition.getQueryWrapper()`
|
- 分页统一使用 `Condition.getPage(query)` + `Condition.getQueryWrapper()`
|
||||||
|
- 表格的排序默认按照创建时间降序;后端分页 SQL 或 QueryWrapper 应默认使用 `create_time DESC` / `orderByDesc(Xxx::getCreateTime)`,除非用户明确要求业务字段优先排序。
|
||||||
- 禁止 JDBC 直连查询
|
- 禁止 JDBC 直连查询
|
||||||
|
|
||||||
### 6.9 审计字段展示
|
### 6.9 审计字段展示
|
||||||
@@ -204,7 +205,26 @@ Entity 基类的选择**直接决定** Service 和 ServiceImpl 的继承方式
|
|||||||
- Entity → VO 转换统一在 `XxxWrapper` 中完成;审计人姓名通过 `UserCache.getUserRealName(userId)` 获取,优先返回用户真实姓名,缓存未命中时兜底返回用户 ID。
|
- Entity → VO 转换统一在 `XxxWrapper` 中完成;审计人姓名通过 `UserCache.getUserRealName(userId)` 获取,优先返回用户真实姓名,缓存未命中时兜底返回用户 ID。
|
||||||
- Controller 不应在列表、详情方法中重复编写审计人翻译逻辑,避免各模块显示规则不一致。
|
- Controller 不应在列表、详情方法中重复编写审计人翻译逻辑,避免各模块显示规则不一致。
|
||||||
|
|
||||||
### 6.10 日志
|
### 6.10 导出数据格式
|
||||||
|
|
||||||
|
- 导出 Excel、CSV 等文件时,金额类字段必须保留 2 位小数;即使原始数据为整数,也应导出为 `0.00` 形式。
|
||||||
|
- 导出经度、纬度字段时必须保留 6 位小数;导出前仍需遵守经度 `-180 ~ 180`、纬度 `-90 ~ 90` 的有效范围约束。
|
||||||
|
- 精度格式化应在导出 DTO/Excel 模型、导出组装逻辑或专用格式化工具中完成,禁止只依赖前端展示格式。
|
||||||
|
|
||||||
|
### 6.11 导入失败明细导出
|
||||||
|
|
||||||
|
- 所有 Excel 批量导入功能如果存在部分数据无法导入、校验失败或处理异常,后端必须将导入失败的数据导出为 Excel 文件返回给前端下载。
|
||||||
|
- 导入失败 Excel 的字段顺序必须与原导入模板保持一致,并在最后一列追加“导入失败原因”;失败原因应包含明确行号和可读错误信息。
|
||||||
|
- 导入失败 Excel 中仅出错字段/列对应的单元格使用红色文字展示,最后一列“导入失败原因”的错误信息也必须使用红色文字展示;不得将整行失败数据全部标红,表头可保持默认样式。
|
||||||
|
- 失败原因应尽量包含原导入模板中的列名,便于通用导出工具准确定位并标红对应错误单元格;无法定位具体字段的业务错误,仅标红“导入失败原因”列。
|
||||||
|
- 导入失败 Excel 的表头列宽必须按表头和内容自适应,表头文字不得换行。
|
||||||
|
- 后端导入校验必须与前端新增、编辑表单校验保持一致,包括必填、长度、格式、枚举范围、父子级联关系和金额/日期等业务规则;前端校验调整时,必须同步更新对应导入校验。
|
||||||
|
- 导入模板本身不得包含失败原因列;如复用导入模型,应使用 `@ExcelIgnore` 忽略内部错误字段,或单独定义 `XxxImportFailureExcel` 模型。
|
||||||
|
- 导入逻辑应逐行处理:可成功导入的数据正常保存,失败行收集到失败明细;除非业务明确要求全量事务回滚,不得因部分失败回滚已成功行。
|
||||||
|
- Controller 在失败明细非空时应直接通过导入失败明细通用导出工具写入 `HttpServletResponse`,文件名统一包含业务名称、`导入失败明细` 和时间戳;全部成功时返回标准 `R.success`。
|
||||||
|
- Service 层应返回失败明细列表或等效结构,禁止只抛出拼接后的错误字符串导致前端无法下载失败数据。
|
||||||
|
|
||||||
|
### 6.12 日志
|
||||||
|
|
||||||
- 使用 `@Slf4j`,占位符传参(禁止字符串拼接)
|
- 使用 `@Slf4j`,占位符传参(禁止字符串拼接)
|
||||||
- 包含关键业务标识,异常必须携带堆栈,禁止打印敏感信息
|
- 包含关键业务标识,异常必须携带堆栈,禁止打印敏感信息
|
||||||
@@ -344,3 +364,4 @@ id parent_id code name alias path source sort category action is_open component
|
|||||||
2075431753298714626 2075431169720033282 airport_master_status 修改状态 airport_master_status 1 2 0 1 0
|
2075431753298714626 2075431169720033282 airport_master_status 修改状态 airport_master_status 1 2 0 1 0
|
||||||
2075431820189474818 2075431169720033282 airport_master_add 新增 airport_master_add 1 2 0 1 0
|
2075431820189474818 2075431169720033282 airport_master_add 新增 airport_master_add 1 2 0 1 0
|
||||||
```
|
```
|
||||||
|
2.生成的表、字段的排序规则要使用utf8mb4_general_ci
|
||||||
|
|||||||
@@ -25,6 +25,10 @@
|
|||||||
<groupId>org.springblade</groupId>
|
<groupId>org.springblade</groupId>
|
||||||
<artifactId>blade-scope-api</artifactId>
|
<artifactId>blade-scope-api</artifactId>
|
||||||
</exclusion>
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>spring-cloud-starter-bootstrap</artifactId>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
</exclusion>
|
||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -59,10 +63,22 @@
|
|||||||
<groupId>org.springblade</groupId>
|
<groupId>org.springblade</groupId>
|
||||||
<artifactId>blade-user-api</artifactId>
|
<artifactId>blade-user-api</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springblade</groupId>
|
||||||
|
<artifactId>blade-dict-api</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springblade</groupId>
|
<groupId>org.springblade</groupId>
|
||||||
<artifactId>blade-system-api</artifactId>
|
<artifactId>blade-system-api</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springblade</groupId>
|
||||||
|
<artifactId>blade-mk-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springblade</groupId>
|
||||||
|
<artifactId>blade-wechat-api</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springblade</groupId>
|
<groupId>org.springblade</groupId>
|
||||||
<artifactId>blade-resource-api</artifactId>
|
<artifactId>blade-resource-api</artifactId>
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ package org.springblade.auth;
|
|||||||
import org.springblade.core.cloud.client.BladeCloudApplication;
|
import org.springblade.core.cloud.client.BladeCloudApplication;
|
||||||
import org.springblade.core.launch.BladeApplication;
|
import org.springblade.core.launch.BladeApplication;
|
||||||
import org.springblade.core.launch.constant.AppConstant;
|
import org.springblade.core.launch.constant.AppConstant;
|
||||||
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
|
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,9 +39,12 @@ import org.springframework.session.data.redis.config.annotation.web.http.EnableR
|
|||||||
*/
|
*/
|
||||||
@EnableRedisHttpSession
|
@EnableRedisHttpSession
|
||||||
@BladeCloudApplication
|
@BladeCloudApplication
|
||||||
|
@ComponentScan({"org.springblade.auth", "org.springblade.**.feign"})
|
||||||
public class AuthApplication {
|
public class AuthApplication {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
// 禁用框架注入的nacos import配置、config、discovery 配置
|
||||||
|
BladeApplication.disableNacosLaunchConfig();
|
||||||
BladeApplication.run(AppConstant.APPLICATION_AUTH_NAME, AuthApplication.class, args);
|
BladeApplication.run(AppConstant.APPLICATION_AUTH_NAME, AuthApplication.class, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.springblade.auth.config;
|
package org.springblade.auth.config;
|
||||||
|
|
||||||
|
import org.springblade.auth.filter.LogoutLogFilter;
|
||||||
import org.springblade.auth.granter.IamAwareTokenGranterFactory;
|
import org.springblade.auth.granter.IamAwareTokenGranterFactory;
|
||||||
import org.springblade.auth.handler.BladeAuthorizationHandler;
|
import org.springblade.auth.handler.BladeAuthorizationHandler;
|
||||||
import org.springblade.auth.handler.BladeLockHandler;
|
import org.springblade.auth.handler.BladeLockHandler;
|
||||||
@@ -53,9 +54,11 @@ import org.springblade.core.tenant.BladeTenantProperties;
|
|||||||
import org.springblade.system.feign.IUserClient;
|
import org.springblade.system.feign.IUserClient;
|
||||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Primary;
|
import org.springframework.context.annotation.Primary;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -88,6 +91,16 @@ public class BladeAuthConfiguration {
|
|||||||
return new BladeLogHandler(authLogClient, bladeProperties, serverInfo);
|
return new BladeLogHandler(authLogClient, bladeProperties, serverInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public FilterRegistrationBean<LogoutLogFilter> logoutLogFilter(BladeLogHandler logHandler) {
|
||||||
|
FilterRegistrationBean<LogoutLogFilter> registration = new FilterRegistrationBean<>();
|
||||||
|
registration.setFilter(new LogoutLogFilter(logHandler));
|
||||||
|
registration.addUrlPatterns("/*");
|
||||||
|
registration.setName("logoutLogFilter");
|
||||||
|
registration.setOrder(Ordered.LOWEST_PRECEDENCE - 100);
|
||||||
|
return registration;
|
||||||
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public PasswordHandler passwordHandler(OAuth2Properties properties) {
|
public PasswordHandler passwordHandler(OAuth2Properties properties) {
|
||||||
return new BladePasswordHandler(properties);
|
return new BladePasswordHandler(properties);
|
||||||
|
|||||||
@@ -32,4 +32,10 @@ package org.springblade.auth.constant;
|
|||||||
*/
|
*/
|
||||||
public interface BladeAuthConstant {
|
public interface BladeAuthConstant {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 小程序/登录短信验证码资源编号(对应后台 /resource/sms 的 smsCode)
|
||||||
|
*/
|
||||||
|
String LOGIN_SMS_CODE = "ali_reg";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
package org.springblade.auth.dto;
|
package org.springblade.auth.dto;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonAlias;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -43,6 +44,13 @@ public class IamSsoProfileResponse {
|
|||||||
*/
|
*/
|
||||||
private String id;
|
private String id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IAM返回的本系统账号,部分部署直接返回在顶层。
|
||||||
|
*/
|
||||||
|
@JsonProperty("account_no")
|
||||||
|
@JsonAlias({"accountNo", "account", "username", "user_name"})
|
||||||
|
private String accountNo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* IAM用户扩展属性
|
* IAM用户扩展属性
|
||||||
*/
|
*/
|
||||||
@@ -54,12 +62,20 @@ public class IamSsoProfileResponse {
|
|||||||
* @return 本系统账号
|
* @return 本系统账号
|
||||||
*/
|
*/
|
||||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||||
public String getAccountNo() {
|
public String resolveAccountNo() {
|
||||||
if (attributes == null) {
|
if (accountNo != null && !accountNo.isBlank()) {
|
||||||
return null;
|
return accountNo;
|
||||||
}
|
}
|
||||||
Object accountNo = attributes.get("account_no");
|
if (attributes == null) {
|
||||||
return accountNo == null ? null : String.valueOf(accountNo);
|
return id;
|
||||||
|
}
|
||||||
|
for (String key : new String[]{"account_no", "accountNo", "account", "username", "user_name"}) {
|
||||||
|
Object value = attributes.get(key);
|
||||||
|
if (value != null && !String.valueOf(value).isBlank()) {
|
||||||
|
return String.valueOf(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ package org.springblade.auth.endpoint;
|
|||||||
|
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.annotation.security.PermitAll;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import org.springblade.core.oauth2.endpoint.OAuth2TokenEndPoint;
|
import org.springblade.core.oauth2.endpoint.OAuth2TokenEndPoint;
|
||||||
@@ -60,6 +61,7 @@ public class IamSsoEndpoint {
|
|||||||
* @return token
|
* @return token
|
||||||
*/
|
*/
|
||||||
@RequestMapping(value = "/token", method = {RequestMethod.GET, RequestMethod.POST})
|
@RequestMapping(value = "/token", method = {RequestMethod.GET, RequestMethod.POST})
|
||||||
|
@PermitAll
|
||||||
@Operation(summary = "IAM统一身份认证登录", description = "使用IAM授权码换取本系统Token")
|
@Operation(summary = "IAM统一身份认证登录", description = "使用IAM授权码换取本系统Token")
|
||||||
public ResponseEntity<Kv> token(HttpServletRequest request) {
|
public ResponseEntity<Kv> token(HttpServletRequest request) {
|
||||||
String grantType = request.getParameter("grant_type");
|
String grantType = request.getParameter("grant_type");
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package org.springblade.auth.endpoint;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springblade.core.oauth2.endpoint.AbstractOAuth2MKEndpoint;
|
||||||
|
import org.springblade.core.oauth2.granter.TokenGranterFactory;
|
||||||
|
import org.springblade.core.oauth2.handler.TokenHandler;
|
||||||
|
import org.springblade.core.tool.utils.StringUtil;
|
||||||
|
import org.springblade.core.tool.utils.UrlUtil;
|
||||||
|
import org.springblade.thirdparty.mk.config.MKProperties;
|
||||||
|
import org.springblade.thirdparty.mk.service.IMKService;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author bfhuange
|
||||||
|
* @date 2024/9/17
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@Slf4j
|
||||||
|
@Tag(name = "跳转mk认证", description = "跳转mk认证端点")
|
||||||
|
public class OAuth2MKEndpoint extends AbstractOAuth2MKEndpoint {
|
||||||
|
/**
|
||||||
|
* 登录链接
|
||||||
|
*/
|
||||||
|
private static final String LOGIN_URL_FORMAT = "%s%s?appId=%s&redirection_url=%s";
|
||||||
|
|
||||||
|
private final MKProperties mkProperties;
|
||||||
|
private final IMKService mkService;
|
||||||
|
|
||||||
|
public OAuth2MKEndpoint(TokenGranterFactory granterFactory, TokenHandler tokenHandler, MKProperties mkProperties, IMKService mkService) {
|
||||||
|
super(granterFactory, tokenHandler);
|
||||||
|
this.mkProperties = mkProperties;
|
||||||
|
this.mkService = mkService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String generateLoginUrl(String refererUrl) {
|
||||||
|
// 重定向到来源
|
||||||
|
return String.format(LOGIN_URL_FORMAT, mkProperties.getLoginUrl(), mkProperties.getMkSsoLoginUrl(), mkProperties.getOauthAppId(), UrlUtil.encode(refererUrl));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getAccountByMkCode(String mkCode) {
|
||||||
|
return mkService.getMKAccount(mkCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean checkRefererUrl(String refererUrl) {
|
||||||
|
if (!mkProperties.isCheckReferer()) {
|
||||||
|
// 未开启校验来源,校验通过
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (StringUtil.isBlank(mkProperties.getErpBaseUrls())) {
|
||||||
|
log.error("未配置erp地址,无法校验来源");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (StringUtil.isBlank(refererUrl)) {
|
||||||
|
// 来源地址为空,通过,以便用于调试
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 匹配到一个erp基础地址就行了
|
||||||
|
return Stream.of(mkProperties.getErpBaseUrls().split(","))
|
||||||
|
.map(String::trim)
|
||||||
|
.anyMatch(refererUrl::startsWith);
|
||||||
|
}
|
||||||
|
}
|
||||||
+94
@@ -0,0 +1,94 @@
|
|||||||
|
/**
|
||||||
|
* BladeX Commercial License Agreement
|
||||||
|
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||||
|
* <p>
|
||||||
|
* Use of this software is governed by the Commercial License Agreement
|
||||||
|
* obtained after purchasing a license from BladeX.
|
||||||
|
* <p>
|
||||||
|
* 1. This software is for development use only under a valid license
|
||||||
|
* from BladeX.
|
||||||
|
* <p>
|
||||||
|
* 2. Redistribution of this software's source code to any third party
|
||||||
|
* without a commercial license is strictly prohibited.
|
||||||
|
* <p>
|
||||||
|
* 3. Licensees may copyright their own code but cannot use segments
|
||||||
|
* from this software for such purposes. Copyright of this software
|
||||||
|
* remains with BladeX.
|
||||||
|
* <p>
|
||||||
|
* Using this software signifies agreement to this License, and the software
|
||||||
|
* must not be used for illegal purposes.
|
||||||
|
* <p>
|
||||||
|
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||||
|
* not liable for any claims arising from secondary or illegal development.
|
||||||
|
* <p>
|
||||||
|
* Author: Chill Zhuang (bladejava@qq.com)
|
||||||
|
*/
|
||||||
|
package org.springblade.auth.endpoint;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springblade.core.oauth2.endpoint.OAuth2TokenEndPoint;
|
||||||
|
import org.springblade.core.secure.BladeUser;
|
||||||
|
import org.springblade.core.tool.api.R;
|
||||||
|
import org.springblade.core.tool.utils.BeanUtil;
|
||||||
|
import org.springblade.core.tool.utils.Func;
|
||||||
|
import org.springblade.system.feign.ISysClient;
|
||||||
|
import org.springframework.core.MethodParameter;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.converter.HttpMessageConverter;
|
||||||
|
import org.springframework.http.server.ServerHttpRequest;
|
||||||
|
import org.springframework.http.server.ServerHttpResponse;
|
||||||
|
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||||
|
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 增强 /oauth/user-info 响应,补充 permission 权限标识字段
|
||||||
|
*
|
||||||
|
* @author Chill
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RestControllerAdvice(assignableTypes = OAuth2TokenEndPoint.class)
|
||||||
|
public class OAuth2UserInfoResponseAdvice implements ResponseBodyAdvice<Object> {
|
||||||
|
|
||||||
|
private final ISysClient sysClient;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
|
||||||
|
return returnType.getMethod() != null && "userInfo".equals(returnType.getMethod().getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
|
||||||
|
Class<? extends HttpMessageConverter<?>> selectedConverterType,
|
||||||
|
ServerHttpRequest request, ServerHttpResponse response) {
|
||||||
|
if (!(body instanceof BladeUser bladeUser)) {
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
OAuth2UserInfoVO userInfo = BeanUtil.copyProperties(bladeUser, OAuth2UserInfoVO.class);
|
||||||
|
if (userInfo == null) {
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
userInfo.setPermission(loadPermission(bladeUser.getRoleId()));
|
||||||
|
return userInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> loadPermission(String roleId) {
|
||||||
|
if (Func.isBlank(roleId)) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
R<List<String>> result = sysClient.getPermissions(roleId);
|
||||||
|
if (result != null && result.isSuccess() && result.getData() != null) {
|
||||||
|
return result.getData();
|
||||||
|
}
|
||||||
|
} catch (Exception exception) {
|
||||||
|
log.warn("加载用户权限标识失败, roleId={}", roleId, exception);
|
||||||
|
}
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
/**
|
||||||
|
* BladeX Commercial License Agreement
|
||||||
|
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||||
|
* <p>
|
||||||
|
* Use of this software is governed by the Commercial License Agreement
|
||||||
|
* obtained after purchasing a license from BladeX.
|
||||||
|
* <p>
|
||||||
|
* 1. This software is for development use only under a valid license
|
||||||
|
* from BladeX.
|
||||||
|
* <p>
|
||||||
|
* 2. Redistribution of this software's source code to any third party
|
||||||
|
* without a commercial license is strictly prohibited.
|
||||||
|
* <p>
|
||||||
|
* 3. Licensees may copyright their own code but cannot use segments
|
||||||
|
* from this software for such purposes. Copyright of this software
|
||||||
|
* remains with BladeX.
|
||||||
|
* <p>
|
||||||
|
* Using this software signifies agreement to this License, and the software
|
||||||
|
* must not be used for illegal purposes.
|
||||||
|
* <p>
|
||||||
|
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||||
|
* not liable for any claims arising from secondary or illegal development.
|
||||||
|
* <p>
|
||||||
|
* Author: Chill Zhuang (bladejava@qq.com)
|
||||||
|
*/
|
||||||
|
package org.springblade.auth.endpoint;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import org.springblade.core.secure.BladeUser;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OAuth用户信息(含权限标识,供小程序等客户端使用)
|
||||||
|
*
|
||||||
|
* @author Chill
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@Schema(description = "OAuth用户信息")
|
||||||
|
public class OAuth2UserInfoVO extends BladeUser {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限标识集合(菜单按钮 code)
|
||||||
|
*/
|
||||||
|
@Schema(description = "权限标识集合")
|
||||||
|
private List<String> permission;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -28,13 +28,14 @@ package org.springblade.auth.endpoint;
|
|||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
|
import org.springblade.auth.constant.BladeAuthConstant;
|
||||||
import org.springblade.core.oauth2.props.OAuth2Properties;
|
import org.springblade.core.oauth2.props.OAuth2Properties;
|
||||||
import org.springblade.core.oauth2.provider.OAuth2Request;
|
import org.springblade.core.oauth2.provider.OAuth2Request;
|
||||||
import org.springblade.core.oauth2.service.OAuth2User;
|
import org.springblade.core.oauth2.service.OAuth2User;
|
||||||
import org.springblade.core.oauth2.service.OAuth2UserService;
|
import org.springblade.core.oauth2.service.OAuth2UserService;
|
||||||
import org.springblade.core.tool.api.R;
|
import org.springblade.core.tool.api.R;
|
||||||
|
import org.springblade.core.tool.utils.Func;
|
||||||
import org.springblade.core.tool.utils.SM2Util;
|
import org.springblade.core.tool.utils.SM2Util;
|
||||||
import org.springblade.core.tool.utils.StringPool;
|
|
||||||
import org.springblade.core.tool.utils.StringUtil;
|
import org.springblade.core.tool.utils.StringUtil;
|
||||||
import org.springblade.resource.feign.ISmsClient;
|
import org.springblade.resource.feign.ISmsClient;
|
||||||
import org.springblade.resource.utils.SmsUtil;
|
import org.springblade.resource.utils.SmsUtil;
|
||||||
@@ -73,11 +74,14 @@ public class Oauth2SmsEndpoint {
|
|||||||
* 短信验证码发送
|
* 短信验证码发送
|
||||||
*
|
*
|
||||||
* @param tenantId 租户ID
|
* @param tenantId 租户ID
|
||||||
* @param phone 手机号
|
* @param phone 手机号(SM2 加密)
|
||||||
|
* @param code 短信资源编号,默认 ali_reg(后台 /resource/sms)
|
||||||
*/
|
*/
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@PostMapping("/oauth/sms/send-validate")
|
@PostMapping("/oauth/sms/send-validate")
|
||||||
public R sendValidate(@RequestParam String tenantId, @RequestParam String phone) {
|
public R sendValidate(@RequestParam String tenantId,
|
||||||
|
@RequestParam String phone,
|
||||||
|
@RequestParam(required = false) String code) {
|
||||||
// 校验手机加密认证,防止恶意发送验证码
|
// 校验手机加密认证,防止恶意发送验证码
|
||||||
String decryptedPhone = SM2Util.decrypt(phone, properties.getPublicKey(), properties.getPrivateKey());
|
String decryptedPhone = SM2Util.decrypt(phone, properties.getPublicKey(), properties.getPrivateKey());
|
||||||
if (StringUtil.isBlank(decryptedPhone)) {
|
if (StringUtil.isBlank(decryptedPhone)) {
|
||||||
@@ -90,8 +94,9 @@ public class Oauth2SmsEndpoint {
|
|||||||
if (oAuth2User == null) {
|
if (oAuth2User == null) {
|
||||||
return R.fail(USER_PHONE_NOT_FOUND);
|
return R.fail(USER_PHONE_NOT_FOUND);
|
||||||
}
|
}
|
||||||
// 用户存在则发送验证码
|
// 使用指定短信资源(默认 ali_reg)
|
||||||
R result = smsClient.sendValidate(tenantId, StringPool.EMPTY, decryptedPhone);
|
String smsResourceCode = Func.toStr(code, BladeAuthConstant.LOGIN_SMS_CODE);
|
||||||
|
R result = smsClient.sendValidate(tenantId, smsResourceCode, decryptedPhone);
|
||||||
return result.isSuccess() ? R.data(result.getData(), SmsUtil.SEND_SUCCESS) : R.fail(SmsUtil.SEND_FAIL);
|
return result.isSuccess() ? R.data(result.getData(), SmsUtil.SEND_SUCCESS) : R.fail(SmsUtil.SEND_FAIL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package org.springblade.auth.exception;
|
||||||
|
|
||||||
|
import org.springblade.auth.endpoint.OAuth2MKEndpoint;
|
||||||
|
import org.springblade.core.oauth2.exception.OAuth2ExceptionHandler;
|
||||||
|
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author bfhuange
|
||||||
|
* @date 2024/10/16
|
||||||
|
*/
|
||||||
|
@ControllerAdvice(basePackageClasses = OAuth2MKEndpoint.class)
|
||||||
|
public class OAuth2MKExceptionHandler extends OAuth2ExceptionHandler {
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
/**
|
||||||
|
* BladeX Commercial License Agreement
|
||||||
|
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||||
|
* <p>
|
||||||
|
* Use of this software is governed by the Commercial License Agreement
|
||||||
|
* obtained after purchasing a license from BladeX.
|
||||||
|
* <p>
|
||||||
|
* 1. This software is for development use only under a valid license
|
||||||
|
* from BladeX.
|
||||||
|
* <p>
|
||||||
|
* 2. Redistribution of this software's source code to any third party
|
||||||
|
* without a commercial license is strictly prohibited.
|
||||||
|
* <p>
|
||||||
|
* 3. Licensees may copyright their own code but cannot use segments
|
||||||
|
* from this software for such purposes. Copyright of this software
|
||||||
|
* remains with BladeX.
|
||||||
|
* <p>
|
||||||
|
* Using this software signifies agreement to this License, and the software
|
||||||
|
* must not be used for illegal purposes.
|
||||||
|
* <p>
|
||||||
|
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||||
|
* not liable for any claims arising from secondary or illegal development.
|
||||||
|
* <p>
|
||||||
|
* Author: Chill Zhuang (bladejava@qq.com)
|
||||||
|
*/
|
||||||
|
package org.springblade.auth.filter;
|
||||||
|
|
||||||
|
import jakarta.servlet.FilterChain;
|
||||||
|
import jakarta.servlet.ServletException;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springblade.auth.handler.BladeLogHandler;
|
||||||
|
import org.springblade.core.secure.BladeUser;
|
||||||
|
import org.springblade.core.secure.utils.AuthUtil;
|
||||||
|
import org.springblade.core.tool.utils.StringUtil;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 退出登录日志过滤器
|
||||||
|
* 在清除 Token 前解析当前用户并异步写入认证日志
|
||||||
|
*
|
||||||
|
* @author BladeX
|
||||||
|
*/
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class LogoutLogFilter extends OncePerRequestFilter implements Ordered {
|
||||||
|
|
||||||
|
private static final String LOGOUT_PATH = "/oauth/logout";
|
||||||
|
|
||||||
|
private final BladeLogHandler logHandler;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean shouldNotFilter(HttpServletRequest request) {
|
||||||
|
String path = request.getRequestURI();
|
||||||
|
return StringUtil.isBlank(path) || !path.contains(LOGOUT_PATH);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||||
|
throws ServletException, IOException {
|
||||||
|
// 必须在 logout 清 token 之前取用户,否则无法关联账号
|
||||||
|
BladeUser user = AuthUtil.getUser();
|
||||||
|
try {
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
} finally {
|
||||||
|
if (user != null) {
|
||||||
|
logHandler.handleLogoutLog(user, request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOrder() {
|
||||||
|
return Ordered.LOWEST_PRECEDENCE - 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -45,6 +45,7 @@ import org.springblade.core.redis.cache.BladeRedis;
|
|||||||
import org.springblade.core.tool.api.R;
|
import org.springblade.core.tool.api.R;
|
||||||
import org.springblade.core.tool.utils.StringUtil;
|
import org.springblade.core.tool.utils.StringUtil;
|
||||||
import org.springblade.core.tool.utils.StringPool;
|
import org.springblade.core.tool.utils.StringPool;
|
||||||
|
import org.springblade.system.cache.DictCache;
|
||||||
import org.springblade.system.feign.IUserClient;
|
import org.springblade.system.feign.IUserClient;
|
||||||
import org.springblade.system.pojo.entity.User;
|
import org.springblade.system.pojo.entity.User;
|
||||||
import org.springblade.system.pojo.entity.UserInfo;
|
import org.springblade.system.pojo.entity.UserInfo;
|
||||||
@@ -65,8 +66,10 @@ import java.net.http.HttpResponse;
|
|||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -82,8 +85,10 @@ public class IamSsoTokenGranter extends AuthorizationCodeGranter {
|
|||||||
private static final String GRANT_TYPE = "iam_sso";
|
private static final String GRANT_TYPE = "iam_sso";
|
||||||
private static final String IAM_GRANT_TYPE = "authorization_code";
|
private static final String IAM_GRANT_TYPE = "authorization_code";
|
||||||
private static final String IAM_TOKEN_URI = "/iam/sso/token";
|
private static final String IAM_TOKEN_URI = "/iam/sso/token";
|
||||||
private static final String BEARER_PREFIX = "Bearer ";
|
|
||||||
private static final String BASIC_PREFIX = "Basic ";
|
private static final String BASIC_PREFIX = "Basic ";
|
||||||
|
private static final String IAM_DEFAULT_DICT_CODE = "iam_default";
|
||||||
|
private static final String IAM_DEFAULT_ROLE_NAME = "默认角色";
|
||||||
|
private static final String IAM_DEFAULT_DEPT_NAME = "默认部门";
|
||||||
|
|
||||||
private final OAuth2ClientService clientService;
|
private final OAuth2ClientService clientService;
|
||||||
private final IUserClient userClient;
|
private final IUserClient userClient;
|
||||||
@@ -141,7 +146,7 @@ public class IamSsoTokenGranter extends AuthorizationCodeGranter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
IamSsoProfileResponse profileResponse = requestIamProfile(accessToken);
|
IamSsoProfileResponse profileResponse = requestIamProfile(accessToken);
|
||||||
String accountNo = profileResponse.getAccountNo();
|
String accountNo = profileResponse.resolveAccountNo();
|
||||||
if (StringUtil.isBlank(accountNo)) {
|
if (StringUtil.isBlank(accountNo)) {
|
||||||
log.warn("IAM统一身份认证用户信息缺少account_no,iamId={}", profileResponse.getId());
|
log.warn("IAM统一身份认证用户信息缺少account_no,iamId={}", profileResponse.getId());
|
||||||
throw new UserInvalidException(OAuth2TokenConstant.USER_NOT_FOUND);
|
throw new UserInvalidException(OAuth2TokenConstant.USER_NOT_FOUND);
|
||||||
@@ -152,50 +157,98 @@ public class IamSsoTokenGranter extends AuthorizationCodeGranter {
|
|||||||
log.warn("IAM统一身份认证请求缺少租户ID,accountNo={}", accountNo);
|
log.warn("IAM统一身份认证请求缺少租户ID,accountNo={}", accountNo);
|
||||||
throw new UserInvalidException(OAuth2TokenConstant.USER_NOT_FOUND);
|
throw new UserInvalidException(OAuth2TokenConstant.USER_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
log.info("IAM统一身份认证开始匹配本系统账号,tenantId={}, accountNo={}", tenantId, accountNo);
|
||||||
UserInfo userInfo = loadOrCreateIamUser(request, profileResponse, tenantId, accountNo);
|
UserInfo userInfo = loadOrCreateIamUser(request, profileResponse, tenantId, accountNo);
|
||||||
OAuth2User user = TokenUtil.convertUser(userInfo, request);
|
OAuth2User user = TokenUtil.convertUser(userInfo, request);
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
throw new UserInvalidException(OAuth2TokenConstant.USER_NOT_FOUND);
|
throw new UserInvalidException(OAuth2TokenConstant.USER_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
if (user.getAuthorities() == null || user.getAuthorities().isEmpty()) {
|
||||||
|
log.warn("IAM统一身份认证用户缺少可登录角色,tenantId={}, accountNo={}", tenantId, accountNo);
|
||||||
|
throw new UserInvalidException(OAuth2TokenConstant.USER_HAS_NO_ROLE);
|
||||||
|
}
|
||||||
user.setClient(client(request));
|
user.setClient(client(request));
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
private UserInfo loadOrCreateIamUser(OAuth2Request request, IamSsoProfileResponse profileResponse, String tenantId, String accountNo) {
|
private UserInfo loadOrCreateIamUser(OAuth2Request request, IamSsoProfileResponse profileResponse, String tenantId, String accountNo) {
|
||||||
R<UserInfo> result = userClient.userInfo(tenantId, accountNo);
|
R<UserInfo> result = userClient.userInfo(tenantId, accountNo);
|
||||||
if (result.isSuccess() && result.getData() != null) {
|
if (result.isSuccess() && hasLoginAccess(result.getData())) {
|
||||||
return result.getData();
|
return result.getData();
|
||||||
}
|
}
|
||||||
log.info("IAM统一身份认证未匹配到本系统账号,开始自动创建用户,tenantId={}, accountNo={}", tenantId, accountNo);
|
if (result.isSuccess() && hasUser(result.getData())) {
|
||||||
R<Boolean> saveResult = userClient.saveUser(buildIamUser(profileResponse, tenantId, accountNo));
|
log.info("IAM统一身份认证账号缺少有效角色或部门,开始补齐默认配置,tenantId={}, accountNo={}", tenantId, accountNo);
|
||||||
|
} else {
|
||||||
|
log.info("IAM统一身份认证未匹配到本系统账号,开始自动创建用户,tenantId={}, accountNo={}, querySuccess={}",
|
||||||
|
tenantId, accountNo, result.isSuccess());
|
||||||
|
}
|
||||||
|
R<Boolean> saveResult = userClient.saveIamUser(buildIamUser(profileResponse, tenantId, accountNo));
|
||||||
if (!saveResult.isSuccess() || !Boolean.TRUE.equals(saveResult.getData())) {
|
if (!saveResult.isSuccess() || !Boolean.TRUE.equals(saveResult.getData())) {
|
||||||
log.warn("IAM统一身份认证自动创建用户失败,tenantId={}, accountNo={}, msg={}", tenantId, accountNo, saveResult.getMsg());
|
String failMsg = StringUtil.isNotBlank(saveResult.getMsg()) ? saveResult.getMsg() : OAuth2TokenConstant.USER_HAS_NO_ROLE;
|
||||||
throw new UserInvalidException(OAuth2TokenConstant.USER_NOT_FOUND);
|
log.warn("IAM统一身份认证自动创建或补齐用户失败,tenantId={}, accountNo={}, msg={}", tenantId, accountNo, failMsg);
|
||||||
|
throw new UserInvalidException(failMsg);
|
||||||
}
|
}
|
||||||
R<UserInfo> createdResult = userClient.userInfo(tenantId, accountNo);
|
R<UserInfo> createdResult = userClient.userInfo(tenantId, accountNo);
|
||||||
if (!createdResult.isSuccess() || createdResult.getData() == null) {
|
if (!createdResult.isSuccess() || !hasUser(createdResult.getData())) {
|
||||||
log.warn("IAM统一身份认证自动创建用户后未查询到用户,tenantId={}, accountNo={}", tenantId, accountNo);
|
log.warn("IAM统一身份认证自动创建用户后未查询到用户,tenantId={}, accountNo={}", tenantId, accountNo);
|
||||||
throw new UserInvalidException(OAuth2TokenConstant.USER_NOT_FOUND);
|
throw new UserInvalidException(OAuth2TokenConstant.USER_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
if (!hasRoles(createdResult.getData())) {
|
||||||
|
log.warn("IAM统一身份认证自动创建用户后仍缺少可登录角色,tenantId={}, accountNo={}", tenantId, accountNo);
|
||||||
|
throw new UserInvalidException(OAuth2TokenConstant.USER_HAS_NO_ROLE);
|
||||||
|
}
|
||||||
return createdResult.getData();
|
return createdResult.getData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Feign 返回成功时,data 仍可能是一个 user 为空的 UserInfo 包装对象。
|
||||||
|
* IAM 登录必须确保本地用户实体存在,避免被误报为密码校验失败。
|
||||||
|
*/
|
||||||
|
private boolean hasUser(UserInfo userInfo) {
|
||||||
|
return userInfo != null && userInfo.getUser() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasRoles(UserInfo userInfo) {
|
||||||
|
return userInfo != null && userInfo.getRoles() != null && !userInfo.getRoles().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasLoginAccess(UserInfo userInfo) {
|
||||||
|
return hasUser(userInfo) && hasRoles(userInfo) && hasValidAssignment(userInfo.getUser());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasValidAssignment(User user) {
|
||||||
|
return StringUtil.isNotBlank(user.getRoleId())
|
||||||
|
&& !StringPool.MINUS_ONE.equals(user.getRoleId())
|
||||||
|
&& StringUtil.isNotBlank(user.getDeptId())
|
||||||
|
&& !StringPool.MINUS_ONE.equals(user.getDeptId());
|
||||||
|
}
|
||||||
|
|
||||||
private User buildIamUser(IamSsoProfileResponse profileResponse, String tenantId, String accountNo) {
|
private User buildIamUser(IamSsoProfileResponse profileResponse, String tenantId, String accountNo) {
|
||||||
User user = new User();
|
User user = new User();
|
||||||
user.setTenantId(tenantId);
|
user.setTenantId(tenantId);
|
||||||
user.setUserType(UserType.WEB.getCategory());
|
user.setUserType(UserType.WEB.getCategory());
|
||||||
user.setAccount(accountNo);
|
user.setAccount(accountNo);
|
||||||
user.setPassword(accountNo);
|
user.setPassword(UUID.randomUUID().toString());
|
||||||
user.setName(accountNo);
|
user.setName(accountNo);
|
||||||
user.setRealName(accountNo);
|
user.setRealName(accountNo);
|
||||||
user.setRoleId(StringPool.MINUS_ONE);
|
user.setRoleId(resolveIamDefaultId(IAM_DEFAULT_ROLE_NAME));
|
||||||
user.setDeptId(StringPool.MINUS_ONE);
|
user.setDeptId(resolveIamDefaultId(IAM_DEFAULT_DEPT_NAME));
|
||||||
user.setPostId(StringPool.MINUS_ONE);
|
user.setPostId(StringPool.MINUS_ONE);
|
||||||
|
user.setIsOa(1);
|
||||||
|
user.setSyncTime(new Date());
|
||||||
user.setStatus(StatusType.ACTIVE.getType());
|
user.setStatus(StatusType.ACTIVE.getType());
|
||||||
log.info("IAM统一身份认证自动创建用户参数,tenantId={}, accountNo={}, iamId={}", tenantId, accountNo, profileResponse.getId());
|
log.info("IAM统一身份认证自动创建用户参数,tenantId={}, accountNo={}, iamId={}", tenantId, accountNo, profileResponse.getId());
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String resolveIamDefaultId(String dictValue) {
|
||||||
|
String dictKey = DictCache.getKey(IAM_DEFAULT_DICT_CODE, dictValue);
|
||||||
|
if (StringUtil.isBlank(dictKey) || StringPool.MINUS_ONE.equals(dictKey)) {
|
||||||
|
throw new UserInvalidException(StringUtil.format("IAM默认配置 [{}] 未配置有效键值", dictValue));
|
||||||
|
}
|
||||||
|
return dictKey;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean supports(OAuth2Request request) {
|
public boolean supports(OAuth2Request request) {
|
||||||
return isIamRequest(request, false);
|
return isIamRequest(request, false);
|
||||||
}
|
}
|
||||||
@@ -206,13 +259,13 @@ public class IamSsoTokenGranter extends AuthorizationCodeGranter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean isIamRequest(OAuth2Request request, boolean logMiss) {
|
private boolean isIamRequest(OAuth2Request request, boolean logMiss) {
|
||||||
String redirectUri = normalizeRedirectUri(request.getRedirectUri());
|
String redirectUri = resolveRedirectUri(request);
|
||||||
boolean iamRequest = StringUtil.isNotBlank(properties.getRedirectUri())
|
boolean iamRequest = StringUtil.isNotBlank(redirectUri)
|
||||||
&& StringUtil.isNotBlank(request.getCode())
|
&& StringUtil.isNotBlank(request.getCode())
|
||||||
&& StringUtil.equals(properties.getRedirectUri(), redirectUri);
|
&& isDedicatedIamEndpoint(request);
|
||||||
if (!iamRequest && logMiss) {
|
if (!iamRequest && logMiss) {
|
||||||
log.info("IAM统一身份认证请求未命中,configRedirectUri={}, requestRedirectUri={}, normalizedRedirectUri={}, code={}",
|
log.info("IAM统一身份认证请求未命中,configRedirectUri={}, requestRedirectUri={}, normalizedRedirectUri={}, hasCode={}",
|
||||||
properties.getRedirectUri(), request.getRedirectUri(), redirectUri, request.getCode());
|
properties.getRedirectUri(), request.getRedirectUri(), redirectUri, StringUtil.isNotBlank(request.getCode()));
|
||||||
}
|
}
|
||||||
return iamRequest;
|
return iamRequest;
|
||||||
}
|
}
|
||||||
@@ -247,8 +300,7 @@ public class IamSsoTokenGranter extends AuthorizationCodeGranter {
|
|||||||
properties.getTokenUrl(),
|
properties.getTokenUrl(),
|
||||||
properties.getProfileUrl(),
|
properties.getProfileUrl(),
|
||||||
properties.getClientId(),
|
properties.getClientId(),
|
||||||
properties.getClientSecret(),
|
properties.getClientSecret()
|
||||||
properties.getRedirectUri()
|
|
||||||
)) {
|
)) {
|
||||||
throw new UserInvalidException("IAM统一身份认证配置不完整");
|
throw new UserInvalidException("IAM统一身份认证配置不完整");
|
||||||
}
|
}
|
||||||
@@ -265,7 +317,7 @@ public class IamSsoTokenGranter extends AuthorizationCodeGranter {
|
|||||||
.header(HttpHeaders.AUTHORIZATION, authorizationHeader())
|
.header(HttpHeaders.AUTHORIZATION, authorizationHeader())
|
||||||
.POST(HttpRequest.BodyPublishers.ofString(tokenBody, StandardCharsets.UTF_8))
|
.POST(HttpRequest.BodyPublishers.ofString(tokenBody, StandardCharsets.UTF_8))
|
||||||
.build();
|
.build();
|
||||||
log.info("IAM统一身份认证换取Token请求,method={}, headers={}, body={}", httpRequest.method(), httpRequest.headers().map(), tokenBody);
|
log.info("IAM统一身份认证换取Token请求,url={}, method={}", properties.getTokenUrl(), httpRequest.method());
|
||||||
HttpResponse<String> response = httpClient.send(
|
HttpResponse<String> response = httpClient.send(
|
||||||
httpRequest,
|
httpRequest,
|
||||||
HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)
|
HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)
|
||||||
@@ -286,20 +338,24 @@ public class IamSsoTokenGranter extends AuthorizationCodeGranter {
|
|||||||
|
|
||||||
private IamSsoProfileResponse requestIamProfile(String accessToken) {
|
private IamSsoProfileResponse requestIamProfile(String accessToken) {
|
||||||
try {
|
try {
|
||||||
HttpRequest httpRequest = HttpRequest.newBuilder(URI.create(buildProfileUrl(accessToken)))
|
HttpRequest.Builder requestBuilder = HttpRequest.newBuilder(URI.create(buildProfileUrl(accessToken)))
|
||||||
.timeout(Duration.ofSeconds(10))
|
.timeout(Duration.ofSeconds(10))
|
||||||
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
|
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
|
||||||
.header(HttpHeaders.AUTHORIZATION, profileAuthorizationHeader(accessToken))
|
// 与 IAM 同步接口一致:Authorization 走网关凭证,Auth 走业务凭证。
|
||||||
.GET()
|
.header(HttpHeaders.AUTHORIZATION, authorizationHeader());
|
||||||
.build();
|
String profileAuthHeader = profileAuthHeader();
|
||||||
log.info("IAM统一身份认证获取用户信息请求,headers={}, body={}", httpRequest.headers().map(), "无");
|
if (StringUtil.isNotBlank(profileAuthHeader)) {
|
||||||
|
requestBuilder.header("Auth", profileAuthHeader);
|
||||||
|
}
|
||||||
|
HttpRequest httpRequest = requestBuilder.GET().build();
|
||||||
|
log.info("IAM统一身份认证获取用户信息请求,url={}, method={}", properties.getProfileUrl(), httpRequest.method());
|
||||||
HttpResponse<String> response = httpClient.send(
|
HttpResponse<String> response = httpClient.send(
|
||||||
httpRequest,
|
httpRequest,
|
||||||
HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)
|
HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)
|
||||||
);
|
);
|
||||||
log.info("IAM统一身份认证获取用户信息响应,headers={}, body={}", response.headers().map(), response.body());
|
log.info("IAM统一身份认证获取用户信息响应,status={}", response.statusCode());
|
||||||
if (response.statusCode() < 200 || response.statusCode() >= 300) {
|
if (response.statusCode() < 200 || response.statusCode() >= 300) {
|
||||||
log.warn("IAM统一身份认证获取用户信息失败,status={}", response.statusCode());
|
log.warn("IAM统一身份认证获取用户信息失败,status={}, body={}", response.statusCode(), response.body());
|
||||||
throw new UserInvalidException(OAuth2TokenConstant.TOKEN_NOT_CORRECT);
|
throw new UserInvalidException(OAuth2TokenConstant.TOKEN_NOT_CORRECT);
|
||||||
}
|
}
|
||||||
return objectMapper.readValue(response.body(), IamSsoProfileResponse.class);
|
return objectMapper.readValue(response.body(), IamSsoProfileResponse.class);
|
||||||
@@ -321,10 +377,24 @@ public class IamSsoTokenGranter extends AuthorizationCodeGranter {
|
|||||||
if (StringUtil.isNotBlank(request.getState())) {
|
if (StringUtil.isNotBlank(request.getState())) {
|
||||||
params.put("state", request.getState());
|
params.put("state", request.getState());
|
||||||
}
|
}
|
||||||
params.put("redirect_uri", properties.getRedirectUri());
|
params.put("redirect_uri", resolveRedirectUri(request));
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 IAM 回调地址,优先使用前端请求传入的值,兼容未传参时的后端默认配置。
|
||||||
|
*
|
||||||
|
* @param request OAuth2 请求
|
||||||
|
* @return IAM 回调地址
|
||||||
|
*/
|
||||||
|
private String resolveRedirectUri(OAuth2Request request) {
|
||||||
|
String requestRedirectUri = normalizeRedirectUri(request.getRedirectUri());
|
||||||
|
if (StringUtil.isNotBlank(requestRedirectUri)) {
|
||||||
|
return requestRedirectUri;
|
||||||
|
}
|
||||||
|
return normalizeRedirectUri(properties.getRedirectUri());
|
||||||
|
}
|
||||||
|
|
||||||
private String buildTokenBody(Map<String, String> params) {
|
private String buildTokenBody(Map<String, String> params) {
|
||||||
return params.entrySet().stream()
|
return params.entrySet().stream()
|
||||||
.map(entry -> encode(entry.getKey()) + "=" + encode(entry.getValue()))
|
.map(entry -> encode(entry.getKey()) + "=" + encode(entry.getValue()))
|
||||||
@@ -353,11 +423,15 @@ public class IamSsoTokenGranter extends AuthorizationCodeGranter {
|
|||||||
return BASIC_PREFIX + authorization;
|
return BASIC_PREFIX + authorization;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String profileAuthorizationHeader(String accessToken) {
|
/**
|
||||||
if (StringUtil.isNotBlank(properties.getProfileAuthorization())) {
|
* IAM 业务侧 Auth 头。网关 Authorization 使用 {@link #authorizationHeader()},
|
||||||
return withBasicPrefix(properties.getProfileAuthorization());
|
* 用户令牌通过 query 参数 access_token 传递。
|
||||||
|
*/
|
||||||
|
private String profileAuthHeader() {
|
||||||
|
if (StringUtil.isBlank(properties.getProfileAuthorization())) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
return BEARER_PREFIX + accessToken;
|
return withBasicPrefix(properties.getProfileAuthorization());
|
||||||
}
|
}
|
||||||
|
|
||||||
private String encode(String value) {
|
private String encode(String value) {
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
package org.springblade.auth.granter;
|
package org.springblade.auth.granter;
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import org.springblade.auth.constant.BladeAuthConstant;
|
||||||
import org.springblade.core.oauth2.constant.OAuth2TokenConstant;
|
import org.springblade.core.oauth2.constant.OAuth2TokenConstant;
|
||||||
import org.springblade.core.oauth2.exception.UserInvalidException;
|
import org.springblade.core.oauth2.exception.UserInvalidException;
|
||||||
import org.springblade.core.oauth2.granter.AbstractTokenGranter;
|
import org.springblade.core.oauth2.granter.AbstractTokenGranter;
|
||||||
@@ -37,8 +38,8 @@ import org.springblade.core.oauth2.service.OAuth2User;
|
|||||||
import org.springblade.core.oauth2.service.OAuth2UserService;
|
import org.springblade.core.oauth2.service.OAuth2UserService;
|
||||||
import org.springblade.core.sms.model.SmsCode;
|
import org.springblade.core.sms.model.SmsCode;
|
||||||
import org.springblade.core.tool.api.R;
|
import org.springblade.core.tool.api.R;
|
||||||
|
import org.springblade.core.tool.utils.Func;
|
||||||
import org.springblade.core.tool.utils.SM2Util;
|
import org.springblade.core.tool.utils.SM2Util;
|
||||||
import org.springblade.core.tool.utils.StringPool;
|
|
||||||
import org.springblade.core.tool.utils.StringUtil;
|
import org.springblade.core.tool.utils.StringUtil;
|
||||||
import org.springblade.core.tool.utils.WebUtil;
|
import org.springblade.core.tool.utils.WebUtil;
|
||||||
import org.springblade.resource.feign.ISmsClient;
|
import org.springblade.resource.feign.ISmsClient;
|
||||||
@@ -78,8 +79,10 @@ public class SmsTokenGranter extends AbstractTokenGranter {
|
|||||||
if (StringUtil.isBlank(decryptedPhone)) {
|
if (StringUtil.isBlank(decryptedPhone)) {
|
||||||
throw new UserInvalidException(OAuth2TokenConstant.USER_PHONE_NOT_FOUND);
|
throw new UserInvalidException(OAuth2TokenConstant.USER_PHONE_NOT_FOUND);
|
||||||
}
|
}
|
||||||
// 获取短信验证信息
|
// 与发送时使用同一短信资源编号(默认 ali_reg)
|
||||||
R result = smsClient.validateMessage(tenantId, StringPool.EMPTY, smsCode.getId(), smsCode.getValue(), decryptedPhone);
|
HttpServletRequest httpRequest = WebUtil.getRequest();
|
||||||
|
String smsResourceCode = Func.toStr(httpRequest.getParameter("code"), BladeAuthConstant.LOGIN_SMS_CODE);
|
||||||
|
R result = smsClient.validateMessage(tenantId, smsResourceCode, smsCode.getId(), smsCode.getValue(), decryptedPhone);
|
||||||
if (!result.isSuccess()) {
|
if (!result.isSuccess()) {
|
||||||
throw new UserInvalidException(OAuth2TokenConstant.CAPTCHA_NOT_CORRECT);
|
throw new UserInvalidException(OAuth2TokenConstant.CAPTCHA_NOT_CORRECT);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,102 @@
|
|||||||
|
package org.springblade.auth.granter;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springblade.core.oauth2.exception.UserInvalidException;
|
||||||
|
import org.springblade.core.oauth2.granter.AbstractTokenGranter;
|
||||||
|
import org.springblade.core.oauth2.handler.PasswordHandler;
|
||||||
|
import org.springblade.core.oauth2.provider.OAuth2Request;
|
||||||
|
import org.springblade.core.oauth2.service.OAuth2ClientService;
|
||||||
|
import org.springblade.core.oauth2.service.OAuth2User;
|
||||||
|
import org.springblade.core.oauth2.service.OAuth2UserService;
|
||||||
|
import org.springblade.core.tool.api.R;
|
||||||
|
import org.springblade.core.tool.utils.Func;
|
||||||
|
import org.springblade.core.tool.utils.StringUtil;
|
||||||
|
import org.springblade.core.tool.utils.WebUtil;
|
||||||
|
import org.springblade.system.feign.IUserClient;
|
||||||
|
import org.springblade.thirdparty.wechat.constant.WechatMiniConstant;
|
||||||
|
import org.springblade.thirdparty.wechat.exception.WechatMiniException;
|
||||||
|
import org.springblade.thirdparty.wechat.pojo.vo.WechatPhoneVO;
|
||||||
|
import org.springblade.thirdparty.wechat.pojo.vo.WechatSessionVO;
|
||||||
|
import org.springblade.thirdparty.wechat.service.IWechatMiniService;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信小程序手机号一键登录。
|
||||||
|
* <p>
|
||||||
|
* grant_type=wechat_applet,参数:loginCode(wx.login)、phoneCode(getPhoneNumber)。
|
||||||
|
* 流程:换 openid + 手机号 → 按手机号查用户(不存在则拒绝)→ 记录 openid → 发令牌(与密码登录一致)。
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class WechatMiniTokenGranter extends AbstractTokenGranter {
|
||||||
|
|
||||||
|
private final OAuth2UserService userService;
|
||||||
|
private final IWechatMiniService wechatMiniService;
|
||||||
|
private final IUserClient userClient;
|
||||||
|
|
||||||
|
public WechatMiniTokenGranter(OAuth2ClientService clientService,
|
||||||
|
OAuth2UserService userService,
|
||||||
|
PasswordHandler passwordHandler,
|
||||||
|
IWechatMiniService wechatMiniService,
|
||||||
|
IUserClient userClient) {
|
||||||
|
super(clientService, userService, passwordHandler);
|
||||||
|
this.userService = userService;
|
||||||
|
this.wechatMiniService = wechatMiniService;
|
||||||
|
this.userClient = userClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String type() {
|
||||||
|
return WechatMiniConstant.GRANT_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OAuth2User user(OAuth2Request request) {
|
||||||
|
// 先校验客户端是否允许该授权类型,避免先调微信再因客户端配置失败
|
||||||
|
var oauthClient = client(request);
|
||||||
|
|
||||||
|
HttpServletRequest httpRequest = WebUtil.getRequest();
|
||||||
|
String loginCode = httpRequest.getParameter("loginCode");
|
||||||
|
String phoneCode = httpRequest.getParameter("phoneCode");
|
||||||
|
if (StringUtil.isBlank(loginCode) || StringUtil.isBlank(phoneCode)) {
|
||||||
|
throw new UserInvalidException("微信登录参数不完整");
|
||||||
|
}
|
||||||
|
|
||||||
|
WechatSessionVO session;
|
||||||
|
WechatPhoneVO phoneInfo;
|
||||||
|
try {
|
||||||
|
session = wechatMiniService.code2Session(loginCode);
|
||||||
|
phoneInfo = wechatMiniService.getPhoneNumber(phoneCode);
|
||||||
|
} catch (WechatMiniException e) {
|
||||||
|
log.warn("微信小程序登录失败: {}", e.getMessage());
|
||||||
|
throw new UserInvalidException(e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
String phone = Func.toStr(phoneInfo.getPurePhoneNumber(), phoneInfo.getPhoneNumber());
|
||||||
|
if (StringUtil.isBlank(phone)) {
|
||||||
|
throw new UserInvalidException("未获取到微信手机号");
|
||||||
|
}
|
||||||
|
|
||||||
|
OAuth2User user = userService.loadByPhone(phone, request);
|
||||||
|
if (!userService.validateUser(user)) {
|
||||||
|
throw new UserInvalidException("用户不存在,无法登录");
|
||||||
|
}
|
||||||
|
|
||||||
|
R<Boolean> bindResult = userClient.bindWxMiniOpenId(
|
||||||
|
request.getTenantId(),
|
||||||
|
Func.toLong(user.getUserId()),
|
||||||
|
session.getOpenid(),
|
||||||
|
phone
|
||||||
|
);
|
||||||
|
if (bindResult == null || !bindResult.isSuccess()) {
|
||||||
|
String msg = bindResult != null ? bindResult.getMsg() : "绑定 openid 失败";
|
||||||
|
log.warn("绑定微信 openid 失败 userId={} openid={} msg={}", user.getUserId(), session.getOpenid(), msg);
|
||||||
|
throw new UserInvalidException(StringUtil.isBlank(msg) ? "绑定 openid 失败" : msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
user.setClient(oauthClient);
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+10
-1
@@ -40,6 +40,7 @@ import org.springblade.core.tool.jackson.JsonUtil;
|
|||||||
import org.springblade.core.tool.utils.DateUtil;
|
import org.springblade.core.tool.utils.DateUtil;
|
||||||
import org.springblade.core.tool.utils.DesUtil;
|
import org.springblade.core.tool.utils.DesUtil;
|
||||||
import org.springblade.core.tool.utils.SM2Util;
|
import org.springblade.core.tool.utils.SM2Util;
|
||||||
|
import org.springblade.core.tool.utils.StringUtil;
|
||||||
import org.springblade.system.cache.SysCache;
|
import org.springblade.system.cache.SysCache;
|
||||||
import org.springblade.system.pojo.entity.Tenant;
|
import org.springblade.system.pojo.entity.Tenant;
|
||||||
|
|
||||||
@@ -74,6 +75,10 @@ public class BladeAuthorizationHandler extends AbstractAuthorizationHandler {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public OAuth2Validation preValidation(OAuth2Request request) {
|
public OAuth2Validation preValidation(OAuth2Request request) {
|
||||||
|
// IAM授权码已经由外部身份系统完成认证,不读取或校验本地密码。
|
||||||
|
if (StringUtil.equals("iam_sso", request.getGrantType())) {
|
||||||
|
return new OAuth2Validation();
|
||||||
|
}
|
||||||
if (request.isPassword() || request.isCaptchaCode()) {
|
if (request.isPassword() || request.isCaptchaCode()) {
|
||||||
// 生产环境弱密码校验
|
// 生产环境弱密码校验
|
||||||
if (bladeProperties.isProd() && isWeakPassword(request.getPassword())) {
|
if (bladeProperties.isProd() && isWeakPassword(request.getPassword())) {
|
||||||
@@ -149,7 +154,11 @@ public class BladeAuthorizationHandler extends AbstractAuthorizationHandler {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void authFailure(OAuth2User user, OAuth2Request request, OAuth2Validation validation) {
|
public void authFailure(OAuth2User user, OAuth2Request request, OAuth2Validation validation) {
|
||||||
// 自定义认证失败回调
|
log.error("用户:{},认证失败,失败原因:{},grantType={},authorities={}",
|
||||||
|
user == null ? request.getUsername() : user.getAccount(),
|
||||||
|
validation.getMessage(),
|
||||||
|
request.getGrantType(),
|
||||||
|
user == null ? null : user.getAuthorities());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -29,12 +29,15 @@ import org.springblade.core.launch.props.BladeProperties;
|
|||||||
import org.springblade.core.launch.server.ServerInfo;
|
import org.springblade.core.launch.server.ServerInfo;
|
||||||
import org.springblade.core.oauth2.provider.OAuth2Request;
|
import org.springblade.core.oauth2.provider.OAuth2Request;
|
||||||
import org.springblade.core.oauth2.service.OAuth2User;
|
import org.springblade.core.oauth2.service.OAuth2User;
|
||||||
|
import org.springblade.core.secure.BladeUser;
|
||||||
import org.springblade.core.tool.utils.DateUtil;
|
import org.springblade.core.tool.utils.DateUtil;
|
||||||
import org.springblade.core.tool.utils.Func;
|
import org.springblade.core.tool.utils.Func;
|
||||||
|
import org.springblade.core.tool.utils.StringUtil;
|
||||||
import org.springblade.core.tool.utils.WebUtil;
|
import org.springblade.core.tool.utils.WebUtil;
|
||||||
import org.springblade.system.pojo.entity.AuthLog;
|
|
||||||
import org.springblade.system.feign.IAuthLogClient;
|
import org.springblade.system.feign.IAuthLogClient;
|
||||||
|
import org.springblade.system.pojo.entity.AuthLog;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
@@ -42,7 +45,7 @@ import java.util.concurrent.CompletableFuture;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 认证日志处理器
|
* 认证日志处理器
|
||||||
* 在用户认证成功时异步记录登录日志
|
* 在用户认证成功/退出时异步记录登录、退出日志
|
||||||
*
|
*
|
||||||
* @author BladeX
|
* @author BladeX
|
||||||
*/
|
*/
|
||||||
@@ -50,6 +53,11 @@ import java.util.concurrent.CompletableFuture;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class BladeLogHandler {
|
public class BladeLogHandler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 退出登录授权类型标识
|
||||||
|
*/
|
||||||
|
public static final String GRANT_TYPE_LOGOUT = "logout";
|
||||||
|
|
||||||
private final IAuthLogClient authLogClient;
|
private final IAuthLogClient authLogClient;
|
||||||
private final BladeProperties bladeProperties;
|
private final BladeProperties bladeProperties;
|
||||||
private final ServerInfo serverInfo;
|
private final ServerInfo serverInfo;
|
||||||
@@ -72,6 +80,26 @@ public class BladeLogHandler {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 记录退出登录日志
|
||||||
|
*
|
||||||
|
* @param user 当前登录用户
|
||||||
|
* @param request HTTP 请求
|
||||||
|
*/
|
||||||
|
public void handleLogoutLog(BladeUser user, HttpServletRequest request) {
|
||||||
|
if (user == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CompletableFuture.runAsync(() -> {
|
||||||
|
try {
|
||||||
|
AuthLog authLog = buildLogoutLog(user, request);
|
||||||
|
authLogClient.saveAuthLog(authLog);
|
||||||
|
} catch (Exception exception) {
|
||||||
|
log.error("记录退出日志异常:{}", exception.getMessage(), exception);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建认证日志实体
|
* 构建认证日志实体
|
||||||
*
|
*
|
||||||
@@ -96,4 +124,38 @@ public class BladeLogHandler {
|
|||||||
return authLog;
|
return authLog;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建退出日志实体
|
||||||
|
*
|
||||||
|
* @param user 当前登录用户
|
||||||
|
* @param request HTTP 请求
|
||||||
|
* @return AuthLog
|
||||||
|
*/
|
||||||
|
private AuthLog buildLogoutLog(BladeUser user, HttpServletRequest request) {
|
||||||
|
AuthLog authLog = new AuthLog();
|
||||||
|
authLog.setUserId(user.getUserId());
|
||||||
|
authLog.setTenantId(user.getTenantId());
|
||||||
|
authLog.setServiceId(bladeProperties.getName());
|
||||||
|
authLog.setServerIp(serverInfo.getIpWithPort());
|
||||||
|
authLog.setServerHost(serverInfo.getHostName());
|
||||||
|
authLog.setEnv(bladeProperties.getEnv());
|
||||||
|
authLog.setAccount(user.getAccount());
|
||||||
|
authLog.setRealName(resolveRealName(user));
|
||||||
|
authLog.setGrantType(GRANT_TYPE_LOGOUT);
|
||||||
|
authLog.setRemoteIp(WebUtil.getIP(request));
|
||||||
|
authLog.setUserAgent(WebUtil.getUserAgent(request));
|
||||||
|
authLog.setLoginTime(DateUtil.now());
|
||||||
|
return authLog;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String resolveRealName(BladeUser user) {
|
||||||
|
if (StringUtil.isNotBlank(user.getNickName())) {
|
||||||
|
return user.getNickName();
|
||||||
|
}
|
||||||
|
if (StringUtil.isNotBlank(user.getUserName())) {
|
||||||
|
return user.getUserName();
|
||||||
|
}
|
||||||
|
return user.getAccount();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,7 +78,8 @@ public class IamSsoProperties {
|
|||||||
private String authorization;
|
private String authorization;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* IAM用户信息认证头。为空时默认使用 Bearer accessToken
|
* IAM业务侧 Auth 请求头。为空时不传 Auth;
|
||||||
|
* Authorization 统一使用网关凭证 {@link #authorization}。
|
||||||
*/
|
*/
|
||||||
private String profileAuthorization;
|
private String profileAuthorization;
|
||||||
|
|
||||||
|
|||||||
+14
-1
@@ -25,11 +25,17 @@
|
|||||||
*/
|
*/
|
||||||
package org.springblade.auth.service;
|
package org.springblade.auth.service;
|
||||||
|
|
||||||
|
import org.springblade.core.oauth2.constant.OAuth2GranterConstant;
|
||||||
import org.springblade.core.oauth2.provider.OAuth2Request;
|
import org.springblade.core.oauth2.provider.OAuth2Request;
|
||||||
import org.springblade.core.oauth2.service.OAuth2Client;
|
import org.springblade.core.oauth2.service.OAuth2Client;
|
||||||
import org.springblade.core.oauth2.service.impl.OAuth2ClientDetailService;
|
import org.springblade.core.oauth2.service.impl.OAuth2ClientDetailService;
|
||||||
|
import org.springblade.core.tool.utils.Func;
|
||||||
|
import org.springblade.core.tool.utils.StringPool;
|
||||||
import org.springframework.jdbc.core.JdbcTemplate;
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BladeClientDetailService
|
* BladeClientDetailService
|
||||||
*
|
*
|
||||||
@@ -57,6 +63,13 @@ public class BladeClientDetailService extends OAuth2ClientDetailService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean validateGranter(OAuth2Client client, String grantType) {
|
public boolean validateGranter(OAuth2Client client, String grantType) {
|
||||||
return super.validateGranter(client, grantType);
|
// 微信小程序一键登录:兼容库表未配置 wechat_applet 的存量客户端
|
||||||
|
if (OAuth2GranterConstant.WECHAT_APPLET.equals(grantType) || "wechat_mini".equals(grantType)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return Optional.ofNullable(client)
|
||||||
|
.map(c -> Arrays.stream(Func.split(c.getAuthorizedGrantTypes(), StringPool.COMMA))
|
||||||
|
.anyMatch(s -> s.trim().equals(grantType)))
|
||||||
|
.orElse(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,30 @@
|
|||||||
# 在使用Spring默认数据源Hikari的情况下配置以下配置项
|
#服务器端口
|
||||||
|
server:
|
||||||
|
port: 8100
|
||||||
spring:
|
spring:
|
||||||
|
application:
|
||||||
|
name: blade-auth
|
||||||
|
config:
|
||||||
|
import:
|
||||||
|
- nacos:blade.yaml?group=DEFAULT_GROUP&refreshEnabled=true
|
||||||
|
- nacos:blade-${spring.profiles.active}.yaml?group=DEFAULT_GROUP&refreshEnabled=true
|
||||||
|
- optional:nacos:${spring.application.name}-${spring.profiles.active}.yaml?group=DEFAULT_GROUP&refreshEnabled=true
|
||||||
|
cloud:
|
||||||
|
nacos:
|
||||||
|
username: ${NACOS_USERNAME:${NACOS_PROD_USERNAME:nacos}}
|
||||||
|
password: ${NACOS_PASSWORD:${NACOS_PROD_PASSWORD:nacos}}
|
||||||
|
server-addr: ${NACOS_HOST:${NACOS_PROD_HOST:127.0.0.1:8848}}
|
||||||
|
discovery:
|
||||||
|
namespace: "${NACOS_NAMESPACE:}"
|
||||||
|
config:
|
||||||
|
# 文件后缀名
|
||||||
|
file-extension: yaml
|
||||||
|
namespace: "${NACOS_NAMESPACE:}"
|
||||||
datasource:
|
datasource:
|
||||||
|
url: ${blade.datasource.${spring.profiles.active}.url}
|
||||||
|
username: ${blade.datasource.${spring.profiles.active}.username}
|
||||||
|
password: ${blade.datasource.${spring.profiles.active}.password}
|
||||||
|
# 在使用Spring默认数据源Hikari的情况下配置以下配置项
|
||||||
hikari:
|
hikari:
|
||||||
# 自动提交从池中返回的连接
|
# 自动提交从池中返回的连接
|
||||||
auto-commit: true
|
auto-commit: true
|
||||||
@@ -42,6 +66,8 @@ swagger:
|
|||||||
|
|
||||||
#第三方登陆
|
#第三方登陆
|
||||||
social:
|
social:
|
||||||
|
enabled: true
|
||||||
|
domain: http://127.0.0.1:2888
|
||||||
oauth:
|
oauth:
|
||||||
GITHUB:
|
GITHUB:
|
||||||
client-id: 233************
|
client-id: 233************
|
||||||
@@ -67,12 +93,12 @@ social:
|
|||||||
# IAM统一身份认证
|
# IAM统一身份认证
|
||||||
iam:
|
iam:
|
||||||
sso:
|
sso:
|
||||||
token-url: http://172.16.204.83:38000/gwzh/IAM/IAM_SSO_TOKEN
|
token-url: ${IAM_SSO_TOKEN_URL:http://172.16.204.83:38000/gwzh/IAM/IAM_SSO_TOKEN}
|
||||||
profile-url: http://172.16.204.83:38000/gwzh/IAM/IAM_SSO_PROFILE
|
profile-url: ${IAM_SSO_PROFILE_URL:http://172.16.204.83:38000/gwzh/IAM/IAM_SSO_PROFILE}
|
||||||
client-id: f0b52f23f71b1649c468
|
client-id: ${IAM_SSO_CLIENT_ID:f0b52f23f71b1649c468}
|
||||||
client-secret: 5b3d8575f0899946623ab86523ac3dd8ee98
|
client-secret: ${IAM_SSO_CLIENT_SECRET:5b3d8575f0899946623ab86523ac3dd8ee98}
|
||||||
system-client-id: saber3
|
system-client-id: ${IAM_SSO_SYSTEM_CLIENT_ID:saber3}
|
||||||
system-client-secret: saber3_secret
|
system-client-secret: ${IAM_SSO_SYSTEM_CLIENT_SECRET:saber3_secret}
|
||||||
redirect-uri: http://172.16.203.228:8000/callback
|
redirect-uri: ${IAM_SSO_REDIRECT_URI:http://172.16.203.228:8000/callback}
|
||||||
authorization: Z3d6aF90bXMtOVJJU0RVN1U6YW0yYkcwWnBJZ0RQZmtrSjNaZkZjUDBSaGFuSEtxQng=
|
authorization: ${IAM_SSO_AUTHORIZATION:Z3d6aF90bXMtOVJJU0RVN1U6YW0yYkcwWnBJZ0RQZmtrSjNaZkZjUDBSaGFuSEtxQng=}
|
||||||
profile-authorization: Z3d6aF90bXMtOVJJU0RVN1U6YW0yYkcwWnBJZ0RQZmtrSjNaZkZjUDBSaGFuSEtxQng=
|
profile-authorization: ${IAM_SSO_PROFILE_AUTHORIZATION:YjNlZmU0ODEwMzJiNGJhZTpkYzQ5NDY1NmQ4MzE0NThjODI5MzlmNzA2ZjliNDY3MQ==}
|
||||||
|
|||||||
@@ -22,11 +22,44 @@
|
|||||||
<groupId>org.springblade</groupId>
|
<groupId>org.springblade</groupId>
|
||||||
<artifactId>blade-starter-loadbalancer</artifactId>
|
<artifactId>blade-starter-loadbalancer</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springblade</groupId>
|
||||||
|
<artifactId>blade-starter-log</artifactId>
|
||||||
|
<!-- provided:LenientDateParser 编译期需要 ServiceException(位于 starter-log),
|
||||||
|
但 provided 不向下游传递——否则网关会经 common 拿到 blade-core-tool 的
|
||||||
|
BladeConverterConfiguration(implements WebMvcConfigurer),在无 spring-webmvc
|
||||||
|
的 WebFlux 网关里启动即崩。业务服务经 blade-core-boot 自带 starter-log,运行时不受影响。 -->
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.swagger.core.v3</groupId>
|
||||||
|
<artifactId>swagger-annotations</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-aop</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springblade</groupId>
|
<groupId>org.springblade</groupId>
|
||||||
<artifactId>blade-core-auto</artifactId>
|
<artifactId>blade-core-auto</artifactId>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.idev.excel</groupId>
|
||||||
|
<artifactId>fastexcel</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.huaweicloud</groupId>
|
||||||
|
<artifactId>esdk-obs-java-bundle</artifactId>
|
||||||
|
<version>3.24.9</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-io</groupId>
|
||||||
|
<artifactId>commons-io</artifactId>
|
||||||
|
<version>2.16.1</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|||||||
@@ -0,0 +1,145 @@
|
|||||||
|
/**
|
||||||
|
* BladeX Commercial License Agreement
|
||||||
|
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||||
|
* <p>
|
||||||
|
* Use of this software is governed by the Commercial License Agreement
|
||||||
|
* obtained after purchasing a license from BladeX.
|
||||||
|
* <p>
|
||||||
|
* 1. This software is for development use only under a valid license
|
||||||
|
* from BladeX.
|
||||||
|
* <p>
|
||||||
|
* 2. Redistribution of this software's source code to any third party
|
||||||
|
* without a commercial license is strictly prohibited.
|
||||||
|
* <p>
|
||||||
|
* 3. Licensees may copyright their own code but cannot use segments
|
||||||
|
* from this software for such purposes. Copyright of this software
|
||||||
|
* remains with BladeX.
|
||||||
|
* <p>
|
||||||
|
* Using this software signifies agreement to this License, and the software
|
||||||
|
* must not be used for illegal purposes.
|
||||||
|
* <p>
|
||||||
|
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||||
|
* not liable for any claims arising from secondary or illegal development.
|
||||||
|
* <p>
|
||||||
|
* Author: Chill Zhuang (bladejava@qq.com)
|
||||||
|
*/
|
||||||
|
package org.springblade.common.aspect;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.aspectj.lang.ProceedingJoinPoint;
|
||||||
|
import org.aspectj.lang.annotation.Around;
|
||||||
|
import org.aspectj.lang.annotation.Aspect;
|
||||||
|
import org.aspectj.lang.reflect.MethodSignature;
|
||||||
|
import org.springblade.core.log.annotation.ApiLog;
|
||||||
|
import org.springblade.core.log.constant.EventConstant;
|
||||||
|
import org.springblade.core.log.event.ApiLogEvent;
|
||||||
|
import org.springblade.core.log.model.LogApi;
|
||||||
|
import org.springblade.core.log.utils.LogAbstractUtil;
|
||||||
|
import org.springblade.core.tool.constant.BladeConstant;
|
||||||
|
import org.springblade.core.tool.utils.SpringUtil;
|
||||||
|
import org.springblade.core.tool.utils.StringUtil;
|
||||||
|
import org.springblade.core.tool.utils.WebUtil;
|
||||||
|
import org.springframework.core.annotation.AnnotationUtils;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 增删改操作日志切面
|
||||||
|
* 自动拦截 Controller 中的 save/submit/update/remove/delete 等方法,写入 blade_log_api
|
||||||
|
* 已标注 {@link ApiLog} 的方法跳过,避免重复记录
|
||||||
|
*
|
||||||
|
* @author BladeX
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Aspect
|
||||||
|
public class OperationApiLogAspect {
|
||||||
|
|
||||||
|
@Around("""
|
||||||
|
execution(* org.springblade..controller..*.save*(..))
|
||||||
|
|| execution(* org.springblade..controller..*.submit*(..))
|
||||||
|
|| execution(* org.springblade..controller..*.update*(..))
|
||||||
|
|| execution(* org.springblade..controller..*.remove*(..))
|
||||||
|
|| execution(* org.springblade..controller..*.delete*(..))
|
||||||
|
""")
|
||||||
|
public Object around(ProceedingJoinPoint point) throws Throwable {
|
||||||
|
MethodSignature signature = (MethodSignature) point.getSignature();
|
||||||
|
Method method = signature.getMethod();
|
||||||
|
if (!shouldRecord(method)) {
|
||||||
|
return point.proceed();
|
||||||
|
}
|
||||||
|
|
||||||
|
String className = point.getTarget().getClass().getName();
|
||||||
|
String methodName = method.getName();
|
||||||
|
String title = resolveTitle(point.getTarget().getClass(), method);
|
||||||
|
long beginTime = System.currentTimeMillis();
|
||||||
|
Object result = point.proceed();
|
||||||
|
long time = System.currentTimeMillis() - beginTime;
|
||||||
|
try {
|
||||||
|
publishEvent(methodName, className, title, time);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("记录操作日志失败: {}#{} - {}", className, methodName, e.getMessage());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean shouldRecord(Method method) {
|
||||||
|
if (method.getAnnotation(ApiLog.class) != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
HttpServletRequest request = WebUtil.getRequest();
|
||||||
|
if (request == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
String httpMethod = request.getMethod();
|
||||||
|
return !"GET".equalsIgnoreCase(httpMethod)
|
||||||
|
&& !"HEAD".equalsIgnoreCase(httpMethod)
|
||||||
|
&& !"OPTIONS".equalsIgnoreCase(httpMethod);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String resolveTitle(Class<?> targetClass, Method method) {
|
||||||
|
Tag tag = AnnotationUtils.findAnnotation(targetClass, Tag.class);
|
||||||
|
Operation operation = AnnotationUtils.findAnnotation(method, Operation.class);
|
||||||
|
String module = tag == null ? null : firstNonBlank(tag.name(), tag.description());
|
||||||
|
String action = operation == null ? null : firstNonBlank(operation.summary(), operation.description());
|
||||||
|
if (StringUtil.isNotBlank(module) && StringUtil.isNotBlank(action)) {
|
||||||
|
return module + "-" + action;
|
||||||
|
}
|
||||||
|
if (StringUtil.isNotBlank(action)) {
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
String simpleName = targetClass.getSimpleName().replace("Controller", "");
|
||||||
|
return simpleName + "-" + method.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String firstNonBlank(String... values) {
|
||||||
|
if (values == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (String value : values) {
|
||||||
|
if (StringUtil.isNotBlank(value)) {
|
||||||
|
return value.trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void publishEvent(String methodName, String methodClass, String title, long time) {
|
||||||
|
HttpServletRequest request = WebUtil.getRequest();
|
||||||
|
LogApi logApi = new LogApi();
|
||||||
|
logApi.setType(BladeConstant.LOG_NORMAL_TYPE);
|
||||||
|
logApi.setTitle(title);
|
||||||
|
logApi.setTime(String.valueOf(time));
|
||||||
|
logApi.setMethodClass(methodClass);
|
||||||
|
logApi.setMethodName(methodName);
|
||||||
|
LogAbstractUtil.addRequestInfoToLog(request, logApi);
|
||||||
|
Map<String, Object> event = new HashMap<>(16);
|
||||||
|
event.put(EventConstant.EVENT_LOG, logApi);
|
||||||
|
SpringUtil.publishEvent(new ApiLogEvent(event));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+15
-4
@@ -26,16 +26,27 @@
|
|||||||
package org.springblade.common.config;
|
package org.springblade.common.config;
|
||||||
|
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
import org.springblade.common.aspect.OperationApiLogAspect;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 公共封装包配置类
|
* 公共封装包配置类
|
||||||
*
|
*
|
||||||
* @author Chill
|
* @author Chill
|
||||||
*/
|
*/
|
||||||
@Configuration(proxyBeanMethods = false)
|
@AutoConfiguration
|
||||||
@AllArgsConstructor
|
|
||||||
public class BladeCommonConfiguration {
|
public class BladeCommonConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
|
||||||
|
@ConditionalOnClass(name = "org.springblade.core.log.event.ApiLogEvent")
|
||||||
|
@ConditionalOnProperty(value = "blade.log.operation.enabled", havingValue = "true", matchIfMissing = true)
|
||||||
|
public OperationApiLogAspect operationApiLogAspect() {
|
||||||
|
return new OperationApiLogAspect();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,4 +72,35 @@ public interface CommonConstant {
|
|||||||
*/
|
*/
|
||||||
Integer API_SCOPE_CATEGORY = 2;
|
Integer API_SCOPE_CATEGORY = 2;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是 2
|
||||||
|
*/
|
||||||
|
Integer YES = 2;
|
||||||
|
/**
|
||||||
|
* 不是 1
|
||||||
|
*/
|
||||||
|
Integer NO = 1;
|
||||||
|
/**
|
||||||
|
* 系统名称
|
||||||
|
*/
|
||||||
|
String SYSTEM_CODE = "CK-ERP";
|
||||||
|
/**
|
||||||
|
* 默认密码参数值
|
||||||
|
*/
|
||||||
|
String DEFAULT_PARAM_PASSWORD = "account.initPassword";
|
||||||
|
/**
|
||||||
|
* 默认角色参数值
|
||||||
|
*/
|
||||||
|
String DEFAULT_PARAM_ROLE = "account.initRole";
|
||||||
|
/**
|
||||||
|
* 默认角色
|
||||||
|
*/
|
||||||
|
String DEFAULT_ROLE = "default";
|
||||||
|
/**
|
||||||
|
* 同步的子公司名称根公司的值
|
||||||
|
*/
|
||||||
|
String DICT_KEY_SYNC_ROOT_COMPANY_NAME = "root_company";
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package org.springblade.common.constant;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据状态枚举
|
||||||
|
*
|
||||||
|
* @author vic
|
||||||
|
* @date 2024/8/26 6:45 PM
|
||||||
|
* @description DataStatusEnum
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum DataStatusEnum {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
ENABLE(1, "启用"),
|
||||||
|
|
||||||
|
DISABLE(2, "禁用"),
|
||||||
|
|
||||||
|
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
private final Integer code;
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 匹配枚举值
|
||||||
|
*
|
||||||
|
* @param code
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static boolean match(Integer code) {
|
||||||
|
for (DataStatusEnum state : values()) {
|
||||||
|
if (state.code.equals(code)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getNameStr(Integer code) {
|
||||||
|
for (DataStatusEnum state : values()) {
|
||||||
|
if (state.code.equals(code)) {
|
||||||
|
return state.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否可以编辑修改表单 .没有审批业务的使用此方法, 系统设计的是反的
|
||||||
|
* true:可编辑 false:不可编辑
|
||||||
|
* @param value
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static boolean canEdit(Integer value) {
|
||||||
|
return DISABLE.getCode().equals(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
package org.springblade.common.constant;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统字典类型枚举
|
||||||
|
* 响功能和流程的字典
|
||||||
|
* @author vic
|
||||||
|
* @date 2024/9/9 8:56 PM
|
||||||
|
* @description DictTypeEnum
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum DictTypeEnum {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
DATA_STATUS("data_status", "数据状态"),
|
||||||
|
APPROVE_STATUS("approve_state", "通用审批状态"),
|
||||||
|
COOPERATE_STATUS("cooperate_status", "合作状态"),
|
||||||
|
CUSTOMER_TYPE("customer_type", "客户类型"),
|
||||||
|
DELIVERER_TYPE("deliverer_type", "投送人类型"),
|
||||||
|
ENTERPRISE_TYPE("enterprise_type", "企业类型"),
|
||||||
|
SETTLEMENT_TYPE("settlement_type", "结算中心类型"),
|
||||||
|
CUSTOMER_COMPANY_TYPE("company_type","客户管理的企业类型"),
|
||||||
|
PARTNER_TYPE("partner_type","合作伙伴类型"),
|
||||||
|
SYSTEM_YES_NO("yes_no","系统是否"),
|
||||||
|
PLATFORM_TYPE("settlement_type","平台类型/结算中心类型"),
|
||||||
|
TAXPAYER_TYPE("taxpayer_type","纳税人分类"),
|
||||||
|
//部门简称
|
||||||
|
DEPARTMENT_SHORT_NAME("department_short_name","部门简称"),
|
||||||
|
PROCESS_TYPE("process_type","流程类型"),
|
||||||
|
|
||||||
|
//评级维度
|
||||||
|
RATING_DIMENSION("rating_dimension","评级维度"),
|
||||||
|
// oa公司部门同步
|
||||||
|
SYNC_COMPANY_NAME("sync_company_name","同步公司名称"),
|
||||||
|
SYNC_ROOT_COMPANY_DEPT_NAME("sync_root_company_dept_name","同步根公司下部门名称"),
|
||||||
|
DEPARTMENT_CODE("department_code","部门编号"),
|
||||||
|
|
||||||
|
//公式管理
|
||||||
|
FORMULA_TYPE("formula_type","公式类型"),
|
||||||
|
|
||||||
|
//财务管理
|
||||||
|
FINANCIAL_TYPE("financial_type","财务类型"),
|
||||||
|
SAP_TYPE("sap_type","财务结算类型"),
|
||||||
|
SETTLEMENT_CLASSIFICATION("settlement_classification","财务结算分类"),//内部结算、外部结算
|
||||||
|
SAP_DATA_STATUS("sap_data_status","财务结算单状态"),
|
||||||
|
SETTLEMENT_VOUCHER_TYPE("settlement_voucher_type","结算凭证类型"),
|
||||||
|
SETTLEMENT_NEXT_STATUS("settlement_next_status","下一步结算状态"),
|
||||||
|
|
||||||
|
PAYMENT_TYPE("payment_type","财务付款分类"),
|
||||||
|
PAYMENT_DATA_STATUS("payment_data_status","财务付款单状态"),
|
||||||
|
PAYEMENT_CLASSIFICATION("payment_classification","财务付款分类"), //内部付款、外部付款
|
||||||
|
PAYMENT_METHOD_TYPE("payment_method_type","财务付款支付方式"),
|
||||||
|
|
||||||
|
SAP_VERIFICATION_METHOD("verification_method","验证方式"),
|
||||||
|
|
||||||
|
/** 结算管理列表页的开票状态、付款状态 **/
|
||||||
|
SETT_PAYMENT_STATUS("payment_status","付款状态"),
|
||||||
|
SETT_INVOICE_STATUS("invoice_status","开票状态"),
|
||||||
|
|
||||||
|
|
||||||
|
// 车辆运输凭证
|
||||||
|
CERTIFICATE_BATCH_STATUS("certificate_batch_status","车辆运输凭证批次状态"),
|
||||||
|
CERTIFICATE_IDENTIFICATION_STATUS("certificate_identification_status","车辆运输凭证识别状态"),
|
||||||
|
CERTIFICATE_IDENTIFICATION_RESULT("certificate_identification_result","车辆运输凭证识别结果"),
|
||||||
|
|
||||||
|
// 文件任务状态
|
||||||
|
FILE_TASK_STATUS("file_task_status", "文件任务状态"),
|
||||||
|
// 运单管理-差异项 总净重
|
||||||
|
WEIGHT_STATUS("weight_status", "总净重"),
|
||||||
|
// 运单管理-比对结果 差异类型
|
||||||
|
DIFFERENCE_TYPE("difference_type", "差异类型"),
|
||||||
|
// 收款
|
||||||
|
CLAIM_STATUS("claim_status", "认领状态"),
|
||||||
|
RECEIPT_SOURCE("receipt_source", "收款来源"),
|
||||||
|
CURRENCY("currency", "币种"),
|
||||||
|
|
||||||
|
TRAJECTORY_STATUS("trajectory_status", "轨迹状态"),
|
||||||
|
|
||||||
|
HOME_BASE_TYPE("base_type","基地类型"),
|
||||||
|
|
||||||
|
FEE_NAME_TYPE("fee_name_type","费用类型"),
|
||||||
|
|
||||||
|
TAX_CODE("taxCode","税收编码"),
|
||||||
|
INPUT_INVOICE_STATUS("input_invoice_status","录入开票状态"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 城矿结算公司财务负责人
|
||||||
|
*/
|
||||||
|
SETTLEMENT_FINANCE_CONTROLLER("settlement_finance_controller", "城矿结算公司财务负责人"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 付款单据类型
|
||||||
|
*/
|
||||||
|
PAYMENT_DOCUMENT_TYPE("payment_document_type", "付款单据类型"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 归档状态
|
||||||
|
*/
|
||||||
|
ARCHIVE_STATUS("archive_status", "归档状态"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预警类型
|
||||||
|
*/
|
||||||
|
WARNING_TYPE("warning_type", "预警类型"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 消息状态
|
||||||
|
*/
|
||||||
|
MESSAGE_STATUS("message_status", "消息状态"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通知类型
|
||||||
|
*/
|
||||||
|
NOTICE_TYPE("notice_type", "通知类型"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通知对象类型
|
||||||
|
*/
|
||||||
|
NOTICE_OBJECT_TYPE("notice_object_type", "通知对象类型"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 项目磅单校验类型
|
||||||
|
*/
|
||||||
|
PROJECT_POUND_VALID_TYPE("project_pound_valid_type", "项目磅单校验类型"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 推送单据类型
|
||||||
|
*/
|
||||||
|
PUSH_DOC_TYPE("push_doc_type", "推送单据类型"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mk流程状态
|
||||||
|
*/
|
||||||
|
MK_STATUS("mk_status", "mk流程状态"),
|
||||||
|
|
||||||
|
;
|
||||||
|
|
||||||
|
final String type;
|
||||||
|
|
||||||
|
final String text;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 匹配枚举值
|
||||||
|
*
|
||||||
|
* @param type
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static boolean match(String type) {
|
||||||
|
for (DictTypeEnum state : values()) {
|
||||||
|
if (state.type.equals(type)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getNameStr(String type) {
|
||||||
|
for (DictTypeEnum state : values()) {
|
||||||
|
if (state.type.equals(type)) {
|
||||||
|
return state.text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -54,6 +54,12 @@ public interface LauncherConstant {
|
|||||||
*/
|
*/
|
||||||
String NACOS_PROD_ADDR = "172.16.203.228:8848";
|
String NACOS_PROD_ADDR = "172.16.203.228:8848";
|
||||||
|
|
||||||
|
String NACOS_PROD_HOST = "172.16.203.228:8848";
|
||||||
|
|
||||||
|
String NACOS_PROD_USERNAME = "nacos";
|
||||||
|
|
||||||
|
String NACOS_PROD_PASSWORD = "nacosTMS";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* nacos test 地址
|
* nacos test 地址
|
||||||
*/
|
*/
|
||||||
@@ -156,13 +162,42 @@ public interface LauncherConstant {
|
|||||||
* @return addr
|
* @return addr
|
||||||
*/
|
*/
|
||||||
static String nacosAddr(String profile) {
|
static String nacosAddr(String profile) {
|
||||||
|
String profileKey = profile == null ? "" : profile.toUpperCase();
|
||||||
|
String profileAddr = env("NACOS_" + profileKey + "_HOST");
|
||||||
|
if (profileAddr != null) return profileAddr;
|
||||||
|
String configuredAddr = env("NACOS_HOST");
|
||||||
|
if (configuredAddr != null) return configuredAddr;
|
||||||
return switch (profile) {
|
return switch (profile) {
|
||||||
case (AppConstant.PROD_CODE) -> NACOS_PROD_ADDR;
|
case (AppConstant.PROD_CODE) -> NACOS_PROD_HOST;
|
||||||
case (AppConstant.TEST_CODE) -> NACOS_TEST_ADDR;
|
case (AppConstant.TEST_CODE) -> NACOS_TEST_ADDR;
|
||||||
default -> NACOS_DEV_ADDR;
|
default -> NACOS_DEV_ADDR;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static String nacosUsername(String profile) {
|
||||||
|
String profileKey = profile == null ? "" : profile.toUpperCase();
|
||||||
|
String value = env("NACOS_" + profileKey + "_USERNAME");
|
||||||
|
if (value != null) return value;
|
||||||
|
value = env("NACOS_USERNAME");
|
||||||
|
if (value != null) return value;
|
||||||
|
return AppConstant.PROD_CODE.equals(profile) ? NACOS_PROD_USERNAME : NACOS_USERNAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String nacosPassword(String profile) {
|
||||||
|
String profileKey = profile == null ? "" : profile.toUpperCase();
|
||||||
|
String value = env("NACOS_" + profileKey + "_PASSWORD");
|
||||||
|
if (value != null) return value;
|
||||||
|
value = env("NACOS_PASSWORD");
|
||||||
|
if (value != null) return value;
|
||||||
|
return AppConstant.PROD_CODE.equals(profile) ? NACOS_PROD_PASSWORD : NACOS_PASSWORD;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String env(String name) {
|
||||||
|
String value = System.getProperty(name);
|
||||||
|
if (value == null || value.isBlank()) value = System.getenv(name);
|
||||||
|
return value == null || value.isBlank() ? null : value.trim();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 动态获取sentinel地址
|
* 动态获取sentinel地址
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -0,0 +1,411 @@
|
|||||||
|
/**
|
||||||
|
* BladeX Commercial License Agreement
|
||||||
|
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||||
|
* <p>
|
||||||
|
* Use of this software is governed by the Commercial License Agreement
|
||||||
|
* obtained after purchasing a license from BladeX.
|
||||||
|
* <p>
|
||||||
|
* 1. This software is for development use only under a valid license
|
||||||
|
* from BladeX.
|
||||||
|
* <p>
|
||||||
|
* 2. Redistribution of this software's source code to any third party
|
||||||
|
* without a commercial license is strictly prohibited.
|
||||||
|
* <p>
|
||||||
|
* 3. Licensees may copyright their own code but cannot use segments
|
||||||
|
* from this software for such purposes. Copyright of this software
|
||||||
|
* remains with BladeX.
|
||||||
|
* <p>
|
||||||
|
* Using this software signifies agreement to this License, and the software
|
||||||
|
* must not be used for illegal purposes.
|
||||||
|
* <p>
|
||||||
|
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||||
|
* not liable for any claims arising from secondary or illegal development.
|
||||||
|
* <p>
|
||||||
|
* Author: Chill Zhuang (bladejava@qq.com)
|
||||||
|
*/
|
||||||
|
package org.springblade.common.excel;
|
||||||
|
|
||||||
|
import cn.idev.excel.FastExcel;
|
||||||
|
import cn.idev.excel.annotation.ExcelIgnore;
|
||||||
|
import cn.idev.excel.annotation.ExcelProperty;
|
||||||
|
import cn.idev.excel.metadata.data.WriteCellData;
|
||||||
|
import cn.idev.excel.write.handler.CellWriteHandler;
|
||||||
|
import cn.idev.excel.write.metadata.holder.WriteSheetHolder;
|
||||||
|
import cn.idev.excel.write.metadata.holder.WriteTableHolder;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import org.apache.poi.ss.usermodel.Cell;
|
||||||
|
import org.apache.poi.ss.usermodel.CellStyle;
|
||||||
|
import org.apache.poi.ss.usermodel.Font;
|
||||||
|
import org.apache.poi.ss.usermodel.IndexedColors;
|
||||||
|
import org.apache.poi.ss.usermodel.Workbook;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导入失败明细 Excel 导出工具类
|
||||||
|
*
|
||||||
|
* @author Chill
|
||||||
|
*/
|
||||||
|
public class ImportFailureExcelUtil {
|
||||||
|
|
||||||
|
public static String formatErrorMessage(List<String> validationErrors) {
|
||||||
|
StringBuilder errorMessage = new StringBuilder();
|
||||||
|
for (int index = 0; index < validationErrors.size(); index++) {
|
||||||
|
if (index > 0) {
|
||||||
|
errorMessage.append(System.lineSeparator());
|
||||||
|
}
|
||||||
|
errorMessage.append(index + 1).append(". ").append(validationErrors.get(index));
|
||||||
|
}
|
||||||
|
return errorMessage.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addValidationError(List<String> validationErrors, boolean invalid, String message) {
|
||||||
|
if (invalid && message != null && !message.isBlank() && !validationErrors.contains(message)) {
|
||||||
|
validationErrors.add(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addLengthValidationError(List<String> validationErrors, String value, int maxLength, String message) {
|
||||||
|
addValidationError(validationErrors, value != null && value.length() > maxLength, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String FAILURE_REASON = "导入失败原因";
|
||||||
|
private static final String ERROR_MESSAGE_FIELD = "errorMessage";
|
||||||
|
private static final String FAILURE_REASON_FIELD = "failureReason";
|
||||||
|
|
||||||
|
private ImportFailureExcelUtil() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出导入失败明细
|
||||||
|
*
|
||||||
|
* @param response 响应
|
||||||
|
* @param fileName 文件名
|
||||||
|
* @param sheetName 工作表名
|
||||||
|
* @param data 失败数据
|
||||||
|
* @param excelClass 原导入 Excel 类型
|
||||||
|
*/
|
||||||
|
public static void export(HttpServletResponse response, String fileName, String sheetName, List<?> data, Class<?> excelClass) {
|
||||||
|
export(response, fileName, sheetName, data, excelClass, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出仅标红失败原因列的导入失败明细
|
||||||
|
*
|
||||||
|
* @param response 响应
|
||||||
|
* @param fileName 文件名
|
||||||
|
* @param sheetName 工作表名
|
||||||
|
* @param data 失败数据
|
||||||
|
* @param excelClass 原导入 Excel 类型
|
||||||
|
*/
|
||||||
|
public static void exportFailureReasonOnly(HttpServletResponse response, String fileName, String sheetName,
|
||||||
|
List<?> data, Class<?> excelClass) {
|
||||||
|
export(response, fileName, sheetName, data, excelClass, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出导入失败明细
|
||||||
|
*
|
||||||
|
* @param markErrorColumns 是否按失败原因里出现的列名,标红对应字段单元格
|
||||||
|
*/
|
||||||
|
private static void export(HttpServletResponse response, String fileName, String sheetName,
|
||||||
|
List<?> data, Class<?> excelClass, boolean markErrorColumns) {
|
||||||
|
response.setContentType("application/vnd.ms-excel");
|
||||||
|
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
|
||||||
|
String encodeFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8);
|
||||||
|
response.setHeader("Content-disposition", "attachment;filename=" + encodeFileName + ".xlsx");
|
||||||
|
List<Field> excelFields = excelFields(excelClass);
|
||||||
|
List<List<String>> head = buildHead(excelFields);
|
||||||
|
List<List<Object>> rows = buildRows(data, excelFields);
|
||||||
|
try {
|
||||||
|
FastExcel.write(response.getOutputStream())
|
||||||
|
.registerWriteHandler(new ImportFailureCellStyleHandler(excelFields, rows, markErrorColumns))
|
||||||
|
.head(head)
|
||||||
|
.sheet(sheetName)
|
||||||
|
.doWrite(rows);
|
||||||
|
} catch (IOException exception) {
|
||||||
|
throw new IllegalStateException("导出导入失败明细失败", exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Field> excelFields(Class<?> excelClass) {
|
||||||
|
List<Class<?>> classHierarchy = new ArrayList<>();
|
||||||
|
for (Class<?> current = excelClass; current != null; current = current.getSuperclass()) {
|
||||||
|
classHierarchy.add(0, current);
|
||||||
|
}
|
||||||
|
List<Field> fields = new ArrayList<>();
|
||||||
|
for (Class<?> current : classHierarchy) {
|
||||||
|
Arrays.stream(current.getDeclaredFields())
|
||||||
|
.filter(field -> field.getAnnotation(ExcelProperty.class) != null)
|
||||||
|
.filter(field -> field.getAnnotation(ExcelIgnore.class) == null)
|
||||||
|
.filter(field -> !Objects.equals(field.getName(), ERROR_MESSAGE_FIELD))
|
||||||
|
.filter(field -> !Objects.equals(field.getName(), FAILURE_REASON_FIELD))
|
||||||
|
.forEach(fields::add);
|
||||||
|
}
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<List<String>> buildHead(List<Field> excelFields) {
|
||||||
|
List<List<String>> head = new ArrayList<>();
|
||||||
|
for (Field field : excelFields) {
|
||||||
|
ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
|
||||||
|
String[] value = excelProperty.value();
|
||||||
|
head.add(List.of(value.length == 0 ? field.getName() : value[0]));
|
||||||
|
}
|
||||||
|
head.add(List.of(FAILURE_REASON));
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<List<Object>> buildRows(List<?> data, List<Field> excelFields) {
|
||||||
|
List<List<Object>> rows = new ArrayList<>();
|
||||||
|
for (Object item : data) {
|
||||||
|
List<Object> row = new ArrayList<>();
|
||||||
|
for (Field field : excelFields) {
|
||||||
|
row.add(fieldValue(item, field));
|
||||||
|
}
|
||||||
|
row.add(errorMessage(item));
|
||||||
|
rows.add(row);
|
||||||
|
}
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object fieldValue(Object item, Field field) {
|
||||||
|
try {
|
||||||
|
Field targetField = targetField(item.getClass(), field.getName());
|
||||||
|
targetField.setAccessible(true);
|
||||||
|
return targetField.get(item);
|
||||||
|
} catch (NoSuchFieldException | IllegalAccessException exception) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Object errorMessage(Object item) {
|
||||||
|
try {
|
||||||
|
Field field = targetField(item.getClass(), ERROR_MESSAGE_FIELD, FAILURE_REASON_FIELD);
|
||||||
|
field.setAccessible(true);
|
||||||
|
Object value = field.get(item);
|
||||||
|
return value == null ? "" : String.valueOf(value);
|
||||||
|
} catch (NoSuchFieldException | IllegalAccessException exception) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Field targetField(Class<?> itemClass, String... fieldNames) throws NoSuchFieldException {
|
||||||
|
Class<?> currentClass = itemClass;
|
||||||
|
while (currentClass != null) {
|
||||||
|
for (String fieldName : fieldNames) {
|
||||||
|
try {
|
||||||
|
return currentClass.getDeclaredField(fieldName);
|
||||||
|
} catch (NoSuchFieldException ignored) {
|
||||||
|
// 继续查找其他字段名或父类字段。
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentClass = currentClass.getSuperclass();
|
||||||
|
}
|
||||||
|
throw new NoSuchFieldException(String.join(",", fieldNames));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文本归一化:去掉空白、星号与中英文标点并统一小写
|
||||||
|
* <p>
|
||||||
|
* 目的是让「失败原因里写的列名」与「表头列名」能直接做包含匹配,
|
||||||
|
* 不受「*」「/」「:」等写法差异影响。
|
||||||
|
*/
|
||||||
|
private static String normalize(String value) {
|
||||||
|
return value == null ? "" : value.replaceAll("[\\s*_::,,。;;()()\\[\\]【】<>《》//、-]", "").toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 取某一列用于定位的候选关键词
|
||||||
|
* <p>
|
||||||
|
* 包含表头列名、字段名,以及列名按分隔符拆出的片段
|
||||||
|
* (如「车牌号/船号」拆出「车牌号」「船号」,失败原因只写其中一段时也能定位)。
|
||||||
|
*/
|
||||||
|
private static List<String> columnKeywords(Field field) {
|
||||||
|
ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
|
||||||
|
String[] value = excelProperty.value();
|
||||||
|
String columnName = value.length == 0 ? field.getName() : value[0];
|
||||||
|
List<String> keywords = new ArrayList<>();
|
||||||
|
keywords.add(columnName);
|
||||||
|
keywords.add(field.getName());
|
||||||
|
keywords.addAll(Arrays.asList(columnName.replace("*", "").split("[//、()()\\s]+")));
|
||||||
|
return keywords.stream()
|
||||||
|
.map(ImportFailureExcelUtil::normalize)
|
||||||
|
.filter(keyword -> keyword.length() >= 2)
|
||||||
|
.distinct()
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ImportFailureCellStyleHandler implements CellWriteHandler {
|
||||||
|
|
||||||
|
private final int failureReasonColumnIndex;
|
||||||
|
private final boolean markErrorColumns;
|
||||||
|
private final List<List<Object>> rows;
|
||||||
|
private final Map<Integer, Set<Integer>> redColumnsByRow = new HashMap<>();
|
||||||
|
private final Map<Short, CellStyle> redStyleCache = new HashMap<>();
|
||||||
|
private final Map<Short, CellStyle> noWrapStyleCache = new HashMap<>();
|
||||||
|
private final Map<Integer, Integer> columnWidthCache = new HashMap<>();
|
||||||
|
|
||||||
|
private ImportFailureCellStyleHandler(List<Field> excelFields, List<List<Object>> rows, boolean markErrorColumns) {
|
||||||
|
this.failureReasonColumnIndex = excelFields.size();
|
||||||
|
this.markErrorColumns = markErrorColumns;
|
||||||
|
this.rows = rows;
|
||||||
|
if (markErrorColumns) {
|
||||||
|
List<List<String>> columnKeywords = excelFields.stream()
|
||||||
|
.map(ImportFailureExcelUtil::columnKeywords)
|
||||||
|
.toList();
|
||||||
|
for (int rowIndex = 0; rowIndex < rows.size(); rowIndex++) {
|
||||||
|
redColumnsByRow.put(rowIndex, resolveRedColumns(rows.get(rowIndex), columnKeywords));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterCellDispose(
|
||||||
|
WriteSheetHolder writeSheetHolder,
|
||||||
|
WriteTableHolder writeTableHolder,
|
||||||
|
List<WriteCellData<?>> cellDataList,
|
||||||
|
Cell cell,
|
||||||
|
cn.idev.excel.metadata.Head head,
|
||||||
|
Integer relativeRowIndex,
|
||||||
|
Boolean isHead) {
|
||||||
|
if (Boolean.TRUE.equals(isHead)) {
|
||||||
|
adjustColumnWidth(cell);
|
||||||
|
markNoWrap(cell);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
adjustColumnWidth(cell);
|
||||||
|
if (relativeRowIndex == null || relativeRowIndex < 0 || relativeRowIndex >= rows.size()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (shouldMarkRed(relativeRowIndex, cell.getColumnIndex())) {
|
||||||
|
markRed(cell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算某一行需要标红的列
|
||||||
|
* <p>
|
||||||
|
* 失败原因里出现的列名即视为出错列;但当某个命中片段被另一列更长的命中
|
||||||
|
* 片段完全覆盖时(如原因「车船类型不能为空」同时命中「车船类型」和它的
|
||||||
|
* 子串「类型」),只保留更长的那一列,避免把无关列一起标红。
|
||||||
|
*/
|
||||||
|
private Set<Integer> resolveRedColumns(List<Object> row, List<List<String>> columnKeywords) {
|
||||||
|
Set<Integer> redColumns = new LinkedHashSet<>();
|
||||||
|
redColumns.add(failureReasonColumnIndex);
|
||||||
|
String failureReason = normalize(String.valueOf(row.get(failureReasonColumnIndex)));
|
||||||
|
if (failureReason.isEmpty()) {
|
||||||
|
return redColumns;
|
||||||
|
}
|
||||||
|
List<int[]> matches = new ArrayList<>();
|
||||||
|
for (int column = 0; column < columnKeywords.size(); column++) {
|
||||||
|
for (String keyword : columnKeywords.get(column)) {
|
||||||
|
int fromIndex = 0;
|
||||||
|
while (true) {
|
||||||
|
int index = failureReason.indexOf(keyword, fromIndex);
|
||||||
|
if (index < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
matches.add(new int[]{index, index + keyword.length(), column});
|
||||||
|
fromIndex = index + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int[] match : matches) {
|
||||||
|
if (!isSubsumed(match, matches)) {
|
||||||
|
redColumns.add(match[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return redColumns;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断该命中片段是否被另一列更长的命中片段完全覆盖
|
||||||
|
*/
|
||||||
|
private boolean isSubsumed(int[] match, List<int[]> matches) {
|
||||||
|
int matchLength = match[1] - match[0];
|
||||||
|
for (int[] other : matches) {
|
||||||
|
if (other[2] == match[2]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((other[1] - other[0]) > matchLength && other[0] <= match[0] && other[1] >= match[1]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean shouldMarkRed(int rowIndex, int columnIndex) {
|
||||||
|
if (columnIndex == failureReasonColumnIndex) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!markErrorColumns) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Set<Integer> redColumns = redColumnsByRow.get(rowIndex);
|
||||||
|
return redColumns != null && redColumns.contains(columnIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标红单元格
|
||||||
|
* <p>
|
||||||
|
* 统一只改字体颜色,不加底纹;空单元格的红色字体在屏幕上不可见,
|
||||||
|
* 这类错误依靠失败原因列的文字定位。
|
||||||
|
*/
|
||||||
|
private void markRed(Cell cell) {
|
||||||
|
CellStyle currentStyle = cell.getCellStyle();
|
||||||
|
CellStyle redStyle = redStyleCache.computeIfAbsent(currentStyle.getIndex(), styleIndex -> {
|
||||||
|
Workbook workbook = cell.getSheet().getWorkbook();
|
||||||
|
CellStyle newStyle = workbook.createCellStyle();
|
||||||
|
newStyle.cloneStyleFrom(currentStyle);
|
||||||
|
Font font = workbook.createFont();
|
||||||
|
font.setColor(IndexedColors.RED.getIndex());
|
||||||
|
newStyle.setFont(font);
|
||||||
|
newStyle.setWrapText(true);
|
||||||
|
return newStyle;
|
||||||
|
});
|
||||||
|
cell.setCellStyle(redStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void markNoWrap(Cell cell) {
|
||||||
|
CellStyle currentStyle = cell.getCellStyle();
|
||||||
|
CellStyle noWrapStyle = noWrapStyleCache.computeIfAbsent(currentStyle.getIndex(), styleIndex -> {
|
||||||
|
CellStyle newStyle = cell.getSheet().getWorkbook().createCellStyle();
|
||||||
|
newStyle.cloneStyleFrom(currentStyle);
|
||||||
|
newStyle.setWrapText(false);
|
||||||
|
return newStyle;
|
||||||
|
});
|
||||||
|
cell.setCellStyle(noWrapStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void adjustColumnWidth(Cell cell) {
|
||||||
|
int columnIndex = cell.getColumnIndex();
|
||||||
|
int columnWidth = Math.min(Math.max(displayWidth(cell.toString()) + 4, 12), 80) * 256;
|
||||||
|
Integer currentWidth = columnWidthCache.get(columnIndex);
|
||||||
|
if (currentWidth == null || columnWidth > currentWidth) {
|
||||||
|
columnWidthCache.put(columnIndex, columnWidth);
|
||||||
|
cell.getSheet().setColumnWidth(columnIndex, columnWidth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int displayWidth(String value) {
|
||||||
|
int width = 0;
|
||||||
|
for (int index = 0; index < value.length(); index++) {
|
||||||
|
width += value.charAt(index) > 255 ? 2 : 1;
|
||||||
|
}
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,198 @@
|
|||||||
|
/**
|
||||||
|
* BladeX Commercial License Agreement
|
||||||
|
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||||
|
* <p>
|
||||||
|
* Use of this software is governed by the Commercial License Agreement
|
||||||
|
* obtained after purchasing a license from BladeX.
|
||||||
|
* <p>
|
||||||
|
* 1. This software is for development use only under a valid license
|
||||||
|
* from BladeX.
|
||||||
|
* <p>
|
||||||
|
* 2. Redistribution of this software's source code to any third party
|
||||||
|
* without a commercial license is strictly prohibited.
|
||||||
|
* <p>
|
||||||
|
* 3. Licensees may copyright their own code but cannot use segments
|
||||||
|
* from this software for such purposes. Copyright of this software remains with BladeX.
|
||||||
|
* <p>
|
||||||
|
* Using this software signifies agreement to this License, and the software
|
||||||
|
* must not be used for illegal purposes.
|
||||||
|
* <p>
|
||||||
|
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||||
|
* not liable for any claims arising from secondary or illegal development.
|
||||||
|
* <p>
|
||||||
|
* Author: Chill Zhuang (bladejava@qq.com)
|
||||||
|
*/
|
||||||
|
package org.springblade.common.excel;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.LocalTime;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量导入宽容日期解析器(口径见根工作区 docs/import-spec.md)。
|
||||||
|
* <p>
|
||||||
|
* 解析规则:
|
||||||
|
* <ul>
|
||||||
|
* <li>分隔符 {@code -}、{@code /}、{@code .} 均接受;补零与否均可(2026-08-02 ≡ 2026-8-2)</li>
|
||||||
|
* <li>日期时间:日期部分同上,时间 HH:mm:ss,可省略秒或秒+分(2026-8-2 12:3 可解析)</li>
|
||||||
|
* <li>拒绝:两位年份(26-8-2)、日在前(2/8/2026)、无分隔符(20260802)</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @author Chill
|
||||||
|
*/
|
||||||
|
public final class LenientDateParser {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日期部分:4 位年 + 分隔符 + 1~2 位月 + 同一分隔符 + 1~2 位日(年在前,拒绝两位年份与日在前)。
|
||||||
|
*/
|
||||||
|
private static final Pattern DATE_PATTERN = Pattern.compile("(\\d{4})([-/.])(\\d{1,2})\\2(\\d{1,2})");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 时间部分:1~2 位时[:1~2 位分[:1~2 位秒]],逐级可省略。
|
||||||
|
*/
|
||||||
|
private static final Pattern TIME_PATTERN = Pattern.compile("(\\d{1,2})(?::(\\d{1,2})(?::(\\d{1,2}))?)?");
|
||||||
|
|
||||||
|
private LenientDateParser() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析日期文本,失败返回 {@code null}。
|
||||||
|
*
|
||||||
|
* @param value 单元格原始文本
|
||||||
|
* @return 日期;无法识别时返回 null
|
||||||
|
*/
|
||||||
|
public static LocalDate parseDateOrNull(String value) {
|
||||||
|
String normalized = normalize(value);
|
||||||
|
if (normalized == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Matcher matcher = DATE_PATTERN.matcher(normalized);
|
||||||
|
if (!matcher.matches()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return LocalDate.of(Integer.parseInt(matcher.group(1)),
|
||||||
|
Integer.parseInt(matcher.group(3)), Integer.parseInt(matcher.group(4)));
|
||||||
|
} catch (NumberFormatException | java.time.DateTimeException exception) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析日期文本;允许携带合法时间部分并截断(如 2026-9-1 8:0:0 按 2026-09-01 解析,
|
||||||
|
* 与 fastexcel 内置 LocalDate 转换的既有宽容度保持一致),失败返回 {@code null}。
|
||||||
|
*
|
||||||
|
* @param value 单元格原始文本
|
||||||
|
* @return 日期;无法识别时返回 null
|
||||||
|
*/
|
||||||
|
public static LocalDate parseDateLenientlyOrNull(String value) {
|
||||||
|
LocalDate date = parseDateOrNull(value);
|
||||||
|
if (date != null) {
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
LocalDateTime dateTime = parseDateTimeOrNull(value);
|
||||||
|
return dateTime == null ? null : dateTime.toLocalDate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析日期时间文本;纯日期按当日零点处理,失败返回 {@code null}。
|
||||||
|
*
|
||||||
|
* @param value 单元格原始文本
|
||||||
|
* @return 日期时间;无法识别时返回 null
|
||||||
|
*/
|
||||||
|
public static LocalDateTime parseDateTimeOrNull(String value) {
|
||||||
|
String normalized = normalize(value);
|
||||||
|
if (normalized == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// 拆出日期与时间两部分;中间允许 1 个及以上空白或小写 t(2026-8-2t12:3 亦接受)。
|
||||||
|
String[] parts = normalized.split("[ \\t]+|(?<=\\d)t", 2);
|
||||||
|
if (parts.length == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
LocalDate date = parseDateOrNull(parts[0]);
|
||||||
|
if (date == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (parts.length == 1) {
|
||||||
|
return date.atStartOfDay();
|
||||||
|
}
|
||||||
|
Matcher matcher = TIME_PATTERN.matcher(parts[1]);
|
||||||
|
if (!matcher.matches()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
int hour = Integer.parseInt(matcher.group(1));
|
||||||
|
int minute = matcher.group(2) == null ? 0 : Integer.parseInt(matcher.group(2));
|
||||||
|
int second = matcher.group(3) == null ? 0 : Integer.parseInt(matcher.group(3));
|
||||||
|
return LocalDateTime.of(date, LocalTime.of(hour, minute, second));
|
||||||
|
} catch (NumberFormatException | java.time.DateTimeException exception) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析日期文本,失败抛出携带口径文案的 {@link org.springblade.core.log.exception.ServiceException}。
|
||||||
|
* 空白与 {@code null} 返回 {@code null}(可选字段由业务校验决定是否必填)。
|
||||||
|
*
|
||||||
|
* @param value 单元格原始文本
|
||||||
|
* @param columnName 导入模板列名(用于失败原因文案与失败明细标红定位)
|
||||||
|
* @return 日期
|
||||||
|
*/
|
||||||
|
public static LocalDate parseDate(String value, String columnName) {
|
||||||
|
if (value == null || value.trim().isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
LocalDate date = parseDateLenientlyOrNull(value);
|
||||||
|
if (date == null) {
|
||||||
|
throw unrecognizedDate(value, columnName);
|
||||||
|
}
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析日期时间文本,失败抛出携带口径文案的 {@link org.springblade.core.log.exception.ServiceException}。
|
||||||
|
* 空白与 {@code null} 返回 {@code null}(可选字段由业务校验决定是否必填)。
|
||||||
|
*
|
||||||
|
* @param value 单元格原始文本
|
||||||
|
* @param columnName 导入模板列名(用于失败原因文案与失败明细标红定位)
|
||||||
|
* @return 日期时间
|
||||||
|
*/
|
||||||
|
public static LocalDateTime parseDateTime(String value, String columnName) {
|
||||||
|
if (value == null || value.trim().isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
LocalDateTime dateTime = parseDateTimeOrNull(value);
|
||||||
|
if (dateTime == null) {
|
||||||
|
throw unrecognizedDate(value, columnName);
|
||||||
|
}
|
||||||
|
return dateTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 按口径构造「日期格式无法识别」错误。
|
||||||
|
*
|
||||||
|
* @param value 单元格原始文本
|
||||||
|
* @param columnName 导入模板列名
|
||||||
|
* @return 业务异常
|
||||||
|
*/
|
||||||
|
public static org.springblade.core.log.exception.ServiceException unrecognizedDate(String value, String columnName) {
|
||||||
|
String displayName = columnName == null || columnName.isBlank() ? "日期" : columnName;
|
||||||
|
return new org.springblade.core.log.exception.ServiceException(
|
||||||
|
displayName + " 日期格式无法识别:" + (value == null ? "" : value.trim()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 归一化输入:去首尾空白,跳过空白值。
|
||||||
|
*/
|
||||||
|
private static String normalize(String value) {
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String normalized = value.trim();
|
||||||
|
return normalized.isEmpty() ? null : normalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+75
@@ -0,0 +1,75 @@
|
|||||||
|
/**
|
||||||
|
* BladeX Commercial License Agreement
|
||||||
|
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||||
|
* <p>
|
||||||
|
* Use of this software is governed by the Commercial License Agreement
|
||||||
|
* obtained after purchasing a license from BladeX.
|
||||||
|
* <p>
|
||||||
|
* 1. This software is for development use only under a valid license
|
||||||
|
* from BladeX.
|
||||||
|
* <p>
|
||||||
|
* 2. Redistribution of this software's source code to any third party
|
||||||
|
* without a commercial license is strictly prohibited.
|
||||||
|
* <p>
|
||||||
|
* 3. Licensees may copyright their own code but cannot use segments
|
||||||
|
* from this software for such purposes. Copyright of this software remains with BladeX.
|
||||||
|
* <p>
|
||||||
|
* Using this software signifies agreement to this License, and the software
|
||||||
|
* must not be used for illegal purposes.
|
||||||
|
* <p>
|
||||||
|
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||||
|
* not liable for any claims arising from secondary or illegal development.
|
||||||
|
* <p>
|
||||||
|
* Author: Chill Zhuang (bladejava@qq.com)
|
||||||
|
*/
|
||||||
|
package org.springblade.common.excel;
|
||||||
|
|
||||||
|
import cn.idev.excel.converters.Converter;
|
||||||
|
import cn.idev.excel.enums.CellDataTypeEnum;
|
||||||
|
import cn.idev.excel.metadata.GlobalConfiguration;
|
||||||
|
import cn.idev.excel.metadata.data.ReadCellData;
|
||||||
|
import cn.idev.excel.metadata.data.WriteCellData;
|
||||||
|
import cn.idev.excel.metadata.property.ExcelContentProperty;
|
||||||
|
import cn.idev.excel.util.DateUtils;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量导入宽容日期转换器(String 承载):Excel 数值日期(序列号)转 ISO 文本,文本原样透传。
|
||||||
|
* <p>
|
||||||
|
* 文本的宽容解析由服务层调用 {@link LenientDateParser} 完成,不在此处抛错——
|
||||||
|
* 转换器阶段抛出的异常会被 fastexcel 包装成 ExcelDataConvertException 直接中断读取,
|
||||||
|
* 无法进入导入失败明细流程。口径见根工作区 docs/import-spec.md。
|
||||||
|
*
|
||||||
|
* @author Chill
|
||||||
|
*/
|
||||||
|
public class LenientDateStringConverter implements Converter<String> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> supportJavaTypeKey() {
|
||||||
|
return String.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CellDataTypeEnum supportExcelTypeKey() {
|
||||||
|
return CellDataTypeEnum.STRING;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty,
|
||||||
|
GlobalConfiguration globalConfiguration) {
|
||||||
|
if (cellData.getType() == CellDataTypeEnum.NUMBER) {
|
||||||
|
LocalDate date = DateUtils.getLocalDateTime(cellData.getNumberValue().doubleValue(),
|
||||||
|
globalConfiguration.getUse1904windowing()).toLocalDate();
|
||||||
|
return date.toString();
|
||||||
|
}
|
||||||
|
return cellData.getStringValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WriteCellData<?> convertToExcelData(String value, ExcelContentProperty contentProperty,
|
||||||
|
GlobalConfiguration globalConfiguration) {
|
||||||
|
return new WriteCellData<>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+82
@@ -0,0 +1,82 @@
|
|||||||
|
/**
|
||||||
|
* BladeX Commercial License Agreement
|
||||||
|
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||||
|
* <p>
|
||||||
|
* Use of this software is governed by the Commercial License Agreement
|
||||||
|
* obtained after purchasing a license from BladeX.
|
||||||
|
* <p>
|
||||||
|
* 1. This software is for development use only under a valid license
|
||||||
|
* from BladeX.
|
||||||
|
* <p>
|
||||||
|
* 2. Redistribution of this software's source code to any third party
|
||||||
|
* without a commercial license is strictly prohibited.
|
||||||
|
* <p>
|
||||||
|
* 3. Licensees may copyright their own code but cannot use segments
|
||||||
|
* from this software for such purposes. Copyright of this software remains with BladeX.
|
||||||
|
* <p>
|
||||||
|
* Using this software signifies agreement to this License, and the software
|
||||||
|
* must not be used for illegal purposes.
|
||||||
|
* <p>
|
||||||
|
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||||
|
* not liable for any claims arising from secondary or illegal development.
|
||||||
|
* <p>
|
||||||
|
* Author: Chill Zhuang (bladejava@qq.com)
|
||||||
|
*/
|
||||||
|
package org.springblade.common.excel;
|
||||||
|
|
||||||
|
import cn.idev.excel.converters.Converter;
|
||||||
|
import cn.idev.excel.enums.CellDataTypeEnum;
|
||||||
|
import cn.idev.excel.metadata.GlobalConfiguration;
|
||||||
|
import cn.idev.excel.metadata.data.ReadCellData;
|
||||||
|
import cn.idev.excel.metadata.data.WriteCellData;
|
||||||
|
import cn.idev.excel.metadata.property.ExcelContentProperty;
|
||||||
|
import cn.idev.excel.util.DateUtils;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量导入宽容日期时间转换器(String 承载):Excel 数值日期(序列号)转
|
||||||
|
* {@code yyyy-MM-dd HH:mm:ss} 文本,文本原样透传。
|
||||||
|
* <p>
|
||||||
|
* 文本的宽容解析由服务层调用 {@link LenientDateParser} 完成,不在此处抛错——
|
||||||
|
* 转换器阶段抛出的异常会被 fastexcel 包装成 ExcelDataConvertException 直接中断读取,
|
||||||
|
* 无法进入导入失败明细流程。口径见根工作区 docs/import-spec.md。
|
||||||
|
*
|
||||||
|
* @author Chill
|
||||||
|
*/
|
||||||
|
public class LenientDateTimeStringConverter implements Converter<String> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数值日期序列号转文本的输出格式,与导入模板展示格式保持一致。
|
||||||
|
*/
|
||||||
|
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> supportJavaTypeKey() {
|
||||||
|
return String.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CellDataTypeEnum supportExcelTypeKey() {
|
||||||
|
return CellDataTypeEnum.STRING;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty,
|
||||||
|
GlobalConfiguration globalConfiguration) {
|
||||||
|
if (cellData.getType() == CellDataTypeEnum.NUMBER) {
|
||||||
|
LocalDateTime dateTime = DateUtils.getLocalDateTime(cellData.getNumberValue().doubleValue(),
|
||||||
|
globalConfiguration.getUse1904windowing());
|
||||||
|
return dateTime.format(DATE_TIME_FORMATTER);
|
||||||
|
}
|
||||||
|
return cellData.getStringValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WriteCellData<?> convertToExcelData(String value, ExcelContentProperty contentProperty,
|
||||||
|
GlobalConfiguration globalConfiguration) {
|
||||||
|
return new WriteCellData<>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -27,6 +27,7 @@ package org.springblade.common.launch;
|
|||||||
|
|
||||||
import org.springblade.common.constant.LauncherConstant;
|
import org.springblade.common.constant.LauncherConstant;
|
||||||
import org.springblade.core.auto.service.AutoService;
|
import org.springblade.core.auto.service.AutoService;
|
||||||
|
import org.springblade.core.launch.BladeApplication;
|
||||||
import org.springblade.core.launch.service.LauncherService;
|
import org.springblade.core.launch.service.LauncherService;
|
||||||
import org.springblade.core.launch.utils.PropsUtil;
|
import org.springblade.core.launch.utils.PropsUtil;
|
||||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||||
@@ -45,14 +46,24 @@ public class LauncherServiceImpl implements LauncherService {
|
|||||||
public void launcher(SpringApplicationBuilder builder, String appName, String profile, boolean isLocalDev) {
|
public void launcher(SpringApplicationBuilder builder, String appName, String profile, boolean isLocalDev) {
|
||||||
Properties props = System.getProperties();
|
Properties props = System.getProperties();
|
||||||
|
|
||||||
// nacos注册中心配置
|
if (BladeApplication.isNacosConfigEnabled()) {
|
||||||
PropsUtil.setProperty(props, "spring.cloud.nacos.discovery.username", LauncherConstant.NACOS_USERNAME);
|
String nacosUsername = LauncherConstant.nacosUsername(profile);
|
||||||
PropsUtil.setProperty(props, "spring.cloud.nacos.discovery.password", LauncherConstant.NACOS_PASSWORD);
|
String nacosPassword = LauncherConstant.nacosPassword(profile);
|
||||||
PropsUtil.setProperty(props, "spring.cloud.nacos.discovery.server-addr", LauncherConstant.nacosAddr(profile));
|
String nacosAddr = LauncherConstant.nacosAddr(profile);
|
||||||
// nacos配置中心配置
|
// nacos公共配置,spring.config.import在配置中心和注册中心初始化前读取
|
||||||
PropsUtil.setProperty(props, "spring.cloud.nacos.config.username", LauncherConstant.NACOS_USERNAME);
|
PropsUtil.setProperty(props, "spring.cloud.nacos.username", nacosUsername);
|
||||||
PropsUtil.setProperty(props, "spring.cloud.nacos.config.password", LauncherConstant.NACOS_PASSWORD);
|
PropsUtil.setProperty(props, "spring.cloud.nacos.password", nacosPassword);
|
||||||
PropsUtil.setProperty(props, "spring.cloud.nacos.config.server-addr", LauncherConstant.nacosAddr(profile));
|
PropsUtil.setProperty(props, "spring.cloud.nacos.server-addr", nacosAddr);
|
||||||
|
// nacos注册中心配置
|
||||||
|
PropsUtil.setProperty(props, "spring.cloud.nacos.discovery.username", nacosUsername);
|
||||||
|
PropsUtil.setProperty(props, "spring.cloud.nacos.discovery.password", nacosPassword);
|
||||||
|
PropsUtil.setProperty(props, "spring.cloud.nacos.discovery.server-addr", nacosAddr);
|
||||||
|
// nacos配置中心配置
|
||||||
|
PropsUtil.setProperty(props, "spring.cloud.nacos.config.username", nacosUsername);
|
||||||
|
PropsUtil.setProperty(props, "spring.cloud.nacos.config.password", nacosPassword);
|
||||||
|
PropsUtil.setProperty(props, "spring.cloud.nacos.config.server-addr", nacosAddr);
|
||||||
|
}
|
||||||
|
|
||||||
// sentinel配置
|
// sentinel配置
|
||||||
PropsUtil.setProperty(props, "spring.cloud.sentinel.transport.dashboard", LauncherConstant.sentinelAddr(profile));
|
PropsUtil.setProperty(props, "spring.cloud.sentinel.transport.dashboard", LauncherConstant.sentinelAddr(profile));
|
||||||
// 多数据源配置
|
// 多数据源配置
|
||||||
|
|||||||
@@ -0,0 +1,275 @@
|
|||||||
|
package org.springblade.common.utils;
|
||||||
|
|
||||||
|
import com.alibaba.cloud.commons.lang.StringUtils;
|
||||||
|
import com.obs.services.ObsClient;
|
||||||
|
import com.obs.services.exception.ObsException;
|
||||||
|
import com.obs.services.model.*;
|
||||||
|
import jakarta.activation.MimetypesFileTypeMap;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import org.apache.commons.io.FilenameUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ClassName: OBSUtil
|
||||||
|
* @Description:
|
||||||
|
* @Author: zhaowei
|
||||||
|
* @Date: 2024/8/22
|
||||||
|
*/
|
||||||
|
public class ObsUtil {
|
||||||
|
private final static Logger logger = LoggerFactory.getLogger(ObsUtil.class);
|
||||||
|
|
||||||
|
private static MimetypesFileTypeMap fileTypeMap = new MimetypesFileTypeMap();
|
||||||
|
/**
|
||||||
|
* 华为云OBS
|
||||||
|
*/
|
||||||
|
public static String obsEndpoint;
|
||||||
|
public static String obsAccessKeyId;
|
||||||
|
public static String obsAccessKeySecret;
|
||||||
|
public static String obsBucketName;
|
||||||
|
public static String obsRootDirectory;
|
||||||
|
|
||||||
|
@Value("${obs.endpoint}")
|
||||||
|
public void setObsEndpoint(String endpoint) {
|
||||||
|
ObsUtil.obsEndpoint = endpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Value("${obs.access-key-id}")
|
||||||
|
public void setObsAccessKeyId(String accessKeyId) {
|
||||||
|
ObsUtil.obsAccessKeyId = accessKeyId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Value("${obs.access-key-secret}")
|
||||||
|
public void setObsAccessKeySecret(String accessKeySecret) {
|
||||||
|
ObsUtil.obsAccessKeySecret = accessKeySecret;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Value("${obs.bucket-name}")
|
||||||
|
public void setObsBucketName(String bucketName) {
|
||||||
|
ObsUtil.obsBucketName = bucketName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Value("${obs.root-directory}")
|
||||||
|
public void setObsRootDirectory(String rootDirectory) {
|
||||||
|
ObsUtil.obsRootDirectory = rootDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 如果Bucket不存在,则创建它。
|
||||||
|
private static void ensureBucket(ObsClient client, String bucketName) throws ObsException {
|
||||||
|
|
||||||
|
if (client.headBucket(bucketName)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 创建bucket
|
||||||
|
client.createBucket(bucketName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 把Bucket设置为所有人可读
|
||||||
|
private static void setBucketPublicReadable(String bucketName) throws ObsException {
|
||||||
|
//华为云OBS
|
||||||
|
ObsClient obsClient = new ObsClient(obsAccessKeyId, obsAccessKeySecret, obsEndpoint);
|
||||||
|
if (obsClient.doesObjectExist(obsBucketName, obsRootDirectory)) {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
obsClient.createBucket(obsBucketName);
|
||||||
|
}
|
||||||
|
obsClient.setBucketAcl(obsBucketName, com.obs.services.model.AccessControlList.REST_CANNED_PUBLIC_READ_WRITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传一个Object
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* @param content
|
||||||
|
* @throws FileNotFoundException
|
||||||
|
*/
|
||||||
|
public static String putObject(String key, InputStream content) throws IOException {
|
||||||
|
//华为云OBS
|
||||||
|
ObsClient obsClient = new ObsClient(obsAccessKeyId, obsAccessKeySecret, obsEndpoint);
|
||||||
|
try {
|
||||||
|
obsClient.putObject(obsBucketName, key, content);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("obs put object error", e);
|
||||||
|
throw new RuntimeException("obs eror" + e.getMessage());
|
||||||
|
} finally {
|
||||||
|
obsClient.close();
|
||||||
|
}
|
||||||
|
return getExpireUrl(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载文件
|
||||||
|
* @param objectKey 上传到OBS起的名
|
||||||
|
* @param filename 文件下载到本地保存的路径
|
||||||
|
* @throws ObsException
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public static void downloadFile(String objectKey, String filename) throws ObsException, IOException {
|
||||||
|
//华为云OBS
|
||||||
|
ObsClient obsClient = new ObsClient(obsAccessKeyId, obsAccessKeySecret, obsEndpoint);
|
||||||
|
try {
|
||||||
|
obsClient.getObject(obsBucketName, objectKey);
|
||||||
|
} finally {
|
||||||
|
obsClient.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除一个Bucket和其中的Objects
|
||||||
|
*
|
||||||
|
* @param bucketName Bucket名
|
||||||
|
* @throws ObsException
|
||||||
|
*/
|
||||||
|
private static void deleteBucket(String bucketName) throws ObsException {
|
||||||
|
ObsClient obsClient = new ObsClient(obsAccessKeyId, obsAccessKeySecret, obsEndpoint);
|
||||||
|
com.obs.services.model.ObjectListing objectListing = obsClient.listObjects(bucketName);
|
||||||
|
List<ObsObject> listObject = objectListing.getObjects();
|
||||||
|
for (ObsObject o : listObject) {
|
||||||
|
obsClient.deleteObject(bucketName, o.getObjectKey());
|
||||||
|
}
|
||||||
|
obsClient.deleteBucket(bucketName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除Object
|
||||||
|
* @param objList Object的key(目录路径/文件名)
|
||||||
|
*/
|
||||||
|
public static void delelteObjectList(List<String> objList) {
|
||||||
|
ObsClient obsClient = new ObsClient(obsAccessKeyId, obsAccessKeySecret, obsEndpoint);
|
||||||
|
for (String key : objList) {
|
||||||
|
obsClient.deleteObject(obsBucketName, key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除Object
|
||||||
|
* @param key 目录路径/文件名
|
||||||
|
*/
|
||||||
|
public static void delelteObject(String key) {
|
||||||
|
ObsClient obsClient = new ObsClient(obsAccessKeyId, obsAccessKeySecret, obsEndpoint);
|
||||||
|
obsClient.deleteObject(obsBucketName, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过key获取带签名的url
|
||||||
|
*/
|
||||||
|
public static String getExpireUrl(String key) {
|
||||||
|
String fileUrl = "";
|
||||||
|
ObsClient obsClient = new ObsClient(obsAccessKeyId, obsAccessKeySecret, obsEndpoint);
|
||||||
|
// URL有效期,3600秒.5分钟
|
||||||
|
// long expireSeconds = 3600L;
|
||||||
|
// 约等于1年
|
||||||
|
long expireSeconds = 31536000L;
|
||||||
|
TemporarySignatureRequest request = new TemporarySignatureRequest(HttpMethodEnum.GET, expireSeconds);
|
||||||
|
request.setBucketName(obsBucketName);
|
||||||
|
request.setObjectKey(key);
|
||||||
|
TemporarySignatureResponse response = obsClient.createTemporarySignature(request);
|
||||||
|
fileUrl = response.getSignedUrl();
|
||||||
|
return fileUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传文件至OSS并且返回链接地址
|
||||||
|
* @param url 远程路径
|
||||||
|
* @param ossDir OSS目录
|
||||||
|
* @param fileName 文件名
|
||||||
|
* @return 文件OSS链接
|
||||||
|
* @modify
|
||||||
|
*/
|
||||||
|
public static String uploadFile2Oss(URL url, String ossDir, String fileName) {
|
||||||
|
InputStream is;
|
||||||
|
String key = ossDir + fileName;
|
||||||
|
try {
|
||||||
|
is = url.openStream();
|
||||||
|
return uploadFile2OSS(is, key);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
logger.error("上传失败!", ex);
|
||||||
|
}
|
||||||
|
return StringUtils.EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传到OSS服务器 如果同名文件会覆盖服务器上的
|
||||||
|
*
|
||||||
|
* @param inputStream 文件流
|
||||||
|
* @return 返回文件访问路径
|
||||||
|
*/
|
||||||
|
@SneakyThrows
|
||||||
|
public static String uploadFile2OSS(InputStream inputStream, String key) {
|
||||||
|
ObjectMetadata objectMetadata = new ObjectMetadata();
|
||||||
|
objectMetadata.setCacheControl("no-cache");
|
||||||
|
String fileName = FilenameUtils.getName(key);
|
||||||
|
objectMetadata.setContentType(fileTypeMap.getContentType(fileName));
|
||||||
|
objectMetadata.addUserMetadata("filename", fileName);
|
||||||
|
|
||||||
|
ObsClient obsClient = new ObsClient(obsAccessKeyId,
|
||||||
|
obsAccessKeySecret, obsEndpoint);
|
||||||
|
try {
|
||||||
|
obsClient.putObject(obsBucketName, obsRootDirectory + key, inputStream, objectMetadata);
|
||||||
|
return obsRootDirectory + "/" + key;
|
||||||
|
} finally {
|
||||||
|
obsClient.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public Boolean checkObjectExist(String key) {
|
||||||
|
Boolean existsFlag = false;
|
||||||
|
ObsClient obsClient = new ObsClient(obsAccessKeyId, obsAccessKeySecret, obsEndpoint);
|
||||||
|
try {
|
||||||
|
existsFlag = obsClient.doesObjectExist(obsBucketName, key);
|
||||||
|
} finally {
|
||||||
|
obsClient.close();
|
||||||
|
}
|
||||||
|
return existsFlag;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载文件
|
||||||
|
* @param objectKey 上传到OBS起的名
|
||||||
|
* @param filename 文件下载到本地保存的路径
|
||||||
|
* @throws ObsException
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public static void downloadFileNew(String objectKey, String filename){
|
||||||
|
//华为云OBS
|
||||||
|
ObsClient obsClient = new ObsClient(obsAccessKeyId, obsAccessKeySecret, obsEndpoint);
|
||||||
|
try {
|
||||||
|
ObsObject ossObject = obsClient.getObject(obsBucketName, objectKey);
|
||||||
|
|
||||||
|
OutputStream outputStream = null;
|
||||||
|
try {
|
||||||
|
outputStream = new BufferedOutputStream(new FileOutputStream(filename));
|
||||||
|
byte[] buffer = new byte[8192];
|
||||||
|
int bytesRead;
|
||||||
|
while ((bytesRead = ossObject.getObjectContent().read(buffer)) != -1) {
|
||||||
|
outputStream.write(buffer, 0, bytesRead);
|
||||||
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
} finally {
|
||||||
|
if (outputStream != null) {
|
||||||
|
try {
|
||||||
|
outputStream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (obsClient != null) {
|
||||||
|
try {
|
||||||
|
obsClient.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
/**
|
||||||
|
* BladeX Commercial License Agreement
|
||||||
|
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||||
|
* <p>
|
||||||
|
* Use of this software is governed by the Commercial License Agreement
|
||||||
|
* obtained after purchasing a license from BladeX.
|
||||||
|
* <p>
|
||||||
|
* 1. This software is for development use only under a valid license
|
||||||
|
* from BladeX.
|
||||||
|
* <p>
|
||||||
|
* 2. Redistribution of this software's source code to any third party
|
||||||
|
* without a commercial license is strictly prohibited.
|
||||||
|
* <p>
|
||||||
|
* 3. Licensees may copyright their own code but cannot use segments
|
||||||
|
* from this software for such purposes. Copyright of this software
|
||||||
|
* remains with BladeX.
|
||||||
|
* <p>
|
||||||
|
* Using this software signifies agreement to this License, and the software
|
||||||
|
* must not be used for illegal purposes.
|
||||||
|
* <p>
|
||||||
|
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||||
|
* not liable for any claims arising from secondary or illegal development.
|
||||||
|
* <p>
|
||||||
|
* Author: Chill Zhuang (bladejava@qq.com)
|
||||||
|
*/
|
||||||
|
package org.springblade.common.utils;
|
||||||
|
|
||||||
|
import org.springblade.core.tool.utils.StringUtil;
|
||||||
|
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录密码强度规则工具
|
||||||
|
* 规则:大于8位,同时包含字母、数字、特殊字符(.!@#$%^&*)
|
||||||
|
*
|
||||||
|
* @author BladeX
|
||||||
|
*/
|
||||||
|
public final class PasswordRuleUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 允许的特殊字符集合
|
||||||
|
*/
|
||||||
|
public static final String SPECIAL_CHARS = ".!@#$%^&*";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最小长度(大于 8 位 => 至少 9 位)
|
||||||
|
*/
|
||||||
|
public static final int MIN_LENGTH = 9;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动生成密码长度
|
||||||
|
*/
|
||||||
|
public static final int GENERATE_LENGTH = 10;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 规则提示文案
|
||||||
|
*/
|
||||||
|
public static final String RULE_MESSAGE = "密码须大于8位,且同时包含字母、数字和特殊字符(.!@#$%^&*)";
|
||||||
|
|
||||||
|
private static final String LETTERS = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
||||||
|
private static final String DIGITS = "23456789";
|
||||||
|
private static final String ALL_CHARS = LETTERS + DIGITS + SPECIAL_CHARS;
|
||||||
|
private static final Pattern PASSWORD_PATTERN = Pattern.compile(
|
||||||
|
"^(?=.*[A-Za-z])(?=.*\\d)(?=.*[.!@#$%^&*]).{" + MIN_LENGTH + ",}$"
|
||||||
|
);
|
||||||
|
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
|
||||||
|
|
||||||
|
private PasswordRuleUtil() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验密码是否符合强度规则
|
||||||
|
*
|
||||||
|
* @param password 明文密码
|
||||||
|
* @return true-符合
|
||||||
|
*/
|
||||||
|
public static boolean isValid(String password) {
|
||||||
|
return StringUtil.isNotBlank(password) && PASSWORD_PATTERN.matcher(password).matches();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成符合规则的随机密码
|
||||||
|
*
|
||||||
|
* @return 随机密码
|
||||||
|
*/
|
||||||
|
public static String generate() {
|
||||||
|
List<Character> chars = new ArrayList<>(GENERATE_LENGTH);
|
||||||
|
chars.add(LETTERS.charAt(SECURE_RANDOM.nextInt(LETTERS.length())));
|
||||||
|
chars.add(DIGITS.charAt(SECURE_RANDOM.nextInt(DIGITS.length())));
|
||||||
|
chars.add(SPECIAL_CHARS.charAt(SECURE_RANDOM.nextInt(SPECIAL_CHARS.length())));
|
||||||
|
for (int i = chars.size(); i < GENERATE_LENGTH; i++) {
|
||||||
|
chars.add(ALL_CHARS.charAt(SECURE_RANDOM.nextInt(ALL_CHARS.length())));
|
||||||
|
}
|
||||||
|
Collections.shuffle(chars, SECURE_RANDOM);
|
||||||
|
StringBuilder password = new StringBuilder(GENERATE_LENGTH);
|
||||||
|
for (Character ch : chars) {
|
||||||
|
password.append(ch);
|
||||||
|
}
|
||||||
|
return password.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
org.springblade.common.config.BladeCommonConfiguration
|
||||||
@@ -48,13 +48,12 @@ public class AuthProvider {
|
|||||||
DEFAULT_SKIP_URL.add("/oauth/sms/**");
|
DEFAULT_SKIP_URL.add("/oauth/sms/**");
|
||||||
DEFAULT_SKIP_URL.add("/oauth/clear-cache/**");
|
DEFAULT_SKIP_URL.add("/oauth/clear-cache/**");
|
||||||
DEFAULT_SKIP_URL.add("/oauth/user-info");
|
DEFAULT_SKIP_URL.add("/oauth/user-info");
|
||||||
|
DEFAULT_SKIP_URL.add("/oauth/logout/**");
|
||||||
DEFAULT_SKIP_URL.add("/oauth/render/**");
|
DEFAULT_SKIP_URL.add("/oauth/render/**");
|
||||||
DEFAULT_SKIP_URL.add("/oauth/callback/**");
|
DEFAULT_SKIP_URL.add("/oauth/callback/**");
|
||||||
DEFAULT_SKIP_URL.add("/oauth/revoke/**");
|
DEFAULT_SKIP_URL.add("/oauth/revoke/**");
|
||||||
DEFAULT_SKIP_URL.add("/oauth/refresh/**");
|
DEFAULT_SKIP_URL.add("/oauth/refresh/**");
|
||||||
DEFAULT_SKIP_URL.add("/iam/sso/token/**");
|
DEFAULT_SKIP_URL.add("/oauth/mk/**");
|
||||||
DEFAULT_SKIP_URL.add("/blade-auth/iam/sso/token/**");
|
|
||||||
DEFAULT_SKIP_URL.add("/api/blade-auth/iam/sso/token/**");
|
|
||||||
DEFAULT_SKIP_URL.add("/token/**");
|
DEFAULT_SKIP_URL.add("/token/**");
|
||||||
DEFAULT_SKIP_URL.add("/actuator/**");
|
DEFAULT_SKIP_URL.add("/actuator/**");
|
||||||
DEFAULT_SKIP_URL.add("/v3/api-docs/**");
|
DEFAULT_SKIP_URL.add("/v3/api-docs/**");
|
||||||
@@ -63,6 +62,11 @@ public class AuthProvider {
|
|||||||
DEFAULT_SKIP_URL.add("/process/diagram-view");
|
DEFAULT_SKIP_URL.add("/process/diagram-view");
|
||||||
DEFAULT_SKIP_URL.add("/manager/check-upload");
|
DEFAULT_SKIP_URL.add("/manager/check-upload");
|
||||||
DEFAULT_SKIP_URL.add("/assets/**");
|
DEFAULT_SKIP_URL.add("/assets/**");
|
||||||
|
DEFAULT_SKIP_URL.add("/iam/sso/token/**");
|
||||||
|
DEFAULT_SKIP_URL.add("/blade-transport/customer-archive/public/**");
|
||||||
|
DEFAULT_SKIP_URL.add("/customer-archive/public/**");
|
||||||
|
DEFAULT_SKIP_URL.add("/blade-openapi/openApi/mk/process/commonCallback");
|
||||||
|
DEFAULT_SKIP_URL.add("/openApi/mk/process/commonCallback");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -83,11 +83,11 @@
|
|||||||
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
|
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
|
||||||
</dependency>-->
|
</dependency>-->
|
||||||
<!--Taobao-Sdk-->
|
<!--Taobao-Sdk-->
|
||||||
<dependency>
|
<!-- <dependency>
|
||||||
<groupId>com.taobao</groupId>
|
<groupId>com.taobao</groupId>
|
||||||
<artifactId>taobao-sdk</artifactId>
|
<artifactId>taobao-sdk</artifactId>
|
||||||
<version>20201116</version>
|
<version>20201116</version>
|
||||||
</dependency>
|
</dependency>-->
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springblade</groupId>
|
||||||
|
<artifactId>blade-service-api</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>blade-file-api</artifactId>
|
||||||
|
<name>${project.artifactId}</name>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springblade</groupId>
|
||||||
|
<artifactId>blade-ocr-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
package org.springblade.file.callback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解压回调接口
|
||||||
|
* @author bfhuange
|
||||||
|
* @date 2024/11/8
|
||||||
|
*/
|
||||||
|
public interface UnzipCallback {
|
||||||
|
/**
|
||||||
|
* 解压任务的业务编码
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
String getBizCode();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 回调接口
|
||||||
|
* @param bizId 业务id
|
||||||
|
* @param event 事件 org.springblade.file.callback.UnzipEventType name
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
boolean callback(Long bizId, String event);
|
||||||
|
}
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
package org.springblade.file.callback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解压事件类型
|
||||||
|
* @author bfhuange
|
||||||
|
* @date 2024/11/15
|
||||||
|
*/
|
||||||
|
public enum UnzipEventType {
|
||||||
|
/**
|
||||||
|
* 开始解压
|
||||||
|
*/
|
||||||
|
START,
|
||||||
|
/**
|
||||||
|
* 解压成功
|
||||||
|
*/
|
||||||
|
SUCCESS,
|
||||||
|
/**
|
||||||
|
* 解压失败
|
||||||
|
*/
|
||||||
|
FAIL,
|
||||||
|
;
|
||||||
|
}
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
package org.springblade.file.config;
|
||||||
|
|
||||||
|
import feign.codec.Encoder;
|
||||||
|
import feign.form.spring.SpringFormEncoder;
|
||||||
|
import org.springframework.beans.factory.ObjectFactory;
|
||||||
|
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
|
||||||
|
import org.springframework.cloud.openfeign.support.SpringEncoder;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author bfhuange
|
||||||
|
* @since 2025/4/9
|
||||||
|
*/
|
||||||
|
public class FeignFileUploadClientConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public Encoder feignFormEncoder(ObjectFactory<HttpMessageConverters> messageConverters) {
|
||||||
|
return new SpringFormEncoder(new SpringEncoder(messageConverters));
|
||||||
|
}
|
||||||
|
}
|
||||||
+188
@@ -0,0 +1,188 @@
|
|||||||
|
package org.springblade.file.feign;
|
||||||
|
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import jakarta.validation.constraints.NotEmpty;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import org.springblade.core.tool.api.R;
|
||||||
|
import org.springblade.file.pojo.dto.*;
|
||||||
|
import org.springblade.file.pojo.entity.Attachment;
|
||||||
|
import org.springblade.file.pojo.vo.*;
|
||||||
|
import org.springblade.thirdparty.ocr.pojo.vo.TransportCertificateVO;
|
||||||
|
import org.springframework.cloud.openfeign.FeignClient;
|
||||||
|
import org.springframework.cloud.openfeign.SpringQueryMap;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author bfhuange
|
||||||
|
* @date 2024/10/21
|
||||||
|
*/
|
||||||
|
@FeignClient(
|
||||||
|
value = "blade-file",
|
||||||
|
fallback = IFileClientFallback.class
|
||||||
|
)
|
||||||
|
public interface IFileClient {
|
||||||
|
|
||||||
|
String API_PREFIX = "/feign/client/file";
|
||||||
|
String ATTACHMENT = API_PREFIX + "/attachment";
|
||||||
|
String ATTACHMENT_DETAIL = API_PREFIX + "/attachment-detail";
|
||||||
|
String BUSINESS_ATTACHMENT_SAVE_REMOVE = API_PREFIX + "/businessAttachment-save-remove";
|
||||||
|
String BUSINESS_ATTACHMENT_BATCH_SAVE_REMOVE = API_PREFIX + "/businessAttachment-batch-save-remove";
|
||||||
|
String BUSINESS_ATTACHMENT_BATCH_SAVE_REMOVE_BY_PARAM = API_PREFIX + "/businessAttachment-batch-save-remove-by-param";
|
||||||
|
String BUSINESS_ATTACHMENT_BATCH_SAVE_REMOVE_NO_BIZ_ID = API_PREFIX + "/businessAttachment-batch-save-remove-no-bizId";
|
||||||
|
String BUSINESS_ATTACHMENT_REMOVE = API_PREFIX + "/businessAttachment-remove";
|
||||||
|
String BUSINESS_ATTACHMENT_LIST = API_PREFIX + "/businessAttachment-list";
|
||||||
|
String BUSINESS_UNZIP_TASK_SAVE = API_PREFIX + "/businessUnzipTask-save";
|
||||||
|
String BUSINESS_UNZIP_TASK_UPDATE = API_PREFIX + "/businessUnzipTask-update";
|
||||||
|
String BUSINESS_UNZIP_TASK_CANCEL = API_PREFIX + "/businessUnzipTask-cancel";
|
||||||
|
String BUSINESS_UNZIP_TASK_REMOVE = API_PREFIX + "/businessUnzipTask-remove";
|
||||||
|
String BUSINESS_UNZIP_TASK_LIST = API_PREFIX + "/businessUnzipTask-list";
|
||||||
|
String BUSINESS_UNZIP_DETAIL_LIST = API_PREFIX + "/businessUnzipDetail-list";
|
||||||
|
String RECOGNITION_TRANSPORT_CERTIFICATE = API_PREFIX + "/recognitionTransportCertificate";
|
||||||
|
String FILE_URLS = API_PREFIX + "/getFileUrls";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取附件,多个附件id用逗号分割
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
* @return List<Attachment>
|
||||||
|
*/
|
||||||
|
@GetMapping(ATTACHMENT)
|
||||||
|
R<List<Attachment>> getAttachment(@RequestParam("id") String id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取附件详情
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
* @return List<Attachment>
|
||||||
|
*/
|
||||||
|
@GetMapping(ATTACHMENT_DETAIL)
|
||||||
|
R<AttachmentDetailVO> getAttachmentDetail(@RequestParam("id") Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存并删除业务附件,remove为true,先用bizId删除,再新增
|
||||||
|
*
|
||||||
|
* @param param
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping(BUSINESS_ATTACHMENT_SAVE_REMOVE)
|
||||||
|
R saveAndRemoveBusinessAttachment(@RequestBody @Validated BusinessAttachmentBatchAddDTO param);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量保存并删除业务附件,先删除附件,再新增,业务id为空,从params中获取bizId,先删除
|
||||||
|
*
|
||||||
|
* @param params 参数为空不新增
|
||||||
|
* @param bizId 业务id
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping(BUSINESS_ATTACHMENT_BATCH_SAVE_REMOVE)
|
||||||
|
R batchSaveAndRemoveBusinessAttachment(@RequestBody List<@Valid BusinessAttachmentAddDTO> params, @RequestParam(value = "bizId", required = false) Long bizId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量保存并删除业务附件,deleteParam为空,从params获取bizId删除,不为空,使用bizIds和bizCodes删除附件,bizCodes为空删除所有bizIds的附件
|
||||||
|
* deleteParam 中 delete 为false,不删除
|
||||||
|
*
|
||||||
|
* @param params 参数为空不新增
|
||||||
|
* @param deleteParam 删除参数,支持只删除业务编码参数的附件
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping(BUSINESS_ATTACHMENT_BATCH_SAVE_REMOVE_BY_PARAM)
|
||||||
|
R batchSaveAndRemoveByParamBusinessAttachment(@RequestBody List<@Valid BusinessAttachmentAddDTO> params, @Valid @SpringQueryMap BusinessAttachmentDeleteDTO deleteParam);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量保存并删除业务附件,从参数中提取所有bizId
|
||||||
|
*
|
||||||
|
* @param params 参数
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping(BUSINESS_ATTACHMENT_BATCH_SAVE_REMOVE_NO_BIZ_ID)
|
||||||
|
R batchSaveAndRemoveBusinessAttachment(@RequestBody List<@Valid BusinessAttachmentAddDTO> params);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据业务id删除业务附件
|
||||||
|
*
|
||||||
|
* @param bizId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping(BUSINESS_ATTACHMENT_REMOVE)
|
||||||
|
R removeBusinessAttachment(@NotNull(message = "业务id不能为空") @RequestParam(value = "bizId", required = false) Long bizId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询业务附件列表
|
||||||
|
*
|
||||||
|
* @param param
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping(BUSINESS_ATTACHMENT_LIST)
|
||||||
|
R<List<BusinessAttachmentVO>> getBusinessAttachments(@Validated @RequestBody BusinessAttachmentQueryDTO param);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存业务解压任务
|
||||||
|
* @param param
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping(BUSINESS_UNZIP_TASK_SAVE)
|
||||||
|
R saveBusinessUnzipTask(@Validated @RequestBody BusinessUnzipTaskAddDTO param);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新业务解压任务(重新上传)
|
||||||
|
* @param param
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping(BUSINESS_UNZIP_TASK_UPDATE)
|
||||||
|
R updateBusinessUnzipTask(@Validated @RequestBody BusinessUnzipTaskUpdateDTO param);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 取消业务解压任务
|
||||||
|
* @param bizId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping(BUSINESS_UNZIP_TASK_CANCEL)
|
||||||
|
R cancelBusinessUnzipTask(@NotNull(message = "业务id不能为空") @RequestParam("bizId") Long bizId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除业务解压任务
|
||||||
|
* @param bizId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping(BUSINESS_UNZIP_TASK_REMOVE)
|
||||||
|
R removeBusinessUnzipTask(@NotNull(message = "业务id不能为空") @RequestParam("bizId") Long bizId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据业务id列表查询业务解压任务列表
|
||||||
|
* @param bizIds
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping(BUSINESS_UNZIP_TASK_LIST)
|
||||||
|
R<List<BusinessUnzipTaskListVO>> getBusinessUnzipTasksByBizIds(@Validated @NotEmpty(message = "业务id列表不能为空") @RequestBody List<Long> bizIds);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询解压明细列表
|
||||||
|
* @param bizId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@GetMapping(BUSINESS_UNZIP_DETAIL_LIST)
|
||||||
|
R<List<BusinessUnzipDetailListVO>> queryBusinessUnzipDetail(@NotNull(message = "业务id不能为空") @RequestParam("bizId") Long bizId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 识别车辆运输凭证
|
||||||
|
*
|
||||||
|
* @param param 同一车牌文件夹下的附件objectKey列表
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping(RECOGNITION_TRANSPORT_CERTIFICATE)
|
||||||
|
R<TransportCertificateVO> recognitionTransportCertificates(@Validated @RequestBody FileCertificateBatchRecognitionDTO param);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量获取附件下载地址
|
||||||
|
* @param param
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping(FILE_URLS)
|
||||||
|
R<List<FileUrlVO>> getFileUrls(@Validated @RequestBody IdsDTO param);
|
||||||
|
}
|
||||||
+113
@@ -0,0 +1,113 @@
|
|||||||
|
package org.springblade.file.feign;
|
||||||
|
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import org.springblade.core.tool.api.R;
|
||||||
|
import org.springblade.file.pojo.dto.*;
|
||||||
|
import org.springblade.file.pojo.entity.Attachment;
|
||||||
|
import org.springblade.file.pojo.vo.*;
|
||||||
|
import org.springblade.thirdparty.ocr.pojo.vo.TransportCertificateVO;
|
||||||
|
import org.springframework.cloud.openfeign.SpringQueryMap;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author bfhuange
|
||||||
|
* @date 2024/10/21
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class IFileClientFallback implements IFileClient, IFileUploadClient {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public R<List<Attachment>> getAttachment(String id) {
|
||||||
|
return R.fail("获取数据失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public R<AttachmentDetailVO> getAttachmentDetail(Long id) {
|
||||||
|
return R.fail("获取数据失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public R saveAndRemoveBusinessAttachment(BusinessAttachmentBatchAddDTO param) {
|
||||||
|
return R.fail("获取数据失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public R batchSaveAndRemoveBusinessAttachment(List<BusinessAttachmentAddDTO> params, Long bizId) {
|
||||||
|
return R.fail("获取数据失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public R batchSaveAndRemoveByParamBusinessAttachment(List<@Valid BusinessAttachmentAddDTO> params, @Valid @SpringQueryMap BusinessAttachmentDeleteDTO deleteParam) {
|
||||||
|
return R.fail("获取数据失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public R batchSaveAndRemoveBusinessAttachment(List<@Valid BusinessAttachmentAddDTO> params) {
|
||||||
|
return R.fail("获取数据失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public R removeBusinessAttachment(Long bizId) {
|
||||||
|
return R.fail("获取数据失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public R<List<BusinessAttachmentVO>> getBusinessAttachments(BusinessAttachmentQueryDTO param) {
|
||||||
|
return R.fail("获取数据失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public R saveBusinessUnzipTask(BusinessUnzipTaskAddDTO param) {
|
||||||
|
return R.fail("获取数据失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public R updateBusinessUnzipTask(BusinessUnzipTaskUpdateDTO param) {
|
||||||
|
return R.fail("获取数据失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public R cancelBusinessUnzipTask(Long bizId) {
|
||||||
|
return R.fail("获取数据失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public R removeBusinessUnzipTask(Long bizId) {
|
||||||
|
return R.fail("获取数据失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public R<List<BusinessUnzipTaskListVO>> getBusinessUnzipTasksByBizIds(List<Long> bizIds) {
|
||||||
|
return R.fail("获取数据失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public R<List<BusinessUnzipDetailListVO>> queryBusinessUnzipDetail(Long bizId) {
|
||||||
|
return R.fail("获取数据失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public R<TransportCertificateVO> recognitionTransportCertificates(@Validated @RequestBody FileCertificateBatchRecognitionDTO param) {
|
||||||
|
return R.fail("获取数据失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public R<List<FileUrlVO>> getFileUrls(IdsDTO param) {
|
||||||
|
return R.fail("获取数据失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public R<List<Attachment>> ossUpload(MultipartFile[] files) {
|
||||||
|
return R.fail("获取数据失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public R<TempFileUrlVO> ossUploadTemp(MultipartFile file, Integer expires) {
|
||||||
|
return R.fail("获取数据失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
package org.springblade.file.feign;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import org.springblade.core.tool.api.R;
|
||||||
|
import org.springblade.file.config.FeignFileUploadClientConfig;
|
||||||
|
import org.springblade.file.pojo.entity.Attachment;
|
||||||
|
import org.springblade.file.pojo.vo.TempFileUrlVO;
|
||||||
|
import org.springframework.cloud.openfeign.FeignClient;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RequestPart;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author bfhuange
|
||||||
|
* @date 2024/10/21
|
||||||
|
*/
|
||||||
|
@FeignClient(
|
||||||
|
value = "blade-file",
|
||||||
|
fallback = IFileClientFallback.class,
|
||||||
|
configuration = FeignFileUploadClientConfig.class
|
||||||
|
)
|
||||||
|
public interface IFileUploadClient {
|
||||||
|
|
||||||
|
String API_PREFIX = "/feign/client/file";
|
||||||
|
String UPLOAD = API_PREFIX + "/ossUpload";
|
||||||
|
String UPLOAD_TEMP = API_PREFIX + "/ossUploadTemp";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件上传
|
||||||
|
* @param files
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping(value = UPLOAD, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||||
|
R<List<Attachment>> ossUpload(@RequestPart("files") MultipartFile[] files);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传临时文件,不保存到附件表,过期后自动删除
|
||||||
|
* @param file
|
||||||
|
* @param expires 过期时间,单位天,过期后自动删除
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping(value = UPLOAD_TEMP, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||||
|
R<TempFileUrlVO> ossUploadTemp(@RequestPart("file") MultipartFile file, @NotNull(message = "过期时间不能为空") @RequestParam(name = "expires", required = false) Integer expires);
|
||||||
|
}
|
||||||
+53
@@ -0,0 +1,53 @@
|
|||||||
|
package org.springblade.file.pojo.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业务附件信息表 数据传输对象实体类
|
||||||
|
*
|
||||||
|
* @author BladeX
|
||||||
|
* @since 2024-09-04
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "业务附件新增参数")
|
||||||
|
@Builder
|
||||||
|
public class BusinessAttachmentAddDTO implements Serializable {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业务id
|
||||||
|
*/
|
||||||
|
@NotNull(message = "业务id不能为空")
|
||||||
|
@Schema(description = "业务id")
|
||||||
|
private Long bizId;
|
||||||
|
/**
|
||||||
|
* 业务code
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "业务code不能为空")
|
||||||
|
@Schema(description = "业务code")
|
||||||
|
private String bizCode;
|
||||||
|
/**
|
||||||
|
* 附件id
|
||||||
|
*/
|
||||||
|
@NotNull(message = "附件id不能为空")
|
||||||
|
@Schema(description = "附件id")
|
||||||
|
private Long attachmentId;
|
||||||
|
/**
|
||||||
|
* 附件描述
|
||||||
|
*/
|
||||||
|
@Schema(description = "附件描述")
|
||||||
|
private String desc;
|
||||||
|
/**
|
||||||
|
* 排序
|
||||||
|
*/
|
||||||
|
@Schema(description = "排序")
|
||||||
|
private Integer sort;
|
||||||
|
}
|
||||||
+73
@@ -0,0 +1,73 @@
|
|||||||
|
package org.springblade.file.pojo.dto;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业务附件信息表 数据传输对象实体类
|
||||||
|
*
|
||||||
|
* @author BladeX
|
||||||
|
* @since 2024-09-04
|
||||||
|
*/
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Data
|
||||||
|
@Schema(description = "业务附件批量新增参数")
|
||||||
|
public class BusinessAttachmentBatchAddDTO implements Serializable {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业务id
|
||||||
|
*/
|
||||||
|
@NotNull(message = "业务id不能为空")
|
||||||
|
@Schema(description = "业务id")
|
||||||
|
private Long bizId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 明细列表
|
||||||
|
*/
|
||||||
|
@Schema(description = "明细列表")
|
||||||
|
private List<@Valid BusinessAttachmentBatchAddDetailDTO> details;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否先删除,默认 是
|
||||||
|
*/
|
||||||
|
@Schema(description = "是否先删除,默认 是")
|
||||||
|
private boolean remove = true;
|
||||||
|
|
||||||
|
public BusinessAttachmentBatchAddDTO(Long bizId) {
|
||||||
|
this.bizId = bizId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BusinessAttachmentBatchAddDTO(Long bizId, String bizCode, Long attachmentId) {
|
||||||
|
this.bizId = bizId;
|
||||||
|
this.details = Lists.newArrayList(new BusinessAttachmentBatchAddDetailDTO(bizCode, attachmentId));
|
||||||
|
}
|
||||||
|
|
||||||
|
public BusinessAttachmentBatchAddDTO addDetail(BusinessAttachmentBatchAddDetailDTO detail) {
|
||||||
|
if (this.details == null) {
|
||||||
|
this.details = new ArrayList<>();
|
||||||
|
}
|
||||||
|
this.details.add(detail);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BusinessAttachmentBatchAddDTO addDetail(String bizCode, Long attachmentId) {
|
||||||
|
return this.addDetail(new BusinessAttachmentBatchAddDetailDTO(bizCode, attachmentId));
|
||||||
|
}
|
||||||
|
|
||||||
|
public BusinessAttachmentBatchAddDTO addDetail(String bizCode, List<Long> attachmentIds) {
|
||||||
|
return this.addDetail(new BusinessAttachmentBatchAddDetailDTO(bizCode, attachmentIds));
|
||||||
|
}
|
||||||
|
}
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
package org.springblade.file.pojo.dto;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotEmpty;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业务附件信息表 数据传输对象实体类
|
||||||
|
*
|
||||||
|
* @author BladeX
|
||||||
|
* @since 2024-09-04
|
||||||
|
*/
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Data
|
||||||
|
@Schema(description = "业务附件批量新增明细参数")
|
||||||
|
@Builder
|
||||||
|
public class BusinessAttachmentBatchAddDetailDTO implements Serializable {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
/**
|
||||||
|
* 业务code
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "业务编码不能为空")
|
||||||
|
@Schema(description = "业务code")
|
||||||
|
private String bizCode;
|
||||||
|
/**
|
||||||
|
* 附件id列表
|
||||||
|
*/
|
||||||
|
@NotEmpty(message = "附件id列表不能为空")
|
||||||
|
@Schema(description = "附件id列表")
|
||||||
|
private List<@NotNull(message = "附件id不能为空") Long> attachmentIds;
|
||||||
|
|
||||||
|
public BusinessAttachmentBatchAddDetailDTO(String bizCode, Long attachmentId) {
|
||||||
|
this.bizCode = bizCode;
|
||||||
|
this.attachmentIds = Lists.newArrayList(attachmentId);
|
||||||
|
}
|
||||||
|
}
|
||||||
+54
@@ -0,0 +1,54 @@
|
|||||||
|
package org.springblade.file.pojo.dto;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.springblade.core.tool.utils.StringUtil;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业务附件删除参数
|
||||||
|
* @author bfhuange
|
||||||
|
* @since 2024/12/3
|
||||||
|
*/
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Data
|
||||||
|
@Schema(description = "业务附件删除参数")
|
||||||
|
public class BusinessAttachmentDeleteDTO implements Serializable {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
/**
|
||||||
|
* 业务id列表
|
||||||
|
*/
|
||||||
|
@Schema(description = "业务id列表")
|
||||||
|
private List<Long> bizIds;
|
||||||
|
/**
|
||||||
|
* 业务code列表
|
||||||
|
*/
|
||||||
|
@Schema(description = "业务code列表")
|
||||||
|
private List<String> bizCodes;
|
||||||
|
/**
|
||||||
|
* 是否删除
|
||||||
|
*/
|
||||||
|
@Schema(description = "是否删除")
|
||||||
|
private boolean delete = true;
|
||||||
|
|
||||||
|
public BusinessAttachmentDeleteDTO(Long bizId) {
|
||||||
|
this.bizIds = Lists.newArrayList(bizId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BusinessAttachmentDeleteDTO(Long bizId, String bizCode) {
|
||||||
|
this.bizIds = Lists.newArrayList(bizId);
|
||||||
|
if (StringUtil.isNotBlank(bizCode)) {
|
||||||
|
this.bizCodes = Lists.newArrayList(bizCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+62
@@ -0,0 +1,62 @@
|
|||||||
|
package org.springblade.file.pojo.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业务附件信息表 数据传输对象实体类
|
||||||
|
*
|
||||||
|
* @author BladeX
|
||||||
|
* @since 2024-09-04
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Data
|
||||||
|
@Schema(description = "业务附件查询参数")
|
||||||
|
public class BusinessAttachmentQueryDTO implements Serializable {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
/**
|
||||||
|
* 业务id列表
|
||||||
|
*/
|
||||||
|
@NotNull(message = "业务id列表不能为空")
|
||||||
|
@Schema(description = "业务id列表")
|
||||||
|
private List<@NotNull(message = "业务id不能为空") Long> bizIds;
|
||||||
|
/**
|
||||||
|
* 业务code
|
||||||
|
*/
|
||||||
|
@Schema(description = "业务code")
|
||||||
|
private String bizCode;
|
||||||
|
/**
|
||||||
|
* 业务code列表
|
||||||
|
*/
|
||||||
|
@Schema(description = "业务code列表")
|
||||||
|
private List<String> bizCodes;
|
||||||
|
|
||||||
|
public BusinessAttachmentQueryDTO(Long bizId) {
|
||||||
|
this.bizIds = List.of(bizId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BusinessAttachmentQueryDTO(List<Long> bizIds) {
|
||||||
|
this.bizIds = bizIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 兼容历史参数
|
||||||
|
* @param bizId
|
||||||
|
*/
|
||||||
|
public void setBizId(Long bizId) {
|
||||||
|
if (this.bizIds == null) {
|
||||||
|
this.bizIds = new ArrayList<>();
|
||||||
|
}
|
||||||
|
this.bizIds.add(bizId);
|
||||||
|
}
|
||||||
|
}
|
||||||
+55
@@ -0,0 +1,55 @@
|
|||||||
|
package org.springblade.file.pojo.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springblade.file.pojo.enums.UnzipTask;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业务解压任务新增参数
|
||||||
|
* @author bfhuange
|
||||||
|
* @date 2024/10/28
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "业务解压任务新增参数")
|
||||||
|
public class BusinessUnzipTaskAddDTO implements Serializable {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
/**
|
||||||
|
* 业务id
|
||||||
|
*/
|
||||||
|
@NotNull(message = "业务id不能为空")
|
||||||
|
@Schema(description = "业务id")
|
||||||
|
private Long bizId;
|
||||||
|
/**
|
||||||
|
* 部门名称ID
|
||||||
|
*/
|
||||||
|
@Schema(description = "部门名称ID")
|
||||||
|
private Long departmentId;
|
||||||
|
/**
|
||||||
|
* 业务code
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "业务code不能为空")
|
||||||
|
@Schema(description = "业务code")
|
||||||
|
private String bizCode = UnzipTask.BizCode.WAYBILL.getCode();
|
||||||
|
/**
|
||||||
|
* 文档编号
|
||||||
|
*/
|
||||||
|
@Schema(description = "文档编号")
|
||||||
|
private String docCode;
|
||||||
|
/**
|
||||||
|
* 关联类型,file_task 文件任务,attachment 附件
|
||||||
|
*/
|
||||||
|
@Schema(description = "关联类型,file_task 文件任务,attachment 附件")
|
||||||
|
private String relateType = UnzipTask.RelateType.FILE_TASK.getCode();
|
||||||
|
/**
|
||||||
|
* 关联id,文件任务id或附件id
|
||||||
|
*/
|
||||||
|
@NotNull(message = "关联id不能为空")
|
||||||
|
@Schema(description = "关联id,文件任务id或附件id")
|
||||||
|
private Long relateId;
|
||||||
|
}
|
||||||
+55
@@ -0,0 +1,55 @@
|
|||||||
|
package org.springblade.file.pojo.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业务解压任务查询参数
|
||||||
|
* @author bfhuange
|
||||||
|
* @date 2024/10/28
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "业务解压任务查询参数")
|
||||||
|
public class BusinessUnzipTaskQueryDTO implements Serializable {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
/**
|
||||||
|
* 文档编号(凭证批次号)
|
||||||
|
*/
|
||||||
|
@Schema(description = "文档编号(凭证批次号)")
|
||||||
|
private String docCode;
|
||||||
|
/**
|
||||||
|
* 附件名称
|
||||||
|
*/
|
||||||
|
@Schema(description = "附件名称")
|
||||||
|
private String attachmentName;
|
||||||
|
/**
|
||||||
|
* 状态 uploading:正在上传 completed:上传完成 failed:上传失败 paused:已暂停
|
||||||
|
*/
|
||||||
|
@Schema(description = "状态 uploading:正在上传 completed:上传完成 failed:上传失败 paused:已暂停")
|
||||||
|
private String status;
|
||||||
|
/**
|
||||||
|
* 关联类型,file_task 文件任务,attachment 附件
|
||||||
|
*/
|
||||||
|
@Schema(description = "关联类型,file_task 文件任务,attachment 附件", hidden = true)
|
||||||
|
private String relateType;
|
||||||
|
/**
|
||||||
|
* 是否只查询当前用户
|
||||||
|
*/
|
||||||
|
@Schema(description = "是否只查询当前用户")
|
||||||
|
private boolean currentUser;
|
||||||
|
/**
|
||||||
|
* 当前登录人id
|
||||||
|
*/
|
||||||
|
@Schema(hidden = true)
|
||||||
|
private Long currentUserId;
|
||||||
|
/**
|
||||||
|
* 业务id列表
|
||||||
|
*/
|
||||||
|
@Schema(description = "业务id列表", hidden = true)
|
||||||
|
private List<Long> bizIds;
|
||||||
|
}
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
package org.springblade.file.pojo.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springblade.file.pojo.enums.UnzipTask;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业务解压任务更新参数
|
||||||
|
* @author bfhuange
|
||||||
|
* @date 2024/10/28
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "业务解压任务更新参数")
|
||||||
|
public class BusinessUnzipTaskUpdateDTO implements Serializable {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
/**
|
||||||
|
* 主键
|
||||||
|
*/
|
||||||
|
@Schema(description = "主键")
|
||||||
|
private Long id;
|
||||||
|
/**
|
||||||
|
* 业务id
|
||||||
|
*/
|
||||||
|
@Schema(description = "业务id")
|
||||||
|
private Long bizId;
|
||||||
|
/**
|
||||||
|
* 关联id,文件任务id或附件id
|
||||||
|
*/
|
||||||
|
@NotNull(message = "关联id不能为空")
|
||||||
|
@Schema(description = "关联id,文件任务id或附件id")
|
||||||
|
private Long relateId;
|
||||||
|
/**
|
||||||
|
* 关联类型,file_task 文件任务,attachment 附件
|
||||||
|
*/
|
||||||
|
@Schema(description = "关联类型,file_task 文件任务,attachment 附件", hidden = true)
|
||||||
|
private String relateType = UnzipTask.RelateType.FILE_TASK.getCode();
|
||||||
|
}
|
||||||
+44
@@ -0,0 +1,44 @@
|
|||||||
|
package org.springblade.file.pojo.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 凭证识别参数,依次调用单张识别
|
||||||
|
* @author bfhuange
|
||||||
|
* @since 2024/12/13
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "凭证附件参数")
|
||||||
|
public class CertificateAttachmentDTO implements Serializable {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
/**
|
||||||
|
* 磅单ObjectKey
|
||||||
|
*/
|
||||||
|
@Schema(description = "磅单ObjectKey")
|
||||||
|
private String poundObjectKey;
|
||||||
|
/**
|
||||||
|
* 车头ObjectKey
|
||||||
|
*/
|
||||||
|
@Schema(description = "车头ObjectKey")
|
||||||
|
private String carFrontObjectKey;
|
||||||
|
/**
|
||||||
|
* 驾驶证ObjectKey
|
||||||
|
*/
|
||||||
|
@Schema(description = "驾驶证ObjectKey")
|
||||||
|
private String driverLicenseObjectKey;
|
||||||
|
/**
|
||||||
|
* 行驶证ObjectKey
|
||||||
|
*/
|
||||||
|
@Schema(description = "行驶证ObjectKey")
|
||||||
|
private String vehicleLicenseObjectKey;
|
||||||
|
/**
|
||||||
|
* 项目简称
|
||||||
|
*/
|
||||||
|
@Schema(description = "项目简称")
|
||||||
|
private String projectAbbreviation;
|
||||||
|
}
|
||||||
+69
@@ -0,0 +1,69 @@
|
|||||||
|
package org.springblade.file.pojo.dto;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotEmpty;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 凭证批量识别参数,所有图像链接统一识别,不分类
|
||||||
|
* @author bfhuange
|
||||||
|
* @since 2025/3/13
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Data
|
||||||
|
public class FileCertificateBatchRecognitionDTO implements Serializable {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
/**
|
||||||
|
* 所有图像完整 URL 列表
|
||||||
|
*/
|
||||||
|
@NotEmpty(message = "图像 URL 列表不能为空")
|
||||||
|
private List<String> objectKeys;
|
||||||
|
/**
|
||||||
|
* 项目简称
|
||||||
|
*/
|
||||||
|
private String projectAbbreviation;
|
||||||
|
/**
|
||||||
|
* 凭证编码
|
||||||
|
*/
|
||||||
|
private String code;
|
||||||
|
/**
|
||||||
|
* 目录名称
|
||||||
|
*/
|
||||||
|
private String dirName;
|
||||||
|
/**
|
||||||
|
* 磅单完整 URL 列表
|
||||||
|
*/
|
||||||
|
private List<String> poundUrls;
|
||||||
|
/**
|
||||||
|
* 车头完整 URL 列表
|
||||||
|
*/
|
||||||
|
private List<String> carFrontUrls;
|
||||||
|
/**
|
||||||
|
* 驾驶证完整 URL 列表
|
||||||
|
*/
|
||||||
|
private List<String> driverLicenseUrls;
|
||||||
|
/**
|
||||||
|
* 行驶证完整 URL 列表
|
||||||
|
*/
|
||||||
|
private List<String> vehicleLicenseUrls;
|
||||||
|
|
||||||
|
public FileCertificateBatchRecognitionDTO(List<String> objectKeys) {
|
||||||
|
this.objectKeys = objectKeys;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileCertificateBatchRecognitionDTO(
|
||||||
|
List<String> objectKeys, String projectAbbreviation, String code, String dirName) {
|
||||||
|
this.objectKeys = objectKeys;
|
||||||
|
this.projectAbbreviation = projectAbbreviation;
|
||||||
|
this.code = code;
|
||||||
|
this.dirName = dirName;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+68
@@ -0,0 +1,68 @@
|
|||||||
|
package org.springblade.file.pojo.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件任务创建对象
|
||||||
|
* @author bfhuange
|
||||||
|
* @date 2024/10/22
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "文件任务创建对象")
|
||||||
|
public class FileTaskCreateDTO implements Serializable {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Md5
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "Md5不能为空")
|
||||||
|
@Schema(description = "Md5")
|
||||||
|
private String md5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 附件名
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "附件名不能为空")
|
||||||
|
@Schema(description = "附件名")
|
||||||
|
private String attachmentName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 附件大小(单位:B)
|
||||||
|
*/
|
||||||
|
@NotNull(message = "附件大小不能为空")
|
||||||
|
@Schema(description = "附件大小(单位:B)")
|
||||||
|
private Long size;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分段大小
|
||||||
|
*/
|
||||||
|
@NotNull(message = "分段大小不能为空")
|
||||||
|
@Schema(description = "分段大小")
|
||||||
|
private Integer chunkSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分段数量
|
||||||
|
*/
|
||||||
|
@NotNull(message = "分段数量不能为空")
|
||||||
|
@Schema(description = "分段数量")
|
||||||
|
private Integer chunkTotal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否强制创建新任务,默认 false,如果存在相同md5的任务,会创建失败,true则新创建一个任务
|
||||||
|
*/
|
||||||
|
@Schema(description = "是否强制创建新任务,默认 false,如果存在相同md5的任务,会创建失败,true则新创建一个任务")
|
||||||
|
private boolean force;
|
||||||
|
|
||||||
|
@Schema(description = "业务类型")
|
||||||
|
private String businessType;
|
||||||
|
|
||||||
|
@Schema(description = "业务标识")
|
||||||
|
private String businessId;
|
||||||
|
}
|
||||||
+41
@@ -0,0 +1,41 @@
|
|||||||
|
package org.springblade.file.pojo.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springblade.file.pojo.vo.PartEtagVO;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件任务更新对象
|
||||||
|
* @author bfhuange
|
||||||
|
* @date 2024/10/22
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "文件任务更新对象")
|
||||||
|
public class FileTaskUpdateDTO implements Serializable {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件任务id
|
||||||
|
*/
|
||||||
|
@NotNull(message = "文件任务id不能为空")
|
||||||
|
@Schema(description = "主键id")
|
||||||
|
private Long id;
|
||||||
|
/**
|
||||||
|
* 段的ETag值。分段的Base64编码的128位MD5摘要,长度为32的字符串
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "etag不能为空")
|
||||||
|
@Schema(description = "etag")
|
||||||
|
private String etag;
|
||||||
|
/**
|
||||||
|
* 段号。分段号可以是不连续的。取值范围是[1,10000]的非负整数
|
||||||
|
*/
|
||||||
|
@NotNull(message = "段号不能为空")
|
||||||
|
@Schema(description = "段号")
|
||||||
|
private Integer partNumber;
|
||||||
|
}
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
package org.springblade.file.pojo.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Schema(description = "分段上传文件对象")
|
||||||
|
public class FileUploadDTO {
|
||||||
|
private MultipartFile multipartFile; // 文件对象
|
||||||
|
private String md5; // 文件md5值
|
||||||
|
private Long chunkTotal; // 分段总数
|
||||||
|
private Long chunkSize; // 分段大小
|
||||||
|
private Integer index; // 当前分段的位置
|
||||||
|
}
|
||||||
+33
@@ -0,0 +1,33 @@
|
|||||||
|
package org.springblade.file.pojo.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import jakarta.validation.constraints.NotEmpty;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* id批量参数
|
||||||
|
* @author bfhuange
|
||||||
|
* @date 2024/10/29
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Data
|
||||||
|
@Schema(description = "id批量参数")
|
||||||
|
public class IdsDTO implements Serializable {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
/**
|
||||||
|
* id列表
|
||||||
|
*/
|
||||||
|
@NotEmpty(message = "id列表不能为空")
|
||||||
|
@Schema(description = "id列表")
|
||||||
|
private @Valid List<@NotNull(message = "id不能为空") Long> ids;
|
||||||
|
}
|
||||||
+111
@@ -0,0 +1,111 @@
|
|||||||
|
package org.springblade.file.pojo.entity;
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
|
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springblade.core.tool.utils.DateUtil;
|
||||||
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author zhaowei
|
||||||
|
* @date 2024/8/16
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@TableName("blade_attachment")
|
||||||
|
@Schema(description = "附件对象")
|
||||||
|
public class Attachment implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
@Schema(description = "主键id")
|
||||||
|
@TableId(value = "id", type = IdType.ASSIGN_ID)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 附件名
|
||||||
|
*/
|
||||||
|
@Schema(description = "附件名")
|
||||||
|
private String attachmentName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 压缩文件所在压缩包相对路径
|
||||||
|
*/
|
||||||
|
@Schema(description = "压缩文件所在压缩包相对路径")
|
||||||
|
private String dir;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 附件大小(单位:B)
|
||||||
|
*/
|
||||||
|
@Schema(description = "附件大小(单位:B)")
|
||||||
|
private Long size;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* objectKey
|
||||||
|
*/
|
||||||
|
@Schema(description = "华为OBS objectKey")
|
||||||
|
private String objectKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建人-用户名称
|
||||||
|
*/
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
@Schema(description = "创建人-用户名称")
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private String createUserName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建人
|
||||||
|
*/
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
@Schema(description = "创建人")
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private Long createUser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@DateTimeFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@JsonFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@Schema(description = "创建时间")
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新人-用户名称
|
||||||
|
*/
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
@Schema(description = "创建人-用户名称")
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private String updateUserName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新人
|
||||||
|
*/
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
@Schema(description = "更新人")
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private Long updateUser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@DateTimeFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@JsonFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@Schema(description = "更新时间")
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private Date updateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态[0:未删除,1:删除]
|
||||||
|
*/
|
||||||
|
@TableLogic
|
||||||
|
@Schema(description = "是否已删除 [0:未删除,1:删除]")
|
||||||
|
private Integer isDeleted;
|
||||||
|
|
||||||
|
}
|
||||||
+113
@@ -0,0 +1,113 @@
|
|||||||
|
package org.springblade.file.pojo.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
|
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springblade.core.tool.utils.DateUtil;
|
||||||
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业务附件信息表 实体类
|
||||||
|
*
|
||||||
|
* @author BladeX
|
||||||
|
* @since 2024-09-04
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("blade_business_attachment")
|
||||||
|
@Schema(description = "BusinessAttachment对象")
|
||||||
|
public class BusinessAttachment implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业务id
|
||||||
|
*/
|
||||||
|
@Schema(description = "业务id")
|
||||||
|
private Long bizId;
|
||||||
|
/**
|
||||||
|
* 业务code
|
||||||
|
*/
|
||||||
|
@Schema(description = "业务code")
|
||||||
|
private String bizCode;
|
||||||
|
/**
|
||||||
|
* 附件id
|
||||||
|
*/
|
||||||
|
@Schema(description = "附件id")
|
||||||
|
private Long attachmentId;
|
||||||
|
/**
|
||||||
|
* 附件描述
|
||||||
|
*/
|
||||||
|
@Schema(description = "附件描述")
|
||||||
|
@TableField(value = "`desc`")
|
||||||
|
private String desc;
|
||||||
|
/**
|
||||||
|
* 排序
|
||||||
|
*/
|
||||||
|
@Schema(description = "排序")
|
||||||
|
private Integer sort;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 租户ID
|
||||||
|
*/
|
||||||
|
@Schema(description = "租户ID")
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private String tenantId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主键id
|
||||||
|
*/
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
@Schema(description = "主键id")
|
||||||
|
@TableId(value = "id", type = IdType.ASSIGN_ID)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建人
|
||||||
|
*/
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
@Schema(description = "创建人", hidden = true)
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private Long createUser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@DateTimeFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@JsonFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@Schema(description = "创建时间", hidden = true)
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新人
|
||||||
|
*/
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
@Schema(description = "更新人", hidden = true)
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private Long updateUser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@DateTimeFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@JsonFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@Schema(description = "更新时间", hidden = true)
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private Date updateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态[0:未删除,1:删除]
|
||||||
|
*/
|
||||||
|
@TableLogic
|
||||||
|
@Schema(description = "是否已删除", hidden = true)
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private Integer isDeleted;
|
||||||
|
}
|
||||||
+85
@@ -0,0 +1,85 @@
|
|||||||
|
package org.springblade.file.pojo.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
|
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springblade.core.tool.utils.DateUtil;
|
||||||
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* 业务解压明细表
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author bfhuange
|
||||||
|
* @since 2024-10-30
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("blade_business_unzip_detail")
|
||||||
|
@Schema(description = "BusinessUnzipDetail对象")
|
||||||
|
public class BusinessUnzipDetail implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
/**
|
||||||
|
* 主键
|
||||||
|
*/
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
@Schema(description = "主键")
|
||||||
|
@TableId(value = "id", type = IdType.ASSIGN_ID)
|
||||||
|
private Long id;
|
||||||
|
/**
|
||||||
|
* 业务解压任务id
|
||||||
|
*/
|
||||||
|
@Schema(description = "业务解压任务id")
|
||||||
|
private Long businessUnzipTaskId;
|
||||||
|
/**
|
||||||
|
* 附件id
|
||||||
|
*/
|
||||||
|
@Schema(description = "附件id")
|
||||||
|
private Long attachmentId;
|
||||||
|
/**
|
||||||
|
* 创建人
|
||||||
|
*/
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
@Schema(description = "创建人")
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private Long createUser;
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@DateTimeFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@JsonFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@Schema(description = "创建时间")
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private Date createTime;
|
||||||
|
/**
|
||||||
|
* 更新人
|
||||||
|
*/
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
@Schema(description = "更新人")
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private Long updateUser;
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@DateTimeFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@JsonFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@Schema(description = "更新时间")
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private Date updateTime;
|
||||||
|
/**
|
||||||
|
* 状态[0:未删除,1:删除]
|
||||||
|
*/
|
||||||
|
@TableLogic
|
||||||
|
@Schema(description = "是否已删除 [0:未删除,1:删除]")
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private Integer isDeleted;
|
||||||
|
}
|
||||||
+89
@@ -0,0 +1,89 @@
|
|||||||
|
package org.springblade.file.pojo.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
|
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springblade.core.tool.utils.DateUtil;
|
||||||
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* 业务解压任务表
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @author bfhuange
|
||||||
|
* @since 2024-10-28
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("blade_business_unzip_task")
|
||||||
|
@Schema(description = "业务解压任务表")
|
||||||
|
public class BusinessUnzipTask implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@TableId(value = "id", type = IdType.ASSIGN_ID)
|
||||||
|
@Schema(description = "主键")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Schema(description = "业务id")
|
||||||
|
private Long bizId;
|
||||||
|
/**
|
||||||
|
* 部门名称ID
|
||||||
|
*/
|
||||||
|
@Schema(description = "部门名称ID")
|
||||||
|
private Long departmentId;
|
||||||
|
|
||||||
|
@Schema(description = "业务code")
|
||||||
|
private String bizCode;
|
||||||
|
|
||||||
|
@Schema(description = "文档编号")
|
||||||
|
private String docCode;
|
||||||
|
|
||||||
|
@Schema(description = "关联类型,file_task 文件任务,attachment 附件")
|
||||||
|
private String relateType;
|
||||||
|
|
||||||
|
@Schema(description = "关联id,文件任务id或附件id")
|
||||||
|
private Long relateId;
|
||||||
|
|
||||||
|
@Schema(description = "状态 uploading:正在上传 unzipping:解压中 finished:已完成")
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
@Schema(description = "租户ID")
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private String tenantId;
|
||||||
|
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
@Schema(description = "创建人", hidden = true)
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private Long createUser;
|
||||||
|
|
||||||
|
@DateTimeFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@JsonFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@Schema(description = "创建时间", hidden = true)
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
@Schema(description = "更新人", hidden = true)
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private Long updateUser;
|
||||||
|
|
||||||
|
@DateTimeFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@JsonFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@Schema(description = "更新时间", hidden = true)
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private Date updateTime;
|
||||||
|
|
||||||
|
@Schema(description = "是否已删除", hidden = true)
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
@TableLogic
|
||||||
|
private Integer isDeleted;
|
||||||
|
}
|
||||||
+158
@@ -0,0 +1,158 @@
|
|||||||
|
package org.springblade.file.pojo.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
|
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springblade.core.tool.utils.DateUtil;
|
||||||
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author zhaowei
|
||||||
|
* @date 2024/8/23
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@TableName("blade_file_task")
|
||||||
|
@Schema(description = "文件上传任务对象")
|
||||||
|
public class FileTask implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
@Schema(description = "主键id")
|
||||||
|
@TableId(value = "id", type = IdType.ASSIGN_ID)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Md5
|
||||||
|
*/
|
||||||
|
@Schema(description = "Md5")
|
||||||
|
private String md5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OBS objectKey
|
||||||
|
*/
|
||||||
|
@Schema(description = "OBS objectKey")
|
||||||
|
private String objectKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 附件名
|
||||||
|
*/
|
||||||
|
@Schema(description = "附件名")
|
||||||
|
private String attachmentName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件类型
|
||||||
|
*/
|
||||||
|
@Schema(description = "文件类型")
|
||||||
|
private String suffix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 附件大小(单位:B)
|
||||||
|
*/
|
||||||
|
@Schema(description = "附件大小(单位:B)")
|
||||||
|
private Long size;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分段大小
|
||||||
|
*/
|
||||||
|
@Schema(description = "分段大小")
|
||||||
|
private Integer chunkSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分段数量
|
||||||
|
*/
|
||||||
|
@Schema(description = "分段数量")
|
||||||
|
private Integer chunkTotal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前分段的位置(已上传完成的分段),从0开始,上传完第1段,为1
|
||||||
|
*/
|
||||||
|
@Schema(description = "当前分段的位置(已上传完成的分段),从0开始,上传完第1段,为1")
|
||||||
|
private Integer currentIndex;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 华为云初始化上传任务生成的uploadId
|
||||||
|
*/
|
||||||
|
@Schema(description = "华为云初始化上传任务生成的uploadId")
|
||||||
|
private String uploadId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态 uploading:正在上传 completed:上传完成 failed:上传失败 paused:已暂停
|
||||||
|
*/
|
||||||
|
@Schema(description = "状态 uploading:正在上传 completed:上传完成 failed:上传失败 paused:已暂停")
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
@Schema(description = "业务类型")
|
||||||
|
private String businessType;
|
||||||
|
|
||||||
|
@Schema(description = "业务标识")
|
||||||
|
private String businessId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建人-用户名称
|
||||||
|
*/
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
@Schema(description = "创建人-用户名称")
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private String createUserName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建人
|
||||||
|
*/
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
@Schema(description = "创建人")
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private Long createUser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@DateTimeFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@JsonFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@Schema(description = "创建时间")
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新人-用户名称
|
||||||
|
*/
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
@Schema(description = "创建人-用户名称")
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private String updateUserName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新人
|
||||||
|
*/
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
@Schema(description = "更新人")
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private Long updateUser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@DateTimeFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@JsonFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@Schema(description = "更新时间")
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private Date updateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态[0:未删除,1:删除]
|
||||||
|
*/
|
||||||
|
@TableLogic
|
||||||
|
@Schema(description = "是否已删除 [0:未删除,1:删除]")
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private Integer isDeleted;
|
||||||
|
|
||||||
|
}
|
||||||
+69
@@ -0,0 +1,69 @@
|
|||||||
|
package org.springblade.file.pojo.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
|
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springblade.core.tool.utils.DateUtil;
|
||||||
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author zhaowei
|
||||||
|
* @date 2024/8/16
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@TableName("blade_file_task_part")
|
||||||
|
@Schema(description = "文件任务分段表")
|
||||||
|
public class FileTaskPart implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
@Schema(description = "主键id")
|
||||||
|
@TableId(value = "id", type = IdType.ASSIGN_ID)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件任务Id
|
||||||
|
*/
|
||||||
|
@Schema(description = "文件任务Id")
|
||||||
|
private Long fileTaskId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 段号。分段号可以是不连续的。取值范围是[1,10000]的非负整数
|
||||||
|
*/
|
||||||
|
@Schema(description = "段号。分段号可以是不连续的。取值范围是[1,10000]的非负整数")
|
||||||
|
private Integer partNumber;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 段的ETag值。分段的Base64编码的128位MD5摘要,长度为32的字符串
|
||||||
|
*/
|
||||||
|
@Schema(description = "段的ETag值。分段的Base64编码的128位MD5摘要,长度为32的字符串")
|
||||||
|
private String etag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建人
|
||||||
|
*/
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
@Schema(description = "创建人")
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private Long createUser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@DateTimeFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@JsonFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@Schema(description = "创建时间")
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
}
|
||||||
+39
@@ -0,0 +1,39 @@
|
|||||||
|
package org.springblade.file.pojo.enums;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件任务状态
|
||||||
|
* @author bfhuange
|
||||||
|
* @date 2024/10/22
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public enum FileTaskStatus {
|
||||||
|
/**
|
||||||
|
* 正在上传
|
||||||
|
*/
|
||||||
|
UPLOADING("uploading", "正在上传"),
|
||||||
|
/**
|
||||||
|
* 上传完成
|
||||||
|
*/
|
||||||
|
COMPLETED("completed", "上传完成"),
|
||||||
|
/**
|
||||||
|
* 上传失败
|
||||||
|
*/
|
||||||
|
FAILED("failed", "上传失败"),
|
||||||
|
/**
|
||||||
|
* 已暂停
|
||||||
|
*/
|
||||||
|
PAUSED("paused", "已暂停"),
|
||||||
|
;
|
||||||
|
/**
|
||||||
|
* 编码
|
||||||
|
*/
|
||||||
|
private final String code;
|
||||||
|
/**
|
||||||
|
* 名称
|
||||||
|
*/
|
||||||
|
private final String name;
|
||||||
|
}
|
||||||
+99
@@ -0,0 +1,99 @@
|
|||||||
|
package org.springblade.file.pojo.enums;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解压任务枚举类
|
||||||
|
* @author bfhuange
|
||||||
|
* @date 2024/10/27
|
||||||
|
*/
|
||||||
|
public class UnzipTask {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业务编码枚举
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public enum BizCode {
|
||||||
|
/**
|
||||||
|
* 运单
|
||||||
|
*/
|
||||||
|
WAYBILL("waybill", "运单"),
|
||||||
|
/**
|
||||||
|
* 基地
|
||||||
|
*/
|
||||||
|
WAREHOUSE("warehouse", "基地"),
|
||||||
|
;
|
||||||
|
/**
|
||||||
|
* 编码
|
||||||
|
*/
|
||||||
|
private final String code;
|
||||||
|
/**
|
||||||
|
* 名称
|
||||||
|
*/
|
||||||
|
private final String name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关联类型枚举
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public enum RelateType {
|
||||||
|
/**
|
||||||
|
* 文件任务
|
||||||
|
*/
|
||||||
|
FILE_TASK("file_task", "文件任务"),
|
||||||
|
/**
|
||||||
|
* 附件
|
||||||
|
*/
|
||||||
|
ATTACHMENT("attachment", "附件"),
|
||||||
|
;
|
||||||
|
/**
|
||||||
|
* 编码
|
||||||
|
*/
|
||||||
|
private final String code;
|
||||||
|
/**
|
||||||
|
* 名称
|
||||||
|
*/
|
||||||
|
private final String name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态枚举
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public enum Status {
|
||||||
|
/**
|
||||||
|
* 正在上传
|
||||||
|
*/
|
||||||
|
UPLOADING("uploading", "正在上传"),
|
||||||
|
/**
|
||||||
|
* 上传完成
|
||||||
|
*/
|
||||||
|
UPLOADED("uploaded", "上传完成"),
|
||||||
|
/**
|
||||||
|
* 正在解压
|
||||||
|
*/
|
||||||
|
UNZIPPING("unzipping", "正在解压"),
|
||||||
|
/**
|
||||||
|
* 解压失败
|
||||||
|
*/
|
||||||
|
UNZIP_FAIL("unzip_fail", "解压失败"),
|
||||||
|
/**
|
||||||
|
* 已完成
|
||||||
|
*/
|
||||||
|
FINISHED("finished", "已完成"),
|
||||||
|
;
|
||||||
|
/**
|
||||||
|
* 编码
|
||||||
|
*/
|
||||||
|
private final String code;
|
||||||
|
/**
|
||||||
|
* 名称
|
||||||
|
*/
|
||||||
|
private final String name;
|
||||||
|
}
|
||||||
|
}
|
||||||
+73
@@ -0,0 +1,73 @@
|
|||||||
|
package org.springblade.file.pojo.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 附件详情对象
|
||||||
|
* @author bfhuange
|
||||||
|
* @date 2024/10/30
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "附件详情对象")
|
||||||
|
public class AttachmentDetailVO implements Serializable {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
/**
|
||||||
|
* 主键id
|
||||||
|
*/
|
||||||
|
@Schema(description = "主键id")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 附件名
|
||||||
|
*/
|
||||||
|
@Schema(description = "附件名")
|
||||||
|
private String attachmentName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* objectKey
|
||||||
|
*/
|
||||||
|
@Schema(description = "华为OBS objectKey")
|
||||||
|
private String objectKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 附件大小(单位:B)
|
||||||
|
*/
|
||||||
|
@Schema(description = "附件大小(单位:B)")
|
||||||
|
private long size;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建人
|
||||||
|
*/
|
||||||
|
@Schema(description = "创建人")
|
||||||
|
private Long createUser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@Schema(description = "创建时间")
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新人
|
||||||
|
*/
|
||||||
|
@Schema(description = "更新人")
|
||||||
|
private Long updateUser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@Schema(description = "更新时间")
|
||||||
|
private Date updateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载链接
|
||||||
|
*/
|
||||||
|
@Schema(description = "下载链接")
|
||||||
|
private String url;
|
||||||
|
}
|
||||||
+105
@@ -0,0 +1,105 @@
|
|||||||
|
package org.springblade.file.pojo.vo;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
|
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.Size;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springblade.core.tool.utils.DateUtil;
|
||||||
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业务附件信息表 视图实体类
|
||||||
|
*
|
||||||
|
* @author BladeX
|
||||||
|
* @since 2024-09-04
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "业务附件响应参数")
|
||||||
|
public class BusinessAttachmentVO implements Serializable {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主键id
|
||||||
|
*/
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
@Schema(description = "主键id")
|
||||||
|
private Long id;
|
||||||
|
/**
|
||||||
|
* 业务id
|
||||||
|
*/
|
||||||
|
@Schema(description = "业务id")
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
private Long bizId;
|
||||||
|
/**
|
||||||
|
* 业务code
|
||||||
|
*/
|
||||||
|
@Schema(description = "业务code")
|
||||||
|
private String bizCode;
|
||||||
|
/**
|
||||||
|
* 业务code名称
|
||||||
|
*/
|
||||||
|
@Schema(description = "业务code名称")
|
||||||
|
private String bizCodeStr;
|
||||||
|
/**
|
||||||
|
* 附件id
|
||||||
|
*/
|
||||||
|
@Schema(description = "附件id")
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
private Long attachmentId;
|
||||||
|
/**
|
||||||
|
* 附件名
|
||||||
|
*/
|
||||||
|
@Schema(description = "附件名")
|
||||||
|
private String attachmentName;
|
||||||
|
/**
|
||||||
|
* 附件大小(单位:B)
|
||||||
|
*/
|
||||||
|
@Schema(description = "附件大小(单位:B)")
|
||||||
|
private long size;
|
||||||
|
/**
|
||||||
|
* objectKey
|
||||||
|
*/
|
||||||
|
@Schema(description = "华为OBS objectKey")
|
||||||
|
private String objectKey;
|
||||||
|
/**
|
||||||
|
* 附件描述
|
||||||
|
*/
|
||||||
|
@Schema(description = "附件描述")
|
||||||
|
@Size(max = 100, message = "附件描述长度不能超过100")
|
||||||
|
private String desc;
|
||||||
|
/**
|
||||||
|
* 排序
|
||||||
|
*/
|
||||||
|
@Schema(description = "排序")
|
||||||
|
private Integer sort;
|
||||||
|
/**
|
||||||
|
* 上传人id
|
||||||
|
*/
|
||||||
|
@Schema(description = "上传人id")
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
private Long uploadUserId;
|
||||||
|
/**
|
||||||
|
* 上传人姓名
|
||||||
|
*/
|
||||||
|
@Schema(description = "上传人姓名")
|
||||||
|
private String uploadUserName;
|
||||||
|
/**
|
||||||
|
* 上传人姓名
|
||||||
|
*/
|
||||||
|
@Schema(description = "上传人姓名")
|
||||||
|
private String createUserName;
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@DateTimeFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@JsonFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@Schema(description = "创建时间")
|
||||||
|
private Date createTime;
|
||||||
|
}
|
||||||
+94
@@ -0,0 +1,94 @@
|
|||||||
|
package org.springblade.file.pojo.vo;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
|
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.Size;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springblade.core.tool.utils.DateUtil;
|
||||||
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业务解压任务明细对象
|
||||||
|
* @author bfhuange
|
||||||
|
* @date 2024/11/08
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "业务解压任务明细对象")
|
||||||
|
public class BusinessUnzipDetailListVO implements Serializable {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
/**
|
||||||
|
* 主键
|
||||||
|
*/
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
@Schema(description = "主键")
|
||||||
|
private Long id;
|
||||||
|
/**
|
||||||
|
* 附件id
|
||||||
|
*/
|
||||||
|
@Schema(description = "附件id")
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
private Long attachmentId;
|
||||||
|
|
||||||
|
//==========================附件属性=============================
|
||||||
|
/**
|
||||||
|
* 压缩文件所在压缩包相对路径
|
||||||
|
*/
|
||||||
|
@Schema(description = "压缩文件所在压缩包相对路径")
|
||||||
|
private String dir;
|
||||||
|
/**
|
||||||
|
* 附件名
|
||||||
|
*/
|
||||||
|
@Schema(description = "附件名")
|
||||||
|
private String attachmentName;
|
||||||
|
/**
|
||||||
|
* 附件大小(单位:B)
|
||||||
|
*/
|
||||||
|
@Schema(description = "附件大小(单位:B)")
|
||||||
|
private long size;
|
||||||
|
/**
|
||||||
|
* objectKey
|
||||||
|
*/
|
||||||
|
@Schema(description = "华为OBS objectKey")
|
||||||
|
private String objectKey;
|
||||||
|
/**
|
||||||
|
* 附件描述
|
||||||
|
*/
|
||||||
|
@Schema(description = "附件描述")
|
||||||
|
@Size(max = 100, message = "附件描述长度不能超过100")
|
||||||
|
private String desc;
|
||||||
|
/**
|
||||||
|
* 排序
|
||||||
|
*/
|
||||||
|
@Schema(description = "排序")
|
||||||
|
private Integer sort;
|
||||||
|
/**
|
||||||
|
* 上传人id
|
||||||
|
*/
|
||||||
|
@Schema(description = "上传人id")
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
private Long uploadUserId;
|
||||||
|
/**
|
||||||
|
* 上传人姓名
|
||||||
|
*/
|
||||||
|
@Schema(description = "上传人姓名")
|
||||||
|
private String uploadUserName;
|
||||||
|
/**
|
||||||
|
* 上传人姓名
|
||||||
|
*/
|
||||||
|
@Schema(description = "上传人姓名")
|
||||||
|
private String createUserName;
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@DateTimeFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@JsonFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@Schema(description = "创建时间")
|
||||||
|
private Date createTime;
|
||||||
|
}
|
||||||
+169
@@ -0,0 +1,169 @@
|
|||||||
|
package org.springblade.file.pojo.vo;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
|
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springblade.core.tool.utils.DateUtil;
|
||||||
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业务解压任务分页对象
|
||||||
|
* @author bfhuange
|
||||||
|
* @date 2024/10/28
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "业务解压任务分页对象")
|
||||||
|
public class BusinessUnzipTaskListVO implements Serializable {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
/**
|
||||||
|
* 主键
|
||||||
|
*/
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
@Schema(description = "主键")
|
||||||
|
private Long id;
|
||||||
|
/**
|
||||||
|
* 业务id
|
||||||
|
*/
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
@Schema(description = "业务id")
|
||||||
|
private Long bizId;
|
||||||
|
/**
|
||||||
|
* 业务code
|
||||||
|
*/
|
||||||
|
@Schema(description = "业务code")
|
||||||
|
private String bizCode;
|
||||||
|
/**
|
||||||
|
* 文档编号
|
||||||
|
*/
|
||||||
|
@Schema(description = "文档编号")
|
||||||
|
private String docCode;
|
||||||
|
/**
|
||||||
|
* 关联类型,file_task 文件任务,attachment 附件
|
||||||
|
*/
|
||||||
|
@Schema(description = "关联类型,file_task 文件任务,attachment 附件")
|
||||||
|
private String relateType;
|
||||||
|
/**
|
||||||
|
* 关联id,文件任务id或附件id
|
||||||
|
*/
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
@Schema(description = "关联id,文件任务id或附件id")
|
||||||
|
private Long relateId;
|
||||||
|
|
||||||
|
//==========================文件任务属性=============================
|
||||||
|
/**
|
||||||
|
* Md5
|
||||||
|
*/
|
||||||
|
@Schema(description = "Md5")
|
||||||
|
private String md5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OBS objectKey
|
||||||
|
*/
|
||||||
|
@Schema(description = "OBS objectKey")
|
||||||
|
private String objectKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 附件名
|
||||||
|
*/
|
||||||
|
@Schema(description = "附件名")
|
||||||
|
private String attachmentName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件类型
|
||||||
|
*/
|
||||||
|
@Schema(description = "文件类型")
|
||||||
|
private String suffix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 附件大小(单位:B)
|
||||||
|
*/
|
||||||
|
@Schema(description = "附件大小(单位:B)")
|
||||||
|
private Long size;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分段大小
|
||||||
|
*/
|
||||||
|
@Schema(description = "分段大小")
|
||||||
|
private Integer chunkSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分段数量
|
||||||
|
*/
|
||||||
|
@Schema(description = "分段数量")
|
||||||
|
private Integer chunkTotal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前分段的位置(已上传完成的分段),从0开始,上传完第1段,为1
|
||||||
|
*/
|
||||||
|
@Schema(description = "当前分段的位置(已上传完成的分段),从0开始,上传完第1段,为1")
|
||||||
|
private Integer currentIndex;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 华为云初始化上传任务生成的uploadId
|
||||||
|
*/
|
||||||
|
@Schema(description = "华为云初始化上传任务生成的uploadId")
|
||||||
|
private String uploadId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态 uploading:正在上传 completed:上传完成 failed:上传失败 paused:已暂停
|
||||||
|
*/
|
||||||
|
@Schema(description = "状态 uploading:正在上传 completed:上传完成 failed:上传失败 paused:已暂停")
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态名称
|
||||||
|
*/
|
||||||
|
@Schema(description = "状态名称")
|
||||||
|
private String statusStr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建人-用户名称
|
||||||
|
*/
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
@Schema(description = "创建人-用户名称")
|
||||||
|
private String createUserName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建人
|
||||||
|
*/
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
@Schema(description = "创建人")
|
||||||
|
private Long createUser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@DateTimeFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@JsonFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@Schema(description = "创建时间")
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新人-用户名称
|
||||||
|
*/
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
@Schema(description = "创建人-用户名称")
|
||||||
|
private String updateUserName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新人
|
||||||
|
*/
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
@Schema(description = "更新人")
|
||||||
|
private Long updateUser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@DateTimeFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@JsonFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@Schema(description = "更新时间")
|
||||||
|
private Date updateTime;
|
||||||
|
}
|
||||||
+36
@@ -0,0 +1,36 @@
|
|||||||
|
package org.springblade.file.pojo.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件处理结果对象
|
||||||
|
* @author bfhuange
|
||||||
|
* @date 2024/10/30
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "文件处理结果对象")
|
||||||
|
public class FileHandleVO implements Serializable {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
/**
|
||||||
|
* 下载的压缩文件
|
||||||
|
*/
|
||||||
|
@Schema(description = "下载的压缩文件")
|
||||||
|
private File downloadFile;
|
||||||
|
/**
|
||||||
|
* 解压目录
|
||||||
|
*/
|
||||||
|
@Schema(description = "解压目录")
|
||||||
|
private File unzippedDir;
|
||||||
|
/**
|
||||||
|
* 解压后的文件列表
|
||||||
|
*/
|
||||||
|
@Schema(description = "解压后的文件列表")
|
||||||
|
private List<File> unzippedFiles;
|
||||||
|
}
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
package org.springblade.file.pojo.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件任务更新结果
|
||||||
|
* @author bfhuange
|
||||||
|
* @date 2024/10/28
|
||||||
|
*/
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@Data
|
||||||
|
@Schema(description = "文件任务更新结果")
|
||||||
|
public class FileTaskUpdateVO extends FileTaskVO {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
/**
|
||||||
|
* 文档编号(凭证批次号),如果有则返回,没有为空
|
||||||
|
*/
|
||||||
|
@Schema(description = "文档编号(凭证批次号),如果有则返回,没有为空")
|
||||||
|
private String docCode;
|
||||||
|
}
|
||||||
+139
@@ -0,0 +1,139 @@
|
|||||||
|
package org.springblade.file.pojo.vo;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
|
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springblade.core.tool.utils.DateUtil;
|
||||||
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件任务对象
|
||||||
|
* @author bfhuange
|
||||||
|
* @date 2024/10/22
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "文件任务对象")
|
||||||
|
public class FileTaskVO implements Serializable {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
@Schema(description = "主键id")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Md5
|
||||||
|
*/
|
||||||
|
@Schema(description = "Md5")
|
||||||
|
private String md5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OBS objectKey
|
||||||
|
*/
|
||||||
|
@Schema(description = "OBS objectKey")
|
||||||
|
private String objectKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 附件名
|
||||||
|
*/
|
||||||
|
@Schema(description = "附件名")
|
||||||
|
private String attachmentName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件类型
|
||||||
|
*/
|
||||||
|
@Schema(description = "文件类型")
|
||||||
|
private String suffix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 附件大小(单位:B)
|
||||||
|
*/
|
||||||
|
@Schema(description = "附件大小(单位:B)")
|
||||||
|
private Long size;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分段大小
|
||||||
|
*/
|
||||||
|
@Schema(description = "分段大小")
|
||||||
|
private Integer chunkSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分段数量
|
||||||
|
*/
|
||||||
|
@Schema(description = "分段数量")
|
||||||
|
private Integer chunkTotal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前分段的位置(已上传完成的分段),从0开始,上传完第1段,为1
|
||||||
|
*/
|
||||||
|
@Schema(description = "当前分段的位置(已上传完成的分段),从0开始,上传完第1段,为1")
|
||||||
|
private Integer currentIndex;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 华为云初始化上传任务生成的uploadId
|
||||||
|
*/
|
||||||
|
@Schema(description = "华为云初始化上传任务生成的uploadId")
|
||||||
|
private String uploadId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态 uploading:正在上传 completed:上传完成 failed:上传失败 paused:已暂停
|
||||||
|
*/
|
||||||
|
@Schema(description = "状态 uploading:正在上传 completed:上传完成 failed:上传失败 paused:已暂停")
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
@Schema(description = "业务类型")
|
||||||
|
private String businessType;
|
||||||
|
|
||||||
|
@Schema(description = "业务标识")
|
||||||
|
private String businessId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建人-用户名称
|
||||||
|
*/
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
@Schema(description = "创建人-用户名称")
|
||||||
|
private String createUserName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建人
|
||||||
|
*/
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
@Schema(description = "创建人")
|
||||||
|
private Long createUser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@DateTimeFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@JsonFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@Schema(description = "创建时间")
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新人-用户名称
|
||||||
|
*/
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
@Schema(description = "创建人-用户名称")
|
||||||
|
private String updateUserName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新人
|
||||||
|
*/
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
@Schema(description = "更新人")
|
||||||
|
private Long updateUser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@DateTimeFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@JsonFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@Schema(description = "更新时间")
|
||||||
|
private Date updateTime;
|
||||||
|
}
|
||||||
+33
@@ -0,0 +1,33 @@
|
|||||||
|
package org.springblade.file.pojo.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件下载地址对象
|
||||||
|
* @author bfhuange
|
||||||
|
* @since 2025/4/22
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Data
|
||||||
|
@Schema(description = "文件下载地址对象")
|
||||||
|
public class FileUrlVO implements Serializable {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
/**
|
||||||
|
* 主键id
|
||||||
|
*/
|
||||||
|
@Schema(description = "主键id")
|
||||||
|
private Long id;
|
||||||
|
/**
|
||||||
|
* 文件下载地址
|
||||||
|
*/
|
||||||
|
@Schema(description ="文件下载地址")
|
||||||
|
private String url;
|
||||||
|
}
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
package org.springblade.file.pojo.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件对象
|
||||||
|
* @author bfhuange
|
||||||
|
* @date 2024/11/19
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Data
|
||||||
|
@Schema(description = "文件对象")
|
||||||
|
public class FileVO implements Serializable {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OBS objectKey
|
||||||
|
*/
|
||||||
|
@Schema(description = "OBS objectKey")
|
||||||
|
private String objectKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Md5
|
||||||
|
*/
|
||||||
|
@Schema(description = "Md5")
|
||||||
|
private String md5;
|
||||||
|
}
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
package org.springblade.file.pojo.vo;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 待合并的段
|
||||||
|
* @author bfhuange
|
||||||
|
* @date 2024/10/21
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Data
|
||||||
|
public class PartEtagVO implements Serializable {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
/**
|
||||||
|
* 段的ETag值。分段的Base64编码的128位MD5摘要,长度为32的字符串
|
||||||
|
*/
|
||||||
|
private String etag;
|
||||||
|
/**
|
||||||
|
* 段号。分段号可以是不连续的。取值范围是[1,10000]的非负整数
|
||||||
|
*/
|
||||||
|
private Integer partNumber;
|
||||||
|
}
|
||||||
+33
@@ -0,0 +1,33 @@
|
|||||||
|
package org.springblade.file.pojo.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件下载地址对象
|
||||||
|
* @author bfhuange
|
||||||
|
* @since 2025/4/22
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Data
|
||||||
|
@Schema(description = "临时文件下载地址对象")
|
||||||
|
public class TempFileUrlVO implements Serializable {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
/**
|
||||||
|
* obs key
|
||||||
|
*/
|
||||||
|
@Schema(description = "obs key")
|
||||||
|
private String objectKey;
|
||||||
|
/**
|
||||||
|
* 文件下载地址
|
||||||
|
*/
|
||||||
|
@Schema(description ="文件下载地址")
|
||||||
|
private String url;
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springblade</groupId>
|
||||||
|
<artifactId>blade-service-api</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>blade-open-api</artifactId>
|
||||||
|
<name>${project.artifactId}</name>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
</project>
|
||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
package org.springblade.openapi.common.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页参数
|
||||||
|
* @author bfhuange
|
||||||
|
* @since 2025/4/23
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Data
|
||||||
|
@Schema(description = "分页结果")
|
||||||
|
public class PageVO<T> implements Serializable {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
/**
|
||||||
|
* 查询数据列表
|
||||||
|
*/
|
||||||
|
@Schema(description = "查询数据列表")
|
||||||
|
protected List<T> records = Collections.emptyList();
|
||||||
|
/**
|
||||||
|
* 总数
|
||||||
|
*/
|
||||||
|
@Schema(description = "总数")
|
||||||
|
protected long total = 0;
|
||||||
|
/**
|
||||||
|
* 总页数
|
||||||
|
*/
|
||||||
|
@Schema(description = "总页数")
|
||||||
|
protected long pages = 0;
|
||||||
|
}
|
||||||
+34
@@ -0,0 +1,34 @@
|
|||||||
|
package org.springblade.openapi.mk.api;
|
||||||
|
|
||||||
|
import org.springblade.core.tool.api.FR;
|
||||||
|
import org.springblade.openapi.mk.pojo.dto.Api4MKProcessApprovalDTO;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 给mk回调的api
|
||||||
|
* @author bfhuange
|
||||||
|
* @date 2024/9/5
|
||||||
|
*/
|
||||||
|
public interface IApi4MK {
|
||||||
|
String API_PREFIX = "/openApi/mk";
|
||||||
|
String PROCESS_API_PREFIX = API_PREFIX + "/process";
|
||||||
|
String PROCESS_FINISH_CALLBACK = PROCESS_API_PREFIX + "/finishCallback";
|
||||||
|
String PROCESS_COMMON_CALLBACK = PROCESS_API_PREFIX + "/commonCallback";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流程通用回调接口
|
||||||
|
* @param param
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping(PROCESS_COMMON_CALLBACK)
|
||||||
|
FR<Boolean> processCommonCallback(@RequestBody Api4MKProcessApprovalDTO param);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流程结束回调接口
|
||||||
|
* @param param
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping(PROCESS_FINISH_CALLBACK)
|
||||||
|
FR<Boolean> processFinishCallback(@RequestBody Api4MKProcessApprovalDTO param);
|
||||||
|
}
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
package org.springblade.openapi.mk.constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author bfhuange
|
||||||
|
* @date 2024/9/12
|
||||||
|
*/
|
||||||
|
public class MKConstant {
|
||||||
|
}
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
package org.springblade.openapi.mk.constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流程当前处理人相关redis锁key常量类
|
||||||
|
* @author bfhuange
|
||||||
|
* @since 2026/4/9
|
||||||
|
*/
|
||||||
|
public class ProcessLockKeyConstant {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务缓存key前缀
|
||||||
|
*/
|
||||||
|
public static final String TASK_KEY_PREFIX = "process:cur-handler:task:";
|
||||||
|
/**
|
||||||
|
* 等待队列key
|
||||||
|
*/
|
||||||
|
public static final String WAITING_KEY = "process:cur-handler:waiting";
|
||||||
|
/**
|
||||||
|
* 流程锁key前缀
|
||||||
|
*/
|
||||||
|
public static final String PROCESS_LOCK_KEY_PREFIX = "process:cur-handler:lock:";
|
||||||
|
/**
|
||||||
|
* 派工锁key
|
||||||
|
*/
|
||||||
|
public static final String DISPATCH_LOCK_KEY = "process:cur-handler:dispatch-lock";
|
||||||
|
/**
|
||||||
|
* worker租约map key
|
||||||
|
*/
|
||||||
|
public static final String WORKER_LEASE_KEY = "process:cur-handler:worker-leases";
|
||||||
|
}
|
||||||
+72
@@ -0,0 +1,72 @@
|
|||||||
|
package org.springblade.openapi.mk.pojo.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mk审批回调参数
|
||||||
|
* @author bfhuange
|
||||||
|
* @date 2024/9/5
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class Api4MKProcessApprovalDTO implements Serializable {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
/**
|
||||||
|
* 流程实例id
|
||||||
|
*/
|
||||||
|
private String processInstanceId;
|
||||||
|
/**
|
||||||
|
* 表单实例id
|
||||||
|
*/
|
||||||
|
private String formInstanceId;
|
||||||
|
/**
|
||||||
|
* 模板id
|
||||||
|
*/
|
||||||
|
private String templateId;
|
||||||
|
/**
|
||||||
|
* 模板编码,template_拼接 ProcessTypeEnum 的值
|
||||||
|
*/
|
||||||
|
private String templateCode;
|
||||||
|
/**
|
||||||
|
* 流程状态
|
||||||
|
*/
|
||||||
|
private String processStatus;
|
||||||
|
/**
|
||||||
|
* 申请人登录名
|
||||||
|
*/
|
||||||
|
private String applicantLoginName;
|
||||||
|
|
||||||
|
//===========================下面5个参数流程结束没有=========================
|
||||||
|
/**
|
||||||
|
* 驳回节点id N2 是起草节点
|
||||||
|
*/
|
||||||
|
private String rejectNodeId;
|
||||||
|
/**
|
||||||
|
* 当前节点id
|
||||||
|
*/
|
||||||
|
private String currentNodeId;
|
||||||
|
/**
|
||||||
|
* 当前节点编号
|
||||||
|
*/
|
||||||
|
private String currentNodeNumber;
|
||||||
|
/**
|
||||||
|
* 执行的操作
|
||||||
|
*/
|
||||||
|
private String operation;
|
||||||
|
/**
|
||||||
|
* 操作名称
|
||||||
|
*/
|
||||||
|
private String operationName;
|
||||||
|
/**
|
||||||
|
* 审批意见
|
||||||
|
*/
|
||||||
|
private String approvalOpinion;
|
||||||
|
/**
|
||||||
|
* 操作人登录名
|
||||||
|
*/
|
||||||
|
private String operatorLoginName;
|
||||||
|
|
||||||
|
}
|
||||||
+101
@@ -0,0 +1,101 @@
|
|||||||
|
package org.springblade.openapi.mk.pojo.enums;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流程回调类型
|
||||||
|
* @author bfhuange
|
||||||
|
* @since 2024/11/23
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public enum ProcessCallbackType {
|
||||||
|
/**
|
||||||
|
* 提交
|
||||||
|
*/
|
||||||
|
SUBMIT(ProcessOperationType.DRAFTER_SUBMIT),
|
||||||
|
/**
|
||||||
|
* 起草人撤回
|
||||||
|
*/
|
||||||
|
RETRACT(ProcessOperationType.DRAFTER_RETRACT),
|
||||||
|
/**
|
||||||
|
* 审批通过
|
||||||
|
*/
|
||||||
|
PASS(ProcessOperationType.HANDLER_PASS),
|
||||||
|
/**
|
||||||
|
* 审批驳回
|
||||||
|
*/
|
||||||
|
REJECT(ProcessOperationType.HANDLER_REJECT),
|
||||||
|
/**
|
||||||
|
* 废弃(起草人废弃、审批人废弃、超管废弃)
|
||||||
|
*/
|
||||||
|
ABANDON(ProcessOperationType.DRAFTER_ABANDON, ProcessOperationType.HANDLER_ABANDON, ProcessOperationType.ADMIN_ABANDON),
|
||||||
|
/**
|
||||||
|
* 修改当前处理人(超管修改当前处理人、审批人转办、审批人撤回、特权人前后跳转)
|
||||||
|
*/
|
||||||
|
CHANGE_CUR_HANDLER(ProcessOperationType.ADMIN_CHANGE_CUR_HANDLER, ProcessOperationType.HANDLER_TRANSFER, ProcessOperationType.HISTORY_HANDLER_RETRACT, ProcessOperationType.ADMIN_JUMP),
|
||||||
|
/**
|
||||||
|
* 流程结束
|
||||||
|
*/
|
||||||
|
FINISH(ProcessOperationType.PROCESS_FINISH),
|
||||||
|
;
|
||||||
|
/**
|
||||||
|
* 编码
|
||||||
|
*/
|
||||||
|
private final List<String> operationTypes;
|
||||||
|
|
||||||
|
ProcessCallbackType(ProcessOperationType... operationTypes) {
|
||||||
|
if (operationTypes.length == 0) {
|
||||||
|
throw new RuntimeException("流程操作类型不能为空");
|
||||||
|
}
|
||||||
|
this.operationTypes = Stream.of(operationTypes)
|
||||||
|
.map(ProcessOperationType::getCode)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
ProcessCallbackType(String... operationTypes) {
|
||||||
|
if (operationTypes.length == 0) {
|
||||||
|
throw new RuntimeException("流程操作类型不能为空");
|
||||||
|
}
|
||||||
|
this.operationTypes = Lists.newArrayList(operationTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流程操作类型对应的事件
|
||||||
|
*/
|
||||||
|
private static final Map<String, ProcessCallbackType> CALLBACK_MAP = initCallbackMap();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化流程操作类型对应的事件
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private static Map<String, ProcessCallbackType> initCallbackMap() {
|
||||||
|
return Stream.of(values())
|
||||||
|
.flatMap(callbackType -> callbackType.getOperationTypes().stream()
|
||||||
|
.collect(Collectors.toMap(Function.identity(), type -> callbackType))
|
||||||
|
.entrySet()
|
||||||
|
.stream()
|
||||||
|
)
|
||||||
|
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> {
|
||||||
|
throw new RuntimeException("流程操作类型不能重复");
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据流程操作类型,获取对应的回调
|
||||||
|
* @param operationType
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static ProcessCallbackType getCallbackType(String operationType) {
|
||||||
|
return CALLBACK_MAP.get(operationType);
|
||||||
|
}
|
||||||
|
}
|
||||||
+175
@@ -0,0 +1,175 @@
|
|||||||
|
package org.springblade.openapi.mk.pojo.enums;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流程操作类型枚举
|
||||||
|
* @author bfhuange
|
||||||
|
* @since 2024/11/23
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public enum ProcessOperationType {
|
||||||
|
/**
|
||||||
|
* 起草人协同
|
||||||
|
*/
|
||||||
|
DRAFTER_COOPERATE("drafter_cooperate", "起草人协同"),
|
||||||
|
/**
|
||||||
|
* 起草人取消协同
|
||||||
|
*/
|
||||||
|
DRAFTER_CANCEL_DRAFT_COOPERATE("drafter_cancelDraftCooperate", "起草人取消协同"),
|
||||||
|
/**
|
||||||
|
* 协同接收人-回复协同
|
||||||
|
*/
|
||||||
|
HANDLER_REPLY_DRAFT_COOPERATE("handler_replyDraftCooperate", "协同接收人-回复协同"),
|
||||||
|
/**
|
||||||
|
* 起草人-提交
|
||||||
|
*/
|
||||||
|
DRAFTER_SUBMIT("drafter_submit", "起草人-提交"),
|
||||||
|
/**
|
||||||
|
* 起草人-废弃
|
||||||
|
*/
|
||||||
|
DRAFTER_ABANDON("drafter_abandon", "起草人-废弃"),
|
||||||
|
/**
|
||||||
|
* 起草人-撤回
|
||||||
|
*/
|
||||||
|
DRAFTER_RETRACT("drafter_retract", "起草人-撤回"),
|
||||||
|
/**
|
||||||
|
* 审批人-通过
|
||||||
|
*/
|
||||||
|
HANDLER_PASS("handler_pass", "审批人-通过"),
|
||||||
|
/**
|
||||||
|
* 审批人-驳回
|
||||||
|
*/
|
||||||
|
HANDLER_REJECT("handler_reject", "审批人-驳回"),
|
||||||
|
/**
|
||||||
|
* 审批人-废弃
|
||||||
|
*/
|
||||||
|
HANDLER_ABANDON("handler_abandon", "审批人-废弃"),
|
||||||
|
/**
|
||||||
|
* 审批人-撤回
|
||||||
|
*/
|
||||||
|
HISTORY_HANDLER_RETRACT("historyHandler_retract", "审批人-撤回"),
|
||||||
|
/**
|
||||||
|
* 审批人-签字
|
||||||
|
*/
|
||||||
|
HANDLER_SIGN("handler_sign", "审批人-签字"),
|
||||||
|
/**
|
||||||
|
* 审批人-转办
|
||||||
|
*/
|
||||||
|
HANDLER_TRANSFER("handler_transfer", "审批人-转办"),
|
||||||
|
/**
|
||||||
|
* 审批人-分发
|
||||||
|
*/
|
||||||
|
HANDLER_ASSIGN("handler_assign", "审批人-分发"),
|
||||||
|
/**
|
||||||
|
* 审批人-催办
|
||||||
|
*/
|
||||||
|
HISTORY_HANDLER_URGE("historyHandler_urge", "审批人-催办"),
|
||||||
|
/**
|
||||||
|
* 审批人-沟通
|
||||||
|
*/
|
||||||
|
HANDLER_COMMUNICATE("handler_communicate", "审批人-沟通"),
|
||||||
|
/**
|
||||||
|
* 审批人-回复沟通
|
||||||
|
*/
|
||||||
|
HANDLER_REPLY_COMMUNICATE("handler_replyCommunicate", "审批人-回复沟通"),
|
||||||
|
/**
|
||||||
|
* 审批人-取消沟通
|
||||||
|
*/
|
||||||
|
HANDLER_CANCEL_COMMUNICATE("handler_cancelCommunicate", "审批人-取消沟通"),
|
||||||
|
/**
|
||||||
|
* 审批人-补签
|
||||||
|
*/
|
||||||
|
HANDLER_APPEND_SIGN("handler_appendSign", "审批人-补签"),
|
||||||
|
/**
|
||||||
|
* 审批人-加签
|
||||||
|
*/
|
||||||
|
HANDLER_PREPEND_SIGN("handler_prependSign", "审批人-加签"),
|
||||||
|
/**
|
||||||
|
* 审批人(加签人)-取消加签
|
||||||
|
*/
|
||||||
|
HANDLER_PREPEND_SIGN_CANCEL("handler_prependSignCancel", "审批人(加签人)-取消加签"),
|
||||||
|
/**
|
||||||
|
* 审批人(被加签人)-退回加签
|
||||||
|
*/
|
||||||
|
HANDLER_PREPEND_SIGN_REFUSE("handler_prependSignRefuse", "审批人(被加签人)-退回加签"),
|
||||||
|
/**
|
||||||
|
* 审批人(被加签人)-通过加签
|
||||||
|
*/
|
||||||
|
HANDLER_PREPEND_SIGN_PASS("handler_prependSignPass", "审批人(被加签人)-通过加签"),
|
||||||
|
/**
|
||||||
|
* 审批人(被加签人)-审批人-传阅
|
||||||
|
*/
|
||||||
|
HANDLER_CIRCULATION("handler_circulation", "审批人(被加签人)-审批人-传阅"),
|
||||||
|
/**
|
||||||
|
* 特权人-前后跳转
|
||||||
|
*/
|
||||||
|
ADMIN_JUMP("admin_jump", "特权人-前后跳转"),
|
||||||
|
/**
|
||||||
|
* 特权人-流程暂停
|
||||||
|
*/
|
||||||
|
ADMIN_PROCESS_SUSPEND("admin_processSuspend", "特权人-流程暂停"),
|
||||||
|
/**
|
||||||
|
* 特权人-节点暂停
|
||||||
|
*/
|
||||||
|
ADMIN_NODE_SUSPEND("admin_nodeSuspend", "特权人-节点暂停"),
|
||||||
|
/**
|
||||||
|
* 审批人-节点唤醒
|
||||||
|
*/
|
||||||
|
HANDLER_NODE_RESUME("handler_nodeResume", "审批人-节点唤醒"),
|
||||||
|
/**
|
||||||
|
* 特权人-节点唤醒
|
||||||
|
*/
|
||||||
|
ADMIN_NODE_RESUME("admin_nodeResume", "特权人-节点唤醒"),
|
||||||
|
/**
|
||||||
|
* 特权人-流程唤醒
|
||||||
|
*/
|
||||||
|
ADMIN_PROCESS_RESUME("admin_processResume", "特权人-流程唤醒"),
|
||||||
|
/**
|
||||||
|
* 特权人-修改流程
|
||||||
|
*/
|
||||||
|
ADMIN_MODIFY_PROCESS("admin_modifyProcess", "特权人-修改流程"),
|
||||||
|
/**
|
||||||
|
* 特权人-修改当前处理人
|
||||||
|
*/
|
||||||
|
ADMIN_CHANGE_CUR_HANDLER("admin_changeCurHandler", "特权人-修改当前处理人"),
|
||||||
|
/**
|
||||||
|
* 特权人-终审通过
|
||||||
|
*/
|
||||||
|
ADMIN_END("admin_end", "特权人-终审通过"),
|
||||||
|
/**
|
||||||
|
* 特权人-直接废弃
|
||||||
|
*/
|
||||||
|
ADMIN_ABANDON("admin_abandon", "特权人-直接废弃"),
|
||||||
|
;
|
||||||
|
/**
|
||||||
|
* 编码
|
||||||
|
*/
|
||||||
|
private final String code;
|
||||||
|
/**
|
||||||
|
* 名称
|
||||||
|
*/
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流程结束,mk没有这个操作,但是有这个事件,自定义一个操作类型,统一管理
|
||||||
|
*/
|
||||||
|
public static final String PROCESS_FINISH = "erp_process_finish";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据操作编码获取名称
|
||||||
|
* @param code
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String getOperationName(String code) {
|
||||||
|
return Stream.of(values())
|
||||||
|
.filter(type -> type.getCode().equals(code))
|
||||||
|
.findFirst()
|
||||||
|
.map(ProcessOperationType::getName)
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
+36
@@ -0,0 +1,36 @@
|
|||||||
|
package org.springblade.openapi.ocr.api;
|
||||||
|
|
||||||
|
import org.springblade.core.tool.api.R;
|
||||||
|
import org.springblade.openapi.common.vo.PageVO;
|
||||||
|
import org.springblade.openapi.ocr.pojo.dto.Api4OCRProjectDTO;
|
||||||
|
import org.springblade.openapi.ocr.pojo.dto.Api4OCRResultDTO;
|
||||||
|
import org.springblade.openapi.ocr.pojo.vo.Api4OCRProjectVO;
|
||||||
|
import org.springblade.openapi.ocr.pojo.vo.Api4OCRResultVO;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author bfhuange
|
||||||
|
* @since 2025/04/22
|
||||||
|
*/
|
||||||
|
public interface IApi4OCR {
|
||||||
|
String API_PREFIX = "/openApi/ocr";
|
||||||
|
String PROJECT_LIST = API_PREFIX + "/queryProjectList";
|
||||||
|
String OCR_RESULT_LIST = API_PREFIX + "/queryOCRResultList";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询项目列表
|
||||||
|
* @param param
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping(PROJECT_LIST)
|
||||||
|
R<PageVO<Api4OCRProjectVO>> queryProjectList(@RequestBody(required = false) Api4OCRProjectDTO param);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询凭证ocr识别结果列表
|
||||||
|
* @param param
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping(OCR_RESULT_LIST)
|
||||||
|
R<PageVO<Api4OCRResultVO>> queryOCRResultList(@RequestBody(required = false) Api4OCRResultDTO param);
|
||||||
|
}
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
package org.springblade.openapi.ocr.pojo.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author bfhuange
|
||||||
|
* @since 2025/4/22
|
||||||
|
*/
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@Data
|
||||||
|
@Schema(description = "提供给ocr的项目信息")
|
||||||
|
public class Api4OCRProjectDTO extends PageQuery {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
/**
|
||||||
|
* 关键字,项目名称或简称模糊查询
|
||||||
|
*/
|
||||||
|
@Schema(description = "关键字,项目名称或简称模糊查询")
|
||||||
|
private String keyword;
|
||||||
|
}
|
||||||
+62
@@ -0,0 +1,62 @@
|
|||||||
|
package org.springblade.openapi.ocr.pojo.dto;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import org.springblade.core.tool.utils.DateUtil;
|
||||||
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author bfhuange
|
||||||
|
* @since 2025/4/22
|
||||||
|
*/
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@Data
|
||||||
|
@Schema(description = "凭证ocr结果查询参数")
|
||||||
|
public class Api4OCRResultDTO extends PageQuery {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
/**
|
||||||
|
* 项目id
|
||||||
|
*/
|
||||||
|
@Schema(description = "项目id,项目id、凭证批次号、凭证号至少一个不为空")
|
||||||
|
private Long projectId;
|
||||||
|
/**
|
||||||
|
* 凭证批次号
|
||||||
|
*/
|
||||||
|
@Schema(description = "凭证批次号,项目id、凭证批次号、凭证号至少一个不为空")
|
||||||
|
private String batchCode;
|
||||||
|
/**
|
||||||
|
* 凭证号
|
||||||
|
*/
|
||||||
|
@Schema(description = "凭证号,项目id、凭证批次号、凭证号至少一个不为空")
|
||||||
|
private String detailCode;
|
||||||
|
/**
|
||||||
|
* 是否类型错误(1 否 2 是)
|
||||||
|
*/
|
||||||
|
@Schema(description = "是否类型错误(1 否 2 是)")
|
||||||
|
private Integer isTypeError;
|
||||||
|
/**
|
||||||
|
* 是否数据错误(1 否 2 是)
|
||||||
|
*/
|
||||||
|
@Schema(description = "是否数据错误(1 否 2 是)")
|
||||||
|
private Integer isDataError;
|
||||||
|
/**
|
||||||
|
* 创建时间始
|
||||||
|
*/
|
||||||
|
@DateTimeFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@JsonFormat(pattern = DateUtil.PATTERN_DATETIME, timezone = "GMT+8")
|
||||||
|
@Schema(description = "创建时间始,格式yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date createTimeStart;
|
||||||
|
/**
|
||||||
|
* 创建时间至
|
||||||
|
*/
|
||||||
|
@DateTimeFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@JsonFormat(pattern = DateUtil.PATTERN_DATETIME, timezone = "GMT+8")
|
||||||
|
@Schema(description = "创建时间至,格式yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date createTimeEnd;
|
||||||
|
}
|
||||||
+41
@@ -0,0 +1,41 @@
|
|||||||
|
package org.springblade.openapi.ocr.pojo.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询参数
|
||||||
|
* @author bfhuange
|
||||||
|
* @since 2025/4/22
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "分页查询参数")
|
||||||
|
public class PageQuery implements Serializable {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
/**
|
||||||
|
* 每页最大数量 200
|
||||||
|
*/
|
||||||
|
public static final int MAX_SIZE = 200;
|
||||||
|
/**
|
||||||
|
* 当前页
|
||||||
|
*/
|
||||||
|
@Schema(description = "当前页,默认1")
|
||||||
|
protected Integer current = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 每页的数量
|
||||||
|
*/
|
||||||
|
@Schema(description = "每页的数量,默认10,超过200会修改为200")
|
||||||
|
protected Integer size = 10;
|
||||||
|
|
||||||
|
public void setSize(Integer size) {
|
||||||
|
if (size != null && size > MAX_SIZE) {
|
||||||
|
size = MAX_SIZE;
|
||||||
|
}
|
||||||
|
this.size = size;
|
||||||
|
}
|
||||||
|
}
|
||||||
+56
@@ -0,0 +1,56 @@
|
|||||||
|
package org.springblade.openapi.ocr.pojo.vo;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
|
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springblade.core.tool.utils.DateUtil;
|
||||||
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author bfhuange
|
||||||
|
* @since 2025/4/22
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "提供给ocr的项目信息")
|
||||||
|
public class Api4OCRProjectVO implements Serializable {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
/**
|
||||||
|
* 项目id
|
||||||
|
*/
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
@Schema(description = "项目id")
|
||||||
|
private Long id;
|
||||||
|
/**
|
||||||
|
* 项目名称
|
||||||
|
*/
|
||||||
|
@Schema(description = "项目名称")
|
||||||
|
private String name;
|
||||||
|
/**
|
||||||
|
* 项目简称
|
||||||
|
*/
|
||||||
|
@Schema(description = "项目简称")
|
||||||
|
private String abbreviation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@DateTimeFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@JsonFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@Schema(description = "创建时间")
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@DateTimeFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@JsonFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@Schema(description = "更新时间")
|
||||||
|
private Date updateTime;
|
||||||
|
}
|
||||||
+103
@@ -0,0 +1,103 @@
|
|||||||
|
package org.springblade.openapi.ocr.pojo.vo;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
|
import com.fasterxml.jackson.databind.ser.std.NullSerializer;
|
||||||
|
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springblade.core.tool.utils.DateUtil;
|
||||||
|
import org.springblade.openapi.ocr.pojo.vo.result.AttachmentType;
|
||||||
|
import org.springblade.openapi.ocr.pojo.vo.result.ErrorAttachmentType;
|
||||||
|
import org.springblade.openapi.ocr.pojo.vo.result.ErrorField;
|
||||||
|
import org.springblade.openapi.ocr.pojo.vo.result.TransportCertificateBatchDetailData;
|
||||||
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author bfhuange
|
||||||
|
* @since 2025/4/22
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "凭证ocr结果")
|
||||||
|
public class Api4OCRResultVO implements Serializable {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主键
|
||||||
|
*/
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class, nullsUsing = NullSerializer.class)
|
||||||
|
@Schema(description = "主键")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 凭证号
|
||||||
|
*/
|
||||||
|
@Schema(description = "凭证号")
|
||||||
|
private String code;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 识别类型(batch 批量识别,mixed 单张识别合并)
|
||||||
|
*/
|
||||||
|
@Schema(description = "识别类型(batch 批量识别,mixed 单张识别合并)")
|
||||||
|
private String identifyType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否类型错误(1 否 2 是)
|
||||||
|
*/
|
||||||
|
@JsonSerialize(nullsUsing = NullSerializer.class)
|
||||||
|
@Schema(description = "是否类型错误(1 否 2 是)")
|
||||||
|
private Integer isTypeError;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否数据错误(1 否 2 是)
|
||||||
|
*/
|
||||||
|
@JsonSerialize(nullsUsing = NullSerializer.class)
|
||||||
|
@Schema(description = "是否数据错误(1 否 2 是)")
|
||||||
|
private Integer isDataError;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 附件类型列表
|
||||||
|
*/
|
||||||
|
@Schema(description = "附件类型列表")
|
||||||
|
private List<AttachmentType> attachmentTypeList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据
|
||||||
|
*/
|
||||||
|
@Schema(description = "数据")
|
||||||
|
private TransportCertificateBatchDetailData data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误类型列表
|
||||||
|
*/
|
||||||
|
@Schema(description = "错误类型列表")
|
||||||
|
private List<ErrorAttachmentType> errorTypeList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误字段列表
|
||||||
|
*/
|
||||||
|
@Schema(description = "错误字段列表")
|
||||||
|
private List<ErrorField> errorFieldList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误字段列表
|
||||||
|
*/
|
||||||
|
@DateTimeFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@JsonFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@Schema(description = "创建时间")
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@DateTimeFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@JsonFormat(pattern = DateUtil.PATTERN_DATETIME)
|
||||||
|
@Schema(description = "更新时间")
|
||||||
|
private Date updateTime;
|
||||||
|
}
|
||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
package org.springblade.openapi.ocr.pojo.vo.result;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
|
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 附件类型
|
||||||
|
* @author bfhuange
|
||||||
|
* @since 2025/4/18
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "附件类型")
|
||||||
|
public class AttachmentType implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
/**
|
||||||
|
* 附件id
|
||||||
|
*/
|
||||||
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
|
@Schema(description = "附件id")
|
||||||
|
private Long attachmentId;
|
||||||
|
/**
|
||||||
|
* ocr附件类型
|
||||||
|
*/
|
||||||
|
@Schema(description = "ocr附件类型")
|
||||||
|
private String ocrType;
|
||||||
|
/**
|
||||||
|
* 附件url
|
||||||
|
*/
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||||
|
@Schema(description = "附件url")
|
||||||
|
private String url;
|
||||||
|
}
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
package org.springblade.openapi.ocr.pojo.vo.result;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误附件类型
|
||||||
|
* @author bfhuange
|
||||||
|
* @since 2025/4/21
|
||||||
|
*/
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@Data
|
||||||
|
@Schema(description = "错误附件类型")
|
||||||
|
public class ErrorAttachmentType extends AttachmentType {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
/**
|
||||||
|
* erp附件类型
|
||||||
|
*/
|
||||||
|
@Schema(description = "erp附件类型")
|
||||||
|
private String erpType;
|
||||||
|
}
|
||||||
+45
@@ -0,0 +1,45 @@
|
|||||||
|
package org.springblade.openapi.ocr.pojo.vo.result;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误字段
|
||||||
|
* @author bfhuange
|
||||||
|
* @since 2025/4/21
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "错误字段")
|
||||||
|
public class ErrorField implements Serializable {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
/**
|
||||||
|
* 字段名
|
||||||
|
*/
|
||||||
|
@Schema(description = "字段名")
|
||||||
|
private String filed;
|
||||||
|
/**
|
||||||
|
* ocr类型
|
||||||
|
*/
|
||||||
|
@Schema(description = "ocr类型")
|
||||||
|
private String ocrType;
|
||||||
|
/**
|
||||||
|
* ocr字段值
|
||||||
|
*/
|
||||||
|
@Schema(description = "ocr字段值")
|
||||||
|
private String ocrValue;
|
||||||
|
/**
|
||||||
|
* erp字段值
|
||||||
|
*/
|
||||||
|
@Schema(description = "erp字段值")
|
||||||
|
private String erpValue;
|
||||||
|
/**
|
||||||
|
* 附件列表
|
||||||
|
*/
|
||||||
|
@Schema(description = "附件列表")
|
||||||
|
private List<AttachmentType> attachmentList;
|
||||||
|
}
|
||||||
+94
@@ -0,0 +1,94 @@
|
|||||||
|
package org.springblade.openapi.ocr.pojo.vo.result;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
|
import com.fasterxml.jackson.databind.ser.std.NullSerializer;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springblade.core.tool.utils.DateUtil;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 车辆运输凭证批次明细表 实体类
|
||||||
|
*
|
||||||
|
* @author BladeX
|
||||||
|
* @since 2024-10-23
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(description = "凭证数据对象")
|
||||||
|
public class TransportCertificateBatchDetailData implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
/**
|
||||||
|
* 磅单车牌号
|
||||||
|
*/
|
||||||
|
@Schema(description = "磅单车牌号")
|
||||||
|
private String poundPlateNo;
|
||||||
|
/**
|
||||||
|
* 行驶证上的车牌号
|
||||||
|
*/
|
||||||
|
@Schema(description = "行驶证上的车牌号")
|
||||||
|
private String drivingPlateNo;
|
||||||
|
/**
|
||||||
|
* 车头车牌号
|
||||||
|
*/
|
||||||
|
@Schema(description = "车头车牌号")
|
||||||
|
private String carFrontPlateNo;
|
||||||
|
/**
|
||||||
|
* 到货日期
|
||||||
|
*/
|
||||||
|
@JsonFormat(pattern = DateUtil.PATTERN_DATE, timezone = "GMT+8")
|
||||||
|
@Schema(description = "到货日期")
|
||||||
|
private Date supplyMaterialDate;
|
||||||
|
/**
|
||||||
|
* 货物名称
|
||||||
|
*/
|
||||||
|
@Schema(description = "货物名称")
|
||||||
|
private String productName;
|
||||||
|
/**
|
||||||
|
* 规格料型
|
||||||
|
*/
|
||||||
|
@Schema(description = "规格料型")
|
||||||
|
private String specificationMaterial;
|
||||||
|
/**
|
||||||
|
* 毛重(吨)
|
||||||
|
*/
|
||||||
|
@JsonSerialize(nullsUsing = NullSerializer.class)
|
||||||
|
@Schema(description = "毛重(吨)")
|
||||||
|
private BigDecimal grossWeight;
|
||||||
|
/**
|
||||||
|
* 皮重(吨)
|
||||||
|
*/
|
||||||
|
@JsonSerialize(nullsUsing = NullSerializer.class)
|
||||||
|
@Schema(description = "皮重(吨)")
|
||||||
|
private BigDecimal skinWeight;
|
||||||
|
/**
|
||||||
|
* 净重(吨)
|
||||||
|
*/
|
||||||
|
@JsonSerialize(nullsUsing = NullSerializer.class)
|
||||||
|
@Schema(description = "净重(吨)")
|
||||||
|
private BigDecimal netWeight;
|
||||||
|
/**
|
||||||
|
* 扣杂(吨)
|
||||||
|
*/
|
||||||
|
@JsonSerialize(nullsUsing = NullSerializer.class)
|
||||||
|
@Schema(description = "扣杂(吨)")
|
||||||
|
private BigDecimal miscellaneousWeight;
|
||||||
|
/**
|
||||||
|
* 实收(吨)
|
||||||
|
*/
|
||||||
|
@JsonSerialize(nullsUsing = NullSerializer.class)
|
||||||
|
@Schema(description = "实收(吨)")
|
||||||
|
private BigDecimal realityWeight;
|
||||||
|
/**
|
||||||
|
* 驾驶证是否有效(1 否 2 是)
|
||||||
|
*/
|
||||||
|
@JsonSerialize(nullsUsing = NullSerializer.class)
|
||||||
|
@Schema(description = "驾驶证是否有效(1 否 2 是)")
|
||||||
|
private Integer isDriverLicenseValid;
|
||||||
|
}
|
||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
package org.springblade.openapi.track.api;
|
||||||
|
|
||||||
|
import org.springblade.openapi.track.pojo.dto.Api4TrackWaybillDTO;
|
||||||
|
import org.springblade.openapi.track.pojo.vo.Api4TrackResultVO;
|
||||||
|
import org.springblade.openapi.track.pojo.vo.Api4TrackWaybillVO;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author bfhuange
|
||||||
|
* @since 2024/12/16
|
||||||
|
*/
|
||||||
|
public interface IApi4Track {
|
||||||
|
String API_PREFIX = "/openApi/gj";
|
||||||
|
String WAYBILL_LIST = API_PREFIX + "/mycode/zhcx/getZhcx";
|
||||||
|
String WAYBILL_TRACK_UPDATE = API_PREFIX + "/mycode/zhcx/callback";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 轨迹查询运单信息
|
||||||
|
*
|
||||||
|
* @param param@return
|
||||||
|
*/
|
||||||
|
@PostMapping(WAYBILL_LIST)
|
||||||
|
Api4TrackResultVO<List<Api4TrackWaybillVO>> queryWaybillList(@RequestBody Api4TrackWaybillDTO param);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 轨迹更新运单轨迹url和状态
|
||||||
|
* @param waybillId
|
||||||
|
* @param trajectoryId
|
||||||
|
* @param trajectoryStatus
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping(WAYBILL_TRACK_UPDATE)
|
||||||
|
Api4TrackResultVO<Void> updateWaybillTrack(@RequestParam(name = "zhcxId", required = false) String waybillId,
|
||||||
|
@RequestParam(name = "diTuUrl", required = false) String trajectoryId,
|
||||||
|
@RequestParam(name = "guiJiZhuangTai", required = false) String trajectoryStatus);
|
||||||
|
}
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
package org.springblade.openapi.track.constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 轨迹常量
|
||||||
|
* @author bfhuange
|
||||||
|
* @since 2024/12/16
|
||||||
|
*/
|
||||||
|
public class TrackConstant {
|
||||||
|
/**
|
||||||
|
* 响应成功状态码
|
||||||
|
*/
|
||||||
|
public static final Integer SUCCESS_CODE = 200;
|
||||||
|
/**
|
||||||
|
* 响应失败状态码
|
||||||
|
*/
|
||||||
|
public static final Integer FAIL_CODE = 500;
|
||||||
|
}
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
package org.springblade.openapi.track.pojo.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author bfhuange
|
||||||
|
* @since 2024/12/16
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class Api4TrackWaybillDTO implements Serializable {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 运单id列表
|
||||||
|
*/
|
||||||
|
private List<String> ids;
|
||||||
|
}
|
||||||
+33
@@ -0,0 +1,33 @@
|
|||||||
|
package org.springblade.openapi.track.pojo.dto;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author bfhuange
|
||||||
|
* @since 2024/12/16
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class Api4TrackWaybillUpdateDTO implements Serializable {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 运单id
|
||||||
|
*/
|
||||||
|
@JsonProperty("zhcxId")
|
||||||
|
private String waybillId;
|
||||||
|
/**
|
||||||
|
* 轨迹id
|
||||||
|
*/
|
||||||
|
@JsonProperty("diTuUrl")
|
||||||
|
private String trajectoryId;
|
||||||
|
/**
|
||||||
|
* 轨迹状态
|
||||||
|
*/
|
||||||
|
@JsonProperty("guiJiZhuangTai")
|
||||||
|
private String trajectoryStatus;
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user