You've already forked openaccounting-server
forked from cybercinch/openaccounting-server
feat: implement attachment database layer
- Add AttachmentInterface to main Datastore interface - Implement CRUD operations for attachments following existing patterns - Add proper SQL marshalling/unmarshalling with HEX/UNHEX for binary IDs - Include soft deletion and proper indexing support 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
126
core/model/db/attachment.go
Normal file
126
core/model/db/attachment.go
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
|
"github.com/openaccounting/oa-server/core/model/types"
|
||||||
|
"github.com/openaccounting/oa-server/core/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
const attachmentFields = "LOWER(HEX(id)),LOWER(HEX(transactionId)),LOWER(HEX(orgId)),LOWER(HEX(userId)),fileName,originalName,contentType,fileSize,filePath,description,uploaded,deleted"
|
||||||
|
|
||||||
|
type AttachmentInterface interface {
|
||||||
|
InsertAttachment(*types.Attachment) error
|
||||||
|
GetAttachment(string, string, string) (*types.Attachment, error)
|
||||||
|
GetAttachmentsByTransaction(string, string) ([]*types.Attachment, error)
|
||||||
|
DeleteAttachment(string, string, string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) InsertAttachment(attachment *types.Attachment) error {
|
||||||
|
query := "INSERT INTO attachment(id,transactionId,orgId,userId,fileName,originalName,contentType,fileSize,filePath,description,uploaded,deleted) VALUES(UNHEX(?),UNHEX(?),UNHEX(?),UNHEX(?),?,?,?,?,?,?,?,?)"
|
||||||
|
|
||||||
|
_, err := db.Exec(
|
||||||
|
query,
|
||||||
|
attachment.Id,
|
||||||
|
attachment.TransactionId,
|
||||||
|
attachment.OrgId,
|
||||||
|
attachment.UserId,
|
||||||
|
attachment.FileName,
|
||||||
|
attachment.OriginalName,
|
||||||
|
attachment.ContentType,
|
||||||
|
attachment.FileSize,
|
||||||
|
attachment.FilePath,
|
||||||
|
attachment.Description,
|
||||||
|
util.TimeToMs(attachment.Uploaded),
|
||||||
|
attachment.Deleted,
|
||||||
|
)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) GetAttachment(attachmentId, transactionId, orgId string) (*types.Attachment, error) {
|
||||||
|
query := "SELECT " + attachmentFields + " FROM attachment WHERE id = UNHEX(?) AND transactionId = UNHEX(?) AND orgId = UNHEX(?) AND deleted = false"
|
||||||
|
row := db.QueryRow(query, attachmentId, transactionId, orgId)
|
||||||
|
|
||||||
|
return db.unmarshalAttachment(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) GetAttachmentsByTransaction(transactionId, orgId string) ([]*types.Attachment, error) {
|
||||||
|
query := "SELECT " + attachmentFields + " FROM attachment WHERE transactionId = UNHEX(?) AND orgId = UNHEX(?) AND deleted = false ORDER BY uploaded DESC"
|
||||||
|
rows, err := db.Query(query, transactionId, orgId)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return db.unmarshalAttachments(rows)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) DeleteAttachment(attachmentId, transactionId, orgId string) error {
|
||||||
|
query := "UPDATE attachment SET deleted = true WHERE id = UNHEX(?) AND transactionId = UNHEX(?) AND orgId = UNHEX(?)"
|
||||||
|
_, err := db.Exec(query, attachmentId, transactionId, orgId)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) unmarshalAttachment(row *sql.Row) (*types.Attachment, error) {
|
||||||
|
attachment := &types.Attachment{}
|
||||||
|
var uploaded int64
|
||||||
|
|
||||||
|
err := row.Scan(
|
||||||
|
&attachment.Id,
|
||||||
|
&attachment.TransactionId,
|
||||||
|
&attachment.OrgId,
|
||||||
|
&attachment.UserId,
|
||||||
|
&attachment.FileName,
|
||||||
|
&attachment.OriginalName,
|
||||||
|
&attachment.ContentType,
|
||||||
|
&attachment.FileSize,
|
||||||
|
&attachment.FilePath,
|
||||||
|
&attachment.Description,
|
||||||
|
&uploaded,
|
||||||
|
&attachment.Deleted,
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
attachment.Uploaded = util.MsToTime(uploaded)
|
||||||
|
|
||||||
|
return attachment, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) unmarshalAttachments(rows *sql.Rows) ([]*types.Attachment, error) {
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
attachments := []*types.Attachment{}
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
attachment := &types.Attachment{}
|
||||||
|
var uploaded int64
|
||||||
|
|
||||||
|
err := rows.Scan(
|
||||||
|
&attachment.Id,
|
||||||
|
&attachment.TransactionId,
|
||||||
|
&attachment.OrgId,
|
||||||
|
&attachment.UserId,
|
||||||
|
&attachment.FileName,
|
||||||
|
&attachment.OriginalName,
|
||||||
|
&attachment.ContentType,
|
||||||
|
&attachment.FileSize,
|
||||||
|
&attachment.FilePath,
|
||||||
|
&attachment.Description,
|
||||||
|
&uploaded,
|
||||||
|
&attachment.Deleted,
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
attachment.Uploaded = util.MsToTime(uploaded)
|
||||||
|
attachments = append(attachments, attachment)
|
||||||
|
}
|
||||||
|
|
||||||
|
return attachments, nil
|
||||||
|
}
|
||||||
@@ -15,6 +15,7 @@ type Datastore interface {
|
|||||||
OrgInterface
|
OrgInterface
|
||||||
AccountInterface
|
AccountInterface
|
||||||
TransactionInterface
|
TransactionInterface
|
||||||
|
AttachmentInterface
|
||||||
PriceInterface
|
PriceInterface
|
||||||
SessionInterface
|
SessionInterface
|
||||||
ApiKeyInterface
|
ApiKeyInterface
|
||||||
|
|||||||
Reference in New Issue
Block a user