diff --git a/mateclaw-server/src/main/java/vip/mate/workspace/core/config/WorkspaceSchemaMigration.java b/mateclaw-server/src/main/java/vip/mate/workspace/core/config/WorkspaceSchemaMigration.java index cb57ef16..74eebb49 100644 --- a/mateclaw-server/src/main/java/vip/mate/workspace/core/config/WorkspaceSchemaMigration.java +++ b/mateclaw-server/src/main/java/vip/mate/workspace/core/config/WorkspaceSchemaMigration.java @@ -9,11 +9,19 @@ import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Component; +import java.util.List; + /** - * 工作区 Schema 迁移 + * Workspace schema bootstrap. *
- * 确保默认工作区(id=1, slug='default')存在。 - * 在 DatabaseBootstrapRunner (@Order(1)) 之后执行。 + * Ensures the default workspace (id=1, slug='default') exists, and performs a + * first-run-only bootstrap of an admin owner. This is not a + * reconciliation loop: once the default workspace has any owner, subsequent + * startups make no membership changes — operators may legitimately remove an + * admin from the default workspace and that decision must persist across + * restarts (see issue #29). + *
+ * Runs after {@code DatabaseBootstrapRunner} ({@code @Order(1)}).
*
* @author MateClaw Team
*/
@@ -60,27 +68,41 @@ public class WorkspaceSchemaMigration implements ApplicationRunner {
}
/**
- * Ensure all admin users have an owner-role membership in the default workspace.
- * Non-admin users must be added explicitly via WorkspaceController.addMember.
+ * First-run-only bootstrap: if the default workspace has no owner yet, pick
+ * the lowest-id active admin and add them as owner. If an owner already
+ * exists (or was deliberately removed and re-installed by an operator), do
+ * nothing. If no admin exists at all, log a warning and skip — failing
+ * startup here would be worse than the recoverable "no owner" state.
*/
private void ensureDefaultWorkspaceMembership() {
try {
- int inserted = jdbcTemplate.update("""
- INSERT INTO mate_workspace_member (id, workspace_id, user_id, role, create_time, update_time, deleted)
- SELECT u.id, 1, u.id, 'owner', NOW(), NOW(), 0
- FROM mate_user u
- WHERE u.deleted = 0
- AND u.role = 'admin'
- AND NOT EXISTS (
- SELECT 1 FROM mate_workspace_member wm
- WHERE wm.workspace_id = 1 AND wm.user_id = u.id AND wm.deleted = 0
- )
- """);
- if (inserted > 0) {
- log.info("Added {} admin user(s) as owner of default workspace", inserted);
+ Integer ownerCount = jdbcTemplate.queryForObject(
+ "SELECT COUNT(1) FROM mate_workspace_member "
+ + "WHERE workspace_id = 1 AND role = 'owner' AND deleted = 0",
+ Integer.class);
+ if (ownerCount != null && ownerCount > 0) {
+ return;
}
+ List