From 1393dfcf5b8b67180a776ab8591ca90996054da5 Mon Sep 17 00:00:00 2001
From: Administrator <15274802129@163.com>
Date: Mon, 04 Aug 2025 15:46:49 +0800
Subject: [PATCH] feat(product): 添加 AI 题目配置功能

---
 src/main/java/cc/mrbird/febs/ai/service/AiProductQuestionService.java              |    4 
 src/main/java/cc/mrbird/febs/ai/service/AiProductService.java                      |    2 
 src/main/java/cc/mrbird/febs/ai/service/AiProductQuestionLinkService.java          |    4 
 src/main/java/cc/mrbird/febs/ai/controller/product/ViewController.java             |   45 +++++++++
 src/main/java/cc/mrbird/febs/ai/service/impl/AiProductQuestionServiceImpl.java     |   10 +
 src/main/resources/templates/febs/views/modules/ai/product/productQuestionSet.html |  128 +++++++++++++++++++++++++
 src/main/java/cc/mrbird/febs/ai/service/impl/AiProductQuestionLinkServiceImpl.java |   11 ++
 src/main/java/cc/mrbird/febs/ai/service/impl/AiProductServiceImpl.java             |   31 +++++
 src/main/resources/templates/febs/views/modules/ai/product/list.html               |   19 +++
 src/main/java/cc/mrbird/febs/ai/controller/product/AiProductController.java        |    8 +
 10 files changed, 254 insertions(+), 8 deletions(-)

diff --git a/src/main/java/cc/mrbird/febs/ai/controller/product/AiProductController.java b/src/main/java/cc/mrbird/febs/ai/controller/product/AiProductController.java
index da982f3..816ca69 100644
--- a/src/main/java/cc/mrbird/febs/ai/controller/product/AiProductController.java
+++ b/src/main/java/cc/mrbird/febs/ai/controller/product/AiProductController.java
@@ -84,4 +84,12 @@
 
         return aiProductService.productRoleSet(dto);
     }
+
+
+    @PostMapping("productQuestionSet")
+    @ControllerEndpoint(operation = "AI题目配置", exceptionMessage = "操作失败")
+    public FebsResponse productQuestionSet(@RequestBody @Valid AdminMoveChooseInfoDto dto) {
+
+        return aiProductService.productQuestionSet(dto);
+    }
 }
diff --git a/src/main/java/cc/mrbird/febs/ai/controller/product/ViewController.java b/src/main/java/cc/mrbird/febs/ai/controller/product/ViewController.java
index 6a43ac0..7f874d9 100644
--- a/src/main/java/cc/mrbird/febs/ai/controller/product/ViewController.java
+++ b/src/main/java/cc/mrbird/febs/ai/controller/product/ViewController.java
@@ -37,6 +37,8 @@
     private final AiProductPointLinkService aiProductPointLinkService;
     private final AiProductPointService aiProductPointService;
     private final AiProductRoleLinkService aiProductRoleLinkService;
+    private final AiProductQuestionLinkService aiProductQuestionLinkService;
+    private final AiProductQuestionService aiProductQuestionService;
     private final AiProductRoleService aiProductRoleService;
 
     @GetMapping("list")
@@ -140,4 +142,47 @@
         model.addAttribute("chooseId", id);
         return FebsUtil.view("modules/ai/product/productRoleSet");
     }
+
+
+
+    @GetMapping("productQuestionSet/{id}")
+    @RequiresPermissions("productList:productQuestionSet")
+    public String productQuestionSet(@PathVariable String id, Model model) {
+        List<AdminMoveChooseInfoVo> vos = new ArrayList<>();
+        Set<String> productIds = new HashSet<>();
+
+        AiProduct entity = aiProductService.getById(id);
+        if(ObjectUtil.isNotNull(entity)){
+            //右侧数据
+            LambdaQueryWrapper<AiProductQuestionLink> query = Wrappers.lambdaQuery(AiProductQuestionLink.class);
+            if(StrUtil.isNotEmpty(id)){
+                query.eq(AiProductQuestionLink::getProductId, id);
+            }
+            List<AiProductQuestionLink> selectedList = aiProductQuestionLinkService.selectListByQuery(query);
+            if(CollUtil.isNotEmpty(selectedList)){
+                //stream流操作happyMemberLabelRecords,获取memberId的set集合
+                productIds = selectedList.stream().map(AiProductQuestionLink::getProductQuestionId).collect(Collectors.toSet());
+            }
+
+            //左侧数据
+            LambdaQueryWrapper<AiProductQuestion> aiProductQuestionLambdaQueryWrapper = Wrappers.lambdaQuery(AiProductQuestion.class);
+            aiProductQuestionLambdaQueryWrapper.eq(AiProductQuestion::getProductCategoryId, entity.getProductCategoryId());
+            aiProductQuestionLambdaQueryWrapper.eq(AiProductQuestion::getState, 1);
+            List<AiProductQuestion> allList = aiProductQuestionService.productQuestionTree(aiProductQuestionLambdaQueryWrapper);
+            if(CollUtil.isNotEmpty(allList)){
+                //stream流操作mallMembers,生成一个新的List<MallMemberVo>
+                vos = allList.stream().map(AiProductQuestion -> {
+                    AdminMoveChooseInfoVo vo = new AdminMoveChooseInfoVo();
+                    vo.setId(AiProductQuestion.getId());
+                    vo.setName(AiProductQuestion.getTitle());
+                    return vo;
+                }).collect(Collectors.toList());
+            }
+        }
+
+        model.addAttribute("productQuestionAll", vos);
+        model.addAttribute("productQuestionSelected", productIds);
+        model.addAttribute("chooseId", id);
+        return FebsUtil.view("modules/ai/product/productQuestionSet");
+    }
 }
diff --git a/src/main/java/cc/mrbird/febs/ai/service/AiProductQuestionLinkService.java b/src/main/java/cc/mrbird/febs/ai/service/AiProductQuestionLinkService.java
index 8a7ea6d..0193a77 100644
--- a/src/main/java/cc/mrbird/febs/ai/service/AiProductQuestionLinkService.java
+++ b/src/main/java/cc/mrbird/febs/ai/service/AiProductQuestionLinkService.java
@@ -1,6 +1,7 @@
 package cc.mrbird.febs.ai.service;
 
 import cc.mrbird.febs.ai.entity.AiProductQuestionLink;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.service.IService;
 import java.util.List;
 
@@ -20,4 +21,7 @@
     AiProductQuestionLink getById(String id);
 
 
+    List<AiProductQuestionLink> selectListByQuery(LambdaQueryWrapper<AiProductQuestionLink> query);
+
+    void deleteByQuery(LambdaQueryWrapper<AiProductQuestionLink> eq);
 }
diff --git a/src/main/java/cc/mrbird/febs/ai/service/AiProductQuestionService.java b/src/main/java/cc/mrbird/febs/ai/service/AiProductQuestionService.java
index f9b6212..203750c 100644
--- a/src/main/java/cc/mrbird/febs/ai/service/AiProductQuestionService.java
+++ b/src/main/java/cc/mrbird/febs/ai/service/AiProductQuestionService.java
@@ -1,8 +1,10 @@
 package cc.mrbird.febs.ai.service;
 
 import cc.mrbird.febs.ai.entity.AiProductQuestion;
+import cc.mrbird.febs.ai.entity.AiProductRole;
 import cc.mrbird.febs.common.entity.FebsResponse;
 import cc.mrbird.febs.common.entity.QueryRequest;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.service.IService;
 import java.util.List;
@@ -33,4 +35,6 @@
     FebsResponse delete(String id);
 
     List<AiProductQuestion> questionTree();
+
+    List<AiProductQuestion> productQuestionTree(LambdaQueryWrapper<AiProductQuestion> aiProductQuestionLambdaQueryWrapper);
 }
diff --git a/src/main/java/cc/mrbird/febs/ai/service/AiProductService.java b/src/main/java/cc/mrbird/febs/ai/service/AiProductService.java
index 0622299..06321a9 100644
--- a/src/main/java/cc/mrbird/febs/ai/service/AiProductService.java
+++ b/src/main/java/cc/mrbird/febs/ai/service/AiProductService.java
@@ -39,4 +39,6 @@
     FebsResponse productSet(AdminMoveChooseInfoDto dto);
 
     FebsResponse productRoleSet(AdminMoveChooseInfoDto dto);
+
+    FebsResponse productQuestionSet(AdminMoveChooseInfoDto dto);
 }
diff --git a/src/main/java/cc/mrbird/febs/ai/service/impl/AiProductQuestionLinkServiceImpl.java b/src/main/java/cc/mrbird/febs/ai/service/impl/AiProductQuestionLinkServiceImpl.java
index 1bc9905..4db310b 100644
--- a/src/main/java/cc/mrbird/febs/ai/service/impl/AiProductQuestionLinkServiceImpl.java
+++ b/src/main/java/cc/mrbird/febs/ai/service/impl/AiProductQuestionLinkServiceImpl.java
@@ -30,4 +30,15 @@
         return aiProductQuestionLinkMapper.selectById(id);
     }
 
+    @Override
+    public List<AiProductQuestionLink> selectListByQuery(LambdaQueryWrapper<AiProductQuestionLink> query) {
+
+        return aiProductQuestionLinkMapper.selectList(query);
+    }
+
+    @Override
+    public void deleteByQuery(LambdaQueryWrapper<AiProductQuestionLink> eq) {
+        aiProductQuestionLinkMapper.delete( eq);
+    }
+
 }
diff --git a/src/main/java/cc/mrbird/febs/ai/service/impl/AiProductQuestionServiceImpl.java b/src/main/java/cc/mrbird/febs/ai/service/impl/AiProductQuestionServiceImpl.java
index cdea318..e8009fa 100644
--- a/src/main/java/cc/mrbird/febs/ai/service/impl/AiProductQuestionServiceImpl.java
+++ b/src/main/java/cc/mrbird/febs/ai/service/impl/AiProductQuestionServiceImpl.java
@@ -1,9 +1,6 @@
 package cc.mrbird.febs.ai.service.impl;
 
-import cc.mrbird.febs.ai.entity.AiMemberRole;
-import cc.mrbird.febs.ai.entity.AiProductPoint;
-import cc.mrbird.febs.ai.entity.AiProductQuestion;
-import cc.mrbird.febs.ai.entity.AiProductQuestionItem;
+import cc.mrbird.febs.ai.entity.*;
 import cc.mrbird.febs.ai.mapper.AiProductQuestionMapper;
 import cc.mrbird.febs.ai.service.AiProductQuestionItemService;
 import cc.mrbird.febs.ai.service.AiProductQuestionService;
@@ -160,4 +157,9 @@
         return aiProductQuestionMapper.selectList(null);
     }
 
+    @Override
+    public List<AiProductQuestion> productQuestionTree(LambdaQueryWrapper<AiProductQuestion> aiProductQuestionLambdaQueryWrapper) {
+        return aiProductQuestionMapper.selectList(aiProductQuestionLambdaQueryWrapper);
+    }
+
 }
diff --git a/src/main/java/cc/mrbird/febs/ai/service/impl/AiProductServiceImpl.java b/src/main/java/cc/mrbird/febs/ai/service/impl/AiProductServiceImpl.java
index 9ac7ab2..5ab494e 100644
--- a/src/main/java/cc/mrbird/febs/ai/service/impl/AiProductServiceImpl.java
+++ b/src/main/java/cc/mrbird/febs/ai/service/impl/AiProductServiceImpl.java
@@ -3,10 +3,7 @@
 import cc.mrbird.febs.ai.entity.*;
 import cc.mrbird.febs.ai.mapper.AiProductMapper;
 import cc.mrbird.febs.ai.req.AdminMoveChooseInfoDto;
-import cc.mrbird.febs.ai.service.AiProductCategoryService;
-import cc.mrbird.febs.ai.service.AiProductPointLinkService;
-import cc.mrbird.febs.ai.service.AiProductRoleLinkService;
-import cc.mrbird.febs.ai.service.AiProductService;
+import cc.mrbird.febs.ai.service.*;
 import cc.mrbird.febs.ai.util.UUID;
 import cc.mrbird.febs.common.entity.FebsResponse;
 import cc.mrbird.febs.common.entity.QueryRequest;
@@ -43,6 +40,7 @@
     private final AiProductCategoryService aiProductCategoryService;
     private final AiProductPointLinkService aiProductPointLinkService;
     private final AiProductRoleLinkService aiProductRoleLinkService;
