人员区域授权相关信息

master
aosodo_4812 2026-09-03 15:12:30 +08:00
parent af9ab0aa0d
commit 7c4840cd0f
49 changed files with 1541 additions and 40 deletions

View File

@ -262,7 +262,17 @@ public class XxxJob {
} }
``` ```
## 11. 开发完成检查表 ## 11. 第三方 HTTP 接口鉴权与租户上下文
- EdgeGuard 第三方接口统一使用平台签发的 token 鉴权。调用方通过请求头 `token` 传递 token框架同时兼容 `Magic-Token`,但项目文档和联调统一使用 `token`,禁止通过 URL 查询参数传递 token。
- `jjb-saas-framework-auth``AuthenticationFilter` 负责读取并解析 token构造 `SSOUser`,写入线程级 `AuthContext`,请求结束后清理上下文。业务代码通过 `AuthContext.getTenantId()`、`getOrgId()`、`getAppKey()` 等方法读取可信调用身份。
- 第三方开放接口在进入查询或写入用例时必须校验 `AuthContext.getTenantId()` 非空;缺少租户上下文立即拒绝,禁止退化为跨租户查询或写入租户为空的数据。
- 框架虽然兼容读取 `tenant_id`、`tenantId`、`webTenantId` 请求参数或请求头,但这些值不能替代 token 鉴权,也不能直接作为第三方接口的可信租户身份。租户必须以认证完成后的 `AuthContext` 为准。
- 查询指定企业时仍须受当前租户约束;不传企业 ID 时只查询当前 `tenantId` 下的数据。接收报警时,区域、摄像头、企业及最终写入记录必须属于当前租户。
- Controller 是否标记为 `/open/**` 不代表允许匿名调用。对外接口需同时在网关配置应用授权、签名、防重放、限流和允许访问的企业范围;业务服务继续执行租户兜底校验。
- MyBatis/BaseDO 的租户插件和自动填充只能作为持久化保护不能替代用例入口的租户校验。异步任务、Dubbo、XXL-JOB 等无 HTTP token 场景必须显式建立可信租户上下文或把租户作为经过校验的任务参数。
## 12. 开发完成检查表
- [ ] Client、Adapter、App、Domain、Infrastructure 职责和依赖方向正确。 - [ ] Client、Adapter、App、Domain、Infrastructure 职责和依赖方向正确。
- [ ] Cmd/Co/PageQry 分别继承 `Command`、`ClientObject`、`BasePageQuery`。 - [ ] Cmd/Co/PageQry 分别继承 `Command`、`ClientObject`、`BasePageQuery`。

134
OPEN-API.md Normal file
View File

@ -0,0 +1,134 @@
# EdgeGuard 第三方开放接口
## 1. 查询区域及摄像头关系
```http
GET /open/edgeguard/regions?corpId={corpId}
```
`corpId` 非必填:传入时查询指定企业,不传时查询当前调用方租户下全部企业。接口只返回 `ENABLED` 区域,以及区域下 `BOUND` 状态的摄像头和当前有效的授权人员。
区域中的 `users` 仅包含权限状态为 `ACTIVE`、已到生效开始时间、未到有效结束时间且用户仍处于启用状态的人员;已撤销、已过期、未生效或已停用用户均不返回。
摄像头字段来自海康 `/videopatrol/fixedCamera/videoSelectList`
| 开放字段 | 海康字段 | 说明 |
| --- | --- | --- |
| `cameraId` | `indexCode` | 摄像头唯一编码 |
| `cameraName` | `name` | 摄像头名称 |
| `cameraRegionName` | `regionName` | 海康平台所属区域名称快照 |
| `cameraSource` | 后台固定 | 当前为 `HIKVISION` |
`users` 人员字段:
| 字段 | 说明 |
| --- | --- |
| `userId` | 被授权人员用户 ID |
| `userName` | 人员姓名 |
| `permissionType` | 权限类型:`TEMPORARY`(临时)或 `FIXED`(固定) |
| `effectiveStartTime` | 授权生效开始时间,格式 `yyyy-MM-dd HH:mm:ss` |
| `effectiveEndTime` | 授权生效结束时间;固定权限为 `null` |
| `authorizationSource` | 授权来源:`MANUAL`、`IMPORT` 或 `API` |
响应示例:
```json
{
"success": true,
"data": [{
"regionId": 1001,
"regionCode": "8d9d07f4d9db4c49906a7a891474f3af",
"regionName": "仓库东区",
"corpId": 2001,
"parentId": 0,
"treePath": "/",
"treeLevel": 1,
"sortNo": 10,
"coordinateSystem": "GCJ02",
"fencePoints": [
{"seq": 1, "longitude": 113.1, "latitude": 23.1},
{"seq": 2, "longitude": 113.2, "latitude": 23.1},
{"seq": 3, "longitude": 113.2, "latitude": 23.2},
{"seq": 4, "longitude": 113.1, "latitude": 23.2}
],
"regionStatus": "ENABLED",
"cameras": [{
"cameraSource": "HIKVISION",
"cameraId": "CAMERA-001",
"cameraName": "东门摄像头",
"cameraRegionName": "海康园区一层"
}],
"users": [{
"userId": 3001,
"userName": "张三",
"permissionType": "TEMPORARY",
"effectiveStartTime": "2026-09-03 08:00:00",
"effectiveEndTime": "2026-09-03 18:00:00",
"authorizationSource": "MANUAL"
}]
}]
}
```
## 2. 接收第三方报警
```http
POST /open/edgeguard/alarms
Content-Type: application/json
```
请求字段:
| 字段 | 必填 | 最大长度 | 说明 |
| --- | --- | --- | --- |
| `alarmNo` | 是 | 128 | 第三方报警唯一编号 |
| `sourceSystem` | 是 | 64 | 报警来源系统编码 |
| `recognitionResult` | 是 | 100 | 识别结论,例如“已知人员无权限”或“无法匹配的陌生人员” |
| `alarmTime` | 是 | - | `yyyy-MM-dd HH:mm:ss`,东八区 |
| `regionCode` | 是 | 64 | 区域查询接口返回的稳定编码 |
| `cameraSource` | 是 | 64 | 当前海康摄像头传 `HIKVISION` |
| `cameraId` | 是 | 128 | 区域查询接口返回的摄像头编码 |
| `alarmAddress` | 否 | 500 | 报警位置描述 |
| `alarmImageUrl` | 否 | 1000 | 报警截图地址 |
| `remarks` | 否 | 255 | 补充说明 |
请求示例:
```json
{
"alarmNo": "ALARM-20260903-000001",
"sourceSystem": "FACE_AI",
"recognitionResult": "已知人员无权限",
"alarmTime": "2026-09-03 10:30:00",
"regionCode": "8d9d07f4d9db4c49906a7a891474f3af",
"cameraSource": "HIKVISION",
"cameraId": "CAMERA-001",
"alarmAddress": "仓库东区入口",
"alarmImageUrl": "https://files.example.com/alarm/001.jpg",
"remarks": "人员进入限制区域"
}
```
后台使用 `regionCode + cameraSource + cameraId` 校验摄像头确实绑定在该区域,并从本地数据补充企业、区域和摄像头名称快照。`receivedTime` 和 `disposalStatus=PENDING` 由后台生成。
`sourceSystem + alarmNo` 是幂等键。重复推送返回第一次保存的报警,不重复入库:
```json
{
"success": true,
"data": {
"alarmId": 5001,
"alarmNo": "ALARM-20260903-000001",
"receivedTime": "2026-09-03 10:30:02",
"duplicate": false
}
}
```
## 3. 接入约束
- 两个开放接口都必须携带平台签发的 `token` 请求头。框架认证过滤器解析 token 后把租户信息写入 `AuthContext`,业务接口不接受第三方自行传入的 `tenantId` 作为身份依据。
- 开放接口上线前必须在网关配置第三方应用鉴权、签名、防重放及企业授权范围。
- `corpId` 只作为可选查询条件;不传时按鉴权上下文中的租户范围查询,最终允许访问的企业范围以应用授权为准。
- 第三方报警中的区域和摄像头组合必须来自区域查询接口。
- 报警图片接口只保存 URL不同步下载图片URL 中禁止携带永久明文密钥。

View File

@ -0,0 +1,41 @@
package com.zcloud.gbs.edgeguard.web;
import com.alibaba.cola.dto.MultiResponse;
import com.alibaba.cola.dto.SingleResponse;
import com.zcloud.gbs.edgeguard.api.OpenEdgeGuardServiceI;
import com.zcloud.gbs.edgeguard.dto.OpenAlarmReceiveCmd;
import com.zcloud.gbs.edgeguard.dto.clientobject.OpenAlarmReceiptCo;
import com.zcloud.gbs.edgeguard.dto.clientobject.OpenRegionCo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/** 接收经过网关鉴权的第三方区域查询和报警推送请求。 */
@Api(tags = "边缘人脸识别区域安全-第三方开放接口")
@RestController
@RequestMapping("/open/edgeguard")
@RequiredArgsConstructor
public class OpenEdgeGuardController {
private final OpenEdgeGuardServiceI openEdgeGuardService;
@ApiOperation("查询企业区域及摄像头绑定关系")
@GetMapping("/regions")
public MultiResponse<OpenRegionCo> regions(
@RequestParam(value = "corpId", required = false) Long corpId) {
return openEdgeGuardService.listRegions(corpId);
}
@ApiOperation("接收第三方报警")
@PostMapping("/alarms")
public SingleResponse<OpenAlarmReceiptCo> receiveAlarm(
@Validated @RequestBody OpenAlarmReceiveCmd cmd) {
return openEdgeGuardService.receiveAlarm(cmd);
}
}

View File

