feat(customer): 新增ICC客户信息同步功能 - OAuth2密码模式鉴权拉取org_base客户、按icc_code幂等upsert写入并记录icc_sync_log,注册每10分钟定时任务;客户档案管理页新增手动'同步客户'按钮(POST /api/customers/sync/icc),同步后自动刷新列表与统计;customer表新增icc_code字段及唯一索引,同步db_seed与设计文档

This commit is contained in:
wk authored and trae-solo-agent committed 2026-10-23 18:20:25 +08:00
1 parent be91d04431
commit 9d3bb13ecf
24 files changed
+1159 -5

No files matched your search

+17
View File
@@ -0,0 +1,17 @@
智电改造网关表:electric\_gatway(ICC同步)
| 字段 | 类型 | 键 | 说明 |
| :--------- | :---------- | :----- | :--------------- |
| id | BIGSERIAL | PK | 自增主键 |
| iccImei | VARCHAR(64) | UNIQUE | 设备唯一标识码,设备的imei号 |
| deviceCode | VARCHAR(64) | UNIQUE | 设备编码 |
| deviceName | VARCHAR(64) | <br /> | 设备名称 |
| <br /> | <br /> | <br /> | <br /> |
| <br /> | <br /> | <br /> | <br /> |
| <br /> | <br /> | <br /> | <br /> |
| <br /> | <br /> | <br /> | <br /> |
| <br /> | <br /> | <br /> | <br /> |
| <br /> | <br /> | <br /> | <br /> |
| <br /> | <br /> | <br /> | <br /> |
| <br /> | <br /> | <br /> | <br /> |
+350
View File
@@ -0,0 +1,350 @@
# 鑫潼ICC平台接口文档
## 接口环境变量
host:222.243.55.92,端口:1443
clientId:xtzdapp
clientSecret:1c7e440a-660c-4a5d-a7b8-82b58d9dca4e
username:XTZD接口
password:Xtzdkj\@20260801
version:1.0.0
## 接口鉴权认证
大华ICC平台采用OAuth2 鉴权认证。
OAuth2是目前最流行的授权机制,是一种安全机制保障的方案。数据的所有者告诉系统,同意授权第三方应用进入系统,获取这些数据。系统从而产生一个短期的进入令牌(Access\_token)和刷新该令牌的凭证(Refresh\_token),用来代替密码,供第三方应用使用。客户端认证模式常用于微服务间交互,该模式Access\_token有效期一般较长,两小时,且没有Refresh\_token,过期可以直接认证获取匿名令牌。
授权模式:密码认证模式,密码模式在客户端基础上,从用户方获取帐号密码,再访问授权服务器认证授权。
![图片 2](assets/image1.jpeg)
客户端从用户方获取帐号密码。2、客户端携带用户信息向认证服务器获取令牌。3、认证服务器确认无误后,向客户端提供访问令牌。4、客户端携带令牌访问资源端。
协议鉴权
![图片 4](assets/image2.png)
鉴权接口的access\_token有效期2h,权限与鉴权用户的组织、部门权限保持一致。
获取公钥:访问地址:https\://{{host}}/evo-apigw/evo-oauth/{{version}}/oauth/public-key。其中{{host}}为接口环境变量的host:端口。{{version}}为接口环境变量的version。
```http
GET /evo-apigw/evo-oauth/{{version}}/oauth/public-key HTTP/1.1
```
RSA加密密码。以下是java RSA加密示例:其中使用接口环境变量的username、password。
```java
import javax.crypto.Cipher;
import java.security.KeyFactory;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
public class RSAUtil {
/**
* 基于原生RSA公钥加密
* @param password 用户密码
* @param publicKey 公钥
* @return
* @throws Exception
*/
public static String encrypt(String password, String publicKey) throws Exception {
//base64编码的公钥
byte[] decoded = java.util.Base64.getDecoder().decode(publicKey);
RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
//RSA加密
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
String outStr;
byte[] inputArray = password.getBytes("UTF-8");
int inputLength = inputArray.length;
// 最大加密字节数,超出最大字节数需要分组加密
int MAX_ENCRYPT_BLOCK = 117;
// 标识
int offSet = 0;
byte[] resultBytes = {};
byte[] cache = {};
while (inputLength - offSet > 0) {
if (inputLength - offSet > MAX_ENCRYPT_BLOCK) {
cache = cipher.doFinal(inputArray, offSet, MAX_ENCRYPT_BLOCK);
offSet += MAX_ENCRYPT_BLOCK;
} else {
cache = cipher.doFinal(inputArray, offSet, inputLength - offSet);
offSet = inputLength;
}
resultBytes = Arrays.copyOf(resultBytes, resultBytes.length + cache.length);
System.arraycopy(cache, 0, resultBytes, resultBytes.length - cache.length, cache.length);
}
outStr = java.util.Base64.getEncoder().encodeToString(resultBytes);
return outStr;
}
}
```
认证:访问凭证client\_id与client\_secret,使用接口环境变量的clientId、clientSecret。access\_token有效期2h。访问地址:https\://{{host}}/evo-apigw/evo-oauth/{{version}}/oauth/extend/token其中使用接口环境变量的username、password、clientId、clientSecret。
```http
POST /evo-apigw/evo-oauth/1.0.0/oauth/extend/token HTTP/1.1
Content-Type: application/json;charset=UTF-8
Host: 222.243.55.92:1443
{
"username":"用户账号",
"password":"RSA加密后的密码",
"client_id":"申请的clientId",
"client_secret":"申请的clientSecret",
"public_key":"获取的公钥"
}
```
返回参数示例:
```json
{
"success": true,
"data": {
"access_token": "10000:5cb47cd80-35d9-406d-a8d3-d87dd672d5d3",
"token_type": "bearer",
"refresh_token": "b83403e1-5ea1-41fd-81fc-e86fe5252c93",
"expires_in": 7199,
"scope": "*",
"userId": "10000",
"magicId": "1868a7db-adeb-42de-8064-43b3706ae407"
},
"code": "0",
"errMsg": ""
}
```
请求头设置鉴权码:调用业务接口需在请求头中设置鉴权码:
| **头部参数** | **解释** |
| ------------- | -------------------------------------------------------------------------------------------------------------- |
| Authorization | 格式:token\_type + 空格 + access\_token, token\_type一般为固定值bearer 例如:bearer 1:bb951116-d963-4528-80e1-91c2e94c3e75 |
## 本次接口说明
### 一、组织全量数据同步接口
前置条件:请求头设置Authorization字段
接口URL:https\://{{host}}/evo-apigw/evo-brm/{{version}}/organization/page
请求Method:GET
数据提交格式:application/json
请求参数:
| **参数名** | **必选** | **类型** | **说明** |
| ---------- | ------ | ------ | -------------------- |
| pageNum | false | int | 当前页, 默认1 |
| pageSize | false | int | 每页记录数, 默认10,最大支持1000 |
| orgPreCode | false | String | 组织父节点 |
| maxRangeId | true | long | 同步边界值 |
返回参数:
| **名称** | **类型** | **说明** |
| ------- | ------- | -------------------- |
| success | boolean | 接口返回是否成功 |
| code | string | 错误码:0-为无错误,非0-为具体错误码 |
| errMsg | string | 错误消息 |
| data | PageVO | 分页数据 |
PageVO
| **名称** | **类型** | **说明** |
| ----------- | --------------- | ------ |
| currentPage | int | 当前页码 |
| totalPage | int | 总页数 |
| pageSize | int | 每页记录数 |
| totalRows | int | 总数 |
| pageData | List<OrgInfoVO> | 数据列表 |
OrgInfoVO
| **名称** | **类型** | **说明** |
| ----------------- | ------- | ------------------------ |
| orgCode | string | 组织编码 |
| orgName | string | 组织名称 |
| orgSn | string | 唯一标识码 |
| orgType | string | 组织结构类型,具体参考组织类型 |
| province | string | 省 |
| city | string | 市 |
| county | string | 县 |
| domainId | Long | 域ID |
| stat | Integer | 状态 |
| sort | Integer | 排序码 |
| allSort | string | 所有组织排序 |
| service | string | 子系统服务编码,默认evo-thirdParty |
| forbidBindUser | Integer | 禁止绑定用户 :0-可绑定用户,1-不可绑定用户 |
| forbidBindService | String | 禁止绑定用户服务 |
组织类型
| **类型key** | **类型value** | **备注** |
| ------------- | ----------- | ------ |
| org\_base | 基础组织 | <br /> |
| org\_country | 国家 | <br /> |
| org\_province | 省 | <br /> |
| org\_city | 市 | <br /> |
| org\_county | 区县 | <br /> |
接口数据字段与customer(客户信息表)对应关系:
| **接口字段名称** | **字段说明** | **客户信息表字段名称** | **字段说明** |
| ---------- | -------- | -------------- | -------- |
| orgCode | 组织编码 | iccCode | ICC客户编码 |
| orgName | 组织名称 | customer\_name | 客户名称 |
| province | 省 | province | 省 |
| city | 市 | city | 市 |
| county | 县 | district | 区/县 |
**二、消防物联设备分页查询接口**
前置条件:请求头设置Authorization字段
接口URL:https\://{{host}}/evo-apigw/evo-fdbu/{{version}}/device/subsystem/page
请求Method:POST 数据提交格式:application/json
请求参数:
| **参数名** | **必选** | **类型** | **说明** |
| :----------- | :----- | :----- | :--------------- |
| pageNum | false | int | 当前页, 默认1 |
| pageSize | false | int | 每页记录数, 默认10,最多50 |
| orgCodes | false | array | 设备所属组织编码集合,最多10 |
| typeCode | false | string | 主机类型 |
| keyCondition | false | string | 设备名称/设备编号/imei |
**请求参数示例**
```JSON
{
"pageNum": 1,
"pageSize": 10,
"orgCodes": ["001001", "002001"],
"typeCode": 128,
"keyCondition": "测试"
}
```
**返回参数**
| **名称** | **类型** | **说明** |
| :------ | :--------- | :------------------- |
| success | boolean | 接口返回是否成功 |
| code | string | 错误码:0-为无错误,非0-为具体错误码 |
| errMsg | string | 错误消息 |
| data | **PageVO** | 分页数据 |
**PageVO**
| **名称** | **类型** | **说明** |
| :--------- | :--------------------- | :----- |
| nextPage | int | 当前页码 |
| totalCount | int | 总数 |
| pageData | List<**DeviceInfoVO**> | 数据列表 |
**DeviceInfoVO**
| **名称** | **类型** | **说明** |
| :--------------- | :----- | :--------------- |
| id | string | 唯一主键 |
| imei | String | 设备唯一标识码,设备的imei号 |
| deviceCode | String | 设备编码 |
| deviceName | String | 设备名称 |
| companyId | String | 公司编号 |
| companyName | String | 公司名称 |
| manufacturerCode | String | 厂商编码 |
| manufacturerName | String | 厂商名称 |
| typeCode | String | 主机类型 |
| typeName | String | 主机类型名称 |
| orgCode | String | 组织编码 |
| orgName | String | 组织名称 |
| dcpsId | string | 接入设备域 |
| softwareVersion | string | 软件版本号 |
| systemAddress | int | 系统地址 |
| address | string | 安装地址 |
| productModel | String | 设备型号 |
| isOnline | string | 在离线状态 |
| description | string | 描述 |
接口数据字段与electric\_gateway(智能改造设备表)对应关系:
<br />
三、**消防探测器分页查询**
前置条件:请求头设置Authorization字段
接口URL:https\://{{host}}/evo-apigw/evo-fdbu/{{version}}/detector/subsystem/page
请求Method:POST 数据提交格式:application/json
请求参数:
| **参数名** | **必选** | **类型** | **说明** |
| :----------- | :----- | :----- | :--------------- |
| pageNum | false | int | 当前页, 默认1 |
| pageSize | false | int | 每页记录数, 默认10,最多50 |
| orgCodes | false | array | 设备所属组织编码集合,最多10 |
| typeCode | false | string | 探测器类型 |
| keyCondition | false | string | 设备名称/设备编号/imei |
**请求参数示例**
```JSON
{
"pageNum": 1,
"pageSize": 10,
"orgCodes": ["001001", "002001"],
"typeCode": 128,
"keyCondition": "测试"
}
```
**返回参数**
| **名称** | **类型** | **说明** |
| :------ | :--------- | :------------------- |
| success | boolean | 接口返回是否成功 |
| code | string | 错误码:0-为无错误,非0-为具体错误码 |
| errMsg | string | 错误消息 |
| data | **PageVO** | 分页数据 |
**PageVO**
| **名称** | **类型** | **说明** |
| :--------- | :------------------- | :----- |
| nextPage | int | 当前页码 |
| totalCount | int | 总数 |
| pageData | List<**DetectorVO**> | 数据列表 |
**DetectorVO**
| **名称** | **类型** | **说明** |
| :--------------- | :----- | :----------------- |
| id | string | 唯一主键 |
| detectorCode | string | 探测器编码 |
| detectorName | string | 探测器名称 |
| imei | string | 设备唯一标识码,设备的imei号 |
| detectorStatus | int | 探测器状态,参考上报状态 |
| orgCode | String | 组织编码 |
| orgName | String | 组织名称 |
| companyId | String | 探测器所属单位编码 |
| companyName | String | 探测器所属单位名称 |
| parentCode | string | 父设备编码 |
| parentName | string | 父设备名称 |
| manufacturerCode | String | 厂商编码 |
| manufacturerName | String | 厂商名称 |
| typeCode | String | 探测器类型 |
| typeName | String | 探测器类型名称 |
| fireSystemCode | String | 消防系统编码 |
| fireSystemName | String | 消防系统名称 |
| partUnitLoopCode | string | 探测器回路号 |
| channelNo | string | 探测器通道号 |
| systemAddress | int | 系统地址 |
| channelSn | string | 通道唯一标识码 |
| mapType | String | 地图类型 |
| produceDate | string | 生产日期,格式:yyyy-MM-dd |
**上报状态**
| **状态编码** | **状态名称** | **备注** |
| :------- | :------- | :----- |
| 01 | 在线 | <br /> |
| 02 | 离线 | <br /> |
接口数据字段与electric\_**line**(智电改造线路表)对应关系: