feat: 补充平台端库存管理模块

补齐平台端库存余额、流水、初始化和手工调整能力,并将快递发货接入库存扣减闭环,方便运营侧统一查账与审计。

Made-with: Cursor
This commit is contained in:
AriadenCaseblg
2026-04-19 23:43:46 +08:00
parent b097837aa3
commit 005bd968df
32 changed files with 61113 additions and 59368 deletions

View File

@@ -0,0 +1,26 @@
package com.zbkj.common.enums;
/**
* 库存方向枚举
*/
public enum InventoryPmEnum {
IN(1, "入库"),
OUT(0, "出库");
private final Integer code;
private final String name;
InventoryPmEnum(Integer code, String name) {
this.code = code;
this.name = name;
}
public Integer getCode() {
return code;
}
public String getName() {
return name;
}
}

View File

@@ -0,0 +1,28 @@
package com.zbkj.common.enums;
/**
* 库存来源类型
*/
public enum InventorySourceTypeEnum {
MANUAL_IN("manual_in", "手工入库"),
MANUAL_OUT("manual_out", "手工出库"),
ORDER_DELIVERY("order_delivery", "订单发货出库"),
INIT_SYNC("init_sync", "库存初始化");
private final String code;
private final String name;
InventorySourceTypeEnum(String code, String name) {
this.code = code;
this.name = name;
}
public String getCode() {
return code;
}
public String getName() {
return name;
}
}

View File

