-- 假设当前操作的管理员ID -- 在实际应用中,这个ID会从登录会话中获取 -- SELECT admin_id INTO v_admin_id FROM admins WHERE emp_no = 'A001'; -- 示例:获取张管理的ID -- 为了简化示例,我们直接使用数字,假设 admin_id=1 是一个有效管理员 -- 请根据你的 admins 表实际数据替换 ---------------------------------------------------------------------- -- (1). 图书信息管理(增删改查、批量导入) ---------------------------------------------------------------------- -- A. 新增图书 (Create) INSERT INTO books (isbn, title, authors, publisher, publish_date, price, classification_no, location, total_copies, available_copies, description, cover_url) VALUES ('9787121408881', 'Effective Java 中文版 (原书第3版)', ARRAY['Joshua Bloch'], '机械工业出版社', '2019-01-01', 119.00, 'TP312JA', 'A区3架5层', 5, 5, 'Java程序员必读的经典著作,包含90个实用条目。', 'http://example.com/cover_effective_java.jpg'); -- B. 查询图书 (Read) -- B1. 按ISBN精确查询 SELECT * FROM books WHERE isbn = '9787111624927'; -- B2. 按书名模糊查询 (使用 trigram 索引 idx_books_title_trgm) SELECT * FROM books WHERE title % '计算机'; -- 查询包含“计算机”的 SELECT * FROM books WHERE title ILIKE '%系统%'; -- 不区分大小写查询包含“系统”的 -- B3. 按作者查询 (使用 GIN 索引 idx_books_authors_gin) SELECT * FROM books WHERE authors @> ARRAY['吴军']; -- 查询作者包含“吴军”的 -- B4. 按分类号查询 SELECT * FROM books WHERE classification_no = 'TP301'; -- B5. 查看所有“正常”状态的图书 SELECT * FROM books WHERE status = 'normal'; -- C. 修改图书信息 (Update) -- 假设要修改 book_id 为 1 的图书信息 UPDATE books SET price = 145.00, location = 'A区2架3层 (更新)', description = '计算机系统的经典之作,最新修订版。', updated_at = now() WHERE book_id = 1; -- 假设 book_id 为 1 的是《深入理解计算机系统》 -- 修改图书状态 (例如,将一本损坏的书修复后改为正常) UPDATE books SET status = 'normal', available_copies = available_copies + 1 -- 如果之前因损坏不可借 WHERE isbn = '9787208159659' AND status = 'damaged'; -- 假设《三体全集》某本修复 -- D. 删除图书 (Delete) - 谨慎操作,通常建议逻辑删除或下架 -- 注意:如果该书有关联的借阅记录、预约记录、评价记录,且外键设置了 ON DELETE CASCADE,则这些关联记录也会被删除。 -- 如果设置了 ON DELETE RESTRICT (或默认),且存在关联记录,则删除会失败。 -- 你的表结构中,borrow_records, reservations, reviews 都对 book_id 设置了 ON DELETE CASCADE。 -- DELETE FROM books WHERE isbn = '9780132350884'; -- 删除已下架的《Clean Code》 -- 更好的方式是“下架” UPDATE books SET status = 'removed', available_copies = 0 WHERE isbn = '9780132350884'; -- E. 批量导入图书 (Batch Import) INSERT INTO books (isbn, title, authors, publisher, total_copies, available_copies) VALUES ('ISBN001', '批量导入书1', ARRAY['作者X'], '出版社A', 2, 2), ('ISBN002', '批量导入书2', ARRAY['作者Y', '作者Z'], '出版社B', 3, 3); ---------------------------------------------------------------------- -- (2). 学生账户管理 ---------------------------------------------------------------------- -- A. 新增学生账户 INSERT INTO students (stu_no, name, gender, department, major, grade, class, phone, email, max_borrow) VALUES ('S2024001', '刘新', 'M', '物理学院', '应用物理', '2024', '01班', '13600001111', 'liuxin@example.com', (SELECT setting_value::int FROM system_settings WHERE setting_key='max_borrow_default')); -- B. 查询学生账户 -- B1. 按学号查询 SELECT * FROM students WHERE stu_no = 'S2023001'; -- B2. 按姓名模糊查询 SELECT * FROM students WHERE name LIKE '张%'; -- B3. 查询某院系所有学生 SELECT * FROM students WHERE department = '计算机学院'; -- C. 修改学生账户信息 -- C1. 修改学生联系方式 UPDATE students SET phone = '13511112222', email = 'new_zhangsan@example.com' WHERE stu_no = 'S2023001'; -- C2. 修改学生账户状态 (例如:解挂、冻结/解冻 - 冻结通常由触发器完成,但管理员也可手动操作) -- 解挂 UPDATE students SET account_status = 'active' WHERE stu_no = 'S2023005' AND account_status = 'reported'; -- 手动冻结 (如果业务需要) UPDATE students SET account_status = 'frozen' WHERE stu_no = 'S2023001'; -- 手动解冻 (例如,学生缴清罚款后,触发器未及时更新或有特殊情况) -- 首先确保罚款已清或问题已解决 -- UPDATE fines SET status = 'paid' WHERE student_id = (SELECT student_id FROM students WHERE stu_no = 'S2023005') AND status = 'unpaid'; -- 然后(如果触发器没有将所有欠款清零后的账户自动激活) -- UPDATE students SET account_status = 'active' WHERE stu_no = 'S2023005' AND account_status = 'frozen'; -- C3. 修改学生最大借阅量 UPDATE students SET max_borrow = 12 WHERE stu_no = 'S2023001'; ---------------------------------------------------------------------- -- (3). 处理借阅、归还、续借请求 ---------------------------------------------------------------------- -- A. 处理借阅请求 (学生在前台操作,管理员后台处理或确认) -- 假设学生 (stu_no='S2023002', 李四) 要借阅图书 (isbn='9787111624927', 深入理解计算机系统) -- 前提检查 (通常在应用层完成,但也可在存储过程中封装): -- 1. 图书可借: SELECT available_copies, status FROM books WHERE isbn = '9787111624927'; (available_copies > 0 and status = 'normal') -- 2. 学生账户正常: SELECT account_status FROM students WHERE stu_no = 'S2023002'; (account_status = 'active') -- 3. 学生未超借阅上限: SELECT current_borrow, max_borrow FROM students WHERE stu_no = 'S2023002'; (current_borrow < max_borrow) DO $$ DECLARE v_student_id bigint; v_book_id bigint; v_available_copies int; v_book_status book_status_enum; v_account_status acct_status_enum; v_current_borrow int; v_max_borrow int; v_borrow_duration int; BEGIN SELECT student_id, account_status, current_borrow, max_borrow INTO v_student_id, v_account_status, v_current_borrow, v_max_borrow FROM students WHERE stu_no = 'S2023002'; SELECT book_id, available_copies, status INTO v_book_id, v_available_copies, v_book_status FROM books WHERE isbn = '9787111624927'; IF NOT FOUND THEN RAISE EXCEPTION '学生或图书不存在'; END IF; IF v_book_status != 'normal' THEN RAISE EXCEPTION '图书状态异常,不可借阅: %', v_book_status; END IF; IF v_available_copies <= 0 THEN RAISE EXCEPTION '图书已无馆藏可借'; END IF; IF v_account_status != 'active' THEN RAISE EXCEPTION '学生账户状态异常,不可借阅: %', v_account_status; END IF; IF v_current_borrow >= v_max_borrow THEN RAISE EXCEPTION '已达到最大借阅量'; END IF; SELECT setting_value::int INTO v_borrow_duration FROM system_settings WHERE setting_key = 'borrow_duration_days'; INSERT INTO borrow_records (book_id, student_id, borrow_date, due_date, status) VALUES (v_book_id, v_student_id, current_date, current_date + (v_borrow_duration || ' days')::interval, 'borrowed'); RAISE NOTICE '借阅成功! Book ID: %, Student ID: %', v_book_id, v_student_id; -- 触发器 trg_sync_book_student 会自动更新 books.available_copies 和 students.current_borrow END $$; -- B. 处理归还请求 -- 假设要归还 borrow_id 为 (假设是1,根据实际情况查询得到) 的借阅记录 -- SELECT borrow_id FROM borrow_records br JOIN students s ON br.student_id = s.student_id JOIN books b ON br.book_id = b.book_id -- WHERE s.stu_no='S2023001' AND b.isbn='9787111624927' AND br.status IN ('borrowed', 'overdue'); (假设这是张三借的《深入理解计算机系统》) -- 获取特定借阅记录ID (例如,张三当前借阅的《深入理解计算机系统》) DO $$ DECLARE v_borrow_id bigint; BEGIN SELECT br.borrow_id INTO v_borrow_id FROM borrow_records br JOIN students s ON br.student_id = s.student_id JOIN books b ON br.book_id = b.book_id WHERE s.stu_no = 'S2023001' AND b.isbn = '9787111624927' AND br.status IN ('borrowed', 'overdue') ORDER BY br.borrow_date DESC LIMIT 1; -- 获取最近一次未还的 IF NOT FOUND THEN RAISE NOTICE '未找到该学生对应的此书的当前借阅记录。'; RETURN; END IF; RAISE NOTICE '正在归还 borrow_id: %', v_borrow_id; UPDATE borrow_records SET status = 'returned', return_date = current_date WHERE borrow_id = v_borrow_id; -- 触发器 trg_calc_fine 会在逾期归还时自动记录罚款 -- 触发器 trg_sync_book_student 会自动更新 books.available_copies 和 students.current_borrow RAISE NOTICE '归还操作完成。'; END $$; -- C. 处理续借请求 -- 假设要续借 borrow_id 为 (假设是2,李四借的《数学之美》) 的借阅记录 -- 续借条件检查 (应用层或存储过程): -- 1. 图书未被其他人预约: SELECT 1 FROM reservations WHERE book_id = _book_id_ AND status = 'waiting'; (应为空) -- 2. 未超最大续借次数: SELECT renew_times FROM borrow_records WHERE borrow_id = _borrow_id_; -- (renew_times < (SELECT setting_value::int FROM system_settings WHERE setting_key = 'max_renew_times')) -- 3. 学生账户正常,图书未逾期等。 DO $$ DECLARE v_borrow_id bigint; v_book_id bigint; v_renew_times int; v_max_renew_times int; v_is_reserved int; v_borrow_duration int; v_current_due_date date; v_current_status borrow_status_enum; BEGIN -- 假设李四 (S2023002) 续借《数学之美》 (isbn='9787030598007') SELECT br.borrow_id, br.book_id, br.renew_times, br.due_date, br.status INTO v_borrow_id, v_book_id, v_renew_times, v_current_due_date, v_current_status FROM borrow_records br JOIN students s ON br.student_id = s.student_id JOIN books b ON br.book_id = b.book_id WHERE s.stu_no = 'S2023002' AND b.isbn = '9787030598007' AND br.status = 'borrowed' -- 通常只能续借未逾期的 ORDER BY br.borrow_date DESC LIMIT 1; IF NOT FOUND THEN RAISE EXCEPTION '未找到该借阅记录或记录状态不符。'; END IF; IF v_current_status = 'overdue' THEN RAISE EXCEPTION '图书已逾期,不可续借,请先处理逾期。'; END IF; SELECT setting_value::int INTO v_max_renew_times FROM system_settings WHERE setting_key = 'max_renew_times'; SELECT setting_value::int INTO v_borrow_duration FROM system_settings WHERE setting_key = 'borrow_duration_days'; IF v_renew_times >= v_max_renew_times THEN RAISE EXCEPTION '已达到最大续借次数 (%)', v_max_renew_times; END IF; SELECT count(*) INTO v_is_reserved FROM reservations WHERE book_id = v_book_id AND status = 'waiting' AND student_id != (SELECT student_id FROM students WHERE stu_no = 'S2023002'); -- 排除自己的预约 IF v_is_reserved > 0 THEN RAISE EXCEPTION '该图书已被他人预约,不可续借。'; END IF; UPDATE borrow_records SET due_date = v_current_due_date + (v_borrow_duration || ' days')::interval, -- 从当前应还日期开始延长 renew_times = v_renew_times + 1 WHERE borrow_id = v_borrow_id; RAISE NOTICE '续借成功! Borrow ID: %,新应还日期: %', v_borrow_id, (SELECT due_date FROM borrow_records WHERE borrow_id=v_borrow_id); END $$; ---------------------------------------------------------------------- -- (4). 管理预约队列 ---------------------------------------------------------------------- -- A. 查看某本图书的预约队列 (例如 book_id = 7, Java核心技术) SELECT r.reservation_id, r.reserve_date, r.status, s.stu_no, s.name as student_name, s.email FROM reservations r JOIN students s ON r.student_id = s.student_id WHERE r.book_id = 7 AND r.status = 'waiting' -- 假设book_id 7是《Java核心技术 卷I》 ORDER BY r.reserve_date ASC; -- 按预约日期升序,先预约的在前 -- B. 处理到书通知 (当一本被预约的书归还后) -- 假设 book_id = 7 的书有副本归还,管理员通知队列中的第一个学生 -- B1. 找到第一个等待的学生 DO $$ DECLARE v_reservation_id bigint; v_student_id bigint; v_book_id int := 7; -- 假设是 Java 核心技术 v_reservation_expiry_days int; BEGIN SELECT reservation_id, student_id INTO v_reservation_id, v_student_id FROM reservations WHERE book_id = v_book_id AND status = 'waiting' ORDER BY reserve_date ASC LIMIT 1; IF FOUND THEN SELECT setting_value::int INTO v_reservation_expiry_days FROM system_settings WHERE setting_key = 'reservation_expiry_days'; UPDATE reservations SET status = 'available' -- (可以增加一个 notified_at timestamp 和 available_until timestamp 字段) -- available_until = current_timestamp + (v_reservation_expiry_days || ' days')::interval -- (如果表有此字段) WHERE reservation_id = v_reservation_id; RAISE NOTICE '已通知学生ID % (预约ID %) 图书 % 可取。', v_student_id, v_reservation_id, v_book_id; -- 实际应用中会发送邮件/短信 ELSE RAISE NOTICE '图书 % 没有等待中的预约。', v_book_id; END IF; END $$; -- C. 学生未在规定时间内取书,预约自动或手动过期 UPDATE reservations SET status = 'expired' WHERE status = 'available' AND reservation_id = _reservation_id_ ; -- AND available_until < current_timestamp; -- (如果表有 available_until 字段) -- D. 管理员取消某个预约 (例如,学生请求取消) UPDATE reservations SET status = 'cancelled' WHERE reservation_id = _reservation_id_ ; -- 替换为实际预约ID ---------------------------------------------------------------------- -- (5). 处理图书遗失、损坏等异常情况 ---------------------------------------------------------------------- -- A. 处理图书遗失 -- 假设学生 (stu_no='S2022006', 周八) 遗失了图书 (isbn='9787559620187', 明朝那些事儿) -- A1. 更新借阅记录状态 (如果该书是被借阅后遗失的) -- 你已经在测试数据中插入了一条 borrow_records 状态为 'lost' 的记录,这里假设是管理员新发现的遗失 DO $$ DECLARE v_student_id bigint; v_book_id bigint; v_borrow_id bigint; v_book_price numeric; v_admin_id bigint := (SELECT admin_id FROM admins WHERE emp_no = 'A002'); -- 操作管理员ID BEGIN SELECT student_id INTO v_student_id FROM students WHERE stu_no = 'S2022006'; SELECT book_id, price INTO v_book_id, v_book_price FROM books WHERE isbn = '9787559620187'; -- 查找该学生对这本书的未还借阅记录 SELECT borrow_id INTO v_borrow_id FROM borrow_records WHERE student_id = v_student_id AND book_id = v_book_id AND status IN ('borrowed', 'overdue') LIMIT 1; IF FOUND THEN UPDATE borrow_records SET status = 'lost' WHERE borrow_id = v_borrow_id; RAISE NOTICE '借阅记录 % 已更新为遗失。', v_borrow_id; ELSE RAISE NOTICE '未找到该学生对此书的当前借阅记录,可能是在馆藏中发现遗失。'; END IF; -- A2. 更新图书信息状态和可借数量 (如果这本书之前是可借的) -- total_copies 一般不变,除非彻底报废且不再补充 UPDATE books SET status = 'lost', -- 如果整本书遗失 available_copies = GREATEST(0, available_copies - 1) -- 确保可借不为负 WHERE book_id = v_book_id AND status != 'lost'; -- 避免重复操作,如果已是lost就不再减available_copies -- A3. 生成罚款记录 (按书价赔偿) INSERT INTO fines (student_id, amount, reason, status, issue_date, admin_id) VALUES (v_student_id, v_book_price, '遗失图书《明朝那些事儿》(ISBN:9787559620187)', 'unpaid', current_date, v_admin_id); RAISE NOTICE '遗失罚款已记录。'; -- 触发器 trg_freeze_account 会检查是否需要冻结账户 -- 触发器 trg_sync_book_student 会因为 borrow_records 更新而调整 current_borrow END $$; -- B. 处理图书损坏 -- 假设发现馆藏图书 (isbn='9787208159659', 三体全集) 有一本损坏,需要记录并可能罚款最后借阅人 DO $$ DECLARE v_book_id bigint; v_last_borrower_student_id bigint; v_damage_fine_amount numeric := 20.00; -- 损坏赔偿金额 v_admin_id bigint := (SELECT admin_id FROM admins WHERE emp_no = 'A002'); BEGIN SELECT book_id INTO v_book_id FROM books WHERE isbn = '9787208159659'; -- B1. 更新图书状态,如果损坏到不可借阅,减少可借数量 UPDATE books SET status = 'damaged', available_copies = GREATEST(0, available_copies - 1) -- 如果损坏导致不可借 WHERE book_id = v_book_id; RAISE NOTICE '图书 % 状态已更新为损坏。', v_book_id; -- B2. (可选) 查找最后借阅人并生成罚款 SELECT student_id INTO v_last_borrower_student_id FROM borrow_records WHERE book_id = v_book_id AND return_date IS NOT NULL ORDER BY return_date DESC LIMIT 1; IF FOUND THEN INSERT INTO fines (student_id, amount, reason, status, issue_date, admin_id) VALUES (v_last_borrower_student_id, v_damage_fine_amount, '损坏图书《三体全集》(ISBN:9787208159659)', 'unpaid', current_date, v_admin_id); RAISE NOTICE '已向最后借阅人 (学生ID: %) 记录损坏罚款。', v_last_borrower_student_id; -- 触发器 trg_freeze_account ELSE RAISE NOTICE '未找到该书的最后借阅人,或为馆藏期间损坏。'; END IF; END $$; -- C. 图书下架 (例如,图书过于陈旧或残破不再流通) UPDATE books SET status = 'removed', available_copies = 0 WHERE isbn = 'ISBN_TO_BE_REMOVED'; ---------------------------------------------------------------------- -- (6). 设置和管理罚款规则 (主要通过 system_settings 表) ---------------------------------------------------------------------- -- A. 查看当前罚款规则 SELECT * FROM system_settings WHERE setting_key LIKE 'fine%'; -- B. 修改每日逾期罚款金额 UPDATE system_settings SET setting_value = '0.30' -- 修改为每天0.3元 WHERE setting_key = 'fine_per_day'; -- C. 修改欠款冻结账户的阈值 UPDATE system_settings SET setting_value = '15.00' -- 修改为欠款15元冻结 WHERE setting_key = 'freeze_threshold'; -- D. 添加新的罚款规则 (如果系统支持更复杂的规则,可能需要修改表结构或增加新表) -- 当前设计下,罚款规则比较简单,主要就是每日费率。 ---------------------------------------------------------------------- -- (7). 生成各类统计报表 (主要使用视图和聚合查询) ---------------------------------------------------------------------- -- A. 查看热门图书 (借阅量前20,使用视图) SELECT * FROM v_hot_books; -- B. 查看各院系借阅统计 (使用视图) SELECT * FROM v_dept_borrow_stats; -- C. 查看图书逾期情况 (使用视图) SELECT vd.*, b.title AS book_title, s.name AS student_name, s.phone AS student_phone FROM v_overdue_details vd JOIN books b ON vd.book_id = b.book_id JOIN students s ON vd.student_id = s.student_id; -- D. 自定义报表:某一时段内各类图书的借阅次数 SELECT b.classification_no, COUNT(br.borrow_id) AS borrow_count FROM borrow_records br JOIN books b ON br.book_id = b.book_id WHERE br.borrow_date BETWEEN '2025-01-01' AND '2025-12-31' -- 示例时间段 GROUP BY b.classification_no ORDER BY borrow_count DESC; -- E. 自定义报表:每月借阅总量趋势 SELECT to_char(borrow_date, 'YYYY-MM') AS borrow_month, COUNT(borrow_id) AS total_borrows FROM borrow_records GROUP BY borrow_month ORDER BY borrow_month; -- F. 调用存储过程生成定期报表 (如果 sp_generate_circulation_stats 是设计为手动触发的) -- SELECT sp_generate_circulation_stats(); -- 然后查询报表数据 -- SELECT * FROM circulation_stats ORDER BY stat_date DESC; ---------------------------------------------------------------------- -- (8). 系统参数设置 (主要通过 system_settings 表) ---------------------------------------------------------------------- -- A. 查看所有系统参数 SELECT * FROM system_settings; -- B. 修改默认最大借阅量 UPDATE system_settings SET setting_value = '8' WHERE setting_key = 'max_borrow_default'; -- 注意:这只影响未来新建学生账户的默认值,或在 sp_init_students 中使用此值批量更新。 -- 已有学生的 max_borrow 需要单独修改,或在初始化脚本中覆盖。 -- C. 修改默认借阅期限(天数) UPDATE system_settings SET setting_value = '25' WHERE setting_key = 'borrow_duration_days'; -- D. 修改最大续借次数 UPDATE system_settings SET setting_value = '1' WHERE setting_key = 'max_renew_times'; -- E. 修改预约保留天数 UPDATE system_settings SET setting_value = '2' WHERE setting_key = 'reservation_expiry_days'; -- F. 添加新的系统参数 (如果应用需要) -- INSERT INTO system_settings (setting_key, setting_value) -- VALUES ('new_feature_xyz_enabled', 'true');