Vue + Java Redis 实现 AI 校验按钮控制与状态保持(附Demo)

前言

🤟 找工作,来万码优才:👉 #小程序://万码优才/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 (如果有密码)
  1. 按钮点击事件
/** 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
  }
}
  1. 新增定时查询状态
/** AI校验状态查询 */
const queryAiCheckStatus = async () => {
  try {
    const res = await CabinetPickupApi.getAiCheckStatus()
    aiCheckStatus.value = res || '未开始'
  } catch {
    aiCheckStatus.value = '查询失败'
  }
}
  1. 新增状态展示
<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 中的状态,展示按钮是开启还是关闭

✅ 前端修改

  1. 数据定义
const aiChecking = ref(false)
const aiCheckEnabled = ref(false)
  1. 获取状态函数
const getAiCheckStatus = async () => {
  try {
    const res = await CabinetPickupApi.getAiCheckStatus()
    aiCheckEnabled.value = res
  } catch (e) {
    aiCheckEnabled.value = false
  }
}
onMounted(() => {
  getAiCheckStatus()
})
  1. 切换按钮函数
const handleAiCheck = async () => {
  aiChecking.value = true
  try {
    const res = await CabinetPickupApi.toggleAiCheck()
    aiCheckEnabled.value = res
    message.success(`AI校验已${res ? '开启' : '关闭'}`)
  } finally {
    aiChecking.value = false
  }
}
  1. 按钮 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>

✅ 后端实现

  1. 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;
}
  1. 新增获取 AI 状态接口
@Override
public Boolean isAiCheckEnabled() {
    String key = "cabinet:pickup:ai-check:enabled";
    return redisTemplate.hasKey(key);
}
  1. 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>

用按钮组可以统一高度、内边距、风格,还能简洁排列。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农研究僧

你的鼓励将是我创作的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值