From 490bceb5d9e911a5c4f687dd6570b6699ca11915 Mon Sep 17 00:00:00 2001
From: Administrator <15274802129@163.com>
Date: Tue, 16 Sep 2025 17:27:11 +0800
Subject: [PATCH] feat(ai): 添加会员角色知识点配置功能

---
 src/main/java/cc/mrbird/febs/ai/service/AiProductPointService.java                 |    1 
 src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberRoleServiceImpl.java          |   32 +++++
 src/main/java/cc/mrbird/febs/ai/service/impl/AiProductPointServiceImpl.java        |    8 +
 src/main/java/cc/mrbird/febs/ai/controller/memberRole/AiMemberRoleController.java  |    7 +
 src/main/java/cc/mrbird/febs/ai/service/AiMemberRolePointService.java              |   14 ++
 src/main/resources/templates/febs/views/modules/ai/memberRole/list.html            |   19 +++
 src/main/java/cc/mrbird/febs/ai/controller/memberRole/ViewController.java          |   54 ++++++++-
 src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberRolePointServiceImpl.java     |   29 ++++
 src/main/java/cc/mrbird/febs/ai/service/impl/AiProductServiceImpl.java             |   11 +
 src/main/resources/templates/febs/views/modules/ai/memberRole/productSet.html      |    2 
 src/main/java/cc/mrbird/febs/ai/service/AiMemberRoleService.java                   |    2 
 src/main/resources/templates/febs/views/modules/ai/memberRole/productPointSet.html |  128 +++++++++++++++++++++
 12 files changed, 295 insertions(+), 12 deletions(-)

diff --git a/src/main/java/cc/mrbird/febs/ai/controller/memberRole/AiMemberRoleController.java b/src/main/java/cc/mrbird/febs/ai/controller/memberRole/AiMemberRoleController.java
index 5c9d160..30c585c 100644
--- a/src/main/java/cc/mrbird/febs/ai/controller/memberRole/AiMemberRoleController.java
+++ b/src/main/java/cc/mrbird/febs/ai/controller/memberRole/AiMemberRoleController.java
@@ -77,4 +77,11 @@
 
         return aiMemberRoleService.productSet(dto);
     }
+
+    @PostMapping("productPointSet")
+    @ControllerEndpoint(operation = "产品知识点配置", exceptionMessage = "操作失败")
+    public FebsResponse productPointSet(@RequestBody @Valid AdminMoveChooseInfoDto dto) {
+
+        return aiMemberRoleService.productPointSet(dto);
+    }
 }
diff --git a/src/main/java/cc/mrbird/febs/ai/controller/memberRole/ViewController.java b/src/main/java/cc/mrbird/febs/ai/controller/memberRole/ViewController.java
index b333218..87396e8 100644
--- a/src/main/java/cc/mrbird/febs/ai/controller/memberRole/ViewController.java
+++ b/src/main/java/cc/mrbird/febs/ai/controller/memberRole/ViewController.java
@@ -1,12 +1,8 @@
 package cc.mrbird.febs.ai.controller.memberRole;
 
-import cc.mrbird.febs.ai.entity.AiMemberRole;
-import cc.mrbird.febs.ai.entity.AiMemberRoleProduct;
-import cc.mrbird.febs.ai.entity.AiProduct;
+import cc.mrbird.febs.ai.entity.*;
 import cc.mrbird.febs.ai.res.AdminMoveChooseInfoVo;
-import cc.mrbird.febs.ai.service.AiMemberRoleProductService;
-import cc.mrbird.febs.ai.service.AiMemberRoleService;
-import cc.mrbird.febs.ai.service.AiProductService;
+import cc.mrbird.febs.ai.service.*;
 import cc.mrbird.febs.common.entity.FebsConstant;
 import cc.mrbird.febs.common.utils.FebsUtil;
 import cn.hutool.core.collection.CollUtil;
@@ -38,7 +34,9 @@
 
     private final AiMemberRoleService aiMemberRoleService;
     private final AiMemberRoleProductService aiMemberRoleProductService;
+    private final AiMemberRolePointService aiMemberRolePointService;
     private final AiProductService aiProductService;
+    private final AiProductPointService aiProductPointService;
 
     @GetMapping("list")
     @RequiresPermissions("memberRoleList:view")
@@ -75,6 +73,8 @@
             LambdaQueryWrapper<AiMemberRoleProduct> query = Wrappers.lambdaQuery(AiMemberRoleProduct.class);
             if(StrUtil.isNotEmpty(id)){
                 query.eq(AiMemberRoleProduct::getRoleId, id);
+            }
+            if(StrUtil.isNotEmpty(companyId)){
                 query.eq(AiMemberRoleProduct::getCompanyId, companyId);
             }
             List<AiMemberRoleProduct> selectedList = aiMemberRoleProductService.selectListByQuery(query);
@@ -101,4 +101,46 @@
         model.addAttribute("chooseId", id);
         return FebsUtil.view("modules/ai/memberRole/productSet");
     }
+
+    @GetMapping("productPointSet/{id}")
+    @RequiresPermissions("memberRoleList:productPointSet")
+    public String productPointSet(@PathVariable String id, Model model) {
+        List<AdminMoveChooseInfoVo> vos = new ArrayList<>();
+        Set<String> productPointIds = new HashSet<>();
+
+        AiMemberRole entity = aiMemberRoleService.getById(id);
+        String companyId = entity.getCompanyId();
+        if(ObjectUtil.isNotNull(entity)){
+            //右侧数据
+            LambdaQueryWrapper<AiMemberRolePoint> query = Wrappers.lambdaQuery(AiMemberRolePoint.class);
+            if(StrUtil.isNotEmpty(id)){
+                query.eq(AiMemberRolePoint::getRoleId, id);
+            }
+            if(StrUtil.isNotEmpty(companyId)){
+                query.eq(AiMemberRolePoint::getCompanyId, companyId);
+            }
+            List<AiMemberRolePoint> selectedList = aiMemberRolePointService.selectListByQuery(query);
+            if(CollUtil.isNotEmpty(selectedList)){
+                //stream流操作happyMemberLabelRecords,获取memberId的set集合
+                productPointIds = selectedList.stream().map(AiMemberRolePoint::getProductPointId).collect(Collectors.toSet());
+            }
+
+            //左侧数据
+            List<AiProductPoint> allList = aiProductPointService.selectList(companyId);
+            if(CollUtil.isNotEmpty(allList)){
+                //stream流操作mallMembers,生成一个新的List<MallMemberVo>
+                vos = allList.stream().map(AiProductPoint -> {
+                    AdminMoveChooseInfoVo vo = new AdminMoveChooseInfoVo();
+                    vo.setId(AiProductPoint.getId());
+                    vo.setName(AiProductPoint.getTitle());
+                    return vo;
+                }).collect(Collectors.toList());
+            }
+        }
+
+        model.addAttribute("productPointAll", vos);
+        model.addAttribute("productPointSelected", productPointIds);
+        model.addAttribute("choosePointId", id);
+        return FebsUtil.view("modules/ai/memberRole/productPointSet");
+    }
 }
diff --git a/src/main/java/cc/mrbird/febs/ai/service/AiMemberRolePointService.java b/src/main/java/cc/mrbird/febs/ai/service/AiMemberRolePointService.java
new file mode 100644
index 0000000..eea5a14
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/ai/service/AiMemberRolePointService.java
@@ -0,0 +1,14 @@
+package cc.mrbird.febs.ai.service;
+
+import cc.mrbird.febs.ai.entity.AiMemberRolePoint;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+import java.util.List;
+
+public interface AiMemberRolePointService extends IService<AiMemberRolePoint> {
+
+    List<AiMemberRolePoint> selectListByQuery(LambdaQueryWrapper<AiMemberRolePoint> query);
+
+    void deleteByQuery(LambdaQueryWrapper<AiMemberRolePoint> eq);
+}
diff --git a/src/main/java/cc/mrbird/febs/ai/service/AiMemberRoleService.java b/src/main/java/cc/mrbird/febs/ai/service/AiMemberRoleService.java
index e7c15f3..a39ae57 100644
--- a/src/main/java/cc/mrbird/febs/ai/service/AiMemberRoleService.java
+++ b/src/main/java/cc/mrbird/febs/ai/service/AiMemberRoleService.java
@@ -34,4 +34,6 @@
     FebsResponse delete(String id);
 
     FebsResponse productSet(AdminMoveChooseInfoDto dto);
+
+    FebsResponse productPointSet(AdminMoveChooseInfoDto dto);
 }
diff --git a/src/main/java/cc/mrbird/febs/ai/service/AiProductPointService.java b/src/main/java/cc/mrbird/febs/ai/service/AiProductPointService.java
index a75ed32..9e23052 100644
--- a/src/main/java/cc/mrbird/febs/ai/service/AiProductPointService.java
+++ b/src/main/java/cc/mrbird/febs/ai/service/AiProductPointService.java
@@ -34,4 +34,5 @@
 
     List<AiProductPoint> pointTree(String companyId);
 
+    List<AiProductPoint> selectList(String companyId);
 }
diff --git a/src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberRolePointServiceImpl.java b/src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberRolePointServiceImpl.java
new file mode 100644
index 0000000..0100c57
--- /dev/null
+++ b/src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberRolePointServiceImpl.java
@@ -0,0 +1,29 @@
+package cc.mrbird.febs.ai.service.impl;
+
+import cc.mrbird.febs.ai.entity.AiMemberRolePoint;
+import cc.mrbird.febs.ai.mapper.AiMemberRolePointMapper;
+import cc.mrbird.febs.ai.service.AiMemberRolePointService;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class AiMemberRolePointServiceImpl extends ServiceImpl<AiMemberRolePointMapper, AiMemberRolePoint> implements AiMemberRolePointService {
+
+    private final AiMemberRolePointMapper aiMemberRolePointMapper;
+    @Override
+    public List<AiMemberRolePoint> selectListByQuery(LambdaQueryWrapper<AiMemberRolePoint> query) {
+        return aiMemberRolePointMapper.selectList( query);
+    }
+
+    @Override
+    public void deleteByQuery(LambdaQueryWrapper<AiMemberRolePoint> eq) {
+        aiMemberRolePointMapper.delete( eq);
+    }
+}
diff --git a/src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberRoleServiceImpl.java b/src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberRoleServiceImpl.java
index 2151bc3..488e56b 100644
--- a/src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberRoleServiceImpl.java
+++ b/src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberRoleServiceImpl.java
@@ -1,9 +1,11 @@
 package cc.mrbird.febs.ai.service.impl;
 
 import cc.mrbird.febs.ai.entity.AiMemberRole;
+import cc.mrbird.febs.ai.entity.AiMemberRolePoint;
 import cc.mrbird.febs.ai.entity.AiMemberRoleProduct;
 import cc.mrbird.febs.ai.mapper.AiMemberRoleMapper;
 import cc.mrbird.febs.ai.req.AdminMoveChooseInfoDto;
+import cc.mrbird.febs.ai.service.AiMemberRolePointService;
 import cc.mrbird.febs.ai.service.AiMemberRoleProductService;
 import cc.mrbird.febs.ai.service.AiMemberRoleService;
 import cc.mrbird.febs.ai.service.AiProductService;
@@ -40,6 +42,7 @@
     private final AiMemberRoleMapper aiMemberRoleMapper;
     private final AiProductService aiProductService;
     private final AiMemberRoleProductService aiMemberRoleProductService;
+    private final AiMemberRolePointService aiMemberRolePointService;
     private final IUserService iUserService;
 
     @Override
@@ -148,4 +151,33 @@
         }
         return new FebsResponse().success().message("操作成功");
     }
+
+    @Override
+    public FebsResponse productPointSet(AdminMoveChooseInfoDto dto) {
+
+        String chooseId = dto.getChooseId();
+        List<String> chooseIds = dto.getChooseIds();
+        AiMemberRole aiMemberRole = this.getById(chooseId);
+        if (ObjectUtil.isNotNull(aiMemberRole)) {
+            String companyId = aiMemberRole.getCompanyId();
+            aiMemberRolePointService.deleteByQuery(
+                    Wrappers.lambdaQuery(AiMemberRolePoint.class)
+                            .eq(AiMemberRolePoint::getRoleId,chooseId)
+                            .eq(AiMemberRolePoint::getCompanyId,companyId)
+            );
+            if(CollUtil.isNotEmpty(chooseIds)){
+                Date createdTime = new Date();
+                for (String item : chooseIds){
+                    AiMemberRolePoint entity = new AiMemberRolePoint();
+                    entity.setId(UUID.getSimpleUUIDString());
+                    entity.setRoleId(chooseId);
+                    entity.setProductPointId(item);
+                    entity.setCompanyId(companyId);
+                    entity.setCreatedTime(createdTime);
+                    aiMemberRolePointService.getBaseMapper().insert(entity);
+                }
+            }
+        }
+        return new FebsResponse().success().message("操作成功");
+    }
 }
