优化
This commit is contained in:
@@ -36,9 +36,20 @@ func (uc *AuthorityUsecase) AuthorityTree(ctx context.Context) ([]*Authority, er
|
||||
if item.ParentID != nil && *item.ParentID != 0 && byID[*item.ParentID] != nil {
|
||||
parent := byID[*item.ParentID]
|
||||
parent.Children = append(parent.Children, item)
|
||||
} else {
|
||||
} else if item.ParentID == nil || *item.ParentID == 0 {
|
||||
roots = append(roots, item)
|
||||
}
|
||||
}
|
||||
// In GVA strict-authority mode a non-root actor receives its direct
|
||||
// children as the top-level result even though their parent is omitted.
|
||||
if len(roots) == 0 {
|
||||
if actor, ok := ActorFromContext(ctx); ok {
|
||||
for _, item := range items {
|
||||
if item.ParentID != nil && *item.ParentID == actor.AuthorityID {
|
||||
roots = append(roots, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return roots, nil
|
||||
}
|
||||
|
||||
@@ -17,10 +17,12 @@ type Cache interface {
|
||||
}
|
||||
|
||||
type StoredFile struct {
|
||||
Name string
|
||||
Path string
|
||||
URL string
|
||||
Size int64
|
||||
Name string
|
||||
Path string
|
||||
URL string
|
||||
Size int64
|
||||
LastModified time.Time
|
||||
ContentType string
|
||||
}
|
||||
|
||||
// FileStorage owns the persistence boundary for uploaded files.
|
||||
@@ -50,6 +52,7 @@ type CaptchaSettings struct {
|
||||
type MediaSettings struct {
|
||||
SessionTTL int
|
||||
MaxFileSize int64
|
||||
ChunkDir string
|
||||
}
|
||||
|
||||
// RuntimeSettings exposes only the active values needed by the application.
|
||||
@@ -75,6 +78,7 @@ type AuthClaims struct {
|
||||
Username string
|
||||
NickName string
|
||||
AuthorityID uint
|
||||
UserType string
|
||||
BufferTime time.Duration
|
||||
MustChangePwd bool
|
||||
Issuer string
|
||||
|
||||
@@ -22,11 +22,12 @@ type MediaRepo interface {
|
||||
|
||||
type MediaUsecase struct {
|
||||
MediaRepo
|
||||
files FileStorage
|
||||
files FileStorage
|
||||
settings RuntimeSettings
|
||||
}
|
||||
|
||||
func NewMediaUsecase(repo MediaRepo, files FileStorage) *MediaUsecase {
|
||||
return &MediaUsecase{MediaRepo: repo, files: files}
|
||||
func NewMediaUsecase(repo MediaRepo, files FileStorage, settings RuntimeSettings) *MediaUsecase {
|
||||
return &MediaUsecase{MediaRepo: repo, files: files, settings: settings}
|
||||
}
|
||||
|
||||
var allowedMediaExtensions = map[string]bool{
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
@@ -15,6 +16,20 @@ import (
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func (uc *MediaUsecase) chunkPrefix(uploadID uint) string {
|
||||
directory := ".chunks"
|
||||
if uc.settings != nil {
|
||||
if configured := strings.Trim(uc.settings.MediaSettings().ChunkDir, "/\\ "); configured != "" {
|
||||
directory = configured
|
||||
}
|
||||
}
|
||||
return path.Join(directory, fmt.Sprintf("%d", uploadID))
|
||||
}
|
||||
|
||||
func (uc *MediaUsecase) chunkKey(uploadID uint, index int) string {
|
||||
return path.Join(uc.chunkPrefix(uploadID), fmt.Sprintf("%08d", index))
|
||||
}
|
||||
|
||||
func (uc *MediaUsecase) InitUpload(ctx context.Context, userID uint, name, hash string, size, chunkSize int64, total int) (*UploadSession, *MediaFile, []int, error) {
|
||||
if err := validateMediaName(name); err != nil {
|
||||
return nil, nil, nil, err
|
||||
@@ -57,7 +72,7 @@ func (uc *MediaUsecase) SaveChunk(ctx context.Context, userID, uploadID uint, in
|
||||
return errors.New("上传会话状态不允许收片")
|
||||
}
|
||||
hash := md5.New()
|
||||
key := fmt.Sprintf(".chunks/%d/%08d", uploadID, index)
|
||||
key := uc.chunkKey(uploadID, index)
|
||||
stored, err := uc.files.Put(ctx, key, io.TeeReader(reader, hash))
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -95,7 +110,7 @@ func (uc *MediaUsecase) CompleteUpload(ctx context.Context, userID, uploadID uin
|
||||
if chunk.Index != index {
|
||||
return fail(errors.New("分片序号不连续"))
|
||||
}
|
||||
names = append(names, fmt.Sprintf(".chunks/%d/%08d", uploadID, index))
|
||||
names = append(names, uc.chunkKey(uploadID, index))
|
||||
}
|
||||
ext := strings.ToLower(filepath.Ext(session.FileName))
|
||||
key := time.Now().Format("20060102") + "/" + uuid.NewString() + ext
|
||||
@@ -114,7 +129,7 @@ func (uc *MediaUsecase) CompleteUpload(ctx context.Context, userID, uploadID uin
|
||||
}
|
||||
_ = uc.CompleteUploadSession(ctx, uploadID, key, media.ID)
|
||||
_ = uc.DeleteChunks(ctx, uploadID)
|
||||
_ = uc.files.DeletePrefix(ctx, fmt.Sprintf(".chunks/%d", uploadID))
|
||||
_ = uc.files.DeletePrefix(ctx, uc.chunkPrefix(uploadID))
|
||||
return media, nil
|
||||
}
|
||||
func (uc *MediaUsecase) CancelUpload(ctx context.Context, userID, uploadID uint) error {
|
||||
@@ -123,7 +138,7 @@ func (uc *MediaUsecase) CancelUpload(ctx context.Context, userID, uploadID uint)
|
||||
return errors.New("上传会话不存在或无权操作")
|
||||
}
|
||||
_ = uc.DeleteChunks(ctx, uploadID)
|
||||
_ = uc.files.DeletePrefix(ctx, fmt.Sprintf(".chunks/%d", uploadID))
|
||||
_ = uc.files.DeletePrefix(ctx, uc.chunkPrefix(uploadID))
|
||||
return uc.DeleteUploadSession(ctx, uploadID)
|
||||
}
|
||||
func (uc *MediaUsecase) CleanupStale(ctx context.Context, ttlHours int) error {
|
||||
@@ -138,7 +153,7 @@ func (uc *MediaUsecase) CleanupStale(ctx context.Context, ttlHours int) error {
|
||||
if err = uc.DeleteUploadData(ctx, id); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = uc.files.DeletePrefix(ctx, fmt.Sprintf(".chunks/%d", id)); err != nil {
|
||||
if err = uc.files.DeletePrefix(ctx, uc.chunkPrefix(id)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,8 +91,8 @@ type TaskRuntime interface {
|
||||
TriggerID(context.Context, uint) error
|
||||
NextRuns() map[uint]time.Time
|
||||
Reload(context.Context) error
|
||||
Subscribe() chan []byte
|
||||
Unsubscribe(chan []byte)
|
||||
Subscribe(uint) chan []byte
|
||||
Unsubscribe(uint, chan []byte)
|
||||
}
|
||||
|
||||
type TaskUsecase struct{ TaskRepo }
|
||||
@@ -115,7 +115,7 @@ func (uc *TaskUsecase) Validate(value *TimedTask) error {
|
||||
switch value.ExecutorType {
|
||||
case TaskExecutorMethod:
|
||||
if !registeredTaskMethod(value.MethodName) {
|
||||
return errors.New("方法未注册")
|
||||
return fmt.Errorf("方法 %s 未注册", value.MethodName)
|
||||
}
|
||||
if len(value.Params) > 0 && !json.Valid(value.Params) {
|
||||
return errors.New("params 必须是合法 JSON")
|
||||
@@ -128,7 +128,7 @@ func (uc *TaskUsecase) Validate(value *TimedTask) error {
|
||||
if len(value.HTTPHeader) > 0 {
|
||||
headers := map[string]string{}
|
||||
if json.Unmarshal(value.HTTPHeader, &headers) != nil {
|
||||
return errors.New("httpHeader 必须是 JSON 对象")
|
||||
return errors.New(`httpHeader 必须是 {"Key":"Value"} 形式的 JSON 对象`)
|
||||
}
|
||||
}
|
||||
default:
|
||||
@@ -229,7 +229,9 @@ func (uc *TaskApplicationUsecase) Reload(ctx context.Context) error {
|
||||
return uc.runtime.Reload(ctx)
|
||||
}
|
||||
|
||||
func (uc *TaskApplicationUsecase) Subscribe() chan []byte { return uc.runtime.Subscribe() }
|
||||
func (uc *TaskApplicationUsecase) Unsubscribe(events chan []byte) {
|
||||
uc.runtime.Unsubscribe(events)
|
||||
func (uc *TaskApplicationUsecase) Subscribe(userID uint) chan []byte {
|
||||
return uc.runtime.Subscribe(userID)
|
||||
}
|
||||
func (uc *TaskApplicationUsecase) Unsubscribe(userID uint, events chan []byte) {
|
||||
uc.runtime.Unsubscribe(userID, events)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user