Add organization_user model and enhance relationships in existing models

- Introduced the organization_user model to manage relationships between organizations and users.
- Updated organization and user models to include new relationships for managing memberships and ownerships.
- Refactored system and test model mappings to incorporate the new organization_user model, ensuring consistency across the application.
This commit is contained in:
Max 2025-09-04 17:32:39 +08:00
parent d9c593728e
commit 5a23afb22c
6 changed files with 436 additions and 141 deletions

File diff suppressed because one or more lines are too long

View file

@ -34,6 +34,7 @@ var systemModels = map[string]string{
"__yao.kb.collection": "yao/models/kb/collection.mod.yao",
"__yao.kb.document": "yao/models/kb/document.mod.yao",
"__yao.organization": "yao/models/organization.mod.yao",
"__yao.organization_user": "yao/models/organization_user.mod.yao",
"__yao.user": "yao/models/user.mod.yao",
"__yao.user_role": "yao/models/user_role.mod.yao",
"__yao.user_type": "yao/models/user_type.mod.yao",

View file

@ -50,6 +50,7 @@ var testSystemModels = map[string]string{
"__yao.kb.collection": "yao/models/kb/collection.mod.yao",
"__yao.kb.document": "yao/models/kb/document.mod.yao",
"__yao.organization": "yao/models/organization.mod.yao",
"__yao.organization_user": "yao/models/organization_user.mod.yao",
"__yao.user": "yao/models/user.mod.yao",
"__yao.user_role": "yao/models/user_role.mod.yao",
"__yao.user_type": "yao/models/user_type.mod.yao",

View file

@ -453,6 +453,21 @@
"model": "__yao.user_type",
"key": "type_id",
"foreign": "type_id"
},
"members": {
"type": "hasMany",
"model": "__yao.organization_user",
"key": "org_id",
"foreign": "org_id"
},
"users": {
"type": "hasManyThrough",
"model": "__yao.user",
"through": "__yao.organization_user",
"key": "org_id",
"foreign": "org_id",
"throughKey": "user_id",
"throughForeign": "user_id"
}
},
"values": [],

View file

