Compare commits

...

10 Commits

Author SHA1 Message Date
7c4dfaba54 feat: modify field 2025-12-26 17:27:35 +08:00
5ee1cea0d7 feat: add gls 2025-12-26 17:11:59 +08:00
a538bd6680 fix: modify ip 2025-12-26 16:41:36 +08:00
cd221719cf fix: http req 2025-12-26 16:38:04 +08:00
d0c0d2cbc3 fix: query by task-no 2025-12-26 16:28:49 +08:00
930d782f18 feat: add api for export 2025-12-26 16:24:34 +08:00
bdd21c4b0f Merge branch 'master' into test 2025-12-26 15:48:25 +08:00
0aaafdbaa3 feat: add file export 2025-12-26 15:48:14 +08:00
68a1755a83 Merge branch 'master' into test 2025-12-25 14:02:33 +08:00
073808eb30 feat: add mml 2025-12-20 22:57:14 +08:00
9 changed files with 171 additions and 10 deletions

View File

@@ -26,6 +26,7 @@ func SetupRouter(engine *gin.RouterGroup) {
endpoint := task.NewTaskEndpoint()
taskRouter.POST("/evaluate", endpoint.EvaluateTask)
taskRouter.GET("/list", common.MustAuthMiddleware(), endpoint.GetTaskList)
taskRouter.POST("/export", endpoint.ExportTask)
}
ossRouter := v1.Group("/oss", common.GetAuthMiddleware())

View File

@@ -61,3 +61,31 @@ func (h *TaskEndpoint) GetTaskList(c *gin.Context) {
c.JSON(http.StatusOK, common.SuccessResponse(c, resp))
}
func (h *TaskEndpoint) ExportTask(c *gin.Context) {
var req task.ExportTaskRequest
if err := c.ShouldBindJSON(&req); err != nil {
log.Error(c, "func", "ExportTask", "msg", "Invalid parameters", "error", err)
c.JSON(http.StatusOK, common.ErrorResponse(c, common.CodeParamError, "Invalid parameters"))
return
}
fileData, contentType, err := h.taskService.ExportTask(c, &req)
if err != nil {
c.JSON(http.StatusOK, common.ErrorResponse(c, common.CodeSystemError, "导出任务失败"))
return
}
// set filename based on export type
var filename string
switch req.Type {
case "pdf":
filename = "texpixel_export.pdf"
case "docx":
filename = "texpixel_export.docx"
default:
filename = "texpixel_export"
}
c.Header("Content-Disposition", "attachment; filename="+filename)
c.Data(http.StatusOK, contentType, fileData)
}

2
go.mod
View File

