Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 147 additions & 0 deletions backend/biz/setting/handler/v1/memory_template.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package v1

import (
"log/slog"
"net/http"

"github.com/GoYoko/web"
"github.com/samber/do"

"github.com/chaitin/MonkeyCode/backend/domain"
"github.com/chaitin/MonkeyCode/backend/errcode"
"github.com/chaitin/MonkeyCode/backend/middleware"
)

// MemoryTemplateHandler Memory模板处理器
type MemoryTemplateHandler struct {
usecase domain.UserUsecase
logger *slog.Logger
}

// NewMemoryTemplateHandler 创建Memory模板处理器
func NewMemoryTemplateHandler(i *do.Injector) (*MemoryTemplateHandler, error) {
w := do.MustInvoke[*web.Web](i)
logger := do.MustInvoke[*slog.Logger](i)
usecase := do.MustInvoke[domain.UserUsecase](i)
auth := do.MustInvoke[*middleware.AuthMiddleware](i)
targetActive := do.MustInvoke[*middleware.TargetActiveMiddleware](i)

h := &MemoryTemplateHandler{
logger: logger.With("component", "handler.memory-template"),
usecase: usecase,
}

v1 := w.Group("/api/v1/users/settings")

v1.Use(auth.Auth(), targetActive.TargetActive())
v1.GET("/memory-template", web.BindHandler(h.Get))
v1.PUT("/memory-template", web.BindHandler(h.Update))
v1.DELETE("/memory-template", web.BindHandler(h.Delete))

return h, nil
}

// GetMemoryTemplateReq 获取Memory模板请求
type GetMemoryTemplateReq struct{}

// Get 获取用户Memory模板
//
// @Summary 获取用户Memory模板
// @Description 获取当前用户的Memory模板设置
// @Tags 【用户】Memory模板
// @Accept json
// @Produce json
// @Security MonkeyCodeAIAuth
// @Success 200 {object} web.Resp{data=string} "成功"
// @Failure 401 {object} web.Resp "未授权"
// @Failure 500 {object} web.Resp "服务器内部错误"
// @Router /api/v1/users/settings/memory-template [get]
func (h *MemoryTemplateHandler) Get(c *web.Context, req GetMemoryTemplateReq) error {
user := middleware.GetUser(c)

u, err := h.usecase.Get(c.Request().Context(), user.ID)
if err != nil {
h.logger.ErrorContext(c.Request().Context(), "failed to get user memory template", "error", err, "user_id", user.ID)
return errcode.ErrDatabaseQuery.Wrap(err)
}

// 如果用户没有设置模板,返回空字符串
if u == nil || u.MemoryTemplate == nil {
return c.Success("")
}

return c.Success(*u.MemoryTemplate)
}

// UpdateMemoryTemplateReq 更新Memory模板请求
type UpdateMemoryTemplateReq struct {
MemoryTemplate string `json:"memory_template"`
}

// Update 更新用户Memory模板
//
// @Summary 更新用户Memory模板
// @Description 更新当前用户的Memory模板设置
// @Tags 【用户】Memory模板
// @Accept json
// @Produce json
// @Security MonkeyCodeAIAuth
// @Param req body UpdateMemoryTemplateReq true "更新Memory模板请求"
// @Success 200 {object} web.Resp{} "成功"
// @Failure 400 {object} web.Resp "请求参数错误"
// @Failure 401 {object} web.Resp "未授权"
// @Failure 500 {object} web.Resp "服务器内部错误"
// @Router /api/v1/users/settings/memory-template [put]
func (h *MemoryTemplateHandler) Update(c *web.Context, req UpdateMemoryTemplateReq) error {
user := middleware.GetUser(c)

// 验证模板大小(最大 500KB)
if len(req.MemoryTemplate) > 500*1024 {
return c.JSON(http.StatusBadRequest, map[string]interface{}{
"code": 400,
"message": "模板大小超过限制(最大500KB)",
})
}

// 更新用户Memory模板
_, err := h.usecase.Update(c.Request().Context(), user.ID, "", domain.UpdateUserReq{
MemoryTemplate: &req.MemoryTemplate,
})
if err != nil {
h.logger.ErrorContext(c.Request().Context(), "failed to update user memory template", "error", err, "user_id", user.ID)
return errcode.ErrDatabaseOperation.Wrap(err)
}

return c.Success(nil)
}

