feat: add ERP agent pet bridge and startup guide
This commit is contained in:
@@ -0,0 +1,375 @@
|
||||
SET XACT_ABORT ON;
|
||||
|
||||
IF OBJECT_ID(N'dbo.p_agent_command_idempotency', N'U') IS NULL
|
||||
BEGIN
|
||||
CREATE TABLE dbo.p_agent_command_idempotency
|
||||
(
|
||||
id BIGINT IDENTITY(1, 1) NOT NULL,
|
||||
account_book NVARCHAR(64) NOT NULL,
|
||||
subsystem_id NVARCHAR(32) NOT NULL,
|
||||
user_id NVARCHAR(64) NOT NULL,
|
||||
command_name VARCHAR(128) NOT NULL,
|
||||
idempotency_key VARCHAR(128) NOT NULL,
|
||||
input_fingerprint CHAR(64) NOT NULL,
|
||||
status TINYINT NOT NULL,
|
||||
result_code VARCHAR(128) NULL,
|
||||
record_id NVARCHAR(128) NULL,
|
||||
transaction_evidence_id VARCHAR(128) NULL,
|
||||
business_audit_id VARCHAR(128) NULL,
|
||||
created_at_utc DATETIME2(3) NOT NULL
|
||||
CONSTRAINT DF_p_agent_command_idempotency_created DEFAULT SYSUTCDATETIME(),
|
||||
completed_at_utc DATETIME2(3) NULL,
|
||||
row_version ROWVERSION NOT NULL,
|
||||
CONSTRAINT PK_p_agent_command_idempotency PRIMARY KEY CLUSTERED (id),
|
||||
CONSTRAINT UQ_p_agent_command_idempotency_scope UNIQUE
|
||||
(account_book, subsystem_id, user_id, command_name, idempotency_key),
|
||||
CONSTRAINT CK_p_agent_command_idempotency_status CHECK (status IN (0, 1, 2)),
|
||||
CONSTRAINT CK_p_agent_command_idempotency_fingerprint_length
|
||||
CHECK (LEN(input_fingerprint) = 64)
|
||||
);
|
||||
|
||||
CREATE INDEX IX_p_agent_command_idempotency_created
|
||||
ON dbo.p_agent_command_idempotency(created_at_utc)
|
||||
INCLUDE(status, completed_at_utc);
|
||||
END;
|
||||
|
||||
IF OBJECT_ID(N'dbo.p_agent_business_audit', N'U') IS NULL
|
||||
BEGIN
|
||||
CREATE TABLE dbo.p_agent_business_audit
|
||||
(
|
||||
business_audit_id VARCHAR(128) NOT NULL,
|
||||
transaction_evidence_id VARCHAR(128) NOT NULL,
|
||||
correlation_id VARCHAR(128) NOT NULL,
|
||||
idempotency_id BIGINT NOT NULL,
|
||||
account_book NVARCHAR(64) NOT NULL,
|
||||
subsystem_id NVARCHAR(32) NOT NULL,
|
||||
user_id NVARCHAR(64) NOT NULL,
|
||||
command_name VARCHAR(128) NOT NULL,
|
||||
module_code NVARCHAR(64) NOT NULL,
|
||||
action_name VARCHAR(64) NOT NULL,
|
||||
record_id NVARCHAR(128) NOT NULL,
|
||||
input_fingerprint CHAR(64) NOT NULL,
|
||||
created_at_utc DATETIME2(3) NOT NULL
|
||||
CONSTRAINT DF_p_agent_business_audit_created DEFAULT SYSUTCDATETIME(),
|
||||
CONSTRAINT PK_p_agent_business_audit
|
||||
PRIMARY KEY CLUSTERED (business_audit_id),
|
||||
CONSTRAINT UQ_p_agent_business_audit_transaction
|
||||
UNIQUE (transaction_evidence_id),
|
||||
CONSTRAINT FK_p_agent_business_audit_idempotency
|
||||
FOREIGN KEY (idempotency_id)
|
||||
REFERENCES dbo.p_agent_command_idempotency(id),
|
||||
CONSTRAINT CK_p_agent_business_audit_fingerprint_length
|
||||
CHECK (LEN(input_fingerprint) = 64)
|
||||
);
|
||||
|
||||
CREATE INDEX IX_p_agent_business_audit_correlation
|
||||
ON dbo.p_agent_business_audit(correlation_id, created_at_utc)
|
||||
INCLUDE(module_code, action_name, record_id);
|
||||
END;
|
||||
|
||||
IF OBJECT_ID(N'dbo.p_agent_business_source_document', N'U') IS NULL
|
||||
BEGIN
|
||||
CREATE TABLE dbo.p_agent_business_source_document
|
||||
(
|
||||
id BIGINT IDENTITY(1, 1) NOT NULL,
|
||||
business_audit_id VARCHAR(128) NOT NULL,
|
||||
document_kind VARCHAR(8) NOT NULL,
|
||||
sanitized_filename NVARCHAR(128) NOT NULL,
|
||||
source_sha256 CHAR(64) NOT NULL,
|
||||
extraction_sha256 CHAR(64) NOT NULL,
|
||||
preprocess_contract VARCHAR(64) NOT NULL,
|
||||
size_bytes BIGINT NOT NULL,
|
||||
created_at_utc DATETIME2(3) NOT NULL
|
||||
CONSTRAINT DF_p_agent_business_source_created DEFAULT SYSUTCDATETIME(),
|
||||
CONSTRAINT PK_p_agent_business_source_document PRIMARY KEY CLUSTERED (id),
|
||||
CONSTRAINT UQ_p_agent_business_source_document
|
||||
UNIQUE (business_audit_id, source_sha256),
|
||||
CONSTRAINT FK_p_agent_business_source_audit
|
||||
FOREIGN KEY (business_audit_id)
|
||||
REFERENCES dbo.p_agent_business_audit(business_audit_id),
|
||||
CONSTRAINT CK_p_agent_business_source_kind
|
||||
CHECK (document_kind IN ('image', 'file')),
|
||||
CONSTRAINT CK_p_agent_business_source_hash
|
||||
CHECK (LEN(source_sha256) = 64),
|
||||
CONSTRAINT CK_p_agent_business_extraction_hash
|
||||
CHECK (LEN(extraction_sha256) = 64
|
||||
AND extraction_sha256 COLLATE Latin1_General_100_BIN2
|
||||
NOT LIKE '%[^0-9a-f]%'),
|
||||
CONSTRAINT CK_p_agent_business_preprocess_contract
|
||||
CHECK
|
||||
(
|
||||
(document_kind COLLATE Latin1_General_100_BIN2 = 'image'
|
||||
AND preprocess_contract COLLATE Latin1_General_100_BIN2 =
|
||||
'minimax_vlm_0.0.4'
|
||||
AND
|
||||
(
|
||||
LOWER(RIGHT(sanitized_filename, 4)) IN (N'.png', N'.jpg')
|
||||
OR LOWER(RIGHT(sanitized_filename, 5)) IN (N'.jpeg', N'.webp')
|
||||
))
|
||||
OR
|
||||
(document_kind COLLATE Latin1_General_100_BIN2 = 'file'
|
||||
AND
|
||||
(
|
||||
(LOWER(RIGHT(sanitized_filename, 4)) = N'.pdf'
|
||||
AND preprocess_contract COLLATE Latin1_General_100_BIN2 =
|
||||
'pdfium_minimax_pages_v1')
|
||||
OR
|
||||
(LOWER(RIGHT(sanitized_filename, 4)) = N'.csv'
|
||||
AND preprocess_contract COLLATE Latin1_General_100_BIN2 =
|
||||
'document_sandbox_csv_v1')
|
||||
OR
|
||||
(LOWER(RIGHT(sanitized_filename, 5)) = N'.xlsx'
|
||||
AND preprocess_contract COLLATE Latin1_General_100_BIN2 =
|
||||
'document_sandbox_xlsx_v1')
|
||||
))
|
||||
),
|
||||
CONSTRAINT CK_p_agent_business_source_size
|
||||
CHECK (size_bytes BETWEEN 1 AND 12582912)
|
||||
);
|
||||
END;
|
||||
|
||||
/*
|
||||
Upgrade path for an existing evidence table. Historical rows cannot reconstruct the
|
||||
exact Agent extraction or preprocessing implementation, so added columns remain
|
||||
nullable for those rows. All purchase v1.4 writes require and insert both values.
|
||||
*/
|
||||
IF OBJECT_ID(N'dbo.p_agent_business_source_document', N'U') IS NOT NULL
|
||||
AND COL_LENGTH(
|
||||
N'dbo.p_agent_business_source_document', N'extraction_sha256') IS NULL
|
||||
BEGIN
|
||||
ALTER TABLE dbo.p_agent_business_source_document
|
||||
ADD extraction_sha256 CHAR(64) NULL;
|
||||
END;
|
||||
|
||||
IF OBJECT_ID(N'dbo.p_agent_business_source_document', N'U') IS NOT NULL
|
||||
AND COL_LENGTH(
|
||||
N'dbo.p_agent_business_source_document', N'preprocess_contract') IS NULL
|
||||
BEGIN
|
||||
ALTER TABLE dbo.p_agent_business_source_document
|
||||
ADD preprocess_contract VARCHAR(64) NULL;
|
||||
END;
|
||||
|
||||
IF OBJECT_ID(N'dbo.p_agent_business_source_document', N'U') IS NOT NULL
|
||||
AND NOT EXISTS
|
||||
(
|
||||
SELECT 1
|
||||
FROM sys.check_constraints
|
||||
WHERE parent_object_id = OBJECT_ID(
|
||||
N'dbo.p_agent_business_source_document')
|
||||
AND name = N'CK_p_agent_business_extraction_hash'
|
||||
)
|
||||
BEGIN
|
||||
ALTER TABLE dbo.p_agent_business_source_document WITH CHECK
|
||||
ADD CONSTRAINT CK_p_agent_business_extraction_hash
|
||||
CHECK
|
||||
(
|
||||
extraction_sha256 IS NULL
|
||||
OR
|
||||
(
|
||||
LEN(extraction_sha256) = 64
|
||||
AND extraction_sha256 COLLATE Latin1_General_100_BIN2
|
||||
NOT LIKE '%[^0-9a-f]%'
|
||||
)
|
||||
);
|
||||
END;
|
||||
|
||||
IF OBJECT_ID(N'dbo.p_agent_business_source_document', N'U') IS NOT NULL
|
||||
AND NOT EXISTS
|
||||
(
|
||||
SELECT 1
|
||||
FROM sys.check_constraints
|
||||
WHERE parent_object_id = OBJECT_ID(
|
||||
N'dbo.p_agent_business_source_document')
|
||||
AND name = N'CK_p_agent_business_preprocess_contract'
|
||||
)
|
||||
BEGIN
|
||||
ALTER TABLE dbo.p_agent_business_source_document WITH CHECK
|
||||
ADD CONSTRAINT CK_p_agent_business_preprocess_contract
|
||||
CHECK
|
||||
(
|
||||
preprocess_contract IS NULL
|
||||
OR
|
||||
(
|
||||
(document_kind COLLATE Latin1_General_100_BIN2 = 'image'
|
||||
AND preprocess_contract COLLATE Latin1_General_100_BIN2 =
|
||||
'minimax_vlm_0.0.4'
|
||||
AND
|
||||
(
|
||||
LOWER(RIGHT(sanitized_filename, 4)) IN (N'.png', N'.jpg')
|
||||
OR LOWER(RIGHT(sanitized_filename, 5)) IN (N'.jpeg', N'.webp')
|
||||
))
|
||||
OR
|
||||
(document_kind COLLATE Latin1_General_100_BIN2 = 'file'
|
||||
AND
|
||||
(
|
||||
(LOWER(RIGHT(sanitized_filename, 4)) = N'.pdf'
|
||||
AND preprocess_contract COLLATE Latin1_General_100_BIN2 =
|
||||
'pdfium_minimax_pages_v1')
|
||||
OR
|
||||
(LOWER(RIGHT(sanitized_filename, 4)) = N'.csv'
|
||||
AND preprocess_contract COLLATE Latin1_General_100_BIN2 =
|
||||
'document_sandbox_csv_v1')
|
||||
OR
|
||||
(LOWER(RIGHT(sanitized_filename, 5)) = N'.xlsx'
|
||||
AND preprocess_contract COLLATE Latin1_General_100_BIN2 =
|
||||
'document_sandbox_xlsx_v1')
|
||||
))
|
||||
)
|
||||
);
|
||||
END;
|
||||
|
||||
/*
|
||||
The table is intentionally created empty. A customer DBA may add a row only after
|
||||
finance has approved the source P_CurrencyType to target P_BaseMixInfoTab mapping.
|
||||
The customer-specific write wrapper additionally requires a signed evidence hash;
|
||||
shipping this schema never approves or guesses a currency mapping.
|
||||
*/
|
||||
IF OBJECT_ID(N'dbo.p_agent_purchase_currency_crosswalk', N'U') IS NULL
|
||||
BEGIN
|
||||
CREATE TABLE dbo.p_agent_purchase_currency_crosswalk
|
||||
(
|
||||
source_currency_id INT NOT NULL,
|
||||
target_currency_id INT NOT NULL,
|
||||
is_active BIT NOT NULL,
|
||||
approval_evidence_sha256 CHAR(64) NOT NULL,
|
||||
approved_by NVARCHAR(64) NOT NULL,
|
||||
approved_at_utc DATETIME2(3) NOT NULL,
|
||||
created_at_utc DATETIME2(3) NOT NULL
|
||||
CONSTRAINT DF_p_agent_purchase_currency_created DEFAULT SYSUTCDATETIME(),
|
||||
CONSTRAINT PK_p_agent_purchase_currency_crosswalk
|
||||
PRIMARY KEY CLUSTERED (source_currency_id, target_currency_id),
|
||||
CONSTRAINT CK_p_agent_purchase_currency_source
|
||||
CHECK (source_currency_id > 0),
|
||||
CONSTRAINT CK_p_agent_purchase_currency_target
|
||||
CHECK (target_currency_id > 0),
|
||||
CONSTRAINT CK_p_agent_purchase_currency_hash
|
||||
CHECK (LEN(approval_evidence_sha256) = 64
|
||||
AND approval_evidence_sha256 COLLATE Latin1_General_100_BIN2
|
||||
NOT LIKE '%[^0-9a-f]%'),
|
||||
CONSTRAINT CK_p_agent_purchase_currency_approver
|
||||
CHECK (LEN(LTRIM(RTRIM(approved_by))) BETWEEN 1 AND 64)
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX UX_p_agent_purchase_currency_active
|
||||
ON dbo.p_agent_purchase_currency_crosswalk(source_currency_id)
|
||||
WHERE is_active = 1;
|
||||
END;
|
||||
|
||||
/*
|
||||
The table is intentionally created empty. It grants no access by itself. A customer
|
||||
DBA may add an exact account/subsystem/user + organization/department/purchaser
|
||||
tuple only after the procurement owner has approved that row scope and supplied a
|
||||
signed evidence hash. Wildcards and NULL scope components are not supported.
|
||||
*/
|
||||
IF OBJECT_ID(N'dbo.p_agent_purchase_row_scope', N'U') IS NULL
|
||||
BEGIN
|
||||
CREATE TABLE dbo.p_agent_purchase_row_scope
|
||||
(
|
||||
id BIGINT IDENTITY(1, 1) NOT NULL,
|
||||
account_book NVARCHAR(64) NOT NULL,
|
||||
subsystem_id NVARCHAR(32) NOT NULL,
|
||||
erp_user_id NVARCHAR(64) NOT NULL,
|
||||
group_id INT NOT NULL,
|
||||
department_id INT NOT NULL,
|
||||
purchase_user_id INT NOT NULL,
|
||||
is_active BIT NOT NULL,
|
||||
approval_evidence_sha256 CHAR(64) NOT NULL,
|
||||
approved_by NVARCHAR(64) NOT NULL,
|
||||
approved_at_utc DATETIME2(3) NOT NULL,
|
||||
valid_from_utc DATETIME2(3) NOT NULL,
|
||||
valid_to_utc DATETIME2(3) NULL,
|
||||
created_at_utc DATETIME2(3) NOT NULL
|
||||
CONSTRAINT DF_p_agent_purchase_scope_created DEFAULT SYSUTCDATETIME(),
|
||||
CONSTRAINT PK_p_agent_purchase_row_scope PRIMARY KEY CLUSTERED (id),
|
||||
CONSTRAINT CK_p_agent_purchase_scope_group CHECK (group_id > 0),
|
||||
CONSTRAINT CK_p_agent_purchase_scope_department CHECK (department_id > 0),
|
||||
CONSTRAINT CK_p_agent_purchase_scope_user CHECK (purchase_user_id > 0),
|
||||
CONSTRAINT CK_p_agent_purchase_scope_account
|
||||
CHECK (LEN(LTRIM(RTRIM(account_book))) BETWEEN 1 AND 64),
|
||||
CONSTRAINT CK_p_agent_purchase_scope_subsystem
|
||||
CHECK (LEN(LTRIM(RTRIM(subsystem_id))) BETWEEN 1 AND 32),
|
||||
CONSTRAINT CK_p_agent_purchase_scope_erp_user
|
||||
CHECK (LEN(LTRIM(RTRIM(erp_user_id))) BETWEEN 1 AND 64),
|
||||
CONSTRAINT CK_p_agent_purchase_scope_hash
|
||||
CHECK (LEN(approval_evidence_sha256) = 64
|
||||
AND approval_evidence_sha256 COLLATE Latin1_General_100_BIN2
|
||||
NOT LIKE '%[^0-9a-f]%'),
|
||||
CONSTRAINT CK_p_agent_purchase_scope_approver
|
||||
CHECK (LEN(LTRIM(RTRIM(approved_by))) BETWEEN 1 AND 64),
|
||||
CONSTRAINT CK_p_agent_purchase_scope_window
|
||||
CHECK (valid_to_utc IS NULL OR valid_to_utc > valid_from_utc)
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX UX_p_agent_purchase_row_scope_active
|
||||
ON dbo.p_agent_purchase_row_scope
|
||||
(
|
||||
account_book, subsystem_id, erp_user_id,
|
||||
group_id, department_id, purchase_user_id
|
||||
)
|
||||
WHERE is_active = 1;
|
||||
END;
|
||||
|
||||
IF OBJECT_ID(N'dbo.p_agent_integration_outbox', N'U') IS NULL
|
||||
BEGIN
|
||||
CREATE TABLE dbo.p_agent_integration_outbox
|
||||
(
|
||||
id BIGINT IDENTITY(1, 1) NOT NULL,
|
||||
event_id VARCHAR(128) NOT NULL,
|
||||
business_audit_id VARCHAR(128) NOT NULL,
|
||||
correlation_id VARCHAR(128) NOT NULL,
|
||||
account_book NVARCHAR(64) NOT NULL,
|
||||
subsystem_id NVARCHAR(32) NOT NULL,
|
||||
module_code NVARCHAR(64) NOT NULL,
|
||||
action_name VARCHAR(64) NOT NULL,
|
||||
record_id NVARCHAR(128) NOT NULL,
|
||||
status TINYINT NOT NULL,
|
||||
attempt_count INT NOT NULL
|
||||
CONSTRAINT DF_p_agent_integration_outbox_attempt DEFAULT 0,
|
||||
available_at_utc DATETIME2(3) NOT NULL
|
||||
CONSTRAINT DF_p_agent_integration_outbox_available DEFAULT SYSUTCDATETIME(),
|
||||
locked_at_utc DATETIME2(3) NULL,
|
||||
completed_at_utc DATETIME2(3) NULL,
|
||||
last_error_code VARCHAR(128) NULL,
|
||||
created_at_utc DATETIME2(3) NOT NULL
|
||||
CONSTRAINT DF_p_agent_integration_outbox_created DEFAULT SYSUTCDATETIME(),
|
||||
row_version ROWVERSION NOT NULL,
|
||||
CONSTRAINT PK_p_agent_integration_outbox PRIMARY KEY CLUSTERED (id),
|
||||
CONSTRAINT UQ_p_agent_integration_outbox_event UNIQUE (event_id),
|
||||
CONSTRAINT FK_p_agent_integration_outbox_audit
|
||||
FOREIGN KEY (business_audit_id)
|
||||
REFERENCES dbo.p_agent_business_audit(business_audit_id),
|
||||
CONSTRAINT CK_p_agent_integration_outbox_status
|
||||
CHECK (status IN (0, 1, 2, 3)),
|
||||
CONSTRAINT CK_p_agent_integration_outbox_attempt
|
||||
CHECK (attempt_count >= 0)
|
||||
);
|
||||
|
||||
CREATE INDEX IX_p_agent_integration_outbox_dispatch
|
||||
ON dbo.p_agent_integration_outbox(status, available_at_utc, id)
|
||||
INCLUDE(attempt_count, module_code, action_name, record_id);
|
||||
END;
|
||||
|
||||
/*
|
||||
status: 0=in_progress, 1=completed, 2=terminal_failure
|
||||
|
||||
客户采购/请假适配器必须在“同一个数据库事务”中完成:
|
||||
1. 使用上面的唯一作用域和 UPDLOCK,HOLDLOCK 查询幂等记录;
|
||||
2. 已存在但 input_fingerprint 不同:返回 idempotency_key_conflict;
|
||||
3. status=1:返回原 record_id、transaction_evidence_id、business_audit_id;
|
||||
4. 不存在:插入 status=0;
|
||||
5. 锁定并复核业务来源、调用原 ERP 保存链、写业务审计/Outbox;
|
||||
6. 更新 status=1 及三个证据字段;
|
||||
7. 返回完整证据,由调用方提交 Serializable 外层事务。过程本身不得 COMMIT 或
|
||||
ROLLBACK;任何异常由调用方回滚整个事务。
|
||||
|
||||
不得先提交幂等记录再另开事务保存业务数据,也不得把模型原文、发票全文、
|
||||
ERP 密码、参数值或原始 SQL 写入此表。
|
||||
|
||||
p_agent_business_audit 只保存关联、作用域、指纹和业务记录标识,不保存请假原因、
|
||||
发票全文、附件内容或 SQL。p_agent_business_source_document 只保存受信任本地 Tool
|
||||
注入且已进入输入指纹的附件类型、净化文件名、大小、源/提取 SHA-256 和精确的
|
||||
预处理实现契约,不保存附件内容或本地路径。p_agent_integration_outbox 也只保存
|
||||
重新读取业务记录所需的稳定标识;
|
||||
异步推送进程必须在 ERP 权限上下文中按 record_id 重新读取,并在事务提交后投递,
|
||||
禁止把模型原文或敏感业务载荷直接落入 Outbox。
|
||||
*/
|
||||
@@ -0,0 +1,335 @@
|
||||
SET XACT_ABORT ON;
|
||||
|
||||
IF OBJECT_ID(N'dbo.p_agent_workflow_adapter_evidence', N'U') IS NULL
|
||||
BEGIN
|
||||
CREATE TABLE dbo.p_agent_workflow_adapter_evidence
|
||||
(
|
||||
workflow VARCHAR(32) NOT NULL,
|
||||
module_code NVARCHAR(64) NOT NULL,
|
||||
adapter_id VARCHAR(128) NOT NULL,
|
||||
adapter_version VARCHAR(64) NOT NULL,
|
||||
evidence_id VARCHAR(128) NOT NULL,
|
||||
evidence_sha256 CHAR(64) NOT NULL,
|
||||
customer_configuration_validated BIT NOT NULL,
|
||||
parameterized_read_queries_verified BIT NOT NULL,
|
||||
transactional_write_verified BIT NOT NULL,
|
||||
persistent_idempotency_verified BIT NOT NULL,
|
||||
permission_recheck_verified BIT NOT NULL,
|
||||
windows_integration_verified BIT NOT NULL,
|
||||
validated_by NVARCHAR(128) NOT NULL,
|
||||
validated_at_utc DATETIME2(3) NOT NULL,
|
||||
row_version ROWVERSION NOT NULL,
|
||||
CONSTRAINT PK_p_agent_workflow_adapter_evidence
|
||||
PRIMARY KEY CLUSTERED(workflow, module_code),
|
||||
CONSTRAINT CK_p_agent_workflow_adapter_evidence_workflow
|
||||
CHECK (workflow IN ('purchase', 'leave')),
|
||||
CONSTRAINT CK_p_agent_workflow_adapter_evidence_hash
|
||||
CHECK (LEN(evidence_sha256) = 64)
|
||||
);
|
||||
END;
|
||||
|
||||
IF OBJECT_ID(N'dbo.p_lserp_agent_workflow_readiness', N'P') IS NULL
|
||||
BEGIN
|
||||
EXEC(N'
|
||||
CREATE PROCEDURE dbo.p_lserp_agent_workflow_readiness
|
||||
@workflow VARCHAR(32),
|
||||
@module_code NVARCHAR(64)
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
SELECT
|
||||
adapter_id,
|
||||
adapter_version,
|
||||
evidence_id,
|
||||
customer_configuration_validated,
|
||||
parameterized_read_queries_verified,
|
||||
transactional_write_verified,
|
||||
persistent_idempotency_verified,
|
||||
permission_recheck_verified,
|
||||
windows_integration_verified
|
||||
FROM dbo.p_agent_workflow_adapter_evidence
|
||||
WHERE workflow = @workflow
|
||||
AND module_code = @module_code;
|
||||
END;');
|
||||
END;
|
||||
|
||||
/*
|
||||
V2 证据按账套与子系统隔离,并把签名验收清单的 contentSha256 返回客户端。
|
||||
旧表/旧过程保留只用于审计迁移;当前客户端通过 006 中的 V3 readiness
|
||||
读取这张 V2 证据表,并额外校验已部署过程签名与修改时间,禁止自动复制旧布尔值。
|
||||
*/
|
||||
IF OBJECT_ID(N'dbo.p_agent_workflow_adapter_evidence_v2', N'U') IS NULL
|
||||
BEGIN
|
||||
CREATE TABLE dbo.p_agent_workflow_adapter_evidence_v2
|
||||
(
|
||||
workflow VARCHAR(32) NOT NULL,
|
||||
module_code NVARCHAR(64) NOT NULL,
|
||||
account_book NVARCHAR(128) NOT NULL,
|
||||
subsystem_id NVARCHAR(128) NOT NULL,
|
||||
adapter_id VARCHAR(128) NOT NULL,
|
||||
adapter_version VARCHAR(64) NOT NULL,
|
||||
evidence_id VARCHAR(128) NOT NULL,
|
||||
evidence_sha256 CHAR(64) NOT NULL,
|
||||
customer_configuration_validated BIT NOT NULL,
|
||||
parameterized_read_queries_verified BIT NOT NULL,
|
||||
transactional_write_verified BIT NOT NULL,
|
||||
persistent_idempotency_verified BIT NOT NULL,
|
||||
permission_recheck_verified BIT NOT NULL,
|
||||
windows_integration_verified BIT NOT NULL,
|
||||
validated_by NVARCHAR(128) NOT NULL,
|
||||
validated_at_utc DATETIME2(3) NOT NULL,
|
||||
row_version ROWVERSION NOT NULL,
|
||||
CONSTRAINT PK_p_agent_workflow_adapter_evidence_v2
|
||||
PRIMARY KEY CLUSTERED(workflow, module_code, account_book, subsystem_id),
|
||||
CONSTRAINT CK_p_agent_workflow_adapter_evidence_v2_workflow
|
||||
CHECK (workflow IN ('purchase', 'leave')),
|
||||
CONSTRAINT CK_p_agent_workflow_adapter_evidence_v2_hash
|
||||
CHECK (LEN(evidence_sha256) = 64
|
||||
AND evidence_sha256 NOT LIKE '%[^0-9a-f]%')
|
||||
);
|
||||
END;
|
||||
|
||||
IF OBJECT_ID(N'dbo.p_lserp_agent_workflow_readiness_v2', N'P') IS NULL
|
||||
BEGIN
|
||||
EXEC(N'
|
||||
CREATE PROCEDURE dbo.p_lserp_agent_workflow_readiness_v2
|
||||
@workflow VARCHAR(32),
|
||||
@module_code NVARCHAR(64),
|
||||
@account_book NVARCHAR(128),
|
||||
@subsystem_id NVARCHAR(128)
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
SELECT
|
||||
adapter_id,
|
||||
adapter_version,
|
||||
evidence_id,
|
||||
evidence_sha256,
|
||||
account_book,
|
||||
subsystem_id,
|
||||
customer_configuration_validated,
|
||||
parameterized_read_queries_verified,
|
||||
transactional_write_verified,
|
||||
persistent_idempotency_verified,
|
||||
permission_recheck_verified,
|
||||
windows_integration_verified,
|
||||
validated_by,
|
||||
validated_at_utc
|
||||
FROM dbo.p_agent_workflow_adapter_evidence_v2
|
||||
WHERE workflow = @workflow
|
||||
AND module_code = @module_code
|
||||
AND account_book = @account_book
|
||||
AND subsystem_id = @subsystem_id;
|
||||
END;');
|
||||
END;
|
||||
|
||||
/*
|
||||
客户适配层必须实现下面两个固定过程。部署脚本只在过程不存在时创建拒绝服务的
|
||||
占位过程,绝不会猜测或直接写客户业务表。
|
||||
|
||||
read 按 action 返回:
|
||||
- purchase.resolve_supplier: 零到多行 supplier_code, supplier_name,
|
||||
supplier_tax_id。payload 为 reference, taxId;只允许按客户确认的精确编码、
|
||||
税号、全称或人工维护别名匹配,禁止模型生成 SQL 或自行决定编码。
|
||||
- purchase.resolve_currency: 零到多行 currency_code, currency_name。payload 为
|
||||
reference;必须按精确编码、名称或人工维护别名匹配。
|
||||
- purchase.resolve_material: 零到多行 material_code, material_name,
|
||||
specification, unit。payload 为 lineId, reference, specification, unit,
|
||||
supplierCode;只能查询当前账套允许采购且未停用的物料。
|
||||
- purchase.invoice_exists: exists(bit)
|
||||
- purchase.open_sources: source_order_id, source_order_number, source_line_id,
|
||||
supplier_code, currency_code, material_code, unit, remaining_quantity,
|
||||
unit_price, tax_rate, exchange_rate, closed(bit)。source_order_id 必须是稳定的
|
||||
采购单系统标识,source_line_id 必须是采购来源明细标识;单位和汇率均为必填。
|
||||
过程必须扣除未删除、未取消、未作废的既有单据占用,并在结果超过 10000 行时
|
||||
让调用端失败关闭,禁止静默截断后匹配。
|
||||
- leave.context: current_employee_id, can_apply_for_others(bit), now_local(datetime)
|
||||
- leave.resolve_type: 零到多行 leave_type_code, leave_type_name。payload 为
|
||||
query;只允许在当前用户可用且已启用的假别及人工维护别名中匹配,禁止让模型
|
||||
猜内部编码。结果不唯一时全部返回,由命令层停止并要求用户选择。
|
||||
- leave.resolve_flow_type: 零到多行 flow_type_code, flow_type_name。payload 为
|
||||
employeeId, calculatedHours, query;必须只返回当前模块已配置且未停用的流转
|
||||
类别。flow_type_code 必须是流程类别配置行 id,且步骤表 billType 必须存在对应
|
||||
id;不得返回名称可能重复的业务 billType 字段。客户没有经过书面验收的职级/
|
||||
工时映射时,query 为空必须返回全部候选,
|
||||
由命令层追问用户,禁止按名称、职级或天数猜审批路线。
|
||||
- leave.resolve_calendar_range: 恰好一行 available(bit), reason_code,
|
||||
start_local(datetime), end_local(datetime), hours(decimal), time_zone_id。
|
||||
payload 为 employeeId, localDate(yyyy-MM-dd), dayPart(morning/afternoon/full_day);
|
||||
必须按该员工工作日历、班次、时区和节假日计算,禁止写死 9:00-18:00。
|
||||
available=0 时 start/end/hours/time_zone_id 可为空,但 reason_code 必须为稳定代码。
|
||||
- leave.type_enabled: enabled(bit)
|
||||
- leave.flow_type_enabled: enabled(bit)
|
||||
- leave.calculate_hours: hours(decimal)
|
||||
- leave.has_conflict: has_conflict(bit)
|
||||
- leave.can_submit: can_submit(bit), reason(nvarchar)
|
||||
|
||||
write 必须在调用方已经开启的 Serializable 事务内完成原 ERP 保存链、来源锁定、
|
||||
权限复核、p_agent_command_idempotency 与业务审计,并恰好返回一行:
|
||||
success, code, message, record_id, needs_ui, idempotency_replayed,
|
||||
applied_idempotency_key, applied_input_fingerprint, transaction_evidence_id,
|
||||
business_audit_id。过程不得 COMMIT/ROLLBACK 外层事务。
|
||||
所有写过程还必须接收 correlation_id,并把它与 transaction_evidence_id、
|
||||
business_audit_id 和幂等记录绑定,禁止记录模型原文或原始 SQL。
|
||||
purchase.create_document 的 payload.draft 必须包含 totalWithoutTax、taxAmount、
|
||||
totalWithTax,且每行包含 taxAmount。客户过程必须按配置的含税/不含税口径和币种
|
||||
精度重新计算头行价税平衡;不得只信任 Agent 传入的汇总值。若 payload.draft
|
||||
包含 sourceDocuments,过程必须把每份 kind、filename、sizeBytes、sha256、
|
||||
extractionSha256 与业务审计记录关联保存。extractionSha256 是受信任本地 Tool
|
||||
对实际注入 Agent 的精确 UTF-8 预处理包计算的摘要;不得用模型或数据库重新
|
||||
生成的值替换这些已由解析凭证和 inputFingerprint 绑定的来源凭据。
|
||||
*/
|
||||
|
||||
IF OBJECT_ID(N'dbo.p_lserp_agent_workflow_read', N'P') IS NULL
|
||||
BEGIN
|
||||
EXEC(N'
|
||||
CREATE PROCEDURE dbo.p_lserp_agent_workflow_read
|
||||
@workflow VARCHAR(32),
|
||||
@action VARCHAR(64),
|
||||
@module_code NVARCHAR(64),
|
||||
@account_book NVARCHAR(64),
|
||||
@subsystem_id NVARCHAR(32),
|
||||
@user_id NVARCHAR(64),
|
||||
@payload_json NVARCHAR(MAX)
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
RAISERROR(N''客户只读业务适配过程尚未实施。'', 16, 1);
|
||||
RETURN;
|
||||
END;');
|
||||
END;
|
||||
|
||||
/*
|
||||
数据库兼容级别低于 130 时,ERP 进程先以严格 JSON 规则解析输入,再调用这个
|
||||
固定标量参数过程。占位实现始终拒绝;客户 DBA 必须独立评审并替换只读实现。
|
||||
兼容路径只为 leave 和 purchase 提供下方固定类型写过程;purchase 明细和来源
|
||||
附件摘要使用由受信任 ERP 进程生成、数据库再次按固定 schema 校验的 XML 行集。
|
||||
其他 workflow 在开启事务前拒绝。
|
||||
*/
|
||||
IF OBJECT_ID(N'dbo.p_lserp_agent_workflow_read_compat100', N'P') IS NULL
|
||||
BEGIN
|
||||
EXEC(N'
|
||||
CREATE PROCEDURE dbo.p_lserp_agent_workflow_read_compat100
|
||||
@workflow VARCHAR(32),
|
||||
@action VARCHAR(64),
|
||||
@module_code NVARCHAR(64),
|
||||
@account_book NVARCHAR(64),
|
||||
@subsystem_id NVARCHAR(32),
|
||||
@user_id NVARCHAR(64),
|
||||
@reference NVARCHAR(500) = NULL,
|
||||
@tax_id NVARCHAR(50) = NULL,
|
||||
@line_id NVARCHAR(64) = NULL,
|
||||
@specification NVARCHAR(200) = NULL,
|
||||
@unit NVARCHAR(100) = NULL,
|
||||
@supplier_code NVARCHAR(64) = NULL,
|
||||
@currency_code NVARCHAR(64) = NULL,
|
||||
@invoice_number NVARCHAR(128) = NULL,
|
||||
@query NVARCHAR(500) = NULL,
|
||||
@employee_id NVARCHAR(64) = NULL,
|
||||
@local_date NVARCHAR(10) = NULL,
|
||||
@day_part VARCHAR(16) = NULL,
|
||||
@leave_type_code NVARCHAR(64) = NULL,
|
||||
@flow_type_query NVARCHAR(500) = NULL,
|
||||
@calculated_hours DECIMAL(18, 6) = NULL,
|
||||
@flow_type_code NVARCHAR(64) = NULL,
|
||||
@start_local DATETIME = NULL,
|
||||
@end_local DATETIME = NULL,
|
||||
@record_id NVARCHAR(128) = NULL
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
RAISERROR(N''客户兼容级别 100 的只读业务适配过程尚未实施。'', 16, 1);
|
||||
RETURN;
|
||||
END;');
|
||||
END;
|
||||
|
||||
IF OBJECT_ID(N'dbo.p_lserp_agent_workflow_write', N'P') IS NULL
|
||||
BEGIN
|
||||
EXEC(N'
|
||||
CREATE PROCEDURE dbo.p_lserp_agent_workflow_write
|
||||
@workflow VARCHAR(32),
|
||||
@action VARCHAR(64),
|
||||
@module_code NVARCHAR(64),
|
||||
@account_book NVARCHAR(64),
|
||||
@subsystem_id NVARCHAR(32),
|
||||
@user_id NVARCHAR(64),
|
||||
@correlation_id VARCHAR(128),
|
||||
@payload_json NVARCHAR(MAX),
|
||||
@idempotency_key VARCHAR(128),
|
||||
@input_fingerprint CHAR(64)
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
RAISERROR(N''客户事务写业务适配过程尚未实施。'', 16, 1);
|
||||
RETURN;
|
||||
END;');
|
||||
END;
|
||||
|
||||
/*
|
||||
兼容级别 100 的请假写入口只接受固定强类型标量。占位实现始终拒绝;客户 DBA
|
||||
必须用经过 NOEXEC 复核和独立签署的实现替换。不得增加表名、列名、SQL 文本、
|
||||
过程名或 JSON 参数,也不得在过程内 COMMIT/ROLLBACK 调用方的外层事务。
|
||||
*/
|
||||
IF OBJECT_ID(N'dbo.p_lserp_agent_workflow_write_leave_compat100', N'P') IS NULL
|
||||
BEGIN
|
||||
EXEC(N'
|
||||
CREATE PROCEDURE dbo.p_lserp_agent_workflow_write_leave_compat100
|
||||
@action VARCHAR(64),
|
||||
@module_code NVARCHAR(64),
|
||||
@account_book NVARCHAR(64),
|
||||
@subsystem_id NVARCHAR(32),
|
||||
@user_id NVARCHAR(64),
|
||||
@correlation_id VARCHAR(128),
|
||||
@idempotency_key VARCHAR(128),
|
||||
@input_fingerprint CHAR(64),
|
||||
@employee_id NVARCHAR(64) = NULL,
|
||||
@leave_type_code NVARCHAR(64) = NULL,
|
||||
@flow_type_code NVARCHAR(64) = NULL,
|
||||
@start_local DATETIME = NULL,
|
||||
@end_local DATETIME = NULL,
|
||||
@requested_hours DECIMAL(18, 6) = NULL,
|
||||
@reason NVARCHAR(500) = NULL,
|
||||
@submit_after_save_intent BIT = NULL,
|
||||
@record_id NVARCHAR(128) = NULL
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
RAISERROR(N''客户兼容级别 100 的请假事务写适配过程尚未实施。'', 16, 1);
|
||||
RETURN;
|
||||
END;');
|
||||
END;
|
||||
|
||||
/*
|
||||
兼容级别 100 的采购写入口只接受固定标量和两个固定 XML 行集。占位实现始终拒绝;
|
||||
客户 DBA 必须在币种字段、币种换算、采购行级权限及 Windows 原保存链验收均签署
|
||||
后,用独立发布制品替换。过程不得接受 JSON、表名、列名、过程名或 SQL 文本,也
|
||||
不得在过程内 COMMIT/ROLLBACK 调用方的外层 Serializable 事务。
|
||||
*/
|
||||
IF OBJECT_ID(N'dbo.p_lserp_agent_workflow_write_purchase_compat100', N'P') IS NULL
|
||||
BEGIN
|
||||
EXEC(N'
|
||||
CREATE PROCEDURE dbo.p_lserp_agent_workflow_write_purchase_compat100
|
||||
@action VARCHAR(64),
|
||||
@module_code NVARCHAR(64),
|
||||
@account_book NVARCHAR(64),
|
||||
@subsystem_id NVARCHAR(32),
|
||||
@user_id NVARCHAR(64),
|
||||
@correlation_id VARCHAR(128),
|
||||
@idempotency_key VARCHAR(128),
|
||||
@input_fingerprint CHAR(64),
|
||||
@supplier_code NVARCHAR(64),
|
||||
@currency_code NVARCHAR(64),
|
||||
@invoice_number NVARCHAR(128),
|
||||
@invoice_date DATETIME,
|
||||
@total_without_tax DECIMAL(28, 8),
|
||||
@tax_amount DECIMAL(28, 8),
|
||||
@total_with_tax DECIMAL(28, 8),
|
||||
@lines_xml XML,
|
||||
@source_documents_xml XML
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
RAISERROR(N''客户兼容级别 100 的采购事务写适配过程尚未实施。'', 16, 1);
|
||||
RETURN;
|
||||
END;');
|
||||
END;
|
||||
@@ -0,0 +1,107 @@
|
||||
SET XACT_ABORT ON;
|
||||
|
||||
IF OBJECT_ID(N'dbo.p_lserp_agent_record_workflow_acceptance_v2', N'P') IS NULL
|
||||
BEGIN
|
||||
EXEC(N'
|
||||
CREATE PROCEDURE dbo.p_lserp_agent_record_workflow_acceptance_v2
|
||||
@workflow VARCHAR(32),
|
||||
@module_code NVARCHAR(64),
|
||||
@account_book NVARCHAR(128),
|
||||
@subsystem_id NVARCHAR(128),
|
||||
@adapter_id VARCHAR(128),
|
||||
@adapter_version VARCHAR(64),
|
||||
@evidence_id VARCHAR(128),
|
||||
@evidence_sha256 CHAR(64),
|
||||
@validated_by NVARCHAR(128),
|
||||
@validated_at_utc DATETIME2(3)
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
SET XACT_ABORT ON;
|
||||
|
||||
IF @workflow NOT IN (''purchase'', ''leave'')
|
||||
BEGIN
|
||||
RAISERROR(N''workflow 无效。'', 16, 1);
|
||||
RETURN;
|
||||
END;
|
||||
IF NULLIF(LTRIM(RTRIM(@module_code)), N'''') IS NULL
|
||||
OR NULLIF(LTRIM(RTRIM(@account_book)), N'''') IS NULL
|
||||
OR NULLIF(LTRIM(RTRIM(@subsystem_id)), N'''') IS NULL
|
||||
OR NULLIF(LTRIM(RTRIM(@adapter_id)), '''') IS NULL
|
||||
OR NULLIF(LTRIM(RTRIM(@adapter_version)), '''') IS NULL
|
||||
OR NULLIF(LTRIM(RTRIM(@evidence_id)), '''') IS NULL
|
||||
OR NULLIF(LTRIM(RTRIM(@validated_by)), N'''') IS NULL
|
||||
BEGIN
|
||||
RAISERROR(N''验收证据范围或身份字段不能为空。'', 16, 1);
|
||||
RETURN;
|
||||
END;
|
||||
IF LEN(@evidence_sha256) <> 64 OR @evidence_sha256 LIKE ''%[^0-9a-f]%''
|
||||
BEGIN
|
||||
RAISERROR(N''evidence_sha256 必须是 64 位小写十六进制。'', 16, 1);
|
||||
RETURN;
|
||||
END;
|
||||
IF @validated_at_utc IS NULL
|
||||
OR @validated_at_utc > DATEADD(MINUTE, 5, SYSUTCDATETIME())
|
||||
BEGIN
|
||||
RAISERROR(N''validated_at_utc 无效。'', 16, 1);
|
||||
RETURN;
|
||||
END;
|
||||
|
||||
BEGIN TRANSACTION;
|
||||
UPDATE dbo.p_agent_workflow_adapter_evidence_v2 WITH (UPDLOCK, SERIALIZABLE)
|
||||
SET adapter_id = @adapter_id,
|
||||
adapter_version = @adapter_version,
|
||||
evidence_id = @evidence_id,
|
||||
evidence_sha256 = @evidence_sha256,
|
||||
customer_configuration_validated = 1,
|
||||
parameterized_read_queries_verified = 1,
|
||||
transactional_write_verified = 1,
|
||||
persistent_idempotency_verified = 1,
|
||||
permission_recheck_verified = 1,
|
||||
windows_integration_verified = 1,
|
||||
validated_by = @validated_by,
|
||||
validated_at_utc = @validated_at_utc
|
||||
WHERE workflow = @workflow
|
||||
AND module_code = @module_code
|
||||
AND account_book = @account_book
|
||||
AND subsystem_id = @subsystem_id;
|
||||
|
||||
IF @@ROWCOUNT = 0
|
||||
BEGIN
|
||||
INSERT dbo.p_agent_workflow_adapter_evidence_v2
|
||||
(
|
||||
workflow, module_code, account_book, subsystem_id,
|
||||
adapter_id, adapter_version, evidence_id, evidence_sha256,
|
||||
customer_configuration_validated,
|
||||
parameterized_read_queries_verified,
|
||||
transactional_write_verified,
|
||||
persistent_idempotency_verified,
|
||||
permission_recheck_verified,
|
||||
windows_integration_verified,
|
||||
validated_by, validated_at_utc
|
||||
)
|
||||
VALUES
|
||||
(
|
||||
@workflow, @module_code, @account_book, @subsystem_id,
|
||||
@adapter_id, @adapter_version, @evidence_id, @evidence_sha256,
|
||||
1, 1, 1, 1, 1, 1,
|
||||
@validated_by, @validated_at_utc
|
||||
);
|
||||
END;
|
||||
COMMIT TRANSACTION;
|
||||
|
||||
SELECT
|
||||
@workflow AS workflow,
|
||||
@module_code AS module_code,
|
||||
@account_book AS account_book,
|
||||
@subsystem_id AS subsystem_id,
|
||||
@evidence_id AS evidence_id,
|
||||
@evidence_sha256 AS evidence_sha256,
|
||||
@validated_at_utc AS validated_at_utc;
|
||||
END;');
|
||||
END;
|
||||
|
||||
/*
|
||||
不要把该过程授权给 ERP 日常运行账号。仅由客户 DBA/发布流水线在验收清单已经
|
||||
签名并通过 lserp-cli adapters verify-acceptance-evidence 后调用。
|
||||
*/
|
||||
@@ -0,0 +1,475 @@
|
||||
SET XACT_ABORT ON;
|
||||
|
||||
/*
|
||||
通用低代码能力只固定“安全协议”,不固定客户模块、表或字段。
|
||||
|
||||
- ERP 进程每次从当前登录数据库读取模块配置,向模型只发布
|
||||
m/d + 16 位摘要的不透明参数 ID。
|
||||
- Lookup 过程必须只读;调用方总是在 ReadCommitted 事务中调用并
|
||||
回滚。
|
||||
- 新增过程必须参与调用方已开启的 Serializable 事务,不得在
|
||||
过程内 COMMIT/ROLLBACK 外层事务。
|
||||
- 动态写 v2 对象与v1并存,因为v2增加了原生保存族与执行配置
|
||||
指纹。不会就地改写已部署的v1对象或证据。
|
||||
- 本脚本只创建空证据表和拒绝服务的占位过程,绝不猜测客户业务表。
|
||||
*/
|
||||
|
||||
IF OBJECT_ID(N'dbo.p_agent_dynamic_module_lookup_evidence_v1', N'U') IS NULL
|
||||
BEGIN
|
||||
CREATE TABLE dbo.p_agent_dynamic_module_lookup_evidence_v1
|
||||
(
|
||||
account_book NVARCHAR(128) NOT NULL,
|
||||
subsystem_id NVARCHAR(128) NOT NULL,
|
||||
database_scope_fingerprint CHAR(64) NOT NULL,
|
||||
evidence_sha256 CHAR(64) NOT NULL,
|
||||
parameterized_reads_verified BIT NOT NULL,
|
||||
permission_recheck_verified BIT NOT NULL,
|
||||
no_side_effects_verified BIT NOT NULL,
|
||||
configuration_binding_verified BIT NOT NULL,
|
||||
windows_integration_verified BIT NOT NULL,
|
||||
maximum_candidates INT NOT NULL,
|
||||
validated_by NVARCHAR(128) NOT NULL,
|
||||
validated_at_utc DATETIME2(3) NOT NULL,
|
||||
row_version ROWVERSION NOT NULL,
|
||||
CONSTRAINT PK_p_agent_dynamic_module_lookup_evidence_v1
|
||||
PRIMARY KEY CLUSTERED
|
||||
(account_book, subsystem_id, database_scope_fingerprint),
|
||||
CONSTRAINT CK_p_agent_dynamic_module_lookup_scope_hash_v1
|
||||
CHECK
|
||||
(
|
||||
LEN(database_scope_fingerprint) = 64
|
||||
AND database_scope_fingerprint
|
||||
COLLATE Latin1_General_100_BIN2 NOT LIKE '%[^0-9a-f]%'
|
||||
),
|
||||
CONSTRAINT CK_p_agent_dynamic_module_lookup_evidence_hash_v1
|
||||
CHECK
|
||||
(
|
||||
LEN(evidence_sha256) = 64
|
||||
AND evidence_sha256
|
||||
COLLATE Latin1_General_100_BIN2 NOT LIKE '%[^0-9a-f]%'
|
||||
),
|
||||
CONSTRAINT CK_p_agent_dynamic_module_lookup_candidates_v1
|
||||
CHECK (maximum_candidates BETWEEN 1 AND 20)
|
||||
);
|
||||
END;
|
||||
|
||||
IF OBJECT_ID(N'dbo.p_lserp_agent_module_lookup_readiness_v1', N'P') IS NULL
|
||||
BEGIN
|
||||
EXEC(N'
|
||||
CREATE PROCEDURE dbo.p_lserp_agent_module_lookup_readiness_v1
|
||||
@account_book NVARCHAR(128),
|
||||
@subsystem_id NVARCHAR(128),
|
||||
@database_scope_fingerprint CHAR(64)
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
SELECT
|
||||
''1.0'' AS schema_version,
|
||||
CAST(CASE WHEN
|
||||
parameterized_reads_verified = 1
|
||||
AND permission_recheck_verified = 1
|
||||
AND no_side_effects_verified = 1
|
||||
AND configuration_binding_verified = 1
|
||||
AND windows_integration_verified = 1
|
||||
THEN 1 ELSE 0 END AS BIT) AS ready,
|
||||
evidence_sha256,
|
||||
validated_at_utc,
|
||||
account_book,
|
||||
subsystem_id,
|
||||
database_scope_fingerprint,
|
||||
parameterized_reads_verified,
|
||||
permission_recheck_verified,
|
||||
no_side_effects_verified,
|
||||
configuration_binding_verified,
|
||||
windows_integration_verified,
|
||||
maximum_candidates
|
||||
FROM dbo.p_agent_dynamic_module_lookup_evidence_v1
|
||||
WHERE account_book = @account_book
|
||||
AND subsystem_id = @subsystem_id
|
||||
AND database_scope_fingerprint
|
||||
COLLATE Latin1_General_100_BIN2 =
|
||||
@database_scope_fingerprint COLLATE Latin1_General_100_BIN2;
|
||||
END;');
|
||||
END;
|
||||
|
||||
IF OBJECT_ID(N'dbo.p_lserp_agent_module_lookup_read_v1', N'P') IS NULL
|
||||
BEGIN
|
||||
EXEC(N'
|
||||
CREATE PROCEDURE dbo.p_lserp_agent_module_lookup_read_v1
|
||||
@module_code NVARCHAR(64),
|
||||
@module_kind NVARCHAR(16),
|
||||
@contract_fingerprint CHAR(64),
|
||||
@configuration_fingerprint CHAR(64),
|
||||
@field_configuration_fingerprint CHAR(64),
|
||||
@parameter_id NVARCHAR(17),
|
||||
@scope NVARCHAR(6),
|
||||
@row_number INT,
|
||||
@reference NVARCHAR(2048),
|
||||
@context_xml NVARCHAR(MAX),
|
||||
@account_book NVARCHAR(128),
|
||||
@subsystem_id NVARCHAR(128),
|
||||
@user_id NVARCHAR(128),
|
||||
@correlation_id VARCHAR(128),
|
||||
@database_scope_fingerprint CHAR(64)
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
RAISERROR(N''客户动态 Lookup 只读适配过程尚未实施。'', 16, 1);
|
||||
RETURN;
|
||||
END;');
|
||||
END;
|
||||
|
||||
IF OBJECT_ID(N'dbo.p_agent_dynamic_module_write_evidence_v2', N'U') IS NULL
|
||||
BEGIN
|
||||
CREATE TABLE dbo.p_agent_dynamic_module_write_evidence_v2
|
||||
(
|
||||
module_code NVARCHAR(64) NOT NULL,
|
||||
module_kind VARCHAR(16) NOT NULL,
|
||||
configuration_fingerprint CHAR(64) NOT NULL,
|
||||
native_save_family VARCHAR(64) NOT NULL,
|
||||
native_execution_profile_fingerprint CHAR(64) NOT NULL,
|
||||
account_book NVARCHAR(128) NOT NULL,
|
||||
subsystem_id NVARCHAR(128) NOT NULL,
|
||||
database_scope_fingerprint CHAR(64) NOT NULL,
|
||||
adapter_id VARCHAR(128) NOT NULL,
|
||||
adapter_version VARCHAR(64) NOT NULL,
|
||||
evidence_id VARCHAR(128) NOT NULL,
|
||||
evidence_sha256 CHAR(64) NOT NULL,
|
||||
customer_configuration_validated BIT NOT NULL,
|
||||
native_validation_verified BIT NOT NULL,
|
||||
server_defaults_verified BIT NOT NULL,
|
||||
module_hooks_verified BIT NOT NULL,
|
||||
transactional_write_verified BIT NOT NULL,
|
||||
persistent_idempotency_verified BIT NOT NULL,
|
||||
permission_recheck_verified BIT NOT NULL,
|
||||
configuration_binding_verified BIT NOT NULL,
|
||||
windows_integration_verified BIT NOT NULL,
|
||||
validated_by NVARCHAR(128) NOT NULL,
|
||||
validated_at_utc DATETIME2(3) NOT NULL,
|
||||
row_version ROWVERSION NOT NULL,
|
||||
CONSTRAINT PK_p_agent_dynamic_module_write_evidence_v2
|
||||
PRIMARY KEY CLUSTERED
|
||||
(
|
||||
module_code,
|
||||
module_kind,
|
||||
configuration_fingerprint,
|
||||
native_execution_profile_fingerprint,
|
||||
account_book,
|
||||
subsystem_id,
|
||||
database_scope_fingerprint
|
||||
),
|
||||
CONSTRAINT CK_p_agent_dynamic_module_write_kind_v2
|
||||
CHECK
|
||||
(
|
||||
module_kind COLLATE Latin1_General_100_BIN2
|
||||
IN ('base', 'bill')
|
||||
),
|
||||
CONSTRAINT CK_p_agent_dynamic_module_write_config_hash_v2
|
||||
CHECK
|
||||
(
|
||||
LEN(configuration_fingerprint) = 64
|
||||
AND configuration_fingerprint
|
||||
COLLATE Latin1_General_100_BIN2 NOT LIKE '%[^0-9a-f]%'
|
||||
),
|
||||
CONSTRAINT CK_p_agent_dynamic_module_write_native_family_v2
|
||||
CHECK
|
||||
(
|
||||
native_save_family COLLATE Latin1_General_100_BIN2 IN
|
||||
(
|
||||
'legacy.base-save.p-base-save',
|
||||
'legacy.base-save.p-base-save70',
|
||||
'legacy.bill-save.p-bill-save-pr3',
|
||||
'legacy.bill-save.p-bill-save-pr70'
|
||||
)
|
||||
),
|
||||
CONSTRAINT CK_p_agent_dynamic_module_write_native_hash_v2
|
||||
CHECK
|
||||
(
|
||||
LEN(native_execution_profile_fingerprint) = 64
|
||||
AND native_execution_profile_fingerprint
|
||||
COLLATE Latin1_General_100_BIN2 NOT LIKE '%[^0-9a-f]%'
|
||||
),
|
||||
CONSTRAINT CK_p_agent_dynamic_module_write_scope_hash_v2
|
||||
CHECK
|
||||
(
|
||||
LEN(database_scope_fingerprint) = 64
|
||||
AND database_scope_fingerprint
|
||||
COLLATE Latin1_General_100_BIN2 NOT LIKE '%[^0-9a-f]%'
|
||||
),
|
||||
CONSTRAINT CK_p_agent_dynamic_module_write_evidence_hash_v2
|
||||
CHECK
|
||||
(
|
||||
LEN(evidence_sha256) = 64
|
||||
AND evidence_sha256
|
||||
COLLATE Latin1_General_100_BIN2 NOT LIKE '%[^0-9a-f]%'
|
||||
)
|
||||
);
|
||||
END;
|
||||
|
||||
IF OBJECT_ID(N'dbo.p_lserp_agent_module_write_readiness_v2', N'P') IS NULL
|
||||
BEGIN
|
||||
EXEC(N'
|
||||
CREATE PROCEDURE dbo.p_lserp_agent_module_write_readiness_v2
|
||||
@module_code NVARCHAR(64),
|
||||
@module_kind VARCHAR(16),
|
||||
@configuration_fingerprint CHAR(64),
|
||||
@native_save_family VARCHAR(64),
|
||||
@native_execution_profile_fingerprint CHAR(64),
|
||||
@account_book NVARCHAR(128),
|
||||
@subsystem_id NVARCHAR(128),
|
||||
@database_scope_fingerprint CHAR(64)
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
SELECT
|
||||
''2.0'' AS schema_version,
|
||||
CAST(CASE WHEN
|
||||
customer_configuration_validated = 1
|
||||
AND native_validation_verified = 1
|
||||
AND server_defaults_verified = 1
|
||||
AND module_hooks_verified = 1
|
||||
AND transactional_write_verified = 1
|
||||
AND persistent_idempotency_verified = 1
|
||||
AND permission_recheck_verified = 1
|
||||
AND configuration_binding_verified = 1
|
||||
AND windows_integration_verified = 1
|
||||
THEN 1 ELSE 0 END AS BIT) AS ready,
|
||||
adapter_id,
|
||||
adapter_version,
|
||||
evidence_id,
|
||||
evidence_sha256,
|
||||
module_code,
|
||||
module_kind,
|
||||
configuration_fingerprint,
|
||||
native_save_family,
|
||||
native_execution_profile_fingerprint,
|
||||
account_book,
|
||||
subsystem_id,
|
||||
database_scope_fingerprint,
|
||||
validated_by,
|
||||
validated_at_utc,
|
||||
customer_configuration_validated,
|
||||
native_validation_verified,
|
||||
server_defaults_verified,
|
||||
module_hooks_verified,
|
||||
transactional_write_verified,
|
||||
persistent_idempotency_verified,
|
||||
permission_recheck_verified,
|
||||
configuration_binding_verified,
|
||||
windows_integration_verified
|
||||
FROM dbo.p_agent_dynamic_module_write_evidence_v2
|
||||
WHERE module_code COLLATE Latin1_General_100_BIN2 =
|
||||
@module_code COLLATE Latin1_General_100_BIN2
|
||||
AND module_kind COLLATE Latin1_General_100_BIN2 =
|
||||
@module_kind COLLATE Latin1_General_100_BIN2
|
||||
AND configuration_fingerprint COLLATE Latin1_General_100_BIN2 =
|
||||
@configuration_fingerprint COLLATE Latin1_General_100_BIN2
|
||||
AND native_save_family COLLATE Latin1_General_100_BIN2 =
|
||||
@native_save_family COLLATE Latin1_General_100_BIN2
|
||||
AND native_execution_profile_fingerprint
|
||||
COLLATE Latin1_General_100_BIN2 =
|
||||
@native_execution_profile_fingerprint
|
||||
COLLATE Latin1_General_100_BIN2
|
||||
AND account_book = @account_book
|
||||
AND subsystem_id = @subsystem_id
|
||||
AND database_scope_fingerprint COLLATE Latin1_General_100_BIN2 =
|
||||
@database_scope_fingerprint COLLATE Latin1_General_100_BIN2;
|
||||
END;');
|
||||
END;
|
||||
|
||||
/*
|
||||
此过程只能由客户 DBA/发布流水线在 Windows 真实 ERP 验收完成后调用,
|
||||
不得授权给 ERP 日常运行账号。@evidence_sha256 必须是同一份
|
||||
TrustedPeople RSA-SHA256 验收清单的 contentSha256,@validated_at_utc
|
||||
必须与清单 issuedAtUtc 一致。进程内验签器会再次精确比较。
|
||||
*/
|
||||
IF OBJECT_ID(N'dbo.p_lserp_agent_module_write_acceptance_v2', N'P') IS NULL
|
||||
BEGIN
|
||||
EXEC(N'
|
||||
CREATE PROCEDURE dbo.p_lserp_agent_module_write_acceptance_v2
|
||||
@module_code NVARCHAR(64),
|
||||
@module_kind VARCHAR(16),
|
||||
@configuration_fingerprint CHAR(64),
|
||||
@native_save_family VARCHAR(64),
|
||||
@native_execution_profile_fingerprint CHAR(64),
|
||||
@account_book NVARCHAR(128),
|
||||
@subsystem_id NVARCHAR(128),
|
||||
@database_scope_fingerprint CHAR(64),
|
||||
@adapter_id VARCHAR(128),
|
||||
@adapter_version VARCHAR(64),
|
||||
@evidence_id VARCHAR(128),
|
||||
@evidence_sha256 CHAR(64),
|
||||
@validated_by NVARCHAR(128),
|
||||
@validated_at_utc DATETIME2(3)
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
SET XACT_ABORT ON;
|
||||
|
||||
IF @module_kind COLLATE Latin1_General_100_BIN2 NOT IN (''base'', ''bill'')
|
||||
OR NULLIF(LTRIM(RTRIM(@module_code)), N'''') IS NULL
|
||||
OR @native_save_family COLLATE Latin1_General_100_BIN2 NOT IN
|
||||
(
|
||||
''legacy.base-save.p-base-save'',
|
||||
''legacy.base-save.p-base-save70'',
|
||||
''legacy.bill-save.p-bill-save-pr3'',
|
||||
''legacy.bill-save.p-bill-save-pr70''
|
||||
)
|
||||
OR NULLIF(LTRIM(RTRIM(@account_book)), N'''') IS NULL
|
||||
OR NULLIF(LTRIM(RTRIM(@subsystem_id)), N'''') IS NULL
|
||||
OR NULLIF(LTRIM(RTRIM(@adapter_id)), '''') IS NULL
|
||||
OR NULLIF(LTRIM(RTRIM(@adapter_version)), '''') IS NULL
|
||||
OR NULLIF(LTRIM(RTRIM(@evidence_id)), '''') IS NULL
|
||||
OR NULLIF(LTRIM(RTRIM(@validated_by)), N'''') IS NULL
|
||||
BEGIN
|
||||
RAISERROR(N''动态写验收的模块、作用域或证据字段无效。'', 16, 1);
|
||||
RETURN;
|
||||
END;
|
||||
IF LEN(@configuration_fingerprint) <> 64
|
||||
OR @configuration_fingerprint COLLATE Latin1_General_100_BIN2
|
||||
LIKE ''%[^0-9a-f]%''
|
||||
OR LEN(@native_execution_profile_fingerprint) <> 64
|
||||
OR @native_execution_profile_fingerprint
|
||||
COLLATE Latin1_General_100_BIN2 LIKE ''%[^0-9a-f]%''
|
||||
OR LEN(@database_scope_fingerprint) <> 64
|
||||
OR @database_scope_fingerprint COLLATE Latin1_General_100_BIN2
|
||||
LIKE ''%[^0-9a-f]%''
|
||||
OR LEN(@evidence_sha256) <> 64
|
||||
OR @evidence_sha256 COLLATE Latin1_General_100_BIN2
|
||||
LIKE ''%[^0-9a-f]%''
|
||||
BEGIN
|
||||
RAISERROR(N''动态写验收摘要必须是 64 位小写十六进制。'', 16, 1);
|
||||
RETURN;
|
||||
END;
|
||||
IF @validated_at_utc IS NULL
|
||||
OR @validated_at_utc > DATEADD(MINUTE, 5, SYSUTCDATETIME())
|
||||
OR @validated_at_utc < DATEADD(DAY, -366, SYSUTCDATETIME())
|
||||
BEGIN
|
||||
RAISERROR(N''动态写验收时间无效。'', 16, 1);
|
||||
RETURN;
|
||||
END;
|
||||
|
||||
BEGIN TRANSACTION;
|
||||
UPDATE dbo.p_agent_dynamic_module_write_evidence_v2
|
||||
WITH (UPDLOCK, SERIALIZABLE)
|
||||
SET adapter_id = @adapter_id,
|
||||
adapter_version = @adapter_version,
|
||||
evidence_id = @evidence_id,
|
||||
evidence_sha256 = @evidence_sha256,
|
||||
customer_configuration_validated = 1,
|
||||
native_validation_verified = 1,
|
||||
server_defaults_verified = 1,
|
||||
module_hooks_verified = 1,
|
||||
transactional_write_verified = 1,
|
||||
persistent_idempotency_verified = 1,
|
||||
permission_recheck_verified = 1,
|
||||
configuration_binding_verified = 1,
|
||||
windows_integration_verified = 1,
|
||||
validated_by = @validated_by,
|
||||
validated_at_utc = @validated_at_utc
|
||||
WHERE module_code COLLATE Latin1_General_100_BIN2 =
|
||||
@module_code COLLATE Latin1_General_100_BIN2
|
||||
AND module_kind COLLATE Latin1_General_100_BIN2 =
|
||||
@module_kind COLLATE Latin1_General_100_BIN2
|
||||
AND configuration_fingerprint COLLATE Latin1_General_100_BIN2 =
|
||||
@configuration_fingerprint COLLATE Latin1_General_100_BIN2
|
||||
AND native_save_family COLLATE Latin1_General_100_BIN2 =
|
||||
@native_save_family COLLATE Latin1_General_100_BIN2
|
||||
AND native_execution_profile_fingerprint
|
||||
COLLATE Latin1_General_100_BIN2 =
|
||||
@native_execution_profile_fingerprint
|
||||
COLLATE Latin1_General_100_BIN2
|
||||
AND account_book = @account_book
|
||||
AND subsystem_id = @subsystem_id
|
||||
AND database_scope_fingerprint COLLATE Latin1_General_100_BIN2 =
|
||||
@database_scope_fingerprint COLLATE Latin1_General_100_BIN2;
|
||||
|
||||
IF @@ROWCOUNT = 0
|
||||
BEGIN
|
||||
INSERT dbo.p_agent_dynamic_module_write_evidence_v2
|
||||
(
|
||||
module_code, module_kind, configuration_fingerprint,
|
||||
native_save_family, native_execution_profile_fingerprint,
|
||||
account_book, subsystem_id, database_scope_fingerprint,
|
||||
adapter_id, adapter_version, evidence_id, evidence_sha256,
|
||||
customer_configuration_validated, native_validation_verified,
|
||||
server_defaults_verified, module_hooks_verified,
|
||||
transactional_write_verified, persistent_idempotency_verified,
|
||||
permission_recheck_verified, configuration_binding_verified,
|
||||
windows_integration_verified, validated_by, validated_at_utc
|
||||
)
|
||||
VALUES
|
||||
(
|
||||
@module_code, @module_kind, @configuration_fingerprint,
|
||||
@native_save_family, @native_execution_profile_fingerprint,
|
||||
@account_book, @subsystem_id, @database_scope_fingerprint,
|
||||
@adapter_id, @adapter_version, @evidence_id, @evidence_sha256,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
@validated_by, @validated_at_utc
|
||||
);
|
||||
END;
|
||||
COMMIT TRANSACTION;
|
||||
|
||||
SELECT
|
||||
@module_code AS module_code,
|
||||
@module_kind AS module_kind,
|
||||
@configuration_fingerprint AS configuration_fingerprint,
|
||||
@native_save_family AS native_save_family,
|
||||
@native_execution_profile_fingerprint
|
||||
AS native_execution_profile_fingerprint,
|
||||
@account_book AS account_book,
|
||||
@subsystem_id AS subsystem_id,
|
||||
@database_scope_fingerprint AS database_scope_fingerprint,
|
||||
@evidence_id AS evidence_id,
|
||||
@evidence_sha256 AS evidence_sha256,
|
||||
@validated_at_utc AS validated_at_utc;
|
||||
END;');
|
||||
END;
|
||||
|
||||
/*
|
||||
客户实现 p_lserp_agent_module_create_v2 时必须:
|
||||
1. 按 module_code + module_kind 从“当前数据库”重读低代码完整配置;
|
||||
同时按 NewVer 重新解析 native_save_family,并精确核对服务端生成的
|
||||
native_execution_profile_fingerprint;
|
||||
2. 用 parameterId + scope + fieldConfigurationFingerprint 重新映射当前可编辑字段,
|
||||
trustedFieldName 只能用于相等性校验,不得直接拼接成 SQL 标识符;
|
||||
3. 忽略模型对只读字段、编号、审批状态、用户/账套字段的任何伪造,
|
||||
不得把这些字段发布为可编辑参数;
|
||||
4. 在同一外层事务内重做用户/行级权限、原生校验、服务端默认值、
|
||||
自动编号、模块保存钩子、配置指纹、持久幂等和审计;
|
||||
5. 不得 COMMIT/ROLLBACK 调用方外层事务;成功时恰好返回一行下方标准结果。
|
||||
列必须恰好为 success, code, message, record_id, needs_ui,
|
||||
idempotency_replayed, applied_idempotency_key,
|
||||
applied_input_fingerprint, transaction_evidence_id, business_audit_id。
|
||||
*/
|
||||
IF OBJECT_ID(N'dbo.p_lserp_agent_module_create_v2', N'P') IS NULL
|
||||
BEGIN
|
||||
EXEC(N'
|
||||
CREATE PROCEDURE dbo.p_lserp_agent_module_create_v2
|
||||
@module_code NVARCHAR(64),
|
||||
@module_kind VARCHAR(16),
|
||||
@contract_fingerprint CHAR(64),
|
||||
@configuration_fingerprint CHAR(64),
|
||||
@native_save_family VARCHAR(64),
|
||||
@native_execution_profile_fingerprint CHAR(64),
|
||||
@values_xml NVARCHAR(MAX),
|
||||
@account_book NVARCHAR(128),
|
||||
@subsystem_id NVARCHAR(128),
|
||||
@user_id NVARCHAR(128),
|
||||
@user_name NVARCHAR(128),
|
||||
@correlation_id VARCHAR(128),
|
||||
@database_scope_fingerprint CHAR(64),
|
||||
@idempotency_key VARCHAR(128),
|
||||
@input_fingerprint CHAR(64)
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
RAISERROR(N''客户动态模块事务新增适配过程尚未实施。'', 16, 1);
|
||||
RETURN;
|
||||
END;');
|
||||
END;
|
||||
|
||||
/*
|
||||
生产授权由客户 DBA 显式完成:日常 ERP 账号只能 EXECUTE readiness/read/create,
|
||||
不得对证据表拥有 INSERT/UPDATE/DELETE,也不得 EXECUTE acceptance。
|
||||
*/
|
||||
@@ -0,0 +1,422 @@
|
||||
SET XACT_ABORT ON;
|
||||
|
||||
/*
|
||||
通用修改与通用新增使用独立验收边界。本脚本只允许基础档案 base:
|
||||
|
||||
- snapshot 固定只读过程必须按当前用户权限唯一定位记录,返回完整可编辑
|
||||
参数快照、私有 record_locator、record_version_token 与快照指纹;
|
||||
- update 固定过程由调用方放入 Serializable 事务,必须先检查持久幂等,
|
||||
再锁定目标记录并比较版本和快照,冲突只能返回失败,禁止静默覆盖;
|
||||
- trustedFieldName 只能和当前低代码配置做相等性检查,禁止直接拼 SQL;
|
||||
- 动态更新 v2 对象与v1并存,不就地更改旧证据表或过程;
|
||||
- 本脚本只创建空证据表和拒绝服务占位过程,不读取或修改客户业务表。
|
||||
*/
|
||||
|
||||
IF OBJECT_ID(N'dbo.p_agent_dynamic_module_update_evidence_v2', N'U') IS NULL
|
||||
BEGIN
|
||||
CREATE TABLE dbo.p_agent_dynamic_module_update_evidence_v2
|
||||
(
|
||||
module_code NVARCHAR(64) NOT NULL,
|
||||
module_kind VARCHAR(16) NOT NULL,
|
||||
configuration_fingerprint CHAR(64) NOT NULL,
|
||||
native_save_family VARCHAR(64) NOT NULL,
|
||||
native_execution_profile_fingerprint CHAR(64) NOT NULL,
|
||||
account_book NVARCHAR(128) NOT NULL,
|
||||
subsystem_id NVARCHAR(128) NOT NULL,
|
||||
database_scope_fingerprint CHAR(64) NOT NULL,
|
||||
adapter_id VARCHAR(128) NOT NULL,
|
||||
adapter_version VARCHAR(64) NOT NULL,
|
||||
evidence_id VARCHAR(128) NOT NULL,
|
||||
evidence_sha256 CHAR(64) NOT NULL,
|
||||
customer_configuration_validated BIT NOT NULL,
|
||||
record_resolution_verified BIT NOT NULL,
|
||||
snapshot_binding_verified BIT NOT NULL,
|
||||
optimistic_concurrency_verified BIT NOT NULL,
|
||||
partial_update_verified BIT NOT NULL,
|
||||
native_validation_verified BIT NOT NULL,
|
||||
module_hooks_verified BIT NOT NULL,
|
||||
transactional_write_verified BIT NOT NULL,
|
||||
persistent_idempotency_verified BIT NOT NULL,
|
||||
permission_recheck_verified BIT NOT NULL,
|
||||
configuration_binding_verified BIT NOT NULL,
|
||||
windows_integration_verified BIT NOT NULL,
|
||||
validated_by NVARCHAR(128) NOT NULL,
|
||||
validated_at_utc DATETIME2(3) NOT NULL,
|
||||
row_version ROWVERSION NOT NULL,
|
||||
CONSTRAINT PK_p_agent_dynamic_module_update_evidence_v2
|
||||
PRIMARY KEY CLUSTERED
|
||||
(
|
||||
module_code,
|
||||
module_kind,
|
||||
configuration_fingerprint,
|
||||
native_execution_profile_fingerprint,
|
||||
account_book,
|
||||
subsystem_id,
|
||||
database_scope_fingerprint
|
||||
),
|
||||
CONSTRAINT CK_p_agent_dynamic_module_update_kind_v2
|
||||
CHECK
|
||||
(
|
||||
module_kind COLLATE Latin1_General_100_BIN2 = 'base'
|
||||
),
|
||||
CONSTRAINT CK_p_agent_dynamic_module_update_config_hash_v2
|
||||
CHECK
|
||||
(
|
||||
LEN(configuration_fingerprint) = 64
|
||||
AND configuration_fingerprint
|
||||
COLLATE Latin1_General_100_BIN2 NOT LIKE '%[^0-9a-f]%'
|
||||
),
|
||||
CONSTRAINT CK_p_agent_dynamic_module_update_native_family_v2
|
||||
CHECK
|
||||
(
|
||||
native_save_family COLLATE Latin1_General_100_BIN2 IN
|
||||
(
|
||||
'legacy.base-save.p-base-save',
|
||||
'legacy.base-save.p-base-save70'
|
||||
)
|
||||
),
|
||||
CONSTRAINT CK_p_agent_dynamic_module_update_native_hash_v2
|
||||
CHECK
|
||||
(
|
||||
LEN(native_execution_profile_fingerprint) = 64
|
||||
AND native_execution_profile_fingerprint
|
||||
COLLATE Latin1_General_100_BIN2 NOT LIKE '%[^0-9a-f]%'
|
||||
),
|
||||
CONSTRAINT CK_p_agent_dynamic_module_update_scope_hash_v2
|
||||
CHECK
|
||||
(
|
||||
LEN(database_scope_fingerprint) = 64
|
||||
AND database_scope_fingerprint
|
||||
COLLATE Latin1_General_100_BIN2 NOT LIKE '%[^0-9a-f]%'
|
||||
),
|
||||
CONSTRAINT CK_p_agent_dynamic_module_update_evidence_hash_v2
|
||||
CHECK
|
||||
(
|
||||
LEN(evidence_sha256) = 64
|
||||
AND evidence_sha256
|
||||
COLLATE Latin1_General_100_BIN2 NOT LIKE '%[^0-9a-f]%'
|
||||
)
|
||||
);
|
||||
END;
|
||||
|
||||
IF OBJECT_ID(N'dbo.p_lserp_agent_module_update_readiness_v2', N'P') IS NULL
|
||||
BEGIN
|
||||
EXEC(N'
|
||||
CREATE PROCEDURE dbo.p_lserp_agent_module_update_readiness_v2
|
||||
@module_code NVARCHAR(64),
|
||||
@module_kind VARCHAR(16),
|
||||
@configuration_fingerprint CHAR(64),
|
||||
@native_save_family VARCHAR(64),
|
||||
@native_execution_profile_fingerprint CHAR(64),
|
||||
@account_book NVARCHAR(128),
|
||||
@subsystem_id NVARCHAR(128),
|
||||
@database_scope_fingerprint CHAR(64)
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
SELECT
|
||||
''2.0'' AS schema_version,
|
||||
CAST(CASE WHEN
|
||||
customer_configuration_validated = 1
|
||||
AND record_resolution_verified = 1
|
||||
AND snapshot_binding_verified = 1
|
||||
AND optimistic_concurrency_verified = 1
|
||||
AND partial_update_verified = 1
|
||||
AND native_validation_verified = 1
|
||||
AND module_hooks_verified = 1
|
||||
AND transactional_write_verified = 1
|
||||
AND persistent_idempotency_verified = 1
|
||||
AND permission_recheck_verified = 1
|
||||
AND configuration_binding_verified = 1
|
||||
AND windows_integration_verified = 1
|
||||
THEN 1 ELSE 0 END AS BIT) AS ready,
|
||||
adapter_id,
|
||||
adapter_version,
|
||||
evidence_id,
|
||||
evidence_sha256,
|
||||
module_code,
|
||||
module_kind,
|
||||
configuration_fingerprint,
|
||||
native_save_family,
|
||||
native_execution_profile_fingerprint,
|
||||
account_book,
|
||||
subsystem_id,
|
||||
database_scope_fingerprint,
|
||||
validated_by,
|
||||
validated_at_utc,
|
||||
customer_configuration_validated,
|
||||
record_resolution_verified,
|
||||
snapshot_binding_verified,
|
||||
optimistic_concurrency_verified,
|
||||
partial_update_verified,
|
||||
native_validation_verified,
|
||||
module_hooks_verified,
|
||||
transactional_write_verified,
|
||||
persistent_idempotency_verified,
|
||||
permission_recheck_verified,
|
||||
configuration_binding_verified,
|
||||
windows_integration_verified
|
||||
FROM dbo.p_agent_dynamic_module_update_evidence_v2
|
||||
WHERE module_code COLLATE Latin1_General_100_BIN2 =
|
||||
@module_code COLLATE Latin1_General_100_BIN2
|
||||
AND module_kind COLLATE Latin1_General_100_BIN2 =
|
||||
@module_kind COLLATE Latin1_General_100_BIN2
|
||||
AND configuration_fingerprint COLLATE Latin1_General_100_BIN2 =
|
||||
@configuration_fingerprint COLLATE Latin1_General_100_BIN2
|
||||
AND native_save_family COLLATE Latin1_General_100_BIN2 =
|
||||
@native_save_family COLLATE Latin1_General_100_BIN2
|
||||
AND native_execution_profile_fingerprint
|
||||
COLLATE Latin1_General_100_BIN2 =
|
||||
@native_execution_profile_fingerprint
|
||||
COLLATE Latin1_General_100_BIN2
|
||||
AND account_book = @account_book
|
||||
AND subsystem_id = @subsystem_id
|
||||
AND database_scope_fingerprint COLLATE Latin1_General_100_BIN2 =
|
||||
@database_scope_fingerprint COLLATE Latin1_General_100_BIN2;
|
||||
END;');
|
||||
END;
|
||||
|
||||
/*
|
||||
只允许客户 DBA/发布流水线调用 acceptance;不得授权给 ERP 日常账号。
|
||||
@evidence_sha256 必须等于更新专用 TrustedPeople 签名清单的
|
||||
contentSha256,@validated_at_utc 必须与 issuedAtUtc 一致。
|
||||
*/
|
||||
IF OBJECT_ID(N'dbo.p_lserp_agent_module_update_acceptance_v2', N'P') IS NULL
|
||||
BEGIN
|
||||
EXEC(N'
|
||||
CREATE PROCEDURE dbo.p_lserp_agent_module_update_acceptance_v2
|
||||
@module_code NVARCHAR(64),
|
||||
@module_kind VARCHAR(16),
|
||||
@configuration_fingerprint CHAR(64),
|
||||
@native_save_family VARCHAR(64),
|
||||
@native_execution_profile_fingerprint CHAR(64),
|
||||
@account_book NVARCHAR(128),
|
||||
@subsystem_id NVARCHAR(128),
|
||||
@database_scope_fingerprint CHAR(64),
|
||||
@adapter_id VARCHAR(128),
|
||||
@adapter_version VARCHAR(64),
|
||||
@evidence_id VARCHAR(128),
|
||||
@evidence_sha256 CHAR(64),
|
||||
@validated_by NVARCHAR(128),
|
||||
@validated_at_utc DATETIME2(3)
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
SET XACT_ABORT ON;
|
||||
|
||||
IF @module_kind COLLATE Latin1_General_100_BIN2 <> ''base''
|
||||
OR NULLIF(LTRIM(RTRIM(@module_code)), N'''') IS NULL
|
||||
OR @native_save_family COLLATE Latin1_General_100_BIN2 NOT IN
|
||||
(
|
||||
''legacy.base-save.p-base-save'',
|
||||
''legacy.base-save.p-base-save70''
|
||||
)
|
||||
OR NULLIF(LTRIM(RTRIM(@account_book)), N'''') IS NULL
|
||||
OR NULLIF(LTRIM(RTRIM(@subsystem_id)), N'''') IS NULL
|
||||
OR NULLIF(LTRIM(RTRIM(@adapter_id)), '''') IS NULL
|
||||
OR NULLIF(LTRIM(RTRIM(@adapter_version)), '''') IS NULL
|
||||
OR NULLIF(LTRIM(RTRIM(@evidence_id)), '''') IS NULL
|
||||
OR NULLIF(LTRIM(RTRIM(@validated_by)), N'''') IS NULL
|
||||
BEGIN
|
||||
RAISERROR(N''动态更新验收的模块、作用域或证据字段无效。'', 16, 1);
|
||||
RETURN;
|
||||
END;
|
||||
IF LEN(@configuration_fingerprint) <> 64
|
||||
OR @configuration_fingerprint COLLATE Latin1_General_100_BIN2
|
||||
LIKE ''%[^0-9a-f]%''
|
||||
OR LEN(@native_execution_profile_fingerprint) <> 64
|
||||
OR @native_execution_profile_fingerprint
|
||||
COLLATE Latin1_General_100_BIN2 LIKE ''%[^0-9a-f]%''
|
||||
OR LEN(@database_scope_fingerprint) <> 64
|
||||
OR @database_scope_fingerprint COLLATE Latin1_General_100_BIN2
|
||||
LIKE ''%[^0-9a-f]%''
|
||||
OR LEN(@evidence_sha256) <> 64
|
||||
OR @evidence_sha256 COLLATE Latin1_General_100_BIN2
|
||||
LIKE ''%[^0-9a-f]%''
|
||||
BEGIN
|
||||
RAISERROR(N''动态更新验收摘要必须是 64 位小写十六进制。'', 16, 1);
|
||||
RETURN;
|
||||
END;
|
||||
IF @validated_at_utc IS NULL
|
||||
OR @validated_at_utc > DATEADD(MINUTE, 5, SYSUTCDATETIME())
|
||||
OR @validated_at_utc < DATEADD(DAY, -366, SYSUTCDATETIME())
|
||||
BEGIN
|
||||
RAISERROR(N''动态更新验收时间无效。'', 16, 1);
|
||||
RETURN;
|
||||
END;
|
||||
|
||||
BEGIN TRANSACTION;
|
||||
UPDATE dbo.p_agent_dynamic_module_update_evidence_v2
|
||||
WITH (UPDLOCK, SERIALIZABLE)
|
||||
SET adapter_id = @adapter_id,
|
||||
adapter_version = @adapter_version,
|
||||
evidence_id = @evidence_id,
|
||||
evidence_sha256 = @evidence_sha256,
|
||||
customer_configuration_validated = 1,
|
||||
record_resolution_verified = 1,
|
||||
snapshot_binding_verified = 1,
|
||||
optimistic_concurrency_verified = 1,
|
||||
partial_update_verified = 1,
|
||||
native_validation_verified = 1,
|
||||
module_hooks_verified = 1,
|
||||
transactional_write_verified = 1,
|
||||
persistent_idempotency_verified = 1,
|
||||
permission_recheck_verified = 1,
|
||||
configuration_binding_verified = 1,
|
||||
windows_integration_verified = 1,
|
||||
validated_by = @validated_by,
|
||||
validated_at_utc = @validated_at_utc
|
||||
WHERE module_code COLLATE Latin1_General_100_BIN2 =
|
||||
@module_code COLLATE Latin1_General_100_BIN2
|
||||
AND module_kind COLLATE Latin1_General_100_BIN2 =
|
||||
@module_kind COLLATE Latin1_General_100_BIN2
|
||||
AND configuration_fingerprint COLLATE Latin1_General_100_BIN2 =
|
||||
@configuration_fingerprint COLLATE Latin1_General_100_BIN2
|
||||
AND native_save_family COLLATE Latin1_General_100_BIN2 =
|
||||
@native_save_family COLLATE Latin1_General_100_BIN2
|
||||
AND native_execution_profile_fingerprint
|
||||
COLLATE Latin1_General_100_BIN2 =
|
||||
@native_execution_profile_fingerprint
|
||||
COLLATE Latin1_General_100_BIN2
|
||||
AND account_book = @account_book
|
||||
AND subsystem_id = @subsystem_id
|
||||
AND database_scope_fingerprint COLLATE Latin1_General_100_BIN2 =
|
||||
@database_scope_fingerprint COLLATE Latin1_General_100_BIN2;
|
||||
|
||||
IF @@ROWCOUNT = 0
|
||||
BEGIN
|
||||
INSERT dbo.p_agent_dynamic_module_update_evidence_v2
|
||||
(
|
||||
module_code, module_kind, configuration_fingerprint,
|
||||
native_save_family, native_execution_profile_fingerprint,
|
||||
account_book, subsystem_id, database_scope_fingerprint,
|
||||
adapter_id, adapter_version, evidence_id, evidence_sha256,
|
||||
customer_configuration_validated, record_resolution_verified,
|
||||
snapshot_binding_verified, optimistic_concurrency_verified,
|
||||
partial_update_verified, native_validation_verified,
|
||||
module_hooks_verified, transactional_write_verified,
|
||||
persistent_idempotency_verified, permission_recheck_verified,
|
||||
configuration_binding_verified, windows_integration_verified,
|
||||
validated_by, validated_at_utc
|
||||
)
|
||||
VALUES
|
||||
(
|
||||
@module_code, @module_kind, @configuration_fingerprint,
|
||||
@native_save_family, @native_execution_profile_fingerprint,
|
||||
@account_book, @subsystem_id, @database_scope_fingerprint,
|
||||
@adapter_id, @adapter_version, @evidence_id, @evidence_sha256,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
@validated_by, @validated_at_utc
|
||||
);
|
||||
END;
|
||||
COMMIT TRANSACTION;
|
||||
|
||||
SELECT
|
||||
@module_code AS module_code,
|
||||
@configuration_fingerprint AS configuration_fingerprint,
|
||||
@native_save_family AS native_save_family,
|
||||
@native_execution_profile_fingerprint
|
||||
AS native_execution_profile_fingerprint,
|
||||
@account_book AS account_book,
|
||||
@subsystem_id AS subsystem_id,
|
||||
@database_scope_fingerprint AS database_scope_fingerprint,
|
||||
@evidence_id AS evidence_id,
|
||||
@evidence_sha256 AS evidence_sha256,
|
||||
@validated_at_utc AS validated_at_utc;
|
||||
END;');
|
||||
END;
|
||||
|
||||
/*
|
||||
客户实现 snapshot 时必须:
|
||||
1. 只按当前模块白名单配置和参数化 record_query 查询,禁止动态 SQL;
|
||||
2. 复核当前用户、账套、子系统、数据库作用域及菜单/行级查看与修改权限;
|
||||
3. 零条返回 matched=0 + dynamic_module_update_record_not_found,多条返回
|
||||
matched=0 + dynamic_module_update_record_ambiguous,禁止任取第一条;
|
||||
4. 唯一记录返回完整可编辑参数全集,values_xml 根必须是
|
||||
<moduleUpdateSnapshot schemaVersion="1.0">,每个 value 只含 parameterId;
|
||||
5. record_locator 只供固定 update 过程解释;version_token 和 snapshot 指纹
|
||||
必须绑定锁前的完整持久记录状态,不能使用时间戳猜测或客户端输入;
|
||||
6. 不得产生业务副作用。调用方会回滚整个 ReadCommitted 事务。
|
||||
|
||||
结果 schema_version 必须为 2.0,列必须恰好为 schema_version, matched, code, message, module_code,
|
||||
module_kind, contract_fingerprint, configuration_fingerprint, record_locator,
|
||||
record_display, record_version_token, record_snapshot_fingerprint, values_xml。
|
||||
*/
|
||||
IF OBJECT_ID(N'dbo.p_lserp_agent_module_update_snapshot_v2', N'P') IS NULL
|
||||
BEGIN
|
||||
EXEC(N'
|
||||
CREATE PROCEDURE dbo.p_lserp_agent_module_update_snapshot_v2
|
||||
@module_code NVARCHAR(64),
|
||||
@module_kind VARCHAR(16),
|
||||
@contract_fingerprint CHAR(64),
|
||||
@configuration_fingerprint CHAR(64),
|
||||
@record_query NVARCHAR(256),
|
||||
@changes_xml NVARCHAR(MAX),
|
||||
@account_book NVARCHAR(128),
|
||||
@subsystem_id NVARCHAR(128),
|
||||
@user_id NVARCHAR(128),
|
||||
@user_name NVARCHAR(128),
|
||||
@correlation_id VARCHAR(128),
|
||||
@database_scope_fingerprint CHAR(64)
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
RAISERROR(N''客户动态模块记录快照适配过程尚未实施。'', 16, 1);
|
||||
RETURN;
|
||||
END;');
|
||||
END;
|
||||
|
||||
/*
|
||||
客户实现 update 时必须:
|
||||
1. 首先按 (作用域, 用户, 幂等键) 在持久幂等表中检查完全相同的
|
||||
input_fingerprint;已提交重放直接返回原事务/审计证据;
|
||||
2. 从当前数据库重读模块、字段配置和权限,只允许 base;用 parameterId +
|
||||
fieldConfigurationFingerprint 映射字段,trustedFieldName 仅作相等校验;
|
||||
同时按 NewVer 重新解析并核对 native_save_family 与
|
||||
native_execution_profile_fingerprint;
|
||||
3. 用 UPDLOCK/HOLDLOCK 唯一锁定 record_locator 指向的记录,再重新计算
|
||||
version_token 与完整 snapshot 指纹。任一不一致返回
|
||||
success=0/code=dynamic_module_update_conflict,绝不能写入或自动合并;
|
||||
4. 把变更合并进完整当前记录,执行原生必填/类型/业务校验和模块保存钩子;
|
||||
只更新实际变更字段,不允许修改主键、编号、状态、创建人或系统字段;
|
||||
5. 更新、持久幂等完成、事务证据和业务审计必须处于调用方同一
|
||||
Serializable 事务;过程不得 COMMIT/ROLLBACK 外层事务;
|
||||
6. 成功时恰好返回标准十列:success, code, message, record_id,
|
||||
needs_ui, idempotency_replayed, applied_idempotency_key,
|
||||
applied_input_fingerprint, transaction_evidence_id, business_audit_id。
|
||||
*/
|
||||
IF OBJECT_ID(N'dbo.p_lserp_agent_module_update_v2', N'P') IS NULL
|
||||
BEGIN
|
||||
EXEC(N'
|
||||
CREATE PROCEDURE dbo.p_lserp_agent_module_update_v2
|
||||
@module_code NVARCHAR(64),
|
||||
@module_kind VARCHAR(16),
|
||||
@contract_fingerprint CHAR(64),
|
||||
@configuration_fingerprint CHAR(64),
|
||||
@native_save_family VARCHAR(64),
|
||||
@native_execution_profile_fingerprint CHAR(64),
|
||||
@record_locator NVARCHAR(512),
|
||||
@record_version_token CHAR(64),
|
||||
@record_snapshot_fingerprint CHAR(64),
|
||||
@changes_xml NVARCHAR(MAX),
|
||||
@account_book NVARCHAR(128),
|
||||
@subsystem_id NVARCHAR(128),
|
||||
@user_id NVARCHAR(128),
|
||||
@user_name NVARCHAR(128),
|
||||
@correlation_id VARCHAR(128),
|
||||
@database_scope_fingerprint CHAR(64),
|
||||
@idempotency_key VARCHAR(128),
|
||||
@input_fingerprint CHAR(64)
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
RAISERROR(N''客户动态模块并发更新适配过程尚未实施。'', 16, 1);
|
||||
RETURN;
|
||||
END;');
|
||||
END;
|
||||
|
||||
/*
|
||||
生产授权由客户 DBA 显式完成:ERP 日常账号只能 EXECUTE readiness、snapshot、
|
||||
update;不得直接写证据表,也不得 EXECUTE acceptance。snapshot 账号不得
|
||||
取得任何业务表写权限,update 账号只通过固定过程获得最小权限。
|
||||
*/
|
||||
@@ -0,0 +1,244 @@
|
||||
SET XACT_ABORT ON;
|
||||
|
||||
/*
|
||||
V3 readiness refuses to publish an already accepted adapter after either deployed
|
||||
read/write procedure changes. It also compares the exact ordered SQL parameter
|
||||
signature before returning the V2 evidence row. The procedure reads only system
|
||||
catalogs and the Agent evidence table; it never reads or mutates business rows.
|
||||
*/
|
||||
IF OBJECT_ID(N'dbo.p_lserp_agent_workflow_readiness_v3', N'P') IS NULL
|
||||
BEGIN
|
||||
EXEC(N'
|
||||
CREATE PROCEDURE dbo.p_lserp_agent_workflow_readiness_v3
|
||||
@workflow VARCHAR(32),
|
||||
@module_code NVARCHAR(64),
|
||||
@account_book NVARCHAR(128),
|
||||
@subsystem_id NVARCHAR(128)
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
END;');
|
||||
END;
|
||||
GO
|
||||
|
||||
ALTER PROCEDURE dbo.p_lserp_agent_workflow_readiness_v3
|
||||
@workflow VARCHAR(32),
|
||||
@module_code NVARCHAR(64),
|
||||
@account_book NVARCHAR(128),
|
||||
@subsystem_id NVARCHAR(128)
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
IF @workflow NOT IN ('purchase', 'leave')
|
||||
OR NULLIF(LTRIM(RTRIM(@module_code)), N'') IS NULL
|
||||
OR NULLIF(LTRIM(RTRIM(@account_book)), N'') IS NULL
|
||||
OR NULLIF(LTRIM(RTRIM(@subsystem_id)), N'') IS NULL
|
||||
BEGIN
|
||||
RETURN;
|
||||
END;
|
||||
|
||||
DECLARE @compatibility_level INT;
|
||||
SELECT @compatibility_level = compatibility_level
|
||||
FROM sys.databases
|
||||
WHERE database_id = DB_ID();
|
||||
IF @compatibility_level IS NULL
|
||||
BEGIN
|
||||
RETURN;
|
||||
END;
|
||||
|
||||
DECLARE @read_procedure SYSNAME;
|
||||
DECLARE @write_procedure SYSNAME;
|
||||
DECLARE @expected_read_signature NVARCHAR(MAX);
|
||||
DECLARE @expected_write_signature NVARCHAR(MAX);
|
||||
|
||||
IF @compatibility_level < 130
|
||||
BEGIN
|
||||
SET @read_procedure = N'dbo.p_lserp_agent_workflow_read_compat100';
|
||||
SET @expected_read_signature =
|
||||
N'1:@workflow:varchar(32):0|2:@action:varchar(64):0|'
|
||||
+ N'3:@module_code:nvarchar(64):0|4:@account_book:nvarchar(64):0|'
|
||||
+ N'5:@subsystem_id:nvarchar(32):0|6:@user_id:nvarchar(64):0|'
|
||||
+ N'7:@reference:nvarchar(500):0|8:@tax_id:nvarchar(50):0|'
|
||||
+ N'9:@line_id:nvarchar(64):0|10:@specification:nvarchar(200):0|'
|
||||
+ N'11:@unit:nvarchar(100):0|12:@supplier_code:nvarchar(64):0|'
|
||||
+ N'13:@currency_code:nvarchar(64):0|14:@invoice_number:nvarchar(128):0|'
|
||||
+ N'15:@query:nvarchar(500):0|16:@employee_id:nvarchar(64):0|'
|
||||
+ N'17:@local_date:nvarchar(10):0|18:@day_part:varchar(16):0|'
|
||||
+ N'19:@leave_type_code:nvarchar(64):0|'
|
||||
+ N'20:@flow_type_query:nvarchar(500):0|'
|
||||
+ N'21:@calculated_hours:decimal(18,6):0|'
|
||||
+ N'22:@flow_type_code:nvarchar(64):0|23:@start_local:datetime:0|'
|
||||
+ N'24:@end_local:datetime:0|25:@record_id:nvarchar(128):0';
|
||||
|
||||
IF @workflow = 'purchase'
|
||||
BEGIN
|
||||
SET @write_procedure =
|
||||
N'dbo.p_lserp_agent_workflow_write_purchase_compat100';
|
||||
SET @expected_write_signature =
|
||||
N'1:@action:varchar(64):0|2:@module_code:nvarchar(64):0|'
|
||||
+ N'3:@account_book:nvarchar(64):0|4:@subsystem_id:nvarchar(32):0|'
|
||||
+ N'5:@user_id:nvarchar(64):0|6:@correlation_id:varchar(128):0|'
|
||||
+ N'7:@idempotency_key:varchar(128):0|'
|
||||
+ N'8:@input_fingerprint:char(64):0|'
|
||||
+ N'9:@supplier_code:nvarchar(64):0|'
|
||||
+ N'10:@currency_code:nvarchar(64):0|'
|
||||
+ N'11:@invoice_number:nvarchar(128):0|12:@invoice_date:datetime:0|'
|
||||
+ N'13:@total_without_tax:decimal(28,8):0|'
|
||||
+ N'14:@tax_amount:decimal(28,8):0|'
|
||||
+ N'15:@total_with_tax:decimal(28,8):0|16:@lines_xml:xml:0|'
|
||||
+ N'17:@source_documents_xml:xml:0';
|
||||
END
|
||||
ELSE
|
||||
BEGIN
|
||||
SET @write_procedure =
|
||||
N'dbo.p_lserp_agent_workflow_write_leave_compat100';
|
||||
SET @expected_write_signature =
|
||||
N'1:@action:varchar(64):0|2:@module_code:nvarchar(64):0|'
|
||||
+ N'3:@account_book:nvarchar(64):0|4:@subsystem_id:nvarchar(32):0|'
|
||||
+ N'5:@user_id:nvarchar(64):0|6:@correlation_id:varchar(128):0|'
|
||||
+ N'7:@idempotency_key:varchar(128):0|'
|
||||
+ N'8:@input_fingerprint:char(64):0|'
|
||||
+ N'9:@employee_id:nvarchar(64):0|'
|
||||
+ N'10:@leave_type_code:nvarchar(64):0|'
|
||||
+ N'11:@flow_type_code:nvarchar(64):0|12:@start_local:datetime:0|'
|
||||
+ N'13:@end_local:datetime:0|'
|
||||
+ N'14:@requested_hours:decimal(18,6):0|'
|
||||
+ N'15:@reason:nvarchar(500):0|'
|
||||
+ N'16:@submit_after_save_intent:bit:0|'
|
||||
+ N'17:@record_id:nvarchar(128):0';
|
||||
END;
|
||||
END
|
||||
ELSE
|
||||
BEGIN
|
||||
SET @read_procedure = N'dbo.p_lserp_agent_workflow_read';
|
||||
SET @write_procedure = N'dbo.p_lserp_agent_workflow_write';
|
||||
SET @expected_read_signature =
|
||||
N'1:@workflow:varchar(32):0|2:@action:varchar(64):0|'
|
||||
+ N'3:@module_code:nvarchar(64):0|4:@account_book:nvarchar(64):0|'
|
||||
+ N'5:@subsystem_id:nvarchar(32):0|6:@user_id:nvarchar(64):0|'
|
||||
+ N'7:@payload_json:nvarchar(max):0';
|
||||
SET @expected_write_signature =
|
||||
N'1:@workflow:varchar(32):0|2:@action:varchar(64):0|'
|
||||
+ N'3:@module_code:nvarchar(64):0|4:@account_book:nvarchar(64):0|'
|
||||
+ N'5:@subsystem_id:nvarchar(32):0|6:@user_id:nvarchar(64):0|'
|
||||
+ N'7:@correlation_id:varchar(128):0|'
|
||||
+ N'8:@payload_json:nvarchar(max):0|'
|
||||
+ N'9:@idempotency_key:varchar(128):0|'
|
||||
+ N'10:@input_fingerprint:char(64):0';
|
||||
END;
|
||||
|
||||
DECLARE @read_object_id INT;
|
||||
DECLARE @write_object_id INT;
|
||||
SET @read_object_id = OBJECT_ID(@read_procedure, N'P');
|
||||
SET @write_object_id = OBJECT_ID(@write_procedure, N'P');
|
||||
IF @read_object_id IS NULL OR @write_object_id IS NULL
|
||||
BEGIN
|
||||
RETURN;
|
||||
END;
|
||||
|
||||
DECLARE @actual_read_signature NVARCHAR(MAX);
|
||||
DECLARE @actual_write_signature NVARCHAR(MAX);
|
||||
SELECT @actual_read_signature = STUFF
|
||||
((
|
||||
SELECT
|
||||
N'|' + CONVERT(NVARCHAR(10), contract_parameter.parameter_id)
|
||||
+ N':' + contract_parameter.name + N':'
|
||||
+ LOWER(TYPE_NAME(contract_parameter.system_type_id))
|
||||
+ CASE
|
||||
WHEN TYPE_NAME(contract_parameter.system_type_id)
|
||||
IN (N'varchar', N'char', N'nvarchar', N'nchar')
|
||||
THEN N'(' + CASE WHEN contract_parameter.max_length = -1
|
||||
THEN N'max' ELSE CONVERT(NVARCHAR(10),
|
||||
CASE WHEN TYPE_NAME(contract_parameter.system_type_id)
|
||||
IN (N'nvarchar', N'nchar')
|
||||
THEN contract_parameter.max_length / 2
|
||||
ELSE contract_parameter.max_length END) END + N')'
|
||||
WHEN TYPE_NAME(contract_parameter.system_type_id)
|
||||
IN (N'decimal', N'numeric')
|
||||
THEN N'(' + CONVERT(NVARCHAR(10), contract_parameter.precision)
|
||||
+ N',' + CONVERT(NVARCHAR(10), contract_parameter.scale) + N')'
|
||||
ELSE N''
|
||||
END
|
||||
+ N':' + CONVERT(NVARCHAR(1), contract_parameter.is_output)
|
||||
FROM sys.parameters AS contract_parameter
|
||||
WHERE contract_parameter.object_id = @read_object_id
|
||||
AND contract_parameter.parameter_id > 0
|
||||
ORDER BY contract_parameter.parameter_id
|
||||
FOR XML PATH(N''), TYPE
|
||||
).value(N'.', N'nvarchar(max)'), 1, 1, N'');
|
||||
|
||||
SELECT @actual_write_signature = STUFF
|
||||
((
|
||||
SELECT
|
||||
N'|' + CONVERT(NVARCHAR(10), contract_parameter.parameter_id)
|
||||
+ N':' + contract_parameter.name + N':'
|
||||
+ LOWER(TYPE_NAME(contract_parameter.system_type_id))
|
||||
+ CASE
|
||||
WHEN TYPE_NAME(contract_parameter.system_type_id)
|
||||
IN (N'varchar', N'char', N'nvarchar', N'nchar')
|
||||
THEN N'(' + CASE WHEN contract_parameter.max_length = -1
|
||||
THEN N'max' ELSE CONVERT(NVARCHAR(10),
|
||||
CASE WHEN TYPE_NAME(contract_parameter.system_type_id)
|
||||
IN (N'nvarchar', N'nchar')
|
||||
THEN contract_parameter.max_length / 2
|
||||
ELSE contract_parameter.max_length END) END + N')'
|
||||
WHEN TYPE_NAME(contract_parameter.system_type_id)
|
||||
IN (N'decimal', N'numeric')
|
||||
THEN N'(' + CONVERT(NVARCHAR(10), contract_parameter.precision)
|
||||
+ N',' + CONVERT(NVARCHAR(10), contract_parameter.scale) + N')'
|
||||
ELSE N''
|
||||
END
|
||||
+ N':' + CONVERT(NVARCHAR(1), contract_parameter.is_output)
|
||||
FROM sys.parameters AS contract_parameter
|
||||
WHERE contract_parameter.object_id = @write_object_id
|
||||
AND contract_parameter.parameter_id > 0
|
||||
ORDER BY contract_parameter.parameter_id
|
||||
FOR XML PATH(N''), TYPE
|
||||
).value(N'.', N'nvarchar(max)'), 1, 1, N'');
|
||||
|
||||
IF ISNULL(@actual_read_signature, N'') COLLATE Latin1_General_100_BIN2
|
||||
<> @expected_read_signature COLLATE Latin1_General_100_BIN2
|
||||
OR ISNULL(@actual_write_signature, N'') COLLATE Latin1_General_100_BIN2
|
||||
<> @expected_write_signature COLLATE Latin1_General_100_BIN2
|
||||
BEGIN
|
||||
RETURN;
|
||||
END;
|
||||
|
||||
/* sys.objects.modify_date is server-local; convert using the current server
|
||||
UTC offset. A DST-boundary ambiguity can only fail closed and require a
|
||||
fresh acceptance row. */
|
||||
DECLARE @latest_contract_modified_local DATETIME;
|
||||
DECLARE @latest_contract_modified_utc DATETIME2(3);
|
||||
SELECT @latest_contract_modified_local = MAX(modify_date)
|
||||
FROM sys.procedures
|
||||
WHERE object_id IN (@read_object_id, @write_object_id);
|
||||
SET @latest_contract_modified_utc = CONVERT(DATETIME2(3), DATEADD(
|
||||
MINUTE,
|
||||
DATEDIFF(MINUTE, GETDATE(), GETUTCDATE()),
|
||||
@latest_contract_modified_local));
|
||||
|
||||
SELECT
|
||||
adapter_id,
|
||||
adapter_version,
|
||||
evidence_id,
|
||||
evidence_sha256,
|
||||
account_book,
|
||||
subsystem_id,
|
||||
customer_configuration_validated,
|
||||
parameterized_read_queries_verified,
|
||||
transactional_write_verified,
|
||||
persistent_idempotency_verified,
|
||||
permission_recheck_verified,
|
||||
windows_integration_verified,
|
||||
validated_by,
|
||||
validated_at_utc
|
||||
FROM dbo.p_agent_workflow_adapter_evidence_v2
|
||||
WHERE workflow = @workflow
|
||||
AND module_code = @module_code
|
||||
AND account_book = @account_book
|
||||
AND subsystem_id = @subsystem_id
|
||||
AND validated_at_utc >= @latest_contract_modified_utc
|
||||
AND validated_at_utc <= DATEADD(MINUTE, 5, SYSUTCDATETIME());
|
||||
END;
|
||||
GO
|
||||
Reference in New Issue
Block a user