# 校园生活平台数据库课程设计报告 ## 1. 课题概述 ### 1.1 课题背景 本课题以“校园生活平台”为题,在已有 Web 项目基础上,按照《数据库原理及应用》课程设计要求,重新组织为一个以数据库设计为核心的综合案例。与普通课程项目强调界面展示不同,本课题重点讨论的是如何使用关系数据库对复杂校园业务进行建模,并把关键业务规则尽量下沉到数据库层。 本轮课程设计不追求覆盖平台全部历史模块,而是聚焦三个最能体现数据库设计能力的主线子系统: - 平台基础子系统 - 身份与资料分离、组织结构、RBAC、数据范围、审计日志 - 功能房预约子系统 - 楼房、房间、预约、参与人、封禁、时间冲突与状态流转 - 课程资源分享子系统 - 专业、课程、资源、审核流、去重、下载事件、积分事件 数字图书馆仅作为可选扩展,不占本次正文主体篇幅。 ### 1.2 设计目标 本课题希望通过一个具有多实体、多联系、多状态、多约束的校园场景,体现以下数据库课程目标: 1. 能从需求分析出发识别实体、联系与业务规则,而不是直接写表。 2. 能完成概念结构设计、逻辑结构设计、物理设计、实现与测试验证的完整过程。 3. 能用主键、候选键、外键、函数依赖、范式、完整性约束、索引、触发器和事务解释数据库设计。 4. 能证明关键业务规则确实落到了数据库层,而不是只停留在服务层 `if` 判断。 ### 1.3 技术选型说明 本课题以 PostgreSQL 为核心数据库。Supabase 在本项目中主要承担 PostgreSQL、身份认证和运行环境承载作用,但课程设计报告的重点始终放在 PostgreSQL 的关系设计、完整性控制、事务与并发控制上,而不把托管平台本身当成数据库设计亮点。 数据库设计主线遵循: - 需求分析 - 概念结构设计 - 逻辑结构设计 - 物理设计 - 实施与数据库对象落库 - 测试验证 ## 2. 需求分析与数据需求 ### 2.1 主要角色与核心场景 本课题涉及的主要角色包括: - 普通学生用户 - 专业负责人或课程资源审核者 - 功能房审核管理员 - 平台管理员 围绕这些角色,主线业务需要解决的问题如下: 1. 平台基础子系统需要表达用户、资料、部门、岗位、角色、权限、数据范围和审计日志之间的关系。 2. 功能房预约子系统需要表达空间资源、预约时间段、审核流、参与人集合和封禁记录之间的关系。 3. 课程资源分享子系统需要表达专业、课程、资源、审核、最佳推荐、下载事件和积分事件之间的关系。 ### 2.2 数据需求中的关键业务规则 从需求分析阶段抽取出的数据库关键规则主要包括: - 用户资料必须依附真实身份存在。 - 部门需要支持树结构查询与“本部门及子部门”范围展开。 - 角色、权限和数据范围需要形成可追溯的授权关系。 - 审计日志必须保留历史真实性,不允许事后篡改。 - 同一房间在同一时间段内不能存在相交的活跃预约。 - 一条功能房预约必须对应合法的参与人集合,并且申请人必须出现在参与人中。 - 课程资源需要支持审核发布状态机,且文件资源与外链资源字段组合必须合法。 - 课程资源存在必要冗余字段时,必须由数据库机制锁定一致性。 - 排行榜和统计不能只靠页面计算,应有可解释的数据事实来源。 ### 2.3 从需求到数据库设计的因果链 本课题没有把需求分析与后续数据库设计割裂开来,而是明确建立了以下映射: - 需求中的“部门及子部门可见”对应概念结构中的部门树,逻辑结构中的 `departments` 与 `department_closure`,物理设计中的树查询索引与闭包表主键。 - 需求中的“同一房间同一时间不可冲突”对应概念结构中的预约时序联系,逻辑结构中的 `facility_reservations`,物理设计中的 `EXCLUDE USING gist` 与部分索引。 - 需求中的“资源审核、最佳推荐、积分发放”对应概念结构中的资源主体与事件事实分层,逻辑结构中的 `course_resources`、`course_resource_bests`、`course_resource_score_events`,物理设计中的复合外键、状态 `CHECK`、触发器和唯一约束。 因此,本课题的数据库设计不是“先写代码后补文档”,而是围绕需求规则逐步落到 E-R、关系模式、物理对象和测试验证。 ## 3. 概念结构设计 ### 3.1 总体概念结构 从概念结构角度看,校园生活平台当前主线可以分为三层: 1. 平台基础层 - 用户、资料、部门、岗位、角色、权限、模块、数据范围、审计 2. 业务事务层 - 功能房预约主体与课程资源主体 3. 业务事实层 - 参与人、下载事件、积分事件、最佳推荐、封禁记录 这种分层使得“主数据”“事务数据”“事件事实数据”能够被区分开来,避免把所有业务信息堆进一张大表。 ### 3.2 平台基础子系统概念结构 平台基础子系统的主要实体包括: - 用户身份 `auth.users` - 用户资料 `profiles` - 部门 `departments` - 部门闭包表 `department_closure` - 岗位 `positions` - 角色 `roles` - 权限 `permissions` - 模块字典 `app_modules` - 数据权限模块能力表 `data_scope_modules` - 审计日志 `audit_logs` 其主要联系包括: - 用户与资料是一对一联系。 - 部门与部门之间构成自引用层级联系。 - 用户与部门、岗位、角色之间均为多对多联系,需要拆分桥接表。 - 角色与权限之间是多对多联系。 - 角色与数据范围之间是“角色 + 模块”维度上的受限配置关系。 ### 3.3 功能房预约子系统概念结构 功能房预约子系统的主要实体包括: - 楼房 `facility_buildings` - 房间 `facility_rooms` - 预约主体 `facility_reservations` - 预约参与人 `facility_reservation_participants` - 封禁记录 `facility_bans` 其主要联系包括: - 一栋楼房包含多个房间。 - 一个房间可被多次预约,但有效预约之间不能时间重叠。 - 一条预约对应多个参与人,其中恰有一个参与人是申请人。 - 封禁记录与用户是一对多关系,用于控制预约资格。 ### 3.4 课程资源分享子系统概念结构 课程资源分享子系统的主要实体包括: - 专业 `majors` - 课程 `courses` - 资源主体 `course_resources` - 专业负责人 `major_leads` - 最佳推荐 `course_resource_bests` - 下载事件 `course_resource_download_events` - 积分事件 `course_resource_score_events` 其主要联系包括: - 一个专业下有多门课程。 - 一门课程下可提交多条资源。 - 资源与审核流、最佳推荐、下载事实、积分事实相关联。 - 资源主体与事件事实分层,使统计查询和业务状态控制可以分离建模。 ### 3.5 概念结构到逻辑结构的转换原则 本课题在 E-R 转换为关系模式时遵循以下原则: 1. 一对一联系优先通过共享主键或外键唯一约束实现,如 `profiles.id -> auth.users.id`。 2. 一对多联系在“多”端设置显式物理外键,如 `facility_rooms.building_id`、`courses.major_id`。 3. 多对多联系一律拆成桥接表,如 `user_roles`、`role_permissions`、`major_leads`、`facility_reservation_participants`。 4. 树结构采用“自引用 + 闭包表”双层设计,而不是把完整层级路径塞进单字段。 5. 统计场景优先建立事件事实表,而不是在主体表上做不可追溯的覆盖式累计。 ## 4. 逻辑结构设计 ### 4.1 核心关系模式 围绕当前课程设计主线,可将最关键的关系模式概括如下: - `PROFILES(id, name, username, student_id, status, ...)` - `DEPARTMENTS(id, name, parent_id, sort, ...)` - `DEPARTMENT_CLOSURE(ancestor_id, descendant_id, depth)` - `ROLES(id, code, name, description, ...)` - `PERMISSIONS(id, code, description, ...)` - `APP_MODULES(code, name, enabled, sort, ...)` - `DATA_SCOPE_MODULES(module_code, created_at)` - `USER_ROLES(user_id, role_id, created_at)` - `ROLE_PERMISSIONS(role_id, permission_id, created_at)` - `ROLE_DATA_SCOPES(role_id, module, scope_type, created_at, updated_at)` - `ROLE_DATA_SCOPE_DEPARTMENTS(role_id, module, department_id, created_at)` - `AUDIT_LOGS(id, occurred_at, actor_user_id, actor_name, actor_email, actor_roles, action, target_type, target_id, success, ...)` - `FACILITY_BUILDINGS(id, name, sort, ...)` - `FACILITY_ROOMS(id, building_id, floor_no, name, capacity, ...)` - `FACILITY_RESERVATIONS(id, room_id, applicant_id, status, start_at, end_at, reviewed_by, reviewed_at, cancelled_by, cancelled_at, ...)` - `FACILITY_RESERVATION_PARTICIPANTS(reservation_id, user_id, is_applicant, ...)` - `FACILITY_BANS(id, user_id, created_by, revoked_by, starts_at, ends_at, revoked_at, ...)` - `MAJORS(id, name, enabled, sort, ...)` - `COURSES(id, major_id, name, enabled, sort, ...)` - `MAJOR_LEADS(major_id, user_id, created_at)` - `COURSE_RESOURCES(id, course_id, major_id, created_by, type, status, submitted_at, reviewed_at, published_at, unpublished_at, sha256, link_url_normalized, download_count, ...)` - `COURSE_RESOURCE_BESTS(resource_id, best_by, created_at)` - `COURSE_RESOURCE_DOWNLOAD_EVENTS(id, resource_id, user_id, occurred_at, ...)` - `COURSE_RESOURCE_SCORE_EVENTS(id, resource_id, major_id, user_id, event_type, delta, created_at)` ### 4.2 候选键、主属性、非主属性与函数依赖 数据库课程答辩中,不能只说“这个表有主键”,还需要说明候选键、主属性、非主属性和关键函数依赖。下表列出本课题中最值得重点解释的几个关系。 | 关系 | 候选键 | 主属性 | 非主属性 | 关键函数依赖 | 范式判断 | |------|--------|--------|----------|--------------|----------| | `ROLES(id, code, name, description, ...)` | `id`、`code` | `id`、`code` | `name`、`description`、时间戳等 | `id -> code, name, description, ...`;`code -> id, name, description, ...` | 达到 BCNF | | `USER_ROLES(user_id, role_id, created_at)` | `(user_id, role_id)` | `user_id`、`role_id` | `created_at` | `(user_id, role_id) -> created_at` | 达到 BCNF | | `ROLE_DATA_SCOPES(role_id, module, scope_type, ...)` | `(role_id, module)` | `role_id`、`module` | `scope_type`、时间戳 | `(role_id, module) -> scope_type, created_at, updated_at` | 达到 3NF,接近 BCNF | | `FACILITY_RESERVATION_PARTICIPANTS(reservation_id, user_id, is_applicant, ...)` | `(reservation_id, user_id)` | `reservation_id`、`user_id` | `is_applicant`、时间戳 | `(reservation_id, user_id) -> is_applicant, created_at` | 达到 3NF,接近 BCNF | | `COURSE_RESOURCES(id, course_id, major_id, created_by, type, status, ...)` | 全局稳定候选键为 `id` | `id` | 其余属性 | `id -> course_id, major_id, created_by, type, status, ...`;同时由业务语义有 `courses.id -> courses.major_id` | 主体结构基本达到 3NF,但 `major_id` 属于受控冗余,需单独解释 | | `COURSE_RESOURCE_SCORE_EVENTS(id, resource_id, major_id, user_id, event_type, delta, ...)` | `id`;另有业务唯一约束 `(user_id, resource_id, event_type)` | `id` | 其余属性 | `id -> resource_id, major_id, user_id, event_type, delta, ...` | 主体结构清晰,但 `major_id`、`user_id` 需解释为受控冗余 | 需要特别说明的几点如下: 1. `course_resources` 中 `(course_id, sha256)` 和 `(course_id, link_url_normalized)` 只是在特定资源类型、未删除记录范围内成立的业务唯一性,不应简单等同为全局候选键。 2. `course_resources.major_id` 不是随意重复保存,而是为了支持按专业过滤、统计和复合外键一致性控制而保留的受控冗余。 3. `course_resource_score_events.major_id` 与 `user_id` 同样属于可解释冗余,它们已经通过复合外键与主体表保持一致,不是失控冗余。 ### 4.3 规范化分析 本课题的逻辑结构设计总体遵循第三范式思想,并在多对多关系拆分后使大量桥接表接近 BCNF。 #### 4.3.1 达到 3NF/BCNF 的典型关系 下列关系模式最容易直接说明达到 3NF,很多甚至可以视为达到 BCNF: - `user_roles` - `role_permissions` - `major_leads` - `role_data_scope_departments` - `facility_reservation_participants` - `department_closure` 这些关系的共同特点是: - 候选键明确 - 非主属性很少 - 非主属性直接依赖整个候选键 - 不存在明显的部分依赖和传递依赖 #### 4.3.2 需要重点解释的必要冗余 本课题中存在三类可以被合理解释的冗余,它们不应被当作粗糙重复设计。 第一类是派生关系表冗余: - `department_closure` 不是普通业务表,而是为层级查询服务的派生关系表。 - 它牺牲了一定存储空间,换取“某部门及全部子部门”查询的高效实现。 第二类是审计快照冗余: - `audit_logs.actor_name` - `audit_logs.actor_email` - `audit_logs.actor_roles` 这些字段用于保留操作发生当时的历史语义,因此属于历史真实性优先的快照冗余,而不是范式错误。 第三类是受控统计与过滤冗余: - `course_resources.major_id` - `course_resource_score_events.major_id` - `course_resource_score_events.user_id` 这些字段之所以保留,是为了支持按专业聚合、按作者统计和约束表达。为了避免它们破坏一致性,数据库中已经通过复合唯一键与复合外键将其锁定。 ### 4.4 完整性约束设计 #### 4.4.1 实体完整性 当前主线模块的核心表均具有明确主键: - 单属性主键: - `profiles.id` - `roles.id` - `app_modules.code` - `facility_reservations.id` - `course_resources.id` - 复合主键: - `department_closure(ancestor_id, descendant_id)` - `user_roles(user_id, role_id)` - `role_data_scopes(role_id, module)` - `facility_reservation_participants(reservation_id, user_id)` - `major_leads(major_id, user_id)` 这些主键都较稳定,不随业务状态变化而频繁修改,符合实体完整性要求。 #### 4.4.2 参照完整性 本课题在核心关系上大量采用显式物理外键: - `profiles.id -> auth.users.id` - `facility_rooms.building_id -> facility_buildings.id` - `facility_reservations.room_id -> facility_rooms.id` - `courses.major_id -> majors.id` - `(course_resources.course_id, course_resources.major_id) -> courses(id, major_id)` - `(course_resource_score_events.resource_id, course_resource_score_events.major_id) -> course_resources(id, major_id)` - `(course_resource_score_events.resource_id, course_resource_score_events.user_id) -> course_resources(id, created_by)` - `(role_data_scope_departments.role_id, role_data_scope_departments.module) -> role_data_scopes(role_id, module)` 这说明课程设计的主线关系并不是靠“逻辑外键”口头维护,而是由数据库直接拒绝非法引用。 #### 4.4.3 用户定义完整性 除主外键外,本课题还将大量业务规则下沉为数据库约束: - `CHECK` - 学号格式 - 时间段 `end_at > start_at` - 资源类型与字段组合合法性 - 资源状态与时间字段一致性 - 预约状态与审核/取消字段一致性 - `UNIQUE` / 部分唯一索引 - 角色编码唯一 - 同楼房同楼层房间名唯一 - 同课程文件哈希去重 - 同课程外链规范化 URL 去重 - 同一用户同时最多一条未撤销封禁 - 触发器 / 约束触发器 - 审计日志 append-only - 部门层级防环 - 预约参与人集合一致性 - 未发布资源不得设为最佳 - 已发布资源下架后自动撤销最佳 - 排斥约束 - 同一房间的 `pending/approved` 预约时间段不可重叠 ## 5. 物理设计与实现 ### 5.1 索引设计依据 本课题的索引设计不是“字段上都建一遍”,而是围绕典型查询与冲突检测场景进行。 | 索引/约束对象 | 服务的典型业务 | 设计依据 | |--------------|----------------|----------| | `department_closure_ancestor_id_idx` | 查询某部门及全部子部门 | 树结构展开是平台权限过滤高频场景 | | `role_data_scopes_module_idx` | 按模块查看角色数据范围 | 管理端常按模块维度配置或排查 | | `audit_logs_occurred_at_idx`、`audit_logs_target_idx` | 审计时间线和按对象追溯 | 审计查询按时间、动作、对象过滤最常见 | | `facility_reservations_room_active_time_idx` | 房间在时间窗口内的占用查询 | 房间可用性判断是预约核心查询 | | `facility_reservations_room_active_time_excl` | 并发预约冲突拦截 | 活跃预约之间不允许时间重叠 | | `course_resources_status_idx`、`course_resources_major_id_idx`、`course_resources_course_id_idx` | 审核列表、课程列表、专业过滤 | 资源管理和展示都依赖这些过滤条件 | | `course_resources_download_count_idx` | 下载榜排序 | 排行榜场景需要面向统计排序 | | `course_resources_course_sha256_active_uq`、`course_resources_course_link_active_uq` | 资源去重 | 文件和外链去重是资源模块核心规则 | ### 5.2 删除策略 不同关系采用不同删除策略,体现业务语义而非统一处理: | 场景 | 删除策略 | 业务依据 | |------|----------|----------| | 依附型桥接表跟随主体删除 | `CASCADE` | 如 `user_roles`、`role_permissions`、`major_leads`,其存在意义依赖父记录 | | 核心主体仍被历史业务引用 | `RESTRICT` | 如房间仍被预约引用时不应被随意删除 | | 审核人、更新人、撤销人等辅助历史引用 | `SET NULL` | 保留主体记录,不让辅助用户删除破坏历史 | | 审计日志中的操作者引用 | 不建物理外键 | 历史真实性优先,保留弱引用和快照 | ### 5.3 触发器与数据库函数 本课题不是“只有表和索引”,而是合理使用了数据库对象: 1. `departments_assert_no_cycle()` - 防止部门树形成环。 2. `facility_validate_reservation_participants()` - 通过延迟约束触发器在事务提交时检查“至少 3 人、恰有一个申请人、申请人与 `applicant_id` 一致”。 3. `course_resource_best_requires_published()` - 防止未发布资源被设置为最佳。 4. `course_resource_drop_best_when_not_published()` - 当资源下架时自动撤销最佳记录。 5. 审计日志阻断更新/删除触发器 - 确保审计表为只追加表。 这些数据库对象使系统可以在数据库层直接保证复杂业务规则,而不是完全依赖应用层。 ### 5.4 安全、权限与审计 平台基础子系统的数据库亮点主要体现在以下三个方面: 1. RBAC 采用标准桥接表结构,角色、权限、用户授权关系清晰。 2. 数据范围并未把 `module` 当自由文本,而是通过 `app_modules` 与 `data_scope_modules` 两级字典进行约束。 3. 审计日志采用 append-only 策略,并使用 `actor_name`、`actor_email`、`actor_roles` 保存快照,从而兼顾历史真实性与可读性。 其中 `audit_logs.actor_user_id` 不建立物理外键,是一个需要在答辩中主动解释的设计取舍:审计记录的核心价值在于保留“当时发生过什么”,如果把它和用户主体做强绑定,用户删除或历史变更可能破坏审计可追溯性。因此本课题采用“弱引用 + 快照字段”的组合方案。 ## 6. 典型业务数据库实现 ### 6.1 平台基础子系统 平台基础模块最能体现数据库建模能力的地方包括: - `profiles` 通过共享主键与身份源解耦。 - `departments + department_closure` 同时支撑层级表达与高效范围查询。 - `role_data_scopes + role_data_scope_departments` 通过复合主键和复合外键表达“角色在模块上的数据范围配置”。 - `app_modules + data_scope_modules` 将“系统模块”与“支持数据权限的模块”拆分建模,使域约束显式化。 - `audit_logs` 采用历史事实表思路设计,而不是普通业务表思路。 ### 6.2 功能房预约子系统 功能房预约模块最值得强调的是“事务 + 数据库硬约束”的协同: 1. 服务层事务保证预约主体与参与人集合原子写入。 2. 数据库 `CHECK` 保证时间区间合法和状态字段组合合法。 3. `EXCLUDE USING gist` 保证同一房间活跃预约时间不重叠。 4. 延迟约束触发器保证参与人集合必须满足业务规则。 因此,本模块已经不再是“服务层自己小心别写错”,而是数据库能够直接拒绝非法预约。 ### 6.3 课程资源分享子系统 课程资源模块的数据库实现亮点主要包括: 1. 主数据、主体数据和事件事实分层明确。 2. 文件资源与外链资源分别通过条件唯一约束实现去重。 3. `course_resources.major_id` 与 `course_resource_score_events.major_id/user_id` 虽有冗余,但都已由复合外键控制一致性。 4. 资源状态机、最佳推荐与首次积分语义都已下沉到数据库对象。 这个模块体现了一个数据库课程设计中很重要的思想:当为了查询效率和业务表达保留冗余时,必须同时给出数据库层的一致性锁定机制。 ## 7. 典型 SQL、事务与并发控制 ### 7.1 典型 SQL #### 7.1.1 查询某部门及其全部子部门 ```sql select d.id, d.name, dc.depth from public.department_closure dc join public.departments d on d.id = dc.descendant_id where dc.ancestor_id = :department_id order by dc.depth asc, d.name asc; ``` 该查询体现了闭包表设计和索引支撑关系。 #### 7.1.2 查询某房间在时间窗口内的占用情况 ```sql select r.id, r.status, r.start_at, r.end_at from public.facility_reservations r where r.room_id = :room_id and r.status in ('pending', 'approved') and r.start_at < :window_end and r.end_at > :window_start order by r.start_at asc, r.id asc; ``` 该查询直接服务于房间可用性判断,并与时间冲突排斥约束共同工作。 #### 7.1.3 课程资源积分榜 ```sql select s.user_id, p.name, sum(s.delta) as score from public.course_resource_score_events s join public.profiles p on p.id = s.user_id where (:major_id is null or s.major_id = :major_id) group by s.user_id, p.name order by sum(s.delta) desc, p.name asc limit 20; ``` 该查询说明排行榜建立在事件事实表之上,而不是由页面临时推导。 ### 7.2 典型事务设计 本课题中至少有以下业务必须放在事务中处理: 1. 角色数据范围整体替换 - 先删旧配置,再写入新配置,并保证父表与子表同步更新。 2. 功能房预约创建/重提 - 预约主体与参与人集合必须原子写入。 3. 课程资源审核通过并写入积分事件 - 资源状态、审核信息和积分记录必须保持一致。 4. 设置最佳推荐并补记额外积分 - 最佳记录和积分事件必须处于同一事务边界内。 ### 7.3 并发控制分析 当前最典型的并发风险是“两个用户几乎同时为同一房间提交重叠预约”。如果仅依赖服务层先查后写,会出现竞态条件。因此本课题采用如下协同方案: 1. 服务层事务先做业务前置校验和友好提示。 2. 数据库用 `facility_reservations_room_active_time_excl` 作为最终裁决。 3. 当并发冲突发生时,数据库直接拒绝其中一笔写入,从而保证结果正确。 这体现出数据库课程设计中非常关键的一点:事务并不能替代约束,约束也不能替代事务,二者应当协同工作。 ## 8. 测试与结果分析 ### 8.1 实库验证方式 2026 年 4 月 21 日至 2026 年 4 月 22 日,本课题在新建 Supabase 空项目中完成了真实数据库验证,执行了以下迁移: - `0001_baseline` - `0002_infra` - `0003_department_parent_fk` - `0004_course_resources` - `0005_course_resources_constraints` - `0006_facility_reservations` - `0012_course_design_constraints` - `0013_course_resource_author_and_best_constraints` - `0014_facility_reservation_status_consistency` - `0015_module_dictionary_and_data_scope_fks` - `0016_audit_actor_snapshot_strategy` 这说明报告中的数据库规则不是停留在纸面推演,而是已经完成一次真实数据库对象落库与正反例测试。 ### 8.2 已验证的关键约束 | 测试点 | 验证结果 | 说明 | |--------|----------|------| | 房间活跃预约时间冲突 | 通过 | `EXCLUDE USING gist` 能直接拒绝冲突写入 | | 预约参与人数下限 | 通过 | 少于 3 人时事务提交失败 | | 申请人与参与人一致性 | 通过 | 数据库能拒绝申请人缺失或不一致的情况 | | 课程资源 `(course_id, major_id)` 一致性 | 通过 | 复合外键已生效 | | 积分事件 `(resource_id, major_id)` 一致性 | 通过 | 复合外键已生效 | | 积分事件 `(resource_id, user_id)` 作者归属一致性 | 通过 | 错绑他人会被数据库拒绝 | | 课程资源状态一致性 | 通过 | `CHECK` 已生效 | | `best_by` 外键 | 通过 | 最佳设置人必须是真实用户 | | 未发布资源不得设为最佳 | 通过 | 触发器会拒绝非法设置 | | 已发布资源下架后自动撤销最佳 | 通过 | 触发器可自动清理最佳记录 | | `role_data_scopes.module` 域约束 | 通过 | 只能引用能力表中登记的模块 | | 审计日志 `actor_roles` JSON 结构检查 | 通过 | 非法结构被数据库拒绝 | | 审计日志弱引用策略 | 通过 | `actor_user_id` 未误加物理外键 | ### 8.3 当前仍需如实说明的边界 虽然主线规则已经大量落库,但报告中仍应如实说明以下设计边界: 1. `audit_logs.actor_user_id` 故意保留弱引用,这是历史真实性优先的设计取舍,而不是遗漏。 2. `facility_bans` 当前以 `revoked_at is null` 判定活动封禁,因此“自然过期但未显式撤销”仍有一个可解释的小边界。 3. `audit_logs.actor_name` 的回填本轮是在空审计表上验证了“安全执行”,若答辩需要展示旧数据回填效果,可单独补造样本复演。 ## 9. 总结与展望 本课题围绕校园生活平台中的平台基础、功能房预约和课程资源分享三个子系统,完成了从需求分析到概念结构、逻辑结构、物理设计、实现与测试验证的一条完整数据库设计主线。 与一般只强调页面和接口的课程项目相比,本课题的主要特点在于: 1. 主线关系模式较完整,能够明确说明主键、候选键、外键、函数依赖和范式判断。 2. 大量关键规则已经下沉到数据库层,包括显式外键、`CHECK`、`UNIQUE`、部分唯一索引、排斥约束、触发器和审计策略。 3. 对必要冗余不是回避,而是用数据库一致性机制解释并锁定。 4. 对事务与并发控制不是停留在概念层,而是给出了真实业务中的约束协同方案和实库验证结果。 后续若继续优化,可优先考虑以下方向: - 在答辩材料中进一步精炼“函数依赖、主属性、非主属性、3NF/BCNF”的口头表达。 - 补充少量高质量截图,用于展示实库测试失败样例和约束名称。 - 在保持当前课程设计主线稳定的前提下,再决定是否把数字图书馆作为同构扩展模块正式纳入正文。 ## 10. 支撑材料对应关系 为避免正式正文过长,本报告对应的支撑材料如下: - 概念结构与 E-R:`docs/report/overview/conceptual-structure-and-er.md` - 关系模式总表:`docs/report/overview/relation-schemas.md` - 数据字典:`docs/report/overview/data-dictionary.md` - 平台基础模块:`docs/report/modules/platform-core.md` - 功能房预约模块:`docs/report/modules/facility-reservation.md` - 课程资源模块:`docs/report/modules/course-resources.md` - 物理设计:`docs/report/database/physical-design.md` - 典型 SQL 与事务:`docs/report/database/typical-sql-and-transactions.md` - 约束验证与测试:`docs/report/database/constraint-validation-and-tests.md`