2026-08-04 cfbddbd52f8c4d35b06c8b7e7e68b6cf22ce400a
feat(mes): 支持工艺流程工序产出产品多选功能

- 修改 MesProRouteProcessDO 和 MesProWorkOrderProcessDO 中 outputItemId 字段类型为 String
- 新增 MesProRouteProcessSaveReqVO 和 MesProWorkOrderProcessSaveReqVO 的 outputItemIds 字段
- 在控制器中解析逗号分隔的字符串并转换为产品物料编号列表
- 支持向前兼容,保留 outputItemId、outputItemCode、outputItemName 字段
- 更新相关服务类中的创建和更新逻辑以处理多选产品
- 修改 AI 知识库向量库配置,添加 Milvus 向量存储配置
- 优化向量库批量写入逻辑,限制单批次最多 10 条记录
- 更新售后工单模块,集成附件上传和存储功能
- 添加 StorageRecordTypeEnum 枚举项 aftersales_after_sale_ticket 和 aftersales_after_sale_return
已修改25个文件
已添加1个文件
462 ■■■■ 文件已修改
docs/route_process_multi_output.md 145 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-aftersales/src/main/java/cn/iocoder/yudao/module/aftersales/controller/admin/ticket/AfterSaleTicketController.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-aftersales/src/main/java/cn/iocoder/yudao/module/aftersales/controller/admin/ticket/vo/ticket/AfterSaleTicketRespVO.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-aftersales/src/main/java/cn/iocoder/yudao/module/aftersales/controller/admin/ticket/vo/ticket/AfterSaleTicketSaveReqVO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-aftersales/src/main/java/cn/iocoder/yudao/module/aftersales/service/ticket/AfterSaleTicketServiceImpl.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/AiAutoConfiguration.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/YudaoAiProperties.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/AiModelFactoryImpl.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/MesProRouteProcessController.java 55 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/process/MesProRouteProcessRespVO.java 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/process/MesProRouteProcessSaveReqVO.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkOrderBomController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkOrderProcessController.java 49 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/process/MesProWorkOrderProcessRespVO.java 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/process/MesProWorkOrderProcessSaveReqVO.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/route/MesProRouteProcessDO.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/workorder/MesProWorkOrderProcessDO.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/mps/MesProMpsServiceImpl.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProcessServiceImpl.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskServiceImpl.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkOrderProcessServiceImpl.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/storage/StorageRecordTypeEnum.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-server/src/main/resources/application-dev.yaml 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-server/src/main/resources/application-local.yaml 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-server/src/main/resources/application-test.yaml 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/route_process_multi_output.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,145 @@
# å·¥è‰ºæµç¨‹å·¥åº"产出产品"支持多选 - å‰åŽç«¯æ”¹åŠ¨æ€»ç»“
## çŠ¶æ€ï¼šå·²å®Œæˆ
前端和后端改动均已实施,编译验证通过。
## æ¶‰åŠé¡µé¢
- ç”Ÿäº§ç®¡æŽ§ â†’ äº§å“è®¾è®¡ â†’ å·¥è‰ºæµç¨‹ â†’ å·¥åºç¼–辑弹窗(工艺路线工序)
- ç”Ÿäº§ç®¡æŽ§ â†’ ç”Ÿäº§å·¥å• â†’ å·¥åºç¼–辑弹窗(工单工序)
- ç”Ÿäº§ç®¡æŽ§ â†’ ç”Ÿäº§å·¥å• â†’ å·¥åºåˆ—表展示
## API å˜æ›´
### è¯·æ±‚参数变更
**`MesProRouteProcessSaveReqVO`(工艺路线工序保存):**
| å­—段 | æ—§ç±»åž‹ | æ–°ç±»åž‹ | è¯´æ˜Ž |
|------|--------|--------|------|
| `outputItemId` | `Long` | **已删除** | æ›¿æ¢ä¸º `outputItemIds` |
| `outputItemIds` | - | `List<Long>` | **新增**,产出产品物料编号列表 |
**`MesProWorkOrderProcessSaveReqVO`(工单工序保存):**
| å­—段 | æ—§ç±»åž‹ | æ–°ç±»åž‹ | è¯´æ˜Ž |
|------|--------|--------|------|
| `outputItemId` | `Long` | **已删除** | æ›¿æ¢ä¸º `outputItemIds` |
| `outputItemIds` | - | `List<Long>` | **新增**,产出产品物料编号列表 |
### å“åº”字段变更
**`MesProRouteProcessRespVO`(工艺路线工序响应):**
| å­—段 | ç±»åž‹ | è¯´æ˜Ž |
|------|------|------|
| `outputItemId` | `Long` | **保留**(向后兼容),取第一个产出产品 |
| `outputItemCode` | `String` | **保留**,第一个产出产品编码 |
| `outputItemName` | `String` | **保留**,第一个产出产品名称 |
| `outputItemIds` | `List<Long>` | **新增**,所有产出产品编号列表 |
| `outputItemNames` | `String` | **新增**,所有产出产品名称(逗号拼接) |
**`MesProWorkOrderProcessRespVO`(工单工序响应):**
同上,字段一致。
### åˆ—表接口
| æ–¹æ³• | è·¯å¾„ | è¯´æ˜Ž |
|------|------|------|
| GET | `/mes/pro/route-process/list-by-route?routeId={id}` | å·¥è‰ºè·¯çº¿å·¥åºåˆ—表 |
| GET | `/mes/pro/route-process/list-by-product?productId={id}` | æŒ‰äº§å“èŽ·å–å·¥åºåˆ—è¡¨ï¼ˆå«æŠ•æ–™BOM) |
| GET | `/mes/pro/work-order-process/list-by-work-order-id?workOrderId={id}` | å·¥å•工序列表 |
| GET | `/mes/pro/work-order-process/list-by-product-id?productId={id}` | æŒ‰äº§å“èŽ·å–å·¥åºåˆ—è¡¨ï¼ˆå·¥å•é¢„å¡«ç”¨ï¼‰ |
| POST | `/mes/pro/route-process/create` | åˆ›å»ºå·¥è‰ºè·¯çº¿å·¥åº |
| PUT | `/mes/pro/route-process/update` | æ›´æ–°å·¥è‰ºè·¯çº¿å·¥åº |
| POST | `/mes/pro/work-order-process/create` | åˆ›å»ºå·¥å•工序 |
| PUT | `/mes/pro/work-order-process/update` | æ›´æ–°å·¥å•工序 |
| POST | `/mes/pro/work-order-process/batch-save` | æ‰¹é‡ä¿å­˜å·¥å•工序 |
## å‰ç«¯ä¿®æ”¹ç‚¹
### 1. MdItemSelect ç»„件(`src/views/mes/md/item/components/select.vue`)
新增 `multiple` prop:
| Prop | ç±»åž‹ | é»˜è®¤å€¼ | è¯´æ˜Ž |
|------|------|--------|------|
| `multiple` | `boolean` | `false` | æ˜¯å¦å¤šé€‰æ¨¡å¼ |
多选模式行为:
- `modelValue` ç±»åž‹ä¸º `number[]`
- å±•示名称逗号拼接(取物料 `name` æ‹¼æŽ¥ï¼‰
- å¼¹çª—以 `multiple: true` æ‰“å¼€
- é€‰ä¸­å€¼ä»¥æ•°ç»„形式 emit
单选模式行为不变。
### 2. API ç±»åž‹å®šä¹‰
**`src/api/mes/pro/route/process/index.ts`:**
`RouteProcess` æŽ¥å£æ–°å¢žï¼š
```typescript
outputItemIds?: number[];   // äº§å‡ºäº§å“ç¼–号列表
outputItemNames?: string;   // äº§å‡ºäº§å“åç§°ï¼ˆé€—号拼接)
```
**`src/api/mes/pro/workorder/process/index.ts`:**
`WorkOrderProcess` æŽ¥å£æ–°å¢žåŒä¸Šä¸¤ä¸ªå­—段。
### 3. å·¥è‰ºè·¯çº¿å·¥åºè¡¨å•
**`src/views/mes/process-design/route/data.ts`:**
产出产品字段 schema ä¿®æ”¹ï¼š
- `fieldName` ä»Ž `"outputItemId"` æ”¹ä¸º `"outputItemIds"`
- `componentProps` å¢žåŠ  `multiple: true`
**`src/views/mes/process-design/route/modules/process-list.vue`:**
列表/表格中产出产品列展示改为 `outputItemNames`(逗号分隔的名称)。
### 4. å·¥å•工序表单
**`src/views/mes/pro/workorder/modules/process-form.vue`:**
产出产品字段改为多选组件,`fieldName` æ”¹ä¸º `"outputItemIds"`。
**`src/views/mes/pro/workorder/data.ts`:**
表格列中产出产品列展示改为 `outputItemNames`。
**`src/views/mes/pro/workorder/modules/process-list.vue`:**
工艺路线→工单工序映射时,透传多值 `outputItemIds`/`outputItemNames`。
## æ•°æ®å¸¦å…¥è¯´æ˜Ž
### å·¥è‰ºæµç¨‹æ–°å¢žå·¥åº
1. ç”¨æˆ·åœ¨è¡¨å•中多选产出产品 â†’ `outputItemIds: [101, 102]`
2. ä¿å­˜æ—¶ POST body ä¼  `{ ..., outputItemIds: [101, 102] }`
3. åŽç«¯å°† `[101, 102]` è½¬ä¸º `"101,102"` å­˜å…¥ DB
4. åˆ—表查询时后端返回 `outputItemIds: [101, 102]`, `outputItemNames: "产品A, äº§å“B"`
### å·¥å•从工艺路线带入
调用 `GET /mes/pro/work-order-process/list-by-product-id?productId={id}` èŽ·å–å·¥åºåˆ—è¡¨æ—¶ï¼š
- å“åº”中 `outputItemIds` å’Œ `outputItemNames` å·²ç”±åŽç«¯è§£æžå¥½
- å‰ç«¯ç›´æŽ¥å›žæ˜¾åˆ°è¡¨å•即可
### å·¥å•工序批量保存
请求 `POST /mes/pro/work-order-process/batch-save`:
- æ¯ä¸ªå·¥åºé¡¹ä¼  `outputItemIds: [101, 102]`
- åŽç«¯åˆå¹¶å¤„理
## æ³¨æ„äº‹é¡¹
- æ—§æ•°æ®å…¼å®¹ï¼šDB ä¸­æ—§å€¼ `"101"`(单值字符串)解析后自动转为 `["101"]`,不影响列表展示
- ä¸‹æ¸¸å–第一个:排产任务创建时取 `outputItemIds` ç¬¬ä¸€ä¸ªå€¼ä½œä¸ºä»»åŠ¡ç‰©æ–™ï¼Œå¤šäº§å‡ºåœºæ™¯ä¸‹ç”¨æˆ·éœ€ç¡®è®¤ç¬¬ä¸€ä¸ªäº§å‡ºäº§å“æ˜¯æ­£ç¡®çš„
- æŠ•æ–™ BOM å±•开:仍然按第一个产出产品 ID åŒ¹é…å·¥åºé…æ–¹ï¼Œå¦‚果后续需要支持多个产出产品的 BOM å±•开,需另外改造
- å‰ç«¯ MdItemSelect çš„ `multiple` prop é»˜è®¤ä¸º `false`,确保其他使用此组件的地方不受影响
yudao-module-aftersales/src/main/java/cn/iocoder/yudao/module/aftersales/controller/admin/ticket/AfterSaleTicketController.java
@@ -43,6 +43,8 @@
    private CrmCustomerApi customerApi;
    @Resource
    private AdminUserApi adminUserApi;
    @Resource
    private cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi storageAttachmentApi;
    @PostMapping("/create")
    @Operation(summary = "创建售后工单")
@@ -79,6 +81,7 @@
        List<AfterSaleTicketRespVO> enrichedList = buildTicketDetailList(List.of(ticket));
        AfterSaleTicketRespVO respVO = enrichedList.get(0);
        respVO.setItems(BeanUtils.toBean(items, AfterSaleTicketRespVO.Item.class));
        respVO.setAttachmentList(storageAttachmentApi.listAttachments("aftersales_after_sale_ticket", id));
        return success(respVO);
    }
