feat: 初始化考培练系统项目

- 从服务器拉取完整代码
- 按框架规范整理项目结构
- 配置 Drone CI 测试环境部署
- 包含后端(FastAPI)、前端(Vue3)、管理端

技术栈: Vue3 + TypeScript + FastAPI + MySQL
This commit is contained in:
111
2026-01-24 19:33:28 +08:00
commit 998211c483
1197 changed files with 228429 additions and 0 deletions

View File

@@ -0,0 +1,378 @@
# 知识点DELETE语句未能删除全部记录的原因分析
**问题SQL**
```sql
DELETE FROM knowledge_points WHERE material_id = 34;
```
**问题描述**用户反馈该SQL语句未能删除资料34的全部知识点
---
## 🔍 问题分析
### 1. 数据现状
资料34当前有6个知识点记录
| ID | 知识点名称 | is_deleted | 创建时间 |
|----|-----------|------------|----------|
| 1142 | FAB法则介绍 | 0 | 2025-10-17 07:20:28 |
| 1143 | 全面有效的抗衰需求满足 | 0 | 2025-10-17 07:20:28 |
| 1144 | 能量聚焦与精准技术 | 0 | 2025-10-17 07:20:28 |
| 1145 | 多层次治疗与专利炮头 | 0 | 2025-10-17 07:20:28 |
| 1146 | 国内首款医美合规超声刀的安全性与合规性 | 0 | 2025-10-17 07:20:28 |
| 1147 | 提升舒适度与减少副作用 | 0 | 2025-10-17 07:20:28 |
### 2. 表结构分析
```sql
CREATE TABLE `knowledge_points` (
`id` int NOT NULL AUTO_INCREMENT,
`course_id` int DEFAULT NULL COMMENT '所属课程ID',
`material_id` int NOT NULL COMMENT '关联资料ID必填',
`name` varchar(200) NOT NULL COMMENT '知识点名称',
`description` text COMMENT '知识点描述',
`is_deleted` tinyint(1) DEFAULT '0' COMMENT '是否删除', -- ⚠️ 软删除字段
`deleted_at` datetime DEFAULT NULL COMMENT '删除时间',
`created_by` int DEFAULT NULL,
`updated_by` int DEFAULT NULL,
`created_at` datetime DEFAULT CURRENT_TIMESTAMP,
`updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`type` varchar(50) DEFAULT '理论知识',
`source` tinyint(1) DEFAULT '0',
`topic_relation` varchar(200) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_course_id` (`course_id`),
KEY `idx_is_deleted` (`is_deleted`),
KEY `material_id` (`material_id`),
CONSTRAINT `knowledge_points_ibfk_1` FOREIGN KEY (`course_id`)
REFERENCES `courses` (`id`) ON DELETE CASCADE,
CONSTRAINT `knowledge_points_ibfk_2` FOREIGN KEY (`material_id`)
REFERENCES `course_materials` (`id`) ON DELETE CASCADE
);
```
**关键发现**
- ✅ 表中有 `is_deleted` 字段 → **系统使用软删除机制**
- ✅ material_id有外键约束ON DELETE CASCADE
- ✅ course_id有外键约束ON DELETE CASCADE
### 3. 外键约束检查
```sql
-- exam_mistakes表引用knowledge_points
CONSTRAINT exam_mistakes_ibfk_4
FOREIGN KEY (knowledge_point_id)
REFERENCES knowledge_points(id)
ON DELETE SET NULL
```
**检查结果**
- ✅ 没有错题记录引用资料34的知识点
- ✅ DELETE_RULE是SET NULL不会阻止删除
---
## ❌ 问题根因
### 核心问题系统使用软删除但SQL使用了物理删除
**系统设计**
- 后端代码使用 `soft_delete()` 方法,将 `is_deleted` 设置为1
- 前端查询时使用 `WHERE is_deleted = 0` 筛选未删除的记录
- 所有删除操作都是软删除,不会真正从数据库删除记录
**用户执行的SQL**
```sql
DELETE FROM knowledge_points WHERE material_id = 34; -- ❌ 物理删除
```
**为什么"未能删除全部知识点"**
有以下几种可能原因:
#### 原因1SQL实际上执行成功了最可能
- ✅ DELETE语句执行成功记录被物理删除
- ✅ 但之后系统又重新创建了这些知识点
- 🔍 需要检查是否有后台任务或自动同步机制
#### 原因2未提交事务
```sql
-- 如果在事务中执行但未提交
START TRANSACTION;
DELETE FROM knowledge_points WHERE material_id = 34;
-- 未执行 COMMIT
```
#### 原因3没有执行权限
- 用户可能没有DELETE权限
- 需要检查用户权限
#### 原因4有触发器或其他机制
- 可能有BEFORE DELETE或AFTER DELETE触发器阻止删除
- 需要检查触发器
---
## ✅ 正确的删除方法
### 方法1使用系统的软删除推荐
**后端API方式**
```bash
# 通过后端API删除软删除
curl -X DELETE "https://aiedu.ireborn.com.cn/api/v1/courses/materials/34/knowledge-points/{knowledge_point_id}" \
-H "Authorization: Bearer {token}"
```
**直接SQL方式软删除**
```sql
-- 软删除资料34的所有知识点
UPDATE knowledge_points
SET is_deleted = 1,
deleted_at = NOW()
WHERE material_id = 34
AND is_deleted = 0;
```
### 方法2删除资料级联删除知识点
```sql
-- 软删除资料,知识点会通过外键级联删除
UPDATE course_materials
SET is_deleted = 1,
deleted_at = NOW()
WHERE id = 34
AND is_deleted = 0;
```
### 方法3物理删除谨慎使用
**⚠️ 警告**:物理删除会永久删除数据,无法恢复!
```sql
-- 如果确实需要物理删除
DELETE FROM knowledge_points WHERE material_id = 34;
-- 验证删除结果
SELECT COUNT(*) FROM knowledge_points WHERE material_id = 34;
-- 应该返回 0
```
---
## 🔍 排查步骤
### 1. 检查记录是否真的还在
```sql
-- 查看记录数量
SELECT COUNT(*) as total_count
FROM knowledge_points
WHERE material_id = 34;
-- 查看详细记录
SELECT id, name, is_deleted, deleted_at
FROM knowledge_points
WHERE material_id = 34
ORDER BY id;
```
### 2. 检查事务状态
```sql
-- 查看当前未提交的事务
SELECT * FROM information_schema.INNODB_TRX;
-- 如果有未提交的事务,提交或回滚
COMMIT;
-- 或
ROLLBACK;
```
### 3. 检查触发器
```sql
-- 查看knowledge_points表的触发器
SHOW TRIGGERS LIKE 'knowledge_points';
```
### 4. 检查删除权限
```sql
-- 查看当前用户权限
SHOW GRANTS FOR 'root'@'%';
-- 或查看当前会话权限
SHOW GRANTS FOR CURRENT_USER;
```
### 5. 验证外键约束
```sql
-- 禁用外键检查(谨慎使用)
SET FOREIGN_KEY_CHECKS = 0;
-- 执行删除
DELETE FROM knowledge_points WHERE material_id = 34;
-- 重新启用外键检查
SET FOREIGN_KEY_CHECKS = 1;
-- 验证结果
SELECT COUNT(*) FROM knowledge_points WHERE material_id = 34;
```
---
## 📝 推荐操作流程
### 场景A想要"隐藏"知识点(软删除)✅ 推荐
```sql
-- 1. 软删除知识点
UPDATE knowledge_points
SET is_deleted = 1,
deleted_at = NOW(),
updated_by = {user_id} -- 记录操作人
WHERE material_id = 34
AND is_deleted = 0;
-- 2. 验证结果
SELECT COUNT(*) as deleted_count
FROM knowledge_points
WHERE material_id = 34
AND is_deleted = 1;
-- 3. 前端查询时会自动过滤
-- SELECT * FROM knowledge_points WHERE material_id = 34 AND is_deleted = 0;
-- 返回 0 条记录
```
### 场景B需要永久删除记录物理删除 谨慎
```sql
-- 1. 备份数据(重要!)
CREATE TABLE knowledge_points_backup_20251017 AS
SELECT * FROM knowledge_points WHERE material_id = 34;
-- 2. 检查依赖关系
SELECT COUNT(*) as dependent_count
FROM exam_mistakes
WHERE knowledge_point_id IN (
SELECT id FROM knowledge_points WHERE material_id = 34
);
-- 3. 执行物理删除
DELETE FROM knowledge_points WHERE material_id = 34;
-- 4. 验证结果
SELECT COUNT(*) FROM knowledge_points WHERE material_id = 34;
-- 应该返回 0
-- 5. 提交事务
COMMIT;
```
---
## 🎯 解决方案总结
### 如果要删除资料34的全部知识点
**✅ 推荐通过后端API删除资料**
```bash
# 这会软删除资料及其所有知识点
curl -X DELETE "https://aiedu.ireborn.com.cn/api/v1/courses/{course_id}/materials/34" \
-H "Authorization: Bearer {token}"
```
**✅ 次选SQL软删除**
```sql
-- 软删除所有知识点
UPDATE knowledge_points
SET is_deleted = 1, deleted_at = NOW()
WHERE material_id = 34 AND is_deleted = 0;
```
**⚠️ 不推荐SQL物理删除**
```sql
-- 永久删除(不可恢复)
DELETE FROM knowledge_points WHERE material_id = 34;
COMMIT; -- 记得提交事务!
```
---
## 🔧 故障排除
### 如果DELETE语句执行后记录仍然存在
1. **检查事务是否提交**
```sql
COMMIT;
```
2. **检查是否在错误的数据库**
```sql
SELECT DATABASE();
-- 应该返回 kaopeilian
```
3. **检查WHERE条件是否正确**
```sql
SELECT COUNT(*) FROM knowledge_points WHERE material_id = 34;
-- 执行DELETE前应该有记录
```
4. **查看MySQL错误日志**
```bash
docker exec kaopeilian-mysql-dev tail -100 /var/log/mysql/error.log
```
5. **测试简单的DELETE**
```sql
-- 删除单条记录测试
DELETE FROM knowledge_points WHERE id = 1142 LIMIT 1;
SELECT * FROM knowledge_points WHERE id = 1142;
```
---
## 📚 相关文档
- 数据库架构:`/root/aiedu/kaopeilian-backend/数据库架构-统一版.md`
- Service层代码`/root/aiedu/kaopeilian-backend/app/services/course_service.py`
- Base Service`/root/aiedu/kaopeilian-backend/app/services/base_service.py`
---
## 💡 经验教训
1. **理解系统的删除机制**
- 软删除系统中直接使用DELETE可能导致数据不一致
- 应该使用系统提供的API或软删除SQL
2. **谨慎使用物理删除**
- 物理删除是不可逆的
- 删除前应该备份数据
- 检查是否有外键依赖
3. **使用事务**
- 重要的删除操作应该在事务中进行
- 先验证后提交
4. **记录操作日志**
- 删除操作应该记录操作人和操作时间
- 便于追溯和审计
---
**结论**用户的DELETE语句从语法上是正确的能够执行。如果执行后记录仍然存在最可能的原因是
1. 事务未提交
2. 后台任务重新创建了记录
3. 在错误的数据库环境执行
建议使用软删除而不是物理删除,以保持系统一致性。