☰
JSP科研成果申报系统开发:数据建模到部署避坑全解析
2026/10/5 2:42:01 网站建设 项目流程

简介:这份资源是一份围绕科研成果申报管理系统设计实现展开的JSP+JavaBean开发分析文档,主要面向正在学习Java Web开发、系统分析与设计,或需要完成类似毕业设计选题的高校学生与开发者。文档内容并不止于笼统的功能说明,而是完整覆盖了系统开发背景、设计目标、技术选型、系统模型、可行性分析、需求分析及功能模块划分,并深入展示了数据流图、实体联系图与数据表结构等数据库设计细节。实现部分还具体介绍了JavaBean组件设计、主界面与申报页面等关键模块的交互逻辑,并配有相应源代码分析。资源包含1个doc文档,压缩包整体516KB,采用论文式目录结构,方便按章节逐段查阅。目前已有62人浏览学习,适合作为掌握JSP与SQL Server 2000整合开发、理解科研管理业务流程并参考系统落地思路的实用资料。

1. 科研成果申报管理系统:为什么JSP老技术还能撑起这类项目

“JSP科研成果申报管理系统”这行字,是不少高校计算机专业课程设计和毕业设计题库里的常见题。它要解决的问题很具体:科研人员登录后在线填写论文、专利、软著、课题等成果信息,上传佐证材料,评审专家按批次审核,管理员最后汇总统计。这类系统规模不大、逻辑直白,但涉及登录、表单、文件上传、审批流、列表分页这些Java Web的经典知识点,所以至今还被大量用于教学。我按设计实现到部署排错的顺序,把整套方案拆开讲:表怎么建、页面怎么写、哪些参数最容易坑人、出了问题从哪里查起。

2. 系统设计与数据模型:先把五张表和状态机定下来

2.1 选型边界:JSP+Servlet+MySQL能在哪些场景站得住

先说结论:只要用户量在一两百人以内、并发峰值不过几十、服务器是学院机房的一台Windows,JSP这套组合依然是交付速度最快的方案。

很多人一听JSP就觉得落伍,实际情况是这种“老”恰恰是优势:Tomcat解压即用,不需要Maven全家桶;源码里一个JSP对应一个页面,一个Servlet对应一个动作,新接手的人顺着目录就能理清流程。做课程设计或内部系统,最怕的不是技术旧,而是框架把流程包装成黑匣子,出错时不知道去哪查。Spring Boot虽好,但对这台服务器和这个业务量来说,引入它要付出的依赖管理成本反而更高。

选型还有一个实际理由:题目本身把“JSP”写死在标题里,意味着验收时重点看JSP语法、Servlet生命周期、Session控制和JDBC操作。你要在答辩现场讲清楚这些知识点,用Spring Boot反而讲不到点子上。我一般会在这个阶段先定三条约束:不用任何除Servlet/JSP/JSTL之外的Web框架,不用MyBatis这类ORM,不用前端工程化工具。这样生成的代码每一行都能解释来历,也方便后面一步一步加功能。

2.2 数据库建模:成果表、审批日志表和状态字段

科研成果申报的核心数据是“成果”,围绕它展开的参与者是“申报人”和“评审专家/管理员”。我通常先建四张表:用户表、成果表、审批记录表、附件表。附件表单独建,而不是在成果表里留一个路径字段——这是很多初稿会踩的坑:一份成果可能有多张佐证图片和一个PDF,一个字符串字段根本存不下。

CREATE DATABASE research DEFAULT CHARACTER SET utf8mb4; USE research; CREATE TABLE sys_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(32) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL COMMENT '存储MD5加盐值', real_name VARCHAR(32) NOT NULL, role TINYINT NOT NULL DEFAULT 2 COMMENT '1-管理员 2-申报人 3-评审专家', dept VARCHAR(64), create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB; CREATE TABLE achievement ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, title VARCHAR(200) NOT NULL COMMENT '成果名称', type VARCHAR(16) NOT NULL COMMENT '论文/专利/软著/课题', first_author VARCHAR(32), description TEXT, status TINYINT NOT NULL DEFAULT 0 COMMENT '0草稿 1已提交 2初审通过 3终审通过 4被退回', submit_time DATETIME, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_user (user_id), KEY idx_status (status) ) ENGINE=InnoDB; CREATE TABLE attachment ( id INT PRIMARY KEY AUTO_INCREMENT, achievement_id INT NOT NULL, file_name VARCHAR(200) NOT NULL, file_path VARCHAR(255) NOT NULL, file_size BIGINT, upload_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_achievement (achievement_id) ) ENGINE=InnoDB; CREATE TABLE approval_log ( id INT PRIMARY KEY AUTO_INCREMENT, achievement_id INT NOT NULL, operator_id INT NOT NULL, action VARCHAR(16) NOT NULL COMMENT 'submit/approve/reject/rollback', comment VARCHAR(512), create_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_achievement (achievement_id) ) ENGINE=InnoDB;

