init repo

This commit is contained in:
liuyuanchuang
2025-12-10 18:33:37 +08:00
commit 48e63894eb
2408 changed files with 1053045 additions and 0 deletions

19
api/router.go Normal file
View File

@@ -0,0 +1,19 @@
package api
import (
"gitea.com/bitwsd/document_ai/api/v1/formula"
"gitea.com/bitwsd/document_ai/api/v1/oss"
"gitea.com/bitwsd/document_ai/api/v1/task"
"gitea.com/bitwsd/document_ai/api/v1/user"
"github.com/gin-gonic/gin"
)
func SetupRouter(engine *gin.RouterGroup) {
v1 := engine.Group("/v1")
{
formula.SetupRouter(v1)
oss.SetupRouter(v1)
task.SetupRouter(v1)
user.SetupRouter(v1)
}
}

118
api/v1/formula/handler.go Normal file
View File

@@ -0,0 +1,118 @@
package formula
import (
"net/http"
"path/filepath"
"gitea.com/bitwsd/document_ai/internal/model/formula"
"gitea.com/bitwsd/document_ai/internal/service"
"gitea.com/bitwsd/document_ai/internal/storage/dao"
"gitea.com/bitwsd/document_ai/pkg/common"
"gitea.com/bitwsd/document_ai/pkg/utils"
"github.com/gin-gonic/gin"
)
type FormulaEndpoint struct {
recognitionService *service.RecognitionService
}
func NewFormulaEndpoint() *FormulaEndpoint {
return &FormulaEndpoint{
recognitionService: service.NewRecognitionService(),
}
}
// CreateTask godoc
// @Summary Create a formula recognition task
// @Description Create a new formula recognition task from image
// @Tags Formula
// @Accept json
// @Produce json
// @Param request body CreateFormulaRecognitionRequest true "Create task request"
// @Success 200 {object} common.Response{data=CreateTaskResponse}
// @Failure 400 {object} common.Response
// @Failure 500 {object} common.Response
// @Router /v1/formula/recognition [post]
func (endpoint *FormulaEndpoint) CreateTask(ctx *gin.Context) {
var req formula.CreateFormulaRecognitionRequest
if err := ctx.BindJSON(&req); err != nil {
ctx.JSON(http.StatusOK, common.ErrorResponse(ctx, common.CodeParamError, "Invalid parameters"))
return
}
if !utils.InArray(req.TaskType, []string{string(dao.TaskTypeFormula), string(dao.TaskTypeFormula)}) {
ctx.JSON(http.StatusOK, common.ErrorResponse(ctx, common.CodeParamError, "Invalid task type"))
return
}
fileExt := filepath.Ext(req.FileName)
if !utils.InArray(fileExt, []string{".jpg", ".jpeg", ".png", ".gif", ".bmp", ".tiff", ".webp"}) {
ctx.JSON(http.StatusOK, common.ErrorResponse(ctx, common.CodeParamError, "Invalid file type"))
return
}
task, err := endpoint.recognitionService.CreateRecognitionTask(ctx, &req)
if err != nil {
ctx.JSON(http.StatusOK, common.ErrorResponse(ctx, common.CodeSystemError, "Failed to create task"))
return
}
ctx.JSON(http.StatusOK, common.SuccessResponse(ctx, &formula.CreateTaskResponse{
TaskNo: task.TaskUUID,
Status: int(task.Status),
}))
}
// GetTaskStatus godoc
// @Summary Get formula recognition task status
// @Description Get the status and results of a formula recognition task
// @Tags Formula
// @Accept json
// @Produce json
// @Param task_no path string true "Task No"
// @Success 200 {object} common.Response{data=GetTaskStatusResponse}
// @Failure 400 {object} common.Response
// @Failure 404 {object} common.Response
// @Failure 500 {object} common.Response
// @Router /v1/formula/recognition/{task_no} [get]
func (endpoint *FormulaEndpoint) GetTaskStatus(c *gin.Context) {
var req formula.GetRecognitionStatusRequest
if err := c.ShouldBindUri(&req); err != nil {
c.JSON(http.StatusBadRequest, common.ErrorResponse(c, common.CodeParamError, "invalid task no"))
return
}
task, err := endpoint.recognitionService.GetFormualTask(c, req.TaskNo)
if err != nil {
c.JSON(http.StatusOK, common.ErrorResponse(c, common.CodeSystemError, "failed to get task status"))
return
}
c.JSON(http.StatusOK, common.SuccessResponse(c, task))
}
// AI增强识别
// @Summary AI增强识别
// @Description AI增强识别
// @Tags Formula
// @Accept json
// @Produce json
// @Param request body formula.AIEnhanceRecognitionRequest true "AI增强识别请求"
// @Success 200 {object} common.Response{data=formula.AIEnhanceRecognitionResponse}
// @Router /v1/formula/ai_enhance [post]
func (endpoint *FormulaEndpoint) AIEnhanceRecognition(c *gin.Context) {
var req formula.AIEnhanceRecognitionRequest
if err := c.BindJSON(&req); err != nil {
c.JSON(http.StatusOK, common.ErrorResponse(c, common.CodeParamError, "Invalid parameters"))
return
}
_, err := endpoint.recognitionService.AIEnhanceRecognition(c, &req)
if err != nil {
c.JSON(http.StatusOK, common.ErrorResponse(c, common.CodeSystemError, err.Error()))
return
}
c.JSON(http.StatusOK, common.SuccessResponse(c, nil))
}

