Align origin/main with upstream/main (GitHub). The two branches diverged due to pre-rebase vs post-rebase merge commits for the k8s-amd64-dockerfiles feature. Includes: multi-stage Go compilation Dockerfiles, pan-bolt inventory features, CRM relations, Excel import tooling, proto/gRPC updates, migration scripts, and tools/ directory. Excludes deploy/bin/ pre-compiled binaries (arm64, not needed for amd64 K3s cluster; builds use multi-stage Dockerfiles now).
220 lines
7.2 KiB
Go
Executable File
220 lines
7.2 KiB
Go
Executable File
package model
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/zeromicro/go-zero/core/stores/cache"
|
|
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
|
)
|
|
|
|
var _ InvProductModel = (*customInvProductModel)(nil)
|
|
|
|
// StockGroupResult is kept for GroupBy queries (color / location).
|
|
type StockGroupResult struct {
|
|
Name string `db:"name"`
|
|
Count int64 `db:"count"`
|
|
Quantity float64 `db:"quantity"`
|
|
}
|
|
|
|
type ProductSummary struct {
|
|
ProductName string `db:"product_name"`
|
|
Spec string `db:"spec"`
|
|
ColorCount int64 `db:"color_count"`
|
|
TotalPanCount int64 `db:"total_pan_count"`
|
|
TotalBoltCount int64 `db:"total_bolt_count"`
|
|
TotalLengthM float64 `db:"total_length_m"`
|
|
Colors string `db:"colors"`
|
|
Locations string `db:"locations"`
|
|
}
|
|
|
|
type ColorDetail struct {
|
|
ProductId string `db:"product_id"`
|
|
ProductName string `db:"product_name"`
|
|
Color string `db:"color"`
|
|
Spec string `db:"spec"`
|
|
ImageUrl string `db:"image_url"`
|
|
SalesPrice float64 `db:"sales_price"`
|
|
PanCount int64 `db:"pan_count"`
|
|
BoltCount int64 `db:"bolt_count"`
|
|
TotalLengthM float64 `db:"total_length_m"`
|
|
Locations string `db:"locations"`
|
|
}
|
|
|
|
type (
|
|
InvProductModel interface {
|
|
invProductModel
|
|
FindList(ctx context.Context, tenantId string, page, pageSize int64, productName, spec, color string, status int64) ([]*InvProduct, int64, error)
|
|
FindProductSummary(ctx context.Context, tenantId string) ([]*ProductSummary, error)
|
|
FindColorDetail(ctx context.Context, tenantId, productName string) ([]*ColorDetail, error)
|
|
FindGroupByColor(ctx context.Context, tenantId string) ([]StockGroupResult, error)
|
|
FindGroupByLocation(ctx context.Context, tenantId string) ([]StockGroupResult, error)
|
|
BulkInsert(ctx context.Context, products []*InvProduct) (int64, error)
|
|
}
|
|
|
|
customInvProductModel struct {
|
|
*defaultInvProductModel
|
|
}
|
|
)
|
|
|
|
func NewInvProductModel(conn sqlx.SqlConn, c cache.CacheConf, opts ...cache.Option) InvProductModel {
|
|
return &customInvProductModel{
|
|
defaultInvProductModel: newInvProductModel(conn, c, opts...),
|
|
}
|
|
}
|
|
|
|
const bulkBatchSize = 200
|
|
|
|
func (m *customInvProductModel) BulkInsert(ctx context.Context, products []*InvProduct) (int64, error) {
|
|
if len(products) == 0 {
|
|
return 0, nil
|
|
}
|
|
|
|
var totalAffected int64
|
|
for i := 0; i < len(products); i += bulkBatchSize {
|
|
end := i + bulkBatchSize
|
|
if end > len(products) {
|
|
end = len(products)
|
|
}
|
|
batch := products[i:end]
|
|
|
|
placeholder := "(" + strings.Repeat("?,", 9) + "?)"
|
|
placeholders := make([]string, len(batch))
|
|
args := make([]interface{}, 0, len(batch)*10)
|
|
|
|
for j, p := range batch {
|
|
placeholders[j] = placeholder
|
|
args = append(args,
|
|
p.ProductId, p.ProductName, p.ImageUrl, p.Spec, p.Color,
|
|
p.SalesPrice, p.Remark, p.Status, p.TenantId, p.DeletedAt,
|
|
)
|
|
}
|
|
|
|
query := fmt.Sprintf("INSERT INTO %s (%s) VALUES %s",
|
|
m.table, invProductRowsExpectAutoSet, strings.Join(placeholders, ","))
|
|
|
|
result, err := m.ExecNoCacheCtx(ctx, query, args...)
|
|
if err != nil {
|
|
return totalAffected, fmt.Errorf("batch %d-%d: %w", i, end-1, err)
|
|
}
|
|
n, _ := result.RowsAffected()
|
|
totalAffected += n
|
|
}
|
|
|
|
return totalAffected, nil
|
|
}
|
|
|
|
func (m *customInvProductModel) FindList(ctx context.Context, tenantId string, page, pageSize int64, productName, spec, color string, status int64) ([]*InvProduct, int64, error) {
|
|
where := "WHERE deleted_at IS NULL AND tenant_id = ?"
|
|
args := []interface{}{tenantId}
|
|
|
|
if productName != "" {
|
|
where += " AND product_name LIKE ?"
|
|
args = append(args, "%"+productName+"%")
|
|
}
|
|
if spec != "" {
|
|
where += " AND spec LIKE ?"
|
|
args = append(args, "%"+spec+"%")
|
|
}
|
|
if color != "" {
|
|
where += " AND color = ?"
|
|
args = append(args, color)
|
|
}
|
|
if status >= 0 {
|
|
where += " AND status = ?"
|
|
args = append(args, status)
|
|
}
|
|
|
|
var total int64
|
|
countQuery := fmt.Sprintf("SELECT COUNT(*) FROM %s %s", m.table, where)
|
|
err := m.QueryRowNoCacheCtx(ctx, &total, countQuery, args...)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
var list []*InvProduct
|
|
query := fmt.Sprintf("SELECT %s FROM %s %s ORDER BY created_at DESC LIMIT ? OFFSET ?", invProductRows, m.table, where)
|
|
args = append(args, pageSize, (page-1)*pageSize)
|
|
err = m.QueryRowsNoCacheCtx(ctx, &list, query, args...)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
return list, total, nil
|
|
}
|
|
|
|
func (m *customInvProductModel) FindProductSummary(ctx context.Context, tenantId string) ([]*ProductSummary, error) {
|
|
var list []*ProductSummary
|
|
query := `SELECT p.product_name, p.spec,
|
|
COUNT(DISTINCT p.product_id) AS color_count,
|
|
COUNT(DISTINCT pan.pan_id) AS total_pan_count,
|
|
COUNT(bolt.bolt_id) AS total_bolt_count,
|
|
COALESCE(SUM(bolt.length_m), 0) AS total_length_m,
|
|
GROUP_CONCAT(DISTINCT p.color) AS colors,
|
|
GROUP_CONCAT(DISTINCT pan.position ORDER BY pan.position) AS locations
|
|
FROM ` + m.table + ` p
|
|
LEFT JOIN ` + "`inv_product_pan`" + ` pan ON pan.product_id = p.product_id
|
|
LEFT JOIN ` + "`inv_product_bolt`" + ` bolt ON bolt.pan_id = pan.pan_id
|
|
WHERE p.tenant_id = ? AND p.deleted_at IS NULL
|
|
GROUP BY p.product_name, p.spec
|
|
ORDER BY p.product_name`
|
|
err := m.QueryRowsNoCacheCtx(ctx, &list, query, tenantId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return list, nil
|
|
}
|
|
|
|
func (m *customInvProductModel) FindGroupByColor(ctx context.Context, tenantId string) ([]StockGroupResult, error) {
|
|
var list []StockGroupResult
|
|
query := `SELECT p.color AS name, COUNT(DISTINCT p.product_id) AS count,
|
|
COALESCE(SUM(bolt.length_m), 0) AS quantity
|
|
FROM ` + m.table + ` p
|
|
LEFT JOIN ` + "`inv_product_pan`" + ` pan ON pan.product_id = p.product_id
|
|
LEFT JOIN ` + "`inv_product_bolt`" + ` bolt ON bolt.pan_id = pan.pan_id
|
|
WHERE p.tenant_id = ? AND p.deleted_at IS NULL
|
|
GROUP BY p.color ORDER BY count DESC`
|
|
err := m.QueryRowsNoCacheCtx(ctx, &list, query, tenantId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return list, nil
|
|
}
|
|
|
|
func (m *customInvProductModel) FindGroupByLocation(ctx context.Context, tenantId string) ([]StockGroupResult, error) {
|
|
var list []StockGroupResult
|
|
query := `SELECT pan.position AS name, COUNT(DISTINCT p.product_id) AS count,
|
|
COALESCE(SUM(bolt.length_m), 0) AS quantity
|
|
FROM ` + m.table + ` p
|
|
JOIN ` + "`inv_product_pan`" + ` pan ON pan.product_id = p.product_id
|
|
LEFT JOIN ` + "`inv_product_bolt`" + ` bolt ON bolt.pan_id = pan.pan_id
|
|
WHERE p.tenant_id = ? AND p.deleted_at IS NULL AND pan.position != ''
|
|
GROUP BY pan.position ORDER BY count DESC`
|
|
err := m.QueryRowsNoCacheCtx(ctx, &list, query, tenantId)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return list, nil
|
|
}
|
|
|
|
func (m *customInvProductModel) FindColorDetail(ctx context.Context, tenantId, productName string) ([]*ColorDetail, error) {
|
|
var list []*ColorDetail
|
|
query := `SELECT p.product_id, p.product_name, p.color, p.spec, p.image_url, p.sales_price,
|
|
COUNT(DISTINCT pan.pan_id) AS pan_count,
|
|
COUNT(bolt.bolt_id) AS bolt_count,
|
|
COALESCE(SUM(bolt.length_m), 0) AS total_length_m,
|
|
GROUP_CONCAT(DISTINCT pan.position ORDER BY pan.position) AS locations
|
|
FROM ` + m.table + ` p
|
|
LEFT JOIN ` + "`inv_product_pan`" + ` pan ON pan.product_id = p.product_id
|
|
LEFT JOIN ` + "`inv_product_bolt`" + ` bolt ON bolt.pan_id = pan.pan_id
|
|
WHERE p.tenant_id = ? AND p.deleted_at IS NULL AND p.product_name = ?
|
|
GROUP BY p.product_id
|
|
ORDER BY p.color`
|
|
err := m.QueryRowsNoCacheCtx(ctx, &list, query, tenantId, productName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return list, nil
|
|
}
|