1
0
mirror of https://github.com/fumiama/paper-manager.git synced 2026-06-05 07:50:23 +08:00

finish getFileList getFilePercent and part of analyzeFile

This commit is contained in:
源文雨
2023-04-08 00:30:04 +08:00
parent 773db2e581
commit 2fcb3fd636
15 changed files with 302 additions and 90 deletions

View File

@@ -85,8 +85,8 @@ func init() {
// File stores to paper/Class/2022-2023学年/第一学期/期末/A/xxx.docx // File stores to paper/Class/2022-2023学年/第一学期/期末/A/xxx.docx
type File struct { type File struct {
ID uint64 // ID is the first 8 bytes of the original file's md5 ID int64 // ID is the first 8 bytes of the original file's md5
ListID int // ListID is the foreign key to List(ID) ListID int // ListID is the foreign key to List(ID)
Year StudyYear Year StudyYear
Type PaperType Type PaperType
Date uint32 // Date is the yyyymmdd of 考试日期 Date uint32 // Date is the yyyymmdd of 考试日期
@@ -115,6 +115,7 @@ func (f *FileDatabase) AddFile(lstid int, reg *Regex, istemp bool, progress func
if !user.IsFileManager() && !istemp { if !user.IsFileManager() && !istemp {
return nil, ErrInvalidRole return nil, ErrInvalidRole
} }
progress(1)
lst, err := sql.Find[List](&FileDB.db, FileTableList, "WHERE ID="+strconv.Itoa(lstid)) lst, err := sql.Find[List](&FileDB.db, FileTableList, "WHERE ID="+strconv.Itoa(lstid))
if err != nil { if err != nil {
return nil, err return nil, err
@@ -128,13 +129,14 @@ func (f *FileDatabase) AddFile(lstid int, reg *Regex, istemp bool, progress func
return nil, err return nil, err
} }
defer docf.Close() defer docf.Close()
progress(2)
h := md5.New() h := md5.New()
_, err = io.Copy(h, docf) _, err = io.Copy(h, docf)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var buf [md5.Size]byte var buf [md5.Size]byte
id := binary.LittleEndian.Uint64(h.Sum(buf[:0])) id := int64(binary.LittleEndian.Uint64(h.Sum(buf[:0])))
_, err = docf.Seek(0, io.SeekStart) _, err = docf.Seek(0, io.SeekStart)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -144,10 +146,12 @@ func (f *FileDatabase) AddFile(lstid int, reg *Regex, istemp bool, progress func
return nil, err return nil, err
} }
sz := stat.Size() sz := stat.Size()
progress(3)
doc, err := docx.Parse(docf, sz) doc, err := docx.Parse(docf, sz)
if err != nil { if err != nil {
return nil, err return nil, err
} }
progress(5)
doc.Document.Body.DropDrawingOf("NilPicture") doc.Document.Body.DropDrawingOf("NilPicture")
majorre, err := regexp.Compile(reg.Major) majorre, err := regexp.Compile(reg.Major)
if err != nil { if err != nil {
@@ -157,6 +161,7 @@ func (f *FileDatabase) AddFile(lstid int, reg *Regex, istemp bool, progress func
if len(docs) < 2 { if len(docs) < 2 {
return nil, ErrMajorSplitsTooShort return nil, ErrMajorSplitsTooShort
} }
progress(9)
// filling File struct // filling File struct
file := &File{ file := &File{
ID: id, ID: id,
@@ -186,6 +191,7 @@ func (f *FileDatabase) AddFile(lstid int, reg *Regex, istemp bool, progress func
if err != nil { if err != nil {
return nil, err return nil, err
} }
progress(10)
for _, it := range docs[0].Document.Body.Items { for _, it := range docs[0].Document.Body.Items {
if p, ok := it.(*docx.Paragraph); ok { if p, ok := it.(*docx.Paragraph); ok {
text := p.String() text := p.String()
@@ -206,8 +212,8 @@ func (f *FileDatabase) AddFile(lstid int, reg *Regex, istemp bool, progress func
} }
} }
class := classre.FindStringSubmatch(text) class := classre.FindStringSubmatch(text)
if len(class) >= 2 { if len(class) >= 3 {
file.Class = class[1] file.Class = class[2]
} }
opcl := opclre.FindStringSubmatch(text) opcl := opclre.FindStringSubmatch(text)
if len(opcl) >= 2 { if len(opcl) >= 2 {
@@ -216,7 +222,10 @@ func (f *FileDatabase) AddFile(lstid int, reg *Regex, istemp bool, progress func
date := datere.FindStringSubmatch(text) date := datere.FindStringSubmatch(text)
if len(date) >= 4 { if len(date) >= 4 {
y, m, d := date[1], date[2], date[3] y, m, d := date[1], date[2], date[3]
if y != "" && m != "" && d != "" { if y != "" && m != "" {
if d == "" {
d = "1"
}
yyyy, err := strconv.ParseUint(y, 10, 64) yyyy, err := strconv.ParseUint(y, 10, 64)
if err == nil && yyyy > 1600 { if err == nil && yyyy > 1600 {
mm, err := strconv.ParseUint(m, 10, 64) mm, err := strconv.ParseUint(m, 10, 64)
@@ -237,11 +246,12 @@ func (f *FileDatabase) AddFile(lstid int, reg *Regex, istemp bool, progress func
} }
} }
rate := ratere.FindStringSubmatch(text) rate := ratere.FindStringSubmatch(text)
if len(rate) >= 2 { if len(rate) >= 3 {
file.Rate = rate[1] file.Rate = rate[2]
} }
} }
} }
progress(19)
if file.Class == "" || strings.Contains(file.Class, "..") { if file.Class == "" || strings.Contains(file.Class, "..") {
return nil, ErrEmptyClass return nil, ErrEmptyClass
} }
@@ -250,12 +260,12 @@ func (f *FileDatabase) AddFile(lstid int, reg *Regex, istemp bool, progress func
filebasepath = PaperFolder + "temp/" + strconv.Itoa(*user.ID) + "/" filebasepath = PaperFolder + "temp/" + strconv.Itoa(*user.ID) + "/"
} else { } else {
filebasepath = fmt.Sprintf( filebasepath = fmt.Sprintf(
PaperFolder+file.Class+"/%v/%v/%v/%v/", PaperFolder+file.Class+"/%v/%v/%v/%c/",
file.Year, file.Type.FirstSecond(), file.Type.MiddleFinal(), file.Type.AB(), file.Year, file.Type.FirstSecond(), file.Type.MiddleFinal(), file.Type.AB(),
) )
} }
lst.Path = filebasepath questionpath := filebasepath + "questions/"
err = os.MkdirAll(filebasepath, 0755) err = os.MkdirAll(questionpath, 0755)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -266,7 +276,19 @@ func (f *FileDatabase) AddFile(lstid int, reg *Regex, istemp bool, progress func
return nil, err return nil, err
} }
filequestions := make([]QuestionJSON, 0, len(docs)) filequestions := make([]QuestionJSON, 0, len(docs))
lst.QuesC = 0
progress(20)
p := uint(20)
delta := uint(70 / len(docs))
if delta == 0 {
delta = 1
}
for _, majordoc := range docs { for _, majordoc := range docs {
p += delta
if p > 90 {
p = 90
}
progress(p)
majorq := QuestionJSON{} majorq := QuestionJSON{}
for _, it := range majordoc.Document.Body.Items { for _, it := range majordoc.Document.Body.Items {
if p, ok := it.(*docx.Paragraph); ok { if p, ok := it.(*docx.Paragraph); ok {
@@ -280,6 +302,10 @@ func (f *FileDatabase) AddFile(lstid int, reg *Regex, istemp bool, progress func
} }
} }
subdocs := majordoc.SplitByParagraph(docx.SplitDocxByPlainTextRegex(subre)) subdocs := majordoc.SplitByParagraph(docx.SplitDocxByPlainTextRegex(subre))
if len(subdocs) < 2 {
continue
}
subdocs = subdocs[1:]
majorq.Sub = make([]QuestionJSON, 0, len(subdocs)) majorq.Sub = make([]QuestionJSON, 0, len(subdocs))
for _, subdoc := range subdocs { for _, subdoc := range subdocs {
sb := bytes.NewBuffer(make([]byte, 0, 4096)) sb := bytes.NewBuffer(make([]byte, 0, 4096))
@@ -288,7 +314,7 @@ func (f *FileDatabase) AddFile(lstid int, reg *Regex, istemp bool, progress func
} }
m := md5.Sum(sb.Bytes()) m := md5.Sum(sb.Bytes())
que := &Question{ que := &Question{
ID: binary.LittleEndian.Uint64(m[:8]), ID: int64(binary.LittleEndian.Uint64(m[:8])),
Plain: base14.BytesToString(sb.Bytes()), Plain: base14.BytesToString(sb.Bytes()),
Images: func() []byte { Images: func() []byte {
m := make(map[string]string) m := make(map[string]string)
@@ -340,7 +366,9 @@ func (f *FileDatabase) AddFile(lstid int, reg *Regex, istemp bool, progress func
} }
v := make(map[string]uint8, len(words)*2) v := make(map[string]uint8, len(words)*2)
for _, word := range words { for _, word := range words {
v[word]++ if word != "" && word != "\n" && word != " " {
v[word]++
}
} }
data, err := json.Marshal(v) data, err := json.Marshal(v)
if err != nil { if err != nil {
@@ -361,7 +389,7 @@ func (f *FileDatabase) AddFile(lstid int, reg *Regex, istemp bool, progress func
return nil return nil
} }
var buf [8]byte var buf [8]byte
binary.LittleEndian.PutUint64(buf[:], q.ID) binary.LittleEndian.PutUint64(buf[:], uint64(q.ID))
dupmap[hex.EncodeToString(buf[:])] = r dupmap[hex.EncodeToString(buf[:])] = r
return nil return nil
}) })
@@ -372,11 +400,11 @@ func (f *FileDatabase) AddFile(lstid int, reg *Regex, istemp bool, progress func
w := bytes.NewBuffer(make([]byte, 0, 65536)) w := bytes.NewBuffer(make([]byte, 0, 65536))
_, err = subdoc.WriteTo(w) _, err = subdoc.WriteTo(w)
var buf [8]byte var buf [8]byte
binary.LittleEndian.PutUint64(buf[:], que.ID) binary.LittleEndian.PutUint64(buf[:], uint64(que.ID))
queidstr := hex.EncodeToString(buf[:]) queidstr := hex.EncodeToString(buf[:])
if err == nil { if err == nil {
m5 := md5.Sum(w.Bytes()) m5 := md5.Sum(w.Bytes())
quepath := filebasepath + hex.EncodeToString(m5[:]) + ".docx" quepath := questionpath + hex.EncodeToString(m5[:]) + ".docx"
f, err := os.Create(quepath) f, err := os.Create(quepath)
if err == nil { if err == nil {
_, _ = io.Copy(f, w) _, _ = io.Copy(f, w)
@@ -420,9 +448,29 @@ func (f *FileDatabase) AddFile(lstid int, reg *Regex, istemp bool, progress func
}) })
} }
filequestions = append(filequestions, majorq) filequestions = append(filequestions, majorq)
lst.QuesC += len(majorq.Sub)
} }
progress(90)
file.Questions, _ = json.Marshal(filequestions) file.Questions, _ = json.Marshal(filequestions)
lst.Path += file.Class + ".docx" _, err = docf.Seek(0, io.SeekStart)
if err != nil {
return nil, err
}
lst.Path = filebasepath + file.Class + ".docx"
lst.HasntAnalyzed = false
lst.Desc = fmt.Sprintf("%s%v%v%v%c卷",
file.Class, file.Year, file.Type.FirstSecond(), file.Type.MiddleFinal(), file.Type.AB(),
)
dstf, err := os.Create(lst.Path)
if err != nil {
return nil, err
}
defer dstf.Close()
_, err = io.Copy(dstf, docf)
if err != nil {
return nil, err
}
progress(95)
FileDB.mu.Lock() FileDB.mu.Lock()
if istemp { if istemp {
err = FileDB.db.Insert(FileTableTempFile, file) err = FileDB.db.Insert(FileTableTempFile, file)
@@ -433,6 +481,7 @@ func (f *FileDatabase) AddFile(lstid int, reg *Regex, istemp bool, progress func
} }
_ = FileDB.db.Insert(FileTableList, &lst) _ = FileDB.db.Insert(FileTableList, &lst)
FileDB.mu.Unlock() FileDB.mu.Unlock()
progress(100)
return file, err return file, err
} }
@@ -445,7 +494,7 @@ type QuestionJSON struct {
} }
type Question struct { type Question struct {
ID uint64 // ID is the first 8 bytes of the Plain's md5 ID int64 // ID is the first 8 bytes of the Plain's md5
Path string // Path is the question's docx position Path string // Path is the question's docx position
Plain string // Plain is the plain text of the question (like markdown format) Plain string // Plain is the plain text of the question (like markdown format)
Images []byte // Images is json of the image dhash in XML, ex. ['rId1': '1234567890abcdef', ...] Images []byte // Images is json of the image dhash in XML, ex. ['rId1': '1234567890abcdef', ...]

View File

@@ -7,6 +7,8 @@ import (
"strconv" "strconv"
"strings" "strings"
"time" "time"
sql "github.com/FloatTech/sqlite"
) )
var ( var (
@@ -15,17 +17,21 @@ var (
// List of file path // List of file path
type List struct { type List struct {
ID *int // ID is self-inc ID *int // ID is self-inc
Uploader int // Uploader is uid Uploader int // Uploader is uid
UpTime int64 // UpTime is upload time (unix timestamp) UpName string // UpName is uploader's name
Size int64 // Size of the original file UpTime int64 // UpTime is upload time (unix timestamp)
IsTemp bool // IsTemp whether file is temp Size int64 // Size of the original file
Path string // Path of file QuesC int // QuesC 总小题数
HasntAnalyzed bool // HasntAnalyzed whether file has been analyzed
IsTemp bool // IsTemp whether file is temp
Path string `db:"Path,UNIQUE"` // Path of file, unique
Desc string // Desc is file's description
} }
// SaveFileToTemp copy file to PaperFolder/tmp/uploader/name and add record into list. // SaveFileToTemp copy file to PaperFolder/tmp/uploader/name and add record into list.
func (f *FileDatabase) SaveFileToTemp(uploader int, file io.Reader, name string) (err error) { func (f *FileDatabase) SaveFileToTemp(uploader int, file io.Reader, name string) (id int, err error) {
_, err = UserDB.GetUserByID(uploader) user, err := UserDB.GetUserByID(uploader)
if err != nil { if err != nil {
return return
} }
@@ -38,12 +44,16 @@ func (f *FileDatabase) SaveFileToTemp(uploader int, file io.Reader, name string)
if err != nil { if err != nil {
return return
} }
lst := List{ fpath := tmpdir + "/" + name
Uploader: uploader, FileDB.mu.RLock()
UpTime: time.Now().Unix(), lst, _ := sql.Find[List](&FileDB.db, FileTableList, "WHERE Path='"+fpath+"'")
IsTemp: true, FileDB.mu.RUnlock()
Path: tmpdir + "/" + name, lst.Uploader = uploader
} lst.UpName = user.Name
lst.UpTime = time.Now().Unix()
lst.HasntAnalyzed = true
lst.IsTemp = true
lst.Path = fpath
ff, err := os.Create(lst.Path) ff, err := os.Create(lst.Path)
if err != nil { if err != nil {
return return
@@ -58,5 +68,24 @@ func (f *FileDatabase) SaveFileToTemp(uploader int, file io.Reader, name string)
FileDB.mu.Lock() FileDB.mu.Lock()
err = FileDB.db.Insert(FileTableList, &lst) err = FileDB.db.Insert(FileTableList, &lst)
FileDB.mu.Unlock() FileDB.mu.Unlock()
if err != nil {
return
}
if lst.ID != nil {
id = *lst.ID
return
}
FileDB.mu.RLock()
err = FileDB.db.Find(FileTableList, &lst, "WHERE Path='"+fpath+"'")
FileDB.mu.RUnlock()
id = *lst.ID
return
}
// ListUploadedFile will select all file that HasntAnalyzed && IsTemp or !HasntAnalyzed && !IsTemp
func (f *FileDatabase) ListUploadedFile() (lst []*List, err error) {
FileDB.mu.RLock()
lst, err = sql.FindAll[List](&FileDB.db, FileTableList, "WHERE (HasntAnalyzed AND IsTemp) OR (NOT HasntAnalyzed AND NOT IsTemp) ORDER BY UpTime DESC")
FileDB.mu.RUnlock()
return return
} }

View File

@@ -20,12 +20,12 @@ type Regex struct {
} }
func newRegex() (reg Regex) { func newRegex() (reg Regex) {
reg.Title = `.*(\d{4})\s*-.*学年.*(\d?).*([中末]?).*([AB]?)\s*卷` reg.Title = `.*(\d{4})\s*-.*学年.*(\d).*([中末]).*([AB]?)\s*卷`
reg.Class = `考试科目:\s*(\S+)\s*` reg.Class = `(考试科目|课程名称)\s*(\S+)\s*`
reg.OpenCl = `考试形式:\s*(\S+)\s*` reg.OpenCl = `考试形式:\s*(\S+)\s*`
reg.Date = `考试日期:\s*(\d+)\s*年\s*(\d+)\s*月\s*(\d+)\s*日` reg.Date = `考试日期:\s*(\d+)\s*年\s*(\d+)\s*月\s*(\d*)\s*日`
reg.Time = `考试时长:\s*(\d+)\s*分钟` reg.Time = `考试时长:\s*(\d+)\s*分钟`
reg.Rate = `成绩构成比例:\s*(.*%)\s*` reg.Rate = `(成绩构成比例|课程成绩构成)\s*(.*%)\s*`
reg.Major = `([一二三四五六七八九十]+)、\s*(.*)\s*.*([空题]?)\s*(\d*).*共\s*(\d+)\s*分.*` reg.Major = `([一二三四五六七八九十]+)、\s*(.*)\s*.*([空题]?)\s*(\d*).*共\s*(\d+)\s*分.*`
reg.Sub = `(\d+)、` reg.Sub = `(\d+)、`
return return
@@ -77,5 +77,6 @@ func (u *UserDatabase) GetUserRegex(id int) (*Regex, error) {
u.mu.RLock() u.mu.RLock()
_ = u.db.Find(UserTableRegex, &reg, "WHERE ID="+strconv.Itoa(id)) _ = u.db.Find(UserTableRegex, &reg, "WHERE ID="+strconv.Itoa(id))
u.mu.RUnlock() u.mu.RUnlock()
reg.ID = *user.ID
return &reg, nil return &reg, nil
} }

View File

@@ -95,8 +95,8 @@ type User struct {
Role UserRole Role UserRole
Date int64 // Date is the creating date's unix timestamp Date int64 // Date is the creating date's unix timestamp
Pswd string Pswd string
Last int64 // Last is the last password reseting unix timestamp Last int64 // Last is the last password reseting unix timestamp
Name string Name string `db:"Name,UNIQUE"`
Nick string Nick string
Avtr string // Avtr is the user's avatar, typically a image url Avtr string // Avtr is the user's avatar, typically a image url
Cont string // Cont is the user's contact, ex. phone number Cont string // Cont is the user's contact, ex. phone number

View File

@@ -1,13 +1,158 @@
package backend package backend
import ( import (
"errors"
"net/http" "net/http"
"strconv"
"strings"
"time"
sql "github.com/FloatTech/sqlite"
"github.com/FloatTech/ttl"
"github.com/fumiama/paper-manager/backend/global" "github.com/fumiama/paper-manager/backend/global"
"github.com/fumiama/paper-manager/backend/utils" "github.com/fumiama/paper-manager/backend/utils"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
const (
chineseYYMMDDLayout = "2006年01月02日"
)
// analyzeper 分析进度缓存
var analyzeper = ttl.NewCache[int, uint](time.Hour)
var (
errNoAnalyzePermission = errors.New("no analyze permission")
)
type filelist struct {
ID int `json:"id"`
Title string `json:"title"`
Desc string `json:"description"`
Size float64 `json:"size"`
Ques int `json:"questions"`
Auth string `json:"author"`
Date string `json:"datetime"`
Per uint `json:"percent"`
}
func init() {
apimap["/api/getFileList"] = &apihandler{"GET", func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("Authorization")
user := usertokens.Get(token)
if user == nil {
writeresult(w, codeError, nil, errInvalidToken.Error(), typeError)
return
}
count := -1
var err error
countstr := r.URL.Query().Get("count")
if countstr != "" {
count, err = strconv.Atoi(countstr)
if err != nil {
writeresult(w, codeError, nil, err.Error(), typeError)
return
}
}
lst, err := global.FileDB.ListUploadedFile()
if err != nil && err != sql.ErrNullResult {
writeresult(w, codeError, nil, err.Error(), typeError)
return
}
if count > 0 && len(lst) > count {
lst = lst[:count]
}
result := make([]filelist, len(lst))
for i, v := range lst {
result[i].ID = *v.ID
j := strings.LastIndex(v.Path, "/")
if j <= 0 {
result[i].Title = v.Path
} else {
result[i].Title = v.Path[j+1:]
}
result[i].Desc = v.Desc
result[i].Size = float64(v.Size) / 1024 / 1024 // MB
result[i].Ques = v.QuesC
result[i].Auth = v.UpName
result[i].Date = time.Unix(v.UpTime, 0).Format(chineseYYMMDDLayout)
if !v.HasntAnalyzed {
result[i].Per = 100
} else {
result[i].Per = analyzeper.Get(*v.ID)
}
}
writeresult(w, codeSuccess, &result, messageOk, typeSuccess)
}}
apimap["/api/getFilePercent"] = &apihandler{"GET", func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("Authorization")
user := usertokens.Get(token)
if user == nil {
writeresult(w, codeError, nil, errInvalidToken.Error(), typeError)
return
}
idstr := r.URL.Query().Get("id")
if idstr == "" {
writeresult(w, codeError, nil, "empty id", typeError)
return
}
id, err := strconv.Atoi(idstr)
if err != nil {
writeresult(w, codeError, nil, err.Error(), typeError)
return
}
writeresult(w, codeSuccess, analyzeper.Get(id), messageOk, typeSuccess)
}}
apimap["/api/analyzeFile"] = &apihandler{"GET", func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("Authorization")
user := usertokens.Get(token)
if user == nil {
writeresult(w, codeError, nil, errInvalidToken.Error(), typeError)
return
}
istemp := r.URL.Query().Get("permanent") != "true"
if !user.IsFileManager() && !istemp {
writeresult(w, codeError, nil, errNoAnalyzePermission.Error(), typeError)
return
}
idstr := r.URL.Query().Get("id")
if idstr == "" {
writeresult(w, codeError, nil, "empty id", typeError)
return
}
id, err := strconv.Atoi(idstr)
if err != nil {
writeresult(w, codeError, nil, err.Error(), typeError)
return
}
reg, err := global.UserDB.GetUserRegex(*user.ID)
if err != nil {
writeresult(w, codeError, nil, err.Error(), typeError)
return
}
ch := make(chan struct{}, 1)
type message struct {
M string `json:"msg"`
}
go func() {
_, err = global.FileDB.AddFile(id, reg, istemp, func(u uint) { analyzeper.Set(id, u) })
ch <- struct{}{}
close(ch)
}()
select {
case <-time.After(time.Second):
writeresult(w, codeSuccess, &message{M: "正在分析, 请耐心等待..."}, messageOk, typeSuccess)
return
case <-ch:
if err != nil {
writeresult(w, codeError, nil, err.Error(), typeError)
return
}
writeresult(w, codeSuccess, &message{M: "分析完成"}, messageOk, typeSuccess)
}
}}
}
// PaperHandler serves protected contents in global.FileFolder // PaperHandler serves protected contents in global.FileFolder
func PaperHandler(w http.ResponseWriter, r *http.Request) { func PaperHandler(w http.ResponseWriter, r *http.Request) {
if !utils.IsMethod("GET", w, r) { if !utils.IsMethod("GET", w, r) {

View File

@@ -114,12 +114,12 @@ func UploadHandler(w http.ResponseWriter, r *http.Request) {
writeresult(w, codeError, nil, "invalid filename", typeError) writeresult(w, codeError, nil, "invalid filename", typeError)
return return
} }
err = global.FileDB.SaveFileToTemp(*user.ID, ff, fn) id, err := global.FileDB.SaveFileToTemp(*user.ID, ff, fn)
if err != nil { if err != nil {
writeresult(w, codeError, nil, err.Error(), typeError) writeresult(w, codeError, nil, err.Error(), typeError)
return return
} }
writeresult(w, codeSuccess, "上传"+fn+"成功", messageOk, typeSuccess) writeresult(w, codeSuccess, id, messageOk, typeSuccess)
return return
} }
if err != http.ErrMissingFile { if err != http.ErrMissingFile {

View File

@@ -3,9 +3,9 @@ import { resultError, resultSuccess, getRequestToken, requestParams } from '../_
const deletedIDs: number[] = [] const deletedIDs: number[] = []
const analyzingIDs: { id: number; per: number }[] = [] // const analyzingIDs: { id: number; per: number }[] = []
function createFileList() { /*function createFileList() {
const lst: any[] = [] const lst: any[] = []
for (let i = 100; i > 0; i--) { for (let i = 100; i > 0; i--) {
if (deletedIDs.includes(i)) continue if (deletedIDs.includes(i)) continue
@@ -21,11 +21,11 @@ function createFileList() {
}) })
} }
return lst return lst
} }*/
export default [ export default [
// mock get filelist // mock get filelist
{ /*{
url: '/api/getFileList', url: '/api/getFileList',
timeout: 200, timeout: 200,
method: 'get', method: 'get',
@@ -40,8 +40,8 @@ export default [
} }
return resultSuccess(fl) return resultSuccess(fl)
}, },
}, },*/
{ /*{
url: '/api/getFilePercent', url: '/api/getFilePercent',
timeout: 200, timeout: 200,
method: 'get', method: 'get',
@@ -69,7 +69,7 @@ export default [
percent: 100, percent: 100,
}) })
}, },
}, },*/
{ {
url: '/api/delFile', url: '/api/delFile',
timeout: 200, timeout: 200,
@@ -85,7 +85,7 @@ export default [
}) })
}, },
}, },
{ /*{
url: '/api/analyzeFile', url: '/api/analyzeFile',
timeout: 1000, timeout: 1000,
method: 'get', method: 'get',
@@ -99,5 +99,5 @@ export default [
msg: '正在分析' + id + ', 请耐心等待...', msg: '正在分析' + id + ', 请耐心等待...',
}) })
}, },
}, },*/
] as MockMethod[] ] as MockMethod[]

View File

@@ -35,8 +35,11 @@ export const delFile = (id: number) => {
/** /**
* @description: Analyze file * @description: Analyze file
*/ */
export const analyzeFile = (id: number) => { export const analyzeFile = (id: number, permanent: boolean) => {
return defHttp.get<AnalyzeFile>({ url: Api.AnalyzeFile, params: { id: id } }) return defHttp.get<AnalyzeFile>(
{ url: Api.AnalyzeFile, params: { id: id, permanent: permanent } },
{ errorMessageMode: 'none' },
)
} }
/** /**

View File

@@ -1,5 +1,5 @@
export interface UploadApiResult { export interface UploadApiResult {
message: string message: string
code: number code: number
url: string result: number
} }

View File

@@ -11,12 +11,6 @@
{{ fileList.length }} {{ fileList.length }}
</template> </template>
</template> </template>
<a-button @click="openPreviewModal">
<Icon icon="bi:eye" />
<template v-if="fileList.length && showPreviewNumber">
{{ fileList.length }}
</template>
</a-button>
</Tooltip> </Tooltip>
</Space> </Space>
<UploadModal <UploadModal
@@ -26,18 +20,10 @@
@change="handleChange" @change="handleChange"
@delete="handleDelete" @delete="handleDelete"
/> />
<UploadPreviewModal
:value="fileList"
@register="registerPreviewModal"
@list-change="handlePreviewChange"
@delete="handlePreviewDelete"
/>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, ref, watch, unref, computed } from 'vue' import { defineComponent, ref, watch, unref, computed } from 'vue'
import { Icon } from '/@/components/Icon'
import { Tooltip, Space } from 'ant-design-vue' import { Tooltip, Space } from 'ant-design-vue'
import { useModal } from '/@/components/Modal' import { useModal } from '/@/components/Modal'
import { uploadContainerProps } from './props' import { uploadContainerProps } from './props'
@@ -45,10 +31,9 @@
import { useI18n } from '/@/hooks/web/useI18n' import { useI18n } from '/@/hooks/web/useI18n'
import { isArray } from '/@/utils/is' import { isArray } from '/@/utils/is'
import UploadModal from './UploadModal.vue' import UploadModal from './UploadModal.vue'
import UploadPreviewModal from './UploadPreviewModal.vue'
export default defineComponent({ export default defineComponent({
name: 'BasicUpload', name: 'BasicUpload',
components: { UploadModal, Space, UploadPreviewModal, Icon, Tooltip }, components: { UploadModal, Space, Tooltip },
props: uploadContainerProps, props: uploadContainerProps,
emits: ['change', 'delete', 'preview-delete', 'update:value'], emits: ['change', 'delete', 'preview-delete', 'update:value'],
setup(props, { emit, attrs }) { setup(props, { emit, attrs }) {
@@ -80,12 +65,6 @@
emit('update:value', fileList.value) emit('update:value', fileList.value)
emit('change', fileList.value) emit('change', fileList.value)
} }
// 预览modal保存操作
function handlePreviewChange(urls: string[]) {
fileList.value = [...(urls || [])]
emit('update:value', fileList.value)
emit('change', fileList.value)
}
function handleDelete(record: Recordable) { function handleDelete(record: Recordable) {
emit('delete', record) emit('delete', record)
} }
@@ -96,7 +75,6 @@
registerUploadModal, registerUploadModal,
openUploadModal, openUploadModal,
handleChange, handleChange,
handlePreviewChange,
registerPreviewModal, registerPreviewModal,
openPreviewModal, openPreviewModal,
fileList, fileList,

View File

@@ -2,7 +2,7 @@
<BasicModal <BasicModal
width="800px" width="800px"
:title="t('component.upload.upload')" :title="t('component.upload.upload')"
:okText="t('component.upload.save')" :okText="t('component.upload.complete')"
v-bind="$attrs" v-bind="$attrs"
@register="register" @register="register"
@ok="handleOk" @ok="handleOk"
@@ -236,11 +236,11 @@
if (isUploadingRef.value) { if (isUploadingRef.value) {
return createMessage.warning(t('component.upload.saveWarn')) return createMessage.warning(t('component.upload.saveWarn'))
} }
const fileList: string[] = [] const fileList: number[] = []
for (const item of fileListRef.value) { for (const item of fileListRef.value) {
const { status, responseData } = item const { status, responseData } = item
if (status === UploadResultStatus.SUCCESS && responseData) { if (status === UploadResultStatus.SUCCESS && responseData) {
fileList.push(responseData.url) fileList.push(responseData.result)
} }
} }
// 存在一个上传成功的即可保存 // 存在一个上传成功的即可保存

View File

@@ -91,6 +91,7 @@ export default {
}, },
upload: { upload: {
save: '保存', save: '保存',
complete: '完成',
upload: '上传', upload: '上传',
imgUpload: '图片上传', imgUpload: '图片上传',
uploaded: '已上传', uploaded: '已上传',

View File

@@ -4,10 +4,10 @@
<BasicUpload <BasicUpload
name="paper" name="paper"
v-if="hasPermission([RoleEnum.SUPER, RoleEnum.FILE_MANAGER])" v-if="hasPermission([RoleEnum.SUPER, RoleEnum.FILE_MANAGER])"
:maxSize="20" :maxSize="64"
:maxNumber="10" :maxNumber="16"
@change="handleChange"
:api="uploadApi" :api="uploadApi"
@change="onChange"
:accept="['application/vnd.openxmlformats-officedocument.wordprocessingml.document']" :accept="['application/vnd.openxmlformats-officedocument.wordprocessingml.document']"
/> />
</template> </template>
@@ -19,7 +19,7 @@
</a-col> </a-col>
<a-col :span="8" :class="`${prefixCls}__top-col`"> <a-col :span="8" :class="`${prefixCls}__top-col`">
<div>占用空间</div> <div>占用空间</div>
<p> {{ cardList._totalSize }}MB </p> <p> {{ cardList._totalSize.toFixed(2) }}MB </p>
</a-col> </a-col>
<a-col :span="8" :class="`${prefixCls}__top-col`"> <a-col :span="8" :class="`${prefixCls}__top-col`">
<div>总题目数</div> <div>总题目数</div>
@@ -75,7 +75,7 @@
{{ item.description }} {{ item.description }}
</div> </div>
<div class="info"> <div class="info">
<div><span>文件大小</span>{{ item.size }}MB</div> <div><span>文件大小</span>{{ item.size.toFixed(2) }}MB</div>
<div><span>上传用户</span>{{ item.author }}</div> <div><span>上传用户</span>{{ item.author }}</div>
<div><span>上传时间</span>{{ item.datetime }}</div> <div><span>上传时间</span>{{ item.datetime }}</div>
</div> </div>
@@ -109,6 +109,7 @@
pagination, pagination,
refreshFilePercent, refreshFilePercent,
random, random,
refreshCardList,
} from './data' } from './data'
import { PageWrapper } from '/@/components/Page' import { PageWrapper } from '/@/components/Page'
import { useMessage } from '/@/hooks/web/useMessage' import { useMessage } from '/@/hooks/web/useMessage'
@@ -119,6 +120,7 @@
import { useI18n } from '/@/hooks/web/useI18n' import { useI18n } from '/@/hooks/web/useI18n'
import { delFile, analyzeFile } from '/@/api/page' import { delFile, analyzeFile } from '/@/api/page'
import { useGo } from '/@/hooks/web/usePage' import { useGo } from '/@/hooks/web/usePage'
import { useTabs } from '/@/hooks/web/useTabs'
const { t } = useI18n() const { t } = useI18n()
const { createMessage } = useMessage() const { createMessage } = useMessage()
@@ -144,7 +146,7 @@
async function analFile(item: any) { async function analFile(item: any) {
try { try {
const msg = await analyzeFile(item.id) const msg = await analyzeFile(item.id, true)
if (msg) { if (msg) {
createMessage.success(msg.msg) createMessage.success(msg.msg)
if (!item.hassettimeout && item.percent == 0) { if (!item.hassettimeout && item.percent == 0) {
@@ -171,18 +173,21 @@
}, },
setup() { setup() {
const { hasPermission } = usePermission() const { hasPermission } = usePermission()
const { refreshPage } = useTabs()
const go = useGo() const go = useGo()
function openFile(id: number) { function openFile(id: number) {
go({ name: 'FilePage', params: { id } }) go({ name: 'FilePage', params: { id } })
} }
async function onChange(_: number[]) {
refreshCardList()
refreshPage()
}
return { return {
t, t,
RoleEnum, RoleEnum,
handleChange: (list: string[]) => {
createMessage.info(`已上传文件${JSON.stringify(list)}`)
},
uploadApi, uploadApi,
hasPermission, hasPermission,
prefixCls: 'list-basic', prefixCls: 'list-basic',
@@ -192,6 +197,7 @@
analyzeFile: analFile, analyzeFile: analFile,
cardList, cardList,
pagination, pagination,
onChange,
} }
}, },
}) })

2
go.mod
View File

@@ -3,7 +3,7 @@ module github.com/fumiama/paper-manager
go 1.20 go 1.20
require ( require (
github.com/FloatTech/sqlite v1.6.1 github.com/FloatTech/sqlite v1.6.2
github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b
github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e
github.com/corona10/goimagehash v1.1.0 github.com/corona10/goimagehash v1.1.0

4
go.sum
View File

@@ -1,5 +1,5 @@
github.com/FloatTech/sqlite v1.6.1 h1:FDQM8X4PtO5P3rP9r5f1qYPFI8YvHPmyPlpeUFbOcDE= github.com/FloatTech/sqlite v1.6.2 h1:FytbExjpvYalZxxITtmSenHiPGLPUvlz47LY/P0SCCw=
github.com/FloatTech/sqlite v1.6.1/go.mod h1:zFbHzRfB+CJ+VidfjuVbrcin3DAz283F7hF1hIeHzpY= github.com/FloatTech/sqlite v1.6.2/go.mod h1:zFbHzRfB+CJ+VidfjuVbrcin3DAz283F7hF1hIeHzpY=
github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b h1:tvciXWq2nuvTbFeJGLDNIdRX3BI546D3O7k7vrVueZw= github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b h1:tvciXWq2nuvTbFeJGLDNIdRX3BI546D3O7k7vrVueZw=
github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs= github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs=
github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e h1:wR3MXQ3VbUlPKOOUwLOYgh/QaJThBTYtsl673O3lqSA= github.com/RomiChan/syncx v0.0.0-20221202055724-5f842c53020e h1:wR3MXQ3VbUlPKOOUwLOYgh/QaJThBTYtsl673O3lqSA=