Просмотр исходного кода

feat:ai后端流程跑通
需求->数据表->表结构->查询sql->数据处理->返回图表
两种实现方式,同步和异步
异步使用线程池实现

yangyi 5 месяцев назад
Родитель
Сommit
4b9fcec694

+ 38 - 1
ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/controller/TestController.java

@@ -1,19 +1,56 @@
 package org.dromara.ai.controller;
 
 import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.ai.domain.MessageContext;
 import org.dromara.ai.handler.AIHandler;
 import org.dromara.common.core.domain.R;
+import org.springframework.http.MediaType;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
 
+import java.io.IOException;
+
+@Slf4j
 @RequiredArgsConstructor
 @RestController
 @RequestMapping("/ai")
 public class TestController {
     private final AIHandler aiHandler;
+    private final ThreadPoolTaskExecutor taskExecutor;
     @GetMapping("/get")
     public R test(String message) {
-        return R.ok(aiHandler.handle(message));
+        return R.ok(aiHandler.handle(MessageContext.createDefault(message)));
+    }
+
+    @GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
+    public SseEmitter streamData(String message) {
+        // 创建 SSE 发射器,设置超时时间(例如 1 分钟)
+        SseEmitter emitter = new SseEmitter(60_000L);
+        emitter.onCompletion(()->{
+            log.info("sse 完成");
+        });
+        emitter.onError(e ->{
+            log.error("sse 出错",e);
+            try {
+                emitter.send("服务器发生错误");
+            } catch (IOException ex) {
+                log.error("SSE 推动数据发生错误");
+                throw new RuntimeException(ex);
+            }
+            emitter.completeWithError(e);
+        });
+        emitter.onTimeout(()->{
+            log.info("sse 超时");
+            emitter.complete();
+        });
+        // 创建新线程,防止主程序阻塞
+        taskExecutor.execute(()->{
+            aiHandler.handle(MessageContext.createDefault(message),emitter);
+        });
+        return emitter;
     }
 }

+ 50 - 0
ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/domain/MessageContext.java

@@ -0,0 +1,50 @@
+package org.dromara.ai.domain;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 消息上下文
+ */
+@Data
+public class MessageContext {
+    /**
+     * 保留字段
+     */
+    private Long id;
+    /**
+     * 信息内容
+     */
+    private String message;
+    /**
+     * 状态
+     */
+    private Integer status = 0;
+    /**
+     * 数据表名列表
+     */
+    private List<String> tableNames;
+    /**
+     * 数据表结构列表
+     */
+    private List<String> tableStruct;
+    /**
+     * sql
+     */
+    private String sql;
+    /**
+     * csv格式的数据
+     */
+    private String data;
+    /**
+     * 最终的结果
+     */
+    private Object result;
+
+    public static MessageContext createDefault(String message){
+        MessageContext messageContext = new MessageContext();
+        messageContext.setMessage(message);
+        return messageContext;
+    }
+}

+ 23 - 16
ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/handler/AIHandler.java

@@ -1,49 +1,49 @@
 package org.dromara.ai.handler;
 
+import org.dromara.ai.domain.MessageContext;
+import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
+
 import java.util.List;
 
 public interface AIHandler {
-    default Object handle(String content) {
+    default Object handle(MessageContext messageContext) {
         //获取涉及的表名,通过LLM
-        List<String> tableNames = getTableNames(content);
+        List<String> tableNames = getTableNames(messageContext);
         //获取sql,通过LLM
-        String sql = getSQL(content,tableNames);
+        String sql = getSQL(messageContext);
         //查数据,通过JDBCTemplate
-        String data = getData(content, sql);
+        String data = getData(messageContext);
         //处理数据,通过LLM
-        return dataHandler(content, data);
+        return dataHandler(messageContext);
     };
 
     /**
      * 获取涉及的表名
-     * @param context 用户需求上下文
+     * @param messageContext 用户需求上下文
      * @return 返回涉及表名的列表
      */
-    List<String> getTableNames(String context);
+    List<String> getTableNames(MessageContext messageContext);
 
     /**
      * 获取sql
-     * @param context 用户需求上下文
-     * @param tableNames 涉及的表名
+     * @param messageContext 用户需求上下文
      * @return 返回可执行的查询sql
      */
-    String getSQL(String context, List<String> tableNames);
+    String getSQL(MessageContext messageContext);
 
     /**
      * 查数据,
-     * @param context 用户需求上下文
-     * @param sql sql
+     * @param messageContext 用户需求上下文
      * @return 返回csv格式
      */
-    String getData(String context,String sql);
+    String getData(MessageContext messageContext);
 
     /**
      * 处理数据
-     * @param context
-     * @param data
+     * @param messageContext
      * @return
      */
-    Object dataHandler(String context,String data);
+    Object dataHandler(MessageContext messageContext);
 
     /**
      * 发送消息,调用ai
@@ -51,4 +51,11 @@ public interface AIHandler {
      * @return 返回ai生成的结果
      */
     String sendMessage(String prompt);
+
+    /**
+     * 异步
+     * @param messageContext 用户需求上下文
+     * @param emitter 数据流发送对象
+     */
+    void handle(MessageContext messageContext, SseEmitter emitter);
 }

+ 131 - 57
ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/handler/AIHandlerImpl.java

@@ -3,63 +3,81 @@ package org.dromara.ai.handler;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.ai.domain.MessageContext;
 import org.dromara.ai.domain.deepseek.DeepSeekHttpResponseData;
 import org.dromara.ai.util.DeepSeekAIUtil;
+import org.dromara.common.redis.utils.RedisUtils;
 import org.springframework.jdbc.core.JdbcTemplate;
 import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
 
+import java.io.*;
+import java.nio.charset.StandardCharsets;
 import java.time.LocalDateTime;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
+import java.util.*;
+@Slf4j
 @RequiredArgsConstructor
 @Component
 public class AIHandlerImpl implements AIHandler {
     private final ObjectMapper objectMapper;
     private final JdbcTemplate jdbcTemplate;
     @Override
-    public List<String> getTableNames(String context) {
-        return List.of();
+    public List<String> getTableNames(MessageContext messageContext) {
+        messageContext.setStatus(1);
+        String keyName = "ai_tableNamesList";
+        List<String> tableNamesList = RedisUtils.getCacheList(keyName);
+        if (Objects.isNull(tableNamesList) || tableNamesList.isEmpty()) {
+            tableNamesList = loadTableNameProperty();
+            RedisUtils.setCacheList(keyName, tableNamesList);
+        }
+        StringBuilder tableNamesStringBuilder = new StringBuilder("所有的数据表名称和作用如下:\n");
+        for (String tableName : tableNamesList) {
+            tableNamesStringBuilder.append(tableName).append("\n");
+        }
+        String prompt = new StringBuilder("用户需求:\n").append(messageContext.getMessage())
+            .append("\n")
+            .append(tableNamesStringBuilder)
+            .append("请你根据用户需求和现有的数据表,选择满足用户需求的数据表,返回数据表的名称,以json的格式返回{'tableNames':[]}").toString();
+        String json = sendMessage(prompt);
+        List<String> tableNames = new ArrayList<>();
+        try {
+            tableNames =  (List) objectMapper.readValue(json,Map.class).get("tableNames");
+        } catch (JsonProcessingException e) {
+            throw new RuntimeException(e);
+        }
+        messageContext.setTableNames(tableNames);
+        return tableNames;
     }
 
     @Override
-    public String getSQL(String context, List<String> tableNames) {
-        //todo:获取表的结构
-        String struct = "CREATE TABLE `fa_kuyou_user_order`  (\n" +
-            "  `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,\n" +
-            "  `user_id` int(11) NULL DEFAULT NULL COMMENT '用户ID',\n" +
-            "  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '姓名',\n" +
-            "  `phone` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '联系方式',\n" +
-            "  `address` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '详细地址',\n" +
-            "  `order_num` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '订单号',\n" +
-            "  `order_price` float(11, 2) NOT NULL DEFAULT 0.00 COMMENT '订单价格',\n" +
-            "  `status` tinyint(3) UNSIGNED NULL DEFAULT 0 COMMENT '状态:0=待审核,1=已审核,2=待发货,3=待收货,4=已收货,5=退款中,6=退款完成,7=交易取消,8=交易完成',\n" +
-            "  `remark` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '备注',\n" +
-            "  `pay_status` tinyint(3) UNSIGNED NULL DEFAULT 0 COMMENT '支付状态:0=支付中,1=支付成功,2=支付失败',\n" +
-            "  `pay_time` datetime(0) NULL DEFAULT NULL COMMENT '付款时间',\n" +
-            "  `diver_type` enum('1','2') CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '1' COMMENT '配送类型:1=专业配送,2=到店自取',\n" +
-            "  `store_id` int(11) NULL DEFAULT NULL COMMENT '门店ID',\n" +
-            "  `diver_id` int(11) NULL DEFAULT NULL COMMENT '司机ID',\n" +
-            "  `diver_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '司机姓名',\n" +
-            "  `diver_phone` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '司机电话',\n" +
-            "  `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',\n" +
-            "  `update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',\n" +
-            "  `delete_time` datetime(0) NULL DEFAULT NULL COMMENT '删除时间',\n" +
-            "  `hide` int(11) NOT NULL DEFAULT 0,\n" +
-            "  `note` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT '备注',\n" +
-            "  `confirm_delivery_time` datetime(0) NULL DEFAULT NULL COMMENT '确认收货时间',\n" +
-            "  `factory_user_id` int(10) NOT NULL DEFAULT 0,\n" +
-            "  `order_status` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,\n" +
-            "  `tenant_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,\n" +
-            "  `create_dept` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,\n" +
-            "  `create_by` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,\n" +
-            "  `update_by` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,\n" +
-            "  PRIMARY KEY (`id`) USING BTREE,\n" +
-            "  INDEX `order_num`(`order_num`) USING BTREE\n" +
-            ") ENGINE = InnoDB AUTO_INCREMENT = 1909434245425213445 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '订单表' ROW_FORMAT = Dynamic;";
+    public String getSQL(MessageContext messageContext) {
+        messageContext.setStatus(2);
+        String keyName = "ai_tableStructMap";
+        StringBuilder structBuilder = new StringBuilder();
+        List<String> tableNames = messageContext.getTableNames();
+        for (String tableName : tableNames) {
+            Object cacheMapValue = RedisUtils.getCacheMapValue(keyName, tableName);
+            String struct = "";
+            //redis中没有数据,从配置文件中拿
+            if (Objects.isNull(cacheMapValue)) {
+                Properties properties = new Properties();
+                try {
+                    properties.load(this.getClass().getResourceAsStream("/ai/tableStruct.properties"));
+                } catch (IOException e) {
+                    log.error("读取文件失败: {}", e.getMessage());
+                    throw new RuntimeException(e);
+                }
+                struct = properties.getProperty(tableName);
+                RedisUtils.setCacheMapValue(keyName, tableName, struct);
+            }else{
+                struct = (String) cacheMapValue;
+            }
+            structBuilder.append(struct).append("\n");
+        }
+        String struct = structBuilder.toString();
         String prompt = new StringBuilder("用户需求:\n")
-            .append(context)
+            .append(messageContext.getMessage())
             .append("涉及的表和结构如下:\n")
             .append(struct)
             .append("\n")
@@ -67,23 +85,25 @@ public class AIHandlerImpl implements AIHandler {
             .append(LocalDateTime.now())
             .append("\n")
             .append("请根据用户需求,生成对应的完整的可用的查询数据SQL语句,最大数据量限制为一千条,返回json格式的字符串{“sql”:“”}").toString();
-        String response = sendMessage(prompt);
+//            .append("请根据用户需求,生成对应的完整的可用的查询数据SQL语句,涉及到时间比较时使用'yy-mm-dd HH:MM:ss'格式字符串,最大数据量限制为一千条,返回json格式的字符串{“sql”:“”}").toString();
+        String json = sendMessage(prompt);
         String sql = null;
         try {
-            Map map = objectMapper.readValue(response, Map.class);
+            Map map = objectMapper.readValue(json, Map.class);
             sql = (String) map.get("sql");
-            System.out.println("sql = " + sql);
         } catch (JsonProcessingException e) {
-            System.err.println("response = " + response);
+            log.error("LLM处理数据返回的数据格式错误: {}", e.getMessage());
             throw new RuntimeException(e);
         }
+        log.info("sql = {}", sql);
+        messageContext.setSql(sql);
         return sql;
     }
 
     @Override
-    public String getData(String content, String sql) {
-        System.out.println("sql = \n" + sql);
-        List<Map<String,Object>> maps = jdbcTemplate.queryForList(sql);
+    public String getData(MessageContext messageContext) {
+        messageContext.setStatus(3);
+        List<Map<String,Object>> maps = jdbcTemplate.queryForList(messageContext.getSql());
         StringBuilder dataStringBuilder = new StringBuilder();
         StringBuilder titleStringBuilder = new StringBuilder();
         for (int i = 0; i < maps.size(); i++) {
@@ -98,26 +118,31 @@ public class AIHandlerImpl implements AIHandler {
             }
             dataStringBuilder.append("\n");
         }
-        return titleStringBuilder + "\n" + dataStringBuilder;
+        String data = titleStringBuilder + "\n" + dataStringBuilder;
+        messageContext.setData(data);
+        return data;
     }
 
     @Override
-    public Object dataHandler(String context, String data) {
-        System.out.println("data = \n" + data);
-        String prompt = new StringBuilder("用户需求:\n").append(context)
+    public Object dataHandler(MessageContext messageContext) {
+        messageContext.setStatus(4);
+        String data = messageContext.getData();
+        log.info("data = \n{}", data);
+        String prompt = new StringBuilder("用户需求:\n").append(messageContext.getMessage())
             .append("涉及的数据如下:\n").append(data)
             .append("\n")
-            .append("请根据用户需求和数据给出结论(中文的),并选择表格或者图表(折线图、柱状图、饼图、热力图)将数据展示出来,表格状态为1;图标状态为2,表格或者图表使用svg格式的xml,以json的格式返回.{'conclusion':'','data':'','status':0}").toString();
+//            .append("请根据用户需求和数据给出结论(中文的),并选择表格或者图表(折线图、柱状图、饼图、热力图)将数据展示出来,表格状态为1;图标状态为2,表格或者图表使用svg格式的xml,分辨率为1920*1080,以json的格式返回.{'conclusion':'','data':'','status':0}").toString();
 //            .append("请根据用户需求和数据给出结论(中文的),并选择表格或者图表(折线图、柱状图、饼图、热力图)将数据展示出来,表格状态为1,markdown格式的字符串;图标状态为2,echarts的配置对象json格式的字符串,以json的格式返回.{'conclusion':'','data':'','status':0}").toString();
-        String mes = sendMessage(prompt);
-        System.out.println("mes = " + mes);
+            .append("请根据用户需求和数据给出结论(中文的),并选择表格或者图表(折线图、柱状图、饼图、热力图)将数据展示出来,表格状态为1,使用HTML的table标签;图标状态为2,echarts的配置对象json格式的字符串,以json的格式返回.{'conclusion':'','data':'','status':0}").toString();
+        String json = sendMessage(prompt);
         Map map = null;
         try {
-            map = objectMapper.readValue(mes, Map.class);
+            map = objectMapper.readValue(json, Map.class);
         } catch (JsonProcessingException e) {
-            System.err.println("LLM处理数据返回的数据格式错误:");
+            log.error("LLM处理数据返回的数据格式错误: {}", e.getMessage());
             throw new RuntimeException(e);
         }
+        messageContext.setResult(map);
         return map;
     }
 
@@ -126,4 +151,53 @@ public class AIHandlerImpl implements AIHandler {
         DeepSeekHttpResponseData deepSeekHttpResponseData = DeepSeekAIUtil.doChat(DeepSeekAIUtil.createDefaultRequestData(prompt));
         return  deepSeekHttpResponseData.getChoices().get(0).getMessage().getContent();
     }
+
+    @Override
+    public void handle(MessageContext messageContext, SseEmitter emitter) {
+        try {
+            //获取涉及的表名,通过LLM
+            emitter.send("1正在分析涉及的数据表");
+            List<String> tableNames = getTableNames(messageContext);
+            //获取sql,通过LLM
+            emitter.send("2正在组织sql");
+            String sql = getSQL(messageContext);
+            //查数据,通过JDBCTemplate
+            emitter.send("3正在查询数据");
+            String data = getData(messageContext);
+            //处理数据,通过LLM
+            emitter.send("4正在处理数据");
+            Object object = dataHandler(messageContext);
+            emitter.send(object);
+        } catch (IOException e) {
+            log.error("sseEmitter error: {}", e.getMessage());
+            throw new RuntimeException(e);
+        }
+        emitter.complete();
+    }
+
+    /**
+     * 从文件中获取涉及的表名
+     * @return
+     */
+    private List<String> loadTableNameProperty() {
+        List<String> tableNamesList = new ArrayList<>();
+        InputStream inputStream = this.getClass().getResourceAsStream("/ai/tableNames.properties");
+        InputStreamReader a = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
+        Properties properties = new Properties();
+
+        try {
+            properties.load(a);
+        } catch (IOException e) {
+            System.err.println("读取tables.properties文件失败");
+            throw new RuntimeException(e);
+        }
+        Enumeration<Object> keys = properties.keys();
+        Iterator<Object> iterator = keys.asIterator();
+        while (iterator.hasNext()) {
+            String key = iterator.next().toString();
+            String property = properties.getProperty(key);
+            tableNamesList.add(key + ":" + property);
+        }
+        return tableNamesList;
+    }
 }

+ 2 - 2
ruoyi-modules/ruoyi-ai/src/main/java/org/dromara/ai/util/DeepSeekAIUtil.java

@@ -28,9 +28,9 @@ public class DeepSeekAIUtil {
             .body(json)
             .execute();
         String body = httpResponse.body();
-        int index = body.indexOf('i');
+        int index = body.indexOf("id");
         DeepSeekHttpResponseData httpResponseData = null;
-        if (index == 2){
+        if (index != -1 && index < 10){
             //调用成功
             try {
                 httpResponseData = objectMapper.readValue(body, DeepSeekHttpResponseData.class);

+ 177 - 0
ruoyi-modules/ruoyi-ai/src/main/resources/ai/tableNames.properties

@@ -0,0 +1,177 @@
+#\u8868\u540D=\u8868\u7684\u4F5C\u7528\u63CF\u8FF0
+#Thu Apr 10 11:18:44 CST 2025
+fa_kuyou_customers_user=客户联系人
+fa_kuyou_split_temp=拆卸单模板
+fa_kuyou_picking=生产领料单
+fa_template_dev_order_log=开发订单操作日志
+fa_kuyou_goods_batch_stock=商品批次库存表
+fa_kuyou_adjustment_info=成本调整单详情
+fa_kuyou_mrp=mrp计算
+fa_user_score_log=会员积分变动表
+fa_kuyou_split_info_temp=拆卸单详情模板
+fa_kuyou_user_ordersaleinfo=订货单详情关联表
+fa_kuyou_user_orderinfo=订货单详情表
+fa_kuyou_aux_combination=辅助属性组合
+fa_kuyou_goods_type_extend=商品分类扩展
+fa_kuyou_bom_info=bom商品
+fa_kuyou_settlementtype=结算方式表
+fa_kuyou_sales_invoicing=销货开票表
+fa_kuyou_goods_batch_exchange_info=商品退货批次详情表
+fa_kuyou_goods_sales_order=销货订单
+fa_kuyou_goods_exchange=购货退货单
+fa_kuyou_enclosure_log=原始单据关联表
+fa_kuyou_suppliers_user=供应商联系人
+fa_ykjp_product_unit=商品单位
+fa_kuyou_goods_saleord_info=销售订单关联
+fa_kuyou_address=发货地址表
+fa_kuyou_outsourcing_info=委外加工单详情
+fa_kuyou_goods_exchange_info=退货明细
+fa_kuyou_collection_sale=收款单单据信息
+fa_kuyou_allocation_info=调拨单详情
+fa_kuyou_goods_sales_exchange_info=退货明细
+fa_kuyou_suppliers=供应商表
+fa_kuyou_goods_batch_info=商品批次详情表
+fa_ykjp_product_type=商品分类
+fa_kuyou_production=生产单
+fa_kuyou_goods_batch_salee_info=商品退货批次详情表
+fa_kuyou_goods_serial=商品序列号
+fa_ykjp_info_supplier=供应商表
+fa_kuyou_factory_user=联系人
+fa_kuyou_writeoff_log=核销单据信息
+fa_kuyou_collection_account=收款账户信息
+fa_user=会员表
+fa_kuyou_bom=bom清单
+fa_kuyou_accounts_transfer=资金转账单
+fa_user_money_log=会员余额变动表
+fa_kuyou_adjustment=成本调整单
+fa_kuyou_goods_store_info=入库
+fa_kuyou_auxiliary=商品辅助属性
+fa_kuyou_dibu=款号
+fa_kuyou_goods_stock=商品库存表
+fa_kuyou_inventory_serial=序列号盘点
+fa_kuyou_accounts_copewith=应付账款明细表
+fa_kuyou_plan_info=计划详情
+fa_kuyou_bom_procedure=bom工序
+fa_template_remark=开发订单注释
+fa_kuyou_production_info=生产单详情
+fa_kuyou_unit_conversion=计量单位换算
+fa_kuyou_goods_sales=销货单
+fa_kuyou_goods_saleexc_info=退单关联
+fa_kuyou_garment_factory=成衣厂
+fa_kuyou_unit=计量单位
+fa_kuyou_assemble=组装单
+fa_kuyou_user_ordersale=订货单关联表
+fa_kuyou_stores=仓库表
+fa_kuyou_warehouse=其他出库单
+fa_kuyou_picking_info=领料单详情
+fa_kuyou_writeoff_info=核销单据信息
+fa_china_area=地区表
+fa_template_dahuo_order_log=开发订单大货日志
+fa_kuyou_goods_sales_log=销货单日志表
+fa_kuyou_goods_salethird=三方销售订单关联
+fa_kuyou_user_address=收货地址表
+fa_kuyou_expenditure_info=其他支出单详情
+fa_kuyou_banner_type=轮播位表
+fa_kuyou_goods_saleord=销售订单关联
+fa_kuyou_user_order_template=订单烧花
+fa_kuyou_goods_batch_sale_info=商品销售批次详情表
+fa_kuyou_allocation=调拨单
+fa_kuyou_user_order=订单表
+fa_kuyou_inventory_info=盘点详情
+fa_kuyou_item_name_template=大货名称
+fa_kuyou_goods_attr=商品属性价格表
+fa_kuyou_payment=付款单
+fa_kuyou_payment_account=付款账户信息
+fa_kuyou_warehousing=其他入库单
+fa_kuyou_user_cart=购物车表
+fa_kuyou_attr_combination=属性组合
+fa_kuyou_assemble_temp=组装单模板
+fa_kuyou_spendingtype=支出类别表
+fa_kuyou_user=会员表
+fa_kuyou_goods_sales_order_info=销货订单详情
+fa_kuyou_user_history=浏览历史表
+fa_kuyou_cost=采购销售费用清单
+fa_kuyou_collection=收款单
+fa_kuyou_third_account=接口账号表
+fa_kuyou_goods_gongyi=款号
+fa_kuyou_plan=生产计划
+fa_user_group=会员组表
+fa_kuyou_goods_batch_log=批次跟踪表
+fa_kuyou_income_type=收入类别表
+fa_kuyou_goods_type=商品分类
+fa_kuyou_assemble_info_temp=组装单详情模板
+fa_kuyou_goods_exchangethird=三方销售退货单关联
+fa_kuyou_expend_type=支出类别表
+fa_kuyou_split_info=拆卸单详情
+fa_kuyou_number_orders_placed=下单码数
+fa_template_dev_order=开发订单
+fa_auth_group=分组表
+fa_kuyou_accounts_transfer_info=转账单详情
+fa_kuyou_material_info=退料单详情
+fa_kuyou_writeoff_source=核销单源单
+fa_kuyou_accounts_receivable=应收账款明细表
+fa_kuyou_workshop=车间表
+fa_kuyou_goods_price=商品价格表
+fa_area=地区表
+fa_kuyou_outsourcing=委外加工单
+fa_kuyou_customertype=客户分类表
+fa_kuyou_writeoff=核销单
+fa_kuyou_goods_purchase_order_info=购货订单详情
+fa_kuyou_customer_goodscoding=客户物料编码
+fa_kuyou_goods_purord_info=订单关联
+fa_kuyou_invoicing=采购开票表
+fa_kuyou_sales_payment_log=销货收款一览表
+fa_kuyou_material=生产退料单
+fa_kuyou_acceptance=验收单
+fa_kuyou_mrp_info=mrp详情
+fa_kuyou_enclosure=原始单据表
+fa_kuyou_inventory=盘点
+fa_kuyou_checkout=结账表
+fa_kuyou_goods_sales_info=商品销售明细
+fa_kuyou_goods_purchase_order=购货订单
+fa_kuyou_goods_period=商品期初表
+fa_kuyou_driver=司机表
+fa_kuyou_flower_pattern=花型号
+fa_kuyou_payment_log=采购付款一览表
+fa_kuyou_goods_sales_summary=销售汇总表
+fa_kuyou_goods_excpur=退单关联
+fa_kuyou_factory=委外工厂表
+fa_kuyou_branch_factory=
+fa_kuyou_goods_log=商品收发明细表
+fa_kuyou_supplyertype=供应商分类
+fa_kuyou_assemble_info=组装单详情
+fa_kuyou_goods_purchase_summary=采购汇总表
+fa_kuyou_checkout_info=结账详情表
+fa_kuyou_customerlevel=客户等级
+fa_kuyou_warehouse_info=其他出库单详情
+fa_kuyou_sales_invoicing_log=销货开票表
+fa_kuyou_item_name=款号
+fa_kuyou_user_applet=会员常用设置表
+fa_kuyou_invoicing_log=采购开票表
+fa_kuyou_goods_stock_warning=商品库存预警表
+fa_template_dahuo_order=大货订单
+fa_kuyou_goods_sales_exchange=销货退货单
+fa_kuyou_accounts_log=账户记录表
+fa_kuyou_inventory_serial_info=序列号盘点详情
+fa_kuyou_acceptance_info=验收单详情
+fa_kuyou_expenditure=其他支出单
+fa_kuyou_area=地区表
+fa_kuyou_income=其他收入单
+fa_kuyou_goods_excpur_info=退单关联
+fa_kuyou_split=拆卸单
+fa_kuyou_goods_purchase=购货单
+fa_kuyou_plan_log=
+fa_kuyou_goods=商品表
+fa_kuyou_goods_batch=商品批次表
+fa_kuyou_accounts=账户表
+fa_kuyou_goods_serial_log=商品序列号跟踪
+fa_kuyou_user_sets=订货通设置
+fa_kuyou_goods_sales_profit=销售利润表
+fa_kuyou_payment_sale=付款单单据信息
+fa_kuyou_goods_purchase_info=商品采购明细
+fa_kuyou_income_info=其他收入单详情
+fa_kuyou_goods_saleexc=退单关联
+fa_kuyou_goods_purord=订单关联
+fa_kuyou_warehousing_info=其他入库单详情
+fa_kuyou_staff_member=职员表
+fa_kuyou_customers=客户表

Разница между файлами не показана из-за своего большого размера
+ 4 - 0
ruoyi-modules/ruoyi-ai/src/main/resources/ai/tableStruct.properties


Некоторые файлы не были показаны из-за большого количества измененных файлов