diff --git a/src/main/java/cc/mrbird/febs/ai/service/impl/AiProductPointServiceImpl.java b/src/main/java/cc/mrbird/febs/ai/service/impl/AiProductPointServiceImpl.java
index c496f82..4bde82d 100644
--- a/src/main/java/cc/mrbird/febs/ai/service/impl/AiProductPointServiceImpl.java
+++ b/src/main/java/cc/mrbird/febs/ai/service/impl/AiProductPointServiceImpl.java
@@ -131,6 +131,14 @@
         return aiProductPointMapper.selectList(query);
     }
 
+    @Override
+    public List<AiProductPoint> selectList(String companyId) {
+        LambdaQueryWrapper<AiProductPoint> query = Wrappers.lambdaQuery(AiProductPoint.class);
+        if (StrUtil.isNotEmpty(companyId)){
+            query.eq(AiProductPoint::getCompanyId, companyId);
+        }
+        return aiProductPointMapper.selectList(query);
+    }
 
 
 }
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 2a934fb..3135b2e 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
@@ -162,11 +162,12 @@
 
     @Override
     public List<AiProduct> selectList(String companyId) {
-        return aiProductMapper.selectList(
-                Wrappers.lambdaQuery(AiProduct.class)
-                        .eq(AiProduct::getCompanyId, companyId)
-                .ne(AiProduct::getState, 2)
-        );
+        LambdaQueryWrapper<AiProduct> query = Wrappers.lambdaQuery(AiProduct.class);
+        if (StrUtil.isNotEmpty(companyId)){
+            query.eq(AiProduct::getCompanyId, companyId);
+        }
+        query.ne(AiProduct::getState, 2);
+        return aiProductMapper.selectList(query);
     }
 
     @Override
diff --git a/src/main/resources/templates/febs/views/modules/ai/memberRole/list.html b/src/main/resources/templates/febs/views/modules/ai/memberRole/list.html
index fb030b9..476dca4 100644
--- a/src/main/resources/templates/febs/views/modules/ai/memberRole/list.html
+++ b/src/main/resources/templates/febs/views/modules/ai/memberRole/list.html
@@ -47,6 +47,7 @@
     <div class="layui-btn-container">
         <button class="layui-btn layui-btn-sm layui-btn-primary febs-button-green-plain" shiro:hasPermission="memberRoleList:add" lay-event="memberRoleAdd">新增</button>
         <button class="layui-btn layui-btn-sm layui-btn-primary febs-button-green-plain" shiro:hasPermission="memberRoleList:productSet" lay-event="productSet">产品配置</button>
+        <button class="layui-btn layui-btn-sm layui-btn-primary febs-button-green-plain" shiro:hasPermission="memberRoleList:productPointSet" lay-event="productPointSet">知识点配置</button>
     </div>
 </script>
 
@@ -193,6 +194,24 @@
                     }
                 });
             }
+
+            if (layEvent === 'productPointSet') {
+                var checkData = table.checkStatus('memberRoleTable').data;
+                if (checkData.length > 1 || checkData.length === 0) {
+                    febs.alert.warn('每次操作只能操作一行数据');
+                    return;
+                }
+                febs.modal.open('知识点配置', 'modules/ai/memberRole/productPointSet/' + checkData[0].id, {
+                    btn: ['提交', '取消'],
+                    area:['100%','100%'],
+                    yes: function (index, layero) {
+                        $('#productPoint-set').find('#submit').trigger('click');
+                    },
+                    btn2: function () {
+                        layer.closeAll();
+                    }
+                });
+            }
         });
 
         function initMemberRoleTable() {
diff --git a/src/main/resources/templates/febs/views/modules/ai/memberRole/productPointSet.html b/src/main/resources/templates/febs/views/modules/ai/memberRole/productPointSet.html
new file mode 100644
index 0000000..bf937bc
--- /dev/null
+++ b/src/main/resources/templates/febs/views/modules/ai/memberRole/productPointSet.html
@@ -0,0 +1,128 @@
+<div class="layui-fluid layui-anim febs-anim" id="productPoint-set" lay-title="知识点配置">
+    <div class="layui-row febs-container">
+        <div class="layui-col-md12">
+            <div class="layui-fluid" id="product-type-set">
+                <form class="layui-form" action="" lay-filter="product-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="productPointSetMove"></div>
+                                </div>
+                            </div>
+                        </div>
+                    </div>
+                    <div class="layui-form-item febs-hide">
+                        <button class="layui-btn" lay-submit="" lay-filter="product-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,
+            productTypeAll = [[${productPointAll}]],
+            productTypeChoose = [[${productPointSelected}]],
+            chooseId = [[${choosePointId}]],
+            $view = $('#productPoint-set'),
+
+            $productPointSetQuery = $view.find('#productPointSetQuery')
+        ;
+
+        // 查询按钮
+        $productPointSetQuery.on('click', function () {
+            console.log(transfer.getData('productPointSet-set'))
+
+            let data1 = transfer.getData('productPointSet-set');
+            //获取data1中的value,返回一个数组
+            let productPointIdList = data1.map(function(item){
+                return item.value;
+            });
+
+            console.log(productPointIdList)
+        });
+
+
+        form.render();
+
+        initProductTypeSet();
+
+        function initProductTypeSet() {
+            console.log("productTypeAll:", productTypeAll); // 调试信息
+            console.log("productTypeChoose:", productTypeChoose); // 调试信息
+            console.log("chooseId:", chooseId); // 调试信息
+            form.val("product-type-set-form", {
+                "chooseId": chooseId,
+            });
+            // 转换数据格式(假设接口返回的数据结构需要处理)
+            var dataLeft = productTypeAll.map(function(item){
+                return {
+                    value: item.id,  // 值字段
+                    title: item.name // 显示文本
+                }
+            });
+            var dataRight = productTypeChoose.map(function(item){
+                return {
+                    value: item,  // 值字段
+                }
+            });
+
+            // 渲染穿梭框
+            transfer.render({
+                elem: '#productPointSetMove',
+                data: dataLeft,
+                id: 'productPointSet-set', // 唯一标识
+                title: ['待选择列表', '已选择列表'],
+                width: 300,
+                height: 400,
+                showSearch: true,
+                value: productTypeChoose,
+            });
+        }
+
+        form.on('submit(product-type-set-form-submit)', function (data) {
+            let data1 = transfer.getData('productPointSet-set');
+            //获取data1中的value,返回一个数组
+            let productPointIdList = data1.map(function(item){
+                return item.value;
+            });
+            data.field.chooseIds = productPointIdList;
+            data.field.chooseId = chooseId;
+            $.ajax({
+                'url':ctx + 'admin/memberRole/productPointSet',
+                '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-memberRole').find('#query').click();
+                    }else{
+                        febs.alert.warn(data.message);
+                    }
+                },
+                'error':function () {
+                    febs.alert.warn('服务器繁忙');
+                }
+            })
+            return false;
+        });
+
+
+    });
+</script>
\ No newline at end of file
diff --git a/src/main/resources/templates/febs/views/modules/ai/memberRole/productSet.html b/src/main/resources/templates/febs/views/modules/ai/memberRole/productSet.html
index 7e412a3..249c749 100644
--- a/src/main/resources/templates/febs/views/modules/ai/memberRole/productSet.html
+++ b/src/main/resources/templates/febs/views/modules/ai/memberRole/productSet.html
@@ -111,7 +111,7 @@
                     if(data.code==200){
                         layer.closeAll();
                         febs.alert.success(data.message);
-                        $('#febs-type').find('#query').click();
+                        $('#febs-memberRole').find('#query').click();
                     }else{
                         febs.alert.warn(data.message);
                     }

--
Gitblit v1.9.1