4.20课后作业1

科技政策查询手机端系统设计与实现
系统设计思想

  1. 整体架构设计(MVC模式)
    系统采用经典的MVC分层架构,职责清晰:
    Model层:Policy(政策实体)、PolicyKind(政策分类实体)封装数据,对应数据库表字段;
    Controller层:MobileServlet(处理手机端查询请求)、MobileDetailServlet(处理政策详情请求)接收前端请求,调用Dao层完成业务逻辑,转发数据到视图;
    View层:mobile.jsp(政策列表查询页)、mobileDetail.jsp(政策详情页)采用响应式布局适配手机端;
    Dao层:PolicyDao(政策数据操作)、PolicyKindDao(政策分类操作)封装数据库CRUD逻辑;
    工具层:DBUtil(数据库连接池)基于Druid实现连接复用,提升性能。

  2. 核心功能设计
    (1)关键字模糊查询

  • 支持多维度条件筛选:政策名称、文号、发布机构、全文关键字、政策类型;
  • 全文模糊匹配:关键字同时匹配政策名称和政策正文(text字段);
  • 分页查询:默认每页10条数据,计算总页数,避免移动端数据加载过多。

(2)政策分类统计
查询政策分类(type字段)及对应政策数量,展示在查询页侧边/顶部,方便用户按分类快速筛选。

(3)政策详情查看
点击政策名称跳转新窗口(移动端适配的详情页),展示政策全文、发布机构、发布日期等完整信息。

  1. 数据库设计
    核心表为policy,存储政策全量信息,关键字段包括:
    | 字段名 | 类型 | 说明 |
    | id | bigint | 主键(自增)|
    | name | varchar(255)| 政策名称 |
    | type | varchar(255)| 政策分类 |
    | organ | varchar(255)| 发布机构 |
    | document | varchar(255)| 政策文号 |
    | viadata | date | 发布日期 |
    | text | longtext | 政策全文 |

三、核心源代码实现

  1. 数据库连接工具(DBUtil.java)
    java
    package com.hebei.tech.util;

import com.alibaba.druid.pool.DruidDataSource;
import java.sql.Connection;
import java.sql.SQLException;