@ -0,0 +1,51 @@
package com.zcloud.gbs.edgeguard.web;
import com.alibaba.cola.dto.PageResponse;
import com.alibaba.cola.dto.Response;
import com.alibaba.cola.dto.SingleResponse;
import com.zcloud.gbs.edgeguard.api.UserRegionPermissionServiceI;
import com.zcloud.gbs.edgeguard.dto.UserRegionPermissionAddCmd;
import com.zcloud.gbs.edgeguard.dto.UserRegionPermissionPageQry;
import com.zcloud.gbs.edgeguard.dto.UserRegionPermissionUpdateCmd;
import com.zcloud.gbs.edgeguard.dto.clientobject.UserRegionPermissionCo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@Api(tags = "边缘人脸区域安全-人员区域授权")
@RestController
@RequestMapping("/edgeguard/user-region-permissions")
@RequiredArgsConstructor
public class UserRegionPermissionController {
private final UserRegionPermissionServiceI permissionService;
@ApiOperation("分页查询人员区域授权")
@GetMapping
public PageResponse<UserRegionPermissionCo> page(@Validated UserRegionPermissionPageQry qry) { return permissionService.page(qry); }
@ApiOperation("查询人员区域授权详情")
@GetMapping("/{id}")
public SingleResponse<UserRegionPermissionCo> detail(@PathVariable Long id) { return permissionService.getById(id); }
@ApiOperation("新增人员区域授权")
@PostMapping
public SingleResponse<Long> add(@Validated @RequestBody UserRegionPermissionAddCmd cmd) { return permissionService.add(cmd); }
@ApiOperation("修改人员区域授权")
@PutMapping("/{id}")
public Response update(@PathVariable Long id, @Validated @RequestBody UserRegionPermissionUpdateCmd cmd) { return permissionService.update(id, cmd); }
@ApiOperation("撤销人员区域授权")
@DeleteMapping("/{id}")
public Response remove(@PathVariable Long id, @RequestParam(required = false) String revokeReason) { return permissionService.remove(id, revokeReason); }
}

View File

@ -0,0 +1,119 @@
package com.zcloud.gbs.edgeguard.command;
import com.alibaba.cola.exception.BizException;
import com.jjb.saas.framework.auth.utils.AuthContext;
import com.zcloud.gbs.edgeguard.domain.gateway.AlarmGateway;
import com.zcloud.gbs.edgeguard.domain.gateway.CorpGateway;
import com.zcloud.gbs.edgeguard.domain.gateway.RegionCameraGateway;
import com.zcloud.gbs.edgeguard.domain.gateway.RegionGateway;
import com.zcloud.gbs.edgeguard.domain.model.AlarmE;
import com.zcloud.gbs.edgeguard.domain.model.CorpE;
import com.zcloud.gbs.edgeguard.domain.model.RegionCameraE;
import com.zcloud.gbs.edgeguard.domain.model.RegionE;
import com.zcloud.gbs.edgeguard.dto.OpenAlarmReceiveCmd;
import com.zcloud.gbs.edgeguard.dto.clientobject.OpenAlarmReceiptCo;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/** 校验并组装第三方报警,领域快照均以本地绑定关系为准。 */
@Component
@RequiredArgsConstructor
public class OpenAlarmReceiveExe {
private final AlarmGateway alarmGateway;
private final RegionGateway regionGateway;
private final RegionCameraGateway cameraGateway;
private final CorpGateway corpGateway;
private final OpenAlarmSaveTransaction saveTransaction;
public OpenAlarmReceiptCo execute(OpenAlarmReceiveCmd cmd) {
Long tenantId = AuthContext.getTenantId();
if (tenantId == null) {
throw new BizException("无法获取当前租户信息");
}
String sourceSystem = cmd.getSourceSystem().trim();
String alarmNo = cmd.getAlarmNo().trim();
AlarmE existing = alarmGateway.getBySourceAndAlarmNo(sourceSystem, alarmNo);
if (existing != null) {
return receipt(existing, true);
}
RegionE region = regionGateway.getByCode(cmd.getRegionCode().trim());
if (region == null || !tenantId.equals(region.getTenantId())
|| !"ENABLED".equals(region.getRegionStatus())) {
throw new BizException("区域不存在或已停用");
}
RegionCameraE camera = cameraGateway.getBound(region.getId(),
cmd.getCameraSource().trim().toUpperCase(), cmd.getCameraId().trim());
if (camera == null) {
throw new BizException("摄像头未绑定到指定区域");
}
CorpE corp = corpGateway.getById(region.getCorpId());
Date receivedTime = new Date();
AlarmE alarm = new AlarmE();
alarm.setAlarmNo(alarmNo);
alarm.setSourceSystem(sourceSystem);
alarm.setRecognitionResult(cmd.getRecognitionResult().trim());
alarm.setAlarmTime(cmd.getAlarmTime());
alarm.setReceivedTime(receivedTime);
alarm.setAlarmAddress(trimToNull(cmd.getAlarmAddress()));
alarm.setCorpId(region.getCorpId());
alarm.setCorpNameSnapshot(corp == null ? null : corp.getCorpName());
alarm.setRegionId(region.getId());
alarm.setRegionCode(region.getRegionCode());
alarm.setRegionNameSnapshot(region.getRegionName());
alarm.setRegionPathSnapshot(buildRegionPath(region));
alarm.setCameraSource(camera.getCameraSource());
alarm.setCameraId(camera.getCameraId());
alarm.setCameraNameSnapshot(camera.getCameraName());
alarm.setAlarmImageUrl(trimToNull(cmd.getAlarmImageUrl()));
alarm.setDisposalStatus("PENDING");
alarm.setRemarks(trimToNull(cmd.getRemarks()));
OpenAlarmSaveTransaction.SaveResult saved = saveTransaction.execute(alarm);
return receipt(saved.getAlarm(), saved.isDuplicate());
}
private OpenAlarmReceiptCo receipt(AlarmE alarm, boolean duplicate) {
OpenAlarmReceiptCo result = new OpenAlarmReceiptCo();
result.setAlarmId(alarm.getId());
result.setAlarmNo(alarm.getAlarmNo());
result.setReceivedTime(alarm.getReceivedTime());
result.setDuplicate(duplicate);
return result;
}
private String buildRegionPath(RegionE region) {
List<Long> ancestorIds = new ArrayList<>();
if (region.getTreePath() != null) {
for (String value : region.getTreePath().split("/")) {
if (!value.trim().isEmpty()) {
ancestorIds.add(Long.valueOf(value));
}
}
}
Map<Long, String> names = new HashMap<>();
for (RegionE ancestor : regionGateway.listByIds(ancestorIds)) {
names.put(ancestor.getId(), ancestor.getRegionName());
}
List<String> path = new ArrayList<>();
for (Long ancestorId : ancestorIds) {
if (names.get(ancestorId) != null) {
path.add(names.get(ancestorId));
}
}
path.add(region.getRegionName());
return String.join("/", path);
}
private String trimToNull(String value) {
if (value == null || value.trim().isEmpty()) {
return null;
}
return value.trim();
}
}

View File

@ -0,0 +1,38 @@
package com.zcloud.gbs.edgeguard.command;
import com.alibaba.cola.exception.BizException;
import com.zcloud.gbs.edgeguard.domain.gateway.AlarmGateway;
import com.zcloud.gbs.edgeguard.domain.model.AlarmE;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
/** 在本地事务内完成报警幂等检查和保存。 */
@Component
@RequiredArgsConstructor
public class OpenAlarmSaveTransaction {
private final AlarmGateway alarmGateway;
@Transactional(rollbackFor = Exception.class)
public SaveResult execute(AlarmE alarm) {
AlarmE existing = alarmGateway.getBySourceAndAlarmNo(alarm.getSourceSystem(), alarm.getAlarmNo());
if (existing != null) {
return new SaveResult(existing, true);
}
Long alarmId = alarmGateway.add(alarm);
if (alarmId == null) {
throw new BizException("报警保存失败");
}
return new SaveResult(alarm, false);
}
/** 报警保存结果duplicate 表示本次请求命中了已有报警。 */
@Getter
@AllArgsConstructor
public static class SaveResult {
private final AlarmE alarm;
private final boolean duplicate;
}
}

View File

@ -0,0 +1,39 @@
package com.zcloud.gbs.edgeguard.command;
import com.alibaba.cola.exception.BizException;
import com.zcloud.gbs.edgeguard.domain.gateway.UserRegionPermissionGateway;
import com.zcloud.gbs.edgeguard.domain.model.RegionE;
import com.zcloud.gbs.edgeguard.domain.model.UserRegionPermissionE;
import com.zcloud.gbs.edgeguard.dto.UserRegionPermissionAddCmd;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
@Component
@RequiredArgsConstructor
public class UserRegionPermissionAddExe {
private final UserRegionPermissionGateway permissionGateway;
private final UserRegionPermissionCommandSupport support;
@Transactional(rollbackFor = Exception.class)
public Long execute(UserRegionPermissionAddCmd cmd) {
String type = cmd.getPermissionType().trim().toUpperCase();
String source = cmd.getAuthorizationSource().trim().toUpperCase();
support.validatePeriod(type, cmd.getEffectiveStartTime(), cmd.getEffectiveEndTime());
support.validateSource(source);
RegionE region = support.validateUserAndRegion(cmd.getUserId(), cmd.getRegionId());
support.validateNoOverlap(cmd.getUserId(), cmd.getRegionId(), cmd.getEffectiveStartTime(), cmd.getEffectiveEndTime(), null);
Date now = new Date();
UserRegionPermissionE entity = new UserRegionPermissionE();
entity.setUserId(cmd.getUserId()); entity.setRegionId(cmd.getRegionId()); entity.setCorpId(region.getCorpId());
entity.setPermissionType(type); entity.setEffectiveStartTime(cmd.getEffectiveStartTime()); entity.setEffectiveEndTime(cmd.getEffectiveEndTime());
entity.setAuthorizationSource(source); entity.setSourceRefNo(cmd.getSourceRefNo());
entity.setPermissionStatus(support.currentStatus(cmd.getEffectiveStartTime(), cmd.getEffectiveEndTime(), now));
entity.setRemarks(cmd.getRemarks());
Long id = permissionGateway.add(entity);
if (id == null) throw new BizException("新增人员区域授权失败");
return id;
}
}

View File

@ -0,0 +1,62 @@
package com.zcloud.gbs.edgeguard.command;
import com.alibaba.cola.exception.BizException;
import com.zcloud.gbs.edgeguard.domain.gateway.UserGateway;
import com.zcloud.gbs.edgeguard.domain.gateway.UserRegionPermissionGateway;
import com.zcloud.gbs.edgeguard.domain.model.RegionE;
import com.zcloud.gbs.edgeguard.domain.model.UserE;
import com.zcloud.gbs.edgeguard.domain.model.UserRegionPermissionE;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.List;
@Component
@RequiredArgsConstructor
public class UserRegionPermissionCommandSupport {
private final UserGateway userGateway;
private final UserRegionPermissionGateway permissionGateway;
private final RegionAccessService accessService;
public RegionE validateUserAndRegion(Long userId, Long regionId) {
RegionE region = accessService.requireAccessibleRegion(regionId);
UserE user = userGateway.getById(userId);
if (user == null || !user.isActive()) throw new BizException("被授权人员不存在或已停用");
if (user.getCorpId() != null && !region.getCorpId().equals(user.getCorpId())) {
throw new BizException("被授权人员不属于区域所属企业");
}
return region;
}
public void validatePeriod(String type, Date start, Date end) {
if (!"TEMPORARY".equals(type) && !"FIXED".equals(type)) throw new BizException("权限类型仅支持 TEMPORARY 或 FIXED");
if ("TEMPORARY".equals(type) && end == null) throw new BizException("临时权限必须填写有效结束时间");
if ("FIXED".equals(type) && end != null) throw new BizException("固定权限不能填写有效结束时间");
if (end != null && !end.after(start)) throw new BizException("有效结束时间必须晚于开始时间");
}
public void validateSource(String source) {
if (!"MANUAL".equals(source) && !"IMPORT".equals(source) && !"API".equals(source)) {
throw new BizException("授权来源仅支持 MANUAL、IMPORT 或 API");
}
}
public void validateNoOverlap(Long userId, Long regionId, Date start, Date end, Long excludeId) {
List<UserRegionPermissionE> rows = permissionGateway.listOpenByUserAndRegion(userId, regionId, excludeId);
for (UserRegionPermissionE row : rows) {
if (overlaps(start, end, row.getEffectiveStartTime(), row.getEffectiveEndTime())) {
throw new BizException("该人员在所选区域已有时间重叠的有效授权");
}
}
}
public String currentStatus(Date start, Date end, Date now) {
if (end != null && !end.after(now)) return "EXPIRED";
return start.after(now) ? "PENDING" : "ACTIVE";
}
private boolean overlaps(Date leftStart, Date leftEnd, Date rightStart, Date rightEnd) {
return (rightEnd == null || leftStart.before(rightEnd)) && (leftEnd == null || rightStart.before(leftEnd));
}
}

View File

@ -0,0 +1,27 @@
package com.zcloud.gbs.edgeguard.command;
import com.alibaba.cola.exception.BizException;
import com.zcloud.gbs.edgeguard.domain.gateway.UserRegionPermissionGateway;
import com.zcloud.gbs.edgeguard.domain.model.UserRegionPermissionE;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
@Component
@RequiredArgsConstructor
public class UserRegionPermissionRemoveExe {
private final UserRegionPermissionGateway permissionGateway;
private final RegionAccessService accessService;
@Transactional(rollbackFor = Exception.class)
public void execute(Long id, String revokeReason) {
UserRegionPermissionE entity = permissionGateway.getById(id);
if (entity == null) throw new BizException("人员区域授权记录不存在");
accessService.requireAccessibleRegion(entity.getRegionId());
if ("REVOKED".equals(entity.getPermissionStatus())) throw new BizException("授权已撤销");
entity.setPermissionStatus("REVOKED"); entity.setRevokeTime(new Date()); entity.setRevokeReason(revokeReason);
if (!permissionGateway.update(entity)) throw new BizException("撤销人员区域授权失败");
}
}

View File

@ -0,0 +1,41 @@
package com.zcloud.gbs.edgeguard.command;
import com.alibaba.cola.exception.BizException;
import com.zcloud.gbs.edgeguard.domain.gateway.UserRegionPermissionGateway;
import com.zcloud.gbs.edgeguard.domain.model.RegionE;
import com.zcloud.gbs.edgeguard.domain.model.UserRegionPermissionE;
import com.zcloud.gbs.edgeguard.dto.UserRegionPermissionUpdateCmd;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
@Component
@RequiredArgsConstructor
public class UserRegionPermissionUpdateExe {
private final UserRegionPermissionGateway permissionGateway;
private final UserRegionPermissionCommandSupport support;
@Transactional(rollbackFor = Exception.class)
public void execute(Long id, UserRegionPermissionUpdateCmd cmd) {
UserRegionPermissionE entity = permissionGateway.getById(id);
if (entity == null) throw new BizException("人员区域授权记录不存在");
if ("REVOKED".equals(entity.getPermissionStatus()) || "EXPIRED".equals(entity.getPermissionStatus())
|| (entity.getEffectiveEndTime() != null && !entity.getEffectiveEndTime().after(new Date()))) {
throw new BizException("已撤销或已过期的授权不可修改");
}
String type = cmd.getPermissionType().trim().toUpperCase();
String source = cmd.getAuthorizationSource().trim().toUpperCase();
support.validatePeriod(type, cmd.getEffectiveStartTime(), cmd.getEffectiveEndTime());
support.validateSource(source);
RegionE region = support.validateUserAndRegion(cmd.getUserId(), cmd.getRegionId());
support.validateNoOverlap(cmd.getUserId(), cmd.getRegionId(), cmd.getEffectiveStartTime(), cmd.getEffectiveEndTime(), id);
entity.setUserId(cmd.getUserId()); entity.setRegionId(cmd.getRegionId()); entity.setCorpId(region.getCorpId());
entity.setPermissionType(type); entity.setEffectiveStartTime(cmd.getEffectiveStartTime()); entity.setEffectiveEndTime(cmd.getEffectiveEndTime());
entity.setAuthorizationSource(source); entity.setSourceRefNo(cmd.getSourceRefNo()); entity.setRemarks(cmd.getRemarks());
entity.setPermissionStatus(support.currentStatus(cmd.getEffectiveStartTime(), cmd.getEffectiveEndTime(), new Date()));
entity.setVersion(cmd.getVersion());
if (!permissionGateway.update(entity)) throw new BizException("修改人员区域授权失败,记录可能已被更新");
}
}

View File

@ -1,20 +1,77 @@
package com.zcloud.gbs.edgeguard.command.query; package com.zcloud.gbs.edgeguard.command.query;
import com.alibaba.cola.dto.PageResponse; import com.alibaba.cola.dto.PageResponse;
import com.zcloud.gbs.edgeguard.command.RegionAccessService;
import com.zcloud.gbs.edgeguard.command.AlarmHiddenStatusSyncExe; import com.zcloud.gbs.edgeguard.command.AlarmHiddenStatusSyncExe;
import com.zcloud.gbs.edgeguard.domain.gateway.*; import com.zcloud.gbs.edgeguard.command.RegionAccessService;
import com.zcloud.gbs.edgeguard.domain.model.*; import com.zcloud.gbs.edgeguard.domain.gateway.AlarmDisposalGateway;
import com.zcloud.gbs.edgeguard.domain.gateway.AlarmGateway;
import com.zcloud.gbs.edgeguard.domain.model.AlarmDisposalE;
import com.zcloud.gbs.edgeguard.domain.model.AlarmE;
import com.zcloud.gbs.edgeguard.dto.AlarmPageQry; import com.zcloud.gbs.edgeguard.dto.AlarmPageQry;
import com.zcloud.gbs.edgeguard.dto.clientobject.*; import com.zcloud.gbs.edgeguard.dto.clientobject.AlarmCo;
import com.zcloud.gbs.edgeguard.dto.clientobject.AlarmDisposalCo;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.*;
@Component @RequiredArgsConstructor import java.util.ArrayList;
import java.util.List;
/** 报警分页和详情查询执行器。 */
@Component
@RequiredArgsConstructor
public class AlarmQueryExe { public class AlarmQueryExe {
private final AlarmGateway alarms; private final AlarmDisposalGateway disposals; private final RegionAccessService access; private final AlarmGateway alarms;
private final AlarmDisposalGateway disposals;
private final RegionAccessService access;
private final AlarmHiddenStatusSyncExe hiddenStatusSyncExe; private final AlarmHiddenStatusSyncExe hiddenStatusSyncExe;
public PageResponse<AlarmCo> page(AlarmPageQry q){ if(q.getCorpId()!=null)access.requireAccessibleCorp(q.getCorpId()); if(q.getRegionId()!=null)access.requireAccessibleRegion(q.getRegionId()); int i=q.getPageIndex()<=0?1:q.getPageIndex(),s=q.getPageSize()<=0?10:Math.min(q.getPageSize(),200); List<AlarmCo> out=new ArrayList<>(); for(AlarmE a:alarms.page(q.getAlarmNo(),q.getCorpId(),q.getRegionId(),q.getDisposalStatus(),q.getAlarmTimeStart(),q.getAlarmTimeEnd(),i,s))out.add(co(a,false)); return PageResponse.of(out,(int)alarms.count(q.getAlarmNo(),q.getCorpId(),q.getRegionId(),q.getDisposalStatus(),q.getAlarmTimeStart(),q.getAlarmTimeEnd()),s,i); }
public AlarmCo detail(Long id){ AlarmE a=alarms.getById(id); if(a==null)return null; hiddenStatusSyncExe.sync(a); return co(a,true); } /**
private AlarmCo co(AlarmE a,boolean detail){AlarmCo c=new AlarmCo();BeanUtils.copyProperties(a,c);if(detail){AlarmDisposalE d=disposals.getByAlarmId(a.getId());if(d!=null){AlarmDisposalCo x=new AlarmDisposalCo();BeanUtils.copyProperties(d,x);c.setDisposal(x);}}return c;} *
*/
public PageResponse<AlarmCo> page(AlarmPageQry query) {
if (query.getCorpId() != null) {
access.requireAccessibleCorp(query.getCorpId());
}
if (query.getRegionId() != null) {
access.requireAccessibleRegion(query.getRegionId());
}
int pageIndex = query.getPageIndex() <= 0 ? 1 : query.getPageIndex();
int pageSize = query.getPageSize() <= 0 ? 10 : Math.min(query.getPageSize(), 200);
List<AlarmCo> result = new ArrayList<>();
for (AlarmE alarm : alarms.page(query.getAlarmNo(), query.getCorpId(), query.getRegionId(),
query.getDisposalStatus(), query.getAlarmTimeStart(), query.getAlarmTimeEnd(),
pageIndex, pageSize)) {
result.add(toCo(alarm, false));
}
long total = alarms.count(query.getAlarmNo(), query.getCorpId(), query.getRegionId(),
query.getDisposalStatus(), query.getAlarmTimeStart(), query.getAlarmTimeEnd());
return PageResponse.of(result, (int) total, pageSize, pageIndex);
}
/**
*
*/
public AlarmCo detail(Long id) {
AlarmE alarm = alarms.getById(id);
if (alarm == null) {
return null;
}
hiddenStatusSyncExe.sync(alarm);
return toCo(alarm, true);
}
private AlarmCo toCo(AlarmE alarm, boolean includeDisposal) {
AlarmCo result = new AlarmCo();
BeanUtils.copyProperties(alarm, result);
if (includeDisposal) {
AlarmDisposalE disposal = disposals.getByAlarmId(alarm.getId());
if (disposal != null) {
AlarmDisposalCo disposalCo = new AlarmDisposalCo();
BeanUtils.copyProperties(disposal, disposalCo);
result.setDisposal(disposalCo);
}
}
return result;
}
} }

View File

@ -0,0 +1,103 @@
package com.zcloud.gbs.edgeguard.command.query;
import com.alibaba.cola.dto.MultiResponse;
import com.alibaba.cola.exception.BizException;
import com.alibaba.fastjson.JSON;
import com.jjb.saas.framework.auth.utils.AuthContext;
import com.zcloud.gbs.edgeguard.domain.gateway.RegionCameraGateway;
import com.zcloud.gbs.edgeguard.domain.gateway.RegionGateway;
import com.zcloud.gbs.edgeguard.domain.gateway.UserGateway;
import com.zcloud.gbs.edgeguard.domain.gateway.UserRegionPermissionGateway;
import com.zcloud.gbs.edgeguard.domain.model.RegionCameraE;
import com.zcloud.gbs.edgeguard.domain.model.RegionE;
import com.zcloud.gbs.edgeguard.domain.model.UserE;
import com.zcloud.gbs.edgeguard.domain.model.UserRegionPermissionE;
import com.zcloud.gbs.edgeguard.dto.clientobject.FencePointCo;
import com.zcloud.gbs.edgeguard.dto.clientobject.OpenCameraCo;
import com.zcloud.gbs.edgeguard.dto.clientobject.OpenRegionCo;
import com.zcloud.gbs.edgeguard.dto.clientobject.OpenRegionUserCo;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.HashSet;
import java.util.Date;
import java.util.stream.Collectors;
/** 第三方区域及摄像头关系查询执行器。 */
@Component
@RequiredArgsConstructor
public class OpenRegionQueryExe {
private final RegionGateway regionGateway;
private final RegionCameraGateway cameraGateway;
private final UserRegionPermissionGateway permissionGateway;
private final UserGateway userGateway;
public MultiResponse<OpenRegionCo> execute(Long corpId) {
Long tenantId = AuthContext.getTenantId();
if (tenantId == null) {
throw new BizException("无法获取当前租户信息");
}
List<RegionE> source = corpId == null
? regionGateway.listByTenantId(tenantId)
: regionGateway.listByCorpIds(Collections.singletonList(corpId));
List<RegionE> regions = source.stream()
.filter(region -> "ENABLED".equals(region.getRegionStatus()))
.collect(Collectors.toList());
List<Long> regionIds = regions.stream().map(RegionE::getId).collect(Collectors.toList());
Date now = new Date();
permissionGateway.refreshStatuses(now);
Map<Long, List<OpenCameraCo>> camerasByRegion = new HashMap<>();
for (RegionCameraE camera : cameraGateway.listBoundByRegionIds(regionIds)) {
OpenCameraCo co = new OpenCameraCo();
co.setCameraSource(camera.getCameraSource());
co.setCameraId(camera.getCameraId());
co.setCameraName(camera.getCameraName());
co.setCameraRegionName(camera.getCameraRegionName());
camerasByRegion.computeIfAbsent(camera.getRegionId(), key -> new ArrayList<>()).add(co);
}
List<UserRegionPermissionE> permissions = permissionGateway.listEffectiveByRegionIds(regionIds, now);
Set<Long> userIds = new HashSet<>();
for (UserRegionPermissionE permission : permissions) userIds.add(permission.getUserId());
Map<Long, String> userNames = new HashMap<>();
for (UserE user : userGateway.listByIds(userIds)) {
if (user.isActive()) userNames.put(user.getId(), user.getName());
}
Map<Long, List<OpenRegionUserCo>> usersByRegion = new HashMap<>();
for (UserRegionPermissionE permission : permissions) {
if (!userNames.containsKey(permission.getUserId())) continue;
OpenRegionUserCo co = new OpenRegionUserCo();
co.setUserId(permission.getUserId());
co.setUserName(userNames.get(permission.getUserId()));
co.setPermissionType(permission.getPermissionType());
co.setEffectiveStartTime(permission.getEffectiveStartTime());
co.setEffectiveEndTime(permission.getEffectiveEndTime());
co.setAuthorizationSource(permission.getAuthorizationSource());
usersByRegion.computeIfAbsent(permission.getRegionId(), key -> new ArrayList<>()).add(co);
}
List<OpenRegionCo> result = new ArrayList<>();
for (RegionE region : regions) {
OpenRegionCo co = new OpenRegionCo();
co.setRegionId(region.getId());
co.setRegionCode(region.getRegionCode());
co.setRegionName(region.getRegionName());
co.setCorpId(region.getCorpId());
co.setParentId(region.getParentId());
co.setTreePath(region.getTreePath());
co.setTreeLevel(region.getTreeLevel());
co.setSortNo(region.getSortNo());
co.setCoordinateSystem(region.getCoordinateSystem());
co.setFencePoints(JSON.parseArray(region.getFencePoints(), FencePointCo.class));
co.setRegionStatus(region.getRegionStatus());
co.setCameras(camerasByRegion.getOrDefault(region.getId(), Collections.emptyList()));
co.setUsers(usersByRegion.getOrDefault(region.getId(), Collections.emptyList()));
result.add(co);
}
return MultiResponse.of(result);
}
}

View File

@ -0,0 +1,89 @@
package com.zcloud.gbs.edgeguard.command.query;
import com.alibaba.cola.dto.PageResponse;
import com.alibaba.cola.dto.SingleResponse;
import com.alibaba.cola.exception.BizException;
import com.zcloud.gbs.edgeguard.command.RegionAccessService;
import com.zcloud.gbs.edgeguard.command.UserRegionPermissionCommandSupport;
import com.zcloud.gbs.edgeguard.domain.gateway.RegionGateway;
import com.zcloud.gbs.edgeguard.domain.gateway.UserGateway;
import com.zcloud.gbs.edgeguard.domain.gateway.UserRegionPermissionGateway;
import com.zcloud.gbs.edgeguard.domain.model.RegionE;
import com.zcloud.gbs.edgeguard.domain.model.UserE;
import com.zcloud.gbs.edgeguard.domain.model.UserRegionPermissionE;
import com.zcloud.gbs.edgeguard.dto.UserRegionPermissionPageQry;
import com.zcloud.gbs.edgeguard.dto.clientobject.UserRegionPermissionCo;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@Component
@RequiredArgsConstructor
public class UserRegionPermissionQueryExe {
private final UserRegionPermissionGateway permissionGateway;
private final RegionGateway regionGateway;
private final UserGateway userGateway;
private final RegionAccessService accessService;
private final UserRegionPermissionCommandSupport support;
public PageResponse<UserRegionPermissionCo> page(UserRegionPermissionPageQry qry) {
Long corpId = requireQueryScope(qry);
String status = normalizeStatus(qry.getPermissionStatus());
permissionGateway.refreshStatuses(new Date());
int pageIndex = qry.getPageIndex() <= 0 ? 1 : qry.getPageIndex();
int pageSize = qry.getPageSize() <= 0 ? 10 : Math.min(qry.getPageSize(), 200);
List<UserRegionPermissionE> rows = permissionGateway.page(qry.getUserId(), qry.getRegionId(), corpId, status, pageIndex, pageSize);
return PageResponse.of(toCos(rows), (int) permissionGateway.count(qry.getUserId(), qry.getRegionId(), corpId, status), pageSize, pageIndex);
}
public SingleResponse<UserRegionPermissionCo> detail(Long id) {
UserRegionPermissionE entity = permissionGateway.getById(id);
if (entity == null) throw new BizException("人员区域授权记录不存在");
accessService.requireAccessibleRegion(entity.getRegionId());
return SingleResponse.of(toCos(Collections.singletonList(entity)).get(0));
}
private Long requireQueryScope(UserRegionPermissionPageQry qry) {
if (qry.getRegionId() != null) {
RegionE region = accessService.requireAccessibleRegion(qry.getRegionId());
if (qry.getCorpId() != null && !qry.getCorpId().equals(region.getCorpId())) throw new BizException("区域不属于指定企业");
return region.getCorpId();
}
if (qry.getCorpId() == null) throw new BizException("查询人员区域授权时必须指定企业或区域");
accessService.requireAccessibleCorp(qry.getCorpId());
return qry.getCorpId();
}
private String normalizeStatus(String status) {
if (status == null || status.trim().isEmpty()) return null;
String value = status.trim().toUpperCase();
if (!"PENDING".equals(value) && !"ACTIVE".equals(value) && !"EXPIRED".equals(value) && !"REVOKED".equals(value)) throw new BizException("无效的权限状态");
return value;
}
private List<UserRegionPermissionCo> toCos(List<UserRegionPermissionE> rows) {
Set<Long> userIds = new HashSet<>(); Set<Long> regionIds = new HashSet<>();
for (UserRegionPermissionE row : rows) { userIds.add(row.getUserId()); regionIds.add(row.getRegionId()); }
Map<Long, String> userNames = new HashMap<>();
for (UserE user : userGateway.listByIds(userIds)) userNames.put(user.getId(), user.getName());
Map<Long, String> regionNames = new HashMap<>();
for (RegionE region : regionGateway.listByIds(regionIds)) regionNames.put(region.getId(), region.getRegionName());
List<UserRegionPermissionCo> result = new ArrayList<>(); Date now = new Date();
for (UserRegionPermissionE row : rows) {
UserRegionPermissionCo co = new UserRegionPermissionCo(); BeanUtils.copyProperties(row, co);
co.setUserName(userNames.get(row.getUserId())); co.setRegionName(regionNames.get(row.getRegionId()));
if (!"REVOKED".equals(row.getPermissionStatus())) co.setPermissionStatus(support.currentStatus(row.getEffectiveStartTime(), row.getEffectiveEndTime(), now));
result.add(co);
}
return result;
}
}

View File

@ -0,0 +1,30 @@
package com.zcloud.gbs.edgeguard.service;
import com.alibaba.cola.dto.MultiResponse;
import com.alibaba.cola.dto.SingleResponse;
import com.zcloud.gbs.edgeguard.api.OpenEdgeGuardServiceI;
import com.zcloud.gbs.edgeguard.command.OpenAlarmReceiveExe;
import com.zcloud.gbs.edgeguard.command.query.OpenRegionQueryExe;
import com.zcloud.gbs.edgeguard.dto.OpenAlarmReceiveCmd;
import com.zcloud.gbs.edgeguard.dto.clientobject.OpenAlarmReceiptCo;
import com.zcloud.gbs.edgeguard.dto.clientobject.OpenRegionCo;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
/** 第三方开放接口应用服务实现。 */
@Service
@RequiredArgsConstructor
public class OpenEdgeGuardServiceImpl implements OpenEdgeGuardServiceI {
private final OpenRegionQueryExe regionQueryExe;
private final OpenAlarmReceiveExe alarmReceiveExe;
@Override
public MultiResponse<OpenRegionCo> listRegions(Long corpId) {
return regionQueryExe.execute(corpId);
}
@Override
public SingleResponse<OpenAlarmReceiptCo> receiveAlarm(OpenAlarmReceiveCmd cmd) {
return SingleResponse.of(alarmReceiveExe.execute(cmd));
}
}

View File

@ -0,0 +1,31 @@
package com.zcloud.gbs.edgeguard.service;
import com.alibaba.cola.dto.PageResponse;
import com.alibaba.cola.dto.Response;
import com.alibaba.cola.dto.SingleResponse;
import com.zcloud.gbs.edgeguard.api.UserRegionPermissionServiceI;
import com.zcloud.gbs.edgeguard.command.UserRegionPermissionAddExe;
import com.zcloud.gbs.edgeguard.command.UserRegionPermissionRemoveExe;
import com.zcloud.gbs.edgeguard.command.UserRegionPermissionUpdateExe;
import com.zcloud.gbs.edgeguard.command.query.UserRegionPermissionQueryExe;
import com.zcloud.gbs.edgeguard.dto.UserRegionPermissionAddCmd;
import com.zcloud.gbs.edgeguard.dto.UserRegionPermissionPageQry;
import com.zcloud.gbs.edgeguard.dto.UserRegionPermissionUpdateCmd;
import com.zcloud.gbs.edgeguard.dto.clientobject.UserRegionPermissionCo;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class UserRegionPermissionServiceImpl implements UserRegionPermissionServiceI {
private final UserRegionPermissionQueryExe queryExe;
private final UserRegionPermissionAddExe addExe;
private final UserRegionPermissionUpdateExe updateExe;
private final UserRegionPermissionRemoveExe removeExe;
@Override public PageResponse<UserRegionPermissionCo> page(UserRegionPermissionPageQry qry) { return queryExe.page(qry); }
@Override public SingleResponse<UserRegionPermissionCo> getById(Long id) { return queryExe.detail(id); }
@Override public SingleResponse<Long> add(UserRegionPermissionAddCmd cmd) { return SingleResponse.of(addExe.execute(cmd)); }
@Override public Response update(Long id, UserRegionPermissionUpdateCmd cmd) { updateExe.execute(id, cmd); return Response.buildSuccess(); }
@Override public Response remove(Long id, String revokeReason) { removeExe.execute(id, revokeReason); return Response.buildSuccess(); }
}

View File

