1
0
mirror of https://github.com/fumiama/paper-manager.git synced 2026-06-06 08:20:24 +08:00

finish upload paper to temp

This commit is contained in:
源文雨
2023-04-07 14:34:06 +08:00
parent 42274a0576
commit 773db2e581
6 changed files with 240 additions and 126 deletions

View File

@@ -21,6 +21,7 @@ import (
_ "golang.org/x/image/webp"
sql "github.com/FloatTech/sqlite"
"github.com/corona10/goimagehash"
base14 "github.com/fumiama/go-base16384"
"github.com/fumiama/go-docx"
@@ -29,6 +30,7 @@ import (
)
const (
FileTableList = "lst"
FileTableFile = "file"
FileTableTempFile = "tmpfile"
FileTableQuestion = "question"
@@ -40,120 +42,18 @@ var (
ErrEmptyClass = errors.New("empty class")
)
// PaperType [4 开 一页纸 闭] [4 上下] [4 中末] [4 AB]
type PaperType uint16
// AB default A
func (pt PaperType) AB() byte {
switch pt & 0x0f {
case 1:
return 'A'
case 2:
return 'B'
default:
return 'A'
}
}
func (pt PaperType) SetAB(x byte) PaperType {
n := PaperType(0)
switch x {
case 'A':
n = 1
case 'B':
n = 2
}
return pt | n
}
// MiddleFinal default 平时
func (pt PaperType) MiddleFinal() string {
switch (pt & 0xf0) >> 4 {
case 1:
return "期中"
case 2:
return "期末"
default:
return "平时"
}
}
func (pt PaperType) SetMiddleFinal(x string) PaperType {
n := PaperType(0)
switch x {
case "中":
n = 1 << 4
case "末":
n = 2 << 4
}
return pt | n
}
// FirstSecond default is 年度
func (pt PaperType) FirstSecond() string {
switch (pt & 0x0f00) >> 8 {
case 1:
return "第1学期"
case 2:
return "第2学期"
default:
return "年度"
}
}
func (pt PaperType) SetFirstSecond(x byte) PaperType {
n := PaperType(0)
switch x {
case '1':
n = 1 << 8
case '2':
n = 2 << 8
}
return pt | n
}
// OpenClose default 闭卷
func (pt PaperType) OpenClose() string {
switch (pt & 0xf000) >> 12 {
case 1:
return "开卷"
case 2:
return "一页纸开卷"
case 3:
return "闭卷"
default:
return "闭卷"
}
}
func (pt PaperType) SetOpenClose(x string) PaperType {
n := PaperType(0)
switch x {
case "开卷":
n = 1 << 12
case "一页纸开卷":
n = 2 << 12
case "闭卷":
n = 3 << 12
}
return pt | n
}
// StudyYear 学年
type StudyYear uint16
// String ex. 2022-2023学年
func (sy StudyYear) String() string {
next := sy + 1
return strconv.Itoa(int(sy)) + "-" + strconv.Itoa(int(next)) + "学年"
}
func init() {
err := FileDB.db.Open(time.Hour)
if err != nil {
panic(err)
}
err = FileDB.db.Create(FileTableFile, &File{})
err = FileDB.db.Create(FileTableList, &List{})
if err != nil {
panic(err)
}
err = FileDB.db.Create(FileTableFile, &File{},
"FOREIGN KEY(ListID) REFERENCES "+FileTableList+"(ID)",
)
if err != nil {
panic(err)
}
@@ -183,24 +83,31 @@ func init() {
}
}
// File stores to paper/Class/2022-2023学年/第一学期/期末/A/xxx.docx
type File struct {
ID uint64 // ID is the first 8 bytes of the original file's md5
ListID int // ListID is the foreign key to List(ID)
Year StudyYear
Type PaperType
Date uint32 // Date is the yyyymmdd of 考试日期
UID int // UID is the uploader's ID
UpTime int64 // UpTime is time.Now().Unix() when uploading
Size int64 // Size of the original file
Time time.Duration // Time is 考试时长
Class string // Class is 考试科目
Rate string // Rate is 成绩构成比例
Path string // Path is like paper/Class/2022-2023学年/第一学期/期末/A/xxx.docx
Questions []byte // Questions is for []QuestionJSON
}
// AddFile from FileFolder+tempath and copy it to File.Path.
// The para res must belong to a valid user
func (f *FileDatabase) AddFile(tempath string, reg *Regex, istemp bool, progress func(uint)) (*File, error) {
// StudyYear 学年
type StudyYear uint16
// String ex. 2022-2023学年
func (sy StudyYear) String() string {
next := sy + 1
return strconv.Itoa(int(sy)) + "-" + strconv.Itoa(int(next)) + "学年"
}
// AddFile from lst and copy it to analyzed path.
// The para reg must belong to a valid user
func (f *FileDatabase) AddFile(lstid int, reg *Regex, istemp bool, progress func(uint)) (*File, error) {
user, err := UserDB.GetUserByID(reg.ID)
if err != nil {
return nil, err
@@ -208,10 +115,14 @@ func (f *FileDatabase) AddFile(tempath string, reg *Regex, istemp bool, progress
if !user.IsFileManager() && !istemp {
return nil, ErrInvalidRole
}
if strings.Contains(tempath, "..") {
lst, err := sql.Find[List](&FileDB.db, FileTableList, "WHERE ID="+strconv.Itoa(lstid))
if err != nil {
return nil, err
}
if lst.Path == "" || strings.Contains(lst.Path, "..") {
return nil, os.ErrNotExist
}
tempath = FileFolder + tempath
tempath := lst.Path
docf, err := os.Open(tempath)
if err != nil {
return nil, err
@@ -249,9 +160,7 @@ func (f *FileDatabase) AddFile(tempath string, reg *Regex, istemp bool, progress
// filling File struct
file := &File{
ID: id,
UID: *user.ID,
UpTime: time.Now().Unix(),
Size: sz,
ListID: *lst.ID,
}
titlere, err := regexp.Compile(reg.Title)
if err != nil {
@@ -345,6 +254,7 @@ func (f *FileDatabase) AddFile(tempath string, reg *Regex, istemp bool, progress
file.Year, file.Type.FirstSecond(), file.Type.MiddleFinal(), file.Type.AB(),
)
}
lst.Path = filebasepath
err = os.MkdirAll(filebasepath, 0755)
if err != nil {
return nil, err
@@ -512,12 +422,16 @@ func (f *FileDatabase) AddFile(tempath string, reg *Regex, istemp bool, progress
filequestions = append(filequestions, majorq)
}
file.Questions, _ = json.Marshal(filequestions)
lst.Path += file.Class + ".docx"
FileDB.mu.Lock()
if istemp {
err = FileDB.db.Insert(FileTableTempFile, file)
lst.IsTemp = true
} else {
err = FileDB.db.Insert(FileTableFile, file)
lst.IsTemp = false
}
_ = FileDB.db.Insert(FileTableList, &lst)
FileDB.mu.Unlock()
return file, err
}

62
backend/global/list.go Normal file
View File

@@ -0,0 +1,62 @@
package global
import (
"errors"
"io"
"os"
"strconv"
"strings"
"time"
)
var (
ErrInvalidSaveName = errors.New("invalid save name")
)
// List of file path
type List struct {
ID *int // ID is self-inc
Uploader int // Uploader is uid
UpTime int64 // UpTime is upload time (unix timestamp)
Size int64 // Size of the original file
IsTemp bool // IsTemp whether file is temp
Path string // Path of file
}
// 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) {
_, err = UserDB.GetUserByID(uploader)
if err != nil {
return
}
if strings.Contains(name, "..") || strings.Contains(name, "/") {
err = ErrInvalidSaveName
return
}
tmpdir := PaperFolder + "tmp/" + strconv.Itoa(uploader)
err = os.MkdirAll(tmpdir, 0755)
if err != nil {
return
}
lst := List{
Uploader: uploader,
UpTime: time.Now().Unix(),
IsTemp: true,
Path: tmpdir + "/" + name,
}
ff, err := os.Create(lst.Path)
if err != nil {
return
}
sz, err := io.Copy(ff, file)
_ = ff.Close()
if err != nil {
_ = os.Remove(lst.Path)
return
}
lst.Size = sz
FileDB.mu.Lock()
err = FileDB.db.Insert(FileTableList, &lst)
FileDB.mu.Unlock()
return
}

100
backend/global/papertype.go Normal file
View File

@@ -0,0 +1,100 @@
package global
// PaperType [4 开 一页纸 闭] [4 上下] [4 中末] [4 AB]
type PaperType uint16
// AB default A
func (pt PaperType) AB() byte {
switch pt & 0x0f {
case 1:
return 'A'
case 2:
return 'B'
default:
return 'A'
}
}
func (pt PaperType) SetAB(x byte) PaperType {
n := PaperType(0)
switch x {
case 'A':
n = 1
case 'B':
n = 2
}
return pt | n
}
// MiddleFinal default 平时
func (pt PaperType) MiddleFinal() string {
switch (pt & 0xf0) >> 4 {
case 1:
return "期中"
case 2:
return "期末"
default:
return "平时"
}
}
func (pt PaperType) SetMiddleFinal(x string) PaperType {
n := PaperType(0)
switch x {
case "中":
n = 1 << 4
case "末":
n = 2 << 4
}
return pt | n
}
// FirstSecond default is 年度
func (pt PaperType) FirstSecond() string {
switch (pt & 0x0f00) >> 8 {
case 1:
return "第1学期"
case 2:
return "第2学期"
default:
return "年度"
}
}
func (pt PaperType) SetFirstSecond(x byte) PaperType {
n := PaperType(0)
switch x {
case '1':
n = 1 << 8
case '2':
n = 2 << 8
}
return pt | n
}
// OpenClose default 闭卷
func (pt PaperType) OpenClose() string {
switch (pt & 0xf000) >> 12 {
case 1:
return "开卷"
case 2:
return "一页纸开卷"
case 3:
return "闭卷"
default:
return "闭卷"
}
}
func (pt PaperType) SetOpenClose(x string) PaperType {
n := PaperType(0)
switch x {
case "开卷":
n = 1 << 12
case "一页纸开卷":
n = 2 << 12
case "闭卷":
n = 3 << 12
}
return pt | n
}

View File

@@ -96,5 +96,34 @@ func UploadHandler(w http.ResponseWriter, r *http.Request) {
writeresult(w, codeError, nil, err.Error(), typeError)
return
}
ff, h, err = r.FormFile("paper")
if err == nil {
defer ff.Close()
if !user.IsFileManager() {
writeresult(w, codeError, nil, "no upload permission", typeError)
return
}
ct := h.Header.Get("Content-Type")
fn := h.Filename
logrus.Infoln("[file.UploadHandler] receive paper, name:", fn)
if ct != "application/vnd.openxmlformats-officedocument.wordprocessingml.document" {
writeresult(w, codeError, nil, "invalid mimetype: need docx", typeError)
return
}
if strings.ContainsAny(fn, `/\`) || strings.Contains(fn, "..") {
writeresult(w, codeError, nil, "invalid filename", typeError)
return
}
err = global.FileDB.SaveFileToTemp(*user.ID, ff, fn)
if err != nil {
writeresult(w, codeError, nil, err.Error(), typeError)
return
}
writeresult(w, codeSuccess, "上传"+fn+"成功", messageOk, typeSuccess)
return
}
if err != http.ErrMissingFile {
writeresult(w, codeError, nil, err.Error(), typeError)
return
}
}

View File

@@ -61,6 +61,7 @@
import { warn } from '/@/utils/log'
import FileList from './FileList.vue'
import { useI18n } from '/@/hooks/web/useI18n'
import { ResultEnum } from '/@/enums/httpEnum'
export default defineComponent({
components: { BasicModal, Upload, Alert, FileList },
props: {
@@ -181,10 +182,17 @@
)
item.status = UploadResultStatus.SUCCESS
item.responseData = data
return {
success: true,
error: null,
}
const { code, result } = data
if (code == ResultEnum.SUCCESS)
return {
success: true,
error: null,
}
else
return {
success: false,
error: result,
}
} catch (e) {
console.log(e)
item.status = UploadResultStatus.ERROR

View File

@@ -2,6 +2,7 @@
<PageWrapper :class="prefixCls" :title="t('routes.filelist.name')">
<template #headerContent>
<BasicUpload
name="paper"
v-if="hasPermission([RoleEnum.SUPER, RoleEnum.FILE_MANAGER])"
:maxSize="20"
:maxNumber="10"