This commit is contained in:
yvan 2026-08-17 02:39:40 +08:00
parent 6a05803e08
commit d09e6b42bd
16 changed files with 74 additions and 19 deletions

View File

@ -12,7 +12,7 @@ type Announcement struct {
UpdatedAt time.Time
Title string
Content string
UserID *uint
UserID *int
Attachments json.RawMessage
}

View File

@ -100,10 +100,10 @@ type AuditRecordRepo interface {
type AuditQueryRepo interface {
ListOperations(context.Context, int, int, *OperationRecord) ([]*OperationRecord, int64, error)
FindOperation(context.Context, uint) (*OperationRecord, error)
DeleteOperations(context.Context, []uint) error
DeleteOperations(context.Context, []int) error
ListLogins(context.Context, int, int, *LoginLog) ([]*LoginLog, int64, error)
FindLogin(context.Context, uint) (*LoginLog, error)
DeleteLogins(context.Context, []uint) error
DeleteLogins(context.Context, []int) error
ListDataAccess(context.Context, int, int, *DataAccessLog) ([]*DataAccessLog, int64, error)
DeleteDataAccess(context.Context, []uint) error
UpdateError(context.Context, *ErrorRecord) error

View File

@ -17,7 +17,7 @@ type announcementPO struct {
DeletedAt gorm.DeletedAt `gorm:"index"`
Title string
Content string `gorm:"type:text"`
UserID *uint `gorm:"column:user_id"`
UserID *int `gorm:"column:user_id"`
Attachments jsonPO
}

View File

@ -38,3 +38,22 @@ func TestAnnouncementRepositoryKeepsRawIDQuerySemantics(t *testing.T) {
t.Fatalf("raw invalid ID removed rows: count=%d", count)
}
}
func TestAnnouncementRepositoryPreservesSignedUserID(t *testing.T) {
data := newTransactionTestData(t)
repo := &announcementRepo{data: data}
ctx := context.Background()
userID := -1
item := &biz.Announcement{Title: "notice", UserID: &userID}
if err := repo.Create(ctx, item); err != nil {
t.Fatal(err)
}
found, err := repo.Find(ctx, "1")
if err != nil {
t.Fatal(err)
}
if found.UserID == nil || *found.UserID != userID {
t.Fatalf("found user ID = %v, want %d", found.UserID, userID)
}
}

View File

@ -0,0 +1,25 @@
package data
import (
"context"
"testing"
)
func TestAuditSingleZeroDeleteFailsAndSignedBatchIsAccepted(t *testing.T) {
data := newTransactionTestData(t)
repo := &auditQueryRepo{data: data}
ctx := context.Background()
if err := repo.DeleteOperations(ctx, []int{0}); err == nil {
t.Fatal("operation single delete with ID zero must fail")
}
if err := repo.DeleteLogins(ctx, []int{0}); err == nil {
t.Fatal("login single delete with ID zero must fail")
}
if err := repo.DeleteOperations(ctx, []int{-1}); err != nil {
t.Fatalf("operation signed batch delete failed: %v", err)
}
if err := repo.DeleteLogins(ctx, []int{-1}); err != nil {
t.Fatalf("login signed batch delete failed: %v", err)
}
}

View File

@ -32,7 +32,7 @@ type departmentPO struct {
func (departmentPO) TableName() string { return "sys_departments" }
type userDepartmentPO struct {
// Match GVA's join model: no composite primary key, so repeated IDs in a
// Match the reference join model: no composite primary key, so repeated IDs in a
// request are persisted in the same order/shape as the reference service.
UserID uint `gorm:"column:sys_user_id"`
DepartmentID uint `gorm:"column:sys_department_id"`

View File

@ -73,6 +73,9 @@ func (r *auditQueryRepo) FindLogin(ctx context.Context, id uint) (*biz.LoginLog,
}
return loginFromPO(po), nil
}
func (r *auditQueryRepo) DeleteLogins(ctx context.Context, ids []uint) error {
func (r *auditQueryRepo) DeleteLogins(ctx context.Context, ids []int) error {
if len(ids) == 1 && ids[0] == 0 {
return r.data.gormDB.WithContext(ctx).Delete(&loginLogPO{}).Error
}
return r.data.gormDB.WithContext(ctx).Delete(&loginLogPO{}, "id IN ?", ids).Error
}

View File

@ -97,6 +97,10 @@ func (r *auditQueryRepo) FindOperation(ctx context.Context, id uint) (*biz.Opera
}
return opFromPO(po), nil
}
func (r *auditQueryRepo) DeleteOperations(ctx context.Context, ids []uint) error {
func (r *auditQueryRepo) DeleteOperations(ctx context.Context, ids []int) error {
if len(ids) == 1 && ids[0] == 0 {
return r.data.gormDB.WithContext(ctx).Delete(&operationPO{}).Error
}
return r.data.gormDB.WithContext(ctx).Delete(&operationPO{}, "id IN ?", ids).Error
}

View File

@ -29,7 +29,7 @@ type positionPO struct {
func (positionPO) TableName() string { return "sys_positions" }
type userPositionPO struct {
// GVA does not declare a key on this explicit join model. Do not let a
// The reference implementation does not declare a key on this explicit join model. Do not let a
// database uniqueness constraint change duplicate-assignment behavior.
UserID uint `gorm:"column:sys_user_id"`
PositionID uint `gorm:"column:sys_position_id"`

View File

@ -67,7 +67,7 @@ type menuPO struct {
func (menuPO) TableName() string { return "sys_base_menus" }
type userAuthorityPO struct {
// GVA's explicit join model deliberately has no primary key or uniqueness
// The reference join model deliberately has no primary key or uniqueness
// constraint. Keep duplicate IDs representable; association replacement
// and validation are handled by the service transaction instead.
SysUserID uint `gorm:"column:sys_user_id"`

View File

@ -41,7 +41,7 @@ func TestUserAuthorityWritesAreAtomic(t *testing.T) {
t.Fatalf("authority links = %d, err = %v", links, err)
}
// With strict authority checks disabled, GVA writes the supplied join rows
// With strict authority checks disabled, the reference implementation writes the supplied join rows
// without first requiring every role record to exist.
created.NickName = "compatible update"
if err = repo.UpdateUserWithAuthorities(ctx, created, []uint{888, 123456}); err != nil {

View File

@ -100,14 +100,14 @@ func (h *Audit) DeleteOperation(c *gin.Context) {
httpx.Fail(c, err.Error())
return
}
if err := h.service.DeleteOperations(c.Request.Context(), []uint{req.ID}); err != nil {
if err := h.service.DeleteOperations(c.Request.Context(), []int{int(req.ID)}); err != nil {
httpx.Fail(c, "删除失败")
return
}
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "删除成功")
}
func (h *Audit) DeleteOperations(c *gin.Context) {
var req dto.IDsRequest
var req dto.AuditIDsRequest
if err := c.ShouldBindJSON(&req); err != nil {
httpx.Fail(c, err.Error())
return
@ -151,14 +151,14 @@ func (h *Audit) DeleteLogin(c *gin.Context) {
httpx.Fail(c, err.Error())
return
}
if err := h.service.DeleteLogins(c.Request.Context(), []uint{req.ID}); err != nil {
if err := h.service.DeleteLogins(c.Request.Context(), []int{int(req.ID)}); err != nil {
httpx.Fail(c, "删除失败")
return
}
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "删除成功")
}
func (h *Audit) DeleteLogins(c *gin.Context) {
var req dto.IDsRequest
var req dto.AuditIDsRequest
if err := c.ShouldBindJSON(&req); err != nil {
httpx.Fail(c, err.Error())
return

View File

@ -13,7 +13,7 @@ type AnnouncementInput struct {
ID uint
Title string
Content string
UserID *uint
UserID *int
Attachments json.RawMessage
}

View File

@ -49,7 +49,7 @@ func (s *AuditService) Operation(ctx context.Context, id uint) (*dto.OperationRe
}
return opDTO(v), nil
}
func (s *AuditService) DeleteOperations(ctx context.Context, ids []uint) error {
func (s *AuditService) DeleteOperations(ctx context.Context, ids []int) error {
return s.uc.DeleteOperations(ctx, ids)
}
@ -87,7 +87,7 @@ func (s *AuditService) Login(ctx context.Context, id uint) (*dto.LoginLogRespons
}
return loginDTO(v), nil
}
func (s *AuditService) DeleteLogins(ctx context.Context, ids []uint) error {
func (s *AuditService) DeleteLogins(ctx context.Context, ids []int) error {
return s.uc.DeleteLogins(ctx, ids)
}

View File

@ -9,7 +9,7 @@ type AnnouncementRequest struct {
ID uint `json:"ID"`
Title string `json:"title"`
Content string `json:"content"`
UserID *uint `json:"userID"`
UserID *int `json:"userID"`
Attachments json.RawMessage `json:"attachments"`
}
@ -29,7 +29,7 @@ type AnnouncementResponse struct {
DeletedAt any `json:"-"`
Title string `json:"title"`
Content string `json:"content"`
UserID *uint `json:"userID"`
UserID *int `json:"userID"`
Attachments any `json:"attachments"`
}

View File

@ -55,6 +55,10 @@ type AuditIDQuery struct {
ID uint `form:"ID"`
}
type AuditIDsRequest struct {
IDs []int `json:"ids"`
}
type ErrorRecordRequest struct {
Form string `json:"form"`
Info string `json:"info"`