前提
类似于实现网上商城订单交付的业务,浅浅记录一下
话不多说,直接上码
首先定义我们需要的订单信息以及交付订单后生成的出库单信息,要明确的是一张订单可能会对应多张不同的出库单(因为可能订单数量过大,一个仓库的库存量不足以满足需求)
// Order 订单单据
type Order struct {
MODEL
OrderID uint64 `json:"order_id" gorm:"order_id"` //订单号
EmployeeID uint64 `json:"employee_id" gorm:"employee_id;not null"` //用户ID
ProductID uint64 `json:"product_id" gorm:"product_id;not null"` //产品ID
CustomerName string `json:"customer_name" gorm:"customer_name"` //客户信息
Amount int `json:"amount" gorm:"amount"` //数量
State int `json:"state" gorm:"default:0;comment:0-未支付 1-已支付"` //订单状态
Money float64 `json:"money" gorm:"money"` //订单金额
Destination string `json:"destination" gorm:"destination"` //目的地址
}
// OutgoingOrder 出库单
type OutgoingOrder struct {
MODEL
DeliveryID uint64 `json:"delivery_id" gorm:"delivery_id"` //出库单号
OrderID uint64 `json:"order_id" gorm:"order_id"` //订单号
EmployeeID uint64 `json:"employee_id" gorm:"employee_id;not null"`
DepotID uint64 `json:"depot_id" gorm:"depot_id"` //仓库id
ProductID uint64 `json:"product_id" gorm:"product_id;not null"`
CustomerName string `json:"customer_name" gorm:"customer_name"` //客户信息
Amount int `json:"amount" gorm:"amount"` //出库数量
Money float64 `json:"money" gorm:"money"` //订单金额
Destination string `json:"destination" gorm:"destination"` //目的地址
Path string `json:"path" gorm:"path"` //发货地址
}
```
简化一下流程,我们假设通过提交订单ID以及客户交付金额就可以进行交付业务
//交付订单请求
type ReqDeliveryOrder struct {
OrderID uint64 `json:"order_id"` //要交付的订单号
Money float64 `json:"money"` //交付金额
}
首先根据要交付的订单ID查询出订单的信息并记录下来
amount, err := GetProductAmount(mes.ProductID)
if err != nil {
return
}
//当订单数量大于库存数量时,返回错误货物数量不足
if mes.Amount > amount {
return errors.New("仓库数量不足,请及时补货")
}
//将订单的状态更改为支付完成状态
if order.Money == delivery.Money {
//钱交足了,将状态标位已交付
order.State = 1
}
因为要对多张表(订单,出库单,库存数量)进行操作,我们应该开启一个事务保证各表数据同步,在进行交付业务时首先判断订单状态是否是已经完成状态
//首先要得到订单目的地址的经纬度,并将其与仓储信息表中存有该类物品的仓库的经纬度进行计算得出之间距离
lng, lat := baiduMap.Get_JWData_By_Ctiy(mes.Destination) //获取订单目的地址的经纬度
type distance = struct {
DepotID uint64 `json:"depot_id"` //仓库id
Distance float64 `json:"distance"` //距离
}
var dis []distance
tx.Raw(`
SELECT d.depot_id AS depot_id,
(6371 * acos(cos(radians(?)) * cos(radians(d.latitude)) * cos(radians(d.longitude) - radians(?)) + sin(radians(?)) * sin(radians(d.latitude)))) AS distance
FROM ap_depot d,ap_address a where d.state != 0 and d.depot_id = a.depot_id and a.product_id = ? ORDER BY distance LIMIT 0,5;`, lng, lat, lng, mes.ProductID).Scan(&dis)
计算得出距离信息后,我们开始构造优先级队列,本例中我选择了距离、仓库自身优先级、以及仓库库存量三个因素来影响优先级顺序
//生成优先级队列
priorityQueue := priqueue.PriorityQueue{}
for i, v := range dis {
priorityQueue = append(priorityQueue, &priqueue.Queue{
Index: i, //堆索引
DepotID: v.DepotID,
Distance: v.Distance,
Priority: GetPriByID(v.DepotID), //仓库自身的优先级
Amount: GetAmuByID(mes.ProductID, v.DepotID), //库存数量
})
}
priqueue.GenQueue(&priorityQueue)
//按照比较器规则排序后推出优先级最高的仓库进行发货业务
queue := priorityQueue.Pop().(*priqueue.Queue)
接下来我们需要做一次判断,当第一优先仓库的库存量足以满足订单时就完成发货。第一优先仓库不足时,我们需要继续弹出优先级队列的第二优先的仓库进行补充发货,依此类推
//num是订单需求数量
for num > 0 {
var count int
if num-queue.Amount > 0 {
count = queue.Amount
} else {
count = num
}
//将每个仓库的出库信息记录下来,用以构成出库单
outOrder = append(outOrder, &model.OutgoingOrder{
OrderID: mes.OrderID,
EmployeeID: mes.EmployeeID,
DepotID: queue.DepotID,
ProductID: mes.ProductID,
CustomerName: mes.CustomerName,
Amount: count, //上面if判断决定发货数量
Money: price * float64(count),
Destination: mes.Destination, //目的地址
Path: GetPathByID(queue.DepotID),
})
//修改仓库存储货物信息表中存货量
tx.Model(&model.Address{}).Where("product_id = ? and depot_id = ?", mes.ProductID, queue.DepotID).Update("count", queue.Amount-count)
num = num - queue.Amount
//当第一个仓库的数量无法满足需求时,再推出第二高优先级仓库补充发货
if num > 0 {
queue = priorityQueue.Pop().(*priqueue.Queue)
}
}
//然后就是将我们记录的出库表信息数组在数据库中创建记录
err = CreateOutgoingOrder(outOrder)
//提交事务
到这里我们的交付订单以及仓库出库业务已经差不多完成了,但是还有最后一步,记得将数据库中订单的状态改为支付状态
db.Table("ap_order").Where("order_id = ?", mes.OrderID).Update("state", 1)
结语
第一次做这类业务,想法还不成熟,还有考虑不足的地方,希望大家多多指出