csdesign-smart-library/create_library.sql
2025-06-24 10:56:21 +08:00

239 lines
15 KiB
SQL
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

SET search_path TO library, public;
-- 清理可能存在的旧数据(可选,测试时方便重跑)
-- 请注意CASCADE 会删除关联数据,请谨慎在生产环境使用
-- DELETE FROM fines;
-- DELETE FROM reviews;
-- DELETE FROM reservations;
-- DELETE FROM borrow_records;
-- DELETE FROM admins;
-- DELETE FROM students;
-- DELETE FROM books;
-- DELETE FROM system_settings WHERE setting_key NOT IN ('fine_per_day', 'freeze_threshold', 'max_borrow_default'); -- 保留核心设置
-- 0. 系统参数 (你已插入,这里可以补充更多,如果需要)
INSERT INTO system_settings(setting_key, setting_value) VALUES
('max_renew_times', '2'), -- 最大续借次数
('borrow_duration_days', '30'), -- 默认借阅时长(天)
('reservation_expiry_days', '3'), -- 预约保留天数
('admin_default_password_hash', 'xxxx'); -- 示例:管理员默认密码哈希
-- ON CONFLICT (setting_key) DO UPDATE SET setting_value = EXCLUDED.setting_value; -- 如果要允许更新
-- 1. 管理员信息 (Admins)
INSERT INTO admins (emp_no, name, position, phone, privilege_lv) VALUES
('A001', '张管理', '馆长', '13800138000', 0), -- 最高权限
('A002', '李协助', '图书管理员', '13900139000', 1),
('A003', '王登记', '流通部职员', '13700137000', 2);
-- 2. 图书信息 (Books)
-- 注意: available_copies 初始设为 total_copies后续借阅会通过触发器减少
INSERT INTO books (isbn, title, authors, publisher, publish_date, price, classification_no, location, total_copies, available_copies, status, description, cover_url) VALUES
('9787111624927', '深入理解计算机系统', ARRAY['Randal E. Bryant', 'David R. O''Hallaron'], '机械工业出版社', '2019-05-01', 139.00, 'TP301', 'A区2架3层', 10, 10, 'normal', '计算机系统的经典之作。', 'http://example.com/cover1.jpg'),
('9787030598007', '数学之美', ARRAY['吴军'], '科学出版社', '2020-01-01', 68.00, 'O1', 'B区1架1层', 15, 15, 'normal', '通俗易懂的数学科普读物。', 'http://example.com/cover2.jpg'),
('9787544270878', '百年孤独', ARRAY['加西亚·马尔克斯'], '南海出版公司', '2011-06-01', 39.50, 'I775.45', 'C区5架2层', 8, 8, 'normal', '魔幻现实主义文学的代表作。', 'http://example.com/cover3.jpg'),
('9787115478850', 'Python编程从入门到实践', ARRAY['Eric Matthes'], '人民邮电出版社', '2018-03-01', 89.00, 'TP312PY', 'A区3架1层', 12, 12, 'normal', 'Python入门畅销书。', 'http://example.com/cover4.jpg'),
('9787508647009', '人类简史:从动物到上帝', ARRAY['尤瓦尔·赫拉利'], '中信出版社', '2014-11-01', 68.00, 'K02', 'D区1架1层', 20, 20, 'normal', '一部宏大的人类发展史。', 'http://example.com/cover5.jpg'),
('9787532767406', '追风筝的人', ARRAY['卡勒德·胡赛尼'], '上海人民出版社', '2006-05-01', 29.00, 'I712.45', 'C区4架3层', 7, 7, 'normal', '关于爱、友谊、背叛和救赎的故事。', 'http://example.com/cover6.jpg'),
('9787121362308', 'Java核心技术 卷I', ARRAY['Cay S. Horstmann'], '电子工业出版社', '2019-06-01', 128.00, 'TP312JA', 'A区3架2层', 5, 0, 'normal', 'Java经典教材目前全部被借出用于测试预约。', 'http://example.com/cover7.jpg'), -- 特意设置 available_copies 为 0
('9787208159659', '三体全集', ARRAY['刘慈欣'], '重庆出版社', '2019-01-01', 168.00, 'I247.5', 'C区6架1层', 3, 3, 'damaged', '科幻巨作,其中一本有损坏。', 'http://example.com/cover8.jpg'),
('9787559620187', '明朝那些事儿 (套装全7册)', ARRAY['当年明月'], '北京联合出版公司', '2017-08-01', 258.00, 'K248', 'D区2架1层', 6, 6, 'normal', '通俗易懂的明史。', 'http://example.com/cover9.jpg'),
('9787111600815', '算法导论 (原书第3版)', ARRAY['Thomas H. Cormen'], '机械工业出版社', '2012-12-01', 128.00, 'TP301.6', 'A区2架4层', 9, 9, 'normal', '算法领域的圣经。', 'http://example.com/cover10.jpg'),
('9780132350884', 'Clean Code', ARRAY['Robert C. Martin'], 'Prentice Hall', '2008-08-01', 50.00, 'TP311.1', 'A区1架5层', 4, 4, 'removed', '一本已下架的书,测试状态。', 'http://example.com/cover11.jpg');
-- 3. 学生信息 (Students)
-- max_borrow 使用默认值(通过 get_setting_int('max_borrow_default') 获取即10
-- current_borrow 初始为0会由触发器更新
INSERT INTO students (stu_no, name, gender, department, major, grade, class, phone, email, account_status) VALUES
('S2023001', '张三', 'M', '计算机学院', '软件工程', '2023', '01班', '13512345671', 'zhangsan@example.com', 'active'),
('S2023002', '李四', 'F', '计算机学院', '人工智能', '2023', '02班', '13512345672', 'lisi@example.com', 'active'),
('S2022003', '王五', 'M', '外国语学院', '英语', '2022', '01班', '13512345673', 'wangwu@example.com', 'active'),
('S2021004', '赵六', 'F', '经济管理学院', '工商管理', '2021', '03班', '13512345674', 'zhaoliu@example.com', 'active'),
('S2023005', '孙七', 'M', '计算机学院', '软件工程', '2023', '01班', '13512345675', 'sunqi@example.com', 'reported'), -- 测试挂失状态
('S2022006', '周八', 'F', '人文学院', '历史学', '2022', '02班', '13512345676', 'zhouba@example.com', 'active'),
('S2021007', '吴九', 'M', '理学院', '数学与应用数学', '2021', '01班', '13512345677', 'wujiu@example.com', 'active'),
('S2023008', '郑十', 'F', '计算机学院', '网络工程', '2023', '03班', '13512345678', 'zhengshi@example.com', 'active');
-- 修改一个学生的 max_borrow测试非默认值
UPDATE students SET max_borrow = 15 WHERE stu_no = 'S2023001';
UPDATE students SET max_borrow = 5 WHERE stu_no = 'S2021004'; -- 用于测试借阅上限
-- 4. 借阅记录 (Borrow Records)
-- book_id 和 student_id 需要引用已存在的 ID
-- due_date = borrow_date + (SELECT setting_value::int FROM system_settings WHERE setting_key = 'borrow_duration_days')
-- fine_amount 初始为0, 逾期归还将由触发器在 fines 表中记录罚款
-- 触发器 trg_sync_book_student 会在每次插入/更新/删除后执行
-- 获取 book_id 和 student_id 的示例 (实际使用时,你可能需要根据书名/学号查询得到)
-- SELECT book_id FROM books WHERE isbn = '9787111624927'; -- 1
-- SELECT student_id FROM students WHERE stu_no = 'S2023001'; -- 1
-- 借阅场景 1: 正常借出,未到期
INSERT INTO borrow_records (book_id, student_id, borrow_date, due_date, status) VALUES
(1, 1, current_date - interval '10 days', current_date - interval '10 days' + interval '30 days', 'borrowed'), -- 张三借《深入理解计算机系统》
(2, 2, current_date - interval '5 days', current_date - interval '5 days' + interval '30 days', 'borrowed'); -- 李四借《数学之美》
-- 借阅场景 2: 正常借出,今天到期
INSERT INTO borrow_records (book_id, student_id, borrow_date, due_date, status) VALUES
(3, 1, current_date - interval '30 days', current_date, 'borrowed'); -- 张三借《百年孤独》,今天到期
-- 借阅场景 3: 已逾期,未归还
INSERT INTO borrow_records (book_id, student_id, borrow_date, due_date, status) VALUES
(4, 3, current_date - interval '40 days', current_date - interval '10 days', 'overdue'); -- 王五借《Python编程》已逾期10天
-- 借阅场景 4: 正常归还 (触发器 trg_calc_fine 不会产生罚款)
INSERT INTO borrow_records (book_id, student_id, borrow_date, due_date, return_date, status) VALUES
(5, 4, current_date - interval '20 days', current_date + interval '10 days', current_date - interval '2 days', 'returned'); -- 赵六借《人类简史》,已按时归还
-- 借阅场景 5: 逾期归还 (为了测试 trg_calc_fine先插入为逾期再UPDATE为归还)
-- 步骤A: 先插入一条记录,状态为 'overdue' (或者 'borrowed' 但实际已过 due_date)
INSERT INTO borrow_records (book_id, student_id, borrow_date, due_date, status)
VALUES (6, 5, current_date - interval '35 days', current_date - interval '5 days', 'overdue'); -- 孙七借《追风筝的人》
-- 记录下这条 borrow_id (假设是 6根据实际情况调整)
-- 步骤B: 更新这条记录为 'returned',这将触发 trg_calc_fine
-- UPDATE borrow_records
-- SET status = 'returned', return_date = current_date
-- WHERE borrow_id = (SELECT borrow_id FROM borrow_records WHERE student_id=5 AND book_id=6 AND status='overdue');
-- (该update放到下面演示触发器部分)
-- 借阅场景 6: 书籍遗失
INSERT INTO borrow_records (book_id, student_id, borrow_date, due_date, status) VALUES
(9, 6, current_date - interval '15 days', current_date + interval '15 days', 'lost'); -- 周八借《明朝那些事儿》,遗失
-- 5. 图书预约 (Reservations)
-- 只能预约 available_copies = 0 的书
-- (book_id=7, 'Java核心技术 卷I', available_copies 初始为0)
INSERT INTO reservations (book_id, student_id, reserve_date, status) VALUES
(7, 1, current_date - interval '2 days', 'waiting'), -- 张三预约《Java核心技术》
(7, 2, current_date - interval '1 day', 'waiting'); -- 李四预约《Java核心技术》
-- 假设一本被预约的书有归还了,管理员将其状态改为可取
-- UPDATE reservations SET status = 'available' WHERE book_id = 7 AND student_id = 1;
-- 6. 图书评价 (Reviews)
-- 学生通常评价已借阅过的书
INSERT INTO reviews (book_id, student_id, rating, content, review_time) VALUES
(5, 4, 5, '《人类简史》这本书太棒了,视角独特,值得一读!', now() - interval '1 day'), -- 赵六评价《人类简史》
(1, 1, 4, '《深入理解计算机系统》有点难,但很有收获。', now()), -- 张三评价
(2, 2, 5, '《数学之美》让我对数学有了新的认识。', now()); -- 李四评价
-- 尝试插入重复评价应失败因为有UNIQUE约束
-- INSERT INTO reviews (book_id, student_id, rating, content, review_time) VALUES
-- (5, 4, 3, '第二次评价,内容一般。', now());
-- 7. 罚款记录 (Fines)
-- 部分罚款会由 trg_calc_fine 自动生成 (当逾期借阅记录更新为 'returned' 时)
-- 手动添加一些罚款记录:
-- 罚款场景1: 图书损坏 (假设管理员A002处理)
INSERT INTO fines (student_id, amount, reason, status, issue_date, admin_id) VALUES
( (SELECT student_id FROM students WHERE stu_no = 'S2022003'), -- 王五
20.00,
'损坏图书《Python编程》(ISBN:9787115478850)',
'unpaid',
current_date - interval '1 day',
(SELECT admin_id FROM admins WHERE emp_no = 'A002')
);
-- 罚款场景2: 图书遗失 (假设管理员A002处理)
-- 对应 borrow_records 中周八遗失的《明朝那些事儿》
INSERT INTO fines (student_id, amount, reason, status, issue_date, admin_id) VALUES
( (SELECT student_id FROM students WHERE stu_no = 'S2022006'), -- 周八
(SELECT price FROM books WHERE isbn = '9787559620187'), -- 按书价赔偿
'遗失图书《明朝那些事儿》(ISBN:9787559620187)',
'unpaid',
current_date,
(SELECT admin_id FROM admins WHERE emp_no = 'A002')
);
-- 罚款场景3: 之前的逾期罚款,已缴纳
INSERT INTO fines (student_id, amount, reason, status, issue_date, admin_id) VALUES
( (SELECT student_id FROM students WHERE stu_no = 'S2021007'), -- 吴九
5.50,
'图书逾期11天 (旧记录)',
'paid',
current_date - interval '60 days',
(SELECT admin_id FROM admins WHERE emp_no = 'A003')
);
----------------------------------------------------
-- 测试触发器 trg_calc_fine 和 trg_freeze_account
----------------------------------------------------
DO $$
DECLARE
v_student_id_sunqi bigint;
v_borrow_id_sunqi bigint;
v_fine_per_day numeric;
v_days_overdue int;
v_expected_fine numeric;
BEGIN
-- 获取孙七的 student_id
SELECT student_id INTO v_student_id_sunqi FROM students WHERE stu_no = 'S2023005';
-- 获取孙七之前插入的逾期借阅记录 (book_id=6, 追风筝的人)
SELECT borrow_id INTO v_borrow_id_sunqi
FROM borrow_records
WHERE student_id = v_student_id_sunqi AND book_id = (SELECT book_id FROM books WHERE isbn='9787532767406') AND status = 'overdue';
RAISE NOTICE '孙七(ID:%)的借阅记录ID: % 将被更新为已归还。', v_student_id_sunqi, v_borrow_id_sunqi;
-- 模拟孙七归还之前逾期的书 (book_id=6, 《追风筝的人》)
-- 这将触发 trg_calc_fine
UPDATE borrow_records
SET status = 'returned', return_date = current_date
WHERE borrow_id = v_borrow_id_sunqi;
RAISE NOTICE '已更新孙七的借阅记录。现在检查 fines 表是否自动产生罚款记录...';
-- trg_calc_fine 会根据 borrow_records 中的 due_date 和 return_date 计算罚款并插入 fines 表
-- 假设罚款汇率是 0.5/天逾期5天罚款应为 2.50
-- (current_date - (current_date - interval '5 days')) * 0.50
SELECT setting_value::numeric INTO v_fine_per_day FROM system_settings WHERE setting_key = 'fine_per_day';
SELECT (current_date - (SELECT due_date FROM borrow_records WHERE borrow_id = v_borrow_id_sunqi)) INTO v_days_overdue;
v_expected_fine := v_days_overdue * v_fine_per_day;
RAISE NOTICE '预期罚款金额: % * % = %', v_days_overdue, v_fine_per_day, v_expected_fine;
-- 检查孙七的账户状态是否因为罚款超过阈值 (默认20) 而被冻结
-- trg_freeze_account 会在 fines 表 INSERT/UPDATE/DELETE 后触发
RAISE NOTICE '检查孙七账户是否因罚款自动冻结...';
-- 如果孙七的罚款 (例如 2.50) 没有超过 freeze_threshold (默认20.00),账户不会冻结。
-- 我们再为孙七手动添加一笔大额罚款,使其总欠款超过阈值
IF (SELECT sum(amount) FROM fines WHERE student_id = v_student_id_sunqi AND status = 'unpaid') < (SELECT setting_value::numeric FROM system_settings WHERE setting_key = 'freeze_threshold') THEN
RAISE NOTICE '孙七当前未付罚款未达冻结阈值,为其添加一笔大额罚款...';
INSERT INTO fines (student_id, amount, reason, status, issue_date, admin_id) VALUES
(v_student_id_sunqi, 25.00, '严重损坏图书赔偿', 'unpaid', current_date, (SELECT admin_id FROM admins WHERE emp_no='A001'));
RAISE NOTICE '已为孙七添加大额罚款,再次检查账户状态。';
END IF;
END $$;
-- 查询验证触发器效果
RAISE NOTICE '---------- 查询验证 ----------';
RAISE NOTICE '1. 书籍可借数量和学生当前借阅量 (应由 trg_sync_book_student 更新):';
SELECT book_id, title, total_copies, available_copies FROM books WHERE book_id <= 7;
SELECT student_id, name, max_borrow, current_borrow, account_status FROM students WHERE student_id <= 5;
RAISE NOTICE '2. 孙七 (S2023005) 的罚款记录 (应有 trg_calc_fine 生成的逾期罚款 和 手动添加的罚款):';
SELECT f.*, s.name as student_name
FROM fines f JOIN students s ON f.student_id = s.student_id
WHERE s.stu_no = 'S2023005';
RAISE NOTICE '3. 孙七 (S2023005) 的账户状态 (应由 trg_freeze_account 根据总欠款更新):';
SELECT stu_no, name, account_status, current_borrow
FROM students
WHERE stu_no = 'S2023005';
RAISE NOTICE '4. 查看热门图书视图:';
SELECT * FROM v_hot_books;
RAISE NOTICE '5. 查看院系借阅统计视图:';
SELECT * FROM v_dept_borrow_stats;
RAISE NOTICE '6. 查看逾期详情视图:';
SELECT od.*, b.title, s.name as student_name
FROM v_overdue_details od
JOIN books b ON od.book_id = b.book_id
JOIN students s ON od.student_id = s.student_id;
RAISE NOTICE '测试数据插入完毕。';