From a4110138771d472ce9bb93d3c234438ef65cf57b Mon Sep 17 00:00:00 2001 From: JnyRoad Date: Tue, 8 Sep 2026 18:46:47 -0700 Subject: [PATCH] feat(lock): add unified doorlock integration development baseline --- README.md | 4 +- backend-modules.json | 32 + contracts/lock/v1/openapi.yaml | 318 ++ scripts/test-backend-modules.py | 2 +- smart-h5/docs/superpowers/doorlock/README.md | 2 +- .../doorlock/api-cutover-contract.md | 18 +- .../doorlock/page-and-state-contract.md | 13 +- .../doorlock/test-and-acceptance.md | 31 +- smart-h5/e2e/check-in.spec.ts | 15 +- smart-h5/e2e/dorm-lock.spec.ts | 174 + smart-h5/e2e/help-dorm-lock.spec.ts | 119 +- smart-h5/next.config.ts | 4 +- smart-h5/src/app/check-in/detail/page.tsx | 30 +- smart-h5/src/app/dorm/get-code/page.test.ts | 75 +- smart-h5/src/app/dorm/get-code/page.tsx | 120 +- smart-h5/src/app/dorm/lock/page.tsx | 153 +- smart-h5/src/features/dorm/api.test.ts | 115 + smart-h5/src/features/dorm/api.ts | 151 + .../features/dorm/lock-face-camera.test.ts | 9 +- .../src/features/dorm/lock-face-camera.tsx | 42 +- smart-h5/src/features/dorm/lock-state.test.ts | 614 +++ smart-h5/src/features/dorm/lock-state.ts | 772 ++++ smart-h5/src/lib/api/http.test.ts | 17 + smart-h5/src/lib/api/http.ts | 16 +- smart-module/README.md | 5 + smart-module/pom.xml | 2 + smart-module/smart-bridge-lock/README.md | 35 + .../docs/superpowers/doorlock/README.md | 8 +- smart-module/smart-bridge-lock/pom.xml | 96 + .../smart-bridge-lock-api/pom.xml | 25 + .../contract/AdjudicatedDispatchContext.java | 65 + .../contract/AuthorizationAdjudication.java | 8 + .../lock/api/contract/BridgeAcceptance.java | 73 + .../api/contract/BridgeCommandEnvelope.java | 432 +++ .../api/contract/BridgeCommandResult.java | 275 ++ .../BridgeCommandResultClassifier.java | 30 + .../api/contract/BridgeContractChecks.java | 28 + .../api/contract/BridgeReceiveStatus.java | 9 + .../lock/api/contract/CommandOperation.java | 12 + .../lock/api/contract/CommandResultType.java | 13 + .../bridge/lock/api/contract/CommandType.java | 13 + .../api/contract/DeviceEventEnvelope.java | 141 + .../lock/api/contract/DeviceEventType.java | 8 + .../lock/api/contract/DeviceOutcome.java | 7 + .../HistoricalRelationAdjudication.java | 8 + .../api/contract/MembershipAdjudication.java | 9 + .../bridge/lock/api/contract/SafeSummary.java | 33 + .../api/contract/WireTaskAssignmentState.java | 7 + .../smart-bridge-lock-biz/pom.xml | 30 + .../lock/command/BridgeAttemptStore.java | 22 + .../lock/command/BridgeCommandAttempt.java | 56 + .../lock/command/BridgeCommandChannel.java | 10 + .../command/BridgeCommandFingerprint.java | 104 + .../lock/command/BridgeCommandReceiver.java | 120 + .../lock/command/BridgeCommandReception.java | 22 + .../lock/command/BridgeCommandRoute.java | 64 + .../command/BridgeCommandRouteResolver.java | 8 + .../lock/command/BridgeDispatchMode.java | 8 + .../command/BridgeFencedRealDispatcher.java | 46 + .../BridgeInternalCommandAuthenticator.java | 15 + .../lock/command/BridgeServiceIdentity.java | 48 + .../lock/command/WireTaskIdGenerator.java | 6 + .../smart/bridge/lock/protocol/AsciiHex.java | 13 + .../lock/protocol/B8Acknowledgement.java | 114 + .../smart/bridge/lock/protocol/B8Result.java | 15 + .../lock/protocol/FingerprintAckContext.java | 148 + .../lock/protocol/FingerprintAckDecision.java | 34 + .../lock/protocol/FingerprintAckPolicy.java | 83 + .../lock/protocol/FingerprintPacket.java | 45 + .../lock/protocol/FingerprintPacketCodec.java | 85 + .../protocol/HardwareProtocolProfile.java | 68 + .../protocol/IncrementalJsonObjectFramer.java | 166 + .../bridge/lock/protocol/WireTaskId.java | 59 + .../bridge/lock/receipt/ReceiptDelivery.java | 8 + .../lock/receipt/ReceiptPublication.java | 99 + .../receipt/ReceiptPublicationResult.java | 20 + .../lock/receipt/ReceiptPublicationStore.java | 15 + .../bridge/lock/receipt/ReceiptPublisher.java | 51 + .../session/SingleActiveGatewaySession.java | 1097 ++++++ .../command/BridgeCommandReceiverTest.java | 215 ++ .../lock/protocol/LockProtocolCodecTest.java | 447 +++ .../lock/receipt/ReceiptPublisherTest.java | 92 + .../lock/session/SingleActiveSessionTest.java | 711 ++++ smart-module/smart-lock/README.md | 39 + .../docs/superpowers/doorlock/README.md | 7 +- .../doorlock/implementation-verification.md | 1088 ++++++ .../doorlock/local-test-resources.md | 124 + .../superpowers/doorlock/module-boundaries.md | 142 + .../doorlock/oracle-release-manifest.md | 193 + smart-module/smart-lock/pom.xml | 96 + .../smart-lock/smart-lock-api/pom.xml | 24 + .../tce/smart/lock/api/contract/ApiId.java | 60 + .../lock/api/contract/CommandStatus.java | 14 + .../lock/api/contract/ContractChecks.java | 28 + .../lock/api/contract/ContractErrorCode.java | 24 + .../contract/ContractViolationException.java | 23 + .../smart/lock/api/contract/GrantStatus.java | 10 + .../api/contract/IdempotencyClassifier.java | 23 + .../api/contract/IdempotencyDecision.java | 8 + .../lock/api/contract/ImmutablePayload.java | 80 + .../contract/LifecycleEventClassifier.java | 35 + .../contract/LifecycleEventDisposition.java | 10 + .../api/contract/LifecycleEventEnvelope.java | 380 ++ .../lock/api/contract/LifecycleEventType.java | 12 + .../lock/api/contract/NormalizedTarget.java | 91 + .../api/contract/OperatorAuthorization.java | 125 + .../lock/api/contract/PayloadDigest.java | 55 + .../api/contract/ScopedIdempotencyKey.java | 53 + .../smart/lock/api/contract/TargetKind.java | 9 + .../smart/lock/api/contract/WireValue.java | 93 + .../smart-lock/smart-lock-biz/pom.xml | 91 + .../smart/lock/alert/LockAlertService.java | 406 ++ .../BindingRecalculationCoordinator.java | 414 +++ .../smart/lock/asset/LockAssetService.java | 619 ++++ .../smart/lock/asset/RoomBindingService.java | 415 +++ .../lock/command/CommandIssuanceGate.java | 15 + .../command/CommandRecoveryBatchResult.java | 40 + .../command/CommandRecoveryCandidate.java | 40 + .../command/CommandRecoveryFactsResolver.java | 7 + .../command/CommandRecoveryManualReason.java | 9 + .../lock/command/CommandRecoveryPolicy.java | 718 ++++ .../CommandRecoveryProfileResolver.java | 7 + .../lock/command/CommandRecoveryRun.java | 27 + .../command/CommandRecoveryScheduler.java | 117 + .../lock/command/CommandRecoveryStore.java | 26 + .../lock/command/CommandSubmissionResult.java | 40 + .../lock/command/DeviceStatusReadiness.java | 7 + .../DeviceStatusReadinessResolver.java | 7 + .../smart/lock/command/LockCommandPlan.java | 60 + .../command/LockCommandRecordIdGenerator.java | 11 + .../lock/command/LockCommandService.java | 191 + .../smart/lock/command/LockCommandStore.java | 12 + .../lock/command/MybatisLockCommandStore.java | 31 + .../command/SelfPasswordAdmissionGate.java | 94 + .../command/SelfPasswordCommandService.java | 224 ++ .../CredentialAdministrationService.java | 607 +++ .../lock/grant/GrantConfirmationResult.java | 8 + .../grant/GrantCredentialConfirmation.java | 71 + .../lock/grant/GrantCredentialProjection.java | 237 ++ .../smart/lock/grant/GrantProjectionPlan.java | 135 + .../lock/grant/GrantProjectionService.java | 90 + .../lock/grant/GrantProjectionStore.java | 20 + .../grant/GrantProvisionPlanResolver.java | 13 + .../lock/grant/GrantRecordIdGenerator.java | 13 + .../grant/MybatisGrantProjectionStore.java | 318 ++ .../lifecycle/LifecycleConsumeResult.java | 30 + .../lifecycle/LifecycleEffectContext.java | 33 + .../lock/lifecycle/LifecycleEffectWriter.java | 12 + .../lifecycle/LifecycleEventConsumer.java | 567 +++ .../lifecycle/LifecycleOrderingPolicy.java | 226 ++ .../lifecycle/LifecycleRecordIdGenerator.java | 13 + .../lifecycle/LifecycleReferenceResolver.java | 14 + .../ResolvedLifecycleReferences.java | 71 + .../lock/persistence/AccessGrantMapper.java | 19 + .../lock/persistence/AccessGrantRecord.java | 88 + .../lock/persistence/ArchiveContentScope.java | 42 + .../lock/persistence/AuditEventMapper.java | 11 + .../lock/persistence/AuditEventRecord.java | 64 + .../lock/persistence/AuditObjectScope.java | 19 + .../BindingRecalcMembershipMapper.java | 12 + .../BindingRecalcMembershipRecord.java | 46 + .../persistence/BindingRecalcTaskMapper.java | 18 + .../persistence/BindingRecalcTaskRecord.java | 137 + .../BoundedRedactedClobTypeHandler.java | 40 + .../CanonicalLifecyclePayload.java | 176 + .../CanonicalPayloadTypeHandler.java | 37 + .../persistence/CiphertextTypeHandler.java | 23 + .../persistence/CommandAttemptMapper.java | 10 + .../persistence/CommandAttemptRecord.java | 121 + .../persistence/CommandIdempotencyScope.java | 151 + .../smart/lock/persistence/CommandLease.java | 31 + .../smart/lock/persistence/CommandMapper.java | 15 + .../persistence/CommandPersistenceChecks.java | 23 + .../smart/lock/persistence/CommandRecord.java | 313 ++ .../lock/persistence/ConsumerCursorScope.java | 29 + .../lock/persistence/CredentialMapper.java | 11 + .../lock/persistence/CredentialRecord.java | 91 + .../DeviceEventCanonicalScope.java | 22 + .../persistence/DeviceEventIdentityScope.java | 25 + .../lock/persistence/DeviceEventMapper.java | 23 + .../persistence/DeviceEventPromotion.java | 15 + .../lock/persistence/DeviceEventRecord.java | 110 + .../persistence/DeviceEventSourceScope.java | 21 + .../lock/persistence/DeviceGatewayMapper.java | 10 + .../lock/persistence/DeviceGatewayRecord.java | 61 + .../smart/lock/persistence/DeviceMapper.java | 14 + .../DeviceModelCapabilityMapper.java | 10 + .../DeviceModelCapabilityRecord.java | 82 + .../lock/persistence/DeviceModelMapper.java | 14 + .../lock/persistence/DeviceModelRecord.java | 41 + .../smart/lock/persistence/DeviceRecord.java | 78 + .../persistence/DeviceRoomBindingMapper.java | 10 + .../persistence/DeviceRoomBindingRecord.java | 70 + .../smart/lock/persistence/GatewayMapper.java | 11 + .../smart/lock/persistence/GatewayRecord.java | 74 + .../persistence/GrantCredentialMapper.java | 18 + .../persistence/GrantCredentialRecord.java | 83 + .../lock/persistence/GrantTargetMapper.java | 15 + .../lock/persistence/GrantTargetRecord.java | 69 + .../InboxCanonicalIdentityRecord.java | 17 + .../InboxCanonicalLookupScope.java | 32 + .../InboxConsumerCursorMapper.java | 11 + .../InboxConsumerCursorRecord.java | 48 + .../smart/lock/persistence/InboxMapper.java | 14 + .../smart/lock/persistence/InboxRecord.java | 91 + .../smart/lock/persistence/KeysetPage.java | 13 + .../lock/persistence/LegacyIdMapMapper.java | 12 + .../lock/persistence/LegacyIdMapRecord.java | 46 + .../persistence/LegacyRowArchiveMapper.java | 12 + .../persistence/LegacyRowArchiveRecord.java | 62 + .../persistence/LegacySourceIdentity.java | 55 + .../persistence/LifecycleAggregateScope.java | 29 + .../smart/lock/persistence/MaterialScope.java | 16 + .../persistence/MembershipClosedMapper.java | 10 + .../persistence/MembershipClosedRecord.java | 35 + .../lock/persistence/MembershipScope.java | 25 + .../persistence/MembershipStateMapper.java | 13 + .../persistence/MembershipStateRecord.java | 85 + .../persistence/MigrationBatchMapper.java | 13 + .../persistence/MigrationBatchRecord.java | 44 + .../lock/persistence/MigrationRowMapper.java | 13 + .../lock/persistence/MigrationRowRecord.java | 55 + .../MybatisMembershipPasswordStore.java | 119 + .../lock/persistence/NonceTypeHandler.java | 12 + .../persistence/OperationsAlertMapper.java | 15 + .../persistence/OperationsAlertRecord.java | 41 + .../persistence/Part3PersistenceChecks.java | 54 + .../PasswordVersionReservation.java | 21 + .../lock/persistence/PersistenceChecks.java | 63 + .../lock/persistence/PersistenceScope.java | 23 + .../persistence/ProtectedMaterialMapper.java | 9 + .../persistence/ProtectedMaterialRecord.java | 70 + .../smart/lock/persistence/RecalcLease.java | 23 + .../smart/lock/persistence/ReceiptMapper.java | 12 + .../smart/lock/persistence/ReceiptRecord.java | 112 + .../persistence/ReconciliationMapper.java | 13 + .../persistence/ReconciliationRecord.java | 60 + .../persistence/ReconciliationResolution.java | 25 + .../lock/persistence/RequestAggregation.java | 37 + .../lock/persistence/RequestLookupScope.java | 21 + .../tce/smart/lock/persistence/RowCas.java | 18 + .../smart/lock/persistence/ScopeProgress.java | 22 + .../lock/persistence/SelfRequestScope.java | 55 + .../persistence/SelfServiceRequestMapper.java | 16 + .../persistence/SelfServiceRequestRecord.java | 120 + .../smart/lock/persistence/SettingScope.java | 20 + .../lock/persistence/SystemSettingMapper.java | 14 + .../lock/persistence/SystemSettingRecord.java | 73 + .../lock/persistence/TargetSetFreeze.java | 31 + .../lock/persistence/TargetSetSnapshot.java | 115 + .../TargetSetSnapshotTypeHandler.java | 40 + .../persistence/TenantMembershipScope.java | 21 + .../smart/lock/persistence/TenantScope.java | 12 + .../persistence/UtcMicrosTypeHandler.java | 18 + .../persistence/VerificationConsumption.java | 37 + .../persistence/VerificationLookupScope.java | 27 + .../lock/query/LockHistoryQueryService.java | 330 ++ .../DeviceEventClaimTokenGenerator.java | 6 + .../receipt/DeviceEventConsumeResult.java | 23 + .../lock/receipt/DeviceEventConsumer.java | 100 + .../receipt/DeviceEventDeduplication.java | 169 + .../smart/lock/receipt/DeviceEventLedger.java | 15 + .../receipt/DeviceEventRecordIdGenerator.java | 6 + .../lock/receipt/DeviceEventReference.java | 81 + .../receipt/DeviceEventReferenceResolver.java | 8 + .../receipt/MybatisDeviceEventLedger.java | 220 ++ .../lock/receipt/ReceiptConsumeResult.java | 23 + .../smart/lock/receipt/ReceiptConsumer.java | 121 + .../lock/receipt/ReceiptCorrelation.java | 275 ++ .../tce/smart/lock/receipt/ReceiptLedger.java | 13 + .../smart/lock/receipt/ReceiptReference.java | 30 + .../receipt/ReceiptReferenceResolver.java | 8 + .../security/CredentialMaterialAccess.java | 914 +++++ .../security/LockServiceAuthenticator.java | 163 + .../lock/security/SelfLockVersionSigner.java | 145 + .../security/SelfPasswordIntentSigner.java | 128 + .../lock/security/SensitiveMaterial.java | 48 + .../lock/security/TrustedLockContext.java | 37 + .../doorlock/oracle/candidate-v1-part1.json | 2022 ++++++++++ .../doorlock/oracle/candidate-v1-part2.json | 3280 +++++++++++++++++ .../doorlock/oracle/candidate-v1-part3.json | 2399 ++++++++++++ .../resources/mapper/AccessGrantMapper.xml | 70 + .../resources/mapper/AuditEventMapper.xml | 37 + .../mapper/BindingRecalcMembershipMapper.xml | 80 + .../mapper/BindingRecalcTaskMapper.xml | 143 + .../resources/mapper/CommandAttemptMapper.xml | 71 + .../main/resources/mapper/CommandMapper.xml | 134 + .../resources/mapper/CredentialMapper.xml | 68 + .../resources/mapper/DeviceEventMapper.xml | 104 + .../resources/mapper/DeviceGatewayMapper.xml | 42 + .../main/resources/mapper/DeviceMapper.xml | 57 + .../mapper/DeviceModelCapabilityMapper.xml | 48 + .../resources/mapper/DeviceModelMapper.xml | 48 + .../mapper/DeviceRoomBindingMapper.xml | 43 + .../main/resources/mapper/GatewayMapper.xml | 48 + .../mapper/GrantCredentialMapper.xml | 73 + .../resources/mapper/GrantTargetMapper.xml | 58 + .../mapper/InboxConsumerCursorMapper.xml | 41 + .../src/main/resources/mapper/InboxMapper.xml | 63 + .../resources/mapper/LegacyIdMapMapper.xml | 60 + .../mapper/LegacyRowArchiveMapper.xml | 47 + .../mapper/MembershipClosedMapper.xml | 37 + .../mapper/MembershipStateMapper.xml | 70 + .../resources/mapper/MigrationBatchMapper.xml | 40 + .../resources/mapper/MigrationRowMapper.xml | 45 + .../mapper/OperationsAlertMapper.xml | 38 + .../mapper/ProtectedMaterialMapper.xml | 39 + .../main/resources/mapper/ReceiptMapper.xml | 81 + .../resources/mapper/ReconciliationMapper.xml | 83 + .../mapper/SelfServiceRequestMapper.xml | 130 + .../resources/mapper/SystemSettingMapper.xml | 38 + .../lock/alert/LockAlertServiceTest.java | 172 + .../lock/architecture/ModuleBoundaryTest.java | 514 +++ .../BindingRecalculationCoordinatorTest.java | 236 ++ .../lock/asset/LockAssetServiceTest.java | 333 ++ .../lock/asset/RoomBindingServiceTest.java | 185 + .../command/CommandRecoverySchedulerTest.java | 226 ++ .../lock/command/CommandRecoveryTest.java | 379 ++ .../lock/command/LockCommandServiceTest.java | 217 ++ .../LockCommandTransactionContractTest.java | 27 + .../SelfPasswordCommandServiceTest.java | 477 +++ .../contract/LockEnvelopeContractTest.java | 867 +++++ .../CredentialAdministrationServiceTest.java | 238 ++ .../grant/GrantCredentialProjectionTest.java | 290 ++ .../grant/GrantProjectionServiceTest.java | 178 + .../MybatisGrantProjectionStoreTest.java | 303 ++ .../lifecycle/LifecycleEventConsumerTest.java | 448 +++ .../lock/lifecycle/LifecycleOrderingTest.java | 219 ++ .../CanonicalIdentityBoundaryTest.java | 208 ++ .../CanonicalLifecyclePayloadTest.java | 65 + .../LifecycleCommandBehaviorTest.java | 385 ++ .../LifecycleCommandMapperTest.java | 58 + .../persistence/LifecyclePayloadLobTest.java | 57 + .../MembershipPasswordVersionTest.java | 105 + .../MybatisMembershipPasswordStoreTest.java | 181 + .../OracleBindingBehaviorTest.java | 251 ++ .../persistence/OracleDeclarationTest.java | 47 + .../OracleDmlScenarioProvider.java | 133 + .../persistence/OracleDmlScenarioRunner.java | 477 +++ .../persistence/OracleDmlScenarioTest.java | 190 + .../lock/persistence/OracleItContext.java | 81 + .../persistence/OracleItFoundationTest.java | 249 ++ .../persistence/OracleItSessionHarness.java | 109 + ...fecycleAuthorizationDmlScenarioRunner.java | 507 +++ ...LifecycleAuthorizationDmlScenarioTest.java | 40 + .../OracleLockMapperContractTest.java | 71 + .../persistence/OracleMapperContractTest.java | 68 + .../persistence/OracleMetadataAdapter.java | 17 + .../lock/persistence/OracleRunFixture.java | 94 + ...SelfMigrationArchiveDmlScenarioRunner.java | 515 +++ ...leSelfMigrationArchiveDmlScenarioTest.java | 84 + .../lock/persistence/OracleTestGate.java | 71 + .../lock/persistence/OracleTestGateTest.java | 53 + .../persistence/OracleTypeHandlerTest.java | 67 + .../persistence/Part3LobTypeHandlerTest.java | 77 + .../persistence/Part3MapperContractTest.java | 52 + .../Part3OracleDeclarationTest.java | 125 + .../Part3PersistenceBehaviorTest.java | 627 ++++ .../persistence/PersistenceBoundaryTest.java | 70 + .../query/LockHistoryQueryServiceTest.java | 131 + .../lock/receipt/DeviceEventConsumerTest.java | 118 + .../receipt/DeviceEventDeduplicationTest.java | 115 + .../receipt/MybatisDeviceEventLedgerTest.java | 177 + .../lock/receipt/ReceiptConsumerTest.java | 137 + .../lock/receipt/ReceiptCorrelationTest.java | 183 + .../security/LockTokenIntrospectionTest.java | 114 + .../security/SelfLockVersionSignerTest.java | 159 + .../SelfPasswordIntentSignerTest.java | 124 + .../lock/security/SensitiveMaterialTest.java | 703 ++++ .../lock/security/TrustedLockContextTest.java | 222 ++ .../smart-platform/smart-platform-biz/pom.xml | 6 + .../controller/LockAdminController.java | 343 ++ .../platform/controller/MyLockController.java | 312 ++ .../lock/AccommodationBindingSnapshot.java | 31 + .../lock/AccommodationDepartureRouter.java | 124 + .../lock/AccommodationEntrancePolicy.java | 275 ++ .../lock/AccommodationEventRecorder.java | 30 + .../platform/lock/AccommodationFacts.java | 52 + ...mmodationIdentityRegularizationRouter.java | 102 + ...AccommodationLifecycleEnvelopeFactory.java | 153 + .../lock/AccommodationLifecycleIntent.java | 19 + ...ccommodationLifecycleOutboxEntrypoint.java | 13 + .../lock/AccommodationLifecycleRouter.java | 222 ++ .../lock/AccommodationLifecycleSource.java | 64 + .../lock/AccommodationMembershipFacts.java | 158 + .../AccommodationMembershipFactsLedger.java | 194 + .../lock/AccommodationMembershipPhase.java | 4 + .../AccommodationOutboxConfiguration.java | 16 + .../lock/AccommodationOutboxEvent.java | 260 ++ .../lock/AccommodationOutboxStore.java | 18 + .../platform/lock/AccommodationPosition.java | 22 + .../lock/AccommodationScopeQueryService.java | 325 ++ .../platform/lock/AuthorizedLockAdmin.java | 71 + .../lock/ClaimedAccommodationOutboxEvent.java | 56 + ...dentityRegularizationOutboxEntrypoint.java | 12 + .../lock/IdentityRegularizationSource.java | 35 + .../lock/LifecycleEventDeliveryGateway.java | 14 + .../smart/platform/lock/LockAccessScope.java | 115 + .../lock/LockAdminAccessResolver.java | 19 + .../platform/lock/LockAdminApiContract.java | 441 +++ .../platform/lock/LockAdminDomainFacade.java | 12 + .../platform/lock/LockAdminInvocation.java | 99 + .../platform/lock/LockApiHttpAdvice.java | 77 + .../lock/LockOutboxDeliveryStore.java | 20 + .../platform/lock/LockOutboxPublisher.java | 129 + .../platform/lock/LockSelfAccessResolver.java | 230 ++ .../platform/lock/MyLockApiContract.java | 356 ++ .../platform/lock/MyLockDomainFacade.java | 25 + .../smart/platform/lock/MyLockInvocation.java | 95 + .../tce/smart/platform/lock/MyLockStatus.java | 56 + .../lock/MyLockVerificationFacade.java | 13 + .../lock/MyLockVerificationInvocation.java | 63 + .../lock/MyLockVersionConflictException.java | 6 + .../platform/lock/OutboxClaimRequest.java | 49 + .../platform/lock/OutboxDeliveryStatus.java | 13 + .../platform/lock/OutboxRetryPolicy.java | 55 + ...ccommodationMembershipFactsRepository.java | 11 + ...rustedAccommodationMembershipSnapshot.java | 6 + .../AccommodationOutboxDeliveryMapper.java | 33 + .../AccommodationOutboxDeliveryRow.java | 99 + .../AccommodationOutboxEntity.java | 159 + .../AccommodationOutboxMapper.java | 26 + .../lock/persistence/OutboxEventKey.java | 12 + ...TransactionalAccommodationOutboxStore.java | 75 + .../TransactionalOutboxDeliveryStore.java | 99 + .../impl/SmtDormitoryBedServiceImpl.java | 21 +- .../impl/SmtDormitoryRoomServiceImpl.java | 21 +- .../impl/SmtDormitoryStaffServiceImpl.java | 25 +- .../impl/SmtLeaveHandoverServiceImpl.java | 5 +- .../impl/SmtOutDormitoryStaffServiceImpl.java | 69 +- .../service/impl/SmtStaffServiceImpl.java | 7 +- .../AccommodationOutboxDeliveryMapper.xml | 130 + .../mapper/AccommodationOutboxMapper.xml | 123 + .../controller/LockAdminControllerTest.java | 189 + .../controller/MyLockControllerTest.java | 324 ++ .../AccommodationDepartureRouterTest.java | 111 + .../lock/AccommodationEntrancesTest.java | 360 ++ ...ationIdentityRegularizationRouterTest.java | 118 + .../AccommodationLifecycleRouterTest.java | 165 + .../AccommodationMembershipFactsTest.java | 205 ++ ...ommodationOutboxOracleIntegrationTest.java | 107 + .../AccommodationOutboxPersistenceTest.java | 215 ++ .../lock/AccommodationOutboxTest.java | 131 + .../AccommodationScopeQueryServiceTest.java | 107 + .../platform/lock/LockAccessScopeTest.java | 151 + .../lock/LockAdminApiContractTest.java | 166 + .../platform/lock/LockApiHttpAdviceTest.java | 16 + .../lock/LockOpenApiContractTest.java | 89 + .../lock/LockOutboxPublisherTest.java | 345 ++ .../lock/LockSelfAccessResolverTest.java | 306 ++ .../platform/lock/MyLockApiContractTest.java | 196 + .../smart/platform/lock/MyLockStatusTest.java | 43 + .../lock/OutboxDeliveryPersistenceTest.java | 162 + .../core/mapper/SmtLeaveHandoverMapper.java | 7 - .../mapper/SmtLeaveHandoverMapper.xml | 3 - smart-ui/docs/superpowers/doorlock/README.md | 3 +- .../superpowers/doorlock/parity-acceptance.md | 303 +- .../doorlock/permission-and-route-map.md | 45 + .../surfaces/WEB-L-004-person-credentials.md | 12 +- .../surfaces/WEB-L-005-authorizations.md | 29 +- .../surfaces/WEB-L-006-delivery-results.md | 23 +- .../surfaces/WEB-L-007-records-and-logs.md | 12 +- .../surfaces/WEB-L-008-alarms-settings.md | 10 +- .../scripts/doorlock-browser-smoke/.gitignore | 4 + .../scripts/doorlock-browser-smoke/README.md | 36 + .../scripts/doorlock-browser-smoke/index.html | 28 + .../doorlock-browser-smoke/network-policy.mjs | 53 + .../network-policy.test.mjs | 47 + .../doorlock-browser-smoke/run-result.mjs | 131 + .../run-result.test.mjs | 117 + .../scripts/doorlock-browser-smoke/smoke.mjs | 459 +++ .../doorlock-browser-smoke/src/App.vue | 89 + .../doorlock-browser-smoke/src/main.js | 20 + .../doorlock-browser-smoke/src/smoke.css | 56 + .../src/synthetic-api.js | 314 ++ .../doorlock-browser-smoke/vite.config.mjs | 49 + smart-ui/src/api/lock/admin-contract.js | 236 ++ smart-ui/src/api/lock/admin-contract.test.js | 212 ++ smart-ui/src/api/lock/index.js | 404 ++ smart-ui/src/api/lock/index.test.js | 308 ++ .../src/router/avue-router.contract.test.js | 78 +- smart-ui/src/router/avue-router.js | 3 +- smart-ui/src/router/axios.js | 4 +- smart-ui/src/router/lock-menu-components.js | 28 + .../src/router/lock-menu-components.test.js | 53 + .../lock/__tests__/assets-parity.test.js | 120 + .../lock/__tests__/operations-parity.test.js | 147 + .../lock/alarms/alarm-page-controller.js | 210 ++ .../src/views/lock/alarms/alarm-page-rules.js | 322 ++ .../lock/alarms/alarm-page-rules.test.js | 322 ++ .../lock/alarms/alarm-settings-controller.js | 154 + smart-ui/src/views/lock/alarms/index.test.js | 227 ++ smart-ui/src/views/lock/alarms/index.vue | 222 ++ .../src/views/lock/alarms/settings.test.js | 243 ++ smart-ui/src/views/lock/alarms/settings.vue | 120 + smart-ui/src/views/lock/assets-parity.js | 338 ++ .../views/lock/commands/command-page-rules.js | 150 + .../lock/commands/command-page-rules.test.js | 68 + .../src/views/lock/commands/index.test.js | 114 + smart-ui/src/views/lock/commands/index.vue | 358 ++ .../lock/credentials/credential-page-rules.js | 126 + .../credentials/credential-page-rules.test.js | 84 + .../src/views/lock/credentials/index.test.js | 105 + smart-ui/src/views/lock/credentials/index.vue | 326 ++ .../views/lock/devices/device-page-rules.js | 56 + .../lock/devices/device-page-rules.test.js | 40 + smart-ui/src/views/lock/devices/index.test.js | 121 + smart-ui/src/views/lock/devices/index.vue | 209 ++ .../views/lock/gateways/gateway-page-rules.js | 79 + .../lock/gateways/gateway-page-rules.test.js | 60 + .../src/views/lock/gateways/index.test.js | 98 + smart-ui/src/views/lock/gateways/index.vue | 386 ++ .../src/views/lock/grants/grant-page-rules.js | 164 + .../lock/grants/grant-page-rules.test.js | 93 + smart-ui/src/views/lock/grants/index.test.js | 161 + smart-ui/src/views/lock/grants/index.vue | 453 +++ .../src/views/lock/list/asset-page-rules.js | 91 + .../views/lock/list/asset-page-rules.test.js | 57 + smart-ui/src/views/lock/list/detail.test.js | 127 + smart-ui/src/views/lock/list/detail.vue | 392 ++ smart-ui/src/views/lock/list/index.test.js | 128 + smart-ui/src/views/lock/list/index.vue | 425 +++ smart-ui/src/views/lock/models/index.test.js | 82 + smart-ui/src/views/lock/models/index.vue | 244 ++ .../src/views/lock/models/model-page-rules.js | 58 + .../lock/models/model-page-rules.test.js | 42 + smart-ui/src/views/lock/operations-parity.js | 356 ++ .../src/views/lock/records/communication.vue | 6 + smart-ui/src/views/lock/records/index.test.js | 164 + smart-ui/src/views/lock/records/index.vue | 193 + smart-ui/src/views/lock/records/open.vue | 6 + smart-ui/src/views/lock/records/password.vue | 6 + .../lock/records/record-page-controller.js | 112 + .../views/lock/records/record-page-rules.js | 78 + .../lock/records/record-page-rules.test.js | 36 + .../config/ClientApiGatewayConfiguration.java | 4 + .../filter/LockDownstreamRouteGuard.java | 47 + .../filter/LockRouteIsolationFilter.java | 157 + .../smart/gateway/LockRouteIsolationTest.java | 208 ++ .../contracts/lifecycle-and-device.md | 73 +- specs/011-doorlock-oracle/contracts/web-h5.md | 72 +- specs/011-doorlock-oracle/data-model.md | 318 +- .../011-doorlock-oracle/migration-mapping.md | 16 +- specs/011-doorlock-oracle/oracle-baseline.md | 10 +- specs/011-doorlock-oracle/plan.md | 78 +- specs/011-doorlock-oracle/quickstart.md | 29 +- specs/011-doorlock-oracle/research.md | 11 + specs/011-doorlock-oracle/spec.md | 10 +- specs/011-doorlock-oracle/tasks.md | 202 +- specs/011-doorlock-oracle/validation.md | 2 +- version-plan.json | 12 + 551 files changed, 68342 insertions(+), 526 deletions(-) create mode 100644 contracts/lock/v1/openapi.yaml create mode 100644 smart-h5/e2e/dorm-lock.spec.ts create mode 100644 smart-h5/src/features/dorm/api.test.ts create mode 100644 smart-h5/src/features/dorm/lock-state.test.ts create mode 100644 smart-h5/src/features/dorm/lock-state.ts create mode 100644 smart-module/smart-bridge-lock/README.md create mode 100644 smart-module/smart-bridge-lock/pom.xml create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-api/pom.xml create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-api/src/main/java/com/tce/smart/bridge/lock/api/contract/AdjudicatedDispatchContext.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-api/src/main/java/com/tce/smart/bridge/lock/api/contract/AuthorizationAdjudication.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-api/src/main/java/com/tce/smart/bridge/lock/api/contract/BridgeAcceptance.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-api/src/main/java/com/tce/smart/bridge/lock/api/contract/BridgeCommandEnvelope.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-api/src/main/java/com/tce/smart/bridge/lock/api/contract/BridgeCommandResult.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-api/src/main/java/com/tce/smart/bridge/lock/api/contract/BridgeCommandResultClassifier.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-api/src/main/java/com/tce/smart/bridge/lock/api/contract/BridgeContractChecks.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-api/src/main/java/com/tce/smart/bridge/lock/api/contract/BridgeReceiveStatus.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-api/src/main/java/com/tce/smart/bridge/lock/api/contract/CommandOperation.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-api/src/main/java/com/tce/smart/bridge/lock/api/contract/CommandResultType.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-api/src/main/java/com/tce/smart/bridge/lock/api/contract/CommandType.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-api/src/main/java/com/tce/smart/bridge/lock/api/contract/DeviceEventEnvelope.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-api/src/main/java/com/tce/smart/bridge/lock/api/contract/DeviceEventType.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-api/src/main/java/com/tce/smart/bridge/lock/api/contract/DeviceOutcome.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-api/src/main/java/com/tce/smart/bridge/lock/api/contract/HistoricalRelationAdjudication.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-api/src/main/java/com/tce/smart/bridge/lock/api/contract/MembershipAdjudication.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-api/src/main/java/com/tce/smart/bridge/lock/api/contract/SafeSummary.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-api/src/main/java/com/tce/smart/bridge/lock/api/contract/WireTaskAssignmentState.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-biz/pom.xml create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-biz/src/main/java/com/tce/smart/bridge/lock/command/BridgeAttemptStore.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-biz/src/main/java/com/tce/smart/bridge/lock/command/BridgeCommandAttempt.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-biz/src/main/java/com/tce/smart/bridge/lock/command/BridgeCommandChannel.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-biz/src/main/java/com/tce/smart/bridge/lock/command/BridgeCommandFingerprint.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-biz/src/main/java/com/tce/smart/bridge/lock/command/BridgeCommandReceiver.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-biz/src/main/java/com/tce/smart/bridge/lock/command/BridgeCommandReception.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-biz/src/main/java/com/tce/smart/bridge/lock/command/BridgeCommandRoute.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-biz/src/main/java/com/tce/smart/bridge/lock/command/BridgeCommandRouteResolver.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-biz/src/main/java/com/tce/smart/bridge/lock/command/BridgeDispatchMode.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-biz/src/main/java/com/tce/smart/bridge/lock/command/BridgeFencedRealDispatcher.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-biz/src/main/java/com/tce/smart/bridge/lock/command/BridgeInternalCommandAuthenticator.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-biz/src/main/java/com/tce/smart/bridge/lock/command/BridgeServiceIdentity.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-biz/src/main/java/com/tce/smart/bridge/lock/command/WireTaskIdGenerator.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-biz/src/main/java/com/tce/smart/bridge/lock/protocol/AsciiHex.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-biz/src/main/java/com/tce/smart/bridge/lock/protocol/B8Acknowledgement.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-biz/src/main/java/com/tce/smart/bridge/lock/protocol/B8Result.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-biz/src/main/java/com/tce/smart/bridge/lock/protocol/FingerprintAckContext.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-biz/src/main/java/com/tce/smart/bridge/lock/protocol/FingerprintAckDecision.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-biz/src/main/java/com/tce/smart/bridge/lock/protocol/FingerprintAckPolicy.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-biz/src/main/java/com/tce/smart/bridge/lock/protocol/FingerprintPacket.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-biz/src/main/java/com/tce/smart/bridge/lock/protocol/FingerprintPacketCodec.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-biz/src/main/java/com/tce/smart/bridge/lock/protocol/HardwareProtocolProfile.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-biz/src/main/java/com/tce/smart/bridge/lock/protocol/IncrementalJsonObjectFramer.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-biz/src/main/java/com/tce/smart/bridge/lock/protocol/WireTaskId.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-biz/src/main/java/com/tce/smart/bridge/lock/receipt/ReceiptDelivery.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-biz/src/main/java/com/tce/smart/bridge/lock/receipt/ReceiptPublication.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-biz/src/main/java/com/tce/smart/bridge/lock/receipt/ReceiptPublicationResult.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-biz/src/main/java/com/tce/smart/bridge/lock/receipt/ReceiptPublicationStore.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-biz/src/main/java/com/tce/smart/bridge/lock/receipt/ReceiptPublisher.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-biz/src/main/java/com/tce/smart/bridge/lock/session/SingleActiveGatewaySession.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-biz/src/test/java/com/tce/smart/bridge/lock/command/BridgeCommandReceiverTest.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-biz/src/test/java/com/tce/smart/bridge/lock/protocol/LockProtocolCodecTest.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-biz/src/test/java/com/tce/smart/bridge/lock/receipt/ReceiptPublisherTest.java create mode 100644 smart-module/smart-bridge-lock/smart-bridge-lock-biz/src/test/java/com/tce/smart/bridge/lock/session/SingleActiveSessionTest.java create mode 100644 smart-module/smart-lock/README.md create mode 100644 smart-module/smart-lock/docs/superpowers/doorlock/implementation-verification.md create mode 100644 smart-module/smart-lock/docs/superpowers/doorlock/local-test-resources.md create mode 100644 smart-module/smart-lock/docs/superpowers/doorlock/module-boundaries.md create mode 100644 smart-module/smart-lock/docs/superpowers/doorlock/oracle-release-manifest.md create mode 100644 smart-module/smart-lock/pom.xml create mode 100644 smart-module/smart-lock/smart-lock-api/pom.xml create mode 100644 smart-module/smart-lock/smart-lock-api/src/main/java/com/tce/smart/lock/api/contract/ApiId.java create mode 100644 smart-module/smart-lock/smart-lock-api/src/main/java/com/tce/smart/lock/api/contract/CommandStatus.java create mode 100644 smart-module/smart-lock/smart-lock-api/src/main/java/com/tce/smart/lock/api/contract/ContractChecks.java create mode 100644 smart-module/smart-lock/smart-lock-api/src/main/java/com/tce/smart/lock/api/contract/ContractErrorCode.java create mode 100644 smart-module/smart-lock/smart-lock-api/src/main/java/com/tce/smart/lock/api/contract/ContractViolationException.java create mode 100644 smart-module/smart-lock/smart-lock-api/src/main/java/com/tce/smart/lock/api/contract/GrantStatus.java create mode 100644 smart-module/smart-lock/smart-lock-api/src/main/java/com/tce/smart/lock/api/contract/IdempotencyClassifier.java create mode 100644 smart-module/smart-lock/smart-lock-api/src/main/java/com/tce/smart/lock/api/contract/IdempotencyDecision.java create mode 100644 smart-module/smart-lock/smart-lock-api/src/main/java/com/tce/smart/lock/api/contract/ImmutablePayload.java create mode 100644 smart-module/smart-lock/smart-lock-api/src/main/java/com/tce/smart/lock/api/contract/LifecycleEventClassifier.java create mode 100644 smart-module/smart-lock/smart-lock-api/src/main/java/com/tce/smart/lock/api/contract/LifecycleEventDisposition.java create mode 100644 smart-module/smart-lock/smart-lock-api/src/main/java/com/tce/smart/lock/api/contract/LifecycleEventEnvelope.java create mode 100644 smart-module/smart-lock/smart-lock-api/src/main/java/com/tce/smart/lock/api/contract/LifecycleEventType.java create mode 100644 smart-module/smart-lock/smart-lock-api/src/main/java/com/tce/smart/lock/api/contract/NormalizedTarget.java create mode 100644 smart-module/smart-lock/smart-lock-api/src/main/java/com/tce/smart/lock/api/contract/OperatorAuthorization.java create mode 100644 smart-module/smart-lock/smart-lock-api/src/main/java/com/tce/smart/lock/api/contract/PayloadDigest.java create mode 100644 smart-module/smart-lock/smart-lock-api/src/main/java/com/tce/smart/lock/api/contract/ScopedIdempotencyKey.java create mode 100644 smart-module/smart-lock/smart-lock-api/src/main/java/com/tce/smart/lock/api/contract/TargetKind.java create mode 100644 smart-module/smart-lock/smart-lock-api/src/main/java/com/tce/smart/lock/api/contract/WireValue.java create mode 100644 smart-module/smart-lock/smart-lock-biz/pom.xml create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/alert/LockAlertService.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/asset/BindingRecalculationCoordinator.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/asset/LockAssetService.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/asset/RoomBindingService.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/command/CommandIssuanceGate.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/command/CommandRecoveryBatchResult.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/command/CommandRecoveryCandidate.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/command/CommandRecoveryFactsResolver.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/command/CommandRecoveryManualReason.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/command/CommandRecoveryPolicy.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/command/CommandRecoveryProfileResolver.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/command/CommandRecoveryRun.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/command/CommandRecoveryScheduler.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/command/CommandRecoveryStore.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/command/CommandSubmissionResult.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/command/DeviceStatusReadiness.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/command/DeviceStatusReadinessResolver.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/command/LockCommandPlan.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/command/LockCommandRecordIdGenerator.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/command/LockCommandService.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/command/LockCommandStore.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/command/MybatisLockCommandStore.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/command/SelfPasswordAdmissionGate.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/command/SelfPasswordCommandService.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/credential/CredentialAdministrationService.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/grant/GrantConfirmationResult.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/grant/GrantCredentialConfirmation.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/grant/GrantCredentialProjection.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/grant/GrantProjectionPlan.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/grant/GrantProjectionService.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/grant/GrantProjectionStore.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/grant/GrantProvisionPlanResolver.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/grant/GrantRecordIdGenerator.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/grant/MybatisGrantProjectionStore.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/lifecycle/LifecycleConsumeResult.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/lifecycle/LifecycleEffectContext.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/lifecycle/LifecycleEffectWriter.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/lifecycle/LifecycleEventConsumer.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/lifecycle/LifecycleOrderingPolicy.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/lifecycle/LifecycleRecordIdGenerator.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/lifecycle/LifecycleReferenceResolver.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/lifecycle/ResolvedLifecycleReferences.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/AccessGrantMapper.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/AccessGrantRecord.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/ArchiveContentScope.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/AuditEventMapper.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/AuditEventRecord.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/AuditObjectScope.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/BindingRecalcMembershipMapper.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/BindingRecalcMembershipRecord.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/BindingRecalcTaskMapper.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/BindingRecalcTaskRecord.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/BoundedRedactedClobTypeHandler.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/CanonicalLifecyclePayload.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/CanonicalPayloadTypeHandler.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/CiphertextTypeHandler.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/CommandAttemptMapper.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/CommandAttemptRecord.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/CommandIdempotencyScope.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/CommandLease.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/CommandMapper.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/CommandPersistenceChecks.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/CommandRecord.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/ConsumerCursorScope.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/CredentialMapper.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/CredentialRecord.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/DeviceEventCanonicalScope.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/DeviceEventIdentityScope.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/DeviceEventMapper.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/DeviceEventPromotion.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/DeviceEventRecord.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/DeviceEventSourceScope.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/DeviceGatewayMapper.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/DeviceGatewayRecord.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/DeviceMapper.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/DeviceModelCapabilityMapper.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/DeviceModelCapabilityRecord.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/DeviceModelMapper.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/DeviceModelRecord.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/DeviceRecord.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/DeviceRoomBindingMapper.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/DeviceRoomBindingRecord.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/GatewayMapper.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/GatewayRecord.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/GrantCredentialMapper.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/GrantCredentialRecord.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/GrantTargetMapper.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/GrantTargetRecord.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/InboxCanonicalIdentityRecord.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/InboxCanonicalLookupScope.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/InboxConsumerCursorMapper.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/InboxConsumerCursorRecord.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/InboxMapper.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/InboxRecord.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/KeysetPage.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/LegacyIdMapMapper.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/LegacyIdMapRecord.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/LegacyRowArchiveMapper.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/LegacyRowArchiveRecord.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/LegacySourceIdentity.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/LifecycleAggregateScope.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/MaterialScope.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/MembershipClosedMapper.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/MembershipClosedRecord.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/MembershipScope.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/MembershipStateMapper.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/MembershipStateRecord.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/MigrationBatchMapper.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/MigrationBatchRecord.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/MigrationRowMapper.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/MigrationRowRecord.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/MybatisMembershipPasswordStore.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/NonceTypeHandler.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/OperationsAlertMapper.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/OperationsAlertRecord.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/Part3PersistenceChecks.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/PasswordVersionReservation.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/PersistenceChecks.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/PersistenceScope.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/ProtectedMaterialMapper.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/ProtectedMaterialRecord.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/RecalcLease.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/ReceiptMapper.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/ReceiptRecord.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/ReconciliationMapper.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/ReconciliationRecord.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/ReconciliationResolution.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/RequestAggregation.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/RequestLookupScope.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/RowCas.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/ScopeProgress.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/SelfRequestScope.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/SelfServiceRequestMapper.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/SelfServiceRequestRecord.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/SettingScope.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/SystemSettingMapper.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/SystemSettingRecord.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/TargetSetFreeze.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/TargetSetSnapshot.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/TargetSetSnapshotTypeHandler.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/TenantMembershipScope.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/TenantScope.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/UtcMicrosTypeHandler.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/VerificationConsumption.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/persistence/VerificationLookupScope.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/query/LockHistoryQueryService.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/receipt/DeviceEventClaimTokenGenerator.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/receipt/DeviceEventConsumeResult.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/receipt/DeviceEventConsumer.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/receipt/DeviceEventDeduplication.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/receipt/DeviceEventLedger.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/receipt/DeviceEventRecordIdGenerator.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/receipt/DeviceEventReference.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/receipt/DeviceEventReferenceResolver.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/receipt/MybatisDeviceEventLedger.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/receipt/ReceiptConsumeResult.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/receipt/ReceiptConsumer.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/receipt/ReceiptCorrelation.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/receipt/ReceiptLedger.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/receipt/ReceiptReference.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/receipt/ReceiptReferenceResolver.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/security/CredentialMaterialAccess.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/security/LockServiceAuthenticator.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/security/SelfLockVersionSigner.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/security/SelfPasswordIntentSigner.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/security/SensitiveMaterial.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/java/com/tce/smart/lock/security/TrustedLockContext.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/resources/doorlock/oracle/candidate-v1-part1.json create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/resources/doorlock/oracle/candidate-v1-part2.json create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/resources/doorlock/oracle/candidate-v1-part3.json create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/resources/mapper/AccessGrantMapper.xml create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/resources/mapper/AuditEventMapper.xml create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/resources/mapper/BindingRecalcMembershipMapper.xml create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/resources/mapper/BindingRecalcTaskMapper.xml create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/resources/mapper/CommandAttemptMapper.xml create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/resources/mapper/CommandMapper.xml create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/resources/mapper/CredentialMapper.xml create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/resources/mapper/DeviceEventMapper.xml create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/resources/mapper/DeviceGatewayMapper.xml create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/resources/mapper/DeviceMapper.xml create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/resources/mapper/DeviceModelCapabilityMapper.xml create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/resources/mapper/DeviceModelMapper.xml create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/resources/mapper/DeviceRoomBindingMapper.xml create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/resources/mapper/GatewayMapper.xml create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/resources/mapper/GrantCredentialMapper.xml create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/resources/mapper/GrantTargetMapper.xml create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/resources/mapper/InboxConsumerCursorMapper.xml create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/resources/mapper/InboxMapper.xml create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/resources/mapper/LegacyIdMapMapper.xml create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/resources/mapper/LegacyRowArchiveMapper.xml create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/resources/mapper/MembershipClosedMapper.xml create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/resources/mapper/MembershipStateMapper.xml create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/resources/mapper/MigrationBatchMapper.xml create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/resources/mapper/MigrationRowMapper.xml create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/resources/mapper/OperationsAlertMapper.xml create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/resources/mapper/ProtectedMaterialMapper.xml create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/resources/mapper/ReceiptMapper.xml create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/resources/mapper/ReconciliationMapper.xml create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/resources/mapper/SelfServiceRequestMapper.xml create mode 100644 smart-module/smart-lock/smart-lock-biz/src/main/resources/mapper/SystemSettingMapper.xml create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/alert/LockAlertServiceTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/architecture/ModuleBoundaryTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/asset/BindingRecalculationCoordinatorTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/asset/LockAssetServiceTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/asset/RoomBindingServiceTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/command/CommandRecoverySchedulerTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/command/CommandRecoveryTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/command/LockCommandServiceTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/command/LockCommandTransactionContractTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/command/SelfPasswordCommandServiceTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/contract/LockEnvelopeContractTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/credential/CredentialAdministrationServiceTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/grant/GrantCredentialProjectionTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/grant/GrantProjectionServiceTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/grant/MybatisGrantProjectionStoreTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/lifecycle/LifecycleEventConsumerTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/lifecycle/LifecycleOrderingTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/persistence/CanonicalIdentityBoundaryTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/persistence/CanonicalLifecyclePayloadTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/persistence/LifecycleCommandBehaviorTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/persistence/LifecycleCommandMapperTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/persistence/LifecyclePayloadLobTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/persistence/MembershipPasswordVersionTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/persistence/MybatisMembershipPasswordStoreTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/persistence/OracleBindingBehaviorTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/persistence/OracleDeclarationTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/persistence/OracleDmlScenarioProvider.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/persistence/OracleDmlScenarioRunner.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/persistence/OracleDmlScenarioTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/persistence/OracleItContext.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/persistence/OracleItFoundationTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/persistence/OracleItSessionHarness.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/persistence/OracleLifecycleAuthorizationDmlScenarioRunner.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/persistence/OracleLifecycleAuthorizationDmlScenarioTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/persistence/OracleLockMapperContractTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/persistence/OracleMapperContractTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/persistence/OracleMetadataAdapter.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/persistence/OracleRunFixture.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/persistence/OracleSelfMigrationArchiveDmlScenarioRunner.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/persistence/OracleSelfMigrationArchiveDmlScenarioTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/persistence/OracleTestGate.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/persistence/OracleTestGateTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/persistence/OracleTypeHandlerTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/persistence/Part3LobTypeHandlerTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/persistence/Part3MapperContractTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/persistence/Part3OracleDeclarationTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/persistence/Part3PersistenceBehaviorTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/persistence/PersistenceBoundaryTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/query/LockHistoryQueryServiceTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/receipt/DeviceEventConsumerTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/receipt/DeviceEventDeduplicationTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/receipt/MybatisDeviceEventLedgerTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/receipt/ReceiptConsumerTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/receipt/ReceiptCorrelationTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/security/LockTokenIntrospectionTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/security/SelfLockVersionSignerTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/security/SelfPasswordIntentSignerTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/security/SensitiveMaterialTest.java create mode 100644 smart-module/smart-lock/smart-lock-biz/src/test/java/com/tce/smart/lock/security/TrustedLockContextTest.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/controller/LockAdminController.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/controller/MyLockController.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/AccommodationBindingSnapshot.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/AccommodationDepartureRouter.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/AccommodationEntrancePolicy.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/AccommodationEventRecorder.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/AccommodationFacts.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/AccommodationIdentityRegularizationRouter.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/AccommodationLifecycleEnvelopeFactory.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/AccommodationLifecycleIntent.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/AccommodationLifecycleOutboxEntrypoint.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/AccommodationLifecycleRouter.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/AccommodationLifecycleSource.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/AccommodationMembershipFacts.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/AccommodationMembershipFactsLedger.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/AccommodationMembershipPhase.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/AccommodationOutboxConfiguration.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/AccommodationOutboxEvent.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/AccommodationOutboxStore.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/AccommodationPosition.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/AccommodationScopeQueryService.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/AuthorizedLockAdmin.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/ClaimedAccommodationOutboxEvent.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/IdentityRegularizationOutboxEntrypoint.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/IdentityRegularizationSource.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/LifecycleEventDeliveryGateway.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/LockAccessScope.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/LockAdminAccessResolver.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/LockAdminApiContract.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/LockAdminDomainFacade.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/LockAdminInvocation.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/LockApiHttpAdvice.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/LockOutboxDeliveryStore.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/LockOutboxPublisher.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/LockSelfAccessResolver.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/MyLockApiContract.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/MyLockDomainFacade.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/MyLockInvocation.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/MyLockStatus.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/MyLockVerificationFacade.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/MyLockVerificationInvocation.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/MyLockVersionConflictException.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/OutboxClaimRequest.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/OutboxDeliveryStatus.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/OutboxRetryPolicy.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/TrustedAccommodationMembershipFactsRepository.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/TrustedAccommodationMembershipSnapshot.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/persistence/AccommodationOutboxDeliveryMapper.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/persistence/AccommodationOutboxDeliveryRow.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/persistence/AccommodationOutboxEntity.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/persistence/AccommodationOutboxMapper.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/persistence/OutboxEventKey.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/persistence/TransactionalAccommodationOutboxStore.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/java/com/tce/smart/platform/lock/persistence/TransactionalOutboxDeliveryStore.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/resources/mapper/AccommodationOutboxDeliveryMapper.xml create mode 100644 smart-module/smart-platform/smart-platform-biz/src/main/resources/mapper/AccommodationOutboxMapper.xml create mode 100644 smart-module/smart-platform/smart-platform-biz/src/test/java/com/tce/smart/platform/controller/LockAdminControllerTest.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/test/java/com/tce/smart/platform/controller/MyLockControllerTest.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/test/java/com/tce/smart/platform/lock/AccommodationDepartureRouterTest.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/test/java/com/tce/smart/platform/lock/AccommodationEntrancesTest.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/test/java/com/tce/smart/platform/lock/AccommodationIdentityRegularizationRouterTest.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/test/java/com/tce/smart/platform/lock/AccommodationLifecycleRouterTest.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/test/java/com/tce/smart/platform/lock/AccommodationMembershipFactsTest.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/test/java/com/tce/smart/platform/lock/AccommodationOutboxOracleIntegrationTest.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/test/java/com/tce/smart/platform/lock/AccommodationOutboxPersistenceTest.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/test/java/com/tce/smart/platform/lock/AccommodationOutboxTest.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/test/java/com/tce/smart/platform/lock/AccommodationScopeQueryServiceTest.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/test/java/com/tce/smart/platform/lock/LockAccessScopeTest.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/test/java/com/tce/smart/platform/lock/LockAdminApiContractTest.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/test/java/com/tce/smart/platform/lock/LockApiHttpAdviceTest.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/test/java/com/tce/smart/platform/lock/LockOpenApiContractTest.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/test/java/com/tce/smart/platform/lock/LockOutboxPublisherTest.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/test/java/com/tce/smart/platform/lock/LockSelfAccessResolverTest.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/test/java/com/tce/smart/platform/lock/MyLockApiContractTest.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/test/java/com/tce/smart/platform/lock/MyLockStatusTest.java create mode 100644 smart-module/smart-platform/smart-platform-biz/src/test/java/com/tce/smart/platform/lock/OutboxDeliveryPersistenceTest.java create mode 100644 smart-ui/docs/superpowers/doorlock/permission-and-route-map.md create mode 100644 smart-ui/scripts/doorlock-browser-smoke/.gitignore create mode 100644 smart-ui/scripts/doorlock-browser-smoke/README.md create mode 100644 smart-ui/scripts/doorlock-browser-smoke/index.html create mode 100644 smart-ui/scripts/doorlock-browser-smoke/network-policy.mjs create mode 100644 smart-ui/scripts/doorlock-browser-smoke/network-policy.test.mjs create mode 100644 smart-ui/scripts/doorlock-browser-smoke/run-result.mjs create mode 100644 smart-ui/scripts/doorlock-browser-smoke/run-result.test.mjs create mode 100644 smart-ui/scripts/doorlock-browser-smoke/smoke.mjs create mode 100644 smart-ui/scripts/doorlock-browser-smoke/src/App.vue create mode 100644 smart-ui/scripts/doorlock-browser-smoke/src/main.js create mode 100644 smart-ui/scripts/doorlock-browser-smoke/src/smoke.css create mode 100644 smart-ui/scripts/doorlock-browser-smoke/src/synthetic-api.js create mode 100644 smart-ui/scripts/doorlock-browser-smoke/vite.config.mjs create mode 100644 smart-ui/src/api/lock/admin-contract.js create mode 100644 smart-ui/src/api/lock/admin-contract.test.js create mode 100644 smart-ui/src/api/lock/index.js create mode 100644 smart-ui/src/api/lock/index.test.js create mode 100644 smart-ui/src/router/lock-menu-components.js create mode 100644 smart-ui/src/router/lock-menu-components.test.js create mode 100644 smart-ui/src/views/lock/__tests__/assets-parity.test.js create mode 100644 smart-ui/src/views/lock/__tests__/operations-parity.test.js create mode 100644 smart-ui/src/views/lock/alarms/alarm-page-controller.js create mode 100644 smart-ui/src/views/lock/alarms/alarm-page-rules.js create mode 100644 smart-ui/src/views/lock/alarms/alarm-page-rules.test.js create mode 100644 smart-ui/src/views/lock/alarms/alarm-settings-controller.js create mode 100644 smart-ui/src/views/lock/alarms/index.test.js create mode 100644 smart-ui/src/views/lock/alarms/index.vue create mode 100644 smart-ui/src/views/lock/alarms/settings.test.js create mode 100644 smart-ui/src/views/lock/alarms/settings.vue create mode 100644 smart-ui/src/views/lock/assets-parity.js create mode 100644 smart-ui/src/views/lock/commands/command-page-rules.js create mode 100644 smart-ui/src/views/lock/commands/command-page-rules.test.js create mode 100644 smart-ui/src/views/lock/commands/index.test.js create mode 100644 smart-ui/src/views/lock/commands/index.vue create mode 100644 smart-ui/src/views/lock/credentials/credential-page-rules.js create mode 100644 smart-ui/src/views/lock/credentials/credential-page-rules.test.js create mode 100644 smart-ui/src/views/lock/credentials/index.test.js create mode 100644 smart-ui/src/views/lock/credentials/index.vue create mode 100644 smart-ui/src/views/lock/devices/device-page-rules.js create mode 100644 smart-ui/src/views/lock/devices/device-page-rules.test.js create mode 100644 smart-ui/src/views/lock/devices/index.test.js create mode 100644 smart-ui/src/views/lock/devices/index.vue create mode 100644 smart-ui/src/views/lock/gateways/gateway-page-rules.js create mode 100644 smart-ui/src/views/lock/gateways/gateway-page-rules.test.js create mode 100644 smart-ui/src/views/lock/gateways/index.test.js create mode 100644 smart-ui/src/views/lock/gateways/index.vue create mode 100644 smart-ui/src/views/lock/grants/grant-page-rules.js create mode 100644 smart-ui/src/views/lock/grants/grant-page-rules.test.js create mode 100644 smart-ui/src/views/lock/grants/index.test.js create mode 100644 smart-ui/src/views/lock/grants/index.vue create mode 100644 smart-ui/src/views/lock/list/asset-page-rules.js create mode 100644 smart-ui/src/views/lock/list/asset-page-rules.test.js create mode 100644 smart-ui/src/views/lock/list/detail.test.js create mode 100644 smart-ui/src/views/lock/list/detail.vue create mode 100644 smart-ui/src/views/lock/list/index.test.js create mode 100644 smart-ui/src/views/lock/list/index.vue create mode 100644 smart-ui/src/views/lock/models/index.test.js create mode 100644 smart-ui/src/views/lock/models/index.vue create mode 100644 smart-ui/src/views/lock/models/model-page-rules.js create mode 100644 smart-ui/src/views/lock/models/model-page-rules.test.js create mode 100644 smart-ui/src/views/lock/operations-parity.js create mode 100644 smart-ui/src/views/lock/records/communication.vue create mode 100644 smart-ui/src/views/lock/records/index.test.js create mode 100644 smart-ui/src/views/lock/records/index.vue create mode 100644 smart-ui/src/views/lock/records/open.vue create mode 100644 smart-ui/src/views/lock/records/password.vue create mode 100644 smart-ui/src/views/lock/records/record-page-controller.js create mode 100644 smart-ui/src/views/lock/records/record-page-rules.js create mode 100644 smart-ui/src/views/lock/records/record-page-rules.test.js create mode 100644 smart/smart-gateway/src/main/java/com/tce/smart/gateway/filter/LockDownstreamRouteGuard.java create mode 100644 smart/smart-gateway/src/main/java/com/tce/smart/gateway/filter/LockRouteIsolationFilter.java create mode 100644 smart/smart-gateway/src/test/java/com/tce/smart/gateway/LockRouteIsolationTest.java diff --git a/README.md b/README.md index 772973da..f099ba5d 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ smart/ | 目录 | 责任 | 当前技术与声明版本 | | --- | --- | --- | | [`smart/`](smart/README.md) | 基础平台后端,提供网关、OAuth2 认证、UPMS 和公共组件。 | Java 8、Maven、Spring Boot `2.1.3.RELEASE`、Spring Cloud `Greenwich.RELEASE`(见 [`smart/pom.xml`](smart/pom.xml)) | -| [`smart-module/`](smart-module/README.md) | 业务微服务聚合工程,包含 App、平台、数据、桥接、算法、推送、调度等模块。 | Java 8、Spring Cloud 业务服务、Maven;聚合关系见 [`smart-module/pom.xml`](smart-module/pom.xml) | +| [`smart-module/`](smart-module/README.md) | 业务微服务聚合工程,包含 App、平台、数据、门锁领域、设备桥、算法、推送、调度等模块。 | Java 8、Spring Cloud 业务服务、Maven;聚合关系见 [`smart-module/pom.xml`](smart-module/pom.xml) | | [`smart-ui/`](smart-ui/README.md) | 管理端中后台 SPA,对接网关、认证、UPMS 和园区业务 API。 | Vue `^2.7.16`、Element UI `^2.4.11`、Avue、Vue CLI、pnpm `11.3.0`(见 [`smart-ui/package.json`](smart-ui/package.json)) | | [`smart-print-renderer/`](smart-print-renderer/README.md) | 独立单面模板及双面组合的私有 PDF 渲染;不读取人员库或调用打印机。 | Node.js 24、pdfme `6.1.12` | | [`smart-print-client/`](smart-print-client/README.md) | 独立设备身份领取任务、持久命令去重、手动/自动厂牌及单面访客适配;实机能力按档案验收。 | .NET 10、Windows 官方驱动、Brother b-PAC | @@ -80,6 +80,8 @@ smart/ | `smart-algorithm` | 算法模块。 | | `smart-tool` | 智慧园区服务公共模块。 | | `smart-bridge` | 设备桥接。 | +| [`smart-lock`](smart-module/smart-lock/README.md) | 门锁领域模块;当前开发范围为 Maven 基础、内部契约与离线校验,无可运行服务或数据库接入。 | +| [`smart-bridge-lock`](smart-module/smart-bridge-lock/README.md) | 门锁协议桥模块;当前开发范围为投递/回执契约,无网络监听和真实发送能力。 | | `smart-bridge-isc` | ISC(综合安防平台)集成桥接。 | | `smart-bridge-concentrator` | 设备集中器(YUTO Nexus)。 | | `smart-dispatcher` | 调度 / 分发。 | diff --git a/backend-modules.json b/backend-modules.json index c9b17802..d7cff982 100644 --- a/backend-modules.json +++ b/backend-modules.json @@ -332,6 +332,38 @@ "pom": "smart-module/smart-transfer/pom.xml", "role": "unpackaged-service", "reason": "当前不在可执行 Jar 白名单;打包与部署能力需单独验收" + }, + { + "artifact_id": "smart-lock", + "pom": "smart-module/smart-lock/pom.xml", + "role": "aggregator" + }, + { + "artifact_id": "smart-lock-api", + "pom": "smart-module/smart-lock/smart-lock-api/pom.xml", + "role": "library" + }, + { + "artifact_id": "smart-lock-biz", + "pom": "smart-module/smart-lock/smart-lock-biz/pom.xml", + "role": "unpackaged-service", + "reason": "门锁开发阶段尚未完成运行装配,禁止纳入可部署服务白名单。" + }, + { + "artifact_id": "smart-bridge-lock", + "pom": "smart-module/smart-bridge-lock/pom.xml", + "role": "aggregator" + }, + { + "artifact_id": "smart-bridge-lock-api", + "pom": "smart-module/smart-bridge-lock/smart-bridge-lock-api/pom.xml", + "role": "library" + }, + { + "artifact_id": "smart-bridge-lock-biz", + "pom": "smart-module/smart-bridge-lock/smart-bridge-lock-biz/pom.xml", + "role": "unpackaged-service", + "reason": "门锁开发阶段尚未完成运行装配,禁止纳入可部署服务白名单。" } ] } diff --git a/contracts/lock/v1/openapi.yaml b/contracts/lock/v1/openapi.yaml new file mode 100644 index 00000000..645165ac --- /dev/null +++ b/contracts/lock/v1/openapi.yaml @@ -0,0 +1,318 @@ +{ + "openapi": "3.1.0", + "info": {"title":"裕慧家园门锁统一公共 API","version":"1.0.0","description":"Java/Oracle 既定范围下采用新平台公共 API 标准。未发布候选契约,运行验收未完成。"}, + "servers": [{"url": "/"}], + "security": [{"bearerAuth": []}], + "paths": { + "/api/v1/locks/assets": {"get":{"operationId":"get__api_v1_locks_assets","summary":"ASSETS 查询","x-runtime-ready":false,"description":"当前提供统一 HTTP 边界;实际身份、持久化、调度和设备接线仍按 specs/011-doorlock-oracle 验收。HTTP 受理不是设备成功。","parameters":[{"name":"parkId","in":"query","required":true,"schema":{"type":"integer","minimum":1}},{"name":"X-Trace-Id","in":"header","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"成功资源正文,不包裹 code/data","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"400":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}, + "/api/v1/locks/assets/{deviceId}": {"get":{"operationId":"get__api_v1_locks_assets_deviceId_","summary":"ASSETS 查询","x-runtime-ready":false,"description":"当前提供统一 HTTP 边界;实际身份、持久化、调度和设备接线仍按 specs/011-doorlock-oracle 验收。HTTP 受理不是设备成功。","parameters":[{"name":"deviceId","in":"path","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]+$"}},{"name":"parkId","in":"query","required":true,"schema":{"type":"integer","minimum":1}},{"name":"X-Trace-Id","in":"header","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"成功资源正文,不包裹 code/data","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"400":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}, + "/api/v1/locks/assets/{deviceId}/config": {"get":{"operationId":"get__api_v1_locks_assets_deviceId_config","summary":"ASSETS 查询","x-runtime-ready":false,"description":"当前提供统一 HTTP 边界;实际身份、持久化、调度和设备接线仍按 specs/011-doorlock-oracle 验收。HTTP 受理不是设备成功。","parameters":[{"name":"deviceId","in":"path","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]+$"}},{"name":"parkId","in":"query","required":true,"schema":{"type":"integer","minimum":1}},{"name":"X-Trace-Id","in":"header","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"成功资源正文,不包裹 code/data","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"400":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"patch__api_v1_locks_assets_deviceId_config","summary":"ASSETS 写操作","x-runtime-ready":false,"description":"当前提供统一 HTTP 边界;实际身份、持久化、调度和设备接线仍按 specs/011-doorlock-oracle 验收。HTTP 受理不是设备成功。","parameters":[{"name":"deviceId","in":"path","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]+$"}},{"name":"parkId","in":"query","required":false,"schema":{"type":"integer","minimum":1}},{"name":"X-Trace-Id","in":"header","required":true,"schema":{"type":"string","minLength":1}},{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,128}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminWrite"}}}},"responses":{"200":{"description":"幂等重试返回已确认终态","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"202":{"description":"命令已受理,需查询设备确认结果","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"400":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}, + "/api/v1/locks/devices": {"get":{"operationId":"get__api_v1_locks_devices","summary":"DEVICES 查询","x-runtime-ready":false,"description":"当前提供统一 HTTP 边界;实际身份、持久化、调度和设备接线仍按 specs/011-doorlock-oracle 验收。HTTP 受理不是设备成功。","parameters":[{"name":"parkId","in":"query","required":true,"schema":{"type":"integer","minimum":1}},{"name":"X-Trace-Id","in":"header","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"成功资源正文,不包裹 code/data","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"400":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}, + "/api/v1/locks/gateways": {"get":{"operationId":"get__api_v1_locks_gateways","summary":"GATEWAYS_AND_MODELS 查询","x-runtime-ready":false,"description":"当前提供统一 HTTP 边界;实际身份、持久化、调度和设备接线仍按 specs/011-doorlock-oracle 验收。HTTP 受理不是设备成功。","parameters":[{"name":"parkId","in":"query","required":true,"schema":{"type":"integer","minimum":1}},{"name":"X-Trace-Id","in":"header","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"成功资源正文,不包裹 code/data","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"400":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"post__api_v1_locks_gateways","summary":"GATEWAYS_AND_MODELS 写操作","x-runtime-ready":false,"description":"当前提供统一 HTTP 边界;实际身份、持久化、调度和设备接线仍按 specs/011-doorlock-oracle 验收。HTTP 受理不是设备成功。","parameters":[{"name":"parkId","in":"query","required":false,"schema":{"type":"integer","minimum":1}},{"name":"X-Trace-Id","in":"header","required":true,"schema":{"type":"string","minLength":1}},{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,128}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminWrite"}}}},"responses":{"200":{"description":"成功资源正文,不包裹 code/data","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"400":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}, + "/api/v1/locks/models": {"get":{"operationId":"get__api_v1_locks_models","summary":"GATEWAYS_AND_MODELS 查询","x-runtime-ready":false,"description":"当前提供统一 HTTP 边界;实际身份、持久化、调度和设备接线仍按 specs/011-doorlock-oracle 验收。HTTP 受理不是设备成功。","parameters":[{"name":"parkId","in":"query","required":true,"schema":{"type":"integer","minimum":1}},{"name":"X-Trace-Id","in":"header","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"成功资源正文,不包裹 code/data","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"400":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"post__api_v1_locks_models","summary":"GATEWAYS_AND_MODELS 写操作","x-runtime-ready":false,"description":"当前提供统一 HTTP 边界;实际身份、持久化、调度和设备接线仍按 specs/011-doorlock-oracle 验收。HTTP 受理不是设备成功。","parameters":[{"name":"parkId","in":"query","required":false,"schema":{"type":"integer","minimum":1}},{"name":"X-Trace-Id","in":"header","required":true,"schema":{"type":"string","minLength":1}},{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,128}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminWrite"}}}},"responses":{"200":{"description":"成功资源正文,不包裹 code/data","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"400":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}, + "/api/v1/locks/persons": {"get":{"operationId":"get__api_v1_locks_persons","summary":"PERSONS_AND_CREDENTIALS 查询","x-runtime-ready":false,"description":"当前提供统一 HTTP 边界;实际身份、持久化、调度和设备接线仍按 specs/011-doorlock-oracle 验收。HTTP 受理不是设备成功。","parameters":[{"name":"parkId","in":"query","required":true,"schema":{"type":"integer","minimum":1}},{"name":"X-Trace-Id","in":"header","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"成功资源正文,不包裹 code/data","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"400":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}, + "/api/v1/locks/credentials": {"get":{"operationId":"get__api_v1_locks_credentials","summary":"PERSONS_AND_CREDENTIALS 查询","x-runtime-ready":false,"description":"当前提供统一 HTTP 边界;实际身份、持久化、调度和设备接线仍按 specs/011-doorlock-oracle 验收。HTTP 受理不是设备成功。","parameters":[{"name":"parkId","in":"query","required":true,"schema":{"type":"integer","minimum":1}},{"name":"X-Trace-Id","in":"header","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"成功资源正文,不包裹 code/data","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"400":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}, + "/api/v1/locks/grants": {"get":{"operationId":"get__api_v1_locks_grants","summary":"GRANTS 查询","x-runtime-ready":false,"description":"当前提供统一 HTTP 边界;实际身份、持久化、调度和设备接线仍按 specs/011-doorlock-oracle 验收。HTTP 受理不是设备成功。","parameters":[{"name":"parkId","in":"query","required":true,"schema":{"type":"integer","minimum":1}},{"name":"X-Trace-Id","in":"header","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"成功资源正文,不包裹 code/data","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"400":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"post__api_v1_locks_grants","summary":"GRANTS 写操作","x-runtime-ready":false,"description":"当前提供统一 HTTP 边界;实际身份、持久化、调度和设备接线仍按 specs/011-doorlock-oracle 验收。HTTP 受理不是设备成功。","parameters":[{"name":"parkId","in":"query","required":false,"schema":{"type":"integer","minimum":1}},{"name":"X-Trace-Id","in":"header","required":true,"schema":{"type":"string","minLength":1}},{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,128}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminWrite"}}}},"responses":{"200":{"description":"幂等重试返回已确认终态","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"202":{"description":"命令已受理,需查询设备确认结果","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"400":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}, + "/api/v1/locks/grants/{grantId}": {"get":{"operationId":"get__api_v1_locks_grants_grantId_","summary":"GRANTS 查询","x-runtime-ready":false,"description":"当前提供统一 HTTP 边界;实际身份、持久化、调度和设备接线仍按 specs/011-doorlock-oracle 验收。HTTP 受理不是设备成功。","parameters":[{"name":"grantId","in":"path","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]+$"}},{"name":"parkId","in":"query","required":true,"schema":{"type":"integer","minimum":1}},{"name":"X-Trace-Id","in":"header","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"成功资源正文,不包裹 code/data","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"400":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"patch__api_v1_locks_grants_grantId_","summary":"GRANTS 写操作","x-runtime-ready":false,"description":"当前提供统一 HTTP 边界;实际身份、持久化、调度和设备接线仍按 specs/011-doorlock-oracle 验收。HTTP 受理不是设备成功。","parameters":[{"name":"grantId","in":"path","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]+$"}},{"name":"parkId","in":"query","required":false,"schema":{"type":"integer","minimum":1}},{"name":"X-Trace-Id","in":"header","required":true,"schema":{"type":"string","minLength":1}},{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,128}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminWrite"}}}},"responses":{"200":{"description":"幂等重试返回已确认终态","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"202":{"description":"命令已受理,需查询设备确认结果","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"400":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}, + "/api/v1/locks/commands": {"get":{"operationId":"get__api_v1_locks_commands","summary":"COMMANDS 查询","x-runtime-ready":false,"description":"当前提供统一 HTTP 边界;实际身份、持久化、调度和设备接线仍按 specs/011-doorlock-oracle 验收。HTTP 受理不是设备成功。","parameters":[{"name":"parkId","in":"query","required":true,"schema":{"type":"integer","minimum":1}},{"name":"X-Trace-Id","in":"header","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"成功资源正文,不包裹 code/data","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"400":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}, + "/api/v1/locks/commands/{commandId}": {"get":{"operationId":"get__api_v1_locks_commands_commandId_","summary":"COMMANDS 查询","x-runtime-ready":false,"description":"当前提供统一 HTTP 边界;实际身份、持久化、调度和设备接线仍按 specs/011-doorlock-oracle 验收。HTTP 受理不是设备成功。","parameters":[{"name":"commandId","in":"path","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]+$"}},{"name":"parkId","in":"query","required":true,"schema":{"type":"integer","minimum":1}},{"name":"X-Trace-Id","in":"header","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"成功资源正文,不包裹 code/data","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"400":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}, + "/api/v1/locks/open-records": {"get":{"operationId":"get__api_v1_locks_open_records","summary":"RECORDS 查询","x-runtime-ready":false,"description":"当前提供统一 HTTP 边界;实际身份、持久化、调度和设备接线仍按 specs/011-doorlock-oracle 验收。HTTP 受理不是设备成功。","parameters":[{"name":"parkId","in":"query","required":true,"schema":{"type":"integer","minimum":1}},{"name":"X-Trace-Id","in":"header","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"成功资源正文,不包裹 code/data","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"400":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}, + "/api/v1/locks/password-records": {"get":{"operationId":"get__api_v1_locks_password_records","summary":"RECORDS 查询","x-runtime-ready":false,"description":"当前提供统一 HTTP 边界;实际身份、持久化、调度和设备接线仍按 specs/011-doorlock-oracle 验收。HTTP 受理不是设备成功。","parameters":[{"name":"parkId","in":"query","required":true,"schema":{"type":"integer","minimum":1}},{"name":"X-Trace-Id","in":"header","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"成功资源正文,不包裹 code/data","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"400":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}, + "/api/v1/locks/communication-records": {"get":{"operationId":"get__api_v1_locks_communication_records","summary":"RECORDS 查询","x-runtime-ready":false,"description":"当前提供统一 HTTP 边界;实际身份、持久化、调度和设备接线仍按 specs/011-doorlock-oracle 验收。HTTP 受理不是设备成功。","parameters":[{"name":"parkId","in":"query","required":true,"schema":{"type":"integer","minimum":1}},{"name":"X-Trace-Id","in":"header","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"成功资源正文,不包裹 code/data","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"400":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}, + "/api/v1/locks/alarms": {"get":{"operationId":"get__api_v1_locks_alarms","summary":"ALARMS_AND_SETTINGS 查询","x-runtime-ready":false,"description":"当前提供统一 HTTP 边界;实际身份、持久化、调度和设备接线仍按 specs/011-doorlock-oracle 验收。HTTP 受理不是设备成功。","parameters":[{"name":"parkId","in":"query","required":true,"schema":{"type":"integer","minimum":1}},{"name":"X-Trace-Id","in":"header","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"成功资源正文,不包裹 code/data","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"400":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}, + "/api/v1/locks/alarm-settings": {"get":{"operationId":"get__api_v1_locks_alarm_settings","summary":"ALARMS_AND_SETTINGS 查询","x-runtime-ready":false,"description":"当前提供统一 HTTP 边界;实际身份、持久化、调度和设备接线仍按 specs/011-doorlock-oracle 验收。HTTP 受理不是设备成功。","parameters":[{"name":"parkId","in":"query","required":true,"schema":{"type":"integer","minimum":1}},{"name":"X-Trace-Id","in":"header","required":true,"schema":{"type":"string","minLength":1}}],"responses":{"200":{"description":"成功资源正文,不包裹 code/data","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"400":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"patch__api_v1_locks_alarm_settings","summary":"ALARMS_AND_SETTINGS 写操作","x-runtime-ready":false,"description":"当前提供统一 HTTP 边界;实际身份、持久化、调度和设备接线仍按 specs/011-doorlock-oracle 验收。HTTP 受理不是设备成功。","parameters":[{"name":"parkId","in":"query","required":false,"schema":{"type":"integer","minimum":1}},{"name":"X-Trace-Id","in":"header","required":true,"schema":{"type":"string","minLength":1}},{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,128}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminWrite"}}}},"responses":{"200":{"description":"成功资源正文,不包裹 code/data","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"400":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}, + "/api/v1/locks/assets/{deviceId}/open-requests": {"post":{"operationId":"post__api_v1_locks_assets_deviceId_open_requests","summary":"ASSETS 写操作","x-runtime-ready":false,"description":"当前提供统一 HTTP 边界;实际身份、持久化、调度和设备接线仍按 specs/011-doorlock-oracle 验收。HTTP 受理不是设备成功。","parameters":[{"name":"deviceId","in":"path","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]+$"}},{"name":"parkId","in":"query","required":false,"schema":{"type":"integer","minimum":1}},{"name":"X-Trace-Id","in":"header","required":true,"schema":{"type":"string","minLength":1}},{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,128}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminWrite"}}}},"responses":{"200":{"description":"幂等重试返回已确认终态","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"202":{"description":"命令已受理,需查询设备确认结果","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"400":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}, + "/api/v1/locks/devices/{deviceId}": {"patch":{"operationId":"patch__api_v1_locks_devices_deviceId_","summary":"DEVICES 写操作","x-runtime-ready":false,"description":"当前提供统一 HTTP 边界;实际身份、持久化、调度和设备接线仍按 specs/011-doorlock-oracle 验收。HTTP 受理不是设备成功。","parameters":[{"name":"deviceId","in":"path","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]+$"}},{"name":"parkId","in":"query","required":false,"schema":{"type":"integer","minimum":1}},{"name":"X-Trace-Id","in":"header","required":true,"schema":{"type":"string","minLength":1}},{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,128}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminWrite"}}}},"responses":{"200":{"description":"成功资源正文,不包裹 code/data","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"400":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}, + "/api/v1/locks/devices/{deviceId}/unbinding-requests": {"post":{"operationId":"post__api_v1_locks_devices_deviceId_unbinding_requests","summary":"DEVICES 写操作","x-runtime-ready":false,"description":"当前提供统一 HTTP 边界;实际身份、持久化、调度和设备接线仍按 specs/011-doorlock-oracle 验收。HTTP 受理不是设备成功。","parameters":[{"name":"deviceId","in":"path","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]+$"}},{"name":"parkId","in":"query","required":false,"schema":{"type":"integer","minimum":1}},{"name":"X-Trace-Id","in":"header","required":true,"schema":{"type":"string","minLength":1}},{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,128}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminWrite"}}}},"responses":{"200":{"description":"幂等重试返回已确认终态","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"202":{"description":"命令已受理,需查询设备确认结果","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"400":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}, + "/api/v1/locks/devices/{deviceId}/enablement-requests": {"post":{"operationId":"post__api_v1_locks_devices_deviceId_enablement_requests","summary":"DEVICES 写操作","x-runtime-ready":false,"description":"当前提供统一 HTTP 边界;实际身份、持久化、调度和设备接线仍按 specs/011-doorlock-oracle 验收。HTTP 受理不是设备成功。","parameters":[{"name":"deviceId","in":"path","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]+$"}},{"name":"parkId","in":"query","required":false,"schema":{"type":"integer","minimum":1}},{"name":"X-Trace-Id","in":"header","required":true,"schema":{"type":"string","minLength":1}},{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,128}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminWrite"}}}},"responses":{"200":{"description":"幂等重试返回已确认终态","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"202":{"description":"命令已受理,需查询设备确认结果","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"400":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}, + "/api/v1/locks/gateways/{gatewayId}": {"patch":{"operationId":"patch__api_v1_locks_gateways_gatewayId_","summary":"GATEWAYS_AND_MODELS 写操作","x-runtime-ready":false,"description":"当前提供统一 HTTP 边界;实际身份、持久化、调度和设备接线仍按 specs/011-doorlock-oracle 验收。HTTP 受理不是设备成功。","parameters":[{"name":"gatewayId","in":"path","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]+$"}},{"name":"parkId","in":"query","required":false,"schema":{"type":"integer","minimum":1}},{"name":"X-Trace-Id","in":"header","required":true,"schema":{"type":"string","minLength":1}},{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,128}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminWrite"}}}},"responses":{"200":{"description":"幂等重试返回已确认终态","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"202":{"description":"命令已受理,需查询设备确认结果","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"400":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}, + "/api/v1/locks/models/{modelId}": {"patch":{"operationId":"patch__api_v1_locks_models_modelId_","summary":"GATEWAYS_AND_MODELS 写操作","x-runtime-ready":false,"description":"当前提供统一 HTTP 边界;实际身份、持久化、调度和设备接线仍按 specs/011-doorlock-oracle 验收。HTTP 受理不是设备成功。","parameters":[{"name":"modelId","in":"path","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]+$"}},{"name":"parkId","in":"query","required":false,"schema":{"type":"integer","minimum":1}},{"name":"X-Trace-Id","in":"header","required":true,"schema":{"type":"string","minLength":1}},{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,128}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminWrite"}}}},"responses":{"200":{"description":"成功资源正文,不包裹 code/data","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"400":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}, + "/api/v1/locks/credentials/imports": {"post":{"operationId":"post__api_v1_locks_credentials_imports","summary":"PERSONS_AND_CREDENTIALS 写操作","x-runtime-ready":false,"description":"当前提供统一 HTTP 边界;实际身份、持久化、调度和设备接线仍按 specs/011-doorlock-oracle 验收。HTTP 受理不是设备成功。","parameters":[{"name":"parkId","in":"query","required":false,"schema":{"type":"integer","minimum":1}},{"name":"X-Trace-Id","in":"header","required":true,"schema":{"type":"string","minLength":1}},{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,128}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminWrite"}}}},"responses":{"200":{"description":"成功资源正文,不包裹 code/data","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"400":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}, + "/api/v1/locks/credentials/actions": {"post":{"operationId":"post__api_v1_locks_credentials_actions","summary":"PERSONS_AND_CREDENTIALS 写操作","x-runtime-ready":false,"description":"当前提供统一 HTTP 边界;实际身份、持久化、调度和设备接线仍按 specs/011-doorlock-oracle 验收。HTTP 受理不是设备成功。","parameters":[{"name":"parkId","in":"query","required":false,"schema":{"type":"integer","minimum":1}},{"name":"X-Trace-Id","in":"header","required":true,"schema":{"type":"string","minLength":1}},{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,128}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminWrite"}}}},"responses":{"200":{"description":"幂等重试返回已确认终态","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"202":{"description":"命令已受理,需查询设备确认结果","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"400":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}, + "/api/v1/locks/grants/{grantId}/revocations": {"post":{"operationId":"post__api_v1_locks_grants_grantId_revocations","summary":"GRANTS 写操作","x-runtime-ready":false,"description":"当前提供统一 HTTP 边界;实际身份、持久化、调度和设备接线仍按 specs/011-doorlock-oracle 验收。HTTP 受理不是设备成功。","parameters":[{"name":"grantId","in":"path","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]+$"}},{"name":"parkId","in":"query","required":false,"schema":{"type":"integer","minimum":1}},{"name":"X-Trace-Id","in":"header","required":true,"schema":{"type":"string","minLength":1}},{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,128}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminWrite"}}}},"responses":{"200":{"description":"幂等重试返回已确认终态","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"202":{"description":"命令已受理,需查询设备确认结果","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"400":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}, + "/api/v1/locks/grants/{grantId}/provision-requests": {"post":{"operationId":"post__api_v1_locks_grants_grantId_provision_requests","summary":"GRANTS 写操作","x-runtime-ready":false,"description":"当前提供统一 HTTP 边界;实际身份、持久化、调度和设备接线仍按 specs/011-doorlock-oracle 验收。HTTP 受理不是设备成功。","parameters":[{"name":"grantId","in":"path","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]+$"}},{"name":"parkId","in":"query","required":false,"schema":{"type":"integer","minimum":1}},{"name":"X-Trace-Id","in":"header","required":true,"schema":{"type":"string","minLength":1}},{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,128}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminWrite"}}}},"responses":{"200":{"description":"幂等重试返回已确认终态","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"202":{"description":"命令已受理,需查询设备确认结果","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"400":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}, + "/api/v1/locks/commands/{commandId}/actions": {"post":{"operationId":"post__api_v1_locks_commands_commandId_actions","summary":"COMMANDS 写操作","x-runtime-ready":false,"description":"当前提供统一 HTTP 边界;实际身份、持久化、调度和设备接线仍按 specs/011-doorlock-oracle 验收。HTTP 受理不是设备成功。","parameters":[{"name":"commandId","in":"path","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]+$"}},{"name":"parkId","in":"query","required":false,"schema":{"type":"integer","minimum":1}},{"name":"X-Trace-Id","in":"header","required":true,"schema":{"type":"string","minLength":1}},{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,128}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminWrite"}}}},"responses":{"200":{"description":"幂等重试返回已确认终态","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"202":{"description":"命令已受理,需查询设备确认结果","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"400":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}, + "/api/v1/locks/exports": {"post":{"operationId":"post__api_v1_locks_exports","summary":"RECORDS 写操作","x-runtime-ready":false,"description":"当前提供统一 HTTP 边界;实际身份、持久化、调度和设备接线仍按 specs/011-doorlock-oracle 验收。HTTP 受理不是设备成功。","parameters":[{"name":"parkId","in":"query","required":false,"schema":{"type":"integer","minimum":1}},{"name":"X-Trace-Id","in":"header","required":true,"schema":{"type":"string","minLength":1}},{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,128}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminWrite"}}}},"responses":{"200":{"description":"成功资源正文,不包裹 code/data","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"400":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}, + "/api/v1/locks/alarms/actions": {"post":{"operationId":"post__api_v1_locks_alarms_actions","summary":"ALARMS_AND_SETTINGS 写操作","x-runtime-ready":false,"description":"当前提供统一 HTTP 边界;实际身份、持久化、调度和设备接线仍按 specs/011-doorlock-oracle 验收。HTTP 受理不是设备成功。","parameters":[{"name":"parkId","in":"query","required":false,"schema":{"type":"integer","minimum":1}},{"name":"X-Trace-Id","in":"header","required":true,"schema":{"type":"string","minLength":1}},{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,128}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminWrite"}}}},"responses":{"200":{"description":"成功资源正文,不包裹 code/data","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Resource"}}}},"400":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}, + "/api/v1/me/locks/status": {"get":{"operationId":"get__api_v1_me_locks_status","summary":"本人门锁状态","x-runtime-ready":false,"description":"当前提供统一 HTTP 边界;实际身份、持久化、调度和设备接线仍按 specs/011-doorlock-oracle 验收。HTTP 受理不是设备成功。","parameters":[],"responses":{"200":{"description":"成功资源正文,不包裹 code/data","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Status"}}}},"400":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}, + "/api/v1/me/locks/password-reveals": {"post":{"operationId":"post__api_v1_me_locks_password_reveals","summary":"短时查看本人密码","x-runtime-ready":false,"description":"当前提供统一 HTTP 边界;实际身份、持久化、调度和设备接线仍按 specs/011-doorlock-oracle 验收。HTTP 受理不是设备成功。","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,128}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Reveal"}}}},"responses":{"200":{"description":"成功资源正文,不包裹 code/data","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevealResult"}}}},"400":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}, + "/api/v1/me/locks/password-commands": {"post":{"operationId":"post__api_v1_me_locks_password_commands","summary":"修改本人密码","x-runtime-ready":false,"description":"当前提供统一 HTTP 边界;实际身份、持久化、调度和设备接线仍按 specs/011-doorlock-oracle 验收。HTTP 受理不是设备成功。","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,128}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordCommand"}}}},"responses":{"200":{"description":"幂等重试返回已确认终态","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommandResult"}}}},"202":{"description":"命令已受理,需查询设备确认结果","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommandResult"}}}},"400":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}, + "/api/v1/me/locks/dynamic-code-requests": {"post":{"operationId":"post__api_v1_me_locks_dynamic_code_requests","summary":"请求动态码","x-runtime-ready":false,"description":"当前提供统一 HTTP 边界;实际身份、持久化、调度和设备接线仍按 specs/011-doorlock-oracle 验收。HTTP 受理不是设备成功。","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,128}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Reveal"}}}},"responses":{"200":{"description":"幂等重试返回已确认终态","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommandResult"}}}},"202":{"description":"命令已受理,需查询设备确认结果","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommandResult"}}}},"400":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}, + "/api/v1/me/locks/password-reveal-verifications": {"post":{"operationId":"post__api_v1_me_locks_password_reveal_verifications","summary":"签发密码查看验证引用","x-runtime-ready":false,"description":"当前提供统一 HTTP 边界;实际身份、持久化、调度和设备接线仍按 specs/011-doorlock-oracle 验收。HTTP 受理不是设备成功。","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,128}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Verification"}}}},"responses":{"200":{"description":"成功资源正文,不包裹 code/data","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationResult"}}}},"400":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}, + "/api/v1/me/locks/dynamic-code-verifications": {"post":{"operationId":"post__api_v1_me_locks_dynamic_code_verifications","summary":"签发动态码验证引用","x-runtime-ready":false,"description":"当前提供统一 HTTP 边界;实际身份、持久化、调度和设备接线仍按 specs/011-doorlock-oracle 验收。HTTP 受理不是设备成功。","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{1,128}$"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Verification"}}}},"responses":{"200":{"description":"成功资源正文,不包裹 code/data","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationResult"}}}},"400":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}, + "/api/v1/me/locks/requests/{requestId}": {"get":{"operationId":"get__api_v1_me_locks_requests_requestId_","summary":"查询本人命令状态","x-runtime-ready":false,"description":"当前提供统一 HTTP 边界;实际身份、持久化、调度和设备接线仍按 specs/011-doorlock-oracle 验收。HTTP 受理不是设备成功。","parameters":[{"name":"requestId","in":"path","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]+$"}}],"responses":{"200":{"description":"成功资源正文,不包裹 code/data","headers":{"Cache-Control":{"schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommandResult"}}}},"400":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"稳定错误码;不回显敏感信息","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}} + }, + "components": { + "securitySchemes": { + "bearerAuth": { + "type": "http", + "scheme": "bearer" + } + }, + "schemas": { + "Error": { + "type": "object", + "additionalProperties": false, + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "type": "string", + "minLength": 1 + }, + "message": { + "type": "string", + "minLength": 1 + } + } + }, + "Status": { + "type": "object", + "additionalProperties": false, + "required": [ + "hasCurrentMembership", + "scopeRef", + "authorizationStatus", + "capabilityStatus", + "cutoverFrozen", + "dynamicCodeCapabilityVerified", + "dynamicCodeCompliancePassed" + ], + "properties": { + "scopeRef": { + "type": [ + "string", + "null" + ] + }, + "hasCurrentMembership": { + "type": "boolean" + }, + "authorizationStatus": { + "type": [ + "string", + "null" + ], + "enum": [ + "ACTIVE", + "PENDING_PROVISION", + "PENDING_REVOKE", + "REVOKED", + "RECONCILIATION_REQUIRED", + null + ] + }, + "capabilityStatus": { + "enum": [ + "VERIFIED", + "UNVERIFIED" + ] + }, + "cutoverFrozen": { + "type": "boolean" + }, + "dynamicCodeCapabilityVerified": { + "type": "boolean" + }, + "dynamicCodeCompliancePassed": { + "type": "boolean" + }, + "expectedVersion": { + "type": [ + "string", + "null" + ], + "pattern": "^[A-Za-z0-9_-]{1,128}$", + "description": "领域签发的并发版本;缺失时只读。" + } + } + }, + "PasswordCommand": { + "type": "object", + "additionalProperties": false, + "required": [ + "requestId", + "password", + "expectedVersion" + ], + "properties": { + "requestId": { + "type": "string", + "minLength": 1 + }, + "password": { + "type": "string", + "pattern": "^[0-9]{6}$", + "writeOnly": true + }, + "expectedVersion": { + "type": "string", + "pattern": "^[A-Za-z0-9_-]{1,128}$" + } + } + }, + "Reveal": { + "type": "object", + "additionalProperties": false, + "required": [ + "requestId", + "verificationRef" + ], + "properties": { + "requestId": { + "type": "string", + "minLength": 1 + }, + "verificationRef": { + "type": "string", + "minLength": 1, + "writeOnly": true + } + } + }, + "Verification": { + "type": "object", + "additionalProperties": false, + "required": [ + "requestId", + "faceEvidence" + ], + "properties": { + "requestId": { + "type": "string", + "minLength": 1 + }, + "faceEvidence": { + "type": "string", + "minLength": 1, + "maxLength": 6291456, + "writeOnly": true + } + } + }, + "VerificationResult": { + "type": "object", + "additionalProperties": false, + "required": [ + "verificationRef" + ], + "properties": { + "verificationRef": { + "type": "string", + "minLength": 1 + } + } + }, + "RevealResult": { + "type": "object", + "additionalProperties": false, + "required": [ + "revealId", + "expiresAt", + "password" + ], + "properties": { + "revealId": { + "type": "string", + "minLength": 1 + }, + "expiresAt": { + "type": "string", + "format": "date-time" + }, + "password": { + "type": "string", + "minLength": 1, + "readOnly": true + } + } + }, + "AdminWrite": { + "type": "object", + "additionalProperties": false, + "required": [ + "parkId", + "requestId", + "reason" + ], + "properties": { + "parkId": { + "type": "integer", + "minimum": 1 + }, + "requestId": { + "type": "string", + "minLength": 1 + }, + "reason": { + "type": "string", + "minLength": 1 + }, + "expectedVersion": { + "type": "integer", + "minimum": 0 + }, + "payload": { + "type": "object", + "additionalProperties": true + } + } + }, + "Resource": { + "type": "object", + "description": "管理领域 DTO 尚在接线阶段,细化响应与运行验证是后续门禁,不得据此发布客户端生成模型。", + "additionalProperties": true + }, + "CommandResult": { + "type": "object", + "additionalProperties": false, + "required": [ + "httpStatus", + "traceId", + "requestId", + "commandId", + "commandStatus", + "transportAccepted", + "deviceConfirmed", + "terminal", + "completed" + ], + "properties": { + "httpStatus": { + "type": "integer", + "enum": [ + 200, + 202 + ] + }, + "traceId": { + "type": "string" + }, + "requestId": { + "type": "string" + }, + "commandId": { + "type": "string" + }, + "commandStatus": { + "type": "string" + }, + "transportAccepted": { + "type": "boolean" + }, + "deviceConfirmed": { + "type": "boolean" + }, + "terminal": { + "type": "boolean" + }, + "completed": { + "type": "boolean" + } + } + } + } + } +} diff --git a/scripts/test-backend-modules.py b/scripts/test-backend-modules.py index 1d077217..169212e5 100644 --- a/scripts/test-backend-modules.py +++ b/scripts/test-backend-modules.py @@ -28,7 +28,7 @@ def save(self): def test_all_registered_modules(self): self.assertEqual(validate(self.root, self.plan), []) - self.assertEqual(len(self.inventory['modules']), 60) + self.assertEqual(len(self.inventory['modules']), 66) def test_concentrator_revision_drift(self): p = self.root / 'smart-module/smart-bridge-concentrator/pom.xml' diff --git a/smart-h5/docs/superpowers/doorlock/README.md b/smart-h5/docs/superpowers/doorlock/README.md index e9ff5cb8..094aca03 100644 --- a/smart-h5/docs/superpowers/doorlock/README.md +++ b/smart-h5/docs/superpowers/doorlock/README.md @@ -4,7 +4,7 @@ 当前实现以 [011 门锁统一规格](../../../../specs/011-doorlock-oracle/spec.md)、[Web/H5 契约](../../../../specs/011-doorlock-oracle/contracts/web-h5.md) 和 [任务清单](../../../../specs/011-doorlock-oracle/tasks.md) 为准。本目录保留页面历史参考;旧接口目标、凭据处置及切换假设的冲突按 [设计修订记录](../../../../specs/011-doorlock-oracle/design-revisions.md) 修正,不再单独驱动实现。 -H5 通过平台业务入口取得本人门锁能力,由服务端登录态确定身份;不再信任客户端传入的工号决定凭据目标。HTTP 受理与设备确认分开展示,密码不持久保留在浏览器。本轮仅更新设计,没有改动 H5 代码。 +H5 的目标是通过平台业务入口取得本人门锁能力,由服务端登录态确定身份;不信任客户端传入的工号决定凭据目标。HTTP 受理与设备确认分开展示,密码不持久保留在浏览器。2026-09-07 已完成 H5 API 和三处目标页面的离线接线:旧 `badge` 密码读取/更新/刷新调用及浏览器 AES 解密已从目标页面移除;查看/刷新均通过固定用途的验证签发路径后执行。该状态仍仅是离线合同验证:验证门面未接真实算法、Oracle、网关或设备,生产请求会失败关闭,不能将其描述为生产可用或切换完成;详细要求见 [test-and-acceptance.md](test-and-acceptance.md)。 ## 目标 diff --git a/smart-h5/docs/superpowers/doorlock/api-cutover-contract.md b/smart-h5/docs/superpowers/doorlock/api-cutover-contract.md index a6a469b6..30fec9e6 100644 --- a/smart-h5/docs/superpowers/doorlock/api-cutover-contract.md +++ b/smart-h5/docs/superpowers/doorlock/api-cutover-contract.md @@ -1,8 +1,16 @@ # H5 到 smart-lock 的接口切换契约 -## 当前调用事实 +## 2026-09-08 版本与命令响应 -当前 smart-h5 的 dorm feature 已封装以下“本人”语义调用,定义位于 smart-h5/src/features/dorm/api.ts: +- 状态响应新增 `expectedVersion`;缺失、空值或非有界字符串时禁用修改密码,不能从 scopeRef 或客户端生成版本。 +- `POST /api/v1/me/locks/password-commands` 正文必须带 requestId/password/expectedVersion;幂等键只发标准请求头。 +- HTTP 409 清空并关闭修改表单,重新读取状态,不自动重试密码下发。当前其他网络故障后的可靠原请求重试/查询仍待后续闭环。 +- 密码修改、动态码请求和请求查询返回 CommandResult,不能把 HTTP 200/202 或 transportAccepted 解释为设备完成。动态码请求不返回明文密码。 +- 本增量仅通过本地 Mock/单测验证;版本签发和事务条件更新待领域适配器接线,未获真实 Oracle 或设备验收。 + +## 旧调用与当前切换实现 + +下列旧调用仍保留在 dorm API 适配层,供尚未迁移的兼容范围识别;`/dorm/lock`、`/dorm/get-code` 与入住详情已不再调用它们: | 语义 | 当前路径表达 | 前端边界 | |---|---|---| @@ -10,7 +18,11 @@ | 修改本人门锁密码 | `POST /dormitory/staff/update/lock/pwd`(body:`badge`、`newPwd`) | 仅提交受控密码输入;结果需区分已受理与设备确认。 | | 刷新本人动态密码 | `POST /dormitory/staff/update/pwd`(body:`badge`、`facePic`) | 人脸或其他服务端校验结果必须由后端裁定。 | -以上仅确认客户端调用事实;携带 `badge` 不能证明后端已将其与 token 身份绑定,服务端鉴权需单独验证。这些当前路径是现有 Smart 平台接口表达,不是未来 smart-lock 的最终路径。切换前不得仅改前端 URL;必须先冻结版本化 DTO、错误码、鉴权、幂等、状态和回退兼容策略。 +以上仅确认旧客户端调用事实;携带 `badge` 不能证明后端已将其与 token 身份绑定。它们不是新门锁合同,切换前不得重新接回页面。 + +当前一期页面使用的本人路径为 `GET /api/v1/me/locks/status`、`POST /password-commands`、`POST /password-reveal-verifications`、`POST /password-reveals`、`POST /dynamic-code-verifications` 与 `POST /dynamic-code-requests`。验证签发和执行均从当前登录态解析本人范围;两个 `verifications` 路径分别固定为“查看密码”和“刷新动态码”,不接收客户端 `purpose`、`badge`、人员、租户、房间、设备或客户端人脸成功结论。 + +截至 2026-09-07,此实现是离线合同与失败关闭接线:验证端口尚未装配真实算法/人脸参考资料适配器,也未接 Oracle、网关或门锁设备;生产请求会拒绝,不能宣称可用或用于切换。 ## 新接口必须保证的语义 diff --git a/smart-h5/docs/superpowers/doorlock/page-and-state-contract.md b/smart-h5/docs/superpowers/doorlock/page-and-state-contract.md index 39e655d9..a8d79bf5 100644 --- a/smart-h5/docs/superpowers/doorlock/page-and-state-contract.md +++ b/smart-h5/docs/superpowers/doorlock/page-and-state-contract.md @@ -5,8 +5,8 @@ | 路由 | 当前职责 | 关键实现位置 | |---|---|---| | /dorm | 宿舍总入口,跳转门锁服务。 | smart-h5/src/app/dorm/page.tsx | -| /dorm/lock | 读取本人动态密码、展示/修改、处理未入住及结果提示。 | smart-h5/src/app/dorm/lock/page.tsx | -| /dorm/get-code | 摄像头、人脸校验、动态码刷新和返回。 | smart-h5/src/app/dorm/get-code/page.tsx | +| /dorm/lock | 读取本人门锁状态;不自动读取/解密密码,可提交修改或进入受控查看/刷新流程。 | smart-h5/src/app/dorm/lock/page.tsx | +| /dorm/get-code | 摄像头采集、服务端本人核验、动态码刷新或短时查看和返回。 | smart-h5/src/app/dorm/get-code/page.tsx | | 宿舍人脸组件 | 扫描、校验、成功、失败状态机。 | smart-h5/src/features/dorm/lock-face-camera.tsx | ## 本人身份规则 @@ -18,22 +18,21 @@ | 状态 | 触发条件 | 页面反馈 | 可用动作 | |---|---|---|---| | 加载中 | 初次查询、密码修改提交、刷新动态码。 | 明确 loading,不能复用旧值冒充实时。 | 等待或安全取消。 | -| 有效 | 当前人有有效住宿和可展示的门锁结果。 | 展示最小必要结果、更新时间和使用说明。 | 受控修改本人密码、进入刷新流程。 | +| 有效 | 当前人有有效住宿和可展示的门锁状态。 | 不自动显示密码;说明需二次核验。 | 受控修改本人密码、进入查看或刷新流程。 | | 未入住/无门锁 | 服务端无有效住宿或门锁权限。 | 可理解说明,不显示他人或设备细节。 | 返回宿舍入口、查看帮助。 | | 校验中 | 人脸流程正在扫描或服务端核验。 | 明确阶段文字与隐私提示。 | 取消或重试,遵循次数限制。 | -| 刷新成功 | 服务端返回新动态码/结果。 | 短时展示结果,页面离开或失效后清除。 | 返回门锁页。 | +| 刷新/查看成功 | 服务端返回新动态码或受控查看结果。 | 密码只在当前短时弹窗呈现,不能写 Query、store、URL 或持久化浏览器存储。 | 返回门锁页。 | | 刷新失败 | 人脸失败、限频、服务异常、设备不支持或系统冻结。 | 不同原因给出不同的安全文案,不能统一伪装成“网络异常”。 | 按原因重试、查看帮助或联系宿管。 | | 已受理待确认 | 本人密码或门锁动作已提交但设备尚未确认。 | 显示“已受理/确认中”,不显示已生效。 | 刷新状态、查看帮助。 | ## 修改本人密码 -当前页面有“新密码为 6 位数字、不能与原密码相同”的前端体验约束。新 smart-lock 合同还必须负责身份、频率、设备能力、有效住宿、幂等和真实设备确认。H5 页面不记录密码历史、不打印到日志、不将密码放入 URL 或持久化存储。 +当前页面有“新密码为 6 位数字”的前端体验约束;由于页面不再读取旧密码,是否允许与当前密码相同必须由新 smart-lock 合同裁定。合同还必须负责身份、频率、设备能力、有效住宿、幂等和真实设备确认。H5 页面不记录密码历史、不打印到日志、不将密码放入 URL 或持久化存储。 ## 人脸刷新流程 -人脸组件的前端状态机包含 initial、scanning、checking、success、failure。每个状态应有明确文字和可恢复路径。相机权限拒绝、摄像头不可用、认证超时、连续失败、系统冻结均需独立可理解提示;前端动画或倒计时不能作为身份核验成功的依据。 +人脸组件的前端状态机包含 initial、scanning、checking、success、failure。`success` 在当前组件仅表示材料采集完成,不是本人核验通过;点击动作后才由服务端以固定用途、当前身份和当前住宿签发一次性 `verificationRef`。相机权限拒绝、摄像头不可用、认证超时、连续失败、系统冻结均需独立可理解提示;前端动画或倒计时不能作为身份核验成功的依据。 ## 缓存与失效 本人门锁结果与入住详情共享的查询键必须在修改密码、刷新动态码、调宿、退宿、登录身份变化、登出、授权撤销和运行模式变化后失效或重新读取。页面保留最后成功时间时,应明确标为历史数据,不能冒充实时状态。 - diff --git a/smart-h5/docs/superpowers/doorlock/test-and-acceptance.md b/smart-h5/docs/superpowers/doorlock/test-and-acceptance.md index ea06c374..3d960f5e 100644 --- a/smart-h5/docs/superpowers/doorlock/test-and-acceptance.md +++ b/smart-h5/docs/superpowers/doorlock/test-and-acceptance.md @@ -17,8 +17,38 @@ | 退出与失效 | 登出、身份切换、调宿、撤权、到期、页面卸载后清除短时结果。 | | 可用性 | 弱网、刷新、重复进入、屏幕阅读和键盘替代操作(可用时)有可解释结果。 | +## 2026-09-07 T079 / T083 纯状态与短时内存验证 + +- 实现文件:`src/features/dorm/lock-state.ts` 与 `lock-state.test.ts`。T079 测试开发项已完成;T083 仅完成可复用状态/内存子范围,当前旧 API 和页面未被替换。 +- 从 `smart-h5/` 执行 `pnpm exec vitest run src/features/dorm/lock-state.test.ts`:**1 file / 38 tests passed**;两文件 ESLint、Prettier check 与 `pnpm exec tsc --noEmit --pretty false --incremental false` 均退出 0。未运行本批 H5 build/E2E,未连接真实服务。 +- 红灯覆盖模块缺失、34 项未实现行为,以及完成前新增的 4 项缺陷回归;最终修复了缺失/重复 commandId 误算成功、不同实例同代次票据混用、通用 VERIFIED 错误打开动态码三类问题。独立复审确认规格/质量通过;额外只读复现确认计数矛盾进入对账、显式清空作用域淘汰在途结果。 +- 展示区分确认中、撤权中、已撤销、对账、无住宿、冻结及能力未知;HTTP/传输受理不等于设备确认。动态码还要求独立能力和合规证据。 +- 密码仅存组件实例闭包,普通 getState 不包含秘密;固定用途、到期计时、身份/住宿/冻结变化、修改开始、登出和销毁均有失效保护,不使用 QueryCache、localStorage 或 sessionStorage 存门锁秘密。 + +## 2026-09-07 T081 / T082 / T084 离线接线证据 + +- 后端本人入口增加冻结的 `/platform/lock/v1/me` 合同、当前身份/住宿重校验、未知字段拒绝、查看 no-store,以及按固定路径签发查看/动态码的一次性验证引用。未装配 `MyLockDomainFacade` 或 `MyLockVerificationFacade` 时明确失败关闭;离线 Maven 回归为 **2 files / 14 tests passed**。 +- H5 `api.ts`、`/dorm/lock`、`/dorm/get-code` 与 `/check-in/detail` 已迁出旧 `badge` 密码 API 和 AES 解密。动态码和查看页均先签发验证引用再执行;采集材料和预览在请求结束时清理,查看结果只交给当前短时弹窗。H5 `pnpm check` 与门锁相关 **3 files / 42 tests passed**。 +- 本地 Playwright 已覆盖新本人状态/修改、未入住、验证签发后刷新、状态与修改失败、入住详情不读取历史密码、网络超时、人脸材料质量失败和浏览器持久化存储不含测试密码:`e2e/help-dorm-lock.spec.ts`、`e2e/check-in.spec.ts` 与 `e2e/dorm-lock.spec.ts` 共 **18 tests passed**。全部 API、摄像头和人脸材料均为 Mock;E2E 不等于真机或真实服务验收。 +- 使用临时合成 AES 值和回环代理地址的 H5 `pnpm build` 已通过;它只证明编译与静态路由生成,未使用生产密钥或生产代理。 +- 未运行真实服务、算法、人脸资料、Oracle、网关、门锁、微信真机或数据迁移。上述测试不能证明人脸核验、设备下发、撤权或切换可用。 + +后续 T083/T084 页面状态接线及真实适配必须遵循下列顺序: + +1. 每个页面实例单独创建控制器,保留原票据对象;不得序列化、重建或跨实例传递票据。API 仍由服务端授权,客户端 scope 只负责失效与展示。 +2. 对同 membership 的授权撤销、动态码刷新、非冻结模式变化、授权版本变化,先调用 `setScope(null)` 清除秘密和在途票据,再刷新服务端事实;只有新事实允许时才设置有效 scope。不能只重复传入相同 setScope / setFrozen(false),其同值调用是 no-op。 +3. 组件卸载调用 destroy;登出调用 logout;冻结先 setFrozen(true)。迟到响应必须持原票据交给控制器核验,不能直接 setState 或恢复旧 Query 数据。 +4. 普通状态/请求查询禁止读取或缓存密码。reveal 的 no-store HTTP、一次性 verificationRef、真实身份与住宿事实、页面 DOM 清理、旧 Query 清除及微信场景必须随真实适配再做集成与真机验证。 + ## 验收限制 +### T083 主页门禁与服务端状态 DTO 增量 + +- `/dorm/lock` 已消费 `resolveLockDisplayState` 与组件专属 `createLockStateController`:冻结、撤权、能力未知或缺少 `scopeRef` 禁用敏感按钮;重新读取状态先清空作用域,修改请求通过原票据处理响应,卸载销毁控制器。 +- 服务端 `MyLockDomainFacade.status` 改为 `MyLockStatus` 白名单 DTO。有住宿缺少引用时拒绝,无住宿清除旧引用与授权,缺失能力默认为 UNVERIFIED。作用域引用实际签发/持久化适配仍待实现;不能由前端构造或当授权凭证。 +- 实际回归:后端 MyLockStatusTest / MyLockControllerTest / MyLockApiContractTest 共 17 项通过;前端 43 项单测与类型/静态检查通过;三个相关 Playwright 文件同轮 21 项通过。冻结按钮测试先复现 enabled 错误再通过。 +- T083/T084 仍未完成:`/dorm/get-code` 尚需接入作用域票据、服务端 revealId/expiresAt、到期 DOM 清理、身份失效及迟到响应淘汰;主页完整认证变更订阅、旧 Query 淘汰、幂等重试与请求结果轮询也待补齐。以上均可继续在开发环境实现,真实环境验证另列。 + - H5 不以旧 /key/myKey 或 /key/record 作为一期必测页面,除非 OPEN-H5-001 被明确纳入并补充需求与安全评审。 - 任何本人远程开门能力都需要协议、合规和业务授权 Gate;不能因为旧 bundle 存在按钮就上线。 - 动态码、密码和人脸相关测试的证据不得包含真实生物数据、明文密码、token 或完整个人信息。 @@ -26,4 +56,3 @@ ## 发布前证据 每次涉及 smart-h5/src/app/dorm、smart-h5/src/features/dorm 或认证缓存的改动,都应关联单测、E2E/真机结果、接口契约版本、敏感字段扫描和人工验收截图。若 smart-lock 尚未提供新接口,测试报告必须明确标记为 mock/集成环境结果,不能称为生产可用。 - diff --git a/smart-h5/e2e/check-in.spec.ts b/smart-h5/e2e/check-in.spec.ts index a24e966f..91de7db5 100644 --- a/smart-h5/e2e/check-in.spec.ts +++ b/smart-h5/e2e/check-in.spec.ts @@ -1,5 +1,4 @@ import { expect, test, type Page } from '@playwright/test' -import { injectTestKey, legacyCipherHex } from './helpers' async function seedLogin(page: Page) { await page.addInitScript(() => { @@ -212,10 +211,11 @@ test('宿舍申请:切换楼栋清空房间类型与已选房间', async ({ pa await expect(page.getByRole('button', { name: '请选择房间', exact: true })).toBeVisible() }) -test('宿舍申请详情:分配记录 + 动态码解密展示', async ({ page }) => { +test('宿舍申请详情:分配记录与门锁特征状态,不读取或展示历史动态码', async ({ page }) => { await seedLogin(page) await mockBaseInfo(page) - await injectTestKey(page) + const requests: string[] = [] + page.on('request', (request) => requests.push(new URL(request.url()).pathname)) await page.route('**/platform/dormitory/staff/roomList/YT20180326', (route) => route.fulfill({ json: { @@ -236,18 +236,15 @@ test('宿舍申请详情:分配记录 + 动态码解密展示', async ({ page }, }), ) - await page.route('**/platform/dormitory/staff/get/pwd*', (route) => - route.fulfill({ json: { code: 0, data: legacyCipherHex('246810') } }), - ) - await page.goto('/check-in/detail') await expect(page.getByText('新工厂宿舍楼')).toBeVisible() await expect(page.getByText('302房间')).toBeVisible() await expect(page.getByText('4号床')).toBeVisible() await expect(page.getByText('指纹已录入')).toBeVisible() await expect(page.getByText('已录入', { exact: true })).toBeVisible() - // 密文经 AES 解出明文动态码 - await expect(page.getByTestId('checkin-lock-code')).toHaveText('246810') + await expect(page.getByTestId('checkin-lock-code')).toHaveCount(0) + await expect(page.getByText('246810')).toHaveCount(0) + expect(requests.some((path) => path.includes('/dormitory/staff/get/pwd'))).toBe(false) }) test('死链回归:home 宫格「宿舍申请」入口落到真实页', async ({ page }) => { diff --git a/smart-h5/e2e/dorm-lock.spec.ts b/smart-h5/e2e/dorm-lock.spec.ts new file mode 100644 index 00000000..88070451 --- /dev/null +++ b/smart-h5/e2e/dorm-lock.spec.ts @@ -0,0 +1,174 @@ +import { expect, test, type Page } from '@playwright/test' + +test('未提供并发版本时禁止修改密码', async ({ page }) => { + await seedLogin(page) + await page.route('**/api/v1/me/locks/status', route => route.fulfill({ json: { + scopeRef: 'scope-readonly', hasCurrentMembership: true, authorizationStatus: 'ACTIVE', + capabilityStatus: 'VERIFIED', cutoverFrozen: false, + } })) + await page.goto('/dorm/lock') + await expect(page.getByRole('button', { name: '修改动态码', exact: true })).toBeDisabled() +}) + +test('版本冲突刷新状态但不自动重新下发密码', async ({ page }) => { + await seedLogin(page) + let writes = 0 + let reads = 0 + await page.route('**/api/v1/me/locks/status', route => { + reads += 1 + return route.fulfill({ json: { + scopeRef: 'scope-conflict', expectedVersion: 'version-' + reads, + hasCurrentMembership: true, authorizationStatus: 'ACTIVE', + capabilityStatus: 'VERIFIED', cutoverFrozen: false, + } }) + }) + await page.route('**/api/v1/me/locks/password-commands', route => { + writes += 1 + return route.fulfill({ status: 409, json: { code: 'VERSION_CONFLICT' } }) + }) + await page.goto('/dorm/lock') + await page.getByRole('button', { name: '修改动态码', exact: true }).click() + await page.getByPlaceholder('请输入6位数字动态码').fill('654321') + await page.getByRole('button', { name: '确定', exact: true }).click() + await expect(page.getByText('请核对最新门锁状态后重新操作。')).toBeVisible() + await expect.poll(() => reads).toBeGreaterThan(1) + await expect(page.getByPlaceholder('请输入6位数字动态码')).toBeHidden() + await expect(page.getByPlaceholder('请输入6位数字动态码')).toHaveValue('') + expect(writes).toBe(1) +}) + +for (const restricted of [ + { label: '已撤权', scopeRef: 'scope-revoked', authorizationStatus: 'REVOKED' }, + { label: '缺少作用域', scopeRef: null, authorizationStatus: 'ACTIVE' }, +]) { + test(`${restricted.label}禁止敏感操作`, async ({ page }) => { + await seedLogin(page) + await page.route('**/api/v1/me/locks/status', route => route.fulfill({ json: { ...restricted, hasCurrentMembership: true, capabilityStatus: 'VERIFIED', + cutoverFrozen: false, dynamicCodeCapabilityVerified: true, dynamicCodeCompliancePassed: true } })) + await page.goto('/dorm/lock') + for (const name of ['修改动态码', '查看动态码', '刷新动态码(人脸识别)']) { + await expect(page.getByRole('button', { name, exact: true })).toBeDisabled() + } + }) +} + +test('冻结状态禁止查看修改和刷新', async ({ page }) => { + await seedLogin(page) + await page.route('**/api/v1/me/locks/status', route => route.fulfill({ json: { scopeRef: 'scope-1', hasCurrentMembership: true, + authorizationStatus: 'ACTIVE', capabilityStatus: 'VERIFIED', cutoverFrozen: true } })) + await page.goto('/dorm/lock') + await expect(page.getByRole('button', { name: '修改动态码', exact: true })).toBeDisabled() + await expect(page.getByRole('button', { name: '查看动态码', exact: true })).toBeDisabled() + await expect(page.getByRole('button', { name: '刷新动态码(人脸识别)' })).toBeDisabled() + await expect(page.getByText('门锁模式已冻结')).toBeVisible() +}) + +/** E2E 仅伪造登录态,不接触真实认证、数据库、网关或门锁。 */ +async function seedLogin(page: Page) { + await page.addInitScript(() => { + localStorage.setItem( + 'xc-access_token', + JSON.stringify({ dataType: 'string', content: 'tok-doorlock-e2e', datetime: Date.now() }), + ) + }) +} + +/** 用浏览器内合成前置摄像头覆盖真实媒体设备。 */ +async function mockFrontCamera(page: Page) { + await page.addInitScript(() => { + Object.defineProperty(navigator, 'mediaDevices', { + configurable: true, + value: { getUserMedia: async () => new MediaStream() }, + }) + Object.defineProperty(HTMLVideoElement.prototype, 'play', { + configurable: true, + value: async function () { + Object.defineProperty(this, 'videoWidth', { configurable: true, get: () => 320 }) + Object.defineProperty(this, 'videoHeight', { configurable: true, get: () => 240 }) + }, + }) + Object.defineProperty(HTMLCanvasElement.prototype, 'getContext', { + configurable: true, + value: () => ({ drawImage() {} }), + }) + Object.defineProperty(HTMLCanvasElement.prototype, 'toDataURL', { + configurable: true, + value: () => 'data:image/jpeg;base64,camera-raw-base64', + }) + }) +} + +test('本人门锁主页仅调用新状态和修改契约,且不在页面展示密码', async ({ page }) => { + await seedLogin(page) + const requests: string[] = [] + page.on('request', (request) => requests.push(new URL(request.url()).pathname)) + await page.route('**/api/v1/me/locks/status', (route) => + route.fulfill({ json: { scopeRef: 'scope-e2e', expectedVersion: 'version-e2e', hasCurrentMembership: true, + authorizationStatus: 'ACTIVE', capabilityStatus: 'VERIFIED', cutoverFrozen: false, + dynamicCodeCapabilityVerified: true, dynamicCodeCompliancePassed: true } }), + ) + let changeBody: Record | undefined + await page.route('**/api/v1/me/locks/password-commands', async (route) => { + changeBody = route.request().postDataJSON() as Record + await route.fulfill({ status: 202, json: { status: 'ACCEPTED' } }) + }) + + await page.goto('/dorm/lock') + await expect(page.getByTestId('lock-code')).toHaveText('******') + await expect(page.getByText('为保护宿舍安全,请完成人脸核验后短时查看当前动态码。')).toBeVisible() + await page.getByRole('button', { name: '修改动态码' }).click() + await page.getByPlaceholder('请输入6位数字动态码').fill('654321') + await page.getByRole('button', { name: '确定' }).click() + + await expect.poll(() => changeBody).toBeDefined() + expect(changeBody).toMatchObject({ password: '654321', expectedVersion: 'version-e2e' }) + expect(changeBody).not.toHaveProperty('badge') + await expect + .poll(() => + page.evaluate(() => { + const values = [...Object.values(localStorage), ...Object.values(sessionStorage)] + return values.some((value) => value.includes('654321')) + }), + ) + .toBe(false) + expect(requests.some((path) => path.includes('/dormitory/staff/get/pwd'))).toBe(false) + expect(requests.some((path) => path.includes('/dormitory/staff/update/lock/pwd'))).toBe(false) +}) + +test('本人门锁状态网络超时只展示可重试错误,不伪造可用动态码', async ({ page }) => { + await seedLogin(page) + await page.route('**/api/v1/me/locks/status', (route) => route.abort('timedout')) + + await page.goto('/dorm/lock') + await expect(page.getByText('门锁状态获取失败')).toBeVisible() + await expect(page.getByRole('button', { name: '重试' })).toBeVisible() + await expect(page.getByTestId('lock-code')).toHaveCount(0) +}) + +test('人脸材料质量失败时不签发验证引用或请求动态码', async ({ page }) => { + await seedLogin(page) + await mockFrontCamera(page) + await page.route('**/app/employee/baseinfo', (route) => + route.fulfill({ json: { code: 0, data: { employeeName: '测试员工', employeeBadge: 'TEST-1' } } }), + ) + await page.route('**/algorithm/out/face/cut', (route) => + route.fulfill({ json: { code: 1, message: '未检测到人脸' } }), + ) + let issued = false + let requested = false + await page.route('**/api/v1/me/locks/dynamic-code-verifications', (route) => { + issued = true + return route.fulfill({ json: { verificationRef: 'must-not-exist' } }) + }) + await page.route('**/api/v1/me/locks/dynamic-code-requests', (route) => { + requested = true + return route.fulfill({ status: 202, json: { commandStatus: 'QUEUED', deviceConfirmed: false } }) + }) + + await page.goto('/dorm/get-code') + await page.getByRole('button', { name: '打开摄像头' }).click() + await page.getByRole('button', { name: '拍照核验' }).click() + await expect(page.getByText('未检测到人脸')).toBeVisible() + expect(issued).toBe(false) + expect(requested).toBe(false) +}) diff --git a/smart-h5/e2e/help-dorm-lock.spec.ts b/smart-h5/e2e/help-dorm-lock.spec.ts index 0d335268..f52bbd4f 100644 --- a/smart-h5/e2e/help-dorm-lock.spec.ts +++ b/smart-h5/e2e/help-dorm-lock.spec.ts @@ -149,26 +149,22 @@ test('水电明细:空态', async ({ page }) => { await expect(page.getByText('暂无数据')).toBeVisible() }) -// ===== 门锁(注入测试 key,用旧算法现场生成真密文) ===== -import { injectTestKey, legacyCipherHex } from './helpers' - -test('门锁:密文解密展示 + 修改三连', async ({ page }) => { +// ===== 门锁(新本人契约;全部为本地 Mock) ===== +test('门锁:状态遮罩展示 + 修改六位校验和无 badge 请求', async ({ page }) => { await seedLogin(page) - await injectTestKey(page) - await mockBaseInfo(page) - let pwdCipher = legacyCipherHex('123456') - await page.route('**/platform/dormitory/staff/get/pwd*', (route) => - route.fulfill({ json: { code: 0, data: pwdCipher } }), + await page.route('**/api/v1/me/locks/status', (route) => + route.fulfill({ json: { scopeRef: 'scope-e2e', expectedVersion: 'version-e2e', hasCurrentMembership: true, + authorizationStatus: 'ACTIVE', capabilityStatus: 'VERIFIED', cutoverFrozen: false, + dynamicCodeCapabilityVerified: true, dynamicCodeCompliancePassed: true } }), ) let updateBody: Record | undefined - await page.route('**/platform/dormitory/staff/update/lock/pwd', async (route) => { + await page.route('**/api/v1/me/locks/password-commands', async (route) => { updateBody = route.request().postDataJSON() as Record - pwdCipher = legacyCipherHex('654321') - await route.fulfill({ json: { code: 0 } }) + await route.fulfill({ status: 202, json: { commandStatus: 'QUEUED', deviceConfirmed: false } }) }) await page.goto('/dorm/lock') - await expect(page.getByTestId('lock-code')).toHaveText('123456') + await expect(page.getByTestId('lock-code')).toHaveText('******') await page.getByRole('button', { name: '修改动态码' }).click() const input = page.getByPlaceholder('请输入6位数字动态码') @@ -176,22 +172,17 @@ test('门锁:密文解密展示 + 修改三连', async ({ page }) => { await page.getByRole('button', { name: '确定' }).click() await expect(page.getByText('请输入6位数字动态码')).toBeVisible() - await input.fill('123456') - await page.getByRole('button', { name: '确定' }).click() - await expect(page.getByText('请输入跟当前动态码不一样的新的动态码')).toBeVisible() - await input.fill('654321') await page.getByRole('button', { name: '确定' }).click() - await expect(page.getByTestId('lock-code')).toHaveText('654321') - expect(updateBody).toEqual({ badge: 'YT20180326', newPwd: '654321' }) + await expect.poll(() => updateBody).toBeDefined() + expect(updateBody).toMatchObject({ password: '654321' }) + expect(updateBody).not.toHaveProperty('badge') }) test('门锁:未入住 alert 回跳 /dorm', async ({ page }) => { await seedLogin(page) - await injectTestKey(page) - await mockBaseInfo(page) - await page.route('**/platform/dormitory/staff/get/pwd*', (route) => - route.fulfill({ json: { code: 0, data: '' } }), + await page.route('**/api/v1/me/locks/status', (route) => + route.fulfill({ json: { hasCurrentMembership: false } }), ) await page.goto('/dorm/lock') @@ -200,53 +191,46 @@ test('门锁:未入住 alert 回跳 /dorm', async ({ page }) => { await page.waitForURL('**/dorm') }) -test('刷新动态码:人脸比对 → 生成 → 回门锁页', async ({ page }) => { +test('刷新动态码:采集材料 → 服务端签发 → 刷新 → 回门锁页', async ({ page }) => { await seedLogin(page) - await injectTestKey(page) await mockBaseInfo(page) await mockFrontCamera(page) let cutBody: Record | undefined - let checkFaceBody: Record | undefined await page.route('**/algorithm/out/face/cut', async (route) => { cutBody = route.request().postDataJSON() as Record await route.fulfill({ json: { code: 0, message: 'success', data: 'cut-base64' } }) }) - // Real checkFace returns data.photoId and no resultData.base64; lock refresh must reuse uploaded base64. - await page.route('**/app/wechat/visit/checkFace', async (route) => { - checkFaceBody = route.request().postDataJSON() as Record - await route.fulfill({ - json: { code: 0, message: 'success', data: { photoId: 'p-lock' } }, - }) - }) - // 哨兵 value 不能被当成 photoId 去回查网关图片(坏图回归防护)。 - let brokenImg = false - await page.route('**/platform/image/view/**', (route) => { - brokenImg = true - return route.fulfill({ status: 404, body: '' }) + let verificationBody: Record | undefined + await page.route('**/api/v1/me/locks/dynamic-code-verifications', async (route) => { + verificationBody = route.request().postDataJSON() as Record + await route.fulfill({ json: { verificationRef: 'verification-e2e' } }) }) let refreshBody: Record | undefined - await page.route('**/platform/dormitory/staff/update/pwd', async (route) => { + await page.route('**/api/v1/me/locks/dynamic-code-requests', async (route) => { refreshBody = route.request().postDataJSON() as Record - await route.fulfill({ json: { code: 0 } }) + await route.fulfill({ status: 202, json: { commandStatus: 'QUEUED', deviceConfirmed: false } }) }) - await page.route('**/platform/dormitory/staff/get/pwd*', (route) => - route.fulfill({ json: { code: 0, data: legacyCipherHex('888888') } }), + await page.route('**/api/v1/me/locks/status', (route) => + route.fulfill({ json: { scopeRef: 'scope-e2e', expectedVersion: 'version-e2e', hasCurrentMembership: true, + authorizationStatus: 'ACTIVE', capabilityStatus: 'VERIFIED', cutoverFrozen: false, + dynamicCodeCapabilityVerified: true, dynamicCodeCompliancePassed: true } }), ) await page.goto('/dorm/get-code') await expect(page.locator('input[type="file"]')).toHaveCount(0) await page.getByRole('button', { name: '打开摄像头' }).click() - await page.getByRole('button', { name: '拍照识别' }).click() - await expect(page.getByText('人脸对比成功')).toBeVisible() + await page.getByRole('button', { name: '拍照核验' }).click() + await expect(page.getByText('人脸采集完成')).toBeVisible() expect(cutBody).toEqual({ imageData: 'camera-raw-base64' }) - expect(checkFaceBody).toEqual({ visitorPhoto: 'cut-base64' }) - expect(brokenImg).toBe(false) await page.getByRole('button', { name: '生成动态码' }).click() - await expect(page.getByText('刷新动态码成功!')).toBeVisible() - expect(refreshBody).toEqual({ badge: 'YT20180326', facePic: 'cut-base64' }) + await expect(page.getByText('刷新请求已受理,门锁生效结果待确认。')).toBeVisible() + expect(verificationBody).toMatchObject({ faceEvidence: 'cut-base64' }) + expect(verificationBody).not.toHaveProperty('badge') + expect(refreshBody).toMatchObject({ verificationRef: 'verification-e2e' }) + expect(refreshBody).not.toHaveProperty('badge') await page.getByRole('button', { name: '确定' }).click() await page.waitForURL('**/dorm/lock') - await expect(page.getByTestId('lock-code')).toHaveText('888888') + await expect(page.getByTestId('lock-code')).toHaveText('******') }) test('帮助中心:上拉加载第二页(追加不重复)', async ({ page }) => { @@ -269,40 +253,27 @@ test('帮助中心:上拉加载第二页(追加不重复)', async ({ page await expect(page.locator('button', { hasText: /^问题/ })).toHaveCount(11) }) -test('门锁:修改失败 alert、解密失败 toast、接口业务失败重试块', async ({ page }) => { +test('门锁:状态与修改接口失败都给出受控错误态', async ({ page }) => { await seedLogin(page) - await injectTestKey(page) - await mockBaseInfo(page) - // 1) 解密失败(非法 hex)→ ****** + toast - await page.route('**/platform/dormitory/staff/get/pwd*', (route) => - route.fulfill({ json: { code: 0, data: 'not-valid-hex-!!' } }), - ) - await page.goto('/dorm/lock') - await expect(page.getByTestId('lock-code')).toHaveText('******') - await expect(page.getByText('动态码解析失败')).toBeVisible() - // 解密失败时修改按钮禁用 - await expect(page.getByRole('button', { name: '修改动态码' })).toBeDisabled() - - // 2) 接口业务失败 → 重试块(不显示伪装码) - await page.unroute('**/platform/dormitory/staff/get/pwd*') - await page.route('**/platform/dormitory/staff/get/pwd*', (route) => - route.fulfill({ json: { code: 1, msg: '动态码服务繁忙' } }), + await page.route('**/api/v1/me/locks/status', (route) => + route.fulfill({ status: 503, json: { code: 'LOCK_UNAVAILABLE', message: '门锁服务暂不可用' } }), ) await page.goto('/dorm/lock') - await expect(page.getByText('动态码服务繁忙')).toBeVisible() + await expect(page.getByText('门锁状态获取失败')).toBeVisible() await expect(page.getByRole('button', { name: '重试' })).toBeVisible() - // 3) 修改失败 alert - await page.unroute('**/platform/dormitory/staff/get/pwd*') - await page.route('**/platform/dormitory/staff/get/pwd*', (route) => - route.fulfill({ json: { code: 0, data: legacyCipherHex('123456') } }), + await page.unroute('**/api/v1/me/locks/status') + await page.route('**/api/v1/me/locks/status', (route) => + route.fulfill({ json: { scopeRef: 'scope-e2e', expectedVersion: 'version-e2e', hasCurrentMembership: true, + authorizationStatus: 'ACTIVE', capabilityStatus: 'VERIFIED', cutoverFrozen: false, + dynamicCodeCapabilityVerified: true, dynamicCodeCompliancePassed: true } }), ) - await page.route('**/platform/dormitory/staff/update/lock/pwd', (route) => - route.fulfill({ json: { code: 1, msg: '动态码修改过于频繁' } }), + await page.route('**/api/v1/me/locks/password-commands', (route) => + route.fulfill({ status: 503, json: { code: 'LOCK_UNAVAILABLE', message: '门锁服务暂不可用' } }), ) await page.goto('/dorm/lock') await page.getByRole('button', { name: '修改动态码' }).click() await page.getByPlaceholder('请输入6位数字动态码').fill('654321') await page.getByRole('button', { name: '确定' }).click() - await expect(page.getByText('动态码修改过于频繁')).toBeVisible() + await expect(page.getByText('请求失败:HTTP 503')).toBeVisible() }) diff --git a/smart-h5/next.config.ts b/smart-h5/next.config.ts index 8a7a6fed..450d7717 100644 --- a/smart-h5/next.config.ts +++ b/smart-h5/next.config.ts @@ -85,10 +85,10 @@ const nextConfig: NextConfig = { const configuredTarget = process.env.API_PROXY_TARGET if (process.env.NODE_ENV !== 'development' && !configuredTarget) return [] const target = configuredTarget ?? 'https://xuchang.szyuto.com' - return GATEWAY_MODULES.map((module) => ({ + return [{ source: '/api/v1/:path*', destination: `${target}/api/v1/:path*` }, ...GATEWAY_MODULES.map((module) => ({ source: `/${module}/:path*`, destination: `${target}/${module}/:path*`, - })) + }))] }, // 给所有页面同时挂上:强制 CSP(仅三条零误报指令,立即拦截点击劫持 / base // 劫持 / 表单越权)+ 仅报告模式 CSP(继续观察 script/style 来源,不拦截)。 diff --git a/smart-h5/src/app/check-in/detail/page.tsx b/smart-h5/src/app/check-in/detail/page.tsx index 23ee060c..ecdc64f1 100644 --- a/smart-h5/src/app/check-in/detail/page.tsx +++ b/smart-h5/src/app/check-in/detail/page.tsx @@ -6,9 +6,7 @@ import { PageShell } from '@/components/page-shell' import { SegmentTabs } from '@/components/segment-tabs' import { useRequireAuth } from '@/features/auth/use-require-auth' import { getEmployeeBaseInfo } from '@/features/employee/api' -import { getLockPwd } from '@/features/dorm/api' import { getCheckInRecords } from '@/features/dorm-services/api' -import { decryptFromHex } from '@/lib/crypto/aes' /** Feature codes 1/3 = success, 0/2/4 = failure (legacy icon rule). */ function featureOk(code: number | undefined): boolean { @@ -25,15 +23,7 @@ function FeatureRow({ label, ok, desc }: { label: string; ok: boolean; desc?: st ) } -function safeDecrypt(cipher: string): string { - try { - return decryptFromHex(cipher) - } catch { - return '' - } -} - -/** Allocation result + door-lock identity-feature status. */ +/** 分配结果与门锁身份特征状态;密码仅能在本人门锁页完成二次核验后短时查看。 */ export default function CheckInDetailPage() { const authorized = useRequireAuth() @@ -49,17 +39,9 @@ export default function CheckInDetailPage() { queryFn: () => getCheckInRecords(badge as string), enabled: authorized && badge !== undefined, }) - const pwd = useQuery({ - queryKey: ['lock-pwd', badge], - queryFn: () => getLockPwd(badge as string), - enabled: authorized && badge !== undefined, - }) - if (!authorized) return null const rows = records.data?.code === 0 ? (records.data.data ?? []) : [] - const cipher = pwd.data?.code === 0 ? (pwd.data.data ?? '') : '' - const plainCode = cipher ? safeDecrypt(cipher) : '' const recordsFailed = records.isError || (records.isSuccess && records.data.code !== 0) return ( @@ -68,8 +50,8 @@ export default function CheckInDetailPage() { { - const [recordsResult, pwdResult] = await Promise.all([records.refetch(), pwd.refetch()]) - Toast.show(recordsResult.error || pwdResult.error ? '更新失败' : '更新成功') + const recordsResult = await records.refetch() + Toast.show(recordsResult.error ? '更新失败' : '更新成功') }} > {recordsFailed ? ( @@ -115,12 +97,6 @@ export default function CheckInDetailPage() { desc={record.lockPwd?.dynamicCode === 3 ? '已录入' : record.lockPwd?.dynamicDesc} /> - - {(record.lockPwd?.fingerprintCode ?? 0) !== 0 && plainCode && ( -

- {plainCode} -

- )} ))} diff --git a/smart-h5/src/app/dorm/get-code/page.test.ts b/smart-h5/src/app/dorm/get-code/page.test.ts index 739d81c8..784cbdfc 100644 --- a/smart-h5/src/app/dorm/get-code/page.test.ts +++ b/smart-h5/src/app/dorm/get-code/page.test.ts @@ -11,17 +11,22 @@ const routerMock = vi.hoisted(() => ({ replace: vi.fn(), })) +const searchMock = vi.hoisted(() => ({ value: '' })) + const visitorApiMock = vi.hoisted(() => ({ - checkFace: vi.fn(), faceCut: vi.fn(), })) const dormApiMock = vi.hoisted(() => ({ - refreshLockPwd: vi.fn(), + issueMyLockDynamicCodeVerification: vi.fn(), + requestMyLockDynamicCode: vi.fn(), + issueMyLockPasswordRevealVerification: vi.fn(), + revealMyLockPassword: vi.fn(), })) vi.mock('next/navigation', () => ({ useRouter: () => routerMock, + useSearchParams: () => new URLSearchParams(searchMock.value), })) vi.mock('next/image', async () => { @@ -56,12 +61,14 @@ vi.mock('@/features/employee/api', () => ({ })) vi.mock('@/features/visitor/api', () => ({ - checkFace: visitorApiMock.checkFace, faceCut: visitorApiMock.faceCut, })) vi.mock('@/features/dorm/api', () => ({ - refreshLockPwd: dormApiMock.refreshLockPwd, + issueMyLockDynamicCodeVerification: dormApiMock.issueMyLockDynamicCodeVerification, + requestMyLockDynamicCode: dormApiMock.requestMyLockDynamicCode, + issueMyLockPasswordRevealVerification: dormApiMock.issueMyLockPasswordRevealVerification, + revealMyLockPassword: dormApiMock.revealMyLockPassword, })) const stopTrack = vi.fn() @@ -86,9 +93,15 @@ function renderGetCodePage() { } beforeEach(() => { + searchMock.value = '' visitorApiMock.faceCut.mockResolvedValue({ code: 0, data: 'cut-face-base64' }) - visitorApiMock.checkFace.mockResolvedValue({ code: 0, data: { photoId: 'photo-1' } }) - dormApiMock.refreshLockPwd.mockResolvedValue({ code: 0 }) + dormApiMock.issueMyLockDynamicCodeVerification.mockResolvedValue({ + code: 0, + data: { verificationRef: 'verification-1' }, + }) + dormApiMock.requestMyLockDynamicCode.mockResolvedValue({ code: 0 }) + dormApiMock.issueMyLockPasswordRevealVerification.mockReset() + dormApiMock.revealMyLockPassword.mockReset() stopTrack.mockClear() getUserMedia.mockResolvedValue({ getTracks: () => [{ stop: stopTrack }] }) drawImage.mockClear() @@ -128,8 +141,8 @@ afterEach(() => { routerMock.push.mockClear() routerMock.replace.mockClear() visitorApiMock.faceCut.mockReset() - visitorApiMock.checkFace.mockReset() - dormApiMock.refreshLockPwd.mockReset() + dormApiMock.issueMyLockDynamicCodeVerification.mockReset() + dormApiMock.requestMyLockDynamicCode.mockReset() getUserMedia.mockReset() }) @@ -150,22 +163,56 @@ describe('门锁动态码刷新人脸采集', () => { expect(container.querySelector('input[type="file"]')).toBeNull() }) - it('generates the lock code with the captured face and employee badge', async () => { + it('uses service-side verification before the dynamic-code request without sending the employee badge', async () => { renderGetCodePage() fireEvent.click(await screen.findByTestId('lock-face-camera-start')) fireEvent.click(await screen.findByTestId('lock-face-camera-capture')) - expect(await screen.findByText('人脸识别通过')).toBeTruthy() + expect(await screen.findByText('人脸采集完成')).toBeTruthy() fireEvent.click(screen.getByTestId('lock-face-camera-generate')) await waitFor(() => - expect(dormApiMock.refreshLockPwd).toHaveBeenCalledWith({ - badge: 'YT027468', - facePic: 'cut-face-base64', - }), + expect(dormApiMock.issueMyLockDynamicCodeVerification).toHaveBeenCalledWith( + expect.objectContaining({ faceEvidence: 'cut-face-base64' }), + ), + ) + await waitFor(() => + expect(dormApiMock.requestMyLockDynamicCode).toHaveBeenCalledWith( + expect.objectContaining({ verificationRef: 'verification-1' }), + ), ) + expect(dormApiMock.issueMyLockPasswordRevealVerification).not.toHaveBeenCalled() + expect(dormApiMock.revealMyLockPassword).not.toHaveBeenCalled() await waitFor(() => expect(routerMock.replace).toHaveBeenCalledWith('/dorm/lock')) }) + + it('uses the fixed reveal route for the view flow and never falls back to refresh', async () => { + searchMock.value = 'flow=reveal' + dormApiMock.issueMyLockPasswordRevealVerification.mockResolvedValue({ + code: 0, + data: { verificationRef: 'verification-reveal' }, + }) + dormApiMock.revealMyLockPassword.mockResolvedValue({ code: 0, data: { password: '123456' } }) + renderGetCodePage() + + expect(await screen.findByRole('heading', { name: '查看动态码' })).toBeTruthy() + fireEvent.click(await screen.findByTestId('lock-face-camera-start')) + fireEvent.click(await screen.findByTestId('lock-face-camera-capture')) + fireEvent.click(await screen.findByTestId('lock-face-camera-generate')) + + await waitFor(() => + expect(dormApiMock.issueMyLockPasswordRevealVerification).toHaveBeenCalledWith( + expect.objectContaining({ faceEvidence: 'cut-face-base64' }), + ), + ) + await waitFor(() => + expect(dormApiMock.revealMyLockPassword).toHaveBeenCalledWith( + expect.objectContaining({ verificationRef: 'verification-reveal' }), + ), + ) + expect(dormApiMock.issueMyLockDynamicCodeVerification).not.toHaveBeenCalled() + expect(dormApiMock.requestMyLockDynamicCode).not.toHaveBeenCalled() + }) }) diff --git a/smart-h5/src/app/dorm/get-code/page.tsx b/smart-h5/src/app/dorm/get-code/page.tsx index d9f8d030..9d538236 100644 --- a/smart-h5/src/app/dorm/get-code/page.tsx +++ b/smart-h5/src/app/dorm/get-code/page.tsx @@ -1,21 +1,43 @@ 'use client' import { useQuery } from '@tanstack/react-query' import { Dialog, ErrorBlock } from 'antd-mobile' -import { useRouter } from 'next/navigation' -import { useState } from 'react' +import { useRouter, useSearchParams } from 'next/navigation' +import { Suspense, useState } from 'react' import { toUserMessage } from '@/lib/format/error-message' import { useRequireAuth } from '@/features/auth/use-require-auth' import { getEmployeeBaseInfo } from '@/features/employee/api' -import { refreshLockPwd } from '@/features/dorm/api' +import { + issueMyLockDynamicCodeVerification, + issueMyLockPasswordRevealVerification, + revealMyLockPassword, + requestMyLockDynamicCode, +} from '@/features/dorm/api' import { LockFaceCamera } from '@/features/dorm/lock-face-camera' import styles from './page.module.css' -/** 通过本机人脸核验后刷新宿舍门锁动态码。 */ +/** + * 查询参数仅用于选择本地展示的查看/刷新页面模式;Next 静态生成时必须由 Suspense 隔离 + * useSearchParams 的客户端渲染回退。 + */ export default function GetCodePage() { + return ( + + + + ) +} + +/** 采集人脸材料后,由服务端核验本人范围并刷新或短时查看宿舍门锁动态码。 */ +function GetCodeContent() { const authorized = useRequireAuth() const router = useRouter() - // facePic 是传给 checkFace 的 base64;常规 checkFace 响应只返回 data.photoId。 - const [facePic, setFacePic] = useState('') + const searchParams = useSearchParams() + // 此参数仅影响前端展示和选择固定端点;服务端仍由路由确定用途,不能相信客户端参数授权。 + const flow = searchParams.get('flow') === 'reveal' ? 'reveal' : 'dynamic' + const revealing = flow === 'reveal' + // 仅短时保存待服务端核验的材料;绝不从中派生人员、房间或“核验已通过”的客户端结论。 + const [faceEvidence, setFaceEvidence] = useState('') + const [captureVersion, setCaptureVersion] = useState(0) const [submitting, setSubmitting] = useState(false) const baseInfo = useQuery({ @@ -28,20 +50,54 @@ export default function GetCodePage() { const baseInfoFailed = baseInfo.isError || (baseInfo.isSuccess && baseInfo.data.code !== 0) async function handleGenerate() { - if (!facePic || !badge) return + if (!faceEvidence) return setSubmitting(true) try { - const res = await refreshLockPwd({ badge, facePic }) + const requestId = createRequestId() + const verification = revealing + ? await issueMyLockPasswordRevealVerification({ requestId, faceEvidence }) + : await issueMyLockDynamicCodeVerification({ requestId, faceEvidence }) + const verificationRef = readVerificationRef(verification) + if (verification.code !== 0 || !verificationRef) { + void Dialog.alert({ + title: '错误', + content: verification.msg ?? verification.message ?? '本人核验未通过,请重新拍照', + }) + return + } + + const res = revealing + ? await revealMyLockPassword({ + requestId, + idempotencyKey: `my-lock-reveal-${requestId}`, + verificationRef, + }) + : await requestMyLockDynamicCode({ + requestId, + idempotencyKey: `my-lock-dynamic-${requestId}`, + verificationRef, + }) if (res.code === 0) { - void Dialog.alert({ content: '刷新动态码成功!', confirmText: '确定' }).then(() => + const password = revealing ? readRevealedPassword(res) : undefined + if (revealing && !password) { + void Dialog.alert({ title: '错误', content: '动态码读取结果异常,请重新核验' }) + return + } + void Dialog.alert({ + content: revealing ? `当前动态码:${password}` : '刷新请求已受理,门锁生效结果待确认。', + confirmText: '确定', + }).then(() => router.replace('/dorm/lock'), ) } else { - void Dialog.alert({ title: '错误', content: res.msg ?? res.message ?? '刷新失败' }) + void Dialog.alert({ title: '错误', content: res.msg ?? res.message ?? (revealing ? '查看失败' : '刷新失败') }) } } catch (error) { - void Dialog.alert({ title: '错误', content: toUserMessage(error, '刷新失败') }) + void Dialog.alert({ title: '错误', content: toUserMessage(error, revealing ? '查看失败' : '刷新失败') }) } finally { + // 无论服务端是否接受,都不在页面状态中保留原始人脸材料或预览。 + setFaceEvidence('') + setCaptureVersion((version) => version + 1) setSubmitting(false) } } @@ -51,7 +107,7 @@ export default function GetCodePage() { if (baseInfoFailed) { return (
- router.back()} onClose={() => router.replace('/dorm/lock')} /> + router.back()} onClose={() => router.replace('/dorm/lock')} title={revealing ? '查看动态码' : '刷新动态码'} />
) } -function FaceScanTopbar({ onBack, onClose }: { onBack: () => void; onClose: () => void }) { +/** 当前浏览器只生成请求关联标识,不生成或推断任何权限范围。 */ +function createRequestId(): string { + if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') { + return crypto.randomUUID() + } + return `my-lock-${Date.now()}-${Math.random().toString(36).slice(2)}` +} + +/** 兼容门锁服务端以字符串或对象包装一次性引用的受控响应。 */ +function readVerificationRef(response: { data?: { verificationRef?: string } | string }): string | undefined { + if (typeof response.data === 'string') return response.data || undefined + return response.data?.verificationRef || undefined +} + +/** 查看结果只在当前异步栈中读取后传给短时弹窗,禁止写入 query cache、store 或页面状态。 */ +function readRevealedPassword(response: { data?: unknown }): string | undefined { + if (typeof response.data === 'string') return response.data || undefined + if (response.data && typeof response.data === 'object' && 'password' in response.data) { + const password = (response.data as { password?: unknown }).password + return typeof password === 'string' && password ? password : undefined + } + return undefined +} + +function FaceScanTopbar({ onBack, onClose, title }: { onBack: () => void; onClose: () => void; title: string }) { return (
-

刷新动态码

+

{title}

diff --git a/smart-h5/src/app/dorm/lock/page.tsx b/smart-h5/src/app/dorm/lock/page.tsx index 45eb2d58..4b07581c 100644 --- a/smart-h5/src/app/dorm/lock/page.tsx +++ b/smart-h5/src/app/dorm/lock/page.tsx @@ -3,24 +3,13 @@ import { useQuery } from '@tanstack/react-query' import { Dialog, ErrorBlock, SpinLoading, Toast } from 'antd-mobile' import { useRouter } from 'next/navigation' import { useEffect, useRef, useState } from 'react' +import { ApiError } from '@/lib/api/http' import { toUserMessage } from '@/lib/format/error-message' import { PageShell } from '@/components/page-shell' import { SMS_INPUT_CLASS } from '@/components/sms-code-field' import { useRequireAuth } from '@/features/auth/use-require-auth' -import { getEmployeeBaseInfo } from '@/features/employee/api' -import { getLockPwd, updateLockPwd } from '@/features/dorm/api' -import { decryptFromHex } from '@/lib/crypto/aes' - -/** Decrypt result with an explicit failure reason instead of silent ******. */ -function safeDecrypt(cipher: string): { code: string; error: string | null } { - try { - const plain = decryptFromHex(cipher) - return plain ? { code: plain, error: null } : { code: '', error: '动态码解析失败' } - } catch { - // getKey() throws when securityEncodeKey is missing — a deploy-config error. - return { code: '', error: '加密密钥未配置,请联系管理员' } - } -} +import { changeMyLockPassword, getMyLockStatus } from '@/features/dorm/api' +import { createLockStateController, resolveLockDisplayState, type LockStateController } from '@/features/dorm/lock-state' function RetryBlock({ description, onRetry }: { description: string; onRetry: () => void }) { return ( @@ -37,7 +26,7 @@ function RetryBlock({ description, onRetry }: { description: string; onRetry: () ) } -/** Door-lock dynamic code: decrypted display + change dialog + face refresh entry. */ +/** 本人门锁页:不自动读取或解密密码,敏感查看必须进入受控人脸核验流程。 */ export default function LockPage() { const authorized = useRequireAuth() const router = useRouter() @@ -45,100 +34,150 @@ export default function LockPage() { const [newPwd, setNewPwd] = useState('') const [submitting, setSubmitting] = useState(false) const notCheckedInRef = useRef(false) + const controllerRef = useRef(null) + + // 每次挂载独立创建,兼容 StrictMode 的清理后重新挂载。 + useEffect(() => { + const controller = createLockStateController() + controllerRef.current = controller + return () => { controller.destroy(); controllerRef.current = null } + }, []) - const baseInfo = useQuery({ - queryKey: ['employee', 'baseinfo'], - queryFn: getEmployeeBaseInfo, + const status = useQuery({ + queryKey: ['my-lock-status'], + queryFn: getMyLockStatus, enabled: authorized, }) - const badge = baseInfo.data?.code === 0 ? baseInfo.data.data?.employeeBadge : undefined - const pwd = useQuery({ - queryKey: ['lock-pwd', badge], - queryFn: () => getLockPwd(badge as string), - enabled: authorized && badge !== undefined, + const facts = status.data?.code === 0 ? status.data.data : undefined + const display = resolveLockDisplayState({ + hasCurrentMembership: facts?.hasCurrentMembership === true, + authorizationStatus: facts?.authorizationStatus, + capabilityStatus: facts?.capabilityStatus ?? 'UNVERIFIED', + cutoverFrozen: facts?.cutoverFrozen !== false, + dynamicCodeCapabilityVerified: facts?.dynamicCodeCapabilityVerified, + dynamicCodeCompliancePassed: facts?.dynamicCodeCompliancePassed, }) + const scopeRef = typeof facts?.scopeRef === 'string' && facts.scopeRef.trim() ? facts.scopeRef : null + const ready = authorized && status.isSuccess && !status.isFetching && !status.isError && !!scopeRef + const expectedVersion = typeof facts?.expectedVersion === 'string' + && /^[A-Za-z0-9_-]{1,128}$/.test(facts.expectedVersion) ? facts.expectedVersion : null + const canModify = ready && display.canModifyPassword && !!expectedVersion - const pwdFailed = pwd.isError || (pwd.isSuccess && pwd.data.code !== 0) - const cipher = pwd.data?.code === 0 ? (pwd.data.data ?? '') : '' - const decrypted = cipher ? safeDecrypt(cipher) : { code: '', error: null } + useEffect(() => { + const controller = controllerRef.current + if (!controller) return + // 即使住宿标识相同,状态重新读取也先废弃旧请求票据。 + controller.setScope(null) + controller.setFrozen(facts?.cutoverFrozen !== false) + if (!authorized) controller.logout() + else if (ready && display.canModifyPassword && scopeRef) controller.setScope({ scopeRef }) + return () => { controller.setScope(null) } + }, [authorized, ready, scopeRef, facts, display.canModifyPassword]) - // Not checked in: the gateway returns an empty pwd with a success code. + // 是否有当前住宿只能由服务端状态决定,不能从旧密码是否为空推断。 useEffect(() => { - if (pwd.isSuccess && pwd.data.code === 0 && !pwd.data.data && !notCheckedInRef.current) { + if (status.isSuccess && status.data.code === 0 && !status.data.data?.hasCurrentMembership && !notCheckedInRef.current) { notCheckedInRef.current = true void Dialog.alert({ content: '您暂未入住智能宿舍,请联系宿管入住!', confirmText: '确定', }).then(() => router.replace('/dorm')) } - }, [pwd.isSuccess, pwd.data, router]) - - useEffect(() => { - if (decrypted.error) Toast.show(decrypted.error) - }, [decrypted.error]) + }, [router, status.data, status.isSuccess]) async function handleEditSubmit() { - if (submitting) return + if (submitting || !canModify || !expectedVersion) return if (!/^[0-9]{6}$/.test(newPwd)) { Toast.show('请输入6位数字动态码') return } - if (newPwd === decrypted.code) { - Toast.show('请输入跟当前动态码不一样的新的动态码') - return - } + const controller = controllerRef.current + const operation = controller?.beginPasswordChange() + if (!controller || !operation?.accepted) return setSubmitting(true) try { - const res = await updateLockPwd({ badge: badge as string, newPwd }) + const requestId = createRequestId() + const res = await changeMyLockPassword({ + requestId, + idempotencyKey: `my-lock-change-${requestId}`, + password: newPwd, + expectedVersion, + }) + // 卸载、冻结、调宿或重新查询之后的迟到结果不可继续操作界面。 + if (!controller.finish(operation.ticket)) return if (res.code === 0) { setEditVisible(false) setNewPwd('') - await pwd.refetch() + void Dialog.alert({ content: '修改请求已提交,请以处理结果为准', confirmText: '确定' }) } else { void Dialog.alert({ title: '错误', content: res.msg ?? res.message ?? '修改失败' }) } } catch (error) { - void Dialog.alert({ title: '错误', content: toUserMessage(error, '修改失败') }) + if (!controller.finish(operation.ticket)) return + if (error instanceof ApiError && error.status === 409) { + // 并发冲突只刷新事实,不自动重试密码写入。 + setEditVisible(false) + void status.refetch() + void Dialog.alert({ title: '状态已变化', content: '请核对最新门锁状态后重新操作。' }) + } else { + void Dialog.alert({ title: '错误', content: toUserMessage(error, '修改失败') }) + } } finally { - setSubmitting(false) + if (controllerRef.current === controller) { + setNewPwd('') + setSubmitting(false) + } } } if (!authorized) return null - const baseInfoFailed = baseInfo.isError || (baseInfo.isSuccess && baseInfo.data.code !== 0) + const statusFailed = status.isError || (status.isSuccess && status.data.code !== 0) return ( - {baseInfoFailed ? ( - void baseInfo.refetch()} /> - ) : pwdFailed ? ( + {statusFailed ? ( void pwd.refetch()} + description={status.data?.message ?? status.data?.msg ?? '门锁状态获取失败'} + onRetry={() => void status.refetch()} /> - ) : baseInfo.isPending || pwd.isPending ? ( + ) : status.isPending ? (
) : (

你的门锁动态码

+

{scopeRef ? display.text : '门锁状态待核实'}

- {decrypted.code || '******'} + ****** +

+ +

+ 为保护宿舍安全,请完成人脸核验后短时查看当前动态码。

+ +