public class DBUtil {
private static final DruidDataSource dataSource;

static {
    dataSource = new DruidDataSource();
    dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
    dataSource.setUrl("jdbc:mysql://localhost:3306/febs?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&characterEncoding=UTF-8&useUnicode=true");
    dataSource.setUsername("root");
    dataSource.setPassword("123456");
    dataSource.setInitialSize(5); // 初始连接数
    dataSource.setMaxActive(20); // 最大活跃连接数
}

public static Connection getConnection() throws SQLException {
    return dataSource.getConnection();
}

public static void close(Connection conn) {
    if (conn != null) {
        try {
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

}

  1. 手机端查询请求处理(MobileServlet.java)
    java
    package com.hebei.tech.servlet;

import com.hebei.tech.dao.PolicyDao;
import com.hebei.tech.dao.PolicyKindDao;
import com.hebei.tech.model.Policy;
import com.hebei.tech.model.PolicyKind;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

@WebServlet("/mobile")
public class MobileServlet extends HttpServlet {
private final PolicyDao policyDao = new PolicyDao();
private final PolicyKindDao kindDao = new PolicyKindDao();
private static final int PAGE_SIZE = 10; // 移动端每页展示10条

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    try {
        // 1. 获取前端查询参数
        String keyword = request.getParameter("keyword");
        String docNumber = request.getParameter("docNumber");
        String issuingAgency = request.getParameter("issuingAgency");
        String typeId = request.getParameter("typeId");
        String pageNumStr = request.getParameter("pageNum");

        // 2. 分页参数处理
        int pageNum = 1;
        if (pageNumStr != null && !pageNumStr.isBlank()) {
            try {
                pageNum = Integer.parseInt(pageNumStr);
            } catch (NumberFormatException e) {
                pageNum = 1;
            }
        }

        // 3. 数据查询:分类列表、政策列表、总数计算
        List<PolicyKind> kindList = new ArrayList<>();
        List<Policy> policyList = new ArrayList<>();
        int totalCount = 0;
        int totalPage = 0;

        try {
            kindList = kindDao.findAllWithCount(); // 分类及数量
            policyList = policyDao.findByCondition(
                    keyword, docNumber, issuingAgency, keyword, typeId, pageNum, PAGE_SIZE
            ); // 多条件查询
            totalCount = policyDao.countByCondition(
                    keyword, docNumber, issuingAgency, keyword, typeId
            ); // 总条数
            totalPage = (totalCount + PAGE_SIZE - 1) / PAGE_SIZE; // 总页数
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 4. 绑定数据到请求域
        request.setAttribute("kindList", kindList);
        request.setAttribute("policyList", policyList);
        request.setAttribute("totalCount", totalCount);
        request.setAttribute("totalPage", totalPage);
        request.setAttribute("currentPage", pageNum);
        request.setAttribute("keyword", keyword);
        request.setAttribute("docNumber", docNumber);
        request.setAttribute("issuingAgency", issuingAgency);
        request.setAttribute("selectedTypeId", typeId);

        // 5. 转发到移动端列表页
        request.getRequestDispatcher("/mobile.jsp").forward(request, response);
    } catch (Exception e) {
        e.printStackTrace();
        response.setContentType("text/html;charset=UTF-8");
        response.getWriter().println("<html><body><h1>系统错误</h1><pre>" + e.toString() + "</pre></body></html>");
    }
}

@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    doGet(request, response); // Post请求复用Get逻辑
}

}

  1. 政策详情请求处理(MobileDetailServlet.java)
    java
    package com.hebei.tech.servlet;

import com.hebei.tech.dao.PolicyDao;
import com.hebei.tech.model.Policy;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/mobile/detail")
public class MobileDetailServlet extends HttpServlet {
private final PolicyDao policyDao = new PolicyDao();

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    try {
        String id = request.getParameter("id");
        Policy policy = null;

        // 根据ID查询政策详情
        if (id != null && !id.isBlank()) {
            try {
                policy = policyDao.findById(id);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        request.setAttribute("policy", policy);
        // 转发到移动端详情页
        request.getRequestDispatcher("/mobileDetail.jsp").forward(request, response);
    } catch (Exception e) {
        e.printStackTrace();
        response.setContentType("text/html;charset=UTF-8");
        response.getWriter().println("<html><body><h1>错误</h1><pre>" + e.toString() + "</pre></body></html>");
    }
}

@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    doGet(request, response);
}

}

  1. 移动端政策列表页(mobile.jsp)
    jsp
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
河北科技政策查询-手机端
<!-- 政策分类 -->
<div class="kind-list">
    <h4>政策分类</h4>
    <c:forEach items="${kindList}" var="kind">
        <a href="${pageContext.request.contextPath}/mobile?typeId=${kind.typeId}" class="item">
            ${kind.type}(${kind.policyCount})
        </a>
    </c:forEach>
</div>

<!-- 政策列表 -->
<div class="policy-list">
    <h4>政策列表(共${totalCount}条)</h4>
    <c:choose>
        <c:when test="${empty policyList}">
            <p style="text-align: center; color: #999; margin: 20px 0;">暂无符合条件的政策</p>
        </c:when>
        <c:otherwise>
            <c:forEach items="${policyList}" var="policy">
                <div class="policy-item">
                    <!-- 点击政策名称打开新窗口查看详情 -->
                    <a href="${pageContext.request.contextPath}/mobile/detail?id=${policy.id}" target="_blank">
                        ${policy.policyName}
                    </a>
                    <div class="info">
                        文号:${policy.documentNumber} | 发布机构:${policy.issuingAgency} | 发布日期:${policy.issueDate}
                    </div>
                </div>
            </c:forEach>
        </c:otherwise>
    </c:choose>

    <!-- 分页栏 -->
    <div class="page-bar">
        <c:if test="${currentPage > 1}">
            <a href="${pageContext.request.contextPath}/mobile?keyword=${keyword}&docNumber=${docNumber}&issuingAgency=${issuingAgency}&typeId=${selectedTypeId}&pageNum=${currentPage-1}">上一页</a>
        </c:if>
        <a href="#" class="current">第${currentPage}页/共${totalPage}页</a>
        <c:if test="${currentPage < totalPage}">
            <a href="${pageContext.request.contextPath}/mobile?keyword=${keyword}&docNumber=${docNumber}&issuingAgency=${issuingAgency}&typeId=${selectedTypeId}&pageNum=${currentPage+1}">下一页</a>
        </c:if>
    </div>
</div>
  1. 移动端政策详情页(mobileDetail.jsp)
    jsp
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
${policy.policyName}-政策详情
${policy.policyName}

政策文号:${policy.documentNumber}

政策类型:${policy.policyTypeName}

发布机构:${policy.issuingAgency}

发布日期:${policy.issueDate}

${policy.content}

四、运行结果截图说明

  1. 手机端查询页
    界面:顶部为搜索区(关键字、文号、发布机构输入框+类型下拉框),中间为政策分类标签(带数量统计),底部为政策列表(含政策名称、文号、发布机构、日期),最下方是分页栏;
    交互:输入关键字“科技创新”,点击查询,列表展示匹配的政策;点击分类标签“综合”,筛选出该类型所有政策。

  2. 政策详情页
    交互:点击政策名称(如“关于促进科技创新的若干政策”),打开新窗口;
    界面:顶部显示政策标题,中间为基础信息(文号、类型、发布机构、日期),底部为政策全文,文字自适应手机屏幕宽度,支持滑动查看。

  3. 空数据场景
    输入无匹配的关键字(如“测试123456”),列表显示“暂无符合条件的政策”,界面无报错。

五、编程总结分析

  1. 开发亮点
    性能优化:使用Druid连接池复用数据库连接,避免频繁创建/关闭连接;分页查询减少移动端数据加载量,提升响应速度;
    用户体验:移动端响应式布局,适配不同屏幕尺寸;分类标签带数量统计,分页栏简洁易用;
    容错处理:参数格式校验(如页码非数字默认置1)、异常捕获(数据库操作异常打印日志,前端友好提示)。

  2. 问题与解决方案
    | 问题 | 解决方案 |
    | 中文乱码 | 数据库连接串指定characterEncoding=UTF-8,JSP设置charset=UTF-8,响应设置UTF-8 |
    | 分页计算错误 | 总页数公式:(totalCount + PAGE_SIZE - 1) / PAGE_SIZE,避免整除丢失页数 |
    | 移动端适配失效 | 添加<meta name="viewport" content="width=device-width, initial-scale=1.0"> |

  3. 可优化方向
    增加关键字高亮:查询结果中高亮显示匹配的关键字;
    缓存优化:对政策分类、热门政策做Redis缓存,减少数据库查询;
    前端美化:引入移动端UI框架(如Vant),提升界面美观度;
    全文检索:替换为Elasticsearch,提升海量数据下的查询效率。

六、PSP0级时间记录日志
PSP(Personal Software Process)0级聚焦基础的过程记录,本次开发时间日志如下(单位:分钟):

| 阶段 | 预估时间 | 实际时间 | 说明 |
| Planning(计划) | 30 | 25 | 确定需求、拆分开发任务、预估各阶段时间 |
| Requirements(需求分析) | 40 | 45 | 梳理核心功能(模糊查询、详情查看)、技术栈选型 |
| Design(设计) | 60 | 70 | 架构设计(MVC)、数据库表设计、页面原型设计 |
| Coding(编码) | 240 | 270 | 编写Dao、Servlet、JSP代码,调试数据库连接 |
| Testing(测试) | 60 | 80 | 功能测试(查询、详情、分页)、兼容性测试(不同手机浏览器)、异常场景测试 |
| Postmortem(总结) | 30 | 35 | 整理问题、总结优化点、编写文档 |
| 总计 | 460 | 525 | 实际耗时增加主要因调试数据库编码和分页逻辑 |

时间分析
测试阶段耗时超预估:因新增了“空数据”“非法参数”等异常场景测试,覆盖更全面;
编码阶段耗时超预估:JSP响应式样式调试需适配不同手机尺寸,花费额外时间。

posted @ 2026-04-24 16:48  姜乐融  阅读(3)  评论(0)    收藏  举报