
简介这份库存管理系统设计方案面向信息管理、计算机应用等专业的课程设计与毕业设计读者围绕企业入库、出库、库存查询与预警等业务梳理从需求分析到系统实现的完整设计思路。资源包共1个doc文件约604KB以Word文档形式组织便于通读与摘录。文档依次覆盖管理信息系统概述、国内外库存管理软件现状、数据库系统设计与SQL语句、Visual Basic及Access 2000开发工具、功能模块划分、数据表与关系模型设计、程序结构与源代码以及设计总结与改进方向可帮助读者理解MIS类系统的分析路径和VBAccess的实现方法。目前已有127人学习适合需要参考库存管理模块设计、数据库建模和课程设计文档结构的读者使用。1. 需求先立住为什么这套库存管理系统设计方案选 VB 6.0 加 Access 2000仓库管理员抱着一本出入库台账找一个三个月前的经办人翻了二十分钟还没找到——这是很多中小仓库在信息化之前的标准场景。这套库存管理系统设计方案要处理的不是多高深的问题而是把货品、仓库、入库、出库、经办人、供货单位这几类信息从纸面搬进数据库让一次查询从翻账本变成敲几个条件。整套方案用 Visual Basic 6.0 做界面和业务逻辑用 Access 2000 存数据跑在 Windows 单机或小型局域网上。选这套组合不是因为它新而是因为它在一台普通机器上就能把「界面 数据 报表」三件事一次做完编译出来是单个可执行文件答辩现场不用联网、不用配环境。目标读者很明确要交一份完整库存管理系统课程设计或毕业设计的学生以及要给小型仓库搭一套真能用的内部工具的开发者。2. 数据库设计五步法走完核心表定稿数据库是这套方案的地基地基歪了后面 VB 里写再多代码都是在补窟窿。原文把数据库设计拆成需求分析、概念设计、逻辑设计、物理设计、加载测试五步这个顺序不能跳。原因很朴素库存管理系统的字段几乎全部来自业务单证单证上漏一栏表结构就得改表一改窗体、代码、报表全跟着返工。2.1 从单证流到数据流五步法在库存场景下的具体做法第一步需求分析任务是把业务单证流化成数据流。仓库里实际流转的纸面单据通常就三张入库单、出库单、盘点表。入库单上一般有货品编号、货品名称、规格单位、入库数量、入库单价、供货单位、经办人、入库日期、备注出库单上有货品编号、出库数量、领用部门或客户、经办人、出库日期盘点表上有货品编号、账面数量、实盘数量、差异。把这些栏目一条条列出来就是未来的字段清单这一步产出的东西叫数据字典也是后面写论文时最能体现工作量的一节。第二步概念设计从数据流图出发画实体—关系图。库存场景下的实体不多货品、仓库、供货单位、经办人用户、入库单、出库单。关系也清晰一个供货单位可以供多种货品一种货品可以来自多个供货单位这是多对多落到表里要拆成中间表或者干脆在入库单里冗余供货单位编号一张入库单只对应一种货品一对多一个用户可以经办多张入库单和出库单也是一对多。第三步逻辑设计把 E-R 图转成表确定主键和外键。这里最容易犯的错是把所有信息塞进一张大表。假设有人设计出这样的结构入库表入库单号货品编号货品名称货品类别货品单价供货单位名称供货单位电话入库数量经办人姓名入库日期。这张表的问题是它同时描述了「货品」和「入库行为」两个主题。同一个货品入库十次它的名称、类别、单价就要重复存十遍哪天货品单价调整了十条历史记录都得改改漏一条数据就不一致。正确做法是拆货品档案一张表供货单位一张表入库单一张表入库单里只留货品编号和供货单位编号做外键。这个拆分逻辑和教材里图书、借阅者拆两张表是同一回事。第四步物理设计确定字段类型、宽度、取值范围主键定下来字段名英文化。Access 2000 支持中文字段名但字段名一旦带中文VB 里拼 SQL 字符串就容易出转义和编码问题我一般会把所有字段名写成英文中文只出现在窗体标签和报表标题里。第五步加载测试贯穿整个过程录入、修改、查询、报表打印都可以视为对数据库的加载测试边录边查比最后统一测更容易发现问题。2.2 七张核心表字段、主键与类型定稿按上面的拆分思路这套库存管理系统落地下来是七张表。货品表存档案仓库表存地点供货单位表存往来方用户表存经办人和权限入库表、出库表存流水结存表存当前库存是查询和预警的加速器。表名主要字段主键说明t_useruser_id, user_name, user_pwd, user_roleuser_id用户与角色role 区分管理员/仓管员/查询员t_storestore_id, store_name, store_addr, store_keeperstore_id仓库档案t_suppliersup_id, sup_name, sup_linkman, sup_telsup_id供货单位档案t_goodsgoods_id, goods_name, goods_type, goods_unit, goods_price, goods_min, goods_max, store_idgoods_id货品档案min/max 用于短线与超储预警t_inin_id, goods_id, in_qty, in_price, in_date, sup_id, user_idin_id入库流水t_outout_id, goods_id, out_qty, out_date, user_id, out_receiverout_id出库流水t_stockgoods_id, store_id, stock_qty, stock_dategoods_id store_id结存表联合主键下面这段建表语句可以在 Access 的查询设计视图里切到 SQL 视图直接执行也可以照着在表设计器里手工建。字段宽度按仓库实际规模给货品编号给 12 位足够数量用长整型单价和金额用货币型避免浮点误差。-- 货品档案表goods_min / goods_max 是库存预警的下限和上限 CREATE TABLE t_goods ( goods_id TEXT(12) NOT NULL, -- 货品编号业务主键 goods_name TEXT(50) NOT NULL, -- 货品名称 goods_type TEXT(20), -- 类别编码01 表示常备件 goods_unit TEXT(10), -- 计量单位 goods_price CURRENCY, -- 参考单价 goods_min LONG, -- 库存下限低于此值触发短线预警 goods_max LONG, -- 库存上限高于此值触发超储预警 store_id TEXT(8), -- 默认存放仓库 CONSTRAINT pk_goods PRIMARY KEY (goods_id) ); -- 入库流水表只存外键货品名称通过关联查询取不重复存储 CREATE TABLE t_in ( in_id AUTOINCREMENT, -- 入库单号自动编号 goods_id TEXT(12) NOT NULL, -- 外键指向 t_goods in_qty LONG NOT NULL, -- 入库数量必须大于 0 in_price CURRENCY, -- 实际入库单价 in_date DATETIME, -- 入库日期 sup_id TEXT(10), -- 外键指向 t_supplier user_id TEXT(20), -- 经办人指向 t_user CONSTRAINT pk_in PRIMARY KEY (in_id) ); -- 结存表goods_id store_id 联合主键保证同货品同仓库只有一条结存 CREATE TABLE t_stock ( goods_id TEXT(12) NOT NULL, store_id TEXT(8) NOT NULL, stock_qty LONG DEFAULT 0, -- 当前结存数量 stock_date DATETIME, -- 最近变动时间 CONSTRAINT pk_stock PRIMARY KEY (goods_id, store_id) );结存表要不要存在是这套设计里最值得在答辩时讲的一个取舍。纯理论做法是不要库存数量每次都用SELECT SUM(in_qty) - SUM(out_qty)现算数据绝对一致没有冗余。但出入库流水一旦上几千条每次打开货品列表都做一次全表汇总单机 Access 会明显卡顿预警查询更是雪上加霜。工程上的做法是保留结存表做冗余每次入库、出库保存成功时同步更新t_stock同时提供一个「重算结存」的功能用t_in、t_out全量汇总覆盖一遍出问题时可以一键修复。冗余不可怕冗余没有修复手段才可怕。2.3 索引与规范化的取舍该建的地方建不该建的别乱建索引是这套系统里最容易被忽略的性能杠杆。入库流水表t_in和出库流水表t_out会随时间不断增长按货品查流水是最常见的查询所以在goods_id上建索引几乎是必选项货品档案按名称排序打印报表很常见goods_name也值得一个索引。反过来像in_price、in_qty这种数值字段基本只出现在汇总计算里单独建索引没有收益反而拖慢插入。索引的代价必须说清楚每往t_in插一条记录goods_id上的索引条目也要跟着维护索引文件会变大插入会变慢。Access 单机场景下一张流水表建三到四个索引就差不多了再多就是给自己找麻烦。规范化的程度也是同理第三范式是理论目标实际落地时允许有意识地反规范化比如结存表就是典型例子再比如入库表里冗余一个in_price因为货品档案里的goods_price是参考价实际入库价按批次不同不能替代。3. 模块划分与界面结构登录、入库、出库、查询、报表五条主线数据库定稿之后下一步是把功能拆成模块。模块划分的目的不只是让论文目录好看它直接决定 VB 工程里有多少个窗体、每个窗体负责什么、以后加功能往哪里塞。拆得粗一个窗体里塞十几个按钮代码几千行改一处崩三处拆得细窗体满天飞跳转关系乱成一团。3.1 五条业务主线与模块边界按仓库的实际作业顺序这套系统划成五条主线系统管理、档案管理、入库管理、出库管理、统计报表。系统管理管登录和用户档案管理管货品、仓库、供货单位三类基础数据入库和出库是两条流水线统计报表负责查询和打印。库存预警严格说不是一个独立模块它寄生在档案管理里靠货品表上的goods_min和goods_max两个字段实现。查「短线货品」就是查结存小于下限的查「超储货品」就是查结存大于上限的两条 SQL 就能出结果做成主界面上的一个提示面板比单独开一个模块更实用。这里有个边界要划清楚入库和出库虽然流程对称但不能合并成同一个模块用参数区分。原因是两者的业务校验完全不同——入库要校验供货单位和单价出库要校验库存是否足够、要记录领用方字段都不一样硬合并会让代码里到处是If 类型 入 Then这种分支维护成本反而更高。宁可写两个结构相似但独立的窗体也不要为了「少写代码」把两个业务揉在一起。3.2 登录与权限三种角色的操作范围怎么落到代码里需求里明确要求系统有登录功能并且不同用户的操作权限不同。这套设计划三种角色系统管理员能管用户、能看所有数据、能改所有档案仓库管理员能管货品档案、能录入入库出库、不能管用户查询员只能查和打印报表所有编辑按钮置灰。实现思路很简单登录窗体验证t_user里的用户名和密码验证通过后把user_role写进一个全局变量然后打开主窗体。各个业务窗体在Form_Load里根据这个全局变量决定哪些按钮可见、哪些文本框只读。这种「进门一次校验、进来后按角色控界面」的做法比在每个操作里都查一遍权限要轻得多缺点是如果有人绕过界面直接调数据库就没防线了——对单机或小型局域网场景这个安全级别是够的论文里如实写清楚适用范围即可。 登录校验把用户角色写入全局变量供各业务窗体判断 Private Sub cmdLogin_Click() Dim rs As New ADODB.Recordset Dim sql As String 文本型字段必须加单引号否则 Access 报“操作符丢失” sql SELECT user_id, user_role FROM t_user _ WHERE user_name Replace(txtUser.Text, , ) _ AND user_pwd Replace(txtPwd.Text, , ) rs.Open sql, conn, adOpenStatic, adLockReadOnly If rs.EOF Then MsgBox 用户名或密码不正确, vbExclamation, 登录 Else gUserID rs(user_id) 全局变量当前经办人 gRole rs(user_role) 全局变量ADMIN / STOCK / QUERY Unload Me frmMain.Show End If rs.Close End SubReplace(..., , )这一句是关键它把用户输入里的单引号转义成两个单引号防止拼出来的 SQL 被截断。虽然单机系统谈不上注入攻击但货品名称里带个单引号比如某些规格型号就会让程序直接报错这个转义是必须要有的。3.3 窗体清单与命名规范VB 工程里的窗体建议全部用frm前缀加业务名标准模块用mod前缀全局变量集中在一个模块里。这套系统的窗体清单大致如下学生做课设时可以照着这张表建工程一个窗体对应一个.frm文件模块划分一目了然。窗体/模块名类型职责frmLogin窗体登录校验写全局角色frmMainMDI 主窗体菜单入口库存预警面板frmGoods窗体货品档案增删改查frmStore / frmSupplier窗体仓库、供货单位档案维护frmIn窗体入库录入保存时更新结存frmOut窗体出库录入保存前校验库存frmQuery窗体多条件组合查询结果用 DataGrid 展示frmReport窗体出库报表、库存报表、经办人报表打印frmUser窗体用户管理仅管理员可见modDB标准模块连接字符串、打开/关闭连接、通用查询函数modVar标准模块全局变量当前用户、角色、当前仓库命名规范这件事一个人写代码时觉得无所谓等代码写到三千行Form1、Form2、Command3、Command7混在一起找一段逻辑要翻半天。血泪经验是窗体从第一天就按业务命名按钮按动作命名cmdSave、cmdDel、cmdPrint文本框按字段命名txtGoodsID、txtQty后面改需求时才不至于抓瞎。4. 程序结构与数据访问VB 里怎么写 SQL、怎么绑控件界面画好只是壳子真正的活是让窗体和 Access 里的数据对上话。VB 6.0 提供了好几套数据访问控件选错了会在后面某一天突然翻车所以这一章把工程结构、控件选型和 SQL 写法讲透。4.1 工程结构与标准模块这套系统是一个标准 EXE 工程启动对象设成Sub Main而不是某个窗体这样程序启动时可以先做初始化读配置、开数据库连接、检查数据库文件是否存在都检查完了再决定是弹登录框还是直接退出。把数据库连接放在modDB里统一管理好处是所有窗体共用一条连接不用每个窗体自己开自己关也不会出现连接泄漏。 modDB统一管理连接所有窗体通过 conn 访问数据库 Public conn As New ADODB.Connection Public Sub Main() Dim dbPath As String dbPath App.Path \data\stock.mdb 数据库随程序放在 data 子目录 If Dir(dbPath) Then MsgBox 找不到数据库文件 dbPath, vbCritical, 启动失败 End End If Access 2000 用 Jet 4.0 提供程序 conn.ConnectionString ProviderMicrosoft.Jet.OLEDB.4.0;Data Source dbPath conn.Open frmLogin.Show End SubApp.Path取的是可执行文件所在目录这样程序拷到别的机器上也能跑不用改硬编码路径。ProviderMicrosoft.Jet.OLEDB.4.0是 Access 2000 对应的提供程序如果后面换 Access 2003 及以上版本改成Microsoft.ACE.OLEDB.12.0即可这也是选 ADO 而不是 Data 控件的一个好处——换库只改这一行连接串。4.2 Data 控件和 ADO Data 控件怎么选原文列了五种数据库组件Data 控件、ADO Data 控件、DataList/DataCombo 控件、DataGrid 控件、MSChart 控件。实际做库存管理系统真正会用到的就三类数据源控件Data 或 ADO Data、数据感知控件TextBox、DataGrid、图表控件MSChart 做统计图。Data 控件基于 DAO/Jet配置最省事把DatabaseName指到 mdb 文件、RecordSource填表名或 SQL再拿几个 TextBox 绑上DataSource和DataField就能显示数据一行代码不用写。缺点是它把连接和界面绑得太死换数据库基本等于重写。ADO Data 控件基于 ADO连接串自己写RecordSource可以是表名、SQL 语句或者一个 Command 对象。它的灵活性明显更高同一个 DataGrid把 ADO Data 的RecordSource换一条 SQL 就变成另一个查询结果做多条件组合查询特别顺手。我的建议是课程设计统一用 ADO Data 控件加 ADODB.Recordset 的代码方式Data 控件只用来做最简单的演示窗体。原因有两个一是 ADO 的写法更接近现在的工程实践写进论文里不显得陈旧二是遇到多表关联查询、参数化统计这类需求Data 控件很快就顶不住了到时候半路换方案成本更高。 用代码方式打开记录集比拖控件更可控便于动态拼 SQL Public Function OpenRs(ByVal sql As String) As ADODB.Recordset Dim rs As New ADODB.Recordset adOpenStatic adLockReadOnly只读快照适合列表展示和报表 rs.Open sql, conn, adOpenStatic, adLockReadOnly Set OpenRs rs End Function 货品列表按名称模糊查询结果绑定到 DataGrid Private Sub cmdSearch_Click() Dim sql As String sql SELECT g.goods_id, g.goods_name, g.goods_unit, _ Nz(s.stock_qty,0) AS 结存数量 _ FROM t_goods g LEFT JOIN t_stock s ON g.goods_id s.goods_id _ WHERE g.goods_name LIKE % Replace(txtKey.Text, , ) % _ ORDER BY g.goods_id Set grdGoods.DataSource OpenRs(sql) DataGrid 绑定记录集 End SubNz(s.stock_qty,0)是 Access 的实用函数把空值转成 0避免新货品还没建结存记录时列表里出现空行。LEFT JOIN保证就算结存表里没有对应记录货品档案也能列出来这一点比INNER JOIN友好得多——A 同学第一次做的时候用了内连接结果新建的货品死活不出现在列表里查了一个下午才发现是没入库所以没有结存记录。4.3 SQL 在 VB 里的三种写法与使用场景在 VB 里操作 Access常见的写法有三种各有各的适用场景混着用会让代码难维护最好按场景分工。第一种是给数据源控件直接赋 SQL 字符串适合「点一下按钮换一个结果集」的场景比如组合查询。缺点是 SQL 散落在各个事件里改一个字段名要全局搜。第二种是用 QueryDef 建持久化查询把常用的 SQL 存进 mdb 文件里代码只引用查询名。原文提到的CreateQueryDef(monthquery)就是这个思路。它的好处是查询逻辑集中管理报表窗体可以复用同一个查询对象再在其上叠加WHERE条件。缺点是查询定义藏在数据库里换个人接手不看 mdb 就不知道有哪些查询文档必须写清楚。第三种是用 ADODB.Command 执行带参数的 SQL适合插入和更新。参数化的写法不需要手工拼接引号字段里带单引号也不会出错是三种写法里最稳的。入库保存是这套系统里最值得拆开讲的一段代码它要同时做四件事校验数量为正、写入库流水、更新结存、处理失败回滚。用事务包起来任何一步失败都整体撤销避免出现「流水写了但结存没加」的黑匣子状态。 入库保存校验 写流水 更新结存用事务保证一致 Private Sub cmdSave_Click() Dim qty As Long qty Val(txtQty.Text) If qty 0 Then MsgBox 入库数量必须大于 0, vbExclamation, 校验 Exit Sub End If If Trim(txtGoodsID.Text) Then MsgBox 请先选择货品, vbExclamation, 校验 Exit Sub End If On Error GoTo Rollback 任何一步出错都跳到回滚分支 conn.BeginTrans 1. 写入库流水in_price 为本次实际入库价 conn.Execute INSERT INTO t_in (goods_id, in_qty, in_price, in_date, sup_id, user_id) _ VALUES ( txtGoodsID.Text , qty , Val(txtPrice.Text) _ , Now(), cboSup.Text , gUserID ) 2. 结存表有记录则累加没有则插入Access 没有 MERGE只能先查后判 Dim rs As ADODB.Recordset Set rs OpenRs(SELECT stock_qty FROM t_stock WHERE goods_id txtGoodsID.Text ) If rs.EOF Then conn.Execute INSERT INTO t_stock (goods_id, store_id, stock_qty, stock_date) _ VALUES ( txtGoodsID.Text , gStoreID , qty , Now()) Else conn.Execute UPDATE t_stock SET stock_qty stock_qty qty _ , stock_date Now() WHERE goods_id txtGoodsID.Text End If rs.Close conn.CommitTrans MsgBox 入库成功单号已生成, vbInformation, 提示 RefreshGrid 保存后刷新列表否则界面还是旧数据 Exit Sub Rollback: conn.RollbackTrans MsgBox 入库失败 Err.Description, vbCritical, 错误 End SubBeginTrans/CommitTrans/RollbackTrans这三个方法在 ADO 里是对应的缺一不可。很多课设代码只写插入不写事务测试时一切正常一旦遇上磁盘满、文件被占用这类意外流水表多了一条、结存表没加账就永远对不上。出库的保存逻辑结构完全一样只是把数量改成减并且在扣减前要先查结存够不够这一段放到下一章的排查里细说。统计查询用 Access 的聚合函数会比在 VB 里循环快得多。比如查某货品的累计入库量一条SELECT SUM(in_qty) FROM t_in WHERE goods_id...就够要在界面上显示单个统计值时DSum、DCount、DLookup这几个域函数写起来更短DSum([in_qty], t_in, goods_id gid )一行出结果。域函数在数据量大时性能一般报表类需求建议还是走 SQL 聚合加记录集。5. 常见问题排查五个最容易翻车的点代码能跑通不等于系统能用。下面这五个问题几乎每一个做库存管理系统的项目都会撞上至少两个提前知道现象和原因能省掉大量调试时间。5.1 多人同时保存时提示「无法更新当前被锁定」现象两个人同时在一台共享机器上开程序一个人保存入库单成功另一个人点保存时弹出锁定错误或者干脆数据没写进去但界面显示成功。原因Access 是文件型数据库mdb 文件放在共享目录里多个客户端同时打开同一个文件时靠文件锁协调。ADO 默认的记录锁是乐观锁两个连接同时改同一条记录就会冲突更麻烦的是网络共享目录下文件锁本身就不太可靠掉线、休眠都可能导致锁文件残留。解决把数据库文件单独放后端程序放前端每个客户端拷一份前端 exe后端只放一个 mdb把连接改成短连接模式每次操作打开连接、用完立即关闭不要长时间持有对结存表这种高频更新的表更新语句用UPDATE ... SET stock_qty stock_qty n的增量写法而不是先读出来再算好写回去减少并发窗口。5.2 SQL 拼出来语法错误查不到数据也不报错现象查询窗体输入关键字后点查询DataGrid 一片空白或者直接弹「语法错误操作符丢失」。原因文本型字段的值没加单引号日期型字段没用#包起来或者值里本身带单引号把 SQL 截断了。比如WHERE goods_name 螺丝这种写法Access 会认为螺丝是个字段名自然报错。解决所有拼 SQL 的地方统一走一个辅助函数文本字段自动加单引号并转义内部单引号日期字段用#yyyy-mm-dd#格式包起来数字字段先Val()再拼。调试时用Debug.Print sql把拼好的语句打到立即窗口复制到 Access 查询视图里跑一遍能定位九成的语法问题。5.3 出库之后库存变成负数现象出库单保存成功查结存发现数量是负的或者明明仓库里没货了还能一直出。原因出库保存时只写了t_out流水、直接做了stock_qty - n没有出库前的库存校验或者校验了但校验和扣减之间没加锁两个人同时出同一批货各自校验时都够扣完就负了。解决保存前先查结存If 结存 出库数量 Then MsgBox 库存不足 : Exit Sub校验和扣减放在同一个事务里扣减语句加条件UPDATE t_stock SET stock_qty stock_qty - n WHERE goods_id... AND stock_qty n然后判断受影响行数为 0 说明库存不足回滚。这一招叫「条件更新」比先查后改更抗并发。5.4 保存成功了列表还是旧数据现象入库保存后弹出「入库成功」但 DataGrid 里的结存数量没变要关掉窗体重新打开才刷新。原因DataGrid 绑定的 Recordset 是打开时的快照后台数据变了快照不会自动更新。Data 控件和 ADO Data 控件都有这个问题尤其是把 SQL 直接赋给RecordSource之后又手工改过数据库的情况。解决每次保存成功后显式刷新用rs.Requery重新执行原 SQL或者重新Set grd.DataSource OpenRs(sql)。如果用的是 Data 控件调Data1.Refresh。刷新这件事一定要写在保存成功的分支里而不是放在窗体激活事件里否则用户切来切去会触发大量无谓查询。5.5 删掉货品档案后历史出入库记录成了孤儿现象删除一个不再使用的货品结果出库报表里出现一堆货品名称为空的记录或者结存表里留着永远查不到主人的垃圾数据。原因删除前没有检查引用关系。t_in、t_out、t_stock里都存着goods_id主表记录删了从表里的外键就悬空了。Access 里如果没建关系并勾选「实施参照完整性」数据库层面也不会拦。解决删除前先查三张从表有没有引用有引用就禁止删除并提示「该货品已有出入库记录不能删除」业务上更合理的做法是软删除在t_goods加一个is_active字段停用的货品不出现在下拉框里但历史记录照常能关联出名称。硬删除只留给确实录错的、从未产生过流水的记录。6. 验收与答辩把交付物讲成一套可核对的清单系统做完最后一关是让别人相信它真的能用。验收和答辩的差别在于验收看的是功能是否跑通答辩看的是你能不能把设计决策讲清楚。两件事可以合并成一次准备——先按功能清单自己跑一遍再挑其中最能体现设计功力的三条讲。功能验收建议按「一条主流程 五个边界」组织。主流程是新建货品、入库、出库、查结存、打报表这条线跑通说明系统基本可用五个边界分别是入库数量填 0 或负数、出库数量超过库存、删除有流水的货品、用错误密码登录、按不存在的货品名称查询。这五个边界都能给出正确提示而不是崩溃或脏数据才算合格。验收项操作步骤通过标准登录与权限分别用管理员、仓管员、查询员登录查询员看不到编辑按钮管理员能进用户管理档案维护新增、修改、删除一个货品修改后列表即时刷新删除有流水的货品被拦截入库录入 100 件保存流水表新增一条结存表增加 100出库出 30 件再出 80 件第一次成功结存 70第二次提示库存不足预警把下限设成 100结存 70主界面短线货品列表出现该货品报表打印当月出库报表数量、金额与流水表汇总一致分页正常答辩现场有个小技巧把演示顺序倒过来准备。多数人是先讲需求再演示功能讲完十分钟观众已经走神了。我一般开场直接跑主流程三十秒内让评委看到「录一条入库、结存数字跳了一下、报表上多了一行」然后再回头讲为什么这么拆表、为什么结存要冗余、为什么用 ADO 不用 Data 控件。有了那个跳动的数字做锚点后面的设计说明就有人听了。被问到「数据量大了怎么办」是几乎必考题准备三层回答索引层面说清楚在goods_id、goods_name上建了索引流水表查询走索引不走全表结构层面说结存表就是为大数据量准备的冗余避免每次汇总迁移层面说连接串换一行就能从 Access 换到 SQL Server窗体和业务代码基本不动。这三层答完基本不会追问。从那以后我每次带人做这类系统都会强制在答辩前一天走一遍「删库重来」把数据库清空从零建货品、入库、出库、打报表全程不看代码。能一遍走通说明这套东西真能交付走不通的地方就是第二天会被问倒的地方。希望帮到你。本文还有配套的精品资源点击获取