refactor: 重构资产实体和DTO结构类型

将gjson.Json类型替换为具体的结构体和map类型,修正DAO层链式调用,启用SKU元数据校验逻辑
This commit is contained in:
2026-03-22 20:08:32 +08:00
parent 34a1ba79b6
commit 829dc07747
36 changed files with 932 additions and 793 deletions

View File

@@ -17,8 +17,8 @@ rate:
database:
default:
- type: "pgsql"
host: "116.204.74.41"
port: "15432"
host: "localhost"
port: "5432"
user: "postgres"
pass: "123456"
name: "assets"
@@ -34,8 +34,8 @@ database:
deletedAt: "deleted_at" # (可选)软删除时间字段名称
timeMaintainDisabled: false # (可选)是否完全关闭时间更新特性为true时CreatedAt/UpdatedAt/DeletedAt都将失效
- type: "pgsql"
host: "116.204.74.41"
port: "15432"
host: "localhost"
port: "5432"
user: "postgres"
pass: "123456"
name: "assets"
@@ -52,8 +52,8 @@ database:
timeMaintainDisabled: false # (可选)是否完全关闭时间更新特性为true时CreatedAt/UpdatedAt/DeletedAt都将失效
tenant-1:
- type: "pgsql" # 使用自定义驱动,自动赋值创建人/修改人/租户ID
host: "116.204.74.41"
port: "15432"
host: "localhost"
port: "5432"
user: "postgres"
pass: "123456"
name: "tenant"
@@ -72,7 +72,7 @@ database:
redis:
# 集群模式配置方法
default:
address: 116.204.74.41:6379
address: localhost:6379
db: 0
idleTimeout: "60s"
maxConnLifetime: "90s"
@@ -82,7 +82,7 @@ redis:
writeTimeout: "30s"
maxActive: 100
test:
address: 116.204.74.41:6379
address: localhost:6379
db: 1
idleTimeout: "60s"
maxConnLifetime: "90s"
@@ -93,18 +93,31 @@ redis:
maxActive: 100
consul:
address: 116.204.74.41:8500
address: localhost:8500
jaeger:
addr: 116.204.74.41:4318
addr: localhost:4318
nats:
default:
url: "nats://116.204.74.41:4222"
url: "nats://localhost:4222"
# 文件上传服务地址与oss模块minio中的endpoint一致
filePrefix: "116.204.74.41:9000"
cache:
localTTL: 60
redisTTL: 300
redisTTL: 300
gmq:
redis:
primary:
addr: "localhost"
port: "6379"
db: 0
username: ""
password: ""
poolSize: 10
minIdleConn: 5
maxActiveConn: 10
maxRetries: 30

View File

