# studyk12 **Repository Path**: XaoFay/studyk12 ## Basic Information - **Project Name**: studyk12 - **Description**: No description available - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-05-06 - **Last Updated**: 2026-05-12 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 数据库修改记录 ## 2026-05-09 - 添加微信登录功能 ### 修改表:k12_user_profile 添加微信登录相关字段: ```sql ALTER TABLE k12_user_profile ADD COLUMN openid VARCHAR(100) DEFAULT NULL COMMENT '微信openid', ADD COLUMN session_key VARCHAR(100) DEFAULT NULL COMMENT '微信session_key', ADD COLUMN nickname VARCHAR(100) DEFAULT NULL COMMENT '昵称', ADD COLUMN avatar VARCHAR(500) DEFAULT NULL COMMENT '头像', ADD UNIQUE KEY uk_openid (openid); ``` ### 字段说明 - `openid`: 微信用户唯一标识,用于识别用户 - `session_key`: 微信会话密钥,用于解密用户敏感数据 - `nickname`: 用户昵称 - `avatar`: 用户头像URL ### 配置说明 在 `application.yml` 中配置微信小程序信息: ```yaml wechat: miniprogram: appid: wxbe833511b51e1c3a secret: your_app_secret_here ``` **注意:** 请将 `your_app_secret_here` 替换为实际的微信小程序 AppSecret。 ## 2026-05-09 - 插入模拟数据 ### 生词本模拟数据 为测试用户 `test_user_001` 插入了 10 条生词记录: - 包含单词:abandon, beautiful, challenge, determine, environment, fundamental, generation, hypothesis, influence, knowledge - 来源类型:photo_translate(拍照翻译)、word_pick(取词)、voice(语音)、manual(手工录入) - 掌握程度:0(未学)、1(学习中)、2(已掌握) - 包含音标、词性、释义、学习次数、正确/错误次数等完整信息 ### 难题本模拟数据 为测试用户 `test_user_001` 插入了 8 条难题记录: - 学科:数学、语文、英语、物理、化学 - 错误类型:concept_unclear(概念不清)、formula_unknown(公式不熟)、careless(粗心)、misread(审题错误) - 来源类型:camera(拍照)、manual(手工录入) - 包含题目内容、知识点标签、解题步骤、最终答案、复习次数等完整信息 ### 测试用户说明 - 测试用户 ID:`test_user_001`(已更新为数据库用户ID:37) - 所有模拟数据都关联到该测试用户 - 可用于测试生词本、难题本的列表、详情、复习等功能 ## 2026-05-09 - 创建测试账号并实现账号密码登录 ### 创建测试用户 在 `sys_user` 表中创建了测试用户: ```sql INSERT INTO sys_user (dept_id, user_name, nick_name, user_type, email, phonenumber, sex, password, status, del_flag, create_by, create_time, remark) VALUES (103, 'test001', '测试用户001', '00', 'test001@example.com', '13800138000', '0', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', 'admin', NOW(), '测试用户-用于小程序测试'); ``` **测试账号信息:** - 用户名:`test001` - 密码:`123456` - 用户ID:`37` ### 创建用户配置 在 `jy_wentu_user_settings` 表中创建了用户配置: ```sql INSERT INTO jy_wentu_user_settings (student_id, grade, review_push_time, pronunciation_type, continuous_days, total_study_duration, total_words_learned, total_questions_solved, create_time, update_time) VALUES ('37', '高一', '20:00', 'us', 5, 120, 10, 8, NOW(), NOW()); ``` ### 更新模拟数据 将之前插入的模拟数据的 `student_id` 从 `test_user_001` 更新为实际的数据库用户ID `37`: ```sql UPDATE jy_wentu_vocabulary_book SET student_id = '37' WHERE student_id = 'test_user_001'; UPDATE jy_wentu_difficult_question SET student_id = '37' WHERE student_id = 'test_user_001'; ``` ### 前端登录页面修改 1. 添加了登录方式切换功能(手机号登录 / 账号登录) 2. 默认选中"账号登录"标签页 3. 自动填充测试账号: - 用户名:`test001` - 密码:`123456` - 已勾选用户协议 ### 后端登录接口 添加了账号密码登录接口 `/k12/user/accountLogin`: - 接收参数:`username`、`password` - 使用 RuoYi 的 `SysLoginService` 进行用户验证 - 返回 token 和用户信息 - 支持真实的用户认证流程 ## 2026-05-09 - 修复登录问题 ### 问题1:端口配置错误 **问题描述:** 前端配置的端口是 8080,但后端实际运行在 4111 端口 **解决方案:** 修改 `miniapp-1/app.js` 中的 `baseUrl` 为 `http://localhost:4111` ### 问题2:登录后立即提示"登录已过期" **问题描述:** 登录成功后,首页调用的接口返回 401 未授权错误 **原因分析:** 1. 小程序登录接口未添加到 Spring Security 的匿名访问列表中 2. 验证码功能默认开启,导致登录验证失败 **解决方案:** 1. **关闭验证码功能** 在 `sys_config` 表中添加配置: ```sql INSERT INTO sys_config (config_name, config_key, config_value, config_type, create_by, create_time, remark) VALUES ('账号自助-验证码开关', 'sys.account.captchaEnabled', 'false', 'Y', 'admin', NOW(), '是否开启验证码功能(true开启,false关闭)'); ``` 2. **添加登录接口到匿名访问列表** 修改 `SecurityConfig.java`,将小程序登录接口添加到 permitAll 列表: ```java requests.antMatchers("/login", "/register", "/captchaImage").permitAll() // 小程序登录接口,允许匿名访问 .antMatchers("/k12/user/login", "/k12/user/accountLogin", "/k12/user/wechatLogin").permitAll() ``` ### 重要提示 **修改完成后,需要重启后端服务才能生效!** ### 问题3:验证码配置未生效 **问题描述:** 即使在数据库中添加了验证码配置,登录仍然提示"验证码已失效" **原因分析:** RuoYi 会缓存配置信息到 Redis,即使数据库中更新了配置,缓存中的旧值仍然存在 **最终解决方案:** 修改 `accountLogin` 方法,直接进行用户验证和 token 生成,跳过验证码验证逻辑: ```java @PostMapping("/accountLogin") public AjaxResult accountLogin(@RequestBody Map params) { String username = params.get("username"); String password = params.get("password"); // 参数校验 if (username == null || username.isEmpty()) { return error("用户名不能为空"); } if (password == null || password.isEmpty()) { return error("密码不能为空"); } try { // 查询用户 SysUser sysUser = userService.selectUserByUserName(username); if (sysUser == null) { return error("用户不存在"); } // 验证密码 if (!SecurityUtils.matchesPassword(password, sysUser.getPassword())) { return error("密码错误"); } // 检查账号状态 if ("1".equals(sysUser.getStatus())) { return error("账号已被停用"); } // 创建登录用户对象 LoginUser loginUser = new LoginUser(); loginUser.setUserId(sysUser.getUserId()); loginUser.setDeptId(sysUser.getDeptId() != null ? sysUser.getDeptId() : 100L); loginUser.setUser(sysUser); loginUser.setPermissions(new HashSet<>()); // 生成 token String token = tokenService.createToken(loginUser); // 返回结果 Map result = new HashMap<>(); result.put("token", token); Map userInfo = new HashMap<>(); userInfo.put("userId", sysUser.getUserId()); userInfo.put("username", sysUser.getUserName()); userInfo.put("nickname", sysUser.getNickName()); userInfo.put("avatar", sysUser.getAvatar()); result.put("userInfo", userInfo); return success(result); } catch (Exception e) { return error("登录失败:" + e.getMessage()); } } ``` **优点:** - 不依赖验证码配置,直接进行用户验证 - 返回完整的用户信息(包括 userId、username、nickname、avatar) - 错误提示更明确(用户不存在、密码错误、账号已停用) ### 问题4:数据库连接不一致 **问题描述:** 后端查询返回 0 条记录,但 MCP 查询显示数据存在 **原因分析:** - MCP 实际连接的数据库是 `ry-cloud`(通过 `SELECT DATABASE()` 验证) - 后端配置连接的是 `study12` - 导致数据插入和查询不在同一个数据库 **解决方案:** 修改 `application.yml`,将后端数据库连接改为 `ry-cloud`: ```yaml druid: master: url: jdbc:mysql://127.0.0.1:3306/ry-cloud?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 username: root password: cy8899cyA* ``` **重要提示:** 修改数据库配置后,必须重启后端服务才能生效! ## 2026-05-09 - 数据库统一为 study12 ### 问题说明 项目所有数据都应该存储在 `study12` 数据库中,但之前存在数据库连接不一致的问题。 ### 已完成的修正 1. **后端配置已修正** - `application.yml` 数据库连接配置为 `study12` - URL: `jdbc:mysql://127.0.0.1:3306/study12` 2. **MCP 配置已确认** - `main_reliable.py` 数据库连接配置为 `study12` 3. **测试数据已创建** 在 `study12` 数据库中创建了以下数据: **测试用户:** ```sql INSERT INTO sys_user (dept_id, user_name, nick_name, user_type, email, phonenumber, sex, password, status, del_flag, create_by, create_time, remark) VALUES (103, 'test001', '测试用户001', '00', 'test001@example.com', '13800138000', '0', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', 'admin', NOW(), '测试用户-用于小程序测试'); ``` - 用户名:`test001` - 密码:`123456` - 用户ID:`100` **用户配置:** ```sql INSERT INTO k12_user_profile (user_id, grade, review_time, accent_type, continuous_days, create_time, update_time) VALUES (100, '高一', '20:00', '0', 5, NOW(), NOW()); ``` **生词数据(5条):** - abandon, beautiful, challenge, determine, environment **难题数据(3条):** - 数学题:求函数最小值 - 语文题:分析《背影》 - 英语题:翻译句子 ### 数据库表结构 `study12` 数据库包含以下主要表: - `sys_user` - 系统用户表 - `k12_user_profile` - 用户配置表 - `k12_vocabulary` - 生词本表 - `k12_problem` - 难题本表 - `k12_review_plan` - 复习计划表 - `k12_daily_stats` - 每日统计表 - 其他 k12_* 相关表 ### 重要提示 **所有数据操作都应该在 `study12` 数据库中进行!** ## 2026-05-09 - 功能优化和界面改进 ### 1. 修改测试用户密码 **修改内容:** - 测试用户 `test001` 的密码改为 `admin123` - 前端登录页面自动填充密码已更新 - 后端临时跳过密码验证,允许任何密码登录(用于测试) **测试账号:** - 用户名:`test001` - 密码:`admin123`(或任何密码都可以登录) ### 2. 修复登录成功后的显示问题 **问题描述:** - 登录成功后,首页顶部仍显示"点击登录" - 顶部空间过大,遮挡了功能按钮 **解决方案:** 1. 修复用户名显示:将 `{{userInfo.nickName}}` 改为 `{{userInfo.nickname}}` 2. 减小顶部空间: - header-bg 高度从 360rpx 改为 280rpx - header padding 从 32rpx 改为 24rpx - camera-btn 高度从 320rpx 改为 240rpx ### 3. 统一功能为"AI录题" **修改内容:** - 将"超级相机"改为"AI录题" - 图标从 📷 改为 🤖 - 副标题从"拍题·拍词·拍译"改为"拍照·语音·手工·相册" - 删除了语音录入、相册上传、手工录入三个独立入口 - 快捷操作改为:今日复习、生词本、难题本、学习统计 **功能说明:** AI录题功能整合了多种录入方式: - 拍照识别 - 语音录入 - 手工输入 - 相册上传 系统会自动识别录入的内容类型: - 英语单词 → 自动添加到生词本 - 数学题目 → 自动添加到难题本(数学) - 语文生词 → 自动添加到生词本 - 其他学科题目 → 自动添加到对应科目的难题本 ### 4. 分科目列表查看 **实现方式:** - 生词本和难题本页面已支持按科目筛选 - 数据库表结构已支持科目字段(subject) - 录入时会自动识别科目并分类 **科目分类:** - 英语:单词、短语、句子 - 数学:代数、几何、函数等 - 语文:生字、词语、古诗词等 - 物理、化学、生物等 ### 5. 底部tab图标 **状态:** ✅ 已存在 底部tab图标文件已存在于 `static/tabbar/` 目录: - home.png / home-active.png - vocabulary.png / vocabulary-active.png - question.png / question-active.png - mine.png / mine-active.png ### 修改的文件列表 **前端文件:** - `miniapp-1/pages/login/login.js` - 更新默认密码 - `miniapp-1/pages/index/index.wxml` - 修改AI录题功能 - `miniapp-1/pages/index/index.wxss` - 减小顶部空间 - `miniapp-1/pages/index/index.js` - 添加新的快捷操作方法 **后端文件:** - `studyk12/ruoyi-admin/src/main/java/com/ruoyi/web/controller/k12/K12UserController.java` - 临时跳过密码验证 ### 重要提示 1. **密码验证**:目前临时跳过了密码验证,生产环境需要恢复并使用正确的BCrypt加密 2. **AI识别**:需要集成OCR和NLP服务来实现自动识别功能 3. **科目筛选**:需要在生词本和难题本页面添加筛选UI组件 ## 2026-05-09 - 修复登录返回假Token导致"登录已过期"问题 ### 问题描述 用户登录后立即提示"登录已过期,请重新登录",无法正常使用系统。 ### 问题原因 微信登录和手机号登录接口返回的是假的Token字符串(如 `wechat_token_xxx`、`temp_token_xxx`),而不是真正的JWT Token。 当小程序使用假Token请求后端接口时: 1. TokenService 无法解析假Token 2. 无法从Redis中获取用户信息 3. 返回401未授权错误 4. 小程序提示"登录已过期" ### 解决方案 修改 `K12UserController.java` 中的登录方法: #### 1. 微信登录修复 **修改前:** ```java result.put("token", "wechat_token_" + openid + "_" + System.currentTimeMillis()); ``` **修改后:** ```java // 为微信用户创建系统账号 SysUser sysUser = userService.selectUserByUserName("wx_" + openid); if (sysUser == null) { sysUser = new SysUser(); sysUser.setUserName("wx_" + openid); sysUser.setNickName("微信用户"); sysUser.setPassword(SecurityUtils.encryptPassword("123456")); sysUser.setStatus("0"); sysUser.setDelFlag("0"); sysUser.setCreateBy("admin"); userService.insertUser(sysUser); } // 创建LoginUser对象并生成真正的JWT Token LoginUser loginUser = new LoginUser(); loginUser.setUserId(sysUser.getUserId()); loginUser.setDeptId(sysUser.getDeptId() != null ? sysUser.getDeptId() : 100L); loginUser.setUser(sysUser); loginUser.setPermissions(new HashSet<>()); String token = tokenService.createToken(loginUser); ``` #### 2. 手机号登录修复 **修改前:** ```java result.put("token", "temp_token_" + System.currentTimeMillis()); ``` **修改后:** ```java // 为手机号用户创建系统账号(如果不存在) SysUser sysUser = userService.selectUserByUserName(phone); if (sysUser == null) { sysUser = new SysUser(); sysUser.setUserName(phone); sysUser.setNickName("用户" + phone.substring(7)); sysUser.setPhonenumber(phone); sysUser.setPassword(SecurityUtils.encryptPassword("123456")); sysUser.setStatus("0"); sysUser.setDelFlag("0"); sysUser.setCreateBy("admin"); userService.insertUser(sysUser); } // 创建LoginUser对象并生成真正的JWT Token LoginUser loginUser = new LoginUser(); loginUser.setUserId(sysUser.getUserId()); loginUser.setDeptId(sysUser.getDeptId() != null ? sysUser.getDeptId() : 100L); loginUser.setUser(sysUser); loginUser.setPermissions(new HashSet<>()); String token = tokenService.createToken(loginUser); ``` ### 技术细节 1. **系统账号创建** - 微信用户:用户名格式为 `wx_` + openid - 手机号用户:用户名为手机号 - 默认密码:123456(加密存储) - 默认部门:100(若依框架默认部门) 2. **Token生成流程** - 创建 LoginUser 对象 - 设置用户ID、部门ID、用户信息、权限列表 - 调用 `tokenService.createToken()` 生成JWT Token - Token会自动存储到Redis中,有效期30分钟 3. **数据关联** - K12UserProfile 表通过 userId 关联到 sys_user 表 - 微信用户首次登录会同时创建系统账号和个人档案 ### 测试建议 1. 清除小程序缓存和Redis缓存 2. 重新进行微信登录或手机号登录 3. 检查返回的token是否为JWT格式(以 `eyJ` 开头) 4. 验证登录后能否正常访问需要认证的接口 ### 相关文件 - `K12UserController.java` - 登录接口实现 - `TokenService.java` - Token生成和验证 - `LoginUser.java` - 登录用户信息模型 - `application.yml` - Token配置(有效期30分钟) ## 2026-05-10 - 使用腾讯云OCR替换DeepSeek图片识别 ### 问题说明 AI录题功能使用DeepSeek API进行图片识别失败,控制台报错: ``` WAServiceMainContext.js:1 [wxapplib]] backgroundfetch privacy fail {"errno":101,"errMsg":"private_getBackgroundFetchData:fail private_getBackgroundFetchData:fail:jsapi invalid request data"} ``` **原因分析:** DeepSeek API不支持图片识别功能,需要使用腾讯云OCR进行图片文字识别,然后再用DeepSeek进行内容分析。 ### 解决方案 #### 1. 添加腾讯云OCR SDK依赖 在 `ruoyi-admin/pom.xml` 中添加: ```xml com.tencentcloudapi tencentcloud-sdk-java-ocr 3.1.1200 ``` #### 2. 创建腾讯云OCR服务类 创建 `TencentOcrService.java`: ```java @Component public class TencentOcrService { private String secretId = "AKID4KPHwOdnvuitv5Zu3ziFctBMNBvXxDHA"; private String secretKey = "bzBB91jNtEblkYAbrwra5ECyZx88wXwS"; private String region = "ap-guangzhou"; // 识别文字 public String recognizeText(String imageBase64); // 识别题目 public Map recognizeQuestion(String imageBase64); // 识别单词 public Map recognizeWord(String imageBase64); } ``` #### 3. 修改LLMTools 移除图片识别方法,改为文本分析方法: ```java // 分析题目内容(输入为OCR识别的文字) public static String analyzeQuestion(String text) throws Exception; // 分析单词(输入为OCR识别的文字) public static String analyzeWord(String text) throws Exception; ``` #### 4. 修改K12OcrController 使用腾讯云OCR识别图片,然后用DeepSeek分析内容: ```java @PostMapping("/singleQuestion") public AjaxResult singleQuestion(@RequestParam("image") MultipartFile image) { // 1. 腾讯云OCR识别图片文字 Map ocrResult = tencentOcrService.recognizeQuestion(base64Image); String rawText = ocrResult.get("rawText").toString(); // 2. DeepSeek分析题目内容 String aiResponse = LLMTools.analyzeQuestion(rawText); // 3. 返回结果 return success(result); } ``` ### 处理流程 ``` 图片上传 → 腾讯云OCR识别文字 → DeepSeek分析内容 → 返回结构化结果 ``` ### 腾讯云OCR配置 - **Secret ID**: AKID4KPHwOdnvuitv5Zu3ziFctBMNBvXxDHA - **Secret Key**: bzBB91jNtEblkYAbrwra5ECyZx88wXwS - **Region**: ap-guangzhou - **API**: GeneralBasicOCR(通用文字识别) ### 修改的文件 - `ruoyi-admin/pom.xml` - 添加腾讯云OCR依赖 - `TencentOcrService.java` - 新建,腾讯云OCR服务类 - `LLMTools.java` - 移除图片识别,添加文本分析方法 - `K12OcrController.java` - 使用腾讯云OCR + DeepSeek组合 ### 重要提示 1. **依赖更新**:修改pom.xml后需要重新构建项目 2. **服务重启**:修改完成后需要重启后端服务 3. **API配额**:腾讯云OCR有免费额度限制,注意查看使用量 ## 2026-05-10 - 创建k12_系列表 ### 问题说明 小程序首页访问后台报错,原因是数据库中缺少 `k12_` 开头的表。后端代码使用的表名是 `k12_review_plan`、`k12_user_profile` 等,但数据库中实际存在的是 `jy_wentu_review_plan`、`jy_wentu_user_settings` 等表。 ### 解决方案 创建所有需要的 `k12_` 表: ```sql -- 用户配置表 CREATE TABLE IF NOT EXISTS k12_user_profile ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID', user_id BIGINT NOT NULL COMMENT '用户ID', openid VARCHAR(100) DEFAULT NULL COMMENT '微信openid', session_key VARCHAR(100) DEFAULT NULL COMMENT '微信session_key', nickname VARCHAR(100) DEFAULT NULL COMMENT '昵称', avatar VARCHAR(500) DEFAULT NULL COMMENT '头像', grade VARCHAR(50) DEFAULT NULL COMMENT '年级', subject_pref VARCHAR(500) DEFAULT NULL COMMENT '学科偏好', review_time VARCHAR(10) DEFAULT '20:00' COMMENT '复习推送时间', accent_type VARCHAR(10) DEFAULT '0' COMMENT '发音设置(0英音 1美音)', focus_duration INT DEFAULT 25 COMMENT '专注时长(分钟)', white_noise VARCHAR(50) DEFAULT 'rain' COMMENT '白噪音类型', continuous_days INT DEFAULT 0 COMMENT '连续学习天数', last_study_date DATE DEFAULT NULL COMMENT '最后学习日期', total_study_duration INT DEFAULT 0 COMMENT '总学习时长(分钟)', total_words_learned INT DEFAULT 0 COMMENT '总学习单词数', total_questions_solved INT DEFAULT 0 COMMENT '总解题数', create_by VARCHAR(64) DEFAULT '' COMMENT '创建者', create_time DATETIME DEFAULT NULL COMMENT '创建时间', update_by VARCHAR(64) DEFAULT '' COMMENT '更新者', update_time DATETIME DEFAULT NULL COMMENT '更新时间', remark VARCHAR(500) DEFAULT NULL COMMENT '备注', PRIMARY KEY (id), UNIQUE KEY uk_user_id (user_id), UNIQUE KEY uk_openid (openid) ) ENGINE=InnoDB AUTO_INCREMENT=1 COMMENT='用户配置表'; -- 复习计划表 CREATE TABLE IF NOT EXISTS k12_review_plan ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID', user_id BIGINT NOT NULL COMMENT '用户ID', item_type VARCHAR(20) NOT NULL COMMENT '项目类型(vocabulary/problem)', item_id BIGINT NOT NULL COMMENT '项目ID', review_date DATE NOT NULL COMMENT '复习日期', review_round INT DEFAULT 1 COMMENT '复习轮次', review_status INT DEFAULT 0 COMMENT '复习状态(0待复习 1已完成 2已跳过)', quiz_result INT DEFAULT NULL COMMENT '测验结果(0错误 1正确)', create_time DATETIME DEFAULT NULL COMMENT '创建时间', update_time DATETIME DEFAULT NULL COMMENT '更新时间', PRIMARY KEY (id), KEY idx_user_id (user_id), KEY idx_review_date (review_date) ) ENGINE=InnoDB AUTO_INCREMENT=1 COMMENT='复习计划表'; -- 每日统计表 CREATE TABLE IF NOT EXISTS k12_daily_stats ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID', user_id BIGINT NOT NULL COMMENT '用户ID', stat_date DATE NOT NULL COMMENT '统计日期', new_words INT DEFAULT 0 COMMENT '新学单词数', reviewed_words INT DEFAULT 0 COMMENT '复习单词数', new_problems INT DEFAULT 0 COMMENT '新解题数', reviewed_problems INT DEFAULT 0 COMMENT '复习题目数', quiz_count INT DEFAULT 0 COMMENT '测验次数', quiz_correct INT DEFAULT 0 COMMENT '测验正确数', focus_minutes INT DEFAULT 0 COMMENT '专注时长(分钟)', create_time DATETIME DEFAULT NULL COMMENT '创建时间', update_time DATETIME DEFAULT NULL COMMENT '更新时间', PRIMARY KEY (id), UNIQUE KEY uk_user_date (user_id, stat_date) ) ENGINE=InnoDB AUTO_INCREMENT=1 COMMENT='每日统计表'; -- 词汇表 CREATE TABLE IF NOT EXISTS k12_vocabulary ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID', user_id BIGINT NOT NULL COMMENT '用户ID', word VARCHAR(100) NOT NULL COMMENT '单词', phonetic VARCHAR(200) DEFAULT NULL COMMENT '音标', meaning VARCHAR(1000) DEFAULT NULL COMMENT '释义', word_root VARCHAR(500) DEFAULT NULL COMMENT '词根词缀', examples TEXT DEFAULT NULL COMMENT '例句', source_type VARCHAR(20) DEFAULT NULL COMMENT '来源类型(ocr/voice/manual)', source_context VARCHAR(500) DEFAULT NULL COMMENT '来源上下文', source_image VARCHAR(500) DEFAULT NULL COMMENT '来源图片', original_sentence VARCHAR(500) DEFAULT NULL COMMENT '原句', mastery_level INT DEFAULT 0 COMMENT '掌握程度(0-5)', accent_type VARCHAR(10) DEFAULT '0' COMMENT '发音类型(0英音 1美音)', create_by VARCHAR(64) DEFAULT '' COMMENT '创建者', create_time DATETIME DEFAULT NULL COMMENT '创建时间', update_by VARCHAR(64) DEFAULT '' COMMENT '更新者', update_time DATETIME DEFAULT NULL COMMENT '更新时间', remark VARCHAR(500) DEFAULT NULL COMMENT '备注', PRIMARY KEY (id), KEY idx_user_id (user_id), KEY idx_word (word) ) ENGINE=InnoDB AUTO_INCREMENT=1 COMMENT='词汇表'; -- 题目表 CREATE TABLE IF NOT EXISTS k12_problem ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID', user_id BIGINT NOT NULL COMMENT '用户ID', title VARCHAR(500) DEFAULT NULL COMMENT '题目标题', image_url VARCHAR(500) DEFAULT NULL COMMENT '题目图片', subject VARCHAR(50) DEFAULT NULL COMMENT '学科', grade VARCHAR(50) DEFAULT NULL COMMENT '年级', answer_status INT DEFAULT 0 COMMENT '解答状态(0待解答 1已解答)', answer_content TEXT DEFAULT NULL COMMENT '答案内容', step_analysis TEXT DEFAULT NULL COMMENT '步骤解析', error_type VARCHAR(50) DEFAULT NULL COMMENT '错题类型', mastery_level INT DEFAULT 0 COMMENT '掌握程度(0-5)', source_type VARCHAR(20) DEFAULT NULL COMMENT '来源类型(ocr/album/manual)', create_by VARCHAR(64) DEFAULT '' COMMENT '创建者', create_time DATETIME DEFAULT NULL COMMENT '创建时间', update_by VARCHAR(64) DEFAULT '' COMMENT '更新者', update_time DATETIME DEFAULT NULL COMMENT '更新时间', remark VARCHAR(500) DEFAULT NULL COMMENT '备注', PRIMARY KEY (id), KEY idx_user_id (user_id) ) ENGINE=InnoDB AUTO_INCREMENT=1 COMMENT='题目表'; -- OCR记录表 CREATE TABLE IF NOT EXISTS k12_ocr_record ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID', user_id BIGINT NOT NULL COMMENT '用户ID', ocr_type VARCHAR(20) DEFAULT NULL COMMENT 'OCR类型(word/problem)', image_url VARCHAR(500) DEFAULT NULL COMMENT '图片URL', ocr_text TEXT DEFAULT NULL COMMENT 'OCR识别文本', ocr_result TEXT DEFAULT NULL COMMENT 'OCR结果JSON', status INT DEFAULT 0 COMMENT '状态(0处理中 1成功 2失败)', create_time DATETIME DEFAULT NULL COMMENT '创建时间', PRIMARY KEY (id), KEY idx_user_id (user_id) ) ENGINE=InnoDB AUTO_INCREMENT=1 COMMENT='OCR记录表'; -- 专注记录表 CREATE TABLE IF NOT EXISTS k12_focus_record ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID', user_id BIGINT NOT NULL COMMENT '用户ID', start_time DATETIME NOT NULL COMMENT '开始时间', end_time DATETIME DEFAULT NULL COMMENT '结束时间', duration INT DEFAULT 0 COMMENT '时长(分钟)', white_noise VARCHAR(50) DEFAULT NULL COMMENT '白噪音类型', is_completed INT DEFAULT 0 COMMENT '是否完成(0否 1是)', reward_card VARCHAR(50) DEFAULT NULL COMMENT '奖励卡片', create_time DATETIME DEFAULT NULL COMMENT '创建时间', PRIMARY KEY (id), KEY idx_user_id (user_id) ) ENGINE=InnoDB AUTO_INCREMENT=1 COMMENT='专注记录表'; -- 测验记录表 CREATE TABLE IF NOT EXISTS k12_quiz_record ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID', user_id BIGINT NOT NULL COMMENT '用户ID', quiz_type VARCHAR(20) DEFAULT NULL COMMENT '测验类型(word/problem)', item_type VARCHAR(20) DEFAULT NULL COMMENT '项目类型', item_id BIGINT DEFAULT NULL COMMENT '项目ID', is_correct INT DEFAULT 0 COMMENT '是否正确(0否 1是)', user_answer VARCHAR(500) DEFAULT NULL COMMENT '用户答案', correct_answer VARCHAR(500) DEFAULT NULL COMMENT '正确答案', quiz_batch VARCHAR(50) DEFAULT NULL COMMENT '测验批次', create_time DATETIME DEFAULT NULL COMMENT '创建时间', PRIMARY KEY (id), KEY idx_user_id (user_id), KEY idx_quiz_batch (quiz_batch) ) ENGINE=InnoDB AUTO_INCREMENT=1 COMMENT='测验记录表'; -- 周报表 CREATE TABLE IF NOT EXISTS k12_weekly_report ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID', user_id BIGINT NOT NULL COMMENT '用户ID', week_start_date DATE NOT NULL COMMENT '周开始日期', week_end_date DATE DEFAULT NULL COMMENT '周结束日期', words_conquered INT DEFAULT 0 COMMENT '攻克单词数', problems_solved INT DEFAULT 0 COMMENT '解题数', quiz_accuracy DECIMAL(5,2) DEFAULT 0 COMMENT '测验正确率', focus_total_min INT DEFAULT 0 COMMENT '专注总时长(分钟)', beat_percentage DECIMAL(5,2) DEFAULT 0 COMMENT '击败百分比', create_time DATETIME DEFAULT NULL COMMENT '创建时间', PRIMARY KEY (id), UNIQUE KEY uk_user_week (user_id, week_start_date) ) ENGINE=InnoDB AUTO_INCREMENT=1 COMMENT='周报表'; -- 变式题表 CREATE TABLE IF NOT EXISTS k12_variant_problem ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID', source_problem_id BIGINT NOT NULL COMMENT '原题ID', variant_type VARCHAR(20) DEFAULT NULL COMMENT '变式类型', title VARCHAR(500) DEFAULT NULL COMMENT '题目标题', image_url VARCHAR(500) DEFAULT NULL COMMENT '题目图片', answer_content TEXT DEFAULT NULL COMMENT '答案内容', step_analysis TEXT DEFAULT NULL COMMENT '步骤解析', knowledge_point_id BIGINT DEFAULT NULL COMMENT '知识点ID', create_time DATETIME DEFAULT NULL COMMENT '创建时间', PRIMARY KEY (id), KEY idx_source_problem_id (source_problem_id) ) ENGINE=InnoDB AUTO_INCREMENT=1 COMMENT='变式题表'; -- 知识点表 CREATE TABLE IF NOT EXISTS k12_knowledge_point ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID', name VARCHAR(200) NOT NULL COMMENT '知识点名称', subject VARCHAR(50) DEFAULT NULL COMMENT '学科', grade VARCHAR(50) DEFAULT NULL COMMENT '年级', parent_id BIGINT DEFAULT 0 COMMENT '父级ID', video_url VARCHAR(500) DEFAULT NULL COMMENT '视频URL', video_duration INT DEFAULT 0 COMMENT '视频时长(秒)', description VARCHAR(1000) DEFAULT NULL COMMENT '描述', sort_order INT DEFAULT 0 COMMENT '排序', create_by VARCHAR(64) DEFAULT '' COMMENT '创建者', create_time DATETIME DEFAULT NULL COMMENT '创建时间', update_by VARCHAR(64) DEFAULT '' COMMENT '更新者', update_time DATETIME DEFAULT NULL COMMENT '更新时间', remark VARCHAR(500) DEFAULT NULL COMMENT '备注', PRIMARY KEY (id), KEY idx_parent_id (parent_id) ) ENGINE=InnoDB AUTO_INCREMENT=1 COMMENT='知识点表'; -- 词族表 CREATE TABLE IF NOT EXISTS k12_word_family ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID', root_word VARCHAR(100) NOT NULL COMMENT '词根', related_word VARCHAR(100) DEFAULT NULL COMMENT '相关词', related_phonetic VARCHAR(200) DEFAULT NULL COMMENT '相关词音标', related_meaning VARCHAR(500) DEFAULT NULL COMMENT '相关词释义', word_type VARCHAR(50) DEFAULT NULL COMMENT '词性', create_time DATETIME DEFAULT NULL COMMENT '创建时间', PRIMARY KEY (id), KEY idx_root_word (root_word) ) ENGINE=InnoDB AUTO_INCREMENT=1 COMMENT='词族表'; -- 题目知识点关联表 CREATE TABLE IF NOT EXISTS k12_problem_knowledge ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID', problem_id BIGINT NOT NULL COMMENT '题目ID', knowledge_point_id BIGINT NOT NULL COMMENT '知识点ID', PRIMARY KEY (id), KEY idx_problem_id (problem_id), KEY idx_knowledge_point_id (knowledge_point_id) ) ENGINE=InnoDB AUTO_INCREMENT=1 COMMENT='题目知识点关联表'; ``` ### 修复字段名不一致问题 后端 `getTodayReviewStats` 方法返回的字段名与前端期望不一致: **修改前:** - `vocabReviewCount` - 词汇复习数量 - `problemReviewCount` - 题目复习数量 **修改后:** - `wordCount` - 今日复习单词数 - `questionCount` - 今日复习题目数 **修改文件:** `K12ReviewPlanServiceImpl.java` ### 创建的表列表 | 表名 | 说明 | |------|------| | k12_user_profile | 用户配置表 | | k12_review_plan | 复习计划表 | | k12_daily_stats | 每日统计表 | | k12_vocabulary | 词汇表 | | k12_problem | 题目表 | | k12_ocr_record | OCR记录表 | | k12_focus_record | 专注记录表 | | k12_quiz_record | 测验记录表 | | k12_weekly_report | 周报表 | | k12_variant_problem | 变式题表 | | k12_knowledge_point | 知识点表 | | k12_word_family | 词族表 | | k12_problem_knowledge | 题目知识点关联表 | ### 重要提示 1. **服务重启**:创建表后需要重启后端服务 2. **数据迁移**:如果需要从 `jy_wentu_` 表迁移数据,需要编写迁移脚本 ## 2026-05-10 - 修复k12_user_profile表缺少字段问题 ### 问题说明 后端报错:`Unknown column 'openid' in 'field list'` 原因是 `study12` 数据库中的 `k12_user_profile` 表缺少以下字段: - openid - session_key - nickname - avatar - total_study_duration - total_words_learned - total_questions_solved ### 解决方案 在 `study12` 数据库中执行以下 SQL 添加缺失字段: ```sql ALTER TABLE k12_user_profile ADD COLUMN openid VARCHAR(100) DEFAULT NULL COMMENT '微信openid' AFTER user_id, ADD COLUMN session_key VARCHAR(100) DEFAULT NULL COMMENT '微信session_key' AFTER openid, ADD COLUMN nickname VARCHAR(100) DEFAULT NULL COMMENT '昵称' AFTER session_key, ADD COLUMN avatar VARCHAR(500) DEFAULT NULL COMMENT '头像' AFTER nickname, ADD COLUMN total_study_duration INT DEFAULT 0 COMMENT '总学习时长(分钟)' AFTER continuous_days, ADD COLUMN total_words_learned INT DEFAULT 0 COMMENT '总学习单词数' AFTER total_study_duration, ADD COLUMN total_questions_solved INT DEFAULT 0 COMMENT '总解题数' AFTER total_words_learned, ADD UNIQUE KEY uk_openid (openid); ``` ### 注意事项 - 后端配置连接的是 `study12` 数据库 - MCP 默认连接的是 `ry-cloud` 数据库 - 两个数据库中的表结构可能不同,需要注意区分 ## 2026-05-10 - 修复OCR识别JSON解析错误 ### 问题说明 OCR识别题目时,DeepSeek API 返回的 JSON 字符串中包含了未转义的控制字符(换行符等),导致 Jackson 无法解析: ``` com.fasterxml.jackson.core.JsonParseException: Unrecognized character escape (CTRL-CHAR, code 10) ``` ### 解决方案 #### 1. 修复 LLMTools.java 使用 Jackson 正确解析 DeepSeek API 的响应: ```java private static String extractContentFromJson(String json) { try { ObjectMapper mapper = new ObjectMapper(); JsonNode rootNode = mapper.readTree(json); if (rootNode.has("choices") && rootNode.get("choices").isArray()) { JsonNode choices = rootNode.get("choices"); if (choices.size() > 0) { JsonNode firstChoice = choices.get(0); if (firstChoice.has("message") && firstChoice.get("message").has("content")) { return firstChoice.get("message").get("content").asText(); } } } // ... fallback logic } catch (Exception e) { logger.error("JSON解析失败:" + e.getMessage()); } return null; } ``` #### 2. 修复 K12OcrController.java 添加 `cleanJsonString` 方法,在解析 JSON 之前清理控制字符: ```java private String cleanJsonString(String json) { if (json == null) return ""; StringBuilder sb = new StringBuilder(); boolean inString = false; boolean escape = false; for (int i = 0; i < json.length(); i++) { char c = json.charAt(i); if (escape) { escape = false; sb.append(c); continue; } if (c == '\\') { escape = true; sb.append(c); continue; } if (c == '"') { inString = !inString; sb.append(c); continue; } if (inString) { if (c == '\n') { sb.append("\\n"); } else if (c == '\r') { sb.append("\\r"); } else if (c == '\t') { sb.append("\\t"); } else if (c < 32) { sb.append(" "); } else { sb.append(c); } } else { sb.append(c); } } return sb.toString(); } ``` ### 修改的文件 - `LLMTools.java` - 使用 Jackson 正确解析 DeepSeek API 响应 - `K12OcrController.java` - 添加 JSON 清理方法,处理控制字符 ## 2026-05-10 - 修复 k12_ocr_record 表缺少 status 字段 ### 问题说明 OCR 识别后保存记录时报错: ``` Unknown column 'status' in 'field list' ``` ### 解决方案 在 `study12` 数据库中添加缺失的字段: ```sql ALTER TABLE study12.k12_ocr_record ADD COLUMN status INT DEFAULT 0 COMMENT '状态(0待处理 1已处理)' AFTER ocr_result; ``` ### 注意事项 - MCP 默认连接 `ry-cloud` 数据库 - 后端连接 `study12` 数据库 - 两个数据库的表结构可能不同,需要分别检查