Commit e749d30a authored by 马超's avatar 马超

feat: 从docs仓库迁移到本仓库

parents
# LDP-MCS操作手册
[TOC]
## 1、登录
![登录](progress/1登录.png)
依次输入正确的用户名、密码、验证码,点击登录即可成功登录。
## 2、主界面
![主界面](/progress/2主界面.png)
目前dashboard还没有内容。
## 3、系统维护
系统维护中包含账号管理、组织管理、职位管理、员工管理、管理菜单维护、应用菜单维护、定时任务、定时任务日志等相关信息维护。
### 3.1、账号管理
#### 3.1.1、账号列表
![账号管理列表](/progress/3账号管理列表页.png)
账号列表页面可以进行条件查询、批量删除数据、对单条账号数据的新增、信息修改、删除操作、密码修改等。
#### 3.1.2、条件查询
可以通过名称、状态、用户类型进行**精确查询**,例如查询账号名称为admin的账号:先在名称处输入admin,然后点击查询即可。
#### 3.1.3、批量删除
在列表中勾选要删除的数据,点击列表数据上面的删除按钮,即可实现批量删除。PS:**不能删除当前登录账号,不能删除超级管理员。**
#### 3.1.4、新增账号
![账号新增界面](/progress/4账号新增界面.png)
输入对应的账号信息,点击保存即可。
#### 3.1.5、单条数据修改、删除
点击对应数据后面的修改,修改界面和新增界面类似,修改对应的字段,点击保存即可;
点击删除按钮后,提示是否删除,点击确定即可(**不能删除当前登录账号,不能删除超级管理员账号**);
#### 3.1.6、修改密码
![修改密码](/progress/29修改密码弹窗.png)
点击操作列中的修改密码按钮,输入对应的新密码和管理员密码,即可修改对应账号密码。
### 3.2、组织管理
#### 3.2.1、组织列表
![组织管理列表](/progress/5组织管理列表.png)
组织管理列表支持条件查询、批量删除、对单条数据进行修改和删除。
#### 3.2.2、新增组织
![新增组织](/progress/6新增组织弹窗.png)
输入对应的组织名称、简称、编码等信息,选择上级组织后,自动生成层级和排序值,点击提交即可。
### 3.3、职位管理
#### 3.3.1、职位列表
![职位列表](/progress/7职位列表界面.png)
职位管理列表支持条件查询、批量删除、对单条数据进行修改和删除。通过点击左侧的组织树中的组织,列表中的职位将按组织筛选(只暂时对应的组织下的职位)。
#### 3.3.2、新增职位
![新增职位](/progress/8职位新增界面.png)
选择左侧的组织树中某个组织,点击列表上新增按钮,填写职位信息后,点击提交即可。
### 3.4、员工管理
#### 3.4.1、员工列表
![员工列表](/progress/9员工管理列表界面.png)
员工管理列表支持条件查询、批量删除、对单条数据进行修改和删除。
#### 3.4.2、新增员工
![员工新增](/progress/10员工添加界面.png)
输入对应的信息,并选择关联组织和关联职位之后(先选择关联组织,才能获取到关联职位),点击提交即可。
### 3.5、管理/应用菜单维护
<center>管理菜单维护</center>
![管理菜单维护](/progress/11管理菜单维护.png)
<center>应用菜单维护</center>
![应用菜单维护](/progress/12应用菜单维护.png)
#### 3.5.1、新增菜单
例如:在**系统维护**下建一个新菜单**测试菜单**,直接在右侧填写信息,在**上级节点**字段选中**系统维护**,最后点击提交即可。
#### 3.5.2、删除菜单
在左侧菜单树选中想删除的节点,点击最上面的删除按钮(根节点无法被删除、存在子节点无法被删除,被角色引用的节点无法被删除)。
#### 3.5.3、修改菜单
在左侧菜单树选中想修改的节点,在右侧修改相应的数据,点击提交即可。
### 3.6、定时任务
![定时任务](/progress/13定时任务.png)
定时任务管理列表支持根据任务名称查询、对单条任务进行启动、暂停、修改、删除、手动执行等操作。
#### 3.6.1、新增定时任务
![新增定时任务](/progress/30新增定时任务.png)
输入对应的任务名称,定时调用的接口地址,cron表达式(任务执行时间及频率),或者指定确定的时间执行,请求方式分别为POST、GET,然后填写请求参数,最后点击提交即可。
#### 3.6.2、删除定时任务
在定时任务列表操作列种点击删除按钮即可。
#### 3.6.3、启动和暂停定时任务
定时任务可以通过点击操作列种的第一个按钮,进行启动和暂停,也可以点击最后一个按钮手动执行。
### 3.7、定时任务日志
![定时任务日志](/progress/14定时任务日志.png)
定时任务日志会记录所有定时任务执行的情况,如果是异常,可以让鼠标悬停在详细信息列查看更完整的信息。可以通过任务名称查询。
## 4、权限管理
权限管理包含组织数据权限、角色管理、管理菜单权限、应用菜单权限。
### 4.1、组织数据权限
#### 4.1.1、数据角色关联
![组织数据权限](/progress/15组织数据权限.png)
组织数据权限主要管理的是类型为数据的角色,主要是此类型的角色关联组织数据以及。按照上图的操作,先选择角色,右侧会出现两棵树,左边是已经关联的数据,右边是备选数据,在备选数据中,勾选上相要关联的组织数据,对不想要的组织数据取消勾选,再点击**保存**即可。
#### 4.1.2、角色关联用户
![关联用户](/progress/16角色关联用户.png)
按照上图操作,将数据角色和未关联用户关联起来,或者把已关联的用户取消关联。
### 4.2、角色管理
#### 4.2.1、角色列表
![角色列表](/progress/17角色管理界面.png)
角色管理列表支持**条件查询**,通过角色名称或者角色类型进行**精确查询**,还支持单条记录的**修改****删除**
#### 4.2.2、新增角色
![新增角色](/progress/18新增角色弹窗.png)
选择角色类型,输入角色名称和备注,点击**提交**即可。
### 4.3、管理/应用菜单权限
![管理菜单](/progress/19管理菜单权限.png)
1、首先点击角色名后面的**权限配置**
2、在**菜单角色关联**中对角色关联的权限进行变更(增加/删除关联)
3、点击**保存**即可。
右侧的**角色关联用户**参照[4.1.2、角色关联用户](#4.1.2、角色关联用户)
## 5、数据字典
### 5.1、字典管理
#### 5.1.1、字典列表
![字典列表](/progress/20字典列表界面.png)
依照上图点击做边的字典,右边会刷新出该字典的字典项,在字典或者字典项后方都可以对该字典或字典项进行修改或删除。
#### 5.1.2、新增字典
![新增字典](/progress/21新增字典弹窗.png)
填上对应的字典名称或者字典编码,点击提交即可。
#### 5.1.3、新增字典项
待定(可能需要修改)
## 6、系统状态
系统状态包含操作日志、用户登录日志、在线用户监控、接口调用日志等模块。
### 6.1、操作日志
![操作日志](/progress/22操作日志列表界面.png)
点击左侧日志模块类型,该类型的日志信息列表就会出现在右侧,可以通过模块类型名称对日志类型进行**精确查询**,日志列表中也可以通过**时间段****是否异常**进行**精确查询**
### 6.2、用户登录日志
![用户登录日志](/progress/23用户登录日志.png)
用户登录日志可以通过**用户名****时间段**进行条件查询。
### 6.3、在线用户监控
![在线用户监控](/progress/24在线用户监控.png)
在线用户监控可以通过**用户名**进行**精确查询**
### 6.4、接口调用日志
![接口调用日志](/progress/25接口调用日志.png)
接口调用日志是用来监控第三方应用调用接口的日志,可以通过**接口名称****时间段**进行**精确查询**
## 7、编码规则
### 7.1、编码规则
编码规则是一种固定格式的规则,新建一个编码规则后,每一次调用API,根据规则生成一个自增后的编码。
#### 7.1.1、编码规则列表
![编码规则列表](/progress/26编码规则列表界面.png)
编码规则列表可以使用**编码名称**进行**精确查询**,还可以通过操作列对每条数据做出**修改****删除**
#### 7.1.2、新增编码规则
![新增编码规则](/progress/27新增编码规则弹窗.png)
输入对应的key、名称、描述后,选择间隔符与模板,设定好序号位数(1位表示1-9,2位表示01-99),点击提交即可。
## 8、快速开发
### 8.1、代码生成
代码生成模块通过,定义好模型与关联后,自动生成相关的Entity、Service、Rest代码。
![代码生成](/progress/31代码生成.png)
**业务模型ID**:此业务的标识符。
**业务模型名称**:此业务名称
**数据库连接**:可选,MySQL、Oracle、SQLServer。
#### 8.1.1、新建数据模型
![新建数据模型](/progress/32新建数据模型.png)
**数据模型ID**:业务ID,建立关联依赖于此ID,生成的Rest接口也会以此ID作为业务名称。
**数据模型类名**:代码类名。
**说明**:数据库建表的备注。
**默认控件**:在前端的展现样式。
#### 8.1.2、数据模型字段
![数据模型字段](/progress/33数据模型字段.png)
**字段**:字段名,对应表名中的字段,实体类的属性。
**说明**:对字段的说明
**类型**:对应表中的类型和实体类的类型,目前有primary(主键)、string(普通字符)、longstring(长字符串)、date(日期格式)、int(整型)、decimal(浮点型)。
**计算函数**:目前内置4个自动填充函数,sysuuid(自动生成唯一表示的算法)、sysmd5(字段进行md5加密)、sysuserid(默认填充当前登录用户)、systime(默认填充当前时间)
#### 8.1.3、新建关联
创建好相应的数据模型后,就可以创建关联关系了。目前支持一对多、多对一、多对多。
![关联](/progress/34新增关联关系.png)
输入关系对象ID,再选择多方数据模型(有外键的一方),最后选择关联关系:MTO、MTM,分别为一对多、多对多。
![多对多关联](/progress/35多对多关联.png)
然后再通过外键将模型之间关联起来。点击提交生成代码,浏览器会下载一个压缩包,解压后包含一个项目(或者是部分代码,根据选择的工程代码模块)、数据库建表sql脚本文件。
将代码导入到IDEA中,根据工程中的README.md文档进行编译构建。
## LDP集中式安装部署说明
[TOC]
此文档说明在单服务器下,集中式安装部署所有LDP服务;LDP依赖 mysql,redis等中间件服务,建议使用其它服务器独立安装部署。
## 服务器参数
Server OS:CentOS Linux release 7.5+
Mem:16G
Core:8核
## 运行环境准备
#### JRE 1.8+ (服务器版)
安装包已提供 jre-8u251-linux-x64.tar.gz 需自行解压配置环境,过程略。
#### mysql
安装
```shell
## 配置源
vi /etc/yum.repos.d/mysql57-community.repo
[mysql57-community]
name=MySQL 5.7 Community Server
baseurl=http://repo.mysql.com/yum/mysql-5.7-community/el/7/$basearch/
enabled=1
gpgcheck=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-mysql
## 安装
yum repolist enabled | grep mysql
sudo yum install mysql-community-server
```
配置
```shell
## 加入大小写忽略配置
vi /etc/my.cnf
lower_case_table_names=1
```
服务管理
```shell
## 启动,停止,状态
systemctl start mysqld
systemctl stop mysqld
systemctl status mysqld
```
#### redis
安装
```shell
sudo yum install redis
```
配置
```shell
##配置文件,修改配置远程连接,端口,密码,如下
vi /etc/redis.conf
# bind 127.0.0.1
port 7268
requirepass cacheForldp2020
```
服务管理
```shell
## 查看状态,启动,停止
sudo systemctl status redis
sudo systemctl start redis
sudo systemctl stop redis
```
#### nginx 1.12+
下载
```shell
wget http://downloads.sourceforge.net/project/pcre/pcre/8.35/pcre-8.35.tar.gz
wget http://nginx.org/download/nginx-1.13.6.tar.gz
```
安装
```shell
yum -y install make zlib zlib-devel gcc-c++ libtool openssl openssl-devel
tar -xvf pcre-8.35.tar.gz -C /usr/local
cd /usr/local/pcre-8.35/
./configure
make && make install
tar -xvf nginx-1.13.6.tar.gz -c /usr/local
cd nginx-1.13.6
./configure --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module --with-pcre=/usr/local/pcre-8.35
make && make install
```
服务管理
```shell
cd /usr/local/nginx/sbin
# 启动
./nginx
# 停止
./nginx -s stop
```
#### nacos
LDP安装包已集成。
## LDP安装说明
#### 安装包目录
```shell
├── bin
├── conf
│   ├── application.yml #manage服务配置文件
│   ├── boot.conf #启动配置文件,nacos地址和spring.profiles.active
│   ├── logback.xml #manage服务日志配置文件
│   └── sinra_ldp_db.sql #数据库初始化sql文件
├── extlib
├── fatjar
│   ├── base-startup-1.0-SNAPSHOT.jar #基础服务jar包
│   ├── job-startup-1.0.38.jar #job 服务jar包
│   ├── ldp-generator-1.0-SNAPSHOT.jar #自动生成代码服务包
│   ├── mcs-startup-1.0.38.jar #mcs服务jar包
│   ├── service-gateway-1.0.38.jar #网关服务jar包
│   └── uaa-startup-1.0.38.jar #统一认证uaa服务jar包
├── jre-8u251-linux-x64.tar.gz #jre压缩包
├── lib #服务依赖jar包目录
├── logs #日志目录
├── nacos #naocs目录
│   ├── bin
│   ├── conf
│   ├── data
│   ├── LICENSE
│   ├── logs
│   ├── NOTICE
│   └── target
├── tool
│   ├── base-boot.sh #基础服务启动/关闭脚本
│   ├── gateway-boot.sh #网关服务启动/关闭脚本
│   ├── generator-boot.sh #自动生成代码服务启动/关闭脚本
│   ├── job-boot.sh #job服务启动/关闭脚本
│   ├── ldp-boot.sh #统一启动关闭脚本,所有ldp服务可以通过此脚本一件启动和关闭
│   ├── manage-boot.sh #manage服务启动/关闭脚本
│   ├── mcs-boot.sh #mcs服务启动/关闭脚本
│   └── uaa-boot.sh #uaa服务启动/关闭脚本
└── ui
└── mcs #mcs前端资源目录
```
#### 中间件服务hosts配置
在服务器host配置中加入以下配置,根据实际服务修改相应ip
```shell
# mysql 数据库服务
127.0.0.1 mysql-server0
# redis 服务
127.0.0.1 redis-server0
# nacos 服务
127.0.0.1 nacos-server0
# 网关服务
127.0.0.1 gateway-server0
```
#### 新建数据库
- 在mysql内创建ldp数据库: sinra_ldp_db ,字符集:utf8mb4
#### nacos 启动 & 访问
```shell
# 启动
cd nacos/bin
./startup.sh -m standalone
```
访问地址:http://nacos-server0:8018/nacos
默认用户名/口令: nacos/nacos
### nacos在线配置参数
#### 数据库参数配置
* 进入nacos配置列表
![nacos配置列表](imgs/nacos-config-list.png)
* 修改base-server-nacos.yml
![base-server-nacos.yml](imgs/base-server-nacos-config-list.png)
* 修改如图所示的用户名及密码
![base-server-nacos.yml](imgs/base-server-nacos-config-list-change.png)
* 修改后保存即可
#### redis 参数配置
* 进入nacos配置列表
![nacos配置列表](imgs/nacos-config-list.png)
* 修改 mcs-service-nacos.yml和job-service-nacos.yml
![redis-change-list.png](imgs/redis-change-list.png)
* 修改如图所示的密码或端口
![redis-change-1.png](imgs/redis-change-1.png)
![redis-change-2.png](imgs/redis-change-2.png)
* 修改后保存即可
### 启动 ldp 服务
```shell
# 启动
cd tool/
./ldp-boot.sh start
```
脚本会依次启动base-service(基础服务)、job-service(定时任务服务)、mcs-service(mcs服务)、uaa-service(统一认证服务)、gateway-service(网关服务)、generator(代码生成服务)、service-manage(管理服务)。
基础服务启动时会执行sql对数据库进行初始化,由于mcs、job、uaa等服务启动时依赖基础服务,所以这里会先等基础服务启动完成才会执行其它服务的启动。
可以通过查看status来确认服务是否正常启动
```shell
# 查看服务状态
./ldp-boot.sh status
```
也可以访问地址:http://nacos-server0:8018/nacos
点击服务列表,即可看到以启动并注册的服务信息。如下图:
![nacos服务列表](imgs/nacos-service.png)
如果服务启动失败查看logs/目录下相应的日志文件
### 启动 ldp web 服务
ldp mcs ui 发布
```shell
# 复制mcs 到 nginx/html 目录
cp -r ui/mcs /usr/local/nginx/html/
```
nginx 配置
```shell
vim /usr/local/nginx/conf/nginx.conf
```
```shell
# 配置nginx 服务代理,参考如下
upstream ldpserver {
# 关键配置,网关服务地址
server gateway-server0:9000 weight=1;
}
server {
listen 9080;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root html;
index index.html index.htm;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
#
# 必需配置
location /genservice {
rewrite ^/(genservice.*) /$1 break;
proxy_pass http://127.0.0.1:8600;
proxy_set_header Host $host;
proxy_set_header X-Real-Ip $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# 必需配置
location ~* /.*-service/ {
rewrite ^/(.*-service.*) /$1 break;
proxy_pass http://ldpserver;
proxy_set_header Host $host;
proxy_set_header X-Real-Ip $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
## 代理设置 https
proxy_set_header X-Forwarded-Proto $scheme;
}
}
```
启动web 服务
```shell
cd /usr/local/nginx/sbin/
## 启动服务
./nginx
```
访问mcs-ui地址:
http://服务域名或ip:9080/mcs
![mcs登录界面](imgs/login.png)
初始用户名:admin
初始密码:1234561
### LDP 文档汇总
建议根据需求(安装、操作、开发)先阅读根目录的主要文档,如果有其它需求再查看相应具体文档。
安装文档:
​ 1、**LDP集中式安装部署说明**
​ 2、JRE安装说明
操作文档:
​ 1、**LDP-MCS操作手册**
​ 2、LDP-MCS新建一个管理员账号
开发文档:
​ 1、**基于LDP框架的应用开发文档**
​ 2、Example样例工程开发文档
​ 3、LDP 框架自定义注解
​ 4、git提交规范说明
\ No newline at end of file
# 基于LDP框架的应用开发文档
## 一、简介
此文档介绍基于LDP框架的应用开发。LDP框架已封装好各种依赖库、工具包,应用开发者只需聚焦在具体业务实现,快速构建出可独立运行的应用。通过参考样例工程,免去复杂的配置,开发者可以快速搭建一个基于LDP框架的应用。
## 二、开发环境
### 2.1. 开发工具与运行环境
技术依赖:SpringBoot、Dubbo、Nacos、LDP框架服务,LDP框架API组件jar等。
构建技术:Maven 3.6+
JAVA版本:JRE1.8+
数据库:MySQL
IDE: IDEA(推荐)、Eclipse
### 2.2. 远程服务介绍及地址
#### 2.2.1 私服仓库地址
```
http://repo.dev.shxrtech.com/repository/sinra
```
备注:主要用于发布LDP框架依赖包、以及加快其它依赖库下载速度
#### 2.2.2 配置本地hosts服务地址
以下服务是项目运行必要的服务,请根据实际情况在本地hosts文件中配置。
Nacos服务地址:nacos-server0
Mysql服务地址:mysql-server0
Redis服务地址: redis-server0
LDP服务地址:ldp-server0
内部开发环境配置(仅供参考):
```txt
# mysql 数据库服务
47.103.46.246 mysql-server0
# redis 服务
47.103.46.246 redis-server0
# nacos 服务
47.103.46.246 nacos-server0
# ldp服务
47.103.46.246 ldp-server0
```
#### 2.2.3 YAPI接口文档地址
包含已有接口的API文档
http://api.dev.shxrtech.com/
### 三、工程开发命名规范
下述命名规范中,[projectname]为项目名,[modulename]为模块名
### 3.1. 主工程命名
ldp-app-[projectname]
### 3.2. 子模块命名
实体模块,主要存放实体类
实体模块:[projectname]-api
业务模块:[projectname]-[modulename]
启动模块:[projectname]-startup
### 3.3. JAVA开发命名
基础包名:com.sinra.ldp
实体包名:com.sinra.ldp.model.[projectname].[modulename]
视图层包名:com.sinra.ldp.[projectname].[modulename].rest
服务层包名:com.sinra.ldp.[projectname].[modulename].service
视图层类名:com.sinra.ldp.[projectname].[modulename].rest.XXXRest.java
服务层类名:com.sinra.ldp.[projectname].[modulename].service.XXXService.java
实现层类名:com.sinra.ldp.[projectname].[modulename].service.impl.XXXServiceImpl.java
启动类名:com.sinra.ldp.[projectname].Ldp[projectname]Application.java
### 3.4. jar包输出命名
[projectname]-startup-[version].jar
### 3.5. 服务命名规范
[projectname]-service
### 3.6 视图层接口地址命名规范
为了更方便记录日志,所以推荐使用GET、POST两种方式,并在URL中体现出操作方式。
在Rest类加上注解:
```java
@RequestMapping("/[modulename]")
```
方法注解:
```java
//新增方法请求地址
@PostMapping("/add")
//查询单条数据请求地址
@GetMapping("/get/{id}")
//查询列表数据
@GetMapping("/get/list")
//查询分页数据
@GetMapping("/get/page")
//修改单条数据
@PostMapping("/mod/{id}")
//删除单条数据
@PostMapping("/del/{id}")
//批量增删改查
@PostMapping("/batch")
```
## 四、样例工程说明
### 4.1. 样例工程结构图
![样例工程结构图](./imgs/example-project.png)
### 4.2. 结构图说明
1. 样例工程名称
2. api模块(主要包含实体类)
3. biz子模块(主要是业务接口)
4. startup子模块(主要包含启动类、构建可运行jar包)
### 4.3. 样例工程模块
#### 4.3.1 example-api模块
example-api模块,主要包含业务功能会用到的实体类,并且在开发完成后,需要先将example-api模块的jar发布到基础服务中。数据库接口调用是远程调用,如果实体类在本地,基础服务就无法执行正确的查询和序列化,所以需要先将实体类上传。**上传后基础服务会重启,所以服务会中断一会儿**
下面是example-api模块的**pom.xml**
```xml
<build>
<plugins>
<plugin>
<groupId>com.sinra.ldp</groupId>
<artifactId>ldp-maven-plugin</artifactId>
<version>1.0-SNAPSHOT</version>
<configuration>
<file>target/example-api-1.0-SNAPSHOT.jar</file>
<url>http://ldp-server0:8500/restart</url>
<env>dev</env>
</configuration>
</plugin>
</plugins>
</build>
```
上传流程为:
```shell
#在根目录执行编译
mvn clean install -U
#切换到example-api目录
cd example-api
#执行上传
mvn ldp:upload
```
#### 4.3.2 example-biz模块
example-biz模块,主要作为业务接口开发模块,包含视图层、服务层、sql语句xml文件等相关信息。example-biz需要依赖example-api模块,并在此模块中实现业务开发。
#### 4.3.3 example-startup模块
example-startup作为example工程启动模块、打包模块,主要包含启动类以及各种配置。执行打包命令
```shell
mvn clean install -U
```
会在target目录生成**examp-startup-1.0-SNAPSHOT.jar**,执行下面命令可以启动example:
```shell
java -jar examp-startup-1.0-SNAPSHOT.jar
```
访问: http://localhost:8800/example/get/page?pageIndex=1&pageSize=5 即可看到分页数据
### 4.4. 服务配置说明
#### 4.4.1 POM文件Maven私服仓库配置
在工程pom.xml中配置
```xml
<repositories>
<repository>
<id>sinra-repo</id>
<url>http://repo.dev.shxrtech.com/repository/sinra</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>sinra-repo</id>
<url>http://repo.dev.shxrtech.com/repository/sinra</url>
<releases>
<enabled>true</enabled>
</releases>
</pluginRepository>
</pluginRepositories>
<distributionManagement>
<repository>
<id>ldp-release</id>
<url>http://repo.dev.shxrtech.com/repository/maven-releases/</url>
</repository>
<snapshotRepository>
<id>ldp-snapshot</id>
<url>http://repo.dev.shxrtech.com/repository/maven-snapshots/</url>
</snapshotRepository>
</distributionManagement>
```
#### 4.4.2 POM文件Plugins 配置
工程pom.xml文件:
```xml
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire.version}</version>
<configuration>
<!--是否跳过单元测试-->
<skipTests>true</skipTests>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler.version}</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
</plugins>
</build>
```
实体类模块(例如:example-api)pom.xml文件,主要是为了上传实体类模块包。
```xml
<build>
<plugins>
<plugin>
<groupId>com.sinra.ldp</groupId>
<artifactId>ldp-maven-plugin</artifactId>
<version>1.0-SNAPSHOT</version>
<configuration>
<file>target/example-api-1.0-SNAPSHOT.jar</file>
<url>http://ldp-server0:8500/restart</url>
<env>dev</env>
</configuration>
</plugin>
</plugins>
</build>
```
启动模块(例如:example-startup)pom.xml文件:
```xml
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot-maven.version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
```
#### 4.4.3 POM文件LDP框架包依赖配置
```xml
<!-- 业务公共依赖库,包含自动填充注解、基础Dao、Rest参数注解等通用工具 -->
<dependency>
<groupId>com.sinra.ldp</groupId>
<artifactId>mcs-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- rest依赖库,主要包含返回结果包装以及异常处理 -->
<dependency>
<groupId>com.sinra.ldp</groupId>
<artifactId>common-rest</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- query依赖库,主要包含sql语句xml解析,绑定等 -->
<dependency>
<groupId>com.sinra.ldp</groupId>
<artifactId>common-query</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- cache依赖库,主要包含缓存注解,及缓存处理逻辑 -->
<dependency>
<groupId>com.sinra.ldp</groupId>
<artifactId>common-cache</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- log依赖库,主要系统日志sql -->
<dependency>
<groupId>com.sinra.ldp</groupId>
<artifactId>common-log</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
```
#### 4.4.4 YML服务名和端口配置
参考example-startup模块中resource/bootstrap.yml中**server.port**定义端口,这里为8800
**spring.profiles.active**决定服务启动的时候启用什么配置文件(bootstrap-dev.yml、bootstrap-local.yml),这里设置为**nacos**,所以启用**bootstrap-nacos.yml**文件配置。
```yml
server:
# 服务端口
port: 8800
nacos:
# nacos地址
server: nacos-server0:8018
spring:
profiles:
# 默认启用nacos配置
active: nacos
application:
#服务名
name: example-service
messages:
encoding: UTF-8
cloud:
nacos:
server-addr: ${nacos.server}
config:
server-addr: ${nacos.server}
discovery:
server-addr: ${nacos.server}
```
#### 4.4.5 YML文件Dubbo服务配置
参考**bootstrap-nacos.yml**
```yml
dubbo:
registry:
address: nacos://${spring.cloud.nacos.server-addr}
application:
##日志适配
logger: slf4j
##输出访问日志
protocol:
accesslog: true
serialization: java
consumer:
application: ${spring.application.name}-consumer
parameters:
protocol: dubbo
serialization: java
version: 1.0
timeout: 15000
group: ${spring.cloud.nacos.discovery.group}
```
#### 4.4.6 YML文件注册中心Nacos配置
参考**bootstrap-nacos.yml**
```yml
spring:
cloud:
# nacos 配置
nacos:
discovery:
register-enabled: true
group: nacos_default_group
# 服务消费者和提供者在不同网段,且本服务作为提供者时,配置为提供者实际部署机器名地址,并在消费者hosts文件中配置机器名与ip的映射
#ip: ldp-server0
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
```
#### 4.4.7 YML文件LDP缓存配置
参考**bootstrap-nacos.yml**
```yml
ldp:
query:
location: query/*.xml
dialect: mysql
cache:
## redis配置
redis:
host: redis-server0
port: 7268
password: cacheForldp2020
database: 1
sentinel:
master: ldp-master
nodes:
- redis-server0:6380
- redis-server0:6381
- redis-server0:6382
instances:
#用户会话级缓存
session: 3600
#应用级缓存默认不过期
application: -1
nacos:
group: DEFAULT_GROUP
requestTimeout: 5000
```
#### 4.4.8 logback.xml日志配置
参考**logback.xml**
```xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="LOG_FILE" value="ldp-base-server" />
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
<include resource="org/springframework/boot/logging/logback/console-appender.xml" />
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>${user.home}/.sinra/${LOG_FILE}.log</File>
<encoder>
<pattern>%date [%level] [%thread] %logger{60} [%file : %line] %msg%n</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- 按文件名2018-08-29-15[0].log,如果文件大小超过100kb,则继续分割日志2018-08-29-15-32[1].log,
2018-08-29-15-32[2].log。%d和%i不能缺少 -->
<fileNamePattern>${user.home}/.sinra/${LOG_FILE}.%d{yyyy-MM-dd.HH}_%i.log</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>1GB</totalSizeCap>
<!-- 保留1G日志 -->
</rollingPolicy>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</root>
</configuration>
```
## 五、数据接口调用介绍
### 5.1. 基础服务数据库调用示例及介绍
#### 5.1.2 Service介绍
1. service继承**BaseService**,并在<>传入对应的实体类,**BaseService**中是常用的增删改查接口。
![服务接口](./imgs/example-service.png)
2. 在实现类中继承**AbsBaseDao**,并实现service接口。
![实现类](./imgs/example-service-impl.png)
3. **AbsBaseDao**中使用**dubbo**远程依赖**Hibernate**接口**genericDaoService**,以及**JDBC**接口**jdbcDaoService**,所以实现类中可以直接调用。
#### 5.1.3 Hibernate 调用示例及介绍
Hibernate 接口可以使用实体类进行增删改查,也可以使用sql,这里先介绍使用实体类的操作,使用sql的方式与JDBC类似,下一小节介绍。
1. 新增数据
```java
// AutoService注解在新增时会对实体类中配置了@AutoComputed字段根据规则进行填充
//新增单条数据
@Override
@AutoService
public void add(ExampleUserInfo exampleUserInfo) {
genericDaoService.insert(exampleUserInfo);
}
//新增列表数据
@Override
@AutoService
public void add(List<ExampleUserInfo> list) {
genericDaoService.insertList(list);
}
```
2. 查询数据
```java
//查询单条数据
@Override
public ExampleUserInfo find(String s) {
return (ExampleUserInfo) genericDaoService.findById(ExampleUserInfo.class, s);
}
//查询列表数据
@Override
public List<ExampleUserInfo> findList(Map<String, Object> param) {
return genericDaoService.findByHql(ExampleUserInfo.class, param);
}
//查询分页数据
@Override
public Pagination<ExampleUserInfo> findPage(Pagination page, Map<String, Object> map) {
return genericDaoService.findPageByHql(ExampleUserInfo.class, map, page.getPageIndex(), page.getPageSize());
}
```
3. 更新数据
```java
// AutoService注解在更新时会对实体类中updateId、updateTime、sortName进行自动填充
// 根据实体类更新指定数据
@Override
@AutoService(init = false)
public void update(ExampleUserInfo exampleUserInfo) {
genericDaoService.update(exampleUserInfo);
}
// 更新列表数据
@Override
@AutoService(init = false)
public void update(List<ExampleUserInfo> list) {
genericDaoService.update(list);
}
// 按条件更新指定字段
@Override
public void update(Class<ExampleUserInfo> aClass, Map<String, Object> updateFileds, Map<String, Object> whereParam) {
genericDaoService.update(aClass, updateFileds, whereParam);
}
```
4. 删除数据
```java
// 删除单条数据(实体类id不能为空)
@Override
public void delete(ExampleUserInfo exampleUserInfo) {
genericDaoService.delete(exampleUserInfo);
}
// 删除列表数据
@Override
public void delete(List<ExampleUserInfo> list) {
List<String> idList = list.stream().map(ExampleUserInfo::getId).collect(Collectors.toList());
Map<String, Object> param = new HashMap<>(1);
param.put("id", idList);
genericDaoService.deleteList(ExampleUserInfo.class, param);
}
```
5. 更多接口
<details>
<summary>展开</summary>
<pre><blockcode>
/**
* 新增
*
* @param o entity 对象
*/
void insert(T o);
/**
* 批量新增
*
* @param list 列表数据
*/
void insertList(List<T> list);
/**
* hibernate merge操作
*
* @param o entity 对象
*/
void merge(T o);
/**
* merge批量操作
*
* @param list 列表
*/
void merge(List<T> list);
/**
* 更新
*
* @param o entity 对象
*/
void update(T o);
/**
* 动态更新,未设置的属性不做更新,默认以主键为查询条件
*
* @param o entity 对象
*/
void dynamicUpdate(T o);
/**
* 批量动态更新
*
* @param list 列表
*/
void dynamicUpdate(List<T> list);
/**
* 动态更新,设置属性为查询条件
*
* @param o
* @param whereParam 自定义查询条件 ,map中key必须为entity存在字段
*/
int dynamicUpdate(T o, Map<String, Object> whereParam);
/**
* 批量更新
*
* @param list 列表
*/
void update(List<T> list);
/**
* 批量更新
*
* @param clazz orm对象
* @param updateFields 待更新字段集
* @param whereParam 更新条件集
* field
* @return
*/
int update(Class<T> clazz, Map<String, Object> updateFields, Map<String, Object> whereParam);
/**
* 删除
*
* @param o entity 对象
*/
void delete(T o);
/**
* 批量删除
*
* @param clazz 需要修改的实体类
* @param whereParam key:字段名 value:字段值,根据字段条件删除记录
*/
int deleteList(Class<T> clazz, Map<String, Object> whereParam);
/**
* 删除
*
* @param clazz entity 类型
* @param id 唯一标识
*/
void delete(Class<T> clazz, K id);
/**
* 通过id查询
*
* @param clazz entity类型
* @param id 唯一标识
* @return entity 对象
*/
T findById(Class<T> clazz, K id);
/**
* 计数
*
* @param clazz entity 类型
* @return 数据条数
*/
int count(Class<T> clazz);
/**
* 通过sql计数
*
* @param sql sql语句
* @return 数据条数
*/
int countBySQL(String sql);
/**
* 通过sql和参数计数
*
* @param sql sql语句
* @param paramMap 参数
* @return 数据条数
*/
int countBySQL(String sql, Map<String, Object> paramMap);
/**
* 查询所有数据
*
* @param clazz entity类型
* @param orderColumns 排序字段
* @return 数据集
*/
List<T> findALL(Class<T> clazz, OrderColumn... orderColumns);
/**
* hibernate 方式查询带参数
*
* @param clazz entity类型
* @param paramMap 参数
* @param orderColumns 排序字段
* @return 数据集
*/
List<T> findByHql(Class<T> clazz, Map<String, Object> paramMap, OrderColumn... orderColumns);
/**
* hibernate 方式分页查询,带参数
*
* @param clazz entity类型
* @param paramMap 参数
* @param pageIndex 当前页
* @param pageSize 页面数据量
* @param orderColumns 排序字段
* @return 分页数据
*/
Pagination findPageByHql(Class<T> clazz, Map<String, Object> paramMap, Object pageIndex, Object pageSize, OrderColumn... orderColumns);
/**
* hibernate 方式分页查询,带参数
*
* @param clazz entity类型
* @param pageIndex 当前页
* @param pageSize 页面数据量
* @param orderColumns 排序字段
* @return 分页数据
*/
Pagination findPageByHql(Class<T> clazz, Object pageIndex, Object pageSize, OrderColumn... orderColumns);
/**
* 通过sql和参数查询entity数据集
*
* @param sql sql语句
* @param clazz entity类型
* @param paramMap 参数
* @return 数据集
*/
List<T> findEntityBySQL(String sql, Class<T> clazz, Map<String, Object> paramMap);
/**
* 通过sql查询entity数据集
*
* @param sql sql语句
* @param clazz entity类型
* @return 数据集
*/
List<T> findEntityBySQL(String sql, Class<T> clazz);
/**
* 通过sql和参数查询分页数据集
*
* @param sql sql语句
* @param clazz entity类型
* @param paramMap 参数
* @param pageIndex 当前页
* @param pageSize 页面数据量
* @return 数据集
*/
List<T> findEntityBySQL(String sql, Class<T> clazz, Map<String, Object> paramMap, Object pageIndex, Object pageSize);
/**
* 通过sql查询分页数据
*
* @param sql sql语句
* @param clazz entity类型
* @param pageIndex 当前页
* @param pageSize 页面数据量
* @return 数据集
*/
List<T> findEntityBySQL(String sql, Class<T> clazz, Object pageIndex, Object pageSize);
/**
* 通过sql查询二维数组
*
* @param sql sql语句
* @return 数据集
*/
List<Object[]> findListArrayBySql(String sql);
/**
* 通过sql和参数查询二维数组
*
* @param sql sql语句
* @param paramMap 参数
* @return 数据集
*/
List<Object[]> findListArrayBySql(String sql, Map<String, Object> paramMap);
/**
* 通过sql和参数查询分页二维数组
*
* @param sql sql语句
* @param paramMap 参数
* @param pageIndex 当前页
* @param pageSize 数据量
* @return 数据集
*/
List<Object[]> findListArrayBySql(String sql, Map<String, Object> paramMap, Object pageIndex, Object pageSize);
/**
* 通过sql查询List<Map> 数据
*
* @param sql sql语句
* @return 数据集
*/
List<Map<String, Object>> findListMapBySql(String sql);
/**
* 通过sql查询List<Map> 数据
*
* @param sql sql语句
* @param paramMap 参数
* @return 数据集
*/
List<Map<String, Object>> findListMapBySql(String sql, Map<String, Object> paramMap);
/**
* 通过sql查询分页List<Map> 数据
*
* @param sql sql语句
* @param paramMap 参数
* @param pageIndex 当前页
* @param pageSize 数据量
* @return 数据集
*/
List<Map<String, Object>> findListMapBySql(String sql, Map<String, Object> paramMap, Object pageIndex, Object pageSize);
/**
* 通过sql和参数查询实体分页数据
*
* @param sql sql语句
* @param clazz entity类型
* @param paramMap 参数
* @param pageIndex 当前页
* @param pageSize 数据量
* @return 分页数据
*/
Pagination queryPageForListEntity(String sql, Class<T> clazz, Map<String, Object> paramMap, Object pageIndex, Object pageSize);
/**
* 通过sql和参数查询List<Map>分页数据
*
* @param sql sql语句
* @param paramMap 参数
* @param pageIndex 当前页
* @param pageSize 数据量
* @return 分页数据
*/
Pagination queryPageForListMap(String sql, Map<String, Object> paramMap, Object pageIndex, Object pageSize);
/**
* 通过sql查询实体分页数据
*
* @param sql sql语句
* @param clazz entity类型
* @param pageIndex 当前页
* @param pageSize 数据量
* @return 分页数据
*/
Pagination queryPageForListEntity(String sql, Class<T> clazz, Object pageIndex, Object pageSize);
/**
* 通过sql和参数查询List<Map>分页数据
*
* @param sql sql语句
* @param pageIndex 当前页
* @param pageSize 数据量
* @return 分页数据
*/
Pagination queryPageForListMap(String sql, Object pageIndex, Object pageSize);
int executeSqlUpdate(String sql, Map<String, Object> params);
/**
* 事务级多query执行
*
* @param listQuery 事务集合
* @return 影响行数
*/
int executeTrans(LinkedList<TranscationQuery> listQuery);
</blockcode></pre>
</details>
#### 5.1.4 JDBC 调用示例及介绍
JDBC接口使用,需要先在resource/xxxx-query.xml中编写sql语句,例如exampleservice-query.xml
![query.xml文件](./imgs/example-query-xml.png)
```xml
<?xml version="1.0" encoding="UTF-8"?>
<queryspace
xmlns="http://www.w3schools.com"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="query-template.xsd"
ref="com.sinra.ldp.example.service.impl.ExampleServiceImpl">
<!-- 带普通参数的查询 -->
<query id="examplelist" author="machao" remark="列表数据">
<!-- 使用【:参数名】预留参数,在#间,表示可选,如果不传,就会忽略此参数, 建议在参数和#间添加空格 -->
<!-- 根据数据库类型自动读取对应标签中的sql语句,如果不存在,默认读取sql标签下的语句 -->
<sql>
<![CDATA[
select * from ldp_mcs_user_info where # user_type=:userType #
]]>
</sql>
<oracle>
<![CDATA[
select * from ldp_mcs_user_info where # user_type=:userType #
]]>
</oracle>
<mysql>
<![CDATA[
select * from ldp_mcs_user_info where # user_type=:userType #
]]>
</mysql>
<sqlserver>
<![CDATA[
select * from ldp_mcs_user_info where # user_type=:userType #
]]>
</sqlserver>
</query>
</queryspace>
```
在**com.sinra.ldp.example.service.impl.ExampleServiceImpl**中使用
![jdbc查询](imgs/example-jdbc-demo.png)
通过注解**@QueryBind**自动注入配置在xml中的sql语句
```java
/**
* jdbc查询, QueryBind所对应的sql语句在resource/query/exampleservice中
* 调用时 getUserListByJDBC(StringUtils.EMPTY); 注解会将sql语句自动注入到参数中
*
* @param sql
* @return 列表数据
*/
@QueryBind("examplelist")
@Override
public List<ExampleUserInfo> getUserListByJDBC(String sql, Map<String, Object> param) {
return jdbcDaoService.queryForList(sql, ExampleUserInfo.class, param);
}
```
更多接口
<details>
<summary>展开</summary>
<pre><blockcode>
/**
* 分页查询,带参数,返回分页对象,data类型为list:entity
* 进行分页查询
*
* @param sql sql语句
* @param entityType entity类型
* @param paramMap 参数
* @param pageIndex 当前页码
* @param pageSize 数据量
* @return 分页数据
*/
public <T> Pagination queryPageForListEntity(String sql, Class<T> entityType, Map<String, Object> paramMap, int pageIndex, int pageSize);
/**
* 分页查询,返回分页对象,data类型为list:entity
* 进行分页查询
*
* @param sql sql语句
* @param entityType entity类型
* @param pageIndex 当前页码
* @param pageSize 数据量
* @return 分页数据
*/
public <T> Pagination queryPageForListEntity(String sql, Class<T> entityType, int pageIndex, int pageSize);
/**
* 分页查询,带参数,返回分页对象,data类型为list:map
*
* @param sql sql语句
* @param paramMap 参数
* @param pageIndex 当前页
* @param size 数据量
* @return 分页数据
*/
public <T> Pagination queryPageForListMap(String sql, Map<String, Object> paramMap, int pageIndex, int size);
/**
* 分页查询,带参数,返回分页对象,data类型为list:map
*
* @param sql sql语句
* @param pageIndex 当前页
* @param size 数据量
* @return 分页数据
*/
public <T> Pagination queryPageForListMap(String sql, int pageIndex, int size);
/**
* 查询不带参数,返回list:map
*
* @param sql sql语句
* @return 数据集
*/
public List<Map<String, Object>> queryForList(String sql);
/**
* 查询带参数,返回list:map
*
* @param sql sql语句
* @param paramMap 参数
* @return 数据集
*/
public List<Map<String, Object>> queryForList(String sql, Map<String, Object> paramMap);
/**
* 查询不带参数,返回list:entity
*
* @param sql sql
* @param entityType entity类型
* @return 数据集
*/
public <T> List<T> queryForList(String sql, Class<T> entityType);
/**
* 查询带参数,返回list:entity
*
* @param sql sql
* @param entityType bean类型
* @param paramMap 参数map
* @return 数据集
*/
public <T> List<T> queryForList(String sql, Class<T> entityType, Map<String, Object> paramMap);
/**
* 执行sql
*
* @param sql sql语句
* @return 影响行数
*/
public int excute(String sql);
/**
* 执行sql 带参数
*
* @param sql sql语句
* @param paramMap 参数
* @return 影响行数
*/
public int excute(String sql, Map<String, Object> paramMap);
/**
* 批量sql执行,不带参数
*
* @param sql sql语句
* @return 影响行数数组
*/
public int[] batchExcute(String... sql);
/**
* 执行同一sql,批量参数
*
* @param sql sql语句
* @param listParamMap 参数
* @return 影响行数数组
*/
public int[] execute(String sql, List<Map<String, Object>> listParamMap);
/**
* 事务级多query执行
*
* @param listQuery 事务集合
* @return 影响行数
*/
int executeTrans(LinkedList<TranscationQuery> listQuery);
</blockcode></pre>
</details>
#### 5.1.5 SQL查询扩展——条件IN和条件LIKE
1. **条件IN**
在**exampleservice-query.xml**添加以下SQL(这里为了方便,省略了其它数据库类型标签):
```xml
<!-- 使用in查询用户信息 -->
<query id="example_in_params" author="machao" remark="使用条件in">
<sql>select * from ldp_mcs_user_info where # id in (:ids) #</sql>
</query>
```
在**ExampleServiceImpl**中的调用代码,条件IN支持**数组**与**List集合**,下面是使用数组的例子:
```java
/**
* 使用条件in查询用户列表
*
* @param sql sql语句
* @return 列表数据
*/
@QueryBind("example_in_params")
@Override
public List<ExampleUserInfo> getListByConditionIn(String sql) {
String[] ids = new String[]{"05bdc6e23ce64c6", "2aaa919abbba496", "2bf023cf04eb45d"};
Map<String, Object> param = new HashMap<>();
param.put("ids", ids);
return jdbcDaoService.queryForList(sql, ExampleUserInfo.class, param);
}
```
2. **条件LIKE**
在**exampleservice-query.xml**添加以下SQL,LIKE条件参数需要放到英文单引号中:
```xml
<!-- 使用like查询用户信息 -->
<query id="example_like_params" author="machao" remark="使用条件like">
<sql>select * from ldp_mcs_user_info where # user_name like '%:name%' #</sql>
</query>
```
在**ExampleServiceImpl**中的调用代码:
```java
/**
* 使用条件like查询用户列表
*
* @param sql sql语句
* @return 列表数据
*/
@QueryBind("example_like_params")
@Override
public List<ExampleUserInfo> getListByConditionLike(String sql) {
Map<String, Object> param = new HashMap<>();
param.put("name", "admin");
return jdbcDaoService.queryForList(sql, ExampleUserInfo.class, param);
}
```
### 5.2. 框架内REST服务调用示例及介绍
对于已存在的其它服务接口,需要通过Rest 请求来调用,这里推荐使用**RestTemplate**,根据 http://api.dev.shxrtech.com/ 的API文档,使用**Nacos**自动解析服务名。
#### 5.2.1 mcs服务
根据命名规则,mcs服务名叫**mcs-sevice**,调用mcs模块的[**/user/get/page**](http://api.dev.shxrtech.com/project/25/interface/api/414)接口,拼接完整的请求地址为http://mcs-service/user/get/page ,根据文档,请求方式为GET,可选参数为pageIndex,pageSize,代码如下:
```java
@Autowired
RestTemplate restTemplate;
/**
* rest查询,使用服务名+接口名: 这里使用mcs-service服务,调用用户获取列表接口
*
* @return 分页数据
*/
@Override
public Pagination getUserListByRest() {
//构造请求头
HttpHeaders headers = new HttpHeaders();
headers.add("Accept", MediaType.APPLICATION_JSON.toString());
HttpEntity<String> entity = new HttpEntity(headers);
//构造请求连接
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl("http://mcs-service/user/get/page")
.queryParam("pageIndex", 1)
.queryParam("pageSize", 5);
//发送请求
ResponseEntity<RestResult<Pagination<Map<String, Object>>>> response = restTemplate.exchange(builder.toUriString(),
HttpMethod.GET,
entity,
new ParameterizedTypeReference<RestResult<Pagination<Map<String, Object>>>>() {
});
//获取数据
return response.getBody().getData();
}
```
其它接口,请参照API文档:
http://api.dev.shxrtech.com/project/25/interface/api
### 5.3 框架中记录多个系统之间的接口调用日志
#### 5.3.1 接口调用日志的实现
对于在系统中进行接口调用的方法中,需手动进行接口调用日志的记录。依赖**common-log**模块
```xml
<dependency>
<groupId>com.sinra.ldp</groupId>
<artifactId>common-log</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
```
```java
@Autowired
private InterfaceLogBaseService interfaceLogBaseService;
```
```java
InterfaceAddLogVo interfaceAddLogVo = new InterfaceAddLogVo();
String url = "http://localhost:8800/interface/test";
String params = "&name=lisw&number=001";
String result= sendPost(url,params);
interfaceAddLogVo.setIsOut(true)
.setName("Example工程测试接口")
.setParams(params)
.setResult(result)
.setUrl(url)
.setReceiver("信睿LDP框架接收")
.setSender("信睿LDP框架发送");
interfaceLogBaseService.saveLog(interfaceAddLogVo,request);
```
**注:InterfaceAddLogVo字段说明:**
| 字段名称 | 说明 |
| ---- | ---- |
| name | 接口名称 |
| params | 接口入参 |
| result | 接口出参 |
| isOut | 接口进出标识,false:进。true:出 |
| url | 接口地址,isOut为false时,可不传,系统会通过request自动获取本系统的接口地址 |
| receiver | 接收方 |
| sender | 发送方 |
**注:saveLog参数说明:**
| 参数名称 | 说明 |
| ---- | ---- |
| interfaceAddLogVo | 日志记录内容体 |
| HttpServletRequest | 请求,isOut为false时,可传NULL |
### 5.4 框架中使用编码规则进行自动生成编码
#### 5.4.1 编码规则的创建
很多系统的业务场景当中,存在着需要自动按照顺序号、日期、关键字的组合进行生成编码。
![](imgs/example-codingrule-create.png)
#### 5.4.2 编码规则的使用
创建了编码规则之后。在应用代码中根据编码规则Key生成并获取最新的编码即可。
pom依赖**common-codingrule**模块
```xml
<dependency>
<groupId>com.sinra.ldp</groupId>
<artifactId>common-codingrule</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
```
```java
@Autowired
private CodingruleUtils codingruleUtils;
/**
* 根据预先设置好的编码规则,进行生成编码。
* codingruleUtils.getNumber(key),业务模块代码中,可直接进行注入使用,依赖common-codingrule包
* @param key 编码规则的Key
* @return
*
*/
@GetMapping("/generateNumber")
public RestResult generateNumber(String key){
String number = codingruleUtils.getNumber(key);
/**
* 获取到number后,set到业务实体中即可
* 业务代码.......
*
*/
return new RestResult(ResultMsg.SUCCESS,number);
}
```
### 5.5 框架中使用数据字典
#### 5.5.1 数据字典的关联使用
各业务模块实体中,如果有需要用到数据字典的字段,则将数据字典的主键ID存入到业务实体当中即可。在业务实体当中利用Hibernate关联即可。
```java
/**
* 人员性别
*/
@ManyToOne(fetch = FetchType.EAGER,cascade = {CascadeType.REFRESH})
@JoinColumn(name = "sex")
private DictInfo2 sex;//数据类别
```
### 5.6 框架中记录JOB的执行日志
#### 5.6.1 定时任务的创建
在MCS管理后台中,系统维护-定时任务功能模块中进行创建。
![](imgs/example-job-create-form.png)
**注:字段说明:**
| 字段名称 | 说明 |
| ---- | ---- |
| 任务名称 | 定时任务的名称 |
| 接口地址 | 定时任务开启之后,执行调用的接口地址,接口地址路径中必须包含'ldpjob/'字符串,例如:xx/ldpjob/xx |
| cron表达式 | 配置cron表达式 |
| 按固定时间执行 | |
| 请求方式 | 接口地址的请求方式GET还是POST |
| POST参数 | POST格式的参数 |
| 备注 | |
创建任务之后,在列表中点击第一个按钮手动开启任务,任务开始后,到达设置的时间后则执行定时任务(调用定时任务配置的接口)。
![](imgs/example-job-start.png)
#### 5.6.2 在定时任务执行的接口上增加JOB日志记录
由于系统架构为微服务,服务与服务之间解耦合。因此定时任务设计之初,考虑为定时接口任务调用。
定时任务执行接口调用, 在被调用接口引用的Service具体业务方法添加此@SysJobLog 注解。
注解包含两大功能,1 异步执行,防止接口执行时间过长,否则定时任务调用机器一直连接被占用; 2 更新定时任务执行情况。
定时任务执行流程:
1. 定时任务调度服务启动后,满足定时条件自动执行任务调度。 根据后台配置好的服务名称、接口信息调用其他服务接口。
2. 被调用接口,必须添加@RequestParam String taskJobId 作为接收参数,此参数后续作为更新任务执行状态使用。由于在业务方法上添加@SysJobLog, 业务方法与Controller不在同一个线程中,无法
获取上一个线程中的信息。因此具体业务Service也必须添加名称为String taskJobId 的参数,后续会自动获取参数的值进行更新任务执行情况。
3. 被调用接口也必须添加 @AvoidRepeatableSubmit 防止重复执行
Controller
```java
@PostMapping(value = "/testExampleJob")
@AvoidRepeatableSubmit
public RestResult testJob(@RequestParam String taskJobId, @RequestBody ExampleUserInfo userInfo){
exampleService.doJob (taskJobId,userInfo);
return new RestResult(ResultMsg.SUCCESS,"");
}
```
具体Service
```java
@Override
@AutoService
@SysJobLog
public void doJob(String taskJobId,ExampleUserInfo exampleUserInfo) {
genericDaoService.insert(exampleUserInfo);
}
```
## 六、子模块创建
### 6.1. 子工程创建步骤
#### 6.1.1 创建module
右键工程->new->Module
![](imgs/example-new-module.png)
在弹窗中选择JDK版本,然后点击NEXT
![选择jdk](imgs/example-new-dialog.png)
选择父节点、按照命名规范输入对应的**模块名称**
![模块名](imgs/example-new-module-name.png)
点击**FINISH**
![demo模块](imgs/example-module-demo.png)
#### 6.1.2 配置POM文件
```xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>app-example</artifactId>
<groupId>com.sinra.ldp</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>example-demo</artifactId>
<!-- 配置版本号和打包方式 -->
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<!-- 业务公共依赖库,包含自动填充注解、基础Dao、Rest参数注解等通用工具 -->
<dependency>
<groupId>com.sinra.ldp</groupId>
<artifactId>mcs-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- rest依赖库,主要包含返回结果包装以及异常处理 -->
<dependency>
<groupId>com.sinra.ldp</groupId>
<artifactId>common-rest</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- query依赖库,主要包含sql语句xml解析,绑定等 -->
<dependency>
<groupId>com.sinra.ldp</groupId>
<artifactId>common-query</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- cache依赖库,主要包含缓存注解,及缓存处理逻辑 -->
<dependency>
<groupId>com.sinra.ldp</groupId>
<artifactId>common-cache</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.sinra.ldp</groupId>
<artifactId>example-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
```
#### 6.1.3 编写yml、logback等文件(startup模块)
**注:非启动模块不需要这一步骤**
在resources目录下新建bootstrap.yml、bootstrap-nacos.yml、bootstrap-local.yml、bootstrap-dev.yml(**内部调试用,可以忽略**)配置文件。
**bootstrap.yml**: 这个文件主要配置端口、nacos地址、服务名、程序启动时启用配置
```yml
server:
# 服务端口
port: 8800
nacos:
# nacos地址
server: nacos-server0:8018
spring:
profiles:
# 默认启用nacos配置
active: nacos
application:
#服务名
name: example-service
messages:
encoding: UTF-8
cloud:
nacos:
server-addr: ${nacos.server}
config:
server-addr: ${nacos.server}
discovery:
server-addr: ${nacos.server}
```
**bootstrap-nacos.yml**:这个是基础服务在远程时启用的配置,文中内容包含配置nacos配置、缓存、Dubbo等等相关配置。
```yml
spring:
cloud:
# nacos 配置
nacos:
discovery:
register-enabled: true
group: nacos_default_group
# 服务消费者和提供者在不同网段,且本服务作为提供者时,配置为提供者实际部署机器名地址,并在消费者hosts文件中配置机器名与ip的映射
# ip: ldp-server0
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
ldp:
query:
location: query/*.xml
dialect: mysql
cache:
redis:
host: redis-server0
port: 7268
password: cacheForldp2020
database: 1
sentinel:
master: ldp-master
nodes:
- redis-server0:6380
- redis-server0:6381
- redis-server0:6382
instances:
#用户会话级缓存
session: 3600
#应用级缓存默认不过期
application: -1
nacos:
group: DEFAULT_GROUP
requestTimeout: 5000
demo:
service:
version: 1.0
dubbo:
registry:
address: nacos://${spring.cloud.nacos.server-addr}
application:
##日志适配
logger: slf4j
##输出访问日志
protocol:
accesslog: true
serialization: java
consumer:
application: ${spring.application.name}-consumer
parameters:
protocol: dubbo
serialization: java
version: 1.0
timeout: 15000
group: ${spring.cloud.nacos.discovery.group}
```
**bootstrap-local.yml**:当基础服务ldp-base运行在本地时,可以使用此配置
```yml
## 本地配置
spring:
cloud:
nacos:
discovery:
group: local-default-group
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
ldp:
query:
location: query/*.xml
dialect: mysql
cache:
redis:
host: sinra-server0
port: 7268
password: cacheForldp2020
database: 1
sentinel:
master: ldp-master
nodes:
- sinra-server0:6380
- sinra-server0:6381
- sinra-server0:6382
instances:
# 用户会话级缓存
session: 3600
# 应用级缓存默认不过期
application: -1
nacos:
group: DEFAULT_GROUP
requestTimeout: 5000
demo:
service:
version: 1.0
dubbo:
registry:
address: nacos://${spring.cloud.nacos.server-addr}
application:
## 日志适配
logger: slf4j
## 输出访问日志
protocol:
accesslog: true
serialization: java
consumer:
parameters:
protocol: dubbo
version: 1.0
timeout: 15000
group: ${spring.cloud.nacos.discovery.group}
```
**logback.xml**:主要做为日志文件配置,修改**ldp-app-example**为自己对应工程名,其余配置根据自己需求修改
```xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="LOG_FILE" value="ldp-app-example" />
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
<include resource="org/springframework/boot/logging/logback/console-appender.xml" />
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>${user.home}/.sinra/${LOG_FILE}.log</File>
<encoder>
<pattern>%date [%level] [%thread] %logger{60} [%file : %line] %msg%n</pattern>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- 按文件名2018-08-29-15[0].log,如果文件大小超过100kb,则继续分割日志2018-08-29-15-32[1].log,
2018-08-29-15-32[2].log。%d和%i不能缺少 -->
<fileNamePattern>${user.home}/.sinra/${LOG_FILE}.%d{yyyy-MM-dd.HH}_%i.log</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>1GB</totalSizeCap>
<!-- 保留1G日志 -->
</rollingPolicy>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</root>
</configuration>
```
#### 6.1.4 编写启动类(startup模块)
**注:非启动模块不需要这一步骤**
根据第三章命名规则,在**src/main/java**中新建包名**com.sinra.ldp.[projectname]**,这里 演示为**com.sinra.ldp.example**。在包下面新建**LdpExampleApplication.java**(请根据自己项目名称自行修改),内容如下:
```java
@SpringBootApplication
@EnableCaching
@EnableDiscoveryClient
@ComponentScan("com.sinra.ldp")
public class LdpExampleApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(LdpExampleApplication.class).build()
.run(args);
}
}
```
#### 6.1.5 实体类创建
打开IDEA的Settings->Plugins,在市场中搜索Lombok并安装Lombox插件。
![安装lombok](./imgs/example-idea-lombok.png)
[projectname]-api模块中,根据对应的表编写实体类。下面是部分代码(完整代码请看**com.sinra.ldp.model.example.ExampleUserInfo.java**):
```java
@Table(name = "ldp_mcs_user_info")
@Getter
@Setter
@Entity
@NoArgsConstructor
public class ExampleUserInfo implements Serializable {
private static final long serialVersionUID = -4556501954921265597L;
/**
* 主键
*/
@Id
@GeneratedValue(strategy = GenerationType.AUTO, generator = "custom-uuid")
@GenericGenerator(name = "custom-uuid", strategy = CustomUUIDGenerator.STRATEGY_UUID)
private String id;
/**
* 密码
*/
@Column(name = "user_password")
@AutoComputed(command = ComputedCommand.MD5)
private String userPassword;
/**
* 用户名称
*/
@Column(name = "user_name")
private String userName;
/**
* 创建人ID
*/
@Column(name = "create_id")
@AutoComputed
private String createId;
/**
* 创建时间
*/
@Column(name = "create_time")
@AutoComputed(command = ComputedCommand.DATETIME)
private Date createTime;
/**
* 更新人ID
*/
@Column(name = "update_id")
@AutoComputed(command = ComputedCommand.USERID)
private String updateId;
/**
* 排序字段
*/
@JsonIgnore
@Column(name = "sort_name")
@AutoComputed(command = ComputedCommand.PINYIN, ref = "userName")
private String sortName;
public ExampleUserInfo(String id) {
this.id = id;
}
}
```
类注解:**@Table**:数据表、**@Getter**:自动生成get方法、**@Setter**:自动生成set方法、**@Entity**:指定此类是一个实体**@NoArgsConstructor**:生成无参构造方法。
字段注解:**@Id** 唯一标识、**@GeneratedValue**标识id为自动生成、**@GenericGenerator**指定生成器、**@Column**指定关联的表字段
**@AutoComputed**标识字段根据规则进行自动填充,需要在Service层搭配**@AutoService**注解使用。例如
```java
@Column(name = "create_time")
@AutoComputed(command = ComputedCommand.DATETIME)
private Date createTime;
```
这里会以当前时间填充到**createTime**字段中。
目前command支持6种自动填充,分别为:
**AUTO**:根据字段名进行填充。
**MD5**:将字段值进行一次md5加密。
**DATETIME**:当前时间。
**USERID**:当前登录用户。
**UUID**:生成32位随机字符串。
**PINYIN**:将ref字段的拼音填充到此字段中
#### 6.1.6 业务代码开发
写好实体类后,就可以进行业务代码开发了。参照example-biz模块目录结构,主要分为视图层rest、service层、其它config、util根据自己需要创建,工程目录结构如下:
![example-biz](./imgs/example-biz-struct.png)
1. 在src/main/java中新建包名**com.sinra.ldp.example.demo**,并新建**rest****service**包。
2. 创建**DemoService**接口,后续有新增接口,都先写到这里。
![demoService接口](./imgs/example-new-interface.png)
3.**service**包下面新建实现类包**impl**,并创建实现类**DemoServiceImpl**,实现**DemoService**接口,添加**@Service**注解,实现所有的方法,并填上相应的实现代码。
![实现类](./imgs/example-new-impl.png)
4. 在rest包下面创建**DemoRest.java**,添加**@RestController****@RequestMapping("demo")**注解,注入DemoService,并开发相应的请求接口
![Rest代码](./imgs/example-new-rest.png)
这样一个列表接口就开发好了。
#### 6.1.7 SQL配置xml文件
如果有自定义sql的需求,在resources下新建query目录,并从example-biz模块resources目录下将**query-template.xsd****exampleservice-query.xml**拷贝到新建的**query**目录下。将exampleserrvice-query.xml修改为**demoservice-query.xml**,并将ref修改为**DemoServiceImpl**
![sql文件](./imgs/example-demo-sql.png)
参照query标签编写自己的sql语句,在ServiceImpl中使用**@QueryBind**来注入sql语句。
```java
/**
* jdbc查询, QueryBind所对应的sql语句在resource/query/exampleservice中
* 调用时 getUserListByJDBC(StringUtils.EMPTY); 注解会将sql语句自动注入到参数中
*
* @param sql
* @return 列表数据
*/
@QueryBind("examplelist")
@Override
public List<ExampleUserInfo> getUserListByJDBC(String sql, Map<String, Object> param) {
return jdbcDaoService.queryForList(sql, ExampleUserInfo.class, param);
}
```
方法调用:
```java
exampleServcie.getUserListByJDBC(StringUtils.EMPTY);
```
### 6.2. 运行调试
先在Terminal执行以下shell命令:
```shell
mvn clean install -U
#编译成功后
cd example-api
#上传实体类
mvn ldp:upload
```
开发时运行,可以直接点击IDEA的run按钮或者debug按钮
![运行调试按钮](./imgs/example-run.png)
也可以在Terminal中执行以下命令
```shell
# 如果当前为exampl-api
# cd ..
cd example-startup
java -jar example-startup-1.0-SNAPSHOT.jar
```
正确运行后,根据端口和接口地址拼接请求地址和参数,建议使用**POSTMAN**,get方法可以使用浏览器访问,例如访问http://localhost:8800/example/get/list
![请求案例](./imgs/example-request.png)
## 七、打包部署
开发完成后,可以将打包后的jar包,上传到服务器,使用以下命令后台启动。这里的方法仅供参考,也可以选择用其它方式。
```shell
nohup java -jar example-startup-1.0-SNAPSHOT.jar &
```
## 八、样例工程源码下载
### 8.1. git下载
```shell
git clone http://gitlab.dev.shxrtech.com/ldp/ldp-app-example.git
```
### 8.2. http下载
http://gitlab.dev.shxrtech.com/ldp/ldp-app-example/-/archive/master/ldp-app-example-master.zip
注:下载源码需注册信睿开发账号,账号申请发送邮件至: machao@shxrtech.com, qianli.ma@shxrtech.com
·
\ No newline at end of file
## JRE安装说明(CENTOS)
> 以下安装说明,仅适用于 Server OS:CentOS,其他系统可参考
### 1. 在linux终端中,进入安装包所在目录,解压jre-8u251-linux-x64.tar.gz文件
####
```shell
# 创建/usr/java目录
mkdir /usr/java
# 解压至/usr/java目录下
tar xvf jre-8u251-linux-x64.tar.gz -C /usr/java
```
### 2. 设置环境变量
#### 这里我们需要找到profile文件,添加环境变量
```shell
vim /etc/profile
```
#### 在profile文件下面追加写入下面信息
```shell
export JAVA_HOME=/usr/java/jre1.8.0_251
export CLASSPATH=.:$JAVA_HOME/lib
export PATH=$PATH:$JAVA_HOME/bin
```
#### 使用ESC键,并输入
```shell
:wq
```
#### 保存退出编辑
#### 执行以下命令,使配置生效
```shell
source /etc/profile
```
# Example样例工程开发文档
本文档介绍基于LDP框架的Example样例工程开发流程,包含如何进行增删改查操作、如何使用REST远程调用已存在的业务接口、如何使用缓存等等。
## 一、数据接口
LDP框架支持两种操作方式,分别为Hibernate、JDBC。这两种操作方式都支持增上改查、列表查询、分页查询等。
### 1.1. Hibernate
#### 1.1.1 Hibernate接口方法
<details>
<blockcode>
<pre>
/**
* 新增
*
* @param o entity 对象
*/
void insert(T o);
/**
* 批量新增
*
* @param list 列表数据
*/
void insertList(List<T> list);
/**
* hibernate merge操作
*
* @param o entity 对象
*/
void merge(T o);
/**
* merge批量操作
*
* @param list 列表
*/
void merge(List<T> list);
/**
* 更新
*
* @param o entity 对象
*/
void update(T o);
/**
* 动态更新,未设置的属性不做更新,默认以主键为查询条件
*
* @param o entity 对象
*/
void dynamicUpdate(T o);
/**
* 批量动态更新
*
* @param list 列表
*/
void dynamicUpdate(List<T> list);
/**
* 动态更新,设置属性为查询条件
*
* @param o
* @param whereParam 自定义查询条件 ,map中key必须为entity存在字段
*/
int dynamicUpdate(T o, Map<String, Object> whereParam);
/**
* 批量更新
*
* @param list 列表
*/
void update(List<T> list);
/**
* 批量更新
*
* @param clazz orm对象
* @param updateFields 待更新字段集
* @param whereParam 更新条件集
* field
* @return
*/
int update(Class<T> clazz, Map<String, Object> updateFields, Map<String, Object> whereParam);
/**
* 删除
*
* @param o entity 对象
*/
void delete(T o);
/**
* 批量删除
*
* @param clazz 需要修改的实体类
* @param whereParam key:字段名 value:字段值,根据字段条件删除记录
*/
int deleteList(Class<T> clazz, Map<String, Object> whereParam);
/**
* 删除
*
* @param clazz entity 类型
* @param id 唯一标识
*/
void delete(Class<T> clazz, K id);
/**
* 通过id查询
*
* @param clazz entity类型
* @param id 唯一标识
* @return entity 对象
*/
T findById(Class<T> clazz, K id);
/**
* 计数
*
* @param clazz entity 类型
* @return 数据条数
*/
int count(Class<T> clazz);
/**
* 通过sql计数
*
* @param sql sql语句
* @return 数据条数
*/
int countBySQL(String sql);
/**
* 通过sql和参数计数
*
* @param sql sql语句
* @param paramMap 参数
* @return 数据条数
*/
int countBySQL(String sql, Map<String, Object> paramMap);
/**
* 查询所有数据
*
* @param clazz entity类型
* @param orderColumns 排序字段
* @return 数据集
*/
List<T> findALL(Class<T> clazz, OrderColumn... orderColumns);
/**
* hibernate 方式查询带参数
*
* @param clazz entity类型
* @param paramMap 参数
* @param orderColumns 排序字段
* @return 数据集
*/
List<T> findByHql(Class<T> clazz, Map<String, Object> paramMap, OrderColumn... orderColumns);
/**
* hibernate 方式分页查询,带参数
*
* @param clazz entity类型
* @param paramMap 参数
* @param pageIndex 当前页
* @param pageSize 页面数据量
* @param orderColumns 排序字段
* @return 分页数据
*/
Pagination findPageByHql(Class<T> clazz, Map<String, Object> paramMap, Object pageIndex, Object pageSize, OrderColumn... orderColumns);
/**
* hibernate 方式分页查询,带参数
*
* @param clazz entity类型
* @param pageIndex 当前页
* @param pageSize 页面数据量
* @param orderColumns 排序字段
* @return 分页数据
*/
Pagination findPageByHql(Class<T> clazz, Object pageIndex, Object pageSize, OrderColumn... orderColumns);
/**
* 通过sql和参数查询entity数据集
*
* @param sql sql语句
* @param clazz entity类型
* @param paramMap 参数
* @return 数据集
*/
List<T> findEntityBySQL(String sql, Class<T> clazz, Map<String, Object> paramMap);
/**
* 通过sql查询entity数据集
*
* @param sql sql语句
* @param clazz entity类型
* @return 数据集
*/
List<T> findEntityBySQL(String sql, Class<T> clazz);
/**
* 通过sql和参数查询分页数据集
*
* @param sql sql语句
* @param clazz entity类型
* @param paramMap 参数
* @param pageIndex 当前页
* @param pageSize 页面数据量
* @return 数据集
*/
List<T> findEntityBySQL(String sql, Class<T> clazz, Map<String, Object> paramMap, Object pageIndex, Object pageSize);
/**
* 通过sql查询分页数据
*
* @param sql sql语句
* @param clazz entity类型
* @param pageIndex 当前页
* @param pageSize 页面数据量
* @return 数据集
*/
List<T> findEntityBySQL(String sql, Class<T> clazz, Object pageIndex, Object pageSize);
/**
* 通过sql查询二维数组
*
* @param sql sql语句
* @return 数据集
*/
List<Object[]> findListArrayBySql(String sql);
/**
* 通过sql和参数查询二维数组
*
* @param sql sql语句
* @param paramMap 参数
* @return 数据集
*/
List<Object[]> findListArrayBySql(String sql, Map<String, Object> paramMap);
/**
* 通过sql和参数查询分页二维数组
*
* @param sql sql语句
* @param paramMap 参数
* @param pageIndex 当前页
* @param pageSize 数据量
* @return 数据集
*/
List<Object[]> findListArrayBySql(String sql, Map<String, Object> paramMap, Object pageIndex, Object pageSize);
/**
* 通过sql查询List<Map> 数据
*
* @param sql sql语句
* @return 数据集
*/
List<Map<String, Object>> findListMapBySql(String sql);
/**
* 通过sql查询List<Map> 数据
*
* @param sql sql语句
* @param paramMap 参数
* @return 数据集
*/
List<Map<String, Object>> findListMapBySql(String sql, Map<String, Object> paramMap);
/**
* 通过sql查询分页List<Map> 数据
*
* @param sql sql语句
* @param paramMap 参数
* @param pageIndex 当前页
* @param pageSize 数据量
* @return 数据集
*/
List<Map<String, Object>> findListMapBySql(String sql, Map<String, Object> paramMap, Object pageIndex, Object pageSize);
/**
* 通过sql和参数查询实体分页数据
*
* @param sql sql语句
* @param clazz entity类型
* @param paramMap 参数
* @param pageIndex 当前页
* @param pageSize 数据量
* @return 分页数据
*/
Pagination queryPageForListEntity(String sql, Class<T> clazz, Map<String, Object> paramMap, Object pageIndex, Object pageSize);
/**
* 通过sql和参数查询List<Map>分页数据
*
* @param sql sql语句
* @param paramMap 参数
* @param pageIndex 当前页
* @param pageSize 数据量
* @return 分页数据
*/
Pagination queryPageForListMap(String sql, Map<String, Object> paramMap, Object pageIndex, Object pageSize);
/**
* 通过sql查询实体分页数据
*
* @param sql sql语句
* @param clazz entity类型
* @param pageIndex 当前页
* @param pageSize 数据量
* @return 分页数据
*/
Pagination queryPageForListEntity(String sql, Class<T> clazz, Object pageIndex, Object pageSize);
/**
* 通过sql和参数查询List<Map>分页数据
*
* @param sql sql语句
* @param pageIndex 当前页
* @param pageSize 数据量
* @return 分页数据
*/
Pagination queryPageForListMap(String sql, Object pageIndex, Object pageSize);
int executeSqlUpdate(String sql, Map<String, Object> params);
/**
* 事务级多query执行
*
* @param listQuery 事务集合
* @return 影响行数
*/
int executeTrans(LinkedList<TranscationQuery> listQuery);
</pre>
</blockcode>
</details>
#### 1.1.2 Hibernate调用示例
参照com.sinra.ldp.example.service.impl.ExampleServiceImpl.java,这里需要注意的是 **@AutoService** 注解,这个注解主要是在新增和更新时,对于实体类中配置了@AutoComputed字段根据规则进行自动填充。例如
```java
@Column(name = "create_time")
@AutoComputed(command = ComputedCommand.DATETIME)
private Date createTime;
```
这里会以当前时间填充到createTime字段中。command目前支持6种自动填充,分别为:
AUTO:根据字段名进行填充。
MD5:将字段值进行一次md5加密。
DATETIME:当前时间。
USERID:当前登录用户。
UUID:生成32位随机字符串。
PINYIN:将ref字段的拼音填充到此字段中。
```java
// AutoService注解在新增时会对实体类中配置了@AutoComputed字段根据规则进行填充
@Override
@AutoService
public void add(ExampleUserInfo exampleUserInfo) {
genericDaoService.insert(exampleUserInfo);
}
@Override
@AutoService
public void add(List<ExampleUserInfo> list) {
genericDaoService.insertList(list);
}
@Override
public ExampleUserInfo find(String s) {
return (ExampleUserInfo) genericDaoService.findById(ExampleUserInfo.class, s);
}
// AutoService注解在更新时会对实体类中updateId、updateTime、sortName进行自动填充
@Override
@AutoService(init = false)
public void update(ExampleUserInfo exampleUserInfo) {
genericDaoService.update(exampleUserInfo);
}
@Override
@AutoService(init = false)
public void update(List<ExampleUserInfo> list) {
genericDaoService.update(list);
}
@Override
public void update(Class<ExampleUserInfo> aClass, Map<String, Object> updateFileds, Map<String, Object> whereParam) {
genericDaoService.update(aClass, updateFileds, whereParam);
}
@Override
public void delete(ExampleUserInfo exampleUserInfo) {
genericDaoService.delete(exampleUserInfo);
}
@Override
public void delete(List<ExampleUserInfo> list) {
String[] idList = new String[list.size()];
Map<String, Object> param = new HashMap<>(1);
param.put("id", idList);
genericDaoService.deleteList(ExampleUserInfo.class, param);
}
@Override
public List<ExampleUserInfo> findList(Map<String, Object> map) {
return genericDaoService.findByHql(ExampleUserInfo.class, map);
}
@Override
public Pagination<ExampleUserInfo> findPage(Pagination page, Map<String, Object> map) {
return genericDaoService.findPageByHql(ExampleUserInfo.class, map, page.getPageIndex(), page.getPageSize());
}
```
**PS:带有sql参数的方法使用请参照[JDBC调用示例](#JDBC调用示例)**
### 1.2. JDBC
#### 1.2.1 JDBC接口方法
<details>
<blockcode>
<pre>
/**
* 分页查询,带参数,返回分页对象,data类型为list:entity
* 进行分页查询
*
* @param sql sql语句
* @param entityType entity类型
* @param paramMap 参数
* @param pageIndex 当前页码
* @param pageSize 数据量
* @return 分页数据
*/
public <T> Pagination queryPageForListEntity(String sql, Class<T> entityType, Map<String, Object> paramMap, int pageIndex, int pageSize);
/**
* 分页查询,返回分页对象,data类型为list:entity
* 进行分页查询
*
* @param sql sql语句
* @param entityType entity类型
* @param pageIndex 当前页码
* @param pageSize 数据量
* @return 分页数据
*/
public <T> Pagination queryPageForListEntity(String sql, Class<T> entityType, int pageIndex, int pageSize);
/**
* 分页查询,带参数,返回分页对象,data类型为list:map
*
* @param sql sql语句
* @param paramMap 参数
* @param pageIndex 当前页
* @param size 数据量
* @return 分页数据
*/
public <T> Pagination queryPageForListMap(String sql, Map<String, Object> paramMap, int pageIndex, int size);
/**
* 分页查询,带参数,返回分页对象,data类型为list:map
*
* @param sql sql语句
* @param pageIndex 当前页
* @param size 数据量
* @return 分页数据
*/
public <T> Pagination queryPageForListMap(String sql, int pageIndex, int size);
/**
* 查询不带参数,返回list:map
*
* @param sql sql语句
* @return 数据集
*/
public List<Map<String, Object>> queryForList(String sql);
/**
* 查询带参数,返回list:map
*
* @param sql sql语句
* @param paramMap 参数
* @return 数据集
*/
public List<Map<String, Object>> queryForList(String sql, Map<String, Object> paramMap);
/**
* 查询不带参数,返回list:entity
*
* @param sql sql语句
* @param entityType entity类型
* @return 数据集
*/
public <T> List<T> queryForList(String sql, Class<T> entityType);
/**
* 查询带参数,返回list:entity
*
* @param sql sql语句
* @param entityType bean类型
* @param paramMap 参数map
* @return 数据集
*/
public <T> List<T> queryForList(String sql, Class<T> entityType, Map<String, Object> paramMap);
/**
* 执行sql
*
* @param sql sql语句
* @return 影响行数
*/
public int excute(String sql);
/**
* 执行sql 带参数
*
* @param sql sql语句
* @param paramMap 参数
* @return 影响行数
*/
public int excute(String sql, Map<String, Object> paramMap)
/**
* 批量sql执行,不带参数
*
* @param sql sql语句
* @return 影响行数数组
*/
public int[] batchExcute(String... sql);
/**
* 执行同一sql,批量参数
*
* @param sql sql语句
* @param listParamMap 参数
* @return 影响行数数组
*/
public int[] execute(String sql, List<Map<String, Object>> listParamMap);
/**
* 事务级多query执行
*
* @param listQuery 事务集合
* @return 影响行数
*/
int executeTrans(LinkedList<TranscationQuery> listQuery);
</pre>
</blockcode>
</details>
#### 1.2.2 JDBC调用示例
在example-biz模块,src/main/resources/query目录下新建xxxxservice-query.xml文件【建议拷贝**exampleservice-query.xml**文件】。
1、修改**queryspace**标签中的**ref**属性,ref属性表示文件中的sql语句会在哪里使用,例如下面表示sql语句会在**com.sinra.ldp.example.service.impl.ExampleServiceImpl**中使用到
```xml
<?xml version="1.0" encoding="UTF-8"?>
<queryspace
xmlns="http://www.w3schools.com"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="query-template.xsd"
ref="com.sinra.ldp.example.service.impl.ExampleServiceImpl">
<query id="examplelist" author="machao" remark="列表数据">
<!-- 使用【:参数名】预留参数,在#间,表示可选,如果不传,就会忽略此参数, 建议在参数和#间添加空格 -->
<!-- 根据数据库类型自动读取对应标签中的sql语句,如果不存在,默认读取sql标签下的语句 -->
<sql>
<![CDATA[
select * from ldp_mcs_user_info where # user_type=:userType #
]]>
</sql>
<oracle>
<![CDATA[
select * from ldp_mcs_user_info where # user_type=:userType #
]]>
</oracle>
<mysql>
<![CDATA[
select * from ldp_mcs_user_info where # user_type=:userType #
]]>
</mysql>
<sqlserver>
<![CDATA[
select * from ldp_mcs_user_info where # user_type=:userType #
]]>
</sqlserver>
</query>
</queryspace>
```
2、在对应的的实现类中使用**@QueryBind**注解,这个注解会将指定sql自动注入到方法的**第一个参数**中。
```java
/**
* jdbc查询, QueryBind所对应的sql语句在resource/query/exampleservice中
* 调用时 getUserListByJDBC(StringUtils.EMPTY); 注解会将sql语句自动注入到参数中
*
* @param sql
* @return
*/
@QueryBind("examplelist")
@Override
public List<ExampleUserInfo> getUserListByJDBC(String sql, Map<String, Object> param) {
List<ExampleUserInfo> exampleUserInfos = jdbcDaoService.queryForList(sql, ExampleUserInfo.class, param);
return exampleUserInfos;
}
```
3、在其它地方调用上面定义的方法时,第一个参数直接传入**StringUtils.EMPTY**
```java
Map<String, Object> param = new HashMap<>(1);
param.put("userType", 0);
List<ExampleUserInfo> exampleUserInfos = exampleService.getUserListByJDBC(StringUtils.EMPTY, param);
```
## 二、远程调用其它模块接口
对于已存在的其它服务接口,需要通过Rest 请求来调用,这里推荐使用**RestTemplate**,根据 http://api.dev.shxrtech.com/ 的API文档,使用**NACOS**自动解析服务名。例如,调用**mcs-service**模块的[**/user/get/page**]( http://api.dev.shxrtech.com/project/25/interface/api/414 )接口,拼接完整的请求地址为http://mcs-service/user/get/page ,根据文档,必填参数为pageIndex,pageSize,代码如下:
```java
/**
* rest查询,使用服务名+接口名: 这里使用mcs-service服务,调用用户获取列表接口
*
* @return
*/
@Override
public Pagination getUserListByRest() {
HttpHeaders headers = new HttpHeaders();
headers.add("Accept", MediaType.APPLICATION_JSON.toString());
HttpEntity<String> entity = new HttpEntity(headers);
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl("http://mcs-service/user/get/page")
.queryParam("pageIndex", 1)
.queryParam("pageSize", 5);
ResponseEntity<RestResult<Pagination<Map<String, Object>>>> response = restTemplate.exchange(builder.toUriString(),
HttpMethod.GET,
entity,
new ParameterizedTypeReference<RestResult<Pagination<Map<String, Object>>>>() {
});
return response.getBody().getData();
}
```
## 三、缓存使用
在LDP中缓存分为两种,分别为**SessionCache****ApplicationCache**。在使用**SessionCache**之前,需要先通过**UAA**的登录,或者在请求头中携带有效的**X-Ldp-Token****PS:当前没有会话或者会话过期后,SessionCache就不可用**】,而**ApplicationCache**只要不手动清空,缓存会一直存在。
| 类型 | 生命周期 | 缓存过期时间 |
| ---------------- | ---------------- | ---------------------------- |
| SessionCache | 跟随会话 | 会话过期 |
| ApplicationCache | 跟随应用生命周期 | 永不过期(除非手动删除缓存) |
缓存使用也有2中方式,分别为注解方式、手动调用put与get,使用注解更简单,但是使用put和get更灵活,请根据使用场景判断使用适合的方案。
### 3.1. 注解方式
缓存注解目前支持两种 **@SessionCacheable****@ApplicationCacheable** ,将注解添加到Rest层方法上,会自动将方法结果缓存下来,之后再次调用此接口,会直接从缓存中获取。例如(参考example-biz模块中的com.sinra.ldp.example.rest.ExampleRest.java):
```java
/**
* jdbc 获取用户list
*
* @param paramMap
* @return
* @throws Exception
*/
@GetMapping("/jdbc/list")
@SessionCacheable
public RestResult jdbcList(@RequestParamMap(typeCovertMapping = ExampleUserInfo.class) Map<String, Object> paramMap) throws Exception {
List<ExampleUserInfo> userInfoList = exampleService.getUserListByJDBC(StringUtils.EMPTY, paramMap);
return new RestResult(ResultMsg.SUCCESS, userInfoList);
}
/**
* rest获取用户分页数据
*
* @return
*/
@GetMapping("/rest/page")
@ApplicationCacheable
public RestResult restList() {
return new RestResult(ResultMsg.SUCCESS, exampleService.getUserListByRest());
}
```
### 3.2. 手动put、get
先在需要使用的地方注入**ApplicationCache**或者**SessionCache**,然后根据业务调用对应的get、put方法。参考example-biz模块中的com.sinra.ldp.example.service.impl.ExampleServiceImpl.java:
```java
@Autowired
ApplicationCache applicationCache;
/**
* 使用ApplicationCache做列表数据缓存
*
* @return 列表数据
*/
@Override
public List<ExampleUserInfo> findListByApplicationCache() {
//缓存key
String cacheKey = this.getClass().getName() + "findListByApplicationCache";
//如果缓存有这个数据,则从缓存中获取
if (applicationCache.get(cacheKey) != null) {
return applicationCache.get(cacheKey, List.class);
}
//如果没有则从DB获取,并放到缓存中
List<ExampleUserInfo> list = genericDaoService.findByHql(ExampleUserInfo.class, new HashMap<>(1));
applicationCache.put(cacheKey, list);
return list;
}
```
### 3.3. 获取当前登录用户信息
通过SessionCache,可以获取当前登录用户信息。先注入 **SessionCache** ,然后调用 **existUserToken()** 判断是否存在token,如果存在,就可以获取当前登录用户信息,参考example-biz模块中的com.sinra.ldp.example.service.impl.ExampleServiceImpl.java:
```java
@Autowired
SessionCache sessionCache;
/**
* 从Session缓存中获取当前登录用户,如果没有登录用户则返回一个空用户
*
* @return 当前用户
*/
@Override
public LdpMcsUserInfo getCurrentUser() {
if (sessionCache.existUserToken()) {
return sessionCache.getCurrentUser();
}
return new LdpMcsUserInfo();
}
```
### 3.4. 刷新缓存
**SessionCache** 可以通过重新登录的方式刷新缓存,也可以通过重新调用put方法刷新,**ApplicationCache** 是不会过期的,需要调用put方法刷新缓存。 **ApplicationCache** 以及 **SessioinCache** 都提供了 **getCacheKey** 方法来获取缓存key,在想要刷新缓存的地方直接获取到缓存key,然后put最新数据到缓存中,下面是一个案例:
```java
@Autowired
ApplicationCache applicationCache;
/**
* rest获取用户分页数据
*
* @return
*/
@GetMapping("/rest/page")
//这里使用注解方式
@ApplicationCacheable
public RestResult getListByRest() {
return new RestResult(ResultMsg.SUCCESS, exampleService.getUserListByRest());
}
/**
* 刷新rest获取用户分页参数结果
*
* @return
*/
@GetMapping("/rest/page/refresh")
public RestResult refreshGetListByRest() {
Pagination pagination = exampleService.getUserListByRest();
// 获取到缓存key
String cacheKey = applicationCache.getCacheKey(ExampleRest.class, "getListByRest");
// 重新存入数据到缓存中
applicationCache.put(cacheKey, pagination);
return new RestResult(ResultMsg.SUCCESS, pagination);
}
```
## 四、编码规则使用
LDP框架支持自动生成编码,在使用编码规则时,需预先在系统中配置一个编码规则。各业务模块在使用的时候,
根据编码规则的Key,进行生成最新的一个编码。生成编码后,将生成的编码set到业务实体当中的编码字段中,保存到数据库持久化即可。
**依赖common-codingrule**
```xml
<dependency>
<groupId>com.sinra.ldp</groupId>
<artifactId>common-codingrule</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
```
```java
@Autowired
private CodingruleUtils codingruleUtils;
/**
* 根据预先设置好的编码规则,进行生成编码。
* codingruleUtils.getNumber(key),业务模块代码中,可直接进行注入使用,依赖common-codingrule包
* @param key 编码规则的Key
* @return
*
*/
@GetMapping("/generateNumber")
public RestResult generateNumber(String key){
String number = codingruleUtils.getNumber(key);
/**
* 获取到number后,set到业务实体中即可
* 业务代码.......
*
*/
return new RestResult(ResultMsg.SUCCESS,number);
}
```
## 五、接口调用日志使用介绍
在实际系统业务应用当中,少不了的是系统与系统之间的数据传输。一般情况下是利用系统之间开放接口进行调用。
记录接口调用日志,也成为了系统必须可少的一部分。方便开发人员排查问题。
在LDP框架当中,开放对外的接口或调用了外部系统的接口方法内,需进行记录接口调用日志。
依赖**common-log**模块
```xml
<dependency>
<groupId>com.sinra.ldp</groupId>
<artifactId>common-log</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
```
```java
@Autowired
private InterfaceLogBaseService interfaceLogBaseService;
```
```java
InterfaceAddLogVo interfaceAddLogVo = new InterfaceAddLogVo();
String url = "http://localhost:8800/interface/test";
String params = "&name=lisw&number=001";
String result= sendPost(url,params);
interfaceAddLogVo.setIsOut(true)
.setName("Example工程测试接口")
.setParams(params)
.setResult(result)
.setUrl(url)
.setReceiver("信睿LDP框架接收")
.setSender("信睿LDP框架发送");
interfaceLogBaseService.saveLog(interfaceAddLogVo,request);
```
**注:**
isOut字段标识接口类型,false:外部系统调用本系统,true:本系统调用外部系统,当isOut字段为false时,url字段可不进行设值,saveLog方法会自动通过request进行获取。反之,isOut字段为true时,url字段值需设置为外部系统的接口地址,saveLog方法的request参数,传Null即可。
## 六、数据字典的引用
各业务模块实体中,如果有需要用到数据字典的字段,则将数据字典的主键ID存入到业务实体当中即可。在业务实体当中利用Hibernate关联即可。
```java
/**
* 人员性别
*/
@ManyToOne(fetch = FetchType.EAGER,cascade = {CascadeType.REFRESH})
@JoinColumn(name = "sex")
private DictInfo2 sex;//数据类别
```
# LDP 框架自定义注解使用说明文档
本文档是帮助开发者快速熟悉LDP框架中的自定义注解。
## 一、自动填充注解
### 1.1. @AutoComputed
依赖:base-api。
#### 1.1.1 注解介绍
**@AutoComputed**是一个字段注解,标识**实体类**字段为自动填充的注解,需要配合服务层的 **@AutoService** 使用,拥有两个属性,一个是command、一个是ref。注解可以通过command标识字段自动装配的方式,根据字段名查找对应计算方法,或根据command类型查找对应系统计算方法。command默认值为 **ComputedCommand.AUTO** ,以下是内置command每个值的解释:
**AUTO**:根据字段名进行填充。
**MD5**:将字段值进行一次md5加密。
**DATETIME**:当前时间。
**USERID**:当前登录用户。
**UUID**:生成32位随机字符串。
**PINYIN**:将ref字段的拼音填充到此字段中
#### 1.1.2 使用案例
```java
// 值为AUTO,通过属性名查找计算方式,这里的计算方式就是createId
@AutoComputed
private String createId;
// 值为MD5,将原本的明文做一次md5加密并重新赋值到字段中
@AutoComputed(command = ComputedCommand.MD5)
private String userPassword;
// 值为DATETIME,将当前时间填充到字段中
@AutoComputed(command = ComputedCommand.DATETIME)
private Date createTime;
// 值为USERID,将当前登录用户ID填充到字段中
@AutoComputed(command = ComputedCommand.USERID)
private String updateId;
// 值为UUID,将随机生成的UUID填充到字段中
@AutoComputed(command = ComputedCommand.UUID)
private String uuId;
// 值为PINYIN,ref为userName,将userName字段的中文拼音自动填充到字段中
@AutoComputed(command = ComputedCommand.PINYIN, ref = "userName")
private String sortName;
```
#### 1.1.3 自定义填充算法
当command值为 **ComputedCommand.AUTO** 时,会通过字段名查找计算方式,这里介绍一下如何自定义填充算法。自动填充算法分为两种:
- 一种是有参算法,例如MD5算法需要使用到原字段中的值作为参数:
```java
// 值为MD5,将原本的明文做一次md5加密并重新赋值到字段中
@AutoComputed(command = ComputedCommand.MD5)
private String userPassword;
```
有参算法,需要实现 **ComputerArgs** 接口,将自动填充算法写到computed中。例如Md5填充算法代码如下:
```java
public class ComputerMd5 implements ComputerArgs {
@Override
public String computed(Object plain) {
//这里进行处理,并返回自动填充内容
return DigestUtils.md5Hex(plain.toString());
}
}
```
- 一种是无参算法,例如DATETIME算法就是一个无参算法,只需要获取当前时间:
```java
// 值为DATETIME,将当前时间填充到字段中
@AutoComputed(command = ComputedCommand.DATETIME)
private Date createTime;
```
无参算法与有参类似,只是没有参数,需要实现 **ComputerNoArgs** 接口
```java
public class ComputerDate implements ComputerNoArgs<Date> {
@Override
public Date computed() {
return new Date();
}
}
```
- 完成算法编写后,还需要将计算类与字段名对应起来。
在service包中新建computed包(推荐),新建一个类,加上 **@Configuration** 注解,在 **@Bean** 注解中的name属性填上字段名。例如字段createId的填充算法是一个无参算法,方法返回类型为 **ComputerNoArgs** ,如果为有参算法,则应该返回 **ComputerArgs**
```java
@Configuration
public class ComputedResolver {
@Bean(name = "createId")
public ComputerNoArgs getCreateId() {
return new ComputerUser();
}
}
```
当AOP在处理自动填充时,这个字段就会根据上面编辑的算法进行自动填充。
### 1.2. @AutoService
依赖:mcs-common
#### 1.2.1 注解介绍
**@AutoService**是一个方法注解,被 **@AutoService** 注解的方法,都会被AOP拦截,并根据实体类字段中的 **@AutoComputed** 规则对字段自动填充。拥有两个属性 mode、init,mode为填充方式,默认为 **ComputedMode.COMPUTED** ,init为是否初始化,初始化则全部填充,否则只填充updateId、updateTime、sortName。
默认取方法的第一个参数进行填充,支持单个实体,也支持List集合。
#### 1.2.2 使用案例
一般@AutoService用于新增方法,以及更新方法
```java
// 新增方法
@AutoService
public void add(LdpMcsElementInfo elementInfo) {
genericDaoService.insert(elementInfo);
}
// 更新方法
@AutoService(init = false)
public void update(LdpMcsElementInfo elementInfo) {
genericDaoService.dynamicUpdate(elementInfo);
}
```
### 1.3. @AutoRelativeComputed
依赖:mcs-common
#### 1.3.1 注解介绍
**@AutoRelativeComputed** 是一个方法注解,对使用JPA配置了关联关系实体类进行自动填充,主要作用于Service层,对新增关联和移除关联方法参数进行自动填充。
@AutoRelativeComputed字段详解:
**mode**:关联类型,目前支持RelativeMode.MTM(多对多)、RelativeMode.OTM(一对多)、RelativeMode.MTO(多对一)。**PS:一对多、多对一暂时不建议使用**
**inverse**:是否为逆向,false为单向绑定,true为双向绑定,默认为false。
**source**:参数源类型。
**target**:参数目标类型。
**joinField**:关联字段。
**inverseField**:逆向关联字段。
**exComputed**:扩展的填充字段。
#### 1.3.2 使用案例
##### 1). 多对多关系
这里举例为用户和角色的多对多关系。
用户表(user):
| 字段名称 | 类型 | 备注 |
| --------- | ------ | ---------------- |
| id | String | 用户ID,唯一标识 |
| user_name | String | 用户名 |
角色表(role):
| 字段名称 | 类型 | 备注 |
| --------- | ------ | ---------------- |
| id | String | 角色ID,唯一标识 |
| role_name | String | 角色名称 |
用户角色关联表(user_role):
| 字段名称 | 类型 | 备注 |
| -------- | ------ | ------------ |
| id | String | ID,唯一标识 |
| user_id | String | 用户ID |
| role_id | String | 角色ID |
1. 使用JPA配置实体类关联关系
**User.java**
```java
/**
* 与角色的多对多关联
*/
@JsonIgnoreProperties("userInfos")//避免互相关联死循环
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "user_role", //中间表的名称
joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id")},
inverseJoinColumns = {@JoinColumn(name = "role_id", referencedColumnName = "id")})
private Set<Role> roleInfos = new HashSet<>();
```
**Role.java**
```java
@JsonIgnoreProperties("roleInfos")
@ManyToMany(mappedBy = "roleInfos", fetch = FetchType.EAGER, targetEntity = User.class)
private Set<User> userInfos = new HashSet<>(0);
```
2. **UserServiceImpl**中添加方法和注解
```java
/**
* 添加角色关联
*
* @param id 用户ID
* @param list 通过注解将List<Role>转换为List<UserRole>
* @param param 作为删除条件,避免重复数据
*/
@AutoRelativeComputed(mode = RelativeMode.MTM, source = Role.class, target = UserRole.class, joinField = "userId", inverseField = "roleId")
public void addRoleToUser(String id, List list, Map<String, Object> param) {
LinkedList<TranscationEntity> entityList = new LinkedList<>();
// 删除重复数据
entityList.add(new TranscationEntity(TransItemExeType.DELETE_ENTITY, UserRole.class, param));
// 新增
entityList.add(new TranscationEntity(TransItemExeType.INSERT_ENTITY, list));
genericDaoService.executeTrans(entityList);
}
/**
* 移除角色关联
*
* @param id 用户ID
* @param list 通过注解将List<Role>转换为List<UserRole>
* @param param 作为删除条件,避免重复数据
*/
@AutoRelativeComputed(mode = RelativeMode.MTM, source = Role.class, target = UserRole.class,joinField = "userId", inverseField = "roleId")
public void removeRoleFromUser(String id, List list, Map<String, Object> param) {
genericDaoService.deleteList(UserRole.class, param);
}
```
#### 1.3.3 扩展填充算法
**@AutoRelativeComputed** 注解主要填充中间关联数据,有部分数据和关联无关,但是又必须填充,这种时候就需要 **exComputed** 来扩展填充算法,可以通过算法来填充其它字段。例如,岗位和组织多对多关联自动填充, exComputed字段值为**orgPosition**
```java
@AutoRelativeComputed(mode = RelativeMode.MTM, source = LdpMcsOrganization.class, target = LdpMcsPositionOrganization.class,joinField = "postId", inverseField = "orgId", exComputed = "orgPosition")
```
编写扩展算法需要实现 **CustomComputer** 接口,并在computed方法中编写自动填充逻辑。computed方法有一个参数,这个参数是注解中target类的对象,可以在方法中拿到对象,并对对象中的其它字段进行填充。这里对manager字段进行填充:
```java
public class ComputerOrgPosition implements CustomComputer {
@Override
public void computed(Object target) {
LdpMcsPositionOrganization positionOrganization = (LdpMcsPositionOrganization) target;
positionOrganization.setManager(false);
}
}
```
在有 **@Configuration** 注解类中完成Bean的装配:
```java
@Configuration
public class ExtraComputer {
/**
* 关联表其它业务数据填充
*
* @return
*/
@Bean(name = "orgPosition")
public CustomComputer getUserPositionComputer() {
return new ComputerOrgPosition();
}
}
```
## 二 、缓存注解
### 2.1. @ApplicationCacheable
依赖:common-cache
#### 2.1.1 注解介绍
**@ApplicationCacheable** 注解是方法注解,将注解添加到方法上,会自动将方法结果缓存下来,之后再次调用此接口,会直接从缓存中获取结果。缓存key是自动生成的,生成算法为application:+包名+类名+方法名+:参数长度。其它属性字段与Spring cache 注解 **@Cacheable** 属性字段一致。
#### 2.1.2 使用案例
一般此注解在REST层使用,需要注意的是Application缓存不会过期,会一直存在,如果需要刷新,需要另外写刷新方法,建议在数据变化较小,数据量大的接口使用。
```java
/**
* rest获取用户分页数据
*
* @return
*/
@GetMapping("/rest/page")
@ApplicationCacheable
public RestResult getListByRest() {
return new RestResult(ResultMsg.SUCCESS, exampleService.getUserListByRest());
}
```
### 2.2. @SessionCacheable
依赖:common-cache
#### 2.2.1 注解介绍
**@SessionCacheable** 注解是方法注解,用法与 **@ApplicationCacheable** 注解基本一致,不一致的地方有两点:1、SessionCache生命周期是跟随登录的,也就是退出登录后失效; 2、缓存key生成规则不一致,生成算法:token:+包名+类名+方法名+:参数长度。
#### 2.2.2 使用案例
退出登录后,Session缓存失效
```java
/**
* jdbc 获取用户list
*
* @param paramMap
* @return
* @throws Exception
*/
@GetMapping("/jdbc/list")
@SessionCacheable
public RestResult getListByJdbc(@RequestParamMap(typeCovertMapping = ExampleUserInfo.class) Map<String, Object> paramMap) throws Exception {
List<ExampleUserInfo> userInfoList = exampleService.getUserListByJDBC(StringUtils.EMPTY, paramMap);
return new RestResult(ResultMsg.SUCCESS, userInfoList);
}
```
## 三、日志注解
### 3.1. @AvoidRepeatableSubmit
#### 3.1.1 注解介绍
此注解配合定时任务进行使用,在被需要定时任务调用的服务接口上,添加此注解防止接口由于网络波动或其他原因重复调用。
#### 3.2.2 使用案例
```java
@PostMapping(value = "/testExampleJob")
@AvoidRepeatableSubmit
public RestResult testJob(@RequestParam String taskJobId, @RequestBody ExampleUserInfo userInfo){
exampleService.add(userInfo);
return new RestResult(ResultMsg.SUCCESS,"");
}
```
**使用时仅需要在被定时任务调用的接口上添加@AvoidRepeatableSubmit即可**
### 3.2. @SysAuditLog
#### 3.2.1 注解介绍
@SysAuditLog 是一个方法注解,需要放在controller方法上。 会拦截包裹的方法,会根据登陆信息自动获取当前用户。此注解进行被请求方法拦截,获取请求地址、请求参数、请求内容、请求方法(GET OR POST)、客户端浏览器信息。
根据请求状态,判断是异常请求还是正常请求。
#### 3.2.2 使用案例
```java
/**
* 用户列表(非分页)
*
* @param paramMap 字段条件
* @return
*/
@GetMapping("/get/list")
@SysAuditLog(moduleKey = "example",subKey = "test")
public RestResult findList(@RequestParamMap(typeCovertMapping = ExampleUserInfo.class) Map<String, Object> paramMap) throws Exception {
List<ExampleUserInfo> userInfoList = exampleService.findList(paramMap);
return new RestResult(ResultMsg.SUCCESS, userInfoList);
}
```
**使用时需传递 moduleKey(模块名称,如果是新模块需提前建立表,以ldp_audit_log_ 开头+ modulekey 录入的值结尾的表 如ldp_audit_log_example , 表结构可参考 ldp_audit_log_sys), subKey 子模块名称最终会保存在日志中作为二级子模块**;
### 3.3. @SysJobLog
#### 3.3.1 注解介绍
由于系统架构为微服务,服务与服务之间解耦合。因此定时任务设计之初,考虑为定时接口任务调用。
定时任务执行接口调用, 在被调用接口引用的Service具体业务方法添加此@SysJobLog 注解。
注解包含两大功能,1 异步执行,防止接口执行时间过长,否则定时任务调用机器一直连接被占用; 2 更新定时任务执行情况。
定时任务执行流程:
1 定时任务调度服务启动后,满足定时条件自动执行任务调度。 根据后台配置好的服务名称、接口信息调用其他服务接口。
2 被调用接口,必须添加@RequestParam String taskJobId 作为接收参数,此参数后续作为更新任务执行状态使用。由于在业务方法上添加@SysJobLog, 业务方法与Controller不在同一个线程中,无法
获取上一个线程中的信息。因此具体业务Service也必须添加名称为String taskJobId 的参数,后续会自动获取参数的值进行更新任务执行情况。
3 被调用接口也必须添加 @AvoidRepeatableSubmit 防止重复执行
#### 3.3.2 使用案例
Controller
```java
@PostMapping(value = "/testExampleJob")
@AvoidRepeatableSubmit
public RestResult testJob(@RequestBody ExampleUserInfo userInfo, @RequestParam String taskJobId){
exampleService.doJob (userInfo, taskJobId);
return new RestResult(ResultMsg.SUCCESS,"");
}
```
具体Service
```java
@Override
@AutoService
@SysJobLog
public void doJob(ExampleUserInfo exampleUserInfo, String taskJobId) {
genericDaoService.insert(exampleUserInfo);
}
```
**具体业务方法上添加@SysJobLog 进行异步任务及String taskJobId 参数,名称需要保持一致,系统会自动进行更新执行状态**``
## 四、数据库查询注解
### 4.1. @QueryBind
依赖:common-query
#### 4.1.1 注解介绍
@QueryBind是一个方法注解,将 **xxxxservice-query.xml** 中的参数SQL语句绑定到方法中的第一个参数中。需要先在 **xxxxservice-query.xml** 中将ref属性指向绑定的xxxServiceImpl类中。
#### 4.1.2 使用案例
**Service层代码:**
```java
/**
* jdbc查询, QueryBind所对应的sql语句在resource/query/exampleservice中
* 调用时 getUserListByJDBC(StringUtils.EMPTY); 注解会将sql语句自动注入到参数中
*
* @param sql
* @return
*/
@QueryBind("examplelist")
@Override
public List<ExampleUserInfo> getUserListByJDBC(String sql, Map<String, Object> param) {
List<ExampleUserInfo> exampleUserInfos = jdbcDaoService.queryForList(sql, ExampleUserInfo.class, param);
return exampleUserInfos;
}
```
**REST层代码:**调用时 **getUserListByJDBC(StringUtils.EMPTY)**; 注解会将sql语句自动注入到参数中
```java
Map<String, Object> param = new HashMap<>(1);
param.put("userType", 0);
List<ExampleUserInfo> exampleUserInfos = exampleService.getUserListByJDBC(StringUtils.EMPTY, param);
```
### 4.2. @QueryContextComponent与 @QueryParams
依赖:common-query
#### 4.2.1 注解介绍
**@QueryContextComponent**是一个类注解, **@QueryParams** 是一个方法注解。除了使用 **@QueryBind** 注解注入sql到参数中,还可以使用 **@QueryContextComponent** 结合 **@QueryParams** 注解将xml中的sql全部注入到一个Map对象中。
#### 4.2.2 使用案例
首先在类名上添加 **@QueryContextComponent** 注解,定义一个sqlMap字段,加上 **@QueryParams** 注解,由于 **@QueryParams** 是一个方法注解,所以需要在字段上加上 **@Setter** ,这样会自动生成set方法,注解也会添加到自动生成的方法上
```java
@QueryContextComponent
public class UserBizService {
// 由于@QueryParams是一个方法注解,所以此处需要加上@Setter注解
@Setter
@QueryParams
public Map<String,String> sqlMap;
public void getSqlKey() {
sqlMap.forEach((k,v)->{
log.info("id:{} {} {} ",k,String.format("%n"),v);
});
}
}
```
## 五、REST层参数解析注解
### 5.1. @RequestParamMap
依赖:mcs-common
#### 5.1.1 注解介绍
**@RequestParamMap**是一个参数注解,主要用于在REST层接收GET参数时保持参数类型与实体类一致,可以直接将GET参数接收到的map作为查询条件,另外也是为了将分页参数从查询参数中区分开来。
#### 5.1.2 使用案例
直接在Map参数前使用**@RequestParamMap(typeCovertMapping = XXXX.class)**,这里的XXX指实体类类型。
```java
/**
* 用户列表(非分页)
*
* @param paramMap 字段条件
* @return
*/
@GetMapping("/get/list")
public List<LdpMcsUserInfo> findList(@RequestParamMap(typeCovertMapping = LdpMcsUserInfo.class) Map<String, Object> paramMap) throws Exception {
return userService.findList(paramMap);
}
```
### 5.2 @RequestParamPage
依赖:mcs-common
#### 5.2.1 注解介绍
**@RequestParamPage**是一个参数注解,主要用于GET请求时接收分页参数。
#### 5.2.2 使用案例
使用封装好的分页类 **Pagination** 来接收分页参数,在分页参数前添加 **@RequestParamPage** 即可。这里是将分页参数和查询参数进行拆分的案例:
```java
/**
* 用户列表(分页)
*
* @param paramPage 分页参数为pageIndex,pageSize
* @param paramMap 字段条件
* @return
*/
@GetMapping("/get/page")
public Pagination findPage(@RequestParamPage Pagination paramPage,
@RequestParamMap(typeCovertMapping = LdpMcsUserInfo.class) Map<String, Object> paramMap)
throws Exception {
return userService.findPage(paramPage, paramMap);
}
```
# git代码提交message规范
目前规范使用较多的是 [Angular 团队的规范](https://link.juejin.im/?target=https%3A%2F%2Flink.zhihu.com%2F%3Ftarget%3Dhttps%3A%2F%2Fgithub.com%2Fangular%2Fangular.js%2Fblob%2Fmaster%2FDEVELOPERS.md%23-git-commit-guidelines), 继而衍生了 [Conventional Commits specification](https://link.juejin.im/?target=https%3A%2F%2Flink.zhihu.com%2F%3Ftarget%3Dhttps%3A%2F%2Fconventionalcommits.org%2F). 很多工具也是基于此规范, 它的 message 格式如下:
```
<type>(<scope>): <subject>
// 空一行
<body>
// 空一行
<footer>
```
其中,`Header` 是必填,`Body``Footer` 是选填,`Header` 必填格式如下:
```
<type>: <subject>
```
### `Header`
`Header` 包括三个字段:`type`(必填)、`scope`(选填)和 `subject`(必填)
#### `type`
`type` 用于说明 `commit` 的类别,只允许使用下面 7 个标识。
- feat:新功能(feature)
- fix:修补bug
- docs:文档(documentation)
- style: 格式(不影响代码运行的变动)
- refactor:重构(即不是新增功能,也不是修改bug的代码变动)
- test:增加测试
- chore:构建过程或辅助工具的变动
- perf: 优化相关,比如提升性能、体验
`type``feat``fix`,则该 `commit` 将肯定出现在 `Change log` 之中。
#### `scope`
`scope` 用于说明 `commit` 影响的范围,比如数据层、控制层、视图层等等,视项目不同而不同。
#### `subject`
`subject``commit` 目的的简短描述,不超过50个字符
```
以动词开头,使用第一人称现在时,比如 change,而不是 changed 或 changes
第一个字母小写
结尾不加句号(.)
```
### `body`
`Body` 部分是对本次 `commit` 的详细描述,可以分成多行。下面是一个范例。
```
More detailed explanatory text, if necessary. Wrap it to
about 72 characters or so.
Further paragraphs come after blank lines.
- Bullet points are okay, too
- Use a hanging indent
```
### `Footer`
`Footer` 部分只用于两种情况
- 不兼容变动
如果当前代码与上一个版本不兼容,则 `Footer` 部分以 `BREAKING CHANGE` 开头,后面是对变动的描述、以及变动理由和迁移方法。
- 关闭 `Issue` 如果当前 `commit` 针对某个 `issue`,那么可以在 `Footer` 部分关闭这个 `issue`
```
Closes #123, #245, #992
```
# 新建管理员账号
[TOC]
打开LDP-MCS登录界面,使用admin账号登录。
## 1、创建新账号
打开菜单**系统维护**=>**账号管理**,点击**新增**按钮
![用户列表](../progress/36演示-用户列表.png)
在新增界面填入相关信息,这里演示创建一个账号为**demo**,密码为**123456**的账号,用户类型选择根据需求选择**管理**或者**应用**,由于我们是要一个管理账号,所以这里选择**管理**,其它信息根据需求选填,最后点击提交。
![演示新增](../progress/37演示-新增用户.png)
## 2、创建新角色
*PS:如果已经有管理员角色,可以选择跳过这一节。*
打开菜单**权限管理**=>**角色管理**,点击**新增**按钮
![角色列表](../progress/38演示-角色列表.png)
在新增弹窗中填写角色信息,角色类型一共三种应用、管理、数据,分别对应用菜单权限、管理菜单权限、组织数据权限,这里需要创建一个管理菜单权限的角色,选择**管理**,填入其它信息,点击**提交**按钮。
![新增角色](../progress/39演示-新增角色.png)
## 3、角色关联权限
打开菜单**权限管理**=>**管理菜单权限**
点击角色后面的**权限配置**
![权限配置](../progress/40演示-权限配置.png)
**备选关联菜单**中勾选需要的权限,这里为了演示,只选择了系统维护中的**账号管理****组织管理**,点击**保存**
![关联菜单](../progress/41演示-关联菜单.png)
这样,此角色就拥有了这些权限,接下来将角色和用户关联。
## 4、角色关联账号
将新建的角色与新建的账号关联起来。
![关联用户](../progress/42演示-关联用户.png)
## 5、切换账号
点击右上角头像,点击**退出登录**,会直接跳转到登录界面。
![退出登录](../progress/43演示-退出登录.png)
在登录界面重新登录刚才创建的demo账号
![登录demo](../progress/44演示-登录demo.png)
## 6、何查权限
检查权限是否与角色配置一致(账号权限为所有关联角色的权限总和,例如:账号同时关联角色A、角色B,角色A权限为**账号管理**,角色B权限为**职位管理**,那么此账号将同时拥有**账号管理****职位管理**权限)
![核查权限](../progress/45演示-核查权限.png)
[3、角色关联权限](#3、角色关联权限)中,demo管理角色只关联了**账号管理****组织管理**,所以菜单中也只有**账号管理****组织管理**
## 7、修改密码
新账号登录后,如果需要修改当前账号密码,点击右上角头像,再点击修改密码。在修改密码弹窗中填入旧密码、新密码点击**提交**
![修改密码](../progress/46演示-修改密码.png)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment