DynamicPartService.java 12.8 KB
package com.xly.service;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSONObject;
import com.xly.agent.DynamicPartAiAgent;
import com.xly.agent.SystemPromptGenerator;
import com.xly.entity.ParamRule;
import com.xly.entity.UserSceneSession;
import com.xly.tool.DynamicToolProvider;
import com.xly.util.PinyinUtil;
import com.xly.util.Validator;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/***
 * @Author 钱豹
 * @Date 22:39 2026/1/30
 * @Param
 * @return
 * @Description 动态部件提取类
 **/
@Slf4j
@Service("dynamicPartService")
@RequiredArgsConstructor
public class DynamicPartService {
    private final DynamicToolProvider dynamicToolProvider;

    private final DynamicPartAiAgent aiAgent;

    /***
     * @Author 钱豹
     * @Date 23:07 2026/7/29
     * @Description AI识别获取方法(二级路由:在当前场景内选出一个 sMethodNo)
     **/
    public List<String> dynamicPartDTOChoiceResult(String userId, String userInput, UserSceneSession session) {
        //判断输入
        Boolean isConfirmed = dynamicToolProvider.isConfirmed(userInput,session);
        if(ObjectUtil.isNotEmpty(session.getDynamicPartList())){
            List<String> partList =session.getDynamicPartList();
            List<String> partListBs = getPartBs(session);
            // 移除 partList 中存在于 partListBs 的所有元素
            partList.removeAll(partListBs);
            // 找出 partList 中存在于 partListPz 的元素
            List<String> result = new ArrayList<>(partList);
            List<String> partListPz = getPartPz(session);
            List<String> partListAdd = new ArrayList<>();
            if(isConfirmed){
                //方法添加动态参数
                partListAdd.addAll(partList);
                partList =null;
                session.setBConfirmedDynamicPartFist(true);
            }
            if(!isConfirmed){
                //添加再配置中的动态
                result.retainAll(partListPz);
                partListAdd.addAll(result);
                partList =null;
                session.setBConfirmedDynamicPartFist(true);
            }
            if(ObjectUtil.isNotEmpty(partListAdd)){
                //部件添加数据
                addToolMethod( partListAdd, session);
            }
            return partList;
        }

        //如果存在动态部件
        if(ObjectUtil.isEmpty(session.getCurrentTool())
                || ObjectUtil.isEmpty(session.getCurrentTool().getSdynamicMemo())
                || session.getBConfirmedDynamicPart()
        ){
            return null;
        }

        List<String> partList = getPart(session);

        // 5. 生成系统提示词
        String sSystemPrompt = SystemPromptGenerator.generateChoicePartPrompt(partList);

        // 3. 调大模型
        String raw = aiAgent.getDynamicPartMethod(userId, userInput, sSystemPrompt).content();
        log.debug("方法获取动态部件: userId={}, raw={}", userId, raw);

        // 4. 解析(含 ```json 容错)
        List<String> result = parseChoice(raw);
        if (result == null || ObjectUtil.isEmpty(result)) {
            return null;
        }
        // 5. none / 阈值 / 白名单 三重校验
        if ("none".equalsIgnoreCase(raw)) {
            log.info("方法动态方法:无匹配部件, input={}", userInput);
            return null;
        }
        session.setDynamicPartList(result);
        return result;
    }

    /***
     * @Author 钱豹
     * @Date 20:01 2026/7/30
     * @Param [partListAdd, session]
     * @return void
     * @Description 动态方法 添加参数
     **/
    private void addToolMethod(List<String> partListAdd, UserSceneSession session) {
        if (ObjectUtil.isEmpty(partListAdd)) {
            return;
        }
        List<ParamRule> paramRuleDataAll = new ArrayList<>(session.getCurrentTool().getParamRuleListAll());
        // 获取已有的动态部件名称,用于去重
        Set<String> existingDynamicParts = new HashSet<>();
        // 筛选标准参数(只筛选一次,提高性能)
        List<ParamRule> standardParams = paramRuleDataAll.stream()
                .filter(d -> ObjectUtil.isNotEmpty(d.getSGroup())
                        && !d.getBDynamicParam()
                        && "部件".equals(d.getSGroup())
                        && d.getBStandardParam())
                .collect(Collectors.toList());

        if (ObjectUtil.isEmpty(standardParams)) {
            log.warn("未找到部件标准参数,无法添加动态部件");
            return;
        }
        for (String sVal : partListAdd) {
            // 去重:如果该部件已存在,跳过
            if (existingDynamicParts.contains(sVal)) {
                log.debug("部件已存在,跳过: {}", sVal);
                continue;
            }
            // 拼音转换
            String firstLetters = "";
            try {
                firstLetters = PinyinUtil.convertToPinyin(sVal);
            } catch (Exception e) {
                log.warn("拼音转换失败: {}", sVal, e);
                firstLetters = sVal.length() > 0 ? sVal.substring(0, 1).toUpperCase() : "";
            }

            // 遍历标准参数,生成动态参数
            for (ParamRule standard : standardParams) {
                ParamRule copy = new ParamRule();
                BeanUtils.copyProperties(standard, copy);

                // 解析参数名:取 "-" 后的部分
                String paramName = standard.getSParam();
                if (paramName != null && paramName.contains("-")) {
                    String[] parts = paramName.split("-");
                    paramName = parts.length > 1 ? parts[1] : parts[0];
                }

                copy.setSParam(sVal + "-" + paramName);
                copy.setSParamValue(firstLetters + standard.getSParamValue());
                copy.setIOrder(standard.getIOrder() + 100000);
                copy.setBDynamicParam(true);
                copy.setSDynamicParam(sVal);
                copy.setSDynamicParamName("sDynamicPart");

                paramRuleDataAll.add(copy);
            }
            // 记录已添加的部件
            existingDynamicParts.add(sVal);
        }

        // 重新筛选分类列表
        List<ParamRule> paramRuleList = paramRuleDataAll.stream()
                .filter(one -> Boolean.TRUE.equals(one.getBTipModel()))
                .collect(Collectors.toList());

        List<ParamRule> paramRuleListCheck = paramRuleDataAll.stream()
                .filter(one -> Boolean.TRUE.equals(one.getBEmpty()))
                .collect(Collectors.toList());

        // 更新 session
        session.getCurrentTool().setParamRuleListAll(paramRuleDataAll);
        session.getCurrentTool().setParamRuleListCheck(paramRuleListCheck);
        session.getCurrentTool().setParamRuleList(paramRuleList);

        // 更新 args
        Map<String, Object> args = new HashMap<>();
        if (ObjectUtil.isNotEmpty(session.getArgs())) {
            args.putAll(session.getArgs());
        }
        args.put("sDynamicPart", JSONUtil.toJsonStr(partListAdd));
        session.setArgs(args);
    }



    /***
     * @Author 钱豹
     * @Date 18:13 2026/7/30
     * @Param [session]
     * @return java.util.List<java.lang.String>
     * @Description 获取部件
     **/
    private List<String> getPart( UserSceneSession session){
        // 1. 获取参数规则列表
        List<ParamRule> paramRuleAll = session.getCurrentTool().getParamRuleList();
        // 2. 从 ParamRule 中提取符合条件的部件名称
        Set<String> partSet = new HashSet<>();
        if (ObjectUtil.isNotEmpty(paramRuleAll)) {
            for (ParamRule rule : paramRuleAll) {
                String sGroup = rule.getSGroup();
                String sParam = rule.getSParam();
                if ("部件".equals(sGroup)
                        && !"动态部件".equals(sParam)
                        && !"删除动态部件".equals(sParam)) {
                    // 取 "-" 前的部分作为部件名称
                    String partName = sParam.contains("-") ? sParam.split("-")[0] : sParam;
                    partSet.add(partName);
                }
            }
        }

        // 3. 从 sSdynamicMemo 中获取已保存的部件
        String sSdynamicMemo = session.getCurrentTool().getSdynamicMemo();
        Set<String> mergedSet = new LinkedHashSet<>();

        // 先加入 sSdynamicMemo 中的部件(保持原有顺序)
        if (ObjectUtil.isNotEmpty(sSdynamicMemo)) {
            String[] memoParts = sSdynamicMemo.split(",");
            for (String part : memoParts) {
                String trimmed = part.trim();
                if (!trimmed.isEmpty()) {
                    mergedSet.add(trimmed);
                }
            }
        }

        // 再加入从 ParamRule 提取的部件(自动去重)
        mergedSet.addAll(partSet);

        // 4. 转为 List
        List<String> partList = new ArrayList<>(mergedSet);
        return partList;
    }

