从 PHP 到 AI + Golang,程序员自救转型手记(二十七):管理员登录验证码开关

这是一个系列 Blog,作者将以一个 PHP 全栈工程师的身份,利用 AI 工具(claude code、codex、deepseek、豆包等):从零开始学习 golang 语言,并最终完成 ai-go-admin(github | gitee)开源项目的制作,全程记录分享。

在上一期,我们进行了 "icon 组件封装",本期将完成:管理员登录验证码开关

管理员登录验证码开关

管理员登录人机验证码有时候需要关闭,比如为了内网开发环境下的测试方便等,这里直接将需求描述给 CC:

bash 复制代码
@config/captcha.yaml 增加管理员登录的人机验证码开关配置项,前端登录前使用 GET 请求获取该配置,根据配置确定点选验证码是否弹出,服务端登录接口根据该配置决定是否进行验证码效验

● 所有修改已完成,编译通过。以下是变更总结:

  1. config/captcha.yaml --- 新增 enabled 配置项
yaml 复制代码
captcha:
    enabled: true   # ← 新增,是否启用人机验证码
  1. internal/infra/config/config.go --- CaptchaConfig 增加 Enabled 字段
go 复制代码
type CaptchaConfig struct {
    Enabled         bool         `mapstructure:"enabled"`  // ← 新增
    Elements        []string     `mapstructure:"elements"`
    // ...
}
  1. internal/service/admin/admin.go --- 登录时根据配置决定是否校验验证码
go 复制代码
if config.Get().Captcha.Enabled {
    if ok, err := captcha.Check(req.Captcha, true); !ok {
        return nil, fmt.Errorf("验证码错误:%w", err)
    }
}
  1. internal/handler/common/captcha.go --- 新增 GET /common/captcha/config 接口

返回 {"code":200, "data":{"enabled":true}},供前端查询验证码开关状态。

  1. web/src/api/common.ts --- 新增 getCaptchaConfig() API 函数

  2. web/src/views/admin/login.vue --- 登录流程改造

  • onMounted 时调用 GET /common/captcha/config 获取配置
    • onSubmit 时根据 captchaEnabled 决定:
      • 启用:弹出点选验证码(原流程不变)
      • 关闭:跳过验证码,直接提交登录(captcha 字段给空值)
    • 获取配置失败时默认启用验证码(安全兜底)

使用方式:将 config/captcha.yaml 中的 enabled 设为 false 即可关闭管理员登录的人机验证码。


先简单看了下输出,确实加了配置项,叫做 captcha.enabled,但我们的 config/captcha.yaml,是全局验证码配置文件,而本次增加的开关只是管理员登录接口的验证码开关,所以这个配置名有点太广泛了,这里的规划明显就需要全局观了,未来可能还会增加会员登录接口的验证码开关,或者商户登录接口的,当前又是在 config/captcha.yaml 文件,最合理的应该是建立一个类似 开关 的数组,其内再存放各个模块的具体开关状态,我连变量名都懒得取,将这段思路发给 AI 即可,这波稳了一手,先开 plan mode

bash 复制代码
  # 各接口人机验证码开关
  switches:
    # 管理员登录接口
    admin_login: true

AI 取出来一个 switches.admin_login,看来它理解了我的意思,下面先检查目录结构改动,然后对代码逐行 review

验证码配置获取接口位置错误

获取验证码配置的接口,它放在了单独的公共接口之中,而我希望的是放在 admin/login-cofig 之类的独立接口中,登录页请求一下就行了。

go 复制代码
// internal\handler\common\captcha.go 文件

// Config 返回验证码配置(供前端判断是否启用人机验证)
func (h *CaptchaHandler) Config(c *gin.Context) {
	response.Success(c, response.WithData(gin.H{
		"admin_login": config.Get().Captcha.Switches.AdminLogin,
	}))
}

AI 调整后:

go 复制代码
// internal\handler\admin\admin.go 文件

// GetLoginConfig 返回管理员登录页配置
func (h *AdminHandler) GetLoginConfig(c *gin.Context) {
	response.Success(c, response.WithData(gin.H{
		"admin_login": config.Get().Captcha.Switches.AdminLogin,
	}))
}

// 路由注册增加一行

group.GET("/login-config", h.GetLoginConfig)

缺少合理封装

前端需要根据验证码开关状态确定是否弹出人机验证码,AI是这样写的:

ts 复制代码
if (captchaEnabled.value) {
    clickCaptcha((data) => {
        submitLoading.value = true
        adminLogin({ ...loginForm, captcha: data })
            .then((res) => {
                adminInfo.dataFill(res.data.data, false)
                router.push('/admin')
            })
            .finally(() => {
                submitLoading.value = false
            })
    })
} else {
    submitLoading.value = true
    adminLogin({ ...loginForm, captcha: { key: '', clicks: [], renderedWidth: 0, renderedHeight: 0 } })
        .then((res) => {
            adminInfo.dataFill(res.data.data, false)
            router.push('/admin')
        })
        .finally(() => {
            submitLoading.value = false
        })
}

很明显大部分代码可复用,冗余严重,改为:

ts 复制代码
const doLogin = (captcha?: ClickRequest) => {
    submitLoading.value = true
    adminLogin({ ...loginForm, captcha })
        .then((res) => {
            adminInfo.dataFill(res.data.data, false)
            router.push('/admin')
        })
        .finally(() => {
            submitLoading.value = false
        })
}

if (captchaEnabled.value) {
    clickCaptcha((data) => doLogin(data))
} else {
    doLogin()
}
相关推荐
霸道流氓气质8 分钟前
SpringBoot中事务内同步处理 + 事务后异步调用外部系统的通用模式示例
java·spring boot·后端
YHHLAI13 分钟前
Vue 3 流式输出实战:从零掌握 LLM Streaming 与 SSE 协议
前端·javascript·vue.js
2601_9657984733 分钟前
Arrow Unlocker HTML5 Game Review: Keep Web Arcade Players Coming Back
前端·html·html5
不简说1 小时前
JS 代码技巧 vol.10 — 20 个 V8 引擎原理,解释"为什么这样写快"
前端·javascript·面试
AskHarries1 小时前
用户埋点怎么设计
后端
醉城夜风~1 小时前
HTML常用标签详解学习博客:从基础标签到页面结构完整掌握
前端·学习·html
OpenTiny社区2 小时前
拒绝臃肿!TinyRobot从“AI 输入框”到“AI输入控制台”的可扩展进化
前端·vue.js
极客悟道2 小时前
VS Code + JetTUI 开发 Spring Boot项目,教程来了
后端
Yeauty2 小时前
自建 HLS 第一问:fMP4 还是 TS?用 Rust 在进程内把两种都跑出来
开发语言·后端·rust
Zane19942 小时前
copy 和 deepcopy 到底在拷贝什么?一文讲清赋值、浅拷贝、深拷贝的引用关系
后端·python