159 lines
4.9 KiB
Go
159 lines
4.9 KiB
Go
package repository
|
||
|
||
import (
|
||
"database/sql"
|
||
"strings"
|
||
|
||
"github.com/wmms/wmms/internal/model"
|
||
"github.com/wmms/wmms/pkg/database"
|
||
)
|
||
|
||
type MaterialLogRepo struct{}
|
||
|
||
func NewMaterialLogRepo() *MaterialLogRepo {
|
||
return &MaterialLogRepo{}
|
||
}
|
||
|
||
// normalizeEndDate ensures endDate has a time component for proper date range filtering
|
||
func normalizeEndDate(endDate string) string {
|
||
if endDate == "" {
|
||
return ""
|
||
}
|
||
// If already contains a time component (has : in time part like 23:59:59), return as-is
|
||
if strings.Contains(endDate, " ") && strings.Contains(strings.Split(endDate, " ")[1], ":") {
|
||
return endDate
|
||
}
|
||
// Append end of day time
|
||
return endDate + " 23:59:59"
|
||
}
|
||
|
||
func (r *MaterialLogRepo) insertLog(execer sqlExecer, log *model.MaterialLog) error {
|
||
query := `INSERT INTO material_logs (operation_type, material_id, old_value, new_value, operator, description) VALUES (?, ?, ?, ?, ?, ?)`
|
||
result, err := execer.Exec(query, log.OperationType, log.MaterialID, log.OldValue, log.NewValue, log.Operator, log.Description)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
id, err := result.LastInsertId()
|
||
if err != nil {
|
||
return err
|
||
}
|
||
log.ID = id
|
||
return nil
|
||
}
|
||
|
||
func (r *MaterialLogRepo) Create(log *model.MaterialLog) error {
|
||
return r.insertLog(database.DB, log)
|
||
}
|
||
|
||
// CreateTx 在调用方事务内写入日志,便于和物料主记录的修改一起提交或回滚。
|
||
func (r *MaterialLogRepo) CreateTx(tx *sql.Tx, log *model.MaterialLog) error {
|
||
return r.insertLog(tx, log)
|
||
}
|
||
|
||
// List 只返回当前公司物料的日志:material_logs 本身不带组织列,经 materials
|
||
// 关联过滤,避免 A 看到 B 的物料变更记录。
|
||
func (r *MaterialLogRepo) List(organizationID int64, operationType, materialId string, startDate, endDate string, limit, offset int) ([]model.MaterialLog, int64, error) {
|
||
countQuery := `SELECT COUNT(*) FROM material_logs ml JOIN materials m ON m.id = ml.material_id WHERE m.organization_id = ?`
|
||
countArgs := []interface{}{organizationID}
|
||
|
||
if operationType != "" {
|
||
countQuery += ` AND ml.operation_type = ?`
|
||
countArgs = append(countArgs, operationType)
|
||
}
|
||
if materialId != "" {
|
||
countQuery += ` AND ml.material_id = ?`
|
||
countArgs = append(countArgs, materialId)
|
||
}
|
||
if startDate != "" {
|
||
countQuery += ` AND ml.created_at >= ?`
|
||
countArgs = append(countArgs, startDate)
|
||
}
|
||
if endDate != "" {
|
||
countQuery += ` AND ml.created_at <= ?`
|
||
countArgs = append(countArgs, normalizeEndDate(endDate))
|
||
}
|
||
|
||
var total int64
|
||
err := database.DB.QueryRow(countQuery, countArgs...).Scan(&total)
|
||
if err != nil {
|
||
return nil, 0, err
|
||
}
|
||
|
||
query := `SELECT ml.id, ml.operation_type, ml.material_id, ml.old_value, ml.new_value, ml.operator, ml.description, ml.created_at
|
||
FROM material_logs ml JOIN materials m ON m.id = ml.material_id WHERE m.organization_id = ?`
|
||
args := []interface{}{organizationID}
|
||
|
||
if operationType != "" {
|
||
query += ` AND ml.operation_type = ?`
|
||
args = append(args, operationType)
|
||
}
|
||
if materialId != "" {
|
||
query += ` AND ml.material_id = ?`
|
||
args = append(args, materialId)
|
||
}
|
||
if startDate != "" {
|
||
query += ` AND ml.created_at >= ?`
|
||
args = append(args, startDate)
|
||
}
|
||
if endDate != "" {
|
||
query += ` AND ml.created_at <= ?`
|
||
args = append(args, normalizeEndDate(endDate))
|
||
}
|
||
query += ` ORDER BY ml.id DESC LIMIT ? OFFSET ?`
|
||
args = append(args, limit, offset)
|
||
|
||
rows, err := database.DB.Query(query, args...)
|
||
if err != nil {
|
||
return nil, 0, err
|
||
}
|
||
defer rows.Close()
|
||
|
||
var logs []model.MaterialLog
|
||
for rows.Next() {
|
||
var log model.MaterialLog
|
||
var oldValue, newValue []byte
|
||
err := rows.Scan(&log.ID, &log.OperationType, &log.MaterialID, &oldValue, &newValue, &log.Operator, &log.Description, &log.CreatedAt)
|
||
if err != nil {
|
||
return nil, 0, err
|
||
}
|
||
if oldValue != nil {
|
||
log.OldValue = string(oldValue)
|
||
}
|
||
if newValue != nil {
|
||
log.NewValue = string(newValue)
|
||
}
|
||
logs = append(logs, log)
|
||
}
|
||
return logs, total, nil
|
||
}
|
||
|
||
// GetByMaterialID 只返回属于当前公司的该物料日志;物料不属于本公司时结果为空。
|
||
func (r *MaterialLogRepo) GetByMaterialID(organizationID, materialID int64) ([]model.MaterialLog, error) {
|
||
query := `SELECT ml.id, ml.operation_type, ml.material_id, ml.old_value, ml.new_value, ml.operator, ml.description, ml.created_at
|
||
FROM material_logs ml JOIN materials m ON m.id = ml.material_id
|
||
WHERE m.organization_id = ? AND ml.material_id = ? ORDER BY ml.id DESC`
|
||
rows, err := database.DB.Query(query, organizationID, materialID)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
defer rows.Close()
|
||
|
||
var logs []model.MaterialLog
|
||
for rows.Next() {
|
||
var log model.MaterialLog
|
||
var oldValue, newValue []byte
|
||
err := rows.Scan(&log.ID, &log.OperationType, &log.MaterialID, &oldValue, &newValue, &log.Operator, &log.Description, &log.CreatedAt)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
if oldValue != nil {
|
||
log.OldValue = string(oldValue)
|
||
}
|
||
if newValue != nil {
|
||
log.NewValue = string(newValue)
|
||
}
|
||
logs = append(logs, log)
|
||
}
|
||
return logs, nil
|
||
}
|