liu
8 天以前 38d39b8c11096668d1a49eea84bbec298489d23e
Merge remote-tracking branch 'origin/dev_business' into dev_business
已添加8个文件
已修改6个文件
12145 ■■■■■ 文件已修改
.claude/rules/error-message-precision.md 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.claude/rules/sql-config-export-init.md 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.gitignore 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/qc_export_integration.md 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/qc_template_option_selection.md 100 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/sql/bpm_audit_flow_01_repair_and_category.sql 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/sql/bpm_audit_flow_02_deploy_proc.sql 202 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/sql/config_export_all_20260904.sql 11249 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockServiceImpl.java 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueDetailServiceImpl.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transaction/MesWmTransactionServiceImpl.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-server/src/main/resources/application-yzfx.yaml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-server/src/main/resources/application-zsjc.yaml 206 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.claude/rules/error-message-precision.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,46 @@
# é”™è¯¯æç¤ºæ–‡æ¡ˆç²¾ç¡®æ€§è§„则
## è§„则
- é¢å‘用户的校验/异常提示(后端异常 message、Excel å¯¼å…¥æ ¡éªŒæç¤ºã€å‰ç«¯ toast、接口报错文案),**禁止写成把多个条件堆在一个笼统句里**,例如:`库存记录与提交的物料、批次或库位信息不一致`、`数据不合法`、`参数错误`、`记录不一致`。
- ç”¨æˆ·çœ‹åˆ°æç¤ºåŽåº”能**一眼知道是哪个条件不满足、差在哪、怎么改**,而不是只知道"出错了"。
## å¤šæ¡ä»¶æ ¡éªŒæç¤ºè¦æ±‚
提示必须同时包含三要素:
1. **具体维度**:指出是哪一个字段/规则不满足(如:物料、批次、批次号、仓库、库区、库位、数量)。
2. **双方实际值**:给出"库存/单据侧"与"提交值"两侧的可读值对比,并尽量解析成业务名称(如 `成品仓/2区/A-01`),**不要直接暴露纯数字 ID**;名称解析失败时才回退 `ID=xxx` å¹¶æ˜Žç¡®æ ‡æ³¨ã€‚
3. **可执行动作**:告诉用户接下来怎么办(重新选择哪一项、如何刷新重试、删除后重新添加、退回哪一步、联系谁)。
### å†™æ³•示例
```java
// âŒ ç¬¼ç»Ÿã€å¤šæ¡ä»¶å †å ã€æ— æ³•定位
throw exception(WM_MATERIAL_STOCK_SELECTION_MISMATCH);
// âŒ åªç»™æ¡ä»¶ä¸ç»™å€¼ã€ä¸ç»™åŠ¨ä½œã€æš´éœ²ç”Ÿç¡¬ ID
throw new ServiceException(WM_MATERIAL_STOCK_SELECTION_MISMATCH.getCode(),
    "不一致:库位(库存 8 â‰  æäº¤ null)");
// âœ… ç»´åº¦ + ä¸¤ä¾§å¯è¯»å€¼ + åŠ¨ä½œ
throw new ServiceException(WM_MATERIAL_STOCK_SELECTION_MISMATCH.getCode(),
    "所选库存记录与单据不一致:所选库存库位[A-01],单据库位为[未选择]。"
        + "请重新选择该物料正确的库存;若仓库/库区/库位/批次被禁用无法修改,请删除本条明细后重新添加。");
```
## å®žçŽ°è¦ç‚¹
- é€ç»´åº¦ç‹¬ç«‹æ¯”较(`itemId â†’ batchId â†’ batchCode â†’ warehouseId â†’ locationId â†’ areaId`),**按业务优先级只返回/抛出第一处不满足**,避免一次抛多条让用户抓不住重点。
- åŒä¸€é”™è¯¯ç ä½†ä¸šåŠ¡ä¸Šä¸‹æ–‡ä¸åŒï¼ˆå¦‚"执行扣减时库存已被移库/合并/拆分" vs "新增明细时带错字段" vs "退货来源库存物料不符"),**各自给出贴合场景的专属文案**,不要都复用同一句笼统话。
- åç§°è§£æžæ”¾åœ¨**异常分支**再执行(罕见路径),避免每次正常保存都多查表;解析失败必须回退为可读的 `ID=xxx` è€ŒéžæŠ›äºŒæ¬¡å¼‚常。
- å¦‚果某个字段本应由系统自动带出(如选库存后回填的仓库/库区/库位/批次),一旦不一致基本等于前端回填缺失或数据被改,提示里要写明"请重新选择以自动带出"。
## è‡ªæŸ¥æ¸…单
新增或修改任何可能由多个原因触发的提示前,逐项确认:
- [ ] æç¤ºæ˜¯å¦æŒ‡æ˜Žäº†"具体是哪个条件"?
- [ ] æ˜¯å¦åŒæ—¶ç»™å‡ºäº†"期望值 vs å®žé™…值"(尽量用业务名称而非 ID)?
- [ ] æ˜¯å¦ç»™å‡ºäº†"用户下一步能做的动作"?
- [ ] æ˜¯å¦é¿å…äº†æŠŠå¤šä¸ªæ¡ä»¶å †è¿›ä¸€å¥"XX不一致"?
.claude/rules/sql-config-export-init.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,56 @@
# æ•°æ®åº“初始化与配置导出维护规则
## è§„则
- å…¨åº“初始化只用**一份合并脚本**:`docs/sql/config_export_all_<日期>.sql`(建结构 + è¡¥é…ç½® + è¡¥èº«ä»½ä¸€æ¬¡æ€§å®Œæˆï¼‰ã€‚
- **任何业务/配置表结构、配置数据、系统身份/权限数据有变更**,都只重新生成这一份文件,保证其始终是最新可一键执行的版本。
- åªç»´æŠ¤åˆå¹¶æ–‡ä»¶ï¼›`config_export_<日期>.sql`、`test_identity_seed_<日期>.sql` åªæ˜¯ç”Ÿæˆå®ƒçš„中间体,不再单独提交维护。
- è¿åè§†ä¸ºé—漏:新库初始化出来缺表 / ç¼ºèœå• / ç™»ä¸ä¸Šï¼Œå±žäºŽæœªæŒ‰æœ¬è§„则同步。
## æ•°æ®åº“环境
| åº“ | è§’色 | è¯´æ˜Ž |
|------|------|------|
| `ruoyi-vue-pro` | **源库 / è¿è¡Œåº“ / æƒå¨** | dev/test/jhhg/yzfx ç­‰é…ç½®æŒ‡å‘它;运行后端(48080)读取;系统身份 / æƒé™ / èœå•的权威数据源。生成合并脚本时的数据来源。 |
| `test`(或其它空库) | **初始化目标** | æœ¬åœ° application-local ç­‰æŒ‡å‘的空白库;执行合并脚本在此建结构 + è¡¥é…ç½® + è¡¥èº«ä»½ã€‚ |
| å…¶ä»– | æ‹·è´ | ä¸Žæºåº“同结构同名拷贝,不参与本工作流。 |
> ç”Ÿæˆ/执行合并脚本需在**同时存在 `ruoyi-vue-pro` ä¸Žç›®æ ‡åº“的同一 MySQL å®žä¾‹**上执行(阶段二用跨库 `INSERT..SELECT`)。库名含 `-` æ—¶ SQL é‡Œè¦åŠ åå¼•å·ï¼ˆå¦‚ `` `ruoyi-vue-pro` ``)。
## å”¯ä¸€ç»´æŠ¤æ–‡ä»¶
**`docs/sql/config_export_all_20260904.sql`**(11249 è¡Œï¼Œåˆå¹¶ç‰ˆï¼‰ï¼Œå†…部两个阶段:
| é˜¶æ®µ | å†…容 | æ•°æ®æ¥æº |
|------|------|----------|
| é˜¶æ®µä¸€ï¼ˆåŽŸ config_export) | å…¨åº“ 413 å¼ è¡¨ `CREATE TABLE` + 6 ç±»é…ç½®æ•°æ®ï¼šâ‘ ååŒå®¡æ‰¹æµç¨‹ï¼ˆact_*/bpm æ¿€æ´»æµç¨‹å…ƒæ•°æ®+定义)②协同业务配置(bpm é™„ä»¶/分类/表单/用户组/监听/表达式)③BI å¤§å±/go-view â‘£MES ç¼–码规则 â‘¤ç³»ç»Ÿé…ç½®ä¸»æ•°æ®ï¼ˆsystem_dict_type/data、tenant、company_config)⑥AI/基础设施配置。**刻意排除身份(users)/菜单/日志/流水**。 | ruoyi-vue-pro å­—面量导出(自包含) |
| é˜¶æ®µäºŒï¼ˆåŽŸ test_identity_seed) | è¡¥èº«ä»½ + èœå•最小可登录数据:①`system_menu` å…¨éƒ¨æœ‰æ•ˆèœå• â‘¡`system_dept`/`post`(仅 deleted=0)③`system_role` è¶…管 super_admin â‘£`system_users` admin â‘¤`system_user_role` â‘¥`system_oauth2_client`(client_id='default')。幂等:先 `DELETE` å† `INSERT`。 | ruoyi-vue-pro è·¨åº“ `INSERT..SELECT` |
日常初始化:只需在目标空库执行 `config_export_all_20260904.sql` è¿™ä¸€ä»½ã€‚
## å„变更归属阶段(先判断"该放哪段")
| æ•°æ®ç±»åˆ« | æ”¾è¿›åˆå¹¶æ–‡ä»¶çš„哪个阶段 |
|----------|-----------|
| æ–°ä¸šåŠ¡è¡¨ç»“æž„ï¼ˆå…¨åº“ä»»æ„ CREATE TABLE) | é˜¶æ®µä¸€ï¼ˆç»“构) |
| æ–°å¢ž/改动的配置类数据(字典、BPM è¡¨å•/流程、编码规则、BI、AI、公司配置等) | é˜¶æ®µä¸€ï¼ˆæ•°æ®ï¼‰ |
| èœå• / éƒ¨é—¨ / å²—位 / è§’色 / ç”¨æˆ· / ç”¨æˆ·è§’色 / OAuth2 å®¢æˆ·ç«¯ | **阶段二**(不进阶段一) |
| ä¸šåŠ¡ä¸»æ•°æ® / å•据 / æµæ°´ï¼ˆç‰©æ–™ã€å·¥å•、检验单等运行数据) | éƒ½ä¸è¿›ï¼ˆä»…建空结构);业务数据由各自迁移脚本负责 |
## ç»´æŠ¤æ¸…单(有变更时按序执行)
1. åˆ¤æ–­å˜æ›´å±žäºŽ"表结构 / é…ç½®æ•°æ®"还是"身份 / æƒé™",确定它应落在阶段一还是阶段二。
2. **重新生成合并文件** `config_export_all_<日期>.sql`(用当天日期命名新版本,旧版保留备查):
   - é˜¶æ®µä¸€ï¼šä»Ž `ruoyi-vue-pro` å¯¼å‡ºå…¨åº“结构 + 6 ç±»é…ç½®æ•°æ®ã€‚
   - é˜¶æ®µäºŒï¼šæ›´æ–°èº«ä»½/菜单/OAuth2 è¡¥å¯¼æ®µï¼ˆå¦‚加新菜单、新默认角色)。
   - å°†ä¸¤æ®µå†…容按「阶段一 â†’ åˆ†é𔿳¨é‡Šé˜¶æ®µäºŒ â†’ é˜¶æ®µäºŒã€æ‹¼æŽ¥æˆä¸€ä»½å®Œæ•´æ–‡ä»¶ã€‚