@@ -0,0 +1,71 @@
package com.zbkj.common.model.product;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
/**
* 商品库存余额表
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("eb_product_inventory")
@ApiModel(value = "ProductInventory对象", description = "商品库存余额表")
public class ProductInventory implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "主键")
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
@ApiModelProperty(value = "商户ID")
private Integer merId;
@ApiModelProperty(value = "商品ID")
private Integer productId;
@ApiModelProperty(value = "商品规格值ID")
private Integer attrValueId;
@ApiModelProperty(value = "商品sku")
private String sku;
@ApiModelProperty(value = "商品名称快照")
private String productName;
@ApiModelProperty(value = "商品图片快照")
private String image;
@ApiModelProperty(value = "当前可用库存")
private Integer stock;
@ApiModelProperty(value = "预警库存")
private Integer alertStock;
@ApiModelProperty(value = "最后一次操作时间")
private Date lastOperateTime;
@ApiModelProperty(value = "是否删除")
private Boolean isDel;
@ApiModelProperty(value = "创建时间")
private Date createTime;
@ApiModelProperty(value = "更新时间")
private Date updateTime;
@ApiModelProperty(value = "商户名称")
@TableField(exist = false)
private String merchantName;
}

View File

@@ -0,0 +1,96 @@
package com.zbkj.common.model.product;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
* 商品库存流水表
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("eb_product_inventory_record")
@ApiModel(value = "ProductInventoryRecord对象", description = "商品库存流水表")
public class ProductInventoryRecord implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "主键")
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
@ApiModelProperty(value = "库存ID")
private Integer inventoryId;
@ApiModelProperty(value = "商户ID")
private Integer merId;
@ApiModelProperty(value = "商品ID")
private Integer productId;
@ApiModelProperty(value = "商品规格值ID")
private Integer attrValueId;
@ApiModelProperty(value = "商品sku")
private String sku;
@ApiModelProperty(value = "商品名称快照")
private String productName;
@ApiModelProperty(value = "方向1入库 0出库")
private Integer pm;
@ApiModelProperty(value = "变动数量")
private Integer number;
@ApiModelProperty(value = "变动前库存")
private Integer beforeStock;
@ApiModelProperty(value = "变动后库存")
private Integer afterStock;
@ApiModelProperty(value = "成本价")
private BigDecimal costPrice;
@ApiModelProperty(value = "来源类型")
private String sourceType;
@ApiModelProperty(value = "来源单号")
private String sourceNo;
@ApiModelProperty(value = "来源关联ID")
private Integer sourceId;
@ApiModelProperty(value = "来源详情ID")
private Integer sourceDetailId;
@ApiModelProperty(value = "操作人ID")
private Integer operateAdminId;
@ApiModelProperty(value = "操作人类型")
private Integer operateAdminType;
@ApiModelProperty(value = "操作人名称")
private String operateAdminName;
@ApiModelProperty(value = "备注")
private String remark;
@ApiModelProperty(value = "创建时间")
private Date createTime;
@ApiModelProperty(value = "商户名称")
@TableField(exist = false)
private String merchantName;
}

View File

@@ -0,0 +1,47 @@
package com.zbkj.common.request;
import com.zbkj.common.annotation.StringContains;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
/**
* 库存调整请求对象
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(value = "InventoryAdjustRequest对象", description = "库存调整请求对象")
public class InventoryAdjustRequest implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "商品ID", required = true)
@NotNull(message = "商品ID不能为空")
private Integer productId;
@ApiModelProperty(value = "商品规格值ID")
private Integer attrValueId;
@ApiModelProperty(value = "调整类型manual_in/manual_out", required = true)
@NotBlank(message = "调整类型不能为空")
@StringContains(limitValues = {"manual_in", "manual_out"}, message = "未知的库存调整类型")
private String sourceType;
@ApiModelProperty(value = "调整数量", required = true)
@NotNull(message = "调整数量不能为空")
@Min(value = 1, message = "调整数量必须大于0")
private Integer number;
@ApiModelProperty(value = "备注")
@Length(max = 255, message = "备注最多255个字符")
private String remark;
}

View File

@@ -0,0 +1,27 @@
package com.zbkj.common.request;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* 库存初始化请求对象
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(value = "InventoryInitRequest对象", description = "库存初始化请求对象")
public class InventoryInitRequest implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "商户ID不传表示全量")
private Integer merId;
@ApiModelProperty(value = "是否重建已存在库存记录")
private Boolean rebuild = false;
}

View File

@@ -0,0 +1,36 @@
package com.zbkj.common.request;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* 库存搜索请求对象
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(value = "InventorySearchRequest对象", description = "库存搜索请求对象")
public class InventorySearchRequest extends PageParamRequest implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "商户ID")
private Integer merId;
@ApiModelProperty(value = "商品ID")
private Integer productId;
@ApiModelProperty(value = "商品名称/sku关键词")
private String keywords;
@ApiModelProperty(value = "是否仅预警库存")
private Boolean alertOnly;
@ApiModelProperty(value = "来源类型")
private String sourceType;
}

View File

@@ -0,0 +1,55 @@
package com.zbkj.common.response;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
/**
* 库存分页响应对象
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(value = "InventoryPageResponse对象", description = "库存分页响应对象")
public class InventoryPageResponse implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "库存ID")
private Integer id;
@ApiModelProperty(value = "商户ID")
private Integer merId;
@ApiModelProperty(value = "商户名称")
private String merchantName;
@ApiModelProperty(value = "商品ID")
private Integer productId;
@ApiModelProperty(value = "商品名称")
private String productName;
@ApiModelProperty(value = "商品图片")
private String image;
@ApiModelProperty(value = "商品规格值ID")
private Integer attrValueId;
@ApiModelProperty(value = "商品sku")
private String sku;
@ApiModelProperty(value = "当前库存")
private Integer stock;
@ApiModelProperty(value = "预警库存")
private Integer alertStock;
@ApiModelProperty(value = "最近操作时间")
private Date lastOperateTime;
}

View File

@@ -0,0 +1,89 @@
package com.zbkj.common.response;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
* 库存流水分页响应对象
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(value = "InventoryRecordPageResponse对象", description = "库存流水分页响应对象")
public class InventoryRecordPageResponse implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "流水ID")
private Integer id;
@ApiModelProperty(value = "库存ID")
private Integer inventoryId;
@ApiModelProperty(value = "商户ID")
private Integer merId;
@ApiModelProperty(value = "商户名称")
private String merchantName;
@ApiModelProperty(value = "商品ID")
private Integer productId;
@ApiModelProperty(value = "商品名称")
private String productName;
@ApiModelProperty(value = "商品规格值ID")
private Integer attrValueId;
@ApiModelProperty(value = "商品sku")
private String sku;
@ApiModelProperty(value = "方向")
private Integer pm;
@ApiModelProperty(value = "数量")
private Integer number;
@ApiModelProperty(value = "变更前库存")
private Integer beforeStock;
@ApiModelProperty(value = "变更后库存")
private Integer afterStock;
@ApiModelProperty(value = "成本价")
private BigDecimal costPrice;
@ApiModelProperty(value = "来源类型")
private String sourceType;
@ApiModelProperty(value = "来源单号")
private String sourceNo;
@ApiModelProperty(value = "来源ID")
private Integer sourceId;
@ApiModelProperty(value = "来源详情ID")
private Integer sourceDetailId;
@ApiModelProperty(value = "操作人ID")
private Integer operateAdminId;
@ApiModelProperty(value = "操作人类型")
private Integer operateAdminType;
@ApiModelProperty(value = "操作人名称")
private String operateAdminName;
@ApiModelProperty(value = "备注")
private String remark;
@ApiModelProperty(value = "创建时间")
private Date createTime;
}