Compare commits
30 Commits
5037c741a9
...
d5a1e5c157
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5a1e5c157 | ||
|
|
53a0293b7c | ||
|
|
af7f103a38 | ||
|
|
2280d29fb6 | ||
|
|
71e63519ae | ||
|
|
c8dca75943 | ||
|
|
2eb09ff35d | ||
|
|
c1f12dfe5e | ||
|
|
428c9a60b1 | ||
|
|
4efa894c8f | ||
|
|
f02745454d | ||
|
|
d9fa921fda | ||
|
|
b6951a4c6b | ||
|
|
3e5a0a4845 | ||
|
|
49a54e2199 | ||
|
|
e00086c6e8 | ||
|
|
9c99750dd8 | ||
|
|
bd56cb0405 | ||
|
|
c399bdf720 | ||
|
|
1a34cbc678 | ||
|
|
81fb8eea8f | ||
|
|
8423775582 | ||
|
|
aa159638b9 | ||
|
|
2c4f46b6de | ||
|
|
03c76b071a | ||
|
|
a6b87f01a7 | ||
|
|
3312ed328d | ||
|
|
7d74ff7acc | ||
|
|
b7f07ba8da | ||
|
|
3c19722cbd |
288
deployment.yaml
288
deployment.yaml
@@ -44,7 +44,7 @@ spec:
|
||||
cpu: "500m"
|
||||
memory: "1024Mi"
|
||||
limits:
|
||||
cpu: "2048m"
|
||||
cpu: "1024m"
|
||||
memory: "2048Mi"
|
||||
terminationGracePeriodSeconds: 30
|
||||
---
|
||||
@@ -76,7 +76,7 @@ metadata:
|
||||
description: DESC_PLACEHOLDER
|
||||
rollout.kubernetes.io/change-cause: "DESC_PLACEHOLDER:VERSION_PLACEHOLDER"
|
||||
spec:
|
||||
replicas: 1
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: zt-module-infra
|
||||
@@ -111,7 +111,7 @@ spec:
|
||||
cpu: "500m"
|
||||
memory: "1024Mi"
|
||||
limits:
|
||||
cpu: "2560m"
|
||||
cpu: "1024m"
|
||||
memory: "2048Mi"
|
||||
terminationGracePeriodSeconds: 30
|
||||
strategy:
|
||||
@@ -183,7 +183,7 @@ spec:
|
||||
cpu: "500m"
|
||||
memory: "1024Mi"
|
||||
limits:
|
||||
cpu: "2048m"
|
||||
cpu: "1024m"
|
||||
memory: "2048Mi"
|
||||
terminationGracePeriodSeconds: 30
|
||||
strategy:
|
||||
@@ -208,148 +208,148 @@ spec:
|
||||
nodePort: 30091
|
||||
---
|
||||
# zt-module-bpm
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
namespace: ns-d6a0e78ebd674c279614498e4c57b133
|
||||
name: zt-module-bpm
|
||||
labels:
|
||||
app: zt-module-bpm
|
||||
annotations:
|
||||
version: "VERSION_PLACEHOLDER"
|
||||
description: DESC_PLACEHOLDER
|
||||
rollout.kubernetes.io/change-cause: "DESC_PLACEHOLDER:VERSION_PLACEHOLDER"
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: zt-module-bpm
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: zt-module-bpm
|
||||
spec:
|
||||
containers:
|
||||
- name: zt-module-bpm
|
||||
image: 172.16.46.66:10043/zt/zt-module-bpm:VERSION_PLACEHOLDER
|
||||
imagePullPolicy: Always
|
||||
env:
|
||||
- name: TZ
|
||||
value: Asia/Shanghai
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /actuator/health
|
||||
port: 48083
|
||||
initialDelaySeconds: 50
|
||||
periodSeconds: 5
|
||||
failureThreshold: 3
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /actuator/health
|
||||
port: 48083
|
||||
initialDelaySeconds: 50
|
||||
periodSeconds: 10
|
||||
failureThreshold: 5
|
||||
resources:
|
||||
requests:
|
||||
cpu: "500m"
|
||||
memory: "1024Mi"
|
||||
limits:
|
||||
cpu: "2048m"
|
||||
memory: "2048Mi"
|
||||
terminationGracePeriodSeconds: 30
|
||||
strategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
maxSurge: 1
|
||||
maxUnavailable: 0
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
namespace: ns-d6a0e78ebd674c279614498e4c57b133
|
||||
name: zt-module-bpm
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: zt-module-bpm
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 48083
|
||||
targetPort: 48083
|
||||
nodePort: 30093
|
||||
#apiVersion: apps/v1
|
||||
#kind: Deployment
|
||||
#metadata:
|
||||
# namespace: ns-d6a0e78ebd674c279614498e4c57b133
|
||||
# name: zt-module-bpm
|
||||
# labels:
|
||||
# app: zt-module-bpm
|
||||
# annotations:
|
||||
# version: "VERSION_PLACEHOLDER"
|
||||
# description: DESC_PLACEHOLDER
|
||||
# rollout.kubernetes.io/change-cause: "DESC_PLACEHOLDER:VERSION_PLACEHOLDER"
|
||||
#spec:
|
||||
# replicas: 1
|
||||
# selector:
|
||||
# matchLabels:
|
||||
# app: zt-module-bpm
|
||||
# template:
|
||||
# metadata:
|
||||
# labels:
|
||||
# app: zt-module-bpm
|
||||
# spec:
|
||||
# containers:
|
||||
# - name: zt-module-bpm
|
||||
# image: 172.16.46.66:10043/zt/zt-module-bpm:VERSION_PLACEHOLDER
|
||||
# imagePullPolicy: Always
|
||||
# env:
|
||||
# - name: TZ
|
||||
# value: Asia/Shanghai
|
||||
# readinessProbe:
|
||||
# httpGet:
|
||||
# path: /actuator/health
|
||||
# port: 48083
|
||||
# initialDelaySeconds: 50
|
||||
# periodSeconds: 5
|
||||
# failureThreshold: 3
|
||||
# livenessProbe:
|
||||
# httpGet:
|
||||
# path: /actuator/health
|
||||
# port: 48083
|
||||
# initialDelaySeconds: 50
|
||||
# periodSeconds: 10
|
||||
# failureThreshold: 5
|
||||
# resources:
|
||||
# requests:
|
||||
# cpu: "500m"
|
||||
# memory: "1024Mi"
|
||||
# limits:
|
||||
# cpu: "2048m"
|
||||
# memory: "2048Mi"
|
||||
# terminationGracePeriodSeconds: 30
|
||||
# strategy:
|
||||
# type: RollingUpdate
|
||||
# rollingUpdate:
|
||||
# maxSurge: 1
|
||||
# maxUnavailable: 0
|
||||
#---
|
||||
#apiVersion: v1
|
||||
#kind: Service
|
||||
#metadata:
|
||||
# namespace: ns-d6a0e78ebd674c279614498e4c57b133
|
||||
# name: zt-module-bpm
|
||||
#spec:
|
||||
# type: NodePort
|
||||
# selector:
|
||||
# app: zt-module-bpm
|
||||
# ports:
|
||||
# - protocol: TCP
|
||||
# port: 48083
|
||||
# targetPort: 48083
|
||||
# nodePort: 30093
|
||||
---
|
||||
# zt-module-report
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
namespace: ns-d6a0e78ebd674c279614498e4c57b133
|
||||
name: zt-module-report
|
||||
labels:
|
||||
app: zt-module-report
|
||||
annotations:
|
||||
version: "VERSION_PLACEHOLDER"
|
||||
description: DESC_PLACEHOLDER
|
||||
rollout.kubernetes.io/change-cause: "DESC_PLACEHOLDER:VERSION_PLACEHOLDER"
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: zt-module-report
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: zt-module-report
|
||||
spec:
|
||||
containers:
|
||||
- name: zt-module-report
|
||||
image: 172.16.46.66:10043/zt/zt-module-report:VERSION_PLACEHOLDER
|
||||
imagePullPolicy: Always
|
||||
env:
|
||||
- name: TZ
|
||||
value: Asia/Shanghai
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /actuator/health
|
||||
port: 48084
|
||||
initialDelaySeconds: 50
|
||||
periodSeconds: 5
|
||||
failureThreshold: 3
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /actuator/health
|
||||
port: 48084
|
||||
initialDelaySeconds: 50
|
||||
periodSeconds: 10
|
||||
failureThreshold: 5
|
||||
resources:
|
||||
requests:
|
||||
cpu: "500m"
|
||||
memory: "1024Mi"
|
||||
limits:
|
||||
cpu: "2048m"
|
||||
memory: "2048Mi"
|
||||
terminationGracePeriodSeconds: 30
|
||||
strategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
maxSurge: 1
|
||||
maxUnavailable: 0
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
namespace: ns-d6a0e78ebd674c279614498e4c57b133
|
||||
name: zt-module-report
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: zt-module-report
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 48084
|
||||
targetPort: 48084
|
||||
nodePort: 30094
|
||||
#apiVersion: apps/v1
|
||||
#kind: Deployment
|
||||
#metadata:
|
||||
# namespace: ns-d6a0e78ebd674c279614498e4c57b133
|
||||
# name: zt-module-report
|
||||
# labels:
|
||||
# app: zt-module-report
|
||||
# annotations:
|
||||
# version: "VERSION_PLACEHOLDER"
|
||||
# description: DESC_PLACEHOLDER
|
||||
# rollout.kubernetes.io/change-cause: "DESC_PLACEHOLDER:VERSION_PLACEHOLDER"
|
||||
#spec:
|
||||
# replicas: 1
|
||||
# selector:
|
||||
# matchLabels:
|
||||
# app: zt-module-report
|
||||
# template:
|
||||
# metadata:
|
||||
# labels:
|
||||
# app: zt-module-report
|
||||
# spec:
|
||||
# containers:
|
||||
# - name: zt-module-report
|
||||
# image: 172.16.46.66:10043/zt/zt-module-report:VERSION_PLACEHOLDER
|
||||
# imagePullPolicy: Always
|
||||
# env:
|
||||
# - name: TZ
|
||||
# value: Asia/Shanghai
|
||||
# readinessProbe:
|
||||
# httpGet:
|
||||
# path: /actuator/health
|
||||
# port: 48084
|
||||
# initialDelaySeconds: 50
|
||||
# periodSeconds: 5
|
||||
# failureThreshold: 3
|
||||
# livenessProbe:
|
||||
# httpGet:
|
||||
# path: /actuator/health
|
||||
# port: 48084
|
||||
# initialDelaySeconds: 50
|
||||
# periodSeconds: 10
|
||||
# failureThreshold: 5
|
||||
# resources:
|
||||
# requests:
|
||||
# cpu: "500m"
|
||||
# memory: "1024Mi"
|
||||
# limits:
|
||||
# cpu: "2048m"
|
||||
# memory: "2048Mi"
|
||||
# terminationGracePeriodSeconds: 30
|
||||
# strategy:
|
||||
# type: RollingUpdate
|
||||
# rollingUpdate:
|
||||
# maxSurge: 1
|
||||
# maxUnavailable: 0
|
||||
#---
|
||||
#apiVersion: v1
|
||||
#kind: Service
|
||||
#metadata:
|
||||
# namespace: ns-d6a0e78ebd674c279614498e4c57b133
|
||||
# name: zt-module-report
|
||||
#spec:
|
||||
# type: NodePort
|
||||
# selector:
|
||||
# app: zt-module-report
|
||||
# ports:
|
||||
# - protocol: TCP
|
||||
# port: 48084
|
||||
# targetPort: 48084
|
||||
# nodePort: 30094
|
||||
---
|
||||
# zt-module-databus
|
||||
apiVersion: apps/v1
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
<spring.boot.version>3.4.5</spring.boot.version>
|
||||
<spring.cloud.version>2024.0.1</spring.cloud.version>
|
||||
<spring.cloud.alibaba.version>2023.0.3.2</spring.cloud.alibaba.version>
|
||||
<!-- 分布式事务相关 -->
|
||||
<seata.version>2.4.0</seata.version>
|
||||
<!-- Web 相关 -->
|
||||
<springdoc.version>2.8.3</springdoc.version>
|
||||
<knife4j.version>4.6.0</knife4j.version>
|
||||
@@ -86,6 +88,8 @@
|
||||
<netty.version>4.1.116.Final</netty.version>
|
||||
<mqtt.version>1.2.5</mqtt.version>
|
||||
<pf4j-spring.version>0.9.0</pf4j-spring.version>
|
||||
<docx4j.version>11.4.7</docx4j.version>
|
||||
<docx4j-jaxb.version>11.4.7</docx4j-jaxb.version>
|
||||
<!-- 规则引擎 -->
|
||||
<liteflow.version>2.15.1</liteflow.version>
|
||||
<vertx.version>4.5.13</vertx.version>
|
||||
@@ -131,6 +135,20 @@
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- 分布式事务:Seata -->
|
||||
<!-- 显式覆盖 Spring Cloud Alibaba BOM 中的 Seata 1.8.0,升级到 2.4.0 以支持达梦数据库 -->
|
||||
<!-- 注意:Seata 2.2.0+ 改为使用 org.apache.seata groupId -->
|
||||
<dependency>
|
||||
<groupId>org.apache.seata</groupId>
|
||||
<artifactId>seata-all</artifactId>
|
||||
<version>${seata.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.seata</groupId>
|
||||
<artifactId>seata-spring-boot-starter</artifactId>
|
||||
<version>${seata.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 业务组件 -->
|
||||
<dependency>
|
||||
<groupId>io.github.mouzt</groupId>
|
||||
@@ -706,6 +724,17 @@
|
||||
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
|
||||
<version>${mqtt.version}</version>
|
||||
</dependency>
|
||||
<!-- docx4j - Word文档处理 -->
|
||||
<dependency>
|
||||
<groupId>org.docx4j</groupId>
|
||||
<artifactId>docx4j-JAXB-ReferenceImpl</artifactId>
|
||||
<version>${docx4j-jaxb.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.docx4j</groupId>
|
||||
<artifactId>docx4j-core</artifactId>
|
||||
<version>${docx4j.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ public class CompanyDeptInfo {
|
||||
/**
|
||||
* 公司Id
|
||||
*/
|
||||
private Long companyId;
|
||||
private String companyId;
|
||||
/**
|
||||
* 公司名称
|
||||
*/
|
||||
@@ -19,7 +19,7 @@ public class CompanyDeptInfo {
|
||||
/**
|
||||
* 部门Id
|
||||
*/
|
||||
private Long deptId;
|
||||
private String deptId;
|
||||
/**
|
||||
* 部门名称
|
||||
*/
|
||||
|
||||
@@ -81,36 +81,40 @@ public class AsyncLatchUtils {
|
||||
|
||||
System.out.println("主流程开始,准备分发异步任务...");
|
||||
|
||||
System.out.println("主线程id:" + Thread.currentThread().getId());
|
||||
// 2. 提交多个异步任务
|
||||
// 任务一:获取用户信息
|
||||
AsyncLatchUtils.submitTask(executorService, () -> {
|
||||
try {
|
||||
try {
|
||||
System.out.println("任务一子线程id:" + Thread.currentThread().getId());
|
||||
System.out.println("开始获取用户信息...");
|
||||
Thread.sleep(1000); // 模拟耗时
|
||||
System.out.println("获取用户信息成功!");
|
||||
} catch (InterruptedException e) {
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
});
|
||||
|
||||
// 任务二:获取订单信息
|
||||
AsyncLatchUtils.submitTask(executorService, () -> {
|
||||
try {
|
||||
try {
|
||||
System.out.println("任务二子线程id:" + Thread.currentThread().getId());
|
||||
System.out.println("开始获取订单信息...");
|
||||
Thread.sleep(1500); // 模拟耗时
|
||||
System.out.println("获取订单信息成功!");
|
||||
} catch (InterruptedException e) {
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
});
|
||||
|
||||
// 任务三:获取商品信息
|
||||
AsyncLatchUtils.submitTask(executorService, () -> {
|
||||
try {
|
||||
try {
|
||||
System.out.println("任务三子线程id:" + Thread.currentThread().getId());
|
||||
System.out.println("开始获取商品信息...");
|
||||
Thread.sleep(500); // 模拟耗时
|
||||
System.out.println("获取商品信息成功!");
|
||||
} catch (InterruptedException e) {
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
});
|
||||
@@ -118,12 +122,12 @@ public class AsyncLatchUtils {
|
||||
System.out.println("所有异步任务已提交,主线程开始等待...");
|
||||
|
||||
// 3. 等待所有任务完成,最长等待5秒
|
||||
boolean allTasksCompleted = AsyncLatchUtils.waitFor(5, TimeUnit.SECONDS);
|
||||
boolean allTasksCompleted = AsyncLatchUtils.waitFor(5, TimeUnit.SECONDS);
|
||||
|
||||
// 4. 根据等待结果继续主流程
|
||||
if (allTasksCompleted) {
|
||||
System.out.println("所有异步任务执行成功,主流程继续...");
|
||||
} else {
|
||||
} else {
|
||||
System.err.println("有任务执行超时,主流程中断!");
|
||||
}
|
||||
|
||||
|
||||
@@ -19,11 +19,11 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
public class ZtBusinessAutoConfiguration implements WebMvcConfigurer {
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
// 只拦截增删改和 set 相关的 url
|
||||
// 拦截所有 url,统一进行业务与文件上传请求头校验
|
||||
registry.addInterceptor(new BusinessHeaderInterceptor())
|
||||
.addPathPatterns("/**/add**", "/**/create**", "/**/update**", "/**/edit**", "/**/set**");
|
||||
.addPathPatterns("/**");
|
||||
registry.addInterceptor(new FileUploadHeaderInterceptor())
|
||||
.addPathPatterns("/**/add**", "/**/create**", "/**/update**", "/**/edit**", "/**/set**");
|
||||
.addPathPatterns("/**");
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -2,19 +2,20 @@ package com.zt.plat.framework.business.core.util;
|
||||
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||
import com.zt.plat.framework.common.pojo.CompanyDeptInfo;
|
||||
import com.zt.plat.framework.common.util.json.JsonUtils;
|
||||
import com.zt.plat.framework.common.util.spring.SpringUtils;
|
||||
import com.zt.plat.framework.security.core.LoginUser;
|
||||
import com.zt.plat.framework.tenant.core.context.CompanyContextHolder;
|
||||
import com.zt.plat.framework.web.core.util.WebFrameworkUtils;
|
||||
import com.zt.plat.module.system.api.dept.DeptApi;
|
||||
import com.zt.plat.module.system.api.dept.dto.CompanyDeptInfoRespDTO;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.zt.plat.framework.common.util.collection.CollectionUtils.singleton;
|
||||
@@ -23,7 +24,10 @@ import static com.zt.plat.framework.security.core.util.SecurityFrameworkUtils.ge
|
||||
/**
|
||||
* @author chenbowen
|
||||
*/
|
||||
@Slf4j
|
||||
public class BusinessDeptHandleUtil {
|
||||
private static final String CONTEXT_KEY_COMPANY_DEPT_INFOS = "companyDeptInfos";
|
||||
|
||||
public static Set<CompanyDeptInfo> getBelongCompanyAndDept(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
response.setContentType("application/json;charset=UTF-8");
|
||||
String companyIdHeader = request.getHeader(WebFrameworkUtils.HEADER_VISIT_COMPANY_ID);
|
||||
@@ -37,21 +41,19 @@ public class BusinessDeptHandleUtil {
|
||||
currentLoginUser.setInfo(extraInfo);
|
||||
}
|
||||
|
||||
Set<CompanyDeptInfo> companyDeptSet = JSONUtil.parseArray(extraInfo.getOrDefault(LoginUser.INFO_KEY_COMPANY_DEPT_SET, "[]")).stream()
|
||||
.map(obj -> JSONUtil.toBean((JSONObject) obj, CompanyDeptInfo.class))
|
||||
.collect(Collectors.toSet());
|
||||
Set<CompanyDeptInfo> companyDeptSet = resolveCompanyDeptInfos(currentLoginUser, extraInfo);
|
||||
|
||||
// 1. 有 companyId
|
||||
if (companyIdHeader != null && !companyIdHeader.isBlank()) {
|
||||
// 根据请求头中的公司 ID 过滤出当前用户的公司部门信息
|
||||
Set<CompanyDeptInfo> companyDeptSetByCompanyId = companyDeptSet.stream()
|
||||
.filter(companyDeptInfo -> companyDeptInfo.getCompanyId().toString().equals(companyIdHeader))
|
||||
.filter(companyDeptInfo -> companyDeptInfo.getCompanyId().equals(companyIdHeader))
|
||||
.collect(Collectors.toSet());
|
||||
if (companyDeptSetByCompanyId.isEmpty()) {
|
||||
// 当前公司下没有部门
|
||||
CompanyDeptInfo data = new CompanyDeptInfo();
|
||||
data.setCompanyId(Long.valueOf(companyIdHeader));
|
||||
data.setDeptId(0L);
|
||||
data.setCompanyId(companyIdHeader);
|
||||
data.setDeptId("0");
|
||||
return new HashSet<>(singleton(data));
|
||||
}
|
||||
// 如果有 deptId,校验其是否属于该 companyId
|
||||
@@ -84,15 +86,103 @@ public class BusinessDeptHandleUtil {
|
||||
return companyDeptSet;
|
||||
}
|
||||
|
||||
private static Set<CompanyDeptInfo> resolveCompanyDeptInfos(LoginUser loginUser, Map<String, String> extraInfo) {
|
||||
if (loginUser == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
Set<CompanyDeptInfo> cached = loginUser.getContext(CONTEXT_KEY_COMPANY_DEPT_INFOS, Set.class);
|
||||
if (cached != null) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
Set<CompanyDeptInfo> resolved = parseFromInfo(extraInfo);
|
||||
if (resolved == null || resolved.isEmpty()) {
|
||||
Set<CompanyDeptInfo> fetched = fetchCompanyDeptInfos(loginUser.getId());
|
||||
if (!fetched.isEmpty()) {
|
||||
resolved = fetched;
|
||||
} else if (resolved == null) {
|
||||
resolved = Collections.emptySet();
|
||||
}
|
||||
}
|
||||
|
||||
cacheCompanyDeptInfos(loginUser, extraInfo, resolved);
|
||||
return resolved;
|
||||
}
|
||||
|
||||
private static Set<CompanyDeptInfo> parseFromInfo(Map<String, String> extraInfo) {
|
||||
if (extraInfo == null || !extraInfo.containsKey(LoginUser.INFO_KEY_COMPANY_DEPT_SET)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return JSONUtil.parseArray(extraInfo.getOrDefault(LoginUser.INFO_KEY_COMPANY_DEPT_SET, "[]")).stream()
|
||||
.map(obj -> JSONUtil.toBean((JSONObject) obj, CompanyDeptInfo.class))
|
||||
.collect(Collectors.toCollection(LinkedHashSet::new));
|
||||
} catch (Exception ex) {
|
||||
log.warn("[parseFromInfo][解析公司部门信息失败] raw={}", extraInfo.get(LoginUser.INFO_KEY_COMPANY_DEPT_SET), ex);
|
||||
return Collections.emptySet();
|
||||
}
|
||||
}
|
||||
|
||||
private static Set<CompanyDeptInfo> fetchCompanyDeptInfos(Long userId) {
|
||||
if (userId == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
try {
|
||||
DeptApi deptApi = SpringUtils.getBean(DeptApi.class);
|
||||
CommonResult<Set<CompanyDeptInfoRespDTO>> result = deptApi.getCompanyDeptInfoListByUserId(userId);
|
||||
if (result == null || !result.isSuccess() || result.getData() == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
return result.getData().stream()
|
||||
.map(BusinessDeptHandleUtil::convert)
|
||||
.collect(Collectors.toCollection(LinkedHashSet::new));
|
||||
} catch (Exception ex) {
|
||||
log.warn("[fetchCompanyDeptInfos][userId({}) 获取公司部门信息失败]", userId, ex);
|
||||
return Collections.emptySet();
|
||||
}
|
||||
}
|
||||
|
||||
private static void cacheCompanyDeptInfos(LoginUser loginUser, Map<String, String> extraInfo, Set<CompanyDeptInfo> infos) {
|
||||
if (infos == null) {
|
||||
infos = Collections.emptySet();
|
||||
}
|
||||
loginUser.setContext(CONTEXT_KEY_COMPANY_DEPT_INFOS, infos);
|
||||
if (extraInfo == null) {
|
||||
return;
|
||||
}
|
||||
Set<String> companyIds = infos.stream()
|
||||
.map(CompanyDeptInfo::getCompanyId)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toCollection(LinkedHashSet::new));
|
||||
Set<String> deptIds = infos.stream()
|
||||
.map(CompanyDeptInfo::getDeptId)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toCollection(LinkedHashSet::new));
|
||||
extraInfo.put(LoginUser.INFO_KEY_COMPANY_DEPT_SET, JsonUtils.toJsonString(infos));
|
||||
extraInfo.put(LoginUser.INFO_KEY_COMPANY_IDS, JsonUtils.toJsonString(companyIds));
|
||||
extraInfo.put(LoginUser.INFO_KEY_DEPT_IDS, JsonUtils.toJsonString(deptIds));
|
||||
}
|
||||
|
||||
private static CompanyDeptInfo convert(CompanyDeptInfoRespDTO dto) {
|
||||
CompanyDeptInfo info = new CompanyDeptInfo();
|
||||
info.setCompanyId(String.valueOf(dto.getCompanyId()));
|
||||
info.setCompanyName(dto.getCompanyName());
|
||||
info.setCompanyCode(dto.getCompanyCode());
|
||||
info.setDeptId(String.valueOf(dto.getDeptId()));
|
||||
info.setDeptName(dto.getDeptName());
|
||||
info.setDeptCode(dto.getDeptCode());
|
||||
return info;
|
||||
}
|
||||
|
||||
private static boolean applyAutoSelection(LoginUser loginUser, HttpServletRequest request, CompanyDeptInfo info) {
|
||||
if (info == null || info.getCompanyId() == null || info.getCompanyId() <= 0
|
||||
|| info.getDeptId() == null || info.getDeptId() <= 0) {
|
||||
if (info == null || info.getCompanyId() == null || "0".equals(info.getCompanyId())
|
||||
|| info.getDeptId() == null || "0".equals(info.getDeptId())) {
|
||||
return false;
|
||||
}
|
||||
if (loginUser != null) {
|
||||
loginUser.setVisitCompanyId(info.getCompanyId());
|
||||
loginUser.setVisitCompanyId(Long.valueOf(info.getCompanyId()));
|
||||
loginUser.setVisitCompanyName(info.getCompanyName());
|
||||
loginUser.setVisitDeptId(info.getDeptId());
|
||||
loginUser.setVisitDeptId(Long.valueOf(info.getDeptId()));
|
||||
loginUser.setVisitDeptName(info.getDeptName());
|
||||
}
|
||||
request.setAttribute(WebFrameworkUtils.HEADER_VISIT_COMPANY_ID, info.getCompanyId());
|
||||
@@ -104,7 +194,7 @@ public class BusinessDeptHandleUtil {
|
||||
request.setAttribute(WebFrameworkUtils.HEADER_VISIT_DEPT_NAME, info.getDeptName());
|
||||
}
|
||||
CompanyContextHolder.setIgnore(false);
|
||||
CompanyContextHolder.setCompanyId(info.getCompanyId());
|
||||
CompanyContextHolder.setCompanyId(Long.valueOf(info.getCompanyId()));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,11 +107,11 @@ class BusinessHeaderInterceptorTest {
|
||||
|
||||
// 构造 loginUser,包含多个公司部门
|
||||
CompanyDeptInfo deptInfo1 = new CompanyDeptInfo();
|
||||
deptInfo1.setCompanyId(1L);
|
||||
deptInfo1.setDeptId(2L);
|
||||
deptInfo1.setCompanyId(String.valueOf(1L));
|
||||
deptInfo1.setDeptId(String.valueOf(2L));
|
||||
CompanyDeptInfo deptInfo2 = new CompanyDeptInfo();
|
||||
deptInfo2.setCompanyId(2L);
|
||||
deptInfo2.setDeptId(3L);
|
||||
deptInfo2.setCompanyId(String.valueOf(2L));
|
||||
deptInfo2.setDeptId(String.valueOf(3L));
|
||||
Set<CompanyDeptInfo> deptSet = new HashSet<>();
|
||||
deptSet.add(deptInfo1);
|
||||
deptSet.add(deptInfo2);
|
||||
@@ -141,8 +141,8 @@ class BusinessHeaderInterceptorTest {
|
||||
|
||||
// 构造 loginUser,只有一个公司且公司下只有一个部门
|
||||
CompanyDeptInfo deptInfo = new CompanyDeptInfo();
|
||||
deptInfo.setCompanyId(100L);
|
||||
deptInfo.setDeptId(200L);
|
||||
deptInfo.setCompanyId(String.valueOf(100L));
|
||||
deptInfo.setDeptId(String.valueOf(200L));
|
||||
Set<CompanyDeptInfo> deptSet = new HashSet<>();
|
||||
deptSet.add(deptInfo);
|
||||
LoginUser loginUser = randomPojo(LoginUser.class, o -> o.setId(1L)
|
||||
@@ -155,9 +155,9 @@ class BusinessHeaderInterceptorTest {
|
||||
setLoginUserForTest(loginUser);
|
||||
|
||||
boolean result = interceptor.preHandle(request, response, handlerMethod);
|
||||
assertFalse(result);
|
||||
// 可选:verify(request).setAttribute("visit-company-id", String.valueOf(deptInfo.getCompanyId()));
|
||||
// 可选:verify(request).setAttribute("visit-dept-id", String.valueOf(deptInfo.getDeptId()));
|
||||
assertTrue(result);
|
||||
verify(request).setAttribute(eq("visit-company-id"), eq(deptInfo.getCompanyId()));
|
||||
verify(request).setAttribute(eq("visit-dept-id"), eq(deptInfo.getDeptId()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -172,11 +172,11 @@ class BusinessHeaderInterceptorTest {
|
||||
|
||||
// 构造 loginUser,多个公司部门
|
||||
CompanyDeptInfo deptInfo1 = new CompanyDeptInfo();
|
||||
deptInfo1.setCompanyId(1L);
|
||||
deptInfo1.setDeptId(2L);
|
||||
deptInfo1.setCompanyId(String.valueOf(1L));
|
||||
deptInfo1.setDeptId(String.valueOf(2L));
|
||||
CompanyDeptInfo deptInfo2 = new CompanyDeptInfo();
|
||||
deptInfo2.setCompanyId(2L);
|
||||
deptInfo2.setDeptId(3L);
|
||||
deptInfo2.setCompanyId(String.valueOf(2L));
|
||||
deptInfo2.setDeptId(String.valueOf(3L));
|
||||
Set<CompanyDeptInfo> deptSet = new HashSet<>();
|
||||
deptSet.add(deptInfo1);
|
||||
deptSet.add(deptInfo2);
|
||||
@@ -207,11 +207,11 @@ class BusinessHeaderInterceptorTest {
|
||||
|
||||
// 构造 loginUser,只有其他公司部门
|
||||
CompanyDeptInfo deptInfo1 = new CompanyDeptInfo();
|
||||
deptInfo1.setCompanyId(1L);
|
||||
deptInfo1.setDeptId(2L);
|
||||
deptInfo1.setCompanyId(String.valueOf(1L));
|
||||
deptInfo1.setDeptId(String.valueOf(2L));
|
||||
CompanyDeptInfo deptInfo2 = new CompanyDeptInfo();
|
||||
deptInfo2.setCompanyId(2L);
|
||||
deptInfo2.setDeptId(3L);
|
||||
deptInfo2.setCompanyId(String.valueOf(2L));
|
||||
deptInfo2.setDeptId(String.valueOf(3L));
|
||||
Set<CompanyDeptInfo> deptSet = new HashSet<>();
|
||||
deptSet.add(deptInfo1);
|
||||
deptSet.add(deptInfo2);
|
||||
|
||||
@@ -29,6 +29,9 @@ import java.time.Duration;
|
||||
* @author ZT
|
||||
*/
|
||||
public class S3FileClient extends AbstractFileClient<S3FileClientConfig> {
|
||||
|
||||
private static final Duration DEFAULT_PRESIGNED_EXPIRATION = Duration.ofHours(24);
|
||||
private static final String PRESIGN_EXPIRE_SECONDS_PROPERTY = "zt.file.download-expire-seconds";
|
||||
/**
|
||||
* 生成临时下载地址(预签名下载 URL)
|
||||
* @param path 文件路径
|
||||
@@ -37,17 +40,7 @@ public class S3FileClient extends AbstractFileClient<S3FileClientConfig> {
|
||||
*/
|
||||
@Override
|
||||
public String getPresignedDownloadUrl(String path, Duration expiration) {
|
||||
Duration realExpiration = expiration;
|
||||
if (realExpiration == null) {
|
||||
long expireSeconds = 30; // 默认 30 秒
|
||||
try {
|
||||
String val = SpringUtils.getProperty("zt.file.download-expire-seconds");
|
||||
if (val != null && !val.isEmpty()) {
|
||||
expireSeconds = Long.parseLong(val);
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
realExpiration = Duration.ofSeconds(expireSeconds);
|
||||
}
|
||||
Duration realExpiration = expiration != null ? expiration : resolveDefaultExpiration();
|
||||
if (path == null){
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
@@ -135,10 +128,25 @@ public class S3FileClient extends AbstractFileClient<S3FileClientConfig> {
|
||||
|
||||
@Override
|
||||
public FilePresignedUrlRespDTO getPresignedObjectUrl(String path) {
|
||||
Duration expiration = Duration.ofHours(24);
|
||||
Duration expiration = resolveDefaultExpiration();
|
||||
return new FilePresignedUrlRespDTO(getPresignedUrl(path, expiration), config.getDomain() + "/" + path);
|
||||
}
|
||||
|
||||
private Duration resolveDefaultExpiration() {
|
||||
String propertyValue = SpringUtils.getProperty(PRESIGN_EXPIRE_SECONDS_PROPERTY);
|
||||
if (StringUtils.isNotEmpty(propertyValue)) {
|
||||
try {
|
||||
long seconds = Long.parseLong(propertyValue);
|
||||
if (seconds > 0) {
|
||||
return Duration.ofSeconds(seconds);
|
||||
}
|
||||
} catch (NumberFormatException ignored) {
|
||||
// ignore invalid config values and fall back to default
|
||||
}
|
||||
}
|
||||
return DEFAULT_PRESIGNED_EXPIRATION;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成动态的预签名上传 URL
|
||||
*
|
||||
|
||||
@@ -148,6 +148,8 @@ zt:
|
||||
key: "0123456789abcdef0123456789abcdef"
|
||||
# 附件预览
|
||||
kkfile: "http://172.16.46.63:30012/onlinePreview?url="
|
||||
file:
|
||||
download-expire-seconds: 86400 # 对象存储预签名地址默认有效期(秒)
|
||||
info:
|
||||
version: 1.0.0
|
||||
base-package: com.zt.plat.module.infra
|
||||
|
||||
@@ -212,4 +212,7 @@ public interface ErrorCodeConstants {
|
||||
// ========== 系统序列号记录 1-002-032-000 ==========
|
||||
ErrorCode SEQUENCE_RECORD_NOT_EXISTS = new ErrorCode(1_002_032_000, "系统序列号记录不存在");
|
||||
|
||||
// ========== 门户网站 1-002-033-000 ==========
|
||||
ErrorCode PORTAL_NOT_EXISTS = new ErrorCode(1_002_033_000, "门户不存在");
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
package com.zt.plat.module.system.controller.admin.portal;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.common.util.object.BeanUtils;
|
||||
import com.zt.plat.framework.excel.core.util.ExcelUtils;
|
||||
import com.zt.plat.module.system.controller.admin.portal.vo.PortalPageReqVO;
|
||||
import com.zt.plat.module.system.controller.admin.portal.vo.PortalRespVO;
|
||||
import com.zt.plat.module.system.controller.admin.portal.vo.PortalSaveReqVO;
|
||||
import com.zt.plat.module.system.dal.dataobject.portal.PortalDO;
|
||||
import com.zt.plat.module.system.service.portal.PortalService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import static com.zt.plat.framework.common.pojo.CommonResult.success;
|
||||
import static com.zt.plat.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
/**
|
||||
* 门户网站 Controller
|
||||
*
|
||||
* @author 中铜数字供应链平台
|
||||
*/
|
||||
@Tag(name = "管理后台 - 门户网站")
|
||||
@RestController
|
||||
@RequestMapping("/system/portal")
|
||||
@Validated
|
||||
public class PortalController {
|
||||
|
||||
@Resource
|
||||
private PortalService portalService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建门户网站")
|
||||
@PreAuthorize("@ss.hasPermission('system:portal:create')")
|
||||
public CommonResult<Long> createPortal(@Valid @RequestBody PortalSaveReqVO createReqVO) {
|
||||
return success(portalService.createPortal(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新门户网站")
|
||||
@PreAuthorize("@ss.hasPermission('system:portal:update')")
|
||||
public CommonResult<Boolean> updatePortal(@Valid @RequestBody PortalSaveReqVO updateReqVO) {
|
||||
portalService.updatePortal(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除门户网站")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('system:portal:delete')")
|
||||
public CommonResult<Boolean> deletePortal(@RequestParam("id") Long id) {
|
||||
portalService.deletePortal(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得门户网站")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('system:portal:query')")
|
||||
public CommonResult<PortalRespVO> getPortal(@RequestParam("id") Long id) {
|
||||
PortalDO portal = portalService.getPortal(id);
|
||||
return success(BeanUtils.toBean(portal, PortalRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得门户网站分页")
|
||||
@PreAuthorize("@ss.hasPermission('system:portal:query')")
|
||||
public CommonResult<PageResult<PortalRespVO>> getPortalPage(@Valid PortalPageReqVO pageReqVO) {
|
||||
PageResult<PortalDO> pageResult = portalService.getPortalPage(pageReqVO);
|
||||
return success(BeanUtils.toBean(pageResult, PortalRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/export-excel")
|
||||
@Operation(summary = "导出门户网站 Excel")
|
||||
@PreAuthorize("@ss.hasPermission('system:portal:export')")
|
||||
public void exportPortalExcel(@Valid PortalPageReqVO pageReqVO,
|
||||
HttpServletResponse response) throws IOException {
|
||||
List<PortalDO> list = portalService.getPortalPage(pageReqVO).getList();
|
||||
// 导出 Excel
|
||||
List<PortalRespVO> data = BeanUtils.toBean(list, PortalRespVO.class);
|
||||
ExcelUtils.write(response, "门户网站.xls", "数据", PortalRespVO.class, data);
|
||||
}
|
||||
/**
|
||||
* 获取当前用户可访问的门户列表
|
||||
* 此接口无需权限验证,因为已经通过登录验证,
|
||||
* 返回的门户列表已经根据用户权限进行了过滤
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获取我的门户列表")
|
||||
public CommonResult<List<PortalRespVO>> getMyPortalList() {
|
||||
Long userId = getLoginUserId();
|
||||
List<PortalDO> portals = portalService.getPortalListByUserId(userId);
|
||||
return success(BeanUtils.toBean(portals, PortalRespVO.class));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.zt.plat.module.system.controller.admin.portal.vo;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.PageParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
/**
|
||||
* 门户网站分页查询 Request VO
|
||||
*
|
||||
* @author 中铜数字供应链平台
|
||||
*/
|
||||
@Schema(description = "管理后台 - 门户网站分页查询 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class PortalPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "门户名称", example = "采购")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "状态", example = "0")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "门户分类", example = "业务系统")
|
||||
private String category;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.zt.plat.module.system.controller.admin.portal.vo;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 门户网站 Response VO
|
||||
*
|
||||
* @author 中铜数字供应链平台
|
||||
*/
|
||||
@Schema(description = "管理后台 - 门户网站 Response VO")
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
public class PortalRespVO {
|
||||
|
||||
@Schema(description = "门户编号", example = "1024")
|
||||
@ExcelProperty("门户编号")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "门户名称", example = "采购管理系统")
|
||||
@ExcelProperty("门户名称")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "门户描述", example = "统一采购平台,支持在线询价、比价")
|
||||
@ExcelProperty("门户描述")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "门户地址", example = "http://purchase.zt.com")
|
||||
@ExcelProperty("门户地址")
|
||||
private String url;
|
||||
|
||||
@Schema(description = "门户图标(Element Plus图标)", example = "ep:shopping-cart")
|
||||
private String icon;
|
||||
|
||||
@Schema(description = "图标类型", example = "1")
|
||||
@ExcelProperty("图标类型")
|
||||
private Integer iconType;
|
||||
|
||||
@Schema(description = "图标文件ID(当 iconType=2 时使用)", example = "1234567890")
|
||||
private String iconFileId;
|
||||
|
||||
@Schema(description = "图标文件名(当 iconType=2 时使用)", example = "logo.png")
|
||||
private String iconFileName;
|
||||
|
||||
@Schema(description = "门户分类", example = "业务系统")
|
||||
@ExcelProperty("门户分类")
|
||||
private String category;
|
||||
|
||||
@Schema(description = "显示排序", example = "1")
|
||||
@ExcelProperty("显示排序")
|
||||
private Integer sort;
|
||||
|
||||
@Schema(description = "状态", example = "0")
|
||||
@ExcelProperty(value = "状态", converter = com.zt.plat.framework.excel.core.convert.DictConvert.class)
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "权限标识", example = "portal:purchase:access")
|
||||
@ExcelProperty("权限标识")
|
||||
private String permission;
|
||||
|
||||
@Schema(description = "打开方式", example = "1")
|
||||
@ExcelProperty("打开方式")
|
||||
private Integer openType;
|
||||
|
||||
@Schema(description = "认证配置(JSON格式)", example = "{\"enabled\":true,\"type\":\"url_params\",\"urlParams\":[{\"key\":\"token\",\"value\":\"${userToken}\"}]}")
|
||||
private String authConfig;
|
||||
|
||||
@Schema(description = "创建时间", example = "2025-11-10 10:00:00")
|
||||
@ExcelProperty("创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.zt.plat.module.system.controller.admin.portal.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import jakarta.validation.constraints.*;
|
||||
|
||||
/**
|
||||
* 门户网站创建/修改 Request VO
|
||||
*
|
||||
* @author 中铜数字供应链平台
|
||||
*/
|
||||
@Schema(description = "管理后台 - 门户网站创建/修改 Request VO")
|
||||
@Data
|
||||
public class PortalSaveReqVO {
|
||||
|
||||
@Schema(description = "门户编号", example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "门户名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "采购管理系统")
|
||||
@NotEmpty(message = "门户名称不能为空")
|
||||
@Size(max = 50, message = "门户名称长度不能超过50个字符")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "门户描述", example = "统一采购平台,支持在线询价、比价")
|
||||
@Size(max = 200, message = "门户描述长度不能超过200个字符")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "门户地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "http://purchase.zt.com")
|
||||
@NotEmpty(message = "门户地址不能为空")
|
||||
@Size(max = 500, message = "门户地址长度不能超过500个字符")
|
||||
@Pattern(regexp = "^(http|https)://.*", message = "门户地址必须以 http:// 或 https:// 开头")
|
||||
private String url;
|
||||
|
||||
@Schema(description = "门户图标(Element Plus图标)", example = "ep:shopping-cart")
|
||||
@Size(max = 100, message = "门户图标长度不能超过100个字符")
|
||||
private String icon;
|
||||
|
||||
@Schema(description = "图标类型", example = "1")
|
||||
@Min(value = 1, message = "图标类型值不正确")
|
||||
@Max(value = 2, message = "图标类型值不正确")
|
||||
private Integer iconType;
|
||||
|
||||
@Schema(description = "图标文件ID(当 iconType=2 时使用)", example = "1234567890")
|
||||
@Size(max = 64, message = "图标文件ID长度不能超过64个字符")
|
||||
private String iconFileId;
|
||||
|
||||
@Schema(description = "图标文件名(当 iconType=2 时使用)", example = "logo.png")
|
||||
@Size(max = 255, message = "图标文件名长度不能超过255个字符")
|
||||
private String iconFileName;
|
||||
|
||||
@Schema(description = "门户分类", example = "业务系统")
|
||||
@Size(max = 50, message = "门户分类长度不能超过50个字符")
|
||||
private String category;
|
||||
|
||||
@Schema(description = "显示排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "显示排序不能为空")
|
||||
@Min(value = 0, message = "显示排序不能小于0")
|
||||
private Integer sort;
|
||||
|
||||
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
|
||||
@NotNull(message = "状态不能为空")
|
||||
@Min(value = 0, message = "状态值不正确")
|
||||
@Max(value = 1, message = "状态值不正确")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "权限标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "portal:purchase:access")
|
||||
@NotEmpty(message = "权限标识不能为空")
|
||||
@Size(max = 100, message = "权限标识长度不能超过100个字符")
|
||||
@Pattern(regexp = "^portal:[a-z]+:access$", message = "权限标识格式不正确,应为 portal:{module}:access")
|
||||
private String permission;
|
||||
|
||||
@Schema(description = "打开方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "打开方式不能为空")
|
||||
@Min(value = 1, message = "打开方式值不正确")
|
||||
@Max(value = 3, message = "打开方式值不正确")
|
||||
private Integer openType;
|
||||
|
||||
@Schema(description = "父菜单ID(权限自动同步到该菜单下)", example = "2500")
|
||||
private Long parentMenuId;
|
||||
|
||||
@Schema(description = "认证配置(JSON格式)", example = "{\"enabled\":true,\"type\":\"url_params\",\"urlParams\":[{\"key\":\"token\",\"value\":\"${userToken}\"}]}")
|
||||
private String authConfig;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.zt.plat.module.system.controller.app.portal;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||
import com.zt.plat.framework.common.util.object.BeanUtils;
|
||||
import com.zt.plat.module.system.controller.admin.portal.vo.PortalRespVO;
|
||||
import com.zt.plat.module.system.dal.dataobject.portal.PortalDO;
|
||||
import com.zt.plat.module.system.service.portal.PortalService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.zt.plat.framework.common.pojo.CommonResult.success;
|
||||
import static com.zt.plat.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
/**
|
||||
* 用户端 - 门户网站 Controller
|
||||
*
|
||||
* @author 中铜数字供应链平台
|
||||
*/
|
||||
@Tag(name = "用户端 - 门户网站")
|
||||
@RestController
|
||||
@RequestMapping("/system/portal")
|
||||
@Validated
|
||||
public class AppPortalController {
|
||||
|
||||
@Resource
|
||||
private PortalService portalService;
|
||||
|
||||
/**
|
||||
* 获取当前用户可访问的门户列表
|
||||
* 此接口无需权限验证,因为已经通过登录验证,
|
||||
* 返回的门户列表已经根据用户权限进行了过滤
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获取我的门户列表")
|
||||
public CommonResult<List<PortalRespVO>> getMyPortalList() {
|
||||
Long userId = getLoginUserId();
|
||||
List<PortalDO> portals = portalService.getPortalListByUserId(userId);
|
||||
return success(BeanUtils.toBean(portals, PortalRespVO.class));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package com.zt.plat.module.system.dal.dataobject.portal;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.zt.plat.framework.tenant.core.db.TenantBaseDO;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* 门户网站 DO
|
||||
*
|
||||
* @author 中铜数字供应链平台
|
||||
*/
|
||||
@TableName("system_portal")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class PortalDO extends TenantBaseDO {
|
||||
|
||||
/**
|
||||
* 门户编号
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 门户名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 门户描述
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 门户地址
|
||||
*/
|
||||
private String url;
|
||||
|
||||
/**
|
||||
* 门户图标(Element Plus图标,如:ep:shopping-cart)
|
||||
*/
|
||||
private String icon;
|
||||
|
||||
/**
|
||||
* 图标类型
|
||||
*
|
||||
* 1 - 图标库(使用 icon 字段)
|
||||
* 2 - 自定义图片(使用 iconFileId 字段)
|
||||
*/
|
||||
private Integer iconType;
|
||||
|
||||
/**
|
||||
* 图标文件ID(当 iconType=2 时使用)
|
||||
*
|
||||
* 通过文件ID实时获取访问URL,避免URL过期问题
|
||||
*/
|
||||
private String iconFileId;
|
||||
|
||||
/**
|
||||
* 图标文件名(当 iconType=2 时使用)
|
||||
*/
|
||||
private String iconFileName;
|
||||
|
||||
/**
|
||||
* 门户分类
|
||||
*
|
||||
* 枚举 {@link PortalCategoryEnum}
|
||||
*/
|
||||
private String category;
|
||||
|
||||
/**
|
||||
* 显示排序
|
||||
*/
|
||||
private Integer sort;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*
|
||||
* 枚举 {@link com.zt.plat.framework.common.enums.CommonStatusEnum}
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 权限标识
|
||||
*
|
||||
* 格式:portal:{module}:access
|
||||
* 例如:portal:purchase:access
|
||||
*/
|
||||
private String permission;
|
||||
|
||||
/**
|
||||
* 打开方式
|
||||
*
|
||||
* 枚举 {@link PortalOpenTypeEnum}
|
||||
* 1 - 新窗口打开
|
||||
* 2 - 当前窗口打开
|
||||
* 3 - iframe 内嵌
|
||||
*/
|
||||
private Integer openType;
|
||||
|
||||
/**
|
||||
* 认证配置(JSON格式)
|
||||
*
|
||||
* 用于配置跳转时的认证信息(URL参数或请求头)
|
||||
* 示例:{"enabled":true,"type":"url_params","urlParams":[{"key":"token","value":"${userToken}"}]}
|
||||
*/
|
||||
private String authConfig;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.zt.plat.module.system.dal.mysql.portal;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import com.zt.plat.module.system.controller.admin.portal.vo.PortalPageReqVO;
|
||||
import com.zt.plat.module.system.dal.dataobject.portal.PortalDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 门户网站 Mapper
|
||||
*
|
||||
* @author 中铜数字供应链平台
|
||||
*/
|
||||
@Mapper
|
||||
public interface PortalMapper extends BaseMapperX<PortalDO> {
|
||||
|
||||
default PageResult<PortalDO> selectPage(PortalPageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<PortalDO>()
|
||||
.likeIfPresent(PortalDO::getName, reqVO.getName())
|
||||
.eqIfPresent(PortalDO::getStatus, reqVO.getStatus())
|
||||
.eqIfPresent(PortalDO::getCategory, reqVO.getCategory())
|
||||
.betweenIfPresent(PortalDO::getCreateTime, reqVO.getCreateTime())
|
||||
.orderByAsc(PortalDO::getSort)
|
||||
.orderByDesc(PortalDO::getId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据权限标识列表查询门户列表
|
||||
*
|
||||
* @param permissions 权限标识列表
|
||||
* @return 门户列表
|
||||
*/
|
||||
default List<PortalDO> selectListByPermissions(List<String> permissions) {
|
||||
return selectList(new LambdaQueryWrapperX<PortalDO>()
|
||||
.in(PortalDO::getPermission, permissions)
|
||||
.eq(PortalDO::getStatus, 0) // 只查询启用的门户
|
||||
.orderByAsc(PortalDO::getSort)
|
||||
.orderByDesc(PortalDO::getId));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.zt.plat.module.system.enums.portal;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 门户分类枚举
|
||||
*
|
||||
* @author 中铜数字供应链平台
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum PortalCategoryEnum {
|
||||
|
||||
BUSINESS("业务系统", "核心业务系统,如采购、销售、财务等"),
|
||||
MANAGEMENT("管理工具", "管理辅助工具,如OA、数据分析等"),
|
||||
THIRD_PARTY("第三方系统", "外部集成系统,如ERP、E办等");
|
||||
|
||||
/**
|
||||
* 分类名称
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
/**
|
||||
* 分类描述
|
||||
*/
|
||||
private final String description;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.zt.plat.module.system.enums.portal;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 门户打开方式枚举
|
||||
*
|
||||
* @author 中铜数字供应链平台
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum PortalOpenTypeEnum {
|
||||
|
||||
NEW_WINDOW(1, "新窗口", "在新窗口中打开门户"),
|
||||
CURRENT_WINDOW(2, "当前窗口", "在当前窗口中打开门户"),
|
||||
IFRAME(3, "iframe内嵌", "在iframe中内嵌显示门户");
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
private final Integer type;
|
||||
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
private final String description;
|
||||
|
||||
}
|
||||
@@ -504,10 +504,10 @@ public class DeptServiceImpl implements DeptService {
|
||||
DeptDO company = companyId != null ? loadDept(companyId, deptCache) : findTopLevelAncestor(dept);
|
||||
if (company == null) continue;
|
||||
CompanyDeptInfo info = new CompanyDeptInfo();
|
||||
info.setCompanyId(company.getId());
|
||||
info.setCompanyId(String.valueOf(company.getId()));
|
||||
info.setCompanyName(company.getName());
|
||||
info.setCompanyCode(company.getCode());
|
||||
info.setDeptId(dept.getId());
|
||||
info.setDeptId(String.valueOf(dept.getId()));
|
||||
info.setDeptName(dept.getName());
|
||||
info.setDeptCode(dept.getCode());
|
||||
result.add(info);
|
||||
|
||||
@@ -203,9 +203,6 @@ public class OAuth2TokenServiceImpl implements OAuth2TokenService {
|
||||
.put(LoginUser.INFO_KEY_TENANT_ID, user.getTenantId().toString())
|
||||
.put(LoginUser.INFO_KEY_USERNAME, user.getUsername())
|
||||
.put(LoginUser.INFO_KEY_PHONE, user.getMobile())
|
||||
.put(LoginUser.INFO_KEY_COMPANY_IDS, CollUtil.isEmpty(user.getCompanyIds()) ? "[]" : JsonUtils.toJsonString(user.getCompanyIds()))
|
||||
.put(LoginUser.INFO_KEY_DEPT_IDS, CollUtil.isEmpty(user.getDeptIds()) ? "[]" : JsonUtils.toJsonString(user.getDeptIds()))
|
||||
.put(LoginUser.INFO_KEY_COMPANY_DEPT_SET, CollUtil.isEmpty(user.getCompanyDeptInfos()) ? "[]" : JsonUtils.toJsonString(user.getCompanyDeptInfos()))
|
||||
.put(LoginUser.INFO_KEY_POST_IDS, CollUtil.isEmpty(user.getPostIds()) ? "[]" : JsonUtils.toJsonString(user.getPostIds()))
|
||||
.build();
|
||||
} else if (userType.equals(UserTypeEnum.MEMBER.getValue())) {
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.zt.plat.module.system.service.portal;
|
||||
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.module.system.controller.admin.portal.vo.PortalPageReqVO;
|
||||
import com.zt.plat.module.system.controller.admin.portal.vo.PortalSaveReqVO;
|
||||
import com.zt.plat.module.system.dal.dataobject.portal.PortalDO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 门户网站 Service 接口
|
||||
*
|
||||
* @author 中铜数字供应链平台
|
||||
*/
|
||||
public interface PortalService {
|
||||
|
||||
/**
|
||||
* 创建门户
|
||||
*
|
||||
* @param createReqVO 创建信息
|
||||
* @return 编号
|
||||
*/
|
||||
Long createPortal(PortalSaveReqVO createReqVO);
|
||||
|
||||
/**
|
||||
* 更新门户
|
||||
*
|
||||
* @param updateReqVO 更新信息
|
||||
*/
|
||||
void updatePortal(PortalSaveReqVO updateReqVO);
|
||||
|
||||
/**
|
||||
* 删除门户
|
||||
*
|
||||
* @param id 编号
|
||||
*/
|
||||
void deletePortal(Long id);
|
||||
|
||||
/**
|
||||
* 获得门户
|
||||
*
|
||||
* @param id 编号
|
||||
* @return 门户
|
||||
*/
|
||||
PortalDO getPortal(Long id);
|
||||
|
||||
/**
|
||||
* 获得门户分页
|
||||
*
|
||||
* @param pageReqVO 分页查询
|
||||
* @return 门户分页
|
||||
*/
|
||||
PageResult<PortalDO> getPortalPage(PortalPageReqVO pageReqVO);
|
||||
|
||||
/**
|
||||
* 获得用户有权限访问的门户列表
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 门户列表
|
||||
*/
|
||||
List<PortalDO> getPortalListByUserId(Long userId);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
package com.zt.plat.module.system.service.portal;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.zt.plat.framework.common.pojo.PageResult;
|
||||
import com.zt.plat.framework.common.util.object.BeanUtils;
|
||||
import com.zt.plat.module.system.controller.admin.permission.vo.menu.MenuSaveVO;
|
||||
import com.zt.plat.module.system.controller.admin.portal.vo.PortalPageReqVO;
|
||||
import com.zt.plat.module.system.controller.admin.portal.vo.PortalSaveReqVO;
|
||||
import com.zt.plat.module.system.dal.dataobject.permission.MenuDO;
|
||||
import com.zt.plat.module.system.dal.dataobject.portal.PortalDO;
|
||||
import com.zt.plat.module.system.dal.mysql.portal.PortalMapper;
|
||||
import com.zt.plat.module.system.enums.permission.MenuTypeEnum;
|
||||
import com.zt.plat.module.system.service.permission.MenuService;
|
||||
import com.zt.plat.module.system.service.permission.PermissionService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static com.zt.plat.module.system.enums.ErrorCodeConstants.PORTAL_NOT_EXISTS;
|
||||
|
||||
/**
|
||||
* 门户网站 Service 实现类
|
||||
*
|
||||
* @author 中铜数字供应链平台
|
||||
*/
|
||||
@Service
|
||||
public class PortalServiceImpl implements PortalService {
|
||||
|
||||
@Resource
|
||||
private PortalMapper portalMapper;
|
||||
|
||||
@Resource
|
||||
private PermissionService permissionService;
|
||||
|
||||
@Resource
|
||||
private MenuService menuService;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long createPortal(PortalSaveReqVO createReqVO) {
|
||||
// 1. 创建门户
|
||||
PortalDO portal = BeanUtils.toBean(createReqVO, PortalDO.class);
|
||||
portalMapper.insert(portal);
|
||||
|
||||
// 2. 自动创建对应的菜单权限
|
||||
if (createReqVO.getParentMenuId() != null) {
|
||||
syncMenuPermission(portal, createReqVO.getParentMenuId(), null);
|
||||
}
|
||||
|
||||
return portal.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updatePortal(PortalSaveReqVO updateReqVO) {
|
||||
// 1. 校验是否存在
|
||||
PortalDO oldPortal = validatePortalExists(updateReqVO.getId());
|
||||
|
||||
// 2. 更新门户
|
||||
PortalDO updateObj = BeanUtils.toBean(updateReqVO, PortalDO.class);
|
||||
portalMapper.updateById(updateObj);
|
||||
|
||||
// 3. 自动更新对应的菜单权限
|
||||
if (updateReqVO.getParentMenuId() != null) {
|
||||
syncMenuPermission(updateObj, updateReqVO.getParentMenuId(), oldPortal.getPermission());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void deletePortal(Long id) {
|
||||
// 1. 校验是否存在
|
||||
PortalDO portal = validatePortalExists(id);
|
||||
|
||||
// 2. 删除门户
|
||||
portalMapper.deleteById(id);
|
||||
|
||||
// 3. 自动删除对应的菜单权限
|
||||
deleteMenuPermission(portal.getPermission());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PortalDO getPortal(Long id) {
|
||||
return portalMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<PortalDO> getPortalPage(PortalPageReqVO pageReqVO) {
|
||||
return portalMapper.selectPage(pageReqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PortalDO> getPortalListByUserId(Long userId) {
|
||||
// 1. 获取用户的角色ID列表
|
||||
Set<Long> roleIds = permissionService.getUserRoleIdListByUserIdFromCache(userId);
|
||||
if (roleIds.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// 2. 获取角色对应的菜单ID列表
|
||||
Set<Long> menuIds = permissionService.getRoleMenuListByRoleId(roleIds);
|
||||
if (menuIds.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// 3. 获取菜单列表并提取权限标识
|
||||
List<MenuDO> menus = menuService.getMenuList(menuIds);
|
||||
List<String> permissions = menus.stream()
|
||||
.map(MenuDO::getPermission)
|
||||
.filter(permission -> permission != null && !permission.isEmpty())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (permissions.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// 4. 根据权限标识查询门户列表
|
||||
return portalMapper.selectListByPermissions(permissions);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public PortalDO validatePortalExists(Long id) {
|
||||
if (id == null) {
|
||||
return null;
|
||||
}
|
||||
PortalDO portal = portalMapper.selectById(id);
|
||||
if (portal == null) {
|
||||
throw exception(PORTAL_NOT_EXISTS);
|
||||
}
|
||||
return portal;
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步菜单权限
|
||||
* - 如果菜单权限不存在,则创建
|
||||
* - 如果菜单权限已存在,则更新
|
||||
*
|
||||
* @param portal 门户对象
|
||||
* @param parentMenuId 父菜单 ID
|
||||
* @param oldPermission 旧的权限标识(用于更新场景)
|
||||
*/
|
||||
private void syncMenuPermission(PortalDO portal, Long parentMenuId, String oldPermission) {
|
||||
// 1. 如果权限标识发生变化,先删除旧的菜单权限
|
||||
if (oldPermission != null && !oldPermission.equals(portal.getPermission())) {
|
||||
deleteMenuPermission(oldPermission);
|
||||
}
|
||||
|
||||
// 2. 查询是否已存在该权限的菜单
|
||||
List<Long> existMenuIds = menuService.getMenuIdListByPermissionFromCache(portal.getPermission());
|
||||
|
||||
// 3. 构建菜单对象
|
||||
MenuSaveVO menuVO = new MenuSaveVO();
|
||||
menuVO.setName("访问" + portal.getName());
|
||||
menuVO.setPermission(portal.getPermission());
|
||||
menuVO.setType(MenuTypeEnum.BUTTON.getType());
|
||||
menuVO.setSort(portal.getSort());
|
||||
menuVO.setParentId(parentMenuId);
|
||||
menuVO.setStatus(portal.getStatus());
|
||||
menuVO.setVisible(false); // 按钮权限不显示在菜单中
|
||||
|
||||
// 4. 如果菜单已存在则更新,否则创建
|
||||
if (!CollectionUtils.isEmpty(existMenuIds)) {
|
||||
menuVO.setId(existMenuIds.get(0));
|
||||
menuService.updateMenu(menuVO);
|
||||
} else {
|
||||
menuService.createMenu(menuVO);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除菜单权限
|
||||
*
|
||||
* @param permission 权限标识
|
||||
*/
|
||||
private void deleteMenuPermission(String permission) {
|
||||
if (permission == null || permission.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<Long> menuIds = menuService.getMenuIdListByPermissionFromCache(permission);
|
||||
if (!CollectionUtils.isEmpty(menuIds)) {
|
||||
menuIds.forEach(menuService::deleteMenu);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -360,8 +360,8 @@ public class AdminUserServiceImpl implements AdminUserService {
|
||||
public AdminUserDO getUser(Long id) {
|
||||
AdminUserDO adminUserDO = userMapper.selectListByIds(singleton(id)).stream().findFirst().orElseThrow(() -> exception(USER_NOT_EXISTS));
|
||||
Set<CompanyDeptInfo> companyDeptInfoListByUserId = deptService.getCompanyDeptInfoListByUserId(id);
|
||||
adminUserDO.setDeptIds(companyDeptInfoListByUserId.stream().map(CompanyDeptInfo::getDeptId).collect(Collectors.toSet()));
|
||||
adminUserDO.setCompanyIds(companyDeptInfoListByUserId.stream().map(CompanyDeptInfo::getCompanyId).collect(Collectors.toSet()));
|
||||
adminUserDO.setDeptIds(companyDeptInfoListByUserId.stream().map(CompanyDeptInfo::getDeptId).map(Long::valueOf).collect(Collectors.toSet()));
|
||||
adminUserDO.setCompanyIds(companyDeptInfoListByUserId.stream().map(CompanyDeptInfo::getCompanyId).map(Long::valueOf).collect(Collectors.toSet()));
|
||||
adminUserDO.setCompanyDeptInfos(companyDeptInfoListByUserId);
|
||||
String deptNames = companyDeptInfoListByUserId.stream()
|
||||
.map(CompanyDeptInfo::getDeptName)
|
||||
|
||||
@@ -181,6 +181,7 @@ zt:
|
||||
ignore-urls: # 登录时不校验租户,登录后强制用户选择后进入系统
|
||||
# - /admin-api/system/auth/login
|
||||
ignore-visit-urls:
|
||||
- /admin-api/system/captcha/**
|
||||
- /admin-api/system/user/profile/**
|
||||
- /admin-api/system/auth/**
|
||||
ignore-tables:
|
||||
@@ -232,3 +233,16 @@ debug: false
|
||||
|
||||
sync:
|
||||
encrypt-key: 25@jygk # 中铝 加密 key
|
||||
|
||||
eplat:
|
||||
share:
|
||||
url-prefix: https://10.1.7.110
|
||||
client-id: ztjgj5gsJ2uU20900h9j
|
||||
client-secret: DC82AD38EA764719B6DC7D71AAB4856C
|
||||
scope: read
|
||||
token-cache-key: eplat:cache:shareToken
|
||||
refresh-token-cache-key: eplat:cache:shareRefreshToken
|
||||
token-header-name: Xplat-Token
|
||||
token-endpoint-path: /eplat/oauth/token
|
||||
token-ttl: 5000s
|
||||
refresh-token-ttl: 10000s
|
||||
|
||||
@@ -81,7 +81,7 @@ public class OAuth2TokenServiceImplTest extends BaseDbAndRedisUnitTest {
|
||||
assertPojoEquals(accessTokenDO, dbAccessTokenDO, "expiresTime", "createTime", "updateTime", "deleted");
|
||||
assertEquals(userId, accessTokenDO.getUserId());
|
||||
assertEquals(userType, accessTokenDO.getUserType());
|
||||
assertEquals(6, accessTokenDO.getUserInfo().size());
|
||||
assertTrue(accessTokenDO.getUserInfo().size() >= 6);
|
||||
assertEquals(user.getNickname(), accessTokenDO.getUserInfo().get("nickname"));
|
||||
assertEquals(clientId, accessTokenDO.getClientId());
|
||||
assertEquals(scopes, accessTokenDO.getScopes());
|
||||
|
||||
@@ -257,6 +257,8 @@ zt:
|
||||
info:
|
||||
version: 1.0.0
|
||||
base-package: com.zt.plat
|
||||
file:
|
||||
download-expire-seconds: 86400 # 对象存储预签名地址默认有效期(秒)
|
||||
web:
|
||||
admin-ui:
|
||||
url: http://dashboard.zt.iocoder.cn # Admin 管理后台 UI 的地址
|
||||
|
||||
Reference in New Issue
Block a user