这里有几个字段的设计意图要说明。status故意用TINYINT而不是VARCHAR,因为状态机比较适合用数字表达;初版只做“提交-审核-通过”时可以简单把状态值加一,一旦出现“退回”就必须显式定义流转路径,后面讲审批那章会再展开。type字段也不要设计成枚举值写死在数据库,因为科研成果类型每年可能加“标准”“获奖”等新类别,存字符串并单独做字典维护更灵活。

sys_user的password字段长度给了64,因为存的是MD5加盐后的十六进制值,不是明文。username上的UNIQUE约束看起来简单,但它是登录模块防重名的基础,没有这个约束,注册逻辑里每次都要先查一遍再插入,多一次查询还容易出并发重复。附件表单独存储的好处是:上传的实物文件路径只有一个归属点,删除成果时按achievement_id统一清理,不会因为多个字段存路径而漏删。

如果你接手的是别人的库,常见缺失是approval_log表。没有日志表时,“成果为什么被退回”只能靠管理员记忆,这是答辩时最容易被问倒的点。所以我在第一版就把审批记录表加上了,哪怕只用action和comment两个字段。

2.3 JDBC连接池与基础工具类:别让每个请求都开新连接

学生作品里最常见的数据库代码是每次都Class.forName然后DriverManager.getConnection。这种方式在教学示例里没问题,但一套申报系统跑几天后,MySQL端会报Too many connections,原因就是连接用完没关。正确做法是用连接池统一管理,我用的是Apache Commons DBCP2,配置如下:

