diff --git a/.workbuddy/memory/2026-07-21.md b/.workbuddy/memory/2026-07-21.md new file mode 100644 index 0000000..f8c5e71 --- /dev/null +++ b/.workbuddy/memory/2026-07-21.md @@ -0,0 +1,28 @@ +# 2026-07-21 工作记录 + +## 邀请成为开发者报错分析(UID:35739) +- 报错「该用户不是平台开发者,无法邀请为开发者角色」来源:`websopy-java` 项目(非 com.gxwebsoft.core)。 +- 拦截点共 3 处,逻辑一致:邀请角色为 `developer` 时,跨库查 `gxwebsoft_core.sys_user.is_developer`,必须为 `1` 才放行: + - `com/gxwebsoft/app/service/impl/AppUserServiceImpl.java:168-172`(API: POST /api/app/developer/.../invite) + - `com/gxwebsoft/app/service/impl/AppInviteServiceImpl.java:257-261` + - `com/gxwebsoft/app/controller/AppMpInviteController.java:179-183`(小程序邀请) +- 「平台开发者」定义: + - `sys_user.type = 2`(见 AppUserController /check-access 判断) + - `sys_user.is_developer = 1`(邀请 developer 角色的硬性门槛,由 SysUserCrossDbMapper.selectIsDeveloperByUserId 查询) +- 关键发现:websopy-java 与 com.gxwebsoft.core 后端代码中**没有任何写入 is_developer=1 / type=2 的逻辑**,即「成为开发者」的赋权由 websopy-pc 前端开发者门户注册/认证流程或数据库操作完成(后端仅做读取校验)。 +- 结论:UID:35739 的 `sys_user.is_developer` 为 0 或 NULL → 报错。需先让其完成「成为开发者」注册使 is_developer=1(并建议同步 type=2)。 +- 潜在 Bug:邀请门槛用 is_developer,而 /check-access 用 type=2。若两字段不一致,会出现「能进开发者中心但无法被邀请为开发者」的矛盾。 + +## 统一开发者身份判定为 is_developer=1(已实施) +- 用户选定方案:把开发者身份判定统一为「sys_user.is_developer = 1」。 +- 改动(websopy-java 项目): + - `AppUserController.checkAccess()`:原 `selectUserType()==2` → 改为 `selectIsDeveloperByUserId()==1`,同步更新 Javadoc。 + - `AppProductServiceImpl` 转让应用所有权校验:原 `selectUserType()==2` → 改为 `selectIsDeveloperByUserId()==1`。 + - 三处邀请门槛(AppUserServiceImpl / AppInviteServiceImpl / AppMpInviteController)本就使用 is_developer=1,无需改。 +- 结果:全站「平台开发者」判定统一为 is_developer=1 单一标准。 +- 注意:此变更使 is_developer 成为唯一依据;存量仅 type=2、无 is_developer 的开发者将失去权限。需数据回填:`UPDATE gxwebsoft_core.sys_user SET is_developer=1 WHERE type=2 AND deleted=0;`(先备份)。UID:35739 仍需 is_developer=1 才能被邀请。 + +## Tenant 实体补充 superAdminUserId 字段 +- 用户已在 `TenantMapper.xml` 的 `selectSql` 中 SELECT `u.user_id as superAdminUserId`(LEFT JOIN gxwebsoft_core.sys_user u ON u.tenant_id=a.tenant_id AND u.is_super_admin=1 AND u.deleted=0)。 +- 在 `Tenant.java` 中补充 `private Integer superAdminUserId;` 并标注 `@TableField(exist = false)`,使 MyBatis 能映射该衍生字段(不是 sys_tenant 真实列,不参与增改)。 +- `selectPageRel` / `selectListRel` 用 resultType=Tenant,可直接返回该字段。 diff --git a/src/main/java/com/gxwebsoft/common/system/entity/Tenant.java b/src/main/java/com/gxwebsoft/common/system/entity/Tenant.java index 3ba8201..c5cfc5e 100644 --- a/src/main/java/com/gxwebsoft/common/system/entity/Tenant.java +++ b/src/main/java/com/gxwebsoft/common/system/entity/Tenant.java @@ -104,6 +104,10 @@ public class Tenant implements Serializable { @TableField(exist = false) private String phone; + @Schema(description = "超级管理员用户ID") + @TableField(exist = false) + private Integer superAdminUserId; + @Schema(description = "管理地址") @TableField(exist = false) private String adminUrl; diff --git a/src/main/java/com/gxwebsoft/common/system/mapper/xml/TenantMapper.xml b/src/main/java/com/gxwebsoft/common/system/mapper/xml/TenantMapper.xml index 8765748..34e432a 100644 --- a/src/main/java/com/gxwebsoft/common/system/mapper/xml/TenantMapper.xml +++ b/src/main/java/com/gxwebsoft/common/system/mapper/xml/TenantMapper.xml @@ -5,7 +5,7 @@ SELECT a.*,b.company_name,b.company_logo as logo,b.admin_url,b.domain,b.free_domain, - u.phone,u.username + u.phone,u.username,u.user_id as superAdminUserId FROM sys_tenant a LEFT JOIN sys_company b ON a.tenant_id = b.tenant_id LEFT JOIN gxwebsoft_core.sys_user u ON u.tenant_id = a.tenant_id AND u.is_super_admin = 1 AND u.deleted = 0