+    private final AiProductQuestionLinkService aiProductQuestionLinkService;
 
     @Override
     public AiProduct getById(String id) {
@@ -218,4 +216,29 @@
         }
         return new FebsResponse().success().message("操作成功");
     }
+
+    @Override
+    public FebsResponse productQuestionSet(AdminMoveChooseInfoDto dto) {
+        String chooseId = dto.getChooseId();
+        List<String> chooseIds = dto.getChooseIds();
+        AiProduct aiProduct = this.getById(chooseId);
+        if (ObjectUtil.isNotNull(aiProduct)) {
+            aiProductQuestionLinkService.deleteByQuery(
+                    Wrappers.lambdaQuery(AiProductQuestionLink.class)
+                            .eq(AiProductQuestionLink::getProductId,chooseId)
+            );
+            if(CollUtil.isNotEmpty(chooseIds)){
+                Date createdTime = new Date();
+                for (String item : chooseIds){
+                    AiProductQuestionLink entity = new AiProductQuestionLink();
+                    entity.setId(UUID.getSimpleUUIDString());
+                    entity.setProductId(chooseId);
+                    entity.setProductQuestionId(item);
+                    entity.setCreatedTime(createdTime);
+                    aiProductQuestionLinkService.getBaseMapper().insert(entity);
+                }
+            }
+        }
+        return new FebsResponse().success().message("操作成功");
+    }
 }
diff --git a/src/main/resources/templates/febs/views/modules/ai/product/list.html b/src/main/resources/templates/febs/views/modules/ai/product/list.html
index ebd5512..a41f5f6 100644
--- a/src/main/resources/templates/febs/views/modules/ai/product/list.html
+++ b/src/main/resources/templates/febs/views/modules/ai/product/list.html
@@ -54,6 +54,7 @@
         <button class="layui-btn layui-btn-sm layui-btn-primary febs-button-green-plain" shiro:hasPermission="productList:add" lay-event="productAdd">新增</button>
         <button class="layui-btn layui-btn-sm layui-btn-primary febs-button-green-plain" shiro:hasPermission="productList:pointSet" lay-event="pointSet">知识点配置</button>
         <button class="layui-btn layui-btn-sm layui-btn-primary febs-button-green-plain" shiro:hasPermission="productList:productRoleSet" lay-event="productRoleSet">AI陪练配置</button>
+        <button class="layui-btn layui-btn-sm layui-btn-primary febs-button-green-plain" shiro:hasPermission="productList:productQuestionSet" lay-event="productQuestionSet">AI题目配置</button>
     </div>
 </script>
 
@@ -282,6 +283,24 @@
                     }
                 });
             }
+
+            if (layEvent === 'productQuestionSet') {
+                var checkData = table.checkStatus('productTable').data;
+                if (checkData.length > 1 || checkData.length === 0) {
+                    febs.alert.warn('每次操作只能操作一行数据');
+                    return;
+                }
+                febs.modal.open('AI题目配置', 'modules/ai/product/productQuestionSet/' + checkData[0].id, {
+                    btn: ['提交', '取消'],
+                    area:['100%','100%'],
+                    yes: function (index, layero) {
+                        $('#productQuestion-set').find('#submit').trigger('click');
+                    },
+                    btn2: function () {
+                        layer.closeAll();
+                    }
+                });
+            }
         });
 
         function initproductTable() {
diff --git a/src/main/resources/templates/febs/views/modules/ai/product/productQuestionSet.html b/src/main/resources/templates/febs/views/modules/ai/product/productQuestionSet.html
new file mode 100644
index 0000000..918d9cd
--- /dev/null
+++ b/src/main/resources/templates/febs/views/modules/ai/product/productQuestionSet.html
@@ -0,0 +1,128 @@
+<div class="layui-fluid layui-anim febs-anim" id="productQuestion-set" lay-title="AI题目配置">
+    <div class="layui-row febs-container">
+        <div class="layui-col-md12">
+            <div class="layui-fluid" id="productQuestion-type-set">
+                <form class="layui-form" action="" lay-filter="productQuestion-type-set-form">
+                    <div class="layui-tab layui-tab-brief" lay-filter="docDemoTabBrief">
+                        <ul class="layui-tab-title">
+                            <li class="layui-this">题目配置</li>
+                        </ul>
+                        <div class="layui-tab-content">
+                            <input type="text" name="chooseId"
+                                   placeholder="" autoComplete="off" class="layui-input febs-hide">
+                            <div class="layui-tab-item layui-show">
+                                <div class="layui-form-item">
+                                    <div id="productQuestionSetMove"></div>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                    <div class="layui-form-item febs-hide">
+                        <button class="layui-btn" lay-submit="" lay-filter="productQuestion-type-set-form-submit" id="submit">保存</button>
+                    </div>
+                </form>
+            </div>
+        </div>
+    </div>
+</div>
+
+<script data-th-inline="javascript">
+    layui.use(['febs','form', 'transfer'], function () {
+        var $ = layui.jquery,
+            febs = layui.febs,
+            layer = layui.layer,
+            form = layui.form,
+            transfer = layui.transfer,
+            productQuestionTypeAll = [[${productQuestionAll}]],
+            productQuestionTypeChoose = [[${productQuestionSelected}]],
+            chooseId = [[${chooseId}]],
+            $view = $('#productQuestion-set'),
+
+            $productQuestionSetMoveQuery = $view.find('#productQuestionSetMoveQuery')
+        ;
+
+        // 查询按钮
+        $productQuestionSetMoveQuery.on('click', function () {
+            console.log(transfer.getData('productQuestionSetMove-set'))
+
+            let data1 = transfer.getData('productQuestionSetMove-set');
+            //获取data1中的value,返回一个数组
+            let productQuestionIdList = data1.map(function(item){
+                return item.value;
+            });
+
+            console.log(productQuestionIdList)
+        });
+
+
+        form.render();
+
+        initproductQuestionTypeSet();
+
+        function initproductQuestionTypeSet() {
+            console.log("productQuestionTypeAll:", productQuestionTypeAll); // 调试信息
+            console.log("productQuestionTypeChoose:", productQuestionTypeChoose); // 调试信息
+            console.log("chooseId:", chooseId); // 调试信息
+            form.val("productQuestion-type-set-form", {
+                "chooseId": chooseId,
+            });
+            // 转换数据格式(假设接口返回的数据结构需要处理)
+            var dataLeft = productQuestionTypeAll.map(function(item){
+                return {
+                    value: item.id,  // 值字段
+                    title: item.name // 显示文本
+                }
+            });
+            var dataRight = productQuestionTypeChoose.map(function(item){
+                return {
+                    value: item,  // 值字段
+                }
+            });
+
+            // 渲染穿梭框
+            transfer.render({
+                elem: '#productQuestionSetMove',
+                data: dataLeft,
+                id: 'productQuestionSetMove-set', // 唯一标识
+                title: ['待选择列表', '已选择列表'],
+                width: 300,
+                height: 400,
+                showSearch: true,
+                value: productQuestionTypeChoose,
+            });
+        }
+
+        form.on('submit(productQuestion-type-set-form-submit)', function (data) {
+            let data1 = transfer.getData('productQuestionSetMove-set');
+            //获取data1中的value,返回一个数组
+            let productQuestionIdList = data1.map(function(item){
+                return item.value;
+            });
+            data.field.chooseIds = productQuestionIdList;
+            data.field.chooseId = chooseId;
+            $.ajax({
+                'url':ctx + 'admin/product/productQuestionSet',
+                'type':'post',
+                'dataType':'json',
+                'headers' : {'Content-Type' : 'application/json;charset=utf-8'}, //接口json格式
+                'traditional': true,//ajax传递数组必须添加属性
+                'data':JSON.stringify(data.field),
+                'success':function (data) {
+                    if(data.code==200){
+                        layer.closeAll();
+                        febs.alert.success(data.message);
+                        $('#febs-type').find('#query').click();
+                    }else{
+                        febs.alert.warn(data.message);
+                    }
+                },
+                'error':function () {
+                    febs.alert.warn('服务器繁忙');
+                }
+            })
+            return false;
+        });
+
+
+    });
+</script>
\ No newline at end of file

--
Gitblit v1.9.1