会员系统Java端接口交接文档
Hzqi_110 editou esta página 4 anos atrás

会员系统Java端接口交接文档

GIT分支说明

目前在192.168.20.122:3000上的项目是MemberCenter_DW,一共有两个分支,其实大部分功能都是本人开发的,剩余一些模块是祖传代码,没作过多修改,本来也没文档留下来。

  • master 正式在服务器中使用的分支代码
  • develop 一些额外的功能的开发代码,目前并没有投入使用

master

主要是用的代码,里面有很多模块,比较复杂,后面详细说明

develop

主要内容和master相同,主要是多了开发动态传递查询参数来实现查询代码,同时编写了一个小模块,用于简化单表CRUD逻辑,仅需要编写小量代码即可,CRUD逻辑均已在抽象层已经做好实现,该模块在member-web-common中,后面说明。

会员系统

会员系统是用于管理公司的会员,同时与大数据端相连接,用于展示离线分析的结果。因为这个系统成因复杂,后端是由两种语言语言实现,dotnet和Java,我在到职时是Java程序员,而因为历史遗留问题,这个项目一开始是使用dotnet实现的,而后面也有计划迁移到Java中,所以我这边就使用Java开发新功能和迁移部分老代码。

目前一共有6个模块:

  • member-common 通用模块,部分祖传代码
  • member-services 主要业务功能模块
  • member-services-models 业务功能的映射对象、DAO层
  • member-web 项目入口模块,spring 通用配置
  • member-web-common web端通用模块(由于member-common模块代码结构复杂)
  • member-weixin-service 微信端功能

member-common

开始了,这个模块因为历史遗留问题,原来有个旧项目是「元数据管理」,这块是用于管理数据库表的信息的。而这个功能其实是使用kettle的依赖包,但是呢,这个kettle的依赖包的功能也不满足,所以要去修改它依赖包的代码,而这个修改却又是直接导入了依赖包,然后又在本项目代码中重新创建一个依赖包中同名的类型,没有运用继承等方式,而是想要直接覆盖。

由于kettle的依赖在maven仓库中没有找到,依赖十分不好整理,所以我就直接把要用的kettle依赖在maven中从本地导入。而因为不好重构,上级也没有给到什么文档,本人就把整个历史项目代码搬过来使用。

需要注意的一点是,给模块在启动时会报错kettle的错误,我在com.liangjian.dataplatform.member.startup.KettleDatabaseRunner中添加了spring启动时的处理,如果后期在kettle方面出了问题,可以尝试从这里开始排查。(至于为什么这个模块会这么乱,可能已经失传了)

member-services-models

业务模块的映射对象和dao层。需要注意的是,者整个项目中使用了非常多的数据源,而dao层使用的是JPA,因此在spring配置中配置了多数据源的配置,这些不同的数据源所对应的映射对象在com.liangjian.dataplatform.member.entity.po下,通过不同的包来区分。

另外,这些映射对象的id的值是通过雪花算法来实现生成的,代码在com.liangjian.dataplatform.member.entity.genernator中。

下面是表格说明不同的映射对象的说明,只会列出由本人实现的部分,非本人实现的部分我可能也不太了解。

目录 名称 说明
po/elasticsearch UserGrowthLog 在ES中的,用户成长值日志信息
po/elasticsearch UserPointLog 在ES中的,用户积分日志信息
po/primary FeatureInfo 前端图表的配置信息
po/primary FeatureLog 前端图表的变更日志信息(人工输入)
po/primary JobInfo 标签任务
po/primary LayoutConfig 前端动态页面的配置信息
po/primary MetaCore 元数据管理的元数据存储结构
po/primary MetaRel 元数据管理的元数据关系结构
po/primary SqlEntity SQL查询配置对象,前端用于动态自行配置的sql
po/primary TagRule 标签规则
po/secondary UserLevelDesc 用户等级描述
po/secondary UserLevelUnit 用于等级实例
po/secondary UserPointDesc 用户解纷描述
po/secondary WeixinUserInfo 微信用户信息关联
po/tertiary OrderProducts 商品信息
po/tertiary Orders 订单信息
po/tertiary ProductImages 商品图片
po/tertiary ProductSamll 商品简略信息
vo TransportInfo 物流信息
vo TransportTrace 物流详细信息

member-services

主要的核心功能。

权限控制

