Files
full/module/ec/order/internal/logic/summary/submit.go
2026-09-22 21:15:34 +08:00

270 lines
7.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package summary
import (
"context"
"errors"
"strconv"
"strings"
"time"
"bsm/full/module/ec/order/internal/impl"
"bsm/full/module/ec/order/internal/logic/common"
"bsm/full/module/ec/order/internal/models"
pb "bsm/full/module/ec/order/pb"
"git.apinb.com/bsm-sdk/core/errcode"
"git.apinb.com/bsm-sdk/core/printer"
"git.apinb.com/bsm-sdk/core/service"
"git.apinb.com/bsm-sdk/core/types"
"git.apinb.com/bsm-sdk/core/utils"
"gorm.io/gorm"
)
// 将购物车的数据提交生成订单
func Submit(ctx context.Context, in *pb.SubmitRequest) (reply *pb.OrderStatusReply, err error) {
// parse authorization meta.
RoleValve := &service.ParseOptions{RoleValue: "Mall_Admin"}
if in.GetAddress() != nil {
RoleValve = nil
}
auth, err := service.ParseMetaCtx(ctx, RoleValve)
if err != nil {
return nil, err
}
var (
cart = make([]*models.OrderCart, 0)
details = make([]*models.OrderDetails, 0)
summary = make([]*models.OrderSummary, 0)
keys = make([]string, 0)
address = models.OrderAddress{}
)
// 获取购物车内数据信息
err = impl.DBService.Where("passport_identity = ?", auth.Identity).Find(&cart).Error
if err != nil {
printer.Error(err.Error())
if errors.Is(err, models.ErrNotFound) {
return nil, errcode.ErrRecordNotFound
}
return nil, errcode.ErrDB
}
// 只处理请求中选中的购物车条目;未指定 id 时按整车处理,兼容旧调用方。
if ids := in.GetId(); len(ids) > 0 {
wanted := make(map[uint]struct{}, len(ids))
for _, id := range ids {
if v, e := strconv.ParseUint(id, 10, 64); e == nil {
wanted[uint(v)] = struct{}{}
}
}
selected := make([]*models.OrderCart, 0, len(ids))
for _, item := range cart {
if _, ok := wanted[item.ID]; ok {
selected = append(selected, item)
}
}
cart = selected
}
if len(cart) == 0 {
return nil, errcode.ErrInvalidArgument
}
// 获取地址信息
if in.GetAddress() != nil {
address = models.OrderAddress{
Country: in.Address.Country,
Province: in.Address.Province,
City: in.Address.City,
Detail: in.Address.Detail,
Contact: in.Address.Contact,
Phone: in.Address.Phone,
Area: in.Address.Area,
Email: in.Address.Email,
ZipCode: in.Address.ZipCode,
}
} else {
// 地址库按归属过滤:仅允许使用属于当前登录者的收货地址,避免盗用他人地址下单。
err = impl.DBService.Table("address_library").
Where("identity = ? and owner_identity = ?", in.AddressIdentity, auth.Identity).
Take(&address).Error
if err != nil {
printer.Error(err.Error())
if errors.Is(err, models.ErrNotFound) {
return nil, errcode.ErrPermissionDenied
}
return nil, errcode.ErrDB
}
}
// 按店铺分组购物车商品,并记录每个店铺对应的 store_id。
storeGroups := make(map[string][]*models.OrderCart)
storeIDs := make(map[string]uint)
for _, item := range cart {
product := models.Product{}
err = impl.DBService.Select("store_id,store_identity").Table("mall_product").Where("id = ?", item.ProductID).First(&product).Error
if err != nil {
printer.Error(err.Error())
if errors.Is(err, models.ErrNotFound) {
return nil, errcode.ErrRecordNotFound
}
return nil, errcode.ErrDB
}
storeIDs[product.StoreIdentity] = product.StoreId
storeGroups[product.StoreIdentity] = append(storeGroups[product.StoreIdentity], item)
}
// 处理每个店铺
for StoreIdentity, items := range storeGroups {
var storeTotal int64 = 0
var storeDetails []*models.OrderDetails
//订单号2位年份6位随机数共18位
summaryIdentity := utils.UUID()
orderNo := common.CreateOrderNo()
// 处理店铺内所有商品
for _, item := range items {
product := models.Product{}
spec := models.Spec{}
// 获取产品信息
err = impl.DBService.Select("title,cover_image,cost_price,supply_id,args,gas_types").Table("mall_product").
Where("id = ?", item.ProductID).First(&product).Error
if err != nil {
printer.Error(err.Error())
if errors.Is(err, models.ErrNotFound) {
return nil, errcode.ErrRecordNotFound
}
return nil, errcode.ErrDB
}
// 获取规格信息
err := impl.DBService.Select("title,serial_number,price").Table("mall_product_spec").
Where("id = ?", item.SpecID).First(&spec).Error
if err != nil {
printer.Error(err.Error())
if errors.Is(err, models.ErrNotFound) {
return nil, errcode.ErrRecordNotFound
}
return nil, errcode.ErrDB
}
// 计算商品总价
itemTotal := int64(item.Number) * spec.Price
storeTotal += itemTotal
// 添加到订单详情
storeDetails = append(storeDetails, &models.OrderDetails{
Type: 1,
ProductID: item.ProductID,
ProductIdentity: item.ProductIdentity,
SpecID: item.SpecID,
OrderNo: orderNo,
Title: product.Title,
CoverImage: product.CoverImage,
UnitPrice: spec.Price,
SalesPrice: spec.Price,
Number: item.Number,
ProductArgs: item.ProductArgs,
SummaryIdentity: summaryIdentity,
SpecTitle: spec.Title,
SpecNo: spec.SerialNumber,
GasType: int32(product.GasTypes),
SupplyId: product.SupplyId,
TotalPrice: int64(item.Number) * spec.Price,
})
}
// 创建店铺订单摘要
summary = append(summary, &models.OrderSummary{
OrderNo: orderNo,
Std_Identity: types.Std_Identity{Identity: summaryIdentity},
PartnerID: in.PartnerId,
TransPrice: storeTotal,
TotalPrice: storeTotal,
StoreID: storeIDs[StoreIdentity],
StoreIdentity: StoreIdentity,
LogisticsFee: 0,
Remark: "",
AddressIdentity: in.AddressIdentity,
County: address.Country,
Province: address.Province,
City: address.City,
Area: address.Area,
Address: address.Detail,
Contact: address.Contact,
Phone: address.Phone,
Std_Passport: types.Std_Passport{PassportID: auth.ID, PassportIdentity: auth.Identity},
Status: 1,
})
// 添加到总详情列表
details = append(details, storeDetails...)
keys = append(keys, StoreIdentity)
}
err = impl.DBService.Transaction(func(tx *gorm.DB) error {
// 1. 创建订单摘要
if err := tx.Create(summary).Error; err != nil {
printer.Error(err.Error())
return err
}
// 2. 创建订单详情
if err := tx.Model(&models.OrderDetails{}).CreateInBatches(details, len(details)).Error; err != nil {
printer.Error(err.Error())
return err
}
// 3. 扣减库存:按规格做原子条件更新,影响行数为 0 即视为库存不足并回滚整个下单事务,避免超卖。
for _, detail := range details {
if detail.Number > 0 {
result := tx.Table("mall_product_spec").
Where("id = ? AND stock >= ?", detail.SpecID, detail.Number).
UpdateColumn("stock", gorm.Expr("stock - ?", detail.Number))
if result.Error != nil {
printer.Error(result.Error.Error())
return result.Error
}
if result.RowsAffected == 0 {
printer.Error("库存不足或规格不存在")
return errors.New("库存不足或规格不存在")
}
}
}
// 4. 只删除本次已下单的购物车条目,保留用户未结算的其它条目。
cartIDs := make([]uint, 0, len(cart))
for _, item := range cart {
cartIDs = append(cartIDs, item.ID)
}
if err := tx.Where("id in ?", cartIDs).
Delete(&models.OrderCart{}).Error; err != nil {
printer.Error(err.Error())
return err
}
return nil
})
if err != nil {
printer.Error(err.Error())
return nil, errcode.ErrDB
}
// 返回第一个订单号作为标识
firstOrderNo := ""
if len(summary) > 0 {
firstOrderNo = summary[0].OrderNo
}
return &pb.OrderStatusReply{
Code: 0,
Identity: firstOrderNo,
Message: strings.Join(keys, ","),
Timeseq: time.Now().UnixNano(),
}, nil
}