JavaWeb医院药品管理系统:从三层架构到库存并发控制实战解析
简介本资源是一套完整可用的基于JavaWeb的医院药品管理系统专为计算机专业本科生毕业设计、课程设计及Java初学者项目实战打造解决药品入库、出库、库存查询、供应商管理等核心业务场景建模与系统实现问题。压缩包共195个文件涵盖53个Java后端逻辑类、21个HTML前端页面、22个JavaScript交互脚本、6个CSS样式文件及1个SQL数据库脚本辅以Layui框架静态资源如layui.css、layer.css、iconfont字体文件等结构清晰、模块解耦开箱即用。资源包仅869KB轻量高效已通过导师评审并获高分所有功能均经严格调试可直接部署运行。目前已有651人学习下载配套提供详细项目说明文档与数据库初始化脚本显著降低环境配置门槛与调试成本助力快速掌握SSM或Servlet/JSP技术栈在医疗信息化场景中的典型应用。1. 项目概述与核心价值最近在整理硬盘翻出来一个压箱底的老项目——一个基于JavaWeb的医院药品管理系统。这个项目虽然技术栈不算新潮但麻雀虽小五脏俱全从药品入库、库存管理、处方划价到药房发药整个业务流程都覆盖了。我当年参与过类似系统的实施深知这类管理软件的核心痛点不在于用了多炫的技术而在于业务流程的严谨性和数据的一致性。这个源码包通常包含src、WebContent、sql脚本和数据库文件对于想入门JavaWeb企业级开发或者想了解医疗行业基础业务逻辑的朋友来说是个非常不错的练手和参考材料。它不像一些教学Demo那样功能单薄而是真实模拟了药库、药房、收费处、医生站等多个角色的协作能让你对MVC分层、数据库设计、事务控制有一个立体的认识。2. 系统核心架构与设计思路拆解拿到一个完整的项目源码第一步不是急着运行而是先看它的“骨架”。这个医院药品管理系统典型的采用了JavaWeb经典的三层架构表现层、业务逻辑层、数据访问层并辅以MVC模式进行职责分离。2.1 技术栈选型与考量项目通常基于Servlet JSP JavaBean的经典组合数据库则大概率是MySQL。为什么是这套组合在项目诞生的年代以及现在很多传统医院信息系统的初期这套组合成熟、稳定、学习资源丰富、部署简单。Servlet作为控制器Controller接收请求JSP负责渲染视图ViewJavaBean则充当模型Model和业务逻辑的载体。数据库选择MySQL是因为它开源、免费、性能足够支撑中小型医院的药品流转数据并且与Java的JDBC连接非常方便。你可能会在lib文件夹里看到mysql-connector-java-5.1.x.jar这样的驱动包以及c3p0或Druid数据库连接池的JAR包后者对于管理数据库连接、提升性能至关重要。2.2 核心业务模块解析系统的功能模块设计紧密围绕医院药品流转的生命周期基础数据管理这是系统的基石。包括药品信息药品编号、通用名、商品名、规格、剂型、单位、生产厂家、批准文号、库存上下限、进货价、零售价等、供应商信息、科室信息、员工药师、医生信息等。任何业务操作都依赖于这些基础数据的准确性和完整性。药库管理模块这是药品的“总仓库”。核心功能包括采购计划制定、采购订单管理、药品入库验收核对批号、有效期、数量、库存盘点、药品报损、库存查询支持库存量、有效期预警以及向药房调拨药品。这里涉及大量的库存数量更新对事务的原子性要求很高。药房管理模块包括门诊药房和住院药房。主要功能是接收药库的调拨管理药房库存并处理处方。药师需要审核医生开具的电子处方或核对纸质处方然后进行配药、发药操作。发药后系统会自动扣减药房库存并可能触发向收费系统的结算信号。处方与收费管理医生在医生工作站开具处方系统会自动计算处方总金额。处方流转到收费处收费员完成收费或记账操作后处方状态变为“已收费”药房才能看到并执行发药。这个“收费-发药”的联动是系统业务流程的关键控制点。统计与报表模块生成各类报表如药品入库明细/汇总表、药品出库明细/汇总表、库存盘点表、药品有效期预警表、药品销售排行、医生用药统计等。这些报表是医院药事管理和决策的重要依据。这套设计思路的核心是“物流、资金流、信息流三流合一”。每一次药品的物理移动入库、调拨、发药都伴随着库存数量的变化信息流和可能发生的资金结算资金流。系统必须保证这三者在任何业务操作后都是一致的。3. 数据库设计与关键表结构剖析数据库是这类管理系统的灵魂。一个设计良好的数据库结构是业务稳定运行的基础。我们打开附带的hospital_drug.sql文件假设名为此可以看到一系列的表。我们来剖析几个最核心的表。3.1 核心实体表-- 药品信息表 (t_drug 或 drug_info) CREATE TABLE t_drug ( drug_id varchar(20) NOT NULL COMMENT 药品编号, drug_name varchar(100) NOT NULL COMMENT 药品通用名, trade_name varchar(100) DEFAULT NULL COMMENT 商品名, specification varchar(50) NOT NULL COMMENT 规格, unit varchar(10) NOT NULL COMMENT 单位盒/瓶/支, manufacturer varchar(100) DEFAULT NULL COMMENT 生产厂家, stock_min int(11) DEFAULT 0 COMMENT 库存下限, stock_max int(11) DEFAULT 0 COMMENT 库存上限, purchase_price decimal(10,2) DEFAULT NULL COMMENT 进货价, retail_price decimal(10,2) NOT NULL COMMENT 零售价, location varchar(50) DEFAULT NULL COMMENT 货位, PRIMARY KEY (drug_id) ) ENGINEInnoDB DEFAULT CHARSETutf8 COMMENT药品信息表;注意drug_id药品编号的设计至关重要。它通常不是简单的自增ID而是包含一定规则的编码例如“YP20240801001”YP日期序列号便于识别和管理。retail_price零售价是划价收费的基础。-- 药品库存表 (t_stock 或 drug_stock) CREATE TABLE t_stock ( stock_id int(11) NOT NULL AUTO_INCREMENT, drug_id varchar(20) NOT NULL COMMENT 药品ID, batch_number varchar(50) NOT NULL COMMENT 批号, expiry_date date NOT NULL COMMENT 有效期至, quantity int(11) NOT NULL DEFAULT 0 COMMENT 当前数量, storage_location varchar(20) NOT NULL COMMENT 库存地点如药库、门诊药房、住院药房, PRIMARY KEY (stock_id), KEY idx_drug_id (drug_id), KEY idx_expiry (expiry_date) ) ENGINEInnoDB DEFAULT CHARSETutf8 COMMENT药品库存表按批号管理;关键点这里采用了医药行业通用的“批号有效期”管理方式。同一药品不同批号、不同有效期必须作为不同的库存记录。这直接关系到“先进先出/近效期先出”的发药原则。storage_location字段标识了药品物理存放的位置是实现药库、药房多级库存管理的关键。3.2 核心业务表-- 处方主表 (t_prescription) CREATE TABLE t_prescription ( prescription_id varchar(30) NOT NULL COMMENT 处方号, patient_id varchar(20) DEFAULT NULL COMMENT 患者ID, patient_name varchar(50) NOT NULL COMMENT 患者姓名, doctor_id varchar(20) NOT NULL COMMENT 开方医生ID, dept_name varchar(50) DEFAULT NULL COMMENT 科室, total_amount decimal(10,2) DEFAULT 0.00 COMMENT 处方总金额, status tinyint(4) NOT NULL DEFAULT 0 COMMENT 状态0-待收费1-已收费2-已发药3-已退费, create_time datetime NOT NULL COMMENT 开方时间, PRIMARY KEY (prescription_id) ) ENGINEInnoDB DEFAULT CHARSETutf8 COMMENT处方主表;-- 处方明细表 (t_prescription_detail) CREATE TABLE t_prescription_detail ( detail_id int(11) NOT NULL AUTO_INCREMENT, prescription_id varchar(30) NOT NULL COMMENT 处方号, drug_id varchar(20) NOT NULL COMMENT 药品ID, drug_name varchar(100) NOT NULL COMMENT 药品名称快照, specification varchar(50) NOT NULL COMMENT 规格快照, quantity int(11) NOT NULL COMMENT 数量, unit_price decimal(10,2) NOT NULL COMMENT 单价快照, subtotal decimal(10,2) NOT NULL COMMENT 小计, PRIMARY KEY (detail_id), KEY idx_prescription_id (prescription_id) ) ENGINEInnoDB DEFAULT CHARSETutf8 COMMENT处方明细表;实操心得注意明细表中的drug_name、specification、unit_price都是“快照”数据。这意味着它们存储的是开处方那一刻的药品信息和价格即使后续药品信息或价格变动这张处方的历史记录也不会改变。这是保证财务和医疗记录可追溯性的重要设计。status字段的流转0-1-2清晰地刻画了处方的生命周期。3.3 库存流水与事务一致性任何涉及库存变动的操作入库、出库、调拨、报损除了更新t_stock表的quantity字段必须同步记录流水账。这就是t_stock_flow库存流水表的作用。CREATE TABLE t_stock_flow ( flow_id bigint(20) NOT NULL AUTO_INCREMENT, drug_id varchar(20) NOT NULL, batch_number varchar(50) NOT NULL, flow_type tinyint(4) NOT NULL COMMENT 流水类型1-采购入库2-药房发药3-库房调拨4-药品报损..., change_quantity int(11) NOT NULL COMMENT 变动数量正数为增负数为减, before_quantity int(11) NOT NULL COMMENT 变动前数量, after_quantity int(11) NOT NULL COMMENT 变动后数量, related_id varchar(50) DEFAULT NULL COMMENT 关联业务单号如处方号、入库单号, operator varchar(20) NOT NULL, operate_time datetime NOT NULL, PRIMARY KEY (flow_id), KEY idx_drug_batch (drug_id, batch_number) ) ENGINEInnoDB DEFAULT CHARSETutf8 COMMENT库存流水表;这个表是数据审计和问题排查的“黑匣子”。任何库存异常都可以通过回溯流水记录找到根源。在代码实现时更新库存和插入流水记录必须在同一个数据库事务中完成确保要么都成功要么都失败这是实现数据一致性的铁律。4. 核心功能模块代码实现与解析我们以“药房发药”这个最核心、最复杂的业务流程为例拆解其代码实现。这个过程涉及库存扣减、处方状态更新必须保证事务性。4.1 数据访问层DAO设计首先需要一个负责库存操作的DAO接口及其实现。这里会用到JDBC或MyBatis。// StockDao.java 接口 public interface StockDao { /** * 根据药品ID、批号和库存地点查找特定库存记录用于加锁 */ DrugStock findStockForUpdate(String drugId, String batchNumber, String location) throws SQLException; /** * 更新库存数量 */ int updateStockQuantity(Long stockId, int newQuantity) throws SQLException; /** * 插入库存流水记录 */ int insertStockFlow(StockFlow flow) throws SQLException; }注意findStockForUpdate方法使用了SELECT ... FOR UPDATE如果在MySQL中这是一个悲观锁。在发药前锁定这条库存记录防止其他并发操作如同时发药、盘点导致数据错乱。这是处理高并发场景下库存超卖问题的关键手段之一。4.2 业务逻辑层Service实现业务逻辑层是核心它协调多个DAO操作并管理事务边界。// DispenseService.java public class DispenseService { private StockDao stockDao; private PrescriptionDao prescriptionDao; private Connection connection; // 通常由连接池管理事务也从这里控制 public boolean dispenseDrug(String prescriptionId, String operator) { // 1. 开启事务通常使用注解Transactional或编程式事务 setAutoCommit(false); try { // 2. 获取处方及明细 Prescription prescription prescriptionDao.getById(prescriptionId); if (prescription null || !1.equals(prescription.getStatus())) { throw new BusinessException(处方不存在或未收费无法发药); } ListPrescriptionDetail details prescriptionDao.getDetails(prescriptionId); // 3. 循环处理处方中每一种药品 for (PrescriptionDetail detail : details) { // 3.1 查询并锁定满足条件的库存批号最早、有效期最近 DrugStock stock stockDao.findStockForUpdate( detail.getDrugId(), detail.getBatchNumber(), // 实际业务中可能需要一个策略来选择批号 门诊药房 ); if (stock null || stock.getQuantity() detail.getQuantity()) { throw new BusinessException(药品[ detail.getDrugName() ]库存不足); } // 3.2 计算新库存 int newQuantity stock.getQuantity() - detail.getQuantity(); // 3.3 更新库存 stockDao.updateStockQuantity(stock.getStockId(), newQuantity); // 3.4 记录库存流水 StockFlow flow new StockFlow(); flow.setDrugId(detail.getDrugId()); flow.setBatchNumber(stock.getBatchNumber()); flow.setFlowType(2); // 发药出库 flow.setChangeQuantity(-detail.getQuantity()); // 负数表示减少 flow.setBeforeQuantity(stock.getQuantity()); flow.setAfterQuantity(newQuantity); flow.setRelatedId(prescriptionId); flow.setOperator(operator); flow.setOperateTime(new Date()); stockDao.insertStockFlow(flow); } // 4. 更新处方状态为“已发药” prescriptionDao.updateStatus(prescriptionId, 2, operator); // 5. 提交事务 connection.commit(); return true; } catch (Exception e) { // 6. 发生任何异常回滚事务 connection.rollback(); log.error(发药失败处方号: prescriptionId, e); return false; } finally { setAutoCommit(true); } } }这段代码是发药业务的核心。它清晰地展示了在一个事务内如何顺序执行查询校验 - 锁定资源 - 计算 - 更新库存 - 记录流水 - 更新主业务状态。任何一个步骤失败整个事务回滚数据状态回退到操作前保证了“要么全发要么全不发”。4.3 控制层Servlet与页面交互Servlet负责接收HTTP请求调用Service并转发到JSP页面显示结果。// DispenseServlet.java WebServlet(/pharmacy/dispense) public class DispenseServlet extends HttpServlet { private DispenseService dispenseService new DispenseService(); protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String prescriptionId request.getParameter(prescriptionId); String operator (String) request.getSession().getAttribute(username); boolean success false; String message; try { success dispenseService.dispenseDrug(prescriptionId, operator); message success ? 发药成功 : 发药失败。; } catch (BusinessException e) { message 业务错误 e.getMessage(); } catch (Exception e) { log.error(系统错误, e); message 系统异常发药失败。; } request.setAttribute(message, message); request.getRequestDispatcher(/pharmacy/dispense_result.jsp).forward(request, response); } }对应的JSP页面dispense_result.jsp则简单显示操作结果。而药房药师的操作界面通常会是一个待发药处方列表页面列出所有状态为“已收费”的处方每条处方后有“发药”按钮点击后通过Ajax或表单提交触发上面的Servlet。5. 项目部署与运行实操指南假设你拿到了一个名为HospitalDrugMS.zip的源码包下面是如何在本地以IntelliJ IDEA Tomcat MySQL为例让它跑起来的步骤。5.1 环境准备与数据库初始化安装JDK 8确保环境变量JAVA_HOME配置正确。安装MySQL 5.7创建数据库例如hospital_drug_db。mysql -u root -p CREATE DATABASE hospital_drug_db CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;导入数据库脚本找到源码包中的SQL文件可能是.sql或.dump在MySQL中执行。mysql -u root -p hospital_drug_db /path/to/hospital_drug.sql安装Tomcat 8.5下载并解压。安装IntelliJ IDEA确保已安装Java EE和Web开发插件。5.2 项目导入与配置解压并导入IDEA将HospitalDrugMS.zip解压。打开IDEA选择File-Open选择解压后的项目文件夹。IDEA通常会识别为Java Web项目。配置项目SDK和依赖在Project StructureCtrlAltShiftS中确保Project选项卡下的Project SDK是JDK 8。在Modules选项卡下检查项目的src目录是否被标记为SourcesWebContent或webapp目录是否被标记为Web Resource Directories。在Libraries选项卡添加项目WebContent/WEB-INF/lib下的所有JAR包。这是项目依赖的库。配置数据库连接找到项目中的数据库配置文件通常是WebContent/WEB-INF/classes下的jdbc.properties或dbconfig.properties。# jdbc.properties 示例 jdbc.drivercom.mysql.jdbc.Driver # 注意MySQL 8.0 驱动类名是 com.mysql.cj.jdbc.Driver连接串需要加时区参数 jdbc.urljdbc:mysql://localhost:3306/hospital_drug_db?useUnicodetruecharacterEncodingUTF-8useSSLfalse jdbc.usernameroot jdbc.passwordyour_password根据你的MySQL版本和安装情况修改url、username和password。配置Tomcat运行环境点击IDEA右上角Add Configuration点击选择Tomcat Server-Local。在Deployment选项卡点击-Artifact选择项目生成的War包或exploded版本推荐后者支持热更新。在Application context可以设置为/或/drugms这将是你的访问根路径。5.3 运行与访问点击IDEA的绿色运行按钮启动Tomcat。控制台无报错后打开浏览器访问http://localhost:8080/或你配置的上下文路径。通常登录页是login.jsp。使用SQL脚本中初始化的管理员账号登录常见如admin/admin或admin/123456请查看SQL文件中的t_user表。踩坑记录乱码问题如果页面显示乱码检查三处1) JSP文件头% page contentType“text/html;charsetUTF-8”%2) Servlet中request.setCharacterEncoding(“UTF-8”)和response.setCharacterEncoding(“UTF-8”)3) 数据库连接URL中的characterEncodingUTF-8。ClassNotFoundException通常是缺少JAR包或JAR包路径不对。确保所有依赖库都在WEB-INF/lib下并且被正确添加到项目库中。数据库连接失败检查jdbc.properties配置、MySQL服务是否启动、用户名密码是否正确、数据库名是否存在。MySQL 8.0以上版本需要更换驱动类名和连接URL。6. 常见业务场景深度优化探讨一个可用的系统和一个好用的系统之间差的就是对这些业务场景的深度优化。6.1 库存并发控制与超卖预防在门诊高峰期多个窗口可能同时为不同患者发放同一种药品。我们之前使用了SELECT ... FOR UPDATE行级锁这在高并发下可能成为瓶颈甚至导致死锁。另一种更高效的方案是使用乐观锁。-- 在t_stock表增加一个版本号字段 ALTER TABLE t_stock ADD COLUMN version INT DEFAULT 0 COMMENT 版本号;更新库存时采用CASCompare And Swap机制// 在StockDao中 int updateStockWithCAS(Long stockId, int changeQuantity, int currentVersion) { String sql UPDATE t_stock SET quantity quantity ?, version version 1 WHERE stock_id ? AND version ?; // 执行更新返回受影响行数。如果返回0说明版本号不对库存已被其他事务修改。 }在业务代码中如果更新返回0则捕获异常提示“库存已变化请重试”并让用户重新操作。这能极大提升并发吞吐量。6.2 药品有效期预警与近效期管理药品过期是药房管理的重大风险。系统需要主动预警。在库存表中设计索引如前所述对expiry_date字段建立索引方便快速查询。定时任务扫描使用Quartz或Spring Task创建定时任务每天凌晨扫描t_stock表。Scheduled(cron 0 0 2 * * ?) // 每天凌晨2点执行 public void checkDrugExpiry() { // 查询距离过期还有30天、15天、7天的药品 Date warningDate1 DateUtils.addDays(new Date(), 30); Date warningDate2 DateUtils.addDays(new Date(), 15); Date warningDate3 DateUtils.addDays(new Date(), 7); ListDrugStock expiringDrugs stockDao.findExpiringDrugs(warningDate1); // 将预警信息存入消息表或发送给相关人员 for (DrugStock drug : expiringDrugs) { // 生成预警记录... } }发药时强制近效期先出在DispenseService中查询库存时ORDER BY expiry_date ASC优先选择有效期最近的批次。6.3 复杂的药品拆零与库存单位管理有些药品需要拆零销售比如一瓶100片的药按“片”出售。这要求库存管理能支持多级单位。数据库设计扩展在t_drug表中增加base_unit基础单位如“片”、“毫升”和package_unit包装单位如“瓶”、“盒”以及conversion_rate换算率如一盒10板一板12片。ALTER TABLE t_drug ADD base_unit VARCHAR(10) COMMENT 基础单位, ADD package_unit VARCHAR(10) COMMENT 包装单位, ADD conversion INT COMMENT 包装单位到基础单位的换算系数如1盒100片系数为100;库存表存储t_stock表的quantity可以统一存储为基础单位的数量。入库时录入“2盒”程序自动转换为“200片”存储。业务逻辑处理发药时如果开出“15片”程序需要计算先从库存中扣除15片基础单位。当库存中某批次的药品以基础单位计算数量小于一个包装单位时就需要进行“拆零”标记和管理并在界面上给予特殊提示。7. 系统安全与数据完整性加固对于医疗系统安全和数据完整性是生命线。7.1 权限控制系统不能只有一个管理员。需要基于角色的访问控制RBAC。表设计t_user用户、t_role角色、t_permission权限、t_user_role、t_role_permission。权限拦截编写一个AuthFilter对每个请求的URL进行过滤检查当前用户是否拥有访问该URL的权限。权限信息可以缓存在Session或Redis中。WebFilter(/*) public class AuthFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) { HttpServletRequest request (HttpServletRequest) req; String uri request.getRequestURI(); // 排除登录、静态资源等 if (uri.contains(/login) || uri.contains(.css) || uri.contains(.js)) { chain.doFilter(req, resp); return; } User user (User) request.getSession().getAttribute(user); if (user null) { response.sendRedirect(/login.jsp); return; } // 检查用户权限列表是否包含当前URI if (!user.hasPermission(uri)) { request.getRequestDispatcher(/error/403.jsp).forward(req, resp); return; } chain.doFilter(req, resp); } }7.2 操作日志审计所有关键业务操作增、删、改、登录、发药、入库等都必须记录日志。设计日志表t_operation_log字段包括id,user_id,user_name,operation,module,ip,parameters,result,operate_time。使用AOP或过滤器记录在Service方法上使用注解或通过过滤器拦截请求将操作信息异步写入数据库或日志文件。parameters字段可以存储请求参数的JSON字符串便于追溯。7.3 数据备份与恢复定期备份编写Shell脚本使用mysqldump命令定期备份数据库。# backup.sh mysqldump -u root -ppassword hospital_drug_db | gzip /backup/drug_db_$(date %Y%m%d).sql.gz # 保留最近30天的备份 find /backup -name drug_db_*.sql.gz -mtime 30 -delete系统层面将备份脚本加入Linux的crontab定时任务。crontab -e # 每天凌晨3点执行备份 0 3 * * * /path/to/backup.sh8. 从学习到二次开发项目进阶之路这个项目作为一个学习范本和起点价值巨大。但如果你想把它变成一个真正可用的系统或者作为你简历上的一个亮点还需要做很多工作。8.1 技术栈升级框架升级将原生的Servlet/JSP逐步迁移到Spring Boot。这能极大简化配置内置Tomcat方便打包部署。用Spring MVC替代Servlet用Thymeleaf或前后端分离替代JSP。持久层升级将JDBC或旧的Hibernate/MyBatis用法升级到MyBatis-Plus或Spring Data JPA。它们能大幅减少样板代码。前端重构JSP页面耦合度高维护困难。可以尝试前后端分离后端提供RESTful API前端使用Vue.js或React等框架提升用户体验和开发效率。8.2 功能扩展移动端支持开发药师使用的PDA扫码发药APP或医生移动查房开方的小程序。后端需要提供相应的API接口。智能预警除了有效期还可以增加库存上下限预警、药品配伍禁忌预警在医生开方时提示、特殊药品麻、精、毒、放开方量超限预警等。数据可视化利用ECharts等库在管理后台展示药品消耗趋势、库存周转率、医生用药排名等可视化图表。接口集成与医院的HIS医院信息系统、LIS检验系统、PACS影像系统进行接口对接实现患者信息、检验结果、检查报告的共享。8.3 性能优化数据库优化为高频查询字段建立合适的索引对大数据量表如库存流水t_stock_flow进行分表按年/月。缓存引入使用Redis缓存基础数据如药品字典、员工信息、热点查询结果。在发药时可以先快速检查Redis中的库存快照再走数据库事务。异步处理对于非实时强一致的操作如生成复杂的统计报表、发送预警短信/邮件可以放入消息队列如RabbitMQ异步处理提升主流程响应速度。这个基于JavaWeb的医院药品管理系统源码就像一座老房子结构扎实功能齐全。通过深入剖析它的每一处设计修复它的每一处瑕疵并按照现代软件工程的方法进行改造升级你不仅能学到企业级开发的完整知识链更能获得一个极具分量的实战项目经验。从读懂它到改进它再到超越它这个过程本身就是一名开发者最好的成长路径。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →