fix: 页面优化,工具栏显示

This commit is contained in:
old burden 2026-03-30 13:05:04 +08:00
parent fc1ecf7bc9
commit 8436c3515c
8 changed files with 63 additions and 16 deletions

2
.gitignore vendored
View File

@ -4,7 +4,7 @@
.gradle
/build/
!gradle/wrapper/gradle-wrapper.jar
*.sql
target/
!.mvn/wrapper/maven-wrapper.jar

View File

@ -51,6 +51,9 @@ public class AiManagerApiController extends BaseController {
@Anonymous
public AjaxResult selectInfo(String aiType) {
AiManager aiManager = aiManagerService.selectAiManagerByType(aiType);
if (aiManager == null) {
return AjaxResult.error("该功能未配置或已停用");
}
aiManager.setPrompt(null);
return AjaxResult.success(aiManager);
}

View File

@ -60,6 +60,17 @@ public class PortalVideoController extends BaseController {
return byteDeptApiKeyService.resolveVolcApiKey(SecurityUtils.getAiUserId());
}
/** 与 ai_manager.type、portal.video.function-type 对齐,用于扣费 */
private String resolveFunctionType(PortalVideoGenRequest req) {
if (StringUtils.isNotEmpty(req.getFunctionType())) {
return req.getFunctionType();
}
if (StringUtils.isNotEmpty(portalVideoProperties.getFunctionType())) {
return portalVideoProperties.getFunctionType();
}
return "21";
}
private void applyOptionalParams(ByteBodyReq body, PortalVideoGenRequest req) {
PortalVideoProperties.Defaults d = portalVideoProperties.getDefaults();
body.setDuration(req.getDuration() != null ? req.getDuration() : d.getDuration());
@ -97,7 +108,7 @@ public class PortalVideoController extends BaseController {
/**
* 写入订单提示词生成模式图床 URL以及模型/时长/分辨率/比例与完整 JSON 参数便于对账与审计
*/
private void fillVideoOrderRecord(AiOrder aiOrder, PortalVideoGenRequest req, String mode, ByteBodyReq body) {
private void fillVideoOrderRecord(AiOrder aiOrder, PortalVideoGenRequest req, String mode, ByteBodyReq body, String functionTypeResolved) {
aiOrder.setText(req.getText());
aiOrder.setMode(mode);
applyOrderImages(aiOrder, req);
@ -119,7 +130,7 @@ public class PortalVideoController extends BaseController {
Map<String, Object> snap = new LinkedHashMap<>();
snap.put("generationMode", mode);
snap.put("prompt", req.getText());
snap.put("functionType", StringUtils.isNotEmpty(req.getFunctionType()) ? req.getFunctionType() : "21");
snap.put("functionType", functionTypeResolved);
snap.put("model", body.getModel());
snap.put("duration", body.getDuration());
snap.put("resolution", body.getResolution());
@ -143,13 +154,13 @@ public class PortalVideoController extends BaseController {
}
private AjaxResult submitOrderAndCreate(PortalVideoGenRequest req, String mode, ByteBodyReq byteBodyReq) {
AiOrder aiOrder = aiOrderService.getAiOrder(
StringUtils.isNotEmpty(req.getFunctionType()) ? req.getFunctionType() : "21");
String functionType = resolveFunctionType(req);
AiOrder aiOrder = aiOrderService.getAiOrder(functionType);
if (aiOrder == null) {
return AjaxResult.error(-1, "You have a low balance, please recharge");
}
try {
fillVideoOrderRecord(aiOrder, req, mode, byteBodyReq);
fillVideoOrderRecord(aiOrder, req, mode, byteBodyReq, functionType);
aiOrderService.updateAiOrder(aiOrder);
String key = apiKey();

View File

@ -23,6 +23,19 @@ public class PortalVideoProperties {
private List<String> resolutions = new ArrayList<>();
/**
* ai_manager.type 一致用于扣费与订单库中需存在对应记录且 status=0del_flag=0
*/
private String functionType = "21";
public String getFunctionType() {
return functionType;
}
public void setFunctionType(String functionType) {
this.functionType = functionType != null ? functionType : "21";
}
public Defaults getDefaults() {
return defaults;
}

View File

@ -238,6 +238,8 @@ volcengine:
# 门户视频生成页:模型 / 比例 / 时长 / 分辨率均由此处维护,前后端不写死业务枚举
portal:
video:
# 与库表 ai_manager.type 一致(用于扣费);若报错 functionType does not exist请插入对应 type 或改此处与库一致
function-type: "21"
defaults:
model: ep-20260326165811-dlkth
duration: 4

View File

@ -124,6 +124,8 @@ public class AiManagerServiceImpl implements IAiManagerService {
public AiManager selectAiManagerByType(String aiType) {
QueryWrapper<AiManager> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("type", aiType);
queryWrapper.eq("del_flag", "0");
queryWrapper.eq("status", 0);
return aiManagerMapper.selectOne(queryWrapper);
}
}

View File

@ -147,7 +147,9 @@ public class AiOrderServiceImpl implements IAiOrderService {
public AiOrder getAiOrder(String aiType) {
AiManager aiManager = aiManagerService.selectAiManagerByType(aiType);
if (aiManager == null) {
throw new ServiceException("Corresponding functionType does not exist", HttpStatus.BAD_REQUEST);
throw new ServiceException(
"未找到可用的功能类型请在「AI管理」中新增 type=" + aiType + " 且状态为正常的记录,或执行 sql/seed_ai_manager_type_21.sql 初始化",
HttpStatus.BAD_REQUEST);
}
// 判断用户余额是否足够
AiUser aiUser = aiUserService.selectAiUserById(SecurityUtils.getAiUserId());

View File

@ -16,7 +16,9 @@ import java.util.concurrent.TimeUnit;
@Service
public class ByteDeptApiKeyServiceImpl implements IByteDeptApiKeyService {
private static final String NO_PERM_MSG = "用户没有权限,请联系管理员分配部门";
private static final String NO_DEPT_MSG = "用户未分配部门:请在后台为门户用户设置 ai_user.dept_id关联 sys_dept.dept_id";
private static final String NO_DEPT_ROW_MSG = "用户所属部门不存在或已删除,请核对 ai_user.dept_id";
private static final String NO_API_KEY_MSG = "部门未配置火山 API Key请在 sys_dept 为用户所在部门(或其上级二级部门)配置 byte_api_key";
private static final int CACHE_HOURS = 1;
@Autowired
@ -31,7 +33,7 @@ public class ByteDeptApiKeyServiceImpl implements IByteDeptApiKeyService {
@Override
public String resolveVolcApiKey(Long aiUserId) {
if (aiUserId == null) {
throw new ServiceException(NO_PERM_MSG);
throw new ServiceException(NO_DEPT_MSG);
}
String cacheKey = aiUserId + "_byte_api_key";
String cached = redisCache.getCacheObject(cacheKey);
@ -40,22 +42,34 @@ public class ByteDeptApiKeyServiceImpl implements IByteDeptApiKeyService {
}
AiUser aiUser = aiUserService.selectAiUserById(aiUserId);
if (aiUser == null || aiUser.getDeptId() == null) {
throw new ServiceException(NO_PERM_MSG);
throw new ServiceException(NO_DEPT_MSG);
}
SysDept userDept = sysDeptService.selectDeptById(aiUser.getDeptId());
if (userDept == null) {
throw new ServiceException(NO_PERM_MSG);
throw new ServiceException(NO_DEPT_ROW_MSG);
}
Long secondLevelDeptId = resolveSecondLevelDeptId(userDept);
SysDept keyDept = sysDeptService.selectDeptById(secondLevelDeptId);
if (keyDept == null || StringUtils.isEmpty(keyDept.getByteApiKey())) {
throw new ServiceException(NO_PERM_MSG);
// 优先使用用户直接归属部门的 Key多数业务把用户挂在分公司 101并在该节点配 byte_api_key
String apiKey = trimKey(userDept.getByteApiKey());
if (StringUtils.isEmpty(apiKey)) {
Long fallbackDeptId = resolveSecondLevelDeptId(userDept);
if (!fallbackDeptId.equals(userDept.getDeptId())) {
SysDept keyDept = sysDeptService.selectDeptById(fallbackDeptId);
if (keyDept != null) {
apiKey = trimKey(keyDept.getByteApiKey());
}
}
}
if (StringUtils.isEmpty(apiKey)) {
throw new ServiceException(NO_API_KEY_MSG);
}
String apiKey = keyDept.getByteApiKey().trim();
redisCache.setCacheObject(cacheKey, apiKey, CACHE_HOURS, TimeUnit.HOURS);
return apiKey;
}
private static String trimKey(String raw) {
return raw == null ? null : raw.trim();
}
/**
* 二级部门祖级路径中紧接在一级ancestors 第二段之下的部门节点
* 深度不足时退回用户当前部门