# 微服务架构的国内航班查询与预订系统 **Repository Path**: Xtreeroot/CSC_FlightPort ## Basic Information - **Project Name**: 微服务架构的国内航班查询与预订系统 - **Description**: 请使用bisher分支 常州信息移动互联应用学生毕业设计 微服务架构的国内航班查询与预订系统---接口 - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2022-04-18 - **Last Updated**: 2023-12-30 ## Categories & Tags **Categories**: Uncategorized **Tags**: 毕业设计 ## README # 微服务架构的国内航班查询与预订系统 ## 工程目录 ## common.common_utils 该目录中存放的是公共处理中心。将经常使用到的或者不作为影响主体业务的工具类做统一管理,再通过引入依赖方式,提供给消费者 **result包:**同一结果返回处理中心 - ResultCode :统一状态码标志 - 成功:Integer SUCCESS = 20001 - 失败:Integer ERROR = 20000 - R:结果返回类 - R类的属性:Boolean success是否成功 Integer code返回的状态码 message返回的信息 Map data = new HashMap() 返回数据 - R类静态方法:public static R ok(){}成功静态方法 public static R error() {}失败静态方法 这里使用静态的目的是方便全局使用 **exceptionhandler包**:异常处理中心 - MyException extends RuntimeException继承异常类使之成为异常类 - Integer code 状态码 - String msg 异常信息 - GlobalExceptionHandler (自定义的异常处理)全局异常处理程序 **ordervo**:订单调用封装类 1. PlanelnfoOrder返回一个PlanelnfoOrder类信息 2. Userinfo Order 返回一个Userinfo Order类信息 **handler**:*@class mybatisPlus属性自动填充*处理中心 - MyMetaObjectHandler > 在我们向数据库插入一条数据的时候,少不了一些向createTime、updateTime此类字段,每次插入的数据都要设置这些个值,很烦,通过实现MetaObjectHandler接口重写insertFill、updateFill方法解决 > > MetaObjectHandler接口是mybatisPlus为我们提供的的一个扩展接口,我们可以利用这个接口在我们插入或者更新数据的时候,为一些字段指定默认值 > > *对应的实体类字段上需要加@TableField(fill = FieldFill.INSERT_UPDATE)* **utils**:工具包使用中心 - Http Client :http请求客户端 - JwtUtils:token的生成与解析器 - MD5: MD5 32位加密方式 - OrderNoUtil: 生成订单号 - RandomUtil:生成4位/6位随机数 - ResponseUtil:自定义json工具类*ResponseUtil*,然后把数据转为json数据【项目中未使用】 - 灰度发布 - Apiversion - ApiVersionHandleMapping - ApiVersionMappingRegister - ApiVersionRequestCondition - BaseConfiguration - 添加一个自定义注解用于标注接口类以及接口方法 - 自定义HandleMapping - 自定义封裝Requestc - 注册自定义的 ApiVersionHandleMappingondition, > 效果图 > > > > - AOP令牌桶 - ``` 不同的接口,不同的流量控制 * map的key为 Limiter.key * RateLimiter 创建具有指定稳定吞吐量的速率限制器,以“每秒许可数”(通常称为QPs,每秒查询数)表示。 * 返回的RateLimitor确保在任何给定的一秒钟内平均每秒发出的许可数不超过permitsPer,持续的请求在每秒钟内平滑分布。 * 当传入请求速率超过permitsPerSecond时,速率限制器将每(1.0/permitsPerSecond)秒释放一个许可。 * 当速率限制器未使用时,将允许爆发高达permitsPerSecond的许可,随后的请求将以稳定的permitsPerSecond速率顺利限制。 * Params:permitsPerSecond—返回的速率限制器的速率,以每秒可用的许可数量来衡量 * 抛出:ILLegalArgumentException-如果permitsPerSecond为负或零 ``` - hystrixRegistrationBean - 统一开启数据通道 - ![](https://guli-parent-tree-root.oss-cn-hangzhou.aliyuncs.com/2022%3A6%3A25%3A1656146424192.png) ## common.service_base **config**:工具配置包 - MybatisPlusConfig : 逻辑删除插件 分页插件 乐观锁插件 - RedisConfig: redis - SwaggerConfig API测试中心 ## infrastructure.apigateway **config**:配置信息 - CorsConfig:处理跨域 - 跨域,是指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对JavaScript实施的安全限制。浏览器从一个域名的网页去请求另一个域名的资源时,出现域名、端口、协议任一不同,都属于跨域。 - filter:过滤器 - AuthGlobalFilter:身份全局认证 - 对特定的api进行处理,禁止普通用户去访问特定资源 起到保护作用 - handler:解决方案 - ErrorHandlerConfig 网关错误流程 覆盖默认的异常处理 - JsonExceptionHandler 异常信息json反馈 异常时用JSON代替HTML异常信息 - 主要依赖 ```xml org.springframework.cloud spring-cloud-starter-gateway ``` - application.yml - 对其他的服务开启转发网关 ```yaml spring: boot: cloud: routes: - id: 需要配置网关的服务名称 uri: lb://需要配置网关的服务名称 predicates: //指定 该服务的某个api接使用gateway服务的资源 - Path=/*/acl/** ``` ## infrastructure.service-admin ### 功能需求 > 统应当具备对自身运行状况的监控及报警能力,并提供相应的可视化界面;需要为非功能需求中的熔断/限流/灰度等能力提供相应的可视化配置界面。 监控能力主要由 Spring Boot Admin来实现: 使用Actuator可以收集应用系统的健康状态、内存、线程、堆栈、配置等信息,比较全面地监控了Spring Boot应用的整个生命周期 使用Spring Boot Admin呈现这些采集到的应用监控数据、性能数据 Spring Boot Admin是一个管理和监控Spring Boot应用程序的开源项目,在对单一应用服务监控的同时也提供了集群监控方案,支持通过eureka、consul、zookeeper等注册中心的方式实现多服务监控与管理。Spring Boot Admin UI部分使用Vue JS将数据展示在前端。 Spring Boot Admin分为服务端(spring-boot-admin-server)和客户端(spring-boot-admin-client)两个组件: spring-boot-admin-server通过采集actuator端点数据显示在spring-boot-admin-ui上,已知的端点几乎都有进行采集。 spring-boot-admin-client是对Actuator的封装,提供应用系统的性能监控数据。此外,还可以通过spring-boot-admin动态切换日志级别、导出日志、导出heapdump、监控各项性能指标等。 Spring Boot Admin服务器端负责收集各个客户的数据。各台客户端配置服务器地址,启动后注册到服务器。服务器不停地请求客户端的信息(通过Actuator接口) 在每个Spring Boot应用程序上增加Spring Boot Admin Client组件。这样每个Spring Boot应用即Admin客户端,Admin服务端通过请求Admin客户端的接口收集所有的Spring Boot应用信息并进行数据呈现,从而实现Spring Boot应用监控。 设置Spring Boot Admin的邮件提醒,需要用到Spring Boot的邮件组件:spring-boot-starter-mail。这里展示邮件报警功能的使用 - 主要依赖 ```xml 控。--> org.springframework.cloud spring-cloud-starter-netflix-hystrix-dashboard 2.2.10.RELEASE nl.devillers spring-boot-admin-hystrix-dashboard 1.0.2 org.springframework.cloud spring-cloud-starter-netflix-hystrix org.springframework.boot spring-boot-starter-actuator org.springframework.boot spring-boot-starter-mail org.springframework.cloud spring-cloud-starter-alibaba-nacos-discovery de.codecentric spring-boot-admin-starter-client 2.2.2 de.codecentric spring-boot-admin-starter-server 2.2.1 ``` - application.yml ```yaml spring: boot: admin: # server端的访问路径 context-path: /admin # client端状态的生命周期,该生命周期内不会更新client状态 monitor: status-lifetime: 100000 # 报警邮箱服务 notify: mail: enabled: true # 接收者 to: 553503639@qq.com # 发送者 from: xu_genyin@163.com ui: title: "国内航班微服务监控及报警" client: # 要注册的server端的url地址。如果要同时在多个server端口注册,则用逗号分隔各个server端的url地址 url: http://localhost:8007/admin instance: # server端获取client信息的路径,默认情况下server通过访问/instances请求来获取到client端的信息。 prefer-ip: true #默认值instances # 获取到元数据的账号密码 使管理端有权限获取客户端端点数据 metadata: user.name: ${spring.boot.admin.client.username} user.password: ${spring..boot.admin.client.password} # 如果server端需要进行认证时,该属性用于配置用户名 username: admin password: 123456 # 是否启用springbootAdmin客户端 enabled: true # 注册连接超时时间 connect-timeout: 50000 # 监控暴露所有端点 management: endpoints: web: exposure: include: '*' exclude: env,beans endpoint: # health端点公开的信息 health: # 详细信息显示给所有用户。health健康检查显示详细信息 show-details: always # 开启shutdown端口 shutdown: enabled: true logfile: # 日志文件存储位置 external-file: ./logs/log_file.log ``` ### 报警能力: ### 系统监控能力: ### 熔断可视化配置界面: ## service.service order ### 功能需求 > 机票预订功能:【必选】至少应当支持对给定日期、航班机票的预订及预订后的机票改期退票功能;应当具备基本的座位库存控制能力,能正确处理多人并发预订机票时的库存扣减事务。【可选】座位价格可动态变化 #### 生成订单 接口:/order/pcorderinfo/createBaseOrder/{planeId} 生成订单的实现: 配置nacos服务发现与服务注册中心 通过feign远程调用需要的服务 通过用户登录存在浏览器head的token获取会员id 再通过会员id获取到会员的全部信息 与远程调用通过航班列表的航班id获取到航班信息 两者生成新的实体类 订单信息 #### 订单支付 接口:/order/paylog/toPay/{type} 具体实现,feign远程调用修改航班信息的方法 具体实现: service.service_plane.mapper ```java @Update( { "UPDATE pc_plane set seating = seating -1 WHERE seating !=0 AND id = #{planeId}"} ) void updateSeatingByIdOrderReduce(@Param("planeId") String planeId ); @Update( "UPDATE pc_plane set seating = seating +1 WHERE id = #{planeId};" ) void updateFlagByIdOrderAdd(@Param("planeId") String planeId); ``` #### 【前端展示】 ## service.service_plane ### 功能需求 > 【必选】至少应当支持在给定日期、航班号或给定日期、出发地、目的地两种组合模式下的航班查询,查询结果中至少应当包含航班的起降时间、剩余座位数、票价。【可选】如能提供按其他有现实需求的条件组合来进行航班查询 #### 航班查询 接口:**/pc/plane/getPlaneTypeCondition/{current}/{limit}** #### 提供中转航班查询、提供往返或联程航班查询 查询条件封装于queryPlaneVo类中 ```java @Data @ApiModel(value = "航班对象", description = "航班对象") public class queryPlaneVo { @ApiModelProperty(value = "出发日期") @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") @DateTimeFormat(pattern = "yyyy-MM-dd") private String startDateTime; @ApiModelProperty(value = "航班号 ") private String flightNumber; @ApiModelProperty(value = "出发城市 ") private String startCity; @ApiModelProperty(value = "到达城市") private String endCity; @TableField(exist = false) @ApiModelProperty(value = "开始价格 ") private String priceStart; @TableField(exist = false) @ApiModelProperty(value = "结束价格") private String priceEnd; @TableField(exist = false) @ApiModelProperty(value = "航班类型") private String type; } ``` #### 给定日期、航班号或给定日期、出发地、目的地两种组合模式下的航班查询 #### 以价格区间来查询航班 ```java // 以价格区间来查询航班 .between(PcPlaneTransfer::getPrice, query.getPriceStart(), query.getPriceEnd()) ``` #### 【前端展示】 ## service.service_ user ### 用户登录 接口:/user/pcuserinfo/login 具体实现:用户传入参数phone 与password 交给后台进行参数效验 将密码加密为MD32位密文 再用数据库进行对比 这里主要使用到了JWT生成Token随机字符串与 MD5加密工具 登录完成后的用户会将自己的信息信息转化为token,前端工程师可通过“通过token获取用户信息”对用户对的信息进行效验 #### JWT实现 使用Jwts.builder()方法 返回一个新的JwtBuilder实例,该实例可以配置,然后用于创建JWT压缩序列化字符串。 主要设置为:头部标题:type:JWT&alg:HS256 alg属性表示签名使用的算法,默认为HMAC SHA256(写为HS256);typ属性表示令牌的类型,JWT令牌统一写为JWT **playload**:subject:scsFp-user expiration:1000 * 60 * 60 * 24; jwt id:id signwith:SignatureAlgorithm.HS256, APP_SECRET 标准中注册的声明(常用) iss(issuer):表示JWT的签发者 sub(subject):表示JWT的所有者 aud(audience):表示JWT的接受者 exp(expiration):一个时间戳,表示JWT的过期时间 nbf(not before):一个时间戳,表示该JWT的生效时间,在这个时间之前验证JWT是会失败的 iat(issued at):一个时间戳,表示这个JWT的签发时间 jti(jwt id):JWT的唯一标识(也常用于redis的键) 公共的声明(可以存放任何信息,通常用Map保存键值对,然后再加入其中) 私有的声明(可以存放签发者和接收者共同定义的信息,不常用) MD5具体实现: ```java public static String encrypt(String strSrc) { try { char hexChars[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; byte[] bytes = strSrc.getBytes(); MessageDigest md = MessageDigest.getInstance("MD5"); md.update(bytes); bytes = md.digest(); int j = bytes.length; char[] chars = new char[j * 2]; int k = 0; for (int i = 0; i < bytes.length; i++) { byte b = bytes[i]; chars[k++] = hexChars[b >>> 4 & 0xf]; chars[k++] = hexChars[b & 0xf]; } return new String(chars); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); throw new RuntimeException("MD5加密出错!!+" + e); } } ``` ### 用户注册 接口:/user/pcuserinfo/regist 用户传入关键参数 code(具体获得code请看service.service_sms) phone password 交给后台进行参数效验 将密码加密为MD32位密文 再存入数据库 这里主要使用到 MD5加密工具(与上同) ### 通过token获取用户信息 接口:/user/pcuserinfo/getUserInfoByToken 具体实现:通过获取浏览器头部不的关键字“token”获取到用户token码 返回用户id 这里是一个反向token的过程 ## service.service_sms ### 短信服务 在我的配置文件中写入自己的配置信息: ```yaml tencent: sms: account: secret_id: xxxxx secret_key: xxxxx sms_sdk_app_id: xxxxx template_id: xxxxx sign_name: xxxxx ``` 设置腾讯短信发送基本配置 腾讯短信网关、区域: ```java private static String URL ="sms.tencentcloudapi.com"; private static final String REGION = "ap-guangzhou"; ``` 从配置文件中注入到基本配置 认证实例化 ```java public SmsClient smsClient(){ // 实例化一个认证对象,入参需要传入腾讯云账户secretId,secretKey, Credential cred = new Credential(SECRET_ID, SECRET_KEY); // 实例化一个http选项,可选的,没有特殊需求可以跳过 HttpProfile httpProfile = new HttpProfile(); httpProfile.setEndpoint(URL); // 实例化一个client选项,可选的,没有特殊需求可以跳过 ClientProfile clientProfile = new ClientProfile(); clientProfile.setHttpProfile(httpProfile); //实例化 SMS 的 client 对象 return new SmsClient(cred, REGION, clientProfile); } ``` 接口:/tencentSms/send/{phone} 具体实现: 先从redis里去获取验证码 如果redis获取不到验证码就到腾讯发送 发送成功后将验证码发到redis里面 设置有效时间3分钟 ## service.service_upload ### 上传服务 接口:/upload/ 在我的配置文件中写入自己的保存路径信息: ```yaml yuan: file: root: ## 定义文件保存的路径 path: /Users/xugenyin/CSC/CSC_FlightPort/service/service_upload/src/main/java/com/treeroot/uploadservice/static/ ``` 将保存在本地的资源映射到文件根路径 WebMvcConfigurer配置类其实Spring内部的一种配置方式,采用JavaBean的形式来代替传统的xml配置文件形式进行针对框架个性化定制,可以自定义一些Handler,Interceptor,ViewResolver,MessageConverter。基于java-based方式的spring mvc配置,需要创建一个配置类并实现WebMvcConfigurer 接口 通过ResourceHandlers实现静态资源的地址映射 **配置ResourceHandlers** 此方法用来专门注册一个`Handler`,来处理静态资源的,例如:图片,js,css等 具体实现: 判断上传的文件是否为空 不为空则返回文件原始名称,截取到原始名称`.`后至\ 作为分类文件夹 如果上传的文件没有该分类目录则创建并将文件保存至此目录 再通过 requst请求获取到端口号文件名变成一个可在网络访问的文件 ## service.service_ address ### 地区管理 接口:/address/list/tree/{provinceCode}/{cityCode} 代码 ```java // 通过省code与城市中省code匹配 查出该省下的市 @Select( "select city.* " + "from pc_ad_province province, pc_ad_city city " + "where (province.code = city.provinceCode) AND province.code =#{provinceCode}" ) List getCity(@Param("provinceCode") String provinceCode); // 通过城市code与镇中省code匹配 查出该市下的镇 @Select( "select town.* " + "from pc_ad_city city ,pc_ad_town town " + "where (city.code = town.cityCode) AND city .code = #{cityCode}" ) List getTown(@Param("cityCode") String cityCode);} ``` 具体实现: 首次加载时返回所有的省份与省份code用于获取用户选择的省份下的市区 # END ------二零二二年六月二十日 #### 起步 Create in 22:52 / 2022/4/18
Author:TreeRoot #### 使用说明 #### 参与贡献 TreeRoot **2022-04-18 21:02** **author:TreeRoot** **2022-12-29 15:24** **author:TreeRoot**