.agents/rules/compile-check.md
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,32 @@ # ç¼è¯æ£æ¥è§å ## è§å - 跨模å API è°ç¨å¨ä¿®æ¹å®ææ¶ï¼å¿ é¡»è¿è¡ä¸æ¬¡ç¼è¯æ£æ¥ï¼ç¡®ä¿æ²¡æå¼å ¥ç¼è¯é误ã - 使ç¨å½ä»¤ï¼`mvn compile -pl <模åå> -am -q` è¿è¡å¿«éç¼è¯éªè¯ã - 妿æ¶åå¤ä¸ªæ¨¡åä¿®æ¹ï¼åºæ§è¡ `mvn compile -q` è¿è¡å ¨é¡¹ç®ç¼è¯éªè¯ã ## 常è§è·¨æ¨¡å API è°ç¨åºæ¯ | è°ç¨æ¹ | API æä¾æ¹ | 说æ | |--------|-----------|------| | CRM 模å | MdmItemApi | MDM ç©ææ¥å£ | | CRM 模å | MdmUnitMeasureApi | MDM 计éå使¥å£ | | CRM 模å | BpmProcessInstanceApi | BPM æµç¨å®ä¾æ¥å£ | | CRM 模å | BpmCategoryService | BPM åç±»æå¡ | | CRM 模å | BpmProcessDefinitionService | BPM æµç¨å®ä¹æå¡ | | ERP 模å | CrmCustomerApi | CRM å®¢æ·æ¥å£ | | ERP 模å | MdmItemApi | MDM ç©ææ¥å£ | ## ç¼è¯æ£æ¥ç¤ºä¾ ```bash # ä» ç¼è¯ CRM 模ååå ¶ä¾èµ mvn compile -pl yudao-module-crm -am -q # ä» ç¼è¯ ERP 模ååå ¶ä¾èµ mvn compile -pl yudao-module-erp -am -q # å ¨é¡¹ç®ç¼è¯ mvn compile -q ``` .agents/rules/database-mcp.md
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,48 @@ # æ°æ®åºæä½è§å ## æ°æ®åºçæ¬ - **MySQL çæ¬**: 8.4.8 - 注æ MySQL 8.4 çè¯æ³å·®å¼ï¼å¦ `GROUP BY` æ´ä¸¥æ ¼ãé¨ååºå¼å½æ°çï¼ ## Docker é¨ç½²é ç½® ```bash --sql-mode="STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION" \ --innodb_strict_mode=OFF \ --innodb_page_size=32k \ --max_connections=1020 \ --lower_case_table_names=1 \ --innodb_buffer_pool_size=70G \ --innodb_flush_log_at_trx_commit=2 \ --innodb_log_file_size=2G \ --innodb_log_buffer_size=64M ``` **é 置说æï¼** - `lower_case_table_names=1`: 表åä¸åºå大å°å - `innodb_page_size=32k`: è¾å¤§ç页大å°éåå¤§æ°æ®éåºæ¯ - `innodb_buffer_pool_size=70G`: ç¼å²æ± 大å°ï¼å»ºè®®ä¸ºç©çå åç 60-80% - `innodb_flush_log_at_trx_commit=2`: æ¯ç§å·åæ¥å¿ï¼æ§è½ä¼å ï¼ééèåºæ¯ï¼ ## è§å - ä¼å ä½¿ç¨ MySQL MCP å·¥å ·ï¼`mcp__designcomputer-mysql_mcp_server__execute_sql`ã`mcp__designcomputer-mysql_mcp_server__get_schema_info`ã`mcp__designcomputer-mysql_mcp_server__get_table_sample`ï¼ç´æ¥æä½æ°æ®åºï¼èééè¿ Bash æ§è¡ mysql å½ä»¤ã - æ¥è¯¢ãéªè¯ãè°è¯æ°æ®åºæ¶ï¼ç´æ¥è°ç¨ MCP å·¥å ·æ§è¡ SQLï¼æ éåç¨æ·è¾åº SQL è¯å¥æè¯¢é®æ¯å¦æ§è¡ã - ä» å¨ä»¥ä¸åºæ¯è¾åº SQLï¼ç¨æ·æç¡®è¦æ±æ¥ç SQLãéè¦çæ SQL èæ¬æä»¶ï¼å¦è¿ç§»èæ¬ï¼ãMCP å·¥å ·ä¸å¯ç¨æ¶åéå° Bashã ## MCP å·¥å ·è¯´æ | å·¥å · | ç¨é | |------|------| | `execute_sql` | æ§è¡ SQL è¯å¥ï¼SELECT/INSERT/UPDATE/DELETE/DDLï¼ | | `get_schema_info` | è·åè¡¨ç»æä¿¡æ¯ï¼ååãç±»åãæ³¨éçï¼ | | `get_table_sample` | è·åè¡¨æ°æ®æ ·æ¬ï¼ç¨äºäºè§£æ°æ®æ ¼å¼ï¼ | ## æ§è¡ SQL èæ¬ å½ç¨æ·è¦æ±æ§è¡ SQL æä»¶æ¶ï¼ 1. å 读åæä»¶å 容 2. éæ¡æ§è¡ SQL è¯å¥ 3. éå°éè¯¯æ¶æ£æ¥è¡¨ç»æï¼è°æ´è¯å¥åéè¯ 4. æ±æ»æ§è¡ç»æ .agents/rules/file-upload.md
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,120 @@ # æä»¶ä¸ä¼ è§å ## æ ¸å¿ååï¼AI å¿ é¡»ä¸¥æ ¼éµå®ï¼ **æææä»¶ä¸ä¼ å¿ é¡»èµ° `yudao-module-system` 模åç Storage API + Attachment ä¸é´è¡¨æºå¶ã** **ç¦æ¢å¨ä»»ä½ä¸å¡æ¨¡åèªè¡è®¾è®¡æä»¶ä¸ä¼ æ¹æ¡ï¼å æ¬ä½ä¸éäºï¼æ·»å `file_url` åãæ°å¢ upload æ¥å£ãæ³¨å ¥ FileApiã** ## æä»¶ä¸ä¸å¡çå ³èæ¹å¼ æä»¶ä¸ä¸å¡è®°å½çå ³è**åªè½éè¿ `system_storage_attachment` ä¸é´è¡¨**ï¼ä¸å 许å¨ä¸å¡è¡¨ä¸æ·»å `file_url`ã`file_urls`ã`attachment_url` çåç¬¦ä¸²åæ®µå卿件å°åã å ³èæµç¨ï¼ 1. å端è°ç¨ `POST /system/storage-blob/upload` ä¸ä¼ æä»¶ â è·å¾ `blobId` 2. å端è°ç¨ `POST /system/storage-attachment/bind` ç»å®æä»¶å°ä¸å¡è®°å½ï¼ä¼ å ¥ `recordType` + `recordId` + `blobItems`ï¼ 3. å端è°ç¨ `GET /system/storage-attachment/list` æ¥è¯¢éä»¶ 4. å端è°ç¨ `DELETE /system/storage-attachment/delete` å é¤éä»¶ ## æ£ç¡®æ¥å£ï¼system 模åï¼ | æ¹æ³ | è·¯å¾ | 说æ | |------|------|------| | POST | `/system/storage-blob/upload` | ä¸ä¼ æä»¶ï¼`files` åæ°ï¼multipartï¼ | | POST | `/system/storage-blob/public-upload` | å ¬å ±æä»¶ä¸ä¼ ï¼æ°¸ä¹ ææï¼ | | GET | `/system/storage-blob/preview/{fileName}` | æä»¶é¢è§ | | GET | `/system/storage-blob/download/{fileName}` | æä»¶ä¸è½½ | | GET | `/system/storage-attachment/list` | æ¥è¯¢éä»¶å表 | | POST | `/system/storage-attachment/bind` | ç»å®éä»¶å°ä¸å¡è®°å½ | | DELETE | `/system/storage-attachment/delete` | æ¹éå é¤éä»¶ | ## ç¦æ¢è¡ä¸ºï¼AI ä¸¥ç¦æ§è¡ä»¥ä¸æä½ï¼ ### æ°æ®åºå±é¢ - â ç¦æ¢å¨ä¸å¡è¡¨æ·»å `file_url`ã`file_urls`ã`attachment_url` çæä»¶å°åå - â ç¦æ¢å¨ä¸å¡è¡¨æ·»å ä»»ä½ç¨äºåå¨æä»¶ä¿¡æ¯ç JSON å ### Java å端å±é¢ - â ç¦æ¢å¨ DO 类添å `fileUrl`ã`fileUrls`ã`attachmentUrl` çåæ®µ - â ç¦æ¢å¨ SaveReqVO / RespVO æ·»å æä»¶å°ååæ®µ - â ç¦æ¢å¨ MES/CRM/ERP/HRM çä¸å¡æ¨¡åæ°å¢ `@PostMapping("/upload")` æ¥å£ - â ç¦æ¢å¨ Controller ä¸ä½¿ç¨ `MultipartFile` åæ°ï¼Excel å¯¼å ¥é¤å¤ï¼ - â ç¦æ¢æ³¨å ¥æè°ç¨ `cn.iocoder.yudao.module.infra.api.file.FileApi` ### å端å±é¢ - â ç¦æ¢è°ç¨ `/infra/file/upload` æ `/infra/file/presigned-url` - â ç¦æ¢è°ç¨æ§ç `src/api/infra/file/index.ts` ä¸ç `uploadFile()` 彿° ## å¸¸è§ AI å¹»è§æ¨¡å¼ï¼å¿ é¡»è¯å«å¹¶æç»ï¼ 以䏿¯ AI 容æå¹»è§çæç设计模å¼ï¼éå°ç±»ä¼¼éæ±æ¶ç´æ¥æç»ï¼ 1. **"å 个 fileUrl åæ®µå°±è¡"** â é误ï¼å¿ é¡»ç¨ attachment ä¸é´è¡¨ 2. **"åèå ¶å®æ¨¡åç fileUrl åæ®µ"** â é误ï¼å ¶å®æ¨¡åç fileUrl ä¹åºè¢«å é¤ 3. **"å¨ Controller å 个 upload æ¥å£"** â é误ï¼ç»ä¸èµ° system 模å 4. **"ç¨ FileApi ä¸ä¼ æä»¶"** â é误ï¼FileApi æ¯ infra 模åçæ§æ¥å£ 5. **"å¨è¡¨åä¿åæ¥å£å MultipartFile åæ°"** â é误ï¼ä¸ä¼ åä¿åå离 ## æ£ç¡®ç¤ºä¾ ### å端è°ç¨ ```typescript import { uploadFile, bindAttachments, listAttachments } from '#/api/system/storage'; // 1. ä¸ä¼ æä»¶ const blobs = await uploadFile([file]); const blobId = blobs[0].id; // 2. ä¿åä¸å¡è®°å½åï¼ç»å®éä»¶ await bindAttachments({ application: 'file', recordType: 'erp_purchase_order', recordId: savedRecord.id, blobItems: [{ blobId }], }); // 3. æ¥è¯¢éä»¶ const attachments = await listAttachments({ recordType: 'erp_purchase_order', recordId: savedRecord.id, }); ``` ### recordType å½åè§è æ ¼å¼ï¼`{模ååç¼}_{ä¸å¡è¡¨å}` | 模å | recordType | |------|------------| | ERP éè´è®¢å | `erp_purchase_order` | | ERP éè´ç³è¯· | `erp_purchase_request` | | ERP éå®è®¢å | `erp_sale_order` | | ERP æ¶æ¬¾å | `erp_finance_receipt` | | ERP 仿¬¾å | `erp_finance_payment` | | HRM 请å | `hrm_leave_application` | | HRM 离è | `hrm_resignation_application` | | HRM è°å¨ | `hrm_transfer_application` | | MES éè´è®¢å | `mes_purchase_order` | | MES å·¥å | `mes_pro_work_order` | | CRM å®¢æ· | `crm_customer` | ## AI èªæ¥æ¸ å å¨å®ç°ä»»ä½æä»¶ä¸ä¼ ç¸å ³éæ±åï¼éé¡¹ç¡®è®¤ï¼ - [ ] æ¯å¦ä½¿ç¨äº `/system/storage-blob/upload` ä¸ä¼ ï¼ - [ ] æ¯å¦ä½¿ç¨äº `/system/storage-attachment/bind` å ³èï¼ - [ ] æ¯å¦æ²¡æå¨ä¸å¡è¡¨ä¸æ·»å æä»¶å°ååï¼ - [ ] æ¯å¦æ²¡æå¨ DO/VO 䏿·»å æä»¶å°ååæ®µï¼ - [ ] æ¯å¦æ²¡æå建æ°ç upload Controller æ¥å£ï¼ - [ ] æ¯å¦æ²¡æä½¿ç¨ infra 模åç FileApiï¼ ä»¥ä¸ 6 é¡¹å ¨é¨ä¸º"æ¯"æå¯ç»§ç»ã ## 为ä»ä¹ - åå¨é ç½®ï¼æ¬å°/S3ï¼å¨ system 模åç»ä¸ç®¡ç - éä»¶ç»å®æºå¶ï¼storage-attachmentï¼æä¾ `recordType + recordId` çæ åå ³èæ¹å¼ï¼æ¯æå¤æä»¶ãç¨éåç±» - infra 模åç `/infra/file/*` æ¯æ§çæ¥å£ï¼å卿件ä¸ä¸å¡è®°å½æ æ³å ³èçé®é¢ - ä¸å¡è¡¨åå¨ file_url åç¬¦ä¸²æ æ³æ¯æå¤æä»¶ï¼ä¸æä»¶ä¸ä¸å¡è®°å½ä¹é´ç¼ºä¹æ ååçå ³è管ç .agents/rules/frontend-doc-generation.md
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,93 @@ --- name: frontend-doc-generation description: å端èè°æ¹æ¡çæè§å metadata: type: feedback --- # å端èè°æ¹æ¡è§å ## è§å å端æ¥å£åæ´æ¶ï¼**ä¸çæå端代ç **ï¼åªçæå端èè°æ¹æ¡ææ¡£ï¼è¾åºå° `docs/` ç®å½ã ## ç¦æ¢å 容 - â ç¦æ¢å Vue 模æ¿ä»£ç ï¼`<el-table-column>`ã`<el-form-item>` çï¼ - â ç¦æ¢å JavaScript/TypeScript 代ç ï¼æ¹æ³ã彿°ãé»è¾å¤æçï¼ - â ç¦æ¢å CSS æ ·å¼ä»£ç - â ç¦æ¢åå端ç»ä»¶ä½¿ç¨ç¤ºä¾ ## èè°æ¹æ¡å å®¹è¦æ± ææ¡£åºåªå å«ä»¥ä¸å å®¹ï¼ - **API 说æ**ï¼è·¯å¾ãæ¹æ³ã请æ±åæ°ãååºæ ¼å¼ - **æ¶åçå端页é¢**ï¼ååºéè¦ä¿®æ¹ç页é¢åç§° - **æ°æ®å¸¦å ¥è¯´æ**ï¼éç¹å¼ºè°ä¿¡æ¯å¦ä½ä»ä¸ä¸æ¥å¸¦å ¥ï¼å¦éè´è®¢å -> å°è´§éç¥ -> éè´å ¥åºï¼ - **æ¥å£è°ç¨æ¶åº**ï¼è¯´æä½æ¶è°ç¨åªä¸ªæ¥å£è·åå ³èæ°æ® - **åæ®µå±ç¤ºè§å**ï¼ç¨è¡¨æ ¼è¯´æå段å±ç¤ºä½ç½®å说æï¼ä¸å代ç - **ä¸å¡è§å说æ**ï¼ç¨æåæè¡¨æ ¼è¯´æä¸å¡é»è¾ - **注æäºé¡¹**ï¼éè¦å端注æçè¾¹çæ åµ ## ææ¡£æ¨¡æ¿ ```markdown # åè½åç§° - å端èè°æ¹æ¡ ## æ¶åé¡µé¢ - 页é¢1 - 页é¢2 ## ä¸å¡æµç¨ä¸æ°æ®å¸¦å ¥ 1. æ¥éª¤1 -> æ¥éª¤2ï¼å段Aãåæ®µB 仿¥éª¤1èªå¨å¸¦å ¥ 2. æ¥éª¤2 -> æ¥éª¤3ï¼å段C 仿¥éª¤2èªå¨å¸¦å ¥ ## API | æ¹æ³ | è·¯å¾ | 说æ | |------|------|------| | POST | /xxx/xxx | 说æ | **请æ±åæ°ï¼** | åæ° | ç±»å | å¿ å¡« | 说æ | |------|------|------|------| | xxx | String | æ¯ | xxx | **ååºå段æ°å¢ï¼** | åæ®µ | ç±»å | 说æ | |------|------|------| | xxx | String | xxx | **ååºç¤ºä¾ï¼** ```json { "code": 0, "data": {} } ``` ## åæ®µå±ç¤ºè§å | åæ®µ | å±ç¤ºä½ç½® | 说æ | |------|----------|------| | åæ®µA | å表ã详æ | 说æ | | åæ®µB | 详æ | 说æ | ## ä¸å¡è§å说æ | åºæ¯ | è§å | |------|------| | åºæ¯1 | è§å说æ | | åºæ¯2 | è§å说æ | ## 注æäºé¡¹ - 注æäºé¡¹1 - 注æäºé¡¹2 ``` .agents/rules/frontend-docs.md
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,64 @@ # æ¥å£åæ´å端èè°æ¹æ¡è§å ## è§å - æ¯æ¬¡æ¶åæ¥å£åæ´ï¼æ°å¢æ¥å£ãä¿®æ¹è¯·æ±åæ°ãä¿®æ¹ååºç»æï¼æ¶ï¼å¿ é¡»è¾åºä¸ä»½**å端èè°æ¹æ¡**å° `docs/` ç®å½ã - **ä¸çæå端代ç **ï¼åªçæèè°æ¹æ¡ææ¡£ï¼æå¯¼å端å¼å人åå¦ä½å¯¹æ¥ã - ææ¡£ä»¥ Markdown æ ¼å¼ç¼åï¼æä»¶å使ç¨å°å忝åä¸å线ï¼å¦ `docs/purchase_receipt_integration.md`ã - ææ¡£å 容éç¹å¼ºè°**ä¿¡æ¯å¸¦å ¥**é®é¢ï¼åªäºæ°æ®ä»ä¸ä¸æ¥èªå¨å¸¦å ¥ã使¶è·åå ³èæ°æ®ãåæ®µå¦ä½æ å°ã ## ææ¡£æ¨¡æ¿ ```markdown # åè½åç§° ## æ¶åé¡µé¢ - 页é¢1 - 页é¢2 ## API | æ¹æ³ | è·¯å¾ | 说æ | |------|------|------| | POST | /xxx/xxx | 说æ | **请æ±åæ°ï¼** | åæ° | ç±»å | å¿ å¡« | 说æ | |------|------|------|------| | xxx | String | æ¯ | xxx | **ååºï¼** `{ "code": 200, "msg": "æä½æå" }` ## å端修æ¹ç¹ ### 1. æé®/å¼¹çª/è¡¨æ ¼ç \`\`\`html <el-button>xxx</el-button> \`\`\` ### 2. data æ°æ® \`\`\`js data() { return { xxx: '', } } \`\`\` ### 3. æ¹æ³ \`\`\`js handleXxx() { // ... } \`\`\` ## 注æäºé¡¹ - 注æäºé¡¹1 - 注æäºé¡¹2 ``` .agents/rules/frontend-project.md
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,16 @@ # å端项ç®è§å ## è§å - `mom-pro2-before` æ¯å端项ç®ï¼ä½äºåç«¯é¡¹ç®æ ¹ç®å½ä¸ï¼`D:\ç马\åé\å端\mom-pro2-after\mom-pro2-before`ï¼ã - å¯ä»¥æ¥çå端代ç ä½ä¸ºåèï¼å¸®å©çè§£ä¸å¡é»è¾ã设计åç«¯æ¹æ¡ã - **æªç»ç¨æ·æç¡®å 许ï¼ç¦æ¢ä¿®æ¹å端项ç®ä¸ç任使件ã** ## 使ç¨åºæ¯ | å 许 | ç¦æ¢ | |------|------| | 读åå端æä»¶äºè§£ç°æå®ç° | ä¿®æ¹å端代ç | | åèå端 API è°ç¨æ¹å¼è®¾è®¡å端æ¥å£ | æ°å¢å端ç»ä»¶/é¡µé¢ | | åèåç«¯æ°æ®ç»æçè§£ä¸å¡ | å é¤å端æä»¶ | | å¨å端项ç®ä¸æç´¢/æ¥æ¾ä»£ç | éæå端代ç | .agents/rules/handover-data-query.md
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,103 @@ # æ°æ®äº¤æ¥æ¥è¯¢è§å ## è§å æææ¨¡å卿¥è¯¢"æçæ°æ®"ï¼æç¨æ· ID è¿æ»¤çä¸å¡æ°æ®åè¡¨ï¼æ¶ï¼**å¿ é¡»è°ç¨ `HrmUserHandoverApi.expandUserIds()`** æ©å±ç¨æ· ID éåï¼ç¡®ä¿äº¤æ¥äººè½æ¥ç已离è交æ¥äººçä¸å¡æ°æ®ã ## èæ¯ å½åå·¥ç¦»èæ¶ï¼å ¶ä¸å¡æ°æ®ï¼å®¢æ·ãåæºãå·¥åãéè´ç³è¯·çï¼éè¦äº¤ç±äº¤æ¥äººç®¡çãç³»ç»éè¿ `hrm_user_handover` æ å°è¡¨è®°å½äºç¦»èç¨æ·ä¸äº¤æ¥ç®æ ç¨æ·çå ³èï¼èéç´æ¥ä¿®æ¹ä¸å¡æ°æ®çææè åæ®µã æ¥è¯¢æ¶éè¿ `expandUserIds` å°äº¤æ¥æºç¨æ· ID çº³å ¥æ¥è¯¢èå´ï¼ä½¿äº¤æ¥äººè½åæ¶çå°èªå·±å离èäººçæ°æ®ã ## æ ¸å¿ API ```java // æ¥å£ï¼HrmUserHandoverApi (yudao-module-hrm-api) public interface HrmUserHandoverApi { Set<Long> expandUserIds(Set<Long> userIds); } ``` ## 忍¡åéææ¹å¼ ### åºæ¬ç¨æ³ ```java // æ³¨å ¥ API @Resource private HrmUserHandoverApi handoverApi; // 卿¥è¯¢åæ©å± userId public PageResult<XxxDO> getXxxPage(XxxPageReqVO reqVO) { Long currentUserId = getCurrentUserId(); Set<Long> expandedUserIds = handoverApi.expandUserIds(Set.of(currentUserId)); // ç¨ expandedUserIds å IN æ¥è¯¢ return mapper.selectPage(reqVO, new LambdaQueryWrapperX<XxxDO>() .in(XxxDO::getOwnerUserId, expandedUserIds) ...); } ``` ### Mapper å±åæ³ ```java // 妿 userId æ¯å¨æä¼ å ¥çï¼å¦ç®¡çå坿¥çææäººçæ°æ®ï¼ï¼å åå¤æï¼ default PageResult<XxxDO> selectPage(XxxPageReqVO reqVO, Set<Long> userIds) { return selectPage(reqVO, new LambdaQueryWrapperX<XxxDO>() .inIfPresent(XxxDO::getOwnerUserId, CollUtil.isNotEmpty(userIds) ? userIds : null) ...); } ``` ## 忍¡åæ¶åçæ¥è¯¢åºæ¯ | 模å | æ¥è¯¢åºæ¯ | æ¶å表 | ç¨æ·å段 | |------|----------|--------|----------| | CRM | æçå®¢æ· | `crm_customer` | `ownerUserId` | | CRM | æç线索 | `crm_clue` | `ownerUserId` | | CRM | æçè系人 | `crm_contact` | `ownerUserId` | | CRM | æçåå | `crm_contract` | `ownerUserId` | | CRM | æçåæº | `crm_business` | `ownerUserId` | | CRM | æç忬¾ | `crm_receivable` | `ownerUserId` | | CRM | æç忬¾è®¡å | `crm_receivable_plan` | `ownerUserId` | | CRM | æçæ¥ä»· | `crm_sale_quotation` | `ownerUserId` | | ERP | æçéè´ç³è¯· | `erp_purchase_request` | `requestUserId` | | ERP | æçéå®è®¢å | `erp_sale_order` | `saleUserId` | | ERP | æç仿¬¾å | `erp_finance_payment` | `financeUserId` | | ERP | æçæ¶æ¬¾å | `erp_finance_receipt` | `financeUserId` | | MES | æçå·¥å/æ¥å·¥/è´¨æ£ç | å MES 表 | `userId` / `inspectorUserId` / `chargeUserId` ç | ## expandUserIds åç ``` hrm_user_handover 表记å½: ââââââ¬âââââââââââââââ¬âââââââââââââ¬ââââââââââââââââ¬ââââââââââââââââââ â id â from_user_id â to_user_id â resignation_idâ handover_status â ââââââ¼âââââââââââââââ¼âââââââââââââ¼ââââââââââââââââ¼âââââââââââââââââ⤠â 1 â 100(离è人) â 101(交æ¥äºº)â 1 â 20(已宿) â ââââââ´âââââââââââââââ´âââââââââââââ´ââââââââââââââââ´ââââââââââââââââââ expandUserIds({101}) â {101, 100} â 交æ¥äºº101æ¥è¯¢æ¶ï¼WHERE owner_user_id IN (101, 100) â åæ¶çå°èªå·±çå离èäººçæ°æ® ``` ## 注æäºé¡¹ - `expandUserIds` åªæ©å±**已宿ç交æ¥è®°å½**ï¼`handoverStatus = 20`ï¼ - ä¼ å ¥ç©ºéåè¿å空éå - æ¥è¯¢æ§è½ï¼æ å°è¡¨æ°æ®éæå°ï¼ä» 为已离èäººåæ°ï¼ï¼å¯æ¾å¿å¨æ¯æ¬¡æ¥è¯¢åè°ç¨ - 妿æ¥è¯¢è¦æ±ä¸¥æ ¼åªçæ¬äººæ°æ®ï¼å¦ä¸ªäººè®¾ç½®é¡µï¼ï¼åä¸åºè°ç¨ `expandUserIds` ## Maven ä¾èµ éè¦å¨æ¨¡åç `pom.xml` 䏿·»å 对 HRM API çä¾èµï¼ ```xml <dependency> <groupId>cn.iocoder.boot</groupId> <artifactId>yudao-module-hrm-api</artifactId> <version>${revision}</version> </dependency> ``` .agents/rules/powershell-commands.md
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,35 @@ # PowerShell å½ä»¤è§å ## è§å ç±äºé¡¹ç®è¿è¡å¨ Windows ç¯å¢ä¸ï¼ç¨æ·ç»ç«¯é»è®¤ä½¿ç¨ PowerShell èé bashã卿ä¾å½ä»¤è¡æå¯¼æ¶ï¼å¿ é¡»ä½¿ç¨ PowerShell è¯æ³ã ## 常ç¨å½ä»¤å¯¹ç § | æä½ | Bash | PowerShell | |------|------|-------------| | å é¤ç®å½ | `rm -rf <path>` | `Remove-Item -Recurse -Force <path>` æç®å `ri -r -fo <path>` | | å¤å¶æä»¶ | `cp <src> <dest>` | `Copy-Item <src> <dest>` æç®å `cp <src> <dest>` | | ç§»å¨æä»¶ | `mv <src> <dest>` | `Move-Item <src> <dest>` æç®å `mv <src> <dest>` | | å建ç®å½ | `mkdir -p <path>` | `New-Item -ItemType Directory -Path <path>` æç®å `md <path>` | | æ¥çæä»¶ | `cat <file>` | `Get-Content <file>` æç®å `gc <file>` | | ååºæä»¶ | `ls` | `Get-ChildItem` æç®å `ls`/`dir` | | æ¥æ¾æä»¶ | `find . -name "*.java"` | `Get-ChildItem -Recurse -Filter "*.java"` æç®å `gci -r -fi "*.java"` | | æ¥æ¾å 容 | `grep -r "pattern" .` | `Select-String -Path "*.java" -Pattern "pattern"` æç®å `sls "pattern" *.java -r` | ## å é¤ç®å½ç¤ºä¾ ```powershell # æ¨èï¼ä½¿ç¨ Remove-Item 宿´å½ä»¤ Remove-Item -Recurse -Force yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/client # ç®åå½¢å¼ ri -r -fo yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/client ``` ## 注æäºé¡¹ - PowerShell åæ°ä½¿ç¨ `-` åç¼ï¼å¦ `-Recurse`ã`-Force` - åæ°å¯ä»¥ç®åé¦åæ¯ï¼å¦ `-r`ã`-fo` - è·¯å¾åéç¬¦å¨ PowerShell ä¸å¯ä»¥ä½¿ç¨ `/` æ `\`ï¼æ¨èä½¿ç¨ `/` ä»¥ä¿æè·¨å¹³å°å ¼å®¹ - é¿å `-rf` è¿æ ·ç bash 飿 ¼åæ°ç»åï¼PowerShell 䏿¯æ .agents/rules/security-guardrails.md
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,37 @@ # å®å ¨æ¤æ ## SQL æ³¨å ¥é²æ¤ - æ°¸è¿ä¸è¦å°ç¨æ·è¾å ¥ç´æ¥æ¼æ¥å° SQL æ¥è¯¢ä¸ã - å§ç»ä½¿ç¨åæ°åæ¥è¯¢æ ORM æ¹æ³ã - 卿°æ®åºæä½åéªè¯åæ¸ çææè¾å ¥ã ## XSS 鲿¤ - å¨ HTML 䏿¸²æåè½¬ä¹ææç¨æ·çæçå 容ã - ä½¿ç¨æ¨¡æ¿èªå¨è½¬ä¹åè½ã - å¨å®¢æ·ç«¯åæå¡å¨ç«¯é½è¿è¡è¾å ¥éªè¯åæ¸ çã ## å¯é¥ç®¡ç - æ°¸è¿ä¸è¦å¨æºä»£ç ä¸ç¡¬ç¼ç å¯é¥ãAPI å¯é¥æå¯ç ã - 使ç¨ç¯å¢åéæå®å ¨ä¿é©åºåå¨æææ°æ®ã - å®æè½®æ¢å¯é¥ï¼é¿å 卿¥å¿ä¸è®°å½å®ä»¬ã ## è¾å ¥éªè¯ - å¨ç³»ç»è¾¹çå¤éªè¯ææè¾å ¥ã - 使ç¨ç½ååèéé»ååè¿è¡éªè¯ã - å¨å¤çä¸ä¼ åæ£æ¥æä»¶ç±»åå大å°ã ## é误å¤ç - ä¸è¦å¨éè¯¯æ¶æ¯ä¸æ´é²ææä¿¡æ¯ã - å¨å é¨è®°å½è¯¦ç»é误ï¼åç¨æ·è¿åéç¨æ¶æ¯ã - å¤çææå¼å¸¸ä»¥é²æ¢ä¿¡æ¯æ³é²ã ## ä¾èµç®¡ç - ä¿æä¾èµé¡¹æ´æ°ã - 审计ä¾èµé¡¹ä¸çå·²ç¥æ¼æ´ã - åºå®çæ¬ä»¥é¿å æå¤çç ´åæ§åæ´ã .agents/rules/sql-docs-sync.md
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,12 @@ --- description: Automatically sync new SQL files to docs/sql and update the index --- # SQL Files Management Rule **CRITICAL RULE**: Whenever you create or add a new SQL file (e.g. database migrations, init scripts) in this project (such as in `sql/mysql/` or anywhere else), you MUST: 1. **Copy/Create a duplicate** of that SQL file in the `docs/sql/` directory. 2. **Update the SQL Index**: Record the newly added SQL file in `docs/sql/README.md` (create the README file if it does not exist) with a brief description of what the SQL file does. Always remember this when you are generating new SQL scripts to maintain the project's documentation consistency. .agents/rules/sql-script.md
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,6 @@ # SQL èæ¬è§è ## è§å - SQL èæ¬ä¸ä¸è¦å å« tenant_id åæ®µï¼é¡¹ç®ä¸ä½¿ç¨å¤ç§æ·æ¨¡å¼ã - INSERT è¯å¥åªå å«å¿ è¦çä¸å¡å段ï¼ä¸éè¦ tenant_idã .agents/rules/sql-syntax.md
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,40 @@ # SQL è¯æ³è§è ## è§å - MySQL `ALTER TABLE ADD COLUMN` 䏿¯æ `IF NOT EXISTS` è¯æ³ï¼å³ä½¿ MySQL 8.0+ ä¹ä¸æ¯æã - æ·»å åæ®µåéè¦å æ£æ¥å段æ¯å¦åå¨ï¼æç´æ¥æ§è¡å¹¶å¿½ç¥"åæ®µå·²åå¨"é误ã ## æ£ç¡®ç¤ºä¾ ```sql -- æ£ç¡®ï¼ç´æ¥æ·»å åæ®µ ALTER TABLE mes_wm_sn ADD COLUMN uuid VARCHAR(64) COMMENT 'æ¹æ¬¡UUID' AFTER id; -- æ£ç¡®ï¼å æ£æ¥åæ·»å ï¼å ¼å®¹èæ¬ï¼ SET @exists = (SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'mes_wm_sn' AND column_name = 'uuid'); SET @sql = IF(@exists = 0, 'ALTER TABLE mes_wm_sn ADD COLUMN uuid VARCHAR(64) COMMENT ''æ¹æ¬¡UUID'' AFTER id', 'SELECT 1'); PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; ``` ## éè¯¯ç¤ºä¾ ```sql -- é误ï¼ADD COLUMN 䏿¯æ IF NOT EXISTS ALTER TABLE mes_wm_sn ADD COLUMN IF NOT EXISTS uuid VARCHAR(64) COMMENT 'æ¹æ¬¡UUID' AFTER id; ``` ## IF NOT EXISTS æ¯æåºæ¯ | è¯å¥ | æ¯å¦æ¯æ | |------|---------| | CREATE TABLE IF NOT EXISTS | â æ¯æ | | CREATE INDEX IF NOT EXISTS | â æ¯æï¼MySQL 8.0+ï¼ | | ALTER TABLE ADD COLUMN IF NOT EXISTS | â 䏿¯æ | | ALTER TABLE ADD INDEX IF NOT EXISTS | â æ¯æï¼MySQL 8.0+ï¼ | | ALTER TABLE ADD CONSTRAINT IF NOT EXISTS | â 䏿¯æ | docs/sql/README.md
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,5 @@ # SQL èæ¬ç´¢å¼ æ¬é¡¹ç®å å«ç SQL èæ¬æ±æ»å¦ä¸ï¼ - [mes_pro_task_assignee.sql](mes_pro_task_assignee.sql): å¢å çäº§ä»»å¡æ¥å·¥äººåå ³è表ï¼ç¨äºå¤äººåæ¥å·¥åé ã docs/sql/mes_pro_task_assignee.sql
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,14 @@ CREATE TABLE `mes_pro_task_assignee` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ç¼å·', `task_id` bigint(20) NOT NULL COMMENT 'çäº§ä»»å¡ ID', `user_id` bigint(20) NOT NULL COMMENT 'ç³»ç»ç¨æ· ID (æå®æ¥å·¥äººå)', `creator` varchar(64) DEFAULT '' COMMENT 'å建è ', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'å建æ¶é´', `updater` varchar(64) DEFAULT '' COMMENT 'æ´æ°è ', `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ´æ°æ¶é´', `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT 'æ¯å¦å é¤', `tenant_id` bigint(20) NOT NULL DEFAULT '0' COMMENT 'ç§æ·ç¼å·', PRIMARY KEY (`id`), KEY `idx_task_id` (`task_id`), KEY `idx_user_id` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='çäº§ä»»å¡æå®æ¥å·¥äººå表'; sql/mysql/mes_pro_task_assignee.sql
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,14 @@ CREATE TABLE `mes_pro_task_assignee` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ç¼å·', `task_id` bigint(20) NOT NULL COMMENT 'çäº§ä»»å¡ ID', `user_id` bigint(20) NOT NULL COMMENT 'ç³»ç»ç¨æ· ID (æå®æ¥å·¥äººå)', `creator` varchar(64) DEFAULT '' COMMENT 'å建è ', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'å建æ¶é´', `updater` varchar(64) DEFAULT '' COMMENT 'æ´æ°è ', `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ´æ°æ¶é´', `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT 'æ¯å¦å é¤', `tenant_id` bigint(20) NOT NULL DEFAULT '0' COMMENT 'ç§æ·ç¼å·', PRIMARY KEY (`id`), KEY `idx_task_id` (`task_id`), KEY `idx_user_id` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='çäº§ä»»å¡æå®æ¥å·¥äººå表'; yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/MesProTaskController.java
@@ -349,6 +349,15 @@ inTransitMap.merge(fb.getTaskId(), fbQty, BigDecimal::add); } } // æ¥è¯¢ææ´¾æ¥å·¥äººå Set<Long> taskIds = convertSet(list, MesProTaskDO::getId); Map<Long, List<cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskAssigneeDO>> assigneeMap = taskService.getTaskAssigneeMap(taskIds); Set<Long> allAssigneeUserIds = new HashSet<>(); assigneeMap.values().forEach(assignees -> assignees.forEach(assignee -> allAssigneeUserIds.add(assignee.getUserId()))); Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(allAssigneeUserIds); // æ¼æ¥ VO return convertList(list, task -> { MesProTaskRespVO vo = BeanUtils.toBean(task, MesProTaskRespVO.class); @@ -379,6 +388,22 @@ findAndThen(processCheckMap, task.getProcessId(), vo::setCheckFlag)); findAndThen(routeProcessBackflushFlagMap, task.getRouteId(), processBackflushMap -> findAndThen(processBackflushMap, task.getProcessId(), vo::setBackflushFlag)); // è®¾ç½®ææ´¾æ¥å·¥äººå List<cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskAssigneeDO> assignees = assigneeMap.get(task.getId()); if (CollUtil.isNotEmpty(assignees)) { List<Long> assigneeUserIds = convertList(assignees, cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskAssigneeDO::getUserId); vo.setAssigneeUserIds(assigneeUserIds); List<String> assigneeUserNames = new ArrayList<>(); for (Long userId : assigneeUserIds) { AdminUserRespDTO user = userMap.get(userId); if (user != null && user.getNickname() != null) { assigneeUserNames.add(user.getNickname()); } } vo.setAssigneeUserNames(String.join(",", assigneeUserNames)); } return vo; }); } yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/vo/MesProTaskRespVO.java
@@ -134,4 +134,10 @@ @Schema(description = "åå·¥åå²è®°å½") private List<MesProTaskPauseRecordRespVO> pauseRecords; @Schema(description = "æå®æ¥å·¥äººå ID éå") private List<Long> assigneeUserIds; @Schema(description = "æå®æ¥å·¥äººåå§åéå") private List<String> assigneeUserNames; } yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/vo/MesProTaskSaveReqVO.java
@@ -51,4 +51,7 @@ @Schema(description = "夿³¨", example = "夿³¨") private String remark; @Schema(description = "æå®æ¥å·¥äººå ID éå") private java.util.List<Long> assigneeUserIds; } yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/task/MesProTaskAssigneeDO.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,40 @@ package cn.iocoder.yudao.module.mes.dal.dataobject.pro.task; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.*; /** * çäº§ä»»å¡æå®æ¥å·¥äººå表 DO * * @author èªå¨çæ */ @TableName("mes_pro_task_assignee") @KeySequence("mes_pro_task_assignee_seq") @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) @Builder @NoArgsConstructor @AllArgsConstructor public class MesProTaskAssigneeDO extends BaseDO { /** * ç¼å· */ @TableId private Long id; /** * çäº§ä»»å¡ ID * * å ³è {@link MesProTaskDO#getId()} */ private Long taskId; /** * ç³»ç»ç¨æ· ID (æå®æ¥å·¥äººå) */ private Long userId; } yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/task/MesProTaskAssigneeMapper.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,38 @@ package cn.iocoder.yudao.module.mes.dal.mysql.pro.task; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskAssigneeDO; import org.apache.ibatis.annotations.Mapper; import java.util.Collection; import java.util.List; /** * çäº§ä»»å¡æå®æ¥å·¥äººå Mapper * * @author èªå¨çæ */ @Mapper public interface MesProTaskAssigneeMapper extends BaseMapperX<MesProTaskAssigneeDO> { default List<MesProTaskAssigneeDO> selectListByTaskId(Long taskId) { return selectList(MesProTaskAssigneeDO::getTaskId, taskId); } default List<MesProTaskAssigneeDO> selectListByTaskIds(Collection<Long> taskIds) { return selectList(new LambdaQueryWrapperX<MesProTaskAssigneeDO>() .inIfPresent(MesProTaskAssigneeDO::getTaskId, taskIds)); } default void deleteByTaskId(Long taskId) { delete(MesProTaskAssigneeDO::getTaskId, taskId); } default void deleteByTaskIdAndUserIds(Long taskId, Collection<Long> userIds) { delete(new LambdaQueryWrapperX<MesProTaskAssigneeDO>() .eq(MesProTaskAssigneeDO::getTaskId, taskId) .in(MesProTaskAssigneeDO::getUserId, userIds)); } } yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskService.java
@@ -176,4 +176,23 @@ */ void resumeTask(cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProTaskResumeReqVO resumeReqVO); /** * è·åç产任å¡çæå®æ¥å·¥äººååè¡¨ï¼æä»»å¡ç¼å·åç» * * @param taskIds ä»»å¡ç¼å·éå * @return ä»»å¡å ³èç人åå表 Map */ default Map<Long, List<cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskAssigneeDO>> getTaskAssigneeMap(Collection<Long> taskIds) { if (cn.hutool.core.collection.CollUtil.isEmpty(taskIds)) { return java.util.Collections.emptyMap(); } return cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap( getTaskAssigneeListByTaskIds(taskIds), cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskAssigneeDO::getTaskId); } /** * æ ¹æ®ä»»å¡ç¼å·éåè·åå ³è人åå表 */ List<cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskAssigneeDO> getTaskAssigneeListByTaskIds(Collection<Long> taskIds); } yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskServiceImpl.java
@@ -58,6 +58,8 @@ import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskPauseRecordDO; import cn.iocoder.yudao.module.mes.dal.mysql.pro.task.MesProTaskPauseRecordMapper; import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskAssigneeDO; import cn.iocoder.yudao.module.mes.dal.mysql.pro.task.MesProTaskAssigneeMapper; import cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProTaskPauseReqVO; import cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProTaskResumeReqVO; @@ -78,6 +80,9 @@ @Resource private MesProTaskPauseRecordMapper taskPauseRecordMapper; @Resource private MesProTaskAssigneeMapper taskAssigneeMapper; @Resource @Lazy @@ -141,6 +146,10 @@ } // 2.2 æå ¥ taskMapper.insert(task); // 2.3 ä¿åæå®æ¥å·¥äººå updateTaskAssignees(task.getId(), createReqVO.getAssigneeUserIds()); return task.getId(); } @@ -173,6 +182,34 @@ MesProTaskDO updateObj = BeanUtils.toBean(updateReqVO, MesProTaskDO.class) .setName(buildTaskName(item, quantity)); taskMapper.updateById(updateObj); // 3. æ´æ°æå®æ¥å·¥äººåï¼æ¯å¯¹æ´æ°ï¼ updateTaskAssignees(task.getId(), updateReqVO.getAssigneeUserIds()); } private void updateTaskAssignees(Long taskId, List<Long> assigneeUserIds) { if (assigneeUserIds == null) { assigneeUserIds = Collections.emptyList(); } // 1. æ¥è¯¢æ°æ®åºä¸åæç List<MesProTaskAssigneeDO> oldAssignees = taskAssigneeMapper.selectListByTaskId(taskId); Set<Long> oldUserIds = convertSet(oldAssignees, MesProTaskAssigneeDO::getUserId); Set<Long> newUserIds = new java.util.HashSet<>(assigneeUserIds); // 2. 计ç®éè¦æ°å¢çåéè¦å é¤ç Set<Long> toAddUserIds = newUserIds.stream().filter(id -> !oldUserIds.contains(id)).collect(java.util.stream.Collectors.toSet()); Set<Long> toRemoveUserIds = oldUserIds.stream().filter(id -> !newUserIds.contains(id)).collect(java.util.stream.Collectors.toSet()); // 3. æ°å¢ if (CollUtil.isNotEmpty(toAddUserIds)) { List<MesProTaskAssigneeDO> addList = convertList(toAddUserIds, userId -> new MesProTaskAssigneeDO().setTaskId(taskId).setUserId(userId)); taskAssigneeMapper.insertBatch(addList); } // 4. å é¤ if (CollUtil.isNotEmpty(toRemoveUserIds)) { taskAssigneeMapper.deleteByTaskIdAndUserIds(taskId, toRemoveUserIds); } } /** @@ -245,8 +282,10 @@ if (feedbackMapper.selectCountByTaskId(id) > 0) { throw exception(PRO_TASK_HAS_FEEDBACK); } // 3. å é¤ // 3. å é¤ä»»å¡ä¸»è¡¨ taskMapper.deleteById(id); // 4. å é¤å ³èç人å taskAssigneeMapper.deleteByTaskId(id); } @Override @@ -273,6 +312,14 @@ } @Override public List<MesProTaskAssigneeDO> getTaskAssigneeListByTaskIds(Collection<Long> taskIds) { if (CollUtil.isEmpty(taskIds)) { return Collections.emptyList(); } return taskAssigneeMapper.selectListByTaskIds(taskIds); } @Override public MesProTaskDO validateTaskExists(Long id) { MesProTaskDO task = taskMapper.selectById(id); if (task == null) {