    /***
     * @Author 钱豹
     * @Date 18:13 2026/7/30
     * @Param [session]
     * @return java.util.List<java.lang.String>
     * @Description 获取部件
     **/
    private List<String> getPartBs( UserSceneSession session){
        // 1. 获取参数规则列表
        List<ParamRule> paramRuleAll = session.getCurrentTool().getParamRuleList();
        // 2. 从 ParamRule 中提取符合条件的部件名称
        Set<String> partSet = new HashSet<>();
        if (ObjectUtil.isNotEmpty(paramRuleAll)) {
            for (ParamRule rule : paramRuleAll) {
                String sGroup = rule.getSGroup();
                String sParam = rule.getSParam();
                if ("部件".equals(sGroup)
                        && !"动态部件".equals(sParam)
                        && !"删除动态部件".equals(sParam)) {
                    // 取 "-" 前的部分作为部件名称
                    String partName = sParam.contains("-") ? sParam.split("-")[0] : sParam;
                    partSet.add(partName);
                }
            }
        }
        return new ArrayList<>(partSet);
    }

    private List<String> getPartPz( UserSceneSession session){
        String sSdynamicMemo = session.getCurrentTool().getSdynamicMemo();
        Set<String> mergedSet = new LinkedHashSet<>();

        // 先加入 sSdynamicMemo 中的部件(保持原有顺序)
        if (ObjectUtil.isNotEmpty(sSdynamicMemo)) {
            String[] memoParts = sSdynamicMemo.split(",");
            for (String part : memoParts) {
                String trimmed = part.trim();
                if (!trimmed.isEmpty()) {
                    mergedSet.add(trimmed);
                }
            }
        }
        return  new ArrayList<>(mergedSet);
    }

    /***
     * @Author 钱豹
     * @Date 23:07 2026/7/29
     * @Description
     **/
    public String doCheckDynamicPart(String userId,String userInput,UserSceneSession session) {
        List<String> partList = getPart(session);
        //判断是否存返回的部件是否有不存在
        List<String> dDynamicPartList = session.getDynamicPartList();
        // 判断 dDynamicPartList 中不存在于 partList 的数据(即多余的、已失效的部件)
        List<String> invalidParts = new ArrayList<>();
        if (ObjectUtil.isNotEmpty(dDynamicPartList)) {
            // 将 partList 转为 Set 提高查询效率
            Set<String> partSet = new HashSet<>(partList);
            for (String dynamicPart : dDynamicPartList) {
                if (!partSet.contains(dynamicPart)) {
                    invalidParts.add(dynamicPart);
                }
            }
        }
        //存在动态部件,没有采集 需要添加参数
        if (ObjectUtil.isNotEmpty(dDynamicPartList) && ObjectUtil.isEmpty(invalidParts)) {
            List<String> partListBs = getPartBs(session);
            // 移除 partList 中存在于 partListBs 的所有元素
            dDynamicPartList.removeAll(partListBs);
            List<String> partListAdd = new ArrayList<>(dDynamicPartList);
            addToolMethod(partListAdd,session);
        }
        if(ObjectUtil.isNotEmpty(invalidParts)){
            return String.join(",", invalidParts)+"部件不存在,是否是部件,如果是,输入是,否则输入否";
        }
        return null;
    }


    /** 解析模型输出,兼容被 ```json ... ``` 包裹或前后带杂字符的情况 */
    private List<String> parseChoice(String raw) {
        if (ObjectUtil.isEmpty(raw)) return null;
        try {
            return JSONUtil.toList(raw,String.class);
        } catch (Exception e) {
            log.error("方法路由结果解析失败, raw={}", raw, e);
            return null;
        }
    }

}