@@ -11,6 +11,7 @@ require (
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/gin-gonic/gin v1.10.0
github.com/google/uuid v1.6.0
github.com/jtolds/gls v4.20.0+incompatible
github.com/redis/go-redis/v9 v9.7.0
github.com/rs/zerolog v1.33.0
github.com/spf13/viper v1.19.0
@@ -43,6 +44,7 @@ require (
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.20.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect

2
go.sum
View File

@@ -79,6 +79,7 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 h1:l5lAOZEym3oK3SQ2HBHWsJUfbNBiTXJDeW2QDxw9AQ0=
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
@@ -89,6 +90,7 @@ github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=

View File

@@ -34,3 +34,8 @@ type TaskListResponse struct {
TaskList []*TaskListDTO `json:"task_list"`
Total int64 `json:"total"`
}
type ExportTaskRequest struct {
TaskNo string `json:"task_no" binding:"required"`
Type string `json:"type" binding:"required,oneof=pdf docx"`
}

View File

@@ -1,8 +1,13 @@
package service
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"mime/multipart"
"net/http"
"strings"
"gitea.com/bitwsd/document_ai/internal/model/task"
@@ -111,3 +116,84 @@ func (svc *TaskService) GetTaskList(ctx context.Context, req *task.TaskListReque
}
return resp, nil
}
func (svc *TaskService) ExportTask(ctx context.Context, req *task.ExportTaskRequest) ([]byte, string, error) {
recognitionTask, err := svc.recognitionTaskDao.GetByTaskNo(dao.DB.WithContext(ctx), req.TaskNo)
if err != nil {
log.Error(ctx, "func", "ExportTask", "msg", "get task by task id failed", "error", err)
return nil, "", err
}
if recognitionTask == nil {
log.Error(ctx, "func", "ExportTask", "msg", "task not found")
return nil, "", errors.New("task not found")
}
if recognitionTask.Status != dao.TaskStatusCompleted {
log.Error(ctx, "func", "ExportTask", "msg", "task not finished")
return nil, "", errors.New("task not finished")
}
recognitionResult, err := svc.recognitionResultDao.GetByTaskID(dao.DB.WithContext(ctx), recognitionTask.ID)
if err != nil {
log.Error(ctx, "func", "ExportTask", "msg", "get recognition result by task id failed", "error", err)
return nil, "", err
}
if recognitionResult == nil {
log.Error(ctx, "func", "ExportTask", "msg", "recognition result not found")
return nil, "", errors.New("recognition result not found")
}
markdown := recognitionResult.Markdown
if markdown == "" {
log.Error(ctx, "func", "ExportTask", "msg", "markdown not found")
return nil, "", errors.New("markdown not found")
}
// call http://localhost:8055/export
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
_ = writer.WriteField("markdown_input", markdown)
_ = writer.WriteField("type", req.Type)
writer.Close()
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, "http://192.168.5.56:8055/export", body)
if err != nil {
log.Error(ctx, "func", "ExportTask", "msg", "create http request failed", "error", err)
return nil, "", err
}
httpReq.Header.Set("Content-Type", writer.FormDataContentType())
client := &http.Client{}
resp, err := client.Do(httpReq)
if err != nil {
log.Error(ctx, "func", "ExportTask", "msg", "http request failed", "error", err)
return nil, "", err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
log.Error(ctx, "func", "ExportTask", "msg", "http request failed", "status", resp.StatusCode)
return nil, "", fmt.Errorf("export service returned status: %d", resp.StatusCode)
}
fileData, err := io.ReadAll(resp.Body)
if err != nil {
log.Error(ctx, "func", "ExportTask", "msg", "read response body failed", "error", err)
return nil, "", err
}
// determine content type based on export type
var contentType string
switch req.Type {
case "pdf":
contentType = "application/pdf"
case "docx":
contentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
default:
contentType = "application/octet-stream"
}
return fileData, contentType, nil
}

View File

@@ -8,6 +8,8 @@ import (
"runtime"
"time"
"gitea.com/bitwsd/document_ai/pkg/requestid"
"github.com/rs/zerolog"
"gopkg.in/natefinch/lumberjack.v2"
)
@@ -67,8 +69,13 @@ func log(ctx context.Context, level zerolog.Level, logType LogType, kv ...interf
// 添加日志类型
event.Str("type", string(logType))
// 添加请求ID
if reqID, exists := ctx.Value("request_id").(string); exists {
reqID := requestid.GetRequestID()
if reqID == "" {
if id, exists := ctx.Value("request_id").(string); exists {
reqID = id
}
}
if reqID != "" {
event.Str("request_id", reqID)
}
@@ -149,4 +156,3 @@ func Fatal(ctx context.Context, kv ...interface{}) {
func Access(ctx context.Context, kv ...interface{}) {
log(ctx, zerolog.InfoLevel, TypeAccess, kv...)
}

View File

@@ -1,19 +1,23 @@
package middleware
import (
"gitea.com/bitwsd/document_ai/pkg/requestid"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
)
func RequestID() gin.HandlerFunc {
return func(c *gin.Context) {
requestID := c.Request.Header.Get("X-Request-ID")
if requestID == "" {
requestID = uuid.New().String()
reqID := c.Request.Header.Get("X-Request-ID")
if reqID == "" {
reqID = uuid.New().String()
}
c.Request.Header.Set("X-Request-ID", requestID)
c.Set("request_id", requestID)
c.Next()
c.Request.Header.Set("X-Request-ID", reqID)
c.Set("request_id", reqID)
requestid.SetRequestID(reqID, func() {
c.Next()
})
}
}

View File

@@ -0,0 +1,27 @@
package requestid
import (
"github.com/jtolds/gls"
)
// requestIDKey 是 gls 中存储 request_id 的 key
var requestIDKey = gls.GenSym()
// glsMgr 是 gls 管理器
var glsMgr = gls.NewContextManager()
// SetRequestID 在 gls 中设置 request_id并在 fn 执行期间保持有效
func SetRequestID(requestID string, fn func()) {
glsMgr.SetValues(gls.Values{requestIDKey: requestID}, fn)
}
// GetRequestID 从 gls 中获取当前 goroutine 的 request_id
func GetRequestID() string {
val, ok := glsMgr.GetValue(requestIDKey)
if !ok {
return ""
}
reqID, _ := val.(string)
return reqID
}