@@ -1,9 +1,28 @@
package consts
import "github.com/gogf/gf/v2/util/gconv"
// AssetSkuStatus SKU状态枚举
type AssetSkuStatusType *int
var (
AssetSkuStatusDisable = newAssetSkuStatus(gconv.PtrInt8(0), "disable")
AssetSkuStatusEnable = newAssetSkuStatus(gconv.PtrInt8(1), "enable")
)
type AssetSkuStatus struct {
Status AssetSkuStatusType
type AssetSkuStatus *int8
type assetSkuStatus struct {
code AssetSkuStatus
desc string
}
func (s assetSkuStatus) Code() AssetSkuStatus {
return s.code
}
func (s assetSkuStatus) Desc() string {
return s.desc
}
func newAssetSkuStatus(code AssetSkuStatus, desc string) assetSkuStatus {
return assetSkuStatus{code: code, desc: desc}
}

View File

@@ -1,9 +1,28 @@
package consts
import "github.com/gogf/gf/v2/util/gconv"
// AssetStatus 资产状态枚举
type AssetStatusType *int
var (
AssetStatusDisable = newAssetStatus(gconv.PtrInt8(0), "disable")
AssetStatusEnable = newAssetStatus(gconv.PtrInt8(1), "enable")
)
type AssetStatus struct {
Status AssetStatusType
type AssetStatus *int8
type assetStatus struct {
code AssetStatus
desc string
}
func (s assetStatus) Code() AssetStatus {
return s.code
}
func (s assetStatus) Desc() string {
return s.desc
}
func newAssetStatus(code AssetStatus, desc string) assetStatus {
return assetStatus{code: code, desc: desc}
}

View File

@@ -1,14 +1,28 @@
package consts
import "github.com/gogf/gf/v2/util/gconv"
// CategoryStatus 分类状态枚举
type CategoryStatusType *int
var (
CategoryStatusDisable = newCategoryStatus(gconv.PtrInt8(0), "disable")
CategoryStatusEnable = newCategoryStatus(gconv.PtrInt8(1), "enable")
)
type CategoryStatus struct {
Status CategoryStatusType
type CategoryStatus *int8
type categoryStatus struct {
code CategoryStatus
desc string
}
//var CategoryStatusVal = CategoryStatus{
// CategoryStatusActive: gconv.PtrInt(1),
// CategoryStatusInactive: gconv.PtrInt(0),
//}
func (s categoryStatus) Code() CategoryStatus {
return s.code
}
func (s categoryStatus) Desc() string {
return s.desc
}
func newCategoryStatus(code CategoryStatus, desc string) categoryStatus {
return categoryStatus{code: code, desc: desc}
}

View File

@@ -2,16 +2,16 @@ package public
// MongoDB集合名称常量
const (
AssetCollection = "assets_asset" // 资产集合
CategoryCollection = "assets_category" // 分类集合
AssetCollection = "assets_asset" // 资产集合
AssetSkuCollection = "assets_asset_sku" // SKU集合
StockDetailsCollection = "assets_stock_details" // 库存明细集合
StockBatchCollection = "assets_stock_batch" // 库存批次集合
PrivateCategoryCollection = "private_category" // 私域分类集合
StockDetailsCollection = "stock_details" // 库存明细集合
StockBatchCollection = "stock_batch" // 库存批次集合
PrivateStockCollection = "private_stock" // 私域库存批次集合
PurchaseOrderCollection = "purchase_order" // 采购订单主表集合(统一模式)
PurchaseOrderItemCollection = "purchase_order_item" // 采购订单明细集合
PurchaseBidCollection = "purchase_bid" // 采购投标单集合(供应商抢单参与记录)
AssetSkuCollection = "asset_sku" // SKU集合
PrivateSkuCollection = "private_sku" // 私域sku集合
SupplierCollection = "supplier" // 供应商集合
ExpiryMessageCollection = "expiry_message" // 临期消息集合(兼容旧数据)

View File

@@ -1,13 +1,12 @@
package public
const StockDetailLockKey = "stock:lock:skuId-%s"
const StockDetailLockKey = "stock:lock:skuId-%v"
// 消费者配置(从 Redis Stream 消费请求)
const StockDetailQueueName = "assets:stock:detail:request:stream" // 请求 Stream 键名与发消息的key一致
const StockDetailGroupName = "assets:stock:detail:consumer:group" // 消费者
const StockDetailConsumerName = "message-consumer-1" // 消费者名称(唯一标识
const StockDetailPrefetchCount = 1 // 批处理大小每次读取1条
const StockDetailAutoAck = false // ACK是否自动确认true自动确认false不确认
const StockDetailQueueName = "assets:stock:stream" // 请求 Stream 键名与发消息的key一致
const StockDetailConsumerName = "assets-stock-consumer" // 消费者名称(唯一标识)
const StockDetailPrefetchCount = 1 // 批处理大小每次读取1条
const StockDetailAutoAck = false // ACK是否自动确认true自动确认false不确认
// 业务自增序列号前缀 Redis Key
const (

View File

@@ -1,21 +1,30 @@
package stock
// BatchStatus 批次状态枚举
type BatchStatus int
import "github.com/gogf/gf/v2/util/gconv"
const (
BatchStatusActive BatchStatus = 1 // 活跃
BatchStatusExpiring BatchStatus = 2 // 临期
BatchStatusExpired BatchStatus = 3 // 过期
BatchStatusSoldOut BatchStatus = 4 // 售罄
// BatchStatus 批次状态枚举
var (
BatchStatusActive = newBatchStatus(gconv.PtrInt8(1), "活跃")
BatchStatusExpiring = newBatchStatus(gconv.PtrInt8(2), "临期")
BatchStatusExpired = newBatchStatus(gconv.PtrInt8(3), "过期")
BatchStatusSoldOut = newBatchStatus(gconv.PtrInt8(4), "售罄")
)
// GetAllBatchStatuses 获取所有批次状态
func GetAllBatchStatuses() []BatchStatus {
return []BatchStatus{
BatchStatusActive,
BatchStatusExpiring,
BatchStatusExpired,
BatchStatusSoldOut,
}
type BatchStatus *int8
type batchStatus struct {
code BatchStatus
desc string
}
func (s batchStatus) Code() BatchStatus {
return s.code
}
func (s batchStatus) Desc() string {
return s.desc
}
func newBatchStatus(code BatchStatus, desc string) batchStatus {
return batchStatus{code: code, desc: desc}
}

View File

@@ -1,21 +1,30 @@
package stock
// StockStatus 库存状态枚举
type StockStatus int
import "github.com/gogf/gf/v2/util/gconv"
const (
StockStatusAvailable StockStatus = 1 // 可用,未分配渠道
StockStatusSold StockStatus = 2 // 已售出
StockStatusReserved StockStatus = 3 // 预留
StockStatusLocked StockStatus = 4 // 锁定
// StockStatus 库存状态枚举
var (
StockStatusAvailable = newStockStatus(gconv.PtrInt8(1), "可用,未分配渠道")
StockStatusSold = newStockStatus(gconv.PtrInt8(2), "已售出")
StockStatusReserved = newStockStatus(gconv.PtrInt8(3), "预留")
StockStatusLocked = newStockStatus(gconv.PtrInt8(4), "锁定")
)
// GetAllStockStatuses 获取所有库存状态
func GetAllStockStatuses() []StockStatus {
return []StockStatus{
StockStatusAvailable,
StockStatusSold,
StockStatusReserved,
StockStatusLocked,
}
type StockStatus *int8
type stockStatus struct {
code StockStatus
desc string
}
func (s stockStatus) Code() StockStatus {
return s.code
}
func (s stockStatus) Desc() string {
return s.desc
}
func newStockStatus(code StockStatus, desc string) stockStatus {
return stockStatus{code: code, desc: desc}
}

View File

@@ -4,14 +4,6 @@
// 注意Update/Delete返回*beans.ResponseEmpty直接return
package controller
import (
dto "assets/model/dto/stock"
service "assets/service/stock"
"context"
"gitea.com/red-future/common/beans"
)
type stockBatchController struct{}
var StockBatch = new(stockBatchController)
@@ -19,24 +11,24 @@ var StockBatch = new(stockBatchController)
func init() {
}
func (c *stockBatchController) CreateBatch(ctx context.Context, req *dto.CreateBatchReq) (res *dto.CreateBatchRes, err error) {
return service.StockBatch.Create(ctx, req)
}
func (c *stockBatchController) UpdateBatch(ctx context.Context, req *dto.UpdateBatchReq) (res *beans.ResponseEmpty, err error) {
err = service.StockBatch.Update(ctx, req)
return
}
func (c *stockBatchController) DeleteBatch(ctx context.Context, req *dto.DeleteBatchReq) (res *beans.ResponseEmpty, err error) {
err = service.StockBatch.Delete(ctx, req)
return
}
func (c *stockBatchController) GetBatch(ctx context.Context, req *dto.GetBatchReq) (res *dto.GetBatchRes, err error) {
return service.StockBatch.GetOne(ctx, req)
}
func (c *stockBatchController) ListBatches(ctx context.Context, req *dto.ListBatchReq) (res *dto.ListBatchRes, err error) {
return service.StockBatch.List(ctx, req)
}
//func (c *stockBatchController) CreateBatch(ctx context.Context, req *dto.CreateBatchReq) (res *dto.CreateBatchRes, err error) {
// return service.StockBatch.Create(ctx, req)
//}
//
//func (c *stockBatchController) UpdateBatch(ctx context.Context, req *dto.UpdateBatchReq) (res *beans.ResponseEmpty, err error) {
// err = service.StockBatch.Update(ctx, req)
// return
//}
//
//func (c *stockBatchController) DeleteBatch(ctx context.Context, req *dto.DeleteBatchReq) (res *beans.ResponseEmpty, err error) {
// err = service.StockBatch.Delete(ctx, req)
// return
//}
//
//func (c *stockBatchController) GetBatch(ctx context.Context, req *dto.GetBatchReq) (res *dto.GetBatchRes, err error) {
// return service.StockBatch.GetOne(ctx, req)
//}
//
//func (c *stockBatchController) ListBatches(ctx context.Context, req *dto.ListBatchReq) (res *dto.ListBatchRes, err error) {
// return service.StockBatch.List(ctx, req)
//}

View File

@@ -4,12 +4,6 @@
// 注意:只读接口,无写操作
package controller
import (
dto "assets/model/dto/stock"
service "assets/service/stock"
"context"
)
type stockDetails struct{}
// StockDetails 库存控制器
@@ -18,10 +12,10 @@ var StockDetails = new(stockDetails)
func init() {
}
func (c *stockDetails) GetStockDetails(ctx context.Context, req *dto.GetStockDetailsReq) (res *dto.GetStockDetailsRes, err error) {
return service.StockDetails.GetOne(ctx, req)
}
func (c *stockDetails) ListStockDetails(ctx context.Context, req *dto.ListStockDetailsReq) (res *dto.ListStockDetailsRes, err error) {
return service.StockDetails.List(ctx, req)
}
//func (c *stockDetails) GetStockDetails(ctx context.Context, req *dto.GetStockDetailsReq) (res *dto.GetStockDetailsRes, err error) {
// return service.StockDetails.GetOne(ctx, req)
//}
//
//func (c *stockDetails) ListStockDetails(ctx context.Context, req *dto.ListStockDetailsReq) (res *dto.ListStockDetailsRes, err error) {
// return service.StockDetails.List(ctx, req)
//}

View File

@@ -23,7 +23,7 @@ func (d *assetDao) Insert(ctx context.Context, req *dto.CreateAssetReq) (id int6
if err = gconv.Struct(req, &res); err != nil {
return
}
r, err := gfdb.DB(ctx).Model(ctx, public.AssetCollection).Ctx(ctx).Data(&res).Insert()
r, err := gfdb.DB(ctx).Model(ctx, public.AssetCollection).Data(&res).Insert()
if err != nil {
return
}
@@ -32,7 +32,7 @@ func (d *assetDao) Insert(ctx context.Context, req *dto.CreateAssetReq) (id int6
// Update 更新资产
func (d *assetDao) Update(ctx context.Context, req *dto.UpdateAssetReq) (rows int64, err error) {
r, err := gfdb.DB(ctx).Model(ctx, public.AssetCollection).Ctx(ctx).OmitEmpty().Where(entity.AssetCol.Id, req.Id).Update()
r, err := gfdb.DB(ctx).Model(ctx, public.AssetCollection).Data(&req).OmitEmpty().Where(entity.AssetCol.Id, req.Id).Update()
if err != nil {
return
}
@@ -50,7 +50,7 @@ func (d *assetDao) Delete(ctx context.Context, req *dto.DeleteAssetReq) (rows in
// GetOne 获取单个资产
func (d *assetDao) GetOne(ctx context.Context, req *dto.GetAssetReq, fields ...string) (res *entity.Asset, err error) {
r, err := gfdb.DB(ctx).Model(ctx, public.AssetCollection).Ctx(ctx).Where(entity.AssetCol.Id, req.Id).Fields(fields).One()
r, err := gfdb.DB(ctx).Model(ctx, public.AssetCollection).Where(entity.AssetCol.Id, req.Id).Fields(fields).One()
if err != nil {
return
}

View File

@@ -23,7 +23,7 @@ func (d *assetSku) Insert(ctx context.Context, req *dto.CreateAssetSkuReq) (id i
if err = gconv.Struct(req, &res); err != nil {
return
}
r, err := gfdb.DB(ctx).Model(ctx, public.AssetSkuCollection).Ctx(ctx).Data(&res).Insert()
r, err := gfdb.DB(ctx).Model(ctx, public.AssetSkuCollection).Data(&res).Insert()
if err != nil {
return
}
@@ -32,7 +32,7 @@ func (d *assetSku) Insert(ctx context.Context, req *dto.CreateAssetSkuReq) (id i
// Update 更新SKU
func (d *assetSku) Update(ctx context.Context, req *dto.UpdateAssetSkuReq) (rows int64, err error) {
r, err := gfdb.DB(ctx).Model(ctx, public.AssetSkuCollection).Ctx(ctx).OmitEmpty().Where(entity.AssetCol.Id, req.Id).Update()
r, err := gfdb.DB(ctx).Model(ctx, public.AssetSkuCollection).Data(&req).OmitEmpty().Where(entity.AssetCol.Id, req.Id).Update()
if err != nil {
return
}
@@ -50,7 +50,7 @@ func (d *assetSku) Delete(ctx context.Context, req *dto.DeleteAssetSkuReq) (rows
// GetOne 获取单个SKU
func (d *assetSku) GetOne(ctx context.Context, req *dto.GetAssetSkuReq, fields ...string) (res *entity.AssetSku, err error) {
r, err := gfdb.DB(ctx).Model(ctx, public.AssetSkuCollection).Ctx(ctx).Where(entity.AssetCol.Id, req.Id).Fields(fields).One()
r, err := gfdb.DB(ctx).Model(ctx, public.AssetSkuCollection).Where(entity.AssetCol.Id, req.Id).Fields(fields).One()
if err != nil {
return
}
@@ -104,8 +104,10 @@ func (d *assetSku) buildListFilter(ctx context.Context, req *dto.ListAssetSkuReq
}
model.Where(entity.AssetSkuCol.Id, req.Id)
model.Where(entity.AssetSkuCol.Status, req.Status)
model.WhereGT(entity.AssetSkuCol.Price, req.MinPrice)
model.WhereLT(entity.AssetSkuCol.Price, req.MaxPrice)
if req.MinPrice > 0 && req.MaxPrice > 0 {
model.WhereGT(entity.AssetSkuCol.Price, req.MinPrice)
model.WhereLT(entity.AssetSkuCol.Price, req.MaxPrice)
}
model.OmitEmptyWhere()
return model
}

View File

@@ -10,9 +10,10 @@ import (
entity "assets/model/entity/stock"
"context"
"gitea.com/red-future/common/db/mongo"
"gitea.com/red-future/common/utils"
"go.mongodb.org/mongo-driver/v2/bson"
"gitea.com/red-future/common/db/gfdb"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/gconv"
)
var StockBatch = new(stockBatch)
@@ -21,58 +22,48 @@ type stockBatch struct {
}
// Insert 插入
func (d *stockBatch) Insert(ctx context.Context, req *dto.CreateBatchReq) (ids []interface{}, err error) {
var result *entity.StockBatch
if err = utils.Struct(req, &result); err != nil {
func (d *stockBatch) Insert(ctx context.Context, req *dto.CreateSockBatchReq) (id int64, err error) {
var res *entity.StockBatch
if err = gconv.Struct(req, &res); err != nil {
return
}
ids, err = mongo.DB().Insert(ctx, []interface{}{&result}, public.StockBatchCollection)
return
}
// Update 更新批次数量(使用$inc原子操作并发安全
func (d *stockBatch) Update(ctx context.Context, req *dto.UpdateBatchReq) (err error) {
filter := bson.M{"_id": req.Id}
update := bson.M{
"$inc": bson.M{
"batchQty": req.BatchQty,
"availableQty": req.AvailableQty,
},
r, err := gfdb.DB(ctx).Model(ctx, public.StockBatchCollection).Data(&res).Insert()
if err != nil {
return
}
_, err = mongo.DB().Update(ctx, filter, update, public.StockBatchCollection)
return
return r.LastInsertId()
}
// GetOne 根据批次号查询使用NoCache跳过缓存确保获取最新数据
func (d *stockBatch) GetOne(ctx context.Context, batchNo string) (res *entity.StockBatch, err error) {
filter := bson.M{"batchNo": batchNo}
err = mongo.DB().NoCache().FindOne(ctx, filter, &res, public.StockBatchCollection)
return
}
// GetOneById 根据ID查询批次
func (d *stockBatch) GetOneById(ctx context.Context, req *dto.GetBatchReq) (res *entity.StockBatch, err error) {
filter := bson.M{"_id": req.Id}
err = mongo.DB().FindOne(ctx, filter, &res, public.StockBatchCollection)
return
}
// DeleteFake 软删除批次
func (d *stockBatch) DeleteFake(ctx context.Context, req *dto.DeleteBatchReq) error {
filter := bson.M{"_id": req.Id}
_, err := mongo.DB().DeleteSoft(ctx, filter, public.StockBatchCollection)
return err
}
// List 查询批次列表
func (d *stockBatch) List(ctx context.Context, req *dto.ListBatchReq) (res []entity.StockBatch, total int64, err error) {
filter := bson.M{}
if req.AssetId != nil {
filter["assetId"] = req.AssetId
func (d *stockBatch) Update(ctx context.Context, req *dto.UpdateSockBatchReq) (rows int64, err error) {
model := gfdb.DB(ctx).Model(ctx, public.StockBatchCollection).OmitEmpty().Where(entity.StockBatchCol.Id, req.Id)
model.Data(entity.StockBatchCol.BatchQty, &gdb.Counter{
Field: entity.StockBatchCol.BatchQty,
Value: gconv.Float64(req.BatchQty),
})
model.Data(entity.StockBatchCol.AvailableQty, &gdb.Counter{
Field: entity.StockBatchCol.AvailableQty,
Value: gconv.Float64(req.AvailableQty),
})
r, err := model.Update()
if err != nil {
return
}
if req.AssetSkuId != nil {
filter["assetSkuId"] = req.AssetSkuId
return r.RowsAffected()
}
// One 根据批次号查询使用NoCache跳过缓存确保获取最新数据
func (d *stockBatch) One(ctx context.Context, req *dto.GetSockBatchReq, fields ...string) (res *entity.StockBatch, err error) {
model := gfdb.DB(ctx).Model(ctx, public.StockBatchCollection)
if !g.IsEmpty(req.Id) {
model.Where(entity.StockBatchCol.Id, req.Id)
}
total, err = mongo.DB().Find(ctx, filter, &res, public.StockBatchCollection, req.Page, req.OrderBy)
if !g.IsEmpty(req.BatchNo) {
model.Where(entity.StockBatchCol.BatchNo, req.BatchNo)
}
r, err := model.Fields(fields).One()
if err != nil {
return
}
err = r.Struct(&res)
return
}

View File

@@ -6,15 +6,13 @@ package dao
import (
"assets/consts/public"
"assets/consts/stock"
dto "assets/model/dto/stock"
entity "assets/model/entity/stock"
"context"
"gitea.com/red-future/common/beans"
"gitea.com/red-future/common/db/mongo"
"github.com/gogf/gf/v2/frame/g"
"go.mongodb.org/mongo-driver/v2/bson"
"gitea.com/red-future/common/db/gfdb"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/util/gconv"
)
var StockDetails = new(stockDetails)
@@ -23,58 +21,52 @@ type stockDetails struct {
}
// BatchInsert 批量插入库存
func (d *stockDetails) BatchInsert(ctx context.Context, stockInterfaces []interface{}) (ids []interface{}, err error) {
ids, err = mongo.DB().Insert(ctx, stockInterfaces, public.StockDetailsCollection)
func (d *stockDetails) BatchInsert(ctx context.Context, req []*dto.CreateSockDetailsReq) (rows int64, err error) {
var res []*entity.StockDetails
if err = gconv.Structs(req, &res); err != nil {
return
}
r, err := gfdb.DB(ctx).Model(ctx, public.StockDetailsCollection).Data(res).Save()
if err != nil {
return
}
return r.RowsAffected()
}
func (d *stockDetails) Delete(ctx context.Context, req []dto.DeleteSockDetailsReq) (rows int64, err error) {
r, err := gfdb.DB(ctx).Model(ctx, public.StockDetailsCollection).Where(entity.StockDetailsCol.Id, &req).Delete()
if err != nil {
return
}
return r.RowsAffected()
}
func (d *stockDetails) Count(ctx context.Context, req *dto.GetSockDetailsReq) (count int, err error) {
return d.buildListFilter(ctx, req).Count()
}
func (d *stockDetails) List(ctx context.Context, req *dto.GetSockDetailsReq, fields ...string) (res []entity.StockDetails, total int, err error) {
model := d.buildListFilter(ctx, req)
model.Fields(fields)
model.OrderDesc(entity.StockDetailsCol.CreatedAt)
if req.Page != nil {
model.Page(int(req.Page.PageNum), int(req.Page.PageSize))
}
r, total, err := model.AllAndCount(false)
if err != nil {
return
}
err = r.Structs(&res)
return
}
// DeleteManyByIds 根据ID批量删除库存
func (d *stockDetails) DeleteManyByIds(ctx context.Context, allStockIds []*bson.ObjectID) (count int64, err error) {
if len(allStockIds) == 0 {
return 0, nil
}
filter := bson.M{"_id": bson.M{"$in": allStockIds}}
count, err = mongo.DB().Delete(ctx, filter, public.StockDetailsCollection)
return
}
// GetStockCountBySkuId 获取库存数根据SKU ID
func (d *stockDetails) GetStockCountBySkuId(ctx context.Context, assetSkuId int64) (total int64, err error) {
// 构建查询过滤条件
filter := bson.M{}
filter["assetSkuId"] = assetSkuId
filter["status"] = stock.StockStatusAvailable
// 检查总数
total, err = mongo.DB().NoCache().Count(ctx, filter, public.StockDetailsCollection)
return total, err
}
// GetOneById 根据ID查询库存明细
func (d *stockDetails) GetOneById(ctx context.Context, req *dto.GetStockDetailsReq) (res *entity.StockDetails, err error) {
filter := bson.M{"_id": req.Id}
err = mongo.DB().FindOne(ctx, filter, &res, public.StockDetailsCollection)
return
}
// List 获取SKU列表
func (d *stockDetails) List(ctx context.Context, req *dto.ListStockDetailsReq) (res []entity.StockDetails, total int64, err error) {
// 构建查询过滤条件
filter := bson.M{}
if !g.IsEmpty(req.AssetId) {
filter["assetId"] = req.AssetId
}
if !g.IsEmpty(req.AssetSkuId) {
filter["assetSkuId"] = req.AssetSkuId
}
if !g.IsEmpty(req.Status) {
filter["status"] = req.Status
}
// 排序处理
req.OrderBy = []beans.OrderBy{
{Field: "sort", Order: beans.Asc},
{Field: "createdAt", Order: beans.Desc},
}
total, err = mongo.DB().Find(ctx, filter, &res, public.StockDetailsCollection, req.Page, req.OrderBy)
return
// buildListFilter 构建列表查询的过滤条件
func (d *stockDetails) buildListFilter(ctx context.Context, req *dto.GetSockDetailsReq) *gdb.Model {
model := gfdb.DB(ctx).Model(ctx, public.StockDetailsCollection).Model
model.Where(entity.StockDetailsCol.Id, req.Id)
model.Where(entity.StockDetailsCol.AssetId, req.AssetId)
model.Where(entity.StockDetailsCol.AssetSkuId, req.AssetSkuId)
model.Where(entity.StockDetailsCol.Status, req.Status)
model.OmitEmptyWhere()
return model
}

51
go.mod
View File

@@ -4,14 +4,17 @@ go 1.25.7
require (
gitea.com/red-future/common v0.0.4
github.com/bjang03/gmq v0.0.0-20251219093200-000000000000
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.10.0
github.com/gogf/gf/v2 v2.10.0
github.com/redis/go-redis/v9 v9.12.1
github.com/xuri/excelize/v2 v2.10.0
go.mongodb.org/mongo-driver/v2 v2.4.1
github.com/redis/go-redis/v9 v9.18.0
github.com/xuri/excelize/v2 v2.10.1
go.mongodb.org/mongo-driver/v2 v2.5.0
)
//replace gitea.com/red-future/common v0.0.4 => ../common
replace gitea.com/red-future/common v0.0.4 => ../common
replace github.com/bjang03/gmq => ../gmq
require (
github.com/BurntSushi/toml v1.5.0 // indirect
@@ -25,11 +28,15 @@ require (
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/emirpasic/gods/v2 v2.0.0-alpha // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/fatih/color v1.19.0 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.13 // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.30.1 // indirect
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/gogf/gf/contrib/nosql/redis/v2 v2.9.1 // indirect
github.com/gogf/gf/contrib/registry/consul/v2 v2.9.5 // indirect
@@ -54,33 +61,39 @@ require (
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
github.com/hashicorp/golang-lru v1.0.2 // indirect
github.com/hashicorp/serf v0.10.1 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/klauspost/compress v1.18.4 // indirect
github.com/klauspost/cpuid/v2 v2.2.11 // indirect
github.com/klauspost/crc32 v1.3.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/lib/pq v1.10.9 // indirect
github.com/magiconair/properties v1.8.10 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/minio/crc64nvme v1.1.0 // indirect
github.com/minio/crc64nvme v1.1.1 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/minio/minio-go/v7 v7.0.97 // indirect
github.com/minio/minio-go/v7 v7.0.99 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/nats-io/nats.go v1.49.0 // indirect
github.com/nats-io/nkeys v0.4.15 // indirect
github.com/nats-io/nuid v1.0.1 // indirect
github.com/olekukonko/errors v1.1.0 // indirect
github.com/olekukonko/ll v0.0.9 // indirect
github.com/olekukonko/tablewriter v1.1.0 // indirect
github.com/philhofer/fwd v1.2.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/richardlehane/mscfb v1.0.4 // indirect
github.com/richardlehane/msoleps v1.0.4 // indirect
github.com/rabbitmq/amqp091-go v1.10.0 // indirect
github.com/richardlehane/mscfb v1.0.6 // indirect
github.com/richardlehane/msoleps v1.0.6 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rs/xid v1.6.0 // indirect
github.com/spf13/cast v1.10.0 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/tiendc/go-deepcopy v1.7.1 // indirect
github.com/tiendc/go-deepcopy v1.7.2 // indirect
github.com/tiger1103/gfast-token v1.0.10 // indirect
github.com/tinylib/msgp v1.3.0 // indirect
github.com/tinylib/msgp v1.6.1 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.2 // indirect
github.com/xdg-go/scram v1.2.0 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/xuri/efp v0.0.1 // indirect
github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 // indirect
@@ -94,12 +107,14 @@ require (
go.opentelemetry.io/otel/sdk v1.38.0 // indirect
go.opentelemetry.io/otel/trace v1.38.0 // indirect
go.opentelemetry.io/proto/otlp v1.7.1 // indirect
golang.org/x/crypto v0.44.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/crypto v0.48.0 // indirect
golang.org/x/exp v0.0.0-20250808145144-a408d31f581a // indirect
golang.org/x/net v0.47.0 // indirect
golang.org/x/sync v0.18.0 // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/text v0.31.0 // indirect
golang.org/x/net v0.50.0 // indirect
golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.42.0 // indirect
golang.org/x/text v0.34.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect
google.golang.org/grpc v1.75.0 // indirect

60
go.sum
View File

@@ -63,8 +63,12 @@ github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w=
github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM=
github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
@@ -76,6 +80,12 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w=
github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
@@ -189,6 +199,8 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c=
github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.11 h1:0OwqZRYI2rFrjS4kvkDnqJkKHdHaRnCm68/DY4OxRzU=
github.com/klauspost/cpuid/v2 v2.2.11/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
@@ -203,6 +215,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE=
@@ -230,10 +244,14 @@ github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=
github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs=
github.com/minio/crc64nvme v1.1.0 h1:e/tAguZ+4cw32D+IO/8GSf5UVr9y+3eJcxZI2WOO/7Q=
github.com/minio/crc64nvme v1.1.0/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg=
github.com/minio/crc64nvme v1.1.1 h1:8dwx/Pz49suywbO+auHCBpCtlW1OfpcLN7wYgVR6wAI=
github.com/minio/crc64nvme v1.1.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg=
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
github.com/minio/minio-go/v7 v7.0.97 h1:lqhREPyfgHTB/ciX8k2r8k0D93WaFqxbJX36UZq5occ=
github.com/minio/minio-go/v7 v7.0.97/go.mod h1:re5VXuo0pwEtoNLsNuSr0RrLfT/MBtohwdaSmPPSRSk=
github.com/minio/minio-go/v7 v7.0.99 h1:2vH/byrwUkIpFQFOilvTfaUpvAX3fEFhEzO+DR3DlCE=
github.com/minio/minio-go/v7 v7.0.99/go.mod h1:EtGNKtlX20iL2yaYnxEigaIvj0G0GwSDnifnG8ClIdw=
github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
@@ -243,6 +261,12 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nats-io/nats.go v1.49.0 h1:yh/WvY59gXqYpgl33ZI+XoVPKyut/IcEaqtsiuTJpoE=
github.com/nats-io/nats.go v1.49.0/go.mod h1:fDCn3mN5cY8HooHwE2ukiLb4p4G4ImmzvXyJt+tGwdw=
github.com/nats-io/nkeys v0.4.15 h1:JACV5jRVO9V856KOapQ7x+EY8Jo3qw1vJt/9Jpwzkk4=
github.com/nats-io/nkeys v0.4.15/go.mod h1:CpMchTXC9fxA5zrMo4KpySxNjiDVvr8ANOSZdiNfUrs=
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM=
github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
github.com/olekukonko/ll v0.0.9 h1:Y+1YqDfVkqMWuEQMclsF9HUR5+a82+dxJuL1HHSRpxI=
@@ -275,13 +299,21 @@ github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8b
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzukfVhBw=
github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o=
github.com/redis/go-redis/v9 v9.12.1 h1:k5iquqv27aBtnTm2tIkROUDp8JBXhXZIVu1InSgvovg=
github.com/redis/go-redis/v9 v9.12.1/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
github.com/redis/go-redis/v9 v9.18.0 h1:pMkxYPkEbMPwRdenAzUNyFNrDgHx9U+DrBabWNfSRQs=
github.com/redis/go-redis/v9 v9.18.0/go.mod h1:k3ufPphLU5YXwNTUcCRXGxUoF1fqxnhFQmscfkCoDA0=
github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM=
github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk=
github.com/richardlehane/mscfb v1.0.6 h1:eN3bvvZCp00bs7Zf52bxNwAx5lJDBK1tCuH19qq5aC8=
github.com/richardlehane/mscfb v1.0.6/go.mod h1:pe0+IUIc0AHh0+teNzBlJCtSyZdFOGgV4ZK9bsoV+Jo=
github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
github.com/richardlehane/msoleps v1.0.4 h1:WuESlvhX3gH2IHcd8UqyCuFY5yiq/GR/yqaSM/9/g00=
github.com/richardlehane/msoleps v1.0.4/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
github.com/richardlehane/msoleps v1.0.6 h1:9BvkpjvD+iUBalUY4esMwv6uBkfOip/Lzvd93jvR9gg=
github.com/richardlehane/msoleps v1.0.6/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
@@ -294,6 +326,8 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUt
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
@@ -307,21 +341,29 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/tiendc/go-deepcopy v1.7.1 h1:LnubftI6nYaaMOcaz0LphzwraqN8jiWTwm416sitff4=
github.com/tiendc/go-deepcopy v1.7.1/go.mod h1:4bKjNC2r7boYOkD2IOuZpYjmlDdzjbpTRyCx+goBCJQ=
github.com/tiendc/go-deepcopy v1.7.2 h1:Ut2yYR7W9tWjTQitganoIue4UGxZwCcJy3orjrrIj44=
github.com/tiendc/go-deepcopy v1.7.2/go.mod h1:4bKjNC2r7boYOkD2IOuZpYjmlDdzjbpTRyCx+goBCJQ=
github.com/tiger1103/gfast-token v1.0.10 h1:fNiBE/Dq5iTHvTGlCx3DmXa2o4hr0NtumFpffZ39k6s=
github.com/tiger1103/gfast-token v1.0.10/go.mod h1:a/21mxmj7zFeNvjhZSC0XpEAFHfb1aT2k6DXnufFU1s=
github.com/tinylib/msgp v1.3.0 h1:ULuf7GPooDaIlbyvgAxBV/FI7ynli6LZ1/nVUNu+0ww=
github.com/tinylib/msgp v1.3.0/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0=
github.com/tinylib/msgp v1.6.1 h1:ESRv8eL3u+DNHUoSAAQRE50Hm162zqAnBoGv9PzScPY=
github.com/tinylib/msgp v1.6.1/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
github.com/xdg-go/scram v1.2.0 h1:bYKF2AEwG5rqd1BumT4gAnvwU/M9nBp2pTSxeZw7Wvs=
github.com/xdg-go/scram v1.2.0/go.mod h1:3dlrS0iBaWKYVt2ZfA4cj48umJZ+cAEbR6/SjLA88I8=
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
github.com/xuri/efp v0.0.1 h1:fws5Rv3myXyYni8uwj2qKjVaRP30PdjeYe2Y6FDsCL8=
github.com/xuri/efp v0.0.1/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
github.com/xuri/excelize/v2 v2.10.0 h1:8aKsP7JD39iKLc6dH5Tw3dgV3sPRh8uRVXu/fMstfW4=
github.com/xuri/excelize/v2 v2.10.0/go.mod h1:SC5TzhQkaOsTWpANfm+7bJCldzcnU/jrhqkTi/iBHBU=
github.com/xuri/excelize/v2 v2.10.1 h1:V62UlqopMqha3kOpnlHy2CcRVw1V8E63jFoWUmMzxN0=
github.com/xuri/excelize/v2 v2.10.1/go.mod h1:iG5tARpgaEeIhTqt3/fgXCGoBRt4hNXgCp3tfXKoOIc=
github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 h1:+C0TIdyyYmzadGaL/HBLbf3WdLgC29pgyhTjAT/0nuE=
github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
@@ -331,6 +373,8 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.mongodb.org/mongo-driver/v2 v2.4.1 h1:hGDMngUao03OVQ6sgV5csk+RWOIkF+CuLsTPobNMGNI=
go.mongodb.org/mongo-driver/v2 v2.4.1/go.mod h1:jHeEDJHJq7tm6ZF45Issun9dbogjfnPySb1vXA7EeAI=
go.mongodb.org/mongo-driver/v2 v2.5.0 h1:yXUhImUjjAInNcpTcAlPHiT7bIXhshCTL3jVBkF3xaE=
go.mongodb.org/mongo-driver/v2 v2.5.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0=
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
@@ -351,8 +395,12 @@ go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJr
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4=
go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
@@ -361,6 +409,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU=
golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc=
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20250808145144-a408d31f581a h1:Y+7uR/b1Mw2iSXZ3G//1haIiSElDQZ8KWh0h+sZPG90=
golang.org/x/exp v0.0.0-20250808145144-a408d31f581a/go.mod h1:rT6SFzZ7oxADUDx58pcaKFTcZ+inxAa9fTrYx/uVYwg=
@@ -391,6 +441,9 @@ golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -402,6 +455,8 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -431,6 +486,9 @@ golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -441,6 +499,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=

21
main.go
View File

@@ -1,11 +1,13 @@
package main
import (
"assets/consts/public"
assetController "assets/controller/asset"
enumController "assets/controller/enum"
procurementController "assets/controller/procurement"
stockController "assets/controller/stock"
syncController "assets/controller/sync"
stockService "assets/service/stock"
"context"
"os"
"os/signal"
@@ -13,10 +15,11 @@ import (
"gitea.com/red-future/common/http"
"gitea.com/red-future/common/jaeger"
_ "gitea.com/red-future/common/swagger"
gmq "github.com/bjang03/gmq/core/gmq"
"github.com/bjang03/gmq/mq"
"github.com/bjang03/gmq/types"
_ "github.com/gogf/gf/contrib/drivers/pgsql/v2"
"github.com/gogf/gf/v2/frame/g"
)
@@ -86,6 +89,20 @@ func main() {
// })
//}
gmq.Init("config.yml")
err := gmq.GetGmq("primary").GmqSubscribe(ctx, &mq.RedisSubMessage{
SubMessage: types.SubMessage{
Topic: public.StockDetailQueueName,
ConsumerName: public.StockDetailConsumerName,
AutoAck: public.StockDetailAutoAck,
FetchCount: public.StockDetailPrefetchCount,
HandleFunc: stockService.StockManage.AddStock,
},
})
if err != nil {
return
}
// 监听退出信号
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)

View File

@@ -5,6 +5,7 @@ import (
"assets/consts/stock"
"assets/model/config"
enumDto "assets/model/dto/enum"
entity "assets/model/entity/asset"
"gitea.com/red-future/common/beans"
"github.com/gogf/gf/v2/frame/g"
@@ -15,18 +16,18 @@ import (
type CreateAssetReq struct {
g.Meta `path:"/createAsset" method:"post" tags:"资产管理" summary:"创建资产" dc:"创建新的资产"`
// 基础信息
Name string `json:"name" v:"required" dc:"资产名称"`
Description string `json:"description" dc:"资产描述"`
Type consts.AssetType `json:"type" v:"required" dc:"资产类型physical实物/virtual虚拟/service服务"`
CategoryId int64 `json:"categoryId" v:"required" dc:"分类ID"`
CategoryPath string `json:"categoryPath" dc:"分类路径"`
ImageURL string `json:"imageUrl" dc:"主图URL"`
Images []string `json:"images" dc:"图片列表"`
Status consts.AssetStatusType `json:"status" dc:"状态1/0" d:"1"`
UnlimitedStock bool `json:"unlimitedStock" dc:"是否无库存限制"`
StockMode stock.StockMode `json:"stockMode" dc:"库存管理模式1-明细模式 2-批次模式" d:"2"`
OnlineTime *gtime.Time `json:"onlineTime,omitempty" dc:"上线时间格式2006-01-02 15:04:05"`
OfflineTime *gtime.Time `json:"offlineTime,omitempty" dc:"下线时间格式2006-01-02 15:04:05"`
Name string `json:"name" v:"required" dc:"资产名称"`
Description string `json:"description" dc:"资产描述"`
Type consts.AssetType `json:"type" v:"required" dc:"资产类型physical实物/virtual虚拟/service服务"`
CategoryId int64 `json:"categoryId" v:"required" dc:"分类ID"`
CategoryPath string `json:"categoryPath" dc:"分类路径"`
ImageURL string `json:"imageUrl" dc:"主图URL"`
Images []string `json:"images" dc:"图片列表"`
Status consts.AssetStatus `json:"status" dc:"状态1/0" d:"1"`
UnlimitedStock bool `json:"unlimitedStock" dc:"是否无库存限制"`
StockMode stock.StockMode `json:"stockMode" dc:"库存管理模式1-明细模式 2-批次模式" d:"2"`
OnlineTime *gtime.Time `json:"onlineTime,omitempty" dc:"上线时间格式2006-01-02 15:04:05"`
OfflineTime *gtime.Time `json:"offlineTime,omitempty" dc:"下线时间格式2006-01-02 15:04:05"`
// 类型专用配置 - 实物资产配置
PhysicalAssetConfig *config.PhysicalAssetConfig `json:"physicalAssetConfig"`
// 类型专用配置 - 服务资产配置
@@ -52,7 +53,7 @@ type ListAssetReq struct {
Type consts.AssetType `json:"type" dc:"资产类型"`
CategoryId int64 `json:"categoryId" dc:"分类ID"`
CategoryPath string `json:"categoryPath" dc:"分类路径"`
Status consts.AssetStatusType `json:"status" dc:"状态"`
Status consts.AssetStatus `json:"status" dc:"状态"`
TenantModuleType beans.TenantModuleType `json:"tenantModuleType" dc:"租户模块类型"`
Keyword string `json:"keyword" dc:"关键词搜索"`
}
@@ -64,18 +65,18 @@ type ListAssetRes struct {
}
type AssetItem struct {
Id int64 `json:"id"` // 资产ID
Name string `json:"name"` // 资产名称
Type consts.AssetType `json:"type"` // 资产类型physical实物/virtual虚拟/service服务
TypeName string `json:"typeName"` // 资产类型physical实物/virtual虚拟/service服务
CategoryId int64 `json:"categoryId"` // 分类ID
UnlimitedStock bool `json:"unlimitedStock"` // 是否无库存限制
Status consts.AssetStatusType `json:"status"` // 资产状态active启用/inactive停用
BasePrice int `json:"basePrice"` // 基础价格(分为单位)
OnlineTime *gtime.Time `json:"onlineTime,omitempty"` // 上线时间
OfflineTime *gtime.Time `json:"offlineTime,omitempty"` // 下线时间
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间"`
Id int64 `json:"id,string"` // 资产ID
Name string `json:"name"` // 资产名称
Type consts.AssetType `json:"type"` // 资产类型physical实物/virtual虚拟/service服务
TypeName string `json:"typeName"` // 资产类型physical实物/virtual虚拟/service服务
CategoryId int64 `json:"categoryId,string"` // 分类ID
UnlimitedStock bool `json:"unlimitedStock"` // 是否无库存限制
Status consts.AssetStatus `json:"status"` // 资产状态active启用/inactive停用
BasePrice int `json:"basePrice"` // 基础价格(分为单位)
OnlineTime *gtime.Time `json:"onlineTime,omitempty"` // 上线时间
OfflineTime *gtime.Time `json:"offlineTime,omitempty"` // 下线时间
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间"`
}
// GetAssetReq 获取资产详情请求
@@ -86,7 +87,7 @@ type GetAssetReq struct {
// GetAssetRes 获取资产详情响应
type GetAssetRes struct {
*AssetItem
*entity.Asset
CategoryName string `json:"categoryName" dc:"分类名称"`
ImgAddressPrefix string `json:"imgAddressPrefix"`
}
@@ -99,7 +100,7 @@ type GetAssetAndSkuReq struct {
// GetAssetAndSkuRes 获取资产详情响应
type GetAssetAndSkuRes struct {
*AssetItem
*entity.Asset
Skus []AssetSkuItem `json:"skus" dc:"SKU列表"`
TenantModuleType []enumDto.KeyValue `json:"tenantModuleType" dc:"租户模块类型"`
ImgAddressPrefix string `json:"imgAddressPrefix"`
@@ -108,16 +109,16 @@ type GetAssetAndSkuRes struct {
// UpdateAssetReq 更新资产请求
type UpdateAssetReq struct {
g.Meta `path:"/updateAsset" method:"put" tags:"资产管理" summary:"更新资产" dc:"更新资产信息"`
Id int64 `json:"id" v:"required" dc:"资产ID"`
Name string `json:"name" dc:"资产名称"`
Description string `json:"description" dc:"资产描述"`
Type consts.AssetType `json:"type" dc:"资产类型physical实物/virtual虚拟/service服务"`
CategoryId int64 `json:"categoryId" dc:"分类ID"`
ImageURL string `json:"imageUrl" dc:"主图URL"`
Images []string `json:"images" dc:"图片列表"`
Status consts.AssetStatusType `json:"status,omitempty" dc:"状态1/0"`
OnlineTime *gtime.Time `json:"onlineTime,omitempty" dc:"上线时间格式2006-01-02 15:04:05"`
OfflineTime *gtime.Time `json:"offlineTime,omitempty" dc:"下线时间格式2006-01-02 15:04:05"`
Id int64 `json:"id" v:"required" dc:"资产ID"`
Name string `json:"name" dc:"资产名称"`
Description string `json:"description" dc:"资产描述"`
Type consts.AssetType `json:"type" dc:"资产类型physical实物/virtual虚拟/service服务"`
CategoryId int64 `json:"categoryId" dc:"分类ID"`
ImageURL string `json:"imageUrl" dc:"主图URL"`
Images []string `json:"images" dc:"图片列表"`
Status consts.AssetStatus `json:"status,omitempty" dc:"状态1/0"`
OnlineTime *gtime.Time `json:"onlineTime,omitempty" dc:"上线时间格式2006-01-02 15:04:05"`
OfflineTime *gtime.Time `json:"offlineTime,omitempty" dc:"下线时间格式2006-01-02 15:04:05"`
// 类型专用配置 - 实物资产配置
PhysicalAssetConfig *config.PhysicalAssetConfig `json:"physicalAssetConfig"`
// 类型专用配置 - 服务资产配置
@@ -131,8 +132,8 @@ type UpdateAssetReq struct {
// UpdateAssetStatusReq 更新资产状态请求
type UpdateAssetStatusReq struct {
g.Meta `path:"/updateAssetStatus" method:"put" tags:"资产管理" summary:"更新资产状态" dc:"更新资产状态"`
Id int64 `json:"id" v:"required" dc:"资产ID"`
Status consts.AssetStatusType `json:"status" v:"required|in:1,0" dc:"状态1启用/0停用"`
Id int64 `json:"id" v:"required" dc:"资产ID"`
Status consts.AssetStatus `json:"status" v:"required|in:1,0" dc:"状态1启用/0停用"`
}
// DeleteAssetReq 删除资产请求

View File

@@ -23,7 +23,7 @@ type CreateAssetSkuReq struct {
ImageURL string `json:"imageUrl" v:"required" dc:"SKU主图"`
Price int `json:"price" v:"required|min:0" dc:"价格(分为单位)"`
Sort int `json:"sort" v:"required|min:0" dc:"排序"`
Status consts.AssetSkuStatusType `json:"status" v:"required|in:1,0" dc:"状态"`
Status consts.AssetSkuStatus `json:"status" v:"required|in:1,0" dc:"状态"`
UnlimitedStock bool `json:"unlimitedStock" v:"required" dc:"是否无库存限制"`
StockMode stock.StockMode `json:"stockMode" dc:"库存管理模式:1-明细模式 2-批次模式"`
CategoryId int64 `json:"categoryId" dc:"分类ID"`
@@ -49,7 +49,7 @@ type UpdateAssetSkuReq struct {
Price int `json:"price" dc:"价格(分为单位)"`
Sort int `json:"sort" dc:"排序"`
Stock int `json:"stock" dc:"库存数量"`
Status consts.AssetSkuStatusType `json:"status" dc:"状态"`
Status consts.AssetSkuStatus `json:"status" dc:"状态"`
}
// DeleteAssetSkuReq 删除SKU请求
@@ -86,13 +86,13 @@ type GetAssetSkuRes struct {
type ListAssetSkuReq struct {
g.Meta `path:"/listAssetSkus" method:"get" tags:"SKU管理" summary:"获取SKU列表" dc:"分页查询SKU列表支持多条件筛选"`
*beans.Page
Id int64 `json:"id" dc:"SKU ID"`
AssetId int64 `json:"assetId" v:"required" dc:"资产ID"`
Status consts.AssetSkuStatusType `json:"status" dc:"状态"`
Keyword string `json:"keyword" dc:"关键词搜索"`
MinPrice int `json:"minPrice" dc:"最低价格"`
MaxPrice int `json:"maxPrice" dc:"最高价格"`
CategoryPath string `json:"categoryPath" dc:"分类路径"`
Id int64 `json:"id" dc:"SKU ID"`
AssetId int64 `json:"assetId" v:"required" dc:"资产ID"`
Status consts.AssetSkuStatus `json:"status" dc:"状态"`
Keyword string `json:"keyword" dc:"关键词搜索"`
MinPrice int `json:"minPrice" dc:"最低价格"`
MaxPrice int `json:"maxPrice" dc:"最高价格"`
CategoryPath string `json:"categoryPath" dc:"分类路径"`
}
// ListAssetSkuRes 获取SKU列表响应
@@ -102,8 +102,8 @@ type ListAssetSkuRes struct {
}
type AssetSkuItem struct {
Id int64 `json:"id"` // SKU ID
AssetId int64 `json:"assetId"`
Id int64 `json:"id,string"` // SKU ID
AssetId int64 `json:"assetId,string"`
AssetName string `json:"assetName"` // 资产名称
SkuName string `json:"skuName"` // SKU名称
SpecsCount int `json:"specsCount"` // 规格数量
@@ -113,8 +113,9 @@ type AssetSkuItem struct {
UnlimitedStock bool `json:"unlimitedStock"` // 是否无库存限制
Stock int `json:"stock"` // 库存数量
Sort int `json:"sort"` // 排序
Status consts.AssetSkuStatusType `json:"status"` // 状态active/inactive/disabled
Status consts.AssetSkuStatus `json:"status"` // 状态active/inactive/disabled
StockMode stock.StockMode `json:"stockMode"` // 库存管理模式1-明细模式 2-批次模式
ImageURL string `json:"imageUrl"` // SKU主图
CreatedAt *gtime.Time `json:"createdAt"` // 创建时间
UpdatedAt *gtime.Time `json:"updatedAt"` // 更新时间
}

View File

@@ -12,15 +12,15 @@ import (
// CreateCategoryReq 创建分类请求
type CreateCategoryReq struct {
g.Meta `path:"/createCategory" method:"post" tags:"分类管理" summary:"创建分类" dc:"创建新的分类"`
Name string `json:"name" v:"required" dc:"分类名称"`
ParentId int64 `json:"parentId" dc:"父分类ID"`
IsLeafNode *bool `json:"isLeafNode" dc:"是否叶子节点"`
Image string `json:"image" dc:"分类图片"`
Sort int `json:"sort" dc:"排序"`
Path string `json:"path" dc:"分类路径"`
Level int `json:"level" dc:"分类层级"`
Status consts.CategoryStatusType `json:"status" v:"in:1,0" default:"1" dc:"状态1启用0禁用"`
Attrs []entity.CategoryAttr `json:"attrs" dc:"分类属性"`
Name string `json:"name" v:"required" dc:"分类名称"`
ParentId int64 `json:"parentId" dc:"父分类ID"`
IsLeafNode *bool `json:"isLeafNode" dc:"是否叶子节点"`
Image string `json:"image" dc:"分类图片"`
Sort int `json:"sort" dc:"排序"`
Path string `json:"path" dc:"分类路径"`
Level int `json:"level" dc:"分类层级"`
Status consts.CategoryStatus `json:"status" v:"in:1,0" default:"1" dc:"状态1启用0禁用"`
Attrs []entity.CategoryAttr `json:"attrs" dc:"分类属性"`
}
// CreateCategoryRes 创建分类响应
@@ -31,21 +31,21 @@ type CreateCategoryRes struct {
// UpdateCategoryReq 更新分类请求
type UpdateCategoryReq struct {
g.Meta `path:"/updateCategory" method:"put" tags:"分类管理" summary:"更新分类" dc:"更新分类信息"`
Id int64 `json:"id" v:"required" dc:"分类ID"`
Name string `json:"name" dc:"分类名称"`
ParentId int64 `json:"parentId" dc:"父分类ID"`
Image string `json:"image" dc:"分类图片"`
Sort int `json:"sort" dc:"排序"`
IsLeafNode *bool `json:"isLeafNode" dc:"是否叶子节点"`
Attrs []entity.CategoryAttr `json:"attrs" dc:"分类属性"`
Status consts.CategoryStatusType `json:"status" dc:"状态1启用0禁用"`
Id int64 `json:"id" v:"required" dc:"分类ID"`
Name string `json:"name" dc:"分类名称"`
ParentId int64 `json:"parentId" dc:"父分类ID"`
Image string `json:"image" dc:"分类图片"`
Sort int `json:"sort" dc:"排序"`
IsLeafNode *bool `json:"isLeafNode" dc:"是否叶子节点"`
Attrs []entity.CategoryAttr `json:"attrs" dc:"分类属性"`
Status consts.CategoryStatus `json:"status" dc:"状态1启用0禁用"`
}
// UpdateCategoryStatusReq 更新分类状态请求
type UpdateCategoryStatusReq struct {
g.Meta `path:"/updateCategoryStatus" method:"put" tags:"分类管理" summary:"更新分类状态" dc:"更新分类状态"`
Id int64 `json:"id" v:"required" dc:"分类ID"`
Status consts.CategoryStatusType `json:"status" v:"in:1,0" dc:"状态1启用0禁用"`
Id int64 `json:"id" v:"required" dc:"分类ID"`
Status consts.CategoryStatus `json:"status" v:"in:1,0" dc:"状态1启用0禁用"`
}
// DeleteCategoryReq 删除分类请求
@@ -67,26 +67,26 @@ type GetCategoryTreeRes struct {
// CategoryTreeNode 分类树节点
type CategoryTreeNode struct {
Id int64 `json:"id,string" dc:"分类ID"`
Name string `json:"name" dc:"分类名称"`
Level int `json:"level" dc:"分类层级"`
Type string `json:"type" dc:"分类类型"`
Path string `json:"path" dc:"分类路径"`
IsLeafNode bool `json:"isLeafNode" dc:"是否叶子节点"`
Status consts.CategoryStatusType `json:"status" dc:"状态"`
Sort int `json:"sort" dc:"排序"`
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间"`
Children []*CategoryTreeNode `json:"children" dc:"子分类"`
Id int64 `json:"id,string" dc:"分类ID"`
Name string `json:"name" dc:"分类名称"`
Level int `json:"level" dc:"分类层级"`
Type string `json:"type" dc:"分类类型"`
Path string `json:"path" dc:"分类路径"`
IsLeafNode bool `json:"isLeafNode" dc:"是否叶子节点"`
Status consts.CategoryStatus `json:"status" dc:"状态"`
Sort int `json:"sort" dc:"排序"`
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间"`
Children []*CategoryTreeNode `json:"children" dc:"子分类"`
}
// ListCategoryReq 获取分类列表请求
type ListCategoryReq struct {
g.Meta `path:"/listCategories" method:"get" tags:"分类管理" summary:"获取分类列表" dc:"获取分类列表"`
*beans.Page
ParentId int64 `json:"parentId" dc:"父分类ID"`
Status consts.CategoryStatusType `json:"status" dc:"状态1启用0禁用"`
Keyword string `json:"keyword" dc:"关键词搜索"`
ParentId int64 `json:"parentId" dc:"父分类ID"`
Status consts.CategoryStatus `json:"status" dc:"状态1启用0禁用"`
Keyword string `json:"keyword" dc:"关键词搜索"`
}
// ListCategoryRes 获取分类列表响应
@@ -103,18 +103,18 @@ type GetCategoryReq struct {
// GetCategoryRes 获取分类详情响应
type GetCategoryRes struct {
Id int64 `json:"id,string" dc:"分类ID"`
Name string `json:"name" dc:"分类名称"`
ParentId int64 `json:"parentId,string" dc:"父分类ID"`
Path string `json:"path" dc:"分类路径"`
Level int `json:"level" dc:"分类层级"`
IsLeafNode bool `json:"isLeafNode" dc:"是否叶子节点"`
Sort int `json:"sort" dc:"排序"`
Image string `json:"image" dc:"分类图片"`
Attrs []entity.CategoryAttr `json:"attrs" dc:"分类属性"`
Status consts.CategoryStatusType `json:"status" dc:"状态1启用0禁用"`
Creator string `json:"creator" dc:"创建人"`
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
Updater string `json:"updater" dc:"更新人"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间"`
Id int64 `json:"id,string" dc:"分类ID"`
Name string `json:"name" dc:"分类名称"`
ParentId int64 `json:"parentId,string" dc:"父分类ID"`
Path string `json:"path" dc:"分类路径"`
Level int `json:"level" dc:"分类层级"`
IsLeafNode bool `json:"isLeafNode" dc:"是否叶子节点"`
Sort int `json:"sort" dc:"排序"`
Image string `json:"image" dc:"分类图片"`
Attrs []entity.CategoryAttr `json:"attrs" dc:"分类属性"`
Status consts.CategoryStatus `json:"status" dc:"状态1启用0禁用"`
Creator string `json:"creator" dc:"创建人"`
CreatedAt *gtime.Time `json:"createdAt" dc:"创建时间"`
Updater string `json:"updater" dc:"更新人"`
UpdatedAt *gtime.Time `json:"updatedAt" dc:"更新时间"`
}

View File

@@ -3,24 +3,11 @@ package dto
import (
"assets/consts/stock"
"gitea.com/red-future/common/beans"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
"go.mongodb.org/mongo-driver/v2/bson"
)
// CommonResp 通用响应
type CommonResp struct {
Success bool `json:"success"` // 是否成功
Message string `json:"message"` // 消息
}
// --- 批次管理API相关结构 ---
// CreateBatchReq 创建批次请求
type CreateBatchReq struct {
g.Meta `path:"/createBatch" method:"post" tags:"库存批次管理" summary:"创建批次" dc:"创建新的库存批次"`
// CreateSockBatchReq 创建批次请求
type CreateSockBatchReq struct {
AssetId int64 `json:"assetId" v:"required" dc:"资产ID"`
AssetSkuId int64 `json:"assetSkuId" v:"required" dc:"SKU ID"`
BatchNo string `json:"batchNo" v:"required" dc:"批次号"`
@@ -33,58 +20,13 @@ type CreateBatchReq struct {
ExpiryWarningDate *gtime.Time `json:"expiryWarningDate" dc:"临期预警时间(格式:2006-01-02)"`
}
// CreateBatchRes 创建批次响应
type CreateBatchRes struct {
Id *bson.ObjectID `json:"id"` // 批次ID
}
// UpdateBatchReq 更新批次请求
type UpdateBatchReq struct {
g.Meta `path:"/updateBatch" method:"put" tags:"库存批次管理" summary:"更新批次" dc:"更新批次信息"`
type UpdateSockBatchReq struct {
Id int64 `json:"id" v:"required" dc:"批次ID"`
BatchQty int `json:"batchQty" v:"required|min:1" dc:"批次数量"`
AvailableQty int `json:"availableQty" v:"required|min:1" dc:"可用数量"`
}
// DeleteBatchReq 删除批次请求
type DeleteBatchReq struct {
g.Meta `path:"/deleteBatch" method:"delete" tags:"库存批次管理" summary:"删除批次" dc:"删除批次"`
Id *bson.ObjectID `json:"id" v:"required" dc:"批次ID"`
}
// GetBatchReq 获取批次详情请求
type GetBatchReq struct {
g.Meta `path:"/getBatch" method:"get" tags:"库存批次管理" summary:"获取批次详情" dc:"获取批次详情"`
Id *bson.ObjectID `json:"id" v:"required" dc:"批次ID"`
}
// GetBatchRes 获取批次详情响应
type GetBatchRes struct {
Id *bson.ObjectID `json:"id"`
AssetId *bson.ObjectID `json:"assetId"`
AssetSkuId *bson.ObjectID `json:"assetSkuId"`
BatchNo string `json:"batchNo"`
BatchQty int `json:"batchQty"`
AvailableQty int `json:"availableQty"`
Metadata []map[string]interface{} `json:"metadata"`
Status stock.BatchStatus `json:"status"`
ProductionDate *gtime.Time `json:"productionDate"`
ExpiryDate *gtime.Time `json:"expiryDate"`
ExpiryWarningDate *gtime.Time `json:"expiryWarningDate"`
}
// ListBatchReq 获取批次列表请求
type ListBatchReq struct {
g.Meta `path:"/listBatches" method:"get" tags:"库存批次管理" summary:"获取批次列表" dc:"分页查询批次列表"`
*beans.Page
OrderBy []beans.OrderBy `json:"orderBy" dc:"排序规则"`
AssetId *bson.ObjectID `json:"assetId" dc:"资产ID"`
AssetSkuId *bson.ObjectID `json:"assetSkuId" dc:"SKU ID"`
}
// ListBatchRes 获取批次列表响应
type ListBatchRes struct {
List []GetBatchRes `json:"list"`
Total int64 `json:"total"`
type GetSockBatchReq struct {
Id int64 `json:"id" v:"required" dc:"批次ID"`
BatchNo string `json:"batchNo" dc:"批次"`
}

View File

@@ -4,64 +4,25 @@ import (
"assets/consts/stock"
"gitea.com/red-future/common/beans"
"github.com/gogf/gf/v2/frame/g"
"go.mongodb.org/mongo-driver/v2/bson"
)
// GetStockDetailsReq 获取库存明细详情请求
type GetStockDetailsReq struct {
g.Meta `path:"/getStockDetails" method:"get" tags:"库存明细管理" summary:"获取库存明细详情" dc:"获取库存明细详情"`
Id *bson.ObjectID `json:"id" v:"required" dc:"库存明细ID"`
// CreateSockDetailsReq 创建明细请求
type CreateSockDetailsReq struct {
AssetId int64 `json:"assetId" v:"required" dc:"资产ID"`
AssetSkuId int64 `json:"assetSkuId" v:"required" dc:"SKU ID"`
Status stock.StockStatus `json:"status" dc:"状态"`
Metadata []map[string]interface{} `json:"metadata" dc:"元数据"`
}
// GetStockDetailsRes 获取库存明细详情响应
type GetStockDetailsRes struct {
Id *bson.ObjectID `json:"id"`
AssetId *bson.ObjectID `json:"assetId"`
AssetSkuId *bson.ObjectID `json:"assetSkuId"`
Status stock.StockStatus `json:"status"`
OrderId *bson.ObjectID `json:"orderId"`
LockExpire string `json:"lockExpire"`
Metadata map[string]interface{} `json:"metadata"`
TokenId string `json:"tokenId"`
AssignedChannel string `json:"assignedChannel"`
ChannelSKU string `json:"channelSku"`
AllocatedAt string `json:"allocatedAt"`
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
type DeleteSockDetailsReq struct {
Id int64 `json:"Id" v:"required" dc:"库存明细ID"`
}
// ListStockDetailsReq 获取库存明细列表请求
type ListStockDetailsReq struct {
g.Meta `path:"/listStockDetails" method:"get" tags:"库存明细管理" summary:"获取库存明细列表" dc:"分页查询库存明细列表,支持多条件筛选"`
type GetSockDetailsReq struct {
*beans.Page
OrderBy []beans.OrderBy `json:"orderBy" dc:"排序规则"`
Id int64 `json:"id" dc:"库存明细ID"`
AssetId int64 `json:"assetId" dc:"资产ID"`
AssetSkuId int64 `json:"assetSkuId" dc:"SKU ID"`
CategoryPath string `json:"categoryPath" dc:"分类路径"`
Status stock.StockStatus `json:"status" dc:"状态"`
}
// ListStockDetailsRes 获取库存明细列表响应
type ListStockDetailsRes struct {
List []*StockDetailsListItem `json:"list" dc:"库存明细列表"`
Total int64 `json:"total" dc:"总数"`
}
// StockDetailsListItem 库存明细列表项
type StockDetailsListItem struct {
Id *bson.ObjectID `json:"id"`
AssetId *bson.ObjectID `json:"assetId"`
AssetSkuId *bson.ObjectID `json:"assetSkuId"`
Status stock.StockStatus `json:"status"`
OrderId *bson.ObjectID `json:"orderId"`
LockExpire string `json:"lockExpire"`
Metadata map[string]interface{} `json:"metadata"`
TokenId *bson.ObjectID `json:"tokenId"`
AssignedChannel string `json:"assignedChannel"`
ChannelSKU string `json:"channelSku"`
AllocatedAt string `json:"allocatedAt"`
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
}

View File

@@ -29,8 +29,8 @@ type StockPublishMessage struct {
StockCount int `json:"stockCount"`
OperationType string `json:"operationType"`
Metadata []map[string]interface{} `json:"metadata"`
StockId string `json:"stockId"`
StockMode int `json:"stockMode"`
StockId int64 `json:"stockId"`
StockMode stock.StockMode `json:"stockMode"`
BatchNo string `json:"batchNo"`
ProductionDate *gtime.Time `json:"productionDate"`
ExpiryDate *gtime.Time `json:"expiryDate"`

View File

@@ -3,9 +3,9 @@ package entity
import (
consts "assets/consts/asset"
"assets/consts/stock"
"assets/model/config"
"gitea.com/red-future/common/beans"
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/os/gtime"
)
@@ -59,29 +59,29 @@ var AssetCol = assetCol{
type Asset struct {
beans.SQLBaseDO `orm:",inherit"`
// 基础信息
Name string `orm:"name" json:"name" description:"资产名称"`
Description string `orm:"description" json:"description" description:"资产描述"`
Type consts.AssetType `orm:"type" json:"type" description:"资产类型physical实物/virtual虚拟/service服务"`
CategoryId int64 `orm:"category_id" json:"categoryId" description:"分类ID"`
CategoryPath string `orm:"category_path" json:"categoryPath" description:"分类路径"`
ImageURL string `orm:"image_url" json:"imageUrl" description:"主图URL"`
Images []string `orm:"images" json:"images" description:"图片列表(JSONB)"`
Status consts.AssetStatusType `orm:"status" json:"status" description:"资产状态1启用/0停用"`
BasePrice int `orm:"base_price" json:"basePrice" description:"基础价格(分为单位)"`
Currency string `orm:"currency" json:"currency" description:"货币单位默认CNY"`
UnlimitedStock bool `orm:"unlimited_stock" json:"unlimitedStock" description:"是否无库存限制"`
StockMode stock.StockMode `orm:"stock_mode" json:"stockMode" description:"库存管理模式1-明细模式 2-批次模式"`
OnlineTime *gtime.Time `orm:"online_time" json:"onlineTime,omitempty" description:"上线时间"` // 上线和下线时间配置(由定时任务处理资产状态)
OfflineTime *gtime.Time `orm:"offline_time" json:"offlineTime,omitempty" description:"下线时间"` // 上线和下线时间配置(由定时任务处理资产状态)
Name string `orm:"name" json:"name" description:"资产名称"`
Description string `orm:"description" json:"description" description:"资产描述"`
Type consts.AssetType `orm:"type" json:"type" description:"资产类型physical实物/virtual虚拟/service服务"`
CategoryId int64 `orm:"category_id" json:"categoryId,string" description:"分类ID"`
CategoryPath string `orm:"category_path" json:"categoryPath" description:"分类路径"`
ImageURL string `orm:"image_url" json:"imageUrl" description:"主图URL"`
Images []string `orm:"images" json:"images" description:"图片列表(JSONB)"`
Status consts.AssetStatus `orm:"status" json:"status" description:"资产状态1启用/0停用"`
BasePrice int `orm:"base_price" json:"basePrice" description:"基础价格(分为单位)"`
Currency string `orm:"currency" json:"currency" description:"货币单位默认CNY"`
UnlimitedStock bool `orm:"unlimited_stock" json:"unlimitedStock" description:"是否无库存限制"`
StockMode stock.StockMode `orm:"stock_mode" json:"stockMode" description:"库存管理模式1-明细模式 2-批次模式"`
OnlineTime *gtime.Time `orm:"online_time" json:"onlineTime,omitempty" description:"上线时间"` // 上线和下线时间配置(由定时任务处理资产状态)
OfflineTime *gtime.Time `orm:"offline_time" json:"offlineTime,omitempty" description:"下线时间"` // 上线和下线时间配置(由定时任务处理资产状态)
// 类型专用配置 - 实物资产配置(JSONB)
PhysicalAssetConfig *gjson.Json `orm:"physical_asset_config" json:"physicalAssetConfig" description:"实物资产配置(JSONB)"`
PhysicalAssetConfig *config.PhysicalAssetConfig `orm:"physical_asset_config" json:"physicalAssetConfig" description:"实物资产配置(JSONB)"`
// 类型专用配置 - 服务资产配置(JSONB)
ServiceAssetConfig *gjson.Json `orm:"service_asset_config" json:"serviceAssetConfig" description:"服务资产配置(JSONB)"`
ServiceAssetConfig *config.ServiceAssetConfig `orm:"service_asset_config" json:"serviceAssetConfig" description:"服务资产配置(JSONB)"`
// 类型专用配置 - 虚拟资产配置(JSONB)
VirtualAssetConfig *gjson.Json `orm:"virtual_asset_config" json:"virtualAssetConfig" description:"虚拟资产配置(JSONB)"`
VirtualAssetConfig *config.VirtualAssetConfig `orm:"virtual_asset_config" json:"virtualAssetConfig" description:"虚拟资产配置(JSONB)"`
// 扩展字段(JSONB)
Metadata []gjson.Json `orm:"metadata" json:"metadata" description:"动态元数据(JSONB)"`
Metadata []map[string]interface{} `orm:"metadata" json:"metadata" description:"动态元数据(JSONB)"`
TenantModuleType beans.TenantModuleType `orm:"tenant_module_type" json:"tenantModuleType" description:"租户模块类型"`
}

View File

@@ -3,77 +3,75 @@ package entity
import (
consts "assets/consts/asset"
"assets/consts/stock"
"assets/model/config"
"gitea.com/red-future/common/beans"
"github.com/gogf/gf/v2/encoding/gjson"
)
type assetSkuCol struct {
beans.SQLBaseCol
AssetId string
AssetName string
SkuName string
ImageURL string
SpecValues string
Price string
UnlimitedStock string
Stock string
SpecsCount string
SpecsUnit string
Sort string
Status string
StockMode string
CategoryId string
CategoryPath string
CapacityUnitType string
Capacity string
AssetId string
AssetName string
SkuName string
ImageURL string
SpecValues string
Price string
UnlimitedStock string
Stock string
SpecsCount string
SpecsUnit string
Sort string
Status string
StockMode string
CategoryId string
CategoryPath string
//CapacityUnitType string
//Capacity string
TenantModuleType string
}
var AssetSkuCol = assetSkuCol{
SQLBaseCol: beans.DefSQLBaseCol,
AssetId: "asset_id",
AssetName: "asset_name",
SkuName: "sku_name",
ImageURL: "image_url",
SpecValues: "spec_values",
Price: "price",
UnlimitedStock: "unlimited_stock",
Stock: "stock",
SpecsCount: "specs_count",
SpecsUnit: "specs_unit",
Sort: "sort",
Status: "status",
StockMode: "stock_mode",
CategoryId: "category_id",
CategoryPath: "category_path",
CapacityUnitType: "capacity_unit_type",
Capacity: "capacity",
SQLBaseCol: beans.DefSQLBaseCol,
AssetId: "asset_id",
AssetName: "asset_name",
SkuName: "sku_name",
ImageURL: "image_url",
SpecValues: "spec_values",
Price: "price",
UnlimitedStock: "unlimited_stock",
Stock: "stock",
SpecsCount: "specs_count",
SpecsUnit: "specs_unit",
Sort: "sort",
Status: "status",
StockMode: "stock_mode",
CategoryId: "category_id",
CategoryPath: "category_path",
//CapacityUnitType: "capacity_unit_type",
//Capacity: "capacity",
TenantModuleType: "tenant_module_type",
}
// AssetSku 资产SKU实体
type AssetSku struct {
beans.SQLBaseDO `orm:",inherit"`
AssetId int64 `orm:"asset_id" json:"assetId"` // 关联资产ID
AssetName string `orm:"asset_name" json:"assetName"` // 资产名称
SkuName string `orm:"sku_name" json:"skuName"` // SKU名称
ImageURL string `orm:"image_url,omitempty" json:"imageUrl"` // SKU主图
SpecValues []gjson.Json `orm:"spec_values" json:"specValues"` // 规格值:{"颜色":"红色","尺寸":"L","时长":"1个月","平台":"抖音"}
Price int `orm:"price" json:"price"` // 价格(分为单位)
UnlimitedStock bool `orm:"unlimited_stock" json:"unlimitedStock"` // 是否无库存限制
Stock int `orm:"stock" json:"stock"` // 库存数量
SpecsCount int `orm:"specs_count" json:"specsCount"` // 规格数量
SpecsUnit *gjson.Json `orm:"specs_unit" json:"specsUnit"` // 规格单位 SpecsUnitKeyValue
Sort int `orm:"sort" json:"sort"` // 排序
Status consts.AssetSkuStatusType `orm:"status" json:"status"` // 状态active/inactive/disabled
StockMode stock.StockMode `orm:"stock_mode" json:"stockMode"` // 库存管理模式1-明细模式 2-批次模式
CategoryId int64 `orm:"category_id" json:"categoryId"` // 分类ID
CategoryPath string `orm:"category_path" json:"categoryPath"` // 分类路径
CapacityUnitType stock.CapacityUnitType `orm:"capacity_unit_type" json:"capacityUnitType"` // 容量单位类型
Capacity config.Capacity `orm:"capacity" json:"capacity"` // 容量
TenantModuleType beans.TenantModuleType `orm:"tenant_module_type" json:"tenantModuleType"` // 租户模块类型
beans.SQLBaseDO `orm:",inherit"`
AssetId int64 `orm:"asset_id" json:"assetId"` // 关联资产ID
AssetName string `orm:"asset_name" json:"assetName"` // 资产名称
SkuName string `orm:"sku_name" json:"skuName"` // SKU名称
ImageURL string `orm:"image_url,omitempty" json:"imageUrl"` // SKU主图
SpecValues []map[string]interface{} `orm:"spec_values" json:"specValues"` // 规格值:{"颜色":"红色","尺寸":"L","时长":"1个月","平台":"抖音"}
Price int `orm:"price" json:"price"` // 价格(分为单位)
UnlimitedStock bool `orm:"unlimited_stock" json:"unlimitedStock"` // 是否无库存限制
Stock int `orm:"stock" json:"stock"` // 库存数量
SpecsCount int `orm:"specs_count" json:"specsCount"` // 规格数量
SpecsUnit *SpecsUnitKeyValue `orm:"specs_unit" json:"specsUnit"` // 规格单位 SpecsUnitKeyValue
Sort int `orm:"sort" json:"sort"` // 排序
Status consts.AssetSkuStatus `orm:"status" json:"status"` // 状态active/inactive/disabled
StockMode stock.StockMode `orm:"stock_mode" json:"stockMode"` // 库存管理模式1-明细模式 2-批次模式
CategoryId int64 `orm:"category_id" json:"categoryId"` // 分类ID
CategoryPath string `orm:"category_path" json:"categoryPath"` // 分类路径
//CapacityUnitType stock.CapacityUnitType `orm:"capacity_unit_type" json:"capacityUnitType"` // 容量单位类型
//Capacity int `orm:"capacity" json:"capacity"` // 容量
TenantModuleType beans.TenantModuleType `orm:"tenant_module_type" json:"tenantModuleType"` // 租户模块类型
}
type SpecsUnitKeyValue struct {

View File

@@ -35,15 +35,15 @@ var CategoryCol = categoryCol{
// Category 分类实体
type Category struct {
beans.SQLBaseDO `orm:",inherit"`
Name string `orm:"name" json:"name"` // 分类名称
ParentId int64 `orm:"parent_id" json:"parentId"` // 父分类ID为空表示根分类
Path string `orm:"path" json:"path"` // 分类路径,如:/root/parent/child
Level int `orm:"level" json:"level"` // 分类层级
IsLeafNode *bool `orm:"is_leaf_node" json:"isLeafNode"` // 是叶子节点
Sort int `orm:"sort" json:"sort"` // 排序
Image string `orm:"image" json:"image"` // 分类图片
Status consts.CategoryStatusType `orm:"status" json:"status"` // 状态1启用/0禁用
Attrs []CategoryAttr `orm:"attrs" json:"attrs,omitempty"` // 分类属性 CategoryAttr
Name string `orm:"name" json:"name"` // 分类名称
ParentId int64 `orm:"parent_id" json:"parentId"` // 父分类ID为空表示根分类
Path string `orm:"path" json:"path"` // 分类路径,如:/root/parent/child
Level int `orm:"level" json:"level"` // 分类层级
IsLeafNode *bool `orm:"is_leaf_node" json:"isLeafNode"` // 是叶子节点
Sort int `orm:"sort" json:"sort"` // 排序
Image string `orm:"image" json:"image"` // 分类图片
Status consts.CategoryStatus `orm:"status" json:"status"` // 状态1启用/0禁用
Attrs []CategoryAttr `orm:"attrs" json:"attrs,omitempty"` // 分类属性 CategoryAttr
// 使用场景说明:
// 1. 商品分类属性:为该分类下的商品定义标准化的属性模板,如服装分类可定义尺寸、颜色、材质等属性
// 2. 服务分类属性:为服务类目定义特性参数,如咨询服务可定义服务时长、服务方式、专业领域等

View File

@@ -1,48 +1,80 @@
package entity
import (
"assets/consts/public"
"assets/consts/stock"
"gitea.com/red-future/common/beans"
"github.com/gogf/gf/v2/os/gtime"
"go.mongodb.org/mongo-driver/v2/bson"
)
type stockBatchCol struct {
beans.SQLBaseCol
AssetId string
AssetSkuId string
BatchNo string
BatchQty string
AvailableQty string
Metadata string
Status string
OrderId string
AssignedChannel string
ChannelSKU string
ChannelMetadata string
AllocatedAt string
ProductionDate string
ExpiryDate string
ExpiryWarningDate string
CategoryPath string
}
var StockBatchCol = stockBatchCol{
SQLBaseCol: beans.DefSQLBaseCol,
AssetId: "asset_id",
AssetSkuId: "asset_sku_id",
BatchNo: "batch_no",
BatchQty: "batch_qty",
AvailableQty: "available_qty",
Metadata: "metadata",
Status: "status",
OrderId: "order_id",
AssignedChannel: "assigned_channel",
ChannelSKU: "channel_sku",
ChannelMetadata: "channel_metadata",
AllocatedAt: "allocated_at",
ProductionDate: "production_date",
ExpiryDate: "expiry_date",
ExpiryWarningDate: "expiry_warning_date",
CategoryPath: "category_path",
}
// StockBatch 库存批次实体(用于批次管理模式)
type StockBatch struct {
beans.MongoBaseDO `bson:",inline"` // 嵌入基础字段Id, Creator, CreatedAt, Updater, UpdatedAt, TenantId, IsDeleted
AssetID *bson.ObjectID `bson:"assetId" json:"assetId"` // 关联资产ID
AssetSkuID *bson.ObjectID `bson:"assetSkuId" json:"assetSkuId"` // 关联资产SKU ID
BatchNo string `bson:"batchNo" json:"batchNo"` // 批次
BatchQty int `bson:"batchQty" json:"batchQty"` // 批次总数量(入库后不可变
AvailableQty int `bson:"availableQty" json:"availableQty"` // 可用数量(实时变化)
beans.SQLBaseDO `orm:",inherit"`
AssetId int64 `orm:"asset_id" json:"assetId"` // 关联资产ID
AssetSkuId int64 `orm:"asset_sku_id" json:"assetSkuId"` // 关联资产SKU ID
BatchNo string `orm:"batch_no" json:"batchNo"` // 批次号
BatchQty int `orm:"batch_qty" json:"batchQty"` // 批次总数量(入库后不可变)
AvailableQty int `orm:"available_qty" json:"availableQty"` // 可用数量(实时变化
// 批次元数据
Metadata []map[string]interface{} `bson:"metadata" json:"metadata"` // 其他元数据
Metadata []map[string]interface{} `orm:"metadata" json:"metadata"` // 其他元数据
// 状态
Status *stock.BatchStatus `bson:"status" json:"status"` // 批次状态
Status stock.BatchStatus `orm:"status" json:"status"` // 批次状态
// 锁定数量 = BatchQty - AvailableQty
// 订单关联
OrderID *bson.ObjectID `bson:"orderId" json:"orderId"` // 关联订单ID如果有
OrderID int64 `orm:"order_id" json:"orderId"` // 关联订单ID如果有
// 渠道分配信息
AssignedChannel string `bson:"assignedChannel" json:"assignedChannel"` // 分配的销售渠道
ChannelSKU string `bson:"channelSku" json:"channelSku"` // 渠道商品SKU
ChannelMetadata map[string]interface{} `bson:"channelMetadata" json:"channelMetadata"` // 渠道专属数据
AllocatedAt *gtime.Time `bson:"allocatedAt" json:"allocatedAt"` // 分配时间
AssignedChannel string `orm:"assigned_channel" json:"assignedChannel"` // 分配的销售渠道
ChannelSKU string `orm:"channel_sku" json:"channelSku"` // 渠道商品SKU
ChannelMetadata map[string]interface{} `orm:"channel_metadata" json:"channelMetadata"` // 渠道专属数据
AllocatedAt *gtime.Time `orm:"allocated_at" json:"allocatedAt"` // 分配时间
// 临期管理
ProductionDate *gtime.Time `bson:"productionDate" json:"productionDate"` // 生产日期
ExpiryDate *gtime.Time `bson:"expiryDate" json:"expiryDate"` // 过期日期
ExpiryWarningDate *gtime.Time `bson:"expiryWarningDate" json:"expiryWarningDate"` // 临期预警时间(有过期日期时建议填写)
CategoryPath string `bson:"categoryPath" json:"categoryPath"` // 分类路径
}
// CollectionName 获取集合名称
func (StockBatch) CollectionName() string {
return public.StockBatchCollection
ProductionDate *gtime.Time `orm:"production_date" json:"productionDate"` // 生产日期
ExpiryDate *gtime.Time `orm:"expiry_date" json:"expiryDate"` // 过期日期
ExpiryWarningDate *gtime.Time `orm:"expiry_warning_date" json:"expiryWarningDate"` // 临期预警时间(有过期日期时建议填写)
CategoryPath string `orm:"category_path" json:"categoryPath"` // 分类路径
}

View File

@@ -1,35 +1,59 @@
package entity
import (
"assets/consts/public"
"assets/consts/stock"
"gitea.com/red-future/common/beans"
"github.com/gogf/gf/v2/os/gtime"
"go.mongodb.org/mongo-driver/v2/bson"
)
type stockDetailsCol struct {
beans.SQLBaseCol
AssetId string
AssetSkuId string
Status string
OrderId string
LockExpire string
Metadata string
TokenId string
AssignedChannel string
ChannelSKU string
ChannelMetadata string
AllocatedAt string
CategoryPath string
}
var StockDetailsCol = stockDetailsCol{
SQLBaseCol: beans.DefSQLBaseCol,
AssetId: "asset_id",
AssetSkuId: "asset_sku_id",
Status: "status",
OrderId: "order_id",
LockExpire: "lock_expire",
Metadata: "metadata",
TokenId: "token_id",
AssignedChannel: "assigned_channel",
ChannelSKU: "channel_sku",
ChannelMetadata: "channel_metadata",
AllocatedAt: "allocated_at",
CategoryPath: "category_path",
}
// StockDetails 库存实体每一件商品都有独立ID用于后期做区块链虚拟资产
type StockDetails struct {
beans.MongoBaseDO `bson:",inline"` // 嵌入基础字段Id, Creator, CreatedAt, Updater, UpdatedAt, TenantId, IsDeleted
AssetId int64 `bson:"assetId" json:"assetId"` // 关联资产ID
AssetSkuId int64 `bson:"assetSkuId" json:"assetSkuId"` // 关联资产SKU ID
Status stock.StockStatus `bson:"status" json:"status"` // 库存状态
OrderId *bson.ObjectID `bson:"orderId" json:"orderId"` // 关联订单ID如果有
LockExpire *gtime.Time `bson:"lockExpire" json:"lockExpire"` // 锁定过期时间
Metadata []map[string]interface{} `bson:"metadata" json:"metadata"` // 其他元数据
TokenId string `bson:"tokenId" json:"tokenId"` // 区块链TokenID如果有
beans.SQLBaseDO `orm:",inherit"`
AssetId int64 `orm:"asset_id" json:"assetId"` // 关联资产ID
AssetSkuId int64 `orm:"asset_sku_id" json:"assetSkuId"` // 关联资产SKU ID
Status stock.StockStatus `orm:"status" json:"status"` // 库存状态
OrderId int64 `orm:"order_id" json:"orderId"` // 关联订单ID如果有
LockExpire *gtime.Time `orm:"lock_expire" json:"lockExpire"` // 锁定过期时间
Metadata []map[string]interface{} `orm:"metadata" json:"metadata"` // 其他元数据
TokenId string `orm:"token_id" json:"tokenId"` // 区块链TokenID如果有
// 渠道分配信息
AssignedChannel string `bson:"assignedChannel" json:"assignedChannel"` // 分配的销售渠道
ChannelSKU string `bson:"channelSku" json:"channelSku"` // 渠道商品SKU
ChannelMetadata map[string]interface{} `bson:"channelMetadata" json:"channelMetadata"` // 渠道专属数据
AllocatedAt *gtime.Time `bson:"allocatedAt" json:"allocatedAt"` // 分配时间
CategoryPath string `bson:"categoryPath" json:"categoryPath"` // 分类路径
}
// CollectionName 库存集合名称
func (StockDetails) CollectionName() string {
return public.StockDetailsCollection
AssignedChannel string `orm:"assigned_channel" json:"assignedChannel"` // 分配的销售渠道
ChannelSKU string `orm:"channel_sku" json:"channelSku"` // 渠道商品SKU
ChannelMetadata map[string]interface{} `orm:"channel_metadata" json:"channelMetadata"` // 渠道专属数据
AllocatedAt *gtime.Time `orm:"allocated_at" json:"allocatedAt"` // 分配时间
CategoryPath string `orm:"category_path" json:"categoryPath"` // 分类路径
}

View File

@@ -82,8 +82,8 @@ func (s *asset) GetOne(ctx context.Context, req *dto.GetAssetReq) (res *dto.GetA
if assetOne, err = dao.Asset.GetOne(ctx, req); err != nil {
return
}
var assetListItem *dto.AssetItem
if err = gconv.Struct(assetOne, assetListItem); err != nil {
var assetListItem *entity.Asset
if err = gconv.Struct(assetOne, &assetListItem); err != nil {
return
}
getCategoryRes, err := dao.Category.GetOne(ctx, &dto.GetCategoryReq{
@@ -93,7 +93,7 @@ func (s *asset) GetOne(ctx context.Context, req *dto.GetAssetReq) (res *dto.GetA
return
}
return &dto.GetAssetRes{
AssetItem: assetListItem,
Asset: assetListItem,
CategoryName: getCategoryRes.Name,
ImgAddressPrefix: minio.GetFileAddressPrefix(ctx),
}, nil
@@ -109,7 +109,7 @@ func (s *asset) GetAssetAndSku(ctx context.Context, req *dto.GetAssetAndSkuReq)
if err != nil {
return
}
var assetListItem *dto.AssetItem
var assetListItem *entity.Asset
if err = gconv.Struct(assetOne, assetListItem); err != nil {
return
}
@@ -126,7 +126,7 @@ func (s *asset) GetAssetAndSku(ctx context.Context, req *dto.GetAssetAndSkuReq)
return
}
return &dto.GetAssetAndSkuRes{
AssetItem: assetListItem,
Asset: assetListItem,
Skus: assetSkuListResItem,
TenantModuleType: moduleType.Options,
ImgAddressPrefix: minio.GetFileAddressPrefix(ctx),

View File

@@ -1,6 +1,7 @@
package service
import (
consts "assets/consts/asset"
"assets/consts/public"
dao "assets/dao/asset"
dto "assets/model/dto/asset"
@@ -10,7 +11,6 @@ import (
"reflect"
"gitea.com/red-future/common/beans"
"gitea.com/red-future/common/utils"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/gconv"
)
@@ -84,19 +84,18 @@ func (s *assetSku) parameterValidation(ctx context.Context, assetEntity *entity.
specNoExist := true
metadataList := make([]string, 0)
// 验证,自定义属性传过来的全不全
// TODO: Metadata类型从[]map[string]interface{}变为*gjson.Json需要适配
// if assetEntity.Metadata != nil {
// for _, metadata := range assetEntity.Metadata {
// attributeType := gconv.String(gconv.Map(metadata)["type"])
// if attributeType == string(consts.AttributeTypeMultiSelect) {
// metadataList = append(metadataList, attributeType)
// }
// }
// if len(metadataList) != len(specValues) {
// // 如果请求参数中不存在该键,则跳过
// return errors.New("规格参数填写不完整")
// }
// }
if assetEntity.Metadata != nil {
for _, metadata := range assetEntity.Metadata {
attributeType := gconv.String(gconv.Map(metadata)["type"])
if attributeType == string(consts.AttributeTypeMultiSelect) {
metadataList = append(metadataList, attributeType)
}
}
if len(metadataList) != len(specValues) {
// 如果请求参数中不存在该键,则跳过
return errors.New("规格参数填写不完整")
}
}
// 验证自定义属性和sku名称重不重复
for _, list := range list {
if list.SkuName == skuName {
@@ -155,7 +154,7 @@ func (s *assetSku) GetAssetSku(ctx context.Context, req *dto.GetAssetSkuReq) (re
if err != nil {
return
}
err = utils.Struct(one, &res)
err = gconv.Struct(one, &res)
return
}
@@ -169,7 +168,7 @@ func (s *assetSku) ListAssetSkus(ctx context.Context, req *dto.ListAssetSkuReq)
res = &dto.ListAssetSkuRes{
Total: total,
}
err = utils.Struct(list, &res.List)
err = gconv.Struct(list, &res.List)
return
}

View File

@@ -77,7 +77,7 @@ func (s *CategoryService) GetOne(ctx context.Context, req *dto.GetCategoryReq) (
}
res := new(dto.GetCategoryRes)
if err = gconv.Scan(one, &res); err != nil {
if err = gconv.Struct(one, &res); err != nil {
panic(err)
}

View File

@@ -7,6 +7,8 @@ import (
"context"
"gitea.com/red-future/common/beans"
"gitea.com/red-future/common/http"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/gconv"
)
@@ -39,17 +41,25 @@ func (s *enum) GetSpecsUnit(ctx context.Context, req *dto.GetSpecsUnitReq) (res
return
}
} else {
// 使用简化的 RPC 调用方式 - 直接传 map 参数
//dictData := &dto.GetDictRes{}
//if err = message.CallRPC(ctx, "dictService.GetDictWithDataByType", map[string]interface{}{"dictType": gconv.String(req.AssetType)}, dictData); err != nil {
// return
//}
//for _, v := range dictData.Values {
// res.Options = append(res.Options, dto.KeyValue{
// Key: v.DictValue,
// Value: v.DictLabel,
// })
//}
// 获取当前请求的 headers 并传递到下游
headers := make(map[string]string)
if r := g.RequestFromCtx(ctx); r != nil {
for k, v := range r.Request.Header {
if len(v) > 0 {
headers[k] = v[0]
}
}
}
dictData := &dto.GetDictRes{}
if err = http.Get(ctx, "admin-go/api/v1/system/dict/data/getDictData", headers, &dictData, "dictType", gconv.String(req.AssetType)); err != nil {
return
}
for _, v := range dictData.Values {
res.Options = append(res.Options, dto.KeyValue{
Key: v.DictValue,
Value: v.DictLabel,
})
}
}
return
}

View File

@@ -4,57 +4,48 @@
// 注意区别于PrivateStock的实物库存批次库存是逻辑概念不记录物理位置
package service
import (
dao "assets/dao/stock"
dto "assets/model/dto/stock"
"context"
"gitea.com/red-future/common/utils"
"go.mongodb.org/mongo-driver/v2/bson"
)
type stockBatch struct{}
// StockBatch 批次服务
var StockBatch = new(stockBatch)
func (s *stockBatch) Create(ctx context.Context, req *dto.CreateBatchReq) (res *dto.CreateBatchRes, err error) {
ids, err := dao.StockBatch.Insert(ctx, req)
if err != nil {
return
}
id := ids[0].(bson.ObjectID)
res = &dto.CreateBatchRes{
Id: &id,
}
return
}
func (s *stockBatch) Update(ctx context.Context, req *dto.UpdateBatchReq) error {
return dao.StockBatch.Update(ctx, req)
}
func (s *stockBatch) Delete(ctx context.Context, req *dto.DeleteBatchReq) error {
return dao.StockBatch.DeleteFake(ctx, req)
}
func (s *stockBatch) GetOne(ctx context.Context, req *dto.GetBatchReq) (res *dto.GetBatchRes, err error) {
one, err := dao.StockBatch.GetOneById(ctx, req)
if err != nil {
return
}
err = utils.Struct(one, &res)
return
}
func (s *stockBatch) List(ctx context.Context, req *dto.ListBatchReq) (res *dto.ListBatchRes, err error) {
list, total, err := dao.StockBatch.List(ctx, req)
if err != nil {
return
}
res = &dto.ListBatchRes{
Total: total,
}
err = utils.Struct(list, &res.List)
return
}
//func (s *stockBatch) Create(ctx context.Context, req *dto.CreateBatchReq) (res *dto.CreateBatchRes, err error) {
// ids, err := dao.StockBatch.Insert(ctx, req)
// if err != nil {
// return
// }
// id := ids[0].(bson.ObjectID)
// res = &dto.CreateBatchRes{
// Id: &id,
// }
// return
//}
//
//func (s *stockBatch) Update(ctx context.Context, req *dto.UpdateBatchReq) error {
// return dao.StockBatch.Update(ctx, req)
//}
//
//func (s *stockBatch) Delete(ctx context.Context, req *dto.DeleteBatchReq) error {
// return dao.StockBatch.Delete(ctx, req)
//}
//
//func (s *stockBatch) GetOne(ctx context.Context, req *dto.GetBatchReq) (res *dto.GetBatchRes, err error) {
// one, err := dao.StockBatch.GetOneById(ctx, req)
// if err != nil {
// return
// }
// err = utils.Struct(one, &res)
// return
//}
//
//func (s *stockBatch) List(ctx context.Context, req *dto.ListBatchReq) (res *dto.ListBatchRes, err error) {
// list, total, err := dao.StockBatch.List(ctx, req)
// if err != nil {
// return
// }
// res = &dto.ListBatchRes{
// Total: total,
// }
// err = utils.Struct(list, &res.List)
// return
//}

View File

@@ -4,36 +4,28 @@
// 注意区别于PrivateStock的实物库存明细库存是逻辑概念不记录物理位置
package service
import (
dao "assets/dao/stock"
dto "assets/model/dto/stock"
"context"
"gitea.com/red-future/common/utils"
)
type stockDetails struct{}
// StockDetails 库存服务
var StockDetails = new(stockDetails)
func (s *stockDetails) GetOne(ctx context.Context, req *dto.GetStockDetailsReq) (res *dto.GetStockDetailsRes, err error) {
one, err := dao.StockDetails.GetOneById(ctx, req)
if err != nil {
return
}
err = utils.Struct(one, &res)
return
}
func (s *stockDetails) List(ctx context.Context, req *dto.ListStockDetailsReq) (res *dto.ListStockDetailsRes, err error) {
list, total, err := dao.StockDetails.List(ctx, req)
if err != nil {
return
}
res = &dto.ListStockDetailsRes{
Total: total,
}
err = utils.Struct(list, &res.List)
return
}
//func (s *stockDetails) GetOne(ctx context.Context, req *dto.GetStockDetailsReq) (res *dto.GetStockDetailsRes, err error) {
// one, err := dao.StockDetails.GetOne(ctx, req)
// if err != nil {
// return
// }
// err = utils.Struct(one, &res)
// return
//}
//
//func (s *stockDetails) List(ctx context.Context, req *dto.ListStockDetailsReq) (res *dto.ListStockDetailsRes, err error) {
// list, total, err := dao.StockDetails.List(ctx, req)
// if err != nil {
// return
// }
// res = &dto.ListStockDetailsRes{
// Total: total,
// }
// err = utils.Struct(list, &res.List)
// return
//}

View File

@@ -1,4 +1,4 @@
// 库存管理服务Stock公共库存
// Package service 库存管理服务Stock公共库存
// 职责:入库/出库操作,支持明细模式(StockDetails)和批次模式(StockBatch)
// 调用链Controller → StockOperation → stockPublishMessage → NATS → AddStock(消费者)
// 紧密耦合dao.StockDetails、dao.StockBatch、dao.AssetSku(更新库存数)、common/message(NATS发布)
@@ -13,18 +13,19 @@ import (
assetDto "assets/model/dto/asset"
stockDto "assets/model/dto/stock"
assetEntity "assets/model/entity/asset"
entity "assets/model/entity/stock"
"context"
"fmt"
"gitea.com/red-future/common/beans"
"gitea.com/red-future/common/redis"
"gitea.com/red-future/common/utils"
gmq "github.com/bjang03/gmq/core/gmq"
"github.com/bjang03/gmq/mq"
"github.com/bjang03/gmq/types"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/util/gconv"
"go.mongodb.org/mongo-driver/v2/bson"
)
type stockManage struct{}
@@ -94,20 +95,25 @@ func (s *stockManage) StockOperation(ctx context.Context, req *stockDto.StockOpe
return
}
if !assetSku.UnlimitedStock && req.Stock >= 0 {
var stockId *bson.ObjectID
var stockId int64
count := 0
if assetSku.StockMode == stock.StockModeDetail {
_count, err := dao.StockDetails.GetStockCountBySkuId(ctx, assetSku.Id)
detailsReq := &stockDto.GetSockDetailsReq{
AssetSkuId: assetSku.Id,
Status: stock.StockStatusAvailable.Code(),
}
count, err = dao.StockDetails.Count(ctx, detailsReq)
if err != nil {
return err
}
count = gconv.Int(_count)
}
if assetSku.StockMode == stock.StockModeBatch {
if g.IsEmpty(req.BatchNo) {
return gerror.New("批次号不能为空")
}
getOne, err := dao.StockBatch.GetOne(ctx, req.BatchNo)
getOne, err := dao.StockBatch.One(ctx, &stockDto.GetSockBatchReq{
BatchNo: req.BatchNo,
})
if err != nil {
return err
}
@@ -138,7 +144,7 @@ func (s *stockManage) StockOperation(ctx context.Context, req *stockDto.StockOpe
// stockPublishMessage 发布库存变更消息到NATS
// 消费者接收后执行实际的入库/出库操作(异步解耦)
func (s *stockManage) stockPublishMessage(ctx context.Context, assetSku *assetEntity.AssetSku, stockId *bson.ObjectID, stockCount int, operationType string, req *stockDto.StockOperationReq) (err error) {
func (s *stockManage) stockPublishMessage(ctx context.Context, assetSku *assetEntity.AssetSku, stockId int64, stockCount int, operationType string, req *stockDto.StockOperationReq) (err error) {
// 用户信息
user, err := utils.GetUserInfo(ctx)
if err != nil {
@@ -152,160 +158,128 @@ func (s *stockManage) stockPublishMessage(ctx context.Context, assetSku *assetEn
StockCount: stockCount,
OperationType: operationType,
Metadata: gconv.Maps(assetSku.SpecValues),
StockMode: int(assetSku.StockMode),
StockMode: assetSku.StockMode,
BatchNo: req.BatchNo,
ProductionDate: req.ProductionDate,
ExpiryDate: req.ExpiryDate,
ExpiryWarningDate: req.ExpiryWarningDate,
}
if !g.IsEmpty(stockId) && !stockId.IsZero() {
publishMessage.StockId = stockId.Hex()
if !g.IsEmpty(stockId) {
publishMessage.StockId = stockId
}
// 发布到 NATS
//plugin, err := message.GetMsgPlugin(ctx, message.MessageNATS)
//if err != nil {
// return gerror.Newf("NATS插件未就绪: %v", err)
//}
//err = plugin.Publish(ctx, &message.NatsPublishMsgConfig{
// QueueName: public.StockDetailGroupName,
// Durable: true,
// Data: publishMessage,
//})
//_, err = message.PublishMessage(ctx, &message.RedisMessageConfig{StreamKey: public.StockDetailStreamKey}, publishMessage)
//plugin, err := message.GetMsgPlugin(message.MessageRedis)
//if err != nil {
// return err
//}
//err = plugin.Publish(ctx, &message.RedisPublishMsgConfig{
// QueueName: public.StockDetailQueueName,
// Data: publishMessage,
//})
err = gmq.GetGmq("primary").GmqPublish(ctx, &mq.RedisPubMessage{
PubMessage: types.PubMessage{
Topic: public.StockDetailQueueName,
Data: publishMessage,
},
})
return
}
// AddStock NATS消费者调用执行实际的入库/出库操作
// 使用Redis分布式锁防止并发冲突支持明细模式和批次模式
func (s *stockManage) AddStock(ctx context.Context, msg map[string]interface{}) error {
assetId := gconv.Int64(msg["assetId"])
assetSkuId := gconv.Int64(msg["assetSkuId"])
stockId := gconv.Int64(msg["stockId"])
userName := gconv.String(msg["userName"])
tenantId := gconv.Float64(msg["tenantId"])
stockCount := gconv.Int(msg["stockCount"])
operationType := gconv.String(msg["operationType"])
metadata := gconv.Maps(msg["metadata"])
stockMode := stock.StockMode(gconv.Int(msg["stockMode"]))
batchNo := gconv.String(msg["batchNo"])
productionDate := gtime.New(msg["productionDate"])
expiryDate := gtime.New(msg["expiryDate"])
expiryWarningDate := gtime.New(msg["expiryWarningDate"])
func (s *stockManage) AddStock(ctx context.Context, msg any) error {
var req = new(stockDto.StockPublishMessage)
if err := gconv.Struct(msg, &req); err != nil {
return err
}
// 设置 userId 和 tenantId 到 ctx
ctx = context.WithValue(ctx, "userName", userName)
ctx = context.WithValue(ctx, "tenantId", tenantId)
ctx = context.WithValue(ctx, "userName", req.UserName)
ctx = context.WithValue(ctx, "tenantId", req.TenantId)
// 获取redis-租户存储-锁key
fileLockKey := fmt.Sprintf(public.StockDetailLockKey, assetSkuId)
fileLockKey := fmt.Sprintf(public.StockDetailLockKey, req.AssetSkuId)
success, err := redis.Lock(ctx, fileLockKey, int64(60), func(ctx context.Context) error {
if operationType == "add" {
if stockMode == stock.StockModeBatch {
if !g.IsEmpty(stockId) {
batch := stockDto.UpdateBatchReq{
Id: stockId,
BatchQty: stockCount,
AvailableQty: stockCount,
if req.OperationType == "add" {
if req.StockMode == stock.StockModeBatch {
if !g.IsEmpty(req.StockId) {
var updateReq = new(stockDto.UpdateSockBatchReq)
if err := gconv.Struct(req, &updateReq); err != nil {
return err
}
if err := dao.StockBatch.Update(ctx, &batch); err != nil {
if _, err := dao.StockBatch.Update(ctx, updateReq); err != nil {
return err
}
} else {
batch := stockDto.CreateBatchReq{
AssetId: assetId,
AssetSkuId: assetSkuId,
Status: stock.BatchStatusActive,
Metadata: metadata,
BatchNo: batchNo,
BatchQty: stockCount,
AvailableQty: stockCount,
ProductionDate: productionDate,
ExpiryDate: expiryDate,
ExpiryWarningDate: expiryWarningDate,
var createReq = new(stockDto.CreateSockBatchReq)
if err := gconv.Struct(req, &createReq); err != nil {
return err
}
if _, err := dao.StockBatch.Insert(ctx, &batch); err != nil {
if _, err := dao.StockBatch.Insert(ctx, createReq); err != nil {
return err
}
}
}
if stockMode == stock.StockModeDetail {
if req.StockMode == stock.StockModeDetail {
// 创建指定数量的库存
var stockInterfaces []interface{}
for i := 0; i < stockCount; i++ {
stockInterfaces = append(stockInterfaces, entity.StockDetails{
AssetId: assetId,
AssetSkuId: assetSkuId,
Status: stock.StockStatusAvailable,
Metadata: metadata,
})
stockDetailsList := make([]*stockDto.CreateSockDetailsReq, req.StockCount)
for i := 0; i < req.StockCount; i++ {
var createReq = new(stockDto.CreateSockDetailsReq)
if err := gconv.Struct(req, &createReq); err != nil {
return err
}
stockDetailsList = append(stockDetailsList, createReq)
}
// 批量插入数据库
if _, err := dao.StockDetails.BatchInsert(ctx, stockInterfaces); err != nil {
if _, err := dao.StockDetails.BatchInsert(ctx, stockDetailsList); err != nil {
return err
}
}
}
if operationType == "del" {
if stockMode == stock.StockModeBatch {
stockCount = 0 - stockCount
if req.OperationType == "del" {
if req.StockMode == stock.StockModeBatch {
req.StockCount = 0 - req.StockCount
// 更新批次
batch := stockDto.UpdateBatchReq{
Id: stockId,
BatchQty: stockCount,
AvailableQty: stockCount,
batch := stockDto.UpdateSockBatchReq{
Id: req.StockId,
BatchQty: req.StockCount,
AvailableQty: req.StockCount,
}
if err := dao.StockBatch.Update(ctx, &batch); err != nil {
if _, err := dao.StockBatch.Update(ctx, &batch); err != nil {
return err
}
}
if stockMode == stock.StockModeDetail {
if req.StockMode == stock.StockModeDetail {
// 分页查询所有库存明细收集所有ID
var allStockIds []*bson.ObjectID
var allStockIds []stockDto.DeleteSockDetailsReq
pageSize := int64(50)
for pageNum := int64(1); ; pageNum++ {
details, total, err := dao.StockDetails.List(ctx,
&stockDto.ListStockDetailsReq{
AssetSkuId: assetSkuId,
Status: stock.StockStatusAvailable,
&stockDto.GetSockDetailsReq{
AssetSkuId: req.AssetSkuId,
Status: stock.StockStatusAvailable.Code(),
Page: &beans.Page{PageNum: pageNum, PageSize: pageSize},
})
if err != nil {
return err
}
if pageNum == 1 && int(total) < stockCount {
if pageNum == 1 && total < req.StockCount {
return gerror.New("可操作库存数量不足")
}
// 收集当前页的ID
for _, detail := range details {
if detail.Id != nil && !detail.Id.IsZero() {
allStockIds = append(allStockIds, detail.Id)
if len(allStockIds) >= stockCount {
if !g.IsEmpty(detail.Id) {
allStockIds = append(allStockIds, stockDto.DeleteSockDetailsReq{Id: detail.Id})
if len(allStockIds) >= req.StockCount {
break
}
}
}
if len(allStockIds) >= stockCount {
if len(allStockIds) >= req.StockCount {
break
}
}
// 根据ID批量删除库存
delCount, err := dao.StockDetails.DeleteManyByIds(ctx, allStockIds)
delCount, err := dao.StockDetails.Delete(ctx, allStockIds)
if err != nil {
return err
}
if delCount != int64(stockCount) {
if delCount != int64(req.StockCount) {
return gerror.New("删除库存数量不匹配")
}
stockCount = 0 - stockCount
req.StockCount = 0 - req.StockCount
}
}
_, err := assetDao.AssetSku.Update(ctx, &assetDto.UpdateAssetSkuReq{Id: assetSkuId, Stock: stockCount})
_, err := assetDao.AssetSku.Update(ctx, &assetDto.UpdateAssetSkuReq{Id: req.AssetSkuId, Stock: req.StockCount})
return err
})
if err != nil {

View File

@@ -123,4 +123,73 @@ COMMENT ON COLUMN assets_asset.virtual_asset_config IS '虚拟资产配置(JSONB
COMMENT ON COLUMN assets_asset.metadata IS '动态元数据(JSONB)';
COMMENT ON COLUMN assets_asset.tenant_module_type IS '租户模块类型';
--------------------pgsql创建assets_asset表语句---------------------------
--------------------pgsql创建assets_asset表语句---------------------------
--------------------pgsql创建assets_asset_sku表语句---------------------------
-- 资产SKU表
CREATE TABLE IF NOT EXISTS assets_asset_sku (
-- 基础字段(继承 SQLBaseDO 通用字段)
id BIGINT PRIMARY KEY, -- 主键ID非自增
creator VARCHAR(64) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updater VARCHAR(64) NOT NULL,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
deleted_at timestamp(6),
-- SKU核心字段
asset_id BIGINT NOT NULL, -- 关联资产ID
asset_name VARCHAR(128) NOT NULL, -- 资产名称
sku_name VARCHAR(128) NOT NULL, -- SKU名称
image_url VARCHAR(256) DEFAULT '', -- SKU主图
spec_values JSONB NOT NULL DEFAULT '[]'::JSONB, -- 规格值:{"颜色":"红色","尺寸":"L"}
price INT NOT NULL DEFAULT 0, -- 价格(分为单位)
unlimited_stock BOOLEAN NOT NULL DEFAULT FALSE, -- 是否无库存限制
stock INT NOT NULL DEFAULT 0, -- 库存数量
specs_count INT NOT NULL DEFAULT 0, -- 规格数量
specs_unit JSONB DEFAULT '{}'::JSONB, -- 规格单位 SpecsUnitKeyValue
sort INT NOT NULL DEFAULT 0, -- 排序
status SMALLINT NOT NULL DEFAULT 1, -- 资产状态1启用/0停用
stock_mode SMALLINT NOT NULL DEFAULT 1, -- 库存管理模式1-明细模式 2-批次模式
category_id BIGINT NOT NULL DEFAULT 0, -- 分类ID
category_path VARCHAR(512) DEFAULT '', -- 分类路径
tenant_module_type VARCHAR(32) DEFAULT '', -- 租户模块类型
-- 外键约束(可选,根据业务需要启用)
CONSTRAINT fk_sku_asset_id FOREIGN KEY (asset_id) REFERENCES assets_asset(id) ON DELETE CASCADE,
CONSTRAINT fk_sku_category_id FOREIGN KEY (category_id) REFERENCES assets_category(id) ON DELETE SET DEFAULT
);
-- 索引(根据查询场景优化)
CREATE INDEX idx_sku_asset_id ON assets_asset_sku(asset_id);
CREATE INDEX idx_sku_category_id ON assets_asset_sku(category_id);
CREATE INDEX idx_sku_status ON assets_asset_sku(status);
CREATE INDEX idx_sku_stock_mode ON assets_asset_sku(stock_mode);
CREATE INDEX idx_sku_deleted_at ON assets_asset_sku(deleted_at); -- 软删字段索引
CREATE INDEX idx_sku_tenant_module_type ON assets_asset_sku(tenant_module_type);
-- 表和字段注释
COMMENT ON TABLE assets_asset_sku IS '资产SKU表';
COMMENT ON COLUMN assets_asset_sku.id IS '主键ID非自增';
COMMENT ON COLUMN assets_asset_sku.creator IS '创建人';
COMMENT ON COLUMN assets_asset_sku.created_at IS '创建时间';
COMMENT ON COLUMN assets_asset_sku.updater IS '更新人';
COMMENT ON COLUMN assets_asset_sku.updated_at IS '更新时间';
COMMENT ON COLUMN assets_asset_sku.deleted_at IS '删除时间(软删)';
COMMENT ON COLUMN assets_asset_sku.asset_id IS '关联资产ID';
COMMENT ON COLUMN assets_asset_sku.asset_name IS '资产名称';
COMMENT ON COLUMN assets_asset_sku.sku_name IS 'SKU名称';
COMMENT ON COLUMN assets_asset_sku.image_url IS 'SKU主图';
COMMENT ON COLUMN assets_asset_sku.spec_values IS '规格值:{"颜色":"红色","尺寸":"L","时长":"1个月","平台":"抖音"}';
COMMENT ON COLUMN assets_asset_sku.price IS '价格(分为单位)';
COMMENT ON COLUMN assets_asset_sku.unlimited_stock IS '是否无库存限制';
COMMENT ON COLUMN assets_asset_sku.stock IS '库存数量';
COMMENT ON COLUMN assets_asset_sku.specs_count IS '规格数量';
COMMENT ON COLUMN assets_asset_sku.specs_unit IS '规格单位 SpecsUnitKeyValue';
COMMENT ON COLUMN assets_asset_sku.sort IS '排序';
COMMENT ON COLUMN assets_asset_sku.status IS '状态active-启用/inactive-未启用/disabled-禁用';
COMMENT ON COLUMN assets_asset_sku.stock_mode IS '库存管理模式1-明细模式 2-批次模式';
COMMENT ON COLUMN assets_asset_sku.category_id IS '分类ID';
COMMENT ON COLUMN assets_asset_sku.category_path IS '分类路径';
COMMENT ON COLUMN assets_asset_sku.tenant_module_type IS '租户模块类型';
--------------------pgsql创建assets_sku表语句---------------------------