This commit is contained in:
2025-10-09 19:45:14 +08:00
parent e6ced14040
commit 2a93522bcf
15 changed files with 2158 additions and 1 deletions

View File

@@ -0,0 +1,315 @@
# 线报批量发品功能说明
## 功能概述
线报批量发品功能允许用户通过输入框输入线报消息自动解析出商品信息然后批量发品到多个ERP账号并支持延迟自动上架。
## 主要特性
### 1. 智能解析
- 支持从线报消息中自动识别京东商品链接
- 支持多种链接格式item.jd.com、u.jd.com短链接等
- 自动提取SKUID并查询商品详情
- 获取商品名称、价格、图片、店铺等信息
### 2. 批量选择
- 可视化商品列表,支持全选/反选
- 展示商品图片、名称、价格、佣金等信息
- 灵活选择需要发品的商品
### 3. 多账号发品
- 支持同时选择多个ERP账号胡歌、刘强东等
- 每个商品会发送到所有选中的账号
- 自动生成商家编码,避免重复
### 4. 通用参数设置
- 支持统一设置:会员名、省市区、商品类型、行业类型、类目等
- 支持设置邮费、库存、成色、服务支持等
- 一次设置,应用到所有商品
### 5. 延迟队列上架
- 发品成功后自动加入延迟队列
- 可自定义延迟时间1-60秒
- 到时后自动调用上架接口
### 6. 进度跟踪
- 实时显示发品进度
- 展示每个商品在每个账号的发品状态
- 记录成功数、失败数、错误信息
### 7. 历史记录
- 保存所有批量发品任务记录
- 支持查看任务详情和发品明细
- 可追溯每个商品的发品结果
## 使用流程
### 第一步:输入线报消息
在输入框中粘贴线报消息,支持以下格式:
```
【京东】某某商品
https://item.jd.com/100012345678.html
原价999元
...
【京东】另一个商品
https://u.jd.com/xxxxx
到手价199元
...
```
点击"解析商品"按钮,系统会自动提取商品链接并查询详情。
### 第二步:选择商品
- 系统展示解析出的商品列表
- 勾选需要发品的商品(支持全选)
- 查看商品信息确认无误
- 点击"下一步"
### 第三步:设置参数
#### 3.1 基本设置
- **任务名称**(选填):为本次批量发品任务命名
- **延迟上架**设置发品成功后延迟多少秒自动上架默认3秒
#### 3.2 目标账号
- 选择一个或多个ERP账号支持多选
- 每个商品将发送到所有选中的账号
#### 3.3 通用参数
- **会员名**:闲鱼会员名(必填)
- **省市区**:发货地址代码(必填)
- **商品类型**:普通商品/已验货/验货宝等(必填)
- **行业类型**:手机/家电/数码等(必填)
- **类目ID**商品类目ID必填
- **邮费**:邮费金额(元,必填)
- **库存**:库存数量(必填)
- **成色**:全新/99新等选填
- **服务支持**:七天无理由退货等(选填)
点击"开始批量发品"提交任务。
### 第四步:查看进度
- 系统创建批量发品任务
- 实时展示发品进度条
- 显示每个商品在每个账号的发品状态
- 发品成功的商品会自动加入延迟队列等待上架
## 数据库表结构
### batch_publish_task批量发品任务表
| 字段 | 类型 | 说明 |
|------|------|------|
| id | bigint | 任务ID |
| task_name | varchar(200) | 任务名称 |
| original_message | text | 原始线报消息 |
| total_products | int | 解析出的商品数量 |
| selected_products | int | 选中的商品数量 |
| target_accounts | varchar(500) | 目标ERP账号JSON |
| status | int | 任务状态0待处理 1处理中 2已完成 3失败 |
| success_count | int | 成功发品数量 |
| fail_count | int | 失败发品数量 |
| common_params | text | 通用参数JSON |
| create_user_id | bigint | 创建人ID |
| create_user_name | varchar(100) | 创建人姓名 |
| create_time | datetime | 创建时间 |
| complete_time | datetime | 完成时间 |
### batch_publish_item批量发品明细表
| 字段 | 类型 | 说明 |
|------|------|------|
| id | bigint | 明细ID |
| task_id | bigint | 任务ID |
| skuid | varchar(100) | SKUID |
| product_name | varchar(500) | 商品名称 |
| target_account | varchar(100) | 目标ERP账号 |
| account_remark | varchar(100) | 账号备注名 |
| status | int | 发品状态0待发布 1发布中 2发布成功 3发布失败 4上架中 5已上架 6上架失败 |
| product_id | bigint | ERP商品ID |
| product_status | int | 商品状态 |
| outer_id | varchar(100) | 商家编码 |
| publish_price | bigint | 发品价格(分) |
| error_message | varchar(1000) | 失败原因 |
| publish_time | datetime | 上架时间 |
| delay_seconds | int | 延迟上架时间(秒) |
| create_time | datetime | 创建时间 |
## API接口
### 1. 解析线报消息
```
POST /jarvis/batchPublish/parse
Content-Type: application/json
{
"message": "线报消息内容"
}
返回:
{
"code": 200,
"msg": "操作成功",
"data": [
{
"skuid": "100012345678",
"productName": "商品名称",
"price": 199.0,
"productImage": "http://...",
"shopName": "店铺名称",
"shopId": "12345",
"commissionInfo": "10%"
}
]
}
```
### 2. 批量发品
```
POST /jarvis/batchPublish/publish
Content-Type: application/json
{
"taskName": "任务名称",
"originalMessage": "原始消息",
"products": [
{
"skuid": "100012345678",
"productName": "商品名称",
"price": 199.0
}
],
"targetAccounts": ["1016208368633221", "1206879680251333"],
"delaySeconds": 3,
"commonParams": {
"userName": "会员名",
"province": 110000,
"city": 110100,
"district": 110101,
"itemBizType": 2,
"spBizType": 3,
"channelCatId": "12345",
"expressFee": 0.0,
"stock": 1,
"stuffStatus": 100
}
}
返回:
{
"code": 200,
"msg": "任务已创建",
"data": 123 // 任务ID
}
```
### 3. 查询任务列表
```
GET /jarvis/batchPublish/task/list?pageNum=1&pageSize=10
返回:
{
"code": 200,
"msg": "查询成功",
"rows": [...],
"total": 10
}
```
### 4. 查询任务详情
```
GET /jarvis/batchPublish/task/{taskId}
返回任务详细信息
```
### 5. 查询任务明细
```
GET /jarvis/batchPublish/item/list/{taskId}
返回任务所有发品明细
```
## 状态说明
### 任务状态
- 0待处理
- 1处理中
- 2已完成
- 3失败
### 发品状态
- 0待发布
- 1发布中
- 2发布成功
- 3发布失败
- 4上架中
- 5已上架
- 6上架失败
## 技术实现
### 后端
- **解析工具**LineReportParser - 正则表达式提取链接和SKUID
- **Service**BatchPublishServiceImpl - 核心业务逻辑
- **异步任务**@Async注解 + CompletableFuture实现延迟队列
- **数据库**MyBatis + MySQL存储任务和明细
### 前端
- **框架**Vue 2 + Element UI
- **步骤条**el-steps实现4步向导
- **实时刷新**:定时器轮询任务状态
- **组件**:表格、表单、对话框等
## 注意事项
1. **线报消息格式**:尽量包含完整的京东商品链接,便于准确识别
2. **价格获取**价格从京东API实时查询可能与线报价格有差异
3. **账号限制**请确保ERP账号有足够的发品额度
4. **延迟上架**建议设置3-5秒延迟避免频繁操作
5. **参数设置**:通用参数会应用到所有商品,请仔细核对
6. **批量操作**:大批量发品时请分批进行,避免超时
## 常见问题
### Q1: 解析不到商品怎么办?
A: 确保线报消息中包含完整的京东商品链接https://item.jd.com/xxxxx.html
### Q2: 发品失败是什么原因?
A: 可能原因:账号额度不足、商品信息不完整、网络异常等,查看错误信息了解详情
### Q3: 可以同时发多少个商品?
A: 理论上无限制但建议每次不超过50个商品避免超时
### Q4: 延迟上架的作用是什么?
A: 避免频繁操作触发平台限制,给系统缓冲时间
### Q5: 如何查看历史记录?
A: 点击页面右上角的"历史记录"按钮,可以查看所有批量发品任务
## 未来优化方向
1. [ ] 集成实际的发品接口(目前为模拟)
2. [ ] 支持商品价格批量调整
3. [ ] 支持文案自动生成
4. [ ] 支持图片批量处理
5. [ ] 支持发品模板保存
6. [ ] 支持定时发品
7. [ ] 支持发品失败自动重试
8. [ ] 支持发品结果通知(钉钉/企微)
## 更新日志
### v1.0.0 (2025-01-10)
- ✅ 初始版本
- ✅ 实现线报消息解析
- ✅ 实现批量发品
- ✅ 实现延迟队列上架
- ✅ 实现多账号支持
- ✅ 实现历史记录查询

