前言
🤟 找工作,来万码优才:👉 #小程序://万码优才/r6rqmzDaXpYkJZF
接到对应的需求,需要将其设计合理
以下各个目录是层层递进进行优化!
后端需要有两个关键点:
按钮状态的保存(比如保存在内存、数据库或缓存里)
上传时判断按钮状态,决定是否调用接口
1. Demo
✅ 1. 设计一个控制按钮状态的接口:
@RestController
@RequestMapping("/upload-listen")
public class UploadListenController {
// 用于保存状态,可以替换成 Redis 或数据库
private volatile boolean isListening = false;
@PostMapping("/toggle")
public ResponseEntity<String> toggleListening(@RequestParam boolean enable) {
this.isListening = enable;
return ResponseEntity.ok("监听状态已设置为: " + enable);
}
@GetMapping("/status")
public ResponseEntity<Boolean> getStatus() {
return ResponseEntity.ok(isListening);
}
public boolean isListening() {
return isListening;
}
}
✅ 2. 上传接口中根据状态决定是否走处理逻辑
@RestController
@RequestMapping("/upload")
public class UploadController {
@Autowired
private UploadListenController listenController; // 也可以抽成一个 Service
@PostMapping
public ResponseEntity<String> uploadData(@RequestBody MyData data) {
// 正常上传逻辑
// 如果监听开关是开的,调用指定处理接口
if (listenController.isListening()) {
// 调用接口处理 data
processUpload(data);
}
return ResponseEntity.ok("上传成功");
}
private void processUpload(MyData data) {
// 你想触发的接口逻辑,例如:
System.out.println("监听处理上传数据:" + data);
}
}
🖼️ 前端建议(假设你用 Vue 或 HTML)
用一个开关按钮 <el-switch>
或普通 <button>
调用 /upload-listen/toggle
接口
上传接口照常调用 /upload
后端根据监听状态判断是否走自动处理逻辑
上述这种逻辑,对应肯定是存储在Redis或者其他缓存是最好的,直接获取到!
2. 实战改进
确保项目里引入了 Redis 依赖(Spring Boot 2.7.5 适配):
<!-- pom.xml -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
配置 Redis 连接(application.yml):
spring:
redis:
host: localhost
port: 6379
# password: yourpassword (如果有密码)
- 按钮点击事件
/** AI校验按钮操作 */
const handleAiCheck = async () => {
try {
await message.confirm('是否确认执行 AI 校验?')
aiChecking.value = true
await CabinetPickupApi.startAiCheck()
message.success('AI 校验已开始,稍后查询状态')
} catch {
message.error('AI 校验启动失败')
} finally {
aiChecking.value = false
}
}
- 新增定时查询状态
/** AI校验状态查询 */
const queryAiCheckStatus = async () => {
try {
const res = await CabinetPickupApi.getAiCheckStatus()
aiCheckStatus.value = res || '未开始'
} catch {
aiCheckStatus.value = '查询失败'
}
}
- 新增状态展示
<el-button @click="queryAiCheckStatus">查询AI状态</el-button>
<p>当前状态:{{ aiCheckStatus }}</p>
✅ 4. 后端 AI校验接口
@RestController
@RequestMapping("/gate/cabinet-pickup/ai-check")
@Tag(name = "AI校验")
public class CabinetPickupAiCheckController {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@PostMapping("/start")
@Operation(summary = "开始 AI 校验")
@PreAuthorize("@ss.hasPermission('gate:cabinet-pickup:ai-check')")
public CommonResult<String> startAiCheck() {
// 模拟校验逻辑
redisTemplate.opsForValue().set("ai-check:status", "IN_PROGRESS", 10, TimeUnit.MINUTES);
// 模拟耗时任务
new Thread(() -> {
try {
Thread.sleep(5000); // 模拟 AI 校验耗时
redisTemplate.opsForValue().set("ai-check:status", "COMPLETED", 10, TimeUnit.MINUTES);
} catch (InterruptedException e) {
redisTemplate.opsForValue().set("ai-check:status", "FAILED", 10, TimeUnit.MINUTES);
}
}).start();
return success("AI校验已开始,稍后可查询状态");
}
@GetMapping("/status")
@Operation(summary = "获取 AI 校验状态")
@PreAuthorize("@ss.hasPermission('gate:cabinet-pickup:ai-check')")
public CommonResult<String> getAiCheckStatus() {
String status = redisTemplate.opsForValue().get("ai-check:status");
if (status == null) {
return success("未开始");
}
return success(status);
}
}
上述这种逻辑没有状态显示,不知开启与否,所以还需要进一步优化!
这些都是题外话,我本身已经配置了,而且是默认全局1h的
需要将其设置为无限时间!
3. 状态显示(优化)
界面如下:
-
AI校验按钮是一个**“开关按钮”**(Toggle):
第一次点击时 ➜ 写入 Redis:开启 AI 校验。
再次点击时 ➜ 删除 Redis:关闭 AI 校验。 -
状态持久化在 Redis:
默认全局缓存过期为 1 小时,但这个 Redis key 不能设置过期时间(永久有效) -
前端刷新后仍能看到状态:
页面加载时会调用后端接口,读取 Redis 中的状态,展示按钮是开启还是关闭
✅ 前端修改
- 数据定义
const aiChecking = ref(false)
const aiCheckEnabled = ref(false)
- 获取状态函数
const getAiCheckStatus = async () => {
try {
const res = await CabinetPickupApi.getAiCheckStatus()
aiCheckEnabled.value = res
} catch (e) {
aiCheckEnabled.value = false
}
}
onMounted(() => {
getAiCheckStatus()
})
- 切换按钮函数
const handleAiCheck = async () => {
aiChecking.value = true
try {
const res = await CabinetPickupApi.toggleAiCheck()
aiCheckEnabled.value = res
message.success(`AI校验已${res ? '开启' : '关闭'}`)
} finally {
aiChecking.value = false
}
}
- 按钮 UI(状态感知)
<el-button
type="warning"
:plain="!aiCheckEnabled"
@click="handleAiCheck"
:loading="aiChecking"
v-hasPermi="['gate:cabinet-pickup:export']"
>
<Icon icon="ep:cpu" class="mr-5px" />
{{ aiCheckEnabled ? 'AI校验已开启' : '开启 AI校验' }}
</el-button>
✅ 后端实现
- Redis Toggle 接口(保留现有逻辑)
@Override
public Boolean toggleAiCheck() {
String key = "cabinet:pickup:ai-check:enabled";
Boolean newStatus = redisTemplate.opsForValue().get(key) == null;
if (newStatus) {
redisTemplate.opsForValue().set(key, "true"); // 不设置过期时间,永久有效
} else {
redisTemplate.delete(key);
}
return newStatus;
}
- 新增获取 AI 状态接口
@Override
public Boolean isAiCheckEnabled() {
String key = "cabinet:pickup:ai-check:enabled";
return redisTemplate.hasKey(key);
}
- Controller 示例
@RestController
@RequestMapping("/gate/cabinet-pickup/ai-check")
@Tag(name = "AI校验")
public class CabinetPickupAiCheckController {
@Resource
private CabinetPickupService cabinetPickupService;
@PostMapping("/toggle")
@Operation(summary = "切换 AI 校验开关")
public CommonResult<Boolean> toggleAiCheck() {
return success(cabinetPickupService.toggleAiCheck());
}
@GetMapping("/status")
@Operation(summary = "获取 AI 校验是否开启")
public CommonResult<Boolean> isAiCheckEnabled() {
return success(cabinetPickupService.isAiCheckEnabled());
}
}
初次进入页面调用 /status 获取是否开启
按钮文字/样式实时变更
点击按钮调用 /toggle 切换状态并更新 Redis
Redis 永久保存开关状态,页面刷新也能保持按钮状态不变
后续需要校验逻辑是否正确:
时间是-1,代表没有时间限制
4. 样式细节(优化)
把原本的 el-button 替换为 el-switch,并加上二次确认弹窗,这样用户切换开关时会确认是否开启/关闭 AI 校验,更加友好和安全
💡 1. 替换按钮为 el-switch
<el-switch
v-model="aiCheckEnabled"
:loading="aiChecking"
:active-text="'AI校验已开启'"
:inactive-text="'AI校验已关闭'"
@change="confirmToggleAiCheck"
v-hasPermi="['gate:cabinet-pickup:export']"
/>
💡 2. 增加确认弹窗逻辑
const confirmToggleAiCheck = async (newVal: boolean) => {
try {
await message.confirm(`确认${newVal ? '开启' : '关闭'}AI校验功能?`)
await handleAiCheck() // 调用原切换方法
} catch {
// 用户点击取消,恢复旧值
aiCheckEnabled.value = !newVal
}
}
💡 3. 原 handleAiCheck 方法不变(它只管发送请求)
const handleAiCheck = async () => {
aiChecking.value = true
try {
const res = await CabinetPickupApi.toggleAiCheck()
aiCheckEnabled.value = res
message.success(`AI校验已${res ? '开启' : '关闭'}`)
} catch {
message.error('AI校验操作失败')
} finally {
aiChecking.value = false
}
}
对应的请求如下:
// AI check 的按钮
toggleAiCheck: async () => {
return await request.post({ url: `/gate/cabinet-pickup/aitoggle` })
},
// AI check 的按钮
getAiCheckStatus: async () => {
return await request.get({ url: `/gate/cabinet-pickup/aistatus`})
},
后端请求如下:
@PostMapping("/aitoggle")
@Operation(summary = "切换 AI 校验状态")
@PreAuthorize("@ss.hasPermission('gate:cabinet-pickup:export')")
public CommonResult<Boolean> toggleAiCheck() {
return success(cabinetPickupService.toggleAiCheck());
}
@GetMapping("/aistatus")
@Operation(summary = "获取 AI 校验是否开启")
public CommonResult<Boolean> isAiCheckEnabled() {
return success(cabinetPickupService.isAiCheckEnabled());
}
两者的实现类非常简单:
@Override
public Boolean toggleAiCheck(){
// 写入 Redis 状态
String key = "cabinet:pickup:ai-check:enabled";
Boolean newStatus = redisTemplate.opsForValue().get(key) == null;
if (newStatus) {
redisTemplate.opsForValue().set(key, "true"); // 不设置过期时间,永久有效
} else {
redisTemplate.delete(key);
}
return newStatus;
}
@Override
public Boolean isAiCheckEnabled(){
String key = "cabinet:pickup:ai-check:enabled";
return redisTemplate.hasKey(key);
}
5. 彩蛋
后续的迭代当时是修正这个按钮变化:
后续选中了第二种方式:
不过三种方式也大致说一说:
✅ 方案一:把 Switch 包在一个 Button 样式中,视觉统一
用 el-button 包装 el-switch,保持高度统一 + 左右间距好看
<el-button
type="warning"
plain
style="display: flex; align-items: center; gap: 8px"
:loading="aiChecking"
@click="confirmToggleAiCheck(aiCheckEnabled)"
v-hasPermi="['gate:cabinet-pickup:export']"
>
<Icon icon="ep:cpu" />
<span>{{ aiCheckEnabled ? 'AI校验已开启' : '开启 AI校验' }}</span>
</el-button>
⚠️ 同时禁用 el-switch 本身,使用按钮控制切换,风格会统一很多
✅ 方案二:用图标按钮 + tooltip + switch 式 UI,简洁优雅
<el-tooltip content="开启或关闭 AI 校验" placement="top">
<el-switch
v-model="aiCheckEnabled"
:loading="aiChecking"
@change="confirmToggleAiCheck"
inline-prompt
active-text="开"
inactive-text="关"
style="margin-left: 12px;"
v-hasPermi="['gate:cabinet-pickup:export']"
/>
</el-tooltip>
用 inline-prompt 可以让 switch 上有“开/关”字样,增强可读性,也加了 margin-left 来给它和前面的按钮分个距离
✅ 方案三:整体按钮组设计,更整齐
使用 el-button-group 将按钮视觉上捆绑在一起,排布统一:
<el-button-group>
<el-button
type="success"
plain
@click="handleExport"
:loading="exportLoading"
v-hasPermi="['gate:cabinet-pickup:export']"
>
<Icon icon="ep:download" class="mr-5px" />
导出
</el-button>
<el-button
type="warning"
:loading="aiChecking"
@click="confirmToggleAiCheck(aiCheckEnabled)"
v-hasPermi="['gate:cabinet-pickup:export']"
>
<Icon icon="ep:cpu" class="mr-5px" />
{{ aiCheckEnabled ? 'AI校验已开启' : '开启 AI校验' }}
</el-button>
</el-button-group>
用按钮组可以统一高度、内边距、风格,还能简洁排列。