diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml new file mode 100644 index 0000000..55c2d29 --- /dev/null +++ b/.idea/dataSources.xml @@ -0,0 +1,17 @@ + + + + + mysql.8 + true + com.mysql.cj.jdbc.Driver + jdbc:mysql://nlb-kd2xz70qhllfet2koj.cn-beijing.nlb.aliyuncsslb.com:33068 + + + + + + $ProjectFileDir$ + + + \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..0bf3e5b --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,278 @@ +# ZCloud Java 项目公共开发规范与模板 + +> 本文件可复制到新项目直接使用。服务名、包名、网关前缀、领域边界和业务规则应写在项目自己的文档中,不要写入本公共模板。 + +## 1. DDD 分层 + +新项目统一采用 DDD 多模块结构,实际包名前缀、服务名和网关前缀由项目自行定义。 + +| 模块 | 职责 | 禁止事项 | +| --- | --- | --- | +| `web-client` | API、Cmd、Qry、Co | 依赖 App、Domain、Infrastructure | +| `web-adapter` | Web/App/Socket Controller、Dubbo Facade 等入站适配 | 直接访问 Mapper、编写业务规则 | +| `web-app` | Client 接口实现、执行器、用例编排、事务 | 堆积可复用领域规则、长事务调用远程服务 | +| `web-domain` | 领域实体、值对象、领域服务、Gateway 接口 | 依赖 MyBatis、Dubbo、HTTP、外部 DTO | +| `web-infrastructure` | Gateway 实现、Dubbo/HTTP 调用、DO、Mapper、Repository | 向 Domain 泄漏 DO 或外部 DTO | +| `start` | 启动、配置、XXL-JOB 装配 | 编写业务逻辑 | + +标准调用链: + +```text +Adapter -> Client接口(App实现) -> Executor -> Domain/Gateway -> Infrastructure +``` + +公司文档允许执行器或 Adapter 直接调用基础设施能力;采用 DDD 分层的项目优先通过 Domain Gateway。纯协议转换且不进入领域用例的场景,Adapter 才可直接引用外部 Facade。 + +## 2. 命名与对象基类 + +| 类型 | 命名/基类 | 放置位置 | +| --- | --- | --- | +| 领域实体 | `XxxE` | `web-domain/model` | +| 领域网关 | `XxxGateway` | `web-domain/gateway` | +| 网关实现 | `XxxGatewayImpl` | `web-infrastructure/gatewayimpl` | +| 持久化对象 | `XxxDO extends BaseDO` | `persistence/dataobject` | +| Mapper | `XxxMapper` | `persistence/mapper` | +| 新增/修改命令 | `XxxAddCmd`、`XxxUpdateCmd extends Command` | `web-client` | +| 分页查询 | `XxxPageQry extends BasePageQuery` | `web-client` | +| 返回对象 | `XxxCo extends ClientObject` | `web-client` | +| 执行器 | `XxxAddExe`、`XxxUpdateExe`、`XxxRemoveExe`、`XxxQueryExe` | `web-app` | + +- Cmd、Qry、Co 和 Adapter 使用的服务接口统一定义在 `web-client`。 +- 不得直接返回 DO、领域实体或第三方 DTO。 +- 不得在 `persistence.dataobject` 和 `persistence.domainobject` 创建同名 DO;新增 DO 统一放 `dataobject`。 + +## 3. HTTP 接口模板 + +- 路径使用 `/{gateway-prefix}/{resource}`,其中网关前缀由具体项目确定。 +- GET、POST、PUT、DELETE 按资源语义使用。 +- Controller 只做参数接收、校验、权限控制和应用服务调用。 +- 新增、修改分别使用 AddCmd、UpdateCmd,配合 `@Validated` 和 Bean Validation。 +- Swagger 使用 `@Api`、`@ApiOperation`、`@ApiModelProperty`;Swagger 的 `required` 不能代替参数校验注解。 +- 当前用户通过 `AuthContext.getCurrentUser()` 获取,不信任前端传入的租户、单位、创建人等身份字段。 + +```java +@ApiOperation("新增") +@PostMapping("/save") +@PreAuthorize("@pms.hasPermission('实际菜单或按钮权限编码')") +public SingleResponse add(@Validated @RequestBody XxxAddCmd cmd) { + return xxxService.add(cmd); +} +``` + +接口权限编码必须取自实际菜单/按钮配置,禁止复制示例编码。 + +### 标准返回类 + +| 类型 | 场景 | +| --- | --- | +| `SingleResponse` | 单条数据 | +| `PageResponse` | 分页数据 | +| `MultiResponse` | 集合数据 | +| `Response` | 无返回数据 | + +禁止新增另一套通用响应外壳。批量操作存在部分成功时,在标准响应承载的 Co 中返回逐条结果。 + +## 4. 数据权限模板 + +接口权限控制“能否调用”,数据权限控制“能看到哪些数据”,两者不能互相替代。 + +```java +@Mapper +@DataScopes({ + @DataScope( + method = "selectOne", + menuPerms = "实际菜单权限编码", + tenantAlias = "tenant_id" + ) +}) +public interface XxxMapper extends BaseMapper { +} +``` + +- `method` 与实际 Mapper 方法名一致。 +- `menuPerms` 使用实际权限编码。 +- `tenantAlias` 与 SQL 中租户列或表别名一致;多表查询必须写明确表别名。 +- 定时任务和 Dubbo 等非登录态入口必须明确租户、单位、环境范围,禁止默认跨租户扫描。 + +## 5. App、Domain 与异常模板 + +- App 服务实现负责把 Client 请求分派到执行器。 +- 执行器负责参数转换、用例编排、事务和 Gateway 调用。 +- 可复用业务规则放入领域实体或领域服务。 +- 写操作使用 `@Transactional(rollbackFor = Exception.class)`。 +- 领域模型不得调用基础设施层、Mapper、Repository 实现或 Dubbo Facade。 +- 保存失败、业务校验失败及远程调用失败统一转换为项目 `BizException`。 +- 转换异常时保留原异常日志/异常链;错误信息不能泄露密钥、人脸、证件或完整敏感报文。 + +## 6. Dubbo 服务模板 + +Dubbo 公共契约统一维护在组织公共接口项目 `zcloud_gbs_common`。新项目使用公共仓库的实际发布版本,不在规范中固定开发机绝对路径。 + +Maven 坐标: + +```xml + + com.zcloud.gbscommon + zcloud_gbscommon + ${zcloud.gbscommon.version} + +``` + +使用已有 Dubbo 能力时,先在 Common 项目中查找对应 Facade、Request、Response,禁止在业务项目内重复定义同名公共契约。 + +### 发布服务 + +1. 在 Common 项目的 `src/main/java/com/zcloud/gbscommon/{业务域}` 定义公共契约: + - `facade/XxxFacade` + - `request/XxxQry`、`XxxCmd` + - `response/XxxCo` +2. 服务项目在 Adapter 实现 Facade,使用 `@DubboService` 发布。 +3. Facade 只做校验、协议转换和应用服务调用。 +4. 修改 Common 契约属于跨项目变更:字段和方法确认后再修改、发布约定版本,并在消费项目验证依赖更新;未经明确需求不得直接修改 Common 项目。 + +```java +@DubboService +public class XxxFacadeImpl implements XxxFacade { + // 调用应用服务 +} +``` + +### 调用服务 + +- 启动类增加 `@EnableFacadeRpcClient`。 +- 消费方使用公共契约,并通过 `@DubboReference(check = false)` 注入。 +- DDD 项目优先在 Infrastructure GatewayImpl 中调用 Facade 并转换外部 DTO。 +- `check = false` 只是不在启动时检查提供方,不代表可以忽略运行期失败。 +- 写接口自动重试可能造成重复业务,必须结合幂等性设置超时和重试策略。 + +```java +@DubboReference(check = false) +private XxxFacade xxxFacade; +``` + +## 7. 用户、岗位、部门、企业翻译模板 + +按 ID 补充系统内公共信息时使用 `TranslateField`: + +- 用户:`getZcloudUserInfoById(s)` +- 岗位:`getZcloudPostInfoById(s)` +- 部门:`getZcloudDpetInfoById(s)` +- 企业:`getZcloudCorpInfoById(s)` +- 对象/集合:`translateZcloudCommonInformation(data, XxxCo.class)` + +列表必须使用 `ByIds` 或集合翻译,禁止循环逐条兑换造成 N+1 远程调用。 + +```java +@ZCloudTranslates({ + @ZCloudTranslate( + primaryKeyType = TranslateEunm.USER, + primaryKey = "userId", + mappedFields = {"userName:username"} + ), + @ZCloudTranslate( + primaryKeyType = TranslateEunm.POST, + primaryKey = "postId", + mappedFields = {"postName:name"} + ), + @ZCloudTranslate( + primaryKeyType = TranslateEunm.DEPT, + primaryKey = "deptId", + mappedFields = {"deptName:name"} + ), + @ZCloudTranslate( + primaryKeyType = TranslateEunm.CORP, + primaryKey = "corpId", + mappedFields = {"corpName:name"} + ) +}) +public class XxxCo extends ClientObject { +} +``` + +- `primaryKeyType`:翻译类型。 +- `primaryKey`:当前类中的 ID 属性。 +- `mappedFields`:`当前类目标属性:common Co来源属性`。 +- 翻译结果用于展示补充;核心业务判断应显式查询并校验。 + +## 8. 分页和动态查询模板 + +- 分页 Qry 继承 `BasePageQuery`,返回 `PageResponse`。 +- `toHashMap` 查询键使用“操作符前缀 + 驼峰字段”,基础设施层转换为下划线列名。 + +| 前缀 | SQL 条件 | 示例 | +| --- | --- | --- | +| `Like` | `LIKE` | `LikePersonName` | +| `eq` | `=` | `eqTaskStatus` | +| `gt` | `>` | `gtCreateTime` | +| `lt` | `<` | `ltCreateTime` | +| `le` | `<=` | `leCreateTime` | +| `ge` | `>=` | `geCreateTime` | +| `ne` | `<>` | `neTaskStatus` | + +前缀大小写必须与公共 `toHashMap` 解析规则一致;新增条件前先核对公共组件,禁止自行拼接 SQL 运算符。 + +## 9. 数据库模板 + +所有新建业务表必须包含以下公共字段: + +```sql +`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 '环境标识' +``` + +- 表使用 InnoDB、`utf8mb4`、`utf8mb4_0900_ai_ci`。 +- `version` 是本地乐观锁版本,业务版本另设 `data_version`。 +- `delete_enum` 是本地逻辑删除,不等于业务 `DELETE/DISABLE/REVOKE/EXPIRE`。 +- 多租户业务索引考虑 `tenant_id`,共享环境同时考虑 `env`。 +- 任务号、幂等键、外部回执号等必须有唯一约束。 +- 状态扫描组合索引应覆盖状态、下次执行时间、优先级等实际条件。 +- 不配置跨服务语义的数据库物理外键。 +- DDL、DO、Mapper、XML 保持一致;变更表结构同步维护 DDL 和正式迁移脚本。 +- 大报文和敏感信息应加密或仅存脱敏快照,禁止写普通日志。 + +## 10. XXL-JOB 模板 + +- 生产定时任务统一使用 XXL-JOB,不新增仅依赖本地 `@Scheduled` 的任务。 +- Handler 使用唯一且稳定的 `@XxlJob` 名称。 +- Job 只读取调度参数、触发应用用例、返回结果;业务逻辑放在 App/Domain。 +- 通过任务号、唯一键或锁保证业务幂等。 +- 失败必须抛出或返回失败,让调度中心感知;禁止吞异常后返回成功。 + +```java +@Component +@RequiredArgsConstructor +public class XxxJob { + private final XxxApplicationService xxxApplicationService; + + @XxlJob("projectXxxJob") + public ReturnT execute(String param) { + xxxApplicationService.execute(param); + return ReturnT.SUCCESS; + } +} +``` + +## 11. 开发完成检查表 + +- [ ] Client、Adapter、App、Domain、Infrastructure 职责和依赖方向正确。 +- [ ] Cmd/Co/PageQry 分别继承 `Command`、`ClientObject`、`BasePageQuery`。 +- [ ] Controller 无 Mapper 调用和业务规则,返回标准 Response 类型。 +- [ ] 接口配置真实 `@PreAuthorize` 权限编码,查询配置正确 `@DataScopes`。 +- [ ] Dubbo 契约来自 `zcloud_gbs_common`,提供方使用 `@DubboService`,消费方启用 RPC Client。 +- [ ] 外部调用完成 DTO 防腐转换、超时、异常和幂等处理。 +- [ ] 公共信息使用批量翻译,`mappedFields` 方向正确。 +- [ ] 写操作事务和 `BizException` 处理正确,不存在远程长事务。 +- [ ] DDL、DO、Mapper、XML、公共字段和索引一致。 +- [ ] XXL-JOB 名称唯一,任务幂等,失败可被调度中心感知。 +- [ ] 日志和响应未泄露人脸、证件、文件、密钥或完整敏感报文。 +- [ ] 完成与改动风险匹配的 Maven 编译、测试和 `git diff` 检查。 diff --git a/start/src/main/resources/nacos-prod.yml b/start/src/main/resources/nacos-prod.yml index ce849d4..081ecee 100644 --- a/start/src/main/resources/nacos-prod.yml +++ b/start/src/main/resources/nacos-prod.yml @@ -2,7 +2,7 @@ nacos: url: prod-nacos:8848 namespace: jjb-dragon application: - name: jjb-saas-zcloud-edgeguard + name: zcloud_gbs_edgeguard version: gateway: edgeguard cn-name: 边缘人脸区域安全 diff --git a/start/src/main/resources/nacos.yml b/start/src/main/resources/nacos.yml index 68c0cf7..6fc9378 100644 --- a/start/src/main/resources/nacos.yml +++ b/start/src/main/resources/nacos.yml @@ -2,7 +2,7 @@ nacos: url: 192.168.20.100:30290 namespace: jjb-dragon application: - name: jjb-saas-zcloud-edgeguard + name: zcloud_gbs_edgeguard version: gateway: edgeguard cn-name: 边缘人脸区域安全 diff --git a/start/src/main/resources/sdk-prod.yml b/start/src/main/resources/sdk-prod.yml index 4e99a6d..89ae2a0 100644 --- a/start/src/main/resources/sdk-prod.yml +++ b/start/src/main/resources/sdk-prod.yml @@ -1,6 +1,6 @@ sdk: server: - app-key: 722091ff53dd4abba078c2a00efd4a42 + app-key: 99626aa02f274e43be0f8acbe6f625f8 client: gateway: url: ${common.gateway.network.http.external} diff --git a/start/src/main/resources/sdk.yml b/start/src/main/resources/sdk.yml index cffc4f1..bb64bef 100644 --- a/start/src/main/resources/sdk.yml +++ b/start/src/main/resources/sdk.yml @@ -1,6 +1,6 @@ sdk: server: - app-key: 722091ff53dd4abba078c2a00efd4a42 + app-key: 99626aa02f274e43be0f8acbe6f625f8 client: gateway: url: ${common.gateway.network.http.external} diff --git a/web-domain/src/main/java/com/zcloud/gbs/edgeguard/domain/gateway/AlarmDisposalGateway.java b/web-domain/src/main/java/com/zcloud/gbs/edgeguard/domain/gateway/AlarmDisposalGateway.java new file mode 100644 index 0000000..74fda19 --- /dev/null +++ b/web-domain/src/main/java/com/zcloud/gbs/edgeguard/domain/gateway/AlarmDisposalGateway.java @@ -0,0 +1,9 @@ +package com.zcloud.gbs.edgeguard.domain.gateway; + +import com.zcloud.gbs.edgeguard.domain.model.AlarmDisposalE; + +public interface AlarmDisposalGateway { + Boolean add(AlarmDisposalE disposal); + Boolean update(AlarmDisposalE disposal); + AlarmDisposalE getById(Long id); +} 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 new file mode 100644 index 0000000..0df82fc --- /dev/null +++ b/web-domain/src/main/java/com/zcloud/gbs/edgeguard/domain/gateway/AlarmGateway.java @@ -0,0 +1,9 @@ +package com.zcloud.gbs.edgeguard.domain.gateway; + +import com.zcloud.gbs.edgeguard.domain.model.AlarmE; + +public interface AlarmGateway { + Boolean add(AlarmE alarm); + Boolean update(AlarmE alarm); + AlarmE 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 new file mode 100644 index 0000000..118eae9 --- /dev/null +++ b/web-domain/src/main/java/com/zcloud/gbs/edgeguard/domain/gateway/RegionCameraGateway.java @@ -0,0 +1,10 @@ +package com.zcloud.gbs.edgeguard.domain.gateway; + +import com.zcloud.gbs.edgeguard.domain.model.RegionCameraE; + +public interface RegionCameraGateway { + Boolean add(RegionCameraE regionCamera); + Boolean update(RegionCameraE regionCamera); + Boolean deleteById(Long id); + RegionCameraE getById(Long id); +} 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 new file mode 100644 index 0000000..ad06e93 --- /dev/null +++ b/web-domain/src/main/java/com/zcloud/gbs/edgeguard/domain/gateway/RegionGateway.java @@ -0,0 +1,10 @@ +package com.zcloud.gbs.edgeguard.domain.gateway; + +import com.zcloud.gbs.edgeguard.domain.model.RegionE; + +public interface RegionGateway { + Boolean add(RegionE region); + Boolean update(RegionE region); + Boolean deleteById(Long id); + RegionE getById(Long id); +} diff --git a/web-domain/src/main/java/com/zcloud/gbs/edgeguard/domain/model/AlarmDisposalE.java b/web-domain/src/main/java/com/zcloud/gbs/edgeguard/domain/model/AlarmDisposalE.java new file mode 100644 index 0000000..12388fe --- /dev/null +++ b/web-domain/src/main/java/com/zcloud/gbs/edgeguard/domain/model/AlarmDisposalE.java @@ -0,0 +1,30 @@ +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 AlarmDisposalE extends BaseE { + 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 String hiddenCreateStatus; + private String hiddenCreateError; + private Long handlerUserId; + private String handlerUserName; + private Long handlerDeptId; + private String handlerDeptName; + private Date disposedTime; +} 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 new file mode 100644 index 0000000..46c3881 --- /dev/null +++ b/web-domain/src/main/java/com/zcloud/gbs/edgeguard/domain/model/AlarmE.java @@ -0,0 +1,32 @@ +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 AlarmE extends BaseE { + 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; +} diff --git a/web-domain/src/main/java/com/zcloud/gbs/edgeguard/domain/model/RegionCameraE.java b/web-domain/src/main/java/com/zcloud/gbs/edgeguard/domain/model/RegionCameraE.java new file mode 100644 index 0000000..d54c495 --- /dev/null +++ b/web-domain/src/main/java/com/zcloud/gbs/edgeguard/domain/model/RegionCameraE.java @@ -0,0 +1,36 @@ +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 RegionCameraE extends BaseE { + private Long id; + private Long regionId; + private Long corpId; + private String cameraSource; + private String cameraId; + private String cameraName; + private String cameraUnitId; + private String cameraUnitName; + private String cameraTypeCode; + private String cameraTypeName; + private String cameraLevelCode; + private String cameraLevelName; + private String cameraDeptId; + private String cameraDeptName; + private String onlineStatus; + private String cameraSnapshot; + private Date cameraSyncedAt; + private Long leaderUserId; + private String bindStatus; + private Date boundTime; + private Date unboundTime; +} diff --git a/web-domain/src/main/java/com/zcloud/gbs/edgeguard/domain/model/RegionE.java b/web-domain/src/main/java/com/zcloud/gbs/edgeguard/domain/model/RegionE.java new file mode 100644 index 0000000..56600a6 --- /dev/null +++ b/web-domain/src/main/java/com/zcloud/gbs/edgeguard/domain/model/RegionE.java @@ -0,0 +1,26 @@ +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; + +/** 区域领域模型。 */ +@Data +@Entity +@EqualsAndHashCode(callSuper = true) +public class RegionE extends BaseE { + private Long id; + private String regionCode; + private String regionName; + private Long corpId; + private Long parentId; + private String treePath; + private Integer treeLevel; + private Integer sortNo; + private Long leaderUserId; + private String coordinateSystem; + /** 四个有序坐标点的 JSON 数组。 */ + private String fencePoints; + private String regionStatus; +} diff --git a/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/gatewayimpl/AlarmDisposalGatewayImpl.java b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/gatewayimpl/AlarmDisposalGatewayImpl.java new file mode 100644 index 0000000..83e0499 --- /dev/null +++ b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/gatewayimpl/AlarmDisposalGatewayImpl.java @@ -0,0 +1,45 @@ +package com.zcloud.gbs.edgeguard.gatewayimpl; + +import com.zcloud.gbs.edgeguard.domain.gateway.AlarmDisposalGateway; +import com.zcloud.gbs.edgeguard.domain.model.AlarmDisposalE; +import com.zcloud.gbs.edgeguard.persistence.dataobject.AlarmDisposalDO; +import com.zcloud.gbs.edgeguard.persistence.repository.AlarmDisposalRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class AlarmDisposalGatewayImpl implements AlarmDisposalGateway { + private final AlarmDisposalRepository repository; + + @Override + public Boolean add(AlarmDisposalE disposal) { + return repository.save(toDO(disposal)); + } + + @Override + public Boolean update(AlarmDisposalE disposal) { + return repository.updateById(toDO(disposal)); + } + + @Override + public AlarmDisposalE getById(Long id) { + return toEntity(repository.getById(id)); + } + + private AlarmDisposalDO toDO(AlarmDisposalE source) { + AlarmDisposalDO target = new AlarmDisposalDO(); + BeanUtils.copyProperties(source, target); + return target; + } + + private AlarmDisposalE toEntity(AlarmDisposalDO source) { + if (source == null) { + return null; + } + AlarmDisposalE target = new AlarmDisposalE(); + BeanUtils.copyProperties(source, target); + return target; + } +} 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 new file mode 100644 index 0000000..f783f89 --- /dev/null +++ b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/gatewayimpl/AlarmGatewayImpl.java @@ -0,0 +1,45 @@ +package com.zcloud.gbs.edgeguard.gatewayimpl; + +import com.zcloud.gbs.edgeguard.domain.gateway.AlarmGateway; +import com.zcloud.gbs.edgeguard.domain.model.AlarmE; +import com.zcloud.gbs.edgeguard.persistence.dataobject.AlarmDO; +import com.zcloud.gbs.edgeguard.persistence.repository.AlarmRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class AlarmGatewayImpl implements AlarmGateway { + private final AlarmRepository repository; + + @Override + public Boolean add(AlarmE alarm) { + return repository.save(toDO(alarm)); + } + + @Override + public Boolean update(AlarmE alarm) { + return repository.updateById(toDO(alarm)); + } + + @Override + public AlarmE getById(Long id) { + return toEntity(repository.getById(id)); + } + + private AlarmDO toDO(AlarmE source) { + AlarmDO target = new AlarmDO(); + BeanUtils.copyProperties(source, target); + return target; + } + + private AlarmE toEntity(AlarmDO source) { + if (source == null) { + return null; + } + AlarmE target = new AlarmE(); + BeanUtils.copyProperties(source, target); + return target; + } +} 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 new file mode 100644 index 0000000..d4f4b36 --- /dev/null +++ b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/gatewayimpl/RegionCameraGatewayImpl.java @@ -0,0 +1,50 @@ +package com.zcloud.gbs.edgeguard.gatewayimpl; + +import com.zcloud.gbs.edgeguard.domain.gateway.RegionCameraGateway; +import com.zcloud.gbs.edgeguard.domain.model.RegionCameraE; +import com.zcloud.gbs.edgeguard.persistence.dataobject.RegionCameraDO; +import com.zcloud.gbs.edgeguard.persistence.repository.RegionCameraRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class RegionCameraGatewayImpl implements RegionCameraGateway { + private final RegionCameraRepository repository; + + @Override + public Boolean add(RegionCameraE regionCamera) { + return repository.save(toDO(regionCamera)); + } + + @Override + public Boolean update(RegionCameraE regionCamera) { + return repository.updateById(toDO(regionCamera)); + } + + @Override + public Boolean deleteById(Long id) { + return repository.removeById(id); + } + + @Override + public RegionCameraE getById(Long id) { + return toEntity(repository.getById(id)); + } + + private RegionCameraDO toDO(RegionCameraE source) { + RegionCameraDO target = new RegionCameraDO(); + BeanUtils.copyProperties(source, target); + return target; + } + + private RegionCameraE toEntity(RegionCameraDO source) { + if (source == null) { + return null; + } + RegionCameraE target = new RegionCameraE(); + BeanUtils.copyProperties(source, target); + return target; + } +} 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 new file mode 100644 index 0000000..d9949e6 --- /dev/null +++ b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/gatewayimpl/RegionGatewayImpl.java @@ -0,0 +1,50 @@ +package com.zcloud.gbs.edgeguard.gatewayimpl; + +import com.zcloud.gbs.edgeguard.domain.gateway.RegionGateway; +import com.zcloud.gbs.edgeguard.domain.model.RegionE; +import com.zcloud.gbs.edgeguard.persistence.dataobject.RegionDO; +import com.zcloud.gbs.edgeguard.persistence.repository.RegionRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class RegionGatewayImpl implements RegionGateway { + private final RegionRepository repository; + + @Override + public Boolean add(RegionE region) { + return repository.save(toDO(region)); + } + + @Override + public Boolean update(RegionE region) { + return repository.updateById(toDO(region)); + } + + @Override + public Boolean deleteById(Long id) { + return repository.removeById(id); + } + + @Override + public RegionE getById(Long id) { + return toEntity(repository.getById(id)); + } + + private RegionDO toDO(RegionE source) { + RegionDO target = new RegionDO(); + BeanUtils.copyProperties(source, target); + return target; + } + + private RegionE toEntity(RegionDO source) { + if (source == null) { + return null; + } + RegionE target = new RegionE(); + 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 new file mode 100644 index 0000000..e15b094 --- /dev/null +++ b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/dataobject/AlarmDO.java @@ -0,0 +1,30 @@ +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_alarm") +public class AlarmDO extends BaseDO { + 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; +} diff --git a/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/dataobject/AlarmDisposalDO.java b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/dataobject/AlarmDisposalDO.java new file mode 100644 index 0000000..48afde0 --- /dev/null +++ b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/dataobject/AlarmDisposalDO.java @@ -0,0 +1,28 @@ +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_alarm_disposal") +public class AlarmDisposalDO extends BaseDO { + private Long alarmId; + private Integer isFalseAlarm; + private Integer isHiddenHazard; + private String disposalResultCode; + private String disposalResultName; + private String disposalDescription; + private String hiddenId; + private String hiddenCreateStatus; + private String hiddenCreateError; + private Long handlerUserId; + private String handlerUserName; + private Long handlerDeptId; + private String handlerDeptName; + private Date disposedTime; +} diff --git a/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/dataobject/RegionCameraDO.java b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/dataobject/RegionCameraDO.java new file mode 100644 index 0000000..effcfd1 --- /dev/null +++ b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/dataobject/RegionCameraDO.java @@ -0,0 +1,34 @@ +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_region_camera") +public class RegionCameraDO extends BaseDO { + private Long regionId; + private Long corpId; + private String cameraSource; + private String cameraId; + private String cameraName; + private String cameraUnitId; + private String cameraUnitName; + private String cameraTypeCode; + private String cameraTypeName; + private String cameraLevelCode; + private String cameraLevelName; + private String cameraDeptId; + private String cameraDeptName; + private String onlineStatus; + private String cameraSnapshot; + private Date cameraSyncedAt; + private Long leaderUserId; + private String bindStatus; + private Date boundTime; + private Date unboundTime; +} diff --git a/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/dataobject/RegionDO.java b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/dataobject/RegionDO.java new file mode 100644 index 0000000..323078c --- /dev/null +++ b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/dataobject/RegionDO.java @@ -0,0 +1,23 @@ +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; + +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("edgeguard_region") +public class RegionDO extends BaseDO { + private String regionCode; + private String regionName; + private Long corpId; + private Long parentId; + private String treePath; + private Integer treeLevel; + private Integer sortNo; + private Long leaderUserId; + private String coordinateSystem; + private String fencePoints; + private String regionStatus; +} diff --git a/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/mapper/AlarmDisposalMapper.java b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/mapper/AlarmDisposalMapper.java new file mode 100644 index 0000000..d434bd0 --- /dev/null +++ b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/mapper/AlarmDisposalMapper.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.AlarmDisposalDO; + +public interface AlarmDisposalMapper extends BaseMapper { +} diff --git a/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/mapper/AlarmMapper.java b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/mapper/AlarmMapper.java new file mode 100644 index 0000000..db78a91 --- /dev/null +++ b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/mapper/AlarmMapper.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.AlarmDO; + +public interface AlarmMapper extends BaseMapper { +} diff --git a/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/mapper/RegionCameraMapper.java b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/mapper/RegionCameraMapper.java new file mode 100644 index 0000000..6c2a866 --- /dev/null +++ b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/mapper/RegionCameraMapper.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.RegionCameraDO; + +public interface RegionCameraMapper extends BaseMapper { +} diff --git a/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/mapper/RegionMapper.java b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/mapper/RegionMapper.java new file mode 100644 index 0000000..6549be3 --- /dev/null +++ b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/mapper/RegionMapper.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.RegionDO; + +public interface RegionMapper extends BaseMapper { +} diff --git a/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/repository/AlarmDisposalRepository.java b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/repository/AlarmDisposalRepository.java new file mode 100644 index 0000000..8d697c8 --- /dev/null +++ b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/repository/AlarmDisposalRepository.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.AlarmDisposalDO; + +public interface AlarmDisposalRepository extends BaseRepository { +} diff --git a/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/repository/AlarmRepository.java b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/repository/AlarmRepository.java new file mode 100644 index 0000000..b2e7773 --- /dev/null +++ b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/repository/AlarmRepository.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.AlarmDO; + +public interface AlarmRepository extends BaseRepository { +} diff --git a/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/repository/RegionCameraRepository.java b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/repository/RegionCameraRepository.java new file mode 100644 index 0000000..f99b426 --- /dev/null +++ b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/repository/RegionCameraRepository.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.RegionCameraDO; + +public interface RegionCameraRepository extends BaseRepository { +} diff --git a/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/repository/RegionRepository.java b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/repository/RegionRepository.java new file mode 100644 index 0000000..a771c81 --- /dev/null +++ b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/repository/RegionRepository.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.RegionDO; + +public interface RegionRepository extends BaseRepository { +} diff --git a/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/repository/impl/AlarmDisposalRepositoryImpl.java b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/repository/impl/AlarmDisposalRepositoryImpl.java new file mode 100644 index 0000000..cd7ed85 --- /dev/null +++ b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/repository/impl/AlarmDisposalRepositoryImpl.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.AlarmDisposalDO; +import com.zcloud.gbs.edgeguard.persistence.mapper.AlarmDisposalMapper; +import com.zcloud.gbs.edgeguard.persistence.repository.AlarmDisposalRepository; +import org.springframework.stereotype.Service; + +@Service +public class AlarmDisposalRepositoryImpl extends BaseRepositoryImpl + implements AlarmDisposalRepository { +} diff --git a/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/repository/impl/AlarmRepositoryImpl.java b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/repository/impl/AlarmRepositoryImpl.java new file mode 100644 index 0000000..f34c1c3 --- /dev/null +++ b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/repository/impl/AlarmRepositoryImpl.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.AlarmDO; +import com.zcloud.gbs.edgeguard.persistence.mapper.AlarmMapper; +import com.zcloud.gbs.edgeguard.persistence.repository.AlarmRepository; +import org.springframework.stereotype.Service; + +@Service +public class AlarmRepositoryImpl extends BaseRepositoryImpl + implements AlarmRepository { +} diff --git a/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/repository/impl/RegionCameraRepositoryImpl.java b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/repository/impl/RegionCameraRepositoryImpl.java new file mode 100644 index 0000000..77917e9 --- /dev/null +++ b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/repository/impl/RegionCameraRepositoryImpl.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.RegionCameraDO; +import com.zcloud.gbs.edgeguard.persistence.mapper.RegionCameraMapper; +import com.zcloud.gbs.edgeguard.persistence.repository.RegionCameraRepository; +import org.springframework.stereotype.Service; + +@Service +public class RegionCameraRepositoryImpl extends BaseRepositoryImpl + implements RegionCameraRepository { +} diff --git a/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/repository/impl/RegionRepositoryImpl.java b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/repository/impl/RegionRepositoryImpl.java new file mode 100644 index 0000000..af3b606 --- /dev/null +++ b/web-infrastructure/src/main/java/com/zcloud/gbs/edgeguard/persistence/repository/impl/RegionRepositoryImpl.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.RegionDO; +import com.zcloud.gbs.edgeguard.persistence.mapper.RegionMapper; +import com.zcloud.gbs.edgeguard.persistence.repository.RegionRepository; +import org.springframework.stereotype.Service; + +@Service +public class RegionRepositoryImpl extends BaseRepositoryImpl + implements RegionRepository { +} diff --git a/web-infrastructure/src/main/resources/RegionManagementDDL.sql b/web-infrastructure/src/main/resources/RegionManagementDDL.sql new file mode 100644 index 0000000..c021afe --- /dev/null +++ b/web-infrastructure/src/main/resources/RegionManagementDDL.sql @@ -0,0 +1,155 @@ +-- 综合平台区域与摄像头绑定表结构(MySQL 8.0) +-- 摄像头主数据来自第三方,本系统只保存区域、摄像头绑定和负责人。 + +CREATE TABLE `edgeguard_region` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '区域主键id', + `region_code` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '区域编码,对外稳定唯一标识', + `region_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '区域名称', + `corp_id` bigint NOT NULL COMMENT '所属企业id,企业主数据来自公共服务', + `parent_id` bigint NOT NULL DEFAULT 0 COMMENT '父区域id,0表示企业下的顶级区域', + `tree_path` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '/' COMMENT '祖先路径,例如/1/10/', + `tree_level` int NOT NULL DEFAULT 1 COMMENT '区域层级,企业下顶级区域为1', + `sort_no` int NOT NULL DEFAULT 0 COMMENT '同级排序号', + `leader_user_id` bigint NOT NULL COMMENT '区域负责人用户id', + `coordinate_system` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '坐标系WGS84/GCJ02/BD09', + `fence_points` json NOT NULL COMMENT '有序电子围栏坐标点,一期必须为4个点', + `region_status` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'ENABLED' COMMENT '区域状态ENABLED/DISABLED', + `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`), + UNIQUE KEY `uk_region_code` (`region_code`), + KEY `idx_region_tree` (`tenant_id`, `corp_id`, `parent_id`, `delete_enum`, `sort_no`), + KEY `idx_region_path` (`tenant_id`, `corp_id`, `tree_path`(191)), + KEY `idx_region_status` (`tenant_id`, `corp_id`, `region_status`, `delete_enum`), + KEY `idx_region_leader` (`leader_user_id`), + CONSTRAINT `chk_region_fence_four_points` CHECK ((JSON_TYPE(`fence_points`) = 'ARRAY') AND (JSON_LENGTH(`fence_points`) = 4)) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='企业区域树表'; + +CREATE TABLE `edgeguard_region_camera` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '区域摄像头绑定主键id', + `region_id` bigint NOT NULL COMMENT '本地区域id', + `corp_id` bigint NOT NULL COMMENT '所属企业id,冗余用于权限和查询校验', + `camera_source` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '摄像头来源系统编码', + `camera_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '第三方摄像头唯一标识', + `camera_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '摄像头名称缓存,第三方为权威数据源', + `camera_unit_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '摄像头所属单位第三方标识缓存', + `camera_unit_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '摄像头所属单位名称缓存', + `camera_type_code` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '摄像头类型编码缓存', + `camera_type_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '摄像头类型名称缓存', + `camera_level_code` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '摄像头等级编码缓存', + `camera_level_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '摄像头等级名称缓存', + `camera_dept_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '摄像头主管部门第三方标识缓存', + `camera_dept_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '摄像头主管部门名称缓存', + `online_status` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '在线状态缓存ONLINE/OFFLINE/UNKNOWN', + `camera_snapshot` json DEFAULT NULL COMMENT '第三方摄像头扩展字段快照', + `camera_synced_at` datetime DEFAULT NULL COMMENT '摄像头缓存最近同步时间', + `leader_user_id` bigint DEFAULT NULL COMMENT '摄像头负责人用户id', + `bind_status` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'BOUND' COMMENT '绑定状态BOUND/UNBOUND', + `bound_time` datetime NOT NULL COMMENT '最近绑定时间', + `unbound_time` datetime 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`), + UNIQUE KEY `uk_camera_source_id` (`camera_source`, `camera_id`), + KEY `idx_region_camera` (`tenant_id`, `region_id`, `bind_status`, `delete_enum`), + KEY `idx_camera_corp` (`tenant_id`, `corp_id`, `delete_enum`), + KEY `idx_camera_filter` (`tenant_id`, `region_id`, `camera_level_code`, `online_status`, `delete_enum`), + KEY `idx_camera_name` (`camera_name`), + KEY `idx_camera_leader` (`leader_user_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='区域与第三方摄像头绑定表'; + +-- 第三方报警主数据。隐患主流程复用 ZcloudHiddenFacade,本系统只保存隐患id。 +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 '第三方报警发生时间', + `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', + `corp_name_snapshot` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '报警时企业名称快照', + `region_id` bigint DEFAULT NULL COMMENT '报警所属区域id', + `region_code` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '报警时区域编码快照', + `region_name_snapshot` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '报警时区域名称快照', + `region_path_snapshot` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '报警时区域完整路径快照', + `camera_source` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '摄像头来源系统编码', + `camera_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '第三方摄像头唯一标识', + `camera_name_snapshot` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '报警时摄像头名称快照', + `alarm_image_url` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '第三方报警截图地址', + `disposal_status` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT 'PENDING' COMMENT '处置状态PENDING/DISPOSED', + `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`), + UNIQUE KEY `uk_source_alarm_no` (`source_system`, `alarm_no`), + KEY `idx_alarm_disposal` (`tenant_id`, `corp_id`, `disposal_status`, `alarm_time`), + KEY `idx_alarm_region` (`tenant_id`, `corp_id`, `region_id`, `disposal_status`, `alarm_time`), + KEY `idx_alarm_pending_timeout` (`tenant_id`, `disposal_status`, `received_time`, `region_id`), + KEY `idx_alarm_camera` (`camera_source`, `camera_id`, `alarm_time`), + KEY `idx_alarm_statistics` (`tenant_id`, `corp_id`, `disposal_status`, `alarm_time`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='第三方报警主表'; + +CREATE TABLE `edgeguard_alarm_disposal` ( + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '报警处置主键id', + `alarm_id` bigint NOT NULL COMMENT '报警主键id', + `is_false_alarm` tinyint(1) NOT NULL COMMENT '是否误报0否1是', + `is_hidden_hazard` tinyint(1) DEFAULT NULL COMMENT '是否隐患0否1是,非误报时必填', + `disposal_result_code` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '处置结果字典编码', + `disposal_result_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '处置结果名称快照', + `disposal_description` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '处置说明', + `hidden_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '关联隐患id,仅判定为隐患时有值', + `hidden_create_status` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '隐患创建状态PENDING/SUCCESS/FAILED', + `hidden_create_error` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '最近隐患创建失败信息', + `handler_user_id` bigint NOT NULL COMMENT '处置人用户id', + `handler_user_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '处置人姓名快照', + `handler_dept_id` bigint DEFAULT NULL COMMENT '处置部门id', + `handler_dept_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '处置部门名称快照', + `disposed_time` datetime NOT 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`), + UNIQUE KEY `uk_disposal_alarm` (`alarm_id`), + KEY `idx_disposal_hidden` (`hidden_id`), + KEY `idx_hidden_create_retry` (`hidden_create_status`, `update_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`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='报警处置结论表'; diff --git a/web-infrastructure/src/main/resources/mapper/AlarmDisposalMapper.xml b/web-infrastructure/src/main/resources/mapper/AlarmDisposalMapper.xml new file mode 100644 index 0000000..5d9ffcd --- /dev/null +++ b/web-infrastructure/src/main/resources/mapper/AlarmDisposalMapper.xml @@ -0,0 +1,4 @@ + + + + diff --git a/web-infrastructure/src/main/resources/mapper/AlarmMapper.xml b/web-infrastructure/src/main/resources/mapper/AlarmMapper.xml new file mode 100644 index 0000000..ad0fb25 --- /dev/null +++ b/web-infrastructure/src/main/resources/mapper/AlarmMapper.xml @@ -0,0 +1,4 @@ + + + + diff --git a/web-infrastructure/src/main/resources/mapper/RegionCameraMapper.xml b/web-infrastructure/src/main/resources/mapper/RegionCameraMapper.xml new file mode 100644 index 0000000..66f3737 --- /dev/null +++ b/web-infrastructure/src/main/resources/mapper/RegionCameraMapper.xml @@ -0,0 +1,4 @@ + + + + diff --git a/web-infrastructure/src/main/resources/mapper/RegionMapper.xml b/web-infrastructure/src/main/resources/mapper/RegionMapper.xml new file mode 100644 index 0000000..b30036d --- /dev/null +++ b/web-infrastructure/src/main/resources/mapper/RegionMapper.xml @@ -0,0 +1,4 @@ + + + + diff --git a/边缘人脸识别区域安全设计方案.md b/边缘人脸识别区域安全设计方案.md new file mode 100644 index 0000000..3f21c71 --- /dev/null +++ b/边缘人脸识别区域安全设计方案.md @@ -0,0 +1,398 @@ +# 边缘人脸识别区域安全设计方案 + +## 1. 当前需求结论 + +综合平台维护企业下面的区域树,并向第三方提供区域查询接口。 + +当前功能范围: + +- 一个企业可以维护多棵或多层区域树。 +- 区域支持新增同级区域、新增下级区域、修改和删除。 +- 区域包含区域名称、负责人和电子围栏坐标。 +- 当前原型要求电子围栏由 4 个有顺序的坐标点组成。 +- 摄像头主数据来自第三方接口,综合平台不维护摄像头主档。 +- 综合平台保存区域与摄像头的绑定关系。 +- 每个已绑定摄像头可以设置负责人。 +- 选择区域后,右侧列表展示该区域直接绑定的摄像头。 +- 人员、公司、部门、人脸和人员区域准入关系不属于本功能维护范围。 +- 综合平台不主动推送,由第三方主动调用区域开放接口。 + +## 2. 领域边界 + +```text +企业(外部公共数据) +└── 区域(综合平台维护,树结构) + ├── 下级区域 + └── 摄像头绑定(摄像头主数据来自第三方) + └── 摄像头负责人 +``` + +### 2.1 企业 + +- 企业作为区域树的根节点和数据隔离维度。 +- 企业主数据不在区域表中重复维护,只保存 `corp_id`。 +- 页面树由“企业虚拟根节点 + 本地区域节点”组合生成。 +- 企业名称通过公共信息翻译或企业公共接口获取,不作为区域的权威数据保存。 + +### 2.2 区域 + +区域是本功能的聚合根,负责: + +- 企业归属。 +- 父子层级。 +- 区域名称。 +- 区域负责人。 +- 电子围栏坐标。 +- 区域状态和逻辑删除。 + +### 2.3 摄像头 + +- 摄像头名称、所属单位、类型、等级、主管部门、在线状态等来自第三方接口。 +- 本系统不成为摄像头主数据的权威维护方,但会冗余列表展示和查询所需的摄像头缓存字段。 +- 本系统保存第三方摄像头唯一标识、来源系统、区域绑定、摄像头负责人,以及名称、所属单位、类型、等级、主管部门、在线状态等快照。 +- 绑定摄像头时写入快照;后续可按查询或定时策略刷新,第三方数据始终是权威来源。 +- 第三方临时不可用时可以展示本地最近快照,并显示最近同步时间;不得把缓存快照作为修改第三方摄像头的依据。 + +## 3. 关键设计决策 + +### 3.1 公司节点不进入区域表 + +区域表只存真实区域。顶级区域的 `parent_id` 为空或为 `0`,通过 `corp_id` 归属企业。这样不会把外部企业主数据和本地区域数据混为一种实体。 + +页面树建议返回统一节点结构: + +```text +nodeType = CORP 企业虚拟节点 +nodeType = REGION 本地区域节点 +``` + +### 3.2 区域树规则 + +- 同一企业下,同一父节点中的区域名称建议唯一。 +- 子区域必须与父区域属于同一企业、租户和单位。 +- 禁止把节点移动到自身或自己的子孙节点下面。 +- 删除包含子区域的节点时,默认禁止删除,先迁移或删除子节点。 +- 删除仍绑定摄像头的区域时,默认禁止删除,先解除或迁移绑定。 +- 查询区域树时使用批量查询后在内存组树,禁止递归逐节点查询数据库。 + +是否允许跨层级移动区域、区域最大层级和区域排序规则仍需产品确认。 + +### 3.3 四点电子围栏 + +建议数据库保存一个有顺序的坐标数组,而不是设计 `point1_x` 到 `point4_y` 八个固定字段: + +```json +[ + {"seq": 1, "longitude": 113.123456, "latitude": 23.123456}, + {"seq": 2, "longitude": 113.223456, "latitude": 23.123456}, + {"seq": 3, "longitude": 113.223456, "latitude": 23.223456}, + {"seq": 4, "longitude": 113.123456, "latitude": 23.223456} +] +``` + +一期保存时强制校验: + +- 必须恰好 4 个点。 +- 点序号不可重复,按顺时针或逆时针排序。 +- 经度范围 `[-180, 180]`,纬度范围 `[-90, 90]`。 +- 相邻点和首尾点不能重复。 +- 四条边不能自相交。 +- 围栏面积不能为 0。 +- 必须明确坐标系,例如 WGS84、GCJ-02 或 BD-09;坐标系作为字段保存,禁止默认猜测。 + +使用 JSON 可以满足当前 4 点要求,同时允许未来扩展为多边形。如果后续需要“判断坐标是否在区域内”等空间检索,再增加 MySQL `POLYGON` 空间字段和空间索引。 + +### 3.4 负责人 + +- 区域负责人保存用户 ID,不重复保存用户主档。 +- 摄像头负责人保存在区域摄像头绑定记录中。 +- 页面通过 `TranslateField` 批量翻译负责人姓名,禁止循环逐人查询。 +- 原型中负责人是必填单选;是否允许多个负责人,需要产品确认。 +- 人员被停用或删除后,负责人绑定如何处理,需要明确校验或提示规则。 + +### 3.5 摄像头绑定 + +- 选择摄像头时调用第三方接口获取可选摄像头列表。 +- 保存时只提交选中的第三方摄像头 ID,本系统必须再次校验摄像头存在性和调用方数据范围。 +- 建议一期约束“一个来源系统中的一个摄像头只能绑定一个区域”。如果业务允许一个摄像头属于多个区域,再取消唯一约束。 +- 重复选择已经绑定到当前区域的摄像头应幂等成功。 +- 已绑定其他区域时,应返回明确提示,不静默迁移。 +- 解绑只删除本地关系,不调用第三方删除摄像头。 +- 在线状态等动态字段不写入本地绑定表,以第三方实时结果为准。 + +## 4. 表结构草案 + +原型可以确定两张核心业务表。所有表都包含根目录 `AGENTS.md` 规定的公共字段。可执行评审的 MySQL 8.0 DDL 草案见 `web-infrastructure/src/main/resources/RegionManagementDDL.sql`。 + +### 4.1 区域表 `security_region` + +| 字段 | 类型建议 | 说明 | +| --- | --- | --- | +| `id` | `bigint` | 区域主键 | +| `region_code` | `varchar(64)` | 区域编码,建议提供稳定的对外标识 | +| `region_name` | `varchar(100)` | 区域名称 | +| `corp_id` | `bigint` | 所属企业 ID | +| `parent_id` | `bigint` | 父区域 ID,顶级区域为空或 0 | +| `tree_path` | `varchar(1000)` | 祖先路径,用于子树查询和防循环 | +| `tree_level` | `int` | 区域层级,企业下顶级区域从 1 开始 | +| `sort_no` | `int` | 同级排序 | +| `leader_user_id` | `bigint` | 区域负责人用户 ID | +| `coordinate_system` | `varchar(32)` | 坐标系,例如 GCJ02 | +| `fence_points` | `json` | 有序电子围栏坐标点,一期固定 4 点 | +| `region_status` | `varchar(32)` | `ENABLED/DISABLED` | + +建议索引: + +- 唯一索引:`region_code`,是否叠加租户维度按编码生成规则确定。 +- 同级名称唯一:`tenant_id + corp_id + parent_id + region_name + delete_enum`。由于可空字段会影响 MySQL 唯一约束,正式 DDL 需采用非空默认值或规范化唯一键。 +- 树查询索引:`tenant_id + corp_id + parent_id + delete_enum + sort_no`。 +- 负责人查询索引:`leader_user_id`,仅在存在反向查询需求时增加。 + +### 4.2 区域摄像头绑定表 `security_region_camera` + +| 字段 | 类型建议 | 说明 | +| --- | --- | --- | +| `id` | `bigint` | 主键 | +| `region_id` | `bigint` | 本地区域 ID | +| `camera_source` | `varchar(64)` | 摄像头来源系统编码 | +| `camera_id` | `varchar(128)` | 第三方摄像头唯一标识 | +| `camera_name` | `varchar(255)` | 摄像头名称缓存 | +| `camera_unit_id/name` | `varchar` | 所属单位标识及名称缓存 | +| `camera_type_code/name` | `varchar` | 类型编码及名称缓存 | +| `camera_level_code/name` | `varchar` | 等级编码及名称缓存 | +| `camera_dept_id/name` | `varchar` | 主管部门标识及名称缓存 | +| `online_status` | `varchar(32)` | 在线状态缓存 | +| `camera_snapshot` | `json` | 第三方其他扩展字段快照 | +| `camera_synced_at` | `datetime` | 缓存最近同步时间 | +| `leader_user_id` | `bigint` | 摄像头负责人用户 ID | +| `bind_status` | `varchar(32)` | `BOUND/UNBOUND`,若逻辑删除足够可不单设 | +| `bound_time` | `datetime` | 绑定时间 | + +建议索引: + +- 唯一索引:`tenant_id + camera_source + camera_id`,落实一个摄像头只绑定一个区域。 +- 区域列表索引:`tenant_id + region_id + delete_enum`。 +- 摄像头负责人索引:`leader_user_id`,仅在需要按负责人查询时增加。 + +当前只缓存已经绑定的摄像头,因此展示字段直接放在绑定表中即可。如果后续要求缓存全部第三方摄像头、未绑定摄像头也要离线搜索,或一个摄像头允许绑定多个区域,则应拆分为“摄像头缓存表 + 区域摄像头关系表”。 + +## 5. 页面功能拆解 + +### 5.1 区域树 + +- 按当前用户数据权限加载企业。 +- 批量查询企业下全部区域并组装树。 +- 支持新增顶级区域、同级区域和下级区域。 +- 支持修改和删除区域。 +- 选中区域后显示企业/区域面包屑。 + +### 5.2 新增或修改区域 + +入参建议: + +- `parentId`:新增顶级区域时为空。 +- `corpId`:必填。 +- `regionName`:必填。 +- `leaderUserId`:按当前原型必填。 +- `coordinateSystem`:必填。 +- `fencePoints`:必填且恰好 4 点。 +- `sortNo`:可选。 + +### 5.3 选择摄像头 + +- 显示当前区域路径。 +- 调用第三方接口分页查询摄像头。 +- 支持视频名称、等级、在线状态等第三方支持的条件。 +- 已绑定当前区域的摄像头默认勾选。 +- 已绑定其他区域的摄像头应禁选或明确标识。 +- 确认后计算新增绑定和解除绑定差异,在一个本地事务中保存。 + +### 5.4 区域摄像头列表 + +列表字段来自两部分: + +| 字段 | 来源 | +| --- | --- | +| 视频名称、所属单位、类型、等级、主管部门、在线状态 | 本地缓存字段展示,第三方接口负责刷新 | +| 所属区域、摄像头负责人、绑定时间 | 本地绑定字段 | + +列表操作:设置摄像头负责人、查看第三方摄像头详情、解除绑定。原型中的“删除”建议在业务文案中改为“解绑”,避免误解为删除第三方摄像头。 + +## 6. 接口建议 + +### 6.1 内部区域管理接口 + +| 能力 | 建议接口语义 | +| --- | --- | +| 区域树 | 按数据权限返回企业虚拟根和区域树 | +| 新增区域 | 新增顶级或下级区域 | +| 修改区域 | 修改名称、负责人、围栏和排序 | +| 删除区域 | 校验子区域和摄像头绑定后逻辑删除 | +| 区域摄像头分页 | 查询选中区域直接绑定的摄像头 | +| 可选摄像头分页 | 代理/调用第三方摄像头查询能力 | +| 保存摄像头绑定 | 批量计算并保存绑定差异 | +| 设置摄像头负责人 | 更新绑定记录负责人 | +| 解除摄像头绑定 | 逻辑删除本地绑定关系 | + +### 6.2 第三方区域开放接口 + +综合平台作为区域数据提供方,建议至少提供: + +- 区域分页查询。 +- 区域详情查询。 +- 区域树查询。 + +开放接口是否返回区域绑定摄像头需要第三方确认。建议区域和摄像头绑定分别返回,避免区域树报文过大: + +- `RegionCo`:区域自身信息、层级、负责人、围栏。 +- `RegionCameraCo`:区域 ID、第三方摄像头 ID、摄像头负责人。 + +第三方开放接口使用独立鉴权和数据范围,不直接复用页面登录权限。 + +## 7. DDD 落位 + +| 模块 | 内容 | +| --- | --- | +| `web-client` | Region、RegionCamera 的 Cmd、Qry、Co 和服务接口 | +| `web-adapter` | 区域管理 Controller、第三方区域开放接口 | +| `web-app` | 区域增删改查、组树、摄像头绑定和负责人设置执行器 | +| `web-domain` | `RegionE`、`RegionCameraBindingE`、树及围栏规则、Gateway | +| `web-infrastructure` | DO、Mapper、Repository、GatewayImpl、第三方摄像头 Facade 适配 | +| `start` | RPC Client 和配置装配;当前不需要推送定时任务 | + +第三方摄像头访问能力在 Domain 定义 Gateway,在 Infrastructure 通过 Dubbo 或 HTTP 实现。外部摄像头 DTO 不进入 Domain 和 Client。 + +## 8. 待确认问题 + +### 区域 + +- 是否需要区域编码,对外唯一标识采用 ID 还是编码? +- 顶级区域 `parent_id` 使用 `NULL` 还是 `0`? +- 区域最大层级是多少,是否允许移动节点? +- 区域负责人是否只能有一个? +- 坐标使用哪种坐标系?四点是否永远固定,还是后续允许任意多边形? +- 是否要求判断摄像头坐标或告警坐标是否处于区域内? +- 区域是否需要启用/停用状态? + +### 摄像头 + +- 摄像头第三方接口的系统、协议和 Common Facade 是什么? +- 摄像头唯一主键是什么,是否跨企业唯一? +- 是否支持分页及原型中的名称、等级、在线状态筛选? +- 一个摄像头是否只能绑定一个区域? +- 查询某区域时只展示直接绑定摄像头,还是包含所有子区域摄像头? +- 解绑后负责人是否随绑定记录一起失效? +- 第三方不可用时页面是报错,还是允许展示本地快照? + +### 开放接口 + +- 第三方只查询区域,还是同时查询区域摄像头绑定? +- 需要全量分页、区域树还是更新时间增量查询? +- 是否需要返回逻辑删除区域和已解除绑定关系? +- 鉴权、签名、IP 白名单及租户范围如何确定? + +## 9. 当前不开发内容 + +- 人员、企业、部门、岗位和人脸主数据维护。 +- 人员与区域准入关系。 +- 摄像头主数据维护和删除。 +- 主动推送、推送任务、回执和失败重试。 + +上一版人员推送 DDL 已删除。当前 `RegionManagementDDL.sql` 是根据原型形成的区域表草案;坐标系、摄像头唯一性和区域删除规则确认后,再作为正式迁移脚本执行。 + +## 10. 第三方报警接收与处置 + +### 10.1 接入方式 + +报警由第三方产生,建议综合平台提供报警接收回调接口,由第三方主动调用: + +```text +第三方 -> 鉴权/验签 -> 幂等校验 -> 报警落库 -> 返回受理成功 + └-> 用户后续处置 +``` + +以 `sourceSystem + alarmNo` 作为幂等键。第三方重复推送同一报警编号时返回已受理,不重复新增。接收接口只负责报警可靠落库,不同步执行隐患长流程。 + +第三方至少传摄像头和区域信息,本系统校验并固化企业、区域及摄像头历史快照。区域或摄像头无法识别时是否拒绝接收,待双方接口协议确定。 + +报警业务报文只要求当前确认字段:报警编号、报警时间、报警地址、区域、摄像头和一张报警截图。来源系统、接收时间及处置状态属于综合平台接入和管理字段,不要求第三方提供。 + +### 10.2 图片传输 + +当前暂定第三方在 JSON 报警报文中直接传单张截图 URL,综合平台在 `alarm_image_url` 中保存并用于详情展示,不接收文件流,也不保存 BLOB 或 Base64。 + +正式联调前需要确认 URL 是否长期有效、是否需要鉴权、是否允许综合平台服务端访问。如果 URL 是短期签名地址,应改为稳定文件 ID,或由综合平台接收后转存统一文件服务。 + +### 10.3 处置状态机 + +```text +PENDING(未处置) + ├─ 误报=是 -> DISPOSED + └─ 误报=否 + ├─ 隐患=否 -> 填写结果 -> DISPOSED + └─ 隐患=是 -> 创建隐患 -> DISPOSED +``` + +- 误报直接闭环,不进入隐患流程。 +- 非误报必须选择是否隐患。 +- 非隐患必须填写处置结果,提交后闭环。 +- 隐患调用 `ZcloudHiddenFacade.aiHiddenAndSave(HiddenAddCmd)`,以报警编号作为 `foreignKey`。 +- 隐患流程和进度以隐患服务为准,本地不再维护报警闭环状态。 +- 当前报警不经过派单、转办或审批,每条报警只提交一次处置结论;处置人、处置时间及公共更新审计字段用于留痕。 + +### 10.4 数据表 + +区域、摄像头绑定、报警和处置统一维护在 `web-infrastructure/src/main/resources/RegionManagementDDL.sql`,报警部分包含: + +| 表 | 用途 | +| --- | --- | +| `edgeguard_alarm` | 报警事实、区域/摄像头快照、当前状态、统计维度 | +| `edgeguard_alarm_disposal` | 是否误报、是否隐患、直接处置结果及关联隐患 ID | + +Common 已有 `ZcloudHiddenFacade` 的 `aiHiddenAndSave`、`listByForeignKey` 和 `queryHiddenCheckStatus` 等能力。本系统不复制隐患主表,也不缓存隐患状态、级别和进度。创建成功后只在报警处置表保存 `hidden_id`;详情页根据该 ID 调用隐患服务查询实时详情。 + +为处理隐患接口临时失败,处置表仅额外保存 `hidden_create_status` 和 `hidden_create_error`。它们属于调用可靠性字段,不是隐患业务数据。以报警编号作为 `foreignKey` 保证隐患创建幂等。 + +### 10.5 统计 + +一期直接聚合报警主表,不建统计结果表: + +- 股份端按 `corp_id` 统计各分公司报警总数、未处置和已处置。 +- 分公司端按 `region_id` 统计各区域报警总数、未处置和已处置。 +- 可统计误报数、隐患数和非隐患处置数。 + +报警保存发生时的企业、区域和摄像头快照,后续改名或移动不影响历史台账。数据量较大后再增加按日汇总表。 + +### 10.6 待确认 + +- 第三方报警接口的 JSON 报文、鉴权、签名及重试规则。 +- 第三方报警 ID 是否在来源系统内永久唯一。 +- 报警截图 URL 的有效期、访问鉴权和网络可达性。 +- 处置结果字典。 +- 误报是否仍需填写处置原因。 +- 隐患级别、确认人、整改人和期限由谁填写。 +- 是否允许修改处置结论、重新打开报警及是否需要审批。 + +### 10.7 报警查看权限升级 + +当前需求只控制报警列表的可见范围,不需要增加业务表: + +- 摄像头负责人从报警入库开始即可查看该摄像头产生的报警。 +- 报警保持 `PENDING` 且超过配置的升级时长后,所属区域负责人也可以查看。 +- 当前升级时长为 10 分钟,必须配置化,禁止硬编码在查询 SQL 中。 +- 超时基准使用综合平台 `received_time`,不使用第三方 `alarm_time`,避免延迟推送导致报警刚入库就被升级。 +- 报警处置后进入台账的查看范围按页面数据权限确定,不再依赖 10 分钟规则。 + +查询逻辑: + +```text +当前用户是摄像头负责人 +OR +当前用户是区域负责人 + AND disposal_status = PENDING + AND received_time <= 当前时间 - 配置分钟数 +``` + +摄像头负责人从 `edgeguard_region_camera.leader_user_id` 获取,区域负责人从 `edgeguard_region.leader_user_id` 获取。负责人发生变更后按当前负责人权限查询;如果未来要求报警产生时锁定负责人,再在报警表增加负责人快照字段。 + +本规则只表示“能够在列表看到”,不表示系统主动通知。若后续要求短信、消息中心或待办提醒,并需要记录发送结果和防止重复通知,再设计通知记录或升级标记。