Add customer contract clearance console
This commit is contained in:
+244
-2
@@ -142,6 +142,13 @@ db.exec("CREATE INDEX IF NOT EXISTS idx_commercial_approvals_requester ON commer
|
||||
db.exec("CREATE INDEX IF NOT EXISTS idx_commercial_approvals_target ON commercial_approval_requests(organization_id, request_type, target_key, status)");
|
||||
db.exec("CREATE INDEX IF NOT EXISTS idx_organization_policies_org_category ON organization_policies(organization_id, category, policy_key)");
|
||||
db.exec("CREATE INDEX IF NOT EXISTS idx_organization_policy_evaluations_scope ON organization_policy_evaluations(organization_id, workspace_id, project_id, result, created_at DESC)");
|
||||
db.exec("CREATE INDEX IF NOT EXISTS idx_customers_org_status ON customers(organization_id, status, updated_at DESC)");
|
||||
db.exec("CREATE INDEX IF NOT EXISTS idx_customer_contacts_customer ON customer_contacts(customer_id, is_primary DESC, status)");
|
||||
db.exec("CREATE INDEX IF NOT EXISTS idx_production_contracts_org_status ON production_contracts(organization_id, status, approval_status, updated_at DESC)");
|
||||
db.exec("CREATE INDEX IF NOT EXISTS idx_production_contracts_customer ON production_contracts(customer_id, status, updated_at DESC)");
|
||||
db.exec("CREATE INDEX IF NOT EXISTS idx_contract_project_bindings_project ON contract_project_bindings(organization_id, workspace_id, project_id, status)");
|
||||
db.exec("CREATE INDEX IF NOT EXISTS idx_contract_project_bindings_contract ON contract_project_bindings(contract_id, status, updated_at DESC)");
|
||||
db.exec("CREATE INDEX IF NOT EXISTS idx_contract_license_events_scope ON contract_license_events(organization_id, contract_id, project_id, created_at DESC)");
|
||||
db.exec("CREATE INDEX IF NOT EXISTS idx_asset_versions_governance ON asset_versions(asset_id, rights_status, expires_at)");
|
||||
db.exec("CREATE INDEX IF NOT EXISTS idx_asset_governance_reviews_asset ON asset_governance_reviews(asset_id, created_at DESC)");
|
||||
db.exec("CREATE INDEX IF NOT EXISTS idx_asset_governance_reviews_scope ON asset_governance_reviews(organization_id, workspace_id, project_id, risk_status, created_at DESC)");
|
||||
@@ -253,6 +260,10 @@ function seedRoles() {
|
||||
["task:complete", "更新本人负责的协作任务状态"],
|
||||
["style:read", "查看组织、工作区和项目风格生产标准"],
|
||||
["style:manage", "管理风格标准、品牌规范和项目绑定"],
|
||||
["customer:read", "查看客户主档、联系人和项目归属"],
|
||||
["customer:manage", "管理客户主档、联系人和客户状态"],
|
||||
["contract:read", "查看合同授权、项目绑定和商业放行"],
|
||||
["contract:manage", "管理合同授权、项目绑定和商业放行"],
|
||||
["script:edit", "编辑剧本和对白"],
|
||||
["script:read", "查看剧本、分集和镜头"],
|
||||
["asset:edit", "编辑角色、场景和道具锁"],
|
||||
@@ -289,10 +300,10 @@ function seedRoles() {
|
||||
org_admin: [
|
||||
"organization:manage", "organization:members:invite", "workspace:create", "workspace:manage",
|
||||
"workspace:members:manage", "project:create", "project:manage", "project:members:manage",
|
||||
"workflow:manage", "task:view", "task:manage", "task:complete", "policy:read", "policy:manage", "style:read", "style:manage", "model:manage", "model:approve", "usage:view", "billing:manage", "quota:manage", "qa:review", "delivery:approve", "delivery:view", "compliance:manage", "audit:view", "queue:manage", "voice:approve",
|
||||
"workflow:manage", "task:view", "task:manage", "task:complete", "policy:read", "policy:manage", "style:read", "style:manage", "customer:read", "customer:manage", "contract:read", "contract:manage", "model:manage", "model:approve", "usage:view", "billing:manage", "quota:manage", "qa:review", "delivery:approve", "delivery:view", "compliance:manage", "audit:view", "queue:manage", "voice:approve",
|
||||
"system:settings:view", "service:health:view", "organization:roles:manage"
|
||||
],
|
||||
producer: ["project:create", "project:manage", "project:members:manage", "workflow:manage", "task:view", "task:manage", "task:complete", "policy:read", "style:read", "style:manage", "script:read", "job:create", "job:prioritize", "usage:view", "delivery:approve", "delivery:view", "voice:approve"],
|
||||
producer: ["project:create", "project:manage", "project:members:manage", "workflow:manage", "task:view", "task:manage", "task:complete", "policy:read", "style:read", "style:manage", "customer:read", "contract:read", "script:read", "job:create", "job:prioritize", "usage:view", "delivery:approve", "delivery:view", "voice:approve"],
|
||||
writer: ["script:read", "script:edit", "task:view", "task:complete", "policy:read", "style:read", "job:create"],
|
||||
art_director: ["asset:edit", "prompt:edit", "task:view", "task:complete", "policy:read", "style:read", "style:manage", "job:create"],
|
||||
voice_editor: ["voice:edit", "voice:approve", "task:view", "task:complete", "policy:read", "style:read", "job:create"],
|
||||
@@ -395,6 +406,8 @@ function commercialEntitlementDefaults(billing = {}) {
|
||||
["limit.projects", "项目数量", "production", 36, "个", 1, "block", { commercialGate: "project:create" }],
|
||||
["limit.generation_jobs_monthly", "月度生成任务", "production", clipLimit, "job", 1, "block", { commercialGate: "generation_job:create" }],
|
||||
["limit.style_kits", "风格生产标准", "production", 24, "套", 1, "block", { commercialGate: "style_kit:create" }],
|
||||
["limit.customers", "客户主档", "commercial", 120, "个", 1, "block", { commercialGate: "customer:create" }],
|
||||
["limit.contracts", "合同授权", "commercial", 240, "份", 1, "block", { commercialGate: "contract:create" }],
|
||||
["limit.storage_gb", "存储容量", "storage", storageGb, "GB", 1, "block", { commercialGate: "storage:write" }],
|
||||
["limit.model_connectors", "模型连接器", "modelops", 16, "个", 1, "block", { commercialGate: "model_connector:create" }],
|
||||
["limit.api_clients", "API 客户端", "system", 8, "个", 1, "block", { commercialGate: "api_client:create" }],
|
||||
@@ -551,6 +564,234 @@ function seedCommercialApprovals() {
|
||||
);
|
||||
}
|
||||
|
||||
function seedCustomersAndContracts() {
|
||||
const timestamp = now();
|
||||
const customers = [
|
||||
{
|
||||
id: "cust-xinghe-release",
|
||||
organizationId: "org-studio-lab",
|
||||
name: "星河发行合作方",
|
||||
legalName: "星河内容发行(模拟)有限公司",
|
||||
code: "XH-RELEASE",
|
||||
customerType: "distributor",
|
||||
status: "active",
|
||||
industry: "短剧发行",
|
||||
region: "CN",
|
||||
billingEmail: "ops@example.local",
|
||||
taxId: "",
|
||||
notes: "用于验证《雷雨口》商用交付、客户门户和授权范围清算。",
|
||||
tags: ["发行", "本地交付", "商用清算"],
|
||||
metadata: { source: "seed://customers/xinghe-release", preferredDelivery: "private-delivery-portal" },
|
||||
createdBy: "u-owner"
|
||||
},
|
||||
{
|
||||
id: "cust-northstar-platform",
|
||||
organizationId: "org-northstar",
|
||||
name: "北辰内部发行台",
|
||||
legalName: "北辰内容厂牌(模拟)",
|
||||
code: "NS-INTERNAL",
|
||||
customerType: "internal",
|
||||
status: "active",
|
||||
industry: "内容厂牌",
|
||||
region: "CN",
|
||||
billingEmail: "northstar@example.local",
|
||||
taxId: "",
|
||||
notes: "用于验证北辰组织的独立合同和跨组织隔离。",
|
||||
tags: ["内部试播", "租户隔离"],
|
||||
metadata: { source: "seed://customers/northstar-internal" },
|
||||
createdBy: "u-producer"
|
||||
}
|
||||
];
|
||||
for (const customer of customers) {
|
||||
insertIgnore(
|
||||
`INSERT OR IGNORE INTO customers(
|
||||
id, organization_id, name, legal_name, code, customer_type, status, industry, region,
|
||||
billing_email, tax_id, notes, tags_json, metadata_json, created_by, created_at, updated_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
[
|
||||
customer.id,
|
||||
customer.organizationId,
|
||||
customer.name,
|
||||
customer.legalName,
|
||||
customer.code,
|
||||
customer.customerType,
|
||||
customer.status,
|
||||
customer.industry,
|
||||
customer.region,
|
||||
customer.billingEmail,
|
||||
customer.taxId,
|
||||
customer.notes,
|
||||
JSON.stringify(customer.tags),
|
||||
JSON.stringify(customer.metadata),
|
||||
customer.createdBy,
|
||||
timestamp,
|
||||
timestamp
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
const contacts = [
|
||||
["contact-xinghe-release-producer", "org-studio-lab", "cust-xinghe-release", "星河发行制片经理", "交付验收", "producer@example.local", "", 1],
|
||||
["contact-xinghe-release-legal", "org-studio-lab", "cust-xinghe-release", "星河法务窗口", "合同授权复核", "legal@example.local", "", 0],
|
||||
["contact-northstar-owner", "org-northstar", "cust-northstar-platform", "北辰试制负责人", "内部验收", "pilot@example.local", "", 1]
|
||||
];
|
||||
for (const contact of contacts) {
|
||||
insertIgnore(
|
||||
"INSERT OR IGNORE INTO customer_contacts(id, organization_id, customer_id, name, role, email, phone, is_primary, status, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'active', ?, ?)",
|
||||
[...contact, timestamp, timestamp]
|
||||
);
|
||||
}
|
||||
|
||||
const contracts = [
|
||||
{
|
||||
id: "contract-xh-rain-night-2026",
|
||||
organizationId: "org-studio-lab",
|
||||
customerId: "cust-xinghe-release",
|
||||
contractNumber: "XH-AIDRAMA-2026-001",
|
||||
title: "《雷雨口》AI 漫剧本地化制作与交付授权",
|
||||
contractType: "production_license",
|
||||
status: "active",
|
||||
approvalStatus: "approved",
|
||||
effectiveAt: "2026-08-01T00:00:00.000Z",
|
||||
expiresAt: "2028-12-31T23:59:59.000Z",
|
||||
signedAt: "2026-08-15T09:30:00.000Z",
|
||||
amount: 0,
|
||||
licenseScope: {
|
||||
channels: ["local-file", "local-webhook", "private-delivery-portal"],
|
||||
territories: ["CN", "GLOBAL"],
|
||||
deliverables: ["vertical-video", "prompt-pack", "subtitle", "delivery-manifest"],
|
||||
platforms: ["private-preview", "internal-review", "owned-channel"],
|
||||
exclusive: false,
|
||||
commercialUse: true,
|
||||
language: ["zh-CN"]
|
||||
},
|
||||
rights: {
|
||||
originalStory: true,
|
||||
derivativeProduction: true,
|
||||
aiGeneratedAssetsAllowed: true,
|
||||
voiceCloneAllowed: false,
|
||||
fixedVoiceRequired: true,
|
||||
singleFramePolicyRequired: true
|
||||
},
|
||||
deliveryTerms: {
|
||||
maxAccessDays: 90,
|
||||
customerPortalAllowed: true,
|
||||
watermarkRequiredBeforeApproval: true,
|
||||
requireClearanceCertificate: true
|
||||
},
|
||||
evidence: {
|
||||
contractRef: "seed://contract/xh-2026-aidrama",
|
||||
rightsEvidenceRef: "seed://original/rain-night",
|
||||
signedCopyPath: "storage/contracts/xh-2026-aidrama/contract-summary.json"
|
||||
},
|
||||
risk: { paidCloudAllowed: false, publicModelAllowed: false, rightsReview: "passed" },
|
||||
approvalNote: "种子合同用于本地商用闭环验证:固定声线、一图一画面、原创权利证据均为强制项。",
|
||||
createdBy: "u-owner"
|
||||
},
|
||||
{
|
||||
id: "contract-ns-pilot-2026",
|
||||
organizationId: "org-northstar",
|
||||
customerId: "cust-northstar-platform",
|
||||
contractNumber: "NS-PILOT-2026-001",
|
||||
title: "《山海志异·试播集》内部试制授权",
|
||||
contractType: "internal",
|
||||
status: "active",
|
||||
approvalStatus: "approved",
|
||||
effectiveAt: "2026-08-10T00:00:00.000Z",
|
||||
expiresAt: "2027-12-31T23:59:59.000Z",
|
||||
signedAt: "2026-08-10T10:00:00.000Z",
|
||||
amount: 0,
|
||||
licenseScope: {
|
||||
channels: ["local-file", "private-delivery-portal"],
|
||||
territories: ["CN"],
|
||||
deliverables: ["vertical-video", "delivery-manifest"],
|
||||
platforms: ["internal-review"],
|
||||
exclusive: false,
|
||||
commercialUse: true,
|
||||
language: ["zh-CN"]
|
||||
},
|
||||
rights: {
|
||||
originalStory: true,
|
||||
derivativeProduction: true,
|
||||
aiGeneratedAssetsAllowed: true,
|
||||
voiceCloneAllowed: false,
|
||||
fixedVoiceRequired: true,
|
||||
singleFramePolicyRequired: true
|
||||
},
|
||||
deliveryTerms: {
|
||||
maxAccessDays: 30,
|
||||
customerPortalAllowed: true,
|
||||
watermarkRequiredBeforeApproval: true,
|
||||
requireClearanceCertificate: true
|
||||
},
|
||||
evidence: {
|
||||
contractRef: "seed://contract/ns-pilot-2026",
|
||||
rightsEvidenceRef: "seed://original/northstar-pilot",
|
||||
signedCopyPath: "storage/contracts/ns-pilot-2026/contract-summary.json"
|
||||
},
|
||||
risk: { paidCloudAllowed: false, publicModelAllowed: false, rightsReview: "passed" },
|
||||
approvalNote: "北辰组织独立授权,不允许被星河组织项目绑定。",
|
||||
createdBy: "u-producer"
|
||||
}
|
||||
];
|
||||
for (const contract of contracts) {
|
||||
insertIgnore(
|
||||
`INSERT OR IGNORE INTO production_contracts(
|
||||
id, organization_id, customer_id, contract_number, title, contract_type, status,
|
||||
approval_status, effective_at, expires_at, signed_at, currency, amount,
|
||||
license_scope_json, rights_json, delivery_terms_json, evidence_json, risk_json,
|
||||
approval_note, created_by, updated_by, created_at, updated_at
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'CNY', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
[
|
||||
contract.id,
|
||||
contract.organizationId,
|
||||
contract.customerId,
|
||||
contract.contractNumber,
|
||||
contract.title,
|
||||
contract.contractType,
|
||||
contract.status,
|
||||
contract.approvalStatus,
|
||||
contract.effectiveAt,
|
||||
contract.expiresAt,
|
||||
contract.signedAt,
|
||||
contract.amount,
|
||||
JSON.stringify(contract.licenseScope),
|
||||
JSON.stringify(contract.rights),
|
||||
JSON.stringify(contract.deliveryTerms),
|
||||
JSON.stringify(contract.evidence),
|
||||
JSON.stringify(contract.risk),
|
||||
contract.approvalNote,
|
||||
contract.createdBy,
|
||||
contract.createdBy,
|
||||
timestamp,
|
||||
timestamp
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
const bindings = [
|
||||
["contract-binding-thunder-mouth-xh", "org-studio-lab", "ws-local-aidrama", "thunder-mouth", "contract-xh-rain-night-2026", "cust-xinghe-release", "primary-license", "active", "《雷雨口》默认商业交付授权。", "u-owner"],
|
||||
["contract-binding-northstar-pilot", "org-northstar", "ws-northstar-main", "northstar-pilot", "contract-ns-pilot-2026", "cust-northstar-platform", "internal-review", "active", "北辰内部试播项目授权。", "u-producer"]
|
||||
];
|
||||
for (const binding of bindings) {
|
||||
insertIgnore(
|
||||
"INSERT OR IGNORE INTO contract_project_bindings(id, organization_id, workspace_id, project_id, contract_id, customer_id, usage_mode, status, notes, created_by, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
[...binding, timestamp, timestamp]
|
||||
);
|
||||
}
|
||||
|
||||
const events = [
|
||||
["contract-event-xh-seed-created", "org-studio-lab", "contract-xh-rain-night-2026", "cust-xinghe-release", "ws-local-aidrama", "thunder-mouth", "seeded", "recorded", "u-owner", { action: "seed", bindingId: "contract-binding-thunder-mouth-xh", channelKinds: ["local-file", "private-delivery-portal"] }],
|
||||
["contract-event-ns-seed-created", "org-northstar", "contract-ns-pilot-2026", "cust-northstar-platform", "ws-northstar-main", "northstar-pilot", "seeded", "recorded", "u-producer", { action: "seed", bindingId: "contract-binding-northstar-pilot" }]
|
||||
];
|
||||
for (const event of events) {
|
||||
insertIgnore(
|
||||
"INSERT OR IGNORE INTO contract_license_events(id, organization_id, contract_id, customer_id, workspace_id, project_id, event_type, status, actor_user_id, metadata_json, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
[...event.slice(0, 9), JSON.stringify(event[9]), timestamp]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function seedMembersAndProjects() {
|
||||
const timestamp = now();
|
||||
const membershipRows = [
|
||||
@@ -1271,6 +1512,7 @@ withTransaction(() => {
|
||||
seedOrganizationEntitlements();
|
||||
rollQuotaPeriods();
|
||||
seedCommercialApprovals();
|
||||
seedCustomersAndContracts();
|
||||
seedMembersAndProjects();
|
||||
seedModels();
|
||||
seedModelCatalog();
|
||||
|
||||
Reference in New Issue
Block a user