权限控制的配置在PermissionConfg中,目前是通过配置文件application.yaml中的runOnProduct字段控制,当这个字段不存在或为false时,不启动权限控制。由于主要的用于权限控制的token信息、后台登录控制等位于dotnet端,所以要先通过接口去后去token信息再进行权限控制。由于本项目的接口都是restful的接口,也就是对于单表会有GET POST PUT DELETE 来对应 查、新增、更新和删除。

sequenceDiagram
Client ->>+ Server: 请求访问,请求的信息
Server ->> dotnetServer: 通过接口获取token字符串
dotnetServer -->> Server: 返回token字符串
Server ->> Server: 该用户是否有权限访问
Server -->>- Client: 响应或拒绝

查看图片

元数据管理

这个功能比较复杂,其实是利用kettle依赖包给实现一个表元数据管理。

先画一下类型的结构设计:

graph BT
subgraph 通过反向工程获取
A2[DataNode: 抽象的数据节点]
B2[MetaProcedure: 元数据 反向工程 存储过程] --> A2
C2[MetaSchema: 元数据 反向工程 Schema] --> A2
D2[MetaTable: 元数据 反向工程 表] --> A2
E2[MetaView: 元数据 反向工程 视图] --> A2
end
subgraph 直接存储记录的
A[DataNode: 抽象的数据节点]
B[MetaCoreRoot: 元数据根] --> A
C[MetaCorePackage: 元数据目录] --> A
D[MetaDataSource: 元数据 数据源] --> A
E[PDM: 元数据 表] --> A
F[PDMColumn: 元数据 表字段] --> A
G[MetaTable: 元数据 反向工程 表] --> A

B1[MetaCorePackageTransfer] --- B
D1[MetaDataSourceTransfer] --- D
E1[PDMTransfer] --- E
F1[PDMColumnTransfer] --- F
H[DataNodeTransfer: 抽象转换器,将抽象的数据库结构转成内存结构] --- B1
H --- D1
H --- E1
H --- F1
I[DataNodeTransferService: spring组件,转换器] --- H
end

查看图片

元数据的逻辑是:对于元数据而言,数据库存储的结构是MetaCore,但是在业务层是有不同的类型的,因此需要将存储层的MetaCore根据不同的标示转换成不同的数据结构,如PDM PDMColumn MetaCorePacakge MetaCoreRoot等。转换的逻辑使用了类型责任链的模式,如下:

graph TB
start --> sp1[从数据库中去po]
sp1 --> sp2[DataNodeTransferService.transfer]
sp2 --> sp3[遍历DataNodeTransferService]
sp3 --> cond[判断Transfer和MetaData的标示]
cond --> |true| sp4[key匹配时直接返回xxTransfer.transfer]
cond --> |all false| sp5[返回初始化无意义的Vo]
sp4 --> finish
sp5 --> finish

查看图片

而在Service层获取元数据的逻辑:首先判断是否需要进行反向工程的,如果需要反向工程的,需要调用kettle的接口,若不需要,直接以上述方式转换MetaCore即可:

graph TB
start --> sp1[queryById获取子节点]
sp1 --> sp2[judgeAndGetChildren 先执行判断是否需要反向工程]
sp2 --> cond{是否需要反向工程}
cond --> |true| sp3[MetaReverseService 获取节点逻辑]
sp3 --> sp4[直接返回结果]
sp4 --> finish[按照其父类DataNode返回结果]
cond --> |false| sp5[从库中取出节点列表]
sp5 --> sp6[通过DataNodeTransfer转换Vo结构]
sp6 --> sp7[返回结果]
sp7 --> finish

查看图片

标签规则管理

标签规则功能主要分两块,一块是标签规则的管理部分,一块是标签规则调度执行部分,我复杂的是前者。标签规则是规定好标签规则的结构,然后执行时是通过这个标签规则生成查询sql。标签规则结构如下:

graph LR
A[TagRule:数据库po] --> B[TagRuleVo]
B --- C[ConditionNode:条件节点]
C --- D[LogicTree:逻辑树]

查看图片

重点在于ConditionNodeLogicTree的结构,用于构建可以不断递归的结构:

BNF 范式:
CondtitionNode(条件节点) ::= LogicTree, Map<String,String> result
LogicTree(逻辑树) ::= BinOperation , LogicNode*
BinOperation(二元运算) ::= BinOperaTye, PDMColumn, String
LogicNode(逻辑节点) ::= SimpleLogicNode | BracketsLogicNode
SimpleLogicNode(单逻辑节点) ::= LogicType, BinOperation
BracketsLogicNode(多逻辑节点) ::= LogicType, BinOperation, LogicNode*
BinOperaType(二元运算符) ::= EQ | LE | GE | NE | CONTAINS | BELONG
LogicType(逻辑类型符) ::= AND | OR | NOT | ANDNEW | ORNEW

