feat: org disable 整改 + biz_meeting 补字段 + 表格 0/1 显示标准化

1. backend: 禁用主账号 dual-write + 子账号登录校验主账号状态

   - BizOrgServiceImpl.toggleStatus: 同步 biz_org.status 与 sys_user.status

   - BizOrgController.toggleStatus: 严格校验 '0'/'1'

   - BizAuthController.smsLogin 4.5: 子账号校验主账号状态拦截

2. fix: BizMeeting 补 9 个字段 getters/setters

   - projectId / projectName / orgName / supervisionOpinion / supervisionBy

   - supervisionTime / invitationUrl / scheduleUrl / laborSigned

   - 解决 Meetings.vue 修改/复制 projectId 为空的 bug

3. frontend: 0/1 status 标准化显示

   - 10 个 Orgs/People 表格 el-tag 由 raw 0/1 改为 正常/禁用

   - 4 个 People 页面修复 row.status || '正常' 在 '0' 时不

     回退的 bug (字符串 '0' 是 truthy)

   - 筛选下拉 / 编辑表单 / onToggleStatus label 全部对齐 0/1 语义

   - DB 迁移: 10 行脏数据 (7 正常/合作中 → 0, 3 禁用 → 1)

   - 备份表 biz_org_backup_20260819

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
郭庆泰
2026-08-19 00:27:15 +08:00
co-authored by Claude
parent 9f132d4f86
commit ec4a8aa8f0
17 changed files with 616 additions and 97 deletions
@@ -145,6 +145,19 @@ public class BizAuthController extends BaseController {
return error("账号已被停用");
}
// 4.5 子账号专属: 校验主账号状态
// sys_user.parent_user_id 不为 NULL = 子账号 (执行方/支持方员工), 需拦截主账号被禁用的情况
// 主账号自己 parent_user_id = NULL, 跳过本步
if (user.getParentUserId() != null) {
SysUser parent = userMapper.selectUserById(user.getParentUserId());
if (parent == null) {
return error("所属主账号不存在, 请联系管理员");
}
if (UserStatus.DISABLE.getCode().equals(parent.getStatus())) {
return error("所属企业主账号已被停用, 请联系企业管理员");
}
}
// 5. 构造 LoginUser 并设 SecurityContext (兼容后续 spring security 鉴权)
LoginUser loginUser = new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user));
Authentication authentication = new UsernamePasswordAuthenticationToken(
@@ -87,6 +87,25 @@ public class BizOrgController extends BaseController {
return toAjax(bizOrgService.updateByPrimaryKey(bizOrg));
}
/**
* 启用/禁用 org, 同步主账号 sys_user.status
* PUT /business/org/toggleStatus body { orgId, status: '0'/'1' }
* 注意: 用 Map 接 body 而不是 BizOrg, 防止前端误传其它字段 (如 remark/updateTime) 覆盖
* 入口只接 orgId + status 2 个字段, 业务可见面可控
* status 严格限定 '0' (正常) / '1' (禁用), 避免历史 '0'/'1'/'正常'/'合作中'/'禁用' 混合
*/
@Log(title = "公司管理", businessType = BusinessType.UPDATE)
@PutMapping("/toggleStatus")
public AjaxResult toggleStatus(@RequestBody Map<String, Object> body) {
if (body.get("orgId") == null) return error("orgId 不能为空");
Long orgId = Long.valueOf(body.get("orgId").toString());
String status = body.get("status") == null ? null : body.get("status").toString();
if (status == null || (!"0".equals(status) && !"1".equals(status))) {
return error("status 必须是 '0' (正常) 或 '1' (禁用)");
}
return toAjax(bizOrgService.toggleStatus(orgId, status));
}
@Log(title = "公司管理", businessType = BusinessType.DELETE)
@DeleteMapping("/{orgIds}")
public AjaxResult remove(@PathVariable Long[] orgIds) {
@@ -104,4 +104,22 @@ public class BizMeeting extends BaseEntity {
public void setUpdateBy(String updateBy) { this.updateBy = updateBy; }
public Date getUpdateTime() { return updateTime; }
public void setUpdateTime(Date updateTime) { this.updateTime = updateTime; }
public Long getProjectId() { return projectId; }
public void setProjectId(Long projectId) { this.projectId = projectId; }
public String getProjectName() { return projectName; }
public void setProjectName(String projectName) { this.projectName = projectName; }
public String getOrgName() { return orgName; }
public void setOrgName(String orgName) { this.orgName = orgName; }
public String getSupervisionOpinion() { return supervisionOpinion; }
public void setSupervisionOpinion(String supervisionOpinion) { this.supervisionOpinion = supervisionOpinion; }
public String getSupervisionBy() { return supervisionBy; }
public void setSupervisionBy(String supervisionBy) { this.supervisionBy = supervisionBy; }
public Date getSupervisionTime() { return supervisionTime; }
public void setSupervisionTime(Date supervisionTime) { this.supervisionTime = supervisionTime; }
public String getInvitationUrl() { return invitationUrl; }
public void setInvitationUrl(String invitationUrl) { this.invitationUrl = invitationUrl; }
public String getScheduleUrl() { return scheduleUrl; }
public void setScheduleUrl(String scheduleUrl) { this.scheduleUrl = scheduleUrl; }
public String getLaborSigned() { return laborSigned; }
public void setLaborSigned(String laborSigned) { this.laborSigned = laborSigned; }
}
@@ -17,4 +17,12 @@ public interface IBizOrgService {
List<Map<String, Object>> selectExecutorOrgOptions(BizOrg entity);
/** 当前登录 sponsor 的所属公司 (主账号可改, 子账号只读) */
Map<String, Object> selectMySponsorCompany(Long userId);
/**
* 启用/禁用 org, 同步主账号 sys_user.status
* biz_org.status: '禁用' → sys_user.status='1', '正常'/'合作中' → '0'
* 只联动主账号 (biz_org.user_id), 子账号不在此操作范围
* (子账号登录时由 BizAuthController 校验主账号状态拦截)
*/
int toggleStatus(Long orgId, String newBizOrgStatus);
}
@@ -4,16 +4,22 @@ import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.ruoyi.business.domain.BizOrg;
import com.ruoyi.business.mapper.BizOrgMapper;
import com.ruoyi.business.service.IBizOrgService;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.id.SnowflakeId;
import com.ruoyi.system.mapper.SysUserMapper;
@Service
public class BizOrgServiceImpl implements IBizOrgService {
@Autowired
private BizOrgMapper bizOrgMapper;
@Autowired
private SysUserMapper sysUserMapper;
@Override
public BizOrg getById(Long orgId) { return bizOrgMapper.selectByPrimaryKey(orgId); }
@@ -50,4 +56,30 @@ public class BizOrgServiceImpl implements IBizOrgService {
public Map<String, Object> selectMySponsorCompany(Long userId) {
return bizOrgMapper.selectMySponsorCompany(userId);
}
/**
* 启用/禁用 org, 同步主账号 sys_user.status
* biz_org.status: '0' (正常) / '1' (禁用) → sys_user.status 同码 ('0' 正常, '1' 停用)
* 只联动主账号 (biz_org.user_id), 子账号不在此操作范围
* (子账号登录时由 BizAuthController 校验主账号状态拦截)
*/
@Override
@Transactional
public int toggleStatus(Long orgId, String newBizOrgStatus) {
// 1. 查 org 取主账号 user_id
BizOrg org = bizOrgMapper.selectByPrimaryKey(orgId);
if (org == null) throw new ServiceException("组织不存在");
if (org.getUserId() == null) throw new ServiceException("该组织未关联主账号");
// 2. UPDATE biz_org.status
BizOrg update = new BizOrg();
update.setOrgId(orgId);
update.setStatus(newBizOrgStatus);
int rows = bizOrgMapper.updateByPrimaryKey(update);
// 3. 同步主账号 sys_user.status (0=正常, 1=停用 — 跟 biz_org.status 同码)
sysUserMapper.updateUserStatus(org.getUserId(), newBizOrgStatus);
return rows;
}
}