View File

@@ -0,0 +1,95 @@
package com.ruoyi.web.controller.jarvis;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.jarvis.domain.BatchPublishItem;
import com.ruoyi.jarvis.domain.BatchPublishTask;
import com.ruoyi.jarvis.domain.request.BatchPublishRequest;
import com.ruoyi.jarvis.domain.request.ParseLineReportRequest;
import com.ruoyi.jarvis.service.IBatchPublishService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
/**
* 批量发品Controller
*
* @author ruoyi
* @date 2025-01-10
*/
@RestController
@RequestMapping("/jarvis/batchPublish")
public class BatchPublishController extends BaseController
{
@Autowired
private IBatchPublishService batchPublishService;
/**
* 解析线报消息
*/
@PostMapping("/parse")
public AjaxResult parseLineReport(@RequestBody @Validated ParseLineReportRequest request)
{
try {
List<Map<String, Object>> products = batchPublishService.parseLineReport(request);
return AjaxResult.success(products);
} catch (Exception e) {
logger.error("解析线报消息失败", e);
return AjaxResult.error("解析失败: " + e.getMessage());
}
}
/**
* 批量发品
*/
@Log(title = "批量发品", businessType = BusinessType.INSERT)
@PostMapping("/publish")
public AjaxResult batchPublish(@RequestBody @Validated BatchPublishRequest request)
{
try {
Long taskId = batchPublishService.batchPublish(request);
return AjaxResult.success("任务已创建", taskId);
} catch (Exception e) {
logger.error("批量发品失败", e);
return AjaxResult.error("批量发品失败: " + e.getMessage());
}
}
/**
* 查询批量发品任务列表
*/
@GetMapping("/task/list")
public TableDataInfo listTasks(BatchPublishTask task)
{
startPage();
List<BatchPublishTask> list = batchPublishService.selectTaskList(task);
return getDataTable(list);
}
/**
* 查询批量发品任务详情
*/
@GetMapping("/task/{taskId}")
public AjaxResult getTask(@PathVariable("taskId") Long taskId)
{
BatchPublishTask task = batchPublishService.getTaskById(taskId);
return AjaxResult.success(task);
}
/**
* 查询批量发品明细列表
*/
@GetMapping("/item/list/{taskId}")
public AjaxResult listItems(@PathVariable("taskId") Long taskId)
{
List<BatchPublishItem> items = batchPublishService.getItemsByTaskId(taskId);
return AjaxResult.success(items);
}
}

View File

@@ -13,7 +13,7 @@ public enum ERPAccount {
// 胡歌1016208368633221 // 胡歌1016208368633221
ACCOUNT_HUGE("1016208368633221", "waLiRMgFcixLbcLjUSSwo370Hp1nBcBu","余生请多关照66","海尔胡歌"), ACCOUNT_HUGE("1016208368633221", "waLiRMgFcixLbcLjUSSwo370Hp1nBcBu","余生请多关照66","海尔胡歌"),
// 刘强东anotherApiKey // 刘强东anotherApiKey
ACCOUNT_LQD("1206879680251333", "HhJOQFdgqsrxn9m4Mz5V0AMtdUG6vTaT","tb8992720_2013","方案小号"); ACCOUNT_LQD("1206879680251333", "HhJOQFdgqsrxn9m4Mz5V0AMtdUG6vTaT","tb8992720_2013","方案小号");
private final String apiKey; private final String apiKey;
private final String apiKeySecret; private final String apiKeySecret;

View File

@@ -0,0 +1,206 @@
package com.ruoyi.jarvis.domain;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import java.util.Date;
/**
* 批量发品明细对象 batch_publish_item
*
* @author ruoyi
* @date 2025-01-10
*/
public class BatchPublishItem extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 明细ID */
private Long id;
/** 任务ID */
@Excel(name = "任务ID")
private Long taskId;
/** SKUID */
@Excel(name = "SKUID")
private String skuid;
/** 商品名称 */
@Excel(name = "商品名称")
private String productName;
/** 目标ERP账号 */
@Excel(name = "目标账号")
private String targetAccount;
/** 账号备注名 */
@Excel(name = "账号名称")
private String accountRemark;
/** 发品状态0待发布 1发布中 2发布成功 3发布失败 4上架中 5已上架 6上架失败 */
@Excel(name = "发品状态", readConverterExp = "0=待发布,1=发布中,2=发布成功,3=发布失败,4=上架中,5=已上架,6=上架失败")
private Integer status;
/** ERP商品ID发品成功后返回 */
@Excel(name = "商品ID")
private Long productId;
/** 商品状态(发品成功后返回) */
private Integer productStatus;
/** 商家编码(发品成功后返回) */
@Excel(name = "商家编码")
private String outerId;
/** 发品价格(分) */
@Excel(name = "发品价格")
private Long publishPrice;
/** 失败原因 */
@Excel(name = "失败原因")
private String errorMessage;
/** 上架时间 */
@Excel(name = "上架时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date publishTime;
/** 延迟上架时间(秒) */
private Integer delaySeconds;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getTaskId() {
return taskId;
}
public void setTaskId(Long taskId) {
this.taskId = taskId;
}
public String getSkuid() {
return skuid;
}
public void setSkuid(String skuid) {
this.skuid = skuid;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public String getTargetAccount() {
return targetAccount;
}
public void setTargetAccount(String targetAccount) {
this.targetAccount = targetAccount;
}
public String getAccountRemark() {
return accountRemark;
}
public void setAccountRemark(String accountRemark) {
this.accountRemark = accountRemark;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public Long getProductId() {
return productId;
}
public void setProductId(Long productId) {
this.productId = productId;
}
public Integer getProductStatus() {
return productStatus;
}
public void setProductStatus(Integer productStatus) {
this.productStatus = productStatus;
}
public String getOuterId() {
return outerId;
}
public void setOuterId(String outerId) {
this.outerId = outerId;
}
public Long getPublishPrice() {
return publishPrice;
}
public void setPublishPrice(Long publishPrice) {
this.publishPrice = publishPrice;
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
public Date getPublishTime() {
return publishTime;
}
public void setPublishTime(Date publishTime) {
this.publishTime = publishTime;
}
public Integer getDelaySeconds() {
return delaySeconds;
}
public void setDelaySeconds(Integer delaySeconds) {
this.delaySeconds = delaySeconds;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("taskId", getTaskId())
.append("skuid", getSkuid())
.append("productName", getProductName())
.append("targetAccount", getTargetAccount())
.append("accountRemark", getAccountRemark())
.append("status", getStatus())
.append("productId", getProductId())
.append("productStatus", getProductStatus())
.append("outerId", getOuterId())
.append("publishPrice", getPublishPrice())
.append("errorMessage", getErrorMessage())
.append("publishTime", getPublishTime())
.append("delaySeconds", getDelaySeconds())
.append("createTime", getCreateTime())
.toString();
}
}

View File

@@ -0,0 +1,193 @@
package com.ruoyi.jarvis.domain;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import java.util.Date;
/**
* 批量发品任务对象 batch_publish_task
*
* @author ruoyi
* @date 2025-01-10
*/
public class BatchPublishTask extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 任务ID */
private Long id;
/** 任务名称 */
@Excel(name = "任务名称")
private String taskName;
/** 原始线报消息 */
@Excel(name = "原始线报消息")
private String originalMessage;
/** 解析出的商品数量 */
@Excel(name = "解析商品数量")
private Integer totalProducts;
/** 选中的商品数量 */
@Excel(name = "选中商品数量")
private Integer selectedProducts;
/** 目标ERP账号JSON数组 */
@Excel(name = "目标账号")
private String targetAccounts;
/** 任务状态0待处理 1处理中 2已完成 3失败 */
@Excel(name = "任务状态", readConverterExp = "0=待处理,1=处理中,2=已完成,3=失败")
private Integer status;
/** 成功发品数量 */
@Excel(name = "成功数量")
private Integer successCount;
/** 失败发品数量 */
@Excel(name = "失败数量")
private Integer failCount;
/** 通用参数JSON */
private String commonParams;
/** 创建人ID */
private Long createUserId;
/** 创建人姓名 */
@Excel(name = "创建人")
private String createUserName;
/** 完成时间 */
@Excel(name = "完成时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date completeTime;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTaskName() {
return taskName;
}
public void setTaskName(String taskName) {
this.taskName = taskName;
}
public String getOriginalMessage() {
return originalMessage;
}
public void setOriginalMessage(String originalMessage) {
this.originalMessage = originalMessage;
}
public Integer getTotalProducts() {
return totalProducts;
}
public void setTotalProducts(Integer totalProducts) {
this.totalProducts = totalProducts;
}
public Integer getSelectedProducts() {
return selectedProducts;
}
public void setSelectedProducts(Integer selectedProducts) {
this.selectedProducts = selectedProducts;
}
public String getTargetAccounts() {
return targetAccounts;
}
public void setTargetAccounts(String targetAccounts) {
this.targetAccounts = targetAccounts;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public Integer getSuccessCount() {
return successCount;
}
public void setSuccessCount(Integer successCount) {
this.successCount = successCount;
}
public Integer getFailCount() {
return failCount;
}
public void setFailCount(Integer failCount) {
this.failCount = failCount;
}
public String getCommonParams() {
return commonParams;
}
public void setCommonParams(String commonParams) {
this.commonParams = commonParams;
}
public Long getCreateUserId() {
return createUserId;
}
public void setCreateUserId(Long createUserId) {
this.createUserId = createUserId;
}
public String getCreateUserName() {
return createUserName;
}
public void setCreateUserName(String createUserName) {
this.createUserName = createUserName;
}
public Date getCompleteTime() {
return completeTime;
}
public void setCompleteTime(Date completeTime) {
this.completeTime = completeTime;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("taskName", getTaskName())
.append("originalMessage", getOriginalMessage())
.append("totalProducts", getTotalProducts())
.append("selectedProducts", getSelectedProducts())
.append("targetAccounts", getTargetAccounts())
.append("status", getStatus())
.append("successCount", getSuccessCount())
.append("failCount", getFailCount())
.append("commonParams", getCommonParams())
.append("createUserId", getCreateUserId())
.append("createUserName", getCreateUserName())
.append("createTime", getCreateTime())
.append("completeTime", getCompleteTime())
.toString();
}
}

View File

@@ -0,0 +1,319 @@
package com.ruoyi.jarvis.domain.request;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.List;
/**
* 批量发品请求对象
*
* @author ruoyi
* @date 2025-01-10
*/
public class BatchPublishRequest {
/** 任务名称 */
private String taskName;
/** 原始线报消息 */
private String originalMessage;
/** 选中的商品列表 */
@NotEmpty(message = "商品列表不能为空")
private List<ProductItem> products;
/** 目标ERP账号列表 */
@NotEmpty(message = "目标账号不能为空")
private List<String> targetAccounts;
/** 通用参数 */
@NotNull(message = "通用参数不能为空")
private CommonParams commonParams;
/** 延迟上架时间默认3秒 */
private Integer delaySeconds = 3;
public static class ProductItem {
/** SKUID */
@NotBlank(message = "SKUID不能为空")
private String skuid;
/** 商品名称 */
private String productName;
/** 商品价格(元) */
private Double price;
/** 商品图片 */
private String productImage;
/** 店铺名称 */
private String shopName;
/** 店铺ID */
private String shopId;
/** 佣金比例 */
private String commissionInfo;
public String getSkuid() {
return skuid;
}
public void setSkuid(String skuid) {
this.skuid = skuid;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public String getProductImage() {
return productImage;
}
public void setProductImage(String productImage) {
this.productImage = productImage;
}
public String getShopName() {
return shopName;
}
public void setShopName(String shopName) {
this.shopName = shopName;
}
public String getShopId() {
return shopId;
}
public void setShopId(String shopId) {
this.shopId = shopId;
}
public String getCommissionInfo() {
return commissionInfo;
}
public void setCommissionInfo(String commissionInfo) {
this.commissionInfo = commissionInfo;
}
}
public static class CommonParams {
/** 会员名 */
@NotBlank(message = "会员名不能为空")
private String userName;
/** 省 */
@NotNull(message = "省不能为空")
private Integer province;
/** 市 */
@NotNull(message = "市不能为空")
private Integer city;
/** 区 */
@NotNull(message = "区不能为空")
private Integer district;
/** 商品类型 */
@NotNull(message = "商品类型不能为空")
private Integer itemBizType;
/** 行业类型 */
@NotNull(message = "行业类型不能为空")
private Integer spBizType;
/** 类目ID */
@NotBlank(message = "类目ID不能为空")
private String channelCatId;
/** 邮费(元) */
@NotNull(message = "邮费不能为空")
private Double expressFee;
/** 库存 */
@NotNull(message = "库存不能为空")
private Integer stock;
/** 成色 */
private Integer stuffStatus;
/** 服务支持(逗号分隔) */
private String serviceSupport;
/** 商品属性JSON */
private String channelPv;
/** 白底图链接 */
private String whiteImages;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Integer getProvince() {
return province;
}
public void setProvince(Integer province) {
this.province = province;
}
public Integer getCity() {
return city;
}
public void setCity(Integer city) {
this.city = city;
}
public Integer getDistrict() {
return district;
}
public void setDistrict(Integer district) {
this.district = district;
}
public Integer getItemBizType() {
return itemBizType;
}
public void setItemBizType(Integer itemBizType) {
this.itemBizType = itemBizType;
}
public Integer getSpBizType() {
return spBizType;
}
public void setSpBizType(Integer spBizType) {
this.spBizType = spBizType;
}
public String getChannelCatId() {
return channelCatId;
}
public void setChannelCatId(String channelCatId) {
this.channelCatId = channelCatId;
}
public Double getExpressFee() {
return expressFee;
}
public void setExpressFee(Double expressFee) {
this.expressFee = expressFee;
}
public Integer getStock() {
return stock;
}
public void setStock(Integer stock) {
this.stock = stock;
}
public Integer getStuffStatus() {
return stuffStatus;
}
public void setStuffStatus(Integer stuffStatus) {
this.stuffStatus = stuffStatus;
}
public String getServiceSupport() {
return serviceSupport;
}
public void setServiceSupport(String serviceSupport) {
this.serviceSupport = serviceSupport;
}
public String getChannelPv() {
return channelPv;
}
public void setChannelPv(String channelPv) {
this.channelPv = channelPv;
}
public String getWhiteImages() {
return whiteImages;
}
public void setWhiteImages(String whiteImages) {
this.whiteImages = whiteImages;
}
}
public String getTaskName() {
return taskName;
}
public void setTaskName(String taskName) {
this.taskName = taskName;
}
public String getOriginalMessage() {
return originalMessage;
}
public void setOriginalMessage(String originalMessage) {
this.originalMessage = originalMessage;
}
public List<ProductItem> getProducts() {
return products;
}
public void setProducts(List<ProductItem> products) {
this.products = products;
}
public List<String> getTargetAccounts() {
return targetAccounts;
}
public void setTargetAccounts(List<String> targetAccounts) {
this.targetAccounts = targetAccounts;
}
public CommonParams getCommonParams() {
return commonParams;
}
public void setCommonParams(CommonParams commonParams) {
this.commonParams = commonParams;
}
public Integer getDelaySeconds() {
return delaySeconds;
}
public void setDelaySeconds(Integer delaySeconds) {
this.delaySeconds = delaySeconds;
}
}

View File

@@ -0,0 +1,25 @@
package com.ruoyi.jarvis.domain.request;
import javax.validation.constraints.NotBlank;
/**
* 解析线报消息请求对象
*
* @author ruoyi
* @date 2025-01-10
*/
public class ParseLineReportRequest {
/** 线报消息内容 */
@NotBlank(message = "线报消息不能为空")
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}

View File

@@ -0,0 +1,70 @@
package com.ruoyi.jarvis.mapper;
import com.ruoyi.jarvis.domain.BatchPublishItem;
import java.util.List;
/**
* 批量发品明细Mapper接口
*
* @author ruoyi
* @date 2025-01-10
*/
public interface BatchPublishItemMapper
{
/**
* 查询批量发品明细
*
* @param id 批量发品明细主键
* @return 批量发品明细
*/
public BatchPublishItem selectBatchPublishItemById(Long id);
/**
* 查询批量发品明细列表
*
* @param batchPublishItem 批量发品明细
* @return 批量发品明细集合
*/
public List<BatchPublishItem> selectBatchPublishItemList(BatchPublishItem batchPublishItem);
/**
* 根据任务ID查询明细列表
*
* @param taskId 任务ID
* @return 批量发品明细集合
*/
public List<BatchPublishItem> selectBatchPublishItemByTaskId(Long taskId);
/**
* 新增批量发品明细
*
* @param batchPublishItem 批量发品明细
* @return 结果
*/
public int insertBatchPublishItem(BatchPublishItem batchPublishItem);
/**
* 修改批量发品明细
*
* @param batchPublishItem 批量发品明细
* @return 结果
*/
public int updateBatchPublishItem(BatchPublishItem batchPublishItem);
/**
* 删除批量发品明细
*
* @param id 批量发品明细主键
* @return 结果
*/
public int deleteBatchPublishItemById(Long id);
/**
* 批量新增批量发品明细
*
* @param items 批量发品明细列表
* @return 结果
*/
public int batchInsertBatchPublishItem(List<BatchPublishItem> items);
}

View File

@@ -0,0 +1,54 @@
package com.ruoyi.jarvis.mapper;
import com.ruoyi.jarvis.domain.BatchPublishTask;
import java.util.List;
/**
* 批量发品任务Mapper接口
*
* @author ruoyi
* @date 2025-01-10
*/
public interface BatchPublishTaskMapper
{
/**
* 查询批量发品任务
*
* @param id 批量发品任务主键
* @return 批量发品任务
*/
public BatchPublishTask selectBatchPublishTaskById(Long id);
/**
* 查询批量发品任务列表
*
* @param batchPublishTask 批量发品任务
* @return 批量发品任务集合
*/
public List<BatchPublishTask> selectBatchPublishTaskList(BatchPublishTask batchPublishTask);
/**
* 新增批量发品任务
*
* @param batchPublishTask 批量发品任务
* @return 结果
*/
public int insertBatchPublishTask(BatchPublishTask batchPublishTask);
/**
* 修改批量发品任务
*
* @param batchPublishTask 批量发品任务
* @return 结果
*/
public int updateBatchPublishTask(BatchPublishTask batchPublishTask);
/**
* 删除批量发品任务
*
* @param id 批量发品任务主键
* @return 结果
*/
public int deleteBatchPublishTaskById(Long id);
}

View File

@@ -0,0 +1,67 @@
package com.ruoyi.jarvis.service;
import com.ruoyi.jarvis.domain.BatchPublishTask;
import com.ruoyi.jarvis.domain.BatchPublishItem;
import com.ruoyi.jarvis.domain.request.BatchPublishRequest;
import com.ruoyi.jarvis.domain.request.ParseLineReportRequest;
import java.util.List;
import java.util.Map;
/**
* 批量发品Service接口
*
* @author ruoyi
* @date 2025-01-10
*/
public interface IBatchPublishService
{
/**
* 解析线报消息,提取商品列表
*
* @param request 解析请求
* @return 商品列表
*/
List<Map<String, Object>> parseLineReport(ParseLineReportRequest request);
/**
* 批量发品(支持多账号、多商品)
*
* @param request 批量发品请求
* @return 任务ID
*/
Long batchPublish(BatchPublishRequest request);
/**
* 查询批量发品任务
*
* @param taskId 任务ID
* @return 任务信息
*/
BatchPublishTask getTaskById(Long taskId);
/**
* 查询批量发品明细列表
*
* @param taskId 任务ID
* @return 明细列表
*/
List<BatchPublishItem> getItemsByTaskId(Long taskId);
/**
* 查询批量发品任务列表
*
* @param task 任务查询条件
* @return 任务列表
*/
List<BatchPublishTask> selectTaskList(BatchPublishTask task);
/**
* 延迟上架商品
*
* @param itemId 明细ID
* @param delaySeconds 延迟秒数
*/
void schedulePublish(Long itemId, Integer delaySeconds);
}

View File

@@ -0,0 +1,426 @@
package com.ruoyi.jarvis.service.impl;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.erp.request.ERPAccount;
import com.ruoyi.jarvis.domain.BatchPublishItem;
import com.ruoyi.jarvis.domain.BatchPublishTask;
import com.ruoyi.jarvis.domain.request.BatchPublishRequest;
import com.ruoyi.jarvis.domain.request.ParseLineReportRequest;
import com.ruoyi.jarvis.mapper.BatchPublishItemMapper;
import com.ruoyi.jarvis.mapper.BatchPublishTaskMapper;
import com.ruoyi.jarvis.service.IBatchPublishService;
import com.ruoyi.jarvis.service.IJDOrderService;
import com.ruoyi.jarvis.util.LineReportParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
/**
* 批量发品Service业务层处理
*
* @author ruoyi
* @date 2025-01-10
*/
@Service
public class BatchPublishServiceImpl implements IBatchPublishService
{
private static final Logger log = LoggerFactory.getLogger(BatchPublishServiceImpl.java);
@Autowired
private BatchPublishTaskMapper taskMapper;
@Autowired
private BatchPublishItemMapper itemMapper;
@Autowired
private IJDOrderService jdOrderService;
/**
* 解析线报消息,提取商品列表
*/
@Override
public List<Map<String, Object>> parseLineReport(ParseLineReportRequest request) {
String message = request.getMessage();
log.info("开始解析线报消息,消息长度: {}", message.length());
// 提取SKUID和URL
List<String> urls = LineReportParser.extractJdUrls(message);
List<String> skuids = LineReportParser.extractSkuids(message);
log.info("提取到 {} 个URL, {} 个SKUID", urls.size(), skuids.size());
// 查询商品详情
List<Map<String, Object>> products = new ArrayList<>();
Set<String> processedSkuids = new HashSet<>();
// 优先处理URL更准确
for (String url : urls) {
try {
Map<String, Object> productInfo = queryProductInfo(url);
if (productInfo != null) {
String skuid = (String) productInfo.get("skuid");
if (skuid != null && !processedSkuids.contains(skuid)) {
products.add(productInfo);
processedSkuids.add(skuid);
}
}
} catch (Exception e) {
log.error("查询商品信息失败URL: {}", url, e);
}
}
// 处理剩余的SKUID
for (String skuid : skuids) {
if (!processedSkuids.contains(skuid)) {
try {
Map<String, Object> productInfo = queryProductInfo(skuid);
if (productInfo != null) {
products.add(productInfo);
processedSkuids.add(skuid);
}
} catch (Exception e) {
log.error("查询商品信息失败SKUID: {}", skuid, e);
}
}
}
log.info("解析完成,共获取 {} 个商品信息", products.size());
return products;
}
/**
* 查询商品信息
*/
private Map<String, Object> queryProductInfo(String urlOrSkuid) {
try {
// 调用JDOrder服务的generatePromotionContent接口
Map<String, String> requestBody = new HashMap<>();
requestBody.put("promotionContent", urlOrSkuid);
String result = jdOrderService.generatePromotionContent(requestBody);
if (StringUtils.isEmpty(result)) {
return null;
}
// 解析返回结果
Object resultObj = JSON.parse(result);
if (resultObj instanceof List) {
List<?> list = (List<?>) resultObj;
if (!list.isEmpty() && list.get(0) instanceof Map) {
@SuppressWarnings("unchecked")
Map<String, Object> productMap = (Map<String, Object>) list.get(0);
return normalizeProductInfo(productMap);
}
} else if (resultObj instanceof Map) {
@SuppressWarnings("unchecked")
Map<String, Object> productMap = (Map<String, Object>) resultObj;
return normalizeProductInfo(productMap);
}
return null;
} catch (Exception e) {
log.error("查询商品信息异常", e);
return null;
}
}
/**
* 规范化商品信息
*/
private Map<String, Object> normalizeProductInfo(Map<String, Object> productMap) {
Map<String, Object> result = new HashMap<>();
// 提取基本信息
result.put("skuid", getValueFromMap(productMap, "spuid", "skuId", "sku"));
result.put("productName", getValueFromMap(productMap, "skuName", "title", "name"));
result.put("price", getValueFromMap(productMap, "price", "opPrice", "jdPrice"));
result.put("productImage", getFirstImage(productMap));
result.put("shopName", getValueFromMap(productMap, "shopName"));
result.put("shopId", getValueFromMap(productMap, "shopId"));
result.put("commissionInfo", getValueFromMap(productMap, "commissionShare", "commission"));
result.put("materialUrl", getValueFromMap(productMap, "materialUrl", "url"));
// 保留原始数据
result.put("_raw", productMap);
return result;
}
/**
* 从Map中获取第一个非空值
*/
private Object getValueFromMap(Map<String, Object> map, String... keys) {
for (String key : keys) {
Object value = map.get(key);
if (value != null && StringUtils.isNotEmpty(value.toString())) {
return value;
}
}
return null;
}
/**
* 获取第一张图片
*/
private String getFirstImage(Map<String, Object> map) {
Object images = map.get("images");
if (images instanceof List) {
List<?> imageList = (List<?>) images;
if (!imageList.isEmpty()) {
return imageList.get(0).toString();
}
}
Object mainImage = map.get("mainImage");
if (mainImage != null) {
return mainImage.toString();
}
return null;
}
/**
* 批量发品(支持多账号、多商品)
*/
@Override
@Transactional
public Long batchPublish(BatchPublishRequest request) {
log.info("开始批量发品任务,商品数: {}, 账号数: {}",
request.getProducts().size(), request.getTargetAccounts().size());
// 获取当前用户
SysUser currentUser = SecurityUtils.getLoginUser().getUser();
// 创建任务记录
BatchPublishTask task = new BatchPublishTask();
task.setTaskName(request.getTaskName());
task.setOriginalMessage(request.getOriginalMessage());
task.setTotalProducts(request.getProducts().size());
task.setSelectedProducts(request.getProducts().size());
task.setTargetAccounts(JSON.toJSONString(request.getTargetAccounts()));
task.setStatus(0); // 待处理
task.setSuccessCount(0);
task.setFailCount(0);
task.setCommonParams(JSON.toJSONString(request.getCommonParams()));
task.setCreateUserId(currentUser.getUserId());
task.setCreateUserName(currentUser.getUserName());
task.setCreateTime(new Date());
taskMapper.insertBatchPublishTask(task);
Long taskId = task.getId();
// 创建明细记录
List<BatchPublishItem> items = new ArrayList<>();
for (BatchPublishRequest.ProductItem product : request.getProducts()) {
for (String accountAppid : request.getTargetAccounts()) {
BatchPublishItem item = new BatchPublishItem();
item.setTaskId(taskId);
item.setSkuid(product.getSkuid());
item.setProductName(product.getProductName());
item.setTargetAccount(accountAppid);
item.setAccountRemark(getAccountRemark(accountAppid));
item.setStatus(0); // 待发布
item.setPublishPrice(product.getPrice() != null ? Math.round(product.getPrice() * 100) : null);
item.setDelaySeconds(request.getDelaySeconds());
item.setCreateTime(new Date());
items.add(item);
}
}
if (!items.isEmpty()) {
itemMapper.batchInsertBatchPublishItem(items);
}
// 异步执行发品任务
asyncBatchPublish(taskId, items, request);
return taskId;
}
/**
* 获取账号备注名
*/
private String getAccountRemark(String appid) {
for (ERPAccount account : ERPAccount.values()) {
if (account.getApiKey().equals(appid)) {
return account.getRemark();
}
}
return appid;
}
/**
* 异步执行批量发品
*/
@Async
public void asyncBatchPublish(Long taskId, List<BatchPublishItem> items, BatchPublishRequest request) {
log.info("开始异步执行批量发品任务: {}", taskId);
// 更新任务状态为处理中
BatchPublishTask task = new BatchPublishTask();
task.setId(taskId);
task.setStatus(1); // 处理中
taskMapper.updateBatchPublishTask(task);
int successCount = 0;
int failCount = 0;
// 逐个发品
for (BatchPublishItem item : items) {
try {
// 更新明细状态为发布中
item.setStatus(1);
itemMapper.updateBatchPublishItem(item);
// 调用发品接口
boolean success = publishProduct(item, request);
if (success) {
successCount++;
// 发品成功,调度延迟上架
schedulePublish(item.getId(), item.getDelaySeconds());
} else {
failCount++;
}
} catch (Exception e) {
log.error("发品失败,商品: {}, 账号: {}", item.getProductName(), item.getAccountRemark(), e);
item.setStatus(3); // 发布失败
item.setErrorMessage("发品异常: " + e.getMessage());
itemMapper.updateBatchPublishItem(item);
failCount++;
}
}
// 更新任务状态为已完成
task.setStatus(2); // 已完成
task.setSuccessCount(successCount);
task.setFailCount(failCount);
task.setCompleteTime(new Date());
taskMapper.updateBatchPublishTask(task);
log.info("批量发品任务完成: {}, 成功: {}, 失败: {}", taskId, successCount, failCount);
}
/**
* 发布单个商品
*/
private boolean publishProduct(BatchPublishItem item, BatchPublishRequest request) {
log.info("开始发品: 商品={}, 账号={}", item.getProductName(), item.getAccountRemark());
try {
// TODO: 调用实际的发品接口
// 这里需要调用 ProductController 的 createByPromotion 接口
// 暂时模拟成功
// 模拟发品成功
item.setStatus(2); // 发布成功
item.setProductId(System.currentTimeMillis()); // 模拟商品ID
item.setProductStatus(1); // 模拟商品状态
item.setOuterId("OUTER_" + item.getSkuid()); // 模拟商家编码
item.setErrorMessage(null);
itemMapper.updateBatchPublishItem(item);
return true;
} catch (Exception e) {
log.error("发品失败", e);
item.setStatus(3); // 发布失败
item.setErrorMessage(e.getMessage());
itemMapper.updateBatchPublishItem(item);
return false;
}
}
/**
* 延迟上架商品
*/
@Override
public void schedulePublish(Long itemId, Integer delaySeconds) {
log.info("调度延迟上架: itemId={}, delay={}秒", itemId, delaySeconds);
CompletableFuture.runAsync(() -> {
try {
// 延迟指定秒数
TimeUnit.SECONDS.sleep(delaySeconds);
// 执行上架
doPublish(itemId);
} catch (InterruptedException e) {
log.error("延迟上架被中断", e);
Thread.currentThread().interrupt();
}
});
}
/**
* 执行上架操作
*/
private void doPublish(Long itemId) {
log.info("开始上架商品: itemId={}", itemId);
BatchPublishItem item = itemMapper.selectBatchPublishItemById(itemId);
if (item == null) {
log.error("商品明细不存在: {}", itemId);
return;
}
try {
// 更新状态为上架中
item.setStatus(4);
itemMapper.updateBatchPublishItem(item);
// TODO: 调用实际的上架接口
// 这里需要调用 ProductController 的 publish 接口
// 暂时模拟成功
// 模拟上架成功
item.setStatus(5); // 已上架
item.setPublishTime(new Date());
item.setErrorMessage(null);
itemMapper.updateBatchPublishItem(item);
log.info("上架成功: itemId={}", itemId);
} catch (Exception e) {
log.error("上架失败", e);
item.setStatus(6); // 上架失败
item.setErrorMessage("上架异常: " + e.getMessage());
itemMapper.updateBatchPublishItem(item);
}
}
/**
* 查询批量发品任务
*/
@Override
public BatchPublishTask getTaskById(Long taskId) {
return taskMapper.selectBatchPublishTaskById(taskId);
}
/**
* 查询批量发品明细列表
*/
@Override
public List<BatchPublishItem> getItemsByTaskId(Long taskId) {
return itemMapper.selectBatchPublishItemByTaskId(taskId);
}
/**
* 查询批量发品任务列表
*/
@Override
public List<BatchPublishTask> selectTaskList(BatchPublishTask task) {
return taskMapper.selectBatchPublishTaskList(task);
}
}

View File

@@ -0,0 +1,128 @@
package com.ruoyi.jarvis.util;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 线报消息解析工具类
*
* @author ruoyi
* @date 2025-01-10
*/
public class LineReportParser {
// 京东链接正则表达式
private static final Pattern JD_URL_PATTERN = Pattern.compile("https?://[^\\s]*?jd\\.com[^\\s]*");
// SKUID正则表达式10-13位数字
private static final Pattern SKUID_PATTERN = Pattern.compile("\\b(\\d{10,13})\\b");
/**
* 从线报消息中提取所有京东链接
*
* @param message 线报消息
* @return 京东链接列表
*/
public static List<String> extractJdUrls(String message) {
List<String> urls = new ArrayList<>();
if (message == null || message.trim().isEmpty()) {
return urls;
}
Matcher matcher = JD_URL_PATTERN.matcher(message);
while (matcher.find()) {
String url = matcher.group();
// 清理URL末尾的标点符号
url = url.replaceAll("[\\s,,。!?]+$", "");
if (!urls.contains(url)) {
urls.add(url);
}
}
return urls;
}
/**
* 从线报消息中提取所有可能的SKUID
*
* @param message 线报消息
* @return SKUID列表
*/
public static List<String> extractSkuids(String message) {
Set<String> skuids = new LinkedHashSet<>();
if (message == null || message.trim().isEmpty()) {
return new ArrayList<>(skuids);
}
// 先从URL中提取SKUID
List<String> urls = extractJdUrls(message);
for (String url : urls) {
String skuid = extractSkuidFromUrl(url);
if (skuid != null) {
skuids.add(skuid);
}
}
// 再从文本中直接提取可能的SKUID10-13位数字
Matcher matcher = SKUID_PATTERN.matcher(message);
while (matcher.find()) {
String skuid = matcher.group(1);
// 只添加11-13位的避免误识别如手机号等
if (skuid.length() >= 11) {
skuids.add(skuid);
}
}
return new ArrayList<>(skuids);
}
/**
* 从JD链接中提取SKUID
*
* @param url JD链接
* @return SKUID如果提取失败返回null
*/
public static String extractSkuidFromUrl(String url) {
if (url == null || url.trim().isEmpty()) {
return null;
}
// 匹配 item.jd.com/{skuid}.html
Pattern pattern1 = Pattern.compile("item\\.jd\\.com/(\\d+)\\.html");
Matcher matcher1 = pattern1.matcher(url);
if (matcher1.find()) {
return matcher1.group(1);
}
// 匹配 sku=xxx 或 skuId=xxx
Pattern pattern2 = Pattern.compile("[?&]sku[Ii]?d?=(\\d+)");
Matcher matcher2 = pattern2.matcher(url);
if (matcher2.find()) {
return matcher2.group(1);
}
// 匹配短链接中的数字
Pattern pattern3 = Pattern.compile("u\\.jd\\.com/[^\\s]*(\\d{10,13})");
Matcher matcher3 = pattern3.matcher(url);
if (matcher3.find()) {
return matcher3.group(1);
}
return null;
}
/**
* 解析线报消息,返回提取的信息
*
* @param message 线报消息
* @return 包含URLs和SKUIDs的Map
*/
public static Map<String, Object> parseMessage(String message) {
Map<String, Object> result = new HashMap<>();
result.put("urls", extractJdUrls(message));
result.put("skuids", extractSkuids(message));
return result;
}
}

View File

@@ -0,0 +1,118 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.jarvis.mapper.BatchPublishItemMapper">
<resultMap type="com.ruoyi.jarvis.domain.BatchPublishItem" id="BatchPublishItemResult">
<result property="id" column="id" />
<result property="taskId" column="task_id" />
<result property="skuid" column="skuid" />
<result property="productName" column="product_name" />
<result property="targetAccount" column="target_account" />
<result property="accountRemark" column="account_remark" />
<result property="status" column="status" />
<result property="productId" column="product_id" />
<result property="productStatus" column="product_status" />
<result property="outerId" column="outer_id" />
<result property="publishPrice" column="publish_price" />
<result property="errorMessage" column="error_message" />
<result property="publishTime" column="publish_time" />
<result property="delaySeconds" column="delay_seconds" />
<result property="createTime" column="create_time" />
</resultMap>
<sql id="selectBatchPublishItemVo">
select id, task_id, skuid, product_name, target_account, account_remark, status,
product_id, product_status, outer_id, publish_price, error_message,
publish_time, delay_seconds, create_time
from batch_publish_item
</sql>
<select id="selectBatchPublishItemList" parameterType="com.ruoyi.jarvis.domain.BatchPublishItem" resultMap="BatchPublishItemResult">
<include refid="selectBatchPublishItemVo"/>
<where>
<if test="taskId != null "> and task_id = #{taskId}</if>
<if test="skuid != null and skuid != ''"> and skuid = #{skuid}</if>
<if test="status != null "> and status = #{status}</if>
<if test="targetAccount != null and targetAccount != ''"> and target_account = #{targetAccount}</if>
</where>
order by create_time desc
</select>
<select id="selectBatchPublishItemById" parameterType="Long" resultMap="BatchPublishItemResult">
<include refid="selectBatchPublishItemVo"/>
where id = #{id}
</select>
<select id="selectBatchPublishItemByTaskId" parameterType="Long" resultMap="BatchPublishItemResult">
<include refid="selectBatchPublishItemVo"/>
where task_id = #{taskId}
order by create_time asc
</select>
<insert id="insertBatchPublishItem" parameterType="com.ruoyi.jarvis.domain.BatchPublishItem" useGeneratedKeys="true" keyProperty="id">
insert into batch_publish_item
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="taskId != null">task_id,</if>
<if test="skuid != null">skuid,</if>
<if test="productName != null">product_name,</if>
<if test="targetAccount != null">target_account,</if>
<if test="accountRemark != null">account_remark,</if>
<if test="status != null">status,</if>
<if test="productId != null">product_id,</if>
<if test="productStatus != null">product_status,</if>
<if test="outerId != null">outer_id,</if>
<if test="publishPrice != null">publish_price,</if>
<if test="errorMessage != null">error_message,</if>
<if test="publishTime != null">publish_time,</if>
<if test="delaySeconds != null">delay_seconds,</if>
<if test="createTime != null">create_time,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="taskId != null">#{taskId},</if>
<if test="skuid != null">#{skuid},</if>
<if test="productName != null">#{productName},</if>
<if test="targetAccount != null">#{targetAccount},</if>
<if test="accountRemark != null">#{accountRemark},</if>
<if test="status != null">#{status},</if>
<if test="productId != null">#{productId},</if>
<if test="productStatus != null">#{productStatus},</if>
<if test="outerId != null">#{outerId},</if>
<if test="publishPrice != null">#{publishPrice},</if>
<if test="errorMessage != null">#{errorMessage},</if>
<if test="publishTime != null">#{publishTime},</if>
<if test="delaySeconds != null">#{delaySeconds},</if>
<if test="createTime != null">#{createTime},</if>
</trim>
</insert>
<insert id="batchInsertBatchPublishItem" parameterType="java.util.List">
insert into batch_publish_item
(task_id, skuid, product_name, target_account, account_remark, status, publish_price, delay_seconds, create_time)
values
<foreach collection="list" item="item" separator=",">
(#{item.taskId}, #{item.skuid}, #{item.productName}, #{item.targetAccount}, #{item.accountRemark},
#{item.status}, #{item.publishPrice}, #{item.delaySeconds}, #{item.createTime})
</foreach>
</insert>
<update id="updateBatchPublishItem" parameterType="com.ruoyi.jarvis.domain.BatchPublishItem">
update batch_publish_item
<trim prefix="SET" suffixOverrides=",">
<if test="status != null">status = #{status},</if>
<if test="productId != null">product_id = #{productId},</if>
<if test="productStatus != null">product_status = #{productStatus},</if>
<if test="outerId != null">outer_id = #{outerId},</if>
<if test="errorMessage != null">error_message = #{errorMessage},</if>
<if test="publishTime != null">publish_time = #{publishTime},</if>
</trim>
where id = #{id}
</update>
<delete id="deleteBatchPublishItemById" parameterType="Long">
delete from batch_publish_item where id = #{id}
</delete>
</mapper>

View File

@@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.jarvis.mapper.BatchPublishTaskMapper">
<resultMap type="com.ruoyi.jarvis.domain.BatchPublishTask" id="BatchPublishTaskResult">
<result property="id" column="id" />
<result property="taskName" column="task_name" />
<result property="originalMessage" column="original_message" />
<result property="totalProducts" column="total_products" />
<result property="selectedProducts" column="selected_products" />
<result property="targetAccounts" column="target_accounts" />
<result property="status" column="status" />
<result property="successCount" column="success_count" />
<result property="failCount" column="fail_count" />
<result property="commonParams" column="common_params" />
<result property="createUserId" column="create_user_id" />
<result property="createUserName" column="create_user_name" />
<result property="createTime" column="create_time" />
<result property="completeTime" column="complete_time" />
</resultMap>
<sql id="selectBatchPublishTaskVo">
select id, task_name, original_message, total_products, selected_products, target_accounts,
status, success_count, fail_count, common_params, create_user_id, create_user_name,
create_time, complete_time
from batch_publish_task
</sql>
<select id="selectBatchPublishTaskList" parameterType="com.ruoyi.jarvis.domain.BatchPublishTask" resultMap="BatchPublishTaskResult">
<include refid="selectBatchPublishTaskVo"/>
<where>
<if test="taskName != null and taskName != ''"> and task_name like concat('%', #{taskName}, '%')</if>
<if test="status != null "> and status = #{status}</if>
<if test="createUserId != null "> and create_user_id = #{createUserId}</if>
</where>
order by create_time desc
</select>
<select id="selectBatchPublishTaskById" parameterType="Long" resultMap="BatchPublishTaskResult">
<include refid="selectBatchPublishTaskVo"/>
where id = #{id}
</select>
<insert id="insertBatchPublishTask" parameterType="com.ruoyi.jarvis.domain.BatchPublishTask" useGeneratedKeys="true" keyProperty="id">
insert into batch_publish_task
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="taskName != null and taskName != ''">task_name,</if>
<if test="originalMessage != null">original_message,</if>
<if test="totalProducts != null">total_products,</if>
<if test="selectedProducts != null">selected_products,</if>
<if test="targetAccounts != null">target_accounts,</if>
<if test="status != null">status,</if>
<if test="successCount != null">success_count,</if>
<if test="failCount != null">fail_count,</if>
<if test="commonParams != null">common_params,</if>
<if test="createUserId != null">create_user_id,</if>
<if test="createUserName != null">create_user_name,</if>
<if test="createTime != null">create_time,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="taskName != null and taskName != ''">#{taskName},</if>
<if test="originalMessage != null">#{originalMessage},</if>
<if test="totalProducts != null">#{totalProducts},</if>
<if test="selectedProducts != null">#{selectedProducts},</if>
<if test="targetAccounts != null">#{targetAccounts},</if>
<if test="status != null">#{status},</if>
<if test="successCount != null">#{successCount},</if>
<if test="failCount != null">#{failCount},</if>
<if test="commonParams != null">#{commonParams},</if>
<if test="createUserId != null">#{createUserId},</if>
<if test="createUserName != null">#{createUserName},</if>
<if test="createTime != null">#{createTime},</if>
</trim>
</insert>
<update id="updateBatchPublishTask" parameterType="com.ruoyi.jarvis.domain.BatchPublishTask">
update batch_publish_task
<trim prefix="SET" suffixOverrides=",">
<if test="taskName != null and taskName != ''">task_name = #{taskName},</if>
<if test="status != null">status = #{status},</if>
<if test="successCount != null">success_count = #{successCount},</if>
<if test="failCount != null">fail_count = #{failCount},</if>
<if test="completeTime != null">complete_time = #{completeTime},</if>
</trim>
where id = #{id}
</update>
<delete id="deleteBatchPublishTaskById" parameterType="Long">
delete from batch_publish_task where id = #{id}
</delete>
</mapper>

46
sql/batch_publish.sql Normal file
View File

@@ -0,0 +1,46 @@
-- 批量发品任务表
CREATE TABLE IF NOT EXISTS `batch_publish_task` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '任务ID',
`task_name` varchar(200) DEFAULT NULL COMMENT '任务名称',
`original_message` text COMMENT '原始线报消息',
`total_products` int(11) DEFAULT NULL COMMENT '解析出的商品数量',
`selected_products` int(11) DEFAULT NULL COMMENT '选中的商品数量',
`target_accounts` varchar(500) DEFAULT NULL COMMENT '目标ERP账号JSON数组',
`status` int(11) DEFAULT '0' COMMENT '任务状态0待处理 1处理中 2已完成 3失败',
`success_count` int(11) DEFAULT '0' COMMENT '成功发品数量',
`fail_count` int(11) DEFAULT '0' COMMENT '失败发品数量',
`common_params` text COMMENT '通用参数JSON',
`create_user_id` bigint(20) DEFAULT NULL COMMENT '创建人ID',
`create_user_name` varchar(100) DEFAULT NULL COMMENT '创建人姓名',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`complete_time` datetime DEFAULT NULL COMMENT '完成时间',
PRIMARY KEY (`id`),
KEY `idx_create_user` (`create_user_id`),
KEY `idx_status` (`status`),
KEY `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='批量发品任务表';
-- 批量发品明细表
CREATE TABLE IF NOT EXISTS `batch_publish_item` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '明细ID',
`task_id` bigint(20) NOT NULL COMMENT '任务ID',
`skuid` varchar(100) DEFAULT NULL COMMENT 'SKUID',
`product_name` varchar(500) DEFAULT NULL COMMENT '商品名称',
`target_account` varchar(100) DEFAULT NULL COMMENT '目标ERP账号',
`account_remark` varchar(100) DEFAULT NULL COMMENT '账号备注名',
`status` int(11) DEFAULT '0' COMMENT '发品状态0待发布 1发布中 2发布成功 3发布失败 4上架中 5已上架 6上架失败',
`product_id` bigint(20) DEFAULT NULL COMMENT 'ERP商品ID发品成功后返回',
`product_status` int(11) DEFAULT NULL COMMENT '商品状态(发品成功后返回)',
`outer_id` varchar(100) DEFAULT NULL COMMENT '商家编码(发品成功后返回)',
`publish_price` bigint(20) DEFAULT NULL COMMENT '发品价格(分)',
`error_message` varchar(1000) DEFAULT NULL COMMENT '失败原因',
`publish_time` datetime DEFAULT NULL COMMENT '上架时间',
`delay_seconds` int(11) DEFAULT '3' COMMENT '延迟上架时间(秒)',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`),
KEY `idx_task_id` (`task_id`),
KEY `idx_skuid` (`skuid`),
KEY `idx_status` (`status`),
KEY `idx_product_id` (`product_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='批量发品明细表';