@ -67,29 +67,80 @@ public class RegionCameraBindTransactionTest {
private final List<RegionCameraE> rows = new ArrayList<>(); private final List<RegionCameraE> rows = new ArrayList<>();
private long sequence = 10; private long sequence = 10;
@Override public Boolean add(RegionCameraE value) { value.setId(sequence++); rows.add(value); return true; } @Override
@Override public Boolean update(RegionCameraE value) { return true; } public Boolean add(RegionCameraE value) {
@Override public Boolean deleteById(Long id) { return false; } value.setId(sequence++);
@Override public RegionCameraE getById(Long id) { return null; } rows.add(value);
@Override public boolean existsBoundByRegionId(Long regionId) { return false; } return true;
@Override public List<RegionCameraE> pageBound(Long regionId, String name, int index, int size) { return Collections.emptyList(); } }
@Override public long countBound(Long regionId, String name) { return 0; }
@Override public List<RegionCameraE> listBoundByRegionId(Long regionId) { @Override
public Boolean update(RegionCameraE value) {
return true;
}
@Override
public Boolean deleteById(Long id) {
return false;
}
@Override
public RegionCameraE getById(Long id) {
return null;
}
@Override
public boolean existsBoundByRegionId(Long regionId) {
return false;
}
@Override
public List<RegionCameraE> pageBound(Long regionId, String name, int index, int size) {
return Collections.emptyList();
}
@Override
public long countBound(Long regionId, String name) {
return 0;
}
@Override
public List<RegionCameraE> listBoundByRegionId(Long regionId) {
List<RegionCameraE> result = new ArrayList<>(); List<RegionCameraE> result = new ArrayList<>();
for (RegionCameraE row : rows) if (regionId.equals(row.getRegionId()) && "BOUND".equals(row.getBindStatus())) result.add(row); for (RegionCameraE row : rows) {
if (regionId.equals(row.getRegionId()) && "BOUND".equals(row.getBindStatus())) {
result.add(row);
}
}
return result; return result;
} }
@Override public List<RegionCameraE> listByKeys(Long corpId, Collection<CameraKeyE> keys) {
@Override
public List<RegionCameraE> listBoundByRegionIds(Collection<Long> regionIds) {
return Collections.emptyList();
}
@Override
public RegionCameraE getBound(Long regionId, String cameraSource, String cameraId) {
return null;
}
@Override
public List<RegionCameraE> listByKeys(Long corpId, Collection<CameraKeyE> keys) {
List<RegionCameraE> result = new ArrayList<>(); List<RegionCameraE> result = new ArrayList<>();
for (CameraKeyE key : keys) { for (CameraKeyE key : keys) {
RegionCameraE value = find(key.identity(), false); RegionCameraE value = find(key.identity(), false);
if (value != null) result.add(value); if (value != null) {
result.add(value);
}
} }
return result; return result;
} }
private RegionCameraE find(String identity, boolean ignored) { private RegionCameraE find(String identity, boolean ignored) {
for (RegionCameraE row : rows) { for (RegionCameraE row : rows) {
if ((row.getCameraSource() + ":" + row.getCameraId()).equals(identity)) return row; if ((row.getCameraSource() + ":" + row.getCameraId()).equals(identity)) {
return row;
}
} }
return null; return null;
} }

View File

@ -1,12 +1,17 @@
package com.zcloud.gbs.edgeguard.api; package com.zcloud.gbs.edgeguard.api;
import com.alibaba.cola.dto.PageResponse; import com.alibaba.cola.dto.PageResponse;
import com.alibaba.cola.dto.Response; import com.alibaba.cola.dto.Response;
import com.alibaba.cola.dto.SingleResponse; import com.alibaba.cola.dto.SingleResponse;
import com.zcloud.gbs.edgeguard.dto.AlarmDisposeCmd; import com.zcloud.gbs.edgeguard.dto.AlarmDisposeCmd;
import com.zcloud.gbs.edgeguard.dto.AlarmPageQry; import com.zcloud.gbs.edgeguard.dto.AlarmPageQry;
import com.zcloud.gbs.edgeguard.dto.clientobject.AlarmCo; import com.zcloud.gbs.edgeguard.dto.clientobject.AlarmCo;
/** 报警查询与处置应用服务。 */
public interface AlarmServiceI { public interface AlarmServiceI {
PageResponse<AlarmCo> page(AlarmPageQry qry); PageResponse<AlarmCo> page(AlarmPageQry qry);
SingleResponse<AlarmCo> detail(Long id); SingleResponse<AlarmCo> detail(Long id);
Response dispose(Long id, AlarmDisposeCmd cmd); Response dispose(Long id, AlarmDisposeCmd cmd);
} }

View File

@ -0,0 +1,12 @@
package com.zcloud.gbs.edgeguard.api;
import com.alibaba.cola.dto.MultiResponse;
import com.alibaba.cola.dto.SingleResponse;
import com.zcloud.gbs.edgeguard.dto.OpenAlarmReceiveCmd;
import com.zcloud.gbs.edgeguard.dto.clientobject.OpenAlarmReceiptCo;
import com.zcloud.gbs.edgeguard.dto.clientobject.OpenRegionCo;
public interface OpenEdgeGuardServiceI {
MultiResponse<OpenRegionCo> listRegions(Long corpId);
SingleResponse<OpenAlarmReceiptCo> receiveAlarm(OpenAlarmReceiveCmd cmd);
}

View File

@ -0,0 +1,17 @@
package com.zcloud.gbs.edgeguard.api;
import com.alibaba.cola.dto.PageResponse;
import com.alibaba.cola.dto.Response;
import com.alibaba.cola.dto.SingleResponse;
import com.zcloud.gbs.edgeguard.dto.UserRegionPermissionAddCmd;
import com.zcloud.gbs.edgeguard.dto.UserRegionPermissionPageQry;
import com.zcloud.gbs.edgeguard.dto.UserRegionPermissionUpdateCmd;
import com.zcloud.gbs.edgeguard.dto.clientobject.UserRegionPermissionCo;
public interface UserRegionPermissionServiceI {
PageResponse<UserRegionPermissionCo> page(UserRegionPermissionPageQry qry);
SingleResponse<UserRegionPermissionCo> getById(Long id);
SingleResponse<Long> add(UserRegionPermissionAddCmd cmd);
Response update(Long id, UserRegionPermissionUpdateCmd cmd);
Response remove(Long id, String revokeReason);
}

View File

@ -1,9 +1,14 @@
package com.zcloud.gbs.edgeguard.dto; package com.zcloud.gbs.edgeguard.dto;
import com.alibaba.cola.dto.PageQuery; import com.alibaba.cola.dto.PageQuery;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import java.util.Date; import java.util.Date;
@Data @EqualsAndHashCode(callSuper = true)
/** 报警分页查询条件。 */
@Data
@EqualsAndHashCode(callSuper = true)
public class AlarmPageQry extends PageQuery { public class AlarmPageQry extends PageQuery {
private String alarmNo; private String alarmNo;
private Long corpId; private Long corpId;

View File

@ -0,0 +1,51 @@
package com.zcloud.gbs.edgeguard.dto;
import com.alibaba.cola.dto.Command;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.Date;
@Data
@EqualsAndHashCode(callSuper = true)
public class OpenAlarmReceiveCmd extends Command {
@NotBlank(message = "报警编号不能为空")
@Size(max = 128, message = "报警编号长度不能超过128")
@ApiModelProperty(value = "第三方报警唯一编号", required = true)
private String alarmNo;
@NotBlank(message = "来源系统编码不能为空")
@Size(max = 64, message = "来源系统编码长度不能超过64")
@ApiModelProperty(value = "报警来源系统编码", required = true)
private String sourceSystem;
@NotBlank(message = "识别结论不能为空")
@Size(max = 100, message = "识别结论长度不能超过100")
@ApiModelProperty(value = "识别结论,例如:已知人员无权限、无法匹配的陌生人员", required = true)
private String recognitionResult;
@NotNull(message = "报警时间不能为空")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@ApiModelProperty(value = "报警发生时间", required = true)
private Date alarmTime;
@NotBlank(message = "区域编码不能为空")
@Size(max = 64, message = "区域编码长度不能超过64")
@ApiModelProperty(value = "开放区域接口返回的区域编码", required = true)
private String regionCode;
@NotBlank(message = "摄像头来源不能为空")
@Size(max = 64, message = "摄像头来源长度不能超过64")
@ApiModelProperty(value = "摄像头来源海康固定为HIKVISION", required = true)
private String cameraSource;
@NotBlank(message = "摄像头编码不能为空")
@Size(max = 128, message = "摄像头编码长度不能超过128")
@ApiModelProperty(value = "海康摄像头indexCode", required = true)
private String cameraId;
@Size(max = 500, message = "报警地址长度不能超过500")
private String alarmAddress;
@Size(max = 1000, message = "报警图片地址长度不能超过1000")
private String alarmImageUrl;
@Size(max = 255, message = "备注长度不能超过255")
private String remarks;
}

View File

@ -0,0 +1,24 @@
package com.zcloud.gbs.edgeguard.dto;
import com.alibaba.cola.dto.Command;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.Date;
@Data
@EqualsAndHashCode(callSuper = true)
public class UserRegionPermissionAddCmd extends Command {
@NotNull private Long userId;
@NotNull private Long regionId;
@NotBlank @ApiModelProperty(value = "TEMPORARY 或 FIXED", required = true) private String permissionType;
@NotNull private Date effectiveStartTime;
private Date effectiveEndTime;
@NotBlank @ApiModelProperty(value = "MANUAL、IMPORT 或 API", required = true) private String authorizationSource;
@Size(max = 128) private String sourceRefNo;
@Size(max = 255) private String remarks;
}

View File

@ -0,0 +1,14 @@
package com.zcloud.gbs.edgeguard.dto;
import com.alibaba.cola.dto.PageQuery;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
public class UserRegionPermissionPageQry extends PageQuery {
private Long userId;
private Long regionId;
private Long corpId;
private String permissionStatus;
}

View File

@ -0,0 +1,24 @@
package com.zcloud.gbs.edgeguard.dto;
import com.alibaba.cola.dto.Command;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.Date;
@Data
@EqualsAndHashCode(callSuper = true)
public class UserRegionPermissionUpdateCmd extends Command {
@NotNull private Long userId;
@NotNull private Long regionId;
@NotBlank private String permissionType;
@NotNull private Date effectiveStartTime;
private Date effectiveEndTime;
@NotBlank private String authorizationSource;
@Size(max = 128) private String sourceRefNo;
@NotNull private Integer version;
@Size(max = 255) private String remarks;
}

View File

@ -1,12 +1,30 @@
package com.zcloud.gbs.edgeguard.dto.clientobject; package com.zcloud.gbs.edgeguard.dto.clientobject;
import com.alibaba.cola.dto.ClientObject; import com.alibaba.cola.dto.ClientObject;
import lombok.Data; import lombok.Data;
import java.util.Date; import java.util.Date;
/** 报警列表和详情返回对象。 */
@Data @Data
public class AlarmCo extends ClientObject { public class AlarmCo extends ClientObject {
private Long id; private String alarmNo; private String sourceSystem; private Date alarmTime; private Long id;
private Date receivedTime; private String alarmAddress; private Long corpId; private String corpNameSnapshot; private String alarmNo;
private Long regionId; private String regionCode; private String regionNameSnapshot; private String regionPathSnapshot; private String sourceSystem;
private String cameraSource; private String cameraId; private String cameraNameSnapshot; private String alarmImageUrl; private String recognitionResult;
private String disposalStatus; private AlarmDisposalCo disposal; private Date alarmTime;
private Date receivedTime;
private String alarmAddress;
private Long corpId;
private String corpNameSnapshot;
private Long regionId;
private String regionCode;
private String regionNameSnapshot;
private String regionPathSnapshot;
private String cameraSource;
private String cameraId;
private String cameraNameSnapshot;
private String alarmImageUrl;
private String disposalStatus;
private AlarmDisposalCo disposal;
} }

View File

@ -1,12 +1,27 @@
package com.zcloud.gbs.edgeguard.dto.clientobject; package com.zcloud.gbs.edgeguard.dto.clientobject;
import com.alibaba.cola.dto.ClientObject; import com.alibaba.cola.dto.ClientObject;
import lombok.Data; import lombok.Data;
import java.util.Date; import java.util.Date;
/** 报警处置结果返回对象。 */
@Data @Data
public class AlarmDisposalCo extends ClientObject { public class AlarmDisposalCo extends ClientObject {
private Long id; private Long alarmId; private Integer isFalseAlarm; private Integer isHiddenHazard; private Long id;
private String disposalResultCode; private String disposalResultName; private String disposalDescription; private Long alarmId;
private String hiddenId; private Integer hiddenStatus; private String hiddenStatusName; private Date hiddenSyncTime; private Integer isFalseAlarm;
private Long handlerUserId; private String handlerUserName; private Integer isHiddenHazard;
private Long handlerDeptId; private String handlerDeptName; private Date disposedTime; private String disposalResultCode;
private String disposalResultName;
private String disposalDescription;
private String hiddenId;
private Integer hiddenStatus;
private String hiddenStatusName;
private Date hiddenSyncTime;
private Long handlerUserId;
private String handlerUserName;
private Long handlerDeptId;
private String handlerDeptName;
private Date disposedTime;
} }

View File

@ -0,0 +1,16 @@
package com.zcloud.gbs.edgeguard.dto.clientobject;
import com.alibaba.cola.dto.ClientObject;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.util.Date;
@Data
public class OpenAlarmReceiptCo extends ClientObject {
private Long alarmId;
private String alarmNo;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date receivedTime;
private Boolean duplicate;
}

View File

@ -0,0 +1,12 @@
package com.zcloud.gbs.edgeguard.dto.clientobject;
import com.alibaba.cola.dto.ClientObject;
import lombok.Data;
@Data
public class OpenCameraCo extends ClientObject {
private String cameraSource;
private String cameraId;
private String cameraName;
private String cameraRegionName;
}

View File

@ -0,0 +1,24 @@
package com.zcloud.gbs.edgeguard.dto.clientobject;
import com.alibaba.cola.dto.ClientObject;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class OpenRegionCo extends ClientObject {
private Long regionId;
private String regionCode;
private String regionName;
private Long corpId;
private Long parentId;
private String treePath;
private Integer treeLevel;
private Integer sortNo;
private String coordinateSystem;
private List<FencePointCo> fencePoints = new ArrayList<>();
private String regionStatus;
private List<OpenCameraCo> cameras = new ArrayList<>();
private List<OpenRegionUserCo> users = new ArrayList<>();
}

View File

@ -0,0 +1,16 @@
package com.zcloud.gbs.edgeguard.dto.clientobject;
import com.alibaba.cola.dto.ClientObject;
import lombok.Data;
import java.util.Date;
@Data
public class OpenRegionUserCo extends ClientObject {
private Long userId;
private String userName;
private String permissionType;
private Date effectiveStartTime;
private Date effectiveEndTime;
private String authorizationSource;
}

View File

@ -0,0 +1,28 @@
package com.zcloud.gbs.edgeguard.dto.clientobject;
import com.alibaba.cola.dto.ClientObject;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
@Data
@EqualsAndHashCode(callSuper = true)
public class UserRegionPermissionCo extends ClientObject {
private Long id;
private Long userId;
private String userName;
private Long regionId;
private String regionName;
private Long corpId;
private String permissionType;
private Date effectiveStartTime;
private Date effectiveEndTime;
private String authorizationSource;
private String sourceRefNo;
private String permissionStatus;
private Date revokeTime;
private String revokeReason;
private Integer version;
private String remarks;
}

View File

@ -4,9 +4,10 @@ import com.zcloud.gbs.edgeguard.domain.model.AlarmE;
import java.util.List; import java.util.List;
public interface AlarmGateway { public interface AlarmGateway {
Boolean add(AlarmE alarm); Long add(AlarmE alarm);
Boolean update(AlarmE alarm); Boolean update(AlarmE alarm);
AlarmE getById(Long id); AlarmE getById(Long id);
AlarmE getBySourceAndAlarmNo(String sourceSystem, String alarmNo);
List<AlarmE> page(String alarmNo, Long corpId, Long regionId, String disposalStatus, List<AlarmE> page(String alarmNo, Long corpId, Long regionId, String disposalStatus,
java.util.Date alarmTimeStart, java.util.Date alarmTimeEnd, java.util.Date alarmTimeStart, java.util.Date alarmTimeEnd,
int pageIndex, int pageSize); int pageIndex, int pageSize);

View File

@ -7,4 +7,5 @@ import java.util.List;
/** 当前调用人的可访问企业,由公共企业服务提供。 */ /** 当前调用人的可访问企业,由公共企业服务提供。 */
public interface CorpGateway { public interface CorpGateway {
List<CorpE> listAccessibleCorps(); List<CorpE> listAccessibleCorps();
CorpE getById(Long id);
} }

View File

@ -15,5 +15,7 @@ public interface RegionCameraGateway {
List<RegionCameraE> pageBound(Long regionId, String cameraName, int pageIndex, int pageSize); List<RegionCameraE> pageBound(Long regionId, String cameraName, int pageIndex, int pageSize);
long countBound(Long regionId, String cameraName); long countBound(Long regionId, String cameraName);
List<RegionCameraE> listBoundByRegionId(Long regionId); List<RegionCameraE> listBoundByRegionId(Long regionId);
List<RegionCameraE> listBoundByRegionIds(Collection<Long> regionIds);
RegionCameraE getBound(Long regionId, String cameraSource, String cameraId);
List<RegionCameraE> listByKeys(Long corpId, Collection<CameraKeyE> cameraKeys); List<RegionCameraE> listByKeys(Long corpId, Collection<CameraKeyE> cameraKeys);
} }

View File

@ -10,7 +10,10 @@ public interface RegionGateway {
Boolean update(RegionE region); Boolean update(RegionE region);
Boolean deleteById(Long id); Boolean deleteById(Long id);
RegionE getById(Long id); RegionE getById(Long id);
RegionE getByCode(String regionCode);
List<RegionE> listByCorpIds(Collection<Long> corpIds); List<RegionE> listByCorpIds(Collection<Long> corpIds);
List<RegionE> listByTenantId(Long tenantId);
List<RegionE> listByIds(Collection<Long> ids); List<RegionE> listByIds(Collection<Long> ids);
boolean existsSiblingName(Long corpId, Long parentId, String regionName, Long excludeId); boolean existsSiblingName(Long corpId, Long parentId, String regionName, Long excludeId);
boolean existsChild(Long parentId); boolean existsChild(Long parentId);

View File

@ -0,0 +1,18 @@
package com.zcloud.gbs.edgeguard.domain.gateway;
import com.zcloud.gbs.edgeguard.domain.model.UserRegionPermissionE;
import java.util.List;
import java.util.Date;
import java.util.Collection;
public interface UserRegionPermissionGateway {
Long add(UserRegionPermissionE permission);
boolean update(UserRegionPermissionE permission);
UserRegionPermissionE getById(Long id);
List<UserRegionPermissionE> listOpenByUserAndRegion(Long userId, Long regionId, Long excludeId);
List<UserRegionPermissionE> listEffectiveByRegionIds(Collection<Long> regionIds, Date now);
void refreshStatuses(Date now);
List<UserRegionPermissionE> page(Long userId, Long regionId, Long corpId, String status, int pageIndex, int pageSize);
long count(Long userId, Long regionId, Long corpId, String status);
}

View File

@ -15,6 +15,7 @@ public class AlarmE extends BaseE {
private Long id; private Long id;
private String alarmNo; private String alarmNo;
private String sourceSystem; private String sourceSystem;
private String recognitionResult;
private Date alarmTime; private Date alarmTime;
private Date receivedTime; private Date receivedTime;
private String alarmAddress; private String alarmAddress;

View File

@ -0,0 +1,28 @@
package com.zcloud.gbs.edgeguard.domain.model;
import com.alibaba.cola.domain.Entity;
import com.jjb.saas.framework.domain.model.BaseE;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
@Data
@Entity
@EqualsAndHashCode(callSuper = true)
public class UserRegionPermissionE extends BaseE {
private Long id;
private Long userId;
private Long regionId;
private Long corpId;
private String permissionType;
private Date effectiveStartTime;
private Date effectiveEndTime;
private String authorizationSource;
private String sourceRefNo;
private String permissionStatus;
private Date revokeTime;
private String revokeReason;
private Integer version;
private String remarks;
}

View File

@ -20,8 +20,13 @@ public class AlarmGatewayImpl implements AlarmGateway {
private final AlarmRepository repository; private final AlarmRepository repository;
@Override @Override
public Boolean add(AlarmE alarm) { public Long add(AlarmE alarm) {
return repository.save(toDO(alarm)); AlarmDO data = toDO(alarm);
if (!repository.save(data)) {
return null;
}
alarm.setId(data.getId());
return data.getId();
} }
@Override @Override
@ -34,6 +39,14 @@ public class AlarmGatewayImpl implements AlarmGateway {
return toEntity(repository.getById(id)); return toEntity(repository.getById(id));
} }
@Override
public AlarmE getBySourceAndAlarmNo(String sourceSystem, String alarmNo) {
return toEntity(repository.lambdaQuery()
.eq(AlarmDO::getSourceSystem, sourceSystem)
.eq(AlarmDO::getAlarmNo, alarmNo)
.one());
}
@Override @Override
public List<AlarmE> page(String alarmNo, Long corpId, Long regionId, String disposalStatus, public List<AlarmE> page(String alarmNo, Long corpId, Long regionId, String disposalStatus,
Date start, Date end, int pageIndex, int pageSize) { Date start, Date end, int pageIndex, int pageSize) {

View File

@ -1,6 +1,7 @@
package com.zcloud.gbs.edgeguard.gatewayimpl; package com.zcloud.gbs.edgeguard.gatewayimpl;
import com.alibaba.cola.dto.MultiResponse; import com.alibaba.cola.dto.MultiResponse;
import com.alibaba.cola.dto.SingleResponse;
import com.alibaba.cola.exception.BizException; import com.alibaba.cola.exception.BizException;
import com.zcloud.gbs.edgeguard.domain.gateway.CorpGateway; import com.zcloud.gbs.edgeguard.domain.gateway.CorpGateway;
import com.zcloud.gbs.edgeguard.domain.model.CorpE; import com.zcloud.gbs.edgeguard.domain.model.CorpE;
@ -35,4 +36,14 @@ public class CorpGatewayImpl implements CorpGateway {
} }
return result; return result;
} }
@Override
public CorpE getById(Long id) {
SingleResponse<ZcloudCorpInfoCO> response = corpFacade.getCorpInfoById(id);
if (response == null || !response.isSuccess() || response.getData() == null) {
throw new BizException("查询企业信息失败");
}
ZcloudCorpInfoCO item = response.getData();
return new CorpE(item.getId(), item.getCorpName());
}
} }

View File

@ -70,6 +70,28 @@ public class RegionCameraGatewayImpl implements RegionCameraGateway {
.list()); .list());
} }
@Override
public List<RegionCameraE> listBoundByRegionIds(Collection<Long> regionIds) {
if (regionIds == null || regionIds.isEmpty()) {
return Collections.emptyList();
}
return toEntities(repository.lambdaQuery()
.in(RegionCameraDO::getRegionId, regionIds)
.eq(RegionCameraDO::getBindStatus, "BOUND")
.orderByAsc(RegionCameraDO::getRegionId, RegionCameraDO::getCameraName, RegionCameraDO::getId)
.list());
}
@Override
public RegionCameraE getBound(Long regionId, String cameraSource, String cameraId) {
return toEntity(repository.lambdaQuery()
.eq(RegionCameraDO::getRegionId, regionId)
.eq(RegionCameraDO::getCameraSource, cameraSource)
.eq(RegionCameraDO::getCameraId, cameraId)
.eq(RegionCameraDO::getBindStatus, "BOUND")
.one());
}
@Override @Override
public List<RegionCameraE> listByKeys(Long corpId, Collection<CameraKeyE> cameraKeys) { public List<RegionCameraE> listByKeys(Long corpId, Collection<CameraKeyE> cameraKeys) {
if (cameraKeys == null || cameraKeys.isEmpty()) { if (cameraKeys == null || cameraKeys.isEmpty()) {

View File

@ -42,6 +42,13 @@ public class RegionGatewayImpl implements RegionGateway {
return toEntity(repository.getById(id)); return toEntity(repository.getById(id));
} }
@Override
public RegionE getByCode(String regionCode) {
return toEntity(repository.lambdaQuery()
.eq(RegionDO::getRegionCode, regionCode)
.one());
}
@Override @Override
public List<RegionE> listByCorpIds(Collection<Long> corpIds) { public List<RegionE> listByCorpIds(Collection<Long> corpIds) {
if (corpIds == null || corpIds.isEmpty()) { if (corpIds == null || corpIds.isEmpty()) {
@ -58,6 +65,22 @@ public class RegionGatewayImpl implements RegionGateway {
return result; return result;
} }
@Override
public List<RegionE> listByTenantId(Long tenantId) {
if (tenantId == null) {
return Collections.emptyList();
}
List<RegionDO> rows = repository.lambdaQuery()
.eq(RegionDO::getTenantId, tenantId)
.orderByAsc(RegionDO::getCorpId, RegionDO::getTreeLevel, RegionDO::getSortNo, RegionDO::getId)
.list();
List<RegionE> result = new ArrayList<>();
for (RegionDO row : rows) {
result.add(toEntity(row));
}
return result;
}
@Override @Override
public List<RegionE> listByIds(Collection<Long> ids) { public List<RegionE> listByIds(Collection<Long> ids) {
if (ids == null || ids.isEmpty()) { if (ids == null || ids.isEmpty()) {

View File

@ -0,0 +1,89 @@
package com.zcloud.gbs.edgeguard.gatewayimpl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zcloud.gbs.edgeguard.domain.gateway.UserRegionPermissionGateway;
import com.zcloud.gbs.edgeguard.domain.model.UserRegionPermissionE;
import com.zcloud.gbs.edgeguard.persistence.dataobject.UserRegionPermissionDO;
import com.zcloud.gbs.edgeguard.persistence.repository.UserRegionPermissionRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Date;
import java.util.Collection;
import java.util.Collections;
@Service
@RequiredArgsConstructor
public class UserRegionPermissionGatewayImpl implements UserRegionPermissionGateway {
private final UserRegionPermissionRepository repository;
@Override
public Long add(UserRegionPermissionE permission) {
UserRegionPermissionDO data = toDO(permission);
return repository.save(data) ? data.getId() : null;
}
@Override public boolean update(UserRegionPermissionE permission) { return repository.updateById(toDO(permission)); }
@Override public UserRegionPermissionE getById(Long id) { return toEntity(repository.getById(id)); }
@Override
public List<UserRegionPermissionE> listOpenByUserAndRegion(Long userId, Long regionId, Long excludeId) {
return toEntities(repository.lambdaQuery().eq(UserRegionPermissionDO::getUserId, userId)
.eq(UserRegionPermissionDO::getRegionId, regionId)
.in(UserRegionPermissionDO::getPermissionStatus, Arrays.asList("PENDING", "ACTIVE"))
.ne(excludeId != null, UserRegionPermissionDO::getId, excludeId).list());
}
@Override
public List<UserRegionPermissionE> listEffectiveByRegionIds(Collection<Long> regionIds, Date now) {
if (regionIds == null || regionIds.isEmpty()) return Collections.emptyList();
return toEntities(repository.lambdaQuery().in(UserRegionPermissionDO::getRegionId, regionIds)
.eq(UserRegionPermissionDO::getPermissionStatus, "ACTIVE")
.le(UserRegionPermissionDO::getEffectiveStartTime, now)
.and(group -> group.isNull(UserRegionPermissionDO::getEffectiveEndTime)
.or().gt(UserRegionPermissionDO::getEffectiveEndTime, now))
.orderByAsc(UserRegionPermissionDO::getRegionId, UserRegionPermissionDO::getUserId).list());
}
@Override
public void refreshStatuses(Date now) {
UserRegionPermissionDO expired = new UserRegionPermissionDO();
expired.setPermissionStatus("EXPIRED");
repository.update(expired, new LambdaUpdateWrapper<UserRegionPermissionDO>()
.in(UserRegionPermissionDO::getPermissionStatus, Arrays.asList("PENDING", "ACTIVE"))
.isNotNull(UserRegionPermissionDO::getEffectiveEndTime)
.le(UserRegionPermissionDO::getEffectiveEndTime, now));
UserRegionPermissionDO active = new UserRegionPermissionDO();
active.setPermissionStatus("ACTIVE");
repository.update(active, new LambdaUpdateWrapper<UserRegionPermissionDO>()
.eq(UserRegionPermissionDO::getPermissionStatus, "PENDING")
.le(UserRegionPermissionDO::getEffectiveStartTime, now)
.and(group -> group.isNull(UserRegionPermissionDO::getEffectiveEndTime)
.or().gt(UserRegionPermissionDO::getEffectiveEndTime, now)));
}
@Override
public List<UserRegionPermissionE> page(Long userId, Long regionId, Long corpId, String status, int pageIndex, int pageSize) {
Page<UserRegionPermissionDO> page = repository.page(new Page<>(pageIndex, pageSize), query(userId, regionId, corpId, status)
.orderByDesc(UserRegionPermissionDO::getCreateTime).orderByDesc(UserRegionPermissionDO::getId));
return toEntities(page.getRecords());
}
@Override public long count(Long userId, Long regionId, Long corpId, String status) { return repository.count(query(userId, regionId, corpId, status)); }
private LambdaQueryWrapper<UserRegionPermissionDO> query(Long userId, Long regionId, Long corpId, String status) {
return new LambdaQueryWrapper<UserRegionPermissionDO>().eq(userId != null, UserRegionPermissionDO::getUserId, userId)
.eq(regionId != null, UserRegionPermissionDO::getRegionId, regionId)
.eq(corpId != null, UserRegionPermissionDO::getCorpId, corpId)
.eq(status != null && !status.trim().isEmpty(), UserRegionPermissionDO::getPermissionStatus, status);
}
private List<UserRegionPermissionE> toEntities(List<UserRegionPermissionDO> rows) { List<UserRegionPermissionE> result = new ArrayList<>(); for (UserRegionPermissionDO row : rows) result.add(toEntity(row)); return result; }
private UserRegionPermissionDO toDO(UserRegionPermissionE source) { UserRegionPermissionDO target = new UserRegionPermissionDO(); BeanUtils.copyProperties(source, target); return target; }
private UserRegionPermissionE toEntity(UserRegionPermissionDO source) { if (source == null) return null; UserRegionPermissionE target = new UserRegionPermissionE(); BeanUtils.copyProperties(source, target); return target; }
}

View File

@ -13,6 +13,7 @@ import java.util.Date;
public class AlarmDO extends BaseDO { public class AlarmDO extends BaseDO {
private String alarmNo; private String alarmNo;
private String sourceSystem; private String sourceSystem;
private String recognitionResult;
private Date alarmTime; private Date alarmTime;
private Date receivedTime; private Date receivedTime;
private String alarmAddress; private String alarmAddress;

View File

@ -0,0 +1,25 @@
package com.zcloud.gbs.edgeguard.persistence.dataobject;
import com.baomidou.mybatisplus.annotation.TableName;
import com.jjb.saas.framework.repository.basedo.BaseDO;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("edgeguard_user_region_permission")
public class UserRegionPermissionDO extends BaseDO {
private Long userId;
private Long regionId;
private Long corpId;
private String permissionType;
private Date effectiveStartTime;
private Date effectiveEndTime;
private String authorizationSource;
private String sourceRefNo;
private String permissionStatus;
private Date revokeTime;
private String revokeReason;
}

View File

@ -0,0 +1,7 @@
package com.zcloud.gbs.edgeguard.persistence.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.zcloud.gbs.edgeguard.persistence.dataobject.UserRegionPermissionDO;
public interface UserRegionPermissionMapper extends BaseMapper<UserRegionPermissionDO> {
}

View File

@ -0,0 +1,7 @@
package com.zcloud.gbs.edgeguard.persistence.repository;
import com.jjb.saas.framework.repository.repo.BaseRepository;
import com.zcloud.gbs.edgeguard.persistence.dataobject.UserRegionPermissionDO;
public interface UserRegionPermissionRepository extends BaseRepository<UserRegionPermissionDO> {
}

View File

@ -0,0 +1,12 @@
package com.zcloud.gbs.edgeguard.persistence.repository.impl;
import com.jjb.saas.framework.repository.repo.impl.BaseRepositoryImpl;
import com.zcloud.gbs.edgeguard.persistence.dataobject.UserRegionPermissionDO;
import com.zcloud.gbs.edgeguard.persistence.mapper.UserRegionPermissionMapper;
import com.zcloud.gbs.edgeguard.persistence.repository.UserRegionPermissionRepository;
import org.springframework.stereotype.Service;
@Service
public class UserRegionPermissionRepositoryImpl extends BaseRepositoryImpl<UserRegionPermissionMapper, UserRegionPermissionDO>
implements UserRegionPermissionRepository {
}

View File

@ -73,6 +73,7 @@ CREATE TABLE `edgeguard_alarm` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '报警主键id', `id` bigint NOT NULL AUTO_INCREMENT COMMENT '报警主键id',
`alarm_no` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '第三方报警编号', `alarm_no` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '第三方报警编号',
`source_system` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '报警来源系统编码', `source_system` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '报警来源系统编码',
`recognition_result` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '识别结论,例如已知人员无权限、无法匹配的陌生人员',
`alarm_time` datetime NOT NULL COMMENT '第三方报警发生时间', `alarm_time` datetime NOT NULL COMMENT '第三方报警发生时间',
`received_time` datetime NOT NULL COMMENT '综合平台接收时间', `received_time` datetime NOT NULL COMMENT '综合平台接收时间',
`alarm_address` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '报警地址', `alarm_address` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '报警地址',
@ -144,3 +145,35 @@ CREATE TABLE `edgeguard_alarm_disposal` (
KEY `idx_disposal_handler` (`tenant_id`, `handler_user_id`, `disposed_time`), KEY `idx_disposal_handler` (`tenant_id`, `handler_user_id`, `disposed_time`),
KEY `idx_disposal_result` (`tenant_id`, `is_false_alarm`, `is_hidden_hazard`, `disposed_time`) KEY `idx_disposal_result` (`tenant_id`, `is_false_alarm`, `is_hidden_hazard`, `disposed_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='报警处置结论表'; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='报警处置结论表';
CREATE TABLE `edgeguard_user_region_permission` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '人员区域授权记录主键id',
`user_id` bigint NOT NULL COMMENT '被授权人员用户id',
`region_id` bigint NOT NULL COMMENT '区域id',
`corp_id` bigint NOT NULL COMMENT '所属企业id冗余用于权限校验和查询',
`permission_type` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '权限类型TEMPORARY/FIXED',
`effective_start_time` datetime NOT NULL COMMENT '权限生效开始时间',
`effective_end_time` datetime DEFAULT NULL COMMENT '权限生效结束时间;临时权限必填',
`authorization_source` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '授权来源MANUAL/IMPORT/API',
`source_ref_no` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '来源业务单号或外部流水号',
`permission_status` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PENDING' COMMENT '权限状态PENDING/ACTIVE/EXPIRED/REVOKED',
`revoke_time` datetime DEFAULT NULL COMMENT '撤销时间',
`revoke_reason` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '撤销原因',
`delete_enum` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '删除标识true false',
`remarks` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '备注',
`create_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '创建人姓名',
`update_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '更新人姓名',
`tenant_id` bigint DEFAULT NULL COMMENT '租户id',
`org_id` bigint DEFAULT NULL COMMENT '单位id',
`version` int DEFAULT NULL COMMENT '版本',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '修改时间',
`create_id` bigint DEFAULT NULL COMMENT '创建人id',
`update_id` bigint DEFAULT NULL COMMENT '更新人id',
`env` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '环境标识',
PRIMARY KEY (`id`),
KEY `idx_user_permission_query` (`tenant_id`, `user_id`, `permission_status`, `effective_start_time`, `effective_end_time`),
KEY `idx_region_permission_query` (`tenant_id`, `corp_id`, `region_id`, `permission_status`, `effective_start_time`, `effective_end_time`),
KEY `idx_permission_expire_scan` (`tenant_id`, `permission_status`, `effective_end_time`),
KEY `idx_source_ref_no` (`tenant_id`, `authorization_source`, `source_ref_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='人员区域访问授权记录表';