Administrator
2025-07-31 2bfe033e2fd8f3d8ee9e99f43df977e661fcbda8
feat(ai): 添加会员角色、产品和产品分类的新增页面

- 新增会员角色添加页面
- 新增产品添加页面
- 新增产品分类添加页面
- 添加相关请求和响应对象
-移除未使用的Service接口和实现类中的冗余方法
31 files modified
19 files added
6094 ■■■■■ changed files
src/main/java/cc/mrbird/febs/ai/controller/memberRole/AiMemberRoleController.java 78 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/controller/memberRole/ViewController.java 102 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/controller/product/AiProductController.java 70 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/controller/product/ViewController.java 47 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/controller/productCategory/AiProductCategoryController.java 76 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/controller/productCategory/ViewController.java 47 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/entity/AiMemberRole.java 5 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/entity/AiProduct.java 4 ●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/req/AdminMoveChooseInfoDto.java 13 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/res/AdminMoveChooseInfoVo.java 11 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/service/AiMemberAnswerItemService.java 90 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/service/AiMemberAnswerService.java 85 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/service/AiMemberRoleCategoryService.java 78 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/service/AiMemberRoleProductService.java 80 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/service/AiMemberRoleService.java 75 ●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/service/AiMemberTalkItemService.java 54 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/service/AiMemberTalkService.java 61 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/service/AiProductCategoryService.java 98 ●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/service/AiProductPointService.java 83 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/service/AiProductQuestionLinkService.java 100 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/service/AiProductQuestionService.java 106 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/service/AiProductRoleService.java 70 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/service/AiProductService.java 97 ●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberAnswerItemServiceImpl.java 123 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberAnswerServiceImpl.java 117 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberRoleCategoryServiceImpl.java 114 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberRoleProductServiceImpl.java 114 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberRoleServiceImpl.java 167 ●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberTalkItemServiceImpl.java 82 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberTalkServiceImpl.java 90 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/service/impl/AiProductCategoryServiceImpl.java 214 ●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/service/impl/AiProductPointServiceImpl.java 121 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/service/impl/AiProductQuestionLinkServiceImpl.java 149 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/service/impl/AiProductQuestionServiceImpl.java 145 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/service/impl/AiProductRoleServiceImpl.java 103 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/service/impl/AiProductServiceImpl.java 223 ●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/util/UUID.java 495 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/common/entity/AiBaseEntity.java 2 ●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/rabbit/consumer/AgentConsumer.java 2 ●●●●● patch | view | raw | blame | history
src/main/resources/application-test.yml 4 ●●●● patch | view | raw | blame | history
src/main/resources/templates/febs/views/modules/ai/memberRole/add.html 139 ●●●●● patch | view | raw | blame | history
src/main/resources/templates/febs/views/modules/ai/memberRole/info.html 167 ●●●●● patch | view | raw | blame | history
src/main/resources/templates/febs/views/modules/ai/memberRole/list.html 246 ●●●●● patch | view | raw | blame | history
src/main/resources/templates/febs/views/modules/ai/memberRole/productSet.html 128 ●●●●● patch | view | raw | blame | history
src/main/resources/templates/febs/views/modules/ai/product/add.html 283 ●●●●● patch | view | raw | blame | history
src/main/resources/templates/febs/views/modules/ai/product/info.html 328 ●●●●● patch | view | raw | blame | history
src/main/resources/templates/febs/views/modules/ai/product/list.html 329 ●●●●● patch | view | raw | blame | history
src/main/resources/templates/febs/views/modules/ai/productCategory/add.html 179 ●●●●● patch | view | raw | blame | history
src/main/resources/templates/febs/views/modules/ai/productCategory/info.html 213 ●●●●● patch | view | raw | blame | history
src/main/resources/templates/febs/views/modules/ai/productCategory/list.html 287 ●●●●● patch | view | raw | blame | history
src/main/java/cc/mrbird/febs/ai/controller/memberRole/AiMemberRoleController.java
New file
@@ -0,0 +1,78 @@
package cc.mrbird.febs.ai.controller.memberRole;
import cc.mrbird.febs.ai.entity.AiMemberRole;
import cc.mrbird.febs.ai.req.AdminMoveChooseInfoDto;
import cc.mrbird.febs.ai.service.AiMemberRoleService;
import cc.mrbird.febs.common.annotation.ControllerEndpoint;
import cc.mrbird.febs.common.controller.BaseController;
import cc.mrbird.febs.common.entity.FebsResponse;
import cc.mrbird.febs.common.entity.QueryRequest;
import cc.mrbird.febs.mall.dto.clothes.AdminClothesTypeInfoDto;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.util.Map;
/**
 * @author Administrator
 */
@Slf4j
@Validated
@RestController
@RequiredArgsConstructor
@RequestMapping(value = "/admin/memberRole")
public class AiMemberRoleController extends BaseController {
    private final AiMemberRoleService aiMemberRoleService;
    @GetMapping("list")
    public FebsResponse list(AiMemberRole dto, QueryRequest request) {
        Map<String, Object> data = getDataTable(aiMemberRoleService.listInPage(dto, request));
        return new FebsResponse().success().data(data);
    }
    @GetMapping("changeState/{id}/{state}")
    @ControllerEndpoint(operation = "状态操作", exceptionMessage = "操作失败")
    public FebsResponse changeState(
            @NotNull(message = "{required}") @PathVariable String id,
            @NotNull(message = "{required}") @PathVariable Integer state
    ) {
        return aiMemberRoleService.changeState(id,state);
    }
    @PostMapping("add")
    @ControllerEndpoint(operation = "新增", exceptionMessage = "操作失败")
    public FebsResponse add(@RequestBody @Valid AiMemberRole dto) {
        return aiMemberRoleService.memberRoleAdd(dto);
    }
    @PostMapping("update")
    @ControllerEndpoint(operation = "分类-更新", exceptionMessage = "操作失败")
    public FebsResponse update(@RequestBody @Valid AiMemberRole dto) {
        return aiMemberRoleService.updateMemberRole(dto);
    }
    @GetMapping("delete/{id}")
    @ControllerEndpoint(operation = "删除", exceptionMessage = "操作失败")
    public FebsResponse delete(
            @NotNull(message = "{required}") @PathVariable String id
    ) {
        return aiMemberRoleService.delete(id);
    }
    @PostMapping("productSet")
    @ControllerEndpoint(operation = "产品配置", exceptionMessage = "操作失败")
    public FebsResponse productSet(@RequestBody @Valid AdminMoveChooseInfoDto dto) {
        return aiMemberRoleService.productSet(dto);
    }
}
src/main/java/cc/mrbird/febs/ai/controller/memberRole/ViewController.java
New file
@@ -0,0 +1,102 @@
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.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.common.entity.FebsConstant;
import cc.mrbird.febs.common.utils.FebsUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
 * @author Administrator
 */
@Controller("AiMemberRoleView")
@RequestMapping(FebsConstant.VIEW_PREFIX + "modules/ai/memberRole")
@RequiredArgsConstructor
public class ViewController {
    private final AiMemberRoleService aiMemberRoleService;
    private final AiMemberRoleProductService aiMemberRoleProductService;
    private final AiProductService aiProductService;
    @GetMapping("list")
    @RequiresPermissions("memberRoleList:view")
    public String memberRoleList() {
        return FebsUtil.view("modules/ai/memberRole/list");
    }
    @GetMapping(value = "/add")
    @RequiresPermissions("memberRoleList:add")
    public String artAdd() {
        return FebsUtil.view("modules/ai/memberRole/add");
    }
    @GetMapping("info/{id}")
    @RequiresPermissions("memberRoleList:info")
    public String artInfo(@PathVariable String id, Model model) {
        AiMemberRole entity = aiMemberRoleService.getById(id);
        model.addAttribute("aiMemberRole", entity);
        return FebsUtil.view("modules/ai/memberRole/info");
    }
    @GetMapping("productSet/{id}")
    @RequiresPermissions("memberRoleList:productSet")
    public String productSet(@PathVariable String id, Model model) {
        List<AdminMoveChooseInfoVo> vos = new ArrayList<>();
        Set<String> productIds = new HashSet<>();
        AiMemberRole entity = aiMemberRoleService.getById(id);
        if(ObjectUtil.isNotNull(entity)){
            //右侧数据
            LambdaQueryWrapper<AiMemberRoleProduct> query = Wrappers.lambdaQuery(AiMemberRoleProduct.class);
            if(StrUtil.isNotEmpty(id)){
                query.eq(AiMemberRoleProduct::getRoleId, id);
            }
            List<AiMemberRoleProduct> selectedList = aiMemberRoleProductService.selectListByQuery(query);
            if(CollUtil.isNotEmpty(selectedList)){
                //stream流操作happyMemberLabelRecords,获取memberId的set集合
                productIds = selectedList.stream().map(AiMemberRoleProduct::getProductId).collect(Collectors.toSet());
            }
            //左侧数据
            List<AiProduct> allList = aiProductService.selectList();
            if(CollUtil.isNotEmpty(allList)){
                //stream流操作mallMembers,生成一个新的List<MallMemberVo>
                vos = allList.stream().map(AiProduct -> {
                    AdminMoveChooseInfoVo vo = new AdminMoveChooseInfoVo();
                    vo.setId(AiProduct.getId());
                    vo.setName(AiProduct.getName());
                    return vo;
                }).collect(Collectors.toList());
            }
        }
        model.addAttribute("productAll", vos);
        model.addAttribute("productSelected", productIds);
        model.addAttribute("roleId", id);
        return FebsUtil.view("modules/ai/memberRole/productSet");
    }
}
src/main/java/cc/mrbird/febs/ai/controller/product/AiProductController.java
New file
@@ -0,0 +1,70 @@
package cc.mrbird.febs.ai.controller.product;
import cc.mrbird.febs.ai.entity.AiProduct;
import cc.mrbird.febs.ai.service.AiProductService;
import cc.mrbird.febs.common.annotation.ControllerEndpoint;
import cc.mrbird.febs.common.controller.BaseController;
import cc.mrbird.febs.common.entity.FebsResponse;
import cc.mrbird.febs.common.entity.QueryRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.util.Map;
/**
 * @author Administrator
 */
@Slf4j
@Validated
@RestController
@RequiredArgsConstructor
@RequestMapping(value = "/admin/product")
public class AiProductController extends BaseController {
    private final AiProductService service;
    @GetMapping("list")
    public FebsResponse list(AiProduct dto, QueryRequest request) {
        Map<String, Object> data = getDataTable(service.listInPage(dto, request));
        return new FebsResponse().success().data(data);
    }
    @GetMapping("changeState/{id}/{type}/{state}")
    @ControllerEndpoint(operation = "状态操作", exceptionMessage = "操作失败")
    public FebsResponse changeState(
            @NotNull(message = "{required}") @PathVariable String id,
            @NotNull(message = "{required}") @PathVariable Integer type,
            @NotNull(message = "{required}") @PathVariable Integer state
    ) {
        return service.changeState(id,type,state);
    }
    @PostMapping("add")
    @ControllerEndpoint(operation = "新增", exceptionMessage = "操作失败")
    public FebsResponse add(@RequestBody @Valid AiProduct dto) {
        return service.add(dto);
    }
    @PostMapping("update")
    @ControllerEndpoint(operation = "分类-更新", exceptionMessage = "操作失败")
    public FebsResponse update(@RequestBody @Valid AiProduct dto) {
        return service.update(dto);
    }
    @GetMapping("delete/{id}")
    @ControllerEndpoint(operation = "删除", exceptionMessage = "操作失败")
    public FebsResponse delete(
            @NotNull(message = "{required}") @PathVariable String id
    ) {
        return service.delete(id);
    }
}
src/main/java/cc/mrbird/febs/ai/controller/product/ViewController.java
New file
@@ -0,0 +1,47 @@
package cc.mrbird.febs.ai.controller.product;
import cc.mrbird.febs.ai.entity.AiProduct;
import cc.mrbird.febs.ai.service.AiProductService;
import cc.mrbird.febs.common.entity.FebsConstant;
import cc.mrbird.febs.common.utils.FebsUtil;
import lombok.RequiredArgsConstructor;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
/**
 * @author Administrator
 */
@Controller("AiProductView")
@RequestMapping(FebsConstant.VIEW_PREFIX + "modules/ai/product")
@RequiredArgsConstructor
public class ViewController {
    private final AiProductService service;
    @GetMapping("list")
    @RequiresPermissions("productList:view")
    public String memberRoleList() {
        return FebsUtil.view("modules/ai/product/list");
    }
    @GetMapping(value = "/add")
    @RequiresPermissions("productList:add")
    public String artAdd() {
        return FebsUtil.view("modules/ai/product/add");
    }
    @GetMapping("info/{id}")
    @RequiresPermissions("productList:info")
    public String artInfo(@PathVariable String id, Model model) {
        AiProduct entity = service.getById(id);
        model.addAttribute("aiProduct", entity);
        return FebsUtil.view("modules/ai/product/info");
    }
}
src/main/java/cc/mrbird/febs/ai/controller/productCategory/AiProductCategoryController.java
New file
@@ -0,0 +1,76 @@
package cc.mrbird.febs.ai.controller.productCategory;
import cc.mrbird.febs.ai.entity.AiProductCategory;
import cc.mrbird.febs.ai.service.AiProductCategoryService;
import cc.mrbird.febs.common.annotation.ControllerEndpoint;
import cc.mrbird.febs.common.controller.BaseController;
import cc.mrbird.febs.common.entity.FebsResponse;
import cc.mrbird.febs.common.entity.QueryRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.util.Map;
/**
 * @author Administrator
 */
@Slf4j
@Validated
@RestController
@RequiredArgsConstructor
@RequestMapping(value = "/admin/productCategory")
public class AiProductCategoryController extends BaseController {
    private final AiProductCategoryService service;
    @GetMapping("list")
    public FebsResponse list(AiProductCategory dto, QueryRequest request) {
        Map<String, Object> data = getDataTable(service.listInPage(dto, request));
        return new FebsResponse().success().data(data);
    }
    @GetMapping("changeState/{id}/{type}/{state}")
    @ControllerEndpoint(operation = "状态操作", exceptionMessage = "操作失败")
    public FebsResponse changeState(
            @NotNull(message = "{required}") @PathVariable String id,
            @NotNull(message = "{required}") @PathVariable Integer type,
            @NotNull(message = "{required}") @PathVariable Integer state
    ) {
        return service.changeState(id,type,state);
    }
    @PostMapping("add")
    @ControllerEndpoint(operation = "新增", exceptionMessage = "操作失败")
    public FebsResponse add(@RequestBody @Valid AiProductCategory dto) {
        return service.add(dto);
    }
    @PostMapping("update")
    @ControllerEndpoint(operation = "分类-更新", exceptionMessage = "操作失败")
    public FebsResponse update(@RequestBody @Valid AiProductCategory dto) {
        return service.update(dto);
    }
    @GetMapping("delete/{id}")
    @ControllerEndpoint(operation = "删除", exceptionMessage = "操作失败")
    public FebsResponse delete(
            @NotNull(message = "{required}") @PathVariable String id
    ) {
        return service.delete(id);
    }
    @GetMapping(value = "/categoryTree")
    public FebsResponse categoryTree() {
        return new FebsResponse().success().data(service.categoryTree());
    }
}
src/main/java/cc/mrbird/febs/ai/controller/productCategory/ViewController.java
New file
@@ -0,0 +1,47 @@
package cc.mrbird.febs.ai.controller.productCategory;
import cc.mrbird.febs.ai.entity.AiProductCategory;
import cc.mrbird.febs.ai.service.AiProductCategoryService;
import cc.mrbird.febs.common.entity.FebsConstant;
import cc.mrbird.febs.common.utils.FebsUtil;
import lombok.RequiredArgsConstructor;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
/**
 * @author Administrator
 */
@Controller("AiProductCategoryView")
@RequestMapping(FebsConstant.VIEW_PREFIX + "modules/ai/productCategory")
@RequiredArgsConstructor
public class ViewController {
    private final AiProductCategoryService service;
    @GetMapping("list")
    @RequiresPermissions("categoryList:view")
    public String memberRoleList() {
        return FebsUtil.view("modules/ai/productCategory/list");
    }
    @GetMapping(value = "/add")
    @RequiresPermissions("categoryList:add")
    public String artAdd() {
        return FebsUtil.view("modules/ai/productCategory/add");
    }
    @GetMapping("info/{id}")
    @RequiresPermissions("categoryList:info")
    public String artInfo(@PathVariable String id, Model model) {
        AiProductCategory entity = service.getById(id);
        model.addAttribute("aiProductCategory", entity);
        return FebsUtil.view("modules/ai/productCategory/info");
    }
}
src/main/java/cc/mrbird/febs/ai/entity/AiMemberRole.java
@@ -38,4 +38,9 @@
     * 状态 0-禁用 1-启用 2-已删除
     */
    private Integer state;
    /**
     * 排序
     */
    private Integer sort;
}
src/main/java/cc/mrbird/febs/ai/entity/AiProduct.java
@@ -1,6 +1,7 @@
package cc.mrbird.febs.ai.entity;
import cc.mrbird.febs.common.entity.AiBaseEntity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@@ -58,6 +59,9 @@
     */
    private String productCategoryId;
    @TableField(exist = false)
    private String productCategoryName;
    /**
     * 场景(可以根据场景生成prompt提示词)
     */
