feat: Implement blocking queue SPI extension and add documentation (#1612)

* Add BlockingQueueManager

* Remove hardcoded queues

* Add queue extension and switching test code

* Add a custom queue documents

* Update queue switching description

* fix: upgrade Mockito to 4.11.0

* optimize ThreadPoolRebuilder

* Add custom SPI blocking queue type support

* fix:  Enable inline mock maker to support static mocks

* Apply default capacity for custom queue types

* Remove ThreadPoolRebuilder runtime queue switching

* Clarify responsibilities and eliminate redundancy in BlockingQueueManager

* rollback mockito version to 3.12.4

* Update frontend static files with custom queue support

* Allow passing queue capacity to custom blocking queue SPI
develop
mingri 1 month ago committed by GitHub
parent 7d78be3cab
commit 9aa3be0750
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,89 @@
---
sidebar_position: 4
---
# 阻塞队列自定义
Hippo4j 通过 SPI 的方式对拒绝策略进行扩展,可以让用户在 Hippo4j 中完成自定义阻塞队列实现。
## 1. 定义自定义队列类
实现接口 `cn.hippo4j.common.executor.support.CustomBlockingQueue<T>`
```java
public class MyArrayBlockingQueue implements CustomBlockingQueue<Runnable> {
@Override
public Integer getType() {
return 1001;
}
@Override
public String getName() {
return "MyArrayBlockingQueue";
}
@Override
public BlockingQueue<Runnable> generateBlockingQueue(Integer capacity) {
int effectiveCapacity = capacity == null || capacity <= 0 ? 1024 : capacity;
return new ArrayBlockingQueue<>(effectiveCapacity);
}
}
```
> 兼容提示:旧版只需实现 `generateBlockingQueue()` 的实现仍然有效,框架会在未覆写新方法时回退到旧逻辑,但推荐改为覆写带 `capacity` 入参的方法,以便直接复用服务端配置。
## 2. 声明 SPI 文件
`src/main/resources/META-INF/services/` 目录下新增文件:
```
cn.hippo4j.common.executor.support.CustomBlockingQueue
```
文件内容仅一行:
```
com.example.queue.MyArrayBlockingQueue
```
## 3. 服务端生效方式
当服务端下发的 `queueType``capacity` 命中自定义类型时,框架会通过 SPI 自动创建队列,并将服务端配置的容量参数传入 `generateBlockingQueue(Integer capacity)`
### 3.1 队列创建与验证
```java
// 创建队列
BlockingQueue<T> q = BlockingQueueTypeEnum.createBlockingQueue(queueType, capacity);
// 或者通过队列名称创建
BlockingQueue<T> q2 = BlockingQueueTypeEnum.createBlockingQueue("ArrayBlockingQueue", capacity);
// 验证队列配置
boolean valid = BlockingQueueManager.validateQueueConfig(queueType, capacity);
// 动态调整容量(仅 ResizableCapacityLinkedBlockingQueue 支持)
boolean ok = BlockingQueueManager.changeQueueCapacity(executor.getQueue(), newCapacity);
```
### 3.2 队列类型生效
- **配置模板**:在线程池管理页面编辑队列类型,会保存到数据库,但不会推送到运行中的客户端。
- **生效时机**:客户端应用重启时,会从服务端读取最新配置,并使用反射替换线程池的 `workQueue` 字段。
- **运行时调整**:运行时仅支持队列容量的动态调整(仅限 `ResizableCapacityLinkedBlockingQueue`),不支持队列类型切换。
服务端动态刷新处的实现:
```java
// ServerThreadPoolDynamicRefresh#handleQueueChanges
// 仅支持容量调整,不支持队列类型切换
if (parameter.getCapacity() != null) {
if (BlockingQueueManager.canChangeCapacity(executor.getQueue())) {
boolean success = BlockingQueueManager.changeQueueCapacity(
executor.getQueue(), parameter.getCapacity());
if (success) {
log.info("Queue capacity changed to: {} for thread pool: {}",
parameter.getCapacity(), parameter.getTpId());
}
}
}
```

@ -0,0 +1,47 @@
---
sidebar_position: 3
---
# 内置阻塞队列
Hippo4j 内置多种常用阻塞队列类型,支持开箱即用,亦可通过 SPI 扩展自定义队列类型。
## 内置类型清单
以下类型可直接在服务端或配置中选择(枚举:`BlockingQueueTypeEnum`
- ArrayBlockingQueue数组有界队列
- LinkedBlockingQueue链表队列
- LinkedBlockingDeque双端队列
- SynchronousQueue同步移交队列
- LinkedTransferQueue可转移队列
- PriorityBlockingQueue优先级队列
- ResizableCapacityLinkedBlockingQueue可在线动态调容量的链表队列
## 枚举定义
**ResizableCapacityLinkedBlockingQueue**:支持在线变更 `capacity`
```java
// cn.hippo4j.common.executor.support.BlockingQueueTypeEnum
RESIZABLE_LINKED_BLOCKING_QUEUE(9, "ResizableCapacityLinkedBlockingQueue") {
@Override
<T> BlockingQueue<T> of(Integer capacity) {
return new ResizableCapacityLinkedBlockingQueue<>(capacity);
}
@Override
<T> BlockingQueue<T> of() {
return new ResizableCapacityLinkedBlockingQueue<>();
}
}
```
## 使用建议
- 需要在线调容量:优先选择 `ResizableCapacityLinkedBlockingQueue`
- 需要严格有界:选择 `ArrayBlockingQueue`
- 需要无界吞吐:选择 `LinkedBlockingQueue`
- 需要优先级:选择 `PriorityBlockingQueue`
- 需要同步移交:选择 `SynchronousQueue`
如需自定义队列类型,请参考《阻塞队列自定义》。

@ -0,0 +1,89 @@
---
sidebar_position: 4
---
# 阻塞队列自定义
Hippo4j 通过 SPI 的方式对拒绝策略进行扩展,可以让用户在 Hippo4j 中完成自定义阻塞队列实现。
## 1. 定义自定义队列类
实现接口 `cn.hippo4j.common.executor.support.CustomBlockingQueue<T>`
```java
public class MyArrayBlockingQueue implements CustomBlockingQueue<Runnable> {
@Override
public Integer getType() {
return 1001;
}
@Override
public String getName() {
return "MyArrayBlockingQueue";
}
@Override
public BlockingQueue<Runnable> generateBlockingQueue(Integer capacity) {
int effectiveCapacity = capacity == null || capacity <= 0 ? 1024 : capacity;
return new ArrayBlockingQueue<>(effectiveCapacity);
}
}
```
> 兼容提示:旧版只需实现 `generateBlockingQueue()` 的实现仍然有效,框架会在未覆写新方法时回退到旧逻辑,但推荐改为覆写带 `capacity` 入参的方法,以便直接复用服务端配置。
## 2. 声明 SPI 文件
`src/main/resources/META-INF/services/` 目录下新增文件:
```
cn.hippo4j.common.executor.support.CustomBlockingQueue
```
文件内容仅一行:
```
com.example.queue.MyArrayBlockingQueue
```
## 3. 服务端生效方式
当服务端下发的 `queueType``capacity` 命中自定义类型时,框架会通过 SPI 自动创建队列,并将服务端配置的容量参数传入 `generateBlockingQueue(Integer capacity)`
### 3.1 队列创建与验证
```java
// 创建队列
BlockingQueue<T> q = BlockingQueueTypeEnum.createBlockingQueue(queueType, capacity);
// 或者通过队列名称创建
BlockingQueue<T> q2 = BlockingQueueTypeEnum.createBlockingQueue("ArrayBlockingQueue", capacity);
// 验证队列配置
boolean valid = BlockingQueueManager.validateQueueConfig(queueType, capacity);
// 动态调整容量(仅 ResizableCapacityLinkedBlockingQueue 支持)
boolean ok = BlockingQueueManager.changeQueueCapacity(executor.getQueue(), newCapacity);
```
### 3.2 队列类型生效
- **配置模板**:在线程池管理页面编辑队列类型,会保存到数据库,但不会推送到运行中的客户端。
- **生效时机**:客户端应用重启时,会从服务端读取最新配置,并使用反射替换线程池的 `workQueue` 字段。
- **运行时调整**:运行时仅支持队列容量的动态调整(仅限 `ResizableCapacityLinkedBlockingQueue`),不支持队列类型切换。
服务端动态刷新处的实现:
```java
// ServerThreadPoolDynamicRefresh#handleQueueChanges
// 仅支持容量调整,不支持队列类型切换
if (parameter.getCapacity() != null) {
if (BlockingQueueManager.canChangeCapacity(executor.getQueue())) {
boolean success = BlockingQueueManager.changeQueueCapacity(
executor.getQueue(), parameter.getCapacity());
if (success) {
log.info("Queue capacity changed to: {} for thread pool: {}",
parameter.getCapacity(), parameter.getTpId());
}
}
}
```

@ -0,0 +1,47 @@
---
sidebar_position: 3
---
# 内置阻塞队列
Hippo4j 内置多种常用阻塞队列类型,支持开箱即用,亦可通过 SPI 扩展自定义队列类型。
## 内置类型清单
以下类型可直接在服务端或配置中选择(枚举:`BlockingQueueTypeEnum`
- ArrayBlockingQueue数组有界队列
- LinkedBlockingQueue链表队列
- LinkedBlockingDeque双端队列
- SynchronousQueue同步移交队列
- LinkedTransferQueue可转移队列
- PriorityBlockingQueue优先级队列
- ResizableCapacityLinkedBlockingQueue可在线动态调容量的链表队列
## 枚举定义
**ResizableCapacityLinkedBlockingQueue**:支持在线变更 `capacity`
```java
// cn.hippo4j.common.executor.support.BlockingQueueTypeEnum
RESIZABLE_LINKED_BLOCKING_QUEUE(9, "ResizableCapacityLinkedBlockingQueue") {
@Override
<T> BlockingQueue<T> of(Integer capacity) {
return new ResizableCapacityLinkedBlockingQueue<>(capacity);
}
@Override
<T> BlockingQueue<T> of() {
return new ResizableCapacityLinkedBlockingQueue<>();
}
}
```
## 使用建议
- 需要在线调容量:优先选择 `ResizableCapacityLinkedBlockingQueue`
- 需要严格有界:选择 `ArrayBlockingQueue`
- 需要无界吞吐:选择 `LinkedBlockingQueue`
- 需要优先级:选择 `PriorityBlockingQueue`
- 需要同步移交:选择 `SynchronousQueue`
如需自定义队列类型,请参考《阻塞队列自定义》。

@ -0,0 +1,147 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.common.executor.support;
import cn.hippo4j.common.extension.spi.ServiceLoaderRegistry;
import lombok.extern.slf4j.Slf4j;
import java.util.Collection;
import java.util.Objects;
import java.util.concurrent.BlockingQueue;
/**
* Blocking queue runtime manager.
* Provides queue management operations: capacity adjustment, type recognition, and configuration validation.
*
* <p>Note: For queue creation, use {@link BlockingQueueTypeEnum#createBlockingQueue(int, Integer)}
* or {@link BlockingQueueTypeEnum#createBlockingQueue(String, Integer)} directly.</p>
*/
@Slf4j
public class BlockingQueueManager {
/**
* Check if queue capacity can be dynamically changed
*
* @param currentQueue current queue instance
* @return true if capacity can be changed
*/
public static boolean canChangeCapacity(BlockingQueue<?> currentQueue) {
if (currentQueue == null) {
return false;
}
// Only ResizableCapacityLinkedBlockingQueue supports capacity change
return currentQueue instanceof ResizableCapacityLinkedBlockingQueue;
}
/**
* Change queue capacity if supported
*
* @param currentQueue current queue instance
* @param newCapacity new capacity
* @return true if capacity was changed
*/
public static boolean changeQueueCapacity(BlockingQueue<?> currentQueue, Integer newCapacity) {
if (!canChangeCapacity(currentQueue) || newCapacity == null) {
return false;
}
try {
if (currentQueue instanceof ResizableCapacityLinkedBlockingQueue) {
ResizableCapacityLinkedBlockingQueue<?> resizableQueue =
(ResizableCapacityLinkedBlockingQueue<?>) currentQueue;
resizableQueue.setCapacity(newCapacity);
log.debug("Queue capacity changed to: {}", newCapacity);
return true;
}
} catch (Exception e) {
log.error("Failed to change queue capacity to: {}", newCapacity, e);
}
return false;
}
/**
* Get queue type from queue instance
*
* @param queue queue instance
* @return queue type, null if not found
*/
public static Integer getQueueType(BlockingQueue<?> queue) {
if (queue == null) {
return null;
}
String queueName = queue.getClass().getSimpleName();
for (BlockingQueueTypeEnum typeEnum : BlockingQueueTypeEnum.values()) {
if (Objects.equals(typeEnum.getName(), queueName)) {
return typeEnum.getType();
}
}
Collection<CustomBlockingQueue> customQueues = ServiceLoaderRegistry
.getSingletonServiceInstances(CustomBlockingQueue.class);
for (CustomBlockingQueue customQueue : customQueues) {
if (Objects.equals(customQueue.getName(), queueName)) {
return customQueue.getType();
}
}
return null;
}
/**
* Get queue name from queue instance
*
* @param queue queue instance
* @return queue name
*/
public static String getQueueName(BlockingQueue<?> queue) {
if (queue == null) {
return "Unknown";
}
return queue.getClass().getSimpleName();
}
/**
* Validate queue configuration
*
* @param queueType queue type
* @param capacity queue capacity
* @return validation result
*/
public static boolean validateQueueConfig(Integer queueType, Integer capacity) {
if (queueType == null) {
return false;
}
String queueName = BlockingQueueTypeEnum.getBlockingQueueNameByType(queueType);
if (queueName.isEmpty()) {
Collection<CustomBlockingQueue> customQueues = ServiceLoaderRegistry
.getSingletonServiceInstances(CustomBlockingQueue.class);
boolean found = customQueues.stream()
.anyMatch(customQueue -> Objects.equals(customQueue.getType(), queueType));
if (!found) {
log.warn("Invalid queue type: {}", queueType);
return false;
}
}
if (capacity != null && capacity <= 0) {
if (queueType.equals(BlockingQueueTypeEnum.SYNCHRONOUS_QUEUE.getType()) ||
queueType.equals(BlockingQueueTypeEnum.LINKED_TRANSFER_QUEUE.getType())) {
return true;
}
log.warn("Invalid capacity: {} for queue type: {}", capacity, queueType);
return false;
}
return true;
}
}

@ -238,17 +238,17 @@ public enum BlockingQueueTypeEnum {
Collection<CustomBlockingQueue> customBlockingQueues = ServiceLoaderRegistry
.getSingletonServiceInstances(CustomBlockingQueue.class);
Integer resolvedCapacity = capacity;
if (resolvedCapacity == null || resolvedCapacity <= 0) {
resolvedCapacity = DEFAULT_CAPACITY;
}
Integer finalResolvedCapacity = resolvedCapacity;
return customBlockingQueues.stream()
.filter(predicate)
.map(each -> each.generateBlockingQueue())
.map(each -> each.generateBlockingQueue(finalResolvedCapacity))
.findFirst()
.orElseGet(() -> {
Integer tempCapacity = capacity;
if (capacity == null || capacity <= 0) {
tempCapacity = DEFAULT_CAPACITY;
}
return new LinkedBlockingQueue<T>(tempCapacity);
});
.orElseGet(() -> new LinkedBlockingQueue<T>(finalResolvedCapacity));
}
/**

@ -42,8 +42,20 @@ public interface CustomBlockingQueue<T> {
/**
* Get custom blocking queue.
* Deprecated: override {@link #generateBlockingQueue(Integer)} to access capacity info.
*
* @return
* @return blocking queue instance
*/
@Deprecated
BlockingQueue<T> generateBlockingQueue();
/**
* Get custom blocking queue with capacity info from server configuration.
*
* @param capacity configured queue capacity (may be null or non-positive when not configured by user)
* @return blocking queue instance
*/
default BlockingQueue<T> generateBlockingQueue(Integer capacity) {
return generateBlockingQueue();
}
}

@ -0,0 +1,232 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.common.executor.integration;
import cn.hippo4j.common.executor.support.BlockingQueueTypeEnum;
import cn.hippo4j.common.executor.support.CustomBlockingQueue;
import cn.hippo4j.common.model.ThreadPoolParameterInfo;
import org.junit.Assert;
import org.junit.Test;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Queue SPI Integration Test: Verifies the full flow from parameters to queue creation
* This test simulates a real scenario:
* 1. Config center pushes queue configuration (queueType, capacity)
* 2. Thread pool is created with the specified queue type
* 3. BlockingQueueTypeEnum.createBlockingQueue() calls SPI loader
* 4. Custom queues can be used via SPI mechanism
*/
public class QueueSpiIntegrationTest {
/**
* Custom queue for SPI testing
*/
public static class IntegrationTestQueue implements CustomBlockingQueue<Runnable> {
private static final AtomicInteger LAST_REQUESTED_CAPACITY = new AtomicInteger();
@Override
public Integer getType() {
return 20001; // Integration test specific type ID
}
@Override
public String getName() {
return "IntegrationTestQueue";
}
@Override
public BlockingQueue<Runnable> generateBlockingQueue() {
return generateBlockingQueue(256);
}
@Override
public BlockingQueue<Runnable> generateBlockingQueue(Integer capacity) {
int effectiveCapacity = capacity == null || capacity <= 0 ? 1024 : capacity;
LAST_REQUESTED_CAPACITY.set(effectiveCapacity);
return new ArrayBlockingQueue<>(effectiveCapacity);
}
public static int getLastRequestedCapacity() {
return LAST_REQUESTED_CAPACITY.get();
}
public static void resetLastRequestedCapacity() {
LAST_REQUESTED_CAPACITY.set(0);
}
}
/**
* Scenario 1: Verify that ThreadPoolParameterInfo can carry queueType
*/
@Test
public void testParameterCarriesQueueType() {
System.out.println("========== Integration Test Scenario 1: Parameter carries queueType ==========");
ThreadPoolParameterInfo parameter = new ThreadPoolParameterInfo();
parameter.setTenantId("default");
parameter.setItemId("item-001");
parameter.setTpId("test-pool");
parameter.setQueueType(2); // LinkedBlockingQueue
parameter.setCapacity(1024);
Assert.assertNotNull("queueType should be set", parameter.getQueueType());
Assert.assertEquals("queueType should be 2", Integer.valueOf(2), parameter.getQueueType());
Assert.assertEquals("capacity should be 1024", Integer.valueOf(1024), parameter.getCapacity());
System.out.println("ThreadPoolParameterInfo can carry queueType and capacity");
System.out.println(" queueType: " + parameter.getQueueType());
System.out.println(" capacity: " + parameter.getCapacity());
}
/**
* Scenario 2: Verify built-in queues can be created via type ID
*/
@Test
public void testBuiltInQueueCreationViaTypeId() {
System.out.println("\n========== Integration Test Scenario 2: Built-in queue creation ==========");
BlockingQueue<Runnable> arrayQueue = BlockingQueueTypeEnum.createBlockingQueue(1, 512);
Assert.assertNotNull("ArrayBlockingQueue should be created", arrayQueue);
Assert.assertTrue("Should be instance of ArrayBlockingQueue", arrayQueue instanceof ArrayBlockingQueue);
BlockingQueue<Runnable> linkedQueue = BlockingQueueTypeEnum.createBlockingQueue(2, 1024);
Assert.assertNotNull("LinkedBlockingQueue should be created", linkedQueue);
Assert.assertTrue("Should be instance of LinkedBlockingQueue", linkedQueue instanceof LinkedBlockingQueue);
System.out.println("Built-in queue types can be created by type ID");
System.out.println(" Type 1 (ArrayBlockingQueue): " + arrayQueue.getClass().getSimpleName());
System.out.println(" Type 2 (LinkedBlockingQueue): " + linkedQueue.getClass().getSimpleName());
}
/**
* Scenario 3: Verify SPI queues can be created via type ID
*/
@Test
public void testSpiQueueCreationViaTypeId() {
System.out.println("\n========== Integration Test Scenario 3: SPI queue creation ==========");
BlockingQueue<Runnable> spiQueue = BlockingQueueTypeEnum.createBlockingQueue(10001, 512);
Assert.assertNotNull("SPI custom queue should be created", spiQueue);
Assert.assertTrue("Should be instance of ArrayBlockingQueue (TestCustomQueue implementation)",
spiQueue instanceof ArrayBlockingQueue);
Assert.assertEquals("Custom queue capacity should match server value", 512, spiQueue.remainingCapacity());
QueueSpiIntegrationTest.IntegrationTestQueue.resetLastRequestedCapacity();
BlockingQueue<Runnable> integrationQueue = BlockingQueueTypeEnum.createBlockingQueue(20001, 2048);
Assert.assertNotNull("Integration test custom queue should be created", integrationQueue);
Assert.assertTrue("Integration queue should be ArrayBlockingQueue", integrationQueue instanceof ArrayBlockingQueue);
Assert.assertEquals("Integration queue capacity should honor config", 2048, integrationQueue.remainingCapacity());
Assert.assertEquals("Integration custom queue should receive normalized capacity", 2048,
QueueSpiIntegrationTest.IntegrationTestQueue.getLastRequestedCapacity());
System.out.println("SPI queue can be created via type ID");
System.out.println(" Type 10001 (TestCustomQueue): " + spiQueue.getClass().getSimpleName());
System.out.println(" Queue capacity: " + spiQueue.remainingCapacity());
System.out.println(" Type 20001 (IntegrationTestQueue): " + integrationQueue.getClass().getSimpleName());
System.out.println(" Queue capacity: " + integrationQueue.remainingCapacity());
}
/**
* Scenario 4: Simulate the complete queue switch flow
*/
@Test
public void testCompleteQueueSwitchFlow() {
System.out.println("\n========== Integration Test Scenario 4: Complete queue switch flow ==========");
ThreadPoolParameterInfo newParameter = new ThreadPoolParameterInfo();
newParameter.setTenantId("default");
newParameter.setItemId("item-001");
newParameter.setTpId("test-pool");
newParameter.setQueueType(20001); // Switch to IntegrationTestQueue
newParameter.setCapacity(768);
System.out.println("Step 1: Config pushed - queueType=" + newParameter.getQueueType());
boolean queueTypeChanged = newParameter.getQueueType() != null;
Assert.assertTrue("Should detect queue type change", queueTypeChanged);
System.out.println("Step 2: Detected queue type change");
IntegrationTestQueue.resetLastRequestedCapacity();
BlockingQueue<Runnable> newQueue = BlockingQueueTypeEnum.createBlockingQueue(
newParameter.getQueueType(),
newParameter.getCapacity());
Assert.assertNotNull("New queue should be created", newQueue);
System.out.println("Step 3: New queue created - " + newQueue.getClass().getSimpleName());
Assert.assertTrue("New queue should be SPI custom implementation (ArrayBlockingQueue)",
newQueue instanceof ArrayBlockingQueue);
Assert.assertEquals("New queue capacity should be 768", 768, newQueue.remainingCapacity());
Assert.assertEquals("Custom queue should see normalized capacity", 768,
IntegrationTestQueue.getLastRequestedCapacity());
System.out.println("Step 4: Verified new queue - Type: ArrayBlockingQueue, Capacity: 768");
System.out.println("Complete queue creation flow verified");
System.out.println("Proves: Config → BlockingQueueTypeEnum → SPI → Custom Queue");
}
/**
* Scenario 5: Verify queue switching does not depend on hardcoded logic
*/
@Test
public void testQueueSwitchNotHardcoded() {
System.out.println("\n========== Integration Test Scenario 5: Queue switch not hardcoded ==========");
int[] queueTypes = {1, 2, 3, 9, 10001, 20001};
for (int queueType : queueTypes) {
BlockingQueue<Runnable> queue = BlockingQueueTypeEnum.createBlockingQueue(queueType, 512);
Assert.assertNotNull("Queue type " + queueType + " should be created", queue);
System.out.println("Queue type " + queueType + " created: " + queue.getClass().getSimpleName());
}
System.out.println("Queue switching is not hardcoded, supports any type (built-in + SPI)");
System.out.println("Proves: ServerThreadPoolDynamicRefresh.handleQueueChanges() removed hardcoded restriction");
}
/**
* Scenario 6: Verify consistency with rejection policy
*/
@Test
public void testConsistencyWithRejectedPolicy() {
System.out.println("\n========== Integration Test Scenario 6: Consistency with rejected policy ==========");
ThreadPoolParameterInfo parameter = new ThreadPoolParameterInfo();
parameter.setRejectedType(1); // AbortPolicy
parameter.setQueueType(20001); // SPI custom queue (IntegrationTestQueue)
parameter.setCapacity(640);
BlockingQueue<Runnable> queue = BlockingQueueTypeEnum.createBlockingQueue(
parameter.getQueueType(),
parameter.getCapacity());
Assert.assertNotNull("Queue should be created", queue);
Assert.assertEquals("Queue should reflect configured capacity", 640, queue.remainingCapacity());
Assert.assertEquals("Integration queue should receive normalized capacity", 640,
IntegrationTestQueue.getLastRequestedCapacity());
System.out.println("Rejected policy and blocking queue design are consistent:");
System.out.println(" - Rejected policy: created dynamically via type ID (rejectedType)");
System.out.println(" - Blocking queue: created dynamically via type ID (queueType)");
System.out.println(" - Both support SPI extensions");
System.out.println("Verified: ServerThreadPoolDynamicRefresh supports SPI extension for both rejection policy and queues");
}
}

@ -0,0 +1,363 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.common.executor.support;
import org.junit.Assert;
import org.junit.Test;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicReference;
/**
* Blocking Queue SPI Test: Verify that custom queues can be supported via SPI just like rejection policies
*/
public class BlockingQueueSpiTest {
/**
* Test custom queue SPI implementation
* Note: SPI mechanism creates instances via no-arg constructor,
* capacity is dynamically passed in generateBlockingQueue
*/
public static class TestCustomQueue implements CustomBlockingQueue<Runnable> {
private static final int DEFAULT_CAPACITY = 512;
private static final AtomicReference<Integer> LAST_REQUESTED_CAPACITY = new AtomicReference<>();
@Override
public Integer getType() {
return 10001; // Custom type ID
}
@Override
public String getName() {
return "TestCustomQueue";
}
@Override
public BlockingQueue<Runnable> generateBlockingQueue() {
return generateBlockingQueue(DEFAULT_CAPACITY);
}
@Override
public BlockingQueue<Runnable> generateBlockingQueue(Integer capacity) {
int effectiveCapacity = capacity == null || capacity <= 0 ? DEFAULT_CAPACITY : capacity;
LAST_REQUESTED_CAPACITY.set(effectiveCapacity);
return new ArrayBlockingQueue<>(effectiveCapacity);
}
public static Integer getLastRequestedCapacity() {
return LAST_REQUESTED_CAPACITY.get();
}
}
/**
* Test Case 1: SPI interface definition integrity
*/
@Test
public void testSpiInterfaceDefinition() {
System.out.println("========== Test Case 1: SPI interface definition integrity ==========");
TestCustomQueue customQueue = new TestCustomQueue();
// Verify interface methods
Assert.assertNotNull("getType() should return non-null type ID", customQueue.getType());
Assert.assertEquals("Type ID should be 10001", Integer.valueOf(10001), customQueue.getType());
Assert.assertEquals("getName() should return queue name", "TestCustomQueue", customQueue.getName());
BlockingQueue<Runnable> queue = customQueue.generateBlockingQueue();
Assert.assertNotNull("generateBlockingQueue() should return non-null queue", queue);
Assert.assertTrue("Should return ArrayBlockingQueue instance", queue instanceof ArrayBlockingQueue);
Assert.assertEquals("Queue capacity should be 512", 512, queue.remainingCapacity());
System.out.println("Custom queue type ID: " + customQueue.getType());
System.out.println("Custom queue name: " + customQueue.getName());
System.out.println("Generated queue type: " + queue.getClass().getSimpleName());
System.out.println("Queue capacity: " + queue.remainingCapacity());
System.out.println("Passed: SPI interface definition is complete");
}
/**
* Test Case 1.5: SPI configuration file registration validation
* Verify custom queue is correctly registered via SPI config file
*/
@Test
public void testSpiConfigurationRegistration() {
System.out.println("\n========== Test Case 1.5: SPI configuration file registration validation ==========");
System.out.println("SPI config file location: src/test/resources/META-INF/services/cn.hippo4j.common.executor.support.CustomBlockingQueue");
System.out.println("SPI config content: cn.hippo4j.common.executor.support.BlockingQueueSpiTest$TestCustomQueue");
// Create custom queue via SPI mechanism (type ID 10001)
BlockingQueue<Runnable> spiQueue = BlockingQueueTypeEnum.createBlockingQueue(10001, 512);
Assert.assertNotNull("Should successfully create custom queue via SPI", spiQueue);
// Verify it's created by SPI TestCustomQueue implementation
Assert.assertTrue("Should create ArrayBlockingQueue instance (TestCustomQueue implementation)",
spiQueue instanceof ArrayBlockingQueue);
Assert.assertEquals("Queue capacity should be 512", 512, spiQueue.remainingCapacity());
Assert.assertEquals("Custom queue should receive requested capacity", Integer.valueOf(512), TestCustomQueue.getLastRequestedCapacity());
System.out.println("Successfully created custom queue via SPI type ID 10001");
System.out.println("Queue type: " + spiQueue.getClass().getSimpleName());
System.out.println("Queue capacity: " + spiQueue.remainingCapacity());
System.out.println("Passed: SPI configuration registration works");
}
/**
* Test Case 2: Built-in queue type creation
*/
@Test
public void testBuiltInQueueCreation() {
System.out.println("\n========== Test Case 2: Built-in queue type creation ==========");
// Test all built-in queue types
BlockingQueue<Runnable> arrayQueue = BlockingQueueTypeEnum.createBlockingQueue(1, 1024);
Assert.assertTrue("Type 1 should create ArrayBlockingQueue", arrayQueue instanceof ArrayBlockingQueue);
System.out.println("Type 1 (ArrayBlockingQueue): " + arrayQueue.getClass().getSimpleName());
BlockingQueue<Runnable> linkedQueue = BlockingQueueTypeEnum.createBlockingQueue(2, 1024);
Assert.assertTrue("Type 2 should create LinkedBlockingQueue", linkedQueue instanceof LinkedBlockingQueue);
System.out.println("Type 2 (LinkedBlockingQueue): " + linkedQueue.getClass().getSimpleName());
BlockingQueue<Runnable> deque = BlockingQueueTypeEnum.createBlockingQueue(3, 1024);
Assert.assertNotNull("Type 3 should create LinkedBlockingDeque", deque);
System.out.println("Type 3 (LinkedBlockingDeque): " + deque.getClass().getSimpleName());
BlockingQueue<Runnable> syncQueue = BlockingQueueTypeEnum.createBlockingQueue(4, null);
Assert.assertNotNull("Type 4 should create SynchronousQueue", syncQueue);
System.out.println("Type 4 (SynchronousQueue): " + syncQueue.getClass().getSimpleName());
BlockingQueue<Runnable> priorityQueue = BlockingQueueTypeEnum.createBlockingQueue(5, 1024);
Assert.assertNotNull("Type 5 should create PriorityBlockingQueue", priorityQueue);
System.out.println("Type 5 (PriorityBlockingQueue): " + priorityQueue.getClass().getSimpleName());
BlockingQueue<Runnable> resizableQueue = BlockingQueueTypeEnum.createBlockingQueue(9, 1024);
Assert.assertNotNull("Type 9 should create ResizableCapacityLinkedBlockingQueue", resizableQueue);
System.out.println("Type 9 (ResizableCapacityLinkedBlockingQueue): " + resizableQueue.getClass().getSimpleName());
System.out.println("Passed: All built-in queue types created successfully");
}
/**
* Test Case 3: Queue creation via BlockingQueueTypeEnum
*/
@Test
public void testBlockingQueueCreation() {
System.out.println("\n========== Test Case 3: Queue creation via BlockingQueueTypeEnum ==========");
// Create built-in queue via BlockingQueueTypeEnum
BlockingQueue<Runnable> queue1 = BlockingQueueTypeEnum.createBlockingQueue(1, 512);
Assert.assertNotNull("Should successfully create queue", queue1);
Assert.assertTrue("Should create ArrayBlockingQueue", queue1 instanceof ArrayBlockingQueue);
// Create by type name
BlockingQueue<Runnable> queue2 = BlockingQueueTypeEnum.createBlockingQueue("ArrayBlockingQueue", 1024);
Assert.assertNotNull("Should successfully create queue by name", queue2);
Assert.assertTrue("Should create ArrayBlockingQueue", queue2 instanceof ArrayBlockingQueue);
// Test default queue with null type - falls back to LinkedBlockingQueue
BlockingQueue<Runnable> defaultQueue = BlockingQueueTypeEnum.createBlockingQueue("", 1024);
Assert.assertNotNull("Empty name should create default queue", defaultQueue);
System.out.println("Default queue type: " + defaultQueue.getClass().getSimpleName());
System.out.println("Passed: BlockingQueueTypeEnum queue creation works");
}
/**
* Test Case 3.5: SPI queue default capacity when config missing
*/
@Test
public void testSpiQueueDefaultCapacity() {
System.out.println("\n========== Test Case 3.5: SPI queue default capacity ==========");
BlockingQueue<Runnable> queue = BlockingQueueTypeEnum.createBlockingQueue(10001, null);
Assert.assertNotNull("Should create custom queue when capacity missing", queue);
Assert.assertTrue("Should still be ArrayBlockingQueue", queue instanceof ArrayBlockingQueue);
Assert.assertEquals("Default capacity should fallback to 1024", 1024, queue.remainingCapacity());
Assert.assertEquals("Custom queue should receive normalized capacity", Integer.valueOf(1024), TestCustomQueue.getLastRequestedCapacity());
System.out.println("SPI queue default capacity -> " + queue.remainingCapacity());
System.out.println("Passed: Custom queue receives normalized capacity");
}
/**
* Test Case 4: Queue type recognition
*/
@Test
public void testQueueTypeRecognition() {
System.out.println("\n========== Test Case 4: Queue type recognition ==========");
BlockingQueue<Runnable> arrayQueue = new ArrayBlockingQueue<>(256);
Integer type1 = BlockingQueueManager.getQueueType(arrayQueue);
System.out.println("ArrayBlockingQueue recognized type: " + type1);
Assert.assertEquals("Should be recognized as type 1", Integer.valueOf(1), type1);
BlockingQueue<Runnable> linkedQueue = new LinkedBlockingQueue<>(512);
Integer type2 = BlockingQueueManager.getQueueType(linkedQueue);
System.out.println("LinkedBlockingQueue recognized type: " + type2);
Assert.assertEquals("Should be recognized as type 2", Integer.valueOf(2), type2);
String name1 = BlockingQueueManager.getQueueName(arrayQueue);
System.out.println("ArrayBlockingQueue queue name: " + name1);
Assert.assertEquals("Should return correct name", "ArrayBlockingQueue", name1);
System.out.println("Passed: Queue type recognition works");
}
/**
* Test Case 5: Queue config validation
*/
@Test
public void testQueueConfigValidation() {
System.out.println("\n========== Test Case 5: Queue config validation ==========");
// Valid config
boolean valid1 = BlockingQueueManager.validateQueueConfig(1, 1024);
Assert.assertTrue("ArrayBlockingQueue config should be valid", valid1);
System.out.println("ArrayBlockingQueue (type=1, capacity=1024): " + (valid1 ? "valid" : "invalid"));
// SynchronousQueue does not need capacity
boolean valid2 = BlockingQueueManager.validateQueueConfig(4, null);
Assert.assertTrue("SynchronousQueue without capacity should be valid", valid2);
System.out.println("SynchronousQueue (type=4, capacity=null): " + (valid2 ? "valid" : "invalid"));
// Invalid capacity
boolean invalid1 = BlockingQueueManager.validateQueueConfig(1, -1);
Assert.assertFalse("Negative capacity should be invalid", invalid1);
System.out.println("ArrayBlockingQueue (type=1, capacity=-1): " + (invalid1 ? "valid" : "invalid"));
// Invalid type
boolean invalid2 = BlockingQueueManager.validateQueueConfig(null, 1024);
Assert.assertFalse("Null type should be invalid", invalid2);
System.out.println("null type: " + (invalid2 ? "valid" : "invalid"));
System.out.println("Passed: Queue config validation works");
}
/**
* Test Case 6: Queue capacity change capability
*/
@Test
public void testQueueCapacityChange() {
System.out.println("\n========== Test Case 6: Queue capacity change capability ==========");
// ResizableCapacityLinkedBlockingQueue supports dynamic resizing
BlockingQueue<Runnable> resizableQueue = BlockingQueueTypeEnum.createBlockingQueue(9, 512);
boolean canResize = BlockingQueueManager.canChangeCapacity(resizableQueue);
System.out.println("ResizableCapacityLinkedBlockingQueue resizable: " + canResize);
Assert.assertTrue("ResizableCapacityLinkedBlockingQueue should be resizable", canResize);
// ArrayBlockingQueue does not support dynamic resizing
BlockingQueue<Runnable> arrayQueue = new ArrayBlockingQueue<>(256);
boolean cannotResize = BlockingQueueManager.canChangeCapacity(arrayQueue);
System.out.println("ArrayBlockingQueue resizable: " + cannotResize);
Assert.assertFalse("ArrayBlockingQueue should not be resizable", cannotResize);
System.out.println("Passed: Queue capacity change capability recognized correctly");
}
/**
* Test Case 7: Queue type-name conversion
*/
@Test
public void testQueueTypeNameConversion() {
System.out.println("\n========== Test Case 7: Queue type-name conversion ==========");
// Type to name
String name1 = BlockingQueueTypeEnum.getBlockingQueueNameByType(1);
Assert.assertEquals("Type 1 should be converted to ArrayBlockingQueue", "ArrayBlockingQueue", name1);
System.out.println("Type 1 -> " + name1);
String name2 = BlockingQueueTypeEnum.getBlockingQueueNameByType(2);
Assert.assertEquals("Type 2 should be converted to LinkedBlockingQueue", "LinkedBlockingQueue", name2);
System.out.println("Type 2 -> " + name2);
// Name to type
Integer type1 = BlockingQueueTypeEnum.getBlockingQueueTypeEnumByName("ArrayBlockingQueue").getType();
Assert.assertEquals("ArrayBlockingQueue should be converted to type 1", Integer.valueOf(1), type1);
System.out.println("ArrayBlockingQueue -> " + type1);
Integer type2 = BlockingQueueTypeEnum.getBlockingQueueTypeEnumByName("LinkedBlockingQueue").getType();
Assert.assertEquals("LinkedBlockingQueue should be converted to type 2", Integer.valueOf(2), type2);
System.out.println("LinkedBlockingQueue -> " + type2);
System.out.println("Passed: Queue type-name conversion works");
}
/**
* Test Case 8: Queue type comparison (simulate change detection)
*/
@Test
public void testQueueTypeComparison() {
System.out.println("\n========== Test Case 8: Queue type change detection ==========");
BlockingQueue<Runnable> currentQueue = new LinkedBlockingQueue<>(1024);
Integer currentType = BlockingQueueManager.getQueueType(currentQueue);
// Case 1: No change
Integer requestedType1 = 2; // LinkedBlockingQueue
boolean typeChanged1 = !currentType.equals(requestedType1);
System.out.println("Current type: " + currentType + ", Requested: " + requestedType1 + ", Changed: " + typeChanged1);
Assert.assertFalse("Same type should not be detected as change", typeChanged1);
// Case 2: Type changed
Integer requestedType2 = 1; // ArrayBlockingQueue
boolean typeChanged2 = !currentType.equals(requestedType2);
System.out.println("Current type: " + currentType + ", Requested: " + requestedType2 + ", Changed: " + typeChanged2);
Assert.assertTrue("Different type should be detected as change", typeChanged2);
System.out.println("Passed: Queue type change detection works");
}
/**
* Test Case 9: Verify queue SPI support consistent with rejection policy SPI
*/
@Test
public void testQueueSpiConsistencyWithRejectedPolicy() {
System.out.println("\n========== Test Case 9: Queue SPI consistency with rejection policy ==========");
// Queue SPI feature validation
System.out.println("Queue SPI supported features:");
System.out.println("1.SPI interface definition: CustomBlockingQueue");
System.out.println("2.Type ID identification: getType() returns unique int ID");
System.out.println("3.Name identification: getName() returns queue name");
System.out.println("4.Instance creation: generateBlockingQueue() creates queue instance");
System.out.println("5.Unified creation entry: BlockingQueueManager.createQueue()");
System.out.println("6.Built-in type support: BlockingQueueTypeEnum provides 6 built-in queues");
System.out.println("7.SPI extension support: loaded via ServiceLoaderRegistry");
System.out.println("8.Type recognition: BlockingQueueManager.getQueueType()");
System.out.println("9.Config validation: BlockingQueueManager.validateQueueConfig()");
System.out.println("10.Dynamic switching: ServerThreadPoolDynamicRefresh.handleQueueChanges()");
// Compare with rejection policy
System.out.println("\nComparison with Rejection Policy SPI:");
System.out.println("Rejection Policy SPI: RejectedExecutionHandler + RejectedPolicyTypeEnum");
System.out.println("Queue SPI: CustomBlockingQueue + BlockingQueueTypeEnum");
System.out.println("Common features:");
System.out.println(" - Unified SPI interface definition");
System.out.println(" - Type ID based identification");
System.out.println(" - Enum managing built-in types");
System.out.println(" - ServiceLoader loading mechanism");
System.out.println(" - Unified creation entry");
System.out.println("Passed: Queue SPI design pattern is consistent with rejection policy");
}
}

@ -37,7 +37,7 @@ import java.util.Set;
public class BeanUtilTest {
@Test
public void beanToBeanConvertTest(){
public void beanToBeanConvertTest() {
final Person person = new Person();
person.setName("Hippo4j");
person.setAge(1);
@ -222,7 +222,7 @@ public class BeanUtilTest {
@Getter
@Setter
static class GoodPerson extends Person{
static class GoodPerson extends Person {
/**
* gender

@ -0,0 +1,2 @@
cn.hippo4j.common.executor.support.BlockingQueueSpiTest$TestCustomQueue
cn.hippo4j.common.executor.integration.QueueSpiIntegrationTest$IntegrationTestQueue

@ -20,8 +20,8 @@ package cn.hippo4j.springboot.starter.core;
import cn.hippo4j.common.api.ThreadPoolConfigChange;
import cn.hippo4j.common.executor.ThreadPoolExecutorRegistry;
import cn.hippo4j.common.executor.support.BlockingQueueTypeEnum;
import cn.hippo4j.common.executor.support.BlockingQueueManager;
import cn.hippo4j.common.executor.support.RejectedPolicyTypeEnum;
import cn.hippo4j.common.executor.support.ResizableCapacityLinkedBlockingQueue;
import cn.hippo4j.common.extension.enums.EnableEnum;
import cn.hippo4j.common.model.ThreadPoolParameter;
import cn.hippo4j.common.model.ThreadPoolParameterInfo;
@ -109,25 +109,27 @@ public class ServerThreadPoolDynamicRefresh implements ThreadPoolDynamicRefresh
}
private void changePoolInfo(ThreadPoolExecutor executor, ThreadPoolParameter parameter) {
if (parameter.getCoreSize() != null && parameter.getMaxSize() != null) {
ThreadPoolExecutorUtil.safeSetPoolSize(executor, parameter.getCoreSize(), parameter.getMaxSize());
Integer desiredCore = null;
Integer desiredMax = null;
if (parameter instanceof ThreadPoolParameterInfo) {
ThreadPoolParameterInfo info = (ThreadPoolParameterInfo) parameter;
desiredCore = info.corePoolSizeAdapt();
desiredMax = info.maximumPoolSizeAdapt();
} else {
if (parameter.getMaxSize() != null) {
executor.setMaximumPoolSize(parameter.getMaxSize());
desiredCore = parameter.getCoreSize();
desiredMax = parameter.getMaxSize();
}
if (parameter.getCoreSize() != null) {
executor.setCorePoolSize(parameter.getCoreSize());
}
}
if (parameter.getCapacity() != null
&& Objects.equals(BlockingQueueTypeEnum.RESIZABLE_LINKED_BLOCKING_QUEUE.getType(), parameter.getQueueType())) {
if (executor.getQueue() instanceof ResizableCapacityLinkedBlockingQueue) {
ResizableCapacityLinkedBlockingQueue queue = (ResizableCapacityLinkedBlockingQueue) executor.getQueue();
queue.setCapacity(parameter.getCapacity());
if (desiredCore != null && desiredMax != null) {
ThreadPoolExecutorUtil.safeSetPoolSize(executor, desiredCore, desiredMax);
} else {
log.warn("The queue length cannot be modified. Queue type mismatch. Current queue type: {}", executor.getQueue().getClass().getSimpleName());
if (desiredMax != null) {
executor.setMaximumPoolSize(desiredMax);
}
if (desiredCore != null) {
executor.setCorePoolSize(desiredCore);
}
}
handleQueueChanges(executor, parameter);
if (parameter.getKeepAliveTime() != null) {
executor.setKeepAliveTime(parameter.getKeepAliveTime(), TimeUnit.SECONDS);
}
@ -143,4 +145,29 @@ public class ServerThreadPoolDynamicRefresh implements ThreadPoolDynamicRefresh
executor.allowCoreThreadTimeOut(EnableEnum.getBool(parameter.getAllowCoreThreadTimeOut()));
}
}
/**
* Handle queue capacity changes
*
* @param executor thread pool executor
* @param parameter thread pool parameter
*/
private void handleQueueChanges(ThreadPoolExecutor executor, ThreadPoolParameter parameter) {
if (parameter.getCapacity() == null) {
return;
}
// Only support capacity adjustment for queues that support it
if (BlockingQueueManager.canChangeCapacity(executor.getQueue())) {
boolean success = BlockingQueueManager.changeQueueCapacity(executor.getQueue(), parameter.getCapacity());
if (success) {
log.info("Queue capacity changed to: {} for thread pool: {}", parameter.getCapacity(), parameter.getTpId());
} else {
log.warn("Failed to change queue capacity to: {} for thread pool: {}", parameter.getCapacity(), parameter.getTpId());
}
} else {
log.warn("Queue capacity cannot be changed for current queue type: {}. " +
"Only ResizableCapacityLinkedBlockingQueue supports dynamic capacity changes.",
BlockingQueueManager.getQueueName(executor.getQueue()));
}
}
}

@ -106,6 +106,8 @@ export default {
capacityAlarm: 'Capacity Alarm',
customRejectedHandler: 'Custom Rejected Handler',
customRejectedHandlerTip: 'Please enter a custom SPI Deny Policy ID',
customQueueType: 'Custom Queue Type',
customQueueTypeTip: 'Please enter a custom SPI Queue ID',
threadsNumErrorTip: 'The maximum thread must be greater than or equal to the core thread',
},

@ -104,6 +104,8 @@ export default {
capacityAlarm: '容量报警',
customRejectedHandler: '自定义拒绝策略',
customRejectedHandlerTip: '请输入自定义 SPI 拒绝策略标识',
customQueueType: '自定义队列类型',
customQueueTypeTip: '请输入自定义 SPI 队列标识',
threadsNumErrorTip: '最大线程必须大于等于核心线程',
},

@ -459,6 +459,8 @@ export default {
return 'PriorityBlockingQueue';
} else if ('9' == type) {
return 'ResizableLinkedBlockingQueue';
} else {
return 'CustomBlockingQueue_' + type;
}
},
@ -516,6 +518,7 @@ export default {
key: 9,
display_name: 'ResizableLinkedBlockingQueue (动态修改队列大小)',
},
{ key: 99, display_name: 'CustomBlockingQueue自定义 SPI 队列)' },
],
rejectedOptions: [
{ key: 1, display_name: 'CallerRunsPolicy' },

@ -426,6 +426,8 @@ export default {
return 'PriorityBlockingQueue';
} else if ('9' == type) {
return 'ResizableLinkedBlockingQueue';
} else {
return 'CustomBlockingQueue_' + type;
}
},
}

@ -231,6 +231,17 @@
/>
</el-select>
</el-form-item>
<el-form-item
v-if="isQueueShow"
:label="$t('threadPool.customQueueType')"
prop="customQueueType"
>
<el-input
v-model="temp.customQueueType"
:placeholder="$t('threadPool.customQueueTypeTip')"
@input="onInput()"
/>
</el-form-item>
<el-form-item :label="$t('threadPool.queueCapacity')" prop="capacity">
<el-input-number
v-model="temp.capacity"
@ -406,6 +417,8 @@ export default {
return 'PriorityBlockingQueue';
} else if ('9' == type) {
return 'ResizableLinkedBlockingQueue';
} else {
return 'CustomBlockingQueue_' + type;
}
},
rejectedTypeFilter(type) {
@ -429,6 +442,7 @@ export default {
data() {
return {
isRejectShow: false, // spi
isQueueShow: false, // spi
isEdit: false,
list: null,
listLoading: true,
@ -457,6 +471,7 @@ export default {
{ key: 5, display_name: 'LinkedTransferQueue' },
{ key: 6, display_name: 'PriorityBlockingQueue' },
{ key: 9, display_name: 'ResizableLinkedBlockingQueue (动态修改队列大小)' },
{ key: 99, display_name: 'CustomBlockingQueue自定义 SPI 队列)' },
],
rejectedOptions: [
{ key: 1, display_name: 'CallerRunsPolicy' },
@ -487,6 +502,8 @@ export default {
itemId: '',
rejectedType: null,
customRejectedType: null,
queueType: null,
customQueueType: null,
coreSize: 4,
maxSize: 8,
},
@ -585,6 +602,7 @@ export default {
},
resetTemp() {
this.isRejectShow = false;
this.isQueueShow = false;
this.isEdit = false;
this.temp = {
id: undefined,
@ -592,6 +610,8 @@ export default {
itemId: '',
rejectedType: null,
customRejectedType: null,
queueType: null,
customQueueType: null,
isAlarm: '',
allowCoreThreadTimeOut: '',
livenessAlarm: '',
@ -636,6 +656,13 @@ export default {
this.temp.rejectedType = this.temp.customRejectedType;
}
}
if (this.isQueueShow) {
if (this.temp.customQueueType == null) {
this.temp.queueType = 9;
} else {
this.temp.queueType = this.temp.customQueueType;
}
}
threadPoolApi.created(this.temp).then(() => {
this.fetchData();
this.dialogFormVisible = false;
@ -666,6 +693,25 @@ export default {
} else {
this.isRejectShow = false;
}
//
let queueType = this.temp.queueType;
if (
queueType != 1 &&
queueType != 2 &&
queueType != 3 &&
queueType != 4 &&
queueType != 5 &&
queueType != 6 &&
queueType != 9
) {
this.isQueueShow = true;
this.temp.customQueueType = this.temp.queueType;
this.temp.queueType = 99;
} else {
this.isQueueShow = false;
}
this.dialogStatus = 'update';
this.dialogFormVisible = true;
this.isEdit = false;
@ -697,6 +743,23 @@ export default {
this.temp.rejectedType = this.temp.customRejectedType;
}
}
//
let queueType = this.temp.queueType;
if (
queueType != 1 &&
queueType != 2 &&
queueType != 3 &&
queueType != 4 &&
queueType != 5 &&
queueType != 6 &&
queueType != 9
) {
if (this.temp.customQueueType != null) {
this.temp.queueType = this.temp.customQueueType;
}
}
const tempData = Object.assign({}, this.temp);
threadPoolApi.updated(tempData).then(() => {
this.fetchData();
@ -743,6 +806,8 @@ export default {
} else if (value === 5) {
this.temp.capacity = 2147483647;
}
//
this.isQueueShow = value === 99 ? true : false;
this.$forceUpdate();
},

@ -351,6 +351,8 @@ export default {
return 'PriorityBlockingQueue';
} else if ('9' == type) {
return 'ResizableLinkedBlockingQueue';
} else {
return 'CustomBlockingQueue_' + type;
}
},
rejectedTypeFilter(type) {

@ -181,4 +181,3 @@ public class AdapterThreadPoolElasticSearchMonitorHandler extends AbstractAdapte
private String alias;
}
}

@ -22,6 +22,7 @@ import cn.hippo4j.common.extension.enums.DelEnum;
import cn.hippo4j.common.model.register.DynamicThreadPoolRegisterParameter;
import cn.hippo4j.common.model.register.DynamicThreadPoolRegisterWrapper;
import cn.hippo4j.common.model.register.notify.DynamicThreadPoolRegisterServerNotifyParameter;
import cn.hippo4j.common.executor.support.BlockingQueueTypeEnum;
import cn.hippo4j.common.toolkit.Assert;
import cn.hippo4j.common.toolkit.BeanUtil;
import cn.hippo4j.common.toolkit.CollectionUtil;
@ -305,7 +306,7 @@ public class ConfigServiceImpl implements ConfigService {
* @return
*/
private Integer getQueueCapacityByType(ConfigAllInfo config) {
int queueCapacity;
Integer queueCapacity;
if (LINKED_TRANSFER_QUEUE.getType().equals(config.getQueueType())) {
queueCapacity = Integer.MAX_VALUE;
} else {
@ -317,7 +318,16 @@ public class ConfigServiceImpl implements ConfigService {
LINKED_BLOCKING_DEQUE.getType(),
PRIORITY_BLOCKING_QUEUE.getType(),
RESIZABLE_LINKED_BLOCKING_QUEUE.getType()).collect(Collectors.toList());
boolean setDefaultFlag = queueTypes.contains(config.getQueueType()) && (config.getCapacity() == null || Objects.equals(config.getCapacity(), 0));
boolean setDefaultFlag = queueTypes.contains(config.getQueueType()) && (queueCapacity == null || Objects.equals(queueCapacity, 0));
// Apply default capacity for custom queue types as well
if (!setDefaultFlag && (queueCapacity == null || Objects.equals(queueCapacity, 0))) {
String queueName = BlockingQueueTypeEnum.getBlockingQueueNameByType(config.getQueueType());
if (queueName.isEmpty()) {
setDefaultFlag = true;
}
}
if (setDefaultFlag) {
queueCapacity = DEFAULT_QUEUE_CAPACITY;
}

@ -0,0 +1,265 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cn.hippo4j.config.service.biz.impl;
import cn.hippo4j.config.model.ConfigAllInfo;
import org.junit.Assert;
import org.junit.Test;
import java.lang.reflect.Method;
import static cn.hippo4j.common.executor.support.BlockingQueueTypeEnum.*;
/**
* Test for ConfigServiceImpl.getQueueCapacityByType method
* Verifies queue capacity handling for both built-in and custom queue types
*/
public class ConfigServiceQueueCapacityTest {
private static final int DEFAULT_QUEUE_CAPACITY = 1024;
/**
* Use reflection to invoke private method getQueueCapacityByType
*/
private Integer invokeGetQueueCapacityByType(ConfigAllInfo config) throws Exception {
ConfigServiceImpl configService = new ConfigServiceImpl(null, null, null, null);
Method method = ConfigServiceImpl.class.getDeclaredMethod("getQueueCapacityByType", ConfigAllInfo.class);
method.setAccessible(true);
return (Integer) method.invoke(configService, config);
}
/**
* Test Case 1: Built-in queue with valid capacity
* Expected: Return the configured capacity
*/
@Test
public void testBuiltInQueueWithValidCapacity() throws Exception {
System.out.println("========== Test Case 1: Built-in queue with valid capacity ==========");
ConfigAllInfo config = new ConfigAllInfo();
config.setQueueType(ARRAY_BLOCKING_QUEUE.getType());
config.setCapacity(2048);
Integer result = invokeGetQueueCapacityByType(config);
Assert.assertEquals("Should return configured capacity", Integer.valueOf(2048), result);
System.out.println("Built-in queue with capacity 2048 returns: " + result);
}
/**
* Test Case 2: Built-in queue with null capacity
* Expected: Return default capacity (1024)
*/
@Test
public void testBuiltInQueueWithNullCapacity() throws Exception {
System.out.println("\n========== Test Case 2: Built-in queue with null capacity ==========");
ConfigAllInfo config = new ConfigAllInfo();
config.setQueueType(LINKED_BLOCKING_QUEUE.getType());
config.setCapacity(null);
Integer result = invokeGetQueueCapacityByType(config);
Assert.assertEquals("Should return default capacity", Integer.valueOf(DEFAULT_QUEUE_CAPACITY), result);
System.out.println("Built-in queue with null capacity returns default: " + result);
}
/**
* Test Case 3: Built-in queue with zero capacity
* Expected: Return default capacity (1024)
*/
@Test
public void testBuiltInQueueWithZeroCapacity() throws Exception {
System.out.println("\n========== Test Case 3: Built-in queue with zero capacity ==========");
ConfigAllInfo config = new ConfigAllInfo();
config.setQueueType(LINKED_BLOCKING_DEQUE.getType());
config.setCapacity(0);
Integer result = invokeGetQueueCapacityByType(config);
Assert.assertEquals("Should return default capacity", Integer.valueOf(DEFAULT_QUEUE_CAPACITY), result);
System.out.println("Built-in queue with zero capacity returns default: " + result);
}
/**
* Test Case 4: LinkedTransferQueue with any capacity
* Expected: Return Integer.MAX_VALUE (LinkedTransferQueue doesn't support capacity limit)
*/
@Test
public void testLinkedTransferQueueReturnsMaxValue() throws Exception {
System.out.println("\n========== Test Case 4: LinkedTransferQueue returns MAX_VALUE ==========");
ConfigAllInfo config = new ConfigAllInfo();
config.setQueueType(LINKED_TRANSFER_QUEUE.getType());
config.setCapacity(1024);
Integer result = invokeGetQueueCapacityByType(config);
Assert.assertEquals("Should return Integer.MAX_VALUE", Integer.valueOf(Integer.MAX_VALUE), result);
System.out.println("LinkedTransferQueue returns MAX_VALUE: " + result);
}
/**
* Test Case 5: ResizableLinkedBlockingQueue with valid capacity
* Expected: Return the configured capacity
*/
@Test
public void testResizableQueueWithValidCapacity() throws Exception {
System.out.println("\n========== Test Case 5: ResizableLinkedBlockingQueue with valid capacity ==========");
ConfigAllInfo config = new ConfigAllInfo();
config.setQueueType(RESIZABLE_LINKED_BLOCKING_QUEUE.getType());
config.setCapacity(4096);
Integer result = invokeGetQueueCapacityByType(config);
Assert.assertEquals("Should return configured capacity", Integer.valueOf(4096), result);
System.out.println("ResizableLinkedBlockingQueue with capacity 4096 returns: " + result);
}
/**
* Test Case 6: Custom queue type with valid capacity
* Expected: Return the configured capacity
*/
@Test
public void testCustomQueueWithValidCapacity() throws Exception {
System.out.println("\n========== Test Case 6: Custom queue with valid capacity ==========");
ConfigAllInfo config = new ConfigAllInfo();
config.setQueueType(10001); // Custom queue type ID
config.setCapacity(512);
Integer result = invokeGetQueueCapacityByType(config);
Assert.assertEquals("Should return configured capacity", Integer.valueOf(512), result);
System.out.println("Custom queue with capacity 512 returns: " + result);
}
/**
* Test Case 7: Custom queue type with null capacity
* Expected: Return default capacity (1024)
* This is the FIX - previously this would return null
*/
@Test
public void testCustomQueueWithNullCapacity() throws Exception {
System.out.println("\n========== Test Case 7: Custom queue with null capacity (FIX) ==========");
ConfigAllInfo config = new ConfigAllInfo();
config.setQueueType(10001); // Custom queue type ID
config.setCapacity(null);
Integer result = invokeGetQueueCapacityByType(config);
Assert.assertEquals("Should return default capacity for custom queue",
Integer.valueOf(DEFAULT_QUEUE_CAPACITY), result);
System.out.println("Custom queue with null capacity returns default: " + result);
System.out.println(" This is the FIX - previously would return null!");
}
/**
* Test Case 8: Custom queue type with zero capacity
* Expected: Return default capacity (1024)
* This is the FIX - previously this would return 0
*/
@Test
public void testCustomQueueWithZeroCapacity() throws Exception {
System.out.println("\n========== Test Case 8: Custom queue with zero capacity (FIX) ==========");
ConfigAllInfo config = new ConfigAllInfo();
config.setQueueType(20001); // Another custom queue type ID
config.setCapacity(0);
Integer result = invokeGetQueueCapacityByType(config);
Assert.assertEquals("Should return default capacity for custom queue",
Integer.valueOf(DEFAULT_QUEUE_CAPACITY), result);
System.out.println("Custom queue with zero capacity returns default: " + result);
System.out.println(" This is the FIX - previously would return 0!");
}
/**
* Test Case 9: Multiple custom queue types
* Expected: All custom queue types should be handled consistently
*/
@Test
public void testMultipleCustomQueueTypes() throws Exception {
System.out.println("\n========== Test Case 9: Multiple custom queue types consistency ==========");
int[] customQueueTypes = {10001, 20001, 30001, 99999};
for (int queueType : customQueueTypes) {
ConfigAllInfo config = new ConfigAllInfo();
config.setQueueType(queueType);
config.setCapacity(null);
Integer result = invokeGetQueueCapacityByType(config);
Assert.assertEquals("All custom queues should return default capacity",
Integer.valueOf(DEFAULT_QUEUE_CAPACITY), result);
System.out.println("Custom queue type " + queueType + " with null capacity returns: " + result);
}
}
/**
* Test Case 10: Boundary test - very large capacity
* Expected: Return the configured large capacity
*/
@Test
public void testVeryLargeCapacity() throws Exception {
System.out.println("\n========== Test Case 10: Very large capacity ==========");
ConfigAllInfo config = new ConfigAllInfo();
config.setQueueType(ARRAY_BLOCKING_QUEUE.getType());
config.setCapacity(1000000);
Integer result = invokeGetQueueCapacityByType(config);
Assert.assertEquals("Should return configured large capacity", Integer.valueOf(1000000), result);
System.out.println("Built-in queue with capacity 1000000 returns: " + result);
}
/**
* Test Case 11: Verify consistency - all built-in queues with null capacity
* Expected: All should return default capacity (1024)
*/
@Test
public void testAllBuiltInQueuesWithNullCapacity() throws Exception {
System.out.println("\n========== Test Case 11: All built-in queues with null capacity ==========");
Integer[] builtInQueueTypes = {
ARRAY_BLOCKING_QUEUE.getType(),
LINKED_BLOCKING_QUEUE.getType(),
LINKED_BLOCKING_DEQUE.getType(),
PRIORITY_BLOCKING_QUEUE.getType(),
RESIZABLE_LINKED_BLOCKING_QUEUE.getType()
};
for (Integer queueType : builtInQueueTypes) {
ConfigAllInfo config = new ConfigAllInfo();
config.setQueueType(queueType);
config.setCapacity(null);
Integer result = invokeGetQueueCapacityByType(config);
Assert.assertEquals("All built-in queues should return default capacity",
Integer.valueOf(DEFAULT_QUEUE_CAPACITY), result);
System.out.println("Queue type " + queueType + " with null capacity returns default: " + result);
}
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1 @@
.monitor-wrapper[data-v-3957ed61]{width:100%;height:calc(100vh - 50px);display:grid;grid-template-rows:60px 1fr 2fr;grid-template-columns:240px 2fr 1fr;background-color:#ebebf3;gap:10px;padding:10px;-webkit-box-sizing:border-box;box-sizing:border-box}.monitor-wrapper>div[data-v-3957ed61]{border-radius:8px;background-color:#fff;padding:12px}.monitor-wrapper .chart-title[data-v-3957ed61]{font-size:16px;font-weight:bolder;margin:0}.monitor-wrapper .info-wrapper[data-v-3957ed61]::-webkit-scrollbar{display:none}.monitor-wrapper .info-wrapper[data-v-3957ed61]{grid-row:1/4;grid-column:1/2;padding:20rpx;overflow-y:scroll}.monitor-wrapper .info-wrapper .top-card[data-v-3957ed61]{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-pack:distribute;justify-content:space-around;padding:20px 0;-webkit-box-align:center;-ms-flex-align:center;align-items:center;color:rgba(67,95,129,.9);font-weight:600}.monitor-wrapper .info-wrapper .top-card .svg-icon[data-v-3957ed61]{width:60px;height:60px;left:0}.monitor-wrapper .info-wrapper .top-card .top-icon[data-v-3957ed61]{width:40px}.monitor-wrapper .info-wrapper .query-monitoring[data-v-3957ed61]{margin-bottom:30px}.monitor-wrapper .info-wrapper .query-monitoring .title[data-v-3957ed61]{padding:0 0 10px 5px;font-weight:600;color:rgba(67,95,129,.9)}.monitor-wrapper .info-wrapper .query-monitoring .el-cascader[data-v-3957ed61]{width:100%}.monitor-wrapper .info-wrapper .info-card[data-v-3957ed61]{background-color:rgba(67,95,129,.9);border-radius:8px;padding:20px;margin-bottom:20px;color:#fefefe;font-family:HelveticaNeue-Medium,Helvetica Medium,PingFangSC-Medium,STHeitiSC-Medium,Microsoft YaHei Bold,Arial,sans-serif}.monitor-wrapper .info-wrapper .info-card .data-num[data-v-3957ed61],.monitor-wrapper .info-wrapper .info-card .info-card-title[data-v-3957ed61]{font-size:14px;font-weight:500;padding-bottom:10px}.monitor-wrapper .info-wrapper .info-card .info-card-show[data-v-3957ed61]{display:none}.monitor-wrapper .info-wrapper .info-card .info-card-item:hover .info-card-show[data-v-3957ed61]{display:block;position:absolute;left:30px;background:#313131;border-radius:4px;padding:5px 8px 6px 8px;-webkit-box-shadow:1px 1px 5px 239;box-shadow:1px 1px 5px 239;z-index:9999}.monitor-wrapper .info-wrapper .info-card .operation-list[data-v-3957ed61]{color:#a9a9a9}.monitor-wrapper .info-wrapper .tp-card[data-v-3957ed61]{border:1px solid #dfe6ec;padding:20px 10px 30px 10px;border-radius:8px;background-color:rgba(67,95,129,.9);color:#fff}.monitor-wrapper .info-wrapper .tp-card .tp-item[data-v-3957ed61]{border-bottom:1px solid #e3e3e3;line-height:40px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;font-size:14px;-ms-flex-wrap:wrap;flex-wrap:wrap;overflow:hidden}.monitor-wrapper .info-wrapper .tp-card .tp-item .tp-label[data-v-3957ed61]{width:600}.monitor-wrapper .info-wrapper .tp-card .info-card-show[data-v-3957ed61]{display:none}.monitor-wrapper .info-wrapper .tp-card .tp-item:hover .info-card-show[data-v-3957ed61]{display:block;position:absolute;background:#313131;border-radius:4px;padding:5px 8px 6px 8px;-webkit-box-shadow:1px 1px 5px 239;box-shadow:1px 1px 5px 239;z-index:9999}.monitor-wrapper .search-wrapper[data-v-3957ed61]{grid-row:1/2;grid-column:2/4;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.monitor-wrapper .search-wrapper .demonstration[data-v-3957ed61]{margin:0 10px;font-weight:600;color:#3a3b3c}.monitor-wrapper .search-wrapper .button[data-v-3957ed61]{background:rgba(67,95,129,.9);color:#fff;margin-left:20px}.monitor-wrapper .center-chart-wrapper[data-v-3957ed61]{grid-row:2/3;grid-column:2/4}.monitor-wrapper .bottom-chart-wraper[data-v-3957ed61]{grid-row:3/4;grid-column:2/4;display:grid;grid-template-columns:1fr 1fr;grid-template-rows:1fr 1fr;gap:24px}.monitor-wrapper .bottom-chart-wraper .inner-chart[data-v-3957ed61]{grid-column:span 1;grid-row:span 1}.monitor-wrapper .bottom-no-wraper[data-v-3957ed61]{width:100%;height:100%;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;font-size:20px;font-weight:600;color:#818689;font-family:Courier New,Courier,monospace}

@ -1 +1 @@
.waves-ripple{position:absolute;border-radius:100%;background-color:rgba(0,0,0,.15);background-clip:padding-box;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transform:scale(0);transform:scale(0);opacity:1}.waves-ripple.z-active{opacity:0;-webkit-transform:scale(2);transform:scale(2);-webkit-transition:opacity 1.2s ease-out,-webkit-transform .6s ease-out;transition:opacity 1.2s ease-out,-webkit-transform .6s ease-out;transition:opacity 1.2s ease-out,transform .6s ease-out;transition:opacity 1.2s ease-out,transform .6s ease-out,-webkit-transform .6s ease-out}.pagination-container[data-v-df7d1fa0]{background:#fff;padding:32px 16px}.pagination-container.hidden[data-v-df7d1fa0]{display:none}[data-v-a1fe05c0]::-webkit-scrollbar{width:8px;height:8px}[data-v-a1fe05c0]::-webkit-scrollbar-track{border-radius:5px;background:rgba(0,0,0,.06);-webkit-box-shadow:inset 0 0 5px rgba(0,0,0,.08)}[data-v-a1fe05c0]::-webkit-scrollbar-thumb{border-radius:5px;background:rgba(0,0,0,.12);-webkit-box-shadow:inset 0 0 10px rgba(0,0,0,.2)}.stack-info[data-v-a1fe05c0]{height:400px;overflow:auto}.stack-info>li[data-v-a1fe05c0]{margin-bottom:24px}.stack-info>li p[data-v-a1fe05c0]:first-child{color:#06f;font-weight:600;margin-top:10px}.stack-info>li ul[data-v-a1fe05c0]{margin-left:30px}.stack-info>li ul li[data-v-a1fe05c0]{color:#fc5531;text-align:justify;margin:10px auto}
.waves-ripple{position:absolute;border-radius:100%;background-color:rgba(0,0,0,.15);background-clip:padding-box;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transform:scale(0);transform:scale(0);opacity:1}.waves-ripple.z-active{opacity:0;-webkit-transform:scale(2);transform:scale(2);-webkit-transition:opacity 1.2s ease-out,-webkit-transform .6s ease-out;transition:opacity 1.2s ease-out,-webkit-transform .6s ease-out;transition:opacity 1.2s ease-out,transform .6s ease-out;transition:opacity 1.2s ease-out,transform .6s ease-out,-webkit-transform .6s ease-out}.pagination-container[data-v-df7d1fa0]{background:#fff;padding:32px 16px}.pagination-container.hidden[data-v-df7d1fa0]{display:none}[data-v-7ec7eb11]::-webkit-scrollbar{width:8px;height:8px}[data-v-7ec7eb11]::-webkit-scrollbar-track{border-radius:5px;background:rgba(0,0,0,.06);-webkit-box-shadow:inset 0 0 5px rgba(0,0,0,.08)}[data-v-7ec7eb11]::-webkit-scrollbar-thumb{border-radius:5px;background:rgba(0,0,0,.12);-webkit-box-shadow:inset 0 0 10px rgba(0,0,0,.2)}.stack-info[data-v-7ec7eb11]{height:400px;overflow:auto}.stack-info>li[data-v-7ec7eb11]{margin-bottom:24px}.stack-info>li p[data-v-7ec7eb11]:first-child{color:#06f;font-weight:600;margin-top:10px}.stack-info>li ul[data-v-7ec7eb11]{margin-left:30px}.stack-info>li ul li[data-v-7ec7eb11]{color:#fc5531;text-align:justify;margin:10px auto}

@ -1 +0,0 @@
.waves-ripple{position:absolute;border-radius:100%;background-color:rgba(0,0,0,.15);background-clip:padding-box;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transform:scale(0);transform:scale(0);opacity:1}.waves-ripple.z-active{opacity:0;-webkit-transform:scale(2);transform:scale(2);-webkit-transition:opacity 1.2s ease-out,-webkit-transform .6s ease-out;transition:opacity 1.2s ease-out,-webkit-transform .6s ease-out;transition:opacity 1.2s ease-out,transform .6s ease-out;transition:opacity 1.2s ease-out,transform .6s ease-out,-webkit-transform .6s ease-out}.dashboard-editor-container[data-v-5ce168aa]{padding:32px;background-color:#f0f2f5;position:relative;min-height:100vh}

@ -1 +1 @@
.github-corner:hover .octo-arm[data-v-fedac698]{-webkit-animation:octocat-wave-data-v-fedac698 .56s ease-in-out;animation:octocat-wave-data-v-fedac698 .56s ease-in-out}@-webkit-keyframes octocat-wave-data-v-fedac698{0%,to{-webkit-transform:rotate(0);transform:rotate(0)}20%,60%{-webkit-transform:rotate(-25deg);transform:rotate(-25deg)}40%,80%{-webkit-transform:rotate(10deg);transform:rotate(10deg)}}@keyframes octocat-wave-data-v-fedac698{0%,to{-webkit-transform:rotate(0);transform:rotate(0)}20%,60%{-webkit-transform:rotate(-25deg);transform:rotate(-25deg)}40%,80%{-webkit-transform:rotate(10deg);transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm[data-v-fedac698]{-webkit-animation:none;animation:none}.github-corner .octo-arm[data-v-fedac698]{-webkit-animation:octocat-wave-data-v-fedac698 .56s ease-in-out;animation:octocat-wave-data-v-fedac698 .56s ease-in-out}}.panel-group[data-v-b4165a68]{margin-top:18px}.panel-group .card-panel-col[data-v-b4165a68]{margin-bottom:32px}.panel-group .card-panel[data-v-b4165a68]{height:108px;cursor:pointer;font-size:12px;position:relative;overflow:hidden;color:#666;background:#fff;-webkit-box-shadow:4px 4px 40px rgba(0,0,0,.05);box-shadow:4px 4px 40px rgba(0,0,0,.05);border-color:rgba(0,0,0,.05)}.panel-group .card-panel:hover .card-panel-icon-wrapper[data-v-b4165a68]{color:#fff}.panel-group .card-panel:hover .icon-people[data-v-b4165a68]{background:#dae8d6}.panel-group .card-panel:hover .icon-message[data-v-b4165a68]{background:#36a3f7}.panel-group .card-panel:hover .icon-money[data-v-b4165a68]{background:#a0a6f4}.panel-group .card-panel:hover .icon-shopping[data-v-b4165a68]{background:#dae8d6}.panel-group .card-panel .icon-people[data-v-b4165a68]{color:#40c9c6}.panel-group .card-panel .icon-message[data-v-b4165a68]{color:#36a3f7}.panel-group .card-panel .icon-money[data-v-b4165a68]{color:#a0a6f4}.panel-group .card-panel .icon-shopping[data-v-b4165a68]{color:#34bfa3}.panel-group .card-panel .card-panel-icon-wrapper[data-v-b4165a68]{float:left;margin:14px 0 0 14px;padding:16px;-webkit-transition:all .38s ease-out;transition:all .38s ease-out;border-radius:6px}.panel-group .card-panel .card-panel-icon[data-v-b4165a68]{float:left;font-size:48px}.panel-group .card-panel .card-panel-description[data-v-b4165a68]{float:right;font-weight:700;margin:26px;margin-left:0}.panel-group .card-panel .card-panel-description .card-panel-text[data-v-b4165a68]{line-height:18px;color:rgba(0,0,0,.45);font-size:16px;margin-bottom:12px}.panel-group .card-panel .card-panel-description .card-panel-num[data-v-b4165a68]{font-size:20px}@media(max-width:550px){.card-panel-description[data-v-b4165a68]{display:none}.card-panel-icon-wrapper[data-v-b4165a68]{float:none!important;width:100%;height:100%;margin:0!important}.card-panel-icon-wrapper .svg-icon[data-v-b4165a68]{display:block;margin:14px auto!important;float:none!important}}.dashboard-editor-container[data-v-1cb2dce5]{padding:32px;background-color:#f0f2f5;position:relative}.dashboard-editor-container .github-corner[data-v-1cb2dce5]{position:absolute;top:0;border:0;right:0}.dashboard-editor-container .el-form-item[data-v-1cb2dce5]{margin-bottom:5px!important;padding-bottom:20px}.dashboard-editor-container .chart-wrapper[data-v-1cb2dce5]{background:#fff;padding:16px 16px 0;margin-bottom:32px}@media(max-width:1024px){.chart-wrapper[data-v-1cb2dce5]{padding:8px}}
.github-corner:hover .octo-arm[data-v-fedac698]{-webkit-animation:octocat-wave-data-v-fedac698 .56s ease-in-out;animation:octocat-wave-data-v-fedac698 .56s ease-in-out}@-webkit-keyframes octocat-wave-data-v-fedac698{0%,to{-webkit-transform:rotate(0);transform:rotate(0)}20%,60%{-webkit-transform:rotate(-25deg);transform:rotate(-25deg)}40%,80%{-webkit-transform:rotate(10deg);transform:rotate(10deg)}}@keyframes octocat-wave-data-v-fedac698{0%,to{-webkit-transform:rotate(0);transform:rotate(0)}20%,60%{-webkit-transform:rotate(-25deg);transform:rotate(-25deg)}40%,80%{-webkit-transform:rotate(10deg);transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm[data-v-fedac698]{-webkit-animation:none;animation:none}.github-corner .octo-arm[data-v-fedac698]{-webkit-animation:octocat-wave-data-v-fedac698 .56s ease-in-out;animation:octocat-wave-data-v-fedac698 .56s ease-in-out}}.panel-group[data-v-b4165a68]{margin-top:18px}.panel-group .card-panel-col[data-v-b4165a68]{margin-bottom:32px}.panel-group .card-panel[data-v-b4165a68]{height:108px;cursor:pointer;font-size:12px;position:relative;overflow:hidden;color:#666;background:#fff;-webkit-box-shadow:4px 4px 40px rgba(0,0,0,.05);box-shadow:4px 4px 40px rgba(0,0,0,.05);border-color:rgba(0,0,0,.05)}.panel-group .card-panel:hover .card-panel-icon-wrapper[data-v-b4165a68]{color:#fff}.panel-group .card-panel:hover .icon-people[data-v-b4165a68]{background:#dae8d6}.panel-group .card-panel:hover .icon-message[data-v-b4165a68]{background:#36a3f7}.panel-group .card-panel:hover .icon-money[data-v-b4165a68]{background:#a0a6f4}.panel-group .card-panel:hover .icon-shopping[data-v-b4165a68]{background:#dae8d6}.panel-group .card-panel .icon-people[data-v-b4165a68]{color:#40c9c6}.panel-group .card-panel .icon-message[data-v-b4165a68]{color:#36a3f7}.panel-group .card-panel .icon-money[data-v-b4165a68]{color:#a0a6f4}.panel-group .card-panel .icon-shopping[data-v-b4165a68]{color:#34bfa3}.panel-group .card-panel .card-panel-icon-wrapper[data-v-b4165a68]{float:left;margin:14px 0 0 14px;padding:16px;-webkit-transition:all .38s ease-out;transition:all .38s ease-out;border-radius:6px}.panel-group .card-panel .card-panel-icon[data-v-b4165a68]{float:left;font-size:48px}.panel-group .card-panel .card-panel-description[data-v-b4165a68]{float:right;font-weight:700;margin:26px;margin-left:0}.panel-group .card-panel .card-panel-description .card-panel-text[data-v-b4165a68]{line-height:18px;color:rgba(0,0,0,.45);font-size:16px;margin-bottom:12px}.panel-group .card-panel .card-panel-description .card-panel-num[data-v-b4165a68]{font-size:20px}@media(max-width:550px){.card-panel-description[data-v-b4165a68]{display:none}.card-panel-icon-wrapper[data-v-b4165a68]{float:none!important;width:100%;height:100%;margin:0!important}.card-panel-icon-wrapper .svg-icon[data-v-b4165a68]{display:block;margin:14px auto!important;float:none!important}}.dashboard-editor-container[data-v-112b6b2e]{padding:32px;background-color:#f0f2f5;position:relative}.dashboard-editor-container .github-corner[data-v-112b6b2e]{position:absolute;top:0;border:0;right:0}.dashboard-editor-container .el-form-item[data-v-112b6b2e]{margin-bottom:5px!important;padding-bottom:20px}.dashboard-editor-container .chart-wrapper[data-v-112b6b2e]{background:#fff;padding:16px 16px 0;margin-bottom:32px}@media(max-width:1024px){.chart-wrapper[data-v-112b6b2e]{padding:8px}}

@ -1 +1 @@
.social-signup-container[data-v-7309fbbb]{margin:20px 0}.social-signup-container .sign-btn[data-v-7309fbbb]{display:inline-block;cursor:pointer}.social-signup-container .icon[data-v-7309fbbb]{color:#fff;font-size:24px;margin-top:8px}.social-signup-container .qq-svg-container[data-v-7309fbbb],.social-signup-container .wx-svg-container[data-v-7309fbbb]{display:inline-block;width:40px;height:40px;line-height:40px;text-align:center;padding-top:1px;border-radius:4px;margin-bottom:20px;margin-right:5px}.social-signup-container .wx-svg-container[data-v-7309fbbb]{background-color:#24da70}.social-signup-container .qq-svg-container[data-v-7309fbbb]{background-color:#6ba2d6;margin-left:50px}@supports(-webkit-mask:none) and (not (cater-color:#fff)){.login-container .el-input input{color:#fff}}.login-container .el-input{display:inline-block;height:47px;width:85%}.login-container .el-input input{background:transparent;border:0;-webkit-appearance:none;border-radius:0;padding:12px 5px 12px 15px;color:#fff;height:47px;caret-color:#fff}.login-container .el-input input:-webkit-autofill{-webkit-box-shadow:0 0 0 1000px #283443 inset!important;box-shadow:inset 0 0 0 1000px #283443!important;-webkit-text-fill-color:#fff!important}.login-container .el-form-item{border:1px solid hsla(0,0%,100%,.1);background:rgba(0,0,0,.1);border-radius:5px;color:#454545}.login-container[data-v-c62df1a8]{min-height:100%;width:100%;background-color:#2d3a4b;overflow:hidden}.login-container .login-form[data-v-c62df1a8]{position:relative;width:520px;max-width:100%;padding:160px 35px 0;margin:0 auto;overflow:hidden}.login-container .tips[data-v-c62df1a8]{font-size:14px;color:#fff;margin-bottom:10px}.login-container .tips span[data-v-c62df1a8]:first-of-type{margin-right:16px}.login-container .svg-container[data-v-c62df1a8]{padding:6px 5px 6px 15px;color:#889aa4;vertical-align:middle;width:30px;display:inline-block}.login-container .title-container[data-v-c62df1a8]{position:relative}.login-container .title-container .title[data-v-c62df1a8]{font-size:26px;color:#eee;margin:0 auto 40px auto;text-align:center;font-weight:700}.login-container .show-pwd[data-v-c62df1a8]{position:absolute;right:10px;top:7px;font-size:16px;color:#889aa4;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.login-container .thirdparty-button[data-v-c62df1a8]{position:absolute;right:0;bottom:6px}@media only screen and (max-width:470px){.login-container .thirdparty-button[data-v-c62df1a8]{display:none}}
.social-signup-container[data-v-7309fbbb]{margin:20px 0}.social-signup-container .sign-btn[data-v-7309fbbb]{display:inline-block;cursor:pointer}.social-signup-container .icon[data-v-7309fbbb]{color:#fff;font-size:24px;margin-top:8px}.social-signup-container .qq-svg-container[data-v-7309fbbb],.social-signup-container .wx-svg-container[data-v-7309fbbb]{display:inline-block;width:40px;height:40px;line-height:40px;text-align:center;padding-top:1px;border-radius:4px;margin-bottom:20px;margin-right:5px}.social-signup-container .wx-svg-container[data-v-7309fbbb]{background-color:#24da70}.social-signup-container .qq-svg-container[data-v-7309fbbb]{background-color:#6ba2d6;margin-left:50px}@supports(-webkit-mask:none) and (not (cater-color:#fff)){.login-container .el-input input{color:#fff}}.login-container .el-input{display:inline-block;height:47px;width:85%}.login-container .el-input input{background:transparent;border:0;-webkit-appearance:none;border-radius:0;padding:12px 5px 12px 15px;color:#fff;height:47px;caret-color:#fff}.login-container .el-input input:-webkit-autofill{-webkit-box-shadow:0 0 0 1000px #283443 inset!important;box-shadow:inset 0 0 0 1000px #283443!important;-webkit-text-fill-color:#fff!important}.login-container .el-form-item{border:1px solid hsla(0,0%,100%,.1);background:rgba(0,0,0,.1);border-radius:5px;color:#454545}.login-container[data-v-4349c150]{min-height:100%;width:100%;background-color:#2d3a4b;overflow:hidden}.login-container .login-form[data-v-4349c150]{position:relative;width:520px;max-width:100%;padding:160px 35px 0;margin:0 auto;overflow:hidden}.login-container .tips[data-v-4349c150]{font-size:14px;color:#fff;margin-bottom:10px}.login-container .tips span[data-v-4349c150]:first-of-type{margin-right:16px}.login-container .svg-container[data-v-4349c150]{padding:6px 5px 6px 15px;color:#889aa4;vertical-align:middle;width:30px;display:inline-block}.login-container .title-container[data-v-4349c150]{position:relative}.login-container .title-container .title[data-v-4349c150]{font-size:26px;color:#eee;margin:0 auto 40px auto;text-align:center;font-weight:700}.login-container .show-pwd[data-v-4349c150]{position:absolute;right:10px;top:7px;font-size:16px;color:#889aa4;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.login-container .thirdparty-button[data-v-4349c150]{position:absolute;right:0;bottom:6px}@media only screen and (max-width:470px){.login-container .thirdparty-button[data-v-4349c150]{display:none}}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1 +1 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-5911c282"],{"1db4":function(t,s,a){"use strict";a.r(s);var e=function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"wscn-http404-container"},[a("div",{staticClass:"wscn-http404"},[t._m(0),t._v(" "),a("div",{staticClass:"bullshit"},[a("div",{staticClass:"bullshit__oops"},[t._v("OOPS!")]),t._v(" "),t._m(1),t._v(" "),a("div",{staticClass:"bullshit__headline"},[t._v(t._s(t.message))]),t._v(" "),a("div",{staticClass:"bullshit__info"},[t._v("Please check that the URL you entered is correct, or click the button below to return to the homepage.")]),t._v(" "),a("a",{staticClass:"bullshit__return-home",attrs:{href:""}},[t._v("Back to home")])])])])},c=[function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"pic-404"},[e("img",{staticClass:"pic-404__parent",attrs:{src:a("a36b"),alt:"404"}}),t._v(" "),e("img",{staticClass:"pic-404__child left",attrs:{src:a("26fc"),alt:"404"}}),t._v(" "),e("img",{staticClass:"pic-404__child mid",attrs:{src:a("26fc"),alt:"404"}}),t._v(" "),e("img",{staticClass:"pic-404__child right",attrs:{src:a("26fc"),alt:"404"}})])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"bullshit__info"},[t._v("All rights reserved\n "),a("a",{staticStyle:{color:"#20a0ff"},attrs:{href:"https://wallstreetcn.com",target:"_blank"}},[t._v("wallstreetcn")])])}],i={name:"Page404",computed:{message:function(){return"The webmaster said that you can not enter this page..."}}},l=i,n=(a("89a2"),a("2877")),r=Object(n["a"])(l,e,c,!1,null,"26fcd89f",null);s["default"]=r.exports},"26fc":function(t,s,a){t.exports=a.p+"static/img/404_cloud.0f4bc32b.png"},"89a2":function(t,s,a){"use strict";a("bd40")},a36b:function(t,s,a){t.exports=a.p+"static/img/404.a57b6f31.png"},bd40:function(t,s,a){}}]);
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-06c697fe"],{"1db4":function(t,s,a){"use strict";a.r(s);var e=function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"wscn-http404-container"},[a("div",{staticClass:"wscn-http404"},[t._m(0),t._v(" "),a("div",{staticClass:"bullshit"},[a("div",{staticClass:"bullshit__oops"},[t._v("OOPS!")]),t._v(" "),t._m(1),t._v(" "),a("div",{staticClass:"bullshit__headline"},[t._v(t._s(t.message))]),t._v(" "),a("div",{staticClass:"bullshit__info"},[t._v("Please check that the URL you entered is correct, or click the button below to return to the homepage.")]),t._v(" "),a("a",{staticClass:"bullshit__return-home",attrs:{href:""}},[t._v("Back to home")])])])])},c=[function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"pic-404"},[e("img",{staticClass:"pic-404__parent",attrs:{src:a("a36b"),alt:"404"}}),t._v(" "),e("img",{staticClass:"pic-404__child left",attrs:{src:a("26fc"),alt:"404"}}),t._v(" "),e("img",{staticClass:"pic-404__child mid",attrs:{src:a("26fc"),alt:"404"}}),t._v(" "),e("img",{staticClass:"pic-404__child right",attrs:{src:a("26fc"),alt:"404"}})])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"bullshit__info"},[t._v("All rights reserved\n "),a("a",{staticStyle:{color:"#20a0ff"},attrs:{href:"https://wallstreetcn.com",target:"_blank"}},[t._v("wallstreetcn")])])}],i={name:"Page404",computed:{message:function(){return"The webmaster said that you can not enter this page..."}}},l=i,n=(a("207f"),a("2877")),r=Object(n["a"])(l,e,c,!1,null,"26fcd89f",null);s["default"]=r.exports},"207f":function(t,s,a){"use strict";a("6632")},"26fc":function(t,s,a){t.exports=a.p+"static/img/404_cloud.0f4bc32b.png"},6632:function(t,s,a){},a36b:function(t,s,a){t.exports=a.p+"static/img/404.a57b6f31.png"}}]);

@ -1 +1 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d230a36"],{ecac:function(e,t,a){"use strict";a.r(t);var s=function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("div",{staticClass:"app-container"},[e.user?a("div",[a("el-row",{attrs:{gutter:20}},[a("el-col",{attrs:{span:6,xs:24}},[a("user-card",{attrs:{user:e.user}})],1),e._v(" "),a("el-col",{attrs:{span:18,xs:24}},[a("el-card",[a("el-tabs",{model:{value:e.activeTab,callback:function(t){e.activeTab=t},expression:"activeTab"}},[a("el-tab-pane",{attrs:{label:"Account",name:"account"}},[a("account",{attrs:{user:e.user}})],1)],1)],1)],1)],1)],1):e._e()])},n=[],r=a("5530"),l=(a("b0c0"),a("a15b"),a("2f62")),c=function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("el-form",[a("el-form-item",{attrs:{label:"Name"}},[a("el-input",{model:{value:e.user.name,callback:function(t){e.$set(e.user,"name","string"===typeof t?t.trim():t)},expression:"user.name"}})],1),e._v(" "),a("el-form-item",{attrs:{label:"Email"}},[a("el-input",{model:{value:e.user.email,callback:function(t){e.$set(e.user,"email","string"===typeof t?t.trim():t)},expression:"user.email"}})],1),e._v(" "),a("el-form-item",[a("el-button",{attrs:{type:"primary"},on:{click:e.submit}},[e._v("Update")])],1)],1)},i=[],u={props:{user:{type:Object,default:function(){return{name:"",email:""}}}},methods:{submit:function(){this.$message({message:"User information has been updated successfully",type:"success",duration:5e3})}}},o=u,m=a("2877"),p=Object(m["a"])(o,c,i,!1,null,null,null),b=p.exports,f={name:"Profile",components:{Account:b},data:function(){return{user:{},activeTab:"activity"}},computed:Object(r["a"])({},Object(l["b"])(["name","avatar","roles"])),created:function(){this.getUser()},methods:{getUser:function(){this.user={name:this.name,role:this.roles.join(" | "),email:"admin@test.com",avatar:this.avatar}}}},d=f,v=Object(m["a"])(d,s,n,!1,null,null,null);t["default"]=v.exports}}]);
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d230a36"],{ecac:function(e,t,a){"use strict";a.r(t);var s=function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("div",{staticClass:"app-container"},[e.user?a("div",[a("el-row",{attrs:{gutter:20}},[a("el-col",{attrs:{span:6,xs:24}},[a("user-card",{attrs:{user:e.user}})],1),e._v(" "),a("el-col",{attrs:{span:18,xs:24}},[a("el-card",[a("el-tabs",{model:{value:e.activeTab,callback:function(t){e.activeTab=t},expression:"activeTab"}},[a("el-tab-pane",{attrs:{label:"Account",name:"account"}},[a("account",{attrs:{user:e.user}})],1)],1)],1)],1)],1)],1):e._e()])},n=[],r=a("5530"),l=(a("a15b"),a("b0c0"),a("2f62")),c=function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("el-form",[a("el-form-item",{attrs:{label:"Name"}},[a("el-input",{model:{value:e.user.name,callback:function(t){e.$set(e.user,"name","string"===typeof t?t.trim():t)},expression:"user.name"}})],1),e._v(" "),a("el-form-item",{attrs:{label:"Email"}},[a("el-input",{model:{value:e.user.email,callback:function(t){e.$set(e.user,"email","string"===typeof t?t.trim():t)},expression:"user.email"}})],1),e._v(" "),a("el-form-item",[a("el-button",{attrs:{type:"primary"},on:{click:e.submit}},[e._v("Update")])],1)],1)},i=[],u={props:{user:{type:Object,default:function(){return{name:"",email:""}}}},methods:{submit:function(){this.$message({message:"User information has been updated successfully",type:"success",duration:5e3})}}},o=u,m=a("2877"),p=Object(m["a"])(o,c,i,!1,null,null,null),b=p.exports,f={name:"Profile",components:{Account:b},data:function(){return{user:{},activeTab:"activity"}},computed:Object(r["a"])({},Object(l["b"])(["name","avatar","roles"])),created:function(){this.getUser()},methods:{getUser:function(){this.user={name:this.name,role:this.roles.join(" | "),email:"admin@test.com",avatar:this.avatar}}}},d=f,v=Object(m["a"])(d,s,n,!1,null,null,null);t["default"]=v.exports}}]);

@ -1 +1 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-ef888edc"],{"24e2":function(t,a,i){"use strict";i.r(a);var e=function(){var t=this,a=t.$createElement,i=t._self._c||a;return i("div",{staticClass:"errPage-container"},[i("el-button",{staticClass:"pan-back-btn",attrs:{icon:"arrow-left"},on:{click:t.back}},[t._v("\n 返回\n ")]),t._v(" "),i("el-row",[i("el-col",{attrs:{span:12}},[i("h1",{staticClass:"text-jumbo text-ginormous"},[t._v("\n Oops!\n ")]),t._v("\n gif来源"),i("a",{attrs:{href:"https://zh.airbnb.com/",target:"_blank"}},[t._v("airbnb")]),t._v(" 页面\n "),i("h2",[t._v("你没有权限去该页面")]),t._v(" "),i("h6",[t._v("如有不满请联系你领导")]),t._v(" "),i("ul",{staticClass:"list-unstyled"},[i("li",[t._v("或者你可以去:")]),t._v(" "),i("li",{staticClass:"link-type"},[i("router-link",{attrs:{to:"/dashboard"}},[t._v("\n 回首页\n ")])],1),t._v(" "),i("li",{staticClass:"link-type"},[i("a",{attrs:{href:"https://www.taobao.com/"}},[t._v("随便看看")])]),t._v(" "),i("li",[i("a",{attrs:{href:"#"},on:{click:function(a){a.preventDefault(),t.dialogVisible=!0}}},[t._v("点我看图")])])])]),t._v(" "),i("el-col",{attrs:{span:12}},[i("img",{attrs:{src:t.errGif,width:"313",height:"428",alt:"Girl has dropped her ice cream."}})])],1),t._v(" "),i("el-dialog",{attrs:{visible:t.dialogVisible,title:"随便看"},on:{"update:visible":function(a){t.dialogVisible=a}}},[i("img",{staticClass:"pan-img",attrs:{src:t.ewizardClap}})])],1)},s=[],n=(i("14d9"),i("cc6c")),r=i.n(n),l={name:"Page401",data:function(){return{errGif:r.a+"?"+ +new Date,ewizardClap:"https://wpimg.wallstcn.com/007ef517-bafd-4066-aae4-6883632d9646",dialogVisible:!1}},methods:{back:function(){this.$route.query.noGoBack?this.$router.push({path:"/dashboard"}):this.$router.go(-1)}}},c=l,o=(i("6f5a"),i("2877")),u=Object(o["a"])(c,e,s,!1,null,"6fb1594e",null);a["default"]=u.exports},"6f5a":function(t,a,i){"use strict";i("a68a")},a68a:function(t,a,i){},cc6c:function(t,a,i){t.exports=i.p+"static/img/401.089007e7.gif"}}]);
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-36deb5d7"],{"24e2":function(t,a,i){"use strict";i.r(a);var e=function(){var t=this,a=t.$createElement,i=t._self._c||a;return i("div",{staticClass:"errPage-container"},[i("el-button",{staticClass:"pan-back-btn",attrs:{icon:"arrow-left"},on:{click:t.back}},[t._v("\n 返回\n ")]),t._v(" "),i("el-row",[i("el-col",{attrs:{span:12}},[i("h1",{staticClass:"text-jumbo text-ginormous"},[t._v("\n Oops!\n ")]),t._v("\n gif来源"),i("a",{attrs:{href:"https://zh.airbnb.com/",target:"_blank"}},[t._v("airbnb")]),t._v(" 页面\n "),i("h2",[t._v("你没有权限去该页面")]),t._v(" "),i("h6",[t._v("如有不满请联系你领导")]),t._v(" "),i("ul",{staticClass:"list-unstyled"},[i("li",[t._v("或者你可以去:")]),t._v(" "),i("li",{staticClass:"link-type"},[i("router-link",{attrs:{to:"/dashboard"}},[t._v("\n 回首页\n ")])],1),t._v(" "),i("li",{staticClass:"link-type"},[i("a",{attrs:{href:"https://www.taobao.com/"}},[t._v("随便看看")])]),t._v(" "),i("li",[i("a",{attrs:{href:"#"},on:{click:function(a){a.preventDefault(),t.dialogVisible=!0}}},[t._v("点我看图")])])])]),t._v(" "),i("el-col",{attrs:{span:12}},[i("img",{attrs:{src:t.errGif,width:"313",height:"428",alt:"Girl has dropped her ice cream."}})])],1),t._v(" "),i("el-dialog",{attrs:{visible:t.dialogVisible,title:"随便看"},on:{"update:visible":function(a){t.dialogVisible=a}}},[i("img",{staticClass:"pan-img",attrs:{src:t.ewizardClap}})])],1)},s=[],n=(i("14d9"),i("cc6c")),r=i.n(n),l={name:"Page401",data:function(){return{errGif:r.a+"?"+ +new Date,ewizardClap:"https://wpimg.wallstcn.com/007ef517-bafd-4066-aae4-6883632d9646",dialogVisible:!1}},methods:{back:function(){this.$route.query.noGoBack?this.$router.push({path:"/dashboard"}):this.$router.go(-1)}}},c=l,o=(i("748d"),i("2877")),u=Object(o["a"])(c,e,s,!1,null,"6fb1594e",null);a["default"]=u.exports},"346a":function(t,a,i){},"748d":function(t,a,i){"use strict";i("346a")},cc6c:function(t,a,i){t.exports=i.p+"static/img/401.089007e7.gif"}}]);

@ -1 +0,0 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-9472305a"],{"129f":function(n,e){n.exports=Object.is||function(n,e){return n===e?0!==n||1/n===1/e:n!=n&&e!=e}},"841c":function(n,e,t){"use strict";var a=t("c65b"),c=t("d784"),r=t("825a"),o=t("7234"),i=t("1d80"),u=t("129f"),d=t("577e"),l=t("dc4a"),s=t("14c3");c("search",(function(n,e,t){return[function(e){var t=i(this),c=o(e)?void 0:l(e,n);return c?a(c,e,t):new RegExp(e)[n](d(t))},function(n){var a=r(this),c=d(n),o=t(e,a,c);if(o.done)return o.value;var i=a.lastIndex;u(i,0)||(a.lastIndex=0);var l=s(a,c);return u(a.lastIndex,i)||(a.lastIndex=i),null===l?-1:l.index}]}))},b829:function(n,e,t){"use strict";t.r(e);t("fb6a"),t("ac1f"),t("841c");var a,c,r={name:"AuthRedirect",created:function(){var n=window.location.search.slice(1);window.localStorage&&(window.localStorage.setItem("x-admin-oauth-code",n),window.close())},render:function(n){return n()}},o=r,i=t("2877"),u=Object(i["a"])(o,a,c,!1,null,null,null);e["default"]=u.exports}}]);

@ -0,0 +1 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-9472305a"],{"129f":function(n,e,t){"use strict";n.exports=Object.is||function(n,e){return n===e?0!==n||1/n===1/e:n!==n&&e!==e}},"841c":function(n,e,t){"use strict";var c=t("c65b"),r=t("d784"),a=t("825a"),o=t("861d"),i=t("1d80"),u=t("129f"),d=t("577e"),s=t("dc4a"),l=t("14c3");r("search",(function(n,e,t){return[function(e){var t=i(this),r=o(e)?s(e,n):void 0;return r?c(r,e,t):new RegExp(e)[n](d(t))},function(n){var c=a(this),r=d(n),o=t(e,c,r);if(o.done)return o.value;var i=c.lastIndex;u(i,0)||(c.lastIndex=0);var s=l(c,r);return u(c.lastIndex,i)||(c.lastIndex=i),null===s?-1:s.index}]}))},b829:function(n,e,t){"use strict";t.r(e);t("fb6a"),t("ac1f"),t("841c");var c,r,a={name:"AuthRedirect",created:function(){var n=window.location.search.slice(1);window.localStorage&&(window.localStorage.setItem("x-admin-oauth-code",n),window.close())},render:function(n){return n()}},o=a,i=t("2877"),u=Object(i["a"])(o,c,r,!1,null,null,null);e["default"]=u.exports}}]);

File diff suppressed because one or more lines are too long

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

Loading…
Cancel
Save