forked from wangziqi/ruoyi-vue-pro
feat(education): add practice blueprint lifecycle
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
-- EDU-010: bounded tenant Category and Practice Blueprint authoring lifecycles.
|
||||
-- JAVA_READ authority remains an application guard; database rules protect direct writes.
|
||||
|
||||
LOCK TABLE education_category, education_practice_blueprint IN SHARE ROW EXCLUSIVE MODE;
|
||||
|
||||
ALTER TABLE education_category
|
||||
ADD COLUMN publication_status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE',
|
||||
ADD COLUMN authoring_version INTEGER NOT NULL DEFAULT 0;
|
||||
UPDATE education_category SET publication_status = CASE WHEN is_active THEN 'ACTIVE' ELSE 'ARCHIVED' END;
|
||||
ALTER TABLE education_category
|
||||
ADD CONSTRAINT ck_education_category_publication_status CHECK (publication_status IN ('DRAFT','ACTIVE','ARCHIVED')),
|
||||
ADD CONSTRAINT ck_education_category_authoring_version CHECK (authoring_version >= 0),
|
||||
ADD CONSTRAINT ck_education_category_lifecycle_consistent CHECK (
|
||||
(publication_status='ACTIVE' AND is_active) OR (publication_status IN ('DRAFT','ARCHIVED') AND NOT is_active));
|
||||
|
||||
ALTER TABLE education_practice_blueprint
|
||||
ADD COLUMN publication_status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE',
|
||||
ADD COLUMN authoring_version INTEGER NOT NULL DEFAULT 0;
|
||||
UPDATE education_practice_blueprint SET publication_status = CASE WHEN is_active THEN 'ACTIVE' ELSE 'ARCHIVED' END;
|
||||
ALTER TABLE education_practice_blueprint
|
||||
ADD CONSTRAINT ck_education_practice_blueprint_publication_status CHECK (publication_status IN ('DRAFT','ACTIVE','ARCHIVED')),
|
||||
ADD CONSTRAINT ck_education_practice_blueprint_authoring_version CHECK (authoring_version >= 0),
|
||||
ADD CONSTRAINT ck_education_practice_blueprint_lifecycle_consistent CHECK (
|
||||
(publication_status='ACTIVE' AND is_active) OR (publication_status IN ('DRAFT','ARCHIVED') AND NOT is_active)),
|
||||
ADD CONSTRAINT ck_education_practice_blueprint_mode_target CHECK (
|
||||
(mode='NODE' AND node_id IS NOT NULL AND collection_id IS NULL) OR
|
||||
(mode='COLLECTION' AND node_id IS NOT NULL AND collection_id IS NOT NULL)),
|
||||
ADD CONSTRAINT ck_education_practice_blueprint_nonnegative_counts CHECK (
|
||||
eligible_count >= 0 AND total_count >= 0 AND eligible_count = total_count),
|
||||
ADD CONSTRAINT ck_education_practice_blueprint_question_limit CHECK (
|
||||
question_limit IS NULL OR question_limit BETWEEN min_questions AND max_questions),
|
||||
ADD CONSTRAINT ck_education_practice_blueprint_duration CHECK (duration_minutes IS NULL OR duration_minutes > 0);
|
||||
|
||||
CREATE UNIQUE INDEX uk_education_practice_blueprint_tenant_active_node
|
||||
ON education_practice_blueprint(tenant_id,node_id)
|
||||
WHERE deleted=false AND scope='TENANT_OWNED' AND mode='NODE' AND publication_status='ACTIVE';
|
||||
CREATE UNIQUE INDEX uk_education_practice_blueprint_tenant_active_collection
|
||||
ON education_practice_blueprint(tenant_id,collection_id)
|
||||
WHERE deleted=false AND scope='TENANT_OWNED' AND mode='COLLECTION' AND publication_status='ACTIVE';
|
||||
|
||||
CREATE TABLE education_category_lifecycle_audit (
|
||||
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
tenant_id BIGINT NOT NULL, category_id BIGINT NOT NULL, authoring_version INTEGER NOT NULL,
|
||||
actor_id BIGINT NOT NULL, from_status VARCHAR(16) NOT NULL, to_status VARCHAR(16) NOT NULL,
|
||||
occurred_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
creator VARCHAR(64) DEFAULT '', create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updater VARCHAR(64) DEFAULT '', update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted BOOLEAN NOT NULL DEFAULT false,
|
||||
FOREIGN KEY(category_id) REFERENCES education_category(id),
|
||||
CHECK ((from_status='DRAFT' AND to_status='ACTIVE') OR (from_status='ACTIVE' AND to_status='ARCHIVED')),
|
||||
UNIQUE(tenant_id,category_id,authoring_version));
|
||||
COMMENT ON TABLE education_category_lifecycle_audit IS '教育-分类生命周期审计';
|
||||
|
||||
CREATE TABLE education_practice_blueprint_lifecycle_audit (
|
||||
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
||||
tenant_id BIGINT NOT NULL, blueprint_id BIGINT NOT NULL, authoring_version INTEGER NOT NULL,
|
||||
actor_id BIGINT NOT NULL, from_status VARCHAR(16) NOT NULL, to_status VARCHAR(16) NOT NULL,
|
||||
occurred_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
creator VARCHAR(64) DEFAULT '', create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updater VARCHAR(64) DEFAULT '', update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted BOOLEAN NOT NULL DEFAULT false,
|
||||
FOREIGN KEY(blueprint_id) REFERENCES education_practice_blueprint(id),
|
||||
CHECK ((from_status='DRAFT' AND to_status='ACTIVE') OR (from_status='ACTIVE' AND to_status='ARCHIVED')),
|
||||
UNIQUE(tenant_id,blueprint_id,authoring_version));
|
||||
COMMENT ON TABLE education_practice_blueprint_lifecycle_audit IS '教育-练习蓝图生命周期审计';
|
||||
|
||||
CREATE TABLE education_category_lifecycle_transition_token (
|
||||
transaction_id BIGINT NOT NULL, tenant_id BIGINT NOT NULL, category_id BIGINT NOT NULL,
|
||||
authoring_version INTEGER NOT NULL, from_status VARCHAR(16) NOT NULL, to_status VARCHAR(16) NOT NULL,
|
||||
PRIMARY KEY(transaction_id,tenant_id,category_id,authoring_version,from_status,to_status));
|
||||
CREATE TABLE education_practice_blueprint_lifecycle_transition_token (
|
||||
transaction_id BIGINT NOT NULL, tenant_id BIGINT NOT NULL, blueprint_id BIGINT NOT NULL,
|
||||
authoring_version INTEGER NOT NULL, from_status VARCHAR(16) NOT NULL, to_status VARCHAR(16) NOT NULL,
|
||||
PRIMARY KEY(transaction_id,tenant_id,blueprint_id,authoring_version,from_status,to_status));
|
||||
REVOKE ALL ON education_category_lifecycle_transition_token, education_practice_blueprint_lifecycle_transition_token FROM PUBLIC;
|
||||
|
||||
CREATE FUNCTION education_enforce_category_authoring() RETURNS TRIGGER LANGUAGE plpgsql SECURITY DEFINER SET search_path=pg_catalog,pg_temp AS $$
|
||||
DECLARE s_tenant BIGINT; s_scope VARCHAR(20); s_active BOOLEAN; s_deleted BOOLEAN;
|
||||
BEGIN
|
||||
IF TG_OP='DELETE' THEN IF OLD.scope='TENANT_OWNED' THEN RAISE EXCEPTION 'tenant category cannot be deleted' USING ERRCODE='23514'; END IF; RETURN OLD; END IF;
|
||||
IF TG_OP='INSERT' THEN
|
||||
IF NEW.scope='PUBLIC' THEN RAISE EXCEPTION 'PUBLIC category writes are not tenant authoring' USING ERRCODE='23514'; END IF;
|
||||
IF NEW.scope='TENANT_OWNED' AND (NEW.deleted OR NEW.publication_status<>'DRAFT' OR NEW.is_active OR NEW.authoring_version<>0) THEN RAISE EXCEPTION 'tenant category must start DRAFT' USING ERRCODE='23514'; END IF;
|
||||
ELSE
|
||||
IF OLD.scope='TENANT_OWNED' AND NEW.deleted IS DISTINCT FROM OLD.deleted THEN RAISE EXCEPTION 'tenant category cannot be logically deleted' USING ERRCODE='23514'; END IF;
|
||||
IF OLD.publication_status='ARCHIVED' AND NEW IS DISTINCT FROM OLD THEN RAISE EXCEPTION 'archived category is immutable' USING ERRCODE='23514'; END IF;
|
||||
IF NEW.authoring_version<>OLD.authoring_version+1 THEN RAISE EXCEPTION 'category version must advance once' USING ERRCODE='23514'; END IF;
|
||||
IF OLD.publication_status<>'DRAFT' AND (NEW.subject_id IS DISTINCT FROM OLD.subject_id OR NEW.name IS DISTINCT FROM OLD.name OR NEW.sort_order IS DISTINCT FROM OLD.sort_order) THEN RAISE EXCEPTION 'active category is immutable' USING ERRCODE='23514'; END IF;
|
||||
IF NEW.publication_status IS DISTINCT FROM OLD.publication_status THEN
|
||||
IF NOT ((OLD.publication_status='DRAFT' AND NEW.publication_status='ACTIVE') OR (OLD.publication_status='ACTIVE' AND NEW.publication_status='ARCHIVED')) THEN RAISE EXCEPTION 'invalid category lifecycle' USING ERRCODE='23514'; END IF;
|
||||
EXECUTE format('INSERT INTO %I.education_category_lifecycle_transition_token VALUES($1,$2,$3,$4,$5,$6)',TG_TABLE_SCHEMA) USING pg_current_xact_id()::text::BIGINT,NEW.tenant_id,NEW.id,NEW.authoring_version,OLD.publication_status,NEW.publication_status;
|
||||
END IF;
|
||||
END IF;
|
||||
IF NEW.scope='TENANT_OWNED' THEN
|
||||
IF NEW.subject_id IS NULL THEN RAISE EXCEPTION 'category subject required' USING ERRCODE='23514'; END IF;
|
||||
EXECUTE format('SELECT tenant_id,scope,is_active,deleted FROM %I.education_subject WHERE id=$1 FOR SHARE',TG_TABLE_SCHEMA) INTO s_tenant,s_scope,s_active,s_deleted USING NEW.subject_id;
|
||||
IF s_scope IS NULL OR s_deleted OR NOT s_active OR (s_scope='TENANT_OWNED' AND s_tenant IS DISTINCT FROM NEW.tenant_id) OR s_scope NOT IN('PUBLIC','TENANT_OWNED') THEN RAISE EXCEPTION 'category subject unavailable' USING ERRCODE='23514'; END IF;
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END $$;
|
||||
|
||||
CREATE FUNCTION education_enforce_practice_blueprint_authoring() RETURNS TRIGGER LANGUAGE plpgsql SECURITY DEFINER SET search_path=pg_catalog,pg_temp AS $$
|
||||
DECLARE n_tenant BIGINT; n_scope VARCHAR(20); n_entry BIGINT; n_active BOOLEAN; n_hidden BOOLEAN; n_deleted BOOLEAN; n_status VARCHAR(16);
|
||||
c_tenant BIGINT; c_scope VARCHAR(20); c_entry BIGINT; c_node BIGINT; c_active BOOLEAN; c_hidden BOOLEAN; c_deleted BOOLEAN; c_status VARCHAR(16); c_type VARCHAR(50); c_count INTEGER;
|
||||
BEGIN
|
||||
IF TG_OP='DELETE' THEN IF OLD.scope='TENANT_OWNED' THEN RAISE EXCEPTION 'tenant blueprint cannot be deleted' USING ERRCODE='23514'; END IF; RETURN OLD; END IF;
|
||||
IF TG_OP='INSERT' THEN
|
||||
IF NEW.scope='PUBLIC' THEN RAISE EXCEPTION 'PUBLIC blueprint writes are not tenant authoring' USING ERRCODE='23514'; END IF;
|
||||
IF NEW.scope='TENANT_OWNED' AND (NEW.deleted OR NEW.publication_status<>'DRAFT' OR NEW.is_active OR NEW.authoring_version<>0) THEN RAISE EXCEPTION 'tenant blueprint must start DRAFT' USING ERRCODE='23514'; END IF;
|
||||
ELSE
|
||||
IF OLD.scope='TENANT_OWNED' AND NEW.deleted IS DISTINCT FROM OLD.deleted THEN RAISE EXCEPTION 'tenant blueprint cannot be logically deleted' USING ERRCODE='23514'; END IF;
|
||||
IF OLD.publication_status='ARCHIVED' AND NEW IS DISTINCT FROM OLD THEN RAISE EXCEPTION 'archived blueprint is immutable' USING ERRCODE='23514'; END IF;
|
||||
IF NEW.authoring_version<>OLD.authoring_version+1 THEN RAISE EXCEPTION 'blueprint version must advance once' USING ERRCODE='23514'; END IF;
|
||||
IF OLD.publication_status<>'DRAFT' AND (NEW.mode IS DISTINCT FROM OLD.mode OR NEW.entry_id IS DISTINCT FROM OLD.entry_id OR NEW.node_id IS DISTINCT FROM OLD.node_id OR NEW.collection_id IS DISTINCT FROM OLD.collection_id OR NEW.question_limit IS DISTINCT FROM OLD.question_limit OR NEW.duration_minutes IS DISTINCT FROM OLD.duration_minutes OR NEW.available_types IS DISTINCT FROM OLD.available_types OR NEW.available_difficulties IS DISTINCT FROM OLD.available_difficulties OR NEW.min_questions IS DISTINCT FROM OLD.min_questions OR NEW.max_questions IS DISTINCT FROM OLD.max_questions OR NEW.suggested_count IS DISTINCT FROM OLD.suggested_count) THEN RAISE EXCEPTION 'active blueprint is immutable' USING ERRCODE='23514'; END IF;
|
||||
IF NEW.publication_status IS DISTINCT FROM OLD.publication_status THEN
|
||||
IF NOT ((OLD.publication_status='DRAFT' AND NEW.publication_status='ACTIVE') OR (OLD.publication_status='ACTIVE' AND NEW.publication_status='ARCHIVED')) THEN RAISE EXCEPTION 'invalid blueprint lifecycle' USING ERRCODE='23514'; END IF;
|
||||
EXECUTE format('INSERT INTO %I.education_practice_blueprint_lifecycle_transition_token VALUES($1,$2,$3,$4,$5,$6)',TG_TABLE_SCHEMA) USING pg_current_xact_id()::text::BIGINT,NEW.tenant_id,NEW.id,NEW.authoring_version,OLD.publication_status,NEW.publication_status;
|
||||
END IF;
|
||||
END IF;
|
||||
IF NEW.scope='TENANT_OWNED' THEN
|
||||
EXECUTE format('SELECT tenant_id,scope,entry_id,is_active,is_hidden,deleted,publication_status FROM %I.education_content_node WHERE id=$1 FOR SHARE',TG_TABLE_SCHEMA) INTO n_tenant,n_scope,n_entry,n_active,n_hidden,n_deleted,n_status USING NEW.node_id;
|
||||
IF n_tenant IS DISTINCT FROM NEW.tenant_id OR n_scope<>'TENANT_OWNED' OR n_entry IS DISTINCT FROM NEW.entry_id OR n_deleted OR NOT n_active OR n_hidden OR n_status<>'ACTIVE' THEN RAISE EXCEPTION 'blueprint node unavailable' USING ERRCODE='23514'; END IF;
|
||||
IF NEW.mode='COLLECTION' THEN
|
||||
EXECUTE format('SELECT tenant_id,scope,entry_id,node_id,is_active,is_hidden,deleted,publication_status,collection_type,question_count FROM %I.education_question_collection WHERE id=$1 FOR SHARE',TG_TABLE_SCHEMA) INTO c_tenant,c_scope,c_entry,c_node,c_active,c_hidden,c_deleted,c_status,c_type,c_count USING NEW.collection_id;
|
||||
IF c_tenant IS DISTINCT FROM NEW.tenant_id OR c_scope<>'TENANT_OWNED' OR c_entry IS DISTINCT FROM NEW.entry_id OR c_node IS DISTINCT FROM NEW.node_id OR c_deleted OR NOT c_active OR c_hidden OR c_status<>'ACTIVE' OR c_type<>'MANUAL' OR NEW.eligible_count IS DISTINCT FROM c_count OR NEW.total_count IS DISTINCT FROM c_count THEN RAISE EXCEPTION 'blueprint collection unavailable' USING ERRCODE='23514'; END IF;
|
||||
END IF;
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END $$;
|
||||
|
||||
CREATE FUNCTION education_validate_category_audit() RETURNS TRIGGER LANGUAGE plpgsql SECURITY DEFINER SET search_path=pg_catalog,pg_temp AS $$ DECLARE consumed INTEGER; BEGIN
|
||||
IF NEW.deleted THEN RAISE EXCEPTION 'category audit cannot be deleted' USING ERRCODE='23514'; END IF;
|
||||
EXECUTE format('DELETE FROM %I.education_category_lifecycle_transition_token WHERE transaction_id=$1 AND tenant_id=$2 AND category_id=$3 AND authoring_version=$4 AND from_status=$5 AND to_status=$6',TG_TABLE_SCHEMA) USING pg_current_xact_id()::text::BIGINT,NEW.tenant_id,NEW.category_id,NEW.authoring_version,NEW.from_status,NEW.to_status;
|
||||
GET DIAGNOSTICS consumed=ROW_COUNT; IF consumed<>1 THEN RAISE EXCEPTION 'category audit must accompany transition' USING ERRCODE='23514'; END IF; RETURN NEW; END $$;
|
||||
CREATE FUNCTION education_validate_practice_blueprint_audit() RETURNS TRIGGER LANGUAGE plpgsql SECURITY DEFINER SET search_path=pg_catalog,pg_temp AS $$ DECLARE consumed INTEGER; BEGIN
|
||||
IF NEW.deleted THEN RAISE EXCEPTION 'blueprint audit cannot be deleted' USING ERRCODE='23514'; END IF;
|
||||
EXECUTE format('DELETE FROM %I.education_practice_blueprint_lifecycle_transition_token WHERE transaction_id=$1 AND tenant_id=$2 AND blueprint_id=$3 AND authoring_version=$4 AND from_status=$5 AND to_status=$6',TG_TABLE_SCHEMA) USING pg_current_xact_id()::text::BIGINT,NEW.tenant_id,NEW.blueprint_id,NEW.authoring_version,NEW.from_status,NEW.to_status;
|
||||
GET DIAGNOSTICS consumed=ROW_COUNT; IF consumed<>1 THEN RAISE EXCEPTION 'blueprint audit must accompany transition' USING ERRCODE='23514'; END IF; RETURN NEW; END $$;
|
||||
CREATE FUNCTION education_prevent_publication_audit_mutation() RETURNS TRIGGER LANGUAGE plpgsql SET search_path=pg_catalog,pg_temp AS $$ BEGIN RAISE EXCEPTION 'lifecycle audit is append-only' USING ERRCODE='23514'; END $$;
|
||||
CREATE FUNCTION education_require_category_audit() RETURNS TRIGGER LANGUAGE plpgsql SET search_path=pg_catalog,pg_temp AS $$ DECLARE n INTEGER; BEGIN IF NEW.publication_status IS NOT DISTINCT FROM OLD.publication_status THEN RETURN NULL; END IF; EXECUTE format('SELECT count(*) FROM %I.education_category_lifecycle_audit WHERE tenant_id=$1 AND category_id=$2 AND authoring_version=$3 AND from_status=$4 AND to_status=$5 AND deleted=false',TG_TABLE_SCHEMA) INTO n USING NEW.tenant_id,NEW.id,NEW.authoring_version,OLD.publication_status,NEW.publication_status; IF n<>1 THEN RAISE EXCEPTION 'category lifecycle audit required' USING ERRCODE='23514'; END IF; RETURN NULL; END $$;
|
||||
CREATE FUNCTION education_require_practice_blueprint_audit() RETURNS TRIGGER LANGUAGE plpgsql SET search_path=pg_catalog,pg_temp AS $$ DECLARE n INTEGER; BEGIN IF NEW.publication_status IS NOT DISTINCT FROM OLD.publication_status THEN RETURN NULL; END IF; EXECUTE format('SELECT count(*) FROM %I.education_practice_blueprint_lifecycle_audit WHERE tenant_id=$1 AND blueprint_id=$2 AND authoring_version=$3 AND from_status=$4 AND to_status=$5 AND deleted=false',TG_TABLE_SCHEMA) INTO n USING NEW.tenant_id,NEW.id,NEW.authoring_version,OLD.publication_status,NEW.publication_status; IF n<>1 THEN RAISE EXCEPTION 'blueprint lifecycle audit required' USING ERRCODE='23514'; END IF; RETURN NULL; END $$;
|
||||
|
||||
REVOKE ALL ON FUNCTION education_enforce_category_authoring() FROM PUBLIC;
|
||||
REVOKE ALL ON FUNCTION education_enforce_practice_blueprint_authoring() FROM PUBLIC;
|
||||
REVOKE ALL ON FUNCTION education_validate_category_audit() FROM PUBLIC;
|
||||
REVOKE ALL ON FUNCTION education_validate_practice_blueprint_audit() FROM PUBLIC;
|
||||
REVOKE ALL ON FUNCTION education_prevent_publication_audit_mutation() FROM PUBLIC;
|
||||
REVOKE ALL ON FUNCTION education_require_category_audit() FROM PUBLIC;
|
||||
REVOKE ALL ON FUNCTION education_require_practice_blueprint_audit() FROM PUBLIC;
|
||||
CREATE TRIGGER trg_education_category_authoring BEFORE INSERT OR UPDATE OR DELETE ON education_category FOR EACH ROW EXECUTE FUNCTION education_enforce_category_authoring();
|
||||
CREATE TRIGGER trg_education_practice_blueprint_authoring BEFORE INSERT OR UPDATE OR DELETE ON education_practice_blueprint FOR EACH ROW EXECUTE FUNCTION education_enforce_practice_blueprint_authoring();
|
||||
CREATE TRIGGER trg_education_category_audit_validate BEFORE INSERT ON education_category_lifecycle_audit FOR EACH ROW EXECUTE FUNCTION education_validate_category_audit();
|
||||
CREATE TRIGGER trg_education_blueprint_audit_validate BEFORE INSERT ON education_practice_blueprint_lifecycle_audit FOR EACH ROW EXECUTE FUNCTION education_validate_practice_blueprint_audit();
|
||||
CREATE TRIGGER trg_education_category_audit_immutable BEFORE UPDATE OR DELETE ON education_category_lifecycle_audit FOR EACH ROW EXECUTE FUNCTION education_prevent_publication_audit_mutation();
|
||||
CREATE TRIGGER trg_education_blueprint_audit_immutable BEFORE UPDATE OR DELETE ON education_practice_blueprint_lifecycle_audit FOR EACH ROW EXECUTE FUNCTION education_prevent_publication_audit_mutation();
|
||||
CREATE CONSTRAINT TRIGGER trg_education_category_audit_required AFTER UPDATE OF publication_status ON education_category DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION education_require_category_audit();
|
||||
CREATE CONSTRAINT TRIGGER trg_education_blueprint_audit_required AFTER UPDATE OF publication_status ON education_practice_blueprint DEFERRABLE INITIALLY DEFERRED FOR EACH ROW EXECUTE FUNCTION education_require_practice_blueprint_audit();
|
||||
|
||||
DO $$ DECLARE installed INTEGER; BEGIN
|
||||
IF to_regclass('system_menu') IS NULL THEN RETURN; END IF;
|
||||
INSERT INTO system_menu(id,name,permission,type,sort,parent_id,path,icon,component,component_name,status,visible,keep_alive,always_show,creator,updater) VALUES
|
||||
(6809,'分类创作','education:category:author',3,9,6800,'','',NULL,NULL,0,false,true,true,'education-flyway','education-flyway'),
|
||||
(6810,'分类发布','education:category:publish',3,10,6800,'','',NULL,NULL,0,false,true,true,'education-flyway','education-flyway'),
|
||||
(6811,'分类归档','education:category:archive',3,11,6800,'','',NULL,NULL,0,false,true,true,'education-flyway','education-flyway'),
|
||||
(6812,'蓝图创作','education:practice-blueprint:author',3,12,6800,'','',NULL,NULL,0,false,true,true,'education-flyway','education-flyway'),
|
||||
(6813,'蓝图发布','education:practice-blueprint:publish',3,13,6800,'','',NULL,NULL,0,false,true,true,'education-flyway','education-flyway'),
|
||||
(6814,'蓝图归档','education:practice-blueprint:archive',3,14,6800,'','',NULL,NULL,0,false,true,true,'education-flyway','education-flyway') ON CONFLICT(id) DO NOTHING;
|
||||
SELECT count(*) INTO installed FROM system_menu WHERE deleted=0 AND status=0 AND ((id=6809 AND permission='education:category:author') OR (id=6810 AND permission='education:category:publish') OR (id=6811 AND permission='education:category:archive') OR (id=6812 AND permission='education:practice-blueprint:author') OR (id=6813 AND permission='education:practice-blueprint:publish') OR (id=6814 AND permission='education:practice-blueprint:archive'));
|
||||
IF installed<>6 THEN RAISE EXCEPTION 'Education category/blueprint RBAC seed IDs conflict' USING ERRCODE='23505'; END IF;
|
||||
END $$;
|
||||
Reference in New Issue
Block a user