diff --git a/internal/biz/announcement.go b/internal/biz/announcement.go index 0e468b4..a1e8165 100644 --- a/internal/biz/announcement.go +++ b/internal/biz/announcement.go @@ -12,7 +12,7 @@ type Announcement struct { UpdatedAt time.Time Title string Content string - UserID *uint + UserID *int Attachments json.RawMessage } diff --git a/internal/biz/audit.go b/internal/biz/audit.go index be37bde..42e835e 100644 --- a/internal/biz/audit.go +++ b/internal/biz/audit.go @@ -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 diff --git a/internal/data/announcement.go b/internal/data/announcement.go index fb4adcd..c1fa2df 100644 --- a/internal/data/announcement.go +++ b/internal/data/announcement.go @@ -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 } diff --git a/internal/data/announcement_test.go b/internal/data/announcement_test.go index bd51086..d9687cb 100644 --- a/internal/data/announcement_test.go +++ b/internal/data/announcement_test.go @@ -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) + } +} diff --git a/internal/data/audit_delete_test.go b/internal/data/audit_delete_test.go new file mode 100644 index 0000000..cfc0332 --- /dev/null +++ b/internal/data/audit_delete_test.go @@ -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) + } +} diff --git a/internal/data/department.go b/internal/data/department.go index d6fc7c5..5826fb6 100644 --- a/internal/data/department.go +++ b/internal/data/department.go @@ -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"` diff --git a/internal/data/login_log.go b/internal/data/login_log.go index 8e17d38..0d990ae 100644 --- a/internal/data/login_log.go +++ b/internal/data/login_log.go @@ -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 } diff --git a/internal/data/operation_log.go b/internal/data/operation_log.go index a5a4ee2..4943455 100644 --- a/internal/data/operation_log.go +++ b/internal/data/operation_log.go @@ -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 } diff --git a/internal/data/position.go b/internal/data/position.go index 970ede1..da0d9c9 100644 --- a/internal/data/position.go +++ b/internal/data/position.go @@ -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"` diff --git a/internal/data/system.go b/internal/data/system.go index ede6306..5133a6e 100644 --- a/internal/data/system.go +++ b/internal/data/system.go @@ -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"` diff --git a/internal/data/transactions_test.go b/internal/data/transactions_test.go index 849de81..325a510 100644 --- a/internal/data/transactions_test.go +++ b/internal/data/transactions_test.go @@ -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 { diff --git a/internal/server/handler/audit.go b/internal/server/handler/audit.go index 10af202..f57851e 100644 --- a/internal/server/handler/audit.go +++ b/internal/server/handler/audit.go @@ -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 diff --git a/internal/service/announcement.go b/internal/service/announcement.go index 8adac38..b85a727 100644 --- a/internal/service/announcement.go +++ b/internal/service/announcement.go @@ -13,7 +13,7 @@ type AnnouncementInput struct { ID uint Title string Content string - UserID *uint + UserID *int Attachments json.RawMessage } diff --git a/internal/service/audit.go b/internal/service/audit.go index 0a31a27..01fa45b 100644 --- a/internal/service/audit.go +++ b/internal/service/audit.go @@ -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) } diff --git a/internal/service/dto/announcement.go b/internal/service/dto/announcement.go index 4070d83..9172479 100644 --- a/internal/service/dto/announcement.go +++ b/internal/service/dto/announcement.go @@ -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"` } diff --git a/internal/service/dto/audit.go b/internal/service/dto/audit.go index 29decd9..224c4fb 100644 --- a/internal/service/dto/audit.go +++ b/internal/service/dto/audit.go @@ -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"`