### 概况 - 系统采用 maven + springboot + beetl 开发 - 文件结构按规范的 maven-archetype-webapp 原型构建 ### 代码解析 - `pom.xml` 中以资源显示声明让工程能够直接以maven命令打包 ``` ${artifactId}-${version} src/main/java src/main/resources src/main/webapp ``` - web 页面资源文件放在 `src/main/webapp` 中 > 由于在开发时并没有完全的按规范将 页面文件和页面引用文件(js/css/img/png 等)分割开,\ > 以致无法用通配符区分请求路径或资源路径,如果统一拦截所有则会视资源为请求以至样式等失效\ > 这里统一用 aop 拦截,建议 webapp 中将资源文件统一放在一个目录,和servlet 请求路径严格区分\ > 因为 请求是 restful 风格请求所以会造成 资源文件和请求 路径的混淆 - redis 序列化和反序列化 - redis 中用 fastJson 代替序列化工具 - 规范采用对象序列化为字符串存储,取出字符串并反序列化为对象使用 - 用字符串保存对象,可以避免反序列化时的类转换问题 - 范例: - 序列化 ``` public void saveEntityToJson(String key, Object obj, long expire, TimeUnit timeUnit) { String value = JSONObject.toJSONString(obj); stringRedisTemplate.opsForValue().set(key, value); if (expire > 0) { stringRedisTemplate.expire(key, expire, timeUnit); } } ``` - 反序列化 ``` public T getEntity(String key, TypeReference typeReference) { String json = stringRedisTemplate.opsForValue().get(key); if (null == json) { return null; } T result = JSONObject.parseObject(json, typeReference); return result; } ``` - 远程请求 RestfulTemplate - 注意 - 概述 - 由于 openapi 的验证和请求体相关,所以应注意以下几点: 1. 生成 sign 时所使用的对象序列化工具 A 2. restfulTemplate 请求的参数序列化工具 B 3. openapi 中接收信息序列化工具 C > 上面提到的 A,B,C 应该是一致的,因为其中一个不一样,之后的验证或生成的结果都不一样\ > 比如,A 为标准序列化 生成了 sign B 在序列化参数时有 空格或换行制表符\ > 那么 C 在接收到参数时是有特殊字符的,在生成签名验证就不能一致(因为形成 sign 过程中序列化请求体可能有`\t或\n或空格等,而请求工具序列化后却没有以致不一致而签名验证失败`),重要!!重要!! - 代码: - 此处屏蔽掉了特征输出,避免了制表换行符的出现 ``` @Bean public RestTemplate getRestful() { FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter(); FastJsonConfig fastJsonConfig = new FastJsonConfig(); // fastJsonConfig.setSerializerFeatures(); fastConverter.setFastJsonConfig(fastJsonConfig); fastConverter.setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_JSON_UTF8)); RestTemplate restTemplate = new RestTemplateBuilder() .additionalMessageConverters(fastConverter) .build(); return restTemplate; } ``` - AOP 拦截,多重逻辑处理 - 拦截了所有的控制层 ``` @Around("execution(public * com.zzwtec.third.action..*.*(..))") @Autowired private WorkScheduling workScheduling; @Around("execution(public * com.zzwtec.third.action..*.*(..))") public Object handleBase(ProceedingJoinPoint pjp) { Object result; try { workScheduling.baseBefore(pjp); String timeOutUrl = workScheduling.checkTimeOut(); if (null != timeOutUrl) { return timeOutUrl; } result = pjp.proceed(); } catch (BizException e) { logger.warn(e.getMessage()); workScheduling.handleError(e); return null; } catch (Throwable e) { logger.error(e.getMessage(), e.getCause()); workScheduling.handleError(null); return null; } finally { workScheduling.baseFinally(pjp); } return result; } ``` - 由`com.zzwtec.third.aop.aspect.work.WorkScheduling`统一调度拦截逻辑 - 由`com.zzwtec.third.aop.aspect.work.WorkAbstract`落实逻辑细节 - 登录超时 - 固定地址放行 - 请求id处理 - 其它 - com.zzwtec.third.common.OpenApiUrl 从配置文件中映射 openapi 的域名和相应的业务地址 - 配置的 redis 是单结点单服务形式(非集群) ## 关于打包 - 概述 1. 总后台由管理员填写相应的配置信息,后台用传入的信息结合 beetl 模板生成对相应的占位符进行替换,生成文件配置文件 - 有两个配置文件,一个是项目配置文件,一个是微信配置文件 - 配置文件名每次生成唯一 - 文件放置位置是在自动生成的目录里 - 目录名在指定固定目录下,且名字唯一 - 目录名根 项目配置文件名一致 - 以此避免多线程操作的互扰 2. 把生成的配置文件在指定的目录里的动态生成的目录下:即 `/固定目录/动态生成目录/` 3. 紧接着后台调用脚本,传入两个参数(关于动态生成的目录名,和微信配置名) 4. 脚本返回 true 或 false 表示打包成功或失败 5. 再读取生成的 jar 文件存放到 ftp,返回 web 下载地址 6. 如果用户点击下载,根据下载地址下载文件,后台删除相应的文件 - 目录地址 - 命令 `sh 脚本路径全名 动态项目名 微信配置文件名` - /opt/template/ # 项目源码存放地址 - /opt/template/config/ # 配置文件模板存放地址 ( 脚本不管此目录) - /opt/template/generate_config/动态项目名/动态项目名.yml # 生成的配置文件 - /opt/template/generate_config/动态项目名/微信配置文件名.txt # 微信的配置文件 - /opt/template/company/动态项目名/target/动态项目名.jar # 打包好后存放的 动态项目名.jar 的位置