// DeleteMemoryTemplateReq 删除Memory模板请求
type DeleteMemoryTemplateReq struct{}

// Delete 删除用户Memory模板(恢复默认)
//
// @Summary 删除用户Memory模板
// @Description 删除当前用户的Memory模板设置,恢复为默认
// @Tags 【用户】Memory模板
// @Accept json
// @Produce json
// @Security MonkeyCodeAIAuth
// @Success 200 {object} web.Resp{} "成功"
// @Failure 401 {object} web.Resp "未授权"
// @Failure 500 {object} web.Resp "服务器内部错误"
// @Router /api/v1/users/settings/memory-template [delete]
func (h *MemoryTemplateHandler) Delete(c *web.Context, req DeleteMemoryTemplateReq) error {
user := middleware.GetUser(c)

// 将模板设为空字符串
emptyTemplate := ""
_, err := h.usecase.Update(c.Request().Context(), user.ID, "", domain.UpdateUserReq{
MemoryTemplate: &emptyTemplate,
})
if err != nil {
h.logger.ErrorContext(c.Request().Context(), "failed to delete user memory template", "error", err, "user_id", user.ID)
return errcode.ErrDatabaseOperation.Wrap(err)
}

return c.Success(nil)
}
2 changes: 2 additions & 0 deletions backend/biz/setting/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@ func ProvideSetting(i *do.Injector) {
do.Provide(i, v1.NewModelHandler)
do.Provide(i, v1.NewImageHandler)
do.Provide(i, v1.NewMCPHandler)
do.Provide(i, v1.NewMemoryTemplateHandler)
}

// InvokeSetting 触发 setting 模块的 handler 初始化
func InvokeSetting(i *do.Injector) {
do.MustInvoke[*v1.ModelHandler](i)
do.MustInvoke[*v1.ImageHandler](i)
do.MustInvoke[*v1.MCPHandler](i)
do.MustInvoke[*v1.MemoryTemplateHandler](i)
}
7 changes: 7 additions & 0 deletions backend/biz/user/repo/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,13 @@ func (u *userRepo) GetUserByEmail(ctx context.Context, emails []string) ([]*db.U
return u.db.User.Query().WithTeams().Where(user.EmailIn(emails...)).All(ctx)
}

// UpdateMemoryTemplate implements domain.UserRepo.
func (u *userRepo) UpdateMemoryTemplate(ctx context.Context, uid uuid.UUID, memoryTemplate string) error {
return u.db.User.UpdateOneID(uid).
SetMemoryTemplate(memoryTemplate).
Exec(ctx)
}

// SetEmail implements domain.UserRepo.
func (u *userRepo) SetEmail(ctx context.Context, userID uuid.UUID, email string) error {
return u.db.User.UpdateOneID(userID).SetEmail(email).Exec(ctx)
Expand Down
20 changes: 16 additions & 4 deletions backend/biz/user/usecase/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,22 @@ func (u *UserUsecase) Get(ctx context.Context, uid uuid.UUID) (*domain.User, err

// Update implements domain.UserUsecase.
func (u *UserUsecase) Update(ctx context.Context, uid uuid.UUID, avatarURL string, req domain.UpdateUserReq) (*domain.User, error) {
err := u.repo.Update(ctx, uid, req.Name, avatarURL)
if err != nil {
u.logger.ErrorContext(ctx, "update user failed", "error", err, "user_id", uid)
return nil, err
// 如果有 memory_template,更新它
if req.MemoryTemplate != nil {
err := u.repo.UpdateMemoryTemplate(ctx, uid, *req.MemoryTemplate)
if err != nil {
u.logger.ErrorContext(ctx, "update memory template failed", "error", err, "user_id", uid)
return nil, err
}
}

// 更新其他字段
if req.Name != "" || avatarURL != "" {
err := u.repo.Update(ctx, uid, req.Name, avatarURL)
if err != nil {
u.logger.ErrorContext(ctx, "update user failed", "error", err, "user_id", uid)
return nil, err
}
}

user, err := u.Get(ctx, uid)
Expand Down
1 change: 1 addition & 0 deletions backend/db/migrate/schema.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

75 changes: 74 additions & 1 deletion backend/db/mutation.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions backend/db/runtime/runtime.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading