material_manage_system/internal/repository/application.go
2026-09-21 14:13:30 +08:00

330 lines
10 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package repository
import (
"database/sql"
"github.com/wmms/wmms/internal/model"
"github.com/wmms/wmms/pkg/database"
)
type ApplicationRepository struct{}
func NewApplicationRepository() *ApplicationRepository {
return &ApplicationRepository{}
}
func (r *ApplicationRepository) Create(app *model.MaterialApplication) error {
tx, err := database.DB.Begin()
if err != nil {
return err
}
defer tx.Rollback()
query := `INSERT INTO material_applications (user_id, purpose, status) VALUES (?, ?, ?)`
result, err := tx.Exec(query, app.UserID, app.Purpose, app.Status)
if err != nil {
return err
}
id, err := result.LastInsertId()
if err != nil {
return err
}
app.ID = id
// Insert application items
for _, item := range app.Items {
itemQuery := `INSERT INTO application_items (application_id, material_id, quantity) VALUES (?, ?, ?)`
itemResult, err := tx.Exec(itemQuery, app.ID, item.MaterialID, item.Quantity)
if err != nil {
return err
}
itemID, _ := itemResult.LastInsertId()
item.ID = itemID
}
return tx.Commit()
}
func (r *ApplicationRepository) GetByID(id int64) (*model.MaterialApplication, error) {
query := `SELECT a.id, a.user_id, a.purpose, a.status, a.rejection_reason, a.approved_by, a.created_at, a.updated_at,
u.username
FROM material_applications a
JOIN users u ON a.user_id = u.id
WHERE a.id = ?`
var app model.MaterialApplication
err := database.DB.QueryRow(query, id).Scan(
&app.ID, &app.UserID, &app.Purpose, &app.Status,
&app.RejectionReason, &app.ApprovedBy, &app.CreatedAt, &app.UpdatedAt,
&app.Username,
)
if err != nil {
return nil, err
}
// Fetch items
app.Items, err = r.GetApplicationItems(id)
if err != nil {
return nil, err
}
app.ApplicationNo = model.OrderNo("AP", app.ID)
return &app, nil
}
func (r *ApplicationRepository) GetApplicationItems(applicationID int64) ([]model.ApplicationItem, error) {
query := `SELECT ai.id, ai.application_id, ai.material_id, ai.quantity, m.material_code, m.name
FROM application_items ai
JOIN materials m ON ai.material_id = m.id
WHERE ai.application_id = ?`
rows, err := database.DB.Query(query, applicationID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []model.ApplicationItem
for rows.Next() {
var item model.ApplicationItem
err := rows.Scan(&item.ID, &item.ApplicationID, &item.MaterialID, &item.Quantity, &item.MaterialCode, &item.MaterialName)
if err != nil {
return nil, err
}
items = append(items, item)
}
return items, nil
}
func (r *ApplicationRepository) ListAll(status string, userID int64, limit, offset int) ([]model.MaterialApplication, int, error) {
// Count query
countQuery := `SELECT COUNT(*) FROM material_applications a WHERE 1=1`
var countArgs []interface{}
if status != "" {
countQuery += " AND a.status = ?"
countArgs = append(countArgs, status)
}
if userID > 0 {
countQuery += " AND a.user_id = ?"
countArgs = append(countArgs, userID)
}
var total int
err := database.DB.QueryRow(countQuery, countArgs...).Scan(&total)
if err != nil {
return nil, 0, err
}
// Data query
query := `SELECT a.id, a.user_id, a.purpose, a.status, a.rejection_reason, a.approved_by, a.created_at, a.updated_at,
u.username
FROM material_applications a
JOIN users u ON a.user_id = u.id
WHERE 1=1`
var args []interface{}
if status != "" {
query += " AND a.status = ?"
args = append(args, status)
}
if userID > 0 {
query += " AND a.user_id = ?"
args = append(args, userID)
}
query += " ORDER BY a.created_at 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 apps []model.MaterialApplication
for rows.Next() {
var app model.MaterialApplication
err := rows.Scan(
&app.ID, &app.UserID, &app.Purpose, &app.Status,
&app.RejectionReason, &app.ApprovedBy, &app.CreatedAt, &app.UpdatedAt,
&app.Username,
)
if err != nil {
return nil, 0, err
}
// Fetch items for each application
app.Items, err = r.GetApplicationItems(app.ID)
if err != nil {
return nil, 0, err
}
app.ApplicationNo = model.OrderNo("AP", app.ID)
apps = append(apps, app)
}
return apps, total, nil
}
func (r *ApplicationRepository) ListByUserID(userID int64, status string, limit, offset int) ([]model.MaterialApplication, int, error) {
return r.ListAll(status, userID, limit, offset)
}
func (r *ApplicationRepository) ListAllApplications(status string, limit, offset int) ([]model.MaterialApplication, int, error) {
return r.ListAll(status, 0, limit, offset)
}
func (r *ApplicationRepository) UpdateStatus(id int64, status string) error {
query := `UPDATE material_applications SET status = ?, updated_at = NOW() WHERE id = ?`
_, err := database.DB.Exec(query, status, id)
return err
}
func (r *ApplicationRepository) Approve(id int64, approverID int64) error {
query := `UPDATE material_applications SET status = ?, approved_by = ?, updated_at = NOW() WHERE id = ?`
_, err := database.DB.Exec(query, model.ApplicationStatusApproved, approverID, id)
return err
}
func (r *ApplicationRepository) Reject(id int64, reason string) error {
query := `UPDATE material_applications SET status = ?, rejection_reason = ?, updated_at = NOW() WHERE id = ?`
_, err := database.DB.Exec(query, model.ApplicationStatusRejected, reason, id)
return err
}
func (r *ApplicationRepository) Cancel(id int64) error {
return r.UpdateStatus(id, model.ApplicationStatusCancelled)
}
func (r *ApplicationRepository) GetByIDWithDetails(id int64) (*model.MaterialApplication, error) {
return r.GetByID(id)
}
// outbound_orders 没有 status 列:出库单一经创建即为完成态,
// 这里与手工出库路径(OutboundRepo.CreateTx)写入同样的列。
func (r *ApplicationRepository) CreateOutboundOrder(order *model.OutboundOrder) error {
query := `INSERT INTO outbound_orders (outbound_date, operator, handler, receiver) VALUES (NOW(), ?, ?, ?)`
result, err := database.DB.Exec(query, order.Operator, order.Handler, order.Receiver)
if err != nil {
return err
}
id, err := result.LastInsertId()
if err != nil {
return err
}
order.ID = id
order.OutboundNo = model.OrderNo("OUT", id)
if _, err := database.DB.Exec(`UPDATE outbound_orders SET outbound_no = ? WHERE id = ?`, order.OutboundNo, id); err != nil {
return err
}
return nil
}
// outbound_orders 与 inbound_orders 一样没有 status 列,单据状态由查询层统一写死。
// 这里写入的列必须和手工出库(OutboundRepo.CreateTx)一致,多写一列会让 INSERT 直接失败。
func (r *ApplicationRepository) CreateOutboundOrderTx(tx *sql.Tx, order *model.OutboundOrder) error {
query := `INSERT INTO outbound_orders (outbound_date, operator, handler, receiver) VALUES (NOW(), ?, ?, ?)`
result, err := tx.Exec(query, order.Operator, order.Handler, order.Receiver)
if err != nil {
return err
}
id, err := result.LastInsertId()
if err != nil {
return err
}
order.ID = id
order.OutboundNo = model.OrderNo("OUT", id)
if _, err := tx.Exec(`UPDATE outbound_orders SET outbound_no = ? WHERE id = ?`, order.OutboundNo, id); err != nil {
return err
}
return nil
}
func (r *ApplicationRepository) CreateOutboundItem(item *model.OutboundItem) error {
query := `INSERT INTO outbound_items (outbound_order_id, material_id, quantity) VALUES (?, ?, ?)`
result, err := database.DB.Exec(query, item.OutboundOrderID, item.MaterialID, item.Quantity)
if err != nil {
return err
}
id, err := result.LastInsertId()
if err != nil {
return err
}
item.ID = id
return nil
}
func (r *ApplicationRepository) CreateOutboundItemTx(tx *sql.Tx, item *model.OutboundItem) error {
query := `INSERT INTO outbound_items (outbound_order_id, material_id, quantity) VALUES (?, ?, ?)`
result, err := tx.Exec(query, item.OutboundOrderID, item.MaterialID, item.Quantity)
if err != nil {
return err
}
id, err := result.LastInsertId()
if err != nil {
return err
}
item.ID = id
return nil
}
func (r *ApplicationRepository) CreateStockLog(log *model.StockLog) error {
query := `INSERT INTO stock_logs (material_id, type, quantity, related_order_type, related_order_id) VALUES (?, ?, ?, ?, ?)`
result, err := database.DB.Exec(query, log.MaterialID, log.Type, log.Quantity, log.RelatedOrderType, log.RelatedOrderID)
if err != nil {
return err
}
_, err = result.LastInsertId()
return err
}
func (r *ApplicationRepository) CreateStockLogTx(tx *sql.Tx, log *model.StockLog) error {
query := `INSERT INTO stock_logs (material_id, type, quantity, related_order_type, related_order_id) VALUES (?, ?, ?, ?, ?)`
result, err := tx.Exec(query, log.MaterialID, log.Type, log.Quantity, log.RelatedOrderType, log.RelatedOrderID)
if err != nil {
return err
}
_, err = result.LastInsertId()
return err
}
func (r *ApplicationRepository) ApproveTx(tx *sql.Tx, id int64, approverID int64) error {
query := `UPDATE material_applications SET status = ?, approved_by = ?, updated_at = NOW() WHERE id = ?`
_, err := tx.Exec(query, model.ApplicationStatusApproved, approverID, id)
return err
}
func (r *ApplicationRepository) GetApproverName(userID int64) (string, error) {
query := `SELECT username FROM users WHERE id = ?`
var username string
err := database.DB.QueryRow(query, userID).Scan(&username)
return username, err
}
func (r *ApplicationRepository) InitApplicationTable() error {
query := `
CREATE TABLE IF NOT EXISTS material_applications (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT NOT NULL,
purpose VARCHAR(500) DEFAULT '',
status ENUM('pending','approved','rejected','cancelled') NOT NULL DEFAULT 'pending',
rejection_reason VARCHAR(500) DEFAULT '',
approved_by BIGINT DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
)`
_, err := database.DB.Exec(query)
if err != nil {
return err
}
// Create application_items table
itemsQuery := `
CREATE TABLE IF NOT EXISTS application_items (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
application_id BIGINT NOT NULL,
material_id BIGINT NOT NULL,
quantity INT NOT NULL,
FOREIGN KEY (application_id) REFERENCES material_applications(id) ON DELETE CASCADE,
FOREIGN KEY (material_id) REFERENCES materials(id)
)`
_, err = database.DB.Exec(itemsQuery)
return err
}