OLT项目总结
项目背景
项目起因
近期承接了一个0NT项目开发工作因甲方原来的ONT设备管理平台操作繁忙,并会操作的人员并不多,对于维护人员工作量大,并无法保证第一时间进行处理
项目目标
通过主流的技术实现一个用于维护ONT设备的平台,并对ONT设备的注册和注销进行可视化操作,降低维护成本,方便运维人员工作
系统管理员可以管理用户和查看系统的日志信息进行分析
预期成果
为甲方提供一个用于维护ONT设备的平台,提高工作效率。
工作内容和任务
项目采用主流开发模式,前后端分离,前端主要页面的展示和功能处理,后端负责数据的处理,从甲方原来的平台获取ONT设备数据,并且进行序列化返回给前端展示
接口文档
前后端需要共同定义接口文档,包括接口的请求方式、请求参数、返回参数等信息
前端
技术栈
技术 | 说明 | 官网 |
---|---|---|
vue | Web前端框架 | https://cn.vuejs.org/ |
vite | 前端构建工具 | http://www.vitejs.net/ |
vue-router | Vue.js的官方路由 | https://router.vuejs.org/zh/ |
pinia | Vue.js状态管理库 | https://pinia.vuejs.org/zh/ |
axios | HTTP请求库 | http://www.axios-js.com/zh-cn/docs/ |
Ant Design of Vue | ui组件库 | https://antdv.com/components/overview-cn |
yarn | JavaScript软件包管理器 | https://www.yarnpkg.cn/ |
less | css预处理器 | https://less.bootcss.com/ |
ESLint | JavaScript 代码检查工具 | http://eslint.cn/ |
ES modules | 一种标准化的模块化方案 | https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules |
阶段一
根据用户需求设计系统界面同时兼容pc端和移动端,方便工作人员进行维护工作。
阶段二
- 完成系统登录,页面的权限控制
- 搭建模拟数据,用于后端没有进行接口编写时,完成页面的编写
- 完成用户管理,角色管理,日志管理,ONT设备管理页面展示
- 优化系统的页面,兼容移动端
阶段三
对系统的页面和业务流程进行单元测试,准备和后端进行数据对接
后端
技术栈
技术 | 说明 | 官网 |
---|---|---|
SpringBoot | Web应用开发框架 | https://spring.io/projects/spring-boot |
SpringSecurity | 认证和授权框架 | https://spring.io/projects/spring-security |
MyBatisPlus | ORM框架 | https://baomidou.com/ |
Mysql | 数据存储 | https://www.mysql.com/ |
Redis | 数据缓存 | https://redis.io/ |
Lombok | Java语言增强库 | https://github.com/rzwitserloot/lombok |
Spring Validation | 参数验证 | https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#validation |
JWT | JWT登录支持 | https://github.com/jwtk/jjwt |
FastJson | JSON序列化 | https://github.com/alibaba/fastjson |
开发模式
该项目后端采用主流开发模式分别为
- 表现层:主要功能是显示数据和接受传输用户的数据,可以为网站的系统运行提供交互式操作界面,表现层的应用方式比较常见,例如Windows窗体和Web页面。
- 控制层:控制器作用于模型和视图上。它控制数据流向模型对象,并在数据变化时更新视图。它使视图与模型分离开。
- 逻辑层:将用户的输入信息进行甄别处理,分别保存,建立新的数据存储方式。并实现具体的业务逻辑
- DAO层:操作数据(数据库或者文本文件等)的操作层,为业务逻辑层或控制层提供数据服务
阶段一
梳理用户的需求,分析业务流程,设计数据库
阶段二
根据用户的需求进行开发项目
统一响应
封装统一的响应对象,区分状态码和信息,以便前端处理
DTO全局参数验证
在该项目控制层的参数验证,我们采用DTO实体进行验证,用 Spring Validation
提供的注解进行参数验证,并如果参数验证失败,抛出异常,在全局异常中进行捕捉序列化到前端,具体实现流程参考:使用 Spring Boot 和 DTO 进行数据验证
全局异常处理
一般我们不会将错误信息返回前端,自己去try catch
捕获异常,但有个问题:每个方法都这样捕获异常,肯定是不合适,这是我们就需要全局的异常处理了。
使用@ControllerAdvice
+@ExceptionHandler
注解实现全局异常处理
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* DTO参数异常验证处理
* @param e 异常类
* @return 错误信息
*/
@ExceptionHandler(BindException.class)
public ResponseResult methodArgumentNotValidException(BindException e) {
List<ObjectError> allErrors = e.getBindingResult().getAllErrors();
List<String> messages =
allErrors.stream()
.map(DefaultMessageSourceResolvable::getDefaultMessage)
.collect(Collectors.toList());
return ResponseResult.errorResult(400,messages.get(0));
}
/**
* 服务器未知异常
*/
@ExceptionHandler(Exception.class)
public ResponseResult doException(Exception e) {
return ResponseResult.errorResult(AppHttpCodeEnum.SYSTEM_ERROR);
}
}
系统权限认证
系统权限认证我们使用 Spring Security
来进行控制,重写 SpringSecurity
的默认验证方式,采用 JWT 来进行对接口的权限认证
在项目中我们采用 jjwt 进行生成token和解密token,具体生成和解密流程: Java实现JSON Web Token(JWT)的生成、解码和验证
也可以所有工具类进行生成和解密
/**
* JWT工具类
*/
public class JwtUtil {
//有效期为
public static final Long JWT_TTL = 60 * 60 *1000L;// 60 * 60 *1000 一个小时
//设置秘钥明文
public static final String JWT_KEY = "key";
public static String getUUID(){
String token = UUID.randomUUID().toString().replaceAll("-", "");
return token;
}
/**
* 生成jtw
* @param subject token中要存放的数据(json格式)
* @return
*/
public static String createJWT(String subject) {
JwtBuilder builder = getJwtBuilder(subject, null, getUUID());// 设置过期时间
return builder.compact();
}
/**
* 生成jtw
* @param subject token中要存放的数据(json格式)
* @param ttlMillis token超时时间
* @return
*/
public static String createJWT(String subject, Long ttlMillis) {
JwtBuilder builder = getJwtBuilder(subject, ttlMillis, getUUID());// 设置过期时间
return builder.compact();
}
private static JwtBuilder getJwtBuilder(String subject, Long ttlMillis, String uuid) {
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
SecretKey secretKey = generalKey();
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
if(ttlMillis==null){
ttlMillis=JwtUtil.JWT_TTL;
}
long expMillis = nowMillis + ttlMillis;
Date expDate = new Date(expMillis);
return Jwts.builder()
.setId(uuid) //唯一的ID
.setSubject(subject) // 主题 可以是JSON数据
.setIssuer("codexiaoke") // 签发者
.setIssuedAt(now) // 签发时间
.signWith(signatureAlgorithm, secretKey) //使用HS256对称加密算法签名, 第二个参数为秘钥
.setExpiration(expDate);
}
/**
* 创建token
* @param id
* @param subject
* @param ttlMillis
* @return
*/
public static String createJWT(String id, String subject, Long ttlMillis) {
JwtBuilder builder = getJwtBuilder(subject, ttlMillis, id);// 设置过期时间
return builder.compact();
}
/**
* 生成加密后的秘钥 secretKey
* @return
*/
public static SecretKey generalKey() {
byte[] encodedKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);
SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
return key;
}
/**
* 解析
*
* @param jwt
* @return
* @throws Exception
*/
public static Claims parseJWT(String jwt) throws Exception {
SecretKey secretKey = generalKey();
return Jwts.parser()
.setSigningKey(secretKey)
.parseClaimsJws(jwt)
.getBody();
}
}
ONT设备的获取流程
ONT设备的数据来源是甲方ONT设备管理平台,要提供HTTP进行获取数据,所有我们要封装一个HTTP请求工具类,可以在SpringBoot项目中发送HTTP请求
HTTP响应实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class HttpResponseResult {
int code;
String msg;
Object data;
}
分页VO
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PageVo {
private List rows;
private Long total;
}
HTTP工具类
public class HttpUtil {
private static final RestTemplate restTemplate = new RestTemplate();
private static final HttpHeaders headers = new HttpHeaders();
/**
* Get请求
* @param url 地址
* @param responseType 返回类型
* @return List集合
*/
public static <T> PageVo getAll(String url, Class<T> responseType) {
setHeaders();
HttpEntity<?> entity = new HttpEntity<>(headers);
ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
HttpResponseResult body = getBody(responseEntity);
if(body == null) {
return null;
}
PageVo page = JSON.parseObject(String.valueOf(body.getData()), PageVo.class);
List<T> list = JSON.parseArray(page.getRows().toString(), responseType);
return new PageVo(list,page.getTotal());
}
/**
* Get请求
* @param url 地址
* @param responseType 返回类型
* @return List集合
*/
public static <T> T get(String url, Class<T> responseType) {
setHeaders();
HttpEntity<?> entity = new HttpEntity<>(headers);
ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, entity, String.class);
HttpResponseResult body = getBody(responseEntity);
if(body == null) {
return null;
}
return JSON.parseObject(String.valueOf(body.getData()), responseType);
}
/**
* Post 请求
* @param url 地址
* @param request 请求参数
* @param responseType 返回类型
* @return List集合
*/
public static <T> T post(String url, Object request, Class<T> responseType) {
setHeaders();
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
headers.setContentType(MediaType.APPLICATION_JSON);
String body = JSON.toJSONString(request);
HttpEntity<String> entity = new HttpEntity<>(body, headers);
ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.POST, entity, String.class);
HttpResponseResult responseResult = getBody(responseEntity);
if(responseResult == null) {
return null;
}
return JSON.parseObject(String.valueOf(responseResult.getData()), responseType);
}
/**
* 设置请求头
*/
private static void setHeaders() {
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
}
/**
* 转换body为JSON
*/
private static HttpResponseResult getBody(ResponseEntity<String> responseEntity) {
String body = responseEntity.getBody();
HttpResponseResult httpResponseResult = JSON.parseObject(body, HttpResponseResult.class);
assert httpResponseResult != null;
if(httpResponseResult.getCode() != 200) {
return null;
}
return httpResponseResult;
}
}
阶段三
根据甲方的需求,编写系统的业务系统完成
1. 用户管理
在用户管理完成用户的增删改查和用户的角色分配
2. 角色管理
在角色管理,完成角色的增改查,角色的权限分配
3. ONT设备管理
在 ONT设备管理,完成ONT设备增(注册ONT)删(注销ONT)查
4. 日志管理
完成系统的日志捕捉和日志的条件筛选
阶段四
对系统编写的接口进行单元测试,保证系统的稳定性
前后连调
- 前端调用接口:前端根据接口文档,使用Ajax等方式调用后端接口,并且将返回的数据进行处理。
- 联调测试:前后端根据接口文档进行接口联调测试,确认接口的请求和返回数据是否正确。
- 接口调试:如果在联调测试中发现问题,需要进行接口调试,排查问题并解决。
- 完成联调:当所有接口都能够正常调用并返回正确的数据时,联调结束,前后端可以开始进行集成测试和验收测试
总结
通过该项目的实施,成功为甲方建立了一个用于维护ONT设备的平台,提高了工作效率,降低了维护成本。前端和后端团队采用主流技术栈开发,保证了系统的稳定性和可扩展性。同时,通过单元测试和与后端的数据对接,保证了系统的质量和功能完整性。总而言之,OLT项目的顺利完成为甲方提供了一个方便维护ONT设备的平台,实现了维护成本的降低和工作效率的提高,为甲方的运维工作带来了积极的影响。
项目不足
- 用户体验不佳:尽管我们设计了可视化操作界面,但仍然可能存在一些用户体验上的问题。可能需要更多的用户反馈和测试来改进界面的易用性和用户满意度。
- 功能不完善:由于项目周期或其他限制,可能存在某些功能未能完全实现或存在一些功能上的缺陷。这可能会影响用户的实际操作和系统的整体功能性能。
- 缺乏系统的扩展性:在项目开发中,可能没有充分考虑到系统的扩展性和未来的需求变化。这可能导致系统在后续需求变更或功能扩展时存在一定的局限性。
- 安全性风险:在项目中,安全性可能没有得到充分的重视。例如,在系统权限认证和数据传输过程中可能存在潜在的安全风险。需要进一步的安全性评估和增强措施来保护系统和用户的数据安全。
措施
- 收集用户反馈并持续改进:与用户密切合作,收集他们的反馈意见,并持续改进系统的用户体验。通过用户调研和用户测试等方式,深入了解用户需求和痛点,优化系统界面和功能。
- 进行系统功能完善和修复缺陷:及时对项目中存在的功能缺陷进行修复,并根据用户反馈和实际需求进行功能的扩展和改进。确保系统的功能完整性和稳定性。
- 考虑系统的可扩展性和未来需求:在系统设计和开发过程中,充分考虑系统的可扩展性和未来的需求变化。采用合适的架构和设计模式,确保系统能够方便地进行功能扩展和升级。
- 加强系统的安全性保护:进行系统的安全性评估,发现和解决潜在的安全风险。采用合适的安全措施,例如加密传输、访问控制、漏洞修复等,保护系统和用户的数据安全。
通过以上的改进和优化,可以进一步提升OLT项目的质量和用户满意度,满足甲方的需求,并为项目的后续发展打下良好的基础。
个人提升
完成OLT管理项目对于个人的提升是显而易见的。首先,通过参与项目,我在技术方面得到了巨大的提升。作为后端开发程序员,我需要设计数据库、开发API以及实现业务逻辑等任务,这使我深入了解了后端开发领域,并扩展了我的技术广度。此外,与其他技术栈的协同工作也让我能够与前端开发和网络设备配置等领域的专业人士合作,提升了我在跨领域协作方面的能力。
其次,项目还培养了我的解决问题能力。在项目中,我面临各种技术和业务挑战,需要运用自己的知识和经验解决问题。通过不断克服这些挑战,我提高了自己的问题分析和调试能力,并学会了提出有效的解决方案。
此外,项目的完成也提升了我的项目管理和协作能力。我需要制定项目计划、安排任务,并与团队成员和相关部门进行有效的沟通。这些经验锻炼了我的项目管理技巧,增强了团队合作和沟通的能力。
最后,通过完成OLT管理项目,我积累了宝贵的实践经验和案例。这使我能够在求职或者接受客户委托时展示自己的能力和经验,提高了我的竞争力,并为未来的职业发展打下了坚实的基础。
总而言之,OLT管理项目对个人的提升是多方面的,包括技术能力、问题解决能力、项目管理和协作能力的提升,以及实践经验和案例的积累。这些提升将为我的职业发展带来长远的影响。