DrRacket银行用户GUI开发:账号数据存储与加载问题求助
问题修复与解决方案
1. 修复string-blank?报错
Racket没有内置的string-blank?函数,你可以通过两种方式解决:
方法一:自定义string-blank?函数
先导入字符串处理库,再实现判断逻辑:
(require racket/string) (define (string-blank? s) (string=? (string-trim s) ""))
方法二:直接替换判断逻辑
修改load-users-from-file中的空内容判断:
(data (if (string=? (string-trim content) "") '() (string->jsexpr content)))
2. 修正json->user参数缺失问题
你的user结构体包含userPhone字段,但json->user构造时漏掉了这个参数,导致初始化错误,修改后:
(define (json->user json-data) (user (cdr (assoc "userID" json-data)) (cdr (assoc "userType" json-data)) (cdr (assoc "userName" json-data)) (cdr (assoc "userEmail" json-data)) (cdr (assoc "userPhone" json-data)) ; 补上userPhone参数 (cdr (assoc "userPassword" json-data))))
3. 修复用户ID重复问题
原代码中user-id-counter不会根据已加载用户更新,导致新增用户ID重复,加载数据后需要同步计数器:
(load-users-from-file "users.json") ; 加载完成后更新ID计数器 (when (not (hash-empty? userAccount)) (define max-id (apply max (map (lambda (u) (string->number (user-userID u))) (hash-values userAccount)))) (user-id-counter (add1 max-id)))
更稳妥的方式是把userID设为数字类型,避免字符串转数字的麻烦:
(struct user (userID userType userName userEmail userPhone userPassword) #:mutable) (define user-id-counter (make-parameter 1)) (define (generate-user-id) (define id (user-id-counter)) (user-id-counter (add1 id)) id) ; 加载后更新计数器 (when (not (hash-empty? userAccount)) (user-id-counter (add1 (apply max (map user-userID (hash-values userAccount))))))
完整功能实现思路
1. 数据存储层(已完成核心逻辑)
- 用JSON文件持久化用户数据,保证重启程序后数据不丢失
- 哈希表
userAccount作为内存缓存,以邮箱为键实现用户快速查找 - 封装
save-users-to-file和load-users-from-file,保证内存数据与文件数据一致
2. GUI界面实现
需要搭建登录、注册两个核心界面,基于racket/gui/base组件实现:
登录界面
- 创建窗口、垂直布局面板
- 添加邮箱输入框、密码输入框(设置
#:password? #t隐藏输入内容) - 登录按钮点击时调用
find-user验证,根据结果弹出提示框
注册界面
- 包含用户名、邮箱、手机号、密码输入框
- 注册按钮点击时校验输入合法性(如非空),创建
user实例并调用add-user!保存 - 添加界面切换按钮,实现登录/注册窗口的互相跳转
示例GUI代码片段:
; 登录窗口 (define login-frame (new frame% [label "银行系统登录"] [width 300] [height 200])) (define login-panel (new vertical-panel% [parent login-frame] [alignment '(center center)] [spacing 10])) (new message% [parent login-panel] [label "邮箱"]) (define login-email-field (new text-field% [parent login-panel] [init-value ""])) (new message% [parent login-panel] [label "密码"]) (define login-pwd-field (new text-field% [parent login-panel] [init-value ""] [password? #t])) (new button% [parent login-panel] [label "登录"] [callback (lambda (button event) (define email (send login-email-field get-value)) (define pwd (send login-pwd-field get-value)) (define u (find-user email pwd)) (if u (message-box "成功" (string-append "欢迎你," (user-userName u))) (message-box "错误" "邮箱或密码不正确")))]) (new button% [parent login-panel] [label "注册账号"] [callback (lambda (button event) (send login-frame show #f) (send register-frame show #t))]) ; 注册窗口 (define register-frame (new frame% [label "银行系统注册"] [width 300] [height 300] [show #f])) (define register-panel (new vertical-panel% [parent register-frame] [alignment '(center center)] [spacing 10])) (new message% [parent register-panel] [label "用户名"]) (define reg-name-field (new text-field% [parent register-panel] [init-value ""])) (new message% [parent register-panel] [label "邮箱"]) (define reg-email-field (new text-field% [parent register-panel] [init-value ""])) (new message% [parent register-panel] [label "手机号"]) (define reg-phone-field (new text-field% [parent register-panel] [init-value ""])) (new message% [parent register-panel] [label "密码"]) (define reg-pwd-field (new text-field% [parent register-panel] [init-value ""] [password? #t])) (new button% [parent register-panel] [label "注册"] [callback (lambda (button event) (define name (send reg-name-field get-value)) (define email (send reg-email-field get-value)) (define phone (send reg-phone-field get-value)) (define pwd (send reg-pwd-field get-value)) (if (or (string-blank? name) (string-blank? email) (string-blank? phone) (string-blank? pwd)) (message-box "错误" "请填写所有字段") (begin (define new-u (user (generate-user-id) "personal" name email phone pwd)) (add-user! new-u) (message-box "成功" "注册成功,请登录") (send register-frame show #f) (send login-frame show #t))))]) (new button% [parent register-panel] [label "返回登录"] [callback (lambda (button event) (send register-frame show #f) (send login-frame show #t))]) ; 显示登录窗口 (send login-frame show #t)
学习资源推荐
书籍
- Racket Programming the Fun Way:入门级书籍,涵盖Racket基础语法、GUI开发、数据处理,适合快速上手
- Realm of Racket:以游戏开发为载体,讲解Racket核心概念,包括GUI、函数式编程
- Racket Guide:官方指南文档,系统介绍Racket所有核心特性,包含
racket/gui/base的详细用法
视频教程
- Racket官方YouTube频道GUI系列教程:官方出品,讲解GUI组件基本使用与界面布局
- Racket入门到进阶系列视频:覆盖基础语法、函数式编程、GUI开发,适合逐步深入学习
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

