80 lines
2.9 KiB
PL/PgSQL
80 lines
2.9 KiB
PL/PgSQL
BEGIN;
|
|
|
|
ALTER TABLE auth_sms_codes
|
|
DROP CONSTRAINT IF EXISTS auth_sms_codes_client_type_check;
|
|
|
|
ALTER TABLE auth_sms_codes
|
|
ADD CONSTRAINT auth_sms_codes_client_type_check
|
|
CHECK (client_type IN ('app', 'wechat', 'ops'));
|
|
|
|
CREATE TABLE ops_roles (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
role_code TEXT NOT NULL UNIQUE,
|
|
display_name TEXT NOT NULL,
|
|
role_rank INT NOT NULL,
|
|
permissions_jsonb JSONB NOT NULL DEFAULT '{}'::jsonb,
|
|
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'disabled', 'archived')),
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TRIGGER ops_roles_set_updated_at
|
|
BEFORE UPDATE ON ops_roles
|
|
FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
|
|
|
CREATE TABLE ops_users (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
ops_user_public_id TEXT NOT NULL UNIQUE,
|
|
country_code TEXT NOT NULL,
|
|
mobile TEXT NOT NULL,
|
|
display_name TEXT NOT NULL,
|
|
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'disabled', 'deleted')),
|
|
last_login_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
UNIQUE (country_code, mobile)
|
|
);
|
|
|
|
CREATE INDEX ops_users_mobile_idx ON ops_users(country_code, mobile);
|
|
|
|
CREATE TRIGGER ops_users_set_updated_at
|
|
BEFORE UPDATE ON ops_users
|
|
FOR EACH ROW EXECUTE FUNCTION set_updated_at();
|
|
|
|
CREATE TABLE ops_user_roles (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
ops_user_id UUID NOT NULL REFERENCES ops_users(id) ON DELETE CASCADE,
|
|
ops_role_id UUID NOT NULL REFERENCES ops_roles(id) ON DELETE CASCADE,
|
|
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'disabled')),
|
|
assigned_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
assigned_by_ops_user_id UUID REFERENCES ops_users(id) ON DELETE SET NULL,
|
|
UNIQUE (ops_user_id, ops_role_id)
|
|
);
|
|
|
|
CREATE INDEX ops_user_roles_user_idx ON ops_user_roles(ops_user_id);
|
|
CREATE INDEX ops_user_roles_role_idx ON ops_user_roles(ops_role_id);
|
|
|
|
CREATE TABLE ops_refresh_tokens (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
ops_user_id UUID NOT NULL REFERENCES ops_users(id) ON DELETE CASCADE,
|
|
device_key TEXT,
|
|
token_hash TEXT NOT NULL UNIQUE,
|
|
issued_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
expires_at TIMESTAMPTZ NOT NULL,
|
|
revoked_at TIMESTAMPTZ,
|
|
replaced_by_token_id UUID REFERENCES ops_refresh_tokens(id) ON DELETE SET NULL
|
|
);
|
|
|
|
CREATE INDEX ops_refresh_tokens_user_idx ON ops_refresh_tokens(ops_user_id);
|
|
CREATE INDEX ops_refresh_tokens_expires_idx ON ops_refresh_tokens(expires_at);
|
|
|
|
INSERT INTO ops_roles (role_code, display_name, role_rank, permissions_jsonb)
|
|
VALUES
|
|
('owner', '所有者', 100, '{"scope":"all"}'::jsonb),
|
|
('admin', '管理员', 80, '{"scope":"ops_admin"}'::jsonb),
|
|
('operator', '运维专员', 50, '{"scope":"ops_operator"}'::jsonb),
|
|
('viewer', '只读观察员', 10, '{"scope":"ops_viewer"}'::jsonb)
|
|
ON CONFLICT (role_code) DO NOTHING;
|
|
|
|
COMMIT;
|