优化结构
This commit is contained in:
@@ -29,7 +29,6 @@ var ProviderSet = wire.NewSet(
|
||||
wire.Bind(new(datasystem.Provider), new(*Data)),
|
||||
wire.Bind(new(datasystem.DatabaseProvider), new(*Data)),
|
||||
wire.Bind(new(dataintegration.Provider), new(*Data)),
|
||||
wire.Bind(new(datatask.Provider), new(*Data)),
|
||||
wire.Bind(new(datapayment.Provider), new(*Data)),
|
||||
datasystem.ProviderSet,
|
||||
dataintegration.ProviderSet,
|
||||
@@ -235,8 +234,9 @@ func NewData(runtime *config.Store, appLogger *slog.Logger, storageManager *stor
|
||||
usingFallback = true
|
||||
}
|
||||
d.databaseReady.Store(!usingFallback)
|
||||
d.gormDB = newReloadableDB(db, d.enqueueDataScopeAudit)
|
||||
d.gormDB = newReloadableDB(db)
|
||||
d.auditLog = newDataScopeAuditWriter(d, appLogger)
|
||||
registerDataScopeCallbacks(db, d.enqueueDataScopeAudit)
|
||||
d.dbList, err = openDatabaseList(c.DatabaseList, appLogger)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"kra/internal/logging"
|
||||
"kra/pkg/database/gormkit"
|
||||
"kra/pkg/logging"
|
||||
|
||||
"gorm.io/gorm/logger"
|
||||
)
|
||||
|
||||
@@ -14,18 +14,17 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
datapayment "kra/internal/integration/payment"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type paymentRepo struct {
|
||||
data Provider
|
||||
config integrationbiz.PaymentConfigReader
|
||||
data Provider
|
||||
config integrationbiz.PaymentConfigReader
|
||||
factory bizpayment.PaymentAdapterFactory
|
||||
}
|
||||
|
||||
func NewPaymentRepo(data Provider, config integrationbiz.PaymentConfigReader) bizpayment.PaymentRepo {
|
||||
return &paymentRepo{data: data, config: config}
|
||||
func NewPaymentRepo(data Provider, config integrationbiz.PaymentConfigReader, factory bizpayment.PaymentAdapterFactory) bizpayment.PaymentRepo {
|
||||
return &paymentRepo{data: data, config: config, factory: factory}
|
||||
}
|
||||
|
||||
func (r *paymentRepo) values(ctx context.Context, provider string) (map[string]any, error) {
|
||||
@@ -49,12 +48,15 @@ func (r *paymentRepo) values(ctx context.Context, provider string) (map[string]a
|
||||
return values, nil
|
||||
}
|
||||
|
||||
func (r *paymentRepo) adapter(ctx context.Context, provider string) (datapayment.Adapter, map[string]any, error) {
|
||||
func (r *paymentRepo) adapter(ctx context.Context, provider string) (bizpayment.PaymentAdapter, map[string]any, error) {
|
||||
values, err := r.values(ctx, provider)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
adapter, err := datapayment.New(provider)
|
||||
if r.factory == nil {
|
||||
return nil, nil, errors.New("支付渠道适配器未接入")
|
||||
}
|
||||
adapter, err := r.factory.New(provider)
|
||||
return adapter, values, err
|
||||
}
|
||||
|
||||
@@ -81,7 +83,13 @@ func (r *paymentRepo) TestProvider(ctx context.Context, provider string) (*bizpa
|
||||
return test, err
|
||||
}
|
||||
|
||||
adapter, err := datapayment.New(provider)
|
||||
if r.factory == nil {
|
||||
err = errors.New("支付渠道适配器未接入")
|
||||
}
|
||||
var adapter bizpayment.PaymentAdapter
|
||||
if err == nil {
|
||||
adapter, err = r.factory.New(provider)
|
||||
}
|
||||
if err != nil {
|
||||
add("adapter", "failed", err.Error(), "", time.Now())
|
||||
return test, err
|
||||
@@ -237,7 +245,7 @@ func validatePaymentTestResult(provider, tradeNo string, result *bizpayment.Paym
|
||||
return nil
|
||||
}
|
||||
|
||||
func queryPaymentTest(ctx context.Context, adapter datapayment.Adapter, queryID string, values map[string]any) (*bizpayment.PaymentResult, error) {
|
||||
func queryPaymentTest(ctx context.Context, adapter bizpayment.PaymentAdapter, queryID string, values map[string]any) (*bizpayment.PaymentResult, error) {
|
||||
var result *bizpayment.PaymentResult
|
||||
var err error
|
||||
for attempt := 0; attempt < 3; attempt++ {
|
||||
@@ -547,7 +555,3 @@ func contains(values []string, value string) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func validatePaymentConfig(provider string, values map[string]any) error {
|
||||
return integrationbiz.ValidateIntegrationConfig(integrationbiz.IntegrationKindPayment, provider, values)
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ func TestValidatePaymentConfigRequiresDouyinAppIDWhenEnabled(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidatePaymentConfigAcceptsProviderAliases(t *testing.T) {
|
||||
err := validatePaymentConfig(bizpayment.PaymentDouyin, map[string]any{
|
||||
err := integrationbiz.ValidateIntegrationConfig(integrationbiz.IntegrationKindPayment, bizpayment.PaymentDouyin, map[string]any{
|
||||
"app_id": "douyin-app", "merchant_id": "merchant-douyin", "serial_no": "merchant-serial", "api_key": "01234567890123456789012345678901",
|
||||
"private_key": "merchant-private-key", "platform_cert": "platform-public-key", "platform_cert_serial": "platform-serial",
|
||||
})
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
package payment
|
||||
|
||||
import "kra/pkg/paymentkit"
|
||||
import "kra/internal/paymentkit"
|
||||
|
||||
func text(values map[string]any, key string) string {
|
||||
return paymentutil.Text(values, key)
|
||||
return paymentkit.Text(values, key)
|
||||
}
|
||||
|
||||
func firstAny(values map[string]any, keys ...string) string {
|
||||
return paymentutil.FirstText(values, keys...)
|
||||
return paymentkit.FirstText(values, keys...)
|
||||
}
|
||||
|
||||
// configuredInt64 keeps repository-side validation independent from the
|
||||
// provider adapter package while sharing the canonical payment parser.
|
||||
func configuredInt64(values map[string]any, key string, fallback int64) int64 {
|
||||
return paymentutil.ConfiguredInt64(values, key, fallback)
|
||||
return paymentkit.ConfiguredInt64(values, key, fallback)
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ func TestPaymentDefinitionsProvideNonEmptyDefaults(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPaymentValidationRejectsUnknownProvider(t *testing.T) {
|
||||
err := validatePaymentConfig("unknown-provider", map[string]any{})
|
||||
err := integrationbiz.ValidateIntegrationConfig(integrationbiz.IntegrationKindPayment, "unknown-provider", map[string]any{})
|
||||
if err == nil || !strings.Contains(err.Error(), "不支持") {
|
||||
t.Fatalf("error = %v", err)
|
||||
}
|
||||
|
||||
@@ -163,10 +163,9 @@ func (r *rollback) run() {
|
||||
}
|
||||
}
|
||||
|
||||
// newReloadableDB attaches the row-level data scope callbacks the pool needs
|
||||
// before it becomes readable. Data.replacePrimaryDB is the reload counterpart.
|
||||
func newReloadableDB(db *gorm.DB, enqueue dataScopeAuditEnqueue) *reloadable[*gorm.DB] {
|
||||
registerDataScopeCallbacks(db, enqueue)
|
||||
// newReloadableDB owns only the hot-swappable database handle. Domain-specific
|
||||
// callbacks are attached by Data when a pool becomes active.
|
||||
func newReloadableDB(db *gorm.DB, _ ...dataScopeAuditEnqueue) *reloadable[*gorm.DB] {
|
||||
return newReloadable(db, closeGormDB)
|
||||
}
|
||||
|
||||
|
||||
@@ -109,13 +109,6 @@ func (r *apiTokenRepo) ListAPITokens(ctx context.Context, page, size int, userID
|
||||
}
|
||||
return out, total, nil
|
||||
}
|
||||
func (r *apiTokenRepo) DisableAPIToken(ctx context.Context, id uint) (string, error) {
|
||||
var po apiTokenPO
|
||||
if err := r.data.DB().WithContext(ctx).First(&po, id).Error; err != nil {
|
||||
return "", err
|
||||
}
|
||||
return po.Token, r.data.DB().WithContext(ctx).Model(&po).Update("status", false).Error
|
||||
}
|
||||
func (r *apiTokenRepo) DisableAndBlacklistAPIToken(ctx context.Context, id uint) error {
|
||||
// Persist the blacklist row first, then mark the
|
||||
// API-token record disabled. The two writes are intentionally separate in
|
||||
|
||||
@@ -191,12 +191,103 @@ func (r *exportRepo) QueryExport(ctx context.Context, t *system.ExportTemplate,
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
var rows []map[string]any
|
||||
preview := query.ToSQL(func(tx *gorm.DB) *gorm.DB { return tx.Find(&[]map[string]any{}) })
|
||||
err = query.Find(&rows).Error
|
||||
rows, err := queryExportRows(query)
|
||||
return rows, preview, err
|
||||
}
|
||||
|
||||
func queryExportRows(query *gorm.DB) ([]map[string]any, error) {
|
||||
rows, err := query.Rows()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
columns, err := rows.Columns()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
columnTypes, err := rows.ColumnTypes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make([]map[string]any, 0)
|
||||
for rows.Next() {
|
||||
values := make([]any, len(columns))
|
||||
destinations := make([]any, len(values))
|
||||
for index := range values {
|
||||
destinations[index] = &values[index]
|
||||
}
|
||||
if err := rows.Scan(destinations...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
item := make(map[string]any, len(columns))
|
||||
for index, column := range columns {
|
||||
typeName := ""
|
||||
if index < len(columnTypes) {
|
||||
typeName = columnTypes[index].DatabaseTypeName()
|
||||
}
|
||||
item[column] = exportQueryValue(values[index], typeName)
|
||||
}
|
||||
result = append(result, item)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func exportQueryValue(value any, databaseType string) any {
|
||||
bytesValue, ok := value.([]byte)
|
||||
if !ok {
|
||||
return value
|
||||
}
|
||||
raw := string(bytesValue)
|
||||
typeName := strings.ToUpper(databaseType)
|
||||
if strings.Contains(typeName, "INT") || strings.Contains(typeName, "SERIAL") {
|
||||
if integer, err := strconv.ParseInt(raw, 10, 64); err == nil {
|
||||
return integer
|
||||
}
|
||||
}
|
||||
if strings.Contains(typeName, "DECIMAL") || strings.Contains(typeName, "NUMERIC") || strings.Contains(typeName, "NUMBER") || strings.Contains(typeName, "REAL") || strings.Contains(typeName, "DOUBLE") || strings.Contains(typeName, "FLOAT") {
|
||||
if decimal, ok := exactExportFloat(raw); ok {
|
||||
return decimal
|
||||
}
|
||||
}
|
||||
return raw
|
||||
}
|
||||
|
||||
func exactExportFloat(value string) (float64, bool) {
|
||||
decimal, err := strconv.ParseFloat(value, 64)
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
return decimal, normalizeDecimal(value) == normalizeDecimal(strconv.FormatFloat(decimal, 'f', -1, 64))
|
||||
}
|
||||
|
||||
func normalizeDecimal(value string) string {
|
||||
value = strings.TrimSpace(value)
|
||||
negative := strings.HasPrefix(value, "-")
|
||||
value = strings.TrimPrefix(value, "+")
|
||||
value = strings.TrimPrefix(value, "-")
|
||||
parts := strings.SplitN(value, ".", 2)
|
||||
whole := strings.TrimLeft(parts[0], "0")
|
||||
if whole == "" {
|
||||
whole = "0"
|
||||
}
|
||||
fraction := ""
|
||||
if len(parts) == 2 {
|
||||
fraction = strings.TrimRight(parts[1], "0")
|
||||
}
|
||||
result := whole
|
||||
if fraction != "" {
|
||||
result += "." + fraction
|
||||
}
|
||||
if negative && result != "0" {
|
||||
result = "-" + result
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func exportColumns(raw string) ([]string, error) {
|
||||
columns := make([]string, 0)
|
||||
decoder := json.NewDecoder(strings.NewReader(raw))
|
||||
@@ -338,17 +429,6 @@ func (r *exportRepo) PreviewExport(ctx context.Context, t *system.ExportTemplate
|
||||
}
|
||||
return query.ToSQL(func(tx *gorm.DB) *gorm.DB { return tx.Find(&[]map[string]any{}) }), nil
|
||||
}
|
||||
func parseTemplateColumns(raw string) map[string]string {
|
||||
out := map[string]string{}
|
||||
parts := strings.Split(strings.Trim(strings.TrimSpace(raw), "{}"), ",")
|
||||
for _, part := range parts {
|
||||
pair := strings.SplitN(part, ":", 2)
|
||||
if len(pair) == 2 {
|
||||
out[strings.Trim(strings.TrimSpace(pair[0]), `"`)] = strings.Trim(strings.TrimSpace(pair[1]), `"`)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
func (r *exportRepo) ImportExportRows(ctx context.Context, t *system.ExportTemplate, rows []map[string]any) error {
|
||||
if err := system.ValidateExportTemplate(t); err != nil {
|
||||
return err
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package system
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestExportQueryValueUsesDatabaseType(t *testing.T) {
|
||||
if value := exportQueryValue([]byte("42"), "VARCHAR"); value != "42" {
|
||||
t.Fatalf("text value = %#v, want string 42", value)
|
||||
}
|
||||
if value, ok := exportQueryValue([]byte("42"), "BIGINT").(int64); !ok || value != 42 {
|
||||
t.Fatalf("integer value = %#v", exportQueryValue([]byte("42"), "BIGINT"))
|
||||
}
|
||||
if value, ok := exportQueryValue([]byte("12.5"), "DECIMAL").(float64); !ok || value != 12.5 {
|
||||
t.Fatalf("decimal value = %#v", exportQueryValue([]byte("12.5"), "DECIMAL"))
|
||||
}
|
||||
if value, ok := exportQueryValue([]byte("1234567890123456.78"), "DECIMAL").(string); !ok || value != "1234567890123456.78" {
|
||||
t.Fatalf("high-precision decimal value = %#v", exportQueryValue([]byte("1234567890123456.78"), "DECIMAL"))
|
||||
}
|
||||
}
|
||||
@@ -40,9 +40,18 @@ func (r *auditQueryRepo) ListLogins(ctx context.Context, page, size int, q *syst
|
||||
if q.Username != "" {
|
||||
db = db.Where("username LIKE ?", "%"+q.Username+"%")
|
||||
}
|
||||
if q.IP != "" {
|
||||
db = db.Where("ip LIKE ?", "%"+q.IP+"%")
|
||||
}
|
||||
if q.Status != nil {
|
||||
db = db.Where("status = ?", *q.Status)
|
||||
}
|
||||
if q.StartCreatedAt != nil {
|
||||
db = db.Where("created_at >= ?", *q.StartCreatedAt)
|
||||
}
|
||||
if q.EndCreatedAt != nil {
|
||||
db = db.Where("created_at <= ?", *q.EndCreatedAt)
|
||||
}
|
||||
}
|
||||
var total int64
|
||||
if err := db.Count(&total).Error; err != nil {
|
||||
|
||||
@@ -64,13 +64,6 @@ func (r *mediaRepo) FindMedia(ctx context.Context, id uint) (*system.MediaFile,
|
||||
}
|
||||
return mediaFromPO(po), nil
|
||||
}
|
||||
func (r *mediaRepo) FindMediaByHash(ctx context.Context, userID uint, hash string) (*system.MediaFile, error) {
|
||||
var po mediaPO
|
||||
if err := r.data.DB().WithContext(ctx).Where("user_id = ? AND md5 = ?", userID, hash).First(&po).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return mediaFromPO(po), nil
|
||||
}
|
||||
func (r *mediaRepo) ListMedia(ctx context.Context, filter system.MediaFilter) ([]*system.MediaFile, int64, error) {
|
||||
db := r.data.DB().WithContext(ctx).Model(&mediaPO{})
|
||||
if filter.Keyword != "" {
|
||||
|
||||
@@ -281,12 +281,6 @@ func (r *menuRepo) SetAuthorityMenus(ctx context.Context, id uint, ids []uint) e
|
||||
})
|
||||
}
|
||||
|
||||
func (r *menuRepo) AuthorityMenuIDs(ctx context.Context, id uint) ([]uint, error) {
|
||||
var ids []uint
|
||||
err := r.data.DB().WithContext(ctx).Model(&authorityMenuPO{}).Where("sys_authority_authority_id = ?", id).Pluck("sys_base_menu_id", &ids).Error
|
||||
return ids, err
|
||||
}
|
||||
|
||||
// ListAuthorityMenus reads the authority-menu query. It reads the
|
||||
// target authority's relation rows first and then fetches only those base
|
||||
// menus. In particular, it deliberately does not call List: List applies the
|
||||
|
||||
@@ -33,40 +33,6 @@ type authorityButtonPO struct {
|
||||
|
||||
func (authorityButtonPO) TableName() string { return "sys_authority_btns" }
|
||||
|
||||
func (r *permissionRepo) Buttons(ctx context.Context, menuID uint) ([]*system.MenuButton, error) {
|
||||
var pos []menuButtonPO
|
||||
if err := r.data.DB().WithContext(ctx).Where("sys_base_menu_id = ?", menuID).Find(&pos).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]*system.MenuButton, 0, len(pos))
|
||||
for _, po := range pos {
|
||||
out = append(out, menuButtonFromPO(po))
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
func (r *permissionRepo) SetAuthorityButtons(ctx context.Context, aid uint, buttons map[uint][]uint) error {
|
||||
if err := (&authorityAccessRepo{data: r.data}).checkAuthorityIDAuth(ctx, aid); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := r.checkButtonAssignmentAuth(ctx, buttons); err != nil {
|
||||
return err
|
||||
}
|
||||
return r.data.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
if err := tx.Where("authority_id = ?", aid).Delete(&authorityButtonPO{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
var links []authorityButtonPO
|
||||
for menuID, ids := range buttons {
|
||||
for _, id := range ids {
|
||||
links = append(links, authorityButtonPO{AuthorityID: aid, MenuID: menuID, ButtonID: id})
|
||||
}
|
||||
}
|
||||
if len(links) > 0 {
|
||||
return tx.Create(&links).Error
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
func (r *permissionRepo) SelectedButtons(ctx context.Context, aid, menuID uint) ([]uint, error) {
|
||||
var rows []authorityButtonPO
|
||||
if err := r.data.DB().WithContext(ctx).Where("authority_id = ? AND sys_menu_id = ?", aid, menuID).Find(&rows).Error; err != nil {
|
||||
@@ -110,12 +76,6 @@ func (r *permissionRepo) CanRemoveButton(ctx context.Context, id string) (bool,
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
func (r *permissionRepo) AuthorityButtonIDs(ctx context.Context, aid uint) ([]uint, error) {
|
||||
var ids []uint
|
||||
err := r.data.DB().WithContext(ctx).Model(&authorityButtonPO{}).Where("authority_id = ?", aid).Pluck("sys_base_menu_btn_id", &ids).Error
|
||||
return ids, err
|
||||
}
|
||||
|
||||
func (r *permissionRepo) checkButtonAssignmentAuth(ctx context.Context, buttons map[uint][]uint) error {
|
||||
actor, allowedAuthorities, strict, err := (&authorityAccessRepo{data: r.data}).strictAuthorityAccess(ctx)
|
||||
if err != nil || !strict {
|
||||
|
||||
@@ -24,7 +24,7 @@ func newTransactionTestData(t *testing.T) *Data {
|
||||
return data
|
||||
}
|
||||
|
||||
func TestUserAuthorityWritesAreAtomic(t *testing.T) {
|
||||
func TestUserAuthorityWrites(t *testing.T) {
|
||||
data := newTransactionTestData(t)
|
||||
ctx := context.Background()
|
||||
if err := data.gormDB.WithContext(ctx).Create(&[]authorityPO{{AuthorityID: 888, AuthorityName: "admin"}, {AuthorityID: 999, AuthorityName: "operator"}}).Error; err != nil {
|
||||
@@ -44,7 +44,10 @@ func TestUserAuthorityWritesAreAtomic(t *testing.T) {
|
||||
// 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 {
|
||||
if err = repo.UpdateUser(ctx, created); err != nil {
|
||||
t.Fatalf("user update failed: %v", err)
|
||||
}
|
||||
if err = repo.SetUserAuthorities(ctx, created.ID, []uint{888, 123456}); err != nil {
|
||||
t.Fatalf("missing authority ID was rejected in non-strict mode: %v", err)
|
||||
}
|
||||
var persisted userPO
|
||||
|
||||
@@ -329,10 +329,6 @@ func (r *userRepo) ListUsers(ctx context.Context, page, pageSize int, filter *sy
|
||||
return users, total, err
|
||||
}
|
||||
|
||||
func (r *userRepo) CreateUser(ctx context.Context, user *system.User) (*system.User, error) {
|
||||
return r.CreateUserWithAuthorities(ctx, user, nil)
|
||||
}
|
||||
|
||||
func (r *userRepo) CreateUserWithAuthorities(ctx context.Context, user *system.User, authorityIDs []uint) (*system.User, error) {
|
||||
requestedAuthorities := append([]uint{user.AuthorityID}, authorityIDs...)
|
||||
if err := (&authorityAccessRepo{data: r.data}).checkAuthorityIDsAuth(ctx, requestedAuthorities); err != nil {
|
||||
@@ -489,24 +485,6 @@ func (r *userRepo) updateUser(tx *gorm.DB, user *system.User) error {
|
||||
return tx.Model(&userPO{}).Where("id = ?", user.ID).Updates(updates).Error
|
||||
}
|
||||
|
||||
func (r *userRepo) UpdateUserWithAuthorities(ctx context.Context, user *system.User, authorityIDs []uint) error {
|
||||
access := &authorityAccessRepo{data: r.data}
|
||||
if err := r.checkUserUpdateAuth(ctx, user); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := access.checkAuthorityIDsAuth(ctx, authorityIDs); err != nil {
|
||||
return err
|
||||
}
|
||||
return r.data.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
if err := r.updateUser(tx, user); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(authorityIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
return setUserAuthorities(tx, user.ID, authorityIDs)
|
||||
})
|
||||
}
|
||||
func (r *userRepo) DeleteUser(ctx context.Context, id uint) error {
|
||||
if err := (&authorityAccessRepo{data: r.data}).checkUserIDAuth(ctx, id, false); err != nil {
|
||||
return err
|
||||
@@ -561,7 +539,7 @@ func (r *userRepo) SetUserAuthorities(ctx context.Context, id uint, authorityIDs
|
||||
|
||||
func setUserAuthorities(tx *gorm.DB, id uint, authorityIDs []uint) error {
|
||||
if len(authorityIDs) == 0 {
|
||||
return errors.New("角色不能为空")
|
||||
return system.ErrAuthoritiesRequired
|
||||
}
|
||||
if err := tx.Where("sys_user_id = ?", id).Delete(&userAuthorityPO{}).Error; err != nil {
|
||||
return err
|
||||
|
||||
@@ -80,10 +80,6 @@ func TestStrictUserUpdateRejectsPrimaryAuthorityOutsideManagedTree(t *testing.T)
|
||||
if err := repo.UpdateUser(ctx, &system.User{ID: users[1].ID, NickName: "changed", AuthorityID: siblingID}); err == nil {
|
||||
t.Fatal("UpdateUser() accepted a primary authority outside the managed tree")
|
||||
}
|
||||
if err := repo.UpdateUserWithAuthorities(ctx, &system.User{ID: users[1].ID, NickName: "changed", AuthorityID: siblingID}, []uint{childID}); err == nil {
|
||||
t.Fatal("UpdateUserWithAuthorities() accepted a primary authority outside the managed tree")
|
||||
}
|
||||
|
||||
var stored userPO
|
||||
if err := data.gormDB.WithContext(context.Background()).First(&stored, users[1].ID).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -179,24 +179,32 @@ func (r *versionRepo) BuildVersionBundle(ctx context.Context, menuIDs, apiIDs, d
|
||||
}
|
||||
return bundle, nil
|
||||
}
|
||||
func (r *versionRepo) ImportVersionBundle(ctx context.Context, b *system.VersionBundle) error {
|
||||
db := r.data.DB().WithContext(ctx)
|
||||
if len(b.Menus) > 0 {
|
||||
if err := db.Transaction(func(tx *gorm.DB) error { return importVersionMenus(tx, b.Menus, 0) }); err != nil {
|
||||
return &system.VersionStageError{Stage: system.VersionStageMenus, Err: err}
|
||||
func (r *versionRepo) ImportVersionBundle(ctx context.Context, b *system.VersionBundle, audit *system.Version) error {
|
||||
return r.data.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
if len(b.Menus) > 0 {
|
||||
if err := importVersionMenus(tx, b.Menus, 0); err != nil {
|
||||
return &system.VersionStageError{Stage: system.VersionStageMenus, Err: err}
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(b.APIs) > 0 {
|
||||
if err := db.Transaction(func(tx *gorm.DB) error { return importVersionAPIs(tx, b.APIs) }); err != nil {
|
||||
return &system.VersionStageError{Stage: system.VersionStageAPIs, Err: err}
|
||||
if len(b.APIs) > 0 {
|
||||
if err := importVersionAPIs(tx, b.APIs); err != nil {
|
||||
return &system.VersionStageError{Stage: system.VersionStageAPIs, Err: err}
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(b.Dictionaries) > 0 {
|
||||
if err := db.Transaction(func(tx *gorm.DB) error { return importVersionDictionaries(tx, b.Dictionaries) }); err != nil {
|
||||
return &system.VersionStageError{Stage: system.VersionStageDictionaries, Err: err}
|
||||
if len(b.Dictionaries) > 0 {
|
||||
if err := importVersionDictionaries(tx, b.Dictionaries); err != nil {
|
||||
return &system.VersionStageError{Stage: system.VersionStageDictionaries, Err: err}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
if audit != nil {
|
||||
po := versionPO{Name: audit.Name, Code: audit.Code, Description: audit.Description, Data: audit.Data}
|
||||
if err := tx.Create(&po).Error; err != nil {
|
||||
return &system.VersionStageError{Stage: system.VersionStageSave, Err: err}
|
||||
}
|
||||
audit.ID = po.ID
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func importVersionMenus(tx *gorm.DB, items []*system.Menu, parentID uint) error {
|
||||
|
||||
Reference in New Issue
Block a user