3. å…¨åº“结构变化后,核对 Mapper/DO æ˜¯å¦ä¸Žè¡¨ç»“构一致(`mvn compile -q` ä¸å—表结构影响,但需人工核对)。
## å…³é”®çº¦æŸä¸Žå‘(避免踩)
- âš  é˜¶æ®µä¸€å«**全库 `DROP TABLE IF EXISTS`**,会清空非其建表的所有表数据,**只能在全新/可覆盖空库执行**,禁止在运行库直接跑。
- âš  è¶…管 `super_admin` ç™»å½•后由代码**直接返回全部菜单**,故**不需要 `system_role_menu` è¡Œ**;但 `system_menu` è¡¨å¿…须有数据。若未来要支持普通角色登录,则需补该角色 â†’ `system_role_menu` â†’ `system_user_role` å…¨é“¾è·¯ã€‚
- âš  ç™»å½•/刷新令牌**必须**有 `system_oauth2_client` ä¸­ `client_id='default'` çš„记录,否则 `validOAuthClientFromCache` æŠ¥é”™ç™»ä¸ä¸Šã€‚
- å­—å…¸ `system_dict_type`/`system_dict_data` ç”±é˜¶æ®µä¸€å¯¼å…¥ï¼Œé˜¶æ®µäºŒä¸é‡å¤å¤„理。
- æºåº“ `system_dept`/`system_post` å«è¾ƒå¤šè½¯åˆ é™¤è¡Œï¼Œé˜¶æ®µäºŒä»…拷贝 `deleted=0` çš„æœ‰æ•ˆè¡Œã€‚
- é˜¶æ®µä¸€è‡ªåŒ…含(字面量 INSERT),不依赖源库在线;阶段二依赖源库在线做跨库拷贝。
.gitignore
@@ -75,3 +75,4 @@
.agents/
# æ•°æ®åº“变更脚本(手动执行,不入库)
sql/sale_order_bpm_approval.sql
.export_tmp/
docs/qc_export_integration.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,71 @@
# è´¨æ£€å¯¼å‡ºåŠŸèƒ½å¢žå¼º - å‰ç«¯è”调方案
## æ¶‰åŠé¡µé¢
- æ¥æ–™æ£€éªŒå•(IQC)列表页
- è¿‡ç¨‹æ£€éªŒå•(IPQC)列表页
- å‡ºè´§æ£€éªŒå•(OQC)列表页
- é€€è´§æ£€éªŒå•(RQC)列表页
以上四个页面的「导出」功能均需调整。
## å˜æ›´æ¦‚览
1. **导出 Excel æ–°å¢žã€Œæ£€éªŒé¡¹æŒ‡æ ‡ã€åˆ—**:展示该检验单所有检验项的结果汇总。
2. **导出支持按检测时间范围筛选**:用户选择时间范围后,只导出检测日期落在范围内的检验单。
## ä¸šåŠ¡æµç¨‹ä¸Žæ•°æ®å¸¦å…¥
1. ç”¨æˆ·åœ¨åˆ—表页设置筛选条件(含新增的「检测时间范围」)→ ç‚¹å‡»ã€Œå¯¼å‡ºã€ã€‚
2. å¯¼å‡ºæŽ¥å£ä»¥å½“前筛选条件(与分页查询相同的筛选参数)拉取全部数据,生成 Excel ä¸‹è½½ã€‚
3. ä¸é€‰æ‹©æ—¶é—´èŒƒå›´æ—¶ï¼Œå¯¼å‡ºè¡Œä¸ºä¸Žæ”¹åŠ¨å‰ä¸€è‡´ï¼ˆå¯¼å‡ºå…¨éƒ¨ç¬¦åˆå…¶å®ƒç­›é€‰æ¡ä»¶çš„æ•°æ®ï¼‰ã€‚
## API
| æ–¹æ³• | è·¯å¾„ | è¯´æ˜Ž |
|------|------|------|
| GET | /mes/qc/iqc/export-excel | å¯¼å‡ºæ¥æ–™æ£€éªŒå• |
| GET | /mes/qc/ipqc/export-excel | å¯¼å‡ºè¿‡ç¨‹æ£€éªŒå• |
| GET | /mes/qc/oqc/export-excel | å¯¼å‡ºå‡ºè´§æ£€éªŒå• |
| GET | /mes/qc/rqc/export-excel | å¯¼å‡ºé€€è´§æ£€éªŒå• |
**新增请求参数(四个接口一致):**
| å‚æ•° | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
|------|------|------|------|
| inspectDate[0] | String | å¦ | æ£€æµ‹å¼€å§‹æ—¶é—´ï¼Œæ ¼å¼ yyyy-MM-dd HH:mm:ss |
| inspectDate[1] | String | å¦ | æ£€æµ‹ç»“束时间,格式 yyyy-MM-dd HH:mm:ss |
说明:此参数为筛选条件,与分页查询 `GET /mes/qc/xxx/page` çš„传参方式完全一致,前端分页与导出可复用同一套查询参数对象。
**导出 Excel æ–°å¢žåˆ—:**
| åˆ—名 | ç±»åž‹ | è¯´æ˜Ž |
|------|------|------|
| æ£€éªŒé¡¹æŒ‡æ ‡ | String | æ ¼å¼ï¼šæŒ‡æ ‡åç§°:检测值 æˆ– æŒ‡æ ‡åç§°:检测值(备注),多个指标用逗号分隔,例如 `外观:良好(OK), å°ºå¯¸:100.00mm(OK)`;无检验结果时为空 |
## å­—段展示规则
| å­—段 | å±•示位置 | è¯´æ˜Ž |
|------|----------|------|
| æ£€éªŒé¡¹æŒ‡æ ‡ | Excel「检验项指标」列 | ç”±ç³»ç»Ÿè‡ªåŠ¨æ‹¼æŽ¥ï¼Œå‰ç«¯æ— éœ€å¤„ç†æ ¼å¼ |
| æ£€æµ‹æ—¶é—´èŒƒå›´ | åˆ—表页筛选区 | æ–°å¢žæ—¶é—´èŒƒå›´é€‰æ‹©æŽ§ä»¶ï¼Œå»ºè®®ä¸ŽçŽ°æœ‰ã€Œæ£€æµ‹æ—¥æœŸã€åˆ—å¯¹åº” |
## ä¸šåŠ¡è§„åˆ™è¯´æ˜Ž
| åœºæ™¯ | è§„则 |
|------|------|
| æœªé€‰æ‹©æ—¶é—´èŒƒå›´ | å¯¼å‡ºå…¨éƒ¨æ•°æ®ï¼ˆä¸Žæ”¹åŠ¨å‰ä¸€è‡´ï¼‰ |
| ä»…选择开始时间 | å¯¼å‡ºæ£€æµ‹æ—¥æœŸå¤§äºŽç­‰äºŽå¼€å§‹æ—¶é—´çš„记录 |
| ä»…选择结束时间 | å¯¼å‡ºæ£€æµ‹æ—¥æœŸå°äºŽç­‰äºŽç»“束时间的记录 |
| é€‰æ‹©å®Œæ•´èŒƒå›´ | å¯¼å‡ºæ£€æµ‹æ—¥æœŸåœ¨é—­åŒºé—´ [开始, ç»“束] å†…的记录 |
| æ— æ£€éªŒç»“æžœ | ã€Œæ£€éªŒé¡¹æŒ‡æ ‡ã€åˆ—导出为空 |
| è¿‡æ»¤å­—段 | æ—¶é—´èŒƒå›´é’ˆå¯¹å„检验单的「检测日期」字段过滤 |
| å¯¼å‡ºæƒé™ | ä»å—各页面原有 `export` æƒé™æŽ§åˆ¶ï¼ŒOQC å¯¼å‡ºä¿ç•™å®¢æˆ·æ•°æ®æƒé™è¿‡æ»¤ |
## æ³¨æ„äº‹é¡¹
- åˆ†é¡µå’Œå¯¼å‡ºå…±ç”¨åŒä¸€å¥—筛选参数,前端导出时应把当前筛选条件(含时间范围)一并作为请求参数传递。
- æ—¶é—´æŽ§ä»¶æ ¼å¼å¿…须为 `yyyy-MM-dd HH:mm:ss`,否则后端无法解析。
- å››ä¸ªé¡µé¢å„自调用对应的导出接口,参数名完全相同,可复用同一导出函数。
- è‹¥åŽç»­éœ€è¦æŒ‰ã€Œåˆ›å»ºæ—¶é—´ã€ç­‰å…¶å®ƒæ—¶é—´ç»´åº¦ç­›é€‰ï¼Œå¯å¦è¡Œæ‰©å±•,本次仅支持检测时间。
docs/qc_template_option_selection.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,100 @@
# è´¨æ£€æ£€éªŒå•手动选择质检方案 - å‰ç«¯è”调方案
## æ¶‰åŠé¡µé¢
- æ¥æ–™æ£€éªŒï¼ˆIQC)新建表单
- è¿‡ç¨‹æ£€éªŒï¼ˆIPQC)新建表单
- å‡ºè´§æ£€éªŒï¼ˆOQC)新建表单
- é€€è´§æ£€éªŒï¼ˆRQC)新建表单
## èƒŒæ™¯
一个产品可在 `mes_qc_template_item` å…³è”多个质检方案(`mes_qc_template`)。此前后端命中多个方案时静默取第一个,用户无法控制。现支持在检验单**新建表单**中通过「质检方案」下拉手动选择;未配置或仅一个方案时保持原有自动行为不变。
## API
| æ–¹æ³• | è·¯å¾„ | è¯´æ˜Ž |
|------|------|------|
| GET | /mes/qc/template/item/list-by-item-and-type | æ ¹æ®ç‰©æ–™æˆ–工单/任务和检测种类获取可用质检方案列表 |
**请求参数:**
| å‚æ•° | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
|------|------|------|------|
| qcType | Integer | æ˜¯ | æ£€æµ‹ç§ç±»ï¼š1=IQC、2=IPQC、3=OQC、4=RQC |
| itemId | Long | å¦ | äº§å“ç‰©æ–™ ID(mes_md_item.id);IQC/OQC/RQC ä½¿ç”¨ |
| workOrderId | Long | å¦ | ç”Ÿäº§è®¢å• ID;IPQC ä½¿ç”¨ |
| taskId | Long | å¦ | ç”Ÿäº§ä»»åŠ¡ ID;IPQC ä½¿ç”¨ |
> è¯´æ˜Žï¼š`itemId`、`workOrderId`、`taskId` ä¸‰è€…至少传一个。优先顺序:taskId â†’ workOrderId â†’ itemId。IPQC è¡¨å•不选物料,传 workOrderId(+可选 taskId)由后端解析产品;其余三个模块传 itemId。
**响应字段(数组):**
| å­—段 | ç±»åž‹ | è¯´æ˜Ž |
|------|------|------|
| id | Long | æ–¹æ¡ˆ-产品关联编号(templateItem.id) |
| templateId | Long | è´¨æ£€æ–¹æ¡ˆ ID |
| templateCode | String | è´¨æ£€æ–¹æ¡ˆç¼–号 |
| templateName | String | è´¨æ£€æ–¹æ¡ˆåç§° |
| quantityCheck | Integer | æœ€ä½Žæ£€æµ‹æ•° |
| quantityUnqualified | Integer | æœ€å¤§ä¸åˆæ ¼æ•° |
| criticalRate | BigDecimal | æœ€å¤§è‡´å‘½ç¼ºé™·çŽ‡ï¼ˆ%) |
| majorRate | BigDecimal | æœ€å¤§ä¸¥é‡ç¼ºé™·çŽ‡ï¼ˆ%) |
| minorRate | BigDecimal | æœ€å¤§è½»å¾®ç¼ºé™·çŽ‡ï¼ˆ%) |
**响应示例:**
```json
{
  "code": 0,
  "data": [
    {
      "id": 5,
      "templateId": 10,
      "templateCode": "IQC-AQL-Ⅱ",
      "templateName": "IQC æ­£å¸¸æ£€éªŒäºŒçº§æ°´å¹³",
      "quantityCheck": 8,
      "quantityUnqualified": 1,
      "criticalRate": 0,
      "majorRate": 2.5,
      "minorRate": 10
    }
  ]
}
```
## å‰ç«¯è¡¨çް
| æ¨¡å— | è§¦å‘字段 | qcType | è¯´æ˜Ž |
|------|----------|--------|------|
| IQC | itemId | 1 | é€‰ä¸­ç‰©æ–™åŽæŒ‰ qcType=1 æ‹‰å– |
| OQC | itemId | 3 | é€‰ä¸­ç‰©æ–™åŽæŒ‰ qcType=3 æ‹‰å– |
| RQC | itemId | 4 | é€‰ä¸­ç‰©æ–™åŽæŒ‰ qcType=4 æ‹‰å–(后端已修复为按检测种类匹配模板,不再误匹配到 IQC/IPQC æ–¹æ¡ˆï¼‰ |
| IPQC | workOrderId、taskId | 2 | é€‰ä¸­ç”Ÿäº§è®¢å•后按 qcType=2 æ‹‰å–,taskId å¯é€‰ä¼ å…¥ |
## å­—段展示规则
「质检方案」字段(templateId):
| åœºæ™¯ | è§„则 |
|------|------|
| æ–°å»ºã€æœªé€‰ç‰©æ–™/生产订单 | ä¸‹æ‹‰ç¦ç”¨ï¼Œæç¤º"请先选择产品物料/生产订单" |
| æ–°å»ºã€å·²é€‰ç‰©æ–™/生产订单 | ä¸‹æ‹‰å¯ç”¨ï¼Œé»˜è®¤é€‰ä¸­è¿”回列表第一个方案,可切换 |
| ç¼–辑/详情 | ä¸‹æ‹‰ç¦ç”¨ï¼Œä»…回显已保存的方案 |
| äº§å“æœªé…ç½®å¯ç”¨æ–¹æ¡ˆ | ä¸‹æ‹‰ä¸ºç©ºï¼ˆä¿å­˜æ—¶åŽç«¯ä»æ ¡éªŒæŠ¥ã€Œæœªé…ç½®è´¨æ£€æ–¹æ¡ˆã€ï¼‰ |
| äº§å“é…ç½®å¤šä¸ª enable æ–¹æ¡ˆ | ä¸‹æ‹‰å±•示全部可选方案,用户可切换 |
## ä¸šåŠ¡è§„åˆ™è¯´æ˜Ž
| åœºæ™¯ | è§„则 |
|------|------|
| å‰ç«¯æ˜¾å¼ä¼ äº† templateId | åŽç«¯åœ¨æ ¡éªŒæ–¹æ¡ˆå¿…须属于"产品+检测种类"可匹配列表,否则拒绝(防止选到其他产品/其他检测种类的方案) |
| å‰ç«¯æœªä¼  templateId | è‡ªåŠ¨å–åŒ¹é…åˆ—è¡¨ç¬¬ä¸€ä¸ªæ–¹æ¡ˆï¼Œä¸Žæ—§è¡Œä¸ºä¸€è‡´ |
| ç¼–辑检验单 | ä¸å…è®¸ä¿®æ”¹æ–¹æ¡ˆï¼ˆåŽç«¯ update æ—¶å¿½ç•¥ templateId) |
| è‡ªåŠ¨è§¦å‘è·¯å¾„ï¼ˆåˆ°è´§é€šçŸ¥â†’IQC、销售出库→OQC) | ä¸èµ°ä¸‹æ‹‰ï¼Œä»ç”±åŽç«¯è‡ªåŠ¨å–æ–¹æ¡ˆï¼Œè¡Œä¸ºä¸å˜ |
## æ³¨æ„äº‹é¡¹
- åˆ—表、详情回显字段 templateId åœ¨çŽ°æœ‰ RespVO ä¸­å·²å­˜åœ¨ï¼Œæ— éœ€é¢å¤–对接。
- ä¿å­˜è¯·æ±‚参数新增 `templateId`(质检方案 ID),四个模块 SaveReqVO å‡å·²è¡¥å……ï¼›RQC åŽŸæœ¬å°±å¸¦æ­¤å­—æ®µã€‚
- ä¸‹æ‹‰é»˜è®¤é€‰ä¸­ç¬¬ä¸€ä¸ªä¾èµ–组件 `autoSelect: 'first'`,仅当当前值为空时生效,用户手动选择后不会被动覆盖。
docs/sql/bpm_audit_flow_01_repair_and_category.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,110 @@
-- =============================================================================
-- æ–‡ä»¶ï¼šbpm_audit_flow_01_repair_and_category.sql
-- ç”¨é€”:BPM å®¡æ ¸æµç¨‹ã€Œä¿®å¤ + åˆ†ç±»è¡¥é½ã€æ®µï¼ˆç¬¬ 1 ä¸ªæ‰§è¡Œï¼‰
-- ç›®æ ‡åº“:ruoyi-vue-pro(后端 48080 å®žé™…读取的运行库)
-- è¦æ±‚:MySQL 8.0+;建议先备份:mysqldump ruoyi-vue-pro act_re_procdef act_re_model
--       act_re_deployment act_ge_bytearray bpm_category bpm_process_definition_info > bpm_bak.sql
-- ç‰¹æ€§ï¼šæœ¬æ–‡ä»¶å†…所有语句均幂等(可重复执行);判断"已存在/数据不完整"后只做必要修复,
--       ä¸åˆ é™¤ä»»ä½•既有数据,只做状态修正。
-- è¯´æ˜Žï¼š
--   1) ä¿®å¤æ®µåªåšå®‰å…¨ä¿®å¤ï¼Œä¸è‡ªåŠ¨åˆ é™¤"测试/垃圾流程"(涉及历史数据判断,需人工)。
--      å¯¹æ˜Žæ˜¾åžƒåœ¾æµç¨‹ç»Ÿä¸€"挂起",避免出现在前端发起列表,且不影响运行中的流程实例。
--   2) åˆ†ç±»è¡¥é½ï¼šåªä¸ºä»£ç ä¸­å·²å¼•用、但库里缺失的分类 code è¡¥æ’(已存在则跳过)。
-- =============================================================================
SET NAMES utf8mb4;
-- -----------------------------------------------------------------------------
-- â‘  æ•°æ®å®Œæ•´æ€§ä¿®å¤
-- -----------------------------------------------------------------------------
-- 1.1 ACT_RE_PROCDEF.ENGINE_VERSION_ ç©ºä¸²ä¼šå¯¼è‡´ Flowable æäº¤å®¡æ‰¹æŠ¥é”™
--     ï¼ˆInvalid 'engine' for process definition ...),需修正为 NULL。
UPDATE ACT_RE_PROCDEF
   SET ENGINE_VERSION_ = NULL
 WHERE ENGINE_VERSION_ = '';