@ -0,0 +1,234 @@
{
"name": "Organization User",
"label": "Organization User",
"description": "Association table for organization members and their roles",
"tags": ["organization", "user", "association", "membership", "roles"],
"table": {
"name": "organization_user",
"comment": "Association table for organization members and their roles"
},
"columns": [
// ============================================================================
// Basic Fields
// ============================================================================
{
"name": "id",
"type": "ID",
"label": "ID",
"comment": "Primary key identifier",
"primary": true
},
{
"name": "org_id",
"type": "string",
"label": "Organization ID",
"comment": "Organization identifier (references organization.org_id)",
"length": 255,
"nullable": false,
"index": true
},
{
"name": "user_id",
"type": "string",
"label": "User ID",
"comment": "User identifier (references user.user_id)",
"length": 255,
"nullable": false,
"index": true
},
// ============================================================================
// Role & Permission Fields
// ============================================================================
{
"name": "role",
"type": "enum",
"label": "Role",
"comment": "User role within the organization",
"option": [
"owner", // Organization owner (full control, billing, delete org)
"admin", // Administrator (manage users, settings, but not billing)
"member" // Regular member (basic access according to org permissions)
],
"default": "member",
"index": true,
"nullable": false
},
{
"name": "status",
"type": "enum",
"label": "Status",
"comment": "Membership status",
"option": [
"pending", // Invitation sent, not accepted yet
"active", // Active member
"inactive", // Temporarily inactive
"suspended" // Suspended by admin
],
"default": "pending",
"index": true,
"nullable": false
},
// ============================================================================
// Invitation & Join Information
// ============================================================================
{
"name": "invited_by",
"type": "string",
"label": "Invited By",
"comment": "User ID who sent the invitation",
"length": 255,
"nullable": true
},
{
"name": "invited_at",
"type": "timestamp",
"label": "Invited At",
"comment": "When the invitation was sent",
"nullable": true,
"index": true
},
{
"name": "joined_at",
"type": "timestamp",
"label": "Joined At",
"comment": "When the user accepted and joined the organization",
"nullable": true,
"index": true
},
{
"name": "invitation_token",
"type": "string",
"label": "Invitation Token",
"comment": "Unique token for invitation acceptance",
"length": 100,
"nullable": true,
"unique": true,
"index": true
},
{
"name": "invitation_expires_at",
"type": "timestamp",
"label": "Invitation Expires At",
"comment": "When the invitation expires",
"nullable": true
},
// ============================================================================
// Permission Overrides
// ============================================================================
{
"name": "permissions",
"type": "json",
"label": "Permissions",
"comment": "Custom permissions override for this user in this organization",
"nullable": true
},
{
"name": "restrictions",
"type": "json",
"label": "Restrictions",
"comment": "Additional restrictions for this user in this organization",
"nullable": true
},
// ============================================================================
// Activity Tracking
// ============================================================================
{
"name": "last_active_at",
"type": "timestamp",
"label": "Last Active At",
"comment": "When the user was last active in this organization",
"nullable": true,
"index": true
},
{
"name": "login_count",
"type": "integer",
"label": "Login Count",
"comment": "Number of times user has accessed this organization",
"default": 0,
"nullable": true
},
// ============================================================================
// Metadata
// ============================================================================
{
"name": "notes",
"type": "text",
"label": "Notes",
"comment": "Internal notes about this membership",
"nullable": true
},
{
"name": "metadata",
"type": "json",
"label": "Metadata",
"comment": "Additional metadata for this membership",
"nullable": true
}
],
"indexes": [
{
"name": "idx_org_user_unique",
"columns": ["org_id", "user_id"],
"type": "unique",
"comment": "Unique constraint: one user can have only one membership per organization"
},
{
"name": "idx_org_user_role",
"columns": ["org_id", "role", "status"],
"type": "index",
"comment": "Index for finding users by role and status within organization"
},
{
"name": "idx_user_orgs",
"columns": ["user_id", "status", "role"],
"type": "index",
"comment": "Index for finding all organizations for a user"
},
{
"name": "idx_org_invitations",
"columns": ["org_id", "status", "invited_at"],
"type": "index",
"comment": "Index for managing invitations"
},
{
"name": "idx_invitation_token",
"columns": ["invitation_token", "invitation_expires_at"],
"type": "index",
"comment": "Index for invitation token lookup and expiration"
},
{
"name": "idx_org_activity",
"columns": ["org_id", "last_active_at"],
"type": "index",
"comment": "Index for organization activity tracking"
}
],
"relations": {
"organization": {
"type": "hasOne",
"model": "__yao.organization",
"key": "org_id",
"foreign": "org_id"
},
"user": {
"type": "hasOne",
"model": "__yao.user",
"key": "user_id",
"foreign": "user_id"
},
"inviter": {
"type": "hasOne",
"model": "__yao.user",
"key": "invited_by",
"foreign": "user_id"
}
},
"values": [],
"option": { "timestamps": true, "soft_deletes": true, "permission": true }
}

View file

@ -396,6 +396,27 @@
"model": "__yao.user_type",
"key": "type_id",
"foreign": "type_id"
},
"owned_organizations": {
"type": "hasMany",
"model": "__yao.organization",
"key": "user_id",
"foreign": "owner_id"
},
"organization_memberships": {
"type": "hasMany",
"model": "__yao.organization_user",
"key": "user_id",
"foreign": "user_id"
},
"organizations": {
"type": "hasManyThrough",
"model": "__yao.organization",
"through": "__yao.organization_user",
"key": "user_id",
"foreign": "user_id",
"throughKey": "org_id",
"throughForeign": "org_id"
}
},
"values": [],