diff --git a/AGENTS.md b/AGENTS.md index 0bf3e5b..a14ffa1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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 职责和依赖方向正确。 - [ ] Cmd/Co/PageQry 分别继承 `Command`、`ClientObject`、`BasePageQuery`。 diff --git a/OPEN-API.md b/OPEN-API.md new file mode 100644 index 0000000..bca4bac --- /dev/null +++ b/OPEN-API.md @@ -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 中禁止携带永久明文密钥。 diff --git a/web-adapter/src/main/java/com/zcloud/gbs/edgeguard/web/OpenEdgeGuardController.java b/web-adapter/src/main/java/com/zcloud/gbs/edgeguard/web/OpenEdgeGuardController.java new file mode 100644 index 0000000..bb7a9fe --- /dev/null +++ b/web-adapter/src/main/java/com/zcloud/gbs/edgeguard/web/OpenEdgeGuardController.java @@ -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 regions( + @RequestParam(value = "corpId", required = false) Long corpId) { + return openEdgeGuardService.listRegions(corpId); + } + + @ApiOperation("接收第三方报警") + @PostMapping("/alarms") + public SingleResponse receiveAlarm( + @Validated @RequestBody OpenAlarmReceiveCmd cmd) { + return openEdgeGuardService.receiveAlarm(cmd); + } +} diff --git a/web-adapter/src/main/java/com/zcloud/gbs/edgeguard/web/UserRegionPermissionController.java b/web-adapter/src/main/java/com/zcloud/gbs/edgeguard/web/UserRegionPermissionController.java new file mode 100644 index 0000000..d498087 --- /dev/null +++ b/web-adapter/src/main/java/com/zcloud/gbs/edgeguard/web/UserRegionPermissionController.java @@ -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 page(@Validated UserRegionPermissionPageQry qry) { return permissionService.page(qry); } + + @ApiOperation("查询人员区域授权详情") + @GetMapping("/{id}") + public SingleResponse detail(@PathVariable Long id) { return permissionService.getById(id); } + + @ApiOperation("新增人员区域授权") + @PostMapping + public SingleResponse 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); } +} diff --git a/web-app/src/main/java/com/zcloud/gbs/edgeguard/command/OpenAlarmReceiveExe.java b/web-app/src/main/java/com/zcloud/gbs/edgeguard/command/OpenAlarmReceiveExe.java new file mode 100644 index 0000000..8ff1487 --- /dev/null +++ b/web-app/src/main/java/com/zcloud/gbs/edgeguard/command/OpenAlarmReceiveExe.java @@ -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 ancestorIds = new ArrayList<>(); + if (region.getTreePath() != null) { + for (String value : region.getTreePath().split("/")) { + if (!value.trim().isEmpty()) { + ancestorIds.add(Long.valueOf(value)); + } + } + } + Map names = new HashMap<>(); + for (RegionE ancestor : regionGateway.listByIds(ancestorIds)) { + names.put(ancestor.getId(), ancestor.getRegionName()); + } + List 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(); + } +} diff --git a/web-app/src/main/java/com/zcloud/gbs/edgeguard/command/OpenAlarmSaveTransaction.java b/web-app/src/main/java/com/zcloud/gbs/edgeguard/command/OpenAlarmSaveTransaction.java new file mode 100644 index 0000000..6263ca5 --- /dev/null +++ b/web-app/src/main/java/com/zcloud/gbs/edgeguard/command/OpenAlarmSaveTransaction.java @@ -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; + } +} diff --git a/web-app/src/main/java/com/zcloud/gbs/edgeguard/command/UserRegionPermissionAddExe.java b/web-app/src/main/java/com/zcloud/gbs/edgeguard/command/UserRegionPermissionAddExe.java new file mode 100644 index 0000000..96cbe9a --- /dev/null +++ b/web-app/src/main/java/com/zcloud/gbs/edgeguard/command/UserRegionPermissionAddExe.java @@ -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; + } +} diff --git a/web-app/src/main/java/com/zcloud/gbs/edgeguard/command/UserRegionPermissionCommandSupport.java b/web-app/src/main/java/com/zcloud/gbs/edgeguard/command/UserRegionPermissionCommandSupport.java new file mode 100644 index 0000000..db9ed8f --- /dev/null +++ b/web-app/src/main/java/com/zcloud/gbs/edgeguard/command/UserRegionPermissionCommandSupport.java @@ -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 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)); + } +} diff --git a/web-app/src/main/java/com/zcloud/gbs/edgeguard/command/UserRegionPermissionRemoveExe.java b/web-app/src/main/java/com/zcloud/gbs/edgeguard/command/UserRegionPermissionRemoveExe.java new file mode 100644 index 0000000..7797d6a --- /dev/null +++ b/web-app/src/main/java/com/zcloud/gbs/edgeguard/command/UserRegionPermissionRemoveExe.java @@ -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("撤销人员区域授权失败"); + } +} diff --git a/web-app/src/main/java/com/zcloud/gbs/edgeguard/command/UserRegionPermissionUpdateExe.java b/web-app/src/main/java/com/zcloud/gbs/edgeguard/command/UserRegionPermissionUpdateExe.java new file mode 100644 index 0000000..b810a08 --- /dev/null +++ b/web-app/src/main/java/com/zcloud/gbs/edgeguard/command/UserRegionPermissionUpdateExe.java @@ -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("修改人员区域授权失败,记录可能已被更新"); + } +} diff --git a/web-app/src/main/java/com/zcloud/gbs/edgeguard/command/query/AlarmQueryExe.java b/web-app/src/main/java/com/zcloud/gbs/edgeguard/command/query/AlarmQueryExe.java index b270c25..5162b9a 100644 --- a/web-app/src/main/java/com/zcloud/gbs/edgeguard/command/query/AlarmQueryExe.java +++ b/web-app/src/main/java/com/zcloud/gbs/edgeguard/command/query/AlarmQueryExe.java @@ -1,20 +1,77 @@ package com.zcloud.gbs.edgeguard.command.query; + 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.domain.gateway.*; -import com.zcloud.gbs.edgeguard.domain.model.*; +import com.zcloud.gbs.edgeguard.command.RegionAccessService; +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.clientobject.*; +import com.zcloud.gbs.edgeguard.dto.clientobject.AlarmCo; +import com.zcloud.gbs.edgeguard.dto.clientobject.AlarmDisposalCo; import lombok.RequiredArgsConstructor; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Component; -import java.util.*; -@Component @RequiredArgsConstructor + +import java.util.ArrayList; +import java.util.List; + +/** 报警分页和详情查询执行器。 */ +@Component +@RequiredArgsConstructor public class AlarmQueryExe { - private final AlarmGateway alarms; private final AlarmDisposalGateway disposals; private final RegionAccessService access; - private final AlarmHiddenStatusSyncExe hiddenStatusSyncExe; - public PageResponse 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 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;} + private final AlarmGateway alarms; + private final AlarmDisposalGateway disposals; + private final RegionAccessService access; + private final AlarmHiddenStatusSyncExe hiddenStatusSyncExe; + + /** + * 按查询条件分页返回当前用户数据范围内的报警。 + */ + public PageResponse 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 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; + } } diff --git a/web-app/src/main/java/com/zcloud/gbs/edgeguard/command/query/OpenRegionQueryExe.java b/web-app/src/main/java/com/zcloud/gbs/edgeguard/command/query/OpenRegionQueryExe.java new file mode 100644 index 0000000..c82543f --- /dev/null +++ b/web-app/src/main/java/com/zcloud/gbs/edgeguard/command/query/OpenRegionQueryExe.java @@ -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 execute(Long corpId) { + Long tenantId = AuthContext.getTenantId(); + if (tenantId == null) { + throw new BizException("无法获取当前租户信息"); + } + List source = corpId == null + ? regionGateway.listByTenantId(tenantId) + : regionGateway.listByCorpIds(Collections.singletonList(corpId)); + List regions = source.stream() + .filter(region -> "ENABLED".equals(region.getRegionStatus())) + .collect(Collectors.toList()); + List regionIds = regions.stream().map(RegionE::getId).collect(Collectors.toList()); + Date now = new Date(); + permissionGateway.refreshStatuses(now); + Map> 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 permissions = permissionGateway.listEffectiveByRegionIds(regionIds, now); + Set userIds = new HashSet<>(); + for (UserRegionPermissionE permission : permissions) userIds.add(permission.getUserId()); + Map userNames = new HashMap<>(); + for (UserE user : userGateway.listByIds(userIds)) { + if (user.isActive()) userNames.put(user.getId(), user.getName()); + } + Map> 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 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); + } +} diff --git a/web-app/src/main/java/com/zcloud/gbs/edgeguard/command/query/UserRegionPermissionQueryExe.java b/web-app/src/main/java/com/zcloud/gbs/edgeguard/command/query/UserRegionPermissionQueryExe.java new file mode 100644 index 0000000..d1b1573 --- /dev/null +++ b/web-app/src/main/java/com/zcloud/gbs/edgeguard/command/query/UserRegionPermissionQueryExe.java @@ -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 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 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 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 toCos(List rows) { + Set userIds = new HashSet<>(); Set regionIds = new HashSet<>(); + for (UserRegionPermissionE row : rows) { userIds.add(row.getUserId()); regionIds.add(row.getRegionId()); } + Map userNames = new HashMap<>(); + for (UserE user : userGateway.listByIds(userIds)) userNames.put(user.getId(), user.getName()); + Map regionNames = new HashMap<>(); + for (RegionE region : regionGateway.listByIds(regionIds)) regionNames.put(region.getId(), region.getRegionName()); + List 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; + } +} diff --git a/web-app/src/main/java/com/zcloud/gbs/edgeguard/service/OpenEdgeGuardServiceImpl.java b/web-app/src/main/java/com/zcloud/gbs/edgeguard/service/OpenEdgeGuardServiceImpl.java new file mode 100644 index 0000000..40c5e50 --- /dev/null +++ b/web-app/src/main/java/com/zcloud/gbs/edgeguard/service/OpenEdgeGuardServiceImpl.java @@ -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 listRegions(Long corpId) { + return regionQueryExe.execute(corpId); + } + + @Override + public SingleResponse receiveAlarm(OpenAlarmReceiveCmd cmd) { + return SingleResponse.of(alarmReceiveExe.execute(cmd)); + } +} diff --git a/web-app/src/main/java/com/zcloud/gbs/edgeguard/service/UserRegionPermissionServiceImpl.java b/web-app/src/main/java/com/zcloud/gbs/edgeguard/service/UserRegionPermissionServiceImpl.java new file mode 100644 index 0000000..1a2545a --- /dev/null +++ b/web-app/src/main/java/com/zcloud/gbs/edgeguard/service/UserRegionPermissionServiceImpl.java @@ -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 page(UserRegionPermissionPageQry qry) { return queryExe.page(qry); } + @Override public SingleResponse getById(Long id) { return queryExe.detail(id); } + @Override public SingleResponse 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(); } +} diff --git a/web-app/src/test/java/com/zcloud/gbs/edgeguard/command/RegionCameraBindTransactionTest.java b/web-app/src/test/java/com/zcloud/gbs/edgeguard/command/RegionCameraBindTransactionTest.java index 3357a8f..99da3b0 100644 --- a/web-app/src/test/java/com/zcloud/gbs/edgeguard/command/RegionCameraBindTransactionTest.java +++ b/web-app/src/test/java/com/zcloud/gbs/edgeguard/command/RegionCameraBindTransactionTest.java @@ -67,29 +67,80 @@ public class RegionCameraBindTransactionTest { private final List rows = new ArrayList<>(); private long sequence = 10; - @Override public Boolean add(RegionCameraE value) { value.setId(sequence++); rows.add(value); return true; } - @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 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 listBoundByRegionId(Long regionId) { + @Override + public Boolean add(RegionCameraE value) { + value.setId(sequence++); + rows.add(value); + return true; + } + + @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 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 listBoundByRegionId(Long regionId) { List 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; } - @Override public List listByKeys(Long corpId, Collection keys) { + + @Override + public List listBoundByRegionIds(Collection regionIds) { + return Collections.emptyList(); + } + + @Override + public RegionCameraE getBound(Long regionId, String cameraSource, String cameraId) { + return null; + } + + @Override + public List listByKeys(Long corpId, Collection keys) { List result = new ArrayList<>(); for (CameraKeyE key : keys) { RegionCameraE value = find(key.identity(), false); - if (value != null) result.add(value); + if (value != null) { + result.add(value); + } } return result; } private RegionCameraE find(String identity, boolean ignored) { for (RegionCameraE row : rows) { - if ((row.getCameraSource() + ":" + row.getCameraId()).equals(identity)) return row; + if ((row.getCameraSource() + ":" + row.getCameraId()).equals(identity)) { + return row; + } } return null; } diff --git a/web-client/src/main/java/com/zcloud/gbs/edgeguard/api/AlarmServiceI.java b/web-client/src/main/java/com/zcloud/gbs/edgeguard/api/AlarmServiceI.java index 4113b75..6d37ff2 100644 --- a/web-client/src/main/java/com/zcloud/gbs/edgeguard/api/AlarmServiceI.java +++ b/web-client/src/main/java/com/zcloud/gbs/edgeguard/api/AlarmServiceI.java @@ -1,12 +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.AlarmDisposeCmd; import com.zcloud.gbs.edgeguard.dto.AlarmPageQry; import com.zcloud.gbs.edgeguard.dto.clientobject.AlarmCo; + +/** 报警查询与处置应用服务。 */ public interface AlarmServiceI { PageResponse page(AlarmPageQry qry); + SingleResponse detail(Long id); + Response dispose(Long id, AlarmDisposeCmd cmd); } diff --git a/web-client/src/main/java/com/zcloud/gbs/edgeguard/api/OpenEdgeGuardServiceI.java b/web-client/src/main/java/com/zcloud/gbs/edgeguard/api/OpenEdgeGuardServiceI.java new file mode 100644 index 0000000..99a2839 --- /dev/null +++ b/web-client/src/main/java/com/zcloud/gbs/edgeguard/api/OpenEdgeGuardServiceI.java @@ -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 listRegions(Long corpId); + SingleResponse receiveAlarm(OpenAlarmReceiveCmd cmd); +} diff --git a/web-client/src/main/java/com/zcloud/gbs/edgeguard/api/UserRegionPermissionServiceI.java b/web-client/src/main/java/com/zcloud/gbs/edgeguard/api/UserRegionPermissionServiceI.java new file mode 100644 index 0000000..3feda3b --- /dev/null +++ b/web-client/src/main/java/com/zcloud/gbs/edgeguard/api/UserRegionPermissionServiceI.java @@ -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 page(UserRegionPermissionPageQry qry); + SingleResponse getById(Long id); + SingleResponse add(UserRegionPermissionAddCmd cmd); + Response update(Long id, UserRegionPermissionUpdateCmd cmd); + Response remove(Long id, String revokeReason); +} diff --git a/web-client/src/main/java/com/zcloud/gbs/edgeguard/dto/AlarmPageQry.java b/web-client/src/main/java/com/zcloud/gbs/edgeguard/dto/AlarmPageQry.java index 7cfe32f..7cb3034 100644 --- a/web-client/src/main/java/com/zcloud/gbs/edgeguard/dto/AlarmPageQry.java +++ b/web-client/src/main/java/com/zcloud/gbs/edgeguard/dto/AlarmPageQry.java @@ -1,9 +1,14 @@ package com.zcloud.gbs.edgeguard.dto; + import com.alibaba.cola.dto.PageQuery; import lombok.Data; import lombok.EqualsAndHashCode; + import java.util.Date; -@Data @EqualsAndHashCode(callSuper = true) + +/** 报警分页查询条件。 */ +@Data +@EqualsAndHashCode(callSuper = true) public class AlarmPageQry extends PageQuery { private String alarmNo; private Long corpId; diff --git a/web-client/src/main/java/com/zcloud/gbs/edgeguard/dto/OpenAlarmReceiveCmd.java b/web-client/src/main/java/com/zcloud/gbs/edgeguard/dto/OpenAlarmReceiveCmd.java new file mode 100644 index 0000000..0c51fdd --- /dev/null +++ b/web-client/src/main/java/com/zcloud/gbs/edgeguard/dto/OpenAlarmReceiveCmd.java @@ -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; +} diff --git a/web-client/src/main/java/com/zcloud/gbs/edgeguard/dto/UserRegionPermissionAddCmd.java b/web-client/src/main/java/com/zcloud/gbs/edgeguard/dto/UserRegionPermissionAddCmd.java new file mode 100644 index 0000000..c3d46c2 --- /dev/null +++ b/web-client/src/main/java/com/zcloud/gbs/edgeguard/dto/UserRegionPermissionAddCmd.java @@ -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; +} diff --git a/web-client/src/main/java/com/zcloud/gbs/edgeguard/dto/UserRegionPermissionPageQry.java b/web-client/src/main/java/com/zcloud/gbs/edgeguard/dto/UserRegionPermissionPageQry.java new file mode 100644 index 0000000..d5da631 --- /dev/null +++ b/web-client/src/main/java/com/zcloud/gbs/edgeguard/dto/UserRegionPermissionPageQry.java @@ -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; +} diff --git a/web-client/src/main/java/com/zcloud/gbs/edgeguard/dto/UserRegionPermissionUpdateCmd.java b/web-client/src/main/java/com/zcloud/gbs/edgeguard/dto/UserRegionPermissionUpdateCmd.java new file mode 100644 index 0000000..52f6b89 --- /dev/null +++ b/web-client/src/main/java/com/zcloud/gbs/edgeguard/dto/UserRegionPermissionUpdateCmd.java @@ -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; +} diff --git a/web-client/src/main/java/com/zcloud/gbs/edgeguard/dto/clientobject/AlarmCo.java b/web-client/src/main/java/com/zcloud/gbs/edgeguard/dto/clientobject/AlarmCo.java index 8563671..9c18bbd 100644 --- a/web-client/src/main/java/com/zcloud/gbs/edgeguard/dto/clientobject/AlarmCo.java +++ b/web-client/src/main/java/com/zcloud/gbs/edgeguard/dto/clientobject/AlarmCo.java @@ -1,12 +1,30 @@ package com.zcloud.gbs.edgeguard.dto.clientobject; + import com.alibaba.cola.dto.ClientObject; import lombok.Data; + import java.util.Date; + +/** 报警列表和详情返回对象。 */ @Data public class AlarmCo extends ClientObject { - private Long id; private String alarmNo; private String sourceSystem; 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; + private Long id; + private String alarmNo; + private String sourceSystem; + private String recognitionResult; + 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; } diff --git a/web-client/src/main/java/com/zcloud/gbs/edgeguard/dto/clientobject/AlarmDisposalCo.java b/web-client/src/main/java/com/zcloud/gbs/edgeguard/dto/clientobject/AlarmDisposalCo.java index 07ba491..da7a87d 100644 --- a/web-client/src/main/java/com/zcloud/gbs/edgeguard/dto/clientobject/AlarmDisposalCo.java +++ b/web-client/src/main/java/com/zcloud/gbs/edgeguard/dto/clientobject/AlarmDisposalCo.java @@ -1,12 +1,27 @@ package com.zcloud.gbs.edgeguard.dto.clientobject; + import com.alibaba.cola.dto.ClientObject; import lombok.Data; + import java.util.Date; + +/** 报警处置结果返回对象。 */ @Data public class AlarmDisposalCo extends ClientObject { - private Long id; private Long alarmId; private Integer isFalseAlarm; private Integer isHiddenHazard; - 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; + private Long id; + private Long alarmId; + private Integer isFalseAlarm; + private Integer isHiddenHazard; + 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; } diff --git a/web-client/src/main/java/com/zcloud/gbs/edgeguard/dto/clientobject/OpenAlarmReceiptCo.java b/web-client/src/main/java/com/zcloud/gbs/edgeguard/dto/clientobject/OpenAlarmReceiptCo.java new file mode 100644 index 0000000..fd9341b --- /dev/null +++ b/web-client/src/main/java/com/zcloud/gbs/edgeguard/dto/clientobject/OpenAlarmReceiptCo.java @@ -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; +} diff --git a/web-client/src/main/java/com/zcloud/gbs/edgeguard/dto/clientobject/OpenCameraCo.java b/web-client/src/main/java/com/zcloud/gbs/edgeguard/dto/clientobject/OpenCameraCo.java new file mode 100644 index 0000000..fb8cf7c --- /dev/null +++ b/web-client/src/main/java/com/zcloud/gbs/edgeguard/dto/clientobject/OpenCameraCo.java @@ -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; +} diff --git a/web-client/src/main/java/com/zcloud/gbs/edgeguard/dto/clientobject/OpenRegionCo.java b/web-client/src/main/java/com/zcloud/gbs/edgeguard/dto/clientobject/OpenRegionCo.java new file mode 100644 index 0000000..5ab7ecf --- /dev/null +++ b/web-client/src/main/java/com/zcloud/gbs/edgeguard/dto/clientobject/OpenRegionCo.java @@ -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 fencePoints = new ArrayList<>(); + private String regionStatus; + private List cameras = new ArrayList<>(); + private List users = new ArrayList<>(); +} diff --git a/web-client/src/main/java/com/zcloud/gbs/edgeguard/dto/clientobject/OpenRegionUserCo.java b/web-client/src/main/java/com/zcloud/gbs/edgeguard/dto/clientobject/OpenRegionUserCo.java new file mode 100644 index 0000000..aff6eb2 --- /dev/null +++ b/web-client/src/main/java/com/zcloud/gbs/edgeguard/dto/clientobject/OpenRegionUserCo.java @@ -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; +} diff --git a/web-client/src/main/java/com/zcloud/gbs/edgeguard/dto/clientobject/UserRegionPermissionCo.java b/web-client/src/main/java/com/zcloud/gbs/edgeguard/dto/clientobject/UserRegionPermissionCo.java new file mode 100644 index 0000000..f7fb471 --- /dev/null +++ b/web-client/src/main/java/com/zcloud/gbs/edgeguard/dto/clientobject/UserRegionPermissionCo.java @@ -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; +} diff --git a/web-domain/src/main/java/com/zcloud/gbs/edgeguard/domain/gateway/AlarmGateway.java b/web-domain/src/main/java/com/zcloud/gbs/edgeguard/domain/gateway/AlarmGateway.java index 6809bba..04118f7 100644 --- a/web-domain/src/main/java/com/zcloud/gbs/edgeguard/domain/gateway/AlarmGateway.java +++ b/web-domain/src/main/java/com/zcloud/gbs/edgeguard/domain/gateway/AlarmGateway.java @@ -4,9 +4,10 @@ import com.zcloud.gbs.edgeguard.domain.model.AlarmE; import java.util.List; public interface AlarmGateway { - Boolean add(AlarmE alarm); + Long add(AlarmE alarm); Boolean update(AlarmE alarm); AlarmE getById(Long id); + AlarmE getBySourceAndAlarmNo(String sourceSystem, String alarmNo); List page(String alarmNo, Long corpId, Long regionId, String disposalStatus, java.util.Date alarmTimeStart, java.util.Date alarmTimeEnd, int pageIndex, int pageSize); diff --git a/web-domain/src/main/java/com/zcloud/gbs/edgeguard/domain/gateway/CorpGateway.java b/web-domain/src/main/java/com/zcloud/gbs/edgeguard/domain/gateway/CorpGateway.java index 03caeea..1d3616d 100644 --- a/web-domain/src/main/java/com/zcloud/gbs/edgeguard/domain/gateway/CorpGateway.java +++ b/web-domain/src/main/java/com/zcloud/gbs/edgeguard/domain/gateway/CorpGateway.java @@ -7,4 +7,5 @@ import java.util.List; /** 当前调用人的可访问企业,由公共企业服务提供。 */ public interface CorpGateway { List listAccessibleCorps(); + CorpE getById(Long id); } diff --git a/web-domain/src/main/java/com/zcloud/gbs/edgeguard/domain/gateway/RegionCameraGateway.java b/web-domain/src/main/java/com/zcloud/gbs/edgeguard/domain/gateway/RegionCameraGateway.java index 785cb25..99ad9ab 100644 --- a/web-domain/src/main/java/com/zcloud/gbs/edgeguard/domain/gateway/RegionCameraGateway.java +++ b/web-domain/src/main/java/com/zcloud/gbs/edgeguard/domain/gateway/RegionCameraGateway.java @@ -15,5 +15,7 @@ public interface RegionCameraGateway { List pageBound(Long regionId, String cameraName, int pageIndex, int pageSize); long countBound(Long regionId, String cameraName); List listBoundByRegionId(Long regionId); + List listBoundByRegionIds(Collection regionIds); + RegionCameraE getBound(Long regionId, String cameraSource, String cameraId); List listByKeys(Long corpId, Collection cameraKeys); } diff --git a/web-domain/src/main/java/com/zcloud/gbs/edgeguard/domain/gateway/RegionGateway.java b/web-domain/src/main/java/com/zcloud/gbs/edgeguard/domain/gateway/RegionGateway.java index 723cf62..d9d49f6 100644 --- a/web-domain/src/main/java/com/zcloud/gbs/edgeguard/domain/gateway/RegionGateway.java +++ b/web-domain/src/main/java/com/zcloud/gbs/edgeguard/domain/gateway/RegionGateway.java @@ -10,7 +10,10 @@ public interface RegionGateway { Boolean update(RegionE region); Boolean deleteById(Long id); RegionE getById(Long id); + RegionE getByCode(String regionCode); List listByCorpIds(Collection corpIds); + + List listByTenantId(Long tenantId); List listByIds(Collection ids); boolean existsSiblingName(Long corpId, Long parentId, String regionName, Long excludeId); boolean existsChild(Long parentId); diff --git a/web-domain/src/main/java/com/zcloud/gbs/edgeguard/domain/gateway/UserRegionPermissionGateway.java b/web-domain/src/main/java/com/zcloud/gbs/edgeguard/domain/gateway/UserRegionPermissionGateway.java new file mode 100644 index 0000000..5552dbe --- /dev/null +++ b/web-domain/src/main/java/com/zcloud/gbs/edgeguard/domain/gateway/UserRegionPermissionGateway.java @@ -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 listOpenByUserAndRegion(Long userId, Long regionId, Long excludeId); + List listEffectiveByRegionIds(Collection regionIds, Date now); + void refreshStatuses(Date now); + List page(Long userId, Long regionId, Long corpId, String status, int pageIndex, int pageSize); + long count(Long userId, Long regionId, Long corpId, String status); +} diff --git a/web-domain/src/main/java/com/zcloud/gbs/edgeguard/domain/model/AlarmE.java b/web-domain/src/main/java/com/zcloud/gbs/edgeguard/domain/model/AlarmE.java index 46c3881..0f4f92a 100644 --- a/web-domain/src/main/java/com/zcloud/gbs/edgeguard/domain/model/AlarmE.java +++ b/web-domain/src/main/java/com/zcloud/gbs/edgeguard/domain/model/AlarmE.java @@ -15,6 +15,7 @@ public class AlarmE extends BaseE { private Long id; private String alarmNo; private String sourceSystem; + private String recognitionResult; private Date alarmTime; private Date receivedTime; private String alarmAddress; diff --git a/web-domain/src/main/java/com/zcloud/gbs/edgeguard/domain/model/UserRegionPermissionE.java b/web-domain/src/main/java/com/zcloud/gbs/edgeguard/domain/model/UserRegionPermissionE.java new file mode 100644 index 0000000..bad8049 --- /dev/null +++ b/web-domain/src/main/java/com/zcloud/gbs/edgeguard/domain/model/UserRegionPermissionE.java @@ -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; +} diff --git a/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/gatewayimpl/AlarmGatewayImpl.java b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/gatewayimpl/AlarmGatewayImpl.java index 0b5c5ae..15e0c78 100644 --- a/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/gatewayimpl/AlarmGatewayImpl.java +++ b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/gatewayimpl/AlarmGatewayImpl.java @@ -20,8 +20,13 @@ public class AlarmGatewayImpl implements AlarmGateway { private final AlarmRepository repository; @Override - public Boolean add(AlarmE alarm) { - return repository.save(toDO(alarm)); + public Long add(AlarmE alarm) { + AlarmDO data = toDO(alarm); + if (!repository.save(data)) { + return null; + } + alarm.setId(data.getId()); + return data.getId(); } @Override @@ -34,6 +39,14 @@ public class AlarmGatewayImpl implements AlarmGateway { 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 public List page(String alarmNo, Long corpId, Long regionId, String disposalStatus, Date start, Date end, int pageIndex, int pageSize) { diff --git a/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/gatewayimpl/CorpGatewayImpl.java b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/gatewayimpl/CorpGatewayImpl.java index 16bf772..b853443 100644 --- a/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/gatewayimpl/CorpGatewayImpl.java +++ b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/gatewayimpl/CorpGatewayImpl.java @@ -1,6 +1,7 @@ package com.zcloud.gbs.edgeguard.gatewayimpl; import com.alibaba.cola.dto.MultiResponse; +import com.alibaba.cola.dto.SingleResponse; import com.alibaba.cola.exception.BizException; import com.zcloud.gbs.edgeguard.domain.gateway.CorpGateway; import com.zcloud.gbs.edgeguard.domain.model.CorpE; @@ -35,4 +36,14 @@ public class CorpGatewayImpl implements CorpGateway { } return result; } + + @Override + public CorpE getById(Long id) { + SingleResponse 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()); + } } diff --git a/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/gatewayimpl/RegionCameraGatewayImpl.java b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/gatewayimpl/RegionCameraGatewayImpl.java index 771bbb6..a4c634d 100644 --- a/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/gatewayimpl/RegionCameraGatewayImpl.java +++ b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/gatewayimpl/RegionCameraGatewayImpl.java @@ -70,6 +70,28 @@ public class RegionCameraGatewayImpl implements RegionCameraGateway { .list()); } + @Override + public List listBoundByRegionIds(Collection 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 public List listByKeys(Long corpId, Collection cameraKeys) { if (cameraKeys == null || cameraKeys.isEmpty()) { diff --git a/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/gatewayimpl/RegionGatewayImpl.java b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/gatewayimpl/RegionGatewayImpl.java index 1bd7b3d..a6fb83b 100644 --- a/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/gatewayimpl/RegionGatewayImpl.java +++ b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/gatewayimpl/RegionGatewayImpl.java @@ -42,6 +42,13 @@ public class RegionGatewayImpl implements RegionGateway { return toEntity(repository.getById(id)); } + @Override + public RegionE getByCode(String regionCode) { + return toEntity(repository.lambdaQuery() + .eq(RegionDO::getRegionCode, regionCode) + .one()); + } + @Override public List listByCorpIds(Collection corpIds) { if (corpIds == null || corpIds.isEmpty()) { @@ -58,6 +65,22 @@ public class RegionGatewayImpl implements RegionGateway { return result; } + @Override + public List listByTenantId(Long tenantId) { + if (tenantId == null) { + return Collections.emptyList(); + } + List rows = repository.lambdaQuery() + .eq(RegionDO::getTenantId, tenantId) + .orderByAsc(RegionDO::getCorpId, RegionDO::getTreeLevel, RegionDO::getSortNo, RegionDO::getId) + .list(); + List result = new ArrayList<>(); + for (RegionDO row : rows) { + result.add(toEntity(row)); + } + return result; + } + @Override public List listByIds(Collection ids) { if (ids == null || ids.isEmpty()) { diff --git a/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/gatewayimpl/UserRegionPermissionGatewayImpl.java b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/gatewayimpl/UserRegionPermissionGatewayImpl.java new file mode 100644 index 0000000..6ec8a50 --- /dev/null +++ b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/gatewayimpl/UserRegionPermissionGatewayImpl.java @@ -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 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 listEffectiveByRegionIds(Collection 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() + .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() + .eq(UserRegionPermissionDO::getPermissionStatus, "PENDING") + .le(UserRegionPermissionDO::getEffectiveStartTime, now) + .and(group -> group.isNull(UserRegionPermissionDO::getEffectiveEndTime) + .or().gt(UserRegionPermissionDO::getEffectiveEndTime, now))); + } + + @Override + public List page(Long userId, Long regionId, Long corpId, String status, int pageIndex, int pageSize) { + Page 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 query(Long userId, Long regionId, Long corpId, String status) { + return new LambdaQueryWrapper().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 toEntities(List rows) { List 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; } +} diff --git a/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/dataobject/AlarmDO.java b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/dataobject/AlarmDO.java index e15b094..1592cd6 100644 --- a/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/dataobject/AlarmDO.java +++ b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/dataobject/AlarmDO.java @@ -13,6 +13,7 @@ import java.util.Date; public class AlarmDO extends BaseDO { private String alarmNo; private String sourceSystem; + private String recognitionResult; private Date alarmTime; private Date receivedTime; private String alarmAddress; diff --git a/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/dataobject/UserRegionPermissionDO.java b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/dataobject/UserRegionPermissionDO.java new file mode 100644 index 0000000..331af72 --- /dev/null +++ b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/dataobject/UserRegionPermissionDO.java @@ -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; +} diff --git a/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/mapper/UserRegionPermissionMapper.java b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/mapper/UserRegionPermissionMapper.java new file mode 100644 index 0000000..7c437b8 --- /dev/null +++ b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/mapper/UserRegionPermissionMapper.java @@ -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 { +} diff --git a/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/repository/UserRegionPermissionRepository.java b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/repository/UserRegionPermissionRepository.java new file mode 100644 index 0000000..dba09a4 --- /dev/null +++ b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/repository/UserRegionPermissionRepository.java @@ -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 { +} diff --git a/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/repository/impl/UserRegionPermissionRepositoryImpl.java b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/repository/impl/UserRegionPermissionRepositoryImpl.java new file mode 100644 index 0000000..1dc9bb7 --- /dev/null +++ b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/repository/impl/UserRegionPermissionRepositoryImpl.java @@ -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 + implements UserRegionPermissionRepository { +} diff --git a/web-infrastructure/src/main/resources/RegionManagementDDL.sql b/web-infrastructure/src/main/resources/RegionManagementDDL.sql index fc09564..df94386 100644 --- a/web-infrastructure/src/main/resources/RegionManagementDDL.sql +++ b/web-infrastructure/src/main/resources/RegionManagementDDL.sql @@ -72,8 +72,9 @@ CREATE TABLE `edgeguard_region_camera` ( CREATE TABLE `edgeguard_alarm` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '报警主键id', `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 '报警来源系统编码', - `alarm_time` datetime 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 '第三方报警发生时间', `received_time` datetime NOT NULL COMMENT '综合平台接收时间', `alarm_address` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '报警地址', `corp_id` bigint DEFAULT NULL COMMENT '报警所属企业id', @@ -144,3 +145,35 @@ CREATE TABLE `edgeguard_alarm_disposal` ( 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`) ) 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='人员区域访问授权记录表';