This commit is contained in:
parent
6a05803e08
commit
d09e6b42bd
|
|
@ -12,7 +12,7 @@ type Announcement struct {
|
||||||
UpdatedAt time.Time
|
UpdatedAt time.Time
|
||||||
Title string
|
Title string
|
||||||
Content string
|
Content string
|
||||||
UserID *uint
|
UserID *int
|
||||||
Attachments json.RawMessage
|
Attachments json.RawMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -100,10 +100,10 @@ type AuditRecordRepo interface {
|
||||||
type AuditQueryRepo interface {
|
type AuditQueryRepo interface {
|
||||||
ListOperations(context.Context, int, int, *OperationRecord) ([]*OperationRecord, int64, error)
|
ListOperations(context.Context, int, int, *OperationRecord) ([]*OperationRecord, int64, error)
|
||||||
FindOperation(context.Context, uint) (*OperationRecord, 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)
|
ListLogins(context.Context, int, int, *LoginLog) ([]*LoginLog, int64, error)
|
||||||
FindLogin(context.Context, uint) (*LoginLog, 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)
|
ListDataAccess(context.Context, int, int, *DataAccessLog) ([]*DataAccessLog, int64, error)
|
||||||
DeleteDataAccess(context.Context, []uint) error
|
DeleteDataAccess(context.Context, []uint) error
|
||||||
UpdateError(context.Context, *ErrorRecord) error
|
UpdateError(context.Context, *ErrorRecord) error
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ type announcementPO struct {
|
||||||
DeletedAt gorm.DeletedAt `gorm:"index"`
|
DeletedAt gorm.DeletedAt `gorm:"index"`
|
||||||
Title string
|
Title string
|
||||||
Content string `gorm:"type:text"`
|
Content string `gorm:"type:text"`
|
||||||
UserID *uint `gorm:"column:user_id"`
|
UserID *int `gorm:"column:user_id"`
|
||||||
Attachments jsonPO
|
Attachments jsonPO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,3 +38,22 @@ func TestAnnouncementRepositoryKeepsRawIDQuerySemantics(t *testing.T) {
|
||||||
t.Fatalf("raw invalid ID removed rows: count=%d", count)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -32,7 +32,7 @@ type departmentPO struct {
|
||||||
func (departmentPO) TableName() string { return "sys_departments" }
|
func (departmentPO) TableName() string { return "sys_departments" }
|
||||||
|
|
||||||
type userDepartmentPO struct {
|
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.
|
// request are persisted in the same order/shape as the reference service.
|
||||||
UserID uint `gorm:"column:sys_user_id"`
|
UserID uint `gorm:"column:sys_user_id"`
|
||||||
DepartmentID uint `gorm:"column:sys_department_id"`
|
DepartmentID uint `gorm:"column:sys_department_id"`
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,9 @@ func (r *auditQueryRepo) FindLogin(ctx context.Context, id uint) (*biz.LoginLog,
|
||||||
}
|
}
|
||||||
return loginFromPO(po), nil
|
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
|
return r.data.gormDB.WithContext(ctx).Delete(&loginLogPO{}, "id IN ?", ids).Error
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,10 @@ func (r *auditQueryRepo) FindOperation(ctx context.Context, id uint) (*biz.Opera
|
||||||
}
|
}
|
||||||
return opFromPO(po), nil
|
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
|
return r.data.gormDB.WithContext(ctx).Delete(&operationPO{}, "id IN ?", ids).Error
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ type positionPO struct {
|
||||||
func (positionPO) TableName() string { return "sys_positions" }
|
func (positionPO) TableName() string { return "sys_positions" }
|
||||||
|
|
||||||
type userPositionPO struct {
|
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.
|
// database uniqueness constraint change duplicate-assignment behavior.
|
||||||
UserID uint `gorm:"column:sys_user_id"`
|
UserID uint `gorm:"column:sys_user_id"`
|
||||||
PositionID uint `gorm:"column:sys_position_id"`
|
PositionID uint `gorm:"column:sys_position_id"`
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ type menuPO struct {
|
||||||
func (menuPO) TableName() string { return "sys_base_menus" }
|
func (menuPO) TableName() string { return "sys_base_menus" }
|
||||||
|
|
||||||
type userAuthorityPO struct {
|
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
|
// constraint. Keep duplicate IDs representable; association replacement
|
||||||
// and validation are handled by the service transaction instead.
|
// and validation are handled by the service transaction instead.
|
||||||
SysUserID uint `gorm:"column:sys_user_id"`
|
SysUserID uint `gorm:"column:sys_user_id"`
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ func TestUserAuthorityWritesAreAtomic(t *testing.T) {
|
||||||
t.Fatalf("authority links = %d, err = %v", links, err)
|
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.
|
// without first requiring every role record to exist.
|
||||||
created.NickName = "compatible update"
|
created.NickName = "compatible update"
|
||||||
if err = repo.UpdateUserWithAuthorities(ctx, created, []uint{888, 123456}); err != nil {
|
if err = repo.UpdateUserWithAuthorities(ctx, created, []uint{888, 123456}); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -100,14 +100,14 @@ func (h *Audit) DeleteOperation(c *gin.Context) {
|
||||||
httpx.Fail(c, err.Error())
|
httpx.Fail(c, err.Error())
|
||||||
return
|
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, "删除失败")
|
httpx.Fail(c, "删除失败")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "删除成功")
|
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "删除成功")
|
||||||
}
|
}
|
||||||
func (h *Audit) DeleteOperations(c *gin.Context) {
|
func (h *Audit) DeleteOperations(c *gin.Context) {
|
||||||
var req dto.IDsRequest
|
var req dto.AuditIDsRequest
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
httpx.Fail(c, err.Error())
|
httpx.Fail(c, err.Error())
|
||||||
return
|
return
|
||||||
|
|
@ -151,14 +151,14 @@ func (h *Audit) DeleteLogin(c *gin.Context) {
|
||||||
httpx.Fail(c, err.Error())
|
httpx.Fail(c, err.Error())
|
||||||
return
|
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, "删除失败")
|
httpx.Fail(c, "删除失败")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "删除成功")
|
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "删除成功")
|
||||||
}
|
}
|
||||||
func (h *Audit) DeleteLogins(c *gin.Context) {
|
func (h *Audit) DeleteLogins(c *gin.Context) {
|
||||||
var req dto.IDsRequest
|
var req dto.AuditIDsRequest
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
httpx.Fail(c, err.Error())
|
httpx.Fail(c, err.Error())
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ type AnnouncementInput struct {
|
||||||
ID uint
|
ID uint
|
||||||
Title string
|
Title string
|
||||||
Content string
|
Content string
|
||||||
UserID *uint
|
UserID *int
|
||||||
Attachments json.RawMessage
|
Attachments json.RawMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ func (s *AuditService) Operation(ctx context.Context, id uint) (*dto.OperationRe
|
||||||
}
|
}
|
||||||
return opDTO(v), nil
|
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)
|
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
|
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)
|
return s.uc.DeleteLogins(ctx, ids)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ type AnnouncementRequest struct {
|
||||||
ID uint `json:"ID"`
|
ID uint `json:"ID"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
UserID *uint `json:"userID"`
|
UserID *int `json:"userID"`
|
||||||
Attachments json.RawMessage `json:"attachments"`
|
Attachments json.RawMessage `json:"attachments"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -29,7 +29,7 @@ type AnnouncementResponse struct {
|
||||||
DeletedAt any `json:"-"`
|
DeletedAt any `json:"-"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
UserID *uint `json:"userID"`
|
UserID *int `json:"userID"`
|
||||||
Attachments any `json:"attachments"`
|
Attachments any `json:"attachments"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,10 @@ type AuditIDQuery struct {
|
||||||
ID uint `form:"ID"`
|
ID uint `form:"ID"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AuditIDsRequest struct {
|
||||||
|
IDs []int `json:"ids"`
|
||||||
|
}
|
||||||
|
|
||||||
type ErrorRecordRequest struct {
|
type ErrorRecordRequest struct {
|
||||||
Form string `json:"form"`
|
Form string `json:"form"`
|
||||||
Info string `json:"info"`
|
Info string `json:"info"`
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue