Merge remote-tracking branch 'origin/seedance_balance' into seedance_balance

# Conflicts:
#	web-api/ruoyi-system/src/main/java/com/ruoyi/ai/mapper/AiUserMapper.java
#	web-api/ruoyi-system/src/main/resources/mapper/system/AiUserMapper.xml
This commit is contained in:
yys 2026-04-21 11:00:27 +08:00
commit e516105893
5 changed files with 95 additions and 0 deletions

View File

@ -109,6 +109,16 @@ public class PortalVideoController extends BaseController {
} }
} }
/** 二级部门 sys_dept.model_parm 中 JSON 数组第一项的 value与 parseModelParmJson 规则一致)。 */
private String resolveFirstModelValueFromModelParm(Long aiUserId) {
String json = byteDeptApiKeyService.resolveSecondLevelModelParm(aiUserId);
List<PortalVideoProperties.ModelOption> list = parseModelParmJson(json);
if (list == null || list.isEmpty()) {
return null;
}
return list.get(0).getValue();
}
private static boolean isModelInOptions(String modelId, List<PortalVideoProperties.ModelOption> options) { private static boolean isModelInOptions(String modelId, List<PortalVideoProperties.ModelOption> options) {
if (modelId == null || modelId.isEmpty() || options == null) { if (modelId == null || modelId.isEmpty() || options == null) {
return false; return false;
@ -523,6 +533,10 @@ public class PortalVideoController extends BaseController {
Long secondDeptId = byteDeptApiKeyService.resolveSecondLevelDeptId(uid); Long secondDeptId = byteDeptApiKeyService.resolveSecondLevelDeptId(uid);
List<PortalVideoProperties.ModelOption> models = loadModelOptionsForAiUser(uid, secondDeptId); List<PortalVideoProperties.ModelOption> models = loadModelOptionsForAiUser(uid, secondDeptId);
PortalVideoProperties.Defaults defs = portalVideoProperties.resolveEffectiveDefaults(secondDeptId); PortalVideoProperties.Defaults defs = portalVideoProperties.resolveEffectiveDefaults(secondDeptId);
String modelFromDeptParm = resolveFirstModelValueFromModelParm(uid);
if (StringUtils.isNotEmpty(modelFromDeptParm)) {
defs.setModel(modelFromDeptParm);
}
alignDefaultsModelToOptions(defs, models); alignDefaultsModelToOptions(defs, models);
Map<String, Object> data = new LinkedHashMap<>(); Map<String, Object> data = new LinkedHashMap<>();
data.put("defaults", defs); data.put("defaults", defs);

View File

@ -29,6 +29,11 @@ public interface AiUserMapper extends BaseMapper<AiUser> {
/** 归属指定部门的门户用户主键(用于火山 API Key 缓存失效) */ /** 归属指定部门的门户用户主键(用于火山 API Key 缓存失效) */
List<Long> selectAiUserIdsByDeptId(@Param("deptId") Long deptId); List<Long> selectAiUserIdsByDeptId(@Param("deptId") Long deptId);
/**
* 部门下启用中的 AI 用户未删除且 status=0
*/
int countNormalAiUsersByDeptId(@Param("deptId") Long deptId, @Param("excludeUserId") Long excludeUserId);
@Update("update ai_user set dept_id = #{deptId}, update_time = sysdate() where id = #{userId}") @Update("update ai_user set dept_id = #{deptId}, update_time = sysdate() where id = #{userId}")
int updateAiUserDeptId(@Param("userId") Long userId, @Param("deptId") Long deptId); int updateAiUserDeptId(@Param("userId") Long userId, @Param("deptId") Long deptId);
} }

View File

@ -20,8 +20,10 @@ import com.ruoyi.common.core.domain.model.RegisterAiUserBody;
import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.*; import com.ruoyi.common.utils.*;
import com.ruoyi.common.utils.ip.IpUtils; import com.ruoyi.common.utils.ip.IpUtils;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.system.domain.SysConfig; import com.ruoyi.system.domain.SysConfig;
import com.ruoyi.system.mapper.SysConfigMapper; import com.ruoyi.system.mapper.SysConfigMapper;
import com.ruoyi.system.service.ISysDeptService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -65,6 +67,29 @@ public class AiUserServiceImpl implements IAiUserService {
@Autowired @Autowired
private AiRechargeMapper aiRechargeMapper; private AiRechargeMapper aiRechargeMapper;
@Autowired
private ISysDeptService deptService;
/**
* 校验部门下启用中 AI 账号未超过 max_user_count为空或0 表示不限制
*/
private void assertDeptAiUserWithinLimit(Long deptId, Long excludeUserId) {
if (deptId == null) {
return;
}
SysDept dept = deptService.selectDeptById(deptId);
if (dept == null) {
return;
}
Integer max = dept.getMaxUserCount();
if (max == null || max <= 0) {
return;
}
int others = aiUserMapper.countNormalAiUsersByDeptId(deptId, excludeUserId);
if (others + 1 > max) {
throw new ServiceException("团队人数已经超过上限", HttpStatus.BAD_REQUEST);
}
}
/** /**
* 查询ai-用户信息 * 查询ai-用户信息
@ -133,6 +158,9 @@ public class AiUserServiceImpl implements IAiUserService {
if (aiUser.getStatus() == null) { if (aiUser.getStatus() == null) {
aiUser.setStatus(0); aiUser.setStatus(0);
} }
if (aiUser.getStatus() == 0) {
assertDeptAiUserWithinLimit(aiUser.getDeptId(), null);
}
aiUser.setDelFlag("0"); aiUser.setDelFlag("0");
aiUser.setCreateBy(SecurityUtils.getUsername()); aiUser.setCreateBy(SecurityUtils.getUsername());
aiUser.setCreateTime(DateUtils.getNowDate()); aiUser.setCreateTime(DateUtils.getNowDate());
@ -147,6 +175,14 @@ public class AiUserServiceImpl implements IAiUserService {
*/ */
@Override @Override
public int updateAiUser(AiUser aiUser) { public int updateAiUser(AiUser aiUser) {
AiUser old = aiUser.getId() != null ? aiUserMapper.selectById(aiUser.getId()) : null;
if (old != null) {
Long newDeptId = aiUser.getDeptId() != null ? aiUser.getDeptId() : old.getDeptId();
Integer newStatus = aiUser.getStatus() != null ? aiUser.getStatus() : old.getStatus();
if (newStatus != null && newStatus == 0) {
assertDeptAiUserWithinLimit(newDeptId, aiUser.getId());
}
}
aiUser.setUpdateTime(DateUtils.getNowDate()); aiUser.setUpdateTime(DateUtils.getNowDate());
if (StringUtils.isNotEmpty(aiUser.getPassword())) { if (StringUtils.isNotEmpty(aiUser.getPassword())) {
// 登录后 update 常带上库里的 BCrypt 密文勿对密文再 encrypt否则第二次登录永远密码错误 // 登录后 update 常带上库里的 BCrypt 密文勿对密文再 encrypt否则第二次登录永远密码错误
@ -176,6 +212,16 @@ public class AiUserServiceImpl implements IAiUserService {
@Override @Override
public int updateAiUserDept(Long userId, Long deptId) { public int updateAiUserDept(Long userId, Long deptId) {
if (deptId != null) {
AiUser u = aiUserMapper.selectById(userId);
if (u != null) {
Integer st = u.getStatus();
boolean normal = (st == null || st == 0);
if (normal) {
assertDeptAiUserWithinLimit(deptId, userId);
}
}
}
return aiUserMapper.updateAiUserDeptId(userId, deptId); return aiUserMapper.updateAiUserDeptId(userId, deptId);
} }
@ -204,6 +250,11 @@ public class AiUserServiceImpl implements IAiUserService {
@Override @Override
public int updateUserStatus(AiUser aiUser) { public int updateUserStatus(AiUser aiUser) {
AiUser old = aiUser.getId() != null ? aiUserMapper.selectById(aiUser.getId()) : null;
Integer newStatus = aiUser.getStatus() != null ? aiUser.getStatus() : (old != null ? old.getStatus() : null);
if (old != null && newStatus != null && newStatus == 0 && old.getDeptId() != null) {
assertDeptAiUserWithinLimit(old.getDeptId(), aiUser.getId());
}
return aiUserMapper.updateById(aiUser); return aiUserMapper.updateById(aiUser);
} }

View File

@ -87,8 +87,17 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
select count(1) from ai_user where del_flag = '0' and dept_id = #{deptId} select count(1) from ai_user where del_flag = '0' and dept_id = #{deptId}
</select> </select>
<<<<<<< HEAD
<select id="selectAiUserIdsByDeptId" resultType="java.lang.Long"> <select id="selectAiUserIdsByDeptId" resultType="java.lang.Long">
select id from ai_user where del_flag = '0' and dept_id = #{deptId} select id from ai_user where del_flag = '0' and dept_id = #{deptId}
=======
<select id="countNormalAiUsersByDeptId" resultType="int">
select count(1) from ai_user
where del_flag = '0' and status = 0 and dept_id = #{deptId}
<if test="excludeUserId != null">
and id != #{excludeUserId}
</if>
>>>>>>> origin/seedance_balance
</select> </select>
<select id="selectPasswordById" resultType="java.lang.String"> <select id="selectPasswordById" resultType="java.lang.String">
select password from ai_user where id = #{id} select password from ai_user where id = #{id}

View File

@ -219,5 +219,21 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
#{userId} #{userId}
</foreach> </foreach>
</delete> </delete>
<!-- 统计部门下启用中用户(未删除且 status=0与 Java 注释一致 -->
<select id="countNormalUsersByDeptId" resultType="int">
select count(*) from sys_user u
where u.del_flag = '0' and u.status = '0'
and u.dept_id = #{deptId}
<if test="excludeUserId != null">
and u.user_id != #{excludeUserId}
</if>
</select>
<!-- 部门下后台用户数量(含停用,不含已删除) -->
<select id="countSysUsersByDeptId" resultType="int">
select count(*) from sys_user u
where u.del_flag = '0' and u.dept_id = #{deptId}
</select>
</mapper> </mapper>