feat: 用户使用记录接口

This commit is contained in:
yokowu
2025-07-21 15:04:03 +08:00
parent 8ebaf582a1
commit 20c0da6fb0
18 changed files with 520 additions and 59 deletions

View File

@@ -77,10 +77,10 @@ func newServer() (*Server, error) {
userUsecase := usecase4.NewUserUsecase(configConfig, redisClient, userRepo, slogLogger, sessionSession)
dashboardRepo := repo6.NewDashboardRepo(client)
dashboardUsecase := usecase5.NewDashboardUsecase(dashboardRepo)
userHandler := v1_3.NewUserHandler(web, userUsecase, extensionUsecase, dashboardUsecase, authMiddleware, activeMiddleware, sessionSession, slogLogger, configConfig)
dashboardHandler := v1_4.NewDashboardHandler(web, dashboardUsecase, authMiddleware, activeMiddleware)
billingRepo := repo7.NewBillingRepo(client)
billingUsecase := usecase6.NewBillingUsecase(billingRepo)
userHandler := v1_3.NewUserHandler(web, userUsecase, extensionUsecase, dashboardUsecase, billingUsecase, authMiddleware, activeMiddleware, sessionSession, slogLogger, configConfig)
dashboardHandler := v1_4.NewDashboardHandler(web, dashboardUsecase, authMiddleware, activeMiddleware)
billingHandler := v1_5.NewBillingHandler(web, billingUsecase, authMiddleware, activeMiddleware)
server := &Server{
config: configConfig,

View File

@@ -1446,6 +1446,280 @@
"responses": {}
}
},
"/api/v1/user/chat/info": {
"get": {
"description": "获取对话内容",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"User Record"
],
"summary": "获取对话内容",
"operationId": "user-chat-info",
"parameters": [
{
"type": "string",
"description": "对话记录ID",
"name": "id",
"in": "query",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/web.Resp"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/domain.ChatInfo"
}
}
}
]
}
},
"401": {
"description": "Unauthorized",
"schema": {
"type": "string"
}
}
}
}
},
"/api/v1/user/chat/record": {
"get": {
"description": "获取用户对话记录",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"User Record"
],
"summary": "获取用户对话记录",
"operationId": "user-list-chat-record",
"parameters": [
{
"type": "string",
"description": "作者",
"name": "author",
"in": "query"
},
{
"type": "boolean",
"description": "是否接受筛选",
"name": "is_accept",
"in": "query"
},
{
"type": "string",
"description": "语言",
"name": "language",
"in": "query"
},
{
"type": "string",
"description": "下一页标识",
"name": "next_token",
"in": "query"
},
{
"type": "integer",
"description": "分页",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "每页多少条记录",
"name": "size",
"in": "query"
},
{
"type": "string",
"description": "工作模式",
"name": "work_mode",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/web.Resp"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/domain.ListChatRecordResp"
}
}
}
]
}
},
"401": {
"description": "Unauthorized",
"schema": {
"type": "string"
}
}
}
}
},
"/api/v1/user/completion/info": {
"get": {
"description": "获取补全内容",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"User Record"
],
"summary": "获取补全内容",
"operationId": "user-completion-info",
"parameters": [
{
"type": "string",
"description": "补全记录ID",
"name": "id",
"in": "query",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/web.Resp"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/domain.CompletionInfo"
}
}
}
]
}
},
"401": {
"description": "Unauthorized",
"schema": {
"type": "string"
}
}
}
}
},
"/api/v1/user/completion/record": {
"get": {
"description": "获取补全记录",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"User Record"
],
"summary": "获取补全记录",
"operationId": "user-list-completion-record",
"parameters": [
{
"type": "string",
"description": "作者",
"name": "author",
"in": "query"
},
{
"type": "boolean",
"description": "是否接受筛选",
"name": "is_accept",
"in": "query"
},
{
"type": "string",
"description": "语言",
"name": "language",
"in": "query"
},
{
"type": "string",
"description": "下一页标识",
"name": "next_token",
"in": "query"
},
{
"type": "integer",
"description": "分页",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "每页多少条记录",
"name": "size",
"in": "query"
},
{
"type": "string",
"description": "工作模式",
"name": "work_mode",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/web.Resp"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/domain.ListCompletionRecordResp"
}
}
}
]
}
},
"401": {
"description": "Unauthorized",
"schema": {
"type": "string"
}
}
}
}
},
"/api/v1/user/dashboard/events": {
"get": {
"description": "获取用户事件",
@@ -1456,7 +1730,7 @@
"application/json"
],
"tags": [
"Dashboard"
"User Dashboard"
],
"summary": "获取用户事件",
"operationId": "user-dashboard-events",
@@ -1510,6 +1784,12 @@
}
]
}
},
"401": {
"description": "Unauthorized",
"schema": {
"type": "string"
}
}
}
}
@@ -1524,7 +1804,7 @@
"application/json"
],
"tags": [
"Dashboard"
"User Dashboard"
],
"summary": "用户热力图",
"operationId": "user-dashboard-heatmap",
@@ -1546,6 +1826,12 @@
}
]
}
},
"401": {
"description": "Unauthorized",
"schema": {
"type": "string"
}
}
}
}
@@ -1560,7 +1846,7 @@
"application/json"
],
"tags": [
"Dashboard"
"User Dashboard"
],
"summary": "获取用户统计信息",
"operationId": "user-dashboard-stat",
@@ -1611,6 +1897,12 @@
}
]
}
},
"401": {
"description": "Unauthorized",
"schema": {
"type": "string"
}
}
}
}
@@ -1961,13 +2253,15 @@
"browser"
],
"type": "string",
"default": "plugin",
"x-enum-varnames": [
"LoginSourcePlugin",
"LoginSourceBrowser"
],
"description": "登录来源 plugin: 插件 browser: 浏览器; 默认为 plugin",
"name": "source",
"in": "query"
"in": "query",
"required": true
}
],
"responses": {
@@ -2002,7 +2296,7 @@
"application/json"
],
"tags": [
"User"
"User Manage"
],
"summary": "获取用户信息",
"operationId": "user-profile",
@@ -2018,12 +2312,18 @@
"type": "object",
"properties": {
"data": {
"type": "string"
"$ref": "#/definitions/domain.User"
}
}
}
]
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/web.Resp"
}
}
}
},
@@ -2036,10 +2336,10 @@
"application/json"
],
"tags": [
"User"
"User Manage"
],
"summary": "更新用户信息",
"operationId": "user-profile",
"operationId": "user-update-profile",
"parameters": [
{
"description": "param",
@@ -2069,6 +2369,12 @@
}
]
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/web.Resp"
}
}
}
}
@@ -3163,6 +3469,9 @@
},
"domain.LoginReq": {
"type": "object",
"required": [
"source"
],
"properties": {
"password": {
"description": "密码",
@@ -3174,6 +3483,7 @@
},
"source": {
"description": "登录来源 plugin: 插件 browser: 浏览器; 默认为 plugin",
"default": "plugin",
"allOf": [
{
"$ref": "#/definitions/consts.LoginSource"

View File

@@ -13,19 +13,20 @@ import (
type BillingUsecase interface {
ListChatRecord(ctx context.Context, req ListRecordReq) (*ListChatRecordResp, error)
ListCompletionRecord(ctx context.Context, req ListRecordReq) (*ListCompletionRecordResp, error)
CompletionInfo(ctx context.Context, id string) (*CompletionInfo, error)
ChatInfo(ctx context.Context, id string) (*ChatInfo, error)
CompletionInfo(ctx context.Context, id, userID string) (*CompletionInfo, error)
ChatInfo(ctx context.Context, id, userID string) (*ChatInfo, error)
}
type BillingRepo interface {
ListChatRecord(ctx context.Context, req ListRecordReq) (*ListChatRecordResp, error)
ListCompletionRecord(ctx context.Context, req ListRecordReq) (*ListCompletionRecordResp, error)
CompletionInfo(ctx context.Context, id string) (*CompletionInfo, error)
ChatInfo(ctx context.Context, id string) (*ChatInfo, error)
CompletionInfo(ctx context.Context, id, userID string) (*CompletionInfo, error)
ChatInfo(ctx context.Context, id, userID string) (*ChatInfo, error)
}
type ListRecordReq struct {
*web.Pagination
UserID string `json:"-"`
Author string `json:"author" query:"author"` // 作者
Language string `json:"language" query:"language"` // 语言
WorkMode string `json:"work_mode" query:"work_mode"` // 工作模式

View File

@@ -32,11 +32,11 @@ type OAuthUserInfo struct {
}
type OAuthSignUpOrInReq struct {
Source consts.LoginSource `json:"source"` // 登录来源 plugin: 插件 browser: 浏览器; 默认为 plugin
Platform consts.UserPlatform `json:"platform" query:"platform" validate:"required"` // 第三方平台 dingtalk
SessionID string `json:"session_id" query:"session_id"` // 会话ID
RedirectURL string `json:"redirect_url" query:"redirect_url"` // 登录成功后跳转的 URL
InviteCode string `json:"inviate_code" query:"inviate_code"` // 邀请码
Source consts.LoginSource `json:"source" query:"source" validate:"required" default:"plugin"` // 登录来源 plugin: 插件 browser: 浏览器; 默认为 plugin
Platform consts.UserPlatform `json:"platform" query:"platform" validate:"required"` // 第三方平台 dingtalk
SessionID string `json:"session_id" query:"session_id"` // 会话ID
RedirectURL string `json:"redirect_url" query:"redirect_url"` // 登录成功后跳转的 URL
InviteCode string `json:"inviate_code" query:"inviate_code"` // 邀请码
}
func (o OAuthSignUpOrInReq) OAuthKind() consts.OAuthKind {

View File

@@ -92,11 +92,11 @@ type VSCodeAuthInitResp struct {
}
type LoginReq struct {
Source consts.LoginSource `json:"source"` // 登录来源 plugin: 插件 browser: 浏览器; 默认为 plugin
SessionID string `json:"session_id"` // 会话Id插件登录时必填
Username string `json:"username"` // 用户名
Password string `json:"password"` // 密码
IP string `json:"-"` // IP地址
Source consts.LoginSource `json:"source" validate:"required" default:"plugin"` // 登录来源 plugin: 插件 browser: 浏览器; 默认为 plugin
SessionID string `json:"session_id"` // 会话Id插件登录时必填
Username string `json:"username"` // 用户名
Password string `json:"password"` // 密码
IP string `json:"-"` // IP地址
}
type AdminLoginReq struct {

View File

@@ -4,7 +4,7 @@ go 1.23.7
require (
entgo.io/ent v0.14.4
github.com/GoYoko/web v1.0.0
github.com/GoYoko/web v1.1.0
github.com/cloudwego/eino v0.3.51
github.com/cloudwego/eino-ext/components/model/openai v0.0.0-20250710065240-482d48888f25
github.com/golang-migrate/migrate/v4 v4.18.3

View File

@@ -8,8 +8,8 @@ github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/GoYoko/web v1.0.0 h1:kcNxz8BvpKavE0/iqatOmUeCXVghaoD5xYDiHDulVaE=
github.com/GoYoko/web v1.0.0/go.mod h1:DL9/gvuUG2jcBE1XUIY+9QBrrhdshzPEdxMCzR9jUHo=
github.com/GoYoko/web v1.1.0 h1:nIbtol5z0Y03d0nHsvGjv+W0fgmFRGUL8fzPN3kmrOY=
github.com/GoYoko/web v1.1.0/go.mod h1:DL9/gvuUG2jcBE1XUIY+9QBrrhdshzPEdxMCzR9jUHo=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo=

View File

@@ -84,7 +84,7 @@ func (h *BillingHandler) ListCompletionRecord(c *web.Context, req domain.ListRec
// @Success 200 {object} web.Resp{data=domain.CompletionInfo}
// @Router /api/v1/billing/completion/info [get]
func (h *BillingHandler) CompletionInfo(c *web.Context) error {
info, err := h.usecase.CompletionInfo(c.Request().Context(), c.QueryParam("id"))
info, err := h.usecase.CompletionInfo(c.Request().Context(), c.QueryParam("id"), "")
if err != nil {
return err
}
@@ -103,7 +103,7 @@ func (h *BillingHandler) CompletionInfo(c *web.Context) error {
// @Success 200 {object} web.Resp{data=domain.ChatInfo}
// @Router /api/v1/billing/chat/info [get]
func (h *BillingHandler) ChatInfo(c *web.Context) error {
info, err := h.usecase.ChatInfo(c.Request().Context(), c.QueryParam("id"))
info, err := h.usecase.ChatInfo(c.Request().Context(), c.QueryParam("id"), "")
if err != nil {
return err
}

View File

@@ -13,6 +13,7 @@ import (
"github.com/chaitin/MonkeyCode/backend/domain"
"github.com/chaitin/MonkeyCode/backend/pkg/cvt"
"github.com/chaitin/MonkeyCode/backend/pkg/entx"
"github.com/google/uuid"
)
type BillingRepo struct {
@@ -24,14 +25,21 @@ func NewBillingRepo(db *db.Client) domain.BillingRepo {
}
// ChatInfo implements domain.BillingRepo.
func (b *BillingRepo) ChatInfo(ctx context.Context, id string) (*domain.ChatInfo, error) {
record, err := b.db.Task.Query().
func (b *BillingRepo) ChatInfo(ctx context.Context, id, userID string) (*domain.ChatInfo, error) {
q := b.db.Task.Query().
WithTaskRecords(func(trq *db.TaskRecordQuery) {
trq.Order(taskrecord.ByCreatedAt(sql.OrderAsc()))
trq.Where(taskrecord.RoleNEQ(consts.ChatRoleSystem))
}).
Where(task.TaskID(id)).
First(ctx)
Where(task.TaskID(id))
if userID != "" {
uid, err := uuid.Parse(userID)
if err != nil {
return nil, err
}
q.Where(task.UserID(uid))
}
record, err := q.First(ctx)
if err != nil {
return nil, err
}
@@ -40,13 +48,20 @@ func (b *BillingRepo) ChatInfo(ctx context.Context, id string) (*domain.ChatInfo
}
// CompletionInfo implements domain.BillingRepo.
func (b *BillingRepo) CompletionInfo(ctx context.Context, id string) (*domain.CompletionInfo, error) {
record, err := b.db.Task.Query().
func (b *BillingRepo) CompletionInfo(ctx context.Context, id, userID string) (*domain.CompletionInfo, error) {
q := b.db.Task.Query().
WithTaskRecords(func(trq *db.TaskRecordQuery) {
trq.Order(taskrecord.ByCreatedAt(sql.OrderAsc()))
}).
Where(task.TaskID(id)).
First(ctx)
Where(task.TaskID(id))
if userID != "" {
uid, err := uuid.Parse(userID)
if err != nil {
return nil, err
}
q.Where(task.UserID(uid))
}
record, err := q.First(ctx)
if err != nil {
return nil, err
}
@@ -86,6 +101,12 @@ func filterTask(q *db.TaskQuery, req domain.ListRecordReq) {
q.Where(task.IsAccept(*req.IsAccept))
}
if req.UserID != "" {
if uid, err := uuid.Parse(req.UserID); err == nil {
q.Where(task.UserID(uid))
}
}
if req.Author != "" {
q.Where(task.HasUserWith(func(s *sql.Selector) {
s.Where(sql.Like(s.C(user.FieldUsername), "%"+req.Author+"%"))

View File

@@ -25,11 +25,11 @@ func (b *BillingUsecase) ListCompletionRecord(ctx context.Context, req domain.Li
}
// CompletionInfo implements domain.BillingUsecase.
func (b *BillingUsecase) CompletionInfo(ctx context.Context, id string) (*domain.CompletionInfo, error) {
return b.repo.CompletionInfo(ctx, id)
func (b *BillingUsecase) CompletionInfo(ctx context.Context, id, userID string) (*domain.CompletionInfo, error) {
return b.repo.CompletionInfo(ctx, id, userID)
}
// ChatInfo implements domain.BillingUsecase.
func (b *BillingUsecase) ChatInfo(ctx context.Context, id string) (*domain.ChatInfo, error) {
return b.repo.ChatInfo(ctx, id)
func (b *BillingUsecase) ChatInfo(ctx context.Context, id, userID string) (*domain.ChatInfo, error) {
return b.repo.ChatInfo(ctx, id, userID)
}

View File

@@ -35,6 +35,12 @@ func (a *ActiveMiddleware) Active(scope string) echo.MiddlewareFunc {
}
}
case "user":
if user := GetUser((c)); user != nil {
if err := a.redis.Set(context.Background(), fmt.Sprintf(consts.UserActiveKeyFmt, user.ID), time.Now().Unix(), 0).Err(); err != nil {
a.logger.With("error", err).ErrorContext(c.Request().Context(), "failed to set user active status in Redis")
}
}
case "apikey":
if apikey := GetApiKey(c); apikey != nil {
if err := a.redis.Set(context.Background(), fmt.Sprintf(consts.UserActiveKeyFmt, apikey.UserID), time.Now().Unix(), 0).Err(); err != nil {
a.logger.With("error", err).ErrorContext(c.Request().Context(), "failed to set user active status in Redis")

View File

@@ -57,9 +57,17 @@ func (m *AuthMiddleware) Auth() echo.MiddlewareFunc {
}
func GetAdmin(c echo.Context) *domain.AdminUser {
return c.Get(adminKey).(*domain.AdminUser)
i := c.Get(adminKey)
if i == nil {
return nil
}
return i.(*domain.AdminUser)
}
func GetUser(c echo.Context) *domain.User {
return c.Get(userKey).(*domain.User)
i := c.Get(userKey)
if i == nil {
return nil
}
return i.(*domain.User)
}

View File

@@ -53,5 +53,9 @@ func (p *ProxyMiddleware) Auth() echo.MiddlewareFunc {
}
func GetApiKey(c echo.Context) *domain.ApiKey {
return c.Get(ApiContextKey).(*domain.ApiKey)
i := c.Get(ApiContextKey)
if i == nil {
return nil
}
return i.(*domain.ApiKey)
}

View File

@@ -49,11 +49,11 @@ func NewV1Handler(
g := w.Group("/v1", middleware.Auth())
g.GET("/models", web.BaseHandler(h.ModelList))
g.POST("/completion/accept", web.BindHandler(h.AcceptCompletion), active.Active("user"))
g.POST("/report", web.BindHandler(h.Report), active.Active("user"))
g.POST("/chat/completions", web.BaseHandler(h.ChatCompletion), active.Active("user"))
g.POST("/completions", web.BaseHandler(h.Completions), active.Active("user"))
g.POST("/embeddings", web.BaseHandler(h.Embeddings), active.Active("user"))
g.POST("/completion/accept", web.BindHandler(h.AcceptCompletion), active.Active("apikey"))
g.POST("/report", web.BindHandler(h.Report), active.Active("apikey"))
g.POST("/chat/completions", web.BaseHandler(h.ChatCompletion), active.Active("apikey"))
g.POST("/completions", web.BaseHandler(h.Completions), active.Active("apikey"))
g.POST("/embeddings", web.BaseHandler(h.Embeddings), active.Active("apikey"))
return h
}

View File

@@ -9,7 +9,7 @@ import (
// UserStat 获取用户统计信息
//
// @Tags Dashboard
// @Tags User Dashboard
// @Summary 获取用户统计信息
// @Description 获取用户统计信息
// @ID user-dashboard-stat
@@ -17,6 +17,7 @@ import (
// @Produce json
// @Param filter query domain.StatisticsFilter true "筛选参数"
// @Success 200 {object} web.Resp{data=domain.UserStat}
// @Failure 401 {object} string
// @Router /api/v1/user/dashboard/stat [get]
func (h *UserHandler) UserStat(c *web.Context, req domain.StatisticsFilter) error {
req.UserID = middleware.GetUser(c).ID
@@ -29,7 +30,7 @@ func (h *UserHandler) UserStat(c *web.Context, req domain.StatisticsFilter) erro
// UserEvents 获取用户事件
//
// @Tags Dashboard
// @Tags User Dashboard
// @Summary 获取用户事件
// @Description 获取用户事件
// @ID user-dashboard-events
@@ -37,6 +38,7 @@ func (h *UserHandler) UserStat(c *web.Context, req domain.StatisticsFilter) erro
// @Produce json
// @Param filter query domain.StatisticsFilter true "筛选参数"
// @Success 200 {object} web.Resp{data=[]domain.UserEvent}
// @Failure 401 {object} string
// @Router /api/v1/user/dashboard/events [get]
func (h *UserHandler) UserEvents(c *web.Context) error {
userEvents, err := h.duse.UserEvents(c.Request().Context(), domain.StatisticsFilter{
@@ -52,13 +54,14 @@ func (h *UserHandler) UserEvents(c *web.Context) error {
// UserHeatmap 用户热力图
//
// @Tags Dashboard
// @Tags User Dashboard
// @Summary 用户热力图
// @Description 用户热力图
// @ID user-dashboard-heatmap
// @Accept json
// @Produce json
// @Success 200 {object} web.Resp{data=domain.UserHeatmapResp}
// @Failure 401 {object} string
// @Router /api/v1/user/dashboard/heatmap [get]
func (h *UserHandler) UserHeatmap(c *web.Context) error {
userID := middleware.GetUser(c).ID

View File

@@ -0,0 +1,91 @@
package v1
import (
"github.com/GoYoko/web"
"github.com/chaitin/MonkeyCode/backend/domain"
"github.com/chaitin/MonkeyCode/backend/internal/middleware"
)
// ListChatRecord 获取用户对话记录
//
// @Tags User Record
// @Summary 获取用户对话记录
// @Description 获取用户对话记录
// @ID user-list-chat-record
// @Accept json
// @Produce json
// @Param page query domain.ListRecordReq true "参数"
// @Success 200 {object} web.Resp{data=domain.ListChatRecordResp}
// @Failure 401 {object} string
// @Router /api/v1/user/chat/record [get]
func (h *UserHandler) ListChatRecord(c *web.Context, req domain.ListRecordReq) error {
req.Pagination = c.Page()
req.UserID = middleware.GetUser(c).ID
records, err := h.buse.ListChatRecord(c.Request().Context(), req)
if err != nil {
return err
}
return c.Success(records)
}
// ListCompletionRecord 获取补全记录
//
// @Tags User Record
// @Summary 获取补全记录
// @Description 获取补全记录
// @ID user-list-completion-record
// @Accept json
// @Produce json
// @Param page query domain.ListRecordReq true "参数"
// @Success 200 {object} web.Resp{data=domain.ListCompletionRecordResp}
// @Failure 401 {object} string
// @Router /api/v1/user/completion/record [get]
func (h *UserHandler) ListCompletionRecord(c *web.Context, req domain.ListRecordReq) error {
req.Pagination = c.Page()
req.UserID = middleware.GetUser(c).ID
records, err := h.buse.ListCompletionRecord(c.Request().Context(), req)
if err != nil {
return err
}
return c.Success(records)
}
// CompletionInfo 获取补全内容
//
// @Tags User Record
// @Summary 获取补全内容
// @Description 获取补全内容
// @ID user-completion-info
// @Accept json
// @Produce json
// @Param id query string true "补全记录ID"
// @Success 200 {object} web.Resp{data=domain.CompletionInfo}
// @Failure 401 {object} string
// @Router /api/v1/user/completion/info [get]
func (h *UserHandler) CompletionInfo(c *web.Context) error {
info, err := h.buse.CompletionInfo(c.Request().Context(), c.QueryParam("id"), middleware.GetUser(c).ID)
if err != nil {
return err
}
return c.Success(info)
}
// ChatInfo 获取对话内容
//
// @Tags User Record
// @Summary 获取对话内容
// @Description 获取对话内容
// @ID user-chat-info
// @Accept json
// @Produce json
// @Param id query string true "对话记录ID"
// @Success 200 {object} web.Resp{data=domain.ChatInfo}
// @Failure 401 {object} string
// @Router /api/v1/user/chat/info [get]
func (h *UserHandler) ChatInfo(c *web.Context) error {
info, err := h.buse.ChatInfo(c.Request().Context(), c.QueryParam("id"), middleware.GetUser(c).ID)
if err != nil {
return err
}
return c.Success(info)
}

View File

@@ -32,6 +32,7 @@ type UserHandler struct {
usecase domain.UserUsecase
euse domain.ExtensionUsecase
duse domain.DashboardUsecase
buse domain.BillingUsecase
session *session.Session
logger *slog.Logger
cfg *config.Config
@@ -45,6 +46,7 @@ func NewUserHandler(
usecase domain.UserUsecase,
euse domain.ExtensionUsecase,
duse domain.DashboardUsecase,
buse domain.BillingUsecase,
auth *middleware.AuthMiddleware,
active *middleware.ActiveMiddleware,
session *session.Session,
@@ -55,6 +57,7 @@ func NewUserHandler(
usecase: usecase,
euse: euse,
duse: duse,
buse: buse,
session: session,
logger: logger,
cfg: cfg,
@@ -103,6 +106,17 @@ func NewUserHandler(
d.GET("/events", web.BaseHandler(u.UserEvents))
d.GET("/heatmap", web.BaseHandler(u.UserHeatmap))
// user record
uc := w.Group("/api/v1/user/chat")
uc.Use(auth.UserAuth(), active.Active("user"))
uc.GET("/record", web.BindHandler(u.ListChatRecord, web.WithPage()))
uc.GET("/info", web.BaseHandler(u.ChatInfo))
cplt := w.Group("/api/v1/user/completion")
cplt.Use(auth.UserAuth(), active.Active("user"))
cplt.GET("/record", web.BindHandler(u.ListCompletionRecord, web.WithPage()))
cplt.GET("/info", web.BaseHandler(u.CompletionInfo))
return u
}
@@ -489,6 +503,7 @@ func (h *UserHandler) UpdateSetting(c *web.Context, req domain.UpdateSettingReq)
// @Success 200 {object} web.Resp{data=domain.OAuthURLResp}
// @Router /api/v1/user/oauth/signup-or-in [get]
func (h *UserHandler) OAuthSignUpOrIn(ctx *web.Context, req domain.OAuthSignUpOrInReq) error {
h.logger.With("req", req).DebugContext(ctx.Request().Context(), "OAuthSignUpOrIn")
resp, err := h.usecase.OAuthSignUpOrIn(ctx.Request().Context(), &req)
if err != nil {
return err
@@ -514,13 +529,14 @@ func (h *UserHandler) OAuthCallback(ctx *web.Context, req domain.OAuthCallbackRe
// Profile 获取用户信息
//
// @Tags User
// @Tags User Manage
// @Summary 获取用户信息
// @Description 获取用户信息
// @ID user-profile
// @Accept json
// @Produce json
// @Success 200 {object} web.Resp{data=string}
// @Success 200 {object} web.Resp{data=domain.User}
// @Failure 401 {object} web.Resp{}
// @Router /api/v1/user/profile [get]
func (h *UserHandler) Profile(ctx *web.Context) error {
return ctx.Success(middleware.GetUser(ctx))
@@ -528,14 +544,15 @@ func (h *UserHandler) Profile(ctx *web.Context) error {
// UpdateProfile 更新用户信息
//
// @Tags User
// @Tags User Manage
// @Summary 更新用户信息
// @Description 更新用户信息
// @ID user-profile
// @ID user-update-profile
// @Accept json
// @Produce json
// @Param req body domain.ProfileUpdateReq true "param"
// @Success 200 {object} web.Resp{data=domain.User}
// @Failure 401 {object} web.Resp{}
// @Router /api/v1/user/profile [put]
func (h *UserHandler) UpdateProfile(ctx *web.Context, req domain.ProfileUpdateReq) error {
req.UID = middleware.GetUser(ctx).ID

View File

@@ -239,7 +239,7 @@ func (u *UserUsecase) Login(ctx context.Context, req *domain.LoginReq) (*domain.
}, nil
}
return nil, fmt.Errorf("invalid login kind")
return nil, fmt.Errorf("invalid login source")
}
func (u *UserUsecase) getVSCodeURL(ctx context.Context, sessionID, apiKey, username string) (string, *domain.VSCodeSession, error) {
@@ -533,10 +533,10 @@ func (u *UserUsecase) OAuthCallback(c *web.Context, req *domain.OAuthCallbackReq
if session.Source == consts.LoginSourceBrowser {
resUser := cvt.From(user, &domain.User{})
u.logger.With("user", resUser).With("host", c.Request().Host).DebugContext(ctx, "save user session")
if _, err := u.session.Save(c, consts.UserSessionName, c.Request().Host, resUser); err != nil {
return err
}
return c.Success(resUser)
}
c.Redirect(http.StatusFound, redirect)
@@ -599,7 +599,7 @@ func (u *UserUsecase) WithOAuthCallback(ctx context.Context, req *domain.OAuthCa
}
}
u.logger.Debug("oauth callback", "redirect", redirect)
u.logger.With("session", session).Debug("oauth callback", "redirect", redirect)
return user, redirect, nil
}