mirror of
https://github.com/fumiama/paper-manager.git
synced 2026-06-12 12:10:25 +08:00
finish upload paper to temp
This commit is contained in:
@@ -21,6 +21,7 @@ import (
|
|||||||
|
|
||||||
_ "golang.org/x/image/webp"
|
_ "golang.org/x/image/webp"
|
||||||
|
|
||||||
|
sql "github.com/FloatTech/sqlite"
|
||||||
"github.com/corona10/goimagehash"
|
"github.com/corona10/goimagehash"
|
||||||
base14 "github.com/fumiama/go-base16384"
|
base14 "github.com/fumiama/go-base16384"
|
||||||
"github.com/fumiama/go-docx"
|
"github.com/fumiama/go-docx"
|
||||||
@@ -29,6 +30,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
FileTableList = "lst"
|
||||||
FileTableFile = "file"
|
FileTableFile = "file"
|
||||||
FileTableTempFile = "tmpfile"
|
FileTableTempFile = "tmpfile"
|
||||||
FileTableQuestion = "question"
|
FileTableQuestion = "question"
|
||||||
@@ -40,120 +42,18 @@ var (
|
|||||||
ErrEmptyClass = errors.New("empty class")
|
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() {
|
func init() {
|
||||||
err := FileDB.db.Open(time.Hour)
|
err := FileDB.db.Open(time.Hour)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
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 {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@@ -183,24 +83,31 @@ func init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 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
|
Year StudyYear
|
||||||
Type PaperType
|
Type PaperType
|
||||||
Date uint32 // Date is the yyyymmdd of 考试日期
|
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 考试时长
|
Time time.Duration // Time is 考试时长
|
||||||
Class string // Class is 考试科目
|
Class string // Class is 考试科目
|
||||||
Rate string // Rate is 成绩构成比例
|
Rate string // Rate is 成绩构成比例
|
||||||
Path string // Path is like paper/Class/2022-2023学年/第一学期/期末/A/xxx.docx
|
|
||||||
Questions []byte // Questions is for []QuestionJSON
|
Questions []byte // Questions is for []QuestionJSON
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddFile from FileFolder+tempath and copy it to File.Path.
|
// StudyYear 学年
|
||||||
// The para res must belong to a valid user
|
type StudyYear uint16
|
||||||
func (f *FileDatabase) AddFile(tempath string, reg *Regex, istemp bool, progress func(uint)) (*File, error) {
|
|
||||||
|
// 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)
|
user, err := UserDB.GetUserByID(reg.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -208,10 +115,14 @@ func (f *FileDatabase) AddFile(tempath string, reg *Regex, istemp bool, progress
|
|||||||
if !user.IsFileManager() && !istemp {
|
if !user.IsFileManager() && !istemp {
|
||||||
return nil, ErrInvalidRole
|
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
|
return nil, os.ErrNotExist
|
||||||
}
|
}
|
||||||
tempath = FileFolder + tempath
|
tempath := lst.Path
|
||||||
docf, err := os.Open(tempath)
|
docf, err := os.Open(tempath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -249,9 +160,7 @@ func (f *FileDatabase) AddFile(tempath string, reg *Regex, istemp bool, progress
|
|||||||
// filling File struct
|
// filling File struct
|
||||||
file := &File{
|
file := &File{
|
||||||
ID: id,
|
ID: id,
|
||||||
UID: *user.ID,
|
ListID: *lst.ID,
|
||||||
UpTime: time.Now().Unix(),
|
|
||||||
Size: sz,
|
|
||||||
}
|
}
|
||||||
titlere, err := regexp.Compile(reg.Title)
|
titlere, err := regexp.Compile(reg.Title)
|
||||||
if err != nil {
|
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(),
|
file.Year, file.Type.FirstSecond(), file.Type.MiddleFinal(), file.Type.AB(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
lst.Path = filebasepath
|
||||||
err = os.MkdirAll(filebasepath, 0755)
|
err = os.MkdirAll(filebasepath, 0755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -512,12 +422,16 @@ func (f *FileDatabase) AddFile(tempath string, reg *Regex, istemp bool, progress
|
|||||||
filequestions = append(filequestions, majorq)
|
filequestions = append(filequestions, majorq)
|
||||||
}
|
}
|
||||||
file.Questions, _ = json.Marshal(filequestions)
|
file.Questions, _ = json.Marshal(filequestions)
|
||||||
|
lst.Path += file.Class + ".docx"
|
||||||
FileDB.mu.Lock()
|
FileDB.mu.Lock()
|
||||||
if istemp {
|
if istemp {
|
||||||
err = FileDB.db.Insert(FileTableTempFile, file)
|
err = FileDB.db.Insert(FileTableTempFile, file)
|
||||||
|
lst.IsTemp = true
|
||||||
} else {
|
} else {
|
||||||
err = FileDB.db.Insert(FileTableFile, file)
|
err = FileDB.db.Insert(FileTableFile, file)
|
||||||
|
lst.IsTemp = false
|
||||||
}
|
}
|
||||||
|
_ = FileDB.db.Insert(FileTableList, &lst)
|
||||||
FileDB.mu.Unlock()
|
FileDB.mu.Unlock()
|
||||||
return file, err
|
return file, err
|
||||||
}
|
}
|
||||||
|
|||||||
62
backend/global/list.go
Normal file
62
backend/global/list.go
Normal 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
100
backend/global/papertype.go
Normal 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
|
||||||
|
}
|
||||||
@@ -96,5 +96,34 @@ func UploadHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
writeresult(w, codeError, nil, err.Error(), typeError)
|
writeresult(w, codeError, nil, err.Error(), typeError)
|
||||||
return
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,6 +61,7 @@
|
|||||||
import { warn } from '/@/utils/log'
|
import { warn } from '/@/utils/log'
|
||||||
import FileList from './FileList.vue'
|
import FileList from './FileList.vue'
|
||||||
import { useI18n } from '/@/hooks/web/useI18n'
|
import { useI18n } from '/@/hooks/web/useI18n'
|
||||||
|
import { ResultEnum } from '/@/enums/httpEnum'
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: { BasicModal, Upload, Alert, FileList },
|
components: { BasicModal, Upload, Alert, FileList },
|
||||||
props: {
|
props: {
|
||||||
@@ -181,10 +182,17 @@
|
|||||||
)
|
)
|
||||||
item.status = UploadResultStatus.SUCCESS
|
item.status = UploadResultStatus.SUCCESS
|
||||||
item.responseData = data
|
item.responseData = data
|
||||||
return {
|
const { code, result } = data
|
||||||
success: true,
|
if (code == ResultEnum.SUCCESS)
|
||||||
error: null,
|
return {
|
||||||
}
|
success: true,
|
||||||
|
error: null,
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: result,
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e)
|
console.log(e)
|
||||||
item.status = UploadResultStatus.ERROR
|
item.status = UploadResultStatus.ERROR
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
<PageWrapper :class="prefixCls" :title="t('routes.filelist.name')">
|
<PageWrapper :class="prefixCls" :title="t('routes.filelist.name')">
|
||||||
<template #headerContent>
|
<template #headerContent>
|
||||||
<BasicUpload
|
<BasicUpload
|
||||||
|
name="paper"
|
||||||
v-if="hasPermission([RoleEnum.SUPER, RoleEnum.FILE_MANAGER])"
|
v-if="hasPermission([RoleEnum.SUPER, RoleEnum.FILE_MANAGER])"
|
||||||
:maxSize="20"
|
:maxSize="20"
|
||||||
:maxNumber="10"
|
:maxNumber="10"
|
||||||
|
|||||||
Reference in New Issue
Block a user