yudao-module-aftersales/src/main/java/cn/iocoder/yudao/module/aftersales/controller/admin/ticket/vo/ticket/AfterSaleTicketRespVO.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.aftersales.controller.admin.ticket.vo.ticket;
import cn.iocoder.yudao.module.system.api.storage.dto.StorageBlobRespDTO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@@ -101,6 +102,9 @@
    @Schema(description = "更新时间")
    private LocalDateTime updateTime;
    @Schema(description = "附件列表")
    private List<StorageBlobRespDTO> attachmentList;
    @Schema(description = "明细行列表")
    private List<Item> items;
yudao-module-aftersales/src/main/java/cn/iocoder/yudao/module/aftersales/controller/admin/ticket/vo/ticket/AfterSaleTicketSaveReqVO.java
@@ -53,6 +53,9 @@
    @Schema(description = "备注", example = "客户要求尽快处理")
    private String remark;
    @Schema(description = "附件 blobId åˆ—表", example = "[1, 2, 3]")
    private List<Long> blobIds;
    @Schema(description = "明细行列表")
    private List<Item> items;
yudao-module-aftersales/src/main/java/cn/iocoder/yudao/module/aftersales/service/ticket/AfterSaleTicketServiceImpl.java
@@ -15,6 +15,7 @@
import cn.iocoder.yudao.module.aftersales.enums.permission.AfterSalePermissionLevelEnum;
import cn.iocoder.yudao.module.aftersales.service.permission.AfterSalePermissionService;
import cn.iocoder.yudao.module.aftersales.service.permission.bo.AfterSalePermissionCreateReqBO;
import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -47,6 +48,9 @@
    @Resource
    private AfterSalePermissionService permissionService;
    @Resource
    private StorageAttachmentApi storageAttachmentApi;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createTicket(AfterSaleTicketSaveReqVO createReqVO, Long userId) {
@@ -72,7 +76,11 @@
            });
        }
        // 4. åˆ›å»ºæ•°æ®æƒé™ï¼ˆè´Ÿè´£äººï¼‰
        // 4. ç»‘定附件
        storageAttachmentApi.bindAttachments("file", "aftersales_after_sale_ticket",
                ticket.getId(), createReqVO.getBlobIds());
        // 5. åˆ›å»ºæ•°æ®æƒé™ï¼ˆè´Ÿè´£äººï¼‰
        AfterSalePermissionCreateReqBO permBO = new AfterSalePermissionCreateReqBO();
        permBO.setBizType(AfterSaleBizTypeEnum.AFTER_SALE_TICKET.getType());
        permBO.setBizId(ticket.getId());
@@ -103,6 +111,9 @@
                ticketItemMapper.insert(itemDO);
            });
        }
        // 4. æ›´æ–°é™„ä»¶
        storageAttachmentApi.updateAttachments("file", "aftersales_after_sale_ticket", updateReqVO.getId(), updateReqVO.getBlobIds());
    }
    @Override
@@ -113,6 +124,7 @@
        // è½¯åˆ é™¤
        ticketMapper.deleteById(id);
        ticketItemMapper.deleteByTicketId(id);
        storageAttachmentApi.deleteAttachmentsByRecord("aftersales_after_sale_ticket", id);
        permissionService.deletePermission(AfterSaleBizTypeEnum.AFTER_SALE_TICKET.getType(), id);
    }
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/AiAutoConfiguration.java
@@ -16,8 +16,6 @@
import org.springframework.ai.retry.RetryUtils;
import org.springframework.ai.tokenizer.JTokkitTokenCountEstimator;
import org.springframework.ai.tokenizer.TokenCountEstimator;
import org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusServiceClientProperties;
import org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusVectorStoreProperties;
import org.springframework.ai.vectorstore.qdrant.autoconfigure.QdrantVectorStoreProperties;
import org.springframework.ai.vectorstore.redis.autoconfigure.RedisVectorStoreProperties;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@@ -29,14 +27,13 @@
 * èЋ道 AI è‡ªåŠ¨é…ç½®
 *
 * ä½¿ç”¨ OpenAI å…¼å®¹æŽ¥å£å¯¹æŽ¥é€šä¹‰åƒé—® DashScope
 * å‘量库配置见 yudao.ai.vector-store èŠ‚ç‚¹ï¼Œä¸å†ä¾èµ– Spring AI çš„ Milvus è‡ªåŠ¨é…ç½®å±žæ€§
 */
@Configuration
@EnableConfigurationProperties({
        YudaoAiProperties.class,
        QdrantVectorStoreProperties.class,
        RedisVectorStoreProperties.class,
        MilvusVectorStoreProperties.class,
        MilvusServiceClientProperties.class
        RedisVectorStoreProperties.class
})
@Slf4j
public class AiAutoConfiguration {
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/YudaoAiProperties.java
@@ -12,10 +12,37 @@
    private WebSearch webSearch;
    /** å‘量库配置 */
    private VectorStore vectorStore = new VectorStore();
    @Data
    public static class WebSearch {
        private boolean enable;
        private String apiKey;
    }
    @Data
    public static class VectorStore {
        /** å‘量库类型:milvus */
        private String type = "milvus";
        /** Milvus é…ç½® */
        private Milvus milvus = new Milvus();
        @Data
        public static class Milvus {
            /** æ˜¯å¦åˆå§‹åŒ– Schema */
            private boolean initializeSchema = true;
            /** æ•°æ®åº“名称 */
            private String databaseName = "default";
            /** é›†åˆåç§° */
            private String collectionName = "knowledge_segment";
            /** ä¸»æœºåœ°å€ */
            private String host = "127.0.0.1";
            /** ç«¯å£ */
            private int port = 19530;
        }
    }
}
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/AiModelFactoryImpl.java
@@ -7,22 +7,20 @@
import cn.hutool.extra.spring.SpringUtil;
import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;
import cn.iocoder.yudao.module.ai.framework.ai.config.AiAutoConfiguration;
import cn.iocoder.yudao.module.ai.framework.ai.config.YudaoAiProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.embedding.BatchingStrategy;
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.embedding.TokenCountBatchingStrategy;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.ai.vectorstore.milvus.MilvusVectorStore;
import org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusServiceClientProperties;
import org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusVectorStoreProperties;
import java.util.Map;
/**
 * AI Model æ¨¡åž‹å·¥åŽ‚å®žçŽ°ç±»
 *
 * ä½¿ç”¨ OpenAI å…¼å®¹æŽ¥å£å¯¹æŽ¥é€šä¹‰åƒé—® + Milvus å‘量存储
 * ä½¿ç”¨ OpenAI å…¼å®¹æŽ¥å£å¯¹æŽ¥é€šä¹‰åƒé—® + å¯é…ç½®å‘量存储(Milvus)
 */
@Slf4j
public class AiModelFactoryImpl implements AiModelFactory {
@@ -61,7 +59,6 @@
    public VectorStore getOrCreateVectorStore(Class<? extends VectorStore> type,
                                              EmbeddingModel embeddingModel,
                                              Map<String, Class<?>> metadataFields) {
        // metadataFields å‚与缓存 key,确保不同知识库使用不同配置时不会复用
        String cacheKey = buildCacheKey(VectorStore.class, embeddingModel, type, metadataFields.hashCode());
        return Singleton.get(cacheKey, (Func0<VectorStore>) () -> {
            if (type == MilvusVectorStore.class) {
@@ -72,20 +69,20 @@
    }
    private MilvusVectorStore buildMilvusVectorStore(EmbeddingModel embeddingModel) {
        MilvusVectorStoreProperties serverProperties = SpringUtil.getBean(MilvusVectorStoreProperties.class);
        MilvusServiceClientProperties clientProperties = SpringUtil.getBean(MilvusServiceClientProperties.class);
        YudaoAiProperties aiProperties = SpringUtil.getBean(YudaoAiProperties.class);
        YudaoAiProperties.VectorStore.Milvus milvusConfig = aiProperties.getVectorStore().getMilvus();
        var connectParam = io.milvus.param.ConnectParam.newBuilder()
                .withHost(clientProperties.getHost())
                .withPort(clientProperties.getPort())
                .withDatabaseName(serverProperties.getDatabaseName())
                .withHost(milvusConfig.getHost())
                .withPort(milvusConfig.getPort())
                .withDatabaseName(milvusConfig.getDatabaseName())
                .build();
        var milvusClient = new io.milvus.client.MilvusServiceClient(connectParam);
        MilvusVectorStore vectorStore = MilvusVectorStore.builder(milvusClient, embeddingModel)
                .databaseName(serverProperties.getDatabaseName())
                .collectionName(serverProperties.getCollectionName())
                .initializeSchema(serverProperties.isInitializeSchema())
                .databaseName(milvusConfig.getDatabaseName())
                .collectionName(milvusConfig.getCollectionName())
                .initializeSchema(milvusConfig.isInitializeSchema())
                .batchingStrategy(new TokenCountBatchingStrategy())
                .build();
        try {
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentServiceImpl.java
@@ -212,9 +212,13 @@
            documents.add(doc);
        }
        // å‘量库限制单批次最多 10 æ¡ï¼Œåˆ†æ‰¹å†™å…¥
        int batchSize = 10;
        try {
            vectorStore.add(documents);
            // æ›´æ–° vectorId(Milvus è¿”回的 ID å°±æ˜¯ä¼ å…¥çš„ docId)
            for (int i = 0; i < documents.size(); i += batchSize) {
                int end = Math.min(i + batchSize, documents.size());
                vectorStore.add(documents.subList(i, end));
            }
            for (AiKnowledgeSegmentDO segment : segments) {
                segment.setVectorId(segment.getId().toString());
                segmentMapper.updateById(segment);
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/MesProRouteProcessController.java
@@ -24,11 +24,7 @@
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@@ -150,21 +146,50 @@
        list.forEach(item -> { if (item.getNextProcessId() != null) processIds.add(item.getNextProcessId()); });
        Map<Long, MesProProcessDO> processMap = convertMap(
                processService.getProcessList(new ArrayList<>(processIds)), MesProProcessDO::getId);
        // 2. æ‰¹é‡èŽ·å–äº§å‡ºäº§å“ç‰©æ–™ä¿¡æ¯
        Set<Long> outputItemIds = convertSet(list, MesProRouteProcessDO::getOutputItemId);
        outputItemIds.remove(null);
        Map<Long, MesMdItemDO> outputItemMap = outputItemIds.isEmpty()
                ? Collections.emptyMap()
                : mdItemService.getItemMap(outputItemIds);
        // 2. æ‰¹é‡èŽ·å–äº§å‡ºäº§å“ç‰©æ–™ä¿¡æ¯ï¼ˆè§£æžé€—å·åˆ†éš”çš„å­—ç¬¦ä¸²ï¼‰
        Set<Long> allOutputItemIds = new HashSet<>();
        Map<Long, List<Long>> voOutputItemIdsMap = new HashMap<>();
        for (int i = 0; i < list.size(); i++) {
            String outputItemIdStr = list.get(i).getOutputItemId();
            if (outputItemIdStr != null && !outputItemIdStr.isBlank()) {
                List<Long> ids = Arrays.stream(outputItemIdStr.split(","))
                        .map(String::trim).filter(s -> !s.isEmpty())
                        .map(Long::valueOf).collect(Collectors.toList());
                voOutputItemIdsMap.put((long) i, ids);
                allOutputItemIds.addAll(ids);
            }
        }
        Map<Long, MesMdItemDO> outputItemMap = allOutputItemIds.isEmpty()
                ? Collections.emptyMap() : mdItemService.getItemMap(allOutputItemIds);
        // 3. æ‹¼æŽ¥ VO
        return BeanUtils.toBean(list, MesProRouteProcessRespVO.class, vo -> {
        List<MesProRouteProcessRespVO> result = BeanUtils.toBean(list, MesProRouteProcessRespVO.class);
        for (int i = 0; i < result.size(); i++) {
            MesProRouteProcessRespVO vo = result.get(i);
            MapUtils.findAndThen(processMap, vo.getProcessId(),
                    process -> vo.setProcessCode(process.getCode()).setProcessName(process.getName()));
            MapUtils.findAndThen(processMap, vo.getNextProcessId(),
                    nextProcess -> vo.setNextProcessName(nextProcess.getName()));
            MapUtils.findAndThen(outputItemMap, vo.getOutputItemId(),
                    item -> vo.setOutputItemCode(item.getCode()).setOutputItemName(item.getName()));
        });
            // äº§å‡ºäº§å“ï¼ˆæ”¯æŒå¤šé€‰ï¼‰
            List<Long> ids = voOutputItemIdsMap.getOrDefault((long) i, Collections.emptyList());
            if (!ids.isEmpty()) {
                vo.setOutputItemIds(ids);
                List<String> names = ids.stream()
                        .map(id -> {
                            MesMdItemDO item = outputItemMap.get(id);
                            return item != null ? item.getName() : String.valueOf(id);
                        }).collect(Collectors.toList());
                vo.setOutputItemNames(String.join(", ", names));
                // å‘后兼容:第一个产出产品
                Long firstId = ids.get(0);
                vo.setOutputItemId(firstId);
                MesMdItemDO firstItem = outputItemMap.get(firstId);
                if (firstItem != null) {
                    vo.setOutputItemCode(firstItem.getCode());
                    vo.setOutputItemName(firstItem.getName());
                }
            }
        }
        return result;
    }
    private MesProRouteProcessRespVO buildRouteProcessRespVO(MesProRouteProcessDO routeProcess) {
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/process/MesProRouteProcessRespVO.java
@@ -55,15 +55,21 @@
    @Schema(description = "是否倒冲(true=报工时按BOM自动扣料;false=不自动扣料)", example = "true")
    private Boolean backflushFlag;
    @Schema(description = "产出产品物料编号", example = "100")
    @Schema(description = "产出产品物料编号(第一个,向后兼容)", example = "100")
    private Long outputItemId;
    @Schema(description = "产出产品物料编码")
    @Schema(description = "产出产品物料编码(第一个)")
    private String outputItemCode;
    @Schema(description = "产出产品物料名称")
    @Schema(description = "产出产品物料名称(第一个)")
    private String outputItemName;
    @Schema(description = "产出产品物料编号列表", example = "[100, 101]")
    private List<Long> outputItemIds;
    @Schema(description = "产出产品物料名称(逗号拼接)", example = "产品A, äº§å“B")
    private String outputItemNames;
    @Schema(description = "备注")
    private String remark;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/route/vo/process/MesProRouteProcessSaveReqVO.java
@@ -4,6 +4,8 @@
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.util.List;
@Schema(description = "管理后台 - MES å·¥è‰ºè·¯çº¿å·¥åºæ–°å¢ž/修改 Request VO")
@Data
public class MesProRouteProcessSaveReqVO {
@@ -44,8 +46,8 @@
    @Schema(description = "是否倒冲(true=报工时按BOM自动扣料;false=不自动扣料)", example = "true")
    private Boolean backflushFlag;
    @Schema(description = "产出产品物料编号(为空时默认用工单成品)", example = "100")
    private Long outputItemId;
    @Schema(description = "产出产品物料编号列表(多选,为空时默认用工单成品)", example = "[100, 101]")
    private List<Long> outputItemIds;
    @Schema(description = "备注")
    private String remark;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkOrderBomController.java
@@ -249,7 +249,7 @@
                    items.add(bom);
                }
            }
            processBomMap.put(process.getOutputItemId(), items);
            processBomMap.put(Long.valueOf(process.getOutputItemId().split(",")[0].trim()), items);
        }
        // 2. å–最后一道工序(sort æœ€å¤§ï¼‰ä½œä¸ºå±•开起点,逐层向前递推
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkOrderProcessController.java
@@ -28,6 +28,7 @@
import org.springframework.web.bind.annotation.*;
import java.util.*;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
@@ -134,11 +135,21 @@
        processIds.addAll(nextProcessIds);
        Map<Long, MesProProcessDO> processMap = CollUtil.isNotEmpty(processIds)
                ? processService.getProcessMap(processIds) : Collections.emptyMap();
        // èŽ·å–äº§å‡ºç‰©æ–™
        Set<Long> outputItemIds = convertSet(list, MesProWorkOrderProcessDO::getOutputItemId);
        outputItemIds.removeAll(Collections.singleton(null));
        Map<Long, MesMdItemDO> itemMap = CollUtil.isNotEmpty(outputItemIds)
                ? itemService.getItemMap(outputItemIds) : Collections.emptyMap();
        // èŽ·å–äº§å‡ºç‰©æ–™ï¼ˆè§£æžé€—å·åˆ†éš”çš„å­—ç¬¦ä¸²ï¼‰
        Set<Long> allOutputItemIds = new HashSet<>();
        Map<Long, List<Long>> voOutputItemIdsMap = new HashMap<>();
        for (int i = 0; i < list.size(); i++) {
            String outputItemIdStr = list.get(i).getOutputItemId();
            if (outputItemIdStr != null && !outputItemIdStr.isBlank()) {
                List<Long> ids = Arrays.stream(outputItemIdStr.split(","))
                        .map(String::trim).filter(s -> !s.isEmpty())
                        .map(Long::valueOf).collect(Collectors.toList());
                voOutputItemIdsMap.put((long) i, ids);
                allOutputItemIds.addAll(ids);
            }
        }
        Map<Long, MesMdItemDO> itemMap = CollUtil.isNotEmpty(allOutputItemIds)
                ? itemService.getItemMap(allOutputItemIds) : Collections.emptyMap();
        // 2. æŸ¥è¯¢æŠ•æ–™BOM(按工序分组)
        Set<Long> workOrderIds = convertSet(list, MesProWorkOrderProcessDO::getWorkOrderId);
@@ -165,17 +176,35 @@
        }));
        // 3. æ‹¼æŽ¥ VO
        List<MesProWorkOrderProcessRespVO> result = BeanUtils.toBean(list, MesProWorkOrderProcessRespVO.class, vo -> {
        List<MesProWorkOrderProcessRespVO> result = BeanUtils.toBean(list, MesProWorkOrderProcessRespVO.class);
        for (int i = 0; i < result.size(); i++) {
            MesProWorkOrderProcessRespVO vo = result.get(i);
            MapUtils.findAndThen(processMap, vo.getProcessId(), process -> {
                vo.setProcessCode(process.getCode()).setProcessName(process.getName());
            });
            MapUtils.findAndThen(processMap, vo.getNextProcessId(), process -> {
                vo.setNextProcessName(process.getName());
            });
            MapUtils.findAndThen(itemMap, vo.getOutputItemId(), item -> {
                vo.setOutputItemCode(item.getCode()).setOutputItemName(item.getName());
            });
        });
            // äº§å‡ºç‰©æ–™ï¼ˆæ”¯æŒå¤šé€‰ï¼‰
            List<Long> ids = voOutputItemIdsMap.getOrDefault((long) i, Collections.emptyList());
            if (!ids.isEmpty()) {
                vo.setOutputItemIds(ids);
                List<String> names = ids.stream()
                        .map(id -> {
                            MesMdItemDO item = itemMap.get(id);
                            return item != null ? item.getName() : String.valueOf(id);
                        }).collect(Collectors.toList());
                vo.setOutputItemNames(String.join(", ", names));
                // å‘后兼容:第一个产出物料
                Long firstId = ids.get(0);
                vo.setOutputItemId(firstId);
                MesMdItemDO firstItem = itemMap.get(firstId);
                if (firstItem != null) {
                    vo.setOutputItemCode(firstItem.getCode());
                    vo.setOutputItemName(firstItem.getName());
                }
            }
        }
        // è®¾ç½®æ¯ä¸ªå·¥åºå„自的投料BOM
        result.forEach(vo -> vo.setBomItems(bomItemsByProcessId.getOrDefault(vo.getProcessId(), Collections.emptyList())));
        return result;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/process/MesProWorkOrderProcessRespVO.java
@@ -68,15 +68,21 @@
    @ExcelProperty("是否倒冲")
    private Boolean backflushFlag;
    @Schema(description = "产出产品物料编号", example = "100")
    @Schema(description = "产出产品物料编号(第一个,向后兼容)", example = "100")
    private Long outputItemId;
    @Schema(description = "产出产品物料编码", example = "M001")
    @Schema(description = "产出产品物料编码(第一个)", example = "M001")
    private String outputItemCode;
    @Schema(description = "产出产品物料名称", example = "产品A")
    @Schema(description = "产出产品物料名称(第一个)", example = "产品A")
    private String outputItemName;
    @Schema(description = "产出产品物料编号列表", example = "[100, 101]")
    private List<Long> outputItemIds;
    @Schema(description = "产出产品物料名称(逗号拼接)", example = "产品A, äº§å“B")
    private String outputItemNames;
    @Schema(description = "备注", example = "备注")
    @ExcelProperty("备注")
    private String remark;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/process/MesProWorkOrderProcessSaveReqVO.java
@@ -7,6 +7,8 @@
import java.math.BigDecimal;
import java.util.List;
import java.util.List;
@Schema(description = "管理后台 - MES ç”Ÿäº§å·¥å•工序新增/修改 Request VO")
@Data
public class MesProWorkOrderProcessSaveReqVO {
@@ -47,8 +49,8 @@
    @Schema(description = "是否倒冲", example = "true")
    private Boolean backflushFlag;
    @Schema(description = "产出产品物料编号", example = "100")
    private Long outputItemId;
    @Schema(description = "产出产品物料编号列表(多选,为空时默认用工单成品)", example = "[100, 101]")
    private List<Long> outputItemIds;
    @Schema(description = "备注")
    private String remark;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/route/MesProRouteProcessDO.java
@@ -84,12 +84,12 @@
     */
    private Boolean backflushFlag;
    /**
     * äº§å‡ºäº§å“ç‰©æ–™ç¼–号
     * äº§å‡ºäº§å“ç‰©æ–™ç¼–号(多个用逗号分隔)
     *
     * å…³è” {@link cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO#getId()}
     * ä¸ºç©ºæ—¶é»˜è®¤ç”¨å·¥å•的成品
     */
    private Long outputItemId;
    private String outputItemId;
    /**
     * å¤‡æ³¨
     */
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/workorder/MesProWorkOrderProcessDO.java
@@ -83,12 +83,12 @@
     */
    private Boolean backflushFlag;
    /**
     * äº§å‡ºäº§å“ç‰©æ–™ç¼–号
     * äº§å‡ºäº§å“ç‰©æ–™ç¼–号(多个用逗号分隔)
     *
     * å…³è” {@link cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO#getId()}
     * ä¸ºç©ºæ—¶é»˜è®¤ç”¨å·¥å•的成品
     */
    private Long outputItemId;
    private String outputItemId;
    /**
     * å¤‡æ³¨
     */
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/mps/MesProMpsServiceImpl.java
@@ -40,10 +40,12 @@
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
@@ -552,7 +554,11 @@
            process.setKeyFlag(rp.getKeyFlag());
            process.setCheckFlag(rp.getCheckFlag());
            process.setBackflushFlag(rp.getBackflushFlag());
            process.setOutputItemId(rp.getOutputItemId());
            if (rp.getOutputItemId() != null && !rp.getOutputItemId().isBlank()) {
                process.setOutputItemIds(Arrays.stream(rp.getOutputItemId().split(","))
                        .map(String::trim).filter(s -> !s.isEmpty())
                        .map(Long::valueOf).collect(Collectors.toList()));
            }
            process.setColorCode(rp.getColorCode());
            // å¸¦å…¥å·¥åºæŠ•æ–™BOM
            List<MesProRouteProductBomDO> routeBomItems = bomMap != null ? bomMap.get(rp.getProcessId()) : null;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/route/MesProRouteProcessServiceImpl.java
@@ -63,6 +63,12 @@
        // 2. æ’å…¥
        MesProRouteProcessDO routeProcess = BeanUtils.toBean(createReqVO, MesProRouteProcessDO.class);
        if (CollUtil.isNotEmpty(createReqVO.getOutputItemIds())) {
            routeProcess.setOutputItemId(
                createReqVO.getOutputItemIds().stream()
                    .map(String::valueOf)
                    .collect(Collectors.joining(",")));
        }
        routeProcessMapper.insert(routeProcess);
        // 3. æ›´æ–°å·¥åºé“¾
@@ -86,6 +92,12 @@
        // 2. æ›´æ–°
        MesProRouteProcessDO updateObj = BeanUtils.toBean(updateReqVO, MesProRouteProcessDO.class);
        if (CollUtil.isNotEmpty(updateReqVO.getOutputItemIds())) {
            updateObj.setOutputItemId(
                updateReqVO.getOutputItemIds().stream()
                    .map(String::valueOf)
                    .collect(Collectors.joining(",")));
        }
        routeProcessMapper.updateById(updateObj);
        // 3. é‡å»ºå·¥åºé“¾
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskServiceImpl.java
@@ -112,7 +112,7 @@
            MesProRouteProcessDO routeProcess = routeProcessService
                    .getRouteProcessByRouteIdAndProcessId(createReqVO.getRouteId(), createReqVO.getProcessId());
            if (routeProcess != null && routeProcess.getOutputItemId() != null) {
                itemId = routeProcess.getOutputItemId();
                itemId = Long.valueOf(routeProcess.getOutputItemId().split(",")[0].trim());
            } else {
                itemId = workOrder.getProductId();
            }
@@ -408,7 +408,7 @@
        List<Long> taskIds = new java.util.ArrayList<>();
        for (MesProRouteProcessDO rp : routeProcesses) {
            cn.iocoder.yudao.module.mes.dal.dataobject.pro.process.MesProProcessDO proc = procMap.get(rp.getProcessId());
            Long itemId = rp.getOutputItemId() != null ? rp.getOutputItemId() : workOrder.getProductId();
            Long itemId = rp.getOutputItemId() != null ? Long.valueOf(rp.getOutputItemId().split(",")[0].trim()) : workOrder.getProductId();
            cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO item = itemService.validateItemExists(itemId);
            MesProTaskDO task = new MesProTaskDO()
                    .setWorkOrderId(workOrder.getId())
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkOrderProcessServiceImpl.java
@@ -24,6 +24,7 @@
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
@@ -60,6 +61,12 @@
        // 2. æ’入数据
        MesProWorkOrderProcessDO workOrderProcess = BeanUtils.toBean(createReqVO, MesProWorkOrderProcessDO.class);
        if (CollUtil.isNotEmpty(createReqVO.getOutputItemIds())) {
            workOrderProcess.setOutputItemId(
                createReqVO.getOutputItemIds().stream()
                    .map(String::valueOf)
                    .collect(Collectors.joining(",")));
        }
        workOrderProcessMapper.insert(workOrderProcess);
        // 3. é‡å»ºå·¥åºé“¾
@@ -75,6 +82,12 @@
        // 2. æ›´æ–°æ•°æ®
        MesProWorkOrderProcessDO updateObj = BeanUtils.toBean(updateReqVO, MesProWorkOrderProcessDO.class);
        if (CollUtil.isNotEmpty(updateReqVO.getOutputItemIds())) {
            updateObj.setOutputItemId(
                updateReqVO.getOutputItemIds().stream()
                    .map(String::valueOf)
                    .collect(Collectors.joining(",")));
        }
        workOrderProcessMapper.updateById(updateObj);
        // 3. é‡å»ºå·¥åºé“¾
@@ -175,10 +188,16 @@
            if (item.getId() == null) {
                // æ–°å¢ž
                MesProWorkOrderProcessDO process = BeanUtils.toBean(item, MesProWorkOrderProcessDO.class);
                if (CollUtil.isNotEmpty(item.getOutputItemIds())) {
                    process.setOutputItemId(item.getOutputItemIds().stream().map(String::valueOf).collect(Collectors.joining(",")));
                }
                workOrderProcessMapper.insert(process);
            } else {
                // æ›´æ–°
                MesProWorkOrderProcessDO updateObj = BeanUtils.toBean(item, MesProWorkOrderProcessDO.class);
                if (CollUtil.isNotEmpty(item.getOutputItemIds())) {
                    updateObj.setOutputItemId(item.getOutputItemIds().stream().map(String::valueOf).collect(Collectors.joining(",")));
                }
                workOrderProcessMapper.updateById(updateObj);
            }
        }
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/enums/storage/StorageRecordTypeEnum.java
@@ -54,8 +54,8 @@
    HRM_ATTENDANCE_EXCEPTION("hrm_attendance_exception", "HRM è€ƒå‹¤å¼‚常"),
    // ===== å”®åŽ =====
    AFTER_SALE_TICKET("aftersales_ticket", "售后工单"),
    AFTER_SALE_RETURN("aftersales_return", "售后退货申请"),
    AFTER_SALE_TICKET("aftersales_after_sale_ticket", "售后工单"),
    AFTER_SALE_RETURN("aftersales_after_sale_return", "售后退货申请"),
    // ===== MES =====
    MES_PD_DOCUMENT("mes_pd_document", "MES è®¾è®¡èµ„æ–™");
yudao-server/src/main/resources/application-dev.yaml
@@ -175,6 +175,15 @@
# èŠ‹é“é…ç½®é¡¹ï¼Œè®¾ç½®å½“å‰é¡¹ç›®æ‰€æœ‰è‡ªå®šä¹‰çš„é…ç½®
yudao:
  ai:
    vector-store:
      type: milvus # å‘量库类型:milvus
      milvus:
        initialize-schema: true
        database-name: default
        collection-name: knowledge_segment
        host: 127.0.0.1
        port: 19530
  pay:
    order-notify-url: http://yunai.natapp1.cc/admin-api/pay/notify/order # æ”¯ä»˜æ¸ é“的【支付】回调地址
    refund-notify-url: http://yunai.natapp1.cc/admin-api/pay/notify/refund # æ”¯ä»˜æ¸ é“的【退款】回调地址
yudao-server/src/main/resources/application-local.yaml
@@ -222,6 +222,17 @@
debug: false
yudao:
  ai:
    vector-store:
      type: milvus # å‘量库类型:milvus
      milvus:
        initialize-schema: true
        database-name: default
        collection-name: knowledge_segment
        host: 36.213.180.217
        port: 19530
--- #################### å¾®ä¿¡å…¬ä¼—号、小程序相关配置 ####################
wx:
  mp: # å…¬ä¼—号配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-mp-spring-boot-starter/README.md æ–‡æ¡£
yudao-server/src/main/resources/application-test.yaml
@@ -220,6 +220,17 @@
debug: false
yudao:
  ai:
    vector-store:
      type: milvus # å‘量库类型:milvus
      milvus:
        initialize-schema: true
        database-name: test
        collection-name: knowledge_segment
        host: 36.213.180.217
        port: 19530
--- #################### å¾®ä¿¡å…¬ä¼—号、小程序相关配置 ####################
wx:
  mp: # å…¬ä¼—号配置(必填),参见 https://github.com/Wechat-Group/WxJava/blob/develop/spring-boot-starters/wx-java-mp-spring-boot-starter/README.md æ–‡æ¡£