ai勘料
parent
e8610841e8
commit
6f30570604
|
|
@ -125,6 +125,11 @@
|
||||||
<artifactId>aspectjweaver</artifactId>
|
<artifactId>aspectjweaver</artifactId>
|
||||||
<version>1.9.7</version>
|
<version>1.9.7</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.volcengine</groupId>
|
||||||
|
<artifactId>volcengine-java-sdk-ark-runtime</artifactId>
|
||||||
|
<version>LATEST</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,520 @@
|
||||||
|
# 豆包垃圾分析接口文档
|
||||||
|
|
||||||
|
## 接口概述
|
||||||
|
|
||||||
|
本接口基于火山引擎豆包AI模型,提供垃圾车辆图片分析功能。通过上传多张垃圾车辆图片,AI会自动识别车棚状态、垃圾类型、轻物质和重物质占比等信息。
|
||||||
|
|
||||||
|
**接口路径:** `/douBao/analyzeGarbage`
|
||||||
|
**请求方式:** `POST`
|
||||||
|
**Content-Type:** `application/json`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 请求参数
|
||||||
|
|
||||||
|
### 请求体(GarbageAnalysisParam)
|
||||||
|
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
|
|--------|------|------|------|
|
||||||
|
| imageUrls | List<String> | 是 | 图片URL列表,支持多张图片,至少需要一张 |
|
||||||
|
| orderSn | String | 否 | 订单号,用于关联订单信息 |
|
||||||
|
| remark | String | 否 | 备注信息,可提供额外的分析上下文 |
|
||||||
|
|
||||||
|
### 参数说明
|
||||||
|
|
||||||
|
- **imageUrls**:图片URL列表,支持HTTP/HTTPS协议的图片链接。建议使用可公开访问的图片URL。
|
||||||
|
- **orderSn**:订单号,用于在分析结果中关联订单信息,便于后续查询和追踪。
|
||||||
|
- **remark**:备注信息,可以包含订单相关的额外信息,如装货地点、卸货地点等,有助于AI更准确地进行分析。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 响应结果
|
||||||
|
|
||||||
|
### 响应体(GarbageAnalysisResult)
|
||||||
|
|
||||||
|
| 字段名 | 类型 | 说明 |
|
||||||
|
|--------|------|------|
|
||||||
|
| isShedOpened | Boolean | 车棚是否完全打开。true表示已打开,false表示未打开 |
|
||||||
|
| details | String | 明细信息,车厢内包含的物品列表。格式:物品1(数量描述),物品2(数量描述),例如:石块(多),木材(少) |
|
||||||
|
| lightMaterialPercentage | BigDecimal | 轻物质占比(百分比,0-100) |
|
||||||
|
| heavyMaterialPercentage | BigDecimal | 重物质占比(百分比,0-100) |
|
||||||
|
| garbageType | String | 垃圾类型,可能的值:拆除垃圾、装修垃圾、抛货 |
|
||||||
|
| judgmentBasis | String | 判断依据,说明AI判断垃圾类型的原因 |
|
||||||
|
| analysisTime | Long | 分析时间戳(毫秒) |
|
||||||
|
| rawResponse | String | AI原始响应内容,用于调试和问题排查 |
|
||||||
|
| garbageTypes | List<GarbageTypeInfo> | 垃圾类型分析列表(保留字段,用于兼容) |
|
||||||
|
| totalWeight | BigDecimal | 总重量(单位:千克)(保留字段,用于兼容) |
|
||||||
|
|
||||||
|
### GarbageTypeInfo 对象结构(保留字段)
|
||||||
|
|
||||||
|
| 字段名 | 类型 | 说明 |
|
||||||
|
|--------|------|------|
|
||||||
|
| typeName | String | 垃圾类型名称 |
|
||||||
|
| category | String | 具体垃圾种类 |
|
||||||
|
| judgmentBasis | String | 判断依据 |
|
||||||
|
| percentage | BigDecimal | 占比(百分比,0-100) |
|
||||||
|
| weight | BigDecimal | 估算重量(单位:千克) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 请求示例
|
||||||
|
|
||||||
|
### 场景一:最小参数(仅图片URL)
|
||||||
|
|
||||||
|
**适用场景:** 快速测试,仅提供必要的图片URL
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"imageUrls": [
|
||||||
|
"https://example.com/garbage-truck-1.jpg"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**cURL 命令:**
|
||||||
|
```bash
|
||||||
|
curl -X POST "http://localhost:8080/douBao/analyzeGarbage" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"imageUrls": [
|
||||||
|
"https://example.com/garbage-truck-1.jpg"
|
||||||
|
]
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 场景二:单张图片 + 订单号
|
||||||
|
|
||||||
|
**适用场景:** 关联订单信息
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"imageUrls": [
|
||||||
|
"https://example.com/garbage-truck-1.jpg"
|
||||||
|
],
|
||||||
|
"orderSn": "ORD20240101001"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**cURL 命令:**
|
||||||
|
```bash
|
||||||
|
curl -X POST "http://localhost:8080/douBao/analyzeGarbage" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"imageUrls": [
|
||||||
|
"https://example.com/garbage-truck-1.jpg"
|
||||||
|
],
|
||||||
|
"orderSn": "ORD20240101001"
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 场景三:多张图片(推荐)
|
||||||
|
|
||||||
|
**适用场景:** 从不同角度拍摄,提高分析准确性
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"imageUrls": [
|
||||||
|
"https://example.com/garbage-truck-front.jpg",
|
||||||
|
"https://example.com/garbage-truck-side.jpg",
|
||||||
|
"https://example.com/garbage-truck-back.jpg"
|
||||||
|
],
|
||||||
|
"orderSn": "ORD20240101001",
|
||||||
|
"remark": "装货地点:南京市建邺区,卸货地点:南京市江宁区"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**cURL 命令:**
|
||||||
|
```bash
|
||||||
|
curl -X POST "http://localhost:8080/douBao/analyzeGarbage" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"imageUrls": [
|
||||||
|
"https://example.com/garbage-truck-front.jpg",
|
||||||
|
"https://example.com/garbage-truck-side.jpg",
|
||||||
|
"https://example.com/garbage-truck-back.jpg"
|
||||||
|
],
|
||||||
|
"orderSn": "ORD20240101001",
|
||||||
|
"remark": "装货地点:南京市建邺区,卸货地点:南京市江宁区"
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 场景四:完整参数示例
|
||||||
|
|
||||||
|
**适用场景:** 生产环境,包含所有可选信息
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"imageUrls": [
|
||||||
|
"https://cdn.example.com/orders/2024/01/01/truck-front-001.jpg",
|
||||||
|
"https://cdn.example.com/orders/2024/01/01/truck-side-001.jpg"
|
||||||
|
],
|
||||||
|
"orderSn": "ORD20240101001",
|
||||||
|
"remark": "订单编号:ORD20240101001;装货地点:南京市建邺区奥体中心;卸货地点:南京市江宁区垃圾处理厂;装货时间:2024-01-01 10:00:00"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**cURL 命令:**
|
||||||
|
```bash
|
||||||
|
curl -X POST "http://localhost:8080/douBao/analyzeGarbage" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"imageUrls": [
|
||||||
|
"https://cdn.example.com/orders/2024/01/01/truck-front-001.jpg",
|
||||||
|
"https://cdn.example.com/orders/2024/01/01/truck-side-001.jpg"
|
||||||
|
],
|
||||||
|
"orderSn": "ORD20240101001",
|
||||||
|
"remark": "订单编号:ORD20240101001;装货地点:南京市建邺区奥体中心;卸货地点:南京市江宁区垃圾处理厂;装货时间:2024-01-01 10:00:00"
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Java 示例
|
||||||
|
|
||||||
|
#### 方式一:使用链式调用(推荐)
|
||||||
|
|
||||||
|
```java
|
||||||
|
import com.njzscloud.supervisory.doubao.param.GarbageAnalysisParam;
|
||||||
|
import com.njzscloud.supervisory.doubao.result.GarbageAnalysisResult;
|
||||||
|
import com.njzscloud.common.core.utils.R;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
// 最小参数
|
||||||
|
GarbageAnalysisParam param1 = new GarbageAnalysisParam()
|
||||||
|
.setImageUrls(Arrays.asList("https://example.com/garbage-truck-1.jpg"));
|
||||||
|
|
||||||
|
// 完整参数
|
||||||
|
GarbageAnalysisParam param2 = new GarbageAnalysisParam()
|
||||||
|
.setImageUrls(Arrays.asList(
|
||||||
|
"https://example.com/garbage-truck-front.jpg",
|
||||||
|
"https://example.com/garbage-truck-side.jpg"
|
||||||
|
))
|
||||||
|
.setOrderSn("ORD20240101001")
|
||||||
|
.setRemark("装货地点:南京市建邺区,卸货地点:南京市江宁区");
|
||||||
|
|
||||||
|
R<GarbageAnalysisResult> result = douBaoController.analyzeGarbage(param2);
|
||||||
|
if (result.getCode() == 200) {
|
||||||
|
GarbageAnalysisResult data = result.getData();
|
||||||
|
System.out.println("车棚状态:" + (data.getIsShedOpened() ? "已打开" : "未打开"));
|
||||||
|
System.out.println("垃圾类型:" + data.getGarbageType());
|
||||||
|
System.out.println("轻物质占比:" + data.getLightMaterialPercentage() + "%");
|
||||||
|
System.out.println("重物质占比:" + data.getHeavyMaterialPercentage() + "%");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 方式二:传统方式
|
||||||
|
|
||||||
|
```java
|
||||||
|
GarbageAnalysisParam param = new GarbageAnalysisParam();
|
||||||
|
param.setImageUrls(Arrays.asList(
|
||||||
|
"https://example.com/garbage-truck-1.jpg",
|
||||||
|
"https://example.com/garbage-truck-2.jpg"
|
||||||
|
));
|
||||||
|
param.setOrderSn("ORD20240101001");
|
||||||
|
param.setRemark("装货地点:南京市建邺区,卸货地点:南京市江宁区");
|
||||||
|
|
||||||
|
R<GarbageAnalysisResult> result = douBaoController.analyzeGarbage(param);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### JavaScript/Axios 示例
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// 使用 fetch
|
||||||
|
fetch('http://localhost:8080/douBao/analyzeGarbage', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
imageUrls: [
|
||||||
|
'https://example.com/garbage-truck-1.jpg',
|
||||||
|
'https://example.com/garbage-truck-2.jpg'
|
||||||
|
],
|
||||||
|
orderSn: 'ORD20240101001',
|
||||||
|
remark: '装货地点:南京市建邺区,卸货地点:南京市江宁区'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.code === 200) {
|
||||||
|
console.log('分析成功:', data.data);
|
||||||
|
console.log('车棚状态:', data.data.isShedOpened ? '已打开' : '未打开');
|
||||||
|
console.log('垃圾类型:', data.data.garbageType);
|
||||||
|
} else {
|
||||||
|
console.error('分析失败:', data.msg);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => console.error('请求错误:', error));
|
||||||
|
|
||||||
|
// 使用 axios
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
const analyzeGarbage = async () => {
|
||||||
|
try {
|
||||||
|
const response = await axios.post('http://localhost:8080/douBao/analyzeGarbage', {
|
||||||
|
imageUrls: [
|
||||||
|
'https://example.com/garbage-truck-1.jpg',
|
||||||
|
'https://example.com/garbage-truck-2.jpg'
|
||||||
|
],
|
||||||
|
orderSn: 'ORD20240101001',
|
||||||
|
remark: '装货地点:南京市建邺区,卸货地点:南京市江宁区'
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.data.code === 200) {
|
||||||
|
console.log('分析结果:', response.data.data);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('请求失败:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Postman/Apifox 测试配置
|
||||||
|
|
||||||
|
#### 请求配置
|
||||||
|
- **请求方式:** `POST`
|
||||||
|
- **请求URL:** `http://localhost:8080/douBao/analyzeGarbage`
|
||||||
|
- **Headers:**
|
||||||
|
- `Content-Type: application/json`
|
||||||
|
|
||||||
|
#### Body(raw JSON)示例
|
||||||
|
|
||||||
|
**示例1:最小参数**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"imageUrls": [
|
||||||
|
"https://example.com/garbage-truck-1.jpg"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**示例2:完整参数**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"imageUrls": [
|
||||||
|
"https://example.com/garbage-truck-front.jpg",
|
||||||
|
"https://example.com/garbage-truck-side.jpg",
|
||||||
|
"https://example.com/garbage-truck-back.jpg"
|
||||||
|
],
|
||||||
|
"orderSn": "ORD20240101001",
|
||||||
|
"remark": "装货地点:南京市建邺区奥体中心;卸货地点:南京市江宁区垃圾处理厂"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Python 示例
|
||||||
|
|
||||||
|
```python
|
||||||
|
import requests
|
||||||
|
import json
|
||||||
|
|
||||||
|
url = "http://localhost:8080/douBao/analyzeGarbage"
|
||||||
|
|
||||||
|
# 最小参数
|
||||||
|
payload1 = {
|
||||||
|
"imageUrls": [
|
||||||
|
"https://example.com/garbage-truck-1.jpg"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
# 完整参数
|
||||||
|
payload2 = {
|
||||||
|
"imageUrls": [
|
||||||
|
"https://example.com/garbage-truck-front.jpg",
|
||||||
|
"https://example.com/garbage-truck-side.jpg"
|
||||||
|
],
|
||||||
|
"orderSn": "ORD20240101001",
|
||||||
|
"remark": "装货地点:南京市建邺区,卸货地点:南京市江宁区"
|
||||||
|
}
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.post(url, headers=headers, data=json.dumps(payload2))
|
||||||
|
result = response.json()
|
||||||
|
|
||||||
|
if result.get("code") == 200:
|
||||||
|
data = result.get("data")
|
||||||
|
print(f"车棚状态:{'已打开' if data.get('isShedOpened') else '未打开'}")
|
||||||
|
print(f"垃圾类型:{data.get('garbageType')}")
|
||||||
|
print(f"轻物质占比:{data.get('lightMaterialPercentage')}%")
|
||||||
|
print(f"重物质占比:{data.get('heavyMaterialPercentage')}%")
|
||||||
|
else:
|
||||||
|
print(f"分析失败:{result.get('msg')}")
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 测试图片URL说明
|
||||||
|
|
||||||
|
**注意:** 实际测试时,请替换为真实可访问的图片URL。图片URL需要满足以下条件:
|
||||||
|
|
||||||
|
1. **可公开访问**:图片URL必须可以通过HTTP/HTTPS协议直接访问
|
||||||
|
2. **支持格式**:建议使用 JPG、PNG 等常见图片格式
|
||||||
|
3. **图片质量**:建议图片清晰,能够清楚看到车辆和垃圾内容
|
||||||
|
4. **推荐来源**:
|
||||||
|
- OSS对象存储(如阿里云OSS、腾讯云COS等)
|
||||||
|
- CDN加速域名
|
||||||
|
- 公网可访问的图片服务器
|
||||||
|
|
||||||
|
**测试用占位图片(仅供参考,实际使用时请替换):**
|
||||||
|
```
|
||||||
|
https://via.placeholder.com/800x600.jpg?text=Garbage+Truck+Front
|
||||||
|
https://via.placeholder.com/800x600.jpg?text=Garbage+Truck+Side
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 本地开发环境测试
|
||||||
|
|
||||||
|
如果是在本地开发环境测试,请确保:
|
||||||
|
|
||||||
|
1. **服务已启动**:确保Spring Boot应用已启动
|
||||||
|
2. **端口正确**:默认端口通常是 `8080`,请根据实际配置调整
|
||||||
|
3. **跨域配置**:如果前端调用,可能需要配置CORS
|
||||||
|
4. **完整URL示例**:
|
||||||
|
- 本地:`http://localhost:8080/douBao/analyzeGarbage`
|
||||||
|
- 开发环境:`http://dev.example.com:8080/douBao/analyzeGarbage`
|
||||||
|
- 生产环境:`https://api.example.com/douBao/analyzeGarbage`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 响应示例
|
||||||
|
|
||||||
|
### 成功响应(车棚已打开)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "操作成功",
|
||||||
|
"data": {
|
||||||
|
"isShedOpened": true,
|
||||||
|
"details": "石块(多),木材(少)",
|
||||||
|
"lightMaterialPercentage": 10.00,
|
||||||
|
"heavyMaterialPercentage": 90.00,
|
||||||
|
"garbageType": "拆除垃圾",
|
||||||
|
"judgmentBasis": "车厢内主要为石块,这类垃圾多来自建筑物拆除等工程",
|
||||||
|
"analysisTime": 1704067200000,
|
||||||
|
"rawResponse": "明细:石块(多),木材(少);轻物质:10%;重物质:90%;垃圾类型:拆除垃圾;判断依据:车厢内主要为石块,这类垃圾多来自建筑物拆除等工程",
|
||||||
|
"garbageTypes": null,
|
||||||
|
"totalWeight": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 成功响应(车棚未打开)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "操作成功",
|
||||||
|
"data": {
|
||||||
|
"isShedOpened": false,
|
||||||
|
"details": "未打开车棚",
|
||||||
|
"lightMaterialPercentage": null,
|
||||||
|
"heavyMaterialPercentage": null,
|
||||||
|
"garbageType": null,
|
||||||
|
"judgmentBasis": null,
|
||||||
|
"analysisTime": 1704067200000,
|
||||||
|
"rawResponse": "未打开车棚",
|
||||||
|
"garbageTypes": null,
|
||||||
|
"totalWeight": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 错误响应
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 500,
|
||||||
|
"msg": "垃圾分析失败:垃圾分析参数不能为空,且必须包含至少一张图片URL",
|
||||||
|
"data": null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 错误码说明
|
||||||
|
|
||||||
|
| 错误码 | 说明 |
|
||||||
|
|--------|------|
|
||||||
|
| 200 | 请求成功 |
|
||||||
|
| 400 | 请求参数错误(如:图片URL列表为空) |
|
||||||
|
| 500 | 服务器内部错误(如:AI服务调用失败、解析失败等) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 业务逻辑说明
|
||||||
|
|
||||||
|
### 分析流程
|
||||||
|
|
||||||
|
1. **车棚状态检测**:AI首先判断车棚是否完全打开
|
||||||
|
- 如果车棚未完全打开,直接返回"未打开车棚",不进行后续分析
|
||||||
|
- 如果车棚已打开,继续执行垃圾分析
|
||||||
|
|
||||||
|
2. **垃圾识别**(仅当车棚已打开时):
|
||||||
|
- 识别车厢内包含的物品类型和数量
|
||||||
|
- 如果有多辆车,只分析画面中间的车辆
|
||||||
|
- 分析轻物质和重物质的占比(百分比,0-100,两者之和应为100%)
|
||||||
|
- 判断垃圾类型:拆除垃圾、装修垃圾或抛货
|
||||||
|
- 提供判断依据说明
|
||||||
|
|
||||||
|
### 垃圾类型说明
|
||||||
|
|
||||||
|
- **拆除垃圾**:主要来自建筑物拆除等工程,通常包含大量石块、混凝土块等重物质
|
||||||
|
- **装修垃圾**:主要来自室内装修,通常包含白色板材、泡沫、木棍等轻物质
|
||||||
|
- **抛货**:其他类型的垃圾
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. **图片要求**:
|
||||||
|
- 图片URL必须可公开访问
|
||||||
|
- 支持HTTP/HTTPS协议
|
||||||
|
- 建议图片清晰,能够清楚看到车辆和垃圾内容
|
||||||
|
- 支持多张图片,建议从不同角度拍摄
|
||||||
|
|
||||||
|
2. **性能考虑**:
|
||||||
|
- AI分析需要一定时间,建议设置合理的超时时间
|
||||||
|
- 图片数量越多,分析时间可能越长
|
||||||
|
|
||||||
|
3. **数据准确性**:
|
||||||
|
- AI分析结果仅供参考,建议结合人工审核
|
||||||
|
- 如果分析结果不准确,可以通过 `rawResponse` 字段查看AI原始响应进行排查
|
||||||
|
|
||||||
|
4. **字段说明**:
|
||||||
|
- `garbageTypes` 和 `totalWeight` 为保留字段,当前版本可能为null
|
||||||
|
- `isShedOpened` 为false时,其他分析字段(如 `garbageType`、`lightMaterialPercentage` 等)可能为null
|
||||||
|
|
||||||
|
5. **格式要求**:
|
||||||
|
- AI返回的格式为固定格式,解析失败时会记录警告日志
|
||||||
|
- 如果解析结果不完整,建议查看 `rawResponse` 字段获取原始响应
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 更新日志
|
||||||
|
|
||||||
|
| 版本 | 日期 | 更新内容 |
|
||||||
|
|------|------|----------|
|
||||||
|
| 1.0.0 | 2024-01-01 | 初始版本,支持垃圾车辆图片分析功能 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 联系方式
|
||||||
|
|
||||||
|
如有问题或建议,请联系开发团队。
|
||||||
|
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
package com.njzscloud.supervisory.doubao.config;
|
||||||
|
|
||||||
|
import com.volcengine.ark.runtime.service.ArkService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import okhttp3.ConnectionPool;
|
||||||
|
import okhttp3.Dispatcher;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 火山引擎豆包配置类
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@EnableConfigurationProperties(DouBaoProperties.class)
|
||||||
|
public class DouBaoConfiguration {
|
||||||
|
|
||||||
|
private final DouBaoProperties properties;
|
||||||
|
|
||||||
|
@Bean(destroyMethod = "shutdownExecutor")
|
||||||
|
@ConditionalOnMissingBean
|
||||||
|
public ArkService arkService() {
|
||||||
|
String apiKey = properties.getApiKey();
|
||||||
|
if (!StringUtils.hasText(apiKey)) {
|
||||||
|
throw new IllegalStateException("豆包API Key未配置,请设置环境变量 ARK_API_KEY 或在配置文件中设置 doubao.api-key");
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionPool connectionPool = new ConnectionPool(
|
||||||
|
properties.getConnectionPoolSize(),
|
||||||
|
properties.getConnectionKeepAliveSeconds(),
|
||||||
|
TimeUnit.SECONDS
|
||||||
|
);
|
||||||
|
Dispatcher dispatcher = new Dispatcher();
|
||||||
|
|
||||||
|
return ArkService.builder()
|
||||||
|
.dispatcher(dispatcher)
|
||||||
|
.connectionPool(connectionPool)
|
||||||
|
.baseUrl(properties.getBaseUrl())
|
||||||
|
.apiKey(apiKey)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
package com.njzscloud.supervisory.doubao.config;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.ToString;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 火山引擎豆包配置
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@ToString
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@ConfigurationProperties(prefix = "doubao")
|
||||||
|
public class DouBaoProperties {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API Key,优先从环境变量 ARK_API_KEY 获取,如果环境变量不存在则使用配置值
|
||||||
|
*/
|
||||||
|
private String apiKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基础URL,默认为 https://ark.cn-beijing.volces.com/api/v3
|
||||||
|
*/
|
||||||
|
private String baseUrl = "https://ark.cn-beijing.volces.com/api/v3";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模型ID,默认为 doubao-seed-1-6-vision-250815
|
||||||
|
*/
|
||||||
|
private String model = "doubao-seed-1-6-vision-250815";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 连接池大小,默认5
|
||||||
|
*/
|
||||||
|
private Integer connectionPoolSize = 5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 连接保持时间(秒),默认1
|
||||||
|
*/
|
||||||
|
private Long connectionKeepAliveSeconds = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取API Key,优先从环境变量获取
|
||||||
|
*/
|
||||||
|
public String getApiKey() {
|
||||||
|
String envApiKey = System.getenv("ARK_API_KEY");
|
||||||
|
return envApiKey != null ? envApiKey : apiKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
package com.njzscloud.supervisory.doubao.controller;
|
||||||
|
|
||||||
|
import com.njzscloud.common.core.utils.R;
|
||||||
|
import com.njzscloud.supervisory.doubao.param.GarbageAnalysisParam;
|
||||||
|
import com.njzscloud.supervisory.doubao.result.GarbageAnalysisResult;
|
||||||
|
import com.njzscloud.supervisory.doubao.sevice.DouBaoService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 豆包
|
||||||
|
* 使用豆包官方SDK的生产级实现
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/douBao")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class DouBaoController {
|
||||||
|
|
||||||
|
private final DouBaoService douBaoService;
|
||||||
|
|
||||||
|
@PostMapping("/analyzeGarbage")
|
||||||
|
public R<GarbageAnalysisResult> analyzeGarbage(@RequestBody GarbageAnalysisParam param) {
|
||||||
|
return R.success(douBaoService.analyzeGarbage(param));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
package com.njzscloud.supervisory.doubao.param;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.ToString;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 垃圾分析请求参数
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@ToString
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class GarbageAnalysisParam {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图片URL列表(支持多张图片)
|
||||||
|
*/
|
||||||
|
private List<String> imageUrls;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单号(可选)
|
||||||
|
*/
|
||||||
|
private String orderSn;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 备注信息(可选)
|
||||||
|
*/
|
||||||
|
private String remark;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,105 @@
|
||||||
|
package com.njzscloud.supervisory.doubao.result;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.ToString;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 垃圾分析结果
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@ToString
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class GarbageAnalysisResult {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 车棚是否完全打开
|
||||||
|
*/
|
||||||
|
private Boolean isShedOpened;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 明细:车厢内包含的物品列表,格式:物品1(数量描述),物品2(数量描述)
|
||||||
|
* 例如:石块(多),木材(少)
|
||||||
|
*/
|
||||||
|
private String details;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 轻物质占比(百分比,0-100)
|
||||||
|
*/
|
||||||
|
private BigDecimal lightMaterialPercentage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重物质占比(百分比,0-100)
|
||||||
|
*/
|
||||||
|
private BigDecimal heavyMaterialPercentage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 垃圾类型:拆除垃圾、装修垃圾、抛货
|
||||||
|
*/
|
||||||
|
private String garbageType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断依据:说明判断垃圾类型的原因
|
||||||
|
*/
|
||||||
|
private String judgmentBasis;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分析时间戳
|
||||||
|
*/
|
||||||
|
private Long analysisTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 原始响应内容
|
||||||
|
*/
|
||||||
|
private String rawResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 垃圾类型分析列表(保留用于兼容)
|
||||||
|
*/
|
||||||
|
private List<GarbageTypeInfo> garbageTypes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 总重量(单位:千克)(保留用于兼容)
|
||||||
|
*/
|
||||||
|
private BigDecimal totalWeight;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 垃圾类型信息(保留用于兼容)
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@ToString
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public static class GarbageTypeInfo {
|
||||||
|
/**
|
||||||
|
* 垃圾类型名称(如:可回收垃圾、有害垃圾、厨余垃圾、其他垃圾等)
|
||||||
|
*/
|
||||||
|
private String typeName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 具体垃圾种类(如:塑料瓶、废纸、电池等)
|
||||||
|
*/
|
||||||
|
private String category;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断依据(AI识别该垃圾类型的理由)
|
||||||
|
*/
|
||||||
|
private String judgmentBasis;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 占比(百分比,0-100)
|
||||||
|
*/
|
||||||
|
private BigDecimal percentage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 估算重量(单位:千克)
|
||||||
|
*/
|
||||||
|
private BigDecimal weight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
package com.njzscloud.supervisory.doubao.sevice;
|
||||||
|
|
||||||
|
import com.njzscloud.supervisory.doubao.param.GarbageAnalysisParam;
|
||||||
|
import com.njzscloud.supervisory.doubao.result.GarbageAnalysisResult;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 火山引擎豆包服务接口
|
||||||
|
*/
|
||||||
|
public interface DouBaoService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 垃圾分析 - 分析多张图片中的垃圾类型、占比和重量
|
||||||
|
*
|
||||||
|
* @param param 垃圾分析请求参数,包含多张图片URL
|
||||||
|
* @return 垃圾分析结果,包含垃圾类型、判断依据、占比、重量等信息
|
||||||
|
*/
|
||||||
|
GarbageAnalysisResult analyzeGarbage(GarbageAnalysisParam param);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,242 @@
|
||||||
|
package com.njzscloud.supervisory.doubao.sevice.impl;
|
||||||
|
|
||||||
|
import com.njzscloud.supervisory.doubao.config.DouBaoProperties;
|
||||||
|
import com.njzscloud.supervisory.doubao.param.GarbageAnalysisParam;
|
||||||
|
import com.njzscloud.supervisory.doubao.result.GarbageAnalysisResult;
|
||||||
|
import com.njzscloud.supervisory.doubao.sevice.DouBaoService;
|
||||||
|
import com.volcengine.ark.runtime.model.completion.chat.ChatCompletionContentPart;
|
||||||
|
import com.volcengine.ark.runtime.model.completion.chat.ChatCompletionRequest;
|
||||||
|
import com.volcengine.ark.runtime.model.completion.chat.ChatMessage;
|
||||||
|
import com.volcengine.ark.runtime.model.completion.chat.ChatMessageRole;
|
||||||
|
import com.volcengine.ark.runtime.service.ArkService;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 火山引擎豆包服务实现类
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class DouBaoServiceImpl implements DouBaoService {
|
||||||
|
|
||||||
|
private final ArkService arkService;
|
||||||
|
private final DouBaoProperties douBaoProperties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 垃圾分析 - 分析多张图片中的垃圾类型、占比和重量
|
||||||
|
*
|
||||||
|
* @param param 垃圾分析请求参数,包含多张图片URL
|
||||||
|
* @return 垃圾分析结果,包含垃圾类型、判断依据、占比、重量等信息
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public GarbageAnalysisResult analyzeGarbage(GarbageAnalysisParam param) {
|
||||||
|
try {
|
||||||
|
log.info("开始调用火山引擎豆包API进行垃圾分析,订单号:{},图片数量:{}",
|
||||||
|
param != null ? param.getOrderSn() : "未知",
|
||||||
|
param != null && param.getImageUrls() != null ? param.getImageUrls().size() : 0);
|
||||||
|
|
||||||
|
// 参数校验
|
||||||
|
if (param == null || CollectionUtils.isEmpty(param.getImageUrls())) {
|
||||||
|
throw new IllegalArgumentException("垃圾分析参数不能为空,且必须包含至少一张图片URL");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建消息列表
|
||||||
|
final List<ChatMessage> messages = new ArrayList<>();
|
||||||
|
final List<ChatCompletionContentPart> multiParts = new ArrayList<>();
|
||||||
|
|
||||||
|
// 添加所有图片URL
|
||||||
|
for (String imageUrl : param.getImageUrls()) {
|
||||||
|
if (StringUtils.hasText(imageUrl)) {
|
||||||
|
multiParts.add(ChatCompletionContentPart.builder()
|
||||||
|
.type("image_url")
|
||||||
|
.imageUrl(new ChatCompletionContentPart.ChatCompletionContentPartImageURL(imageUrl))
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加文本提示词,要求返回JSON格式
|
||||||
|
String textPrompt = buildGarbageAnalysisPrompt(param);
|
||||||
|
multiParts.add(ChatCompletionContentPart.builder()
|
||||||
|
.type("text")
|
||||||
|
.text(textPrompt)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
// 构建用户消息
|
||||||
|
final ChatMessage userMessage = ChatMessage.builder()
|
||||||
|
.role(ChatMessageRole.USER)
|
||||||
|
.multiContent(multiParts)
|
||||||
|
.build();
|
||||||
|
messages.add(userMessage);
|
||||||
|
|
||||||
|
// 构建请求
|
||||||
|
ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder()
|
||||||
|
.model(douBaoProperties.getModel())
|
||||||
|
.messages(messages)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// 调用API并获取响应
|
||||||
|
String response = arkService.createChatCompletion(chatCompletionRequest)
|
||||||
|
.getChoices()
|
||||||
|
.stream()
|
||||||
|
.map(choice -> String.valueOf(choice.getMessage().getContent()))
|
||||||
|
.collect(Collectors.joining("\n"));
|
||||||
|
|
||||||
|
log.info("火山引擎豆包API调用成功,原始响应内容:{}", response);
|
||||||
|
|
||||||
|
// 解析响应并转换为结果对象
|
||||||
|
GarbageAnalysisResult result = parseGarbageAnalysisResponse(response);
|
||||||
|
result.setRawResponse(response);
|
||||||
|
result.setAnalysisTime(System.currentTimeMillis());
|
||||||
|
|
||||||
|
log.info("垃圾分析完成,车棚状态:{},垃圾类型:{},轻物质占比:{}%,重物质占比:{}%",
|
||||||
|
result.getIsShedOpened() != null && result.getIsShedOpened() ? "已打开" : "未打开",
|
||||||
|
result.getGarbageType(),
|
||||||
|
result.getLightMaterialPercentage(),
|
||||||
|
result.getHeavyMaterialPercentage());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("调用火山引擎豆包API进行垃圾分析失败,订单号:{}",
|
||||||
|
param != null ? param.getOrderSn() : "未知", e);
|
||||||
|
throw new RuntimeException("垃圾分析失败:" + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建垃圾分析提示词
|
||||||
|
* 要求AI返回指定格式的文本
|
||||||
|
*/
|
||||||
|
private String buildGarbageAnalysisPrompt(GarbageAnalysisParam param) {
|
||||||
|
StringBuilder prompt = new StringBuilder();
|
||||||
|
prompt.append("请仔细分析这些图片中的垃圾内容。\n\n");
|
||||||
|
|
||||||
|
// 添加订单信息(如果有)
|
||||||
|
if (StringUtils.hasText(param.getOrderSn())) {
|
||||||
|
prompt.append("订单号:").append(param.getOrderSn()).append("\n");
|
||||||
|
}
|
||||||
|
if (StringUtils.hasText(param.getRemark())) {
|
||||||
|
prompt.append("备注:").append(param.getRemark()).append("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt.append("\n请按照以下要求进行分析:\n");
|
||||||
|
prompt.append("1. 首先判断图片中是否有垃圾运输车辆:\n");
|
||||||
|
prompt.append(" - 如果图片中没有车辆(直接是垃圾堆、垃圾场等场景),请直接跳到第3步分析垃圾内容\n");
|
||||||
|
prompt.append(" - 如果图片中有车辆,请继续第2步判断车棚状态\n");
|
||||||
|
prompt.append("2. 如果图片中有车辆,判断车棚是否完全打开:\n");
|
||||||
|
prompt.append(" - 如果车棚没有完全打开,无法看到车厢内的垃圾内容,直接回答:未打开车棚\n");
|
||||||
|
prompt.append(" - 如果车棚完全打开,可以看到车厢内的垃圾,请继续第3步分析\n");
|
||||||
|
prompt.append("3. 分析垃圾内容(适用于:无车辆的场景,或有车辆且车棚已打开的场景):\n");
|
||||||
|
prompt.append(" - 识别垃圾中包含哪几种物品(如果有多辆车,只分析画面中间的车辆或主要垃圾堆)\n");
|
||||||
|
prompt.append(" - 说明哪种物品占比最多\n");
|
||||||
|
prompt.append(" - 给出轻物质和重物质的大概占比(使用百分比数值,0-100)\n");
|
||||||
|
prompt.append(" - 判断垃圾类型:拆除垃圾、装修垃圾或抛货\n");
|
||||||
|
prompt.append(" - 说明判断垃圾类型的原因\n\n");
|
||||||
|
|
||||||
|
prompt.append("请严格按照以下格式返回结果,不要添加任何其他文字说明:\n");
|
||||||
|
prompt.append("如果图片中有车辆且车棚未打开,直接回答:未打开车棚\n");
|
||||||
|
prompt.append("如果图片中没有车辆,或者有车辆且车棚已打开,按照以下格式回答:\n");
|
||||||
|
prompt.append("明细:物品1(数量描述),物品2(数量描述);轻物质:x%;重物质:x%;垃圾类型:x;判断依据:x\n\n");
|
||||||
|
prompt.append("格式说明:\n");
|
||||||
|
prompt.append("- 明细:列出垃圾中包含的所有物品,用逗号分隔,每个物品后标注数量(多、少、较多、较少等)\n");
|
||||||
|
prompt.append("- 轻物质:轻物质的占比百分比(0-100)\n");
|
||||||
|
prompt.append("- 重物质:重物质的占比百分比(0-100),轻物质和重物质占比之和应为100%\n");
|
||||||
|
prompt.append("- 垃圾类型:拆除垃圾、装修垃圾或抛货\n");
|
||||||
|
prompt.append("- 判断依据:详细说明为什么判断为该垃圾类型\n\n");
|
||||||
|
prompt.append("示例:\n");
|
||||||
|
prompt.append("明细:石块(多);轻物质:0%;重物质:100%;垃圾类型:拆除垃圾;判断依据:主要为石块,这类垃圾多来自建筑物拆除等工程\n");
|
||||||
|
prompt.append("明细:白色板材(多),白色泡沫(少),木棍(少);轻物质:90%;重物质:10%;垃圾类型:装修垃圾;判断依据:主要是白色板材、泡沫等装修常见废弃物\n");
|
||||||
|
prompt.append("明细:纸箱(多),塑料瓶(少),泡沫箱(少);轻物质:95%;重物质:5%;垃圾类型:抛货;判断依据:主要是轻质包装材料,属于抛货类垃圾\n\n");
|
||||||
|
prompt.append("注意:必须严格按照上述格式返回,不要添加任何其他文字说明。");
|
||||||
|
|
||||||
|
return prompt.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析垃圾分析响应
|
||||||
|
* 从AI响应中提取信息并转换为结果对象
|
||||||
|
* 格式:明细:xx(x),xx(x);轻物质:x%;重物质:x%;垃圾类型:x;判断依据:x
|
||||||
|
* 或者:未打开车棚
|
||||||
|
*/
|
||||||
|
private GarbageAnalysisResult parseGarbageAnalysisResponse(String response) {
|
||||||
|
if (!StringUtils.hasText(response)) {
|
||||||
|
throw new RuntimeException("AI响应为空,无法解析");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
GarbageAnalysisResult result = new GarbageAnalysisResult();
|
||||||
|
String trimmed = response.trim();
|
||||||
|
|
||||||
|
// 检查是否是"未打开车棚"的情况
|
||||||
|
if (trimmed.contains("未打开车棚")) {
|
||||||
|
result.setIsShedOpened(false);
|
||||||
|
result.setDetails("未打开车棚");
|
||||||
|
result.setLightMaterialPercentage(BigDecimal.ZERO);
|
||||||
|
result.setHeavyMaterialPercentage(BigDecimal.ZERO);
|
||||||
|
result.setGarbageType("无");
|
||||||
|
result.setJudgmentBasis("车棚未完全打开,无法进行分析");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 车棚已打开,解析详细格式
|
||||||
|
result.setIsShedOpened(true);
|
||||||
|
|
||||||
|
// 提取明细
|
||||||
|
Pattern detailsPattern = Pattern.compile("明细[::]([^;]+)");
|
||||||
|
Matcher detailsMatcher = detailsPattern.matcher(trimmed);
|
||||||
|
if (detailsMatcher.find()) {
|
||||||
|
result.setDetails(detailsMatcher.group(1).trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取轻物质占比
|
||||||
|
Pattern lightPattern = Pattern.compile("轻物质[::](\\d+(?:\\.\\d+)?)%");
|
||||||
|
Matcher lightMatcher = lightPattern.matcher(trimmed);
|
||||||
|
if (lightMatcher.find()) {
|
||||||
|
result.setLightMaterialPercentage(new BigDecimal(lightMatcher.group(1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取重物质占比
|
||||||
|
Pattern heavyPattern = Pattern.compile("重物质[::](\\d+(?:\\.\\d+)?)%");
|
||||||
|
Matcher heavyMatcher = heavyPattern.matcher(trimmed);
|
||||||
|
if (heavyMatcher.find()) {
|
||||||
|
result.setHeavyMaterialPercentage(new BigDecimal(heavyMatcher.group(1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取垃圾类型
|
||||||
|
Pattern typePattern = Pattern.compile("垃圾类型[::]([^;]+)");
|
||||||
|
Matcher typeMatcher = typePattern.matcher(trimmed);
|
||||||
|
if (typeMatcher.find()) {
|
||||||
|
result.setGarbageType(typeMatcher.group(1).trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提取判断依据
|
||||||
|
Pattern basisPattern = Pattern.compile("判断依据[::](.+)");
|
||||||
|
Matcher basisMatcher = basisPattern.matcher(trimmed);
|
||||||
|
if (basisMatcher.find()) {
|
||||||
|
result.setJudgmentBasis(basisMatcher.group(1).trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证必要字段
|
||||||
|
if (result.getDetails() == null || result.getGarbageType() == null) {
|
||||||
|
log.warn("解析结果不完整,响应内容:{}", response);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("解析垃圾分析响应失败,响应内容:{}", response, e);
|
||||||
|
throw new RuntimeException("解析AI响应失败:" + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -80,6 +80,9 @@ wechat:
|
||||||
notify-url: http://115.29.236.92:8082/payment/wechat/notify
|
notify-url: http://115.29.236.92:8082/payment/wechat/notify
|
||||||
# 退款回调地址
|
# 退款回调地址
|
||||||
refund-notify-url: http://115.29.236.92:8082/payment/wechat/refundNotify
|
refund-notify-url: http://115.29.236.92:8082/payment/wechat/refundNotify
|
||||||
|
doubao:
|
||||||
|
api-name: api-key-20250411103646
|
||||||
|
api-key: e0fc97bb-2bb6-4525-a79b-1e3555026821
|
||||||
mqtt:
|
mqtt:
|
||||||
enabled: true
|
enabled: true
|
||||||
broker: tcp://139.224.54.144:1883
|
broker: tcp://139.224.54.144:1883
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,9 @@ wechat:
|
||||||
notify-url: http://139.224.32.69:80/api/payment/wechat/notify
|
notify-url: http://139.224.32.69:80/api/payment/wechat/notify
|
||||||
# 退款回调地址
|
# 退款回调地址
|
||||||
refund-notify-url: http://139.224.32.69:80/api/payment/wechat/refundNotify
|
refund-notify-url: http://139.224.32.69:80/api/payment/wechat/refundNotify
|
||||||
|
doubao:
|
||||||
|
api-name: api-key-20250411103646
|
||||||
|
api-key: e0fc97bb-2bb6-4525-a79b-1e3555026821
|
||||||
mqtt:
|
mqtt:
|
||||||
enabled: true
|
enabled: true
|
||||||
broker: tcp://127.0.0.1:1883
|
broker: tcp://127.0.0.1:1883
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue