Commit 270b1666 authored by custom's avatar custom

Merge branch master

parents 45bd7b4f cf8e3a29
## 合同文本生成
合同文本生成允许用户定义不同内容的合同模板,通过各项数据配置,对其中的文字、表格、水印以及批注等内容进行修改。
1. 进入合同模板定义功能
先在表格中定义好模板的代码、名称、用途、类型以及说明等信息进行保存。
| 字段名 | 说明 |
| :--: | :--: |
| 模板代码 | 模板的唯一标示,不可重复 |
| 模板名称 | 用于标识一个文件模板 |
| 模板用途 | 暂时只有*合同文本*一种用途 |
| 模板类型 | |
| 说明 | 文件模板的说明 |
| 参数集 | 参与该模板生成时,所需要使用到的参数 |
![合同模板定义功能](/assets/tempDefine.png)
2. 上传合同模板
通过合同模板定义功能表格中的**模板上传**功能,将预先设计好的模板上传到服务器。**该功能只支持2007版本以上的,docx后缀的word文档**
![合同模板上传](/assets/tempUpload.png)
3. 定义合同模板参数
通过合同模板定义功能表格中**参数集**设置,将需要与合同模板关联的参数进行设置。
![合同模板参数集设置](/assets/paramSet.png)
- 若是有在已有的参数列表中没有需要的使用参数设置,则可以在参数集配置中手动添加
- 参数定义中的字段说明
> | 字段名 | 说明 |
> | :--: | :--: |
> | 书签代码 | 书签的唯一标识,需要和模板文件中定义的书签名称一致 |
> | 书签描述 | 对该书签的文字描述 |
> | 书签类型 | 书签类型目前有:文本、表单、表单域、横向表单四种 <ul><li>文本类型对应的是替换后为文档中普通文本 </li><li>表单类型对应着word文档中一个表格的数据,表格的每一列的配置信息都是在列配置中完成</li><li>表单域对应word文档中一个文本域\窗体域,最终的替换结果会显示在文本域的位置</li><li>横向表单对应的是替换的文档中,书签所对应的是文档中一整块内容,内容中可能包含了多个书签,每个书签的名称在列配置中进行配置,最后根据结果生成多块结构类似的文本</ul> |
> | 数据源 | 选择该参数的值来源,配置该项之后可不用配置SQL |
> | SQL | 从数据库进行数据查询使用的SQL语句 |
> | 列配置 | 只有在书签类型为表单或横向表单时,才可以进行配置 |
> | 字体型号 | 生成的文本使用的字体类型 |
> | 字体大小 | 生成的文本使用的字体大小 |
> | 下划线 | 生成的文本使用的下划线类型:无、单下划线、双下划线 |
> | 是否加粗 | 生成的文本是否需要加粗 |
![合同模板参数定义](/assets/paramDefine.png)
4. 去生成最终的合同文本
生成合同文本时在合同状态为**新建**时才可以生成合同文本
![合同文本生成入口](/assets/docxGenEntry.png)
![合同文本生成](/assets/docxGen.png)
\ No newline at end of file
## Excel文件导入
使用poi解析xml方式进行导入,只支持单表导入。
数据库批量导入默认batch为100.
支持多sheet导入,sheet1从第三行开始读取数据,其他sheet页从第一行导入数据。
使用方法:
1. Excel文件上传
```javascript
$("#files").kendoUpload({
async: {
saveUrl: "${base.contextPath}/hls/excel/import?${_csrf.parameterName}=${_csrf.token}&templateCode=HLS_FIN_STATEMENT_LN",
removeUrl: "remove"
},
showFileList: false,
upload: onUpload,
success: onSuccess
});
```
| 参数名 | 描述 |
| :--: | :--: |
| _csrf.token | 防跨域token |
| templateCode | 作为模块标识,会出现先在fnd_interface_header中,供后续业务逻辑进行处理 |
2. 处理数据
经过第一步的文件上传,后台会在fnd_interface_header表中生成一条数据,其中包含了传递的templateCode参数作为code,并且,在fnd_interface_lines表中按照顺序,将Excel文件中的每一行数据保存为一条数据,数据的第一列保存在attributes_1上,依次类推。
![ExcelImport](/assets/excelImport.png)
## Excel文件批量导出
本系统支持Excel文件导出排队处理,支持超大数据量,能实时查询文件生成情况,支持取消导出。
#### 前端使用方法
调用js方法
```javascript
Hel.exportExcel=function(opts){
var contextPath =opts.contextPath,
id=opts.id,
view_model=opts.viewModel,
viewModelData=opts.viewModelData,
controller_name = opts.controller,
fileName = opts.fileName,
temp_div_id=opts.tempDivId,
dataSourceId = opts.dataSourceId,
_csrf_token = opts._csrf_token;
```
| 参数名 | 描述 |
| :--: | :--: |
| contextPath | 项目的url路径 |
| id | 页面上grid的id |
| view_model | grid绑定的model数据模型 |
| viewModelData | model模型中的数据 |
| controller_name | 后端控制器名称 |
| fileName | excel文件名 |
| temp_div_id | 临时div的id |
| dataSourceId | 数据源id |
| _csrf_token | 防跨域的token |
#### 后端使用方法
1. 在代码中注入Excel导出Service
```java
@AutoWired
private ExcelExportServiceImpl excelService;
```
2. 在自己的controller中调用方法
```java
excelService.saveExportInfo(sqlId,iRequest,config,rowMaxNumber);
```
| 参数名 | 类型 | 描述 |
| :--: | :--: | :--: |
| sqlId | String | mybatis对应的数据库查询语句的ID |
| iRequest | IRequest | 带有上下文信息的reuqest对象 |
| config | ExportConfig | 包含Excel各列信息的对象,由前台传递json对象转换而来 |
| rowMaxNumber | int | 生成Excel的最大行数,可使用重载方法,不包含次参数默认为1,000,000 |
3. 运行Excel导出程序(获取该程序,请联系部门相关负责人)
```shell
java -jar hel-batch-parent.jar &
```
4. Excel导出情况查询
用户若是为管理员身份,则查询全部人的导出情况,否则只显示当前用户的导出数据
```java
excelService.queryExportInfo(status, iRequest);
```
| 参数名 | 类型 | 描述 |
| :--: | :--: | :--: |
| status | String | 指定Excel导出的状态,该参数可为空查询全部状态数据<br><ll><li>new->等待</li><li>generating->文件导出中</li><li>finished->导出完成</li><li>failed-> 导出失败</li></ll> |
| iRequest | IRequest | 带有上下文信息的reuqest对象 |
5. 下载已完成的Excel文件
```java
excel.downloadExcel(filePath, fileName, request, response);
```
| 参数名 | 类型 | 描述 |
| :--: | :--: | :--: |
| filePath | String | 需要下载的文件的路径 |
| fileName | String | 想要保存的文件的名称 |
| request | HttpServletRequest | 用户的请求对象 |
| response | HttpServletResponse | 用户的响应对象 |
**下载前,需要检查config.properties文件中的export.offerUrl配置项是否配置正确,且为hel-batch-parent.jar提供的文件下载url**
\ No newline at end of file
...@@ -30,25 +30,25 @@ ...@@ -30,25 +30,25 @@
### **主要属性** ### **主要属性**
| 属性名 | 类型 | | 属性名 | 类型 | 描述 |
| --- | --- | | :--- | :--- | :--- |
| data | Array & String | | data | Array & String | 数据源 |
| autoSync | Boolean | | autoSync | Boolean | 自动同步 |
| batch | Boolean | | batch | Boolean | 批处理 |
| page | Integer | | page | Integer | 每页显示大小 |
| pageSize | Integer | | pageSize | Integer | 提供每页显示的大小选择 |
| serverAggregates | Boolean | | serverAggregates | Boolean | 为true数据源将聚合远程计算 |
| serverFiltering | Boolean | | serverFiltering | Boolean | 为true将过滤远程数据 |
| serverGrouping | Boolean | | serverGrouping | Boolean | 为true将远程数据分组 |
| serverPaging | Boolean | | serverPaging | Boolean | 为true将远程数据分页 |
| serverSorting | Boolean | | serverSorting | Boolean | 为true将远程数据排序 |
| type | String | | type | String | 类型 |
| change | Function | | change | Function | 改变事件 |
| error | Function | | error | Function | 错误事件 |
| push | Function | | push | Function | 接受数据源推送式通知 |
| requestEnd | Function | | requestEnd | Function | 请求结束调用事件 |
| requestStart | Function | | requestStart | Function | 请求开始时调用事件 |
| sync | Function | | sync | Function | 自动同步改变的数据 |
> **提示:** 属性用法请参考kendoui API文档 [http://docs.telerik.com/kendo-ui/api/javascript/data/datasource](http://docs.telerik.com/kendo-ui/api/javascript/data/datasource) > **提示:** 属性用法请参考kendoui API文档 [http://docs.telerik.com/kendo-ui/api/javascript/data/datasource](http://docs.telerik.com/kendo-ui/api/javascript/data/datasource)
...@@ -64,10 +64,10 @@ ...@@ -64,10 +64,10 @@
#### **transport属性** #### **transport属性**
| 属性名 | 类型 | | 属性名 | 类型 | 描述 |
| --- | --- | | :--- | :--- | :--- |
| parameterMap | Function | | parameterMap | Function | 将请求数据改为远程接受的数据格式 |
| push | Function | | push | Function | 数据初始化时设置为推送式通知 |
```xml ```xml
<h:dataSource id="gridDataSource" > <h:dataSource id="gridDataSource" >
...@@ -86,14 +86,14 @@ ...@@ -86,14 +86,14 @@
* update * update
* destroy * destroy
| 属性名 | 类型 | | 属性名 | 类型 | 描述 |
| --- | --- | | :--- | :--- | :--- |
| cache | Boolean | | cache | Boolean | 设置为true将数据缓存住 |
| contentType | String | | contentType | String | 发送到服务器的内容的http的头 |
| data | Object \| Function | | data | Object \| Function | 发送到远程服务的附加参数 |
| dataType | String | | dataType | String | 服务器期望的返回类型 |
| type | String | | type | String | 请求类型 |
| url | String \| Function | | url | String \| Function | 请求地址 |
```xml ```xml
<h:transport parameterMap="parameterMap"> <h:transport parameterMap="parameterMap">
...@@ -110,14 +110,14 @@ ...@@ -110,14 +110,14 @@
#### **schema属性** #### **schema属性**
| 属性名 | 类型 | | 属性名 | 类型 | 描述 |
| --- | --- | | :--- | :--- | :--- |
| data | String \| Function | | data | String \| Function | 包含数据项的服务器的相应字段 |
| errors | String \| Function | | errors | String \| Function | 包含服务器端的错误的数据 |
| groups | String \| Function | | groups | String \| Function | 包含组的服务器端数据 |
| parse | Function | | parse | Function | 预处理解析服务器响应 |
| total | String \| Function | | total | String \| Function | 数据项的总数 |
| type | String | | type | String | 相应类型支持XML和JSON |
```xml ```xml
<h:dataSource id="gridDataSource" > <h:dataSource id="gridDataSource" >
...@@ -131,10 +131,10 @@ ...@@ -131,10 +131,10 @@
* model * model
| 属性名 | 类型 | | 属性名 | 类型 | 描述 |
| --- | --- | | :--- | :--- | :--- |
| id | String | | id | String | 唯一标识 |
| editable | Function | | editable | Function | 设置是否可编辑 |
```xml ```xml
<h:schema data="rows" total="total" errors="schemaError"> <h:schema data="rows" total="total" errors="schemaError">
...@@ -150,17 +150,17 @@ ...@@ -150,17 +150,17 @@
* field * field
| 属性名 | 类型 | | 属性名 | 类型 | 描述 |
| --- | --- | | :--- | :--- | :--- |
| name | String | | name | String | 名字 |
| defaultValue | Object | | defaultValue | Object | 默认值 |
| editable | Boolean | | editable | Boolean | 是否可编辑 |
| nullable | Boolean | | nullable | Boolean | |
| parse | Function | | parse | Function | 设为推送式 |
| type | String | | type | String | 类型 |
| from | String | | from | String | |
| checkedValue | String | | checkedValue | String | 为true设为选中 |
| uncheckedValue | String | | uncheckedValue | String | 为true设为没选中 |
```xml ```xml
<h:schema data="rows" total="total" errors="schemaError"> <h:schema data="rows" total="total" errors="schemaError">
...@@ -184,19 +184,19 @@ ...@@ -184,19 +184,19 @@
* validation * validation
| 属性名 | 类型 | | 属性名 | 类型 | 描述 |
| --- | --- | | :--- | :--- | :--- |
| required | Boolean | | required | Boolean | 设为必填 |
| max | Integer | | max | Integer | 设最大 |
| min | Integer | | min | Integer | 设最小 |
### **sort** ### **sort**
| 属性名 | 类型 | | 属性名 | 类型 | 描述 |
| --- | --- | | :--- | :--- | :--- |
| dir | String | | dir | String | 分为升序或者降序 |
| field | String | | field | String | 字段的id |
| compare | function | | compare | function | 比较事件 |
```xml ```xml
<h:dataSource id="gridDataSource" batch="true" pageSize="10" serverPaging="true" serverSorting="true" error="requestError"> <h:dataSource id="gridDataSource" batch="true" pageSize="10" serverPaging="true" serverSorting="true" error="requestError">
......
# HlsHbox(分行标签)
说明:将标签的内的内容放在同一模块里面(一般放在同一行宽度不够时会转行),继承&lt;div&gt;标签的一般属性
```html
<h:hlsHBox>
<h:hlsMaskedTextBox name="userId" id="maskedtextbox" bind="enabled: isEnabled, value:data.userId" colspan="2" prompt="TextBox:" promptColspan="1" required="true" validationMessage="Enter {0}"/>
<h:hlsCombobox name="combobox" id="combobox" bind="enabled: isEnabled, source: comboboxSource, value:data.productId" colspan="2" dataTextField="text" dataValueField="value" placeholder="combobox" prompt="Combobox:" promptColspan="1" valuePrimitive="true"/>
<h:hlsLov name="lov" id="lov" code="LOV_ROLE" colspan="2" contextPath="${base.contextPath}" data-bind="enabled: isEnabled, value:data.userId,text:data.userName" locale="${base.locale}" placeholder="lov" prompt="Lov:" promptColspan="1" query="lovQuery" select="lovSelect" style="width: 100%"/>
<h:hlsDatePicker name="datepicker" id="datepicker" bind="enabled: isEnabled,value:data.birthday" colspan="2" placeholder="datepicker" prompt="datepicker:" promptColspan="1" required="true"/>
</h:hlsHBox>
<h:hlsHBox>
<h:hlsTlEdit id="tl" bind="value:data.name" dto="com.hand.hap.function.dto.Resource" field="name" idField="id" model="viewModel.data" placeholder="多语言" prompt="TlEdit:"/>
<h:hlsCheckbox name="checkbox" bind="enabled: isEnabled,value:data.checkbox" bindModel="viewModel" checked="checked" checkedValue="Y" prompt="Checkbox:" uncheckedValue="N"/>
<h:hlsCombobox id="products" cascadeFrom="combobox" dataSource="productsData" dataTextField="code" dataValueField="code" disabled="true" filter="contains" placeholder="Select product..." prompt="Combobox:"/>
<h:hlsDropDownList name="dropdownlist" id="dropdownlist" bind="enabled: isEnabled, source: comboboxSource, value:data.productId" colspan="2" dataTextField="text" dataValueField="value" placeholder="dropdownlist" prompt="DropDownList:" promptColspan="1" valuePrimitive="true"/>
</h:hlsHBox>
<h:hlsHBox>
<h:hlsTextArea id="tla" bind="value:data.name" colspan="4" style="width:500px" dto="com.hand.hap.function.dto.Resource" field="name" idField="id" model="viewModel.data" placeholder="多语言" prompt="TlEdit:"/>
</h:hlsHBox>
```
效果图![](/assets/802170358.png)
...@@ -6,16 +6,15 @@ ...@@ -6,16 +6,15 @@
### 一般属性: ### 一般属性:
| 属性 | 类型 | | 属性 | 类型 | 描述 |
| :--- | :--- | | :--- | :--- | :--- |
| navigationBarTitle | String | | navigationBarTitle | String | 导航栏标题 |
| navigationBar | String | | navigationBar | String | 标记为导航栏(true) |
| navigationBarContent | String | | treeTitle | String | 导航栏父节点标题 |
| treeTitle | String | | navigationBarId | String | 导航栏唯一标识 |
| navigationBarId | String | | navigatonBarClass | String | 导航栏通用标识 |
| navigatonBarClass | String | | barType | String | 导航栏类型(可以为tree或normal) |
| barType | String | | nodeLevel | String | 设置导航栏为父节点或者子节点 |
| nodeLevel | String |
### 使用: ### 使用:
......
## NumericTextBox ## hlsNumericTextBox\(数字框\)
一般属性:
| 属性名 | 类型 | 描述 |
| :--- | :--- | :--- |
| id | String | 唯一标识 |
| bind | String | 绑定的事件 |
| style | String | 样式 |
| prompt | String | 文本框前的标题 |
| promptColspan | String | 文本框前面的标题的宽度 |
| colspan | String | 文本框的宽度 |
| enable | String | 设置是否可编辑 |
| readOnly | String | 设置只读 |
| placeHolder | String | 设置文本框默认值 |
| format | String | 设置数字格式化 |
### 使用方法: ### 使用方法:
......
# HlsRadioGroup,HlsRadioButton\(单选按钮组合标签\)
##### 提示:这两个标签必须在一起用且HlsRadioButton标签需写在hlsRadioGroup标签内
### 一般属性:
### hlsRadioGroup:
| 属性名称 | 类型 | 描述 |
| :--- | :--- | :--- |
| bindModel | String | 绑定数据模型 |
| bind | String | 绑定值 |
### HlsRadioButton:
| 属性名称 | 类型 | 描述 |
| :--- | :--- | :--- |
| prompt | String | 展示的值 |
| value | String | 设置默认值 |
| width | String | 设置宽度 |
| icon | String | 图标 |
### 用法:
```html
<script>
var model = kendo.observable({
data: {}
});
</script>
<h:hlsRadioGroup bindModel="model" bind="value: data.value">
<h:hlsRadioButton prompt="aa" value="AA" width="12px" icon="fa fa-arrow"></h:hlsRadioButton>
<h:hlsRadioButton prompt="bb" value="BB" width="12px" icon="fa fa-arrow"></h:hlsRadioButton>
<h:hlsRadioButton prompt="cc" value="CC" width="12px" icon="fa fa-arrow"></h:hlsRadioButton>
</h:hlsRadioGroup>
```
选中后效果图:![](/assets/2172053.png)
获取选中的值:
```html
<script>
var checkedValue = model.data.value//获取值为BB
</script>
```
# 开发流程
## 后端开发
### 业务需求整理
### 设计表结构
### 编写 liquibase 脚本
先列一个参考资料 https://github.com/tlberglund/groovy-liquibase/blob/master/src/test/changelog/changelog.groovy
### 编写 DTO
#### 创建 DTO 类
* DTO 类不需要提供任何实现,所以属于供应方的服务接口层。创建在 `项目模块``xxx.<module>.dto ` 包下。
* 每一个 DTO 类即为一个实体类,对应数据库中的一个具体表。
* 名称与表名称相同,表名中 ` _ ` 替换为驼峰命名法,首字母大写,且忽略前缀。如:` UserRole ` 对应表为 ` sys_user_role `
> 关于字段命名
> HAP 依赖 `java bean` 命名规范.
> 请严格遵循规范定义`属性`,`getter`,`setter`等.
#### 指定对应表
* ` @Table(name = "table_name") ` 指定 DTO 对应数据库中表的名称。
* 每一个 DTO 对应数据库中的一个具体表,一般都需要继承 BaseDTO 类。
#### 属性规范
* 所有属性均为`private`属性。
* 每一个属性需要生成对应的 ` getter `` setter ` 方法。
* 字段名称应根据`驼峰命名规则`从数据库列名转换过来
* 例如:数据库列名为 USER_NAME,则字段名为 userName
* 特殊字段名称,可以在字段在添加`@Column(name = "xxx")` 注解,指定数据库列名
* 非数据库字段,需要用`@Transient` 标注
* javax.persistence.Transient
* 属性的的类型与字段的 type 对应
* 不使用基本类型,全部使用基本类型的包装类,如 ` Long ` 对应数据库中的 ` INTEGER `,而不是使用 ` long `
* 数字类型主键统一采用 `Long`
* 金额、数量 等精度严格浮点类型采用 `BigDecimal`
* 注意 BigDecimal 在计算、比较方面的特殊性
* 所有的主键字段都需要用`@Id`标注
* 对于自增张、序列(SEQUENCE)类型的主键,需要添加注解`@GeneratedValue`
* 序列命名规范:`表名_S`
* 例如:表 SYS_USER 对应的序列为 SYS_USER_S
#### 数据多语言支持
> hap 数据多语言可以通过在 DTO 上添加注解来自动完成
* DTO 类上添加`@MultiLanguage`
* 此注解说明该 DTO 需要支持数据多语言
* DTO 字段上添加 `@MultiLanguageField`
* 此注解说明该字段是一个多语言字段
* 当有多个多语言字段时,这些字段都需要添加
> 实际作用
* 当使用 DTO 执行标准 insert 操作时,框架会自动插入多条数据到对应的 TL 表
* 当执行 delete 操作时,会自动删除对应的多语言数据
* 当执行标准 query 时,会自动关联 TL 表
#### 参照
https://rdc.hand-china.com/gitlab/HAP/hap/raw/master/hap/src/main/java/com/hand/hap/account/dto/Role.java
### 编写 Mapper
#### 创建 Mapper 接口类
* Mapper 接口类即为传统意义上的 DAO,但与 ` interface ` 不同,Mapper 本身就是对数据访问的具体实现,所以属于供应方的服务实现层。创建在 `项目模块``xxx.<module>.mapper` 包下。
* 每一个 Mapper 接口类封装了对数据库表的操作,每一个 Mapper 对应一个 DTO 类,所以命名为 DTO 类名 + `Mapper`。如:` UserRoleMapper ` 对应表为 ` UserRole ` 类。
* 基础的 CRUD 操作不需要再次实现,通过继承 ` Mapper<T> ` 类实现。其中 T 为 对应 DTO 的泛型。
* 复杂的数据库操作需要定义具体的接口方法。
#### 创建 Mapper.xml
* Mapper.xml 是数据库的的具体映射,与 Mapper 接口同级,创建在 `项目模块` resources 目录的 ` xxx.<module>.mapper ` 包下。
* Mapper.xml,与 Mapper 接口对应。所以命名 Mapper 接口类相同。
* 对于基本的 CRUD 不需要进行配置,所以也就不需要创建对应的 Mapper.xml 文件。
* 对于自定义的数据库方法,需要创建对应的 Mapper.xml 文件。
* Mapper.xml 中的操作 id 对应 Mapper 接口类的方法名。
#### 基础 CRUD 操作
| 操作 | 方法 |
| -------- | -------- |
| **插入** | ### |
| 插入一条 | ` int insertSelective( T entity ); ` |
| **删除** | ### |
| 根据 ID 删除一条 | ` int deleteByPrimaryKey( Object id ); ` |
| **修改** | ### |
| 根据 ID 修改 | ` int updateByPrimaryKeySelective( T entity ); ` |
| **查询** | ### |
| 根据 ID 查询 | ` T selectByPrimaryKey( Object id ); ` |
| 根据条件查询一条记录 | ` T selectOne( T entity ); ` |
| 根据条件,查询全部记录 | ` List<T> select(T example); ` |
### 编写Service
#### 创建 Service 接口类
* Service 接口类定义了业务操作的一系列接口,并不提供实现,具体实现需要通过服务实现层提供,所以属于供应方的服务接口层。创建在 `项目模块`` xxx.<module>.service ` 包下。
* 接口(interface)统一以大写字母 `I` 做为命名前缀
* 每一个 Service 对应一个 DTO 类,所以命名为`I` + DTO 类名 + ` Service `。如:` IUserRoleService ` 对应表为 ` UserRole ` 类。
* Service 里的每一个方法需要加上IRequest对象作为参数。
* Service 接口,如无特殊例外,需要继承 `ProxySelf< T >` 接口
* T 为 Service 本身
#### 创建 Service 实现类
* Service 接口的具体实现通过服务实现层提供,所以属于供应方的服务实现层。创建在 `项目模块`` xxx.<module>.service.impl ` 包下。
* 每一个 Service 实现类对应一个 Service 接口类,所以命名为 Service 接口类名(去掉`I`前缀) + ` Impl `。如:` UserRoleServiceImpl ` 对应 ` IUserRoleService ` 类。
* 实现类,如无特殊情况,需要用`@Service`标注,以自动扫描注册
* 实现类可以通过继承`BaseServiceImpl< T >` 来获得标准的 CRUD 操作支持
> 需要 Service 接口类 继承 `IBaseService< T >`
* ServiceImpl 中对于 Mapper 的 CRUD 操作参照 [基础 CRUD 操作](#基础 CRUD 操作])。
### 编写Controller
#### 创建 Controller 类
* Controller 负责对 Model 和 View 的处理,创建在 `项目模块`` xxx.<module>.controllers ` 包下。
* 每一个 Controller 是对一个具体的 DTO 资源进行处理的,所以命名为 DTO 类名 + ` Controller `。如: ` UserRoleController ` 对应 ` UserRole ` 类。
* 需要通过 ` @Controller ` 指定该类为一个 Controller 类。
* 需要在每一个 Controller 中通过 ` @Autowired ` 注入 Service。
* Controller 的每一个方法只在最后调用一次该 Controller 所注入的 Service ,因此当有调用多个Service的需求应该放在注入的 Service 中。
## 前端开发
### 界面设计
## redis 安装与部署
## 一、安装
### 1.WINDOWS环境
Redis 支持 32 位和 64 位。这个需要根据你系统平台的实际情况选择,这里我们下载 Redis-x64-xxx.zip压缩包到 C 盘,解压后,将文件夹重新命名为 redis。
打开一个 cmd 窗口 使用cd命令切换目录到 C:\redis 运行 redis-server.exe redis.windows.conf (如果觉得麻烦可以将c:\redis加入到path中)。
### 2.LINUX环境
> $ wget http://download.redis.io/releases/redis-2.8.17.tar.gz
> $ tar xzf redis-2.8.17.tar.gz
> $ cd redis-2.8.17
> $ make
make完后 redis-2.8.17目录下会出现编译后的redis服务程序redis-server,还有用于测试的客户端程序redis-cli,两个程序位于安装目录 src 目录下:
下面启动redis服务.
>$ cd src
$ ./redis-server redis.conf
上述安装完成后可以正常运行redis,但是redis推荐的做法是常用的文件和程序文件分离,这样做的好处是可以通过新建多个启动文件夹,以不同的参数启动多个redis服务,
手动复制配置文件 redis.conf、sentinel.conf和执行脚本:redis-cli 、redis-server 、redis-sentinel到redis1目录下:
![redis-dir](/assets/redis-dir.jpg)
新安装的redis需注意以下几点,按需求调整
1. 默认情况redis不是在后台运行,这种情况下关闭shell(命令窗口)就关闭了redis,我们需要把redis放在后台运行,设置了后台运行就得自己设置日志的输出位置
**设置redis.conf**
```
daemonize yes
loglevel notice#日志等级
logfile "/usr/redis/log/redis.log"#日志保存路径
```
2. Redis默认只让本机连,对其它服务器不开放,需要配置bind参数。
```
bind *
```
3. Redis默认开机不启动,需要将启动命令加入到/etc/rc.local中
```
/opt/redis1/src/redis-server /opt/redis/redis1/redis.conf
```
## 二、部署
#### 1.搭建多个redis服务间的主从关系
**Redis的主从(master-slave)就是为了数据冗余备份、保证数据的安全、提高性能。**
这里我们准备3个redis服务器,分别复制上面写到的redis1两份命名为redis2和redis3,修改每个redis的redis.conf文件 port和pidfile参数。
注意:pidfile也要改,Linux为每一个redis分配了一个pid进程号,如果这里不作修改,会影响后面redis服务的启动
服务名|端口|pidfile|身份
| -- | -- | --| --|
redis1|6380|/var/run/redis_6380.pid|master|
redis2|6381|/var/run/redis_6381.pid|slave|
redis3|6382|/var/run/redis_6382 .pid|slave|
我们计划搭建成表中的关系,即redis1为主,redis2和redis3为从,给redis设置主从只需要在从服务的redis.conf加入
> slaveof 主服务器ip 主服务器端口
分别以下列方式启动3个redis服务:
```
./opt/redis1/redis-server /opt/redis1/redis.conf
./opt/redis2/redis-server /opt/redis2/redis.conf
./opt/redis3/redis-server /opt/redis3/redis.conf
```
3个redis服务都已经启动:
![redis-server](/assets/redis01.png)
查看主redis服务的身份:
```
# redis-cli -h localhost -p 6380
# >info
```
![redis](/assets/redis02.png)
>- redis主从关系到此搭建完成,可是如果其中的主redis服务器宕机怎么办,岂不是整个服务都崩掉了?请看下节redis哨兵
## 2.redis哨兵
> Sentinel(哨兵)是Redis 的高可用性解决方案:由一个或多个Sentinel 实例组成的Sentinel系统可以监视任意多个主服务器,以及这些主服务器属下的所有从服务器,并在被监视的主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器。
![redis](/assets/redis-sentinel.jpg)
> **提示:**
> - redis哨兵会监控主redis以及主redis下面的从redis,如果主redis下线后满足换老大(配置文件配置)的条件,哨兵会从从服务中选取某个redis成为主服务。
#### 配置redis哨兵
1. 配置端口
在sentinel.conf 配置文件中, 我们可以找到port 属性,这里是用来设置sentinel 的端口,一般情况下,至少会需要三个哨兵对redis 进行监控,我们可以通过修改端口启动多个sentinel 服务。
![redis](/assets/redis-sentinel01.png)
2. 配置主服务器的ip和端口
我们把监听的端口修改成6380,并且加上权值为2,这里的权值,表示必须有两个哨兵监控到主redis下线才满足切换master的条件
![redis](/assets/redis-sentinel02.png)
3. 启动Sentinel
![redis](/assets/redis-sentinel03.png)
sentinel 启动之后,就会监视到现在有一个主服务器,两个从服务器
- **注意:** **我们这里只启动了一个哨兵,可是权值设置的是2**
当我们把redis2从服务器关闭之后,我们可以看到日志
![redis](/assets/redis-sentinel04.png)
再启动redis2 (哨兵监听主服务器老大哥的同时,也关注了小弟的动态)
![redis](/assets/redis-sentinel05.png)
关闭主redis,发现并没有将提升slave成为master,原因是sentinel的权值设置为2,需要两个哨兵都认为主redis断线才切换。
![redis](/assets/redis-sentinel06.png)
**所以我们必须要启动两个哨兵对主redis进行监控,修改sentinel启动端口,监控对象不变,再启动一个哨兵**
关闭redis1(6380)后,哨兵告诉我们换了大哥了。
查看redis3(6382),发现它已经成为大哥了
![redis](/assets/redis-sentinel07.png)
原先没有写的权限,现在也有了 。
![redis](/assets/redis-sentinel08.png)
当老大哥redis1(6380)回来后,怎么办,谁来当老大?
![redis](/assets/redis-sentinel09.png)
与实际相符,redis1只能当小弟了。。
#### **总结Sentinel的作用:**
* Master 状态监测
* 如果Master 异常,则会进行Master-slave 转换,将其中一个Slave作为Master,将之前的Master作为Slave
* **Master-Slave切换后,master_redis.conf、slave_redis.conf和sentinel.conf的内容都会发生改变,即master_redis.conf中会多一行slaveof的配置,sentinel.conf的监控目标会随之调换**
#### **总结Sentinel的工作方式:**
1. 每个Sentinel以每秒钟一次的频率向它所知的Master,Slave以及其他 Sentinel 实例发送一个 PING 命令
2. 如果一个实例(instance)距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所指定的值, 则这个实例会被 Sentinel 标记为主观下线。
3. 如果一个Master被标记为主观下线,则正在监视这个Master的所有 Sentinel 要以每秒一次的频率确认Master的确进入了主观下线状态。
4. 当有足够数量的 Sentinel(大于等于配置文件指定的值)在指定的时间范围内确认Master的确进入了主观下线状态, 则Master会被标记为客观下线
5. 在一般情况下, 每个 Sentinel 会以每 10 秒一次的频率向它已知的所有Master,Slave发送 INFO 命令
6. 当Master被 Sentinel 标记为客观下线时,Sentinel 向下线的 Master 的所有 Slave 发送 INFO 命令的频率会从 10 秒一次改为每秒一次
7. 若没有足够数量的 Sentinel 同意 Master 已经下线, Master 的客观下线状态就会被移除。 若 Master 重新向 Sentinel 的 PING 命令返回有效回复, Master 的主观下线状态就会被移除。
# 消息机制
简介:实时弹出提示消息或待办
### 1.消息机制入口
对任何需要提示的操作,都需要将这个消息以键-值对的形式存入到redis中,调用setNoticeCache()方法即可
```java
SysEventServieImpl.java
@Override
public void setNoticeCache(HlsSystemNotice hlsSystemNotice) {
sysNoticePushService.sendUserNotice(hlsSystemNotice.getSource_user_id(),hlsSystemNotice);
List<HlsSystemNotice> noticeToList= hlsSystemNoticeService.selectSendNotice(hlsSystemNotice);
if(noticeToList!=null){
for(HlsSystemNotice hlsSystemNotice1:noticeToList){
sysNoticePushService.sendUserNotice(hlsSystemNotice1.getUserId(),hlsSystemNotice);
}
}
}
```
redis中的存在形式:
```java
SysNoticePushServiceImpl.java
@Override
public void sendUserNotice(Long userId, HlsSystemNotice hlsSystemNotice) {
String hashKey = "messageBox-" + userId.toString() + "-" + System.currentTimeMillis() + "-" + Math.random();
String key = "messageBox-" + userId.toString();
hashOperations.put(hashKey, "userId", hlsSystemNotice);
listOperations.rightPush(key, hashKey);
}
```
前端以ajax轮询的方法发送请求获取需要展示的消息:
```js
<script>
setInterval(moment_show,6000);
function moment_show(contextPath){
$.ajax({
type: 'POST',
async: false, //设为同步
url: '${base.contextPath}/hls/user/send/notice',
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (datas) {
if(datas.length>0) {
push_float_msg(datas);
setTimeout(function(){moment_hide(msgLength);},3000);
}
}
});
};
</script>
```
redis中取值机制:
```java
SysNoticePushServiceImpl.java
@Override
public List<HlsSystemNotice> getUserNotice(Long userId) {
String key = "messageBox-" + userId.toString();
List<HlsSystemNotice> retList = new ArrayList<HlsSystemNotice>();
if(listOperations!=null){
List<String> keys = listOperations.range(key, 0, -1);
for (int i = 0; i < keys.size(); i++) {
if (i == 5) {
break;
}
boolean rep = true;
boolean currentUserTodo = false;
if(hashOperations!=null){
HlsSystemNotice hlsSystemNotice = (HlsSystemNotice) hashOperations.get(keys.get(i), "userId");
for (int m = 0; m < retList.size(); m++) {
if (retList.get(m).getNotice_id() == hlsSystemNotice.getNotice_id()) {
rep = false;
}
}
if (hlsSystemNotice.getNotice_type() == "TO_DO") {
if (hlsSystemNotice.getSource_user_id() == userId) {
currentUserTodo = true;
} else {
currentUserTodo = false;
}
} else {
currentUserTodo = true;
}
if (rep && currentUserTodo) {
retList.add(hlsSystemNotice);
}
listOperations.remove(key,1,keys.get(i));
hashOperations.delete(keys.get(i),"userId");
}
}
}else {
HlsSystemNotice notice = new HlsSystemNotice();
notice.setNotice_title("test");
notice.setUserId(Long.parseLong("10172"));
notice.setRead_flag("N");
notice.setDone_flag("N");
notice.setNotice_message("test-msg");
retList.add(notice);
}
return retList;
}
}
```
...@@ -4,7 +4,8 @@ ...@@ -4,7 +4,8 @@
支持多sheet导入,sheet1从第三行开始读取数据,其他sheet页从第一行导入数据。 支持多sheet导入,sheet1从第三行开始读取数据,其他sheet页从第一行导入数据。
使用方法: 使用方法:
1. Excel文件上传 1. Excel文件上传
```javascript
```javascript
$("#files").kendoUpload({ $("#files").kendoUpload({
async: { async: {
saveUrl: "${base.contextPath}/hls/excel/import?${_csrf.parameterName}=${_csrf.token}&templateCode=HLS_FIN_STATEMENT_LN", saveUrl: "${base.contextPath}/hls/excel/import?${_csrf.parameterName}=${_csrf.token}&templateCode=HLS_FIN_STATEMENT_LN",
...@@ -14,8 +15,7 @@ ...@@ -14,8 +15,7 @@
upload: onUpload, upload: onUpload,
success: onSuccess success: onSuccess
}); });
``` ```
| 参数名 | 描述 | | 参数名 | 描述 |
| :--: | :--: | | :--: | :--: |
| _csrf.token | 防跨域token | | _csrf.token | 防跨域token |
...@@ -56,51 +56,51 @@ ...@@ -56,51 +56,51 @@
#### 后端使用方法 #### 后端使用方法
1. 在代码中注入Excel导出Service 1. 在代码中注入Excel导出Service
```java ```java
@AutoWired @AutoWired
private ExcelExportServiceImpl excelService; private ExcelExportServiceImpl excelService;
``` ```
2. 在自己的controller中调用方法 2. 在自己的controller中调用方法
```java ```java
excelService.saveExportInfo(sqlId,iRequest,config,rowMaxNumber); excelService.saveExportInfo(sqlId,iRequest,config,rowMaxNumber);
``` ```
| 参数名 | 类型 | 描述 | | 参数名 | 类型 | 描述 |
| :--: | :--: | :--: | | :--: | :--: | :--: |
| sqlId | String | mybatis对应的数据库查询语句的ID | | sqlId | String | mybatis对应的数据库查询语句的ID |
| iRequest | IRequest | 带有上下文信息的reuqest对象 | | iRequest | IRequest | 带有上下文信息的reuqest对象 |
| config | ExportConfig | 包含Excel各列信息的对象,由前台传递json对象转换而来 | | config | ExportConfig | 包含Excel各列信息的对象,由前台传递json对象转换而来 |
| rowMaxNumber | int | 生成Excel的最大行数,可使用重载方法,不包含次参数默认为1,000,000 | | rowMaxNumber | int | 生成Excel的最大行数,可使用重载方法,不包含次参数默认为1,000,000 |
3. 运行Excel导出程序(获取该程序,请联系部门相关负责人) 3. 运行Excel导出程序(获取该程序,请联系部门相关负责人)
```shell ```shell
java -jar hel-batch-parent.jar & java -jar hel-batch-parent.jar &
``` ```
4. Excel导出情况查询 4. Excel导出情况查询
用户若是为管理员身份,则查询全部人的导出情况,否则只显示当前用户的导出数据 用户若是为管理员身份,则查询全部人的导出情况,否则只显示当前用户的导出数据
```java ```java
excelService.queryExportInfo(status, iRequest); excelService.queryExportInfo(status, iRequest);
``` ```
| 参数名 | 类型 | 描述 | | 参数名 | 类型 | 描述 |
| :--: | :--: | :--: | | :--: | :--: | :--: |
| status | String | 指定Excel导出的状态,该参数可为空查询全部状态数据<br><ll><li>new->等待</li><li>generating->文件导出中</li><li>finished->导出完成</li><li>failed-> 导出失败</li></ll> | | status | String | 指定Excel导出的状态,该参数可为空查询全部状态数据<br><ul><li>new->等待</li><li>generating->文件导出中</li><li>finished->导出完成</li><li>failed-> 导出失败</li></ul> |
| iRequest | IRequest | 带有上下文信息的reuqest对象 | | iRequest | IRequest | 带有上下文信息的reuqest对象 |
5. 下载已完成的Excel文件 5. 下载已完成的Excel文件
```java ```java
excel.downloadExcel(filePath, fileName, request, response); excel.downloadExcel(filePath, fileName, request, response);
``` ```
| 参数名 | 类型 | 描述 | | 参数名 | 类型 | 描述 |
| :--: | :--: | :--: | | :--: | :--: | :--: |
| filePath | String | 需要下载的文件的路径 | | filePath | String | 需要下载的文件的路径 |
| fileName | String | 想要保存的文件的名称 | | fileName | String | 想要保存的文件的名称 |
| request | HttpServletRequest | 用户的请求对象 | | request | HttpServletRequest | 用户的请求对象 |
| response | HttpServletResponse | 用户的响应对象 | | response | HttpServletResponse | 用户的响应对象 |
**下载前,需要检查config.properties文件中的export.offerUrl配置项是否配置正确,且为hel-batch-parent.jar提供的文件下载url** **下载前,需要检查config.properties文件中的export.offerUrl配置项是否配置正确,且为hel-batch-parent.jar提供的文件下载url**
\ No newline at end of file
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
* [GitBook使用](/gitbook.md) * [GitBook使用](/gitbook.md)
--- ---
* I. 开发环境准备 * I. 开发环境准备
* [1.1 Git 使用](/git-guide.md "git指令") * [1.1 Git 使用](/git-guide.md "git指令")
...@@ -18,12 +19,14 @@ ...@@ -18,12 +19,14 @@
* 2.3 Checkstyle * 2.3 Checkstyle
* III. 后端开发 * III. 后端开发
* [3.1 用户安全策略](/后端开发/user-security-strategy.md)
* [3.2 审计](/后端开发/audit.md) * [3.1 后端开发说明\(1.0\)](/后端开发/dev-flow.md)
* [3.3 锁机制](/后端开发/lock.md) * [3.2 用户安全策略](/后端开发/user-security-strategy.md)
* [3.4 日志管理](/后端开发/elk.md) * [3.3 审计](/后端开发/audit.md)
* [3.5 代码生成器](/后端开发/code-generator.md) * [3.4 锁机制](/后端开发/lock.md)
* [3.6 跨域访问](/后端开发/3.24-cors.md) * [3.5 日志管理](/后端开发/elk.md)
* [3.6 代码生成器](/后端开发/code-generator.md)
* [3.7 跨域访问](/后端开发/3.24-cors.md)
* IV. 前端JavaScript开发 * IV. 前端JavaScript开发
...@@ -34,33 +37,34 @@ ...@@ -34,33 +37,34 @@
* V. 前端UI开发 * V. 前端UI开发
* [5.1 hlsCombobox(下拉框)](/前端组件/hlsCombobox.md) * [5.1 hlsCombobox\(下拉框\)](/前端组件/hlsCombobox.md)
* [5.2 hlsDataSource](/前端组件/hlsDataSource.md) * 5.2 [hlsDataSource\(数据源\)](/前端组件/hlsDataSource.md)
* [5.3 hlsMaskedTextBox](/前端组件/hlsMaskedTextBox.md) * [5.3 hlsMaskedTextBox](/前端组件/hlsMaskedTextBox.md)
* [5.4 hlsPage](/前端组件/hlsPage.md) * [5.4 hlsPage](/前端组件/hlsPage.md)
* [5.5 hlsTextArea](/前端组件/hlsTextArea.md) * [5.5 hlsTextArea](/前端组件/hlsTextArea.md)
* [5.6 hlsTlEdit](/前端组件/hlsTlEdit.md) * [5.6 hlsTlEdit](/前端组件/hlsTlEdit.md)
* [5.7 hlsDatePicker](/前端组件/hlsDatePicker.md) * [5.7 hlsDatePicker\(日期选择框\)](/前端组件/hlsDatePicker.md)
* [5.8 hlsDateTimePicker](/前端组件/HlsDateTimePicker.md) * [5.8 hlsDateTimePicker\(时间选择框\)](/前端组件/hlsDateTimePicker.md)
* [5.9 hlsLov](/前端组件/HlsLov.md) * [5.9 hlsLov\(lov选择框\)](/前端组件/hlsLov.md)
* [5.10 hlsToolBar](/前端组件/HlsToolBar.md) * [5.10 hlsToolBar\(底部菜单栏\)](/前端组件/hlsToolBar.md)
* [5.11 TabStrip](/前端组件/TabStrip.md) * [5.11 TabStrip\(sheet页切换卡\)](/前端组件/tabStrip.md)
* [5.12 hlsForm](/前端组件/hlsForm.md) * [5.12 hlsForm\(表单\)](/前端组件/hlsForm.md)
* [5.13 hlsCombobox](/前端组件/HlsCombobox.md) * [5.13 hlsCombobox](/前端组件/hlsCombobox.md)
* [5.14 hlsCheckBox](/前端组件/HlsCheckBox.md) * [5.14 hlsCheckBox\(复选框\)](/前端组件/hlsCheckBox.md)
* [5.15 Grid](/前端组件/Grid.md) * [5.15 grid\(表格\)](/前端组件/grid.md)
* [5.16 DataSource](/前端组件/DataSource.md) * [5.16 dataSource\(数据源\)](/前端组件/dataSource.md)
* [5.17 hlsGridBox](/前端组件/hlsGridBox.md) * [5.17 hlsGridBox\(表格\)](/前端组件/hlsGridBox.md)
* [5.18 NumericTextBx](/前端组件/NumericTextBox.md) * [5.18 hlsNumericTextBox\(数字框\)](/前端组件/hlsNumericTextBox.md)
* [5.19 HlsNavigationBar](/前端组件/HlsNavigationBar.md) * [5.19 hlsNavigationBar\(导航栏\)](/前端组件/hlsNavigationBar.md)
* VI. 框架功能描述 * VI. 框架功能描述
* [6.1 计划任务](/框架功能描述/计划任务.md) * [6.1 计划任务](/框架功能描述/计划任务.md)
* [6.2 RabbitMq消息队列](/后端开发/RabbitMq消息队列.md) * [6.2 RabbitMq消息队列](/后端开发/RabbitMq消息队列.md)
* [6.3 应用服务部署(tomcat/weblogic)](/框架功能描述/deployment.md) * [6.3 应用服务部署\(tomcat/weblogic\)](/框架功能描述/deployment.md)
* [6.4 合同文本生成](/框架功能描述/docx4j.md) * [6.4 合同文本生成](/框架功能描述/docx4j.md)
* [6.5 Excel导入导出](/框架功能描述/jad.md) * [6.5 Excel导入导出](/框架功能描述/jad.md)
* [6.6 redis安装和部署](/后端开发/redis.md)
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