feat: add ERP agent pet bridge and startup guide

This commit is contained in:
郎速科技
2026-08-14 14:28:28 +08:00
parent a803070819
commit 4c08f4c948
252 changed files with 134752 additions and 72 deletions
@@ -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