资讯详情

资讯详情

ASP图书馆管理代码:借还书事务、并发与权限校验实战

简介这份ASP图书馆管理代码面向Web开发初学者、课程设计或小型图书馆信息化场景基于ASP与Access构建一套可运行的图书查询、借阅、归还及后台管理示例帮助读者理解动态网页与关系型数据库的协作方式适合课程设计、毕业设计参考与小型馆藏管理原型搭建。压缩包共53个文件约426KB其中31个ASP文件承载登录、图书维护、借阅还书与管理入口等服务端逻辑4个JS和3个CSS负责前端交互与界面样式另含Access的MDB数据库、DB文件、说明文本及图片等配套资源。已有55人学习。代码覆盖书籍表、读者表与借阅记录表的数据组织并涉及SQL注入防护、MD5处理、登录校验和上传等实践点读者可据此梳理小型管理系统的目录结构、页面分工与数据库读写流程涵盖按书名作者分类检索、借阅状态更新及管理员增删图书等流程也可作为课程设计或二次开发的基础模板。1. 从「能跑就行」到「能验收」ASP 图书馆管理代码真正的难点在哪每年做 ASP 图书馆管理代码的同学十个里有八个卡在同一个地方增删改查一周就写完了答辩前一天却发现借书功能在两个人同时点击时会多扣一本库存。图书馆管理系统看着是课程设计里最没有技术含量的题目实际上它把事务、并发、状态机、权限校验这四件事全塞进了不到十张表的业务里。这套方案面向两类人要交毕业设计或课程设计、需要一个能演示也能讲清结构的学生以及接手一套老旧 ASP 代码、想把它改成可维护版本的开发者。下面从需求、结构、借还书主流程、后台权限一路讲到排查和答辩验收每一段都对应代码里真实存在的一两个文件不是那种只讲概念不看实现的方案书。2. 架构与选型三层结构、六张核心表和三段式目录怎么摆先把方向定死后面写代码才不会反复推翻。图书馆管理系统的功能边界其实很清楚图书编目、读者管理、借还书、查询统计、权限控制五个模块撑死。真正决定这套代码好不好改的是技术路线和表结构而这两样东西一旦中期定了后期再改成本极高。2.1 经典 ASP、Web Forms、MVC 三条路线怎么选拿到手的所谓asp 图书馆管理代码后缀可能是.asp也可能是.aspx这两者完全不是一个东西答辩时说错会非常扣分。经典 ASP 用 VBScript 写在 HTML 里% %中间直接拼 SQL 字符串典型写法是select * from books where id request(id)SQL 注入几乎是标配ASP.NET Web Forms 是.aspx加.aspx.cs的代码后置模型有服务端控件、事件回发和 ViewStateASP.NET MVC 则是路由加控制器加视图分层最干净。三条路线的取舍建议如下路线上手成本答辩讲清难度适合的情况经典 ASP.asp低语法随意低但容易被追问安全只要求跑通的老旧系统维护Web Forms.aspx中控件和事件要理解中讲清页面生命周期即可课设、毕设时间紧任务重MVC / 前后端分离高多一层接口契约高要讲路由、模型绑定、鉴权有一定基础、想拿高分的毕设我一般会推荐 Web Forms 加三层GridView 直接支持分页排序做完就能演示代码后置天然把页面和逻辑分开了部署到 IIS 上是复制粘贴加一个应用程序池的事。如果手上拿到的是.asp的老代码第一件事不是加功能而是把所有字符串拼接的 SQL 换成参数化查询顺手把拼接的请求参数全部走一遍过滤这一改安全分基本就保住了。2.2 三段式目录UI、业务、SQL 各归各的位置分层这件事最怕做成三层文件夹但是逻辑全在页面里。判断标准很简单业务层文件里出现SqlConnection说明分层失败数据层文件里出现超期不能借这种业务规则同样说明分层失败。LibrarySys/ ├── App_Code/ │ ├── Model/ # 实体类一张表对应一个类只放属性 │ │ ├── Book.cs │ │ ├── Reader.cs │ │ └── BorrowRecord.cs │ ├── DAL/ # 只做开连接、拼参数化 SQL、返回实体或 DataTable │ │ ├── BookDal.cs │ │ ├── CopyDal.cs │ │ └── BorrowDal.cs │ └── BLL/ # 只做业务规则判断不出现任何数据库连接对象 │ ├── BorrowService.cs │ └── ReaderService.cs ├── Admin/ # 后台页面图书编目、读者、借还、统计 │ └── BookList.aspx ├── Reader/ # 读者端检索、我的借阅、续借 ├── Sql/ # 建表脚本、初始化数据、索引脚本 └── Web.config # 连接字符串、授权节点这里要提醒一句App_Code是 Web Site 项目模型的产物代码会动态编译如果你的项目是 Web Application 项目对应的做法是拆成LibrarySys.Model、LibrarySys.DAL、LibrarySys.BLL三个类库然后在主项目里引用。答辩时被问到为什么这样分一句话就够借期从 30 天改成 21 天我只改规则表的一行数据不动任何一行页面代码。2.3 六张核心表图书和副本为什么必须拆开这是整套系统里最容易被简化掉、又最容易被答辩追问的设计点。很多同学只建一张Books表加个Stock字段表示还剩几本结果借还书的记录没法对应到具体的某一本实体书上丢了书也查不出是哪一本。正确的做法是把书目和副本拆成两张表Books一个 ISBN 一条描述这本书的公共属性BookCopies每一本实体书一条条码是借还操作的唯一入口。-- 书目表描述“这本书是什么”一个 ISBN 一条记录 CREATE TABLE Books ( BookId INT IDENTITY(1,1) PRIMARY KEY, ISBN VARCHAR(20) NOT NULL, Title NVARCHAR(200) NOT NULL, Author NVARCHAR(100) NULL, Publisher NVARCHAR(100) NULL, CategoryId INT NULL, Price DECIMAL(10,2) NULL, Status TINYINT NOT NULL DEFAULT 1 -- 1 可借阅 0 已下架 ); CREATE UNIQUE INDEX UX_Books_ISBN ON Books(ISBN); -- 副本表描述“馆里有哪几本”条码是借还操作的唯一入口 CREATE TABLE BookCopies ( CopyId INT IDENTITY(1,1) PRIMARY KEY, BookId INT NOT NULL REFERENCES Books(BookId), Barcode VARCHAR(32) NOT NULL, Location NVARCHAR(50) NULL, -- 馆藏位置演示时很好用 State TINYINT NOT NULL DEFAULT 0 -- 0 在架 1 借出 2 破损 3 遗失 ); CREATE UNIQUE INDEX UX_Copies_Barcode ON BookCopies(Barcode); -- 读者表借阅上限按读者类型给默认值不要写死在代码里 CREATE TABLE Readers ( ReaderId INT IDENTITY(1,1) PRIMARY KEY, CardNo VARCHAR(20) NOT NULL UNIQUE, Name NVARCHAR(50) NOT NULL, ReaderType TINYINT NOT NULL DEFAULT 1, -- 1 学生 2 教师 MaxBorrow INT NOT NULL DEFAULT 5, Status TINYINT NOT NULL DEFAULT 1 -- 1 正常 0 挂失 -1 注销 ); -- 借阅记录表DueDate 必须落库不能每次用借书日期现算 CREATE TABLE BorrowRecords ( RecordId BIGINT IDENTITY(1,1) PRIMARY KEY, CopyId INT NOT NULL REFERENCES BookCopies(CopyId), ReaderId INT NOT NULL REFERENCES Readers(ReaderId), BorrowDate DATETIME NOT NULL, DueDate DATETIME NOT NULL, -- 应还日期借出时一次算好写死 ReturnDate DATETIME NULL, RenewCount TINYINT NOT NULL DEFAULT 0, Fine DECIMAL(10,2) NOT NULL DEFAULT 0, Status TINYINT NOT NULL DEFAULT 0 -- 0 在借 1 已还 );几个字段的用意值得在答辩时主动讲。DueDate落库而不是现算是因为借期规则会变三月份改成 21 天不能让一月份借出去的书也按新规则算逾期历史记录必须保持当时的约定。RecordId用BIGINT因为借阅记录是只增不改的流水表几年下来几十万条很正常。Status用TINYINT不用字符串是为了建索引和做统计时不用处理大小写和空格。另外还建议补两张小表FineRules存每日罚款金额和封顶值Reservations存预约排队。这两张表数据量极小但能让逾期罚款和预约到馆这两个功能从写死在代码里变成可配置答辩时是明显的加分项。3. 借还书主流程四道校验、一个事务和逾期罚款的计算口径借还书是整套系统的核心业务也是唯一一个必须用事务收尾的地方。这一块写对了剩下的增删改查都是体力活写错了演示时一次并发点击就能让你当场翻车。3.1 借书前的四道校验一道都不能省借书不是点一下就完事它是一次带状态迁移的操作副本从在架变成借出读者从借了 N 本变成借了 N1 本借阅记录从无到有。这三次变更要么一起成功要么一起失败中间任何一个失败都必须回滚。借出之前必须过四道校验顺序也有讲究从便宜的判断到昂贵的判断依次往下读者证状态正常不是挂失、注销或冻结当前在借数量没有达到该读者的借阅上限该读者没有尚未归还的同一本书按 BookId 判断不是按条码目标副本确实在架状态为 0。第 3 条容易被漏掉但它直接影响演示效果不拦的话同一个人可以把同一本书的三个副本全借走逻辑上说得通业务上很怪导师一般都会问一句这合理吗。// BorrowService.cs —— 借书主流程四道校验与两次写入在同一个事务里 public BorrowResult Borrow(int readerId, string barcode) { using (var conn new SqlConnection(Config.ConnStr)) { conn.Open(); using (var tran conn.BeginTransaction(IsolationLevel.ReadCommitted)) { try { // 第一道读者状态和在借数量 var reader ReaderDal.GetById(conn, tran, readerId); if (reader null || reader.Status ! 1) return BorrowResult.Fail(读者证状态异常无法借阅); int onLoan BorrowDal.CountOnLoan(conn, tran, readerId); if (onLoan reader.MaxBorrow) return BorrowResult.Fail(已达借阅上限 reader.MaxBorrow 本); // 第二道锁定副本行防止两人同时借走同一本 var copy CopyDal.GetForUpdate(conn, tran, barcode); if (copy null || copy.State ! 0) return BorrowResult.Fail(该副本不在架无法借出); // 第三道同一读者不能重复借同一书目 if (BorrowDal.HasActiveByBook(conn, tran, readerId, copy.BookId)) return BorrowResult.Fail(该书尚未归还不能重复借阅); // 第四道写记录 改副本状态两件事必须同生共死 DateTime borrowDate DateTime.Now; int loanDays RuleDal.GetLoanDays(conn, tran, reader.ReaderType); BorrowDal.Insert(conn, tran, copy.CopyId, readerId, borrowDate, borrowDate.AddDays(loanDays)); CopyDal.SetState(conn, tran, copy.CopyId, 1); tran.Commit(); return BorrowResult.Ok(borrowDate.AddDays(loanDays)); } catch (Exception ex) { tran.Rollback(); Log.Error(借书失败 reader readerId barcode barcode, ex); return BorrowResult.Fail(系统繁忙请稍后重试); } } } }IsolationLevel.ReadCommitted是 SQL Server 的默认隔离级别够用且不会带来额外锁开销除非你清楚知道自己在做什么否则不要顺手改成Serializable。真正起并发保护作用的是下面这条 SQL 里的锁提示-- 取副本时加更新锁行锁一直持有到事务结束谁先拿到谁先借 SELECT CopyId, BookId, State FROM BookCopies WITH (UPDLOCK, ROWLOCK) WHERE Barcode Barcode;UPDLOCK是更新锁读的时候就把行占住第二个事务再读同一行会阻塞直到前一个事务提交或回滚ROWLOCK把粒度限制在行上避免锁升级把整张表锁住。两个参数合起来就是同一本书不会被两个人同时借走的底层保证。BorrowResult是一个简单的返回对象包含Success布尔值和Message字符串页面层只负责把它翻译成提示文字不参与判断——这也是分层是否彻底的试金石如果页面里出现if (onLoan 5)说明业务规则又漏回 UI 了。3.2 还书、逾期判定与罚款按自然日算不按 24 小时算还书的逻辑比借书短但坑更深因为逾期天数的计算口径一旦定错后面所有报表、罚款、催还通知全是错的。// 还书先算逾期再更新记录最后把副本放回在架 public ReturnResult Return(string barcode) { using (var conn new SqlConnection(Config.ConnStr)) { conn.Open(); using (var tran conn.BeginTransaction()) { var record BorrowDal.GetActiveByBarcode(conn, tran, barcode); if (record null) return ReturnResult.Fail(该条码没有在借记录); DateTime returnDate DateTime.Now; // 关键两边都取 .Date 再相减按自然日算不按小时算 int overdueDays (returnDate.Date - record.DueDate.Date).Days; decimal fine 0m; if (overdueDays 0) { var rule RuleDal.GetFineRule(conn, tran, record.ReaderType); // 每天罚款 × 天数但不超过规则里的封顶值 fine Math.Min(overdueDays * rule.PerDay, rule.MaxFine); } BorrowDal.Close(conn, tran, record.RecordId, returnDate, fine); CopyDal.SetState(conn, tran, record.CopyId, 0); tran.Commit(); return ReturnResult.Ok(overdueDays, fine); } } }为什么强调DateTime.Today而不是TimeSpan.TotalDays因为借书时间是下午三点应还日期写的是AddDays(30)同样是下午三点如果读者在第 31 天上午十点来还TotalDays算出来是30.79取整得 30判定为没逾期而按自然日算第 31 天就是逾期 1 天。两种口径差一天罚款差一笔读者投诉就来了。用.Date把时间部分抹掉再相减是最不容易出错的写法。封顶值MaxFine的意义在演示时特别直观一本 30 块钱的书逾期半年按每天 0.2 元罚出来是 36 元比书还贵业务上不合理。规则表里配一个 20 元的封顶既有逻辑又能讲清为什么罚款要有上限。罚款的去向建议两处都记BorrowRecords.Fine存这一笔的明细读者表旁边再挂一张缴费或余额记录答辩时能解释明细可追溯余额可结算。3.3 续借与预约容易被砍掉、又容易被追问的两个功能续借的逻辑不复杂但条件要卡死当前记录未归还、未逾期、续借次数没超过上限一般 1 次、并且这本书没有其他读者在排队预约。最后一条最容易被忘忘了就会出现我预约了结果被人续借走了的尴尬。-- 续借一次性判断四个条件返回受影响行数0 表示不满足 UPDATE BorrowRecords SET DueDate DATEADD(DAY, 30, DueDate), RenewCount RenewCount 1 WHERE RecordId RecordId AND ReturnDate IS NULL AND DueDate GETDATE() AND RenewCount 1 AND NOT EXISTS ( SELECT 1 FROM Reservations r JOIN BookCopies c ON c.BookId r.BookId WHERE c.CopyId BorrowRecords.CopyId AND r.Status 0 );把条件全塞进WHERE里比先查再判断再更新安全得多判断和更新是同一条语句中间没有时间窗不会出现判断时还能续写进去时已经被预约的情况。返回值是受影响行数0 就提示不满足续借条件页面不用自己去拼错误原因。预约功能则是在还书那一刻触发的。某人还书之后系统把这本书最早的一条预约置为待到馆状态-- 还书成功后台执行把该书最早的一条预约标记为待到馆 UPDATE Reservations SET Status 1, NotifyTime GETDATE() WHERE ReservationId ( SELECT TOP (1) ReservationId FROM Reservations WHERE BookId BookId AND Status 0 ORDER BY CreateTime ASC );这里不能写成UPDATE TOP (1) ... ORDER BY因为UPDATE TOP不接受ORDER BY写出来会直接语法报错——这属于典型的看别人博客抄一半翻车点。用子查询先取出唯一的主键再更新语义明确也不会有歧义。4. 后台与权限编目入库、读者管理和并发下的库存一致性后台是演示时最容易加分也最容易出事的地方。编目入库考验的是数据一致性读者管理考验的是表单校验权限考验的是你到底有没有真的做过安全三者加起来正好覆盖答辩常问的那几个问题。4.1 图书编目ISBN 查重之后批量生成副本入库流程我一般拆成三步扫 ISBN 查重、填书目信息、按数量生成副本。查重不是为了好看是为了避免同一本书被录入两次导致检索结果里出现重复条目。副本条码建议用规则的字符串而不是直接用自增的CopyId前缀 入库年份 四位流水比如TS2024000137。好处是条码本身能看出入库批次标签打印机打出来也整齐坏处是需要一个流水号生成逻辑但这个逻辑用一个SELECT MAX加事务就能解决。// 批量生成副本数量填 5 就插 5 条任一条失败整批回滚 public int AddCopies(int bookId, int count, string prefix) { using (var conn new SqlConnection(Config.ConnStr)) { conn.Open(); using (var tran conn.BeginTransaction()) { try { // 取当前最大流水号事务内完成避免并发重号 int seq CopyDal.GetMaxSeq(conn, tran, prefix); for (int i 1; i count; i) { string barcode prefix (seq i).ToString(D4); CopyDal.Insert(conn, tran, bookId, barcode, A区-3排); } tran.Commit(); return count; } catch { tran.Rollback(); throw; } } } }参数count建议在页面上限死 1 到 20prefix从分类代码带过来。GetMaxSeq里必须用WITH (UPDLOCK)把当前最大号的行锁住否则两个管理员同时录入同一批书会生成两组一模一样的条码而条码上有唯一索引第二个人的整批插入会直接失败——失败比重复好但体验很差加锁能从源头避免。4.2 权限菜单藏起来不等于接口挡住了后台权限最常见的错误做法是登录后根据角色动态隐藏菜单项。看着挺像回事实际上任何人拿到地址栏直接敲/Admin/ReaderList.aspx照样能进。原因很简单菜单只是 HTML服务端根本没校验。正确做法是在页面基类里做统一拦截所有后台页面继承这个基类// AdminBasePage.cs —— 所有后台页面的父类在 OnInit 里做登录与功能权限校验 public class AdminBasePage : System.Web.UI.Page { protected override void OnInit(EventArgs e) { base.OnInit(e); // 1) 登录态校验未登录直接跳登录页并带上回跳地址 var user Session[AdminUser] as AdminUser; if (user null) { Response.Redirect(~/Login.aspx?returnUrl Server.UrlEncode(Request.RawUrl)); return; } // 2) 功能权限校验用页面类名当功能码去权限表里查 string pageCode GetType().Name.Replace(_aspx, ).ToLower(); if (!PermissionService.Has(user.RoleId, pageCode)) { Response.StatusCode 403; Response.End(); return; } } }两个细节值得讲。第一校验放在OnInit而不是Page_Load因为Page_Load执行时回发事件比如删除按钮的Click已经开始处理了等你判断完权限删除动作可能已经执行OnInit是页面生命周期最早的稳定节点挡得住。第二功能码用页面类名生成新增一个页面只要在权限表里补一行数据就能授权不用改代码——这也是答辩时权限系统可扩展的实证。如果不想写基类Web.config的location节点也能挡一层作为兜底location pathAdmin system.web authorization deny users?/ /authorization /system.web /location但要注意这只是未登录不能进做不了登录了但不是这个角色也不能进所以两层配合着用才完整。4.3 并发下的库存一致性别在书目表上挂可用数量很多现成代码喜欢在Books表加一个AvailableCount字段借出减一、归还加一看起来省掉了一次关联查询。我不建议这么做原因是这个字段和BookCopies.State是同一份事实的两个副本一旦某次事务没写全两边就对不上而且对不上之后没人知道该信哪个。更稳的做法是可用数量永远靠COUNT实时算或者放在缓存里定时刷新绝不在主表里冗余存储。-- 检索时直接算可借数量索引落在 BookCopies(BookId, State) 上速度够用 SELECT b.BookId, b.Title, b.Author, COUNT(c.CopyId) AS TotalCopies, SUM(CASE WHEN c.State 0 THEN 1 ELSE 0 END) AS AvailableCopies FROM Books b LEFT JOIN BookCopies c ON c.BookId b.BookId WHERE b.Status 1 AND b.Title LIKE Keyword GROUP BY b.BookId, b.Title, b.Author;配套的索引必须建CREATE INDEX IX_Copies_BookState ON BookCopies(BookId, State);。图书馆的副本总量撑死几万条这个查询在普通笔记本上都是毫秒级完全没必要为了省这点开销引入一个可能不一致的冗余字段。真要优化那也是上缓存的事不是改表结构的事。最后一道保险是数据库层面的唯一约束防止应用层判断漏掉时写出重复数据-- 同一读者对同一副本在未归还状态下只能有一条记录 CREATE UNIQUE INDEX UX_Borrow_Active ON BorrowRecords(ReaderId, CopyId) WHERE ReturnDate IS NULL;这是 SQL Server 2008 之后支持的筛选索引只对满足条件的行建唯一约束。如果部署环境的数据库版本更低就退化成在业务层加UPDLOCK判断或者用触发器兜底效果差一点但也能用。5. 常见问题排查借还书模块上线后最容易翻车的五个点这一章列的五个问题基本覆盖了图书馆管理系统从本地跑通到真正交上去之间百分之八十的返工。每一条我都按看到什么现象、为什么会这样、怎么改来说照着对一遍能省掉不少返工时间。5.1 借书提示成功库存却没减现象点击借书页面弹出借阅成功但图书列表里的可借数量纹丝不动重新进详情页才正常。原因写借阅记录和改副本状态被拆成了两次独立的数据库操作第一次成功第二次失败或者第二次压根没写。更隐蔽的一种是页面用了UpdatePanel局部刷新把旧数据又渲染回来了看着像没变。解决把这两步放进同一个SqlTransaction任何一步抛异常就整体回滚代码就是 3.1 节里那段。同时确认列表页没有开OutputCache或者缓存时间设得极短否则刷新出来的还是旧数据。5.2 同一个人借出了两条一模一样的记录现象测试时手快连点两下借书按钮或者两个人同时帮同一个读者操作结果借阅记录里出现两条相同的CopyId副本状态还是借出。原因校验和写入之间存在时间窗。两个请求几乎同时进来各自查了一遍是否已借都查到没有然后都执行了插入。解决三层一起上。数据库加 4.3 节里的筛选唯一索引从根上杜绝重复业务层取副本时加UPDLOCK把并发请求串行化前端按钮点击后立刻disabled防止用户误触。前两条是必须的第三条是体验优化。5.3 逾期天数总是差一天现象明明昨天到期今天来还系统显示逾期 0 天罚款 0 元。原因用(DateTime.Now - DueDate).Days计算。DueDate是BorrowDate.AddDays(30)算出来的带着借书那一刻的时分秒如果借书是下午三点第二天上午十点来还时间差是30 天 19 小时.Days取整还是 30判定不逾期。解决两边都取.Date再相减(DateTime.Today - DueDate.Date).Days。改完之后要补一组测试用例到期当天还、到期次日还、跨月还、跨年还四个用例全过才算修好。5.4 部署到 IIS 后中文变问号日期全变成 1900-01-01现象本地 VS 里跑得好好的发布到服务器之后页面上的张三变成??日期显示1900-01-01。原因两个地方没配。一是Web.config里没声明全局编码服务器默认按系统区域解析二是连接字符串里没指定字符集数据库的排序规则和程序端对不上中文写进去就是乱码。日期变成 1900 通常是空值被强转成DateTime的默认值说明查询返回了NULL但代码里没判空。解决Web.config的globalization节点显式声明requestEncoding和responseEncoding为utf-8建库时排序规则选支持中文的Chinese_PRC_CI_AS或更新版本的对应规则读取DateTime字段时统一走reader.IsDBNull(i) ? (DateTime?)null : reader.GetDateTime(i)不要直接强转。5.5 菜单藏起来了接口还能直接调现象用低权限账号登录菜单里看不到读者管理但地址栏输入/Admin/ReaderList.aspx照样打开还能删人。原因权限只做在Menu控件的可见性上服务端没有任何校验。解决所有后台页面继承统一的基类在OnInit里做登录态和功能权限双重判断见 4.2 节必要时再加Web.config的location授权兜底。改完之后一定要用低权限账号手工敲三次地址栏这是验收时最容易被现场抽查的一项。6. 答辩与验收一张演示流程表和八条边界用例怎么用验收这件事最忌讳的是打开系统随口点两下。评委看不到重点你自己也容易漏功能。我习惯提前准备一张演示流程表从上到下走一条完整链路每一步都对应一个能讲的技术点。顺序操作预期结果对应的技术点1管理员登录后台进入首页菜单按角色显示登录态与角色权限2新增一本图书生成 3 个副本列表出现 3 条在架副本事务与批量插入3读者登录检索该书显示可借 3 本关联查询与索引4借出其中一本可借变 2借阅记录新增一条借书四道校验5再借同一本书的另一副本提示不可重复借阅业务规则拦截6后台把还书日期改到过去还书显示逾期天数与罚款自然日计算与封顶7用低权限账号访问后台地址返回 403服务端权限校验8同一账号两个浏览器同时点借书只有一条成功锁与唯一索引表下面是边界用例不一定都演示但自己必须跑一遍答辩被追问时才答得上来借满上限再借一本、借一本已经被借走的副本、到期当天还书、挂失的读者证借书、条码输入不存在的值、续借两次、预约后被人续借、后台并发录入同一批书。再补一个小技巧把数据库脚本和初始化数据单独放在一个Sql目录答辩前十分钟现场重建一次库。这一步看着多余实际上能保证你演示用的数据和代码里的字段完全对得上不会出现我这里明明有数据的尴尬。从那以后我每次交方案之前都会把借书按钮在两个浏览器里同时点一遍再拿一条恰好到期的记录去还一次最后用最低权限账号把后台地址挨个敲一遍。这三件事花不到十分钟但能挡掉绝大多数现场翻车。希望帮到你。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →