feat(mes): 支持工艺流程工序产出产品多选功能
- 修改 MesProRouteProcessDO 和 MesProWorkOrderProcessDO 中 outputItemId 字段类型为 String
- 新增 MesProRouteProcessSaveReqVO 和 MesProWorkOrderProcessSaveReqVO 的 outputItemIds 字段
- 在控制器中解析逗号分隔的字符串并转换为产品物料编号列表
- 支持向前兼容,保留 outputItemId、outputItemCode、outputItemName 字段
- 更新相关服务类中的创建和更新逻辑以处理多选产品
- 修改 AI 知识库向量库配置,添加 Milvus 向量存储配置
- 优化向量库批量写入逻辑,限制单批次最多 10 条记录
- 更新售后工单模块,集成附件上传和存储功能
- 添加 StorageRecordTypeEnum 枚举项 aftersales_after_sale_ticket 和 aftersales_after_sale_return
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # å·¥èºæµç¨å·¥åº"产åºäº§å"æ¯æå¤é - åå端æ¹å¨æ»ç» |
| | | |
| | | ## ç¶æï¼å·²å®æ |
| | | |
| | | å端åå端æ¹å¨å已宿½ï¼ç¼è¯éªè¯éè¿ã |
| | | |
| | | ## æ¶åé¡µé¢ |
| | | |
| | | - çäº§ç®¡æ§ â 产å设计 â å·¥èºæµç¨ â å·¥åºç¼è¾å¼¹çªï¼å·¥èºè·¯çº¿å·¥åºï¼ |
| | | - çäº§ç®¡æ§ â ç产工å â å·¥åºç¼è¾å¼¹çªï¼å·¥åå·¥åºï¼ |
| | | - çäº§ç®¡æ§ â ç产工å â å·¥åºå表å±ç¤º |
| | | |
| | | ## 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`ï¼ç¡®ä¿å
¶ä»ä½¿ç¨æ¤ç»ä»¶çå°æ¹ä¸åå½±å |
| | |
| | | private CrmCustomerApi customerApi; |
| | | @Resource |
| | | private AdminUserApi adminUserApi; |
| | | @Resource |
| | | private cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi storageAttachmentApi; |
| | | |
| | | @PostMapping("/create") |
| | | @Operation(summary = "å建å®åå·¥å") |
| | |
| | | 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); |
| | | } |
| | | |
| | |
| | | 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; |
| | | |
| | |
| | | @Schema(description = "æ´æ°æ¶é´") |
| | | private LocalDateTime updateTime; |
| | | |
| | | @Schema(description = "éä»¶å表") |
| | | private List<StorageBlobRespDTO> attachmentList; |
| | | |
| | | @Schema(description = "æç»è¡å表") |
| | | private List<Item> items; |
| | | |
| | |
| | | @Schema(description = "夿³¨", example = "客æ·è¦æ±å°½å¿«å¤ç") |
| | | private String remark; |
| | | |
| | | @Schema(description = "éä»¶ blobId å表", example = "[1, 2, 3]") |
| | | private List<Long> blobIds; |
| | | |
| | | @Schema(description = "æç»è¡å表") |
| | | private List<Item> items; |
| | | |
| | |
| | | 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; |
| | |
| | | @Resource |
| | | private AfterSalePermissionService permissionService; |
| | | |
| | | @Resource |
| | | private StorageAttachmentApi storageAttachmentApi; |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Long createTicket(AfterSaleTicketSaveReqVO createReqVO, Long userId) { |
| | |
| | | }); |
| | | } |
| | | |
| | | // 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()); |
| | |
| | | ticketItemMapper.insert(itemDO); |
| | | }); |
| | | } |
| | | |
| | | // 4. æ´æ°éä»¶ |
| | | storageAttachmentApi.updateAttachments("file", "aftersales_after_sale_ticket", updateReqVO.getId(), updateReqVO.getBlobIds()); |
| | | } |
| | | |
| | | @Override |
| | |
| | | // 软å é¤ |
| | | ticketMapper.deleteById(id); |
| | | ticketItemMapper.deleteByTicketId(id); |
| | | storageAttachmentApi.deleteAttachmentsByRecord("aftersales_after_sale_ticket", id); |
| | | permissionService.deletePermission(AfterSaleBizTypeEnum.AFTER_SALE_TICKET.getType(), id); |
| | | } |
| | | |
| | |
| | | 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; |
| | |
| | | * èé 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 { |
| | |
| | | |
| | | 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; |
| | | } |
| | | } |
| | | |
| | | } |
| | |
| | | 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 { |
| | |
| | | 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) { |
| | |
| | | } |
| | | |
| | | 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 { |
| | |
| | | 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); |
| | |
| | | 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; |
| | |
| | | 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) { |
| | |
| | | @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; |
| | | |
| | |
| | | import jakarta.validation.constraints.NotNull; |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | | |
| | | @Schema(description = "管çåå° - MES å·¥èºè·¯çº¿å·¥åºæ°å¢/ä¿®æ¹ Request VO") |
| | | @Data |
| | | public class MesProRouteProcessSaveReqVO { |
| | |
| | | @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; |
| | |
| | | items.add(bom); |
| | | } |
| | | } |
| | | processBomMap.put(process.getOutputItemId(), items); |
| | | processBomMap.put(Long.valueOf(process.getOutputItemId().split(",")[0].trim()), items); |
| | | } |
| | | |
| | | // 2. åæåä¸éå·¥åºï¼sort æå¤§ï¼ä½ä¸ºå±å¼èµ·ç¹ï¼éå±åå鿍 |
| | |
| | | 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; |
| | |
| | | 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); |
| | |
| | | })); |
| | | |
| | | // 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; |
| | |
| | | @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; |
| | |
| | | import java.math.BigDecimal; |
| | | import java.util.List; |
| | | |
| | | import java.util.List; |
| | | |
| | | @Schema(description = "管çåå° - MES ç产工åå·¥åºæ°å¢/ä¿®æ¹ Request VO") |
| | | @Data |
| | | public class MesProWorkOrderProcessSaveReqVO { |
| | |
| | | @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; |
| | |
| | | */ |
| | | private Boolean backflushFlag; |
| | | /** |
| | | * 产åºäº§åç©æç¼å· |
| | | * 产åºäº§åç©æç¼å·ï¼å¤ä¸ªç¨éå·åéï¼ |
| | | * |
| | | * å
³è {@link cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO#getId()} |
| | | * 为空æ¶é»è®¤ç¨å·¥åçæå |
| | | */ |
| | | private Long outputItemId; |
| | | private String outputItemId; |
| | | /** |
| | | * 夿³¨ |
| | | */ |
| | |
| | | */ |
| | | private Boolean backflushFlag; |
| | | /** |
| | | * 产åºäº§åç©æç¼å· |
| | | * 产åºäº§åç©æç¼å·ï¼å¤ä¸ªç¨éå·åéï¼ |
| | | * |
| | | * å
³è {@link cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO#getId()} |
| | | * 为空æ¶é»è®¤ç¨å·¥åçæå |
| | | */ |
| | | private Long outputItemId; |
| | | private String outputItemId; |
| | | /** |
| | | * 夿³¨ |
| | | */ |
| | |
| | | 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.*; |
| | |
| | | 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; |
| | |
| | | |
| | | // 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. æ´æ°å·¥åºé¾ |
| | |
| | | |
| | | // 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. é建工åºé¾ |
| | |
| | | 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(); |
| | | } |
| | |
| | | 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()) |
| | |
| | | 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.*; |
| | |
| | | |
| | | // 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. é建工åºé¾ |
| | |
| | | |
| | | // 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. é建工åºé¾ |
| | |
| | | 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); |
| | | } |
| | | } |
| | |
| | | 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: |
| | | 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 # æ¯ä»æ¸ éçã鿬¾ãåè°å°å |
| | |
| | | |
| | | 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 ææ¡£ |
| | |
| | | |
| | | 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 ææ¡£ |