From 33250ae12ae0649010436bcc636e44d6acd6d86b Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期一, 15 六月 2026 09:40:57 +0800
Subject: [PATCH] 修改客户,销售,协同,报价,质量
---
src/main/java/com/ruoyi/mock/service/DataCheckService.java | 15
docs/routing_operation_plan_person.md | 91 +++
src/main/java/com/ruoyi/mock/vo/DataGenerateResult.java | 37 +
src/main/java/com/ruoyi/mock/vo/DataCheckResult.java | 36 +
src/main/java/com/ruoyi/mock/dto/DataCheckRequest.java | 15
src/main/java/com/ruoyi/mock/service/DataGenerateService.java | 15
docs/mock_data_check.md | 167 ++++++
doc/20260615_add_plan_person_and_executor_to_routing_operation.sql | 8
src/main/java/com/ruoyi/mock/service/impl/DataCheckServiceImpl.java | 146 +++++
src/main/java/com/ruoyi/mock/controller/DataCheckController.java | 54 ++
src/main/java/com/ruoyi/mock/prompt/MockDataPrompt.java | 213 ++++++++
docs/mock_data_generate.md | 243 +++++++++
src/main/java/com/ruoyi/mock/service/impl/DataGenerateServiceImpl.java | 466 +++++++++++++++++
src/main/java/com/ruoyi/mock/dto/DataGenerateRequest.java | 33 +
14 files changed, 1,539 insertions(+), 0 deletions(-)
diff --git a/doc/20260615_add_plan_person_and_executor_to_routing_operation.sql b/doc/20260615_add_plan_person_and_executor_to_routing_operation.sql
new file mode 100644
index 0000000..b98e8ca
--- /dev/null
+++ b/doc/20260615_add_plan_person_and_executor_to_routing_operation.sql
@@ -0,0 +1,8 @@
+# 鐢熶骇宸ュ簭鏂板璁″垝浜哄憳鍜岃鍒掓墽琛屼汉鍛樺瓧娈�
+ALTER TABLE technology_routing_operation
+ ADD plan_person bigint NULL COMMENT '璁″垝浜哄憳ID',
+ ADD executor bigint NULL COMMENT '璁″垝鎵ц浜哄憳ID';
+
+ALTER TABLE production_order_routing_operation
+ ADD plan_person bigint NULL COMMENT '璁″垝浜哄憳ID',
+ ADD executor bigint NULL COMMENT '璁″垝鎵ц浜哄憳ID';
diff --git a/docs/mock_data_check.md b/docs/mock_data_check.md
new file mode 100644
index 0000000..e423eb5
--- /dev/null
+++ b/docs/mock_data_check.md
@@ -0,0 +1,167 @@
+# 鏁版嵁妯℃嫙 - 鍩虹鏁版嵁妫�娴�
+
+## 娑夊強椤甸潰
+
+- 鏁版嵁妯℃嫙鎿嶄綔椤甸潰锛堟柊澧烇級
+
+## API
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| POST | /mock/dataCheck | 妫�娴嬫寚瀹氭ā鍧楃殑鍩虹鏁版嵁鏄惁灏辩华 |
+
+**璇锋眰鍙傛暟锛�**
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|------|------|------|------|
+| modules | List\<String\> | 鏄� | 瑕佹娴嬬殑妯″潡鍒楄〃锛屽彲閫夊�硷細sales锛堥攢鍞級銆乸urchase锛堥噰璐級銆乹uality锛堣川閲忥級 |
+
+璇锋眰浣撶ず渚嬶細
+```json
+{ "modules": ["sales", "purchase", "quality"] }
+```
+
+**鍝嶅簲锛�**
+```json
+{
+ "code": 200,
+ "msg": "鎿嶄綔鎴愬姛",
+ "data": {
+ "totalItems": 6,
+ "passedItems": 3,
+ "items": [
+ {
+ "module": "common",
+ "itemName": "浜у搧鏁版嵁",
+ "minRequired": 1,
+ "currentCount": 5,
+ "passed": true,
+ "message": "閫氳繃"
+ },
+ {
+ "module": "sales",
+ "itemName": "瀹㈡埛鏁版嵁",
+ "minRequired": 1,
+ "currentCount": 0,
+ "passed": false,
+ "message": "缂哄皯瀹㈡埛鏁版嵁锛岃鍏堝湪銆愬熀纭�鏁版嵁-瀹㈡埛绠$悊銆戜腑娣诲姞鑷冲皯1鏉″鎴�"
+ }
+ ]
+ }
+}
+```
+
+## 妫�娴嬭鍒�
+
+### 閿�鍞ā鍧� (sales)
+| 妫�娴嬮」 | 鏈�浣庢暟閲� | 鏈�氳繃鎻愮ず |
+|--------|----------|------------|
+| 浜у搧鏁版嵁 | 1 | 缂哄皯浜у搧鏁版嵁锛岃鍏堝湪銆愬熀纭�鏁版嵁-浜у搧绠$悊銆戜腑娣诲姞鑷冲皯1鏉′骇鍝� |
+| 瀹㈡埛鏁版嵁 | 1 | 缂哄皯瀹㈡埛鏁版嵁锛岃鍏堝湪銆愬熀纭�鏁版嵁-瀹㈡埛绠$悊銆戜腑娣诲姞鑷冲皯1鏉″鎴� |
+| 鎶ヤ环瀹℃壒妯℃澘 | 1 | 缂哄皯鎶ヤ环瀹℃壒妯℃澘锛岃鍏堝湪銆愮郴缁熺鐞�-瀹℃壒妯℃澘銆戜腑鍒涘缓鎶ヤ环瀹℃壒妯℃澘 |
+| 鍙戣揣瀹℃壒妯℃澘 | 1 | 缂哄皯鍙戣揣瀹℃壒妯℃澘锛岃鍏堝湪銆愮郴缁熺鐞�-瀹℃壒妯℃澘銆戜腑鍒涘缓鍙戣揣瀹℃壒妯℃澘 |
+
+### 閲囪喘妯″潡 (purchase)
+| 妫�娴嬮」 | 鏈�浣庢暟閲� | 鏈�氳繃鎻愮ず |
+|--------|----------|------------|
+| 浜у搧鏁版嵁 | 1 | 缂哄皯浜у搧鏁版嵁锛岃鍏堝湪銆愬熀纭�鏁版嵁-浜у搧绠$悊銆戜腑娣诲姞鑷冲皯1鏉′骇鍝� |
+| 渚涘簲鍟嗘暟鎹� | 1 | 缂哄皯渚涘簲鍟嗘暟鎹紝璇峰厛鍦ㄣ�愬熀纭�鏁版嵁-渚涘簲鍟嗙鐞嗐�戜腑娣诲姞鑷冲皯1鏉′緵搴斿晢 |
+| 閲囪喘瀹℃壒妯℃澘 | 1 | 缂哄皯閲囪喘瀹℃壒妯℃澘锛岃鍏堝湪銆愮郴缁熺鐞�-瀹℃壒妯℃澘銆戜腑鍒涘缓閲囪喘瀹℃壒妯℃澘 |
+
+### 璐ㄩ噺妯″潡 (quality)
+| 妫�娴嬮」 | 鏈�浣庢暟閲� | 鏈�氳繃鎻愮ず |
+|--------|----------|------------|
+| 浜у搧鏁版嵁 | 1 | 缂哄皯浜у搧鏁版嵁锛岃鍏堝湪銆愬熀纭�鏁版嵁-浜у搧绠$悊銆戜腑娣诲姞鑷冲皯1鏉′骇鍝� |
+| 妫�娴嬫爣鍑� | 1 | 缂哄皯妫�娴嬫爣鍑嗭紝璇峰厛鍦ㄣ�愯川閲忕鐞�-妫�娴嬫爣鍑嗐�戜腑鍒涘缓妫�娴嬫爣鍑� |
+| 鎸囨爣缁戝畾 | 1 | 缂哄皯鎸囨爣缁戝畾锛岃鍏堝湪銆愯川閲忕鐞�-妫�娴嬫爣鍑嗙粦瀹氥�戜腑灏嗘娴嬫爣鍑嗕笌浜у搧缁戝畾 |
+
+## 鍓嶇淇敼鐐�
+
+### 1. 鏁版嵁妯℃嫙椤甸潰鍏ュ彛
+
+鍦ㄥ乏渚ц彍鍗曟柊澧�"鏁版嵁妯℃嫙"鑿滃崟椤癸紝璺敱 `/mock`銆�
+
+### 2. 鏁版嵁妫�娴嬪尯鍩�
+
+```html
+<template>
+ <div class="mock-container">
+ <el-card header="鍩虹鏁版嵁妫�娴�">
+ <el-checkbox-group v-model="selectedModules">
+ <el-checkbox label="sales">閿�鍞ā鍧�</el-checkbox>
+ <el-checkbox label="purchase">閲囪喘妯″潡</el-checkbox>
+ <el-checkbox label="quality">璐ㄩ噺妯″潡</el-checkbox>
+ </el-checkbox-group>
+ <el-button type="primary" @click="handleCheck" :loading="checking">
+ 寮�濮嬫娴�
+ </el-button>
+ </el-card>
+
+ <el-card v-if="checkResult" header="妫�娴嬬粨鏋�" style="margin-top: 16px">
+ <el-alert
+ :title="`閫氳繃 ${checkResult.passedItems} / ${checkResult.totalItems} 椤筦"
+ :type="checkResult.passedItems === checkResult.totalItems ? 'success' : 'warning'"
+ :closable="false"
+ show-icon
+ />
+ <el-table :data="checkResult.items" style="margin-top: 12px">
+ <el-table-column prop="module" label="妯″潡" width="100" />
+ <el-table-column prop="itemName" label="妫�娴嬮」" width="160" />
+ <el-table-column prop="minRequired" label="鏈�浣庤姹�" width="80" />
+ <el-table-column prop="currentCount" label="褰撳墠鏁伴噺" width="80" />
+ <el-table-column label="鐘舵��" width="80">
+ <template #default="{ row }">
+ <el-tag :type="row.passed ? 'success' : 'danger'">
+ {{ row.passed ? '閫氳繃' : '鏈�氳繃' }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column prop="message" label="鎻愮ず" />
+ </el-table>
+ </el-card>
+ </div>
+</template>
+```
+
+### 3. data 鏁版嵁
+
+```js
+data() {
+ return {
+ selectedModules: ['sales', 'purchase', 'quality'],
+ checking: false,
+ checkResult: null,
+ }
+}
+```
+
+### 4. 鏂规硶
+
+```js
+import request from '@/utils/request'
+
+methods: {
+ async handleCheck() {
+ if (this.selectedModules.length === 0) {
+ this.$message.warning('璇疯嚦灏戦�夋嫨涓�涓ā鍧�')
+ return
+ }
+ this.checking = true
+ try {
+ const res = await request.post('/mock/dataCheck', {
+ modules: this.selectedModules
+ })
+ this.checkResult = res.data
+ } finally {
+ this.checking = false
+ }
+ }
+}
+```
+
+## 娉ㄦ剰浜嬮」
+
+- 浜у搧鏁版嵁锛堝叕鍏卞熀纭�锛夊湪澶氫釜妯″潡闂村叡浜紝妫�娴嬬粨鏋滀腑鍙嚭鐜颁竴娆★紝涓嶄細閲嶅灞曠ず
+- 妫�娴嬩粎鍋氬彧璇绘煡璇紝涓嶅啓鍏ヤ换浣曟暟鎹�
+- 妯″潡鍙傛暟涓虹┖鏁扮粍鏃惰繑鍥炵┖妫�娴嬪垪琛�
+- 寤鸿鍦ㄦ暟鎹ā鎷熷紑濮嬪墠鍏堣皟鐢ㄦ鎺ュ彛纭鍩虹鏁版嵁灏辩华
diff --git a/docs/mock_data_generate.md b/docs/mock_data_generate.md
new file mode 100644
index 0000000..6dc801e
--- /dev/null
+++ b/docs/mock_data_generate.md
@@ -0,0 +1,243 @@
+# 鏁版嵁妯℃嫙 - AI 鏁版嵁鐢熸垚
+
+## 娑夊強椤甸潰
+
+- 鏁版嵁妯℃嫙鎿嶄綔椤甸潰锛堟柊澧�/淇敼锛�
+
+## API
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| POST | /mock/generate | AI 鐢熸垚妯℃嫙鏁版嵁锛堝惈鍓嶇疆鍩虹鏁版嵁妫�娴嬶級 |
+
+**璇锋眰鍙傛暟锛�**
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|------|------|------|------|
+| modules | List\<String\> | 鏄� | 瑕佺敓鎴愭暟鎹殑妯″潡锛歴ales锛堥攢鍞級銆乸urchase锛堥噰璐級銆乹uality锛堣川閲忥級銆乸roduction锛堢敓浜э級銆乻tock锛堝簱瀛橈級 |
+| industries | List\<String\> | 鏄� | 琛屼笟锛屽锛歔"鏈烘鍒堕��", "椋熷搧鍔犲伐", "鐢靛瓙瑁呴厤"] |
+| countMin | int | 鍚� | 姣忎釜瀹炰綋鐢熸垚鏉℃暟鏈�灏忓�硷紝榛樿 3 |
+| countMax | int | 鍚� | 姣忎釜瀹炰綋鐢熸垚鏉℃暟鏈�澶у�硷紝榛樿 10 |
+| dateStart | String | 鍚� | 鏃堕棿鑼冨洿寮�濮嬶紙yyyy-MM-dd锛� |
+| dateEnd | String | 鍚� | 鏃堕棿鑼冨洿缁撴潫锛坹yyy-MM-dd锛� |
+| additionalInfo | String | 鍚� | 鍏朵粬琛ュ厖鎻忚堪锛屽"浜у搧浠ラ噾灞為浂閮ㄤ欢涓轰富" |
+
+璇锋眰浣撶ず渚嬶細
+```json
+{
+ "modules": ["sales", "purchase", "quality"],
+ "industries": ["鏈烘鍒堕��", "鐢靛瓙瑁呴厤"],
+ "countMin": 3,
+ "countMax": 8,
+ "dateStart": "2026-01-01",
+ "dateEnd": "2026-06-01",
+ "additionalInfo": "浜у搧浠ラ噾灞為浂閮ㄤ欢鍜岀數瀛愬厓鍣ㄤ欢涓轰富"
+}
+```
+
+**鍝嶅簲锛堟垚鍔燂級锛�**
+```json
+{
+ "code": 200,
+ "msg": "鎿嶄綔鎴愬姛",
+ "data": {
+ "status": "SUCCESS",
+ "totalGenerated": 24,
+ "moduleSummaries": [
+ { "module": "sales", "entityName": "瀹㈡埛", "generatedCount": 5, "successCount": 5, "failCount": 0 },
+ { "module": "sales", "entityName": "閿�鍞彴璐�", "generatedCount": 5, "successCount": 5, "failCount": 0 },
+ { "module": "purchase", "entityName": "渚涘簲鍟�", "generatedCount": 4, "successCount": 4, "failCount": 0 },
+ { "module": "purchase", "entityName": "閲囪喘鍙拌处", "generatedCount": 4, "successCount": 4, "failCount": 0 },
+ { "module": "quality", "entityName": "妫�娴嬫爣鍑�", "generatedCount": 3, "successCount": 3, "failCount": 0 },
+ { "module": "quality", "entityName": "鎸囨爣缁戝畾", "generatedCount": 3, "successCount": 3, "failCount": 0 }
+ ],
+ "errors": [],
+ "checkResult": null
+ }
+}
+```
+
+**鍝嶅簲锛堝熀纭�鏁版嵁涓嶈冻锛屽墠缃娴嬫湭閫氳繃锛夛細**
+```json
+{
+ "code": 500,
+ "msg": "鍩虹鏁版嵁涓嶈冻锛岃鍏堣ˉ鍏呭悗鍐嶇敓鎴�",
+ "data": {
+ "status": "CHECK_FAILED",
+ "checkResult": {
+ "totalItems": 6,
+ "passedItems": 3,
+ "items": [
+ { "module": "common", "itemName": "浜у搧鏁版嵁", "passed": true, "message": "閫氳繃" },
+ { "module": "sales", "itemName": "瀹㈡埛鏁版嵁", "passed": false, "message": "缂哄皯瀹㈡埛鏁版嵁锛岃鍏�..." }
+ ]
+ }
+ }
+}
+```
+
+## 宸ヤ綔娴佺▼
+
+1. 鐢ㄦ埛鍦ㄩ〉闈㈤�夋嫨妯″潡 + 濉啓琛屼笟/鏁伴噺/鏃堕棿绛変俊鎭�
+2. 璋冪敤 `POST /mock/generate`
+3. 鍚庣棣栧厛璋冪敤 `POST /mock/dataCheck` 妫�娴嬪熀纭�鏁版嵁鏄惁灏辩华
+4. 鑻ュ熀纭�鏁版嵁涓嶈冻锛岃繑鍥炴娴嬬粨鏋滐紝鎻愮ず鐢ㄦ埛鍏堣ˉ榻�
+5. 鑻ュ熀纭�鏁版嵁灏辩华锛岃皟鐢� AI 澶фā鍨嬬敓鎴愮鍚堣涓氱壒寰佺殑 JSON 鏁版嵁
+6. 鎸変緷璧栭『搴忓垱寤烘暟鎹紙瀹㈡埛鈫掗攢鍞彴璐︺�佷緵搴斿晢鈫掗噰璐彴璐︺�佹娴嬫爣鍑嗏啋鎸囨爣缁戝畾...锛�
+7. 杩斿洖鐢熸垚鎽樿
+
+## 鍓嶇淇敼鐐�
+
+### 1. 鏁版嵁鐢熸垚鍖哄煙锛堝湪鏁版嵁妫�娴嬪尯鍩熶笅鏂癸級
+
+```html
+<el-card header="AI 鏁版嵁鐢熸垚" style="margin-top: 16px">
+ <el-form :model="generateForm" label-width="100px">
+ <el-form-item label="閫夋嫨妯″潡">
+ <el-checkbox-group v-model="generateForm.modules">
+ <el-checkbox label="sales">閿�鍞�</el-checkbox>
+ <el-checkbox label="purchase">閲囪喘</el-checkbox>
+ <el-checkbox label="quality">璐ㄩ噺</el-checkbox>
+ <el-checkbox label="production">鐢熶骇</el-checkbox>
+ <el-checkbox label="stock">搴撳瓨</el-checkbox>
+ </el-checkbox-group>
+ </el-form-item>
+
+ <el-form-item label="琛屼笟">
+ <el-select
+ v-model="generateForm.industries"
+ multiple
+ filterable
+ allow-create
+ placeholder="杈撳叆鎴栭�夋嫨琛屼笟"
+ >
+ <el-option label="鏈烘鍒堕��" value="鏈烘鍒堕��" />
+ <el-option label="椋熷搧鍔犲伐" value="椋熷搧鍔犲伐" />
+ <el-option label="鐢靛瓙瑁呴厤" value="鐢靛瓙瑁呴厤" />
+ <el-option label="姹借溅闆堕儴浠�" value="姹借溅闆堕儴浠�" />
+ <el-option label="鍖荤枟鍣ㄦ" value="鍖荤枟鍣ㄦ" />
+ <el-option label="鍖栧伐鏉愭枡" value="鍖栧伐鏉愭枡" />
+ <el-option label="绾虹粐鏈嶈" value="绾虹粐鏈嶈" />
+ <el-option label="瀹跺叿鍒堕��" value="瀹跺叿鍒堕��" />
+ </el-select>
+ </el-form-item>
+
+ <el-row :gutter="16">
+ <el-col :span="12">
+ <el-form-item label="鐢熸垚鏉℃暟">
+ <el-slider
+ v-model="generateForm.countRange"
+ range
+ :min="1"
+ :max="50"
+ :marks="{1:'1', 10:'10', 20:'20', 50:'50'}"
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="12">
+ <el-form-item label="鏃堕棿鑼冨洿">
+ <el-date-picker
+ v-model="generateForm.dateRange"
+ type="daterange"
+ range-separator="鑷�"
+ start-placeholder="寮�濮嬫棩鏈�"
+ end-placeholder="缁撴潫鏃ユ湡"
+ value-format="yyyy-MM-dd"
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+
+ <el-form-item label="琛ュ厖淇℃伅">
+ <el-input
+ v-model="generateForm.additionalInfo"
+ type="textarea"
+ :rows="2"
+ placeholder="濡傦細浜у搧浠ラ噾灞為浂閮ㄤ欢涓轰富锛屽鎴烽泦涓湪鍗庝笢鍦板尯"
+ />
+ </el-form-item>
+
+ <el-form-item>
+ <el-button type="primary" @click="handleGenerate" :loading="generating">
+ 寮�濮嬬敓鎴�
+ </el-button>
+ <el-button @click="handleCheckFirst" :loading="checking">
+ 鍏堟娴嬪啀鐢熸垚
+ </el-button>
+ </el-form-item>
+ </el-form>
+</el-card>
+```
+
+### 3. data 鏁版嵁
+
+```js
+data() {
+ return {
+ generateForm: {
+ modules: [],
+ industries: [],
+ countRange: [3, 10],
+ dateRange: [],
+ additionalInfo: '',
+ },
+ generating: false,
+ generateResult: null,
+ }
+}
+```
+
+### 4. 鏂规硶
+
+```js
+methods: {
+ // 鐩存帴鐢熸垚锛堝唴閮ㄨ嚜鍔ㄦ娴嬶級
+ async handleGenerate() {
+ if (this.generateForm.modules.length === 0) {
+ this.$message.warning('璇疯嚦灏戦�夋嫨涓�涓ā鍧�')
+ return
+ }
+ this.generating = true
+ try {
+ const res = await request.post('/mock/generate', {
+ modules: this.generateForm.modules,
+ industries: this.generateForm.industries,
+ countMin: this.generateForm.countRange[0],
+ countMax: this.generateForm.countRange[1],
+ dateStart: this.generateForm.dateRange[0] || null,
+ dateEnd: this.generateForm.dateRange[1] || null,
+ additionalInfo: this.generateForm.additionalInfo,
+ })
+ if (res.code === 200) {
+ this.generateResult = res.data
+ this.$message.success(`鎴愬姛鐢熸垚 ${res.data.totalGenerated} 鏉℃暟鎹甡)
+ } else {
+ // 鍩虹鏁版嵁涓嶈冻
+ this.generateResult = res.data
+ this.checkResult = res.data.checkResult
+ this.$message.warning(res.msg)
+ }
+ } finally {
+ this.generating = false
+ }
+ },
+
+ // 鍏堟娴嬪啀鐢熸垚
+ async handleCheckFirst() {
+ await this.handleCheck()
+ if (this.checkResult && this.checkResult.passedItems === this.checkResult.totalItems) {
+ await this.handleGenerate()
+ } else {
+ this.$message.warning('璇峰厛琛ラ綈鍩虹鏁版嵁鍚庡啀鐢熸垚')
+ }
+ },
+}
+```
+
+## 娉ㄦ剰浜嬮」
+
+- `POST /mock/generate` 鍐呴儴浼氳嚜鍔ㄨ皟鐢ㄦ暟鎹娴嬶紝涓嶉渶瑕佹墜鍔ㄥ垎涓ゆ璧�
+- AI 鐢熸垚鐨勬暟鎹細灏介噺绗﹀悎琛屼笟鐗瑰緛锛屼絾寤鸿鐢熸垚鍚庢鏌ュ叧閿暟鎹殑鍚堢悊鎬�
+- 鐢熶骇妯″潡鍜屽簱瀛樻ā鍧椾緷璧栦骇鍝佹暟鎹紙Product/ProductModel锛夛紝璇风‘淇濆熀纭�鏁版嵁涓湁浜у搧
+- 閿�鍞彴璐︾殑鍚堝悓閲戦鐢变骇鍝佹槑缁嗚嚜鍔ㄨ绠楋紝AI 鍙渶鐢熸垚浜у搧琛岀殑鍗曚环鍜屾暟閲�
+- 浜х敓鐨勬暟鎹�氳繃鏈嶅姟灞傚垱寤猴紝浼氳嚜鍔ㄥ~鍏� tenantId/deptId/createUser 绛夊瓧娈�
diff --git a/docs/routing_operation_plan_person.md b/docs/routing_operation_plan_person.md
new file mode 100644
index 0000000..999d9a3
--- /dev/null
+++ b/docs/routing_operation_plan_person.md
@@ -0,0 +1,91 @@
+# 鐢熶骇宸ュ簭鏂板璁″垝浜哄憳鍜岃鍒掓墽琛屼汉鍛�
+
+## 娑夊強椤甸潰
+
+- 宸ヨ壓璺嚎宸ュ簭绠$悊椤甸潰锛坱echnology_routing_operation锛�
+
+## API
+
+娑夊強浠ヤ笅鎺ュ彛鐨勮姹�/鍝嶅簲鏂板瀛楁锛�
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| GET | /technologyRoutingOperation/page | 鍒嗛〉鏌ヨ |
+| GET | /technologyRoutingOperation/list | 鍒楄〃鏌ヨ |
+| GET | /technologyRoutingOperation/{id} | 璇︽儏 |
+| POST | /technologyRoutingOperation/add | 鏂板 |
+| PUT | /technologyRoutingOperation | 淇敼 |
+
+**鏂板鍝嶅簲/璇锋眰瀛楁锛�**
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|------|------|------|------|
+| planPerson | Long | 鍚� | 璁″垝浜哄憳ID |
+| executor | Long | 鍚� | 璁″垝鎵ц浜哄憳ID |
+| planPersonName | String | 鍚� | 璁″垝浜哄憳濮撳悕锛堝搷搴斿瓧娈碉紝鍚庣鑷姩鍏宠仈鏌ヨ锛� |
+| executorName | String | 鍚� | 璁″垝鎵ц浜哄憳濮撳悕锛堝搷搴斿瓧娈碉紝鍚庣鑷姩鍏宠仈鏌ヨ锛� |
+
+## 鍓嶇淇敼鐐�
+
+### 1. 琛ㄥ崟鏂板瀛楁
+
+鍦ㄥ伐搴忚〃鍗曚腑鏂板涓や釜浜哄憳閫夋嫨涓嬫媺妗嗭細
+
+```html
+<el-form-item label="璁″垝浜哄憳" prop="planPerson">
+ <el-select v-model="processForm.planPerson"
+ placeholder="璇烽�夋嫨璁″垝浜哄憳"
+ clearable
+ filterable
+ style="width: 100%">
+ <el-option v-for="item in employeeOptions"
+ :key="item.id"
+ :label="item.staffName"
+ :value="item.id" />
+ </el-select>
+</el-form-item>
+<el-form-item label="璁″垝鎵ц浜哄憳" prop="executor">
+ <el-select v-model="processForm.executor"
+ placeholder="璇烽�夋嫨璁″垝鎵ц浜哄憳"
+ clearable
+ filterable
+ style="width: 100%">
+ <el-option v-for="item in employeeOptions"
+ :key="item.id"
+ :label="item.staffName"
+ :value="item.id" />
+ </el-select>
+</el-form-item>
+```
+
+### 2. data 鏁版嵁
+
+```js
+data() {
+ return {
+ processForm: {
+ planPerson: null,
+ executor: null,
+ // ...鍏朵粬瀛楁
+ },
+ employeeOptions: [], // 鍛樺伐鍒楄〃锛岄渶浠庢帴鍙h幏鍙�
+ }
+}
+```
+
+### 3. 琛ㄦ牸鍒楁柊澧�
+
+鍒楄〃涓彲鐩存帴浣跨敤鍚庣杩斿洖鐨勫鍚嶅睍绀猴細
+
+```html
+<el-table-column label="璁″垝浜哄憳" prop="planPersonName" />
+<el-table-column label="璁″垝鎵ц浜哄憳" prop="executorName" />
+```
+
+## 娉ㄦ剰浜嬮」
+
+- `planPerson` 鍜� `executor` 瀛樺偍鐨勬槸鍛樺伐ID锛圠ong绫诲瀷锛夛紝鎻愪氦琛ㄥ崟鏃朵紶ID
+- 鍝嶅簲涓� `planPersonName` 鍜� `executorName` 鐢卞悗绔�氳繃 `staff_on_job` 琛ㄨ嚜鍔ㄥ叧鑱旀煡璇紝鍓嶇鏃犻渶棰濆杞崲
+- 鍛樺伐涓嬫媺鍒楄〃鏁版嵁闇�浠庡憳宸ユ帴鍙h幏鍙栵紙鍙傝�冪幇鏈� `employeeOptions` 鏁版嵁婧愶級
+- 鏁版嵁搴撹縼绉籗QL锛歚doc/20260615_add_plan_person_and_executor_to_routing_operation.sql`
+- 鐢熶骇璁㈠崟宸ュ簭锛坄production_order_routing_operation`锛夊悓姝ユ敮鎸佽繖涓や釜瀛楁
diff --git a/src/main/java/com/ruoyi/mock/controller/DataCheckController.java b/src/main/java/com/ruoyi/mock/controller/DataCheckController.java
new file mode 100644
index 0000000..e8c0b8d
--- /dev/null
+++ b/src/main/java/com/ruoyi/mock/controller/DataCheckController.java
@@ -0,0 +1,54 @@
+package com.ruoyi.mock.controller;
+
+import com.ruoyi.framework.web.domain.R;
+import com.ruoyi.mock.dto.DataCheckRequest;
+import com.ruoyi.mock.dto.DataGenerateRequest;
+import com.ruoyi.mock.service.DataCheckService;
+import com.ruoyi.mock.service.DataGenerateService;
+import com.ruoyi.mock.vo.DataCheckResult;
+import com.ruoyi.mock.vo.DataCheckResult.CheckItem;
+import com.ruoyi.mock.vo.DataGenerateResult;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/mock")
+@RequiredArgsConstructor
+@Tag(name = "鏁版嵁妯℃嫙")
+public class DataCheckController {
+
+ private final DataCheckService dataCheckService;
+ private final DataGenerateService dataGenerateService;
+
+ @PostMapping("/dataCheck")
+ @Operation(summary = "妫�娴嬪熀纭�鏁版嵁")
+ public R<DataCheckResult> dataCheck(@RequestBody DataCheckRequest request) {
+ DataCheckResult result = dataCheckService.check(request);
+ return R.ok(result);
+ }
+
+ @PostMapping("/generate")
+ @Operation(summary = "AI鐢熸垚妯℃嫙鏁版嵁")
+ public R<?> generate(@RequestBody DataGenerateRequest request) {
+ // 鍏堟牎楠屽熀纭�鏁版嵁
+ DataCheckRequest checkRequest = new DataCheckRequest();
+ checkRequest.setModules(request.getModules());
+ DataCheckResult checkResult = dataCheckService.check(checkRequest);
+
+ boolean allPassed = checkResult.getItems().stream().allMatch(CheckItem::isPassed);
+ if (!allPassed) {
+ DataGenerateResult failResult = new DataGenerateResult();
+ failResult.setStatus("CHECK_FAILED");
+ failResult.setCheckResult(checkResult);
+ return R.fail(failResult, "鍩虹鏁版嵁涓嶈冻锛岃鍏堣ˉ鍏呭悗鍐嶇敓鎴�");
+ }
+
+ DataGenerateResult result = dataGenerateService.generate(request);
+ return R.ok(result);
+ }
+}
diff --git a/src/main/java/com/ruoyi/mock/dto/DataCheckRequest.java b/src/main/java/com/ruoyi/mock/dto/DataCheckRequest.java
new file mode 100644
index 0000000..9e1d7db
--- /dev/null
+++ b/src/main/java/com/ruoyi/mock/dto/DataCheckRequest.java
@@ -0,0 +1,15 @@
+package com.ruoyi.mock.dto;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 鍩虹鏁版嵁妫�娴嬭姹�
+ */
+@Data
+public class DataCheckRequest {
+
+ /** 闇�瑕佹娴嬬殑妯″潡: sales / purchase / quality */
+ private List<String> modules;
+}
diff --git a/src/main/java/com/ruoyi/mock/dto/DataGenerateRequest.java b/src/main/java/com/ruoyi/mock/dto/DataGenerateRequest.java
new file mode 100644
index 0000000..9f9522c
--- /dev/null
+++ b/src/main/java/com/ruoyi/mock/dto/DataGenerateRequest.java
@@ -0,0 +1,33 @@
+package com.ruoyi.mock.dto;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * AI鏁版嵁鐢熸垚璇锋眰
+ */
+@Data
+public class DataGenerateRequest {
+
+ /** 瑕佺敓鎴愭暟鎹殑妯″潡: sales / purchase / quality / production / stock */
+ private List<String> modules;
+
+ /** 琛屼笟锛堝涓級锛屽: ["鏈烘鍒堕��", "椋熷搧鍔犲伐", "鐢靛瓙瑁呴厤"] */
+ private List<String> industries;
+
+ /** 姣忎釜瀹炰綋鐢熸垚鏁伴噺鐨勬渶灏忓�� */
+ private int countMin;
+
+ /** 姣忎釜瀹炰綋鐢熸垚鏁伴噺鐨勬渶澶у�� */
+ private int countMax;
+
+ /** 鏃堕棿鑼冨洿-寮�濮�, yyyy-MM-dd */
+ private String dateStart;
+
+ /** 鏃堕棿鑼冨洿-缁撴潫, yyyy-MM-dd */
+ private String dateEnd;
+
+ /** 鍏朵粬琛ュ厖鎻忚堪 */
+ private String additionalInfo;
+}
diff --git a/src/main/java/com/ruoyi/mock/prompt/MockDataPrompt.java b/src/main/java/com/ruoyi/mock/prompt/MockDataPrompt.java
new file mode 100644
index 0000000..e52ab8e
--- /dev/null
+++ b/src/main/java/com/ruoyi/mock/prompt/MockDataPrompt.java
@@ -0,0 +1,213 @@
+package com.ruoyi.mock.prompt;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 鍚勬ā鍧楃殑 AI Prompt 妯℃澘
+ */
+public final class MockDataPrompt {
+
+ private MockDataPrompt() {}
+
+ public static String buildSystemPrompt() {
+ return """
+ 浣犳槸涓�涓紒涓欵RP绯荤粺鐨勬暟鎹ā鎷熶笓瀹躲�備綘闇�瑕佹牴鎹敤鎴锋彁渚涚殑琛屼笟銆佹暟閲忋�佹椂闂磋寖鍥寸瓑淇℃伅锛�
+ 鐢熸垚绗﹀悎涓氬姟閫昏緫鐨勬ā鎷熸暟鎹��
+
+ 瑕佹眰:
+ 1. 杈撳嚭蹇呴』鏄函JSON鏁扮粍锛屼笉瑕佺敤markdown浠g爜鍧楀寘瑁癸紝涓嶈鏈変换浣曞叾浠栨枃瀛�
+ 2. 姣忎釜JSON瀵硅薄蹇呴』鍖呭惈 "entity" 瀛楁锛屽�间负瀹炰綋绫诲瀷鍚�
+ 3. 鏁版嵁鍐呭瑕佺鍚堟寚瀹氳涓氱殑鐗瑰緛锛堝叕鍙稿悕绉般�佷骇鍝佸悕绉般�佽仈绯讳汉绛夎鍍忚琛屼笟鐨勶級
+ 4. 鏃ユ湡瀛楁鍦ㄦ寚瀹氱殑鏃堕棿鑼冨洿鍐呴殢鏈哄垎甯�
+ 5. 閲戦銆佹暟閲忕瓑鏁板�煎瓧娈佃鍚堢悊
+ 6. 鍚屼竴妯″潡鍐呯殑瀹炰綋涔嬮棿瑕佹湁寮曠敤鍏崇郴锛堝閿�鍞彴璐﹀紩鐢ㄥ鎴峰悕绉帮級
+ 7. 鎵�鏈夊瓧绗︿覆瀛楁涓嶈浣跨敤emoji鎴栫壒娈妘nicode瀛楃
+ """;
+ }
+
+ public static String buildUserMessage(List<String> modules, List<String> industries,
+ int countMin, int countMax,
+ String dateStart, String dateEnd,
+ String additionalInfo,
+ List<Long> productIds,
+ List<Long> productModelIds) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("璇蜂负浠ヤ笅琛屼笟鐢熸垚妯℃嫙ERP涓氬姟鏁版嵁:\n");
+ sb.append("- 琛屼笟: ").append(String.join("銆�", industries)).append("\n");
+ sb.append("- 鏁版嵁鏉℃暟: 姣忕瀹炰綋 ").append(countMin).append("-").append(countMax).append(" 鏉n");
+ sb.append("- 鏃堕棿鑼冨洿: ").append(dateStart).append(" ~ ").append(dateEnd).append("\n");
+ if (additionalInfo != null && !additionalInfo.isBlank()) {
+ sb.append("- 琛ュ厖淇℃伅: ").append(additionalInfo).append("\n");
+ }
+ sb.append("\n");
+
+ sb.append("鍙敤鐨勪骇鍝両D鍒楄〃: ").append(productIds.stream().map(String::valueOf).collect(Collectors.joining(","))).append("\n");
+ sb.append("鍙敤鐨勪骇鍝佽鏍糏D鍒楄〃: ").append(productModelIds.stream().map(String::valueOf).collect(Collectors.joining(","))).append("\n");
+ sb.append("\n");
+
+ sb.append("闇�瑕佺敓鎴愮殑妯″潡: ").append(String.join("銆�", modules)).append("\n\n");
+
+ if (modules.contains("sales")) {
+ sb.append(buildSalesPrompt(countMin, countMax, dateStart, dateEnd));
+ }
+ if (modules.contains("purchase")) {
+ sb.append(buildPurchasePrompt(countMin, countMax, dateStart, dateEnd));
+ }
+ if (modules.contains("quality")) {
+ sb.append(buildQualityPrompt(countMin, countMax));
+ }
+ if (modules.contains("production")) {
+ sb.append(buildProductionPrompt(countMin, countMax, dateStart, dateEnd));
+ }
+ if (modules.contains("stock")) {
+ sb.append(buildStockPrompt(countMin, countMax));
+ }
+
+ sb.append("\n璇风洿鎺ヨ緭鍑篔SON鏁扮粍锛屼笉瑕佹湁浠讳綍鍏朵粬鍐呭:");
+ return sb.toString();
+ }
+
+ private static String buildSalesPrompt(int min, int max, String dateStart, String dateEnd) {
+ return """
+ 閿�鍞ā鍧� - 鎸変互涓嬫牸寮忕敓鎴�:
+ {
+ "entity": "customer",
+ "customerName": "XX绉戞妧鏈夐檺鍏徃",
+ "customerType": "浼佷笟瀹㈡埛",
+ "contactPerson": "寮犱笁",
+ "contactPhone": "13800138000",
+ "companyAddress": "XX鐪乆X甯俋X鍖篨X璺疿X鍙�",
+ "taxpayerIdentificationNumber": "91110108XXXXXXXXXX"
+ }
+ {
+ "entity": "salesLedger",
+ "customerName": "寮曠敤涓婇潰鐢熸垚鐨勫鎴峰悕绉�",
+ "salesContractNo": "XS-20260601-001",
+ "projectName": "XX椤圭洰",
+ "entryDate": "2026-06-01",
+ "salesman": "閿�鍞憳濮撳悕",
+ "contractAmount": 50000.00,
+ "paymentMethod": "鏈堢粨30澶�",
+ "executionDate": "2026-06-01",
+ "deliveryDate": "2026-07-01",
+ "type": 1,
+ "productData": [
+ {
+ "productId": 1,
+ "productModelId": 1,
+ "quantity": 100,
+ "taxInclusiveUnitPrice": 500.00,
+ "taxInclusiveTotalPrice": 50000.00,
+ "taxRate": 13.00,
+ "unit": "浠�",
+ "type": 1
+ }
+ ]
+ }
+ 瀹㈡埛鍚嶇О瑕佸儚鎸囧畾琛屼笟鐨勫叕鍙革紝鍚堝悓缂栧彿鏍煎紡XS-骞存湀鏃�-搴忓彿锛岄噾棰濆悎鐞嗐��
+ 鏃ユ湡鑼冨洿: %s ~ %s锛屾瘡绉嶅疄浣撶敓鎴�%d-%d鏉°��
+ """.formatted(dateStart, dateEnd, min, max);
+ }
+
+ private static String buildPurchasePrompt(int min, int max, String dateStart, String dateEnd) {
+ return """
+ 閲囪喘妯″潡 - 鎸変互涓嬫牸寮忕敓鎴�:
+ {
+ "entity": "supplier",
+ "supplierName": "XX鍘熸潗鏂欐湁闄愬叕鍙�",
+ "supplierType": "鍘熸潗鏂欎緵搴斿晢",
+ "contactUserName": "鏉庡洓",
+ "contactUserPhone": "13900139000",
+ "companyAddress": "XX鐪乆X甯俋X鍖篨X璺疿X鍙�",
+ "taxpayerIdentificationNum": "91110108XXXXXXXXXX",
+ "bankAccountName": "XX閾惰XX鏀",
+ "bankAccountNum": "622202XXXXXXXXXXXX",
+ "isWhite": 0
+ }
+ {
+ "entity": "purchaseLedger",
+ "supplierName": "寮曠敤涓婇潰鐢熸垚鐨勪緵搴斿晢鍚嶇О",
+ "purchaseContractNumber": "CG-20260601-001",
+ "projectName": "XX閲囪喘椤圭洰",
+ "entryDate": "2026-06-01",
+ "contractAmount": 30000.00,
+ "paymentMethod": "璐у埌浠樻",
+ "executionDate": "2026-06-01",
+ "productData": [
+ {
+ "productId": 1,
+ "productModelId": 1,
+ "quantity": 50,
+ "taxInclusiveUnitPrice": 600.00,
+ "taxInclusiveTotalPrice": 30000.00,
+ "taxRate": 13.00,
+ "unit": "浠�",
+ "type": 2
+ }
+ ]
+ }
+ 渚涘簲鍟嗗悕绉拌鍍忔寚瀹氳涓氱殑渚涘簲鍟嗭紝鍚堝悓缂栧彿鏍煎紡CG-骞存湀鏃�-搴忓彿銆�
+ 鏃ユ湡鑼冨洿: %s ~ %s锛屾瘡绉嶅疄浣撶敓鎴�%d-%d鏉°��
+ """.formatted(dateStart, dateEnd, min, max);
+ }
+
+ private static String buildQualityPrompt(int min, int max) {
+ return """
+ 璐ㄩ噺妯″潡 - 鎸変互涓嬫牸寮忕敓鎴�:
+ {
+ "entity": "qualityTestStandard",
+ "standardNo": "QTS-20260601-001",
+ "standardName": "XX浜у搧妫�楠屾爣鍑�",
+ "inspectType": 0,
+ "remark": "閫傜敤浜嶺X琛屼笟鐨勮川閲忔楠屾爣鍑�"
+ }
+ inspectType: 0=鍘熸潗鏂欐楠�, 1=杩囩▼妫�楠�, 2=鍑哄巶妫�楠屻�備笁绉嶇被鍨嬮兘瑕佽鐩栥��
+ {
+ "entity": "qualityTestStandardBinding",
+ "productId": 1,
+ "testStandardId": 1
+ }
+ 姣忕瀹炰綋鐢熸垚%d-%d鏉°��
+ """.formatted(min, max);
+ }
+
+ private static String buildProductionPrompt(int min, int max, String dateStart, String dateEnd) {
+ return """
+ 鐢熶骇妯″潡 - 鎸変互涓嬫牸寮忕敓鎴�:
+ {
+ "entity": "productionPlan",
+ "productModelId": 1,
+ "qtyRequired": 200,
+ "requiredDate": "2026-06-15",
+ "source": "閿�鍞�",
+ "promisedDeliveryDate": "2026-07-01",
+ "remark": "XX瀹㈡埛璁㈠崟闇�姹�"
+ }
+ {
+ "entity": "productionOrder",
+ "productModelId": 1,
+ "quantity": 200,
+ "planCompleteTime": "2026-06-30",
+ "remark": "鏍规嵁鐢熶骇璁″垝XX鐢熸垚"
+ }
+ productionPlan鐨剆ource鍙��"閿�鍞�"鎴�"鍐呴儴"銆�
+ 鏃ユ湡鑼冨洿: %s ~ %s锛屾瘡绉嶅疄浣撶敓鎴�%d-%d鏉°��
+ """.formatted(dateStart, dateEnd, min, max);
+ }
+
+ private static String buildStockPrompt(int min, int max) {
+ return """
+ 搴撳瓨妯″潡 - 鎸変互涓嬫牸寮忕敓鎴�:
+ {
+ "entity": "stockInventory",
+ "productModelId": 1,
+ "qualitity": 500,
+ "batchNo": "BATCH-20260601-001",
+ "warnNum": 50,
+ "remark": "瀹夊叏搴撳瓨"
+ }
+ 姣忕瀹炰綋鐢熸垚%d-%d鏉°�俠atchNo鏍煎紡BATCH-骞存湀鏃�-搴忓彿銆�
+ """.formatted(min, max);
+ }
+}
diff --git a/src/main/java/com/ruoyi/mock/service/DataCheckService.java b/src/main/java/com/ruoyi/mock/service/DataCheckService.java
new file mode 100644
index 0000000..ac3b652
--- /dev/null
+++ b/src/main/java/com/ruoyi/mock/service/DataCheckService.java
@@ -0,0 +1,15 @@
+package com.ruoyi.mock.service;
+
+import com.ruoyi.mock.dto.DataCheckRequest;
+import com.ruoyi.mock.vo.DataCheckResult;
+
+/**
+ * 鍩虹鏁版嵁妫�娴嬫湇鍔�
+ */
+public interface DataCheckService {
+
+ /**
+ * 妫�娴嬫寚瀹氭ā鍧楃殑鍩虹鏁版嵁鏄惁灏辩华
+ */
+ DataCheckResult check(DataCheckRequest request);
+}
diff --git a/src/main/java/com/ruoyi/mock/service/DataGenerateService.java b/src/main/java/com/ruoyi/mock/service/DataGenerateService.java
new file mode 100644
index 0000000..1b0b6b0
--- /dev/null
+++ b/src/main/java/com/ruoyi/mock/service/DataGenerateService.java
@@ -0,0 +1,15 @@
+package com.ruoyi.mock.service;
+
+import com.ruoyi.mock.dto.DataGenerateRequest;
+import com.ruoyi.mock.vo.DataGenerateResult;
+
+/**
+ * AI鏁版嵁鐢熸垚鏈嶅姟
+ */
+public interface DataGenerateService {
+
+ /**
+ * 鏍规嵁璇锋眰鍙傛暟璋冪敤AI鐢熸垚妯℃嫙鏁版嵁骞跺垱寤�
+ */
+ DataGenerateResult generate(DataGenerateRequest request);
+}
diff --git a/src/main/java/com/ruoyi/mock/service/impl/DataCheckServiceImpl.java b/src/main/java/com/ruoyi/mock/service/impl/DataCheckServiceImpl.java
new file mode 100644
index 0000000..870e20b
--- /dev/null
+++ b/src/main/java/com/ruoyi/mock/service/impl/DataCheckServiceImpl.java
@@ -0,0 +1,146 @@
+package com.ruoyi.mock.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.ruoyi.approve.pojo.ApprovalTemplate;
+import com.ruoyi.approve.mapper.ApprovalTemplateMapper;
+import com.ruoyi.basic.mapper.CustomerMapper;
+import com.ruoyi.basic.mapper.ProductMapper;
+import com.ruoyi.basic.mapper.SupplierManageMapper;
+import com.ruoyi.common.enums.TypeEnums;
+import com.ruoyi.mock.dto.DataCheckRequest;
+import com.ruoyi.mock.service.DataCheckService;
+import com.ruoyi.mock.vo.DataCheckResult;
+import com.ruoyi.mock.vo.DataCheckResult.CheckItem;
+import com.ruoyi.quality.mapper.QualityTestStandardBindingMapper;
+import com.ruoyi.quality.mapper.QualityTestStandardMapper;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+@Service
+@RequiredArgsConstructor
+public class DataCheckServiceImpl implements DataCheckService {
+
+ private final ProductMapper productMapper;
+ private final CustomerMapper customerMapper;
+ private final SupplierManageMapper supplierManageMapper;
+ private final ApprovalTemplateMapper approvalTemplateMapper;
+ private final QualityTestStandardMapper qualityTestStandardMapper;
+ private final QualityTestStandardBindingMapper qualityTestStandardBindingMapper;
+
+ @Override
+ public DataCheckResult check(DataCheckRequest request) {
+ List<String> modules = request.getModules();
+ if (modules == null) {
+ modules = List.of();
+ }
+
+ Map<String, CheckItem> itemMap = new LinkedHashMap<>();
+
+ if (!modules.isEmpty()) {
+ addProductCheck(itemMap);
+ }
+
+ for (String module : modules) {
+ switch (module) {
+ case "sales" -> addSalesChecks(itemMap);
+ case "purchase" -> addPurchaseChecks(itemMap);
+ case "quality" -> addQualityChecks(itemMap);
+ }
+ }
+
+ List<CheckItem> items = new ArrayList<>(itemMap.values());
+
+ DataCheckResult result = new DataCheckResult();
+ result.setItems(items);
+ result.setTotalItems(items.size());
+ result.setPassedItems((int) items.stream().filter(CheckItem::isPassed).count());
+ return result;
+ }
+
+ private void addProductCheck(Map<String, CheckItem> itemMap) {
+ String key = "common:浜у搧鏁版嵁";
+ if (itemMap.containsKey(key)) {
+ return;
+ }
+ long count = productMapper.selectCount(null);
+ itemMap.put(key, buildItem("common", "浜у搧鏁版嵁", 1, (int) count,
+ "缂哄皯浜у搧鏁版嵁锛岃鍏堝湪銆愬熀纭�鏁版嵁-浜у搧绠$悊銆戜腑娣诲姞鑷冲皯1鏉′骇鍝�"));
+ }
+
+ private void addSalesChecks(Map<String, CheckItem> itemMap) {
+ String customerKey = "sales:瀹㈡埛鏁版嵁";
+ if (!itemMap.containsKey(customerKey)) {
+ long count = customerMapper.selectCount(null);
+ itemMap.put(customerKey, buildItem("sales", "瀹㈡埛鏁版嵁", 1, (int) count,
+ "缂哄皯瀹㈡埛鏁版嵁锛岃鍏堝湪銆愬熀纭�鏁版嵁-瀹㈡埛绠$悊銆戜腑娣诲姞鑷冲皯1鏉″鎴�"));
+ }
+
+ String quotationKey = "sales:鎶ヤ环瀹℃壒妯℃澘";
+ if (!itemMap.containsKey(quotationKey)) {
+ long count = approvalTemplateMapper.selectCount(
+ new LambdaQueryWrapper<ApprovalTemplate>()
+ .eq(ApprovalTemplate::getBusinessType, TypeEnums.QUOTATION_APPROVAL.getCode()));
+ itemMap.put(quotationKey, buildItem("sales", "鎶ヤ环瀹℃壒妯℃澘", 1, (int) count,
+ "缂哄皯鎶ヤ环瀹℃壒妯℃澘锛岃鍏堝湪銆愮郴缁熺鐞�-瀹℃壒妯℃澘銆戜腑鍒涘缓鎶ヤ环瀹℃壒妯℃澘"));
+ }
+
+ String shippingKey = "sales:鍙戣揣瀹℃壒妯℃澘";
+ if (!itemMap.containsKey(shippingKey)) {
+ long count = approvalTemplateMapper.selectCount(
+ new LambdaQueryWrapper<ApprovalTemplate>()
+ .eq(ApprovalTemplate::getBusinessType, TypeEnums.SHIPPING_APPROVAL.getCode()));
+ itemMap.put(shippingKey, buildItem("sales", "鍙戣揣瀹℃壒妯℃澘", 1, (int) count,
+ "缂哄皯鍙戣揣瀹℃壒妯℃澘锛岃鍏堝湪銆愮郴缁熺鐞�-瀹℃壒妯℃澘銆戜腑鍒涘缓鍙戣揣瀹℃壒妯℃澘"));
+ }
+ }
+
+ private void addPurchaseChecks(Map<String, CheckItem> itemMap) {
+ String supplierKey = "purchase:渚涘簲鍟嗘暟鎹�";
+ if (!itemMap.containsKey(supplierKey)) {
+ long count = supplierManageMapper.selectCount(null);
+ itemMap.put(supplierKey, buildItem("purchase", "渚涘簲鍟嗘暟鎹�", 1, (int) count,
+ "缂哄皯渚涘簲鍟嗘暟鎹紝璇峰厛鍦ㄣ�愬熀纭�鏁版嵁-渚涘簲鍟嗙鐞嗐�戜腑娣诲姞鑷冲皯1鏉′緵搴斿晢"));
+ }
+
+ String tplKey = "purchase:閲囪喘瀹℃壒妯℃澘";
+ if (!itemMap.containsKey(tplKey)) {
+ long count = approvalTemplateMapper.selectCount(
+ new LambdaQueryWrapper<ApprovalTemplate>()
+ .eq(ApprovalTemplate::getBusinessType, TypeEnums.PURCHASE_APPROVAL.getCode()));
+ itemMap.put(tplKey, buildItem("purchase", "閲囪喘瀹℃壒妯℃澘", 1, (int) count,
+ "缂哄皯閲囪喘瀹℃壒妯℃澘锛岃鍏堝湪銆愮郴缁熺鐞�-瀹℃壒妯℃澘銆戜腑鍒涘缓閲囪喘瀹℃壒妯℃澘"));
+ }
+ }
+
+ private void addQualityChecks(Map<String, CheckItem> itemMap) {
+ String standardKey = "quality:妫�娴嬫爣鍑�";
+ if (!itemMap.containsKey(standardKey)) {
+ long count = qualityTestStandardMapper.selectCount(null);
+ itemMap.put(standardKey, buildItem("quality", "妫�娴嬫爣鍑�", 1, (int) count,
+ "缂哄皯妫�娴嬫爣鍑嗭紝璇峰厛鍦ㄣ�愯川閲忕鐞�-妫�娴嬫爣鍑嗐�戜腑鍒涘缓妫�娴嬫爣鍑�"));
+ }
+
+ String bindingKey = "quality:鎸囨爣缁戝畾";
+ if (!itemMap.containsKey(bindingKey)) {
+ long count = qualityTestStandardBindingMapper.selectCount(null);
+ itemMap.put(bindingKey, buildItem("quality", "鎸囨爣缁戝畾", 1, (int) count,
+ "缂哄皯鎸囨爣缁戝畾锛岃鍏堝湪銆愯川閲忕鐞�-妫�娴嬫爣鍑嗙粦瀹氥�戜腑灏嗘娴嬫爣鍑嗕笌浜у搧缁戝畾"));
+ }
+ }
+
+ private CheckItem buildItem(String module, String itemName, int minRequired, int currentCount, String failMessage) {
+ CheckItem item = new CheckItem();
+ item.setModule(module);
+ item.setItemName(itemName);
+ item.setMinRequired(minRequired);
+ item.setCurrentCount(currentCount);
+ item.setPassed(currentCount >= minRequired);
+ item.setMessage(item.isPassed() ? "閫氳繃" : failMessage);
+ return item;
+ }
+}
diff --git a/src/main/java/com/ruoyi/mock/service/impl/DataGenerateServiceImpl.java b/src/main/java/com/ruoyi/mock/service/impl/DataGenerateServiceImpl.java
new file mode 100644
index 0000000..9eacfec
--- /dev/null
+++ b/src/main/java/com/ruoyi/mock/service/impl/DataGenerateServiceImpl.java
@@ -0,0 +1,466 @@
+package com.ruoyi.mock.service.impl;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONArray;
+import com.alibaba.fastjson2.JSONObject;
+import com.ruoyi.ai.assistant.Assistant;
+import com.ruoyi.basic.mapper.ProductMapper;
+import com.ruoyi.basic.mapper.ProductModelMapper;
+import com.ruoyi.basic.pojo.Customer;
+import com.ruoyi.basic.pojo.SupplierManage;
+import com.ruoyi.basic.service.ICustomerService;
+import com.ruoyi.basic.service.ISupplierService;
+import com.ruoyi.mock.dto.DataGenerateRequest;
+import com.ruoyi.mock.prompt.MockDataPrompt;
+import com.ruoyi.mock.service.DataGenerateService;
+import com.ruoyi.mock.vo.DataGenerateResult;
+import com.ruoyi.mock.vo.DataGenerateResult.ModuleSummary;
+import com.ruoyi.production.pojo.ProductionOrder;
+import com.ruoyi.production.pojo.ProductionPlan;
+import com.ruoyi.production.service.ProductionOrderService;
+import com.ruoyi.production.service.ProductionPlanService;
+import com.ruoyi.purchase.dto.PurchaseLedgerDto;
+import com.ruoyi.purchase.service.IPurchaseLedgerService;
+import com.ruoyi.quality.pojo.QualityTestStandard;
+import com.ruoyi.quality.pojo.QualityTestStandardBinding;
+import com.ruoyi.quality.service.IQualityTestStandardService;
+import com.ruoyi.quality.service.QualityTestStandardBindingService;
+import com.ruoyi.sales.dto.SalesLedgerDto;
+import com.ruoyi.sales.pojo.SalesLedgerProduct;
+import com.ruoyi.sales.service.ISalesLedgerService;
+import com.ruoyi.stock.dto.StockInventoryDto;
+import com.ruoyi.stock.service.StockInventoryService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class DataGenerateServiceImpl implements DataGenerateService {
+
+ private final Assistant assistant;
+ private final ProductMapper productMapper;
+ private final ProductModelMapper productModelMapper;
+
+ private final ICustomerService customerService;
+ private final ISalesLedgerService salesLedgerService;
+ private final ISupplierService supplierService;
+ private final IPurchaseLedgerService purchaseLedgerService;
+ private final IQualityTestStandardService qualityTestStandardService;
+ private final QualityTestStandardBindingService qualityTestStandardBindingService;
+ private final ProductionPlanService productionPlanService;
+ private final ProductionOrderService productionOrderService;
+ private final StockInventoryService stockInventoryService;
+
+ @Override
+ public DataGenerateResult generate(DataGenerateRequest request) {
+ DataGenerateResult result = new DataGenerateResult();
+ List<ModuleSummary> summaries = new ArrayList<>();
+ List<String> errors = new ArrayList<>();
+ int totalGenerated = 0;
+
+ try {
+ List<Long> productIds = productMapper.selectList(null).stream()
+ .map(p -> p.getId()).collect(Collectors.toList());
+ List<Long> productModelIds = productModelMapper.selectList(null).stream()
+ .map(m -> m.getId()).collect(Collectors.toList());
+
+ String systemPrompt = MockDataPrompt.buildSystemPrompt();
+ String userMessage = MockDataPrompt.buildUserMessage(
+ request.getModules(), request.getIndustries(),
+ request.getCountMin(), request.getCountMax(),
+ request.getDateStart(), request.getDateEnd(),
+ request.getAdditionalInfo(),
+ productIds, productModelIds);
+
+ String fullPrompt = systemPrompt + "\n\n" + userMessage;
+ log.info("璋冪敤AI鐢熸垚妯℃嫙鏁版嵁, modules={}, industries={}", request.getModules(), request.getIndustries());
+ String llmResponse = assistant.chat(fullPrompt);
+ log.info("AI杩斿洖鏁版嵁闀垮害: {}", llmResponse != null ? llmResponse.length() : 0);
+
+ if (llmResponse == null || llmResponse.isBlank()) {
+ result.setStatus("FAILED");
+ errors.add("AI鏈繑鍥炰换浣曟暟鎹�");
+ result.setErrors(errors);
+ return result;
+ }
+
+ List<JSONObject> entities = parseJsonResponse(llmResponse);
+ if (entities.isEmpty()) {
+ result.setStatus("FAILED");
+ errors.add("鏃犳硶瑙f瀽AI杩斿洖鐨勬暟鎹�");
+ result.setErrors(errors);
+ return result;
+ }
+
+ Map<String, List<JSONObject>> grouped = entities.stream()
+ .collect(Collectors.groupingBy(e -> e.getString("entity")));
+
+ // 鍚嶇О鈫扞D 鏄犲皠琛紝鐢ㄤ簬鍒涘缓涓氬姟鍗曟嵁鏃跺洖濉閿�
+ Map<String, Long> customerNameToId = new HashMap<>();
+ Map<String, Long> supplierNameToId = new HashMap<>();
+
+ // Tier 0: 鍩虹鏁版嵁
+ if (grouped.containsKey("customer")) {
+ ModuleSummary s = createCustomers(grouped.get("customer"), customerNameToId);
+ summaries.add(s);
+ totalGenerated += s.getSuccessCount();
+ }
+ if (grouped.containsKey("supplier")) {
+ ModuleSummary s = createSuppliers(grouped.get("supplier"), supplierNameToId);
+ summaries.add(s);
+ totalGenerated += s.getSuccessCount();
+ }
+ if (grouped.containsKey("qualityTestStandard")) {
+ ModuleSummary s = createQualityStandards(grouped.get("qualityTestStandard"));
+ summaries.add(s);
+ totalGenerated += s.getSuccessCount();
+ }
+
+ // Tier 1: 涓氬姟鍗曟嵁
+ if (grouped.containsKey("salesLedger")) {
+ ModuleSummary s = createSalesLedgers(grouped.get("salesLedger"), customerNameToId);
+ summaries.add(s);
+ totalGenerated += s.getSuccessCount();
+ }
+ if (grouped.containsKey("purchaseLedger")) {
+ ModuleSummary s = createPurchaseLedgers(grouped.get("purchaseLedger"), supplierNameToId);
+ summaries.add(s);
+ totalGenerated += s.getSuccessCount();
+ }
+ if (grouped.containsKey("productionPlan")) {
+ ModuleSummary s = createProductionPlans(grouped.get("productionPlan"));
+ summaries.add(s);
+ totalGenerated += s.getSuccessCount();
+ }
+
+ // Tier 2
+ if (grouped.containsKey("productionOrder")) {
+ ModuleSummary s = createProductionOrders(grouped.get("productionOrder"));
+ summaries.add(s);
+ totalGenerated += s.getSuccessCount();
+ }
+ if (grouped.containsKey("qualityTestStandardBinding")) {
+ ModuleSummary s = createQualityBindings(grouped.get("qualityTestStandardBinding"));
+ summaries.add(s);
+ totalGenerated += s.getSuccessCount();
+ }
+ if (grouped.containsKey("stockInventory")) {
+ ModuleSummary s = createStockInventories(grouped.get("stockInventory"));
+ summaries.add(s);
+ totalGenerated += s.getSuccessCount();
+ }
+
+ result.setStatus(errors.isEmpty() ? "SUCCESS" : "PARTIAL");
+ result.setTotalGenerated(totalGenerated);
+ result.setModuleSummaries(summaries);
+ result.setErrors(errors);
+
+ } catch (Exception e) {
+ log.error("鏁版嵁鐢熸垚澶辫触", e);
+ result.setStatus("FAILED");
+ errors.add("鏁版嵁鐢熸垚寮傚父: " + e.getMessage());
+ result.setErrors(errors);
+ }
+
+ return result;
+ }
+
+ private List<JSONObject> parseJsonResponse(String response) {
+ try {
+ String trimmed = response.trim();
+ if (trimmed.startsWith("```")) {
+ int start = trimmed.indexOf("\n");
+ int end = trimmed.lastIndexOf("```");
+ if (start > 0 && end > start) {
+ trimmed = trimmed.substring(start + 1, end).trim();
+ }
+ }
+ return JSON.parseArray(trimmed, JSONObject.class);
+ } catch (Exception e) {
+ log.warn("JSON瑙f瀽澶辫触锛屽皾璇曟彁鍙栨暟缁勭墖娈�: {}", e.getMessage());
+ int start = response.indexOf('[');
+ int end = response.lastIndexOf(']');
+ if (start >= 0 && end > start) {
+ return JSON.parseArray(response.substring(start, end + 1), JSONObject.class);
+ }
+ return List.of();
+ }
+ }
+
+ // ---- Tier 0: 鍩虹鏁版嵁 ----
+
+ private ModuleSummary createCustomers(List<JSONObject> items, Map<String, Long> nameToId) {
+ int success = 0, fail = 0;
+ for (JSONObject item : items) {
+ try {
+ Customer c = new Customer();
+ c.setCustomerName(item.getString("customerName"));
+ c.setCustomerType(item.getString("customerType"));
+ c.setContactPerson(item.getString("contactPerson"));
+ c.setContactPhone(item.getString("contactPhone"));
+ c.setCompanyAddress(item.getString("companyAddress"));
+ c.setTaxpayerIdentificationNumber(item.getString("taxpayerIdentificationNumber"));
+ customerService.insertCustomer(c);
+ if (c.getId() != null) {
+ nameToId.put(c.getCustomerName(), c.getId());
+ }
+ success++;
+ } catch (Exception e) {
+ log.warn("鍒涘缓瀹㈡埛澶辫触: {}", e.getMessage());
+ fail++;
+ }
+ }
+ return summary("sales", "瀹㈡埛", items.size(), success, fail);
+ }
+
+ private ModuleSummary createSuppliers(List<JSONObject> items, Map<String, Long> nameToId) {
+ int success = 0, fail = 0;
+ for (JSONObject item : items) {
+ try {
+ SupplierManage s = new SupplierManage();
+ s.setSupplierName(item.getString("supplierName"));
+ s.setSupplierType(item.getString("supplierType"));
+ s.setContactUserName(item.getString("contactUserName"));
+ s.setContactUserPhone(item.getString("contactUserPhone"));
+ s.setCompanyAddress(item.getString("companyAddress"));
+ s.setTaxpayerIdentificationNum(item.getString("taxpayerIdentificationNum"));
+ s.setBankAccountName(item.getString("bankAccountName"));
+ s.setBankAccountNum(item.getString("bankAccountNum"));
+ if (item.containsKey("isWhite")) {
+ s.setIsWhite(item.getInteger("isWhite"));
+ }
+ supplierService.saveSupplier(s);
+ if (s.getId() != null) {
+ nameToId.put(s.getSupplierName(), s.getId());
+ }
+ success++;
+ } catch (Exception e) {
+ log.warn("鍒涘缓渚涘簲鍟嗗け璐�: {}", e.getMessage());
+ fail++;
+ }
+ }
+ return summary("purchase", "渚涘簲鍟�", items.size(), success, fail);
+ }
+
+ private ModuleSummary createQualityStandards(List<JSONObject> items) {
+ int success = 0, fail = 0;
+ for (JSONObject item : items) {
+ try {
+ QualityTestStandard std = new QualityTestStandard();
+ std.setStandardNo(item.getString("standardNo"));
+ std.setStandardName(item.getString("standardName"));
+ std.setInspectType(item.getInteger("inspectType"));
+ std.setRemark(item.getString("remark"));
+ qualityTestStandardService.save(std);
+ success++;
+ } catch (Exception e) {
+ log.warn("鍒涘缓妫�娴嬫爣鍑嗗け璐�: {}", e.getMessage());
+ fail++;
+ }
+ }
+ return summary("quality", "妫�娴嬫爣鍑�", items.size(), success, fail);
+ }
+
+ // ---- Tier 1: 涓氬姟鍗曟嵁 ----
+
+ private ModuleSummary createSalesLedgers(List<JSONObject> items, Map<String, Long> customerNameToId) {
+ int success = 0, fail = 0;
+ for (JSONObject item : items) {
+ try {
+ String customerName = item.getString("customerName");
+ Long customerId = customerNameToId.get(customerName);
+
+ SalesLedgerDto dto = new SalesLedgerDto();
+ dto.setCustomerId(customerId);
+ dto.setCustomerName(customerName);
+ dto.setProjectName(item.getString("projectName"));
+ dto.setSalesman(item.getString("salesman"));
+ dto.setPaymentMethod(item.getString("paymentMethod"));
+ dto.setType(item.getInteger("type"));
+ if (item.containsKey("entryDate")) {
+ dto.setEntryDate(java.sql.Date.valueOf(item.getString("entryDate")));
+ }
+ if (item.containsKey("executionDate")) {
+ dto.setExecutionDate(LocalDate.parse(item.getString("executionDate")));
+ }
+ if (item.containsKey("deliveryDate")) {
+ dto.setDeliveryDate(LocalDate.parse(item.getString("deliveryDate")));
+ }
+ JSONArray productData = item.getJSONArray("productData");
+ if (productData != null) {
+ List<SalesLedgerProduct> products = new ArrayList<>();
+ for (int i = 0; i < productData.size(); i++) {
+ JSONObject pd = productData.getJSONObject(i);
+ SalesLedgerProduct slp = new SalesLedgerProduct();
+ slp.setProductId(pd.getLong("productId"));
+ slp.setProductModelId(pd.getLong("productModelId"));
+ slp.setQuantity(pd.getBigDecimal("quantity"));
+ slp.setTaxInclusiveUnitPrice(pd.getBigDecimal("taxInclusiveUnitPrice"));
+ slp.setTaxInclusiveTotalPrice(pd.getBigDecimal("taxInclusiveTotalPrice"));
+ slp.setTaxRate(pd.getBigDecimal("taxRate"));
+ slp.setUnit(pd.getString("unit"));
+ slp.setType(pd.getInteger("type"));
+ products.add(slp);
+ }
+ dto.setProductData(products);
+ }
+ salesLedgerService.addOrUpdateSalesLedger(dto);
+ success++;
+ } catch (Exception e) {
+ log.warn("鍒涘缓閿�鍞彴璐﹀け璐�: {}", e.getMessage());
+ fail++;
+ }
+ }
+ return summary("sales", "閿�鍞彴璐�", items.size(), success, fail);
+ }
+
+ private ModuleSummary createPurchaseLedgers(List<JSONObject> items, Map<String, Long> supplierNameToId) {
+ int success = 0, fail = 0;
+ for (JSONObject item : items) {
+ try {
+ String supplierName = item.getString("supplierName");
+ Long supplierId = supplierNameToId.get(supplierName);
+
+ PurchaseLedgerDto dto = new PurchaseLedgerDto();
+ dto.setSupplierId(supplierId);
+ dto.setSupplierName(supplierName);
+ dto.setProjectName(item.getString("projectName"));
+ dto.setContractAmount(item.getBigDecimal("contractAmount"));
+ dto.setPaymentMethod(item.getString("paymentMethod"));
+ if (item.containsKey("entryDate")) {
+ dto.setEntryDate(java.sql.Date.valueOf(item.getString("entryDate")));
+ }
+ if (item.containsKey("executionDate")) {
+ dto.setExecutionDate(java.sql.Date.valueOf(item.getString("executionDate")));
+ }
+ JSONArray productData = item.getJSONArray("productData");
+ if (productData != null) {
+ List<SalesLedgerProduct> products = new ArrayList<>();
+ for (int i = 0; i < productData.size(); i++) {
+ JSONObject pd = productData.getJSONObject(i);
+ SalesLedgerProduct slp = new SalesLedgerProduct();
+ slp.setProductId(pd.getLong("productId"));
+ slp.setProductModelId(pd.getLong("productModelId"));
+ slp.setQuantity(pd.getBigDecimal("quantity"));
+ slp.setTaxInclusiveUnitPrice(pd.getBigDecimal("taxInclusiveUnitPrice"));
+ slp.setTaxInclusiveTotalPrice(pd.getBigDecimal("taxInclusiveTotalPrice"));
+ slp.setTaxRate(pd.getBigDecimal("taxRate"));
+ slp.setUnit(pd.getString("unit"));
+ slp.setType(2);
+ products.add(slp);
+ }
+ dto.setProductData(products);
+ }
+ purchaseLedgerService.addOrEditPurchase(dto);
+ success++;
+ } catch (Exception e) {
+ log.warn("鍒涘缓閲囪喘鍙拌处澶辫触: {}", e.getMessage());
+ fail++;
+ }
+ }
+ return summary("purchase", "閲囪喘鍙拌处", items.size(), success, fail);
+ }
+
+ private ModuleSummary createProductionPlans(List<JSONObject> items) {
+ int success = 0, fail = 0;
+ for (JSONObject item : items) {
+ try {
+ ProductionPlan plan = new ProductionPlan();
+ plan.setProductModelId(item.getLong("productModelId"));
+ plan.setQtyRequired(item.getBigDecimal("qtyRequired"));
+ plan.setSource(item.getString("source"));
+ plan.setRemark(item.getString("remark"));
+ if (item.containsKey("requiredDate")) {
+ plan.setRequiredDate(LocalDate.parse(item.getString("requiredDate")));
+ }
+ if (item.containsKey("promisedDeliveryDate")) {
+ plan.setPromisedDeliveryDate(LocalDate.parse(item.getString("promisedDeliveryDate")));
+ }
+ productionPlanService.save(plan);
+ success++;
+ } catch (Exception e) {
+ log.warn("鍒涘缓鐢熶骇璁″垝澶辫触: {}", e.getMessage());
+ fail++;
+ }
+ }
+ return summary("production", "鐢熶骇璁″垝", items.size(), success, fail);
+ }
+
+ // ---- Tier 2: 鍚庣画鍗曟嵁 ----
+
+ private ModuleSummary createProductionOrders(List<JSONObject> items) {
+ int success = 0, fail = 0;
+ for (JSONObject item : items) {
+ try {
+ ProductionOrder order = new ProductionOrder();
+ order.setProductModelId(item.getLong("productModelId"));
+ order.setQuantity(item.getBigDecimal("quantity"));
+ if (item.containsKey("planCompleteTime")) {
+ order.setPlanCompleteTime(LocalDate.parse(item.getString("planCompleteTime")));
+ }
+ productionOrderService.saveProductionOrder(order);
+ success++;
+ } catch (Exception e) {
+ log.warn("鍒涘缓鐢熶骇璁㈠崟澶辫触: {}", e.getMessage());
+ fail++;
+ }
+ }
+ return summary("production", "鐢熶骇璁㈠崟", items.size(), success, fail);
+ }
+
+ private ModuleSummary createQualityBindings(List<JSONObject> items) {
+ int success = 0, fail = 0;
+ for (JSONObject item : items) {
+ try {
+ QualityTestStandardBinding binding = new QualityTestStandardBinding();
+ binding.setProductId(item.getLong("productId"));
+ binding.setTestStandardId(item.getInteger("testStandardId"));
+ qualityTestStandardBindingService.add(List.of(binding));
+ success++;
+ } catch (Exception e) {
+ log.warn("鍒涘缓鎸囨爣缁戝畾澶辫触: {}", e.getMessage());
+ fail++;
+ }
+ }
+ return summary("quality", "鎸囨爣缁戝畾", items.size(), success, fail);
+ }
+
+ private ModuleSummary createStockInventories(List<JSONObject> items) {
+ int success = 0, fail = 0;
+ for (JSONObject item : items) {
+ try {
+ StockInventoryDto dto = new StockInventoryDto();
+ dto.setProductModelId(item.getLong("productModelId"));
+ dto.setQualitity(item.getBigDecimal("qualitity"));
+ dto.setBatchNo(item.getString("batchNo"));
+ dto.setWarnNum(item.getBigDecimal("warnNum"));
+ dto.setRemark(item.getString("remark"));
+ stockInventoryService.addstockInventory(dto);
+ success++;
+ } catch (Exception e) {
+ log.warn("鍒涘缓搴撳瓨璁板綍澶辫触: {}", e.getMessage());
+ fail++;
+ }
+ }
+ return summary("stock", "搴撳瓨璁板綍", items.size(), success, fail);
+ }
+
+ private ModuleSummary summary(String module, String entityName, int total, int success, int fail) {
+ ModuleSummary s = new ModuleSummary();
+ s.setModule(module);
+ s.setEntityName(entityName);
+ s.setGeneratedCount(total);
+ s.setSuccessCount(success);
+ s.setFailCount(fail);
+ return s;
+ }
+}
diff --git a/src/main/java/com/ruoyi/mock/vo/DataCheckResult.java b/src/main/java/com/ruoyi/mock/vo/DataCheckResult.java
new file mode 100644
index 0000000..ed48b72
--- /dev/null
+++ b/src/main/java/com/ruoyi/mock/vo/DataCheckResult.java
@@ -0,0 +1,36 @@
+package com.ruoyi.mock.vo;
+
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 鍩虹鏁版嵁妫�娴嬬粨鏋�
+ */
+@Data
+public class DataCheckResult {
+
+ /** 妫�娴嬮」鎬绘暟 */
+ private int totalItems;
+ /** 宸查�氳繃鐨勬娴嬮」鏁伴噺 */
+ private int passedItems;
+ /** 妫�娴嬫槑缁� */
+ private List<CheckItem> items = new ArrayList<>();
+
+ @Data
+ public static class CheckItem {
+ /** 妯″潡锛歴ales / purchase / quality / common */
+ private String module;
+ /** 妫�娴嬮」鍚嶇О */
+ private String itemName;
+ /** 鎵�闇�鏈�浣庢暟閲� */
+ private int minRequired;
+ /** 褰撳墠瀹為檯鏁伴噺 */
+ private int currentCount;
+ /** 鏄惁閫氳繃 */
+ private boolean passed;
+ /** 鎻愮ず淇℃伅 */
+ private String message;
+ }
+}
diff --git a/src/main/java/com/ruoyi/mock/vo/DataGenerateResult.java b/src/main/java/com/ruoyi/mock/vo/DataGenerateResult.java
new file mode 100644
index 0000000..e4487fc
--- /dev/null
+++ b/src/main/java/com/ruoyi/mock/vo/DataGenerateResult.java
@@ -0,0 +1,37 @@
+package com.ruoyi.mock.vo;
+
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * AI鏁版嵁鐢熸垚缁撴灉
+ */
+@Data
+public class DataGenerateResult {
+
+ /** 鐢熸垚鐘舵��: SUCCESS / PARTIAL / FAILED */
+ private String status;
+
+ /** 鎬昏鐢熸垚鏉℃暟 */
+ private int totalGenerated;
+
+ /** 鍚勬ā鍧楃敓鎴愭憳瑕� */
+ private List<ModuleSummary> moduleSummaries = new ArrayList<>();
+
+ /** 閿欒淇℃伅锛堝鏈夛級 */
+ private List<String> errors = new ArrayList<>();
+
+ /** 鍩虹鏁版嵁妫�娴嬬粨鏋滐紙鍓嶇疆鏍¢獙鏈�氳繃鏃惰繑鍥烇級 */
+ private DataCheckResult checkResult;
+
+ @Data
+ public static class ModuleSummary {
+ private String module;
+ private String entityName;
+ private int generatedCount;
+ private int successCount;
+ private int failCount;
+ }
+}
--
Gitblit v1.9.3