基于Spring Boot与Vue.js的知识产权综合管理平台设计与实现
系统概述
本系统旨在为企业和机构提供一个高效的知识产权综合管理平台,涵盖产权人管理、数字资产归档、知识产权培训与考核等核心业务。系统采用前后端分离的开发模式,通过多角色权限控制(普通用户、知识产权人、系统管理员),实现业务流程的数字化与规范化。以下为系统核心界面的展示:
技术架构选型
1. 前端框架:Vue.js
前端采用 Vue.js 构建单页面应用(SPA)。该框架以其轻量级和响应式数据绑定机制著称,能够高效地将数据模型映射到 DOM 元素。结合 Vue Router 进行路由管理和 Vuex(或 Pinia)进行全局状态管理,前端团队能够以组件化的方式开发复杂的用户界面,显著提升代码的复用率与可维护性。Vue 3 引入的 Composition API 进一步优化了逻辑复用和 TypeScript 支持。
2. 后端框架:Spring Boot
后端服务基于 Spring Boot 构建,遵循"约定优于配置"的设计理念。通过自动配置机制和内嵌 Web 服务器(如 Tomcat),大幅简化了 Spring 应用的初始化与部署流程。Spring Boot 提供了丰富的 Starter 依赖,使得集成 MyBatis-Plus、安全认证等组件变得极为便捷。同时,结合 Spring Cloud 生态,系统具备良好的横向扩展能力,能够平滑演进至微服务架构。
3. 数据存储:MySQL
系统采用 MySQL 作为核心关系型数据库。作为业界广泛使用的开源 RDBMS,MySQL 在事务处理(ACID)、并发控制及查询优化方面表现优异。系统利用 InnoDB 存储引擎保障数据的一致性与完整性,并通过合理的索引设计与 SQL 调优,确保在海量知识产权数据检索时的高效响应。
4. 系统架构:B/S 模式
整体采用 B/S(Browser/Server)架构,用户无需安装专用客户端,通过标准浏览器即可访问系统。前后端通过 RESTful API 进行交互,数据格式统一采用 JSON。这种架构不仅降低了客户端的维护成本,还使得前后端团队能够并行开发,提升了整体的交付效率。
核心功能与测试验证
1. 测试策略概述
系统测试采用黑盒测试方法,围绕用户端与管理端的核心业务场景设计测试用例。测试重点涵盖身份认证、产权培训视频播放、知识产权考核、交流社区互动以及后台数据维护等模块,旨在验证系统逻辑的准确性与交互的流畅性。
2. 功能测试用例
用户端功能验证
| 编号 | 测试模块 | 操作步骤 | 预期结果 | 实际结果 | 状态 |
|---|---|---|---|---|---|
| 1 | 账号注册 | 输入合规的用户名与密码并提交 | 注册成功并跳转登录页 | 注册成功 | 通过 |
| 2 | 身份认证 | 输入正确的账号与密码 | 登录成功并获取 Token | 登录成功 | 通过 |
| 3 | 产权考核 | 进入考核页面,完成答题并提交 | 交卷成功并生成成绩 | 交卷成功 | 通过 |
| 4 | 培训视频 | 点击产权培训视频列表中的标题 | 视频正常加载并播放 | 播放正常 | 通过 |
| 5 | 交流社区 | 在产权讨论帖下方输入评论并发布 | 评论发布成功并实时显示 | 发布成功 | 通过 |
| 6 | 错题记录 | 进入个人中心查看考核错题本 | 准确展示历史错题及解析 | 展示准确 | 通过 |
管理端功能验证
| 编号 | 测试模块 | 操作步骤 | 预期结果 | 实际结果 | 状态 |
|---|---|---|---|---|---|
| 1 | 管理员登录 | 输入管理员专属账号与密码 | 登录成功进入后台 | 登录成功 | 通过 |
| 2 | 产权人检索 | 在用户管理中输入特定产权人姓名 | 精准返回匹配的用户记录 | 检索成功 | 通过 |
| 3 | 发布公告 | 填写产权政策公告标题与正文并提交 | 公告发布成功并在前台展示 | 发布成功 | 通过 |
| 4 | 更新培训资料 | 修改现有培训视频的简介与关联文件 | 资料更新成功并同步至前端 | 更新成功 | 通过 |
| 5 | 题库维护 | 在考核题库中选中过期试题并执行删除 | 试题从数据库中物理/逻辑删除 | 删除成功 | 通过 |
核心业务代码实现
以下展示系统身份认证、Token 签发及权限拦截的核心逻辑实现。代码经过了结构优化与变量重命名,以提升可读性与安全性。
@SkipAuthentication
@PostMapping("/api/v1/auth/sign-in")
public ApiResponse signIn(@RequestParam String account, @RequestParam String secretKey, HttpServletRequest httpRequest) {
AccountEntity accountEntity = accountService.findOne(new QueryWrapper<AccountEntity>().eq("account_name", account));
if (accountEntity == null || !accountEntity.getSecretKey().equals(secretKey)) {
return ApiResponse.fail("Invalid account or secret key");
}
String accessToken = tokenManager.issueAccessToken(accountEntity.getId(), account, "accounts", accountEntity.getRoleType());
return ApiResponse.success().addData("accessToken", accessToken);
}
@Override
public String issueAccessToken(Long userId, String accountName, String entityTable, String roleType) {
TokenEntity existingToken = this.findOne(new QueryWrapper<TokenEntity>().eq("user_id", userId).eq("role_type", roleType));
String newToken = RandomStringUtils.randomAlphanumeric(32);
LocalDateTime expiryTime = LocalDateTime.now().plusHours(1);
if (existingToken != null) {
existingToken.setAccessToken(newToken);
existingToken.setExpireAt(Timestamp.valueOf(expiryTime));
this.updateById(existingToken);
} else {
this.save(new TokenEntity(userId, accountName, entityTable, roleType, newToken, Timestamp.valueOf(expiryTime)));
}
return newToken;
}
@Component
public class AccessControlInterceptor implements HandlerInterceptor {
private static final String AUTH_HEADER = "Authorization";
@Autowired
private TokenManager tokenManager;
@Override
public boolean preHandle(HttpServletRequest req, HttpServletResponse res, Object handler) throws Exception {
res.setHeader("Access-Control-Allow-Origin", req.getHeader("Origin"));
res.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
res.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type, X-Requested-With");
if ("OPTIONS".equalsIgnoreCase(req.getMethod())) {
res.setStatus(HttpServletResponse.SC_OK);
return false;
}
SkipAuthentication skipAuth = null;
if (handler instanceof HandlerMethod) {
skipAuth = ((HandlerMethod) handler).getMethodAnnotation(SkipAuthentication.class);
} else {
return true;
}
if (skipAuth != null) {
return true;
}
String token = req.getHeader(AUTH_HEADER);
TokenEntity tokenData = null;
if (StringUtils.hasText(token)) {
tokenData = tokenManager.retrieveToken(token);
}
if (tokenData != null) {
req.getSession().setAttribute("currentUserId", tokenData.getUserId());
req.getSession().setAttribute("currentRole", tokenData.getRoleType());
req.getSession().setAttribute("currentTable", tokenData.getEntityTable());
return true;
}
res.setContentType("application/json;charset=UTF-8");
res.getWriter().write("{\"code\":401,\"message\":\"Authentication required\"}");
return false;
}
}
数据库结构设计
以下为核心配置与平台信息表的结构定义,采用了更规范的命名约定与字符集配置。
CREATE TABLE `platform_profile` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'Primary Key',
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Creation Time',
`heading` varchar(200) NOT NULL COMMENT 'Main Title',
`sub_heading` varchar(200) DEFAULT NULL COMMENT 'Sub Title',
`body_content` longtext NOT NULL COMMENT 'Detailed Content',
`image_url_1` longtext COMMENT 'First Image URL',
`image_url_2` longtext COMMENT 'Second Image URL',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Platform Profile Information';
CREATE TABLE `system_settings` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'Primary Key',
`setting_key` varchar(100) NOT NULL COMMENT 'Configuration Key',
`setting_value` varchar(255) DEFAULT NULL COMMENT 'Configuration Value',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_setting_key` (`setting_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='System Configurations';
INSERT INTO `system_settings` (`setting_key`, `setting_value`) VALUES
('platform_logo', 'upload/assets/logo.png'),
('default_avatar', 'upload/assets/default_avatar.png'),
('system_version', 'v2.1.0');