ConditionNode生成SQL的逻辑是,每一个BinOperation用来表示查询的单个条件,每一个LogicNode是用来表示条件与条件之间的关系,最后组装起来就是一个完整的查询条件:

对于条件:a = 1 and b = 2 or (c = 3 and (d = 4 or e = 5))
+-----------+
|LogicTree: | [a = 1] *
|node: a = 1|
+----+------+  and [b = 2]
     |     +----------------+
     |     |SimpleLogicNode:| 
     |     |type:and        |
     +-----+node:b = 2      |
     |     +----------------+
     |        or ([c = 3] (*))
     |     +------------------+
     |     |BracketsLogicNode:| 
     |     |type:OrNew        |
     +-----+node: c = 3       |
           +--------+---------+
                    |     and ([d = 4] (*))
                    |   +------------------+
                    |   |BracketsLogicNode:| 
                    +---+type:AndNew       |
                        |node:d = 4        |
                        +---------+--------+
                                  |    and e = 5
                                  |  +----------------+
                                  |  |SimpleLogicNode:|
                                  +--+type:or         |
                                     |node: e = 5     |
                                     +----------------+

功能管理

功能管理是在管理前端图表的,前端的图表是和动态前端页面设计在一起的。功能管理是在前端动态导入superset的图表,当然superset的设计是要在superset里编辑,然后将编辑的export url导入到功能管理里管理。这里没有特别的说明,仅仅是规定好类型结构后CRUD。

动态页面

前端动态页面功能,这个功能先说一下背景:上级希望通过前端页面的拖拽来实现动态编辑页面,而不需要写代码,因此这块存储的是一个前端模块react-grid-layout所使用的布局信息。这里没有特别的说明,仅仅是规定好类型结构后CRUD。

SQL配置管理

这块是用于管理前端编写管理的sql,这些写的sql会用于在功能管理动态页面中生成的图表而使用。在动态页面中,要拖拽编辑到一些图表例如「各地门店销售情况」,「昨天各网店销售情况」等,因为拖拽编辑时去写sql会很麻烦和不安全,所以在这里管理好,在拖拽时直接输入SQL配置的id即可。这里没有特别的说明,仅仅是规定好类型结构后CRUD。

会员积分、成长值管理

这块就与业务相关度很高,是在「积分商城」中管理会员的积分和成长值的,而「积分商城」是在微信端中的应用的后台管理,后面会说明到。

前端中的「积分商城」-「会员积分」的列表是从ES中查询出的列表,而点击每一个会员右侧的操作按钮就是去操作UserPointEsUserGrowthEs表的CRUD操作。

等级管理

等级管理是微信端的「积分商城」的等级的管理后台,前端由两个tab管理,多个等级的设置和等级的描述。在保存的时候其实是两个表结构UserLevelDesc UserLevelUnit共同处理,其中没什么逻辑。

DynamicParam 动态查询参数功能

这块内容本身是在分支develop中的功能,本意是通过一个结构话的结构来描述CRUD中的查询,有很多个条件的时候自动适配,而不需要特地写查询条件接口,这部分功能并没有实际使用,后期可能不会使用,所以感兴趣的时候可以研究看看。这块的价值在于抽象化查询条件,减少特殊化查询条件接口代码的编写。

这块实际的在member-web-common中,但是spring的配置端在本模块,先简单介绍。

下面仅给出这个结构的设计:

BNF 范式
DynamicParams ::= DynamicFilter*, (分页信息) Integer, Integer, (排序信息) SortDirection, String
DynamicFilter ::= field, OperaType, value
OperaType ::= Equal | NotEqual | Less | LessEqual | Greater | GreateEqual | Contains | StartWith | EndWith
SortDirection ::= ASC | DESC

表格说明

下面由表格来说明一下各个目录和代码。