-- 1.2 ACT_RE_MODEL.META_INFO_ ä¸ºç©ºä¼šå¯¼è‡´æµç¨‹æ¨¡åž‹åˆ—表/页面反序列化异常。
--     è¡¥é½ä¸€ä¸ªæœ€å°çš„合法 JSON(type=10 BPMN æ¨¡å¼ï¼Œå« sort),保证页面可打开。
--     åªè¡¥ç©ºå€¼è¡Œï¼Œä¸å½±å“å·²æœ‰å®Œæ•´é…ç½®ã€‚
UPDATE ACT_RE_MODEL
   SET META_INFO_ = CONCAT('{"type":10,"sort":', UNIX_TIMESTAMP(NOW(3)) * 1000, ',"formType":10,"visible":true}')
 WHERE META_INFO_ IS NULL OR META_INFO_ = '';
-- 1.3 bpm_process_definition_info.model_type éž 10 çš„ BPMN æµç¨‹ï¼Œåˆ›å»ºæµç¨‹å®žä¾‹ä¼š NPE。
--     ä»…当该流程确为 BPMN(在 ACT_RE_PROCDEF å­˜åœ¨ï¼‰ä¸”非 SIMPLE(simple_model ä¸ºç©ºï¼‰æ—¶ä¿®æ­£ä¸º 10。
UPDATE bpm_process_definition_info i
  JOIN ACT_RE_PROCDEF p ON p.ID_ = i.process_definition_id
   SET i.model_type = 10
 WHERE i.simple_model IS NULL
   AND i.model_type <> 10;
-- 1.4 ä¸šåŠ¡è§„èŒƒæµç¨‹çš„å¤šç‰ˆæœ¬æ²»ç†ï¼šåŒ key ä¿ç•™æœ€æ–°ç‰ˆæœ¬æ¿€æ´»ï¼ˆSUSPENSION_STATE_=1),
--     åŽ†å²ç‰ˆæœ¬ä¸€å¾‹æŒ‚èµ·ï¼ˆ=2)。只作用于"在用"白名单 key,其余不动。
UPDATE ACT_RE_PROCDEF p
  JOIN (SELECT KEY_, MAX(VERSION_) AS max_ver
          FROM ACT_RE_PROCDEF
         WHERE KEY_ IN ('erp-finance-payment-audit','crm-invoice-audit',
                        'crm-receivable-audit','mes-pd-project-audit',
                        'purchase_request_approve')
         GROUP BY KEY_) t ON t.KEY_ = p.KEY_
   SET p.SUSPENSION_STATE_ = IF(p.VERSION_ = t.max_ver, 1, 2);
-- 1.5 æ˜Žæ˜¾ä¸º"测试/已废弃"的流程统一挂起(=2),避免出现在发起/待办选择列表。
--     ä»…改状态不删除,可随时人工在界面或 SQL æ¸…理;对运行中实例无影响。
UPDATE ACT_RE_PROCDEF
   SET SUSPENSION_STATE_ = 2
 WHERE KEY_ IN ('Ada','QWEDD','SSQ','_..123abc','b000_abc.','yz001','yz002',
                'caigoushenpi','pus','purchase_approve','contract_approve');
-- 1.6 ï¼ˆå¯é€‰ï¼‰æŸ¥çœ‹ä»å¤„于激活状态的流程,人工确认是否残留垃圾流程:
-- SELECT KEY_, NAME_, VERSION_, SUSPENSION_STATE_ FROM ACT_RE_PROCDEF
--  WHERE SUSPENSION_STATE_ = 1 ORDER BY KEY_;
-- -----------------------------------------------------------------------------
-- â‘¡ æµç¨‹åˆ†ç±»ï¼ˆbpm_category)补齐
--    å¯¹åº”各业务单据发起审批时按 code æ‹‰å–可发起流程列表;代码中已引用但库中缺失则补。
--    æ¯æ¡å‡åš"已存在则跳过"判断(code + æœªåˆ é™¤ï¼‰ã€‚
-- -----------------------------------------------------------------------------
-- 2.1 ERP é‡‡è´­è®¢å•审批
INSERT INTO bpm_category (name, code, description, status, sort, creator, create_time, updater, update_time, deleted)
SELECT '采购订单审批', 'purchase_order_approve', 'ERP é‡‡è´­è®¢å•审批', 0, 10, '1', NOW(), '1', NOW(), 0
 WHERE NOT EXISTS (SELECT 1 FROM bpm_category WHERE code = 'purchase_order_approve' AND deleted = 0);
-- 2.2 ERP é”€å”®è®¢å•审批
INSERT INTO bpm_category (name, code, description, status, sort, creator, create_time, updater, update_time, deleted)
SELECT '销售订单审批', 'sale_order_approve', 'ERP é”€å”®è®¢å•审批', 0, 20, '1', NOW(), '1', NOW(), 0
 WHERE NOT EXISTS (SELECT 1 FROM bpm_category WHERE code = 'sale_order_approve' AND deleted = 0);
-- 2.3 HRM è¯·å‡å®¡æ‰¹
INSERT INTO bpm_category (name, code, description, status, sort, creator, create_time, updater, update_time, deleted)
SELECT '请假审批', 'hrm_leave_approve', 'HRM è¯·å‡ç”³è¯·å®¡æ‰¹', 0, 30, '1', NOW(), '1', NOW(), 0
 WHERE NOT EXISTS (SELECT 1 FROM bpm_category WHERE code = 'hrm_leave_approve' AND deleted = 0);
-- 2.4 HRM åŠ ç­å®¡æ‰¹
INSERT INTO bpm_category (name, code, description, status, sort, creator, create_time, updater, update_time, deleted)
SELECT '加班审批', 'overtime_approve', 'HRM åŠ ç­ç”³è¯·å®¡æ‰¹', 0, 40, '1', NOW(), '1', NOW(), 0
 WHERE NOT EXISTS (SELECT 1 FROM bpm_category WHERE code = 'overtime_approve' AND deleted = 0);
-- 2.5 HRM ç¦»èŒå®¡æ‰¹
INSERT INTO bpm_category (name, code, description, status, sort, creator, create_time, updater, update_time, deleted)
SELECT '离职审批', 'hrm_resignation_approve', 'HRM ç¦»èŒç”³è¯·å®¡æ‰¹', 0, 50, '1', NOW(), '1', NOW(), 0
 WHERE NOT EXISTS (SELECT 1 FROM bpm_category WHERE code = 'hrm_resignation_approve' AND deleted = 0);
-- 2.6 HRM è°ƒåŠ¨å®¡æ‰¹
INSERT INTO bpm_category (name, code, description, status, sort, creator, create_time, updater, update_time, deleted)
SELECT '调动审批', 'hrm_transfer_approve', 'HRM è°ƒåŠ¨ç”³è¯·å®¡æ‰¹', 0, 60, '1', NOW(), '1', NOW(), 0
 WHERE NOT EXISTS (SELECT 1 FROM bpm_category WHERE code = 'hrm_transfer_approve' AND deleted = 0);
-- 2.7 MES æŠ¥å·¥/生产工单审批(代码侧 MES æŠ¥å·¥ä¸Žå·¥å•共用分类 mes_pro_feedback_audit)
INSERT INTO bpm_category (name, code, description, status, sort, creator, create_time, updater, update_time, deleted)
SELECT '报工审批', 'mes_pro_feedback_audit', 'MES ç”Ÿäº§æŠ¥å·¥/工单审批', 0, 70, '1', NOW(), '1', NOW(), 0
 WHERE NOT EXISTS (SELECT 1 FROM bpm_category WHERE code = 'mes_pro_feedback_audit' AND deleted = 0);
-- -----------------------------------------------------------------------------
-- â‘¢ ä¿®å¤ç»“果自查(应全部返回 0)
-- -----------------------------------------------------------------------------
-- SELECT '空串ENGINE_VERSION_残留' AS chk, COUNT(*) FROM ACT_RE_PROCDEF WHERE ENGINE_VERSION_ = ''
-- UNION ALL SELECT '空META_INFO_残留' , COUNT(*) FROM ACT_RE_MODEL WHERE META_INFO_ IS NULL OR META_INFO_ = ''
-- UNION ALL SELECT '分类待补' , 0;
docs/sql/bpm_audit_flow_02_deploy_proc.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,202 @@
-- =============================================================================
-- æ–‡ä»¶ï¼šbpm_audit_flow_02_deploy_proc.sql
-- ç”¨é€”:BPM å®¡æ ¸æµç¨‹ã€Œæµç¨‹å®šä¹‰éƒ¨ç½²ã€æ®µï¼ˆç¬¬ 2 ä¸ªæ‰§è¡Œï¼Œéœ€å…ˆæ‰§è¡Œ 01 ä¿®å¤/分类文件)
-- ç›®æ ‡åº“:ruoyi-vue-pro
-- è¦æ±‚:
--   * MySQL 8.0+,账号需具备 CREATE PROCEDURE æƒé™
--   * ä»¥ mysql CLI æ‰§è¡Œï¼ˆå« DELIMITER):
--        mysql -u<账号> -p ruoyi-vue-pro < bpm_audit_flow_02_deploy_proc.sql
--   * æ¨¡æ¿ï¼šé»˜è®¤ä»Ž ruoyi-vue-pro å†…已有规范流程「erp-finance-payment-audit」复制,
--           å…¶ BPMN ä¸ºâ€œå‘起人节点 + æäº¤å³é€šè¿‡â€(candidateStrategy=36) çš„æœ€ç®€ç»“构,
--           é€‚合所有对接单据先跑通;后续如需多级审批可在「流程模型」界面调整。
-- å¥å£®æ€§è®¾è®¡ï¼š
--   * å¹‚等:目标 key è‹¥å·²å­˜åœ¨ï¼ˆæ— è®ºæ¿€æ´»/挂起)→ æ•´æ®µè·³è¿‡ï¼Œä¸é‡å¤éƒ¨ç½²ã€ä¸äº§ç”Ÿæ–°ç‰ˆæœ¬ã€‚
--   * äº‹åŠ¡ï¼šå•æµç¨‹ 5 å¼ è¡¨å†™å…¥åŒ…在一个事务内,任一步失败自动回滚,不留半成品。
--   * è‡ªæ£€ï¼šprocdef ID = key:1:<32位uuid>,超 64 å­—符直接报错中断。
--   * ç©ºå€¼è§„避:ENGINE_VERSION_ ä¸å†™å…¥ç©ºä¸²(保持 NULL),避免 Flowable æäº¤å®¡æ‰¹æŠ¥é”™ï¼›
--              META_INFO_ å†™å…¥æœ€å°åˆæ³• JSON(含 sort/type=10),避免模型页/列表 NPE。
--   * é‡å¤æ‰§è¡Œæ–‡ä»¶å®‰å…¨ï¼šDROP PROCEDURE IF EXISTS ä¿è¯å¯é‡å¤è¿è¡Œã€‚
-- =============================================================================
SET NAMES utf8mb4;
-- éƒ¨ç½²å‰è‡ªæ£€ï¼šæ¨¡æ¿å­˜åœ¨æ€§ï¼ˆåº”返回 1 è¡Œï¼‰
SELECT KEY_, NAME_, SUSPENSION_STATE_ FROM ACT_RE_PROCDEF WHERE KEY_ = 'erp-finance-payment-audit';
DROP PROCEDURE IF EXISTS bpm_deploy_flow_from_template;
DELIMITER $$
-- ä»Žä¸€ä¸ªâ€œæ¨¡æ¿æµç¨‹â€å¤åˆ¶éƒ¨ç½²ä¸€ä¸ªæ–°å®¡æ ¸æµç¨‹ï¼ˆBPMN ç»“构一致,仅替换 key/名称)
CREATE PROCEDURE bpm_deploy_flow_from_template(
    IN p_key        VARCHAR(64),   -- æ–°æµç¨‹ key(需与代码侧监听器匹配)
    IN p_name       VARCHAR(128),  -- æ–°æµç¨‹æ˜¾ç¤ºå
    IN p_category   VARCHAR(64),   -- å½’属流程分类 code
    IN p_template   VARCHAR(64)    -- å¤åˆ¶æ¥æºæ¨¡æ¿æµç¨‹ key
)
label_deploy: BEGIN
    DECLARE v_exist      INT DEFAULT 0;
    DECLARE v_tpl_key    VARCHAR(64) DEFAULT 'erp-finance-payment-audit';
    DECLARE v_tpl_dep    VARCHAR(64) DEFAULT NULL;
    DECLARE v_tpl_name   VARCHAR(255) DEFAULT NULL;
    DECLARE v_dep        VARCHAR(64);
    DECLARE v_model_id   VARCHAR(64);
    DECLARE v_byte_id    VARCHAR(64);
    DECLARE v_procdef_id VARCHAR(64);
    DECLARE v_meta       TEXT;
    DECLARE v_sort       BIGINT;
    DECLARE v_now        DATETIME;
    -- ä»»ä½•异常 â†’ å›žæ»šå¹¶é‡æ–°æŠ›å‡º
    DECLARE EXIT HANDLER FOR SQLEXCEPTION
    BEGIN
        ROLLBACK;
        RESIGNAL;
    END;
    -- å¹‚等:key å·²å­˜åœ¨ç›´æŽ¥è·³è¿‡
    SELECT COUNT(*) INTO v_exist FROM ACT_RE_PROCDEF WHERE KEY_ = p_key;
    IF v_exist > 0 THEN
        SELECT CONCAT('[SKIP] æµç¨‹å·²å­˜åœ¨ï¼Œè·³è¿‡ï¼š', p_key) AS deploy_result;
        LEAVE label_deploy;
    END IF;
    -- æ¨¡æ¿å®šä½
    IF p_template IS NOT NULL AND p_template <> '' THEN
        SET v_tpl_key = p_template;
    END IF;
    SELECT DEPLOYMENT_ID_ INTO v_tpl_dep FROM ACT_RE_PROCDEF
     WHERE KEY_ = v_tpl_key ORDER BY VERSION_ DESC LIMIT 1;
    SELECT NAME_        INTO v_tpl_name FROM ACT_RE_PROCDEF
     WHERE KEY_ = v_tpl_key ORDER BY VERSION_ DESC LIMIT 1;
    IF v_tpl_dep IS NULL THEN
        SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = CONCAT('模板流程不存在,无法复制:', v_tpl_key);
    END IF;
    SET v_now     = NOW(3);
    SET v_sort    = UNIX_TIMESTAMP(v_now) * 1000;
    SET v_dep     = REPLACE(UUID(), '-', '');
    SET v_model_id = REPLACE(UUID(), '-', '');
    SET v_byte_id  = REPLACE(UUID(), '-', '');
    SET v_procdef_id = CONCAT(p_key, ':1:', v_dep);
    -- procdef ID åˆ— VARCHAR(64) é•¿åº¦å®ˆå«
    IF CHAR_LENGTH(v_procdef_id) > 64 THEN
        SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = CONCAT('流程 key è¿‡é•¿ï¼Œprocdef ID å°†è¶… 64 å­—符:', p_key);
    END IF;
    START TRANSACTION;
    -- 1) éƒ¨ç½²è®°å½•(ACT_RE_DEPLOYMENT æ—  REV_ åˆ—)
    INSERT INTO ACT_RE_DEPLOYMENT
        (ID_, NAME_, CATEGORY_, KEY_, TENANT_ID_, DEPLOY_TIME_, ENGINE_VERSION_)
    VALUES
        (v_dep, p_name, p_category, p_key, '', v_now, NULL);
    -- 2) BPMN å­—节(复制模板字节并替换 key ä¸Žæ˜¾ç¤ºåï¼‰
    INSERT INTO ACT_GE_BYTEARRAY
        (ID_, NAME_, DEPLOYMENT_ID_, BYTES_, GENERATED_)
    SELECT
        v_byte_id,
        CONCAT(p_key, '.bpmn'),
        v_dep,
        CONVERT(
            REPLACE(
                REPLACE(CONVERT(BYTES_ USING utf8mb4), v_tpl_key, p_key),
                v_tpl_name, p_name)
        USING utf8mb4),
        0
      FROM ACT_GE_BYTEARRAY
     WHERE DEPLOYMENT_ID_ = v_tpl_dep
       AND NAME_ = CONCAT(v_tpl_key, '.bpmn');
    -- 3) æµç¨‹å®šä¹‰ï¼ˆæ¿€æ´»æ€ SUSPENSION_STATE_=1;ENGINE_VERSION_ ç•™ NULL)
    INSERT INTO ACT_RE_PROCDEF
        (ID_, REV_, CATEGORY_, NAME_, KEY_, VERSION_, DEPLOYMENT_ID_, RESOURCE_NAME_,
         DGRM_RESOURCE_NAME_, DESCRIPTION_, HAS_START_FORM_KEY_, HAS_GRAPHICAL_NOTATION_,
         SUSPENSION_STATE_, TENANT_ID_, ENGINE_VERSION_, DERIVED_VERSION_)
    VALUES
        (v_procdef_id, 1, p_category, p_name, p_key, 1, v_dep, CONCAT(p_key, '.bpmn'),
         NULL, NULL, 0, 1,
         1, '', NULL, 0);
    -- 4) æµç¨‹æ¨¡åž‹ï¼ˆå«æœ€å°åˆæ³• META_INFO_,避免模型列表 NPE)
    SET v_meta = CONCAT(
        '{"type":10,"formType":10,"sort":', v_sort,
        ',"visible":true,"allowCancelRunningProcess":true,"allowWithdrawTask":false',
        ',"autoApprovalType":0,"description":"","formCustomCreatePath":"","formCustomViewPath":"",',
        '"formId":null,"managerUserIds":[],"startUserIds":[],"startDeptIds":[],"icon":null,',
        '"processBeforeTriggerSetting":null,"processAfterTriggerSetting":null,',
        '"taskBeforeTriggerSetting":null,"taskAfterTriggerSetting":null,',
        '"printTemplateSetting":null,"summarySetting":null,"titleSetting":null,"processIdRule":null}');
    INSERT INTO ACT_RE_MODEL
        (ID_, NAME_, KEY_, CATEGORY_, CREATE_TIME_, LAST_UPDATE_TIME_, VERSION_, META_INFO_, DEPLOYMENT_ID_, TENANT_ID_)
    VALUES
        (v_model_id, p_name, p_key, p_category, v_now, v_now, 1, v_meta, v_dep, '');
    -- 5) æµç¨‹å®šä¹‰æ‰©å±•信息(model_type=10)
    INSERT INTO bpm_process_definition_info
        (process_definition_id, model_id, model_type, category, form_type, visible, sort,
         allow_cancel_running_process, allow_withdraw_task,
         creator, create_time, updater, update_time, deleted)
    VALUES
        (v_procdef_id, v_model_id, 10, p_category, 10, 1, v_sort,
         1, 1,
         '1', v_now, '1', v_now, 0);
    COMMIT;
    SELECT CONCAT('[OK] å·²éƒ¨ç½²æ–°å®¡æ ¸æµç¨‹ï¼š', p_key, ' (procdef=', v_procdef_id, ')') AS deploy_result;