12
api/v1/formula/router.go Normal file
View File

@@ -0,0 +1,12 @@
package formula
import (
"github.com/gin-gonic/gin"
)
func SetupRouter(engine *gin.RouterGroup) {
endpoint := NewFormulaEndpoint()
engine.POST("/formula/recognition", endpoint.CreateTask)
engine.POST("/formula/ai_enhance", endpoint.AIEnhanceRecognition)
engine.GET("/formula/recognition/:task_no", endpoint.GetTaskStatus)
}

106
api/v1/oss/handler.go Normal file
View File

@@ -0,0 +1,106 @@
package oss
import (
"fmt"
"net/http"
"os"
"path/filepath"
"time"
"gitea.com/bitwsd/document_ai/config"
"gitea.com/bitwsd/document_ai/internal/storage/dao"
"gitea.com/bitwsd/document_ai/pkg/common"
"gitea.com/bitwsd/document_ai/pkg/constant"
"gitea.com/bitwsd/document_ai/pkg/oss"
"gitea.com/bitwsd/document_ai/pkg/utils"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
func GetPostObjectSignature(ctx *gin.Context) {
policyToken, err := oss.GetPolicyToken()
if err != nil {
ctx.JSON(http.StatusOK, common.ErrorResponse(ctx, common.CodeSystemError, err.Error()))
return
}
ctx.JSON(http.StatusOK, common.SuccessResponse(ctx, policyToken))
}
// GetSignatureURL handles the request to generate a signed URL for a file upload.
// @Summary Generate signed URL for file upload
// @Description This endpoint generates a signed URL for uploading a file to the OSS (Object Storage Service).
// @Tags file
// @Accept json
// @Produce json
// @Param hash query string true "Hash value of the file"
// @Success 200 {object} common.Response{data=map[string]string{"sign_url":string, "repeat":bool, "path":string}} "Signed URL generated successfully"
// @Failure 200 {object} common.Response "Error response"
// @Router /signature_url [get]
func GetSignatureURL(ctx *gin.Context) {
userID := ctx.GetInt64(constant.ContextUserID)
type Req struct {
FileHash string `json:"file_hash" binding:"required"`
FileName string `json:"file_name" binding:"required"`
}
req := Req{}
err := ctx.BindJSON(&req)
if err != nil {
ctx.JSON(http.StatusOK, common.ErrorResponse(ctx, common.CodeParamError, "param error"))
return
}
taskDao := dao.NewRecognitionTaskDao()
sess := dao.DB.WithContext(ctx)
task, err := taskDao.GetTaskByFileURL(sess, userID, req.FileHash)
if err != nil && err != gorm.ErrRecordNotFound {
ctx.JSON(http.StatusOK, common.ErrorResponse(ctx, common.CodeDBError, "failed to get task"))
return
}
if task.ID != 0 {
ctx.JSON(http.StatusOK, common.SuccessResponse(ctx, gin.H{"sign_url": "", "repeat": true, "path": task.FileURL}))
return
}
extend := filepath.Ext(req.FileName)
if extend == "" {
ctx.JSON(http.StatusOK, common.ErrorResponse(ctx, common.CodeParamError, "invalid file name"))
return
}
if !utils.InArray(extend, []string{".jpg", ".jpeg", ".png", ".gif", ".bmp", ".tiff", ".webp"}) {
ctx.JSON(http.StatusOK, common.ErrorResponse(ctx, common.CodeParamError, "invalid file type"))
return
}
path := filepath.Join(oss.FormulaDir, fmt.Sprintf("%s%s", utils.NewUUID(), extend))
url, err := oss.GetPolicyURL(ctx, path)
if err != nil {
ctx.JSON(http.StatusOK, common.ErrorResponse(ctx, common.CodeSystemError, err.Error()))
return
}
ctx.JSON(http.StatusOK, common.SuccessResponse(ctx, gin.H{"sign_url": url, "repeat": false, "path": path}))
}
func UploadFile(ctx *gin.Context) {
if err := os.MkdirAll(config.GlobalConfig.UploadDir, 0755); err != nil {
ctx.JSON(http.StatusOK, common.ErrorResponse(ctx, common.CodeSystemError, "Failed to create upload directory"))
return
}
// Get file from form
file, err := ctx.FormFile("file")
if err != nil {
ctx.JSON(http.StatusOK, common.ErrorResponse(ctx, common.CodeParamError, "File is required"))
return
}
// Generate unique filename with timestamp
ext := filepath.Ext(file.Filename)
filename := fmt.Sprintf("%d%s", time.Now().UnixNano(), ext)
filepath := filepath.Join(config.GlobalConfig.UploadDir, filename)
// Save file
if err := ctx.SaveUploadedFile(file, filepath); err != nil {
ctx.JSON(http.StatusOK, common.ErrorResponse(ctx, common.CodeSystemError, "Failed to save file"))
return
}
// Return success with file path
ctx.JSON(http.StatusOK, common.SuccessResponse(ctx, gin.H{"sign_url": filepath}))
}

12
api/v1/oss/router.go Normal file
View File

@@ -0,0 +1,12 @@
package oss
import "github.com/gin-gonic/gin"
func SetupRouter(parent *gin.RouterGroup) {
router := parent.Group("oss")
{
router.POST("/signature", GetPostObjectSignature)
router.POST("/signature_url", GetSignatureURL)
router.POST("/file/upload", UploadFile)
}
}

61
api/v1/task/handler.go Normal file
View File

@@ -0,0 +1,61 @@
package task
import (
"net/http"
"gitea.com/bitwsd/core/common/log"
"gitea.com/bitwsd/document_ai/internal/model/task"
"gitea.com/bitwsd/document_ai/internal/service"
"gitea.com/bitwsd/document_ai/pkg/common"
"github.com/gin-gonic/gin"
)
type TaskEndpoint struct {
taskService *service.TaskService
}
func NewTaskEndpoint() *TaskEndpoint {
return &TaskEndpoint{taskService: service.NewTaskService()}
}
func (h *TaskEndpoint) EvaluateTask(c *gin.Context) {
var req task.EvaluateTaskRequest
if err := c.ShouldBindJSON(&req); err != nil {
log.Error(c, "func", "EvaluateTask", "msg", "Invalid parameters", "error", err)
c.JSON(http.StatusOK, common.ErrorResponse(c, common.CodeParamError, "Invalid parameters"))
return
}
err := h.taskService.EvaluateTask(c, &req)
if err != nil {
c.JSON(http.StatusOK, common.ErrorResponse(c, common.CodeSystemError, "评价任务失败"))
return
}
c.JSON(http.StatusOK, common.SuccessResponse(c, nil))
}
func (h *TaskEndpoint) GetTaskList(c *gin.Context) {
var req task.TaskListRequest
if err := c.ShouldBindQuery(&req); err != nil {
log.Error(c, "func", "GetTaskList", "msg", "Invalid parameters", "error", err)
c.JSON(http.StatusOK, common.ErrorResponse(c, common.CodeParamError, "Invalid parameters"))
return
}
if req.Page <= 0 {
req.Page = 1
}
if req.PageSize <= 0 {
req.PageSize = 10
}
resp, err := h.taskService.GetTaskList(c, &req)
if err != nil {
c.JSON(http.StatusOK, common.ErrorResponse(c, common.CodeSystemError, "获取任务列表失败"))
return
}
c.JSON(http.StatusOK, common.SuccessResponse(c, resp))
}

11
api/v1/task/router.go Normal file
View File

@@ -0,0 +1,11 @@
package task
import (
"github.com/gin-gonic/gin"
)
func SetupRouter(engine *gin.RouterGroup) {
endpoint := NewTaskEndpoint()
engine.POST("/task/evaluate", endpoint.EvaluateTask)
engine.GET("/task/list", endpoint.GetTaskList)
}

105
api/v1/user/handler.go Normal file
View File

@@ -0,0 +1,105 @@
package user
import (
"net/http"
"gitea.com/bitwsd/core/common/log"
"gitea.com/bitwsd/document_ai/config"
model "gitea.com/bitwsd/document_ai/internal/model/user"
"gitea.com/bitwsd/document_ai/internal/service"
"gitea.com/bitwsd/document_ai/pkg/common"
"gitea.com/bitwsd/document_ai/pkg/constant"
"gitea.com/bitwsd/document_ai/pkg/jwt"
"github.com/gin-gonic/gin"
)
type UserEndpoint struct {
userService *service.UserService
}
func NewUserEndpoint() *UserEndpoint {
return &UserEndpoint{
userService: service.NewUserService(),
}
}
func (h *UserEndpoint) SendVerificationCode(ctx *gin.Context) {
req := model.SmsSendRequest{}
if err := ctx.ShouldBindJSON(&req); err != nil {
ctx.JSON(http.StatusOK, common.ErrorResponse(ctx, common.CodeParamError, common.CodeParamErrorMsg))
return
}
code, err := h.userService.GetSmsCode(ctx, req.Phone)
if err != nil {
log.Error(ctx, "func", "SendVerificationCode", "msg", "发送验证码失败", "error", err)
ctx.JSON(http.StatusOK, common.ErrorResponse(ctx, common.CodeSystemError, common.CodeSystemErrorMsg))
return
}
ctx.JSON(http.StatusOK, common.SuccessResponse(ctx, model.SmsSendResponse{Code: code}))
}
func (h *UserEndpoint) LoginByPhoneCode(ctx *gin.Context) {
req := model.PhoneLoginRequest{}
if err := ctx.ShouldBindJSON(&req); err != nil {
ctx.JSON(http.StatusOK, common.ErrorResponse(ctx, common.CodeParamError, common.CodeParamErrorMsg))
return
}
if req.Code == "" || req.Phone == "" {
ctx.JSON(http.StatusOK, common.ErrorResponse(ctx, common.CodeParamError, common.CodeParamErrorMsg))
return
}
if config.GlobalConfig.Server.IsDebug() {
uid := 1
token, err := jwt.CreateToken(jwt.User{UserId: int64(uid)})
if err != nil {
ctx.JSON(http.StatusOK, common.ErrorResponse(ctx, common.CodeUnauthorized, common.CodeUnauthorizedMsg))
return
}
ctx.JSON(http.StatusOK, common.SuccessResponse(ctx, model.PhoneLoginResponse{Token: token}))
return
}
uid, err := h.userService.VerifySmsCode(ctx, req.Phone, req.Code)
if err != nil {
ctx.JSON(http.StatusOK, common.ErrorResponse(ctx, common.CodeUnauthorized, common.CodeSmsCodeErrorMsg))
return
}
token, err := jwt.CreateToken(jwt.User{UserId: uid})
if err != nil {
ctx.JSON(http.StatusOK, common.ErrorResponse(ctx, common.CodeUnauthorized, common.CodeUnauthorizedMsg))
return
}
ctx.JSON(http.StatusOK, common.SuccessResponse(ctx, model.PhoneLoginResponse{Token: token}))
}
func (h *UserEndpoint) GetUserInfo(ctx *gin.Context) {
uid := ctx.GetInt64(constant.ContextUserID)
if uid == 0 {
ctx.JSON(http.StatusOK, common.ErrorResponse(ctx, common.CodeUnauthorized, common.CodeUnauthorizedMsg))
return
}
user, err := h.userService.GetUserInfo(ctx, uid)
if err != nil {
ctx.JSON(http.StatusOK, common.ErrorResponse(ctx, common.CodeSystemError, common.CodeSystemErrorMsg))
return
}
status := 0
if user.ID > 0 {
status = 1
}
ctx.JSON(http.StatusOK, common.SuccessResponse(ctx, model.UserInfoResponse{
Username: user.Username,
Phone: user.Phone,
Status: status,
}))
}

16
api/v1/user/router.go Normal file
View File

@@ -0,0 +1,16 @@
package user
import (
"gitea.com/bitwsd/document_ai/pkg/common"
"github.com/gin-gonic/gin"
)
func SetupRouter(router *gin.RouterGroup) {
userEndpoint := NewUserEndpoint()
userRouter := router.Group("/user")
{
userRouter.POST("/get/sms", userEndpoint.SendVerificationCode)
userRouter.POST("/login/phone", userEndpoint.LoginByPhoneCode)
userRouter.GET("/info", common.GetAuthMiddleware(), userEndpoint.GetUserInfo)
}
}