import org.apache.commons.dbcp2.BasicDataSource; import java.sql.Connection; import java.sql.SQLException; public class DbUtil { private static BasicDataSource dataSource; static { dataSource = new BasicDataSource(); dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/research" + "?useUnicode=true&characterEncoding=utf8" + "&serverTimezone=Asia/Shanghai"); dataSource.setUsername("root"); dataSource.setPassword("your_password"); dataSource.setInitialSize(5); dataSource.setMaxTotal(20); dataSource.setMaxIdle(10); dataSource.setMaxWaitMillis(3000); } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } }

这段代码有三个参数值得记:initialSize设5,应用启动后就预建5个连接,避免第一个访问者等待;maxTotal是连接池最大连接数,按你的Tomcat并发线程数来定,通常20够用;maxWaitMillis是拿不到连接时的最长等待时间,设3000毫秒,超时直接抛异常,而不是让请求无限排队把线程池拖死。

驱动类名这里写的是com.mysql.cj.jdbc.Driver,对应MySQL 8.0。如果你用的是MySQL 5.7,要换成com.mysql.jdbc.Driver,这个差异在避坑章还会专门提。使用连接池后,业务代码里取连接和关连接的写法也要规范化,用try-with-resources是最稳妥的:

try (Connection conn = DbUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, username); ResultSet rs = ps.executeQuery(); // 处理结果 } catch (SQLException e) { e.printStackTrace(); }

注意ResultSet也应当在使用完后关闭;用try-with-resources时,Connection和PreparedStatement会自动关闭,但ResultSet如果你不用try-with-resources包一层,仍要手动close。我习惯把ResultSet的数据先拷到List里再关连接,避免在连接关闭后访问结果集报错,这也是新手常遇到的一个隐蔽运行时异常。

3. 核心功能落地:登录、申报、审批和上传的实现

3.1 登录与角色控制:Session里到底该放什么

登录是所有页面的入口,也是权限控制的基础。这里的核心问题不是“怎么查用户”,而是“登录成功后把什么放进Session”。我推荐只放一个user对象和一个role标识,而不是放一堆散落的字符串。

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("UTF-8"); String username = req.getParameter("username"); String password = md5WithSalt(req.getParameter("password"), req.getParameter("salt")); User user = userDao.findByUsernameAndPassword(username, password); if (user == null) { req.setAttribute("error", "用户名或密码错误"); req.getRequestDispatcher("/login.jsp").forward(req, resp); return; } req.getSession().setAttribute("loginUser", user); resp.sendRedirect(req.getContextPath() + "/home"); }

登录失败用forward而不是sendRedirect,是因为forward能保留request域里的error信息,登录页重新渲染时可以直接用EL把错误提示显示出来;如果用重定向,request域会丢失,错误提示就得改放到Session里还要记得清掉,多一道麻烦。密码校验前先setCharacterEncoding("UTF-8"),这一步必须在读取任何参数之前执行,否则中文用户名会乱码。

角色控制建议用Filter统一拦截,而不是在每个Servlet里重复判断。比如你定义三个URL前缀:/user/*只有申报人能访问,/review/*只有专家能访问,/admin/*只有管理员能访问。过滤器里取session中的loginUser.role做比对,不符合就重定向到首页。这样做的好处是以后加新角色,只改一处映射。

3.2 成果申报表单:字段校验、草稿暂存和提交

申报页是这个系统的核心页面,字段至少包括:成果名称、成果类型、第一作者、成果描述、佐证材料。我一般把“保存草稿”和“提交申报”做成两个按钮,对应两个状态:0和1。这个设计在答辩时是加分项,因为体现了对业务状态的理解,而不是简单一张表插一条记录。

<form action="${pageContext.request.contextPath}/achievement/save" method="post" enctype="multipart/form-data"> <div> <label>成果名称</label> <input type="text" name="title" maxlength="200" required> </div> <div> <label>成果类型</label> <select name="type"> <option value="论文">论文</option> <option value="专利">专利</option> <option value="软著">软著</option> <option value="课题">课题</option> </select> </div> <div> <label>成果描述</label> <textarea name="description" rows="5"></textarea> </div> <div> <label>佐证材料</label> <input type="file" name="attachment" multiple> </div> <button type="submit" name="action" value="draft">保存草稿</button> <button type="submit" name="action" value="submit">提交申报</button> </form>

这段JSP里值得说的是action字段:两个按钮共用一个表单,提交时按按钮的value判断是存草稿还是走提交流程。如果只做一个提交按钮,用户填了一半去吃饭,回来刷新页面,填的内容全没了,体验很差。有了草稿,用户可以先存为status=0,想好了再提交转成status=1。

Servlet接收时要注意:表单用了enctype="multipart/form-data",所以普通字段不能直接用req.getParameter拿,必须先从Part接口里解析。常见错误是把表单改成multipart后,发现title字段拿到null,卡了半天,这是JSP上传表单最常见的坑,下面3.4节会专门讲Part的用法。

3.3 审批流转:状态机驱动的流程怎么设计

审批流程看起来就是“状态加一”,但实际业务里有退回和撤回,不能简单做加法。我自己吃过这个亏:第一版用currentStatus + 1表示审核通过,结果评审专家一旦点了“退回”,状态变成了2,系统以为是初审通过,后面流程全乱了。后来改成显式状态机:

public int nextStatus(int currentStatus, String action) { if ("approve".equals(action)) { if (currentStatus == 1) return 2; // 初审通过 if (currentStatus == 2) return 3; // 终审通过 return currentStatus; } if ("reject".equals(action)) { return 4; // 退回申报人 } return currentStatus; }

这个逻辑说到底是张映射表,比加一更安全。退回状态下,申报人修改后再次提交,走的是“重新提交”动作,把状态从4变回1,而不是从4往上加变成5。这样设计,状态数值始终限制在0到4之间,列表筛选、统计报表都简单。

审批操作一定要写approval_log。我一般会在日志里记录achievement_id、operator_id、action和comment,形成一条可回溯的链。这样如果申报人问“为什么被退回”,专家写的comment可以直接展示在页面上,这就是日志表存在的意义。没有日志表的系统,审批记录就是一笔糊涂账,事后查证全靠猜。

3.4 佐证材料上传:Part接口与Tomcat限制

JSP项目里的文件上传,老资料会推荐commons-fileupload包,但Servlet 3.0之后官方提供了Part接口,Tomcat 7以上都支持,不用引第三方库。前提是Servlet类上要加@MultipartConfig注解,并配置大小限制:

@WebServlet("/achievement/save") @MultipartConfig( maxFileSize = 50 * 1024 * 1024, maxRequestSize = 60 * 1024 * 1024, fileSizeThreshold = 1024 * 1024 ) public class AchievementSaveServlet extends HttpServlet { protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Part part = req.getPart("attachment"); String fileName = Paths.get(part.getSubmittedFileName()) .getFileName().toString(); String prefix = UUID.randomUUID().toString().replace("-", ""); String storePath = getServletContext().getRealPath("/upload") + File.separator + prefix + "_" + fileName; part.write(storePath); // 将文件名和路径写入 attachment 表 } }

maxFileSize是单个文件上限,maxRequestSize是整个请求的总上限,因为一次可能传多个文件,后者必须大于前者。fileSizeThreshold是临时文件阈值:超过1MB的文件先写到磁盘临时目录,避免占满内存。这里还有一个点:getSubmittedFileName()在不同容器下实现不统一,Tomcat里能用,换成Jetty可能需要手动解析header里的filename,所以如果要做跨容器部署,最好自己写一段解析逻辑。

路径拼接用了UUID前缀,是为了防止同名文件互相覆盖。佐证材料里常见的文件名是“新建文档.docx”“未命名.jpg”,两个人传上来会互相干扰,加UUID后基本不会撞。

4. JSP页面细节:个人信息展示、图片定位与刷新时机

4.1 个人信息展示页面:JSTL+EL替代Scriptlet

登录后跳转的首页通常就是个人中心的概览页,很多初版代码会在jsp里写这种Java片段:

<% User u = (User) session.getAttribute("loginUser"); if (u != null) { out.println(u.getRealName()); } %>

这种Scriptlet不是不能用,但页面里业务代码一多,JSP就成了可读性极差的混合体。我一般改用JSTL和EL,配合request域里的数据:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %> <div class="profile-card"> <h3>${sessionScope.loginUser.realName}</h3> <p>所属部门:${sessionScope.loginUser.dept}</p> <p>角色: <c:choose> <c:when test="${sessionScope.loginUser.role == 1}">管理员</c:when> <c:when test="${sessionScope.loginUser.role == 2}">申报人</c:when> <c:otherwise>评审专家</c:otherwise> </c:choose> </p> <p>近期待办: <c:forEach items="${pendingList}" var="item"> <a href="detail?id=${item.id}">${item.title}</a> </c:forEach> </p> </div>

EL的取值链${sessionScope.loginUser.role}会自动调用getUser().getRole()方法,不需要再写Java代码。页面上日期显示用fmt:formatDate而不是直接输出Date对象的toString,否则会看到一长串英文时间格式。这个jsp个人信息展示页面要同时兼顾三种角色:申报人看到自己的草稿和驳回记录,专家看到待审列表,管理员看到统计入口。用c:choose做角色分支,页面结构清晰,加新角色时也容易扩展。

4.2 佐证图片的坐标定位:点击标注并回显

经常有人搜“jsp图片如何对坐标定位”,这个需求通常出现在成果佐证页:比如论文中的实验截图,要标注出核心区域;或者专利附图里要圈出某个结构。实现思路并不复杂:图片放在一个相对定位的容器里,用JS监听点击位置,把x、y坐标存进数据库,展示时用绝对定位的标记元素还原。

<div id="imageBox" style="position:relative; display:inline-block;"> <img src="${pageContext.request.contextPath}/upload/${img.fileName}" id="targetImg" alt="佐证图片"> <span id="marker" style="position:absolute; display:none; width:40px; height:40px; border:2px solid red; border-radius:50%;"></span> </div> <script> document.getElementById('targetImg').addEventListener('click', function (e) { const box = document.getElementById('imageBox'); const rect = box.getBoundingClientRect(); const x = Math.round(e.clientX - rect.left); const y = Math.round(e.clientY - rect.top); document.getElementById('marker').style.left = x + 'px'; document.getElementById('marker').style.top = y + 'px'; document.getElementById('marker').style.display = 'block'; document.getElementById('coordX').value = x; document.getElementById('coordY').value = y; }); </script> <input type="hidden" name="coordX" id="coordX"> <input type="hidden" name="coordY" id="coordY">

坐标要基于imageBox的左上角计算,而不是基于图片元素本身,因为图片加载后可能因为缩放产生位移。你如果直接在img上取offsetLeft,会把边框和padding算进去。还有一个坑是:图片没加载完之前点击,getBoundingClientRect拿到的尺寸不对,所以要么等window.onload之后再绑定事件,要么在img.onload里绑定。坐标存的是图片原始尺寸下的像素值,展示时如果图片被CSS缩放过,标记位置会漂移,这个问题可以通过存“百分比坐标”来解决,最后章再提。

4.3 状态变更后只刷新一次:解决审批结果看不到的问题

搜“jsp页面让加载完后刷新一次”的人,多半是遇到这种情况:专家在另一个页面审批完,申报人还停留在旧页面,列表上状态不更新。JSP是服务端渲染,没有数据推送能力,最简单的处理是让页面加载完成后自动刷新一次:

<c:if test="${sessionScope.needRefresh}"> <script> window.onload = function () { setTimeout(function () { location.reload(); }, 500); }; </script> </c:if>

这里有几个讲究。用sessionScope.needRefresh标记,而不是无条件刷新,是为了避免每个页面都闪一下,用户体验很差。标记在Controller里设置,页面渲染后由一段位于页尾的脚本负责消费并清除:

session.setAttribute("needRefresh", true); // 页面返回前设置,目标页面读取后清除

500毫秒的延迟是为了等页面资源加载完再刷新,时间不要设太短。另外要注意刷新必须是location.reload(),不是location.href = location.href,后者在某些浏览器里可能只在地址栏变化时触发一次。这个技巧放这里看起来有点土,但确实是最小改动、最不影响现有结构的方案;等系统大了再考虑WebSocket推送也不迟。

4.4 图标与前端资源:Element UI别硬套进JSP

常有人搜“饿了么elment图标前端jsp”,想直接在JSP页面里用Element UI那套图标和组件。这里要泼一盆冷水:Element UI是Vue 2生态的组件库,它的按钮、图标、表格都是Vue组件,不是你引入一个CSS文件就能用的。强行在JSP页面里引vue.js再挂载组件,很可能出现图标不显示、组件渲染延迟、表单校验和原生JSP提交方式冲突这些怪问题。

我一般这样取舍:如果只是要图标,用Font Awesome或Bootstrap Icons的CDN引入,class直接用,几行搞定。如果确实需要表格、弹窗、日期选择这类交互组件,优先用原生JS或jQuery插件方案,而不是把整个Vue搬进来。等到系统要大规模重构成前后端分离,再统一换Vue加Element Plus,那一步和现在这个JSP版本没有过渡关系,是一次彻底改造。

前端资源这块还有个容易被忽略的问题:JSP页面放在WEB-INF下或WebContent下的路径前缀不同,静态资源引用的写法也不一样。我一般用${pageContext.request.contextPath}拼所有资源路径,避免部署到带context名的路径时图片和样式404。

5. 避坑指南:JSP项目最常见的五个翻车点

5.1 中文乱码:从Tomcat到MySQL全线排查

现象:表单填中文,列表显示成问号,或者数据库里存的是??。

原因:整条链路里任何一处编码不一致都会乱码。常见的有三处:JSP页面本身的pageEncoding、Servlet里读取参数的编码、JDBC连接串里的characterEncoding。

解决:JSP文件头统一写pageEncoding="UTF-8";Servlet在读取任何参数前执行req.setCharacterEncoding("UTF-8");JDBC连接串加useUnicode=true&characterEncoding=utf8;MySQL建库指定utf8mb4。这四步都做了还乱码,检查Tomcat的server.xml里Connector是否设置了URIEncoding,新版Tomcat 8默认UTF-8,老版本不设置就按ISO-8859-1处理GET请求参数。

5.2 文件上传失败:maxPostSize与maxFileSize要同时调

现象:上传PDF或图片时,接口报500,或者明明传了文件,后台拿不到。

原因:Tomcat对请求体有默认大小限制,默认2MB;@MultipartConfig只限制了你自己的接口,但Tomcat的maxPostSize不调大,超过2MB的multipart请求直接被拒,业务代码根本不会执行。

解决:在Tomcat的server.xml里给Connector加maxPostSize="61440"(单位KB,即60MB),或者设-1表示不限制。同时把@MultipartConfig的maxFileSize、maxRequestSize调到业务需要的值。只调注解不调server.xml,是这个问题最常见的漏网之鱼。

5.3 日期格式化:SimpleDateFormat并发之下会错乱

现象:同一时刻多人审批,日志里出现时间错乱,或者页面显示的日期格式不一致。

原因:SimpleDateFormat不是线程安全的,把它定义为static共享,在高并发下会出现错乱;另外java.util.Date的默认toString输出是英文格式,不适合直接展示。

解决:JDK 8及以上用LocalDateTime + DateTimeFormatter,或者坚持用java.util.Date但JSP页面统一用fmt:formatDate格式化;数据库时间字段用DATETIME而不是字符串;排序用ORDER BY create_time,不要用字符串比较。这属于那种“大部分时间不报错,一并发就翻车”的隐患。

5.4 审批结果看不到:三种处理方式按需选

现象:专家审批完成后,申报人的列表页还是旧的,手动刷新才更新。

原因:JSP服务端渲染的页面没有服务端推送能力,状态变更不会主动通知已打开的页面。

解决:轻量做法是用4.3的“加载后刷新一次”标记;进阶做法是列表页轮询接口,每30秒查一次状态;彻底做法是引入WebSocket推送。按系统规模选,课程设计和内部系统用第一种就够了,不用一上来就上框架。

5.5 连接泄漏与MySQL驱动类名不匹配

现象:系统跑了两天,网页打开变慢,MySQL报Too many connections,重启Tomcat又恢复正常。

原因:代码里获取了连接但没有关闭,连接池被耗尽。另一个常见问题是MySQL 5.7项目用了com.mysql.cj.jdbc.Driver,或者MySQL 8项目还在用com.mysql.jdbc.Driver,启动时直接报ClassNotFound或不兼容。

解决:所有取连接的操作统一走try-with-resources;驱动类名和数据库版本对齐;如果项目同时要兼容两种版本,可以在加载时用反射先判断驱动类是否存在,再选择加载哪个。也可以用Druid连接池替代DBCP2,它自带监控页面,能直接看到活跃连接数和泄漏源,排查这类问题比黑匣子式的日志直观得多。

6. 验证清单与后续改造:从跑通到能交付还差这几步

6.1 用一份自测清单走完三种角色

系统写完别急着交,我建议按角色走一遍验收清单。表格式的验证路径比嘴上说“没问题”可靠得多:

功能模块操作路径预期结果常见失败点
登录申报人登录正确进入个人中心密码用MD5后查不到用户
草稿保存草稿后离开刷新后内容还在表单multipart导致字段丢失
提交提交按钮状态变1,专家可见状态机未处理退回后的重新提交
审批专家通过/退回日志记录完整approval_log未写入
上传传20MB PDF入库成功,展示可下载Tomcat maxPostSize未调
回收删除成果附件文件同步删除文件残留占磁盘

按这个清单过一遍,基本能覆盖交付前的主要风险点。验收时最好用三种浏览器各来一遍,JSP老系统在一些旧版浏览器上对EL标签的支持有细微差异,这个不实测很难发现。

6.2 还能往哪改:坐标存百分比、导出、充分支流程

坐标定位可以做一步改进:把x、y在JS里同时算成图片宽高的百分比再存库,展示时按百分比换算left、top坐标,这样图片不管被CSS放大还是缩小,标记都不会漂移,这是我前面留的伏笔,改起来就是JS里多除以一个rect.width和rect.height。审批环节如果业务要求多级会签,把状态机从常量改成配置表,每个流程节点存action顺序即可。最后的附件统计和汇总导出,用Apache POI生成Excel表格,这个技术很成熟,花一天就能接上。

我自己的经验:这类JSP系统交付后,维护成本大多不在功能,而在那些“能跑但没深究”的配置——连接池参数、上传大小限制、字符编码。当初图省事写的每请求一个连接,最后都变成了凌晨的故障电话。所以这篇花这么多篇幅讲边界和参数,就是希望你交付前把这些默认值都调对。这套方案做完,你得到的不仅是一个科研成果申报系统,更是一套能复用的表单加状态机套路。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询