END$$
DELIMITER ;
-- =============================================================================
-- â‘£ æŒ‰å•据逐个部署缺失流程(重复执行会自动跳过已存在项)
--    å‚数:(key, åç§°, åˆ†ç±»code, æ¨¡æ¿key)
-- =============================================================================
-- ERP é”€å”®è®¢å•审批(代码监听 key:sale_order_approve)
CALL bpm_deploy_flow_from_template('sale_order_approve', '销售订单审批', 'sale_order_approve', 'erp-finance-payment-audit');
-- ERP é‡‡è´­è®¢å•审批(代码监听 key:purchase_order_approve)
CALL bpm_deploy_flow_from_template('purchase_order_approve', '采购订单审批', 'purchase_order_approve', 'erp-finance-payment-audit');
-- CRM åˆåŒå®¡æ‰¹ï¼ˆä»£ç ç›‘听 key:crm-contract-audit,挂既有 contract_approve åˆ†ç±»ï¼‰
CALL bpm_deploy_flow_from_template('crm-contract-audit', '合同审批', 'contract_approve', 'erp-finance-payment-audit');
-- HRM è¯·å‡å®¡æ‰¹ï¼ˆä»£ç ç›‘听 key:hrm_leave)
CALL bpm_deploy_flow_from_template('hrm_leave', '请假审批', 'hrm_leave_approve', 'erp-finance-payment-audit');
-- HRM åŠ ç­å®¡æ‰¹ï¼ˆä»£ç ç›‘å¬ key:hrm_overtime)
CALL bpm_deploy_flow_from_template('hrm_overtime', '加班审批', 'overtime_approve', 'erp-finance-payment-audit');
-- HRM ç¦»èŒå®¡æ‰¹ï¼ˆä»£ç ç›‘听 key:hrm_resignation)
CALL bpm_deploy_flow_from_template('hrm_resignation', '离职审批', 'hrm_resignation_approve', 'erp-finance-payment-audit');
-- HRM è°ƒåŠ¨å®¡æ‰¹ï¼ˆä»£ç ç›‘å¬ key:hrm_transfer)
CALL bpm_deploy_flow_from_template('hrm_transfer', '调动审批', 'hrm_transfer_approve', 'erp-finance-payment-audit');
-- HRM è€ƒå‹¤å¼‚常审批(全量监听不挑 key,挂既有 hrm_attendance_approve åˆ†ç±»ï¼‰
CALL bpm_deploy_flow_from_template('hrm_attendance_approve', '考勤异常审批', 'hrm_attendance_approve', 'erp-finance-payment-audit');
-- SRM ä¾›åº”商准入审批(代码监听 key:supplier_apply_approve)
CALL bpm_deploy_flow_from_template('supplier_apply_approve', '供应商准入审批', 'supplier_apply_approve', 'erp-finance-payment-audit');
-- MES ä»“库入库 / å‡ºåº“ / è°ƒæ‹¨å®¡æ‰¹ï¼ˆä»£ç ç›‘听 key å›ºå®šï¼ŒæŒ‚既有 wms_approve åˆ†ç±»ï¼‰
CALL bpm_deploy_flow_from_template('wms_inbound_approve', '仓库入库审批', 'wms_approve', 'erp-finance-payment-audit');
CALL bpm_deploy_flow_from_template('wms_outbound_approve', '仓库出库审批', 'wms_approve', 'erp-finance-payment-audit');
CALL bpm_deploy_flow_from_template('wms_transfer_approve', '仓库调拨审批', 'wms_approve', 'erp-finance-payment-audit');
-- MES é‡‡è´­æ”¶è´§ï¼ˆå…¥åº“单)审批(全量监听,挂既有 item_receipt_approve åˆ†ç±»ï¼‰
CALL bpm_deploy_flow_from_template('item_receipt_approve', '采购入库审批', 'item_receipt_approve', 'erp-finance-payment-audit');
-- MES æŠ¥å·¥/生产工单审批(代码监听 key:mes-pro-feedback-audit)
CALL bpm_deploy_flow_from_template('mes-pro-feedback-audit', '报工审批', 'mes_pro_feedback_audit', 'erp-finance-payment-audit');
-- =============================================================================
-- â‘¤ éƒ¨ç½²ç»“果校验(应看到 14 æ¡æ–°æµç¨‹ä¸” SUSPENSION_STATE_=1、model_type=10)
-- =============================================================================
SELECT p.KEY_, p.NAME_, p.VERSION_, p.SUSPENSION_STATE_ AS active_flag,
       LENGTH(p.ID_) AS procdef_id_len,
       i.model_type, i.category
  FROM ACT_RE_PROCDEF p
  LEFT JOIN bpm_process_definition_info i ON i.process_definition_id = p.ID_
 WHERE p.KEY_ IN ('sale_order_approve','purchase_order_approve','crm-contract-audit',
                  'hrm_leave','hrm_overtime','hrm_resignation','hrm_transfer',
                  'hrm_attendance_approve','supplier_apply_approve',
                  'wms_inbound_approve','wms_outbound_approve','wms_transfer_approve',
                  'item_receipt_approve','mes-pro-feedback-audit')
 ORDER BY p.KEY_;
docs/sql/config_export_all_20260904.sql
¶Ô±ÈÐÂÎļþ
ÎļþÌ«´ó
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
@@ -486,7 +486,7 @@
    ErrorCode WM_TRANSACTION_LIST_EMPTY = new ErrorCode(1_040_703_011, "库存事务列表不能为空");
    ErrorCode WM_TRANSACTION_BATCH_NOT_EXISTS = new ErrorCode(1_040_703_012, "批次记录不存在");
    ErrorCode WM_MATERIAL_STOCK_REQUIRED = new ErrorCode(1_040_703_013, "库存记录不能为空");
    ErrorCode WM_MATERIAL_STOCK_SELECTION_MISMATCH = new ErrorCode(1_040_703_014, "库存记录与提交的物料、批次或库位信息不一致");
    ErrorCode WM_MATERIAL_STOCK_SELECTION_MISMATCH = new ErrorCode(1_040_703_014, "所选库存记录与单据信息不一致,请核对后重新操作");
    ErrorCode WM_MATERIAL_STOCK_QUANTITY_INVALID = new ErrorCode(1_040_703_015, "调整数量不能为空或零");
    ErrorCode WM_MATERIAL_STOCK_FROZEN = new ErrorCode(1_040_703_016, "库存记录已冻结,无法移库");
    ErrorCode WM_MATERIAL_STOCK_IMPORT_LIST_IS_EMPTY = new ErrorCode(1_040_703_017, "导入的库存数据不能为空");
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/materialstock/MesWmMaterialStockServiceImpl.java
@@ -278,14 +278,13 @@
            throw exception(WM_MATERIAL_STOCK_REQUIRED);
        }
        MesWmMaterialStockDO stock = validateMaterialStockExists(materialStockId);
        // 2. æ ¡éªŒåº“存记录的物料、批次、仓库、库区、库位等信息与前端选择的一致,避免串单或越权提交
        if (ObjUtil.notEqual(stock.getItemId(), itemId)
                || ObjUtil.notEqual(stock.getBatchId(), batchId)
                || (batchCode != null && ObjUtil.notEqual(stock.getBatchCode(), batchCode))
                || ObjUtil.notEqual(stock.getWarehouseId(), warehouseId)
                || ObjUtil.notEqual(stock.getLocationId(), locationId)
                || ObjUtil.notEqual(stock.getAreaId(), areaId)) {
            throw exception(WM_MATERIAL_STOCK_SELECTION_MISMATCH);
        // 2. æ ¡éªŒåº“存记录的物料、批次、仓库、库区、库位等信息与单据一致,避免串单或越权提交。
        //    é€ä¸ªç»´åº¦æ¯”对,命中时提示具体差异(库存侧 vs å•据侧)并给出处理办法,避免笼统报"信息不一致"
        String mismatch = buildSelectedStockMismatch(
                stock, itemId, batchId, batchCode, warehouseId, locationId, areaId);
        if (mismatch != null) {
            throw new ServiceException(WM_MATERIAL_STOCK_SELECTION_MISMATCH.getCode(),
                    "所选库存记录与单据不一致:" + mismatch + "。请重新选择该物料正确的库存;若仓库/库区/库位/批次被禁用无法修改,请删除本条明细后重新添加。");
        }
        // 3. æ ¡éªŒåº“存数量充足(如果前端传了 quantity,则必须保证库存数量 >= quantity)
        if (quantity != null && stock.getQuantity() != null && stock.getQuantity().compareTo(quantity) < 0) {
@@ -294,6 +293,74 @@
        return stock;
    }
    /**
     * é€ç»´åº¦æ¯”对待选库存与单据传入的信息,返回第一个不一致维度的可读描述;全部一致返回 null
     */
    private String buildSelectedStockMismatch(MesWmMaterialStockDO stock, Long itemId, Long batchId, String batchCode,
                                              Long warehouseId, Long locationId, Long areaId) {
        if (ObjUtil.notEqual(stock.getItemId(), itemId)) {
            return "所选库存归属物料[" + itemIdText(itemId) + "],单据物料为[" + itemIdText(stock.getItemId()) + "]";
        }
        if (ObjUtil.notEqual(stock.getBatchId(), batchId)) {
            return "所选库存批次[" + batchText(stock.getBatchId(), stock.getBatchCode())
                    + "],单据批次为[" + batchText(batchId, batchCode) + "]";
        }
        if (batchCode != null && ObjUtil.notEqual(stock.getBatchCode(), batchCode)) {
            return "所选库存批次号[" + stock.getBatchCode() + "],单据批次号为[" + batchCode + "]";
        }
        if (ObjUtil.notEqual(stock.getWarehouseId(), warehouseId)) {
            return "所选库存仓库[" + warehouseText(stock.getWarehouseId())
                    + "],单据仓库为[" + warehouseText(warehouseId) + "]";
        }
        if (ObjUtil.notEqual(stock.getLocationId(), locationId)) {
            return "所选库存库区[" + locationText(stock.getLocationId())
                    + "],单据库区为[" + locationText(locationId) + "]";
        }
        if (ObjUtil.notEqual(stock.getAreaId(), areaId)) {
            return "所选库存库位[" + areaText(stock.getAreaId())
                    + "],单据库位为[" + areaText(areaId) + "]";
        }
        return null;
    }
    private String itemIdText(Long itemId) {
        return itemId == null ? "未选择" : ("物料ID=" + itemId);
    }
    private String batchText(Long batchId, String batchCode) {
        if (StrUtil.isNotBlank(batchCode)) {
            return batchCode;
        }
        return batchId == null ? "未选择" : ("批次ID=" + batchId);
    }
    private String warehouseText(Long warehouseId) {
        if (warehouseId == null) {
            return "未选择";
        }
        MesWmWarehouseDO warehouse = warehouseService.getWarehouse(warehouseId);
        return warehouse != null && StrUtil.isNotBlank(warehouse.getName())
                ? warehouse.getName() : ("仓库ID=" + warehouseId);
    }
    private String locationText(Long locationId) {
        if (locationId == null) {
            return "未选择";
        }
        MesWmWarehouseLocationDO location = locationService.getWarehouseLocation(locationId);
        return location != null && StrUtil.isNotBlank(location.getName())
                ? location.getName() : ("库区ID=" + locationId);
    }
    private String areaText(Long areaId) {
        if (areaId == null) {
            return "未选择";
        }
        MesWmWarehouseAreaDO area = areaService.getWarehouseArea(areaId);
        return area != null && StrUtil.isNotBlank(area.getName())
                ? area.getName() : ("库位ID=" + areaId);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long adjustMaterialStockIn(MesWmMaterialStockAdjustReqVO reqVO) {
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueDetailServiceImpl.java
@@ -18,6 +18,8 @@
import java.math.BigDecimal;
import java.util.List;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
@@ -123,9 +125,10 @@
            if (stock == null) {
                throw exception(WM_MATERIAL_STOCK_NOT_EXISTS);
            }
            // æ ¡éªŒç‰©æ–™å’Œæ‰¹æ¬¡ä¸€è‡´
            // æ ¡éªŒç‰©æ–™ä¸€è‡´ï¼šæ­¤å¤„只比对物料,因为来源库存(线边/虚拟仓)与上架目标仓库本就不同属正常
            if (ObjUtil.notEqual(stock.getItemId(), reqVO.getItemId())) {
                throw exception(WM_MATERIAL_STOCK_SELECTION_MISMATCH);
                throw new ServiceException(WM_MATERIAL_STOCK_SELECTION_MISMATCH.getCode(),
                        "所选来源库存归属物料与当前退货物料不一致,请重新选择来源库存");
            }
            // æ ¡éªŒåº“存数量充足
            if (reqVO.getQuantity() != null && stock.getQuantity() != null
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/transaction/MesWmTransactionServiceImpl.java
@@ -31,6 +31,8 @@
import java.time.LocalDateTime;
import java.util.List;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
@@ -89,12 +91,14 @@
            if (materialStock == null) {
                throw exception(WM_MATERIAL_STOCK_NOT_EXISTS);
            }
            // æ ¡éªŒç‰©æ–™ã€æ‰¹æ¬¡ã€ä»“库等维度一致(防止传入错误的 materialStockId)
            // æ ¡éªŒç‰©æ–™ã€æ‰¹æ¬¡ã€ä»“库等维度一致(防止传入错误的 materialStockId)。
            // è‹¥æ­¤å¤„不一致,通常是该库存已被移库/合并/拆分,需退回重新拣货,故给出针对性提示
            if (ObjUtil.notEqual(materialStock.getItemId(), reqDTO.getItemId())
                    || ObjUtil.notEqual(materialStock.getWarehouseId(), reqDTO.getWarehouseId())
                    || ObjUtil.notEqual(materialStock.getLocationId(), reqDTO.getLocationId())
                    || ObjUtil.notEqual(materialStock.getAreaId(), reqDTO.getAreaId())) {
                throw exception(WM_MATERIAL_STOCK_SELECTION_MISMATCH);
                throw new ServiceException(WM_MATERIAL_STOCK_SELECTION_MISMATCH.getCode(),
                        "该库存记录已变动(可能被移库/合并/拆分),与出库明细不一致,无法扣减,请退回重新拣货");
            }
        } else if (isInbound) {
            // å…¥åº“事务:根据 forceCreateNewStock æ ‡å¿—决定是创建新记录还是查找/创建
yudao-server/src/main/resources/application-yzfx.yaml
@@ -65,8 +65,8 @@
      primary: master
      datasource:
        master:
#          url: jdbc:mysql://172.17.0.1:9002/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X è¿žæŽ¥çš„示例
          url: jdbc:mysql://42.63.70.231:9002/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X è¿žæŽ¥çš„示例
          url: jdbc:mysql://172.17.0.1:9002/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X è¿žæŽ¥çš„示例
#          url: jdbc:mysql://42.63.70.231:9002/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X è¿žæŽ¥çš„示例
          username: root
          password: yzfx@123456..
yudao-server/src/main/resources/application-zsjc.yaml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,206 @@
server:
  port: 48080
--- #################### æ•°æ®åº“相关配置 ####################
spring:
  autoconfigure:
    # noinspection SpringBootApplicationYaml
    exclude:
      - org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration # é»˜è®¤ local çŽ¯å¢ƒï¼Œä¸å¼€å¯ Quartz çš„自动配置
      - org.springframework.ai.vectorstore.qdrant.autoconfigure.QdrantVectorStoreAutoConfiguration # ç¦ç”¨ AI æ¨¡å—çš„ Qdrant,手动创建
      - org.springframework.ai.vectorstore.redis.autoconfigure.RedisVectorStoreAutoConfiguration # ç¦ç”¨ AI æ¨¡å—çš„ Redis,手动创建
      - org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusVectorStoreAutoConfiguration # ç¦ç”¨ AI æ¨¡å—çš„ Milvus,手动创建
      - org.dromara.trans.config.TransServiceConfig # TODO èŠ‹è‰¿ï¼šeasy-trans å…¼å®¹ Spring Boot 4 åŽç§»é™¤ï¼ˆTransServiceConfig å¼•用了已移除的 RestTemplateBuilder)
      # ç¦ç”¨æœªé…ç½® API Key çš„æ¨¡åž‹è‡ªåŠ¨é…ç½®ï¼ˆåªä¿ç•™é€šä¹‰åƒé—® Embedding çš„æ‰‹åŠ¨é…ç½®ï¼‰
      - org.springframework.ai.model.openai.autoconfigure.OpenAiChatAutoConfiguration
      - org.springframework.ai.model.openai.autoconfigure.OpenAiEmbeddingAutoConfiguration
      - org.springframework.ai.model.openai.autoconfigure.OpenAiImageAutoConfiguration
      - org.springframework.ai.model.openai.autoconfigure.OpenAiAudioSpeechAutoConfiguration
      - org.springframework.ai.model.openai.autoconfigure.OpenAiAudioTranscriptionAutoConfiguration
      - org.springframework.ai.model.openai.autoconfigure.OpenAiModerationAutoConfiguration
      - org.springframework.ai.model.deepseek.autoconfigure.DeepSeekChatAutoConfiguration
      - org.springframework.ai.model.anthropic.autoconfigure.AnthropicChatAutoConfiguration
      - org.springframework.ai.model.google.genai.autoconfigure.chat.GoogleGenAiChatAutoConfiguration
      - org.springframework.ai.model.google.genai.autoconfigure.embedding.GoogleGenAiEmbeddingConnectionAutoConfiguration
      - org.springframework.ai.model.google.genai.autoconfigure.embedding.GoogleGenAiTextEmbeddingAutoConfiguration
      - org.springframework.ai.model.ollama.autoconfigure.OllamaApiAutoConfiguration
      - org.springframework.ai.model.ollama.autoconfigure.OllamaChatAutoConfiguration
      - org.springframework.ai.model.ollama.autoconfigure.OllamaEmbeddingAutoConfiguration
      - org.springframework.ai.model.stabilityai.autoconfigure.StabilityAiImageAutoConfiguration
  # æ•°æ®æºé…ç½®é¡¹
  datasource:
    druid: # Druid ã€ç›‘控】相关的全局配置
      web-stat-filter:
        enabled: true
      stat-view-servlet:
        enabled: true
        allow: # è®¾ç½®ç™½åå•,不填则允许所有访问
        url-pattern: /druid/*
        login-username: # æŽ§åˆ¶å°ç®¡ç†ç”¨æˆ·åå’Œå¯†ç 
        login-password:
      filter:
        stat:
          enabled: true
          log-slow-sql: true # æ…¢ SQL è®°å½•
          slow-sql-millis: 100
          merge-sql: true
        wall:
          config:
            multi-statement-allow: true
    dynamic: # å¤šæ•°æ®æºé…ç½®
      druid: # Druid ã€è¿žæŽ¥æ± ã€‘相关的全局配置
        initial-size: 1 # åˆå§‹è¿žæŽ¥æ•°
        min-idle: 1 # æœ€å°è¿žæŽ¥æ± æ•°é‡
        max-active: 20 # æœ€å¤§è¿žæŽ¥æ± æ•°é‡
        max-wait: 60000 # é…ç½®èŽ·å–è¿žæŽ¥ç­‰å¾…è¶…æ—¶çš„æ—¶é—´ï¼Œå•ä½ï¼šæ¯«ç§’ï¼ˆ1 åˆ†é’Ÿï¼‰
        time-between-eviction-runs-millis: 60000 # é…ç½®é—´éš”多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒(1 åˆ†é’Ÿï¼‰
        min-evictable-idle-time-millis: 600000 # é…ç½®ä¸€ä¸ªè¿žæŽ¥åœ¨æ± ä¸­æœ€å°ç”Ÿå­˜çš„æ—¶é—´ï¼Œå•位:毫秒(10 åˆ†é’Ÿï¼‰
        max-evictable-idle-time-millis: 1800000 # é…ç½®ä¸€ä¸ªè¿žæŽ¥åœ¨æ± ä¸­æœ€å¤§ç”Ÿå­˜çš„æ—¶é—´ï¼Œå•位:毫秒(30 åˆ†é’Ÿï¼‰
        validation-query: SELECT 1 FROM DUAL # é…ç½®æ£€æµ‹è¿žæŽ¥æ˜¯å¦æœ‰æ•ˆ
        test-while-idle: true
        test-on-borrow: false
        test-on-return: false
        pool-prepared-statements: true # æ˜¯å¦å¼€å¯ PreparedStatement ç¼“å­˜
        max-pool-prepared-statement-per-connection-size: 20 # æ¯ä¸ªè¿žæŽ¥ç¼“存的 PreparedStatement æ•°é‡
      primary: master
      datasource:
        master:
          url: jdbc:mysql://172.17.0.1:9002/mom-zsjc?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X è¿žæŽ¥çš„示例
#          url: jdbc:mysql://42.63.70.231:9002/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X è¿žæŽ¥çš„示例
          username: root
          password: zsjc@123456..
  # Redis é…ç½®ã€‚Redisson é»˜è®¤çš„配置足够使用,一般不需要进行调优
  data:
    redis:
      host: 172.17.0.1 # åœ°å€
      port: 6379 # ç«¯å£
      database: 1 # æ•°æ®åº“索引
#      password: dev # å¯†ç ï¼Œå»ºè®®ç”Ÿäº§çŽ¯å¢ƒå¼€å¯
--- #################### å®šæ—¶ä»»åŠ¡ç›¸å…³é…ç½® ####################
# Quartz é…ç½®é¡¹ï¼Œå¯¹åº” QuartzProperties é…ç½®ç±»
spring:
  quartz:
    auto-startup: true # æœ¬åœ°å¼€å‘环境,尽量不要开启 Job
    scheduler-name: schedulerName # Scheduler åå­—。默认为 schedulerName
    job-store-type: jdbc # Job å­˜å‚¨å™¨ç±»åž‹ã€‚默认为 memory è¡¨ç¤ºå†…存,可选 jdbc ä½¿ç”¨æ•°æ®åº“。
    wait-for-jobs-to-complete-on-shutdown: true # åº”用关闭时,是否等待定时任务执行完成。默认为 false ï¼Œå»ºè®®è®¾ç½®ä¸º true
    properties: # æ·»åŠ  Quartz Scheduler é™„加属性,更多可以看 http://www.quartz-scheduler.org/documentation/2.4.0-SNAPSHOT/configuration.html æ–‡æ¡£
      org:
        quartz:
          # Scheduler ç›¸å…³é…ç½®
          scheduler:
            instanceName: schedulerName
            instanceId: AUTO # è‡ªåŠ¨ç”Ÿæˆ instance ID
          # JobStore ç›¸å…³é…ç½®
          jobStore:
            # JobStore å®žçŽ°ç±»ã€‚å¯è§åšå®¢ï¼šhttps://blog.csdn.net/weixin_42458219/article/details/122247162
            class: org.springframework.scheduling.quartz.LocalDataSourceJobStore
            isClustered: true # æ˜¯é›†ç¾¤æ¨¡å¼
            clusterCheckinInterval: 15000 # é›†ç¾¤æ£€æŸ¥é¢‘率,单位:毫秒。默认为 15000,即 15 ç§’
            misfireThreshold: 60000 # misfire é˜€å€¼ï¼Œå•位:毫秒。
          # çº¿ç¨‹æ± ç›¸å…³é…ç½®
          threadPool:
            threadCount: 25 # çº¿ç¨‹æ± å¤§å°ã€‚默认为 10 ã€‚
            threadPriority: 5 # çº¿ç¨‹ä¼˜å…ˆçº§
            class: org.quartz.simpl.SimpleThreadPool # çº¿ç¨‹æ± ç±»åž‹
    jdbc: # ä½¿ç”¨ JDBC çš„ JobStore çš„æ—¶å€™ï¼ŒJDBC çš„配置
      initialize-schema: NEVER # æ˜¯å¦è‡ªåŠ¨ä½¿ç”¨ SQL åˆå§‹åŒ– Quartz è¡¨ç»“构。这里设置成 never ï¼Œæˆ‘们手动创建表结构。
--- #################### æœåŠ¡ä¿éšœç›¸å…³é…ç½® ####################
# Lock4j é…ç½®é¡¹
lock4j:
  acquire-timeout: 3000 # èŽ·å–åˆ†å¸ƒå¼é”è¶…æ—¶æ—¶é—´ï¼Œé»˜è®¤ä¸º 3000 æ¯«ç§’
  expire: 30000 # åˆ†å¸ƒå¼é”çš„超时时间,默认为 30000 æ¯«ç§’
--- #################### ç›‘控相关配置 ####################
# Actuator ç›‘控端点的配置项
management:
  endpoints:
    web:
      base-path: /actuator # Actuator æä¾›çš„ API æŽ¥å£çš„æ ¹ç›®å½•。默认为 /actuator
      exposure:
        include: '*' # éœ€è¦å¼€æ”¾çš„端点。默认值只打开 health å’Œ info ä¸¤ä¸ªç«¯ç‚¹ã€‚通过设置 * ï¼Œå¯ä»¥å¼€æ”¾æ‰€æœ‰ç«¯ç‚¹ã€‚
# Spring Boot Admin é…ç½®é¡¹
spring:
  boot:
    admin:
      # Spring Boot Admin Client å®¢æˆ·ç«¯çš„相关配置
      client:
        url: http://127.0.0.1:${server.port}/${spring.boot.admin.context-path} # è®¾ç½® Spring Boot Admin Server åœ°å€
        instance:
          service-host-type: IP # æ³¨å†Œå®žä¾‹æ—¶ï¼Œä¼˜å…ˆä½¿ç”¨ IP [IP, HOST_NAME, CANONICAL_HOST_NAME]
        username: admin
        password: admin
      # Spring Boot Admin Server æœåŠ¡ç«¯çš„ç›¸å…³é…ç½®
      context-path: /admin # é…ç½® Spring
      # å…è®¸åµŒå…¥ iframe çš„域名(支持通配符),实际部署时,可以改为 "'self' [你的公网域名]"
      frame-ancestors: "'self' localhost localhost:48080 127.0.0.1 127.0.0.1:48080"
# æ—¥å¿—文件配置
logging:
  file:
    name: logs/${spring.application.name}.log # æ—¥å¿—文件名,相对于项目根目录
  level:
    # é…ç½®è‡ªå·±å†™çš„ MyBatis Mapper æ‰“印日志
    cn.iocoder.yudao.module.bpm.dal.mysql: debug
    cn.iocoder.yudao.module.infra.dal.mysql: debug
    cn.iocoder.yudao.module.infra.dal.mysql.logger.ApiErrorLogMapper: INFO # é…ç½® ApiErrorLogMapper çš„æ—¥å¿—级别为 info,避免和 GlobalExceptionHandler é‡å¤æ‰“印
    cn.iocoder.yudao.module.infra.dal.mysql.job.JobLogMapper: INFO # é…ç½® JobLogMapper çš„æ—¥å¿—级别为 info
    cn.iocoder.yudao.module.infra.dal.mysql.file.FileConfigMapper: INFO # é…ç½® FileConfigMapper çš„æ—¥å¿—级别为 info
    cn.iocoder.yudao.module.system.dal.mysql: debug
    cn.iocoder.yudao.module.system.dal.mysql.sms.SmsChannelMapper: INFO # é…ç½® SmsChannelMapper çš„æ—¥å¿—级别为 info
    cn.iocoder.yudao.module.member.dal.mysql: debug
    cn.iocoder.yudao.module.crm.dal.mysql: debug
    cn.iocoder.yudao.module.erp.dal.mysql: debug
    cn.iocoder.yudao.module.mes.dal.mysql: debug
    cn.iocoder.yudao.module.ai.dal.mysql: debug
    cn.iocoder.yudao.module.im.dal.mysql: debug
    org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO èŠ‹è‰¿ï¼šå…ˆç¦ç”¨ï¼ŒSpring Boot 3.X å­˜åœ¨éƒ¨åˆ†é”™è¯¯çš„ WARN æç¤º
debug: false
yudao:
  ai:
    vector-store:
      type: milvus # å‘量库类型:milvus
      milvus:
        initialize-schema: true
        database-name: test
        collection-name: knowledge_segment_zsjc
        host: 36.213.90.123
        port: 19530
--- #################### è¶…级管理员相关配置 ####################
# è¶…级管理员配置项,设置当前项目所有自定义的配置
yudao:
  captcha:
    enable: false # æœ¬åœ°çŽ¯å¢ƒï¼Œæš‚æ—¶å…³é—­å›¾ç‰‡éªŒè¯ç ï¼Œæ–¹ä¾¿ç™»å½•ç­‰æŽ¥å£çš„æµ‹è¯•ï¼›
  security:
    mock-enable: true
  access-log: # è®¿é—®æ—¥å¿—的配置项
    enable: false
  demo: false # å…³é—­æ¼”示模式
  storage: # æ–‡ä»¶ä¸Šä¼ é…ç½®
    path: /data/javaWork/mom/file # æ–‡ä»¶å­˜å‚¨æ ¹ç›®å½•
    url-prefix: /admin-api/system/storage-blob # URL路径前缀
    domain: http://116.172.73.239:9001 # åŸŸåå‰ç¼€ï¼ˆæŒ‰å®žé™…域名配置),前端配置好像不太对现在是直连后端,所有要后端端口
    expired: 120 # token过期时间(分钟)
    use-limit: 10 # token使用次数上限
    jwt-secret: abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG # JWT密钥
    compress: true # æ˜¯å¦åŽ‹ç¼©å›¾ç‰‡
    need-compress-size: 10MB # è§¦å‘压缩的文件大小阈值
    compress-quality: 0.5 # åŽ‹ç¼©è´¨é‡(0.0-1.0)
  database-backup: # æ•°æ®åº“备份配置
    host: 172.17.0.1 # å¤‡ä»½ç›®æ ‡ MySQL ä¸»æœºï¼Œç•™ç©ºè‡ªåŠ¨ä»Žæ•°æ®æº JDBC URL è§£æž
    port: 9002 # å¤‡ä»½ç›®æ ‡ MySQL ç«¯å£ï¼Œç•™ç©ºè‡ªåŠ¨ä»Žæ•°æ®æº JDBC URL è§£æž
    database: ruoyi-vue-pro # å¤‡ä»½ç›®æ ‡æ•°æ®åº“,留空自动从数据源 JDBC URL è§£æž
    username: root # å¤‡ä»½è´¦å·ï¼Œç•™ç©ºä½¿ç”¨æ•°æ®æºè´¦å·
    password: 'zsjc@123456..' # å¤‡ä»½å¯†ç ï¼Œç•™ç©ºä½¿ç”¨æ•°æ®æºå¯†ç 
    command: # mysqldump å‘½ä»¤ï¼Œç•™ç©ºè‡ªåŠ¨æ£€æµ‹éƒ¨ç½²çŽ¯å¢ƒï¼ˆæœ¬æœº mysqldump æˆ– Docker MySQL å®¹å™¨ï¼‰ï¼›å¯æ‰‹åŠ¨æŒ‡å®šï¼Œå¦‚ï¼šdocker exec mysql8 mysqldump