src/main/java/cc/mrbird/febs/ai/req/AdminMoveChooseInfoDto.java
New file
@@ -0,0 +1,13 @@
package cc.mrbird.febs.ai.req;
import lombok.Data;
import java.util.List;
@Data
public class AdminMoveChooseInfoDto {
    private String roleId;
    private List<String> chooseIds;
}
src/main/java/cc/mrbird/febs/ai/res/AdminMoveChooseInfoVo.java
New file
@@ -0,0 +1,11 @@
package cc.mrbird.febs.ai.res;
import lombok.Data;
@Data
public class AdminMoveChooseInfoVo {
    private String id;
    private String name;
}
src/main/java/cc/mrbird/febs/ai/service/AiMemberAnswerItemService.java
@@ -19,95 +19,5 @@
     */
    AiMemberAnswerItem getById(String id);
    /**
     * 根据答题记录ID查询答题记录项列表
     * @param answerId 答题记录ID
     * @return 答题记录项列表
     */
    List<AiMemberAnswerItem> getByAnswerId(String answerId);
    /**
     * 根据用户ID查询答题记录项列表
     * @param memberId 用户ID
     * @return 答题记录项列表
     */
    List<AiMemberAnswerItem> getByMemberId(String memberId);
    /**
     * 根据产品ID查询答题记录项列表
     * @param productId 产品ID
     * @return 答题记录项列表
     */
    List<AiMemberAnswerItem> getByProductId(String productId);
    /**
     * 根据题目父ID查询答题记录项列表
     * @param productQuestionParentId 题目父ID
     * @return 答题记录项列表
     */
    List<AiMemberAnswerItem> getByProductQuestionParentId(String productQuestionParentId);
    /**
     * 根据题目ID查询答题记录项列表
     * @param productQuestionId 题目ID
     * @return 答题记录项列表
     */
    List<AiMemberAnswerItem> getByProductQuestionId(String productQuestionId);
    /**
     * 根据用户回答状态查询答题记录项列表
     * @param memberAnswerState 用户回答状态 0-未回答 1-答对了 2-答错了
     * @return 答题记录项列表
     */
    List<AiMemberAnswerItem> getByMemberAnswerState(Integer memberAnswerState);
    /**
     * 根据是否加入错题集查询答题记录项列表
     * @param isCollected 是否加入错题集:0-否,1-是
     * @return 答题记录项列表
     */
    List<AiMemberAnswerItem> getByIsCollected(Integer isCollected);
    /**
     * 根据公司ID和用户ID查询答题记录项列表
     * @param companyId 公司ID
     * @param memberId 用户ID
     * @return 答题记录项列表
     */
    List<AiMemberAnswerItem> getByCompanyIdAndMemberId(String companyId, String memberId);
    /**
     * 保存答题记录项
     * @param aiMemberAnswerItem 答题记录项实体
     * @return 是否保存成功
     */
    boolean saveAnswerItem(AiMemberAnswerItem aiMemberAnswerItem);
    /**
     * 批量保存答题记录项
     * @param answerItems 答题记录项列表
     * @return 是否保存成功
     */
    boolean saveBatchAnswerItems(List<AiMemberAnswerItem> answerItems);
    /**
     * 更新答题记录项
     * @param aiMemberAnswerItem 答题记录项实体
     * @return 是否更新成功
     */
    boolean updateAnswerItem(AiMemberAnswerItem aiMemberAnswerItem);
    /**
     * 根据ID删除答题记录项
     * @param id 答题记录项ID
     * @return 是否删除成功
     */
    boolean deleteById(String id);
    /**
     * 根据答题记录ID删除答题记录项
     * @param answerId 答题记录ID
     * @return 是否删除成功
     */
    boolean deleteByAnswerId(String answerId);
}
src/main/java/cc/mrbird/febs/ai/service/AiMemberAnswerService.java
@@ -19,90 +19,5 @@
     */
    AiMemberAnswer getById(String id);
    /**
     * 根据用户ID查询答题记录列表
     * @param memberId 用户ID
     * @return 答题记录列表
     */
    List<AiMemberAnswer> getByMemberId(String memberId);
    /**
     * 根据产品ID查询答题记录列表
     * @param productId 产品ID
     * @return 答题记录列表
     */
    List<AiMemberAnswer> getByProductId(String productId);
    /**
     * 根据公司ID查询答题记录列表
     * @param companyId 公司ID
     * @return 答题记录列表
     */
    List<AiMemberAnswer> getByCompanyId(String companyId);
    /**
     * 根据状态查询答题记录列表
     * @param state 状态 0-进行中 1-已结束
     * @return 答题记录列表
     */
    List<AiMemberAnswer> getByState(Integer state);
    /**
     * 根据用户ID和状态查询答题记录列表
     * @param memberId 用户ID
     * @param state 状态 0-进行中 1-已结束
     * @return 答题记录列表
     */
    List<AiMemberAnswer> getByMemberIdAndState(String memberId, Integer state);
    /**
     * 根据公司ID和用户ID查询答题记录列表
     * @param companyId 公司ID
     * @param memberId 用户ID
     * @return 答题记录列表
     */
    List<AiMemberAnswer> getByCompanyIdAndMemberId(String companyId, String memberId);
    /**
     * 根据评分范围查询答题记录列表
     * @param minScore 最低分
     * @param maxScore 最高分
     * @return 答题记录列表
     */
    List<AiMemberAnswer> getByScoreRange(Integer minScore, Integer maxScore);
    /**
     * 保存答题记录
     * @param aiMemberAnswer 答题记录实体
     * @return 是否保存成功
     */
    boolean saveAnswer(AiMemberAnswer aiMemberAnswer);
    /**
     * 批量保存答题记录
     * @param answers 答题记录列表
     * @return 是否保存成功
     */
    boolean saveBatchAnswers(List<AiMemberAnswer> answers);
    /**
     * 更新答题记录
     * @param aiMemberAnswer 答题记录实体
     * @return 是否更新成功
     */
    boolean updateAnswer(AiMemberAnswer aiMemberAnswer);
    /**
     * 根据ID删除答题记录
     * @param id 答题记录ID
     * @return 是否删除成功
     */
    boolean deleteById(String id);
    /**
     * 根据用户ID删除答题记录
     * @param memberId 用户ID
     * @return 是否删除成功
     */
    boolean deleteByMemberId(String memberId);
}
src/main/java/cc/mrbird/febs/ai/service/AiMemberRoleCategoryService.java
@@ -19,82 +19,4 @@
     */
    AiMemberRoleCategory getById(String id);
    /**
     * 根据角色ID查询关联记录
     * @param roleId 角色ID
     * @return 关联记录列表
     */
    List<AiMemberRoleCategory> getByRoleId(String roleId);
    /**
     * 根据产品分类ID查询关联记录
     * @param productCategoryId 产品分类ID
     * @return 关联记录列表
     */
    List<AiMemberRoleCategory> getByProductCategoryId(String productCategoryId);
    /**
     * 根据公司ID查询关联记录
     * @param companyId 公司ID
     * @return 关联记录列表
     */
    List<AiMemberRoleCategory> getByCompanyId(String companyId);
    /**
     * 根据角色ID和产品分类ID查询关联记录
     * @param roleId 角色ID
     * @param productCategoryId 产品分类ID
     * @return 关联记录列表
     */
    List<AiMemberRoleCategory> getByRoleIdAndProductCategoryId(String roleId, String productCategoryId);
    /**
     * 保存关联记录
     * @param aiMemberRoleCategory 关联记录实体
     * @return 是否保存成功
     */
    boolean saveRoleCategory(AiMemberRoleCategory aiMemberRoleCategory);
    /**
     * 批量保存关联记录
     * @param roleCategories 关联记录列表
     * @return 是否保存成功
     */
    boolean saveBatchRoleCategories(List<AiMemberRoleCategory> roleCategories);
    /**
     * 更新关联记录
     * @param aiMemberRoleCategory 关联记录实体
     * @return 是否更新成功
     */
    boolean updateRoleCategory(AiMemberRoleCategory aiMemberRoleCategory);
    /**
     * 根据ID删除关联记录
     * @param id 关联记录ID
     * @return 是否删除成功
     */
    boolean deleteById(String id);
    /**
     * 根据角色ID删除关联记录
     * @param roleId 角色ID
     * @return 是否删除成功
     */
    boolean deleteByRoleId(String roleId);
    /**
     * 根据产品分类ID删除关联记录
     * @param productCategoryId 产品分类ID
     * @return 是否删除成功
     */
    boolean deleteByProductCategoryId(String productCategoryId);
    /**
     * 根据角色ID和产品分类ID删除关联记录
     * @param roleId 角色ID
     * @param productCategoryId 产品分类ID
     * @return 是否删除成功
     */
    boolean deleteByRoleIdAndProductCategoryId(String roleId, String productCategoryId);
}
src/main/java/cc/mrbird/febs/ai/service/AiMemberRoleProductService.java
@@ -1,6 +1,7 @@
package cc.mrbird.febs.ai.service;
import cc.mrbird.febs.ai.entity.AiMemberRoleProduct;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
@@ -19,82 +20,7 @@
     */
    AiMemberRoleProduct getById(String id);
    /**
     * 根据角色ID查询关联记录
     * @param roleId 角色ID
     * @return 关联记录列表
     */
    List<AiMemberRoleProduct> getByRoleId(String roleId);
    List<AiMemberRoleProduct> selectListByQuery(LambdaQueryWrapper<AiMemberRoleProduct> query);
    /**
     * 根据产品ID查询关联记录
     * @param productId 产品ID
     * @return 关联记录列表
     */
    List<AiMemberRoleProduct> getByProductId(String productId);
    /**
     * 根据公司ID查询关联记录
     * @param companyId 公司ID
     * @return 关联记录列表
     */
    List<AiMemberRoleProduct> getByCompanyId(String companyId);
    /**
     * 根据角色ID和产品ID查询关联记录
     * @param roleId 角色ID
     * @param productId 产品ID
     * @return 关联记录列表
     */
    List<AiMemberRoleProduct> getByRoleIdAndProductId(String roleId, String productId);
    /**
     * 保存关联记录
     * @param aiMemberRoleProduct 关联记录实体
     * @return 是否保存成功
     */
    boolean saveRoleProduct(AiMemberRoleProduct aiMemberRoleProduct);
    /**
     * 批量保存关联记录
     * @param roleProducts 关联记录列表
     * @return 是否保存成功
     */
    boolean saveBatchRoleProducts(List<AiMemberRoleProduct> roleProducts);
    /**
     * 更新关联记录
     * @param aiMemberRoleProduct 关联记录实体
     * @return 是否更新成功
     */
    boolean updateRoleProduct(AiMemberRoleProduct aiMemberRoleProduct);
    /**
     * 根据ID删除关联记录
     * @param id 关联记录ID
     * @return 是否删除成功
     */
    boolean deleteById(String id);
    /**
     * 根据角色ID删除关联记录
     * @param roleId 角色ID
     * @return 是否删除成功
     */
    boolean deleteByRoleId(String roleId);
    /**
     * 根据产品ID删除关联记录
     * @param productId 产品ID
     * @return 是否删除成功
     */
    boolean deleteByProductId(String productId);
    /**
     * 根据角色ID和产品ID删除关联记录
     * @param roleId 角色ID
     * @param productId 产品ID
     * @return 是否删除成功
     */
    boolean deleteByRoleIdAndProductId(String roleId, String productId);
    void deleteByQuery(LambdaQueryWrapper<AiMemberRoleProduct> query);
}
src/main/java/cc/mrbird/febs/ai/service/AiMemberRoleService.java
@@ -1,6 +1,10 @@
package cc.mrbird.febs.ai.service;
import cc.mrbird.febs.ai.entity.AiMemberRole;
import cc.mrbird.febs.ai.req.AdminMoveChooseInfoDto;
import cc.mrbird.febs.common.entity.FebsResponse;
import cc.mrbird.febs.common.entity.QueryRequest;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
@@ -19,74 +23,15 @@
     */
    AiMemberRole getById(String id);
    /**
     * 根据公司ID查询角色列表
     * @param companyId 公司ID
     * @return 角色列表
     */
    List<AiMemberRole> getByCompanyId(String companyId);
    IPage<AiMemberRole> listInPage(AiMemberRole dto, QueryRequest request);
    /**
     * 根据编码查询角色
     * @param code 角色编码
     * @return 角色信息
     */
    AiMemberRole getByCode(String code);
    FebsResponse changeState(String id, Integer state);
    /**
     * 根据名称查询角色列表
     * @param name 角色名称
     * @return 角色列表
     */
    List<AiMemberRole> getByName(String name);
    FebsResponse memberRoleAdd(AiMemberRole dto);
    /**
     * 根据状态查询角色列表
     * @param state 状态 0-禁用 1-启用 2-已删除
     * @return 角色列表
     */
    List<AiMemberRole> getByState(Integer state);
    FebsResponse updateMemberRole(AiMemberRole dto);
    /**
     * 根据公司ID和状态查询角色列表
     * @param companyId 公司ID
     * @param state 状态 0-禁用 1-启用 2-已删除
     * @return 角色列表
     */
    List<AiMemberRole> getByCompanyIdAndState(String companyId, Integer state);
    FebsResponse delete(String id);
    /**
     * 保存角色
     * @param aiMemberRole 角色实体
     * @return 是否保存成功
     */
    boolean saveRole(AiMemberRole aiMemberRole);
    /**
     * 批量保存角色
     * @param roles 角色列表
     * @return 是否保存成功
     */
    boolean saveBatchRoles(List<AiMemberRole> roles);
    /**
     * 更新角色
     * @param aiMemberRole 角色实体
     * @return 是否更新成功
     */
    boolean updateRole(AiMemberRole aiMemberRole);
    /**
     * 根据ID删除角色
     * @param id 角色ID
     * @return 是否删除成功
     */
    boolean deleteById(String id);
    /**
     * 根据公司ID删除角色
     * @param companyId 公司ID
     * @return 是否删除成功
     */
    boolean deleteByCompanyId(String companyId);
    FebsResponse productSet(AdminMoveChooseInfoDto dto);
}
src/main/java/cc/mrbird/febs/ai/service/AiMemberTalkItemService.java
@@ -19,59 +19,5 @@
     */
    AiMemberTalkItem getById(String id);
    /**
     * 根据对话ID查询所有对话项
     * @param memberTalkId 对话ID
     * @return 对话项列表
     */
    List<AiMemberTalkItem> getByMemberTalkId(String memberTalkId);
    /**
     * 根据用户ID查询对话项
     * @param memberId 用户ID
     * @return 对话项列表
     */
    List<AiMemberTalkItem> getByMemberId(String memberId);
    /**
     * 根据类型查询对话项
     * @param type 类型 1-AI提问 2-用户回答 3-AI分析结果
     * @return 对话项列表
     */
    List<AiMemberTalkItem> getByType(Integer type);
    /**
     * 保存对话项
     * @param aiMemberTalkItem 对话项实体
     * @return 是否保存成功
     */
    boolean saveTalkItem(AiMemberTalkItem aiMemberTalkItem);
    /**
     * 批量保存对话项
     * @param talkItems 对话项列表
     * @return 是否保存成功
     */
    boolean saveBatchTalkItems(List<AiMemberTalkItem> talkItems);
    /**
     * 更新对话项
     * @param aiMemberTalkItem 对话项实体
     * @return 是否更新成功
     */
    boolean updateTalkItem(AiMemberTalkItem aiMemberTalkItem);
    /**
     * 根据ID删除对话项
     * @param id 对话项ID
     * @return 是否删除成功
     */
    boolean deleteById(String id);
    /**
     * 根据对话ID删除对话项
     * @param memberTalkId 对话ID
     * @return 是否删除成功
     */
    boolean deleteByMemberTalkId(String memberTalkId);
}
src/main/java/cc/mrbird/febs/ai/service/AiMemberTalkService.java
@@ -19,66 +19,5 @@
     */
    AiMemberTalk getById(String id);
    /**
     * 根据用户ID查询对话记录
     * @param memberId 用户ID
     * @return 对话记录列表
     */
    List<AiMemberTalk> getByMemberId(String memberId);
    /**
     * 根据产品ID查询对话记录
     * @param productId 产品ID
     * @return 对话记录列表
     */
    List<AiMemberTalk> getByProductId(String productId);
    /**
     * 根据角色ID查询对话记录
     * @param productRoleId 角色ID
     * @return 对话记录列表
     */
    List<AiMemberTalk> getByProductRoleId(String productRoleId);
    /**
     * 根据公司ID查询对话记录
     * @param companyId 公司ID
     * @return 对话记录列表
     */
    List<AiMemberTalk> getByCompanyId(String companyId);
    /**
     * 保存对话记录
     * @param aiMemberTalk 对话记录实体
     * @return 是否保存成功
     */
    boolean saveTalk(AiMemberTalk aiMemberTalk);
    /**
     * 批量保存对话记录
     * @param talks 对话记录列表
     * @return 是否保存成功
     */
    boolean saveBatchTalks(List<AiMemberTalk> talks);
    /**
     * 更新对话记录
     * @param aiMemberTalk 对话记录实体
     * @return 是否更新成功
     */
    boolean updateTalk(AiMemberTalk aiMemberTalk);
    /**
     * 根据ID删除对话记录
     * @param id 对话记录ID
     * @return 是否删除成功
     */
    boolean deleteById(String id);
    /**
     * 根据用户ID删除对话记录
     * @param memberId 用户ID
     * @return 是否删除成功
     */
    boolean deleteByMemberId(String memberId);
}
src/main/java/cc/mrbird/febs/ai/service/AiProductCategoryService.java
@@ -1,8 +1,14 @@
package cc.mrbird.febs.ai.service;
import cc.mrbird.febs.ai.entity.AiMemberRole;
import cc.mrbird.febs.ai.entity.AiProductCategory;
import cc.mrbird.febs.common.entity.FebsResponse;
import cc.mrbird.febs.common.entity.QueryRequest;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
 * AI产品类别 Service接口
@@ -19,95 +25,19 @@
     */
    AiProductCategory getById(String id);
    /**
     * 根据公司ID查询AI产品类别列表
     * @param companyId 公司ID
     * @return AI产品类别列表
     */
    List<AiProductCategory> getByCompanyId(String companyId);
    List<AiProductCategory> getList();
    /**
     * 根据编码查询AI产品类别
     * @param code 类别编码
     * @return AI产品类别信息
     */
    AiProductCategory getByCode(String code);
    Map<String, AiProductCategory> selectMapByIds(Set<String> collect);
    /**
     * 根据名称查询AI产品类别列表
     * @param name 类别名称
     * @return AI产品类别列表
     */
    List<AiProductCategory> getByName(String name);
    IPage<AiProductCategory> listInPage(AiProductCategory dto, QueryRequest request);
    /**
     * 根据状态查询AI产品类别列表
     * @param state 状态 0-禁用 1-启用 2-已删除
     * @return AI产品类别列表
     */
    List<AiProductCategory> getByState(Integer state);
    FebsResponse changeState(String id, Integer type, Integer state);
    /**
     * 根据是否推荐查询AI产品类别列表
     * @param hotState 是否推荐到首页 1-推荐 0-不推荐
     * @return AI产品类别列表
     */
    List<AiProductCategory> getByHotState(Integer hotState);
    FebsResponse add(AiProductCategory dto);
    /**
     * 根据公司ID和状态查询AI产品类别列表
     * @param companyId 公司ID
     * @param state 状态 0-禁用 1-启用 2-已删除
     * @return AI产品类别列表
     */
    List<AiProductCategory> getByCompanyIdAndState(String companyId, Integer state);
    FebsResponse update(AiProductCategory dto);
    /**
     * 根据公司ID和推荐状态查询AI产品类别列表
     * @param companyId 公司ID
     * @param hotState 是否推荐到首页 1-推荐 0-不推荐
     * @return AI产品类别列表
     */
    List<AiProductCategory> getByCompanyIdAndHotState(String companyId, Integer hotState);
    FebsResponse delete(String id);
    /**
     * 获取启用状态下的所有AI产品类别,按排序字段升序排列
     * @return 启用的AI产品类别列表
     */
    List<AiProductCategory> getEnabledCategories();
    /**
     * 保存AI产品类别
     * @param aiProductCategory AI产品类别实体
     * @return 是否保存成功
     */
    boolean saveProductCategory(AiProductCategory aiProductCategory);
    /**
     * 批量保存AI产品类别
     * @param productCategories AI产品类别列表
     * @return 是否保存成功
     */
    boolean saveBatchProductCategories(List<AiProductCategory> productCategories);
    /**
     * 更新AI产品类别
     * @param aiProductCategory AI产品类别实体
     * @return 是否更新成功
     */
    boolean updateProductCategory(AiProductCategory aiProductCategory);
    /**
     * 根据ID删除AI产品类别
     * @param id 类别ID
     * @return 是否删除成功
     */
    boolean deleteById(String id);
    /**
     * 根据公司ID删除AI产品类别
     * @param companyId 公司ID
     * @return 是否删除成功
     */
    boolean deleteByCompanyId(String companyId);
    List<AiProductCategory> categoryTree();
}
src/main/java/cc/mrbird/febs/ai/service/AiProductPointService.java
@@ -19,88 +19,5 @@
     */
    AiProductPoint getById(String id);
    /**
     * 根据公司ID查询AI产品知识点列表
     * @param companyId 公司ID
     * @return AI产品知识点列表
     */
    List<AiProductPoint> getByCompanyId(String companyId);
    /**
     * 根据类型查询AI产品知识点列表
     * @param isNormal 类型1:普通内容 2:视频号内容
     * @return AI产品知识点列表
     */
    List<AiProductPoint> getByIsNormal(Integer isNormal);
    /**
     * 根据视频号用户名查询AI产品知识点列表
     * @param finderUserName 视频号用户名
     * @return AI产品知识点列表
     */
    List<AiProductPoint> getByFinderUserName(String finderUserName);
    /**
     * 根据标题查询AI产品知识点列表
     * @param title 标题
     * @return AI产品知识点列表
     */
    List<AiProductPoint> getByTitle(String title);
    /**
     * 根据公司ID和类型查询AI产品知识点列表
     * @param companyId 公司ID
     * @param isNormal 类型1:普通内容 2:视频号内容
     * @return AI产品知识点列表
     */
    List<AiProductPoint> getByCompanyIdAndIsNormal(String companyId, Integer isNormal);
    /**
     * 模糊查询标题或描述包含关键字的AI产品知识点列表
     * @param keyword 关键字
     * @return AI产品知识点列表
     */
    List<AiProductPoint> searchByKeyword(String keyword);
    /**
     * 保存AI产品知识点
     * @param aiProductPoint AI产品知识点实体
     * @return 是否保存成功
     */
    boolean saveProductPoint(AiProductPoint aiProductPoint);
    /**
     * 批量保存AI产品知识点
     * @param productPoints AI产品知识点列表
     * @return 是否保存成功
     */
    boolean saveBatchProductPoints(List<AiProductPoint> productPoints);
    /**
     * 更新AI产品知识点
     * @param aiProductPoint AI产品知识点实体
     * @return 是否更新成功
     */
    boolean updateProductPoint(AiProductPoint aiProductPoint);
    /**
     * 根据ID删除AI产品知识点
     * @param id 知识点ID
     * @return 是否删除成功
     */
    boolean deleteById(String id);
    /**
     * 根据公司ID删除AI产品知识点
     * @param companyId 公司ID
     * @return 是否删除成功
     */
    boolean deleteByCompanyId(String companyId);
    /**
     * 根据视频号FeedID删除AI产品知识点
     * @param feedId 视频号FeedID
     * @return 是否删除成功
     */
    boolean deleteByFeedId(String feedId);
}
src/main/java/cc/mrbird/febs/ai/service/AiProductQuestionLinkService.java
@@ -19,105 +19,5 @@
     */
    AiProductQuestionLink getById(String id);
    /**
     * 根据公司ID查询产品题目关系列表
     * @param companyId 公司ID
     * @return 产品题目关系列表
     */
    List<AiProductQuestionLink> getByCompanyId(String companyId);
    /**
     * 根据产品题目ID查询产品题目关系列表
     * @param productQuestionId 产品题目ID
     * @return 产品题目关系列表
     */
    List<AiProductQuestionLink> getByProductQuestionId(String productQuestionId);
    /**
     * 根据产品ID查询产品题目关系列表
     * @param productId 产品ID
     * @return 产品题目关系列表
     */
    List<AiProductQuestionLink> getByProductId(String productId);
    /**
     * 根据公司ID和产品ID查询产品题目关系列表
     * @param companyId 公司ID
     * @param productId 产品ID
     * @return 产品题目关系列表
     */
    List<AiProductQuestionLink> getByCompanyIdAndProductId(String companyId, String productId);
    /**
     * 根据公司ID和产品题目ID查询产品题目关系列表
     * @param companyId 公司ID
     * @param productQuestionId 产品题目ID
     * @return 产品题目关系列表
     */
    List<AiProductQuestionLink> getByCompanyIdAndProductQuestionId(String companyId, String productQuestionId);
    /**
     * 根据产品ID和产品题目ID查询产品题目关系列表
     * @param productId 产品ID
     * @param productQuestionId 产品题目ID
     * @return 产品题目关系列表
     */
    List<AiProductQuestionLink> getByProductIdAndProductQuestionId(String productId, String productQuestionId);
    /**
     * 保存产品题目关系
     * @param aiProductQuestionLink 产品题目关系实体
     * @return 是否保存成功
     */
    boolean saveProductQuestionLink(AiProductQuestionLink aiProductQuestionLink);
    /**
     * 批量保存产品题目关系
     * @param productQuestionLinks 产品题目关系列表
     * @return 是否保存成功
     */
    boolean saveBatchProductQuestionLinks(List<AiProductQuestionLink> productQuestionLinks);
    /**
     * 更新产品题目关系
     * @param aiProductQuestionLink 产品题目关系实体
     * @return 是否更新成功
     */
    boolean updateProductQuestionLink(AiProductQuestionLink aiProductQuestionLink);
    /**
     * 根据ID删除产品题目关系
     * @param id 关系ID
     * @return 是否删除成功
     */
    boolean deleteById(String id);
    /**
     * 根据产品ID删除产品题目关系
     * @param productId 产品ID
     * @return 是否删除成功
     */
    boolean deleteByProductId(String productId);
    /**
     * 根据产品题目ID删除产品题目关系
     * @param productQuestionId 产品题目ID
     * @return 是否删除成功
     */
    boolean deleteByProductQuestionId(String productQuestionId);
    /**
     * 根据公司ID删除产品题目关系
     * @param companyId 公司ID
     * @return 是否删除成功
     */
    boolean deleteByCompanyId(String companyId);
    /**
     * 根据产品ID和产品题目ID删除产品题目关系
     * @param productId 产品ID
     * @param productQuestionId 产品题目ID
     * @return 是否删除成功
     */
    boolean deleteByProductIdAndProductQuestionId(String productId, String productQuestionId);
}
src/main/java/cc/mrbird/febs/ai/service/AiProductQuestionService.java
@@ -19,110 +19,4 @@
     */
    AiProductQuestion getById(String id);
    /**
     * 根据公司ID查询AI产品题目列表
     * @param companyId 公司ID
     * @return AI产品题目列表
     */
    List<AiProductQuestion> getByCompanyId(String companyId);
    /**
     * 根据AI产品类别ID查询AI产品题目列表
     * @param productCategoryId AI产品类别ID
     * @return AI产品题目列表
     */
    List<AiProductQuestion> getByProductCategoryId(String productCategoryId);
    /**
     * 根据题目查询AI产品题目列表
     * @param title 题目
     * @return AI产品题目列表
     */
    List<AiProductQuestion> getByTitle(String title);
    /**
     * 根据父ID查询AI产品题目列表
     * @param parentId 父ID
     * @return AI产品题目列表
     */
    List<AiProductQuestion> getByParentId(String parentId);
    /**
     * 根据难度查询AI产品题目列表
     * @param difficulty 难度:1-简单,2-中等,3-困难
     * @return AI产品题目列表
     */
    List<AiProductQuestion> getByDifficulty(Integer difficulty);
    /**
     * 根据状态查询AI产品题目列表
     * @param state 状态 0-禁用 1-启用 2-已删除
     * @return AI产品题目列表
     */
    List<AiProductQuestion> getByState(Integer state);
    /**
     * 根据是否正确答案查询AI产品题目列表
     * @param correctAnswer 是否是正确答案 0-否 1-是
     * @return AI产品题目列表
     */
    List<AiProductQuestion> getByCorrectAnswer(Integer correctAnswer);
    /**
     * 根据公司ID和状态查询AI产品题目列表
     * @param companyId 公司ID
     * @param state 状态 0-禁用 1-启用 2-已删除
     * @return AI产品题目列表
     */
    List<AiProductQuestion> getByCompanyIdAndState(String companyId, Integer state);
    /**
     * 根据父ID和状态查询AI产品题目列表(获取同一题目的所有选项)
     * @param parentId 父ID
     * @param state 状态 0-禁用 1-启用 2-已删除
     * @return AI产品题目列表
     */
    List<AiProductQuestion> getByParentIdAndState(String parentId, Integer state);
    /**
     * 保存AI产品题目
     * @param aiProductQuestion AI产品题目实体
     * @return 是否保存成功
     */
    boolean saveProductQuestion(AiProductQuestion aiProductQuestion);
    /**
     * 批量保存AI产品题目
     * @param productQuestions AI产品题目列表
     * @return 是否保存成功
     */
    boolean saveBatchProductQuestions(List<AiProductQuestion> productQuestions);
    /**
     * 更新AI产品题目
     * @param aiProductQuestion AI产品题目实体
     * @return 是否更新成功
     */
    boolean updateProductQuestion(AiProductQuestion aiProductQuestion);
    /**
     * 根据ID删除AI产品题目
     * @param id 题目ID
     * @return 是否删除成功
     */
    boolean deleteById(String id);
    /**
     * 根据父ID删除AI产品题目(删除整个题目及其选项)
     * @param parentId 父ID
     * @return 是否删除成功
     */
    boolean deleteByParentId(String parentId);
    /**
     * 根据公司ID删除AI产品题目
     * @param companyId 公司ID
     * @return 是否删除成功
     */
    boolean deleteByCompanyId(String companyId);
}
src/main/java/cc/mrbird/febs/ai/service/AiProductRoleService.java
@@ -19,74 +19,4 @@
     */
    AiProductRole getById(String id);
    /**
     * 根据公司ID查询AI产品角色列表
     * @param companyId 公司ID
     * @return AI产品角色列表
     */
    List<AiProductRole> getByCompanyId(String companyId);
    /**
     * 根据AI产品ID查询AI产品角色列表
     * @param productId AI产品ID
     * @return AI产品角色列表
     */
    List<AiProductRole> getByProductId(String productId);
    /**
     * 根据名称查询AI产品角色列表
     * @param name 角色名称
     * @return AI产品角色列表
     */
    List<AiProductRole> getByName(String name);
    /**
     * 根据公司ID和产品ID查询AI产品角色列表
     * @param companyId 公司ID
     * @param productId AI产品ID
     * @return AI产品角色列表
     */
    List<AiProductRole> getByCompanyIdAndProductId(String companyId, String productId);
    /**
     * 保存AI产品角色
     * @param aiProductRole AI产品角色实体
     * @return 是否保存成功
     */
    boolean saveProductRole(AiProductRole aiProductRole);
    /**
     * 批量保存AI产品角色
     * @param productRoles AI产品角色列表
     * @return 是否保存成功
     */
    boolean saveBatchProductRoles(List<AiProductRole> productRoles);
    /**
     * 更新AI产品角色
     * @param aiProductRole AI产品角色实体
     * @return 是否更新成功
     */
    boolean updateProductRole(AiProductRole aiProductRole);
    /**
     * 根据ID删除AI产品角色
     * @param id 角色ID
     * @return 是否删除成功
     */
    boolean deleteById(String id);
    /**
     * 根据AI产品ID删除AI产品角色
     * @param productId AI产品ID
     * @return 是否删除成功
     */
    boolean deleteByProductId(String productId);
    /**
     * 根据公司ID删除AI产品角色
     * @param companyId 公司ID
     * @return 是否删除成功
     */
    boolean deleteByCompanyId(String companyId);
}
src/main/java/cc/mrbird/febs/ai/service/AiProductService.java
@@ -1,6 +1,10 @@
package cc.mrbird.febs.ai.service;
import cc.mrbird.febs.ai.entity.AiProduct;
import cc.mrbird.febs.common.entity.FebsResponse;
import cc.mrbird.febs.common.entity.QueryRequest;
import cc.mrbird.febs.mall.entity.ClothesArt;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
@@ -19,96 +23,15 @@
     */
    AiProduct getById(String id);
    /**
     * 根据公司ID查询AI产品列表
     * @param companyId 公司ID
     * @return AI产品列表
     */
    List<AiProduct> getByCompanyId(String companyId);
    IPage<AiProduct> listInPage(AiProduct dto, QueryRequest request);
    /**
     * 根据编码查询AI产品
     * @param code 产品编码
     * @return AI产品信息
     */
    AiProduct getByCode(String code);
    FebsResponse changeState(String id, Integer type, Integer state);
    /**
     * 根据名称查询AI产品列表
     * @param name 产品名称
     * @return AI产品列表
     */
    List<AiProduct> getByName(String name);
    FebsResponse add(AiProduct dto);
    /**
     * 根据产品类别ID查询AI产品列表
     * @param productCategoryId 产品类别ID
     * @return AI产品列表
     */
    List<AiProduct> getByProductCategoryId(String productCategoryId);
    FebsResponse update(AiProduct dto);
    /**
     * 根据状态查询AI产品列表
     * @param state 状态 0-禁用 1-启用 2-已删除
     * @return AI产品列表
     */
    List<AiProduct> getByState(Integer state);
    FebsResponse delete(String id);
    /**
     * 根据是否推荐查询AI产品列表
     * @param hotState 是否推荐到首页 1-推荐 0-不推荐
     * @return AI产品列表
     */
    List<AiProduct> getByHotState(Integer hotState);
    /**
     * 根据公司ID和状态查询AI产品列表
     * @param companyId 公司ID
     * @param state 状态 0-禁用 1-启用 2-已删除
     * @return AI产品列表
     */
    List<AiProduct> getByCompanyIdAndState(String companyId, Integer state);
    /**
     * 根据公司ID和推荐状态查询AI产品列表
     * @param companyId 公司ID
     * @param hotState 是否推荐到首页 1-推荐 0-不推荐
     * @return AI产品列表
     */
    List<AiProduct> getByCompanyIdAndHotState(String companyId, Integer hotState);
    /**
     * 保存AI产品
     * @param aiProduct AI产品实体
     * @return 是否保存成功
     */
    boolean saveProduct(AiProduct aiProduct);
    /**
     * 批量保存AI产品
     * @param products AI产品列表
     * @return 是否保存成功
     */
    boolean saveBatchProducts(List<AiProduct> products);
    /**
     * 更新AI产品
     * @param aiProduct AI产品实体
     * @return 是否更新成功
     */
    boolean updateProduct(AiProduct aiProduct);
    /**
     * 根据ID删除AI产品
     * @param id 产品ID
     * @return 是否删除成功
     */
    boolean deleteById(String id);
    /**
     * 根据公司ID删除AI产品
     * @param companyId 公司ID
     * @return 是否删除成功
     */
    boolean deleteByCompanyId(String companyId);
    List<AiProduct> selectList();
}
src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberAnswerItemServiceImpl.java
@@ -27,128 +27,7 @@
    @Override
    public AiMemberAnswerItem getById(String id) {
        return this.getById(id);
        return aiMemberAnswerItemMapper.selectById(id);
    }
    @Override
    public List<AiMemberAnswerItem> getByAnswerId(String answerId) {
        LambdaQueryWrapper<AiMemberAnswerItem> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiMemberAnswerItem::getAnswerId, answerId);
        queryWrapper.orderByAsc(AiMemberAnswerItem::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiMemberAnswerItem> getByMemberId(String memberId) {
        LambdaQueryWrapper<AiMemberAnswerItem> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiMemberAnswerItem::getMemberId, memberId);
        queryWrapper.orderByDesc(AiMemberAnswerItem::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiMemberAnswerItem> getByProductId(String productId) {
        LambdaQueryWrapper<AiMemberAnswerItem> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiMemberAnswerItem::getProductId, productId);
        queryWrapper.orderByDesc(AiMemberAnswerItem::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiMemberAnswerItem> getByProductQuestionParentId(String productQuestionParentId) {
        LambdaQueryWrapper<AiMemberAnswerItem> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiMemberAnswerItem::getProductQuestionParentId, productQuestionParentId);
        queryWrapper.orderByAsc(AiMemberAnswerItem::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiMemberAnswerItem> getByProductQuestionId(String productQuestionId) {
        LambdaQueryWrapper<AiMemberAnswerItem> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiMemberAnswerItem::getProductQuestionId, productQuestionId);
        queryWrapper.orderByAsc(AiMemberAnswerItem::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiMemberAnswerItem> getByMemberAnswerState(Integer memberAnswerState) {
        LambdaQueryWrapper<AiMemberAnswerItem> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiMemberAnswerItem::getMemberAnswerState, memberAnswerState);
        queryWrapper.orderByDesc(AiMemberAnswerItem::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiMemberAnswerItem> getByIsCollected(Integer isCollected) {
        LambdaQueryWrapper<AiMemberAnswerItem> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiMemberAnswerItem::getIsCollected, isCollected);
        queryWrapper.orderByDesc(AiMemberAnswerItem::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiMemberAnswerItem> getByCompanyIdAndMemberId(String companyId, String memberId) {
        LambdaQueryWrapper<AiMemberAnswerItem> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiMemberAnswerItem::getCompanyId, companyId);
        queryWrapper.eq(AiMemberAnswerItem::getMemberId, memberId);
        queryWrapper.orderByDesc(AiMemberAnswerItem::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveAnswerItem(AiMemberAnswerItem aiMemberAnswerItem) {
        try {
            return this.save(aiMemberAnswerItem);
        } catch (Exception e) {
            log.error("保存答题记录项失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveBatchAnswerItems(List<AiMemberAnswerItem> answerItems) {
        try {
            return this.saveBatch(answerItems);
        } catch (Exception e) {
            log.error("批量保存答题记录项失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateAnswerItem(AiMemberAnswerItem aiMemberAnswerItem) {
        try {
            return this.updateById(aiMemberAnswerItem);
        } catch (Exception e) {
            log.error("更新答题记录项失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteById(String id) {
        try {
            return this.removeById(id);
        } catch (Exception e) {
            log.error("删除答题记录项失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteByAnswerId(String answerId) {
        try {
            LambdaQueryWrapper<AiMemberAnswerItem> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(AiMemberAnswerItem::getAnswerId, answerId);
            return this.remove(queryWrapper);
        } catch (Exception e) {
            log.error("根据答题记录ID删除答题记录项失败: ", e);
            return false;
        }
    }
}
src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberAnswerServiceImpl.java
@@ -27,122 +27,7 @@
    @Override
    public AiMemberAnswer getById(String id) {
        return this.getById(id);
        return aiMemberAnswerMapper.selectById(id);
    }
    @Override
    public List<AiMemberAnswer> getByMemberId(String memberId) {
        LambdaQueryWrapper<AiMemberAnswer> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiMemberAnswer::getMemberId, memberId);
        queryWrapper.orderByDesc(AiMemberAnswer::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiMemberAnswer> getByProductId(String productId) {
        LambdaQueryWrapper<AiMemberAnswer> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiMemberAnswer::getProductId, productId);
        queryWrapper.orderByDesc(AiMemberAnswer::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiMemberAnswer> getByCompanyId(String companyId) {
        LambdaQueryWrapper<AiMemberAnswer> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiMemberAnswer::getCompanyId, companyId);
        queryWrapper.orderByDesc(AiMemberAnswer::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiMemberAnswer> getByState(Integer state) {
        LambdaQueryWrapper<AiMemberAnswer> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiMemberAnswer::getState, state);
        queryWrapper.orderByDesc(AiMemberAnswer::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiMemberAnswer> getByMemberIdAndState(String memberId, Integer state) {
        LambdaQueryWrapper<AiMemberAnswer> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiMemberAnswer::getMemberId, memberId);
        queryWrapper.eq(AiMemberAnswer::getState, state);
        queryWrapper.orderByDesc(AiMemberAnswer::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiMemberAnswer> getByCompanyIdAndMemberId(String companyId, String memberId) {
        LambdaQueryWrapper<AiMemberAnswer> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiMemberAnswer::getCompanyId, companyId);
        queryWrapper.eq(AiMemberAnswer::getMemberId, memberId);
        queryWrapper.orderByDesc(AiMemberAnswer::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiMemberAnswer> getByScoreRange(Integer minScore, Integer maxScore) {
        LambdaQueryWrapper<AiMemberAnswer> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.ge(AiMemberAnswer::getScore, minScore);
        queryWrapper.le(AiMemberAnswer::getScore, maxScore);
        queryWrapper.orderByDesc(AiMemberAnswer::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveAnswer(AiMemberAnswer aiMemberAnswer) {
        try {
            return this.save(aiMemberAnswer);
        } catch (Exception e) {
            log.error("保存答题记录失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveBatchAnswers(List<AiMemberAnswer> answers) {
        try {
            return this.saveBatch(answers);
        } catch (Exception e) {
            log.error("批量保存答题记录失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateAnswer(AiMemberAnswer aiMemberAnswer) {
        try {
            return this.updateById(aiMemberAnswer);
        } catch (Exception e) {
            log.error("更新答题记录失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteById(String id) {
        try {
            return this.removeById(id);
        } catch (Exception e) {
            log.error("删除答题记录失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteByMemberId(String memberId) {
        try {
            LambdaQueryWrapper<AiMemberAnswer> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(AiMemberAnswer::getMemberId, memberId);
            return this.remove(queryWrapper);
        } catch (Exception e) {
            log.error("根据用户ID删除答题记录失败: ", e);
            return false;
        }
    }
}
src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberRoleCategoryServiceImpl.java
@@ -27,119 +27,7 @@
    @Override
    public AiMemberRoleCategory getById(String id) {
        return this.getById(id);
        return aiMemberRoleCategoryMapper.selectById(id);
    }
    @Override
    public List<AiMemberRoleCategory> getByRoleId(String roleId) {
        LambdaQueryWrapper<AiMemberRoleCategory> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiMemberRoleCategory::getRoleId, roleId);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiMemberRoleCategory> getByProductCategoryId(String productCategoryId) {
        LambdaQueryWrapper<AiMemberRoleCategory> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiMemberRoleCategory::getProductCategoryId, productCategoryId);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiMemberRoleCategory> getByCompanyId(String companyId) {
        LambdaQueryWrapper<AiMemberRoleCategory> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiMemberRoleCategory::getCompanyId, companyId);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiMemberRoleCategory> getByRoleIdAndProductCategoryId(String roleId, String productCategoryId) {
        LambdaQueryWrapper<AiMemberRoleCategory> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiMemberRoleCategory::getRoleId, roleId);
        queryWrapper.eq(AiMemberRoleCategory::getProductCategoryId, productCategoryId);
        return this.list(queryWrapper);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveRoleCategory(AiMemberRoleCategory aiMemberRoleCategory) {
        try {
            return this.save(aiMemberRoleCategory);
        } catch (Exception e) {
            log.error("保存角色产品分类关联记录失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveBatchRoleCategories(List<AiMemberRoleCategory> roleCategories) {
        try {
            return this.saveBatch(roleCategories);
        } catch (Exception e) {
            log.error("批量保存角色产品分类关联记录失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateRoleCategory(AiMemberRoleCategory aiMemberRoleCategory) {
        try {
            return this.updateById(aiMemberRoleCategory);
        } catch (Exception e) {
            log.error("更新角色产品分类关联记录失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteById(String id) {
        try {
            return this.removeById(id);
        } catch (Exception e) {
            log.error("删除角色产品分类关联记录失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteByRoleId(String roleId) {
        try {
            LambdaQueryWrapper<AiMemberRoleCategory> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(AiMemberRoleCategory::getRoleId, roleId);
            return this.remove(queryWrapper);
        } catch (Exception e) {
            log.error("根据角色ID删除角色产品分类关联记录失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteByProductCategoryId(String productCategoryId) {
        try {
            LambdaQueryWrapper<AiMemberRoleCategory> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(AiMemberRoleCategory::getProductCategoryId, productCategoryId);
            return this.remove(queryWrapper);
        } catch (Exception e) {
            log.error("根据产品分类ID删除角色产品分类关联记录失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteByRoleIdAndProductCategoryId(String roleId, String productCategoryId) {
        try {
            LambdaQueryWrapper<AiMemberRoleCategory> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(AiMemberRoleCategory::getRoleId, roleId);
            queryWrapper.eq(AiMemberRoleCategory::getProductCategoryId, productCategoryId);
            return this.remove(queryWrapper);
        } catch (Exception e) {
            log.error("根据角色ID和产品分类ID删除角色产品分类关联记录失败: ", e);
            return false;
        }
    }
}
src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberRoleProductServiceImpl.java
@@ -3,7 +3,9 @@
import cc.mrbird.febs.ai.entity.AiMemberRoleProduct;
import cc.mrbird.febs.ai.mapper.AiMemberRoleProductMapper;
import cc.mrbird.febs.ai.service.AiMemberRoleProductService;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -27,119 +29,19 @@
    @Override
    public AiMemberRoleProduct getById(String id) {
        return this.getById(id);
        return aiMemberRoleProductMapper.selectById(id);
    }
    @Override
    public List<AiMemberRoleProduct> getByRoleId(String roleId) {
        LambdaQueryWrapper<AiMemberRoleProduct> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiMemberRoleProduct::getRoleId, roleId);
        return this.list(queryWrapper);
    public List<AiMemberRoleProduct> selectListByQuery(LambdaQueryWrapper<AiMemberRoleProduct> query) {
        return aiMemberRoleProductMapper.selectList( query);
    }
    @Override
    public List<AiMemberRoleProduct> getByProductId(String productId) {
        LambdaQueryWrapper<AiMemberRoleProduct> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiMemberRoleProduct::getProductId, productId);
        return this.list(queryWrapper);
    public void deleteByQuery(LambdaQueryWrapper<AiMemberRoleProduct> query) {
        aiMemberRoleProductMapper.delete( query);
    }
    @Override
    public List<AiMemberRoleProduct> getByCompanyId(String companyId) {
        LambdaQueryWrapper<AiMemberRoleProduct> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiMemberRoleProduct::getCompanyId, companyId);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiMemberRoleProduct> getByRoleIdAndProductId(String roleId, String productId) {
        LambdaQueryWrapper<AiMemberRoleProduct> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiMemberRoleProduct::getRoleId, roleId);
        queryWrapper.eq(AiMemberRoleProduct::getProductId, productId);
        return this.list(queryWrapper);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveRoleProduct(AiMemberRoleProduct aiMemberRoleProduct) {
        try {
            return this.save(aiMemberRoleProduct);
        } catch (Exception e) {
            log.error("保存角色产品关联记录失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveBatchRoleProducts(List<AiMemberRoleProduct> roleProducts) {
        try {
            return this.saveBatch(roleProducts);
        } catch (Exception e) {
            log.error("批量保存角色产品关联记录失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateRoleProduct(AiMemberRoleProduct aiMemberRoleProduct) {
        try {
            return this.updateById(aiMemberRoleProduct);
        } catch (Exception e) {
            log.error("更新角色产品关联记录失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteById(String id) {
        try {
            return this.removeById(id);
        } catch (Exception e) {
            log.error("删除角色产品关联记录失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteByRoleId(String roleId) {
        try {
            LambdaQueryWrapper<AiMemberRoleProduct> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(AiMemberRoleProduct::getRoleId, roleId);
            return this.remove(queryWrapper);
        } catch (Exception e) {
            log.error("根据角色ID删除角色产品关联记录失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteByProductId(String productId) {
        try {
            LambdaQueryWrapper<AiMemberRoleProduct> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(AiMemberRoleProduct::getProductId, productId);
            return this.remove(queryWrapper);
        } catch (Exception e) {
            log.error("根据产品ID删除角色产品关联记录失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteByRoleIdAndProductId(String roleId, String productId) {
        try {
            LambdaQueryWrapper<AiMemberRoleProduct> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(AiMemberRoleProduct::getRoleId, roleId);
            queryWrapper.eq(AiMemberRoleProduct::getProductId, productId);
            return this.remove(queryWrapper);
        } catch (Exception e) {
            log.error("根据角色ID和产品ID删除角色产品关联记录失败: ", e);
            return false;
        }
    }
}
src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberRoleServiceImpl.java
@@ -1,15 +1,28 @@
package cc.mrbird.febs.ai.service.impl;
import cc.mrbird.febs.ai.entity.AiMemberRole;
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.AiMemberRoleProductService;
import cc.mrbird.febs.ai.service.AiMemberRoleService;
import cc.mrbird.febs.ai.service.AiProductService;
import cc.mrbird.febs.ai.util.UUID;
import cc.mrbird.febs.common.entity.FebsResponse;
import cc.mrbird.febs.common.entity.QueryRequest;
import cc.mrbird.febs.mall.entity.ClothesTypeArt;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
import java.util.List;
/**
@@ -24,106 +37,106 @@
public class AiMemberRoleServiceImpl extends ServiceImpl<AiMemberRoleMapper, AiMemberRole> implements AiMemberRoleService {
    private final AiMemberRoleMapper aiMemberRoleMapper;
    private final AiProductService aiProductService;
    private final AiMemberRoleProductService aiMemberRoleProductService;
    @Override
    public AiMemberRole getById(String id) {
        return this.getById(id);
        return aiMemberRoleMapper.selectById(id);
    }
    @Override
    public List<AiMemberRole> getByCompanyId(String companyId) {
        LambdaQueryWrapper<AiMemberRole> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiMemberRole::getCompanyId, companyId);
        queryWrapper.orderByDesc(AiMemberRole::getCreatedTime);
        return this.list(queryWrapper);
    public IPage<AiMemberRole> listInPage(AiMemberRole dto, QueryRequest request) {
        Page<AiMemberRole> page = new Page<>(request.getPageNum(), request.getPageSize());
        LambdaQueryWrapper<AiMemberRole> query = Wrappers.lambdaQuery(AiMemberRole.class);
        query.ne(AiMemberRole::getState, 2);
        query.orderByAsc(AiMemberRole::getSort);
        Page<AiMemberRole> pages = aiMemberRoleMapper.selectPage(page, query);
        return pages;
    }
    @Override
    public AiMemberRole getByCode(String code) {
        LambdaQueryWrapper<AiMemberRole> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiMemberRole::getCode, code);
        return this.getOne(queryWrapper);
    }
    @Override
    public List<AiMemberRole> getByName(String name) {
        LambdaQueryWrapper<AiMemberRole> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiMemberRole::getName, name);
        queryWrapper.orderByDesc(AiMemberRole::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiMemberRole> getByState(Integer state) {
        LambdaQueryWrapper<AiMemberRole> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiMemberRole::getState, state);
        queryWrapper.orderByDesc(AiMemberRole::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiMemberRole> getByCompanyIdAndState(String companyId, Integer state) {
        LambdaQueryWrapper<AiMemberRole> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiMemberRole::getCompanyId, companyId);
        queryWrapper.eq(AiMemberRole::getState, state);
        queryWrapper.orderByDesc(AiMemberRole::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveRole(AiMemberRole aiMemberRole) {
        try {
            return this.save(aiMemberRole);
        } catch (Exception e) {
            log.error("保存角色失败: ", e);
            return false;
    public FebsResponse changeState(String id, Integer state) {
        AiMemberRole entity = this.getById(id);
        if(ObjectUtil.isNotEmpty(entity)){
            this.update(null,
                    Wrappers.lambdaUpdate(AiMemberRole.class)
                            .set(AiMemberRole::getState, state)
                            .set(AiMemberRole::getUpdatedTime, new Date())
                            .eq(AiMemberRole::getId, id));
        }
        return new FebsResponse().success().message("操作成功");
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveBatchRoles(List<AiMemberRole> roles) {
        try {
            return this.saveBatch(roles);
        } catch (Exception e) {
            log.error("批量保存角色失败: ", e);
            return false;
        }
    public FebsResponse memberRoleAdd(AiMemberRole dto) {
        AiMemberRole entity = new AiMemberRole();
        entity.setId(UUID.getSimpleUUIDString());
        entity.setName(dto.getName());
        entity.setCode(dto.getCode());
        entity.setIconImg(dto.getIconImg());
        entity.setSort(dto.getSort());
        entity.setState(dto.getState());
        entity.setCreatedTime(new Date());
        this.save( entity);
        return new FebsResponse().success().message("操作成功");
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateRole(AiMemberRole aiMemberRole) {
        try {
            return this.updateById(aiMemberRole);
        } catch (Exception e) {
            log.error("更新角色失败: ", e);
            return false;
    public FebsResponse updateMemberRole(AiMemberRole dto) {
        String id = dto.getId();
        AiMemberRole entity = this.getById(id);
        if(ObjectUtil.isNotNull( entity)){
            this.update(null,
                    Wrappers.lambdaUpdate(AiMemberRole.class)
                            .set(AiMemberRole::getCode, dto.getCode())
                            .set(AiMemberRole::getName, dto.getName())
                            .set(AiMemberRole::getIconImg, dto.getIconImg())
                            .set(AiMemberRole::getSort, dto.getSort())
                            .set(AiMemberRole::getUpdatedTime, new Date())
                            .eq(AiMemberRole::getId, id));
        }
        return new FebsResponse().success().message("操作成功");
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteById(String id) {
        try {
            return this.removeById(id);
        } catch (Exception e) {
            log.error("删除角色失败: ", e);
            return false;
    public FebsResponse delete(String id) {
        AiMemberRole entity = this.getById(id);
        if(ObjectUtil.isNotNull( entity)){
            this.update(null,
                    Wrappers.lambdaUpdate(AiMemberRole.class)
                            .set(AiMemberRole::getState, 2)
                            .set(AiMemberRole::getUpdatedTime, new Date())
                            .eq(AiMemberRole::getId, id));
        }
        return new FebsResponse().success().message("操作成功");
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteByCompanyId(String companyId) {
        try {
            LambdaQueryWrapper<AiMemberRole> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(AiMemberRole::getCompanyId, companyId);
            return this.remove(queryWrapper);
        } catch (Exception e) {
            log.error("根据公司ID删除角色失败: ", e);
            return false;
    public FebsResponse productSet(AdminMoveChooseInfoDto dto) {
        String roleId = dto.getRoleId();
        List<String> chooseIds = dto.getChooseIds();
        AiMemberRole aiMemberRole = this.getById(roleId);
        if (ObjectUtil.isNotNull(aiMemberRole)) {
            aiMemberRoleProductService.deleteByQuery(
                    Wrappers.lambdaQuery(AiMemberRoleProduct.class)
                            .eq(AiMemberRoleProduct::getRoleId,roleId)
            );
            if(CollUtil.isNotEmpty(chooseIds)){
                Date createdTime = new Date();
                for (String chooseId : chooseIds){
                    AiMemberRoleProduct entity = new AiMemberRoleProduct();
                    entity.setRoleId(roleId);
                    entity.setProductId(chooseId);
                    entity.setCreatedTime(createdTime);
                    aiMemberRoleProductService.getBaseMapper().insert(entity);
                }
            }
        }
        return new FebsResponse().success().message("操作成功");
    }
}
src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberTalkItemServiceImpl.java
@@ -27,87 +27,7 @@
    @Override
    public AiMemberTalkItem getById(String id) {
        return this.getById(id);
        return aiMemberTalkItemMapper.selectById(id);
    }
    @Override
    public List<AiMemberTalkItem> getByMemberTalkId(String memberTalkId) {
        LambdaQueryWrapper<AiMemberTalkItem> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiMemberTalkItem::getMemberTalkId, memberTalkId);
        queryWrapper.orderByAsc(AiMemberTalkItem::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiMemberTalkItem> getByMemberId(String memberId) {
        LambdaQueryWrapper<AiMemberTalkItem> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiMemberTalkItem::getMemberId, memberId);
        queryWrapper.orderByAsc(AiMemberTalkItem::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiMemberTalkItem> getByType(Integer type) {
        LambdaQueryWrapper<AiMemberTalkItem> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiMemberTalkItem::getType, type);
        queryWrapper.orderByAsc(AiMemberTalkItem::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveTalkItem(AiMemberTalkItem aiMemberTalkItem) {
        try {
            return this.save(aiMemberTalkItem);
        } catch (Exception e) {
            log.error("保存对话项失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveBatchTalkItems(List<AiMemberTalkItem> talkItems) {
        try {
            return this.saveBatch(talkItems);
        } catch (Exception e) {
            log.error("批量保存对话项失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateTalkItem(AiMemberTalkItem aiMemberTalkItem) {
        try {
            return this.updateById(aiMemberTalkItem);
        } catch (Exception e) {
            log.error("更新对话项失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteById(String id) {
        try {
            return this.removeById(id);
        } catch (Exception e) {
            log.error("删除对话项失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteByMemberTalkId(String memberTalkId) {
        try {
            LambdaQueryWrapper<AiMemberTalkItem> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(AiMemberTalkItem::getMemberTalkId, memberTalkId);
            return this.remove(queryWrapper);
        } catch (Exception e) {
            log.error("根据对话ID删除对话项失败: ", e);
            return false;
        }
    }
}
src/main/java/cc/mrbird/febs/ai/service/impl/AiMemberTalkServiceImpl.java
@@ -27,95 +27,7 @@
    @Override
    public AiMemberTalk getById(String id) {
        return this.getById(id);
        return aiMemberTalkMapper.selectById(id);
    }
    @Override
    public List<AiMemberTalk> getByMemberId(String memberId) {
        LambdaQueryWrapper<AiMemberTalk> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiMemberTalk::getMemberId, memberId);
        queryWrapper.orderByDesc(AiMemberTalk::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiMemberTalk> getByProductId(String productId) {
        LambdaQueryWrapper<AiMemberTalk> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiMemberTalk::getProductId, productId);
        queryWrapper.orderByDesc(AiMemberTalk::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiMemberTalk> getByProductRoleId(String productRoleId) {
        LambdaQueryWrapper<AiMemberTalk> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiMemberTalk::getProductRoleId, productRoleId);
        queryWrapper.orderByDesc(AiMemberTalk::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiMemberTalk> getByCompanyId(String companyId) {
        LambdaQueryWrapper<AiMemberTalk> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiMemberTalk::getCompanyId, companyId);
        queryWrapper.orderByDesc(AiMemberTalk::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveTalk(AiMemberTalk aiMemberTalk) {
        try {
            return this.save(aiMemberTalk);
        } catch (Exception e) {
            log.error("保存对话记录失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveBatchTalks(List<AiMemberTalk> talks) {
        try {
            return this.saveBatch(talks);
        } catch (Exception e) {
            log.error("批量保存对话记录失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateTalk(AiMemberTalk aiMemberTalk) {
        try {
            return this.updateById(aiMemberTalk);
        } catch (Exception e) {
            log.error("更新对话记录失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteById(String id) {
        try {
            return this.removeById(id);
        } catch (Exception e) {
            log.error("删除对话记录失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteByMemberId(String memberId) {
        try {
            LambdaQueryWrapper<AiMemberTalk> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(AiMemberTalk::getMemberId, memberId);
            return this.remove(queryWrapper);
        } catch (Exception e) {
            log.error("根据用户ID删除对话记录失败: ", e);
            return false;
        }
    }
}
src/main/java/cc/mrbird/febs/ai/service/impl/AiProductCategoryServiceImpl.java
@@ -1,16 +1,25 @@
package cc.mrbird.febs.ai.service.impl;
import cc.mrbird.febs.ai.entity.AiMemberRole;
import cc.mrbird.febs.ai.entity.AiProductCategory;
import cc.mrbird.febs.ai.mapper.AiProductCategoryMapper;
import cc.mrbird.febs.ai.service.AiProductCategoryService;
import cc.mrbird.febs.ai.util.UUID;
import cc.mrbird.febs.common.entity.FebsResponse;
import cc.mrbird.febs.common.entity.QueryRequest;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.*;
/**
 * AI产品类别 Service实现类
@@ -27,135 +36,120 @@
    @Override
    public AiProductCategory getById(String id) {
        return this.getById(id);
        return aiProductCategoryMapper.selectById(id);
    }
    @Override
    public List<AiProductCategory> getByCompanyId(String companyId) {
        LambdaQueryWrapper<AiProductCategory> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiProductCategory::getCompanyId, companyId);
        queryWrapper.orderByAsc(AiProductCategory::getSort);
        queryWrapper.orderByDesc(AiProductCategory::getCreatedTime);
        return this.list(queryWrapper);
    public List<AiProductCategory> getList() {
        LambdaQueryWrapper<AiProductCategory> query = Wrappers.lambdaQuery(AiProductCategory.class);
        query.ne(AiProductCategory::getState, 2);
        query.orderByDesc(AiProductCategory::getHotState);
        query.orderByAsc(AiProductCategory::getSort);
        List<AiProductCategory> aiProductCategories = aiProductCategoryMapper.selectList(query);
        return aiProductCategories;
    }
    @Override
    public AiProductCategory getByCode(String code) {
        LambdaQueryWrapper<AiProductCategory> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiProductCategory::getCode, code);
        return this.getOne(queryWrapper);
    }
    @Override
    public List<AiProductCategory> getByName(String name) {
        LambdaQueryWrapper<AiProductCategory> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(AiProductCategory::getName, name);
        queryWrapper.orderByAsc(AiProductCategory::getSort);
        queryWrapper.orderByDesc(AiProductCategory::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiProductCategory> getByState(Integer state) {
        LambdaQueryWrapper<AiProductCategory> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiProductCategory::getState, state);
        queryWrapper.orderByAsc(AiProductCategory::getSort);
        queryWrapper.orderByDesc(AiProductCategory::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiProductCategory> getByHotState(Integer hotState) {
        LambdaQueryWrapper<AiProductCategory> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiProductCategory::getHotState, hotState);
        queryWrapper.orderByAsc(AiProductCategory::getSort);
        queryWrapper.orderByDesc(AiProductCategory::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiProductCategory> getByCompanyIdAndState(String companyId, Integer state) {
        LambdaQueryWrapper<AiProductCategory> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiProductCategory::getCompanyId, companyId);
        queryWrapper.eq(AiProductCategory::getState, state);
        queryWrapper.orderByAsc(AiProductCategory::getSort);
        queryWrapper.orderByDesc(AiProductCategory::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiProductCategory> getByCompanyIdAndHotState(String companyId, Integer hotState) {
        LambdaQueryWrapper<AiProductCategory> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiProductCategory::getCompanyId, companyId);
        queryWrapper.eq(AiProductCategory::getHotState, hotState);
        queryWrapper.orderByAsc(AiProductCategory::getSort);
        queryWrapper.orderByDesc(AiProductCategory::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiProductCategory> getEnabledCategories() {
        LambdaQueryWrapper<AiProductCategory> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiProductCategory::getState, 1); // 1-启用
        queryWrapper.orderByAsc(AiProductCategory::getSort);
        queryWrapper.orderByDesc(AiProductCategory::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveProductCategory(AiProductCategory aiProductCategory) {
        try {
            return this.save(aiProductCategory);
        } catch (Exception e) {
            log.error("保存AI产品类别失败: ", e);
            return false;
    public Map<String, AiProductCategory> selectMapByIds(Set<String> collect) {
        Map<String,AiProductCategory> map = new HashMap<>();
        if(CollUtil.isNotEmpty( collect)){
            List<AiProductCategory> aiProductCategories = aiProductCategoryMapper.selectList(
                    Wrappers.lambdaQuery(AiProductCategory.class)
                    .in(AiProductCategory::getId,collect)
            );
            if(CollUtil.isNotEmpty(aiProductCategories)){
                aiProductCategories.forEach(aiProductCategory -> map.put(aiProductCategory.getId(),aiProductCategory));
            }
        }
        return map;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveBatchProductCategories(List<AiProductCategory> productCategories) {
        try {
            return this.saveBatch(productCategories);
        } catch (Exception e) {
            log.error("批量保存AI产品类别失败: ", e);
            return false;
        }
    public IPage<AiProductCategory> listInPage(AiProductCategory dto, QueryRequest request) {
        Page<AiProductCategory> page = new Page<>(request.getPageNum(), request.getPageSize());
        LambdaQueryWrapper<AiProductCategory> query = Wrappers.lambdaQuery(AiProductCategory.class);
        query.ne(AiProductCategory::getState, 2);
        query.orderByDesc(AiProductCategory::getHotState);
        query.orderByAsc(AiProductCategory::getSort);
        Page<AiProductCategory> pages = aiProductCategoryMapper.selectPage(page, query);
        return pages;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateProductCategory(AiProductCategory aiProductCategory) {
        try {
            return this.updateById(aiProductCategory);
        } catch (Exception e) {
            log.error("更新AI产品类别失败: ", e);
            return false;
    public FebsResponse changeState(String id, Integer type, Integer state) {
        AiProductCategory entity = this.getById(id);
        if(ObjectUtil.isNotNull(entity)){
            if(1 == type){
                aiProductCategoryMapper.update(null,
                        Wrappers.lambdaUpdate(AiProductCategory.class)
                        .set(AiProductCategory::getState, state)
                        .eq(AiProductCategory::getId, id)
                        );
            }
            if(2 == type){
                aiProductCategoryMapper.update(null,
                        Wrappers.lambdaUpdate(AiProductCategory.class)
                        .set(AiProductCategory::getHotState, state)
                        .eq(AiProductCategory::getId, id)
                        );
            }
        }
        return new FebsResponse().success().message("操作成功");
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteById(String id) {
        try {
            return this.removeById(id);
        } catch (Exception e) {
            log.error("删除AI产品类别失败: ", e);
            return false;
        }
    public FebsResponse add(AiProductCategory dto) {
        AiProductCategory entity = new AiProductCategory();
        entity.setId(UUID.getSimpleUUIDString());
        entity.setCompanyId(dto.getCompanyId());
        entity.setCode(dto.getCode());
        entity.setName(dto.getName());
        entity.setBackImg(dto.getBackImg());
        entity.setIconImg(dto.getIconImg());
        entity.setSort(dto.getSort());
        entity.setCreatedTime(new Date());
        this.save(entity);
        return new FebsResponse().success().message("操作成功");
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteByCompanyId(String companyId) {
        try {
            LambdaQueryWrapper<AiProductCategory> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(AiProductCategory::getCompanyId, companyId);
            return this.remove(queryWrapper);
        } catch (Exception e) {
            log.error("根据公司ID删除AI产品类别失败: ", e);
            return false;
    public FebsResponse update(AiProductCategory dto) {
        String id = dto.getId();
        AiProductCategory entity = this.getById(id);
        if (ObjectUtil.isNotNull( entity)){
            this.update(null,
                    Wrappers.lambdaUpdate(AiProductCategory.class)
                            .set(AiProductCategory::getCode, dto.getCode())
                            .set(AiProductCategory::getName, dto.getName())
                            .set(AiProductCategory::getBackImg, dto.getBackImg())
                            .set(AiProductCategory::getIconImg, dto.getIconImg())
                            .set(AiProductCategory::getSort, dto.getSort())
                            .set(AiProductCategory::getUpdatedTime, new Date())
                            .eq(AiProductCategory::getId, id)
            );
        }
        return new FebsResponse().success().message("操作成功");
    }
    @Override
    public FebsResponse delete(String id) {
        AiProductCategory entity = this.getById(id);
        if(ObjectUtil.isNotNull( entity)){
            this.update(null,
                    Wrappers.lambdaUpdate(AiProductCategory.class)
                            .set(AiProductCategory::getState, 2)
                            .set(AiProductCategory::getUpdatedTime, new Date())
                            .eq(AiProductCategory::getId, id));
        }
        return new FebsResponse().success().message("操作成功");
    }
    @Override
    public List<AiProductCategory> categoryTree() {
        return this.getList() ;
    }
}
src/main/java/cc/mrbird/febs/ai/service/impl/AiProductPointServiceImpl.java
@@ -27,127 +27,8 @@
    @Override
    public AiProductPoint getById(String id) {
        return this.getById(id);
        return aiProductPointMapper.selectById(id);
    }
    @Override
    public List<AiProductPoint> getByCompanyId(String companyId) {
        LambdaQueryWrapper<AiProductPoint> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiProductPoint::getCompanyId, companyId);
        queryWrapper.orderByDesc(AiProductPoint::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiProductPoint> getByIsNormal(Integer isNormal) {
        LambdaQueryWrapper<AiProductPoint> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiProductPoint::getIsNormal, isNormal);
        queryWrapper.orderByDesc(AiProductPoint::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiProductPoint> getByFinderUserName(String finderUserName) {
        LambdaQueryWrapper<AiProductPoint> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiProductPoint::getFinderUserName, finderUserName);
        queryWrapper.orderByDesc(AiProductPoint::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiProductPoint> getByTitle(String title) {
        LambdaQueryWrapper<AiProductPoint> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(AiProductPoint::getTitle, title);
        queryWrapper.orderByDesc(AiProductPoint::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiProductPoint> getByCompanyIdAndIsNormal(String companyId, Integer isNormal) {
        LambdaQueryWrapper<AiProductPoint> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiProductPoint::getCompanyId, companyId);
        queryWrapper.eq(AiProductPoint::getIsNormal, isNormal);
        queryWrapper.orderByDesc(AiProductPoint::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiProductPoint> searchByKeyword(String keyword) {
        LambdaQueryWrapper<AiProductPoint> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.and(wrapper -> wrapper.like(AiProductPoint::getTitle, keyword)
                .or()
                .like(AiProductPoint::getDescription, keyword));
        queryWrapper.orderByDesc(AiProductPoint::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveProductPoint(AiProductPoint aiProductPoint) {
        try {
            return this.save(aiProductPoint);
        } catch (Exception e) {
            log.error("保存AI产品知识点失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveBatchProductPoints(List<AiProductPoint> productPoints) {
        try {
            return this.saveBatch(productPoints);
        } catch (Exception e) {
            log.error("批量保存AI产品知识点失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateProductPoint(AiProductPoint aiProductPoint) {
        try {
            return this.updateById(aiProductPoint);
        } catch (Exception e) {
            log.error("更新AI产品知识点失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteById(String id) {
        try {
            return this.removeById(id);
        } catch (Exception e) {
            log.error("删除AI产品知识点失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteByCompanyId(String companyId) {
        try {
            LambdaQueryWrapper<AiProductPoint> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(AiProductPoint::getCompanyId, companyId);
            return this.remove(queryWrapper);
        } catch (Exception e) {
            log.error("根据公司ID删除AI产品知识点失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteByFeedId(String feedId) {
        try {
            LambdaQueryWrapper<AiProductPoint> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(AiProductPoint::getFeedId, feedId);
            return this.remove(queryWrapper);
        } catch (Exception e) {
            log.error("根据视频号FeedID删除AI产品知识点失败: ", e);
            return false;
        }
    }
}
src/main/java/cc/mrbird/febs/ai/service/impl/AiProductQuestionLinkServiceImpl.java
@@ -27,154 +27,7 @@
    @Override
    public AiProductQuestionLink getById(String id) {
        return this.getById(id);
        return aiProductQuestionLinkMapper.selectById(id);
    }
    @Override
    public List<AiProductQuestionLink> getByCompanyId(String companyId) {
        LambdaQueryWrapper<AiProductQuestionLink> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiProductQuestionLink::getCompanyId, companyId);
        queryWrapper.orderByDesc(AiProductQuestionLink::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiProductQuestionLink> getByProductQuestionId(String productQuestionId) {
        LambdaQueryWrapper<AiProductQuestionLink> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiProductQuestionLink::getProductQuestionId, productQuestionId);
        queryWrapper.orderByDesc(AiProductQuestionLink::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiProductQuestionLink> getByProductId(String productId) {
        LambdaQueryWrapper<AiProductQuestionLink> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiProductQuestionLink::getProductId, productId);
        queryWrapper.orderByDesc(AiProductQuestionLink::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiProductQuestionLink> getByCompanyIdAndProductId(String companyId, String productId) {
        LambdaQueryWrapper<AiProductQuestionLink> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiProductQuestionLink::getCompanyId, companyId);
        queryWrapper.eq(AiProductQuestionLink::getProductId, productId);
        queryWrapper.orderByDesc(AiProductQuestionLink::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiProductQuestionLink> getByCompanyIdAndProductQuestionId(String companyId, String productQuestionId) {
        LambdaQueryWrapper<AiProductQuestionLink> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiProductQuestionLink::getCompanyId, companyId);
        queryWrapper.eq(AiProductQuestionLink::getProductQuestionId, productQuestionId);
        queryWrapper.orderByDesc(AiProductQuestionLink::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiProductQuestionLink> getByProductIdAndProductQuestionId(String productId, String productQuestionId) {
        LambdaQueryWrapper<AiProductQuestionLink> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiProductQuestionLink::getProductId, productId);
        queryWrapper.eq(AiProductQuestionLink::getProductQuestionId, productQuestionId);
        queryWrapper.orderByDesc(AiProductQuestionLink::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveProductQuestionLink(AiProductQuestionLink aiProductQuestionLink) {
        try {
            return this.save(aiProductQuestionLink);
        } catch (Exception e) {
            log.error("保存产品题目关系失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveBatchProductQuestionLinks(List<AiProductQuestionLink> productQuestionLinks) {
        try {
            return this.saveBatch(productQuestionLinks);
        } catch (Exception e) {
            log.error("批量保存产品题目关系失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateProductQuestionLink(AiProductQuestionLink aiProductQuestionLink) {
        try {
            return this.updateById(aiProductQuestionLink);
        } catch (Exception e) {
            log.error("更新产品题目关系失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteById(String id) {
        try {
            return this.removeById(id);
        } catch (Exception e) {
            log.error("删除产品题目关系失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteByProductId(String productId) {
        try {
            LambdaQueryWrapper<AiProductQuestionLink> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(AiProductQuestionLink::getProductId, productId);
            return this.remove(queryWrapper);
        } catch (Exception e) {
            log.error("根据产品ID删除产品题目关系失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteByProductQuestionId(String productQuestionId) {
        try {
            LambdaQueryWrapper<AiProductQuestionLink> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(AiProductQuestionLink::getProductQuestionId, productQuestionId);
            return this.remove(queryWrapper);
        } catch (Exception e) {
            log.error("根据产品题目ID删除产品题目关系失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteByCompanyId(String companyId) {
        try {
            LambdaQueryWrapper<AiProductQuestionLink> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(AiProductQuestionLink::getCompanyId, companyId);
            return this.remove(queryWrapper);
        } catch (Exception e) {
            log.error("根据公司ID删除产品题目关系失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteByProductIdAndProductQuestionId(String productId, String productQuestionId) {
        try {
            LambdaQueryWrapper<AiProductQuestionLink> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(AiProductQuestionLink::getProductId, productId);
            queryWrapper.eq(AiProductQuestionLink::getProductQuestionId, productQuestionId);
            return this.remove(queryWrapper);
        } catch (Exception e) {
            log.error("根据产品ID和产品题目ID删除产品题目关系失败: ", e);
            return false;
        }
    }
}
src/main/java/cc/mrbird/febs/ai/service/impl/AiProductQuestionServiceImpl.java
@@ -27,150 +27,7 @@
    @Override
    public AiProductQuestion getById(String id) {
        return this.getById(id);
        return aiProductQuestionMapper.selectById(id);
    }
    @Override
    public List<AiProductQuestion> getByCompanyId(String companyId) {
        LambdaQueryWrapper<AiProductQuestion> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiProductQuestion::getCompanyId, companyId);
        queryWrapper.orderByDesc(AiProductQuestion::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiProductQuestion> getByProductCategoryId(String productCategoryId) {
        LambdaQueryWrapper<AiProductQuestion> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiProductQuestion::getProductCategoryId, productCategoryId);
        queryWrapper.orderByDesc(AiProductQuestion::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiProductQuestion> getByTitle(String title) {
        LambdaQueryWrapper<AiProductQuestion> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(AiProductQuestion::getTitle, title);
        queryWrapper.orderByDesc(AiProductQuestion::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiProductQuestion> getByParentId(String parentId) {
        LambdaQueryWrapper<AiProductQuestion> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiProductQuestion::getParentId, parentId);
        queryWrapper.orderByAsc(AiProductQuestion::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiProductQuestion> getByDifficulty(Integer difficulty) {
        LambdaQueryWrapper<AiProductQuestion> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiProductQuestion::getDifficulty, difficulty);
        queryWrapper.orderByDesc(AiProductQuestion::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiProductQuestion> getByState(Integer state) {
        LambdaQueryWrapper<AiProductQuestion> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiProductQuestion::getState, state);
        queryWrapper.orderByDesc(AiProductQuestion::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiProductQuestion> getByCorrectAnswer(Integer correctAnswer) {
        LambdaQueryWrapper<AiProductQuestion> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiProductQuestion::getCorrectAnswer, correctAnswer);
        queryWrapper.orderByDesc(AiProductQuestion::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiProductQuestion> getByCompanyIdAndState(String companyId, Integer state) {
        LambdaQueryWrapper<AiProductQuestion> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiProductQuestion::getCompanyId, companyId);
        queryWrapper.eq(AiProductQuestion::getState, state);
        queryWrapper.orderByDesc(AiProductQuestion::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiProductQuestion> getByParentIdAndState(String parentId, Integer state) {
        LambdaQueryWrapper<AiProductQuestion> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiProductQuestion::getParentId, parentId);
        queryWrapper.eq(AiProductQuestion::getState, state);
        queryWrapper.orderByAsc(AiProductQuestion::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveProductQuestion(AiProductQuestion aiProductQuestion) {
        try {
            return this.save(aiProductQuestion);
        } catch (Exception e) {
            log.error("保存AI产品题目失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveBatchProductQuestions(List<AiProductQuestion> productQuestions) {
        try {
            return this.saveBatch(productQuestions);
        } catch (Exception e) {
            log.error("批量保存AI产品题目失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateProductQuestion(AiProductQuestion aiProductQuestion) {
        try {
            return this.updateById(aiProductQuestion);
        } catch (Exception e) {
            log.error("更新AI产品题目失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteById(String id) {
        try {
            return this.removeById(id);
        } catch (Exception e) {
            log.error("删除AI产品题目失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteByParentId(String parentId) {
        try {
            LambdaQueryWrapper<AiProductQuestion> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(AiProductQuestion::getParentId, parentId);
            return this.remove(queryWrapper);
        } catch (Exception e) {
            log.error("根据父ID删除AI产品题目失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteByCompanyId(String companyId) {
        try {
            LambdaQueryWrapper<AiProductQuestion> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(AiProductQuestion::getCompanyId, companyId);
            return this.remove(queryWrapper);
        } catch (Exception e) {
            log.error("根据公司ID删除AI产品题目失败: ", e);
            return false;
        }
    }
}
src/main/java/cc/mrbird/febs/ai/service/impl/AiProductRoleServiceImpl.java
@@ -27,109 +27,8 @@
    @Override
    public AiProductRole getById(String id) {
        return this.getById(id);
        return aiProductRoleMapper.selectById(id);
    }
    @Override
    public List<AiProductRole> getByCompanyId(String companyId) {
        LambdaQueryWrapper<AiProductRole> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiProductRole::getCompanyId, companyId);
        queryWrapper.orderByDesc(AiProductRole::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiProductRole> getByProductId(String productId) {
        LambdaQueryWrapper<AiProductRole> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiProductRole::getProductId, productId);
        queryWrapper.orderByDesc(AiProductRole::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiProductRole> getByName(String name) {
        LambdaQueryWrapper<AiProductRole> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(AiProductRole::getName, name);
        queryWrapper.orderByDesc(AiProductRole::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiProductRole> getByCompanyIdAndProductId(String companyId, String productId) {
        LambdaQueryWrapper<AiProductRole> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiProductRole::getCompanyId, companyId);
        queryWrapper.eq(AiProductRole::getProductId, productId);
        queryWrapper.orderByDesc(AiProductRole::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveProductRole(AiProductRole aiProductRole) {
        try {
            return this.save(aiProductRole);
        } catch (Exception e) {
            log.error("保存AI产品角色失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveBatchProductRoles(List<AiProductRole> productRoles) {
        try {
            return this.saveBatch(productRoles);
        } catch (Exception e) {
            log.error("批量保存AI产品角色失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateProductRole(AiProductRole aiProductRole) {
        try {
            return this.updateById(aiProductRole);
        } catch (Exception e) {
            log.error("更新AI产品角色失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteById(String id) {
        try {
            return this.removeById(id);
        } catch (Exception e) {
            log.error("删除AI产品角色失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteByProductId(String productId) {
        try {
            LambdaQueryWrapper<AiProductRole> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(AiProductRole::getProductId, productId);
            return this.remove(queryWrapper);
        } catch (Exception e) {
            log.error("根据AI产品ID删除AI产品角色失败: ", e);
            return false;
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteByCompanyId(String companyId) {
        try {
            LambdaQueryWrapper<AiProductRole> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(AiProductRole::getCompanyId, companyId);
            return this.remove(queryWrapper);
        } catch (Exception e) {
            log.error("根据公司ID删除AI产品角色失败: ", e);
            return false;
        }
    }
}
src/main/java/cc/mrbird/febs/ai/service/impl/AiProductServiceImpl.java
@@ -1,16 +1,30 @@
package cc.mrbird.febs.ai.service.impl;
import cc.mrbird.febs.ai.entity.AiProduct;
import cc.mrbird.febs.ai.entity.AiProductCategory;
import cc.mrbird.febs.ai.mapper.AiProductMapper;
import cc.mrbird.febs.ai.service.AiProductCategoryService;
import cc.mrbird.febs.ai.service.AiProductService;
import cc.mrbird.febs.ai.util.UUID;
import cc.mrbird.febs.common.entity.FebsResponse;
import cc.mrbird.febs.common.entity.QueryRequest;
import cc.mrbird.febs.mall.entity.ClothesArt;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
 * AI产品表 Service实现类
@@ -24,138 +38,125 @@
public class AiProductServiceImpl extends ServiceImpl<AiProductMapper, AiProduct> implements AiProductService {
    private final AiProductMapper aiProductMapper;
    private final AiProductCategoryService aiProductCategoryService;
    @Override
    public AiProduct getById(String id) {
        return this.getById(id);
        return aiProductMapper.selectById(id);
    }
    @Override
    public List<AiProduct> getByCompanyId(String companyId) {
        LambdaQueryWrapper<AiProduct> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiProduct::getCompanyId, companyId);
        queryWrapper.orderByDesc(AiProduct::getSort);
        queryWrapper.orderByDesc(AiProduct::getCreatedTime);
        return this.list(queryWrapper);
    }
    public IPage<AiProduct> listInPage(AiProduct dto, QueryRequest request) {
    @Override
    public AiProduct getByCode(String code) {
        LambdaQueryWrapper<AiProduct> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiProduct::getCode, code);
        return this.getOne(queryWrapper);
    }
    @Override
    public List<AiProduct> getByName(String name) {
        LambdaQueryWrapper<AiProduct> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(AiProduct::getName, name);
        queryWrapper.orderByDesc(AiProduct::getSort);
        queryWrapper.orderByDesc(AiProduct::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiProduct> getByProductCategoryId(String productCategoryId) {
        LambdaQueryWrapper<AiProduct> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiProduct::getProductCategoryId, productCategoryId);
        queryWrapper.orderByDesc(AiProduct::getSort);
        queryWrapper.orderByDesc(AiProduct::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiProduct> getByState(Integer state) {
        LambdaQueryWrapper<AiProduct> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiProduct::getState, state);
        queryWrapper.orderByDesc(AiProduct::getSort);
        queryWrapper.orderByDesc(AiProduct::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiProduct> getByHotState(Integer hotState) {
        LambdaQueryWrapper<AiProduct> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiProduct::getHotState, hotState);
        queryWrapper.orderByDesc(AiProduct::getSort);
        queryWrapper.orderByDesc(AiProduct::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiProduct> getByCompanyIdAndState(String companyId, Integer state) {
        LambdaQueryWrapper<AiProduct> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiProduct::getCompanyId, companyId);
        queryWrapper.eq(AiProduct::getState, state);
        queryWrapper.orderByDesc(AiProduct::getSort);
        queryWrapper.orderByDesc(AiProduct::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    public List<AiProduct> getByCompanyIdAndHotState(String companyId, Integer hotState) {
        LambdaQueryWrapper<AiProduct> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AiProduct::getCompanyId, companyId);
        queryWrapper.eq(AiProduct::getHotState, hotState);
        queryWrapper.orderByDesc(AiProduct::getSort);
        queryWrapper.orderByDesc(AiProduct::getCreatedTime);
        return this.list(queryWrapper);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveProduct(AiProduct aiProduct) {
        try {
            return this.save(aiProduct);
        } catch (Exception e) {
            log.error("保存AI产品失败: ", e);
            return false;
        Page<AiProduct> page = new Page<>(request.getPageNum(), request.getPageSize());
        LambdaQueryWrapper<AiProduct> query = Wrappers.lambdaQuery(AiProduct.class);
        if (ObjectUtil.isNotNull(dto.getProductCategoryId())){
            query.eq(AiProduct::getProductCategoryId, dto.getProductCategoryId());
        }
        query.ne(AiProduct::getState, 2);
        query.orderByDesc(AiProduct::getHotState);
        query.orderByAsc(AiProduct::getSort);
        Page<AiProduct> pages = aiProductMapper.selectPage(page, query);
        List<AiProduct> records = pages.getRecords();
        if (CollUtil.isNotEmpty( records)){
            Set<String> collect = records.stream().map(AiProduct::getProductCategoryId).collect(Collectors.toSet());
            if(CollUtil.isNotEmpty( collect)){
                Map<String,AiProductCategory> map = aiProductCategoryService.selectMapByIds(collect);
                for (AiProduct record : records){
                    AiProductCategory orDefault = map.getOrDefault(record.getProductCategoryId(), null);
                    if(ObjectUtil.isNotNull(orDefault)){
                        record.setProductCategoryName(orDefault.getName());
                    }
                }
            }
        }
        return pages;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveBatchProducts(List<AiProduct> products) {
        try {
            return this.saveBatch(products);
        } catch (Exception e) {
            log.error("批量保存AI产品失败: ", e);
            return false;
    public FebsResponse changeState(String id, Integer type, Integer state) {
        AiProduct entity = this.getById(id);
        if(ObjectUtil.isNotNull(entity)){
            if(1 == type){
                aiProductMapper.update(null,
                        Wrappers.lambdaUpdate(AiProduct.class)
                                .set(AiProduct::getState, state)
                                .eq(AiProduct::getId, id)
                );
            }
            if(2 == type){
                aiProductMapper.update(null,
                        Wrappers.lambdaUpdate(AiProduct.class)
                                .set(AiProduct::getHotState, state)
                                .eq(AiProduct::getId, id)
                );
            }
        }
        return new FebsResponse().success().message("操作成功");
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateProduct(AiProduct aiProduct) {
        try {
            return this.updateById(aiProduct);
        } catch (Exception e) {
            log.error("更新AI产品失败: ", e);
            return false;
        }
    public FebsResponse add(AiProduct dto) {
        AiProduct entity = new AiProduct();
        entity.setId(UUID.getSimpleUUIDString());
        entity.setCompanyId(dto.getCompanyId());
        entity.setCode(dto.getCode());
        entity.setName(dto.getName());
        entity.setBackImg(dto.getBackImg());
        entity.setIconImg(dto.getIconImg());
        entity.setSort(dto.getSort());
        entity.setProductCategoryId(dto.getProductCategoryId());
        entity.setScene(dto.getScene());
        entity.setTarget(dto.getTarget());
        entity.setDescription(dto.getDescription());
        entity.setQuestionCount(dto.getQuestionCount());
        entity.setCreatedTime(new Date());
        this.save(entity);
        return new FebsResponse().success().message("操作成功");
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteById(String id) {
        try {
            return this.removeById(id);
        } catch (Exception e) {
            log.error("删除AI产品失败: ", e);
            return false;
    public FebsResponse update(AiProduct dto) {
        String id = dto.getId();
        AiProduct entity = this.getById(id);
        if (ObjectUtil.isNotNull( entity)){
            this.update(null,
                    Wrappers.lambdaUpdate(AiProduct.class)
                            .set(AiProduct::getCode, dto.getCode())
                            .set(AiProduct::getName, dto.getName())
                            .set(AiProduct::getBackImg, dto.getBackImg())
                            .set(AiProduct::getIconImg, dto.getIconImg())
                            .set(AiProduct::getSort, dto.getSort())
                            .set(AiProduct::getProductCategoryId, dto.getProductCategoryId())
                            .set(AiProduct::getScene, dto.getScene())
                            .set(AiProduct::getTarget, dto.getTarget())
                            .set(AiProduct::getDescription, dto.getDescription())
                            .set(AiProduct::getQuestionCount, dto.getQuestionCount())
                            .set(AiProduct::getUpdatedTime, new Date())
                            .eq(AiProduct::getId, id)
            );
        }
        return new FebsResponse().success().message("操作成功");
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteByCompanyId(String companyId) {
        try {
            LambdaQueryWrapper<AiProduct> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(AiProduct::getCompanyId, companyId);
            return this.remove(queryWrapper);
        } catch (Exception e) {
            log.error("根据公司ID删除AI产品失败: ", e);
            return false;
    public FebsResponse delete(String id) {
        AiProduct entity = this.getById(id);
        if(ObjectUtil.isNotNull( entity)){
            this.update(null,
                    Wrappers.lambdaUpdate(AiProduct.class)
                            .set(AiProduct::getState, 2)
                            .set(AiProduct::getUpdatedTime, new Date())
                            .eq(AiProduct::getId, id));
        }
        return new FebsResponse().success().message("操作成功");
    }
    @Override
    public List<AiProduct> selectList() {
        return aiProductMapper.selectList( null);
    }
}
src/main/java/cc/mrbird/febs/ai/util/UUID.java
New file
@@ -0,0 +1,495 @@
package cc.mrbird.febs.ai.util;
import com.sun.xml.internal.ws.util.UtilException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
/**
 * 提供通用唯一识别码(universally unique identifier)(UUID)实现
 *
 * @author ruoyi
 */
public final class UUID implements java.io.Serializable, Comparable<UUID>
{
    private static final long serialVersionUID = -1185015143654744140L;
    /**
     * SecureRandom 的单例
     *
     */
    private static class Holder
    {
        static final SecureRandom numberGenerator = getSecureRandom();
    }
    /** 此UUID的最高64有效位 */
    private final long mostSigBits;
    /** 此UUID的最低64有效位 */
    private final long leastSigBits;
    /**
     * 私有构造
     *
     * @param data 数据
     */
    private UUID(byte[] data)
    {
        long msb = 0;
        long lsb = 0;
        assert data.length == 16 : "data must be 16 bytes in length";
        for (int i = 0; i < 8; i++)
        {
            msb = (msb << 8) | (data[i] & 0xff);
        }
        for (int i = 8; i < 16; i++)
        {
            lsb = (lsb << 8) | (data[i] & 0xff);
        }
        this.mostSigBits = msb;
        this.leastSigBits = lsb;
    }
    /**
     * 使用指定的数据构造新的 UUID。
     *
     * @param mostSigBits 用于 {@code UUID} 的最高有效 64 位
     * @param leastSigBits 用于 {@code UUID} 的最低有效 64 位
     */
    public UUID(long mostSigBits, long leastSigBits)
    {
        this.mostSigBits = mostSigBits;
        this.leastSigBits = leastSigBits;
    }
    /**
     * 获取类型 4(伪随机生成的)UUID 的静态工厂。
     *
     * @return 随机生成的 {@code UUID}
     */
    public static UUID fastUUID()
    {
        return randomUUID(false);
    }
    /**
     * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。
     *
     * @return 随机生成的 {@code UUID}
     */
    public static UUID randomUUID()
    {
        return randomUUID(true);
    }
    /**
     * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。
     *
     * @return 随机生成的 {@code UUID}
     */
    public static String getSimpleUUIDString()
    {
        return randomUUID(true).toString(true);
    }
    /**
     * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。
     *
     * @param isSecure 是否使用{@link SecureRandom}如果是可以获得更安全的随机码,否则可以得到更好的性能
     * @return 随机生成的 {@code UUID}
     */
    public static UUID randomUUID(boolean isSecure)
    {
        final Random ng = isSecure ? Holder.numberGenerator : getRandom();
        byte[] randomBytes = new byte[16];
        ng.nextBytes(randomBytes);
        randomBytes[6] &= 0x0f; /* clear version */
        randomBytes[6] |= 0x40; /* set to version 4 */
        randomBytes[8] &= 0x3f; /* clear variant */
        randomBytes[8] |= 0x80; /* set to IETF variant */
        return new UUID(randomBytes);
    }
    /**
     * 根据指定的字节数组获取类型 3(基于名称的)UUID 的静态工厂。
     *
     * @param name 用于构造 UUID 的字节数组。
     *
     * @return 根据指定数组生成的 {@code UUID}
     */
    public static UUID nameUUIDFromBytes(byte[] name)
    {
        MessageDigest md;
        try
        {
            md = MessageDigest.getInstance("MD5");
        }
        catch (NoSuchAlgorithmException nsae)
        {
            throw new InternalError("MD5 not supported");
        }
        byte[] md5Bytes = md.digest(name);
        md5Bytes[6] &= 0x0f; /* clear version */
        md5Bytes[6] |= 0x30; /* set to version 3 */
        md5Bytes[8] &= 0x3f; /* clear variant */
        md5Bytes[8] |= 0x80; /* set to IETF variant */
        return new UUID(md5Bytes);
    }
    /**
     * 根据 {@link #toString()} 方法中描述的字符串标准表示形式创建{@code UUID}。
     *
     * @param name 指定 {@code UUID} 字符串
     * @return 具有指定值的 {@code UUID}
     * @throws IllegalArgumentException 如果 name 与 {@link #toString} 中描述的字符串表示形式不符抛出此异常
     *
     */
    public static UUID fromString(String name)
    {
        String[] components = name.split("-");
        if (components.length != 5)
        {
            throw new IllegalArgumentException("Invalid UUID string: " + name);
        }
        for (int i = 0; i < 5; i++)
        {
            components[i] = "0x" + components[i];
        }
        long mostSigBits = Long.decode(components[0]).longValue();
        mostSigBits <<= 16;
        mostSigBits |= Long.decode(components[1]).longValue();
        mostSigBits <<= 16;
        mostSigBits |= Long.decode(components[2]).longValue();
        long leastSigBits = Long.decode(components[3]).longValue();
        leastSigBits <<= 48;
        leastSigBits |= Long.decode(components[4]).longValue();
        return new UUID(mostSigBits, leastSigBits);
    }
    /**
     * 返回此 UUID 的 128 位值中的最低有效 64 位。
     *
     * @return 此 UUID 的 128 位值中的最低有效 64 位。
     */
    public long getLeastSignificantBits()
    {
        return leastSigBits;
    }
    /**
     * 返回此 UUID 的 128 位值中的最高有效 64 位。
     *
     * @return 此 UUID 的 128 位值中最高有效 64 位。
     */
    public long getMostSignificantBits()
    {
        return mostSigBits;
    }
    /**
     * 与此 {@code UUID} 相关联的版本号. 版本号描述此 {@code UUID} 是如何生成的。
     * <p>
     * 版本号具有以下含意:
     * <ul>
     * <li>1 基于时间的 UUID
     * <li>2 DCE 安全 UUID
     * <li>3 基于名称的 UUID
     * <li>4 随机生成的 UUID
     * </ul>
     *
     * @return 此 {@code UUID} 的版本号
     */
    public int version()
    {
        // Version is bits masked by 0x000000000000F000 in MS long
        return (int) ((mostSigBits >> 12) & 0x0f);
    }
    /**
     * 与此 {@code UUID} 相关联的变体号。变体号描述 {@code UUID} 的布局。
     * <p>
     * 变体号具有以下含意:
     * <ul>
     * <li>0 为 NCS 向后兼容保留
     * <li>2 <a href="http://www.ietf.org/rfc/rfc4122.txt">IETF&nbsp;RFC&nbsp;4122</a>(Leach-Salz), 用于此类
     * <li>6 保留,微软向后兼容
     * <li>7 保留供以后定义使用
     * </ul>
     *
     * @return 此 {@code UUID} 相关联的变体号
     */
    public int variant()
    {
        // This field is composed of a varying number of bits.
        // 0 - - Reserved for NCS backward compatibility
        // 1 0 - The IETF aka Leach-Salz variant (used by this class)
        // 1 1 0 Reserved, Microsoft backward compatibility
        // 1 1 1 Reserved for future definition.
        return (int) ((leastSigBits >>> (64 - (leastSigBits >>> 62))) & (leastSigBits >> 63));
    }
    /**
     * 与此 UUID 相关联的时间戳值。
     *
     * <p>
     * 60 位的时间戳值根据此 {@code UUID} 的 time_low、time_mid 和 time_hi 字段构造。<br>
     * 所得到的时间戳以 100 毫微秒为单位,从 UTC(通用协调时间) 1582 年 10 月 15 日零时开始。
     *
     * <p>
     * 时间戳值仅在在基于时间的 UUID(其 version 类型为 1)中才有意义。<br>
     * 如果此 {@code UUID} 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。
     *
     * @throws UnsupportedOperationException 如果此 {@code UUID} 不是 version 为 1 的 UUID。
     */
    public long timestamp() throws UnsupportedOperationException
    {
        checkTimeBase();
        return (mostSigBits & 0x0FFFL) << 48//
                | ((mostSigBits >> 16) & 0x0FFFFL) << 32//
                | mostSigBits >>> 32;
    }
    /**
     * 与此 UUID 相关联的时钟序列值。
     *
     * <p>
     * 14 位的时钟序列值根据此 UUID 的 clock_seq 字段构造。clock_seq 字段用于保证在基于时间的 UUID 中的时间唯一性。
     * <p>
     * {@code clockSequence} 值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。 如果此 UUID 不是基于时间的 UUID,则此方法抛出
     * UnsupportedOperationException。
     *
     * @return 此 {@code UUID} 的时钟序列
     *
     * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1
     */
    public int clockSequence() throws UnsupportedOperationException
    {
        checkTimeBase();
        return (int) ((leastSigBits & 0x3FFF000000000000L) >>> 48);
    }
    /**
     * 与此 UUID 相关的节点值。
     *
     * <p>
     * 48 位的节点值根据此 UUID 的 node 字段构造。此字段旨在用于保存机器的 IEEE 802 地址,该地址用于生成此 UUID 以保证空间唯一性。
     * <p>
     * 节点值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。<br>
     * 如果此 UUID 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。
     *
     * @return 此 {@code UUID} 的节点值
     *
     * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1
     */
    public long node() throws UnsupportedOperationException
    {
        checkTimeBase();
        return leastSigBits & 0x0000FFFFFFFFFFFFL;
    }
    /**
     * 返回此{@code UUID} 的字符串表现形式。
     *
     * <p>
     * UUID 的字符串表示形式由此 BNF 描述:
     *
     * <pre>
     * {@code
     * UUID                   = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node>
     * time_low               = 4*<hexOctet>
     * time_mid               = 2*<hexOctet>
     * time_high_and_version  = 2*<hexOctet>
     * variant_and_sequence   = 2*<hexOctet>
     * node                   = 6*<hexOctet>
     * hexOctet               = <hexDigit><hexDigit>
     * hexDigit               = [0-9a-fA-F]
     * }
     * </pre>
     *
     * </blockquote>
     *
     * @return 此{@code UUID} 的字符串表现形式
     * @see #toString(boolean)
     */
    @Override
    public String toString()
    {
        return toString(false);
    }
    /**
     * 返回此{@code UUID} 的字符串表现形式。
     *
     * <p>
     * UUID 的字符串表示形式由此 BNF 描述:
     *
     * <pre>
     * {@code
     * UUID                   = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node>
     * time_low               = 4*<hexOctet>
     * time_mid               = 2*<hexOctet>
     * time_high_and_version  = 2*<hexOctet>
     * variant_and_sequence   = 2*<hexOctet>
     * node                   = 6*<hexOctet>
     * hexOctet               = <hexDigit><hexDigit>
     * hexDigit               = [0-9a-fA-F]
     * }
     * </pre>
     *
     * </blockquote>
     *
     * @param isSimple 是否简单模式,简单模式为不带'-'的UUID字符串
     * @return 此{@code UUID} 的字符串表现形式
     */
    public String toString(boolean isSimple)
    {
        final StringBuilder builder = new StringBuilder(isSimple ? 32 : 36);
        // time_low
        builder.append(digits(mostSigBits >> 32, 8));
        if (!isSimple)
        {
            builder.append('-');
        }
        // time_mid
        builder.append(digits(mostSigBits >> 16, 4));
        if (!isSimple)
        {
            builder.append('-');
        }
        // time_high_and_version
        builder.append(digits(mostSigBits, 4));
        if (!isSimple)
        {
            builder.append('-');
        }
        // variant_and_sequence
        builder.append(digits(leastSigBits >> 48, 4));
        if (!isSimple)
        {
            builder.append('-');
        }
        // node
        builder.append(digits(leastSigBits, 12));
        return builder.toString();
    }
    /**
     * 返回此 UUID 的哈希码。
     *
     * @return UUID 的哈希码值。
     */
    @Override
    public int hashCode()
    {
        long hilo = mostSigBits ^ leastSigBits;
        return ((int) (hilo >> 32)) ^ (int) hilo;
    }
    /**
     * 将此对象与指定对象比较。
     * <p>
     * 当且仅当参数不为 {@code null}、而是一个 UUID 对象、具有与此 UUID 相同的 varriant、包含相同的值(每一位均相同)时,结果才为 {@code true}。
     *
     * @param obj 要与之比较的对象
     *
     * @return 如果对象相同,则返回 {@code true};否则返回 {@code false}
     */
    @Override
    public boolean equals(Object obj)
    {
        if ((null == obj) || (obj.getClass() != UUID.class))
        {
            return false;
        }
        UUID id = (UUID) obj;
        return (mostSigBits == id.mostSigBits && leastSigBits == id.leastSigBits);
    }
    // Comparison Operations
    /**
     * 将此 UUID 与指定的 UUID 比较。
     *
     * <p>
     * 如果两个 UUID 不同,且第一个 UUID 的最高有效字段大于第二个 UUID 的对应字段,则第一个 UUID 大于第二个 UUID。
     *
     * @param val 与此 UUID 比较的 UUID
     *
     * @return 在此 UUID 小于、等于或大于 val 时,分别返回 -1、0 或 1。
     *
     */
    @Override
    public int compareTo(UUID val)
    {
        // The ordering is intentionally set up so that the UUIDs
        // can simply be numerically compared as two numbers
        return (this.mostSigBits < val.mostSigBits ? -1 : //
                (this.mostSigBits > val.mostSigBits ? 1 : //
                        (this.leastSigBits < val.leastSigBits ? -1 : //
                                (this.leastSigBits > val.leastSigBits ? 1 : //
                                        0))));
    }
    // -------------------------------------------------------------------------------------------------------------------
    // Private method start
    /**
     * 返回指定数字对应的hex值
     *
     * @param val 值
     * @param digits 位
     * @return 值
     */
    private static String digits(long val, int digits)
    {
        long hi = 1L << (digits * 4);
        return Long.toHexString(hi | (val & (hi - 1))).substring(1);
    }
    /**
     * 检查是否为time-based版本UUID
     */
    private void checkTimeBase()
    {
        if (version() != 1)
        {
            throw new UnsupportedOperationException("Not a time-based UUID");
        }
    }
    /**
     * 获取{@link SecureRandom},类提供加密的强随机数生成器 (RNG)
     *
     * @return {@link SecureRandom}
     */
    public static SecureRandom getSecureRandom()
    {
        try
        {
            return SecureRandom.getInstance("SHA1PRNG");
        }
        catch (NoSuchAlgorithmException e)
        {
            throw new RuntimeException("UUID生成异常");
        }
    }
    /**
     * 获取随机数生成器对象<br>
     * ThreadLocalRandom是JDK 7之后提供并发产生随机数,能够解决多个线程发生的竞争争夺。
     *
     * @return {@link ThreadLocalRandom}
     */
    public static ThreadLocalRandom getRandom()
    {
        return ThreadLocalRandom.current();
    }
}
src/main/java/cc/mrbird/febs/common/entity/AiBaseEntity.java
@@ -31,5 +31,5 @@
     * 主键 (UUID)
     */
    @TableId(value = "id",type = IdType.ASSIGN_UUID)
    private Long id;
    private String id;
}
src/main/java/cc/mrbird/febs/rabbit/consumer/AgentConsumer.java
@@ -9,6 +9,7 @@
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import java.util.Date;
@@ -19,6 +20,7 @@
 **/
@Slf4j
@Component
@ConditionalOnProperty(prefix = "system", name = "job", havingValue = "true")
public class AgentConsumer {
    @Autowired
src/main/resources/application-test.yml
@@ -18,7 +18,7 @@
          username: ct_test
          password: 123456
          driver-class-name: com.mysql.cj.jdbc.Driver
          url: jdbc:mysql://120.27.238.55:3406/clothes_test?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2b8
          url: jdbc:mysql://120.27.238.55:3406/ai_one?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2b8
  redis:
    # Redis数据库索引(默认为 0)
@@ -89,4 +89,4 @@
    path: /mnt/sdc/webresource/file/
system:
  job: true
  job: false
src/main/resources/templates/febs/views/modules/ai/memberRole/add.html
New file
@@ -0,0 +1,139 @@
<div class="layui-fluid layui-anim febs-anim" id="febs-memberRole-add" lay-title="新增">
    <div class="layui-row febs-container">
        <div class="layui-col-md12">
            <div class="layui-fluid" id="memberRole-add">
                <form class="layui-form" action="" lay-filter="memberRole-add-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">
                            <div class="layui-tab-item layui-show">
                                <div class="layui-row layui-col-space10 layui-form-item">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label febs-form-item-require">名称:</label>
                                        <div class="layui-input-block">
                                            <input type="text" name="name" lay-verify="required"
                                                   placeholder="" autocomplete="off" class="layui-input">
                                        </div>
                                    </div>
                                </div>
                                <div class="layui-row layui-col-space10 layui-form-item">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label febs-form-item-require">编码:</label>
                                        <div class="layui-input-block">
                                            <input type="text" name="code" lay-verify="required"
                                                   placeholder="" autocomplete="off" class="layui-input">
                                        </div>
                                    </div>
                                </div>
                                <div class="layui-row layui-col-space10 layui-form-item">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label febs-form-item-require">排序:</label>
                                        <div class="layui-input-block">
                                            <input type="number" name="sort" lay-verify="required"
                                                   placeholder="" autocomplete="off" class="layui-input">
                                        </div>
                                    </div>
                                </div>
                                <div class="layui-row layui-col-space10 layui-form-item">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label febs-form-item-require">小图标:</label>
                                        <div class="layui-input-block">
                                            <div class="layui-upload">
                                                <button type="button" class="layui-btn layui-btn-normal layui-btn" id="memberRoleImageUploadButton">上传</button>
                                                <blockquote class="layui-elem-quote layui-quote-nm" style="margin-top: 10px;">
                                                    <div class="layui-upload-list" id="memberRoleImageUpload"></div>
                                                </blockquote>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                                <div class="layui-row layui-col-space10 layui-form-item febs-hide">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label">小图标链接:</label>
                                        <div class="layui-input-block">
                                            <input type="text" id="iconImg" lay-verify="required" name="iconImg" autocomplete="off" class="layui-input" readonly>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                    <div class="layui-form-item febs-hide">
                        <button class="layui-btn" lay-submit="" lay-filter="memberRole-add-form-submit" id="submit">保存</button>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>
<!-- 表格操作栏 end -->
<script data-th-inline="javascript">
    layui.use(['febs', 'form', 'formSelects', 'validate', 'treeSelect', 'eleTree','dropdown', 'laydate', 'layedit', 'upload', 'element', 'table', 'xmSelect','jquery'], function () {
        var $ = layui.jquery,
            febs = layui.febs,
            layer = layui.layer,
            table = layui.table,
            formSelects = layui.formSelects,
            treeSelect = layui.treeSelect,
            form = layui.form,
            laydate = layui.laydate,
            eleTree = layui.eleTree,
            $view = $('#memberRole-add'),
            layedit = layui.layedit,
            upload = layui.upload,
            validate = layui.validate,
            element = layui.element;
        form.render();
        //图片上传
        upload.render({
            elem: '#memberRoleImageUploadButton'
            ,url: ctx + 'admin/goods/uploadFileBase64' //改成您自己的上传接口
            ,accept: 'file' //普通文件
            ,size: 10240 //限制文件大小,单位 KB
            ,before: function(obj){
                //预读本地文件示例,不支持ie8
                obj.preview(function(index, file, result){
                    $('#memberRoleImageUpload').html('<img src="'+ result +'" alt="'+ file.name +'" class="layui-upload-img single-image" style="width: 130px">')
                });
            }
            ,done: function(res){
                $("#iconImg").val(res.data.src);
            }
        });
        form.on('submit(memberRole-add-form-submit)', function (data) {
            $.ajax({
                'url':ctx + 'admin/memberRole/add',
                '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>
src/main/resources/templates/febs/views/modules/ai/memberRole/info.html
New file
@@ -0,0 +1,167 @@
<div class="layui-fluid layui-anim febs-anim" id="febs-memberRole-Info" lay-title="编辑">
    <div class="layui-row febs-container">
        <div class="layui-col-md12">
            <div class="layui-fluid" id="memberRole-info">
                <form class="layui-form" action="" lay-filter="memberRole-info-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="id"
                                   placeholder="" autoComplete="off" class="layui-input febs-hide">
                            <div class="layui-tab-item layui-show">
                                <div class="layui-row layui-col-space10 layui-form-item">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label febs-form-item-require">名称:</label>
                                        <div class="layui-input-block">
                                            <input type="text" name="name" lay-verify="required"
                                                   placeholder="" autocomplete="off" class="layui-input">
                                        </div>
                                    </div>
                                </div>
                                <div class="layui-row layui-col-space10 layui-form-item">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label febs-form-item-require">编码:</label>
                                        <div class="layui-input-block">
                                            <input type="text" name="code" lay-verify="required"
                                                   placeholder="" autocomplete="off" class="layui-input">
                                        </div>
                                    </div>
                                </div>
                                <div class="layui-row layui-col-space10 layui-form-item">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label febs-form-item-require">排序:</label>
                                        <div class="layui-input-block">
                                            <input type="number" name="sort" lay-verify="required"
                                                   placeholder="" autocomplete="off" class="layui-input">
                                        </div>
                                    </div>
                                </div>
                                <div class="layui-row layui-col-space10 layui-form-item">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label febs-form-item-require">小图标:</label>
                                        <div class="layui-input-block">
                                            <div class="layui-upload">
                                                <button type="button" class="layui-btn layui-btn-normal layui-btn" id="memberRoleImageUploadButton">上传</button>
                                                <blockquote class="layui-elem-quote layui-quote-nm" style="margin-top: 10px;">
                                                    <div class="layui-upload-list" id="memberRoleImageUpload"></div>
                                                </blockquote>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                                <div class="layui-row layui-col-space10 layui-form-item febs-hide">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label">小图标链接:</label>
                                        <div class="layui-input-block">
                                            <input type="text" id="iconImg" lay-verify="required" name="iconImg" autocomplete="off" class="layui-input" readonly>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                    <div class="layui-form-item febs-hide">
                        <button class="layui-btn" lay-submit="" lay-filter="memberRole-info-form-submit" id="submit">保存</button>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>
<style>
    .blue-border {
        border-left-color: #2db7f5;
        font-size: 18px;
    }
    .layui-table-cell {
        height:auto;
    }
    .layui-upload-list {
        margin: 0 !important;
    }
    .multi-images {
        margin: 0 5px !important;
    }
</style>
<!-- 表格操作栏 end -->
<script data-th-inline="javascript">
    layui.use(['febs', 'form', 'validate','formSelects', 'table', 'upload'], function () {
        var $ = layui.jquery,
            febs = layui.febs,
            layer = layui.layer,
            table = layui.table,
            form = layui.form,
            $view = $('#memberRole-info'),
            aiMemberRole = [[${aiMemberRole}]],
            upload = layui.upload,
            validate = layui.validate;
        form.render();
        //图片上传
        upload.render({
            elem: '#memberRoleImageUploadButton'
            ,url: ctx + 'admin/goods/uploadFileBase64' //改成您自己的上传接口
            ,accept: 'file' //普通文件
            ,size: 10240 //限制文件大小,单位 KB
            ,before: function(obj){
                //预读本地文件示例,不支持ie8
                obj.preview(function(index, file, result){
                    $('#memberRoleImageUpload').html('<img src="'+ result +'" alt="'+ file.name +'" class="layui-upload-img single-image" style="width: 130px">')
                });
            }
            ,done: function(res){
                $("#iconImg").val(res.data.src);
            }
        });
        setTimeout(() => {
            initMemberRoleInfo();
        }, 500);
        function initMemberRoleInfo() {
            form.val("memberRole-info-form", {
                "id": aiMemberRole.id,
                "name": aiMemberRole.name,
                "code": aiMemberRole.code,
                "iconImg": aiMemberRole.iconImg,
                "sort": aiMemberRole.sort,
            });
            $('#memberRoleImageUpload').append('<img src="' + aiMemberRole.iconImg + '" alt="" class="layui-upload-img single-image" style="width: 130px">');
            $("#iconImg").val(aiMemberRole.iconImg);
        }
        form.on('submit(memberRole-info-form-submit)', function (data) {
            $.ajax({
                'url':ctx + 'admin/memberRole/update',
                '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>
src/main/resources/templates/febs/views/modules/ai/memberRole/list.html
New file
@@ -0,0 +1,246 @@
<div class="layui-fluid layui-anim febs-anim" id="febs-memberRole" lay-title="角色类型">
    <div class="layui-row febs-container">
        <div class="layui-col-md12">
            <div class="layui-card">
                <div class="layui-card-body febs-table-full">
                    <form class="layui-form layui-table-form" lay-filter="memberRole-table-form">
                        <div class="layui-row">
                            <div class="layui-col-md10">
                                <div class="layui-form-item">
                                </div>
                            </div>
                            <div class="layui-col-md2 layui-col-sm12 layui-col-xs12 table-action-area">
                                <div class="layui-btn layui-btn-sm layui-btn-primary febs-button-blue-plain table-action" id="query">
                                    <i class="layui-icon">&#xe848;</i>
                                </div>
                                <div class="layui-btn layui-btn-sm layui-btn-primary febs-button-green-plain table-action" id="reset">
                                    <i class="layui-icon">&#xe79b;</i>
                                </div>
                            </div>
                        </div>
                    </form>
                    <table lay-filter="memberRoleTable" lay-data="{id: 'memberRoleTable'}"></table>
                    <style type="text/css">
                        .layui-table-cell{
                            text-align:center;
                            height: auto;
                            white-space: nowrap; /*文本不会换行,在同一行显示*/
                            overflow: hidden; /*超出隐藏*/
                            text-overflow: ellipsis; /*省略号显示*/
                        }
                        .layui-table img{
                            max-width:100px
                        }
                        ::-webkit-scrollbar {
                            height: 20px !important;
                            background-color: #f4f4f4;
                        }
                    </style>
                </div>
            </div>
        </div>
    </div>
</div>
<script type="text/html" id="memberRoleToolbar">
    <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>
    </div>
</script>
<script type="text/html" id="memberRoleOption">
    <button class="layui-btn layui-btn-normal layui-btn-sm" type="button" shiro:hasPermission="memberRoleList:info" lay-event="memberRoleInfoEvent">编辑</button>
    <button class="layui-btn layui-btn-danger layui-btn-sm" type="button" shiro:hasPermission="memberRoleList:info" lay-event="memberRoleDeleteEvent">删除</button>
</script>
<script type="text/html" id="memberRoleStateSwitch">
    {{# if(d.state === 1) { }}
    <input type="checkbox" value={{d.id}} lay-text="启用|禁用" checked lay-skin="switch" lay-filter="memberRoleStateSwitch">
    {{# } else { }}
    <input type="checkbox" value={{d.id}} lay-text="启用|禁用" lay-skin="switch" lay-filter="memberRoleStateSwitch">
    {{# } }}
</script>
<style>
    .layui-form-onswitch {
        background-color: #5FB878 !important;
    }
</style>
<!-- 表格操作栏 end -->
<script data-th-inline="none" type="text/javascript">
    // 引入组件并初始化
    layui.use([ 'jquery', 'form', 'table', 'febs'], function () {
        var $ = layui.jquery,
            febs = layui.febs,
            form = layui.form,
            table = layui.table,
            $view = $('#febs-memberRole'),
            $query = $view.find('#query'),
            $reset = $view.find('#reset'),
            $searchForm = $view.find('form'),
            sortObject = {field: 'orderNum', type: null},
            tableIns;
        form.render();
        // 表格初始化
        initMemberRoleTable();
        // 初始化表格操作栏各个按钮功能
        table.on('tool(memberRoleTable)', function (obj) {
            console.log("触发事件:", obj.event); // 调试信息
            var data = obj.data,
                layEvent = obj.event;
            if (layEvent === 'seeImage') {
                var t = $view.find('#seeImage'+data.id+'');
                //页面层
                layer.open({
                    type: 1,
                    title: "小图标",
                    skin: 'layui-layer-rim', //加上边框
                    area: ['100%', '100%'], //宽高
                    shadeClose: true, //开启遮罩关闭
                    end: function (index, layero) {
                        return false;
                    },
                    content: '<div style="text-align:center"><img src="' + $(t).attr('src') + '" /></div>'
                });
            }
            if (layEvent === 'memberRoleInfoEvent') {
                if (data.state == 1){
                    febs.alert.warn('请先禁用这行数据');
                    return;
                }
                febs.modal.open('编辑','modules/ai/memberRole/info/' + data.id, {
                    btn: ['提交', '取消'],
                    area: ['100%', '100%'],
                    yes: function (index, layero) {
                        $('#febs-memberRole-Info').find('#submit').trigger('click');
                    },
                    btn2: function () {
                        layer.closeAll();
                    }
                });
            }
            if (layEvent === 'memberRoleDeleteEvent') {
                if (data.state == 1){
                    febs.alert.warn('请先禁用这行数据');
                    return;
                }
                febs.modal.confirm('删除', '确认删除?', function () {
                    memberRoleDeleteEvent(data.id);
                });
            }
        });
        form.on('switch(memberRoleStateSwitch)', function (data) {
            if (data.elem.checked) {
                memberRoleStateSwitch(data.value,1);
            } else {
                memberRoleStateSwitch(data.value,0);
            }
        })
        function memberRoleStateSwitch(id,state) {
            febs.get(ctx + 'admin/memberRole/changeState/' + id+'/' + state, null, function (data) {
                febs.alert.success(data.message);
                $query.click();
            });
        }
        function memberRoleDeleteEvent(id) {
            febs.get(ctx + 'admin/memberRole/delete/' + id, null, function (data) {
                febs.alert.success(data.message);
                $query.click();
            });
        }
        // 初始化表格操作栏各个按钮功能
        table.on('toolbar(memberRoleTable)', function (obj) {
            let data = obj.data,
                layEvent = obj.event;
            if(layEvent === 'memberRoleAdd'){
                febs.modal.open('新增', 'modules/ai/memberRole/add/', {
                    btn: ['提交', '取消'],
                    area:['100%','100%'],
                    yes: function (index, layero) {
                        $('#febs-memberRole-add').find('#submit').trigger('click');
                    },
                    btn2: function () {
                        layer.closeAll();
                    }
                });
            }
            if (layEvent === 'productSet') {
                var checkData = table.checkStatus('memberRoleTable').data;
                if (checkData.length > 1 || checkData.length === 0) {
                    febs.alert.warn('每次操作只能操作一行数据');
                    return;
                }
                febs.modal.open('产品配置', 'modules/ai/memberRole/productSet/' + checkData[0].id, {
                    btn: ['提交', '取消'],
                    area:['100%','100%'],
                    yes: function (index, layero) {
                        $('#product-set').find('#submit').trigger('click');
                    },
                    btn2: function () {
                        layer.closeAll();
                    }
                });
            }
        });
        function initMemberRoleTable() {
            tableIns = febs.table.init({
                elem: $view.find('table'),
                id: 'memberRoleTable',
                url: ctx + 'admin/memberRole/list',
                toolbar:"#memberRoleToolbar",
                defaultToolbar:[],
                cols: [[
                    {type: 'checkbox'},
                    {type: 'numbers', title: '', width: 80},
                    {title: '操作', toolbar: '#memberRoleOption', minWidth: 200, align: 'center'},
                    {field: 'state', title: '状态', templet: '#memberRoleStateSwitch', minWidth: 130,align:'center'},
                    {field: 'sort', title: '排序', minWidth: 100,align:'center'},
                    {field: 'code', title: '编码', minWidth: 100,align:'center'},
                    {field: 'name', title: '名称', minWidth: 100,align:'center'},
                    {field: 'image',title: '小图标',
                        templet: function (d) {
                            return '<a lay-event="seeImage">' +
                                '<img id="seeImage' + d.id + '" src="' + d.iconImg +
                                '" alt="小图标" style="width: 50px; height: 50px; object-fit: cover; border-radius: 5px; cursor: pointer;">' +
                                '</a>';
                        },
                    minWidth: 150,align: 'center'},
                    {field: 'companyId', title: '公司编码', minWidth: 150,align:'center'},
                ]]
            });
        }
        // 查询按钮
        $query.on('click', function () {
            var params = $.extend(getQueryParams(), {field: sortObject.field, order: sortObject.type});
            tableIns.reload({where: params, page: {curr: 1}});
        });
        // 刷新按钮
        $reset.on('click', function () {
            $searchForm[0].reset();
            sortObject.type = 'null';
            tableIns.reload({where: getQueryParams(), page: {curr: 1}, initSort: sortObject});
        });
        // 获取查询参数
        function getQueryParams() {
            return {
            };
        }
    })
</script>
src/main/resources/templates/febs/views/modules/ai/memberRole/productSet.html
New file
@@ -0,0 +1,128 @@
<div class="layui-fluid layui-anim febs-anim" id="product-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="roleId"
                                   placeholder="" autoComplete="off" class="layui-input febs-hide">
                            <div class="layui-tab-item layui-show">
                                <div class="layui-form-item">
                                    <div id="productSetMove"></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 = [[${productAll}]],
            productTypeChoose = [[${productSelected}]],
            roleId = [[${roleId}]],
            $view = $('#product-set'),
            $productSetMoveQuery = $view.find('#productSetMoveQuery')
        ;
        // 查询按钮
        $productSetMoveQuery.on('click', function () {
            console.log(transfer.getData('productSetMove-set'))
            let data1 = transfer.getData('productSetMove-set');
            //获取data1中的value,返回一个数组
            let productIdList = data1.map(function(item){
                return item.value;
            });
            console.log(productIdList)
        });
        form.render();
        initProductTypeSet();
        function initProductTypeSet() {
            console.log("productTypeAll:", productTypeAll); // 调试信息
            console.log("productTypeChoose:", productTypeChoose); // 调试信息
            console.log("roleId:", roleId); // 调试信息
            form.val("product-type-set-form", {
                "roleId": roleId,
            });
            // 转换数据格式(假设接口返回的数据结构需要处理)
            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: '#productSetMove',
                data: dataLeft,
                id: 'productSetMove-set', // 唯一标识
                title: ['待选择列表', '已选择列表'],
                width: 300,
                height: 400,
                showSearch: true,
                value: productTypeChoose,
            });
        }
        form.on('submit(product-type-set-form-submit)', function (data) {
            let data1 = transfer.getData('productSetMove-set');
            //获取data1中的value,返回一个数组
            let productIdList = data1.map(function(item){
                return item.value;
            });
            data.field.chooseIds = productIdList;
            data.field.roleId = roleId;
            $.ajax({
                'url':ctx + 'admin/memberRole/productSet',
                '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>
src/main/resources/templates/febs/views/modules/ai/product/add.html
New file
@@ -0,0 +1,283 @@
<div class="layui-fluid layui-anim febs-anim" id="febs-product-add" lay-title="新增">
    <div class="layui-row febs-container">
        <div class="layui-col-md12">
            <div class="layui-fluid" id="product-add">
                <form class="layui-form" action="" lay-filter="product-add-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">
                            <div class="layui-tab-item layui-show">
                                <div class="layui-row layui-col-space10 layui-form-item">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label febs-form-item-require">商品分类:</label>
                                        <div class="layui-input-block">
                                            <div id="product-category"></div>
                                        </div>
                                    </div>
                                </div>
                                <div class="layui-row layui-col-space10 layui-form-item">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label febs-form-item-require">名称:</label>
                                        <div class="layui-input-block">
                                            <input type="text" name="name" lay-verify="required"
                                                   placeholder="" autocomplete="off" class="layui-input">
                                        </div>
                                    </div>
                                </div>
                                <div class="layui-row layui-col-space10 layui-form-item">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label febs-form-item-require">编码:</label>
                                        <div class="layui-input-block">
                                            <input type="text" name="code" lay-verify="required"
                                                   placeholder="" autocomplete="off" class="layui-input">
                                        </div>
                                    </div>
                                </div>
                                <div class="layui-row layui-col-space10 layui-form-item">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label febs-form-item-require">排序:</label>
                                        <div class="layui-input-block">
                                            <input type="number" name="sort" lay-verify="required"
                                                   placeholder="" autocomplete="off" class="layui-input">
                                        </div>
                                    </div>
                                </div>
                                <div class="layui-row layui-col-space10 layui-form-item">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label febs-form-item-require">小图标:</label>
                                        <div class="layui-input-block">
                                            <div class="layui-upload">
                                                <button type="button" class="layui-btn layui-btn-normal layui-btn" id="productImageUploadButton">上传</button>
                                                <blockquote class="layui-elem-quote layui-quote-nm" style="margin-top: 10px;">
                                                    <div class="layui-upload-list" id="productImageUpload"></div>
                                                </blockquote>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                                <div class="layui-row layui-col-space10 layui-form-item febs-hide">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label">小图标链接:</label>
                                        <div class="layui-input-block">
                                            <input type="text" id="iconImg" lay-verify="required" name="iconImg" autocomplete="off" class="layui-input" readonly>
                                        </div>
                                    </div>
                                </div>
                                <div class="layui-row layui-col-space10 layui-form-item">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label febs-form-item-require">背景图片:</label>
                                        <div class="layui-input-block">
                                            <div class="layui-upload">
                                                <button type="button" class="layui-btn layui-btn-normal layui-btn" id="productBackImageUploadButton">上传</button>
                                                <blockquote class="layui-elem-quote layui-quote-nm" style="margin-top: 10px;">
                                                    <div class="layui-upload-list" id="productBackImageUpload"></div>
                                                </blockquote>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                                <div class="layui-row layui-col-space10 layui-form-item febs-hide">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label">小图标链接:</label>
                                        <div class="layui-input-block">
                                            <input type="text" id="backImg" lay-verify="required" name="backImg" autocomplete="off" class="layui-input" readonly>
                                        </div>
                                    </div>
                                </div>
                                <div class="layui-row layui-col-space10 layui-form-item">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label febs-form-item-require">场景:</label>
                                        <div class="layui-input-block">
                                            <input type="text" name="scene" lay-verify="required"
                                                   placeholder="" autocomplete="off" class="layui-input">
                                        </div>
                                    </div>
                                </div>
                                <div class="layui-row layui-col-space10 layui-form-item">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label febs-form-item-require">目标:</label>
                                        <div class="layui-input-block">
                                            <input type="text" name="target" lay-verify="required"
                                                   placeholder="" autocomplete="off" class="layui-input">
                                        </div>
                                    </div>
                                </div>
                                <div class="layui-row layui-col-space10 layui-form-item">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label febs-form-item-require">出题数量:</label>
                                        <div class="layui-input-block">
                                            <input type="number" name="questionCount" lay-verify="required"
                                                   placeholder="" autocomplete="off" class="layui-input">
                                        </div>
                                    </div>
                                </div>
                                <div class="layui-form-item">
                                    <label class="layui-form-label febs-form-item-require">详情:</label>
                                    <div class="layui-input-block">
                                        <div style="border: 1px solid #ccc;">
                                            <div id="product-toolbar-container" class="toolbar"></div>
                                            <div id="product-text-container" class="text" style="height: 450px;"></div>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                    <div class="layui-form-item febs-hide">
                        <button class="layui-btn" lay-submit="" lay-filter="product-add-form-submit" id="submit">保存</button>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>
<!-- 表格操作栏 end -->
<script data-th-inline="javascript">
    layui.use(['febs', 'form', 'formSelects', 'validate', 'treeSelect', 'eleTree','dropdown', 'laydate', 'layedit', 'upload', 'element', 'table', 'xmSelect','jquery'], function () {
        var $ = layui.jquery,
            febs = layui.febs,
            layer = layui.layer,
            table = layui.table,
            formSelects = layui.formSelects,
            treeSelect = layui.treeSelect,
            form = layui.form,
            laydate = layui.laydate,
            eleTree = layui.eleTree,
            $view = $('#product-add'),
            layedit = layui.layedit,
            upload = layui.upload,
            validate = layui.validate,
            element = layui.element;
        form.render();
        var category = xmSelect.render({
            el: '#product-category',
            language: 'zn',
            prop : {
                value : 'id',
                children : 'child'
            },
            iconfont: {
                parent: 'hidden',
            },
            tips: '请选择',
            filterable: true,
            radio: true,
            clickClose: true,
            tree: {
                show: true,
                //非严格模式
                strict: false,
            },
            data: []
        })
        febs.get(ctx + 'admin/productCategory/categoryTree', null, function(res) {
            category.update({
                data : res.data,
                autoRow: true,
            });
        })
        const E = window.wangEditor;
        const editor = new E('#product-toolbar-container', '#product-text-container'); // 传入两个元素
        editor.config.showLinkImg = false;
        editor.config.uploadFileName = 'file';
        editor.config.customUploadImg = function (files, insertImgFn) {
            // files 是 input 中选中的文件列表
            // insertImgFn 是获取图片 url 后,插入到编辑器的方法
            // 上传图片,返回结果,将图片插入到编辑器中
            for (let i = 0; i < files.length; i++){
                var form = new FormData();
                form.append("file", files[0]);
                $.ajax({
                    url:'/admin/goods/uploadFileBase64',
                    type: "post",
                    processData: false,
                    contentType: false,
                    data: form,
                    dataType: 'json',
                    success(res) {
                        // 上传代码返回结果之后,将图片插入到编辑器中
                        insertImgFn(res.data.src, res.data.title, '')
                    }
                })
            }
        };
        editor.create();
        //图片上传
        upload.render({
            elem: '#productImageUploadButton'
            ,url: ctx + 'admin/goods/uploadFileBase64' //改成您自己的上传接口
            ,accept: 'file' //普通文件
            ,size: 10240 //限制文件大小,单位 KB
            ,before: function(obj){
                //预读本地文件示例,不支持ie8
                obj.preview(function(index, file, result){
                    $('#productImageUpload').html('<img src="'+ result +'" alt="'+ file.name +'" class="layui-upload-img single-image" style="width: 130px">')
                });
            }
            ,done: function(res){
                $("#iconImg").val(res.data.src);
            }
        });
        //图片上传
        upload.render({
            elem: '#productBackImageUploadButton'
            ,url: ctx + 'admin/goods/uploadFileBase64' //改成您自己的上传接口
            ,accept: 'file' //普通文件
            ,size: 10240 //限制文件大小,单位 KB
            ,before: function(obj){
                //预读本地文件示例,不支持ie8
                obj.preview(function(index, file, result){
                    $('#productBackImageUpload').html('<img src="'+ result +'" alt="'+ file.name +'" class="layui-upload-img single-image" style="width: 130px">')
                });
            }
            ,done: function(res){
                $("#backImg").val(res.data.src);
            }
        });
        form.on('submit(product-add-form-submit)', function (data) {
            data.field.productCategoryId = category.getValue('valueStr');
            data.field.description = editor.txt.html();
            $.ajax({
                'url':ctx + 'admin/product/add',
                '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-product').find('#query').click();
                    }else{
                        febs.alert.warn(data.message);
                    }
                },
                'error':function () {
                    febs.alert.warn('服务器繁忙');
                }
            })
            return false;
        });
    });
</script>
src/main/resources/templates/febs/views/modules/ai/product/info.html
New file
@@ -0,0 +1,328 @@
<div class="layui-fluid layui-anim febs-anim" id="febs-product-Info" lay-title="编辑">
    <div class="layui-row febs-container">
        <div class="layui-col-md12">
            <div class="layui-fluid" id="product-info">
                <form class="layui-form" action="" lay-filter="product-info-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="id"
                                   placeholder="" autoComplete="off" class="layui-input febs-hide">
                            <div class="layui-tab-item layui-show">
                                <div class="layui-row layui-col-space10 layui-form-item">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label febs-form-item-require">商品分类:</label>
                                        <div class="layui-input-block">
                                            <div id="product-category"></div>
                                        </div>
                                    </div>
                                </div>
                                <div class="layui-row layui-col-space10 layui-form-item">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label febs-form-item-require">名称:</label>
                                        <div class="layui-input-block">
                                            <input type="text" name="name" lay-verify="required"
                                                   placeholder="" autocomplete="off" class="layui-input">
                                        </div>
                                    </div>
                                </div>
                                <div class="layui-row layui-col-space10 layui-form-item">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label febs-form-item-require">编码:</label>
                                        <div class="layui-input-block">
                                            <input type="text" name="code" lay-verify="required"
                                                   placeholder="" autocomplete="off" class="layui-input">
                                        </div>
                                    </div>
                                </div>
                                <div class="layui-row layui-col-space10 layui-form-item">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label febs-form-item-require">排序:</label>
                                        <div class="layui-input-block">
                                            <input type="number" name="sort" lay-verify="required"
                                                   placeholder="" autocomplete="off" class="layui-input">
                                        </div>
                                    </div>
                                </div>
                                <div class="layui-row layui-col-space10 layui-form-item">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label febs-form-item-require">小图标:</label>
                                        <div class="layui-input-block">
                                            <div class="layui-upload">
                                                <button type="button" class="layui-btn layui-btn-normal layui-btn" id="productImageUploadButton">上传</button>
                                                <blockquote class="layui-elem-quote layui-quote-nm" style="margin-top: 10px;">
                                                    <div class="layui-upload-list" id="productImageUpload"></div>
                                                </blockquote>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                                <div class="layui-row layui-col-space10 layui-form-item febs-hide">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label">小图标链接:</label>
                                        <div class="layui-input-block">
                                            <input type="text" id="iconImg" lay-verify="required" name="iconImg" autocomplete="off" class="layui-input" readonly>
                                        </div>
                                    </div>
                                </div>
                                <div class="layui-row layui-col-space10 layui-form-item">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label febs-form-item-require">背景图片:</label>
                                        <div class="layui-input-block">
                                            <div class="layui-upload">
                                                <button type="button" class="layui-btn layui-btn-normal layui-btn" id="productBackImageUploadButton">上传</button>
                                                <blockquote class="layui-elem-quote layui-quote-nm" style="margin-top: 10px;">
                                                    <div class="layui-upload-list" id="productBackImageUpload"></div>
                                                </blockquote>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                                <div class="layui-row layui-col-space10 layui-form-item febs-hide">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label">小图标链接:</label>
                                        <div class="layui-input-block">
                                            <input type="text" id="backImg" lay-verify="required" name="backImg" autocomplete="off" class="layui-input" readonly>
                                        </div>
                                    </div>
                                </div>
                                <div class="layui-row layui-col-space10 layui-form-item">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label febs-form-item-require">场景:</label>
                                        <div class="layui-input-block">
                                            <input type="text" name="scene" lay-verify="required"
                                                   placeholder="" autocomplete="off" class="layui-input">
                                        </div>
                                    </div>
                                </div>
                                <div class="layui-row layui-col-space10 layui-form-item">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label febs-form-item-require">目标:</label>
                                        <div class="layui-input-block">
                                            <input type="text" name="target" lay-verify="required"
                                                   placeholder="" autocomplete="off" class="layui-input">
                                        </div>
                                    </div>
                                </div>
                                <div class="layui-row layui-col-space10 layui-form-item">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label febs-form-item-require">出题数量:</label>
                                        <div class="layui-input-block">
                                            <input type="number" name="questionCount" lay-verify="required"
                                                   placeholder="" autocomplete="off" class="layui-input">
                                        </div>
                                    </div>
                                </div>
                                <div class="layui-form-item">
                                    <label class="layui-form-label febs-form-item-require">详情:</label>
                                    <div class="layui-input-block">
                                        <div style="border: 1px solid #ccc;">
                                            <div id="product-toolbar-container" class="toolbar"></div>
                                            <div id="product-text-container" class="text" style="height: 450px;"></div>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                    <div class="layui-form-item febs-hide">
                        <button class="layui-btn" lay-submit="" lay-filter="product-info-form-submit" id="submit">保存</button>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>
<style>
    .blue-border {
        border-left-color: #2db7f5;
        font-size: 18px;
    }
    .layui-table-cell {
        height:auto;
    }
    .layui-upload-list {
        margin: 0 !important;
    }
    .multi-images {
        margin: 0 5px !important;
    }
</style>
<!-- 表格操作栏 end -->
<script data-th-inline="javascript">
    layui.use(['febs', 'form', 'formSelects', 'validate', 'treeSelect', 'eleTree','dropdown', 'laydate', 'layedit', 'upload', 'element', 'table', 'xmSelect','jquery'], function () {
        var $ = layui.jquery,
            febs = layui.febs,
            layer = layui.layer,
            table = layui.table,
            form = layui.form,
            $view = $('#product-info'),
            aiProduct = [[${aiProduct}]],
            upload = layui.upload,
            validate = layui.validate;
        form.render();
        var category = xmSelect.render({
            el: '#product-category',
            language: 'zn',
            prop : {
                value : 'id',
                children : 'child'
            },
            iconfont: {
                parent: 'hidden',
            },
            tips: '请选择',
            filterable: true,
            radio: true,
            clickClose: true,
            tree: {
                show: true,
                //非严格模式
                strict: false,
            },
            data: []
        })
        febs.get(ctx + 'admin/productCategory/categoryTree', null, function(res) {
            category.update({
                data : res.data,
                autoRow: true,
            });
        })
        const E = window.wangEditor;
        const editor = new E('#product-toolbar-container', '#product-text-container'); // 传入两个元素
        editor.config.showLinkImg = false;
        editor.config.uploadFileName = 'file';
        editor.config.customUploadImg = function (files, insertImgFn) {
            // files 是 input 中选中的文件列表
            // insertImgFn 是获取图片 url 后,插入到编辑器的方法
            // 上传图片,返回结果,将图片插入到编辑器中
            for (let i = 0; i < files.length; i++){
                var form = new FormData();
                form.append("file", files[0]);
                $.ajax({
                    url:'/admin/goods/uploadFileBase64',
                    type: "post",
                    processData: false,
                    contentType: false,
                    data: form,
                    dataType: 'json',
                    success(res) {
                        // 上传代码返回结果之后,将图片插入到编辑器中
                        insertImgFn(res.data.src, res.data.title, '')
                    }
                })
            }
        };
        editor.create();
        //图片上传
        upload.render({
            elem: '#productImageUploadButton'
            ,url: ctx + 'admin/goods/uploadFileBase64' //改成您自己的上传接口
            ,accept: 'file' //普通文件
            ,size: 10240 //限制文件大小,单位 KB
            ,before: function(obj){
                //预读本地文件示例,不支持ie8
                obj.preview(function(index, file, result){
                    $('#productImageUpload').html('<img src="'+ result +'" alt="'+ file.name +'" class="layui-upload-img single-image" style="width: 130px">')
                });
            }
            ,done: function(res){
                $("#iconImg").val(res.data.src);
            }
        });
        //图片上传
        upload.render({
            elem: '#productBackImageUploadButton'
            ,url: ctx + 'admin/goods/uploadFileBase64' //改成您自己的上传接口
            ,accept: 'file' //普通文件
            ,size: 10240 //限制文件大小,单位 KB
            ,before: function(obj){
                //预读本地文件示例,不支持ie8
                obj.preview(function(index, file, result){
                    $('#productBackImageUpload').html('<img src="'+ result +'" alt="'+ file.name +'" class="layui-upload-img single-image" style="width: 130px">')
                });
            }
            ,done: function(res){
                $("#backImg").val(res.data.src);
            }
        });
        setTimeout(() => {
            initProductInfo();
        }, 500);
        function initProductInfo() {
            form.val("product-info-form", {
                "id": aiProduct.id,
                "name": aiProduct.name,
                "code": aiProduct.code,
                "scene": aiProduct.scene,
                "target": aiProduct.target,
                "questionCount": aiProduct.questionCount,
                "sort": aiProduct.sort,
            });
            var categoryList = [];
            categoryList.push(aiProduct.productCategoryId)
            category.setValue(categoryList);
            $('#productImageUpload').append('<img src="' + aiProduct.iconImg + '" alt="" class="layui-upload-img single-image" style="width: 130px">');
            $("#iconImg").val(aiProduct.iconImg);
            $('#productBackImageUpload').append('<img src="' + aiProduct.backImg + '" alt="" class="layui-upload-img single-image" style="width: 130px">');
            $("#backImg").val(aiProduct.backImg);
            editor.txt.html(aiProduct.description);
        }
        form.on('submit(product-info-form-submit)', function (data) {
            data.field.productCategoryId = category.getValue('valueStr');
            data.field.description = editor.txt.html();
            $.ajax({
                'url':ctx + 'admin/product/update',
                '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-product').find('#query').click();
                    }else{
                        febs.alert.warn(data.message);
                    }
                },
                'error':function () {
                    febs.alert.warn('服务器繁忙');
                }
            })
            return false;
        });
    });
</script>
src/main/resources/templates/febs/views/modules/ai/product/list.html
New file
@@ -0,0 +1,329 @@
<div class="layui-fluid layui-anim febs-anim" id="febs-product" lay-title="产品">
    <div class="layui-row febs-container">
        <div class="layui-col-md12">
            <div class="layui-card">
                <div class="layui-card-body febs-table-full">
                    <form class="layui-form layui-table-form" lay-filter="product-table-form">
                        <div class="layui-row">
                            <div class="layui-col-md10">
                                <div class="layui-row layui-col-space6 layui-form-item">
                                    <div class="layui-col-lg3">
                                        <label class="layui-form-label">产品分类:</label>
                                        <div class="layui-input-block">
                                            <div id="product-category-query"></div>
                                        </div>
                                    </div>
                                </div>
                            </div>
                            <div class="layui-col-md2 layui-col-sm12 layui-col-xs12 table-action-area">
                                <div class="layui-btn layui-btn-sm layui-btn-primary febs-button-blue-plain table-action" id="query">
                                    <i class="layui-icon">&#xe848;</i>
                                </div>
                                <div class="layui-btn layui-btn-sm layui-btn-primary febs-button-green-plain table-action" id="reset">
                                    <i class="layui-icon">&#xe79b;</i>
                                </div>
                            </div>
                        </div>
                    </form>
                    <table lay-filter="productTable" lay-data="{id: 'productTable'}"></table>
                    <style type="text/css">
                        .layui-table-cell{
                            text-align:center;
                            height: auto;
                            white-space: nowrap; /*文本不会换行,在同一行显示*/
                            overflow: hidden; /*超出隐藏*/
                            text-overflow: ellipsis; /*省略号显示*/
                        }
                        .layui-table img{
                            max-width:100px
                        }
                        ::-webkit-scrollbar {
                            height: 20px !important;
                            background-color: #f4f4f4;
                        }
                    </style>
                </div>
            </div>
        </div>
    </div>
</div>
<script type="text/html" id="productToolbar">
    <div class="layui-btn-container">
        <button class="layui-btn layui-btn-sm layui-btn-primary febs-button-green-plain" type="button" shiro:hasPermission="categoryList:add" lay-event="productAdd">新增</button>
    </div>
</script>
<script type="text/html" id="productOption">
    <button class="layui-btn layui-btn-normal layui-btn-sm" type="button" shiro:hasPermission="categoryList:info" lay-event="productInfoEvent">编辑</button>
    <button class="layui-btn layui-btn-danger layui-btn-sm" type="button" shiro:hasPermission="categoryList:info" lay-event="productDeleteEvent">删除</button>
</script>
<script type="text/html" id="productStateSwitch">
    {{# if(d.state === 1) { }}
    <input type="checkbox" value={{d.id}} lay-text="启用|禁用" checked lay-skin="switch" lay-filter="productStateSwitch">
    {{# } else { }}
    <input type="checkbox" value={{d.id}} lay-text="启用|禁用" lay-skin="switch" lay-filter="productStateSwitch">
    {{# } }}
</script>
<script type="text/html" id="productHotStateSwitch">
    {{# if(d.hotState === 1) { }}
    <input type="checkbox" value={{d.id}} lay-text="启用|禁用" checked lay-skin="switch" lay-filter="productHotStateSwitch">
    {{# } else { }}
    <input type="checkbox" value={{d.id}} lay-text="启用|禁用" lay-skin="switch" lay-filter="productHotStateSwitch">
    {{# } }}
</script>
<style>
    .layui-form-onswitch {
        background-color: #5FB878 !important;
    }
</style>
<!-- 表格操作栏 end -->
<script data-th-inline="none" type="text/javascript">
    // 引入组件并初始化
    layui.use(['febs', 'form', 'formSelects', 'validate', 'treeSelect', 'eleTree','dropdown', 'laydate', 'layedit', 'upload', 'element', 'table', 'xmSelect','jquery'], function () {
        var $ = layui.jquery,
            febs = layui.febs,
            form = layui.form,
            table = layui.table,
            $view = $('#febs-product'),
            $query = $view.find('#query'),
            $reset = $view.find('#reset'),
            $searchForm = $view.find('form'),
            sortObject = {field: 'orderNum', type: null},
            tableIns;
        form.render();
        var category = xmSelect.render({
            el: '#product-category-query',
            language: 'zn',
            prop : {
                value : 'id',
                children : 'child'
            },
            iconfont: {
                parent: 'hidden',
            },
            tips: '请选择',
            filterable: true,
            radio: true,
            clickClose: true,
            tree: {
                show: true,
                //非严格模式
                strict: false,
            },
            data: []
        })
        febs.get(ctx + 'admin/productCategory/categoryTree', null, function(res) {
            category.update({
                data : res.data,
                autoRow: true,
            });
        })
        // 表格初始化
        setTimeout(() => {
            initproductTable();
        }, 500);
        // 初始化表格操作栏各个按钮功能
        table.on('tool(productTable)', function (obj) {
            console.log("触发事件:", obj.event); // 调试信息
            var data = obj.data,
                layEvent = obj.event;
            if (layEvent === 'seeImage') {
                var t = $view.find('#seeImage'+data.id+'');
                //页面层
                layer.open({
                    type: 1,
                    title: "小图标",
                    skin: 'layui-layer-rim', //加上边框
                    area: ['100%', '100%'], //宽高
                    shadeClose: true, //开启遮罩关闭
                    end: function (index, layero) {
                        return false;
                    },
                    content: '<div style="text-align:center"><img src="' + $(t).attr('src') + '" /></div>'
                });
            }
            if (layEvent === 'seeBackImage') {
                var t = $view.find('#seeBackImage'+data.id+'');
                //页面层
                layer.open({
                    type: 1,
                    title: "背景图片",
                    skin: 'layui-layer-rim', //加上边框
                    area: ['100%', '100%'], //宽高
                    shadeClose: true, //开启遮罩关闭
                    end: function (index, layero) {
                        return false;
                    },
                    content: '<div style="text-align:center"><img src="' + $(t).attr('src') + '" /></div>'
                });
            }
            if (layEvent === 'productInfoEvent') {
                if (data.state == 1){
                    febs.alert.warn('请先禁用这行数据');
                    return;
                }
                febs.modal.open('编辑','modules/ai/product/info/' + data.id, {
                    btn: ['提交', '取消'],
                    area: ['100%', '100%'],
                    yes: function (index, layero) {
                        $('#febs-product-Info').find('#submit').trigger('click');
                    },
                    btn2: function () {
                        layer.closeAll();
                    }
                });
            }
            if (layEvent === 'productDeleteEvent') {
                if (data.state == 1){
                    febs.alert.warn('请先禁用这行数据');
                    return;
                }
                febs.modal.confirm('删除', '确认删除?', function () {
                    productDeleteEvent(data.id);
                });
            }
        });
        form.on('switch(productStateSwitch)', function (data) {
            if (data.elem.checked) {
                productSwitch(data.value,1,1);
            } else {
                productSwitch(data.value,1,0);
            }
        })
        form.on('switch(productHotStateSwitch)', function (data) {
            if (data.elem.checked) {
                productSwitch(data.value,2,1);
            } else {
                productSwitch(data.value,2,0);
            }
        })
        function productSwitch(id,type,state) {
            febs.get(ctx + 'admin/product/changeState/' + id+'/'+ type+'/' + state, null, function (data) {
                febs.alert.success(data.message);
                $query.click();
            });
        }
        function productDeleteEvent(id) {
            febs.get(ctx + 'admin/product/delete/' + id, null, function (data) {
                febs.alert.success(data.message);
                $query.click();
            });
        }
        // 初始化表格操作栏各个按钮功能
        table.on('toolbar(productTable)', function (obj) {
            let data = obj.data,
                layEvent = obj.event;
            if(layEvent === 'productAdd'){
                febs.modal.open('新增', 'modules/ai/product/add/', {
                    btn: ['提交', '取消'],
                    area:['100%','100%'],
                    yes: function (index, layero) {
                        $('#febs-product-add').find('#submit').trigger('click');
                    },
                    btn2: function () {
                        layer.closeAll();
                    }
                });
            }
            if (layEvent === 'productSet') {
                var checkData = table.checkStatus('productTable').data;
                if (checkData.length > 1 || checkData.length === 0) {
                    febs.alert.warn('每次操作只能操作一行数据');
                    return;
                }
                febs.modal.open('工艺配置', 'modules/clothesType/productSet/' + checkData[0].id, {
                    btn: ['提交', '取消'],
                    area:['100%','100%'],
                    yes: function (index, layero) {
                        $('#art-set').find('#submit').trigger('click');
                    },
                    btn2: function () {
                        layer.closeAll();
                    }
                });
            }
        });
        function initproductTable() {
            tableIns = febs.table.init({
                elem: $view.find('table'),
                id: 'productTable',
                url: ctx + 'admin/product/list',
                toolbar:"#productToolbar",
                defaultToolbar:[],
                cols: [[
                    {type: 'checkbox'},
                    {type: 'numbers', title: '', width: 80},
                    {title: '操作', toolbar: '#productOption', minWidth: 200, align: 'center'},
                    {field: 'hotState', title: '推荐首页', templet: '#productHotStateSwitch', minWidth: 130,align:'center'},
                    {field: 'state', title: '状态', templet: '#productStateSwitch', minWidth: 130,align:'center'},
                    {field: 'productCategoryName', title: '产品分类', minWidth: 100,align:'center'},
                    {field: 'sort', title: '排序', minWidth: 100,align:'center'},
                    {field: 'code', title: '编码', minWidth: 100,align:'center'},
                    {field: 'name', title: '名称', minWidth: 100,align:'center'},
                    {field: 'iconImg',title: '小图标',
                        templet: function (d) {
                            return '<a lay-event="seeImage">' +
                                '<img id="seeImage' + d.id + '" src="' + d.iconImg +
                                '" alt="小图标" style="width: 50px; height: 50px; object-fit: cover; border-radius: 5px; cursor: pointer;">' +
                                '</a>';
                        },
                    minWidth: 150,align: 'center'},
                    {field: 'backImg',title: '背景图',
                        templet: function (d) {
                            return '<a lay-event="seeBackImage">' +
                                '<img id="seeBackImage' + d.id + '" src="' + d.backImg +
                                '" alt="小图标" style="width: 50px; height: 50px; object-fit: cover; border-radius: 5px; cursor: pointer;">' +
                                '</a>';
                        },
                    minWidth: 150,align: 'center'},
                    {field: 'companyId', title: '公司编码', minWidth: 150,align:'center'},
                ]]
            });
        }
        // 查询按钮
        $query.on('click', function () {
            var params = $.extend(getQueryParams(), {field: sortObject.field, order: sortObject.type});
            tableIns.reload({where: params, page: {curr: 1}});
        });
        // 刷新按钮
        $reset.on('click', function () {
            var categoryList = [];
            category.setValue(categoryList);
            $searchForm[0].reset();
            sortObject.type = 'null';
            tableIns.reload({where: getQueryParams(), page: {curr: 1}, initSort: sortObject});
        });
        // 获取查询参数
        function getQueryParams() {
            return {
                productCategoryId: category.getValue('valueStr'),
            };
        }
    })
</script>
src/main/resources/templates/febs/views/modules/ai/productCategory/add.html
New file
@@ -0,0 +1,179 @@
<div class="layui-fluid layui-anim febs-anim" id="febs-productCategory-add" lay-title="新增">
    <div class="layui-row febs-container">
        <div class="layui-col-md12">
            <div class="layui-fluid" id="productCategory-add">
                <form class="layui-form" action="" lay-filter="productCategory-add-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">
                            <div class="layui-tab-item layui-show">
                                <div class="layui-row layui-col-space10 layui-form-item">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label febs-form-item-require">名称:</label>
                                        <div class="layui-input-block">
                                            <input type="text" name="name" lay-verify="required"
                                                   placeholder="" autocomplete="off" class="layui-input">
                                        </div>
                                    </div>
                                </div>
                                <div class="layui-row layui-col-space10 layui-form-item">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label febs-form-item-require">编码:</label>
                                        <div class="layui-input-block">
                                            <input type="text" name="code" lay-verify="required"
                                                   placeholder="" autocomplete="off" class="layui-input">
                                        </div>
                                    </div>
                                </div>
                                <div class="layui-row layui-col-space10 layui-form-item">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label febs-form-item-require">排序:</label>
                                        <div class="layui-input-block">
                                            <input type="number" name="sort" lay-verify="required"
                                                   placeholder="" autocomplete="off" class="layui-input">
                                        </div>
                                    </div>
                                </div>
                                <div class="layui-row layui-col-space10 layui-form-item">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label febs-form-item-require">小图标:</label>
                                        <div class="layui-input-block">
                                            <div class="layui-upload">
                                                <button type="button" class="layui-btn layui-btn-normal layui-btn" id="productCategoryImageUploadButton">上传</button>
                                                <blockquote class="layui-elem-quote layui-quote-nm" style="margin-top: 10px;">
                                                    <div class="layui-upload-list" id="productCategoryImageUpload"></div>
                                                </blockquote>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                                <div class="layui-row layui-col-space10 layui-form-item febs-hide">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label">小图标链接:</label>
                                        <div class="layui-input-block">
                                            <input type="text" id="iconImg" lay-verify="required" name="iconImg" autocomplete="off" class="layui-input" readonly>
                                        </div>
                                    </div>
                                </div>
                                <div class="layui-row layui-col-space10 layui-form-item">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label febs-form-item-require">背景图片:</label>
                                        <div class="layui-input-block">
                                            <div class="layui-upload">
                                                <button type="button" class="layui-btn layui-btn-normal layui-btn" id="productCategoryBackImageUploadButton">上传</button>
                                                <blockquote class="layui-elem-quote layui-quote-nm" style="margin-top: 10px;">
                                                    <div class="layui-upload-list" id="productCategoryBackImageUpload"></div>
                                                </blockquote>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                                <div class="layui-row layui-col-space10 layui-form-item febs-hide">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label">小图标链接:</label>
                                        <div class="layui-input-block">
                                            <input type="text" id="backImg" lay-verify="required" name="backImg" autocomplete="off" class="layui-input" readonly>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                    <div class="layui-form-item febs-hide">
                        <button class="layui-btn" lay-submit="" lay-filter="productCategory-add-form-submit" id="submit">保存</button>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>
<!-- 表格操作栏 end -->
<script data-th-inline="javascript">
    layui.use(['febs', 'form', 'formSelects', 'validate', 'treeSelect', 'eleTree','dropdown', 'laydate', 'layedit', 'upload', 'element', 'table', 'xmSelect','jquery'], function () {
        var $ = layui.jquery,
            febs = layui.febs,
            layer = layui.layer,
            table = layui.table,
            formSelects = layui.formSelects,
            treeSelect = layui.treeSelect,
            form = layui.form,
            laydate = layui.laydate,
            eleTree = layui.eleTree,
            $view = $('#productCategory-add'),
            layedit = layui.layedit,
            upload = layui.upload,
            validate = layui.validate,
            element = layui.element;
        form.render();
        //图片上传
        upload.render({
            elem: '#productCategoryImageUploadButton'
            ,url: ctx + 'admin/goods/uploadFileBase64' //改成您自己的上传接口
            ,accept: 'file' //普通文件
            ,size: 10240 //限制文件大小,单位 KB
            ,before: function(obj){
                //预读本地文件示例,不支持ie8
                obj.preview(function(index, file, result){
                    $('#productCategoryImageUpload').html('<img src="'+ result +'" alt="'+ file.name +'" class="layui-upload-img single-image" style="width: 130px">')
                });
            }
            ,done: function(res){
                $("#iconImg").val(res.data.src);
            }
        });
        //图片上传
        upload.render({
            elem: '#productCategoryBackImageUploadButton'
            ,url: ctx + 'admin/goods/uploadFileBase64' //改成您自己的上传接口
            ,accept: 'file' //普通文件
            ,size: 10240 //限制文件大小,单位 KB
            ,before: function(obj){
                //预读本地文件示例,不支持ie8
                obj.preview(function(index, file, result){
                    $('#productCategoryBackImageUpload').html('<img src="'+ result +'" alt="'+ file.name +'" class="layui-upload-img single-image" style="width: 130px">')
                });
            }
            ,done: function(res){
                $("#backImg").val(res.data.src);
            }
        });
        form.on('submit(productCategory-add-form-submit)', function (data) {
            $.ajax({
                'url':ctx + 'admin/productCategory/add',
                '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-productCategory').find('#query').click();
                    }else{
                        febs.alert.warn(data.message);
                    }
                },
                'error':function () {
                    febs.alert.warn('服务器繁忙');
                }
            })
            return false;
        });
    });
</script>
src/main/resources/templates/febs/views/modules/ai/productCategory/info.html
New file
@@ -0,0 +1,213 @@
<div class="layui-fluid layui-anim febs-anim" id="febs-productCategory-Info" lay-title="编辑">
    <div class="layui-row febs-container">
        <div class="layui-col-md12">
            <div class="layui-fluid" id="productCategory-info">
                <form class="layui-form" action="" lay-filter="productCategory-info-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="id"
                                   placeholder="" autoComplete="off" class="layui-input febs-hide">
                            <div class="layui-tab-item layui-show">
                                <div class="layui-row layui-col-space10 layui-form-item">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label febs-form-item-require">名称:</label>
                                        <div class="layui-input-block">
                                            <input type="text" name="name" lay-verify="required"
                                                   placeholder="" autocomplete="off" class="layui-input">
                                        </div>
                                    </div>
                                </div>
                                <div class="layui-row layui-col-space10 layui-form-item">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label febs-form-item-require">编码:</label>
                                        <div class="layui-input-block">
                                            <input type="text" name="code" lay-verify="required"
                                                   placeholder="" autocomplete="off" class="layui-input">
                                        </div>
                                    </div>
                                </div>
                                <div class="layui-row layui-col-space10 layui-form-item">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label febs-form-item-require">排序:</label>
                                        <div class="layui-input-block">
                                            <input type="number" name="sort" lay-verify="required"
                                                   placeholder="" autocomplete="off" class="layui-input">
                                        </div>
                                    </div>
                                </div>
                                <div class="layui-row layui-col-space10 layui-form-item">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label febs-form-item-require">小图标:</label>
                                        <div class="layui-input-block">
                                            <div class="layui-upload">
                                                <button type="button" class="layui-btn layui-btn-normal layui-btn" id="productCategoryImageUploadButton">上传</button>
                                                <blockquote class="layui-elem-quote layui-quote-nm" style="margin-top: 10px;">
                                                    <div class="layui-upload-list" id="productCategoryImageUpload"></div>
                                                </blockquote>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                                <div class="layui-row layui-col-space10 layui-form-item febs-hide">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label">小图标链接:</label>
                                        <div class="layui-input-block">
                                            <input type="text" id="iconImg" lay-verify="required" name="iconImg" autocomplete="off" class="layui-input" readonly>
                                        </div>
                                    </div>
                                </div>
                                <div class="layui-row layui-col-space10 layui-form-item">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label febs-form-item-require">背景图片:</label>
                                        <div class="layui-input-block">
                                            <div class="layui-upload">
                                                <button type="button" class="layui-btn layui-btn-normal layui-btn" id="productCategoryBackImageUploadButton">上传</button>
                                                <blockquote class="layui-elem-quote layui-quote-nm" style="margin-top: 10px;">
                                                    <div class="layui-upload-list" id="productCategoryBackImageUpload"></div>
                                                </blockquote>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                                <div class="layui-row layui-col-space10 layui-form-item febs-hide">
                                    <div class="layui-col-lg6">
                                        <label class="layui-form-label">小图标链接:</label>
                                        <div class="layui-input-block">
                                            <input type="text" id="backImg" lay-verify="required" name="backImg" autocomplete="off" class="layui-input" readonly>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                    <div class="layui-form-item febs-hide">
                        <button class="layui-btn" lay-submit="" lay-filter="productCategory-info-form-submit" id="submit">保存</button>
                    </div>
                </form>
            </div>
        </div>
    </div>
</div>
<style>
    .blue-border {
        border-left-color: #2db7f5;
        font-size: 18px;
    }
    .layui-table-cell {
        height:auto;
    }
    .layui-upload-list {
        margin: 0 !important;
    }
    .multi-images {
        margin: 0 5px !important;
    }
</style>
<!-- 表格操作栏 end -->
<script data-th-inline="javascript">
    layui.use(['febs', 'form', 'validate','formSelects', 'table', 'upload'], function () {
        var $ = layui.jquery,
            febs = layui.febs,
            layer = layui.layer,
            table = layui.table,
            form = layui.form,
            $view = $('#productCategory-info'),
            aiProductCategory = [[${aiProductCategory}]],
            upload = layui.upload,
            validate = layui.validate;
        form.render();
        //图片上传
        upload.render({
            elem: '#productCategoryImageUploadButton'
            ,url: ctx + 'admin/goods/uploadFileBase64' //改成您自己的上传接口
            ,accept: 'file' //普通文件
            ,size: 10240 //限制文件大小,单位 KB
            ,before: function(obj){
                //预读本地文件示例,不支持ie8
                obj.preview(function(index, file, result){
                    $('#productCategoryImageUpload').html('<img src="'+ result +'" alt="'+ file.name +'" class="layui-upload-img single-image" style="width: 130px">')
                });
            }
            ,done: function(res){
                $("#iconImg").val(res.data.src);
            }
        });
        //图片上传
        upload.render({
            elem: '#productCategoryBackImageUploadButton'
            ,url: ctx + 'admin/goods/uploadFileBase64' //改成您自己的上传接口
            ,accept: 'file' //普通文件
            ,size: 10240 //限制文件大小,单位 KB
            ,before: function(obj){
                //预读本地文件示例,不支持ie8
                obj.preview(function(index, file, result){
                    $('#productCategoryBackImageUpload').html('<img src="'+ result +'" alt="'+ file.name +'" class="layui-upload-img single-image" style="width: 130px">')
                });
            }
            ,done: function(res){
                $("#backImg").val(res.data.src);
            }
        });
        setTimeout(() => {
            initproductCategoryInfo();
        }, 500);
        function initproductCategoryInfo() {
            form.val("productCategory-info-form", {
                "id": aiProductCategory.id,
                "name": aiProductCategory.name,
                "code": aiProductCategory.code,
                "iconImg": aiProductCategory.iconImg,
                "sort": aiProductCategory.sort,
            });
            $('#productCategoryImageUpload').append('<img src="' + aiProductCategory.iconImg + '" alt="" class="layui-upload-img single-image" style="width: 130px">');
            $("#iconImg").val(aiProductCategory.iconImg);
            $('#productCategoryBackImageUpload').append('<img src="' + aiProductCategory.backImg + '" alt="" class="layui-upload-img single-image" style="width: 130px">');
            $("#backImg").val(aiProductCategory.backImg);
        }
        form.on('submit(productCategory-info-form-submit)', function (data) {
            $.ajax({
                'url':ctx + 'admin/productCategory/update',
                '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-productCategory').find('#query').click();
                    }else{
                        febs.alert.warn(data.message);
                    }
                },
                'error':function () {
                    febs.alert.warn('服务器繁忙');
                }
            })
            return false;
        });
    });
</script>
src/main/resources/templates/febs/views/modules/ai/productCategory/list.html
New file
@@ -0,0 +1,287 @@
<div class="layui-fluid layui-anim febs-anim" id="febs-productCategory" lay-title="产品类型">
    <div class="layui-row febs-container">
        <div class="layui-col-md12">
            <div class="layui-card">
                <div class="layui-card-body febs-table-full">
                    <form class="layui-form layui-table-form" lay-filter="productCategory-table-form">
                        <div class="layui-row">
                            <div class="layui-col-md10">
                                <div class="layui-form-item">
                                </div>
                            </div>
                            <div class="layui-col-md2 layui-col-sm12 layui-col-xs12 table-action-area">
                                <div class="layui-btn layui-btn-sm layui-btn-primary febs-button-blue-plain table-action" id="query">
                                    <i class="layui-icon">&#xe848;</i>
                                </div>
                                <div class="layui-btn layui-btn-sm layui-btn-primary febs-button-green-plain table-action" id="reset">
                                    <i class="layui-icon">&#xe79b;</i>
                                </div>
                            </div>
                        </div>
                    </form>
                    <table lay-filter="productCategoryTable" lay-data="{id: 'productCategoryTable'}"></table>
                    <style type="text/css">
                        .layui-table-cell{
                            text-align:center;
                            height: auto;
                            white-space: nowrap; /*文本不会换行,在同一行显示*/
                            overflow: hidden; /*超出隐藏*/
                            text-overflow: ellipsis; /*省略号显示*/
                        }
                        .layui-table img{
                            max-width:100px
                        }
                        ::-webkit-scrollbar {
                            height: 20px !important;
                            background-color: #f4f4f4;
                        }
                    </style>
                </div>
            </div>
        </div>
    </div>
</div>
<script type="text/html" id="productCategoryToolbar">
    <div class="layui-btn-container">
        <button class="layui-btn layui-btn-sm layui-btn-primary febs-button-green-plain" shiro:hasPermission="categoryList:add" lay-event="productCategoryAdd">新增</button>
    </div>
</script>
<script type="text/html" id="productCategoryOption">
    <button class="layui-btn layui-btn-normal layui-btn-sm" type="button" shiro:hasPermission="categoryList:info" lay-event="productCategoryInfoEvent">编辑</button>
    <button class="layui-btn layui-btn-danger layui-btn-sm" type="button" shiro:hasPermission="categoryList:info" lay-event="productCategoryDeleteEvent">删除</button>
</script>
<script type="text/html" id="productCategoryStateSwitch">
    {{# if(d.state === 1) { }}
    <input type="checkbox" value={{d.id}} lay-text="启用|禁用" checked lay-skin="switch" lay-filter="productCategoryStateSwitch">
    {{# } else { }}
    <input type="checkbox" value={{d.id}} lay-text="启用|禁用" lay-skin="switch" lay-filter="productCategoryStateSwitch">
    {{# } }}
</script>
<script type="text/html" id="productCategoryHotStateSwitch">
    {{# if(d.hotState === 1) { }}
    <input type="checkbox" value={{d.id}} lay-text="启用|禁用" checked lay-skin="switch" lay-filter="productCategoryHotStateSwitch">
    {{# } else { }}
    <input type="checkbox" value={{d.id}} lay-text="启用|禁用" lay-skin="switch" lay-filter="productCategoryHotStateSwitch">
    {{# } }}
</script>
<style>
    .layui-form-onswitch {
        background-color: #5FB878 !important;
    }
</style>
<!-- 表格操作栏 end -->
<script data-th-inline="none" type="text/javascript">
    // 引入组件并初始化
    layui.use([ 'jquery', 'form', 'table', 'febs'], function () {
        var $ = layui.jquery,
            febs = layui.febs,
            form = layui.form,
            table = layui.table,
            $view = $('#febs-productCategory'),
            $query = $view.find('#query'),
            $reset = $view.find('#reset'),
            $searchForm = $view.find('form'),
            sortObject = {field: 'orderNum', type: null},
            tableIns;
        form.render();
        // 表格初始化
        initproductCategoryTable();
        // 初始化表格操作栏各个按钮功能
        table.on('tool(productCategoryTable)', function (obj) {
            console.log("触发事件:", obj.event); // 调试信息
            var data = obj.data,
                layEvent = obj.event;
            if (layEvent === 'seeImage') {
                var t = $view.find('#seeImage'+data.id+'');
                //页面层
                layer.open({
                    type: 1,
                    title: "小图标",
                    skin: 'layui-layer-rim', //加上边框
                    area: ['100%', '100%'], //宽高
                    shadeClose: true, //开启遮罩关闭
                    end: function (index, layero) {
                        return false;
                    },
                    content: '<div style="text-align:center"><img src="' + $(t).attr('src') + '" /></div>'
                });
            }
            if (layEvent === 'seeBackImage') {
                var t = $view.find('#seeBackImage'+data.id+'');
                //页面层
                layer.open({
                    type: 1,
                    title: "背景图片",
                    skin: 'layui-layer-rim', //加上边框
                    area: ['100%', '100%'], //宽高
                    shadeClose: true, //开启遮罩关闭
                    end: function (index, layero) {
                        return false;
                    },
                    content: '<div style="text-align:center"><img src="' + $(t).attr('src') + '" /></div>'
                });
            }
            if (layEvent === 'productCategoryInfoEvent') {
                if (data.state == 1){
                    febs.alert.warn('请先禁用这行数据');
                    return;
                }
                febs.modal.open('编辑','modules/ai/productCategory/info/' + data.id, {
                    btn: ['提交', '取消'],
                    area: ['100%', '100%'],
                    yes: function (index, layero) {
                        $('#febs-productCategory-Info').find('#submit').trigger('click');
                    },
                    btn2: function () {
                        layer.closeAll();
                    }
                });
            }
            if (layEvent === 'productCategoryDeleteEvent') {
                if (data.state == 1){
                    febs.alert.warn('请先禁用这行数据');
                    return;
                }
                febs.modal.confirm('删除', '确认删除?', function () {
                    productCategoryDeleteEvent(data.id);
                });
            }
        });
        form.on('switch(productCategoryStateSwitch)', function (data) {
            if (data.elem.checked) {
                productCategorySwitch(data.value,1,1);
            } else {
                productCategorySwitch(data.value,1,0);
            }
        })
        form.on('switch(productCategoryHotStateSwitch)', function (data) {
            if (data.elem.checked) {
                productCategorySwitch(data.value,2,1);
            } else {
                productCategorySwitch(data.value,2,0);
            }
        })
        function productCategorySwitch(id,type,state) {
            febs.get(ctx + 'admin/productCategory/changeState/' + id+'/'+ type+'/' + state, null, function (data) {
                febs.alert.success(data.message);
                $query.click();
            });
        }
        function productCategoryDeleteEvent(id) {
            febs.get(ctx + 'admin/productCategory/delete/' + id, null, function (data) {
                febs.alert.success(data.message);
                $query.click();
            });
        }
        // 初始化表格操作栏各个按钮功能
        table.on('toolbar(productCategoryTable)', function (obj) {
            let data = obj.data,
                layEvent = obj.event;
            if(layEvent === 'productCategoryAdd'){
                febs.modal.open('新增', 'modules/ai/productCategory/add/', {
                    btn: ['提交', '取消'],
                    area:['100%','100%'],
                    yes: function (index, layero) {
                        $('#febs-productCategory-add').find('#submit').trigger('click');
                    },
                    btn2: function () {
                        layer.closeAll();
                    }
                });
            }
            if (layEvent === 'productSet') {
                var checkData = table.checkStatus('productCategoryTable').data;
                if (checkData.length > 1 || checkData.length === 0) {
                    febs.alert.warn('每次操作只能操作一行数据');
                    return;
                }
                febs.modal.open('工艺配置', 'modules/clothesType/productSet/' + checkData[0].id, {
                    btn: ['提交', '取消'],
                    area:['100%','100%'],
                    yes: function (index, layero) {
                        $('#art-set').find('#submit').trigger('click');
                    },
                    btn2: function () {
                        layer.closeAll();
                    }
                });
            }
        });
        function initproductCategoryTable() {
            tableIns = febs.table.init({
                elem: $view.find('table'),
                id: 'productCategoryTable',
                url: ctx + 'admin/productCategory/list',
                toolbar:"#productCategoryToolbar",
                defaultToolbar:[],
                cols: [[
                    {type: 'checkbox'},
                    {type: 'numbers', title: '', width: 80},
                    {title: '操作', toolbar: '#productCategoryOption', minWidth: 200, align: 'center'},
                    {field: 'hotState', title: '推荐首页', templet: '#productCategoryHotStateSwitch', minWidth: 130,align:'center'},
                    {field: 'state', title: '状态', templet: '#productCategoryStateSwitch', minWidth: 130,align:'center'},
                    {field: 'sort', title: '排序', minWidth: 100,align:'center'},
                    {field: 'code', title: '编码', minWidth: 100,align:'center'},
                    {field: 'name', title: '名称', minWidth: 100,align:'center'},
                    {field: 'iconImg',title: '小图标',
                        templet: function (d) {
                            return '<a lay-event="seeImage">' +
                                '<img id="seeImage' + d.id + '" src="' + d.iconImg +
                                '" alt="小图标" style="width: 50px; height: 50px; object-fit: cover; border-radius: 5px; cursor: pointer;">' +
                                '</a>';
                        },
                    minWidth: 150,align: 'center'},
                    {field: 'backImg',title: '背景图',
                        templet: function (d) {
                            return '<a lay-event="seeBackImage">' +
                                '<img id="seeBackImage' + d.id + '" src="' + d.backImg +
                                '" alt="小图标" style="width: 50px; height: 50px; object-fit: cover; border-radius: 5px; cursor: pointer;">' +
                                '</a>';
                        },
                    minWidth: 150,align: 'center'},
                    {field: 'companyId', title: '公司编码', minWidth: 150,align:'center'},
                ]]
            });
        }
        // 查询按钮
        $query.on('click', function () {
            var params = $.extend(getQueryParams(), {field: sortObject.field, order: sortObject.type});
            tableIns.reload({where: params, page: {curr: 1}});
        });
        // 刷新按钮
        $reset.on('click', function () {
            $searchForm[0].reset();
            sortObject.type = 'null';
            tableIns.reload({where: getQueryParams(), page: {curr: 1}, initSort: sortObject});
        });
        // 获取查询参数
        function getQueryParams() {
            return {
            };
        }
    })
</script>