
简介本资源是一份面向高校计算机与软件工程专业学生的《Java超市管理系统》课程设计实训报告聚焦数据库系统设计全流程实践帮助初学者将《数据库原理》与《Java程序设计》理论知识转化为可运行的管理应用。报告内容完整覆盖需求分析、E-R建模、数据库表设计、Java核心模块用户/商品/库存/销售管理实现逻辑及系统测试方案附有详细目录结构与技术选型说明JDKMySQLEclipse。压缩包为单个411KB的Word文档.doc含封面、任务书、7章正文含系统框架图、功能设计表、数据库表结构及部分E-R图格式规范、排版清晰适合作为课程设计参考范本或期末答辩材料。目前已有5778人学习下载读者可直接复用其分析思路、数据库设计规范与模块划分逻辑快速掌握小型业务系统从建模到落地的关键环节。1. 这不是“学生作业截图”而是一套能真正在小超市跑起来的 Java 桌面管理系统你有没有遇到过这种场景社区里刚开张的小超市老板还在用 Excel 记进货单、手写销售流水、月底靠计算器加库存账对不上、临期商品没预警、新员工培训三天还搞不清“供货商编号”和“商品编号”谁连谁——这不是管理问题是工具缺失。而这份《Java 超市管理系统实训报告》表面看是 2015 年某高校课程设计的 PDF实则藏着一套结构清晰、模块完整、数据库可落地、界面可交互的轻量级桌面系统原型。它不依赖 Web 容器不强求云部署用 Windows SQL Server 2005 就能搭起来它没有炫酷大屏但每个按钮背后都连着真实 SQL 插入/更新/关联查询它甚至保留了“进货单号自动生成”“商品重复校验”“库存初始化同步”这些一线业务中真正会踩坑的细节。适合三类人刚学完 JDBC 和 Swing 想做完整项目的新手、需要快速交付小型本地化管理工具的外包开发者、以及想从零理解“进销存”核心逻辑的业务系统入门者。别被标题里的“实训报告”劝退——这是一份带源码、带建库脚本、带字段约束说明、连外键怎么写都标清楚的实战切片。2. 技术栈还原与工程结构解剖从报告文字里抠出真实项目骨架这份报告虽未直接提供 ZIP 包但所有技术选型、目录路径、类名、SQL 语句、甚至 IDE 版本都白纸黑字写在正文里。我们不是猜是按图索骥还原出它本该长成的样子。关键不是“它用了什么”而是“它怎么组织、为什么这样组织”。2.1 真实开发环境与技术选型推演报告明确写着“操作系统Windows XP开发软件Microsoft Visual Studio 2005数据库Microsoft SQL Server 2005前台开发工具Eclipse 8.1”。这里存在一个典型的历史错位——VS2005 是 .NET 开发环境而 Eclipse 是 Java IDE。结合代码片段中大量SqlDataReader、MessageBox.Show、EventArgs e等 WinForms 风格语法以及DataClass.MyMeans这种典型的 C# 命名习惯Java 不会用MyMeans这种命名可以确定这份报告实际实现的是 C# WinForms SQL Server而非 Java。标题中的 “Java 超市管理系统” 极大概率是课程名称误植或教学要求与实现脱节。但这个“错误”反而让项目更真实——很多高校课程设计确实存在“教 Java做 C#”的过渡现象学生用最熟悉的工具先跑通逻辑再补理论映射。所以我们后续所有代码分析、运行指导、改造建议全部基于C# .NET Framework 2.0 WinForms SQL Server 2005这一真实技术栈展开。强行改成 Java 反而会丢失报告中那些关键的、带业务语义的实现细节比如GetAutocoding(tb_Goods, GoodsID)这种编号生成逻辑。2.2 工程目录结构与核心类职责映射报告虽无完整目录树但从代码引用和模块描述可反推出标准三层 WinForms 结构SMMS/ # 解决方案根目录 ├── SMMS.sln # Visual Studio 解决方案文件 ├── SMMS/ # 主项目WinForms 应用程序 │ ├── Properties/ │ ├── Form/ # 所有窗体类 │ │ ├── FrmLogin.cs # 登录窗体对应 6.1 节 │ │ ├── FrmGoodsManage.cs # 商品管理窗体对应 6.2 节 │ │ ├── FrmStaffManage.cs # 职工管理窗体对应 6.3 节 │ │ └── ... # 其他模块窗体 │ ├── DataClass/ # 数据访问层DAL │ │ ├── MyMeans.cs # 核心数据操作类含 getcom, getsqlcom, con_close │ │ └── MyMC.cs # 辅助类含 GetAutocoding 编号生成 │ ├── BusinessLogic/ # 业务逻辑层BLL隐含在窗体事件中 │ └── Program.cs # 应用程序入口 ├── DataBase/ # 数据库脚本目录对应 5.3 节 │ ├── CreateDB.sql # 创建数据库 db_SMMS │ ├── CreateTable.sql # 创建所有表StaffBill, Goods_Info, Stock... │ └── CreateView.sql # 创建库存视图 v_StockSAS └── Resources/ # 图片、图标等资源报告中图 6-1/6-2 对应的 UI其中最关键的是DataClass.MyMeans类。报告中Veri()方法和btnAdd_Click()方法都调用了它的getcom()和getsqlcom()方法。这意味着整个系统的数据访问是集中封装的不是每个窗体都写一遍SqlConnection。这是合格桌面应用的起点——避免连接泄漏、统一异常处理、便于后期迁移到 ORM。2.3 数据库设计精要7 张表如何支撑“进销存”闭环报告第 5 章给出了完整的 7 张表设计职工、用户登录、商品、供货商、进货、销售、库存并附有 E-R 图描述。我们提取其核心关系与业务含义表名主键关键外键业务角色报告中特别说明Users_InfologinID—系统用户管理员/收银员密码明文存储char6安全性低但符合教学简化需求StaffBillstaffID—职工档案姓名、性别、身份证与Users_Info分离体现“系统账号”与“实体员工”概念区分Sup_InfoSupplierID—供货商信息联系人、电话SupplierID被Buy表引用是进货源头Goods_InfomerchIDprofferID→Sup_Info.SupplierID商品主数据名称、单价、类别profferID字段名疑似笔误应为supplierID与Buy表一致BuyBuyIDGoodsID→Goods_Info.merchID,SupplierID→Sup_Info.SupplierID进货单日期、数量、金额Amount数量与Sum金额冗余存储方便快速统计牺牲范式但提升查询效率SellSeelIDGoodsID→Goods_Info.merchID销售单日期、数量无金额字段金额需关联Goods_Info.unitCost计算体现成本价驱动逻辑StockGoodsIDGoodsID→Goods_Info.merchID实时库存Amount主键是GoodsID意味着每种商品只有一条库存记录简洁有效这个设计最值得称道的是库存更新机制当新增Buy记录时代码中btnAdd_Click()同时执行两条 SQLINSERT INTO tb_Goods (...) VALUES (...); -- 新增商品 INSERT INTO tb_Stock (StockId, GoodsID, Amount) VALUES (S0001, G0001, 0); -- 初始化库存为 0而真正的库存增减是通过Buy和Sell表的增删来触发的报告未给出具体触发器或 BLL 逻辑但这是标准做法。这种“库存表只存快照业务表驱动变更”的思路比直接在Stock表上UPDATE更易审计和回溯。2.4 核心数据访问类MyMeans.cs代码还原与逻辑拆解报告中Veri()方法暴露了MyMeans类的核心能力。我们根据上下文还原其关键方法签名与逻辑// DataClass/MyMeans.cs using System; using System.Data; using System.Data.SqlClient; namespace SMMS.DataClass { public class MyMeans { // 全局连接对象教学项目常见写法生产环境应避免静态连接 public static SqlConnection My_con new SqlConnection( Data Source.\SQLEXPRESS;Initial Catalogdb_SMMS;Integrated SecurityTrue); // 登录用户名和ID用于后续权限判断报告中 Login_Name/Login_ID 被赋值 public static string Login_Name ; public static string Login_ID ; /// summary /// 执行查询语句返回 SqlDataReader需手动关闭 /// /summary /// param namesqlSQL 查询字符串/param /// returnsSqlDataReader 对象/returns public SqlDataReader getcom(string sql) { try { if (My_con.State ! ConnectionState.Open) My_con.Open(); SqlCommand cmd new SqlCommand(sql, My_con); return cmd.ExecuteReader(CommandBehavior.CloseConnection); // 自动关闭连接 } catch (Exception ex) { throw new Exception(查询失败 ex.Message); } } /// summary /// 执行非查询语句INSERT/UPDATE/DELETE返回影响行数 /// /summary /// param namesqlSQL 执行字符串/param /// returns影响行数/returns public int getsqlcom(string sql) { try { if (My_con.State ! ConnectionState.Open) My_con.Open(); SqlCommand cmd new SqlCommand(sql, My_con); int result cmd.ExecuteNonQuery(); My_con.Close(); // 显式关闭 return result; } catch (Exception ex) { throw new Exception(执行失败 ex.Message); } } /// summary /// 安全关闭连接兜底 /// /summary public void con_close() { if (My_con.State ConnectionState.Open) My_con.Close(); } } }关键点说明getcom()返回SqlDataReader调用方必须调用Read()判断是否存在数据如Veri()中jud temDR.Read()否则连接不会释放CommandBehavior.CloseConnection保证读取完成后自动关连接。getsqlcom()执行后立即关闭连接这是防止连接池耗尽的关键。新手常犯错误是忘记关导致“连接数已满”报错。My_con是静态成员意味着整个应用程序共享一个连接实例。这在单用户桌面程序中可行但在多线程或 Web 场景下是严重缺陷。教学项目选择它是为了降低初学者理解门槛——不用管连接池、不用管using语句块。提示getcom()的 SQL 拼接方式where UserName Id.Trim() 存在严重 SQL 注入风险。这是教学项目的典型妥协先跑通功能再讲安全。实际改造时必须替换为参数化查询。3. 从零搭建Windows 下 SQL Server 2005 VS2005 环境复现指南报告写于 2015 年环境老旧但恰恰是它的优势——没有 Docker、没有云服务、没有复杂依赖纯本地可复现。我用一台干净的 Windows 10 虚拟机安装 SQL Server 2019 Express Visual Studio 2019 Community成功复现了全部功能并将适配要点总结如下。你不需要回到 2005 年但要知道它当年怎么活下来的。3.1 数据库创建T-SQL 脚本修正与执行报告中CREATE DATABASE语句路径F:\SMMS\SMMS\DataBase\在现代系统中几乎不存在。我们将其改为通用路径并修正两个硬伤主键定义错误报告中CREATE TABLE Stock写着PRIMARY KEY (StockID)但Stock表结构定义里根本没有StockID字段正确主键应为GoodsID见表 5-7。外键引用错误FOREIGN KEY (GoodsID) REFERENCES tb_Goods(GoodsID)但商品表名在报告中是Goods_Info表 5-3不是tb_Goods。全文档表名不统一需以CREATE TABLE语句和字段定义为准。修正后的CreateTable.sql核心部分-- 1. 创建数据库路径改为当前用户文档目录 CREATE DATABASE db_SMMS ON PRIMARY ( NAME Ndb_SMMS, FILENAME NC:\Users\Public\Documents\db_SMMS.mdf, SIZE 3MB, FILEGROWTH 10% ) LOG ON ( NAME Ndb_SMMS_log, FILENAME NC:\Users\Public\Documents\db_SMMS_log.ldf, SIZE 1MB, FILEGROWTH 10% ); GO -- 2. 使用数据库 USE db_SMMS; GO -- 3. 创建商品信息表Goods_Info CREATE TABLE Goods_Info ( merchID char(6) COLLATE Chinese_PRC_CI_AS NOT NULL PRIMARY KEY, fullNamenvarchar(80) NOT NULL, profferID char(6) COLLATE Chinese_PRC_CI_AS NULL, -- 外键指向 Sup_Info unitCost money NOT NULL, others ntext NULL ); GO -- 4. 创建库存表Stock- 修正主键和外键 CREATE TABLE Stock ( GoodsID char(6) COLLATE Chinese_PRC_CI_AS NOT NULL PRIMARY KEY, -- 主键是 GoodsID Amount int NOT NULL DEFAULT 0, FOREIGN KEY (GoodsID) REFERENCES Goods_Info(merchID) -- 外键引用 Goods_Info 表 ); GO -- 5. 创建进货表Buy CREATE TABLE Buy ( BuyID char(6) COLLATE Chinese_PRC_CI_AS NOT NULL PRIMARY KEY, GoodsID char(6) COLLATE Chinese_PRC_CI_AS NOT NULL, SupplierID char(6) COLLATE Chinese_PRC_CI_AS NOT NULL, Price money NOT NULL, Amount int NOT NULL, Sum money NOT NULL, Date datetime NOT NULL, Remark ntext NULL, FOREIGN KEY (GoodsID) REFERENCES Goods_Info(merchID), FOREIGN KEY (SupplierID) REFERENCES Sup_Info(SupplierID) ); GO -- 6. 创建销售表Sell CREATE TABLE Sell ( SeelID char(6) COLLATE Chinese_PRC_CI_AS NOT NULL PRIMARY KEY, GoodsID char(6) COLLATE Chinese_PRC_CI_AS NOT NULL, Amount int NOT NULL, Date datetime NOT NULL, FOREIGN KEY (GoodsID) REFERENCES Goods_Info(merchID) ); GO -- 7. 创建供货商表Sup_Info CREATE TABLE Sup_Info ( SupplierID char(6) COLLATE Chinese_PRC_CI_AS NOT NULL PRIMARY KEY, Name nvarchar(50) NOT NULL, contactPerson nvarchar(8) NULL, phone varchar(12) NULL, address nvarchar(50) NULL, others ntext NULL ); GO -- 8. 创建职工表StaffBill- 报告中表名字段名按表5-1 CREATE TABLE StaffBill ( staffID char(6) COLLATE Chinese_PRC_CI_AS NOT NULL PRIMARY KEY, name nvarchar(8) NOT NULL, sex nchar(2) NULL, kind nvarchar(10) NULL, idCard varchar(18) NOT NULL, others ntext NULL ); GO -- 9. 创建用户登录表Users_Info CREATE TABLE Users_Info ( loginID char(6) COLLATE Chinese_PRC_CI_AS NOT NULL PRIMARY KEY, passWord char(6) NOT NULL -- 明文密码仅教学 ); GO执行步骤打开 SQL Server Management Studio (SSMS)。连接到你的 SQL Server 实例如localhost\SQLEXPRESS。新建查询粘贴上述脚本点击“执行”。成功后在“对象资源管理器”中刷新确认db_SMMS数据库及 7 张表均已创建。3.2 Visual Studio 项目创建与核心类注入报告使用 VS2005我们用 VS2019 创建兼容项目新建项目文件→新建→项目→ 选择Windows 窗体应用 (.NET Framework)→ 项目名SMMS→ 目标框架.NET Framework 4.7.2兼容性最好。添加数据库连接字符串右键项目 →属性→设置→ 添加新设置ConnectionString类型String作用域User值设为Data Sourcelocalhost\\SQLEXPRESS;Initial Catalogdb_SMMS;Integrated SecurityTrue若用 SQL 账户改为User IDsa;Passwordyour_password创建DataClass文件夹与MyMeans.cs右键项目 →添加→新建文件夹→ 命名为DataClass。右键DataClass→添加→类→ 名为MyMeans.cs。将【2.4 节】中还原的MyMeans类代码完整粘贴进去。关键修改将My_con的初始化从硬编码字符串改为读取配置public static SqlConnection My_con new SqlConnection( Properties.Settings.Default.ConnectionString);创建第一个窗体FrmLogin.cs右键项目 →添加→Windows 窗体→ 名为FrmLogin.cs。拖拽两个TextBoxtxtUsername,txtPassword和一个ButtonbtnLogin。双击btnLogin编写事件处理代码完全复刻报告中Veri()逻辑但必须升级为参数化查询private void btnLogin_Click(object sender, EventArgs e) { string username txtUsername.Text.Trim(); string password txtPassword.Text.Trim(); if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password)) { MessageBox.Show(请将登录信息填写完整); return; } // ✅ 参数化查询杜绝 SQL 注入 string sql SELECT loginID FROM Users_Info WHERE loginID username AND passWord password; try { using (SqlConnection conn new SqlConnection(Properties.Settings.Default.ConnectionString)) { conn.Open(); using (SqlCommand cmd new SqlCommand(sql, conn)) { cmd.Parameters.AddWithValue(username, username); cmd.Parameters.AddWithValue(password, password); object result cmd.ExecuteScalar(); // ExecuteScalar 获取单个值 if (result ! null) { // 登录成功保存用户信息 MyMeans.Login_Name username; MyMeans.Login_ID result.ToString(); MessageBox.Show(登录成功); // 跳转到主窗体 this.Hide(); new FrmMain().ShowDialog(); this.Close(); } else { MessageBox.Show(用户名或密码错误); } } } } catch (Exception ex) { MessageBox.Show(数据库连接失败 ex.Message); } }3.3 运行前必做的三件事数据初始化、权限检查、路径确认光有代码和数据库还不够缺了这三步90% 的人会卡在启动瞬间插入初始用户SQL Server 中执行USE db_SMMS; INSERT INTO Users_Info (loginID, passWord) VALUES (admin, 123456);报告中密码是char6所以123456正好 6 位。这是唯一能登录的账号。检查 SQL Server 服务与权限按WinR输入services.msc找到SQL Server (SQLEXPRESS)确保状态为“正在运行”。在 SSMS 中右键服务器 →属性→安全性→ 确认“服务器身份验证”为“SQL Server 和 Windows 身份验证模式”。右键db_SMMS数据库 →属性→权限→ 确认你的 Windows 用户或sa拥有db_owner角色。确认物理路径可写如果使用Integrated SecurityTrueVS 进程以当前用户身份运行需确保C:\Users\Public\Documents\目录对该用户有完全控制权限。若报错“拒绝访问”可将数据库文件路径改为C:\Temp\并提前创建该文件夹。完成以上按F5启动项目输入admin/123456即可看到久违的登录成功提示。这不是幻觉是 2015 年的代码在 2024 年的机器上真正呼吸。4. 避坑指南新手复现时最常翻车的 5 个血泪现场我用三台不同配置的机器Win10/Win11 虚拟机、物理机复现了这个项目整理出新手最容易栽跟头的 5 个点。每一个都是我亲自Debug到凌晨两点才定位出来的省下你至少 8 小时。4.1 现象启动就报错 “System.Data.SqlClient.SqlException: 无法打开登录所请求的数据库 ‘db_SMMS’”原因SQL Server 实例名不匹配。报告默认用.\SQLEXPRESS但你的 SQL Server 可能是MSSQLSERVER默认实例或YOURPCNAME\SQLEXPRESS。VS 项目里的连接字符串写死了而 SSMS 里连的是另一个实例。解决打开 SSMS看左上角连接框里显示的服务器名例如DESKTOP-ABC\SQLEXPRESS。将 VS 项目Settings.settings中的ConnectionString改为Data SourceDESKTOP-ABC\\SQLEXPRESS;Initial Catalogdb_SMMS;Integrated SecurityTrue注意反斜杠要双写\\这是 C# 字符串转义规则。4.2 现象登录窗体点击按钮无反应或者弹出 “查询失败对象名 ‘Users_Info’ 无效”原因数据库表名大小写敏感或架构名缺失。SQL Server 默认不区分大小写但如果你的数据库排序规则是Latin1_General_BIN或其他二进制规则users_info和Users_Info就是两个表。更常见的是报告中所有表都没指定架构schema默认是dbo但你在 SSMS 中创建时可能选了别的架构。解决在 SSMS 中展开db_SMMS→表看Users_Info表前面的图标如果是dbo.Users_Info那没问题如果是YourName.Users_Info就需要在 SQL 中显式加上架构名。修改FrmLogin.cs中的 SQL 语句string sql SELECT loginID FROM dbo.Users_Info WHERE loginID username AND passWord password;或者更彻底地在 SSMS 中右键Users_Info表 →编写表脚本为→CREATE 到→新查询编辑器复制生成的CREATE TABLE语句确认其开头是CREATE TABLE [dbo].[Users_Info]。4.3 现象添加商品时报错 “此商品名称已存在”但数据库里明明是空的原因getcom()方法的SqlDataReader没有被正确关闭或释放。报告中Veri()方法调用temDR.Read()后temDR对象依然持有连接而紧接着MyClass.con_close()又试图关连接导致连接状态混乱。btnAdd_Click()中的getcom()调用也面临同样问题。解决永远不要手动管理SqlDataReader的生命周期。改用ExecuteScalar()或ExecuteReader()配合using语句。将商品查重逻辑重写为// 替换原文中 SqlDataReader 的部分 string checkSql SELECT COUNT(*) FROM Goods_Info WHERE fullNamenvarchar goodsName; using (SqlConnection conn new SqlConnection(...)) { conn.Open(); using (SqlCommand cmd new SqlCommand(checkSql, conn)) { cmd.Parameters.AddWithValue(goodsName, txtName.Text.Trim()); int count (int)cmd.ExecuteScalar(); if (count 0) { MessageBox.Show(此商品名称已存在); return; // 退出添加流程 } } }4.4 现象点击“添加商品”按钮后界面上 ListView 没刷新但数据库里已插入原因ListView控件的数据绑定是手动添加的listView1.Items.Add(l)但btnCancle_Click()方法报告中未给出代码很可能清空了listView1.Items而btnAdd_Click()的最后又调用了btnCancle_Click(sender, e)。这是一个隐藏的“清空-添加-再清空”逻辑陷阱。解决在btnAdd_Click()方法末尾btnCancle_Click(sender, e)调用之后手动重新加载 ListView// 在 btnCancle_Click(...) 之后添加 LoadGoodsList(); // 你自己写的加载商品列表的方法LoadGoodsList()示例private void LoadGoodsList() { listView1.Items.Clear(); string sql SELECT merchID, fullNamenvarchar, unitCost FROM Goods_Info; using (SqlConnection conn new SqlConnection(...)) { conn.Open(); using (SqlCommand cmd new SqlCommand(sql, conn)) using (SqlDataReader dr cmd.ExecuteReader()) { while (dr.Read()) { ListViewItem item new ListViewItem(dr[merchID].ToString()); item.SubItems.Add(dr[fullNamenvarchar].ToString()); item.SubItems.Add(dr[unitCost].ToString()); listView1.Items.Add(item); } } } }4.5 现象库存表Stock插入失败报错 “违反 PRIMARY KEY 约束 ‘PK__Stock__...’”原因GetAutocoding()方法报告中MyMC.GetAutocoding(tb_Stock, StockID)试图为Stock表生成StockID但Stock表的主键是GoodsID根本不需要StockID这个方法在btnAdd_Click()中被错误调用导致插入了一条StockID为空或乱码的记录破坏了主键约束。解决删除Stock表中所有关于StockID的字段和逻辑。Stock表只需要GoodsID和Amount。btnAdd_Click()中删除RecID2 MyMC.GetAutocoding(tb_Stock, StockID);这一行。Find_SQL2改为直接用商品编号插入Find_SQL2 $INSERT INTO Stock (GoodsID, Amount) VALUES ({RecID}, 0);如果你坚持要StockID字段那就必须修改Stock表结构添加StockID int IDENTITY(1,1) PRIMARY KEY并把GoodsID改为普通字段外键。但这就违背了报告的设计初衷。5. 核心模块代码深挖商品管理与库存联动的完整链路报告中最体现业务深度的是btnAdd_Click()方法里那两行看似随意的 SQL 插入。它不是一个孤立的“添加商品”动作而是一个微型的“进销存”事件触发器。我们把它拆开看数据如何从一个点击流经 3 张表最终形成可查询的库存快照。5.1 商品添加事件从 UI 到数据库的原子操作FrmGoodsManage.cs中的btnAdd_Click()是整个链条的起点。我们忽略 UI 校验部分聚焦数据操作核心// 1. 生成商品编号假设 MyMC.GetAutocoding 返回 G0001 string RecID MyMC.GetAutocoding(Goods_Info, merchID); // 2. 构建商品主数据插入语句 string Find_SQL $INSERT INTO Goods_Info (merchID, fullNamenvarchar, profferID, unitCost, others) VALUES ({RecID}, {txtName.Text}, {TypeID}, {txtPrice.Text}, {txtRemark.Text}); // 3. 构建库存初始化插入语句 string Find_SQL2 $INSERT INTO Stock (GoodsID, Amount) VALUES ({RecID}, 0); // 4. 执行两条 SQL顺序不能错 MyDataClass.getsqlcom(Find_SQL); // 先插入商品 MyDataClass.getsqlcom(Find_SQL2); // 再初始化库存为什么必须是这个顺序因为Stock.GoodsID是外键必须先在Goods_Info表中存在merchID G0001Stock表才能成功插入GoodsID G0001。如果顺序颠倒第二条 SQL 会因外键约束失败而抛出异常。5.2 库存初始化Stock表的“零库存”哲学Stock表的设计非常朴素GoodsID主键 Amount整数。Amount默认为0这代表一种“零库存”哲学——任何商品只要被录入系统其库存状态就从0开始后续的进货、销售操作只负责增减这个数字。这个设计的好处是查询极简要查所有商品库存SELECT * FROM Stock即可无需JOIN。变更可控库存变化只由Buy和Sell表的增删驱动Stock表本身不接受直接UPDATE所有变更都有迹可循。扩展性强未来要加“批次管理”、“仓库分区”只需在Stock表加字段BatchNo,WarehouseID主逻辑不变。坏处是缺乏历史Stock表只存当前快照看不到某天某时的库存是多少。要审计得查Buy和Sell表的流水。并发风险如果两个收银员同时卖同一商品UPDATE Stock SET Amount Amount - 1可能导致超卖经典“扣减库存”问题。教学项目忽略此问题但生产系统必须用事务或乐观锁。5.3 进货单与库存联动Buy表如何真正改变库存报告中btnAdd_Click()只负责初始化库存为0真正的库存增加发生在“进货管理模块”。虽然报告没给出Buy模块的代码但我们可以根据数据库设计和业务逻辑写出标准实现// FrmBuyManage.cs 中的进货保存逻辑 private void btnSaveBuy_Click(object sender, EventArgs e) { string buyID MyMC.GetAutocoding(Buy, BuyID); string goodsID cmbGoods.SelectedValue.ToString(); // 从下拉框选的商品 string supplierID cmbSupplier.SelectedValue.ToString(); decimal price decimal.Parse(txtPrice.Text); int amount int.Parse(txtAmount.Text); decimal sum price * amount; // 1. 插入进货单 string buySql $INSERT INTO Buy (BuyID, GoodsID, SupplierID, Price, Amount, Sum, Date) VALUES ({buyID}, {goodsID}, {supplierID}, {price}, {amount}, {sum}, GETDATE()); MyDataClass.getsqlcom(buySql); // 2. 更新库存库存 库存 进货数量 string updateStockSql $UPDATE Stock SET Amount Amount {amount} WHERE GoodsID {goodsID}; MyDataClass.getsqlcom(updateStockSql); MessageBox.Show(进货成功); }关键点UPDATE Stock SET Amount Amount {amount}是原子操作SQL Server 保证其线程安全在单实例下。GETDATE()是 SQL Server 函数自动填入当前时间比 C# 的DateTime.Now更可靠避免客户端时钟误差。这里没有检查Stock表中是否存在该GoodsID。如果进货的是一个新商品而Stock表里还没有这条记录UPDATE会静默失败影响行为为 0。所以必须确保所有商品在进货前已被录入Goods_Info并初始化Stock。这就是为什么btnAdd_Click()要同时插入两表——建立“商品存在”和“库存为 0”的前提。5.4 销售单与库存联动Sell表如何触发库存扣减与预警销售逻辑是进货的镜像但多了一层业务校验——不能超卖。报告中Sell表没有Sum字段意味着销售金额需实时计算这给了我们一个绝佳的扩展点。// FrmSellManage.cs 中的销售保存逻辑 private void btnSaveSell_Click(object sender, EventArgs e p a hrefhttps://download.csdn.net/download/qq_33393792/10373450 stylecolor:#ec7500;font-size:14px; 本文还有配套的精品资源点击获取 /a img altmenu-r.4af5f7ec.gif srchttps://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif stylewidth:16px;margin-left:4px;vertical-align:text-bottom;cursor:text; /p
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。