目录 名称 说明
algorithm * 代码已作废
config CorsConfig spring-boot的Cors配置
config DataSourceConfig spring多数据源Bean配置
config DB*Config 多数据源的指向配置
config DBSubtablesConfig 数据源的分库分表配置,现已废用
config DuridConfig durid数据源配置
config DynamicParamConfig 用于解析get参数成动态查询的配置的拦截器
config ElasticsearchConfig Elasticsearch配置
config PermissionConfig 权限控制的拦截器配置
config SwaggerConfig swagger文档配置
constant DatabaseUrls 常用数据的url
constant DataNodeConstant 元数据DataNode的类型常量
constant MetaCoreConstant 元数据的标示常量
controller BirdGridController 前端BirdGrid脚手架的控制层,目前好像没用上
controller FeatureInfoController 功能管理控制层
controller ImportSupersetController 导入superset图表的控制层
controller LayoutConfigController 动态页面控制层
controller MetaCoreController 元数据管理控制层
controller SqlApiController Clickhouse查询数据的控制层
controller SqlEntityController SQL配置管理控制层
controller SqlExportController Clickhouce查询数据并导出xslx下载
controller TagRuleController 标签规则控制器
controller UserGrowthLogController 用户成长值控制层
controller UserLevelSettingController 用户等级管理控制层
controller UserPointLogController 用户积分控制层
controller WeixinUserInfoController 微信用户积分、成长值控制层
dao/elasticsearch *Repostitory 用户积分、成长值的ES的DAO层
dao/* * 这块结合member-services-models来看,都是一一对应的DAO层
entity/pojo BirdGirdFilter BirdGird的过滤器类型
entity/pojo DataBaseInfo 元数据中的数据库信息类型
entity/pojo KeyValuePair 一个kv对类型
entity/pojo PkAndNullable 元数据中的内部定义数据的信息类型
entity/token DynamicParamGetter 动态参数的threadlocal
entity/vo/datanodes * 元数据逻辑所需要用的类型结构
entity/vo/detailvo * 元数据逻辑里Vo的逻辑的类型结构
entity/vo/request * 一些restful入参参数类型结构
entity/vo/response BirdGirdTableResponse* 前端Birdgird的响应结构
entity/vo/tagrules * 标签规则逻辑所需要的类型结构
entity/vo LayoutConfigVo 动态页面的VO结构
entity/vo LayoutInfo 动态页面的VO的内部结构
entity/vo MetaRelVo 元数据的关系VO
entity/vo TagRuleVo 标签规则的VO结构
interceptor DynamicParamAnalyze 动态查询参数分析器的拦截器
interceptor PermInterceptor 权限控制的拦截器
service DataNodeTransferService 节点转换逻辑层
service IBirdGirdService 前端BirdGird的逻辑层
service IFeatureInfoService 功能管理的逻辑层
service IFeatureLogService 功能管理的日志的逻辑层
service ILayoutConfigService 动态页面的逻辑层
service IMetaCoreCUDService 元数据的增删改的逻辑层
service IMetaCoreQueryService 元数据的查询的逻辑层
service IPointGrowthRuleService 会员积分成长值逻辑层
service ISqlEntityService SQL配置管理的逻辑层
service ISqlExportService clickhouse查询导出逻辑层
service ITagRuleService 标签规则逻辑层
service ITagRuleSqlService 标签规则转换sql逻辑层
service IUserGrowthLogService 会员成长值逻辑层
service IUserLevelService 积分商城等级管理逻辑层
service IUserPointLogService 会员积分逻辑层
service IWeixinUserInfoQueryService 微信会员信息逻辑层

member-web-common

这个模块是为了让一些通用的模块写在这里,上面有说过,member-common这个模块因为历史遗留原因变得难以整理,所以把一些web通用型模块写在这里。

而目前这个模块并没有什么代码在,只有一个动态查询参数的模块,结构上面已经给出DynamicParam动态查询。

下面就简单说说如何做到动态的查询:因为我们的DAO层用的JPA,而动态查询的这部分已经算是复杂的查询了,所以这里是把DynamicParam转换成Specification<T>,逻辑其实是简单的,就在DynamicParam中有实现:

graph TB
A[DynamicParam] --> B[DynamicFilter]
B --> C[filter.toJpaSpecifition]
C --> |Specifition列表| B
B --> |组合Specifition| A
A --> |page,size,sortDirection| D[完整的Specifition]
D --> E[Dao层]

查看图片

member-web

这个模块其实是整个Web项目的入口,本模块的pom的会指定声明依赖了那些模块。这个模块仅如此。

member-weixin-service

这个模块其实是微信端的「积分商城」的后台管理。功能相对会表简单,有会员的绑定登录,下单积分商品,查看积分、成长值,查看物流等信息。

绑定登录

绑定登录的逻辑是,用户从公众号中点击进入积分商城,然后后台拿到微信的code,后端根据code去获取用户的openidunionid,然后在数据库表中查询,若查询到有此用户就返回登录的用户信息,否则,跳转到绑定。而绑定的逻辑是,用户输入了手机号后,后端将该手机号和随机生成的code放入redis中,过期1分钟,然后后端将该随机code发送短信,后端再作判断:

graph TB
start --> sp1[点击微信公众号按钮]
sp1 --> sp2[微信端带微信code跳转]
sp2 --> sp3[后端根据code获取用户的openid]
sp3 --> cond1{数据库中是否有该用户}
cond1 --> |有| finish[返回登录用户信息]
cond1 --> |无| sp4[跳转到绑定页面]
sp4 --> sp5[用户输入手机号]
sp5 --> sp6[后端发送随机code短信]
sp6 --> sp7[校验,绑定]
sp7 --> finish2[后端重新登录,返回登录用户信息]

查看图片

权限控制(访问控制)

这块的控制大致上也是和会员系统的权限控制是一样的,都是利用jwt来控制。后端拿到客户端传上来的jwt,1、进行key校验,2、进行过期校验。不通过校验的直接视为无法访问,需重新登录(重新进入),而每次通过校验后都会签发新的jwt来保持最新。

查询物流信息

物流的查询逻辑其实很简单,查询物流的接口在别的项目中已经有现成的,这里仅需要去调用该接口然后返回给前端即可。

会员等级控制商品显示

这里的逻辑是这样的,当用户没有登录时,「积分商城」还是能直接显示商品的,只不过都是原始信息。而当用户登录后,用户是有成长值的,每个商品都有对应各个会员等级的优惠。而后台中的会员等级管理就是去管理每个等级对应的成长值。所以在用户登录后将查询出来的商品信息搭配会等级的优惠顶级进行过滤即可。

graph TB
start --> sp1[查询商品连同等级优惠一起]
sp1 --> cond{是否已登陆}
cond --> |是| sp2[对商品的等级优惠进行过滤]
sp2 --> finish[返回分页商品信息]
cond --> |否| sp3[对商品信息的等级优惠给覆盖为空列表]
sp3 --> finish

查看图片

可以看到上面的逻辑是无论是否已登陆都会把等级优惠信息一同全查出来,这个效率其实肯定不好,但是因为是使用了JPA,这块的查询代码上变得简单和无脑,所以缩短开发耗时,直接给查出来后再处理。

表格说明(1)

目录 名称 说明
api MySnsApi 微信通过code获取openid,依赖这块没做好,自己继承实现了个
config WeixinCachingConfig 微信使用的包需要用的缓存配置
config WeixinInterceptorConfig 权限访问控制的配置
controller WeixinController 积分商城主要功能的控制层
controller WeixinIndexController 微信端跳转控制层
controller WeixinRegisterController 积分商城登录绑定控制层
dao/secondary MemIngProductLevelRepository 积分商品等级优惠信息DAO层
dao/secondary MemIngProductPicRepository 积分商品图片信息DAO层
dao/secondary MemIngProductRepository 积分商品信息DAO层
dao/tertiary OrderProductsRepository 正式库中的订单商品的DAO层
dao/tertiary OrderRepository 正式库中的订单的DAO层
entity/token WeixinJwtTokenGetter 权限控制的Jwt中Token的threadlocal
entity/vo/request PhoneSMSInfo 手机号等信息的提交参数
entity/vo/request WeixinMessageInfo 向微信用户发送消息的提交参数
entity/vo/response LevelAndGrowth 用户等级和成长值信息的响应数据
entity/vo WeixinUserInfo 微信用户信息Vo
interceptor WeixinInterceptor 微信用户权限控制拦截器
service IMemIngProductService 积分商品逻辑层
service IOrderProductService 订单查询逻辑层
service IPhoneSMSService 发送短信逻辑层
service IQueryOrderService 查询订单逻辑层
service ITransportQueryService 查询物流信息逻辑层
service IWerxinUserInfoService 微信用户逻辑层

部分配置信息说明

key 值类型 说明
runOnProduct boolean 若开启则开启权限控制
superseturl url字符串 superset的服务端
supersetReplace 字符串,内部以逗号分隔 用于替换由superset端分享过来的url
supersetReplaceAs 字符串 替换superset分享的url的目标值
getTokenUrl url字符串 获取权限token的dotnet服务
jwt-secret 字符串 jwt加密key
clickhouseUrl jdbc url 字符串 clickhouse的服务端
clickhouseUser 字符串 clickhouse用户名
clickhousePassword 字符串 clickhouse密码
jwt.expire-time 数字 jwt过期毫秒数

备注

这个项目仍然涉及到dotnet端的代码,前端和大数据端,有什么问题需要与各个部分沟通。

  • 编写日期:2020-03-17