commit 20ed44aa743de11d391659cd5522071772984334 Author: RichZDS <3388214266@qq.com> Date: Sun Jan 18 18:20:40 2026 +0800 init diff --git a/Backend/.gitattributes b/Backend/.gitattributes new file mode 100644 index 0000000..1fbf887 --- /dev/null +++ b/Backend/.gitattributes @@ -0,0 +1 @@ +* linguist-language=GO \ No newline at end of file diff --git a/Backend/.gitignore b/Backend/.gitignore new file mode 100644 index 0000000..18646b6 --- /dev/null +++ b/Backend/.gitignore @@ -0,0 +1,19 @@ +.buildpath +.hgignore.swp +.project +.orig +.swp +.idea/ +.settings/ +.vscode/ +bin/ +**/.DS_Store +gf +main +main.exe +output/ +manifest/output/ +temp/ +temp.yaml +bin +**/config/config.yaml \ No newline at end of file diff --git a/Backend/Makefile b/Backend/Makefile new file mode 100644 index 0000000..2a6e6e9 --- /dev/null +++ b/Backend/Makefile @@ -0,0 +1,7 @@ +ROOT_DIR = $(shell pwd) +NAMESPACE = "default" +DEPLOY_NAME = "template-single" +DOCKER_NAME = "template-single" + +include ./hack/hack-cli.mk +include ./hack/hack.mk \ No newline at end of file diff --git a/Backend/README.MD b/Backend/README.MD new file mode 100644 index 0000000..d36cedd --- /dev/null +++ b/Backend/README.MD @@ -0,0 +1,4 @@ +# GoFrame Template For SingleRepo + +Quick Start: +- https://goframe.org/quick \ No newline at end of file diff --git a/Backend/api/containment/containment.go b/Backend/api/containment/containment.go new file mode 100644 index 0000000..c9ac16e --- /dev/null +++ b/Backend/api/containment/containment.go @@ -0,0 +1,18 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package containment + +import ( + "context" + + "leke/api/containment/v1" +) + +type IContainmentV1 interface { + ContainmentRepoList(ctx context.Context, req *v1.ContainmentRepoListReq) (res *v1.ContainmentRepoListRes, err error) + ContainmentRepoView(ctx context.Context, req *v1.ContainmentRepoViewReq) (res *v1.ContainmentRepoViewRes, err error) + ContainmentRepoUpdate(ctx context.Context, req *v1.ContainmentRepoUpdateReq) (res *v1.ContainmentRepoUpdateRes, err error) + ContainmentRepoDelete(ctx context.Context, req *v1.ContainmentRepoDeleteReq) (res *v1.ContainmentRepoDeleteRes, err error) +} diff --git a/Backend/api/containment/v1/containment.go b/Backend/api/containment/v1/containment.go new file mode 100644 index 0000000..e2613f4 --- /dev/null +++ b/Backend/api/containment/v1/containment.go @@ -0,0 +1,71 @@ +package v1 + +import ( + "leke/internal/model" + "leke/internal/model/response" + + "github.com/gogf/gf/v2/frame/g" +) + +// 单条数据结构 +type ContainmentRepoInfo struct { + Id uint64 `json:"id" orm:"id" description:"primary key(大于0为修改,其他为新增)"` + TerminalId int `json:"terminalId" orm:"terminal_id" description:"terminal (散逸端)"` + Abnormal string `json:"abnormal" orm:"abnormal" description:"number of contained anomalies (收容异常的数量)"` + AnomalyName string `json:"anomalyName" orm:"anomaly_name" description:"name of the anomaly (异常体的名字)"` + AgentName string `json:"agentName" orm:"agent_name" description:"agent (特工)"` + RepoName string `json:"repoName" orm:"repo_name" description:"containment repository name or code (收容库)"` +} + +// List +type ContainmentRepoListReq struct { + response.PageResult + g.Meta `path:"/containment/list" method:"get" tags:"收容库" summary:"收容库列表"` + + // 查询条件(全部可选) + TerminalId int `json:"terminalId" orm:"terminal_id" description:"terminal (散逸端)"` + AnomalyName string `json:"anomalyName" orm:"anomaly_name" description:"anomaly name"` + AgentName string `json:"agentName" orm:"agent_name" description:"agent name"` + RepoName string `json:"repoName" orm:"repo_name" description:"containment repo name"` +} + +type ContainmentRepoListRes struct { + response.PageResult + List []*model.ContainmentRepo `json:"list"` +} + +// View +type ContainmentRepoViewReq struct { + g.Meta `path:"/containment/view" method:"get" tags:"收容库" summary:"收容库详情"` + + Id uint64 `json:"id" orm:"id" description:"primary key"` +} + +type ContainmentRepoViewRes struct { + ContainmentRepoInfo +} + +// Update(id>0 为修改,id<=0 或不传为新增) +type ContainmentRepoUpdateReq struct { + g.Meta `path:"/containment/update" method:"post" tags:"收容库" summary:"创建或更新收容库"` + + Id uint64 `json:"id" orm:"id" description:"primary key,大于0为修改,其他为新增"` + TerminalId int `json:"terminalId" orm:"terminal_id" description:"terminal (散逸端)"` + Abnormal string `json:"abnormal" orm:"abnormal" description:"number of contained anomalies"` + AnomalyName string `json:"anomalyName" orm:"anomaly_name" description:"anomaly name"` + AgentName string `json:"agentName" orm:"agent_name" description:"agent name"` + RepoName string `json:"repoName" orm:"repo_name" description:"containment repo name"` +} + +type ContainmentRepoUpdateRes struct { + Id uint64 `json:"id" orm:"id" description:"primary key"` +} + +// Delete +type ContainmentRepoDeleteReq struct { + g.Meta `path:"/containment/delete" method:"delete" tags:"收容库" summary:"收容库删除"` + + Id uint64 `json:"id" orm:"id" description:"primary key"` +} + +type ContainmentRepoDeleteRes struct{} diff --git a/Backend/api/department/department.go b/Backend/api/department/department.go new file mode 100644 index 0000000..d475020 --- /dev/null +++ b/Backend/api/department/department.go @@ -0,0 +1,19 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package department + +import ( + "context" + + "leke/api/department/v1" +) + +type IDepartmentV1 interface { + DepartmentList(ctx context.Context, req *v1.DepartmentListReq) (res *v1.DepartmentListRes, err error) + DepartmentView(ctx context.Context, req *v1.DepartmentViewReq) (res *v1.DepartmentViewRes, err error) + DepartmentCreate(ctx context.Context, req *v1.DepartmentCreateReq) (res *v1.DepartmentCreateRes, err error) + DepartmentUpdate(ctx context.Context, req *v1.DepartmentUpdateReq) (res *v1.DepartmentUpdateRes, err error) + DepartmentDelete(ctx context.Context, req *v1.DepartmentDeleteReq) (res *v1.DepartmentDeleteRes, err error) +} diff --git a/Backend/api/department/v1/department.go b/Backend/api/department/v1/department.go new file mode 100644 index 0000000..7912913 --- /dev/null +++ b/Backend/api/department/v1/department.go @@ -0,0 +1,90 @@ +package v1 + +import ( + "leke/internal/model/response" + + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" +) + +// Department 三角机构分部信息 +type Department struct { + Id uint64 `json:"id" description:"自增主键"` + UserId uint64 `json:"userId" description:"所属用户ID(对应 users.id)"` + BranchName string `json:"branchName" description:"分部名称"` + TerminalCount int `json:"terminalCount" description:"分部散逸端的数量"` + Weather string `json:"weather" description:"分部当前天气/气候描述"` + ManagerName string `json:"managerName" description:"分部经理名称"` + Location string `json:"location" description:"分部地址"` + CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"` + UpdatedAt *gtime.Time `json:"updatedAt" description:"更新时间"` +} + +// DepartmentListReq 部门列表请求参数 +type DepartmentListReq struct { + response.PageResult + g.Meta `path:"/department/list" method:"get" tags:"部门" summary:"部门列表"` + BranchName string `json:"branchName" description:"分部名称"` + ManagerName string `json:"managerName" description:"分部经理名称"` + UserId uint64 `json:"userId" description:"所属用户ID"` +} + +// DepartmentListRes 部门列表响应参数 +type DepartmentListRes struct { + response.PageResult + List []*Department `json:"list"` +} + +// DepartmentViewReq 部门详情请求参数 +type DepartmentViewReq struct { + g.Meta `path:"/department/view" method:"get" tags:"部门" summary:"部门详情"` + Id uint64 `json:"id" v:"required#部门ID不能为空"` +} + +// DepartmentViewRes 部门详情响应参数 +type DepartmentViewRes struct { + Department +} + +// DepartmentCreateReq 部门创建请求参数 +type DepartmentCreateReq struct { + g.Meta `path:"/department/create" method:"post" tags:"部门" summary:"创建部门"` + UserId uint64 `json:"userId" v:"required#所属用户ID不能为空"` + BranchName string `json:"branchName" v:"required#分部名称不能为空"` + TerminalCount int `json:"terminalCount" description:"分部散逸端的数量"` + Weather string `json:"weather" description:"分部当前天气/气候描述"` + ManagerName string `json:"managerName" description:"分部经理名称"` + Location string `json:"location" description:"分部地址"` +} + +// DepartmentCreateRes 部门创建响应参数 +type DepartmentCreateRes struct { + Id uint64 `json:"id"` +} + +// DepartmentUpdateReq 部门更新请求参数 +type DepartmentUpdateReq struct { + g.Meta `path:"/department/update" method:"put" tags:"部门" summary:"更新部门"` + Id uint64 `json:"id" v:"required#部门ID不能为空"` + UserId uint64 `json:"userId" v:"required#所属用户ID不能为空"` + BranchName string `json:"branchName" v:"required#分部名称不能为空"` + TerminalCount int `json:"terminalCount" description:"分部散逸端的数量"` + Location string `json:"location" description:"分部地址"` + Weather string `json:"weather" description:"分部当前天气/气候描述"` + ManagerName string `json:"managerName" description:"分部经理名称"` +} + +// DepartmentUpdateRes 部门更新响应参数 +type DepartmentUpdateRes struct { + Id uint64 `json:"id"` +} + +// DepartmentDeleteReq 部门删除请求参数 +type DepartmentDeleteReq struct { + g.Meta `path:"/department/delete" method:"delete" tags:"部门" summary:"删除部门"` + Id uint64 `json:"id" v:"required#部门ID不能为空"` +} + +// DepartmentDeleteRes 部门删除响应参数 +type DepartmentDeleteRes struct { +} diff --git a/Backend/api/forum/forum.if.go b/Backend/api/forum/forum.if.go new file mode 100644 index 0000000..e4ca6bc --- /dev/null +++ b/Backend/api/forum/forum.if.go @@ -0,0 +1,24 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package forum + +import ( + "context" + + "leke/api/forum/v1" +) + +type IForumV1 interface { + ForumCommentsCreate(ctx context.Context, req *v1.ForumCommentsCreateReq) (res *v1.ForumCommentsCreateRes, err error) + ForumCommentsUpdate(ctx context.Context, req *v1.ForumCommentsUpdateReq) (res *v1.ForumCommentsUpdateRes, err error) + ForumCommentsDelete(ctx context.Context, req *v1.ForumCommentsDeleteReq) (res *v1.ForumCommentsDeleteRes, err error) + ForumCommentsView(ctx context.Context, req *v1.ForumCommentsViewReq) (res *v1.ForumCommentsViewRes, err error) + ForumCommentsList(ctx context.Context, req *v1.ForumCommentsListReq) (res *v1.ForumCommentsListRes, err error) + ForumPostsCreate(ctx context.Context, req *v1.ForumPostsCreateReq) (res *v1.ForumPostsCreateRes, err error) + ForumPostsUpdate(ctx context.Context, req *v1.ForumPostsUpdateReq) (res *v1.ForumPostsUpdateRes, err error) + ForumPostsDelete(ctx context.Context, req *v1.ForumPostsDeleteReq) (res *v1.ForumPostsDeleteRes, err error) + ForumPostsView(ctx context.Context, req *v1.ForumPostsViewReq) (res *v1.ForumPostsViewRes, err error) + ForumPostsList(ctx context.Context, req *v1.ForumPostsListReq) (res *v1.ForumPostsListRes, err error) +} diff --git a/Backend/api/forum/v1/forum_comments.go b/Backend/api/forum/v1/forum_comments.go new file mode 100644 index 0000000..5f3c67c --- /dev/null +++ b/Backend/api/forum/v1/forum_comments.go @@ -0,0 +1,84 @@ +package v1 + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" + "leke/internal/model/response" +) + +// ForumCommentsCreateReq 创建评论请求 +type ForumCommentsCreateReq struct { + g.Meta `path:"/forum/comments/create" method:"post" tags:"论坛评论" summary:"创建评论"` + UserId uint64 `json:"userId" description:"评论发布者ID"` + PostId uint64 `json:"postId" description:"所属帖子ID"` + ParentId uint64 `json:"parentId" description:"父评论ID"` + Content string `json:"content" description:"评论内容"` + Status string `json:"status" description:"评论状态"` +} + +// ForumCommentsCreateRes 创建评论响应 +type ForumCommentsCreateRes struct { + Id uint64 `json:"id" description:"评论ID"` +} + +// ForumCommentsUpdateReq 更新评论请求 +type ForumCommentsUpdateReq struct { + g.Meta `path:"/forum/comments/update" method:"put" tags:"论坛评论" summary:"更新评论"` + Id uint64 `json:"id" description:"评论ID"` + UserId uint64 `json:"userId,omitempty" description:"评论发布者ID"` + PostId uint64 `json:"postId,omitempty" description:"所属帖子ID"` + ParentId uint64 `json:"parentId,omitempty" description:"父评论ID"` + Content string `json:"content,omitempty" description:"评论内容"` + Status string `json:"status,omitempty" description:"评论状态"` +} + +// ForumCommentsUpdateRes 更新评论响应 +type ForumCommentsUpdateRes struct { + Id uint64 `json:"id" description:"评论ID"` +} + +// ForumCommentsDeleteReq 删除评论请求 +type ForumCommentsDeleteReq struct { + g.Meta `path:"/forum/comments/delete" method:"delete" tags:"论坛评论" summary:"删除评论"` + Id uint64 `json:"id" description:"评论ID"` +} + +// ForumCommentsDeleteRes 删除评论响应 +type ForumCommentsDeleteRes struct { +} + +// ForumCommentsViewReq 查看评论请求 +type ForumCommentsViewReq struct { + g.Meta `path:"/forum/comments/view" method:"get" tags:"论坛评论" summary:"查看评论"` + Id uint64 `json:"id" description:"评论ID"` +} + +// ForumCommentsViewRes 查看评论响应 +type ForumCommentsViewRes struct { + Id uint64 `json:"id" description:"评论ID"` + UserId uint64 `json:"userId" description:"评论发布者ID"` + PostId uint64 `json:"postId" description:"所属帖子ID"` + ParentId uint64 `json:"parentId" description:"父评论ID"` + Content string `json:"content" description:"评论内容"` + Status string `json:"status" description:"评论状态"` + LikeCount uint `json:"likeCount" description:"点赞数"` + ReplyCount uint `json:"replyCount" description:"回复数"` + CreatedAt *gtime.Time `json:"createdAt" description:"评论创建时间"` + UpdatedAt *gtime.Time `json:"updatedAt" description:"评论更新时间"` +} + +// ForumCommentsListReq 评论列表请求 +type ForumCommentsListReq struct { + response.PageResult + g.Meta `path:"/forum/comments/list" method:"get" tags:"论坛评论" summary:"评论列表"` + UserId uint64 `json:"userId,omitempty" description:"评论发布者ID"` + PostId uint64 `json:"postId,omitempty" description:"所属帖子ID"` + ParentId uint64 `json:"parentId,omitempty" description:"父评论ID"` + Status string `json:"status,omitempty" description:"评论状态"` +} + +// ForumCommentsListRes 评论列表响应 +type ForumCommentsListRes struct { + response.PageResult + List []*ForumCommentsViewRes `json:"list" description:"评论列表"` +} diff --git a/Backend/api/forum/v1/forum_posts.go b/Backend/api/forum/v1/forum_posts.go new file mode 100644 index 0000000..92e81d3 --- /dev/null +++ b/Backend/api/forum/v1/forum_posts.go @@ -0,0 +1,84 @@ +package v1 + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" + "leke/internal/model/response" +) + +// ForumPostsCreateReq 创建帖子请求 +type ForumPostsCreateReq struct { + g.Meta `path:"/forum/posts/create" method:"post" tags:"论坛帖子" summary:"创建帖子"` + UserId uint64 `json:"userId" description:"发帖用户ID"` + Title string `json:"title" description:"帖子标题"` + Content string `json:"content" description:"帖子正文"` + CoverImage string `json:"coverImage" description:"帖子封面图URL"` + Status string `json:"status" description:"帖子状态"` +} + +// ForumPostsCreateRes 创建帖子响应 +type ForumPostsCreateRes struct { + Id uint64 `json:"id" description:"帖子ID"` +} + +// ForumPostsUpdateReq 更新帖子请求 +type ForumPostsUpdateReq struct { + g.Meta `path:"/forum/posts/update" method:"put" tags:"论坛帖子" summary:"更新帖子"` + Id uint64 `json:"id" description:"帖子ID"` + UserId uint64 `json:"userId,omitempty" description:"发帖用户ID"` + Title string `json:"title,omitempty" description:"帖子标题"` + Content string `json:"content,omitempty" description:"帖子正文"` + CoverImage string `json:"coverImage,omitempty" description:"帖子封面图URL"` + Status string `json:"status,omitempty" description:"帖子状态"` +} + +// ForumPostsUpdateRes 更新帖子响应 +type ForumPostsUpdateRes struct { + Id uint64 `json:"id" description:"帖子ID"` +} + +// ForumPostsDeleteReq 删除帖子请求 +type ForumPostsDeleteReq struct { + g.Meta `path:"/forum/posts/delete" method:"delete" tags:"论坛帖子" summary:"删除帖子"` + Id uint64 `json:"id" description:"帖子ID"` +} + +// ForumPostsDeleteRes 删除帖子响应 +type ForumPostsDeleteRes struct { +} + +// ForumPostsViewReq 查看帖子请求 +type ForumPostsViewReq struct { + g.Meta `path:"/forum/posts/view" method:"get" tags:"论坛帖子" summary:"查看帖子"` + Id uint64 `json:"id" description:"帖子ID"` +} + +// ForumPostsViewRes 查看帖子响应 +type ForumPostsViewRes struct { + Id uint64 `json:"id" description:"帖子ID"` + UserId uint64 `json:"userId" description:"发帖用户ID"` + Title string `json:"title" description:"帖子标题"` + Content string `json:"content" description:"帖子正文"` + CoverImage string `json:"coverImage" description:"帖子封面图URL"` + Status string `json:"status" description:"帖子状态"` + ViewCount uint `json:"viewCount" description:"浏览量"` + LikeCount uint `json:"likeCount" description:"点赞数"` + CommentCount uint `json:"commentCount" description:"评论数"` + CreatedAt *gtime.Time `json:"createdAt" description:"发帖时间"` + UpdatedAt *gtime.Time `json:"updatedAt" description:"更新时间"` +} + +// ForumPostsListReq 帖子列表请求 +type ForumPostsListReq struct { + response.PageResult + g.Meta `path:"/forum/posts/list" method:"get" tags:"论坛帖子" summary:"帖子列表"` + UserId uint64 `json:"userId,omitempty" description:"发帖用户ID"` + Title string `json:"title,omitempty" description:"帖子标题"` + Status string `json:"status,omitempty" description:"帖子状态"` +} + +// ForumPostsListRes 帖子列表响应 +type ForumPostsListRes struct { + response.PageResult + List []*ForumPostsViewRes `json:"list" description:"帖子列表"` +} diff --git a/Backend/api/login/login.go b/Backend/api/login/login.go new file mode 100644 index 0000000..fa50815 --- /dev/null +++ b/Backend/api/login/login.go @@ -0,0 +1,20 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package login + +import ( + "context" + + "leke/api/login/v1" +) + +type ILoginV1 interface { + Login(ctx context.Context, req *v1.LoginReq) (res *v1.LoginRes, err error) + Register(ctx context.Context, req *v1.RegisterReq) (res *v1.RegisterRes, err error) + Logout(ctx context.Context, req *v1.LogoutReq) (res *v1.LogoutRes, err error) + LoginByEmail(ctx context.Context, req *v1.LoginByEmailReq) (res *v1.LoginByEmailRes, err error) + RegisterByEmail(ctx context.Context, req *v1.RegisterByEmailReq) (res *v1.RegisterByEmailRes, err error) + SendVerificationCode(ctx context.Context, req *v1.SendVerificationCodeReq) (res *v1.SendVerificationCodeRes, err error) +} diff --git a/Backend/api/login/v1/login.go b/Backend/api/login/v1/login.go new file mode 100644 index 0000000..2ba22d5 --- /dev/null +++ b/Backend/api/login/v1/login.go @@ -0,0 +1,66 @@ +// LoginReq 登录请求 +package v1 + +import ( + "github.com/gogf/gf/v2/frame/g" +) + +type LoginReq struct { + g.Meta `path:"/login" method:"post" tags:"登录" summary:"用户登录"` + Account string `json:"account" v:"required#账号不能为空"` + Password string `json:"password" v:"required#密码不能为空"` +} + +type LoginRes struct { + Token string `json:"token" dc:"JWT token for authentication"` + Account string `json:"account"` +} + +type RegisterReq struct { + g.Meta `path:"/register" method:"post" tags:"注册" summary:"用户注册"` + Account string `json:"account" v:"required#账号不能为空"` + Password string `json:"password" v:"required#密码不能为空"` +} + +type RegisterRes struct { + Account string `json:"account"` +} + +type LogoutReq struct { + g.Meta `path:"/logout" method:"post" tags:"登出" summary:"用户登出"` +} +type LogoutRes struct { +} + +// 用邮箱登录(包含验证码) +type LoginByEmailReq struct { + g.Meta `path:"/login/email" method:"post" tags:"登录" summary:"用户邮箱登录"` + Email string `json:"email" v:"email#邮箱格式不正确"` + Code string `json:"code" v:"required#验证码不能为空"` +} + +type LoginByEmailRes struct { + Token string `json:"token" dc:"JWT token for authentication"` + Email string `json:"email"` +} + +// 通过邮箱注册 +type RegisterByEmailReq struct { + g.Meta `path:"/register/email" method:"post" tags:"注册" summary:"用户邮箱注册"` + Email string `json:"email" v:"email#邮箱格式不正确"` + Code string `json:"code" v:"required#验证码不能为空"` +} + +type RegisterByEmailRes struct { + Email string `json:"email"` +} + +// SendVerificationCodeReq 发送验证码 +type SendVerificationCodeReq struct { + g.Meta `path:"/email/send-code" method:"post" tags:"邮箱" summary:"发送验证码"` + Email string `json:"email" v:"email#邮箱格式不正确"` +} + +type SendVerificationCodeRes struct { + Success bool `json:"success"` +} diff --git a/Backend/api/room/room.go b/Backend/api/room/room.go new file mode 100644 index 0000000..9cc4a15 --- /dev/null +++ b/Backend/api/room/room.go @@ -0,0 +1,20 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package room + +import ( + "context" + + "leke/api/room/v1" +) + +type IRoomV1 interface { + RoomList(ctx context.Context, req *v1.RoomListReq) (res *v1.RoomListRes, err error) + RoomView(ctx context.Context, req *v1.RoomViewReq) (res *v1.RoomViewRes, err error) + RoomCreate(ctx context.Context, req *v1.RoomCreateReq) (res *v1.RoomCreateRes, err error) + RoomUpdate(ctx context.Context, req *v1.RoomUpdateReq) (res *v1.RoomUpdateRes, err error) + RoomDelete(ctx context.Context, req *v1.RoomDeleteReq) (res *v1.RoomDeleteRes, err error) + RoomJoin(ctx context.Context, req *v1.RoomJoinReq) (res *v1.RoomJoinRes, err error) +} diff --git a/Backend/api/room/v1/room.go b/Backend/api/room/v1/room.go new file mode 100644 index 0000000..d4df1f1 --- /dev/null +++ b/Backend/api/room/v1/room.go @@ -0,0 +1,91 @@ +package v1 + +import ( + "leke/internal/model" + "leke/internal/model/response" + + "github.com/gogf/gf/v2/frame/g" +) + +// RoomListReq 房间列表请求 +type RoomListReq struct { + response.PageResult + g.Meta `path:"/room/list" method:"get" tags:"房间" summary:"房间列表"` + RoomCode string `json:"room_code" orm:"room_code" description:"房间号"` + RoomName string `json:"room_name" orm:"room_name" description:"房间名称"` + HostId uint64 `json:"host_id" orm:"host_id" description:"主持人用户ID"` + Status int `json:"status" orm:"status" description:"房间状态:0未开始 1进行中 2已结束 3已关闭"` + SystemName string `json:"system_name" orm:"system_name" description:"规则系统,如COC、DND5E"` +} + +// RoomListRes 房间列表响应 +type RoomListRes struct { + response.PageResult + Rooms []*model.RoomParams `json:"rooms"` +} + +// RoomViewReq 房间详情请求 +type RoomViewReq struct { + g.Meta `path:"/room/view" method:"get" tags:"房间" summary:"房间详情"` + Id uint64 `json:"id" orm:"id" description:"房间ID"` + RoomCode string `json:"room_code" orm:"room_code" description:"房间号"` +} + +// RoomViewRes 房间详情响应 +type RoomViewRes struct { + model.RoomView +} + +// RoomCreateReq 创建房间请求 +type RoomCreateReq struct { + g.Meta `path:"/room/create" method:"post" tags:"房间" summary:"创建房间"` + + // 这里根据你实际的 room 表字段自己增删 + RoomCode string `json:"room_code" orm:"room_code" description:"房间号"` + RoomName string `json:"room_name" orm:"room_name" description:"房间名称"` + HostId uint64 `json:"host_id" orm:"host_id" description:"主持人用户ID"` + MaxPlayers uint `json:"max_players" orm:"max_players" description:"最大玩家人数"` + HasPassword int `json:"has_password" orm:"has_password" description:"是否有密码:0无 1有"` + RoomPassword string `json:"room_password" orm:"room_password" description:"房间密码(建议哈希)"` + IsPrivate int `json:"is_private" orm:"is_private" description:"是否私密房:0公开 1私密"` + SystemName string `json:"system_name" orm:"system_name" description:"规则系统,如COC、DND5E"` + ScenarioName string `json:"scenario_name" orm:"scenario_name" description:"模组/剧本名称"` + Description string `json:"description" orm:"description" description:"房间简介/招募说明"` +} + +// RoomCreateRes 创建房间响应 +type RoomCreateRes struct { + Id uint64 `json:"id" orm:"id" description:"房间ID"` + RoomCode string `json:"room_code" orm:"room_code" description:"房间号"` +} + +// RoomUpdateReq 更新房间请求 +type RoomUpdateReq struct { + g.Meta `path:"/room/update" method:"put" tags:"房间" summary:"更新房间"` + RoomCode string `json:"room_code" orm:"room_code" description:"房间号(作为更新定位字段)"` + model.RoomParams +} + +// RoomUpdateRes 更新房间响应 +type RoomUpdateRes struct { + Id uint64 `json:"id" orm:"id" description:"房间ID"` +} + +// RoomDeleteReq 删除房间请求 +type RoomDeleteReq struct { + g.Meta `path:"/room/delete" method:"delete" tags:"房间" summary:"删除房间"` + RoomCode string `json:"room_code" orm:"room_code" description:"房间号"` +} + +// RoomDeleteRes 删除房间响应 +type RoomDeleteRes struct{} + +type RoomJoinReq struct { + g.Meta `path:"/room/join" method:"post" tags:"房间" summary:"加入房间"` + RoomId uint64 `json:"roomId" description:"房间ID"` +} + +type RoomJoinRes struct { + Id uint64 `json:"id" description:"房间ID"` + RoomCode string `json:"roomCode" description:"房间号"` +} diff --git a/Backend/api/user/user.go b/Backend/api/user/user.go new file mode 100644 index 0000000..6741daf --- /dev/null +++ b/Backend/api/user/user.go @@ -0,0 +1,38 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package user + +import ( + "context" + + "leke/api/user/v1" +) + +type IUserV1 interface { + FansList(ctx context.Context, req *v1.FansListReq) (res *v1.FansListRes, err error) + FansView(ctx context.Context, req *v1.FansViewReq) (res *v1.FansViewRes, err error) + FansUpdate(ctx context.Context, req *v1.FansUpdateReq) (res *v1.FansUpdateRes, err error) + FansDelete(ctx context.Context, req *v1.FansDeleteReq) (res *v1.FansDeleteRes, err error) + FansCreate(ctx context.Context, req *v1.FansCreateReq) (res *v1.FansCreateRes, err error) + RoleCreate(ctx context.Context, req *v1.RoleCreateReq) (res *v1.RoleCreateRes, err error) + RoleUpdate(ctx context.Context, req *v1.RoleUpdateReq) (res *v1.RoleUpdateRes, err error) + RoleView(ctx context.Context, req *v1.RoleViewReq) (res *v1.RoleViewRes, err error) + RoleList(ctx context.Context, req *v1.RoleListReq) (res *v1.RoleListRes, err error) + RoleDelete(ctx context.Context, req *v1.RoleDeleteReq) (res *v1.RoleDeleteRes, err error) + RolePermissionCheck(ctx context.Context, req *v1.RolePermissionCheckReq) (res *v1.RolePermissionCheckRes, err error) + SubscribeList(ctx context.Context, req *v1.SubscribeListReq) (res *v1.SubscribeListRes, err error) + SubscribeView(ctx context.Context, req *v1.SubscribeViewReq) (res *v1.SubscribeViewRes, err error) + SubscribeUpdate(ctx context.Context, req *v1.SubscribeUpdateReq) (res *v1.SubscribeUpdateRes, err error) + SubscribeDelete(ctx context.Context, req *v1.SubscribeDeleteReq) (res *v1.SubscribeDeleteRes, err error) + SubscribeCreate(ctx context.Context, req *v1.SubscribeCreateReq) (res *v1.SubscribeCreateRes, err error) + TraceList(ctx context.Context, req *v1.TraceListReq) (res *v1.TraceListRes, err error) + TraceView(ctx context.Context, req *v1.TraceViewReq) (res *v1.TraceViewRes, err error) + TraceUpdate(ctx context.Context, req *v1.TraceUpdateReq) (res *v1.TraceUpdateRes, err error) + TraceReduce(ctx context.Context, req *v1.TraceReduceReq) (res *v1.TraceReduceRes, err error) + UserList(ctx context.Context, req *v1.UserListReq) (res *v1.UserListRes, err error) + UserView(ctx context.Context, req *v1.UserViewReq) (res *v1.UserViewRes, err error) + UserUpdate(ctx context.Context, req *v1.UserUpdateReq) (res *v1.UserUpdateRes, err error) + UserDelete(ctx context.Context, req *v1.UserDeleteReq) (res *v1.UserDeleteRes, err error) +} diff --git a/Backend/api/user/v1/fans.go b/Backend/api/user/v1/fans.go new file mode 100644 index 0000000..09a4315 --- /dev/null +++ b/Backend/api/user/v1/fans.go @@ -0,0 +1,80 @@ +package v1 + +import ( + "leke/internal/model/response" + + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" +) + +// FansListItem 粉丝列表项 +type FansListItem struct { + Id uint64 `json:"id" description:"自增主键"` + UserId uint64 `json:"userId" description:"用户ID(被关注者,本人)"` + FanId uint64 `json:"fanId" description:"粉丝用户ID"` + Status int `json:"status" description:"状态:1=粉丝 0=取关/无效"` + Remark string `json:"remark" description:"备注名(user_id 对 fan_id 的备注/分组)"` + CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"` + UpdatedAt *gtime.Time `json:"updatedAt" description:"更新时间"` +} + +// FansListReq 粉丝列表请求参数 +type FansListReq struct { + response.PageResult + g.Meta `path:"/fans/list" method:"get" tags:"粉丝" summary:"粉丝列表"` + UserId uint64 `json:"userId" v:"required#用户ID不能为空"` + Status int `json:"status" description:"状态:1=粉丝 0=取关/无效"` +} + +// FansListRes 粉丝列表响应参数 +type FansListRes struct { + response.PageResult + List []*FansListItem `json:"list"` +} + +// FansViewReq 粉丝详情请求参数 +type FansViewReq struct { + g.Meta `path:"/fans/view" method:"get" tags:"粉丝" summary:"粉丝详情"` + Id uint64 `json:"id" v:"required#粉丝关系ID不能为空"` +} + +// FansViewRes 粉丝详情响应参数 +type FansViewRes struct { + FansListItem +} + +// FansUpdateReq 粉丝更新请求参数 +type FansUpdateReq struct { + g.Meta `path:"/fans/update" method:"put" tags:"粉丝" summary:"更新粉丝"` + Id uint64 `json:"id" v:"required#粉丝关系ID不能为空"` + Status int `json:"status" description:"状态:1=粉丝 0=取关/无效"` + Remark string `json:"remark" description:"备注名(user_id 对 fan_id 的备注/分组)"` +} + +// FansUpdateRes 粉丝更新响应参数 +type FansUpdateRes struct { + Id uint64 `json:"id"` +} + +// FansDeleteReq 粉丝删除请求参数 +type FansDeleteReq struct { + g.Meta `path:"/fans/delete" method:"delete" tags:"粉丝" summary:"删除粉丝"` + Id uint64 `json:"id" v:"required#粉丝关系ID不能为空"` +} + +// FansDeleteRes 粉丝删除响应参数 +type FansDeleteRes struct { +} + +// FansCreateReq 粉丝创建请求参数 +type FansCreateReq struct { + g.Meta `path:"/fans/create" method:"post" tags:"粉丝" summary:"创建粉丝"` + UserId uint64 `json:"userId" v:"required#用户ID不能为空"` + FanId uint64 `json:"fanId" v:"required#粉丝用户ID不能为空"` + Remark string `json:"remark" description:"备注名(user_id 对 fan_id 的备注/分组)"` +} + +// FansCreateRes 粉丝创建响应参数 +type FansCreateRes struct { + Id uint64 `json:"id"` +} diff --git a/Backend/api/user/v1/role.go b/Backend/api/user/v1/role.go new file mode 100644 index 0000000..4c5c874 --- /dev/null +++ b/Backend/api/user/v1/role.go @@ -0,0 +1,113 @@ +package v1 + +import ( + "github.com/gogf/gf/v2/frame/g" + "leke/internal/model/response" +) + +type RoleCreateReq struct { + g.Meta `path:"/role/create" method:"post" tags:"角色" summary:"创建角色"` + UserId uint64 `json:"userId" v:"required" description:"所属用户ID"` + DepartmentId uint64 `json:"departmentId" description:"所属部门ID"` + AgentName string `json:"agentName" v:"required|length:1,50" description:"特工名字"` + CodeName string `json:"codeName" v:"required|length:1,50" description:"代号"` + Gender string `json:"gender" v:"required|in:male,female,other" description:"性别"` + ArcAbnormal string `json:"arcAbnormal" description:"ARC:异常"` + ArcReality string `json:"arcReality" description:"ARC:现实"` + ArcPosition string `json:"arcPosition" description:"ARC:职位"` +} + +type RoleCreateRes struct { + Id uint64 `json:"id" description:"角色ID"` +} + +type RoleUpdateReq struct { + g.Meta `path:"/role/update" method:"put" tags:"角色" summary:"更新角色"` + Id uint64 `json:"id" v:"required" description:"角色ID"` + DepartmentId uint64 `json:"departmentId" description:"所属部门ID"` + AgentName string `json:"agentName" v:"required|length:1,50" description:"特工名字"` + CodeName string `json:"codeName" v:"required|length:1,50" description:"代号"` + Gender string `json:"gender" v:"required|in:male,female,other" description:"性别"` + ArcAbnormal string `json:"arcAbnormal" description:"ARC:异常"` + ArcReality string `json:"arcReality" description:"ARC:现实"` + ArcPosition string `json:"arcPosition" description:"ARC:职位"` + Commendation uint `json:"commendation" description:"嘉奖次数"` + Reprimand uint `json:"reprimand" description:"申戒次数"` + BlueTrack uint `json:"blueTrack" description:"蓝轨(0-40)"` + YellowTrack uint `json:"yellowTrack" description:"黄轨(0-40)"` + RedTrack uint `json:"redTrack" description:"红轨(0-40)"` +} + +type RoleUpdateRes struct { + Id uint64 `json:"id" description:"角色ID"` +} + +type RoleViewReq struct { + g.Meta `path:"/role/view" method:"get" tags:"角色" summary:"查看角色"` + Id uint64 `json:"id" v:"required" description:"角色ID"` +} + +type RoleViewRes struct { + RoleViewParams `json:",inline"` +} + +type RoleViewParams struct { + Id uint64 `json:"id" description:"角色卡ID"` + UserId uint64 `json:"userId" description:"所属用户ID"` + DepartmentId uint64 `json:"departmentId" description:"所属部门ID"` + Commendation uint `json:"commendation" description:"嘉奖次数"` + Reprimand uint `json:"reprimand" description:"申戒次数"` + BlueTrack uint `json:"blueTrack" description:"蓝轨(0-40)"` + YellowTrack uint `json:"yellowTrack" description:"黄轨(0-40)"` + RedTrack uint `json:"redTrack" description:"红轨(0-40)"` + ArcAbnormal string `json:"arcAbnormal" description:"ARC:异常"` + ArcReality string `json:"arcReality" description:"ARC:现实"` + ArcPosition string `json:"arcPosition" description:"ARC:职位"` + AgentName string `json:"agentName" description:"特工名字"` + CodeName string `json:"codeName" description:"代号"` + Gender string `json:"gender" description:"性别"` + QaMeticulous uint `json:"qaMeticulous" description:"Meticulousness (0-100, QA)"` + QaDeception uint `json:"qaDeception" description:"Deception (0-100, QA)"` + QaVigor uint `json:"qaVigor" description:"Vigor / Drive (0-100, QA)"` + QaEmpathy uint `json:"qaEmpathy" description:"Empathy (0-100, QA)"` + QaInitiative uint `json:"qaInitiative" description:"Initiative (0-100, QA)"` + QaResilience uint `json:"qaResilience" description:"Resilience / Persistence (0-100, QA)"` + QaPresence uint `json:"qaPresence" description:"Presence / Charisma (0-100, QA)"` + QaProfessional uint `json:"qaProfessional" description:"Professionalism (0-100, QA)"` + QaDiscretion uint `json:"qaDiscretion" description:"Discretion / Low profile (0-100, QA)"` +} + +type RoleListReq struct { + g.Meta `path:"/role/list" method:"get" tags:"角色" summary:"角色列表"` + response.PageResult + UserId uint64 `json:"userId" description:"用户ID"` + DepartmentId uint64 `json:"departmentId" description:"部门ID"` +} + +type RoleListRes struct { + response.PageResult + List []*RoleViewParams `json:"list" description:"角色列表"` +} + +type RoleDeleteReq struct { + g.Meta `path:"/role/delete" method:"delete" tags:"角色" summary:"删除角色"` + Id uint64 `json:"id" v:"required" description:"角色ID"` +} + +type RoleDeleteRes struct { + Id uint64 `json:"id" description:"角色ID"` +} + +// RolePermissionCheckReq 权限查询请求 +type RolePermissionCheckReq struct { + g.Meta `path:"/role/permission/check" method:"post" tags:"角色" summary:"检查角色权限"` + RoleId uint64 `json:"roleId" v:"required" description:"角色ID"` + FileValue uint `json:"fileValue" v:"required|min:0|max:40" description:"文件需要的轨道值(0-40)"` + TrackType string `json:"trackType" v:"required|in:blue,yellow,red" description:"轨道类型:blue(蓝轨),yellow(黄轨),red(红轨)"` +} + +// RolePermissionCheckRes 权限查询响应 +type RolePermissionCheckRes struct { + Code int `json:"code" description:"响应码:333表示成功"` + Mes string `json:"mes" description:"响应消息"` +} diff --git a/Backend/api/user/v1/subscribe.go b/Backend/api/user/v1/subscribe.go new file mode 100644 index 0000000..dd3ebf7 --- /dev/null +++ b/Backend/api/user/v1/subscribe.go @@ -0,0 +1,81 @@ +package v1 + +import ( + "leke/internal/model/response" + + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" +) + +// SubscribeListItem 关注列表项 +type SubscribeListItem struct { + Id uint64 `json:"id" description:"自增主键"` + UserId uint64 `json:"userId" description:"用户ID(关注者,本人)"` + FollowId uint64 `json:"followId" description:"被关注的用户ID"` + Status int `json:"status" description:"状态:1=关注中 0=取消关注"` + Remark string `json:"remark" description:"备注名(user_id 对 follow_id 的备注)"` + CreatedAt *gtime.Time `json:"createdAt" description:"创建时间"` + UpdatedAt *gtime.Time `json:"updatedAt" description:"更新时间"` +} + +// SubscribeListReq 关注列表请求参数 +type SubscribeListReq struct { + response.PageResult + g.Meta `path:"/subscribe/list" method:"get" tags:"关注" summary:"关注列表"` + UserId uint64 `json:"userId" v:"required#用户ID不能为空"` + Status int `json:"status" description:"状态:1=关注中 0=取消关注"` + FollowId uint64 `json:"followId" description:"被关注的用户ID"` +} + +// SubscribeListRes 关注列表响应参数 +type SubscribeListRes struct { + response.PageResult + List []*SubscribeListItem `json:"list"` +} + +// SubscribeViewReq 关注详情请求参数 +type SubscribeViewReq struct { + g.Meta `path:"/subscribe/view" method:"get" tags:"关注" summary:"关注详情"` + Id uint64 `json:"id" v:"required#关注关系ID不能为空"` +} + +// SubscribeViewRes 关注详情响应参数 +type SubscribeViewRes struct { + SubscribeListItem +} + +// SubscribeUpdateReq 关注更新请求参数 +type SubscribeUpdateReq struct { + g.Meta `path:"/subscribe/update" method:"put" tags:"关注" summary:"更新关注"` + Id uint64 `json:"id" v:"required#关注关系ID不能为空"` + Status int `json:"status" description:"状态:1=关注中 0=取消关注"` + Remark string `json:"remark" description:"备注名(user_id 对 follow_id 的备注)"` +} + +// SubscribeUpdateRes 关注更新响应参数 +type SubscribeUpdateRes struct { + Id uint64 `json:"id"` +} + +// SubscribeDeleteReq 关注删除请求参数 +type SubscribeDeleteReq struct { + g.Meta `path:"/subscribe/delete" method:"delete" tags:"关注" summary:"删除关注"` + Id uint64 `json:"id" v:"required#关注关系ID不能为空"` +} + +// SubscribeDeleteRes 关注删除响应参数 +type SubscribeDeleteRes struct { +} + +// SubscribeCreateReq 关注创建请求参数 +type SubscribeCreateReq struct { + g.Meta `path:"/subscribe/create" method:"post" tags:"关注" summary:"创建关注"` + UserId uint64 `json:"userId" v:"required#用户ID不能为空"` + FollowId uint64 `json:"followId" v:"required#被关注用户ID不能为空"` + Remark string `json:"remark" description:"备注名(user_id 对 follow_id 的备注)"` +} + +// SubscribeCreateRes 关注创建响应参数 +type SubscribeCreateRes struct { + Id uint64 `json:"id"` +} diff --git a/Backend/api/user/v1/trace.go b/Backend/api/user/v1/trace.go new file mode 100644 index 0000000..f58886b --- /dev/null +++ b/Backend/api/user/v1/trace.go @@ -0,0 +1,80 @@ +package v1 + +import ( + "github.com/gogf/gf/v2/frame/g" + "leke/internal/model/response" +) + +// TraceListReq 查询轨迹列表请求参数 +type TraceListReq struct { + response.PageResult + g.Meta `path:"/trace/list" method:"get" tags:"轨迹" summary:"轨迹列表"` + UserId uint64 `json:"userId" description:"用户ID"` + RoleId uint64 `json:"roleId" description:"角色ID"` + DepartmentId uint64 `json:"departmentId" description:"部门ID"` + RedTrace int `json:"redTrace" description:"红轨"` + YellowTrace int `json:"yellowTrace" description:"黄轨"` + BlueTrace int `json:"blueTrace" description:"蓝轨"` +} + +// TraceListRes 查询轨迹列表响应数据 +type TraceListRes struct { + response.PageResult + List []*TraceViewParams `json:"list"` +} + +// TraceViewParams 轨迹视图参数 +type TraceViewParams struct { + UserId uint64 `json:"userId" description:"用户ID"` + RoleId uint64 `json:"roleId" description:"角色ID"` + DepartmentId uint64 `json:"departmentId" description:"部门ID"` + RedTrace int `json:"redTrace" description:"红轨"` + YellowTrace int `json:"yellowTrace" description:"黄轨"` + BlueTrace int `json:"blueTrace" description:"蓝轨"` +} + +// TraceViewReq 查看轨迹详情请求参数 +type TraceViewReq struct { + g.Meta `path:"/trace/view" method:"get" tags:"轨迹" summary:"轨迹详情"` + RoleId uint64 `json:"roleId" description:"角色ID"` + RedTrace int `json:"redTrace" description:"红轨"` + YellowTrace int `json:"yellowTrace" description:"黄轨"` + BlueTrace int `json:"blueTrace" description:"蓝轨"` +} + +// TraceViewRes 查看轨迹详情响应数据 +type TraceViewRes struct { + TraceViewParams +} + +// TraceUpdateReq 更新轨迹请求参数 +type TraceUpdateReq struct { + g.Meta `path:"/trace/update" method:"put" tags:"轨迹" summary:"更新轨迹"` + UserId uint64 `json:"userId"` + RoleId uint64 `json:"roleId" description:"角色ID"` + DepartmentId uint64 `json:"departmentId"` + RedTrace int `json:"redTrace" description:"红轨"` + YellowTrace int `json:"yellowTrace" description:"黄轨"` + BlueTrace int `json:"blueTrace" description:"蓝轨"` +} + +// TraceUpdateRes 更新轨迹响应数据 +type TraceUpdateRes struct { + RoleId uint64 `json:"roleId" description:"角色ID"` + RedTrace int `json:"redTrace" description:"红轨"` + YellowTrace int `json:"yellowTrace" description:"黄轨"` + BlueTrace int `json:"blueTrace" description:"蓝轨"` +} + +// TraceReduceReq 删除轨迹请求参数 +type TraceReduceReq struct { + g.Meta `path:"/trace/Reduce" method:"delete" tags:"轨迹" summary:"删除轨迹"` + RoleId uint64 `json:"roleId" description:"角色ID"` + RedTrace int `json:"redTrace" description:"红轨"` + YellowTrace int `json:"yellowTrace" description:"黄轨"` + BlueTrace int `json:"blueTrace" description:"蓝轨"` +} + +// TraceReduceRes 删除轨迹响应数据 +type TraceReduceRes struct { +} diff --git a/Backend/api/user/v1/user.go b/Backend/api/user/v1/user.go new file mode 100644 index 0000000..209fe34 --- /dev/null +++ b/Backend/api/user/v1/user.go @@ -0,0 +1,44 @@ +package v1 + +import ( + "leke/internal/model" + "leke/internal/model/response" + + "github.com/gogf/gf/v2/frame/g" +) + +type UserListReq struct { + response.PageResult + g.Meta `path:"/user/list" method:"get" tags:"用户" summary:"用户列表"` + Account string `json:"account" orm:"account" description:"账号"` + Nickname string `json:"nickname" orm:"nickname" description:"昵称"` +} + +type UserListRes struct { + response.PageResult + Users []*model.UserViewParams +} + +type UserViewReq struct { + g.Meta `path:"/user/view" method:"get" tags:"用户" summary:"用户详情"` + Account string `json:"account" orm:"account" description:"账号"` + Nickname string `json:"nickname" orm:"nickname" description:"昵称"` + Id uint64 `json:"id" orm:"id" description:"用户ID"` +} +type UserViewRes struct { + model.UserViewParams +} +type UserUpdateReq struct { + g.Meta `path:"/user/update" method:"put" tags:"用户" summary:"更新用户"` + Account string `json:"account" orm:"account" description:"账号"` + model.User +} +type UserUpdateRes struct { + Id uint64 `json:"id" orm:"id" description:"用户ID"` +} +type UserDeleteReq struct { + g.Meta `path:"/user/delete" method:"delete" tags:"用户" summary:"删除用户"` + Account string `json:"account" orm:"account" description:"账号"` +} +type UserDeleteRes struct { +} diff --git a/Backend/go.mod b/Backend/go.mod new file mode 100644 index 0000000..07a7ac0 --- /dev/null +++ b/Backend/go.mod @@ -0,0 +1,45 @@ +module leke + +go 1.24.0 + +require ( + github.com/gogf/gf/contrib/drivers/mysql/v2 v2.9.5 + github.com/gogf/gf/v2 v2.9.5 + github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible + github.com/patrickmn/go-cache v2.1.0+incompatible +) + +require ( + github.com/BurntSushi/toml v1.5.0 // indirect + github.com/clbanning/mxj/v2 v2.7.0 // indirect + github.com/clipperhouse/displaywidth v0.6.0 // indirect + github.com/clipperhouse/stringish v0.1.1 // indirect + github.com/clipperhouse/uax29/v2 v2.3.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/fatih/color v1.18.0 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-sql-driver/mysql v1.7.1 // indirect + github.com/golang-jwt/jwt/v5 v5.3.0 + github.com/google/uuid v1.6.0 // indirect + github.com/gorilla/websocket v1.5.3 + github.com/grokify/html-strip-tags-go v0.1.0 // indirect + github.com/magiconair/properties v1.8.10 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.19 // indirect + github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect + github.com/olekukonko/errors v1.1.0 // indirect + github.com/olekukonko/ll v0.1.3 // indirect + github.com/olekukonko/tablewriter v1.1.1 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/otel v1.38.0 // indirect + go.opentelemetry.io/otel/metric v1.38.0 // indirect + go.opentelemetry.io/otel/sdk v1.38.0 // indirect + go.opentelemetry.io/otel/trace v1.38.0 // indirect + golang.org/x/net v0.47.0 // indirect + golang.org/x/sys v0.38.0 // indirect + golang.org/x/text v0.31.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/Backend/go.sum b/Backend/go.sum new file mode 100644 index 0000000..b64a0e6 --- /dev/null +++ b/Backend/go.sum @@ -0,0 +1,95 @@ +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/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME= +github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= +github.com/clipperhouse/displaywidth v0.6.0 h1:k32vueaksef9WIKCNcoqRNyKbyvkvkysNYnAWz2fN4s= +github.com/clipperhouse/displaywidth v0.6.0/go.mod h1:R+kHuzaYWFkTm7xoMmK1lFydbci4X2CicfbGstSGg0o= +github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs= +github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA= +github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4= +github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= +github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/gogf/gf/contrib/drivers/mysql/v2 v2.9.5 h1:0+ZBYhi4sqwxXwL+hIBpp06a7G4m5nmjskQ3NNb8qYc= +github.com/gogf/gf/contrib/drivers/mysql/v2 v2.9.5/go.mod h1:vyB7J/uJcLCrHD5lfFBzxhEEMkePIRzfhd33EcsuLa0= +github.com/gogf/gf/v2 v2.9.5 h1:1scfOdHbMP854oQaiLejl+eL+c4xfuvtWmmZiDJxbKs= +github.com/gogf/gf/v2 v2.9.5/go.mod h1:VUb5eyJKpvW77O/dXsbbLNO/Kjrg0UycIiq0lRiBjjo= +github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= +github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +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/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grokify/html-strip-tags-go v0.1.0 h1:03UrQLjAny8xci+R+qjCce/MYnpNXCtgzltlQbOBae4= +github.com/grokify/html-strip-tags-go v0.1.0/go.mod h1:ZdzgfHEzAfz9X6Xe5eBLVblWIxXfYSQ40S/VKrAOGpc= +github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible h1:jdpOPRN1zP63Td1hDQbZW73xKmzDvZHzVdNYxhnTMDA= +github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible/go.mod h1:1c7szIrayyPPB/987hsnvNzLushdWf4o/79s3P08L8A= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= +github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw= +github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= +github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 h1:zrbMGy9YXpIeTnGj4EljqMiZsIcE09mmF8XsD5AYOJc= +github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6/go.mod h1:rEKTHC9roVVicUIfZK7DYrdIoM0EOr8mK1Hj5s3JjH0= +github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM= +github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y= +github.com/olekukonko/ll v0.1.3 h1:sV2jrhQGq5B3W0nENUISCR6azIPf7UBUpVq0x/y70Fg= +github.com/olekukonko/ll v0.1.3/go.mod h1:b52bVQRRPObe+yyBl0TxNfhesL0nedD4Cht0/zx55Ew= +github.com/olekukonko/tablewriter v1.1.1 h1:b3reP6GCfrHwmKkYwNRFh2rxidGHcT6cgxj/sHiDDx0= +github.com/olekukonko/tablewriter v1.1.1/go.mod h1:De/bIcTF+gpBDB3Alv3fEsZA+9unTsSzAg/ZGADCtn4= +github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= +github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= +go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= +go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= +go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= +go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= +go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= +go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= +go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= +go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= +golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/Backend/hack/config.yaml b/Backend/hack/config.yaml new file mode 100644 index 0000000..5ba5afb --- /dev/null +++ b/Backend/hack/config.yaml @@ -0,0 +1,13 @@ +# CLI tool, only in development environment. +# https://goframe.org/docs/cli +gfcli: + gen: + dao: + - link: "mysql:root:123456@tcp(127.0.0.1:3306)/fitness_app" + table: "forum_posts,forum_comments" + descriptionTag: true + jsonCase: "CamelLower" # 生成json字段时,字段名使用驼峰命名 + docker: + build: "-a amd64 -s linux -p temp -ew" + tagPrefixes: + - my.image.pub/my-app diff --git a/Backend/hack/hack-cli.mk b/Backend/hack/hack-cli.mk new file mode 100644 index 0000000..f4e2ad2 --- /dev/null +++ b/Backend/hack/hack-cli.mk @@ -0,0 +1,20 @@ + +# Install/Update to the latest CLI tool. +.PHONY: cli +cli: + @set -e; \ + wget -O gf \ + https://github.com/gogf/gf/releases/latest/download/gf_$(shell go env GOOS)_$(shell go env GOARCH) && \ + chmod +x gf && \ + ./gf install -y && \ + rm ./gf + + +# Check and install CLI tool. +.PHONY: cli.install +cli.install: + @set -e; \ + gf -v > /dev/null 2>&1 || if [[ "$?" -ne "0" ]]; then \ + echo "GoFame CLI is not installed, start proceeding auto installation..."; \ + make cli; \ + fi; \ No newline at end of file diff --git a/Backend/hack/hack.mk b/Backend/hack/hack.mk new file mode 100644 index 0000000..2f68179 --- /dev/null +++ b/Backend/hack/hack.mk @@ -0,0 +1,75 @@ +.DEFAULT_GOAL := build + +# Update GoFrame and its CLI to latest stable version. +.PHONY: up +up: cli.install + @gf up -a + +# Build binary using configuration from hack/config.yaml. +.PHONY: build +build: cli.install + @gf build -ew + +# Parse api and generate controller/sdk. +.PHONY: ctrl +ctrl: cli.install + @gf gen ctrl + +# Generate Go files for DAO/DO/Entity. +.PHONY: dao +dao: cli.install + @gf gen dao + +# Parse current project go files and generate enums go file. +.PHONY: enums +enums: cli.install + @gf gen enums + +# Generate Go files for Service. +.PHONY: service +service: cli.install + @gf gen service + + +# Build docker image. +.PHONY: image +image: cli.install + $(eval _TAG = $(shell git rev-parse --short HEAD)) +ifneq (, $(shell git status --porcelain 2>/dev/null)) + $(eval _TAG = $(_TAG).dirty) +endif + $(eval _TAG = $(if ${TAG}, ${TAG}, $(_TAG))) + $(eval _PUSH = $(if ${PUSH}, ${PUSH}, )) + @gf docker ${_PUSH} -tn $(DOCKER_NAME):${_TAG}; + + +# Build docker image and automatically push to docker repo. +.PHONY: image.push +image.push: cli.install + @make image PUSH=-p; + + +# Deploy image and yaml to current kubectl environment. +.PHONY: deploy +deploy: cli.install + $(eval _TAG = $(if ${TAG}, ${TAG}, develop)) + + @set -e; \ + mkdir -p $(ROOT_DIR)/temp/kustomize;\ + cd $(ROOT_DIR)/manifest/deploy/kustomize/overlays/${_ENV};\ + kustomize build > $(ROOT_DIR)/temp/kustomize.yaml;\ + kubectl apply -f $(ROOT_DIR)/temp/kustomize.yaml; \ + if [ $(DEPLOY_NAME) != "" ]; then \ + kubectl patch -n $(NAMESPACE) deployment/$(DEPLOY_NAME) -p "{\"spec\":{\"template\":{\"metadata\":{\"labels\":{\"date\":\"$(shell date +%s)\"}}}}}"; \ + fi; + + +# Parsing protobuf files and generating go files. +.PHONY: pb +pb: cli.install + @gf gen pb + +# Generate protobuf files for database tables. +.PHONY: pbentity +pbentity: cli.install + @gf gen pbentity \ No newline at end of file diff --git a/Backend/internal/cmd/cmd.go b/Backend/internal/cmd/cmd.go new file mode 100644 index 0000000..b983c8c --- /dev/null +++ b/Backend/internal/cmd/cmd.go @@ -0,0 +1,54 @@ +package cmd + +import ( + "context" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/net/ghttp" + "github.com/gogf/gf/v2/os/gcmd" + "leke/internal/controller" + "leke/internal/controller/websocket" +) + +var ( + Main = gcmd.Command{ + Name: "main", + Usage: "main", + Brief: "start http server", + Func: func(ctx context.Context, parser *gcmd.Parser) (err error) { + s := g.Server() + + // ... existing code ... + // CORS 中间件 + s.Use(func(r *ghttp.Request) { + r.Response.CORS(ghttp.CORSOptions{ + AllowOrigin: "http://localhost:3000,http://localhost:8080", + AllowMethods: "GET,POST,PUT,DELETE,OPTIONS", + AllowHeaders: "Content-Type,Authorization,X-Requested-With", + AllowCredentials: "true", + MaxAge: 3600, + }) + if r.Method == "OPTIONS" { + r.Response.WriteStatusExit(200) + return + } + r.Middleware.Next() + }) + // ... existing code ... + + // 注册 API 路由组 + s.Group("/api", func(group *ghttp.RouterGroup) { + group.Middleware(ghttp.MiddlewareHandlerResponse) + controller.RegisterControllers(group) + }) + + // 注册 WebSocket 聊天室路由(不在 /api 组下,因为 WebSocket 不需要中间件) + s.BindHandler("/ws/chat", websocket.HandleChatConnections) + + // 配置静态文件服务(用于提供 HTML 客户端页面) + s.SetServerRoot("resource/public") + + s.Run() + return nil + }, + } +) diff --git a/Backend/internal/consts/consts.go b/Backend/internal/consts/consts.go new file mode 100644 index 0000000..cd9d098 --- /dev/null +++ b/Backend/internal/consts/consts.go @@ -0,0 +1,5 @@ +package consts + +// 加盐 +const Salt = "leke" +const qa_empathy, qa_presence, qa_initiative, qa_vigor, qa_tenacity = "empathy", "presence", "initiative", "vigor", "tenacity" diff --git a/Backend/internal/controller/RegisterController.go b/Backend/internal/controller/RegisterController.go new file mode 100644 index 0000000..93745f4 --- /dev/null +++ b/Backend/internal/controller/RegisterController.go @@ -0,0 +1,36 @@ +package controller + +import ( + "leke/internal/controller/containment" + "leke/internal/controller/department" + "leke/internal/controller/forum" + "leke/internal/controller/login" + "leke/internal/controller/room" + "leke/internal/controller/user" + "leke/internal/middleware" + + "github.com/gogf/gf/v2/net/ghttp" +) + +// RegisterControllers 将所有控制器绑定到路由组 +func RegisterControllers(group *ghttp.RouterGroup) { + // 登录相关接口不需要JWT验证 + group.Group("/", func(g *ghttp.RouterGroup) { + g.Bind( + login.NewV1(), + ) + }) + + // 其他需要JWT验证的接口 + group.Group("/", func(g *ghttp.RouterGroup) { + g.Middleware(middleware.JWTAuth) + g.Bind( + user.NewV1(), + user.NewRoleV1(), + department.NewV1(), + containment.NewV1(), + room.NewV1(), + forum.NewV1(), + ) + }) +} diff --git a/Backend/internal/controller/containment/containment_v1_containment.go b/Backend/internal/controller/containment/containment_v1_containment.go new file mode 100644 index 0000000..923c350 --- /dev/null +++ b/Backend/internal/controller/containment/containment_v1_containment.go @@ -0,0 +1,72 @@ +package containment + +import ( + "context" + "leke/api/containment" + + "leke/api/containment/v1" + "leke/internal/service" +) + +// ControllerV1 控制器结构体 +type ControllerV1 struct{} + +// NewV1 创建一个新的v1版本控制器实例 +// +// 返回: +// - containment.IContainmentV1: v1版本控制器接口实现 +func NewV1() containment.IContainmentV1 { + return &ControllerV1{} +} + +// ContainmentRepoList 获取收容库列表 +// +// 参数: +// - ctx context.Context: 上下文信息 +// - req *v1.ContainmentRepoListReq: 收容库列表请求参数 +// +// 返回: +// - res *v1.ContainmentRepoListRes: 收容库列表响应结果 +// - err error: 错误信息 +func (c *ControllerV1) ContainmentRepoList(ctx context.Context, req *v1.ContainmentRepoListReq) (res *v1.ContainmentRepoListRes, err error) { + return service.Containment().ContainmentRepoList(ctx, req) +} + +// ContainmentRepoView 查看收容库详情 +// +// 参数: +// - ctx context.Context: 上下文信息 +// - req *v1.ContainmentRepoViewReq: 收容库详情请求参数 +// +// 返回: +// - res *v1.ContainmentRepoViewRes: 收容库详情响应结果 +// - err error: 错误信息 +func (c *ControllerV1) ContainmentRepoView(ctx context.Context, req *v1.ContainmentRepoViewReq) (res *v1.ContainmentRepoViewRes, err error) { + return service.Containment().ContainmentRepoView(ctx, req) +} + +// ContainmentRepoUpdate 更新收容库信息 +// +// 参数: +// - ctx context.Context: 上下文信息 +// - req *v1.ContainmentRepoUpdateReq: 收容库更新请求参数 +// +// 返回: +// - res *v1.ContainmentRepoUpdateRes: 收容库更新响应结果 +// - err error: 错误信息 +func (c *ControllerV1) ContainmentRepoUpdate(ctx context.Context, req *v1.ContainmentRepoUpdateReq) (res *v1.ContainmentRepoUpdateRes, err error) { + return service.Containment().ContainmentRepoUpdate(ctx, req) +} + +// ContainmentRepoDelete 删除收容库 +// +// 参数: +// - ctx context.Context: 上下文信息 +// - req *v1.ContainmentRepoDeleteReq: 收容库删除请求参数 +// +// 返回: +// - res *v1.ContainmentRepoDeleteRes: 收容库删除响应结果 +// - err error: 错误信息 +func (c *ControllerV1) ContainmentRepoDelete(ctx context.Context, req *v1.ContainmentRepoDeleteReq) (res *v1.ContainmentRepoDeleteRes, err error) { + return service.Containment().ContainmentRepoDelete(ctx, req) +} diff --git a/Backend/internal/controller/department/department_v1_department.go b/Backend/internal/controller/department/department_v1_department.go new file mode 100644 index 0000000..c3c0da8 --- /dev/null +++ b/Backend/internal/controller/department/department_v1_department.go @@ -0,0 +1,35 @@ +package department + +import ( + "context" + + "leke/api/department" + v1 "leke/api/department/v1" + "leke/internal/service" +) + +type ControllerV1 struct{} + +func NewV1() department.IDepartmentV1 { + return &ControllerV1{} +} + +func (c *ControllerV1) DepartmentList(ctx context.Context, req *v1.DepartmentListReq) (res *v1.DepartmentListRes, err error) { + return service.Department().DepartmentList(ctx, req) +} + +func (c *ControllerV1) DepartmentView(ctx context.Context, req *v1.DepartmentViewReq) (res *v1.DepartmentViewRes, err error) { + return service.Department().DepartmentView(ctx, req) +} + +func (c *ControllerV1) DepartmentCreate(ctx context.Context, req *v1.DepartmentCreateReq) (res *v1.DepartmentCreateRes, err error) { + return service.Department().DepartmentCreate(ctx, req) +} + +func (c *ControllerV1) DepartmentUpdate(ctx context.Context, req *v1.DepartmentUpdateReq) (res *v1.DepartmentUpdateRes, err error) { + return service.Department().DepartmentUpdate(ctx, req) +} + +func (c *ControllerV1) DepartmentDelete(ctx context.Context, req *v1.DepartmentDeleteReq) (res *v1.DepartmentDeleteRes, err error) { + return service.Department().DepartmentDelete(ctx, req) +} \ No newline at end of file diff --git a/Backend/internal/controller/forum/forum_v1_forum_comments.go b/Backend/internal/controller/forum/forum_v1_forum_comments.go new file mode 100644 index 0000000..e3f5f29 --- /dev/null +++ b/Backend/internal/controller/forum/forum_v1_forum_comments.go @@ -0,0 +1,32 @@ +package forum + +import ( + "context" + v1 "leke/api/forum/v1" + "leke/internal/service" +) + +// ForumCommentsCreate 创建评论 +func (c *ControllerV1) ForumCommentsCreate(ctx context.Context, req *v1.ForumCommentsCreateReq) (res *v1.ForumCommentsCreateRes, err error) { + return service.ForumComments().Create(ctx, req) +} + +// ForumCommentsUpdate 更新评论 +func (c *ControllerV1) ForumCommentsUpdate(ctx context.Context, req *v1.ForumCommentsUpdateReq) (res *v1.ForumCommentsUpdateRes, err error) { + return service.ForumComments().Update(ctx, req) +} + +// ForumCommentsDelete 删除评论 +func (c *ControllerV1) ForumCommentsDelete(ctx context.Context, req *v1.ForumCommentsDeleteReq) (res *v1.ForumCommentsDeleteRes, err error) { + return service.ForumComments().Delete(ctx, req) +} + +// ForumCommentsView 查看评论详情 +func (c *ControllerV1) ForumCommentsView(ctx context.Context, req *v1.ForumCommentsViewReq) (res *v1.ForumCommentsViewRes, err error) { + return service.ForumComments().View(ctx, req) +} + +// ForumCommentsList 获取评论列表 +func (c *ControllerV1) ForumCommentsList(ctx context.Context, req *v1.ForumCommentsListReq) (res *v1.ForumCommentsListRes, err error) { + return service.ForumComments().List(ctx, req) +} diff --git a/Backend/internal/controller/forum/forum_v1_forum_posts.go b/Backend/internal/controller/forum/forum_v1_forum_posts.go new file mode 100644 index 0000000..0298998 --- /dev/null +++ b/Backend/internal/controller/forum/forum_v1_forum_posts.go @@ -0,0 +1,38 @@ +package forum + +import ( + "context" + v1 "leke/api/forum/v1" + "leke/internal/service" +) + +type ControllerV1 struct{} + +func NewV1() *ControllerV1 { + return &ControllerV1{} +} + +// ForumPostsCreate 创建帖子 +func (c *ControllerV1) Create(ctx context.Context, req *v1.ForumPostsCreateReq) (res *v1.ForumPostsCreateRes, err error) { + return service.ForumPosts().Create(ctx, req) +} + +// ForumPostsUpdate 更新帖子 +func (c *ControllerV1) Update(ctx context.Context, req *v1.ForumPostsUpdateReq) (res *v1.ForumPostsUpdateRes, err error) { + return service.ForumPosts().Update(ctx, req) +} + +// ForumPostsDelete 删除帖子 +func (c *ControllerV1) Delete(ctx context.Context, req *v1.ForumPostsDeleteReq) (res *v1.ForumPostsDeleteRes, err error) { + return service.ForumPosts().Delete(ctx, req) +} + +// ForumPostsView 查看帖子详情 +func (c *ControllerV1) View(ctx context.Context, req *v1.ForumPostsViewReq) (res *v1.ForumPostsViewRes, err error) { + return service.ForumPosts().View(ctx, req) +} + +// ForumPostsList 获取帖子列表 +func (c *ControllerV1) List(ctx context.Context, req *v1.ForumPostsListReq) (res *v1.ForumPostsListRes, err error) { + return service.ForumPosts().List(ctx, req) +} diff --git a/Backend/internal/controller/login/login_v1_login.go b/Backend/internal/controller/login/login_v1_login.go new file mode 100644 index 0000000..f40aa5e --- /dev/null +++ b/Backend/internal/controller/login/login_v1_login.go @@ -0,0 +1,60 @@ +// internal/controller/login/login_v1_login.go +package login + +import ( + "context" + "leke/api/login" + v1 "leke/api/login/v1" + "leke/internal/service" + + "github.com/gogf/gf/v2/errors/gcode" + "github.com/gogf/gf/v2/errors/gerror" +) + +type ControllerV1 struct{} + +func NewV1() login.ILoginV1 { + return &ControllerV1{} +} + +// Login 登录接口 +func (c *ControllerV1) Login(ctx context.Context, req *v1.LoginReq) (res *v1.LoginRes, err error) { + res, err = service.Login().Login(ctx, req) + if err != nil { + return nil, err + } + res = &v1.LoginRes{ + Token: res.Token, + Account: res.Account, + } + return res, nil +} + +func (c *ControllerV1) Register(ctx context.Context, req *v1.RegisterReq) (res *v1.RegisterRes, err error) { + _, err = service.Login().Register(ctx, req) + if err != nil { + return nil, err + } + return &v1.RegisterRes{ + Account: req.Account, + }, nil +} + +func (c *ControllerV1) Logout(ctx context.Context, req *v1.LogoutReq) (res *v1.LogoutRes, err error) { + return nil, gerror.NewCode(gcode.CodeNotImplemented) +} + +// 通过邮箱注册 +func (c *ControllerV1) RegisterByEmail(ctx context.Context, req *v1.RegisterByEmailReq) (res *v1.RegisterByEmailRes, err error) { + return service.Login().RegisterByEmail(ctx, req) +} + +// 通过邮箱登录 +func (c *ControllerV1) LoginByEmail(ctx context.Context, req *v1.LoginByEmailReq) (res *v1.LoginByEmailRes, err error) { + return service.Login().LoginByEmail(ctx, req) +} + +// 发送验证码 +func (c *ControllerV1) SendVerificationCode(ctx context.Context, req *v1.SendVerificationCodeReq) (res *v1.SendVerificationCodeRes, err error) { + return service.Login().SendVerificationCode(ctx, req) +} diff --git a/Backend/internal/controller/room/room_v1_room.go b/Backend/internal/controller/room/room_v1_room.go new file mode 100644 index 0000000..771bb7d --- /dev/null +++ b/Backend/internal/controller/room/room_v1_room.go @@ -0,0 +1,35 @@ +package room + +import ( + "context" + "leke/api/room" + v1 "leke/api/room/v1" + "leke/internal/service" +) + +type ControllerV1 struct{} + +func NewV1() room.IRoomV1 { + return &ControllerV1{} +} + +func (c *ControllerV1) RoomList(ctx context.Context, req *v1.RoomListReq) (res *v1.RoomListRes, err error) { + return service.Room().RoomList(ctx, req) +} +func (c *ControllerV1) RoomView(ctx context.Context, req *v1.RoomViewReq) (res *v1.RoomViewRes, err error) { + return service.Room().RoomView(ctx, req) +} + +func (c *ControllerV1) RoomUpdate(ctx context.Context, req *v1.RoomUpdateReq) (res *v1.RoomUpdateRes, err error) { + return service.Room().RoomUpdate(ctx, req) +} +func (c *ControllerV1) RoomDelete(ctx context.Context, req *v1.RoomDeleteReq) (res *v1.RoomDeleteRes, err error) { + return service.Room().RoomDelete(ctx, req) +} +func (c *ControllerV1) RoomCreate(ctx context.Context, req *v1.RoomCreateReq) (res *v1.RoomCreateRes, err error) { + return service.Room().RoomCreate(ctx, req) +} + +func (c *ControllerV1) RoomJoin(ctx context.Context, req *v1.RoomJoinReq) (res *v1.RoomJoinRes, err error) { + return service.Room().RoomJoin(ctx, req) +} diff --git a/Backend/internal/controller/user/user_v1_role.go b/Backend/internal/controller/user/user_v1_role.go new file mode 100644 index 0000000..e340cb4 --- /dev/null +++ b/Backend/internal/controller/user/user_v1_role.go @@ -0,0 +1,37 @@ +package user + +import ( + "context" + + v1 "leke/api/user/v1" + "leke/internal/service" +) + +type RoleControllerV1 struct{} + +func NewRoleV1() *RoleControllerV1 { + return &RoleControllerV1{} +} + +func (c *RoleControllerV1) RoleCreate(ctx context.Context, req *v1.RoleCreateReq) (res *v1.RoleCreateRes, err error) { + return service.User().RoleCreate(ctx, req) +} + +func (c *RoleControllerV1) RoleUpdate(ctx context.Context, req *v1.RoleUpdateReq) (res *v1.RoleUpdateRes, err error) { + return service.User().RoleUpdate(ctx, req) +} + +func (c *RoleControllerV1) RoleView(ctx context.Context, req *v1.RoleViewReq) (res *v1.RoleViewRes, err error) { + return service.User().RoleView(ctx, req) +} + +func (c *RoleControllerV1) RoleList(ctx context.Context, req *v1.RoleListReq) (res *v1.RoleListRes, err error) { + return service.User().RoleList(ctx, req) +} + +func (c *RoleControllerV1) RoleDelete(ctx context.Context, req *v1.RoleDeleteReq) (res *v1.RoleDeleteRes, err error) { + return service.User().RoleDelete(ctx, req) +} // RolePermissionCheck 权限查询 +func (c *RoleControllerV1) RolePermissionCheck(ctx context.Context, req *v1.RolePermissionCheckReq) (res *v1.RolePermissionCheckRes, err error) { + return service.User().RolePermissionCheck(ctx, req) +} diff --git a/Backend/internal/controller/user/user_v1_user.go b/Backend/internal/controller/user/user_v1_user.go new file mode 100644 index 0000000..984763e --- /dev/null +++ b/Backend/internal/controller/user/user_v1_user.go @@ -0,0 +1,86 @@ +package user + +import ( + "context" + + v1 "leke/api/user/v1" + "leke/internal/service" +) + +type ControllerV1 struct{} + +func NewV1() *ControllerV1 { + return &ControllerV1{} +} + +func (c *ControllerV1) UserList(ctx context.Context, req *v1.UserListReq) (res *v1.UserListRes, err error) { + return service.User().UserList(ctx, req) +} +func (c *ControllerV1) UserView(ctx context.Context, req *v1.UserViewReq) (res *v1.UserViewRes, err error) { + return service.User().UserView(ctx, req) +} +func (c *ControllerV1) UserUpdate(ctx context.Context, req *v1.UserUpdateReq) (res *v1.UserUpdateRes, err error) { + return service.User().UserUpdate(ctx, req) +} +func (c *ControllerV1) UserDelete(ctx context.Context, req *v1.UserDeleteReq) (res *v1.UserDeleteRes, err error) { + return service.User().UserDelete(ctx, req) +} + +// Fans 相关方法 +func (c *ControllerV1) FansList(ctx context.Context, req *v1.FansListReq) (res *v1.FansListRes, err error) { + return service.Fans().FansList(ctx, req) +} + +func (c *ControllerV1) FansView(ctx context.Context, req *v1.FansViewReq) (res *v1.FansViewRes, err error) { + return service.Fans().FansView(ctx, req) +} + +func (c *ControllerV1) FansUpdate(ctx context.Context, req *v1.FansUpdateReq) (res *v1.FansUpdateRes, err error) { + return service.Fans().FansUpdate(ctx, req) +} + +func (c *ControllerV1) FansDelete(ctx context.Context, req *v1.FansDeleteReq) (res *v1.FansDeleteRes, err error) { + return service.Fans().FansDelete(ctx, req) +} + +func (c *ControllerV1) FansCreate(ctx context.Context, req *v1.FansCreateReq) (res *v1.FansCreateRes, err error) { + return service.Fans().FansCreate(ctx, req) +} + +// Subscribe 相关方法 +func (c *ControllerV1) SubscribeList(ctx context.Context, req *v1.SubscribeListReq) (res *v1.SubscribeListRes, err error) { + return service.Subscribe().SubscribeList(ctx, req) +} + +func (c *ControllerV1) SubscribeView(ctx context.Context, req *v1.SubscribeViewReq) (res *v1.SubscribeViewRes, err error) { + return service.Subscribe().SubscribeView(ctx, req) +} + +func (c *ControllerV1) SubscribeUpdate(ctx context.Context, req *v1.SubscribeUpdateReq) (res *v1.SubscribeUpdateRes, err error) { + return service.Subscribe().SubscribeUpdate(ctx, req) +} + +func (c *ControllerV1) SubscribeDelete(ctx context.Context, req *v1.SubscribeDeleteReq) (res *v1.SubscribeDeleteRes, err error) { + return service.Subscribe().SubscribeDelete(ctx, req) +} + +func (c *ControllerV1) SubscribeCreate(ctx context.Context, req *v1.SubscribeCreateReq) (res *v1.SubscribeCreateRes, err error) { + return service.Subscribe().SubscribeCreate(ctx, req) +} + +// Trace 相关方法 +func (c *ControllerV1) TraceList(ctx context.Context, req *v1.TraceListReq) (res *v1.TraceListRes, err error) { + return service.Trace().TraceList(ctx, req) +} + +func (c *ControllerV1) TraceView(ctx context.Context, req *v1.TraceViewReq) (res *v1.TraceViewRes, err error) { + return service.Trace().TraceView(ctx, req) +} + +func (c *ControllerV1) TraceUpdate(ctx context.Context, req *v1.TraceUpdateReq) (res *v1.TraceUpdateRes, err error) { + return service.Trace().TraceUpdate(ctx, req) +} + +func (c *ControllerV1) TraceReduce(ctx context.Context, req *v1.TraceReduceReq) (res *v1.TraceReduceRes, err error) { + return service.Trace().TraceReduce(ctx, req) +} diff --git a/Backend/internal/controller/websocket/ChatRoom.go b/Backend/internal/controller/websocket/ChatRoom.go new file mode 100644 index 0000000..1a6ce4d --- /dev/null +++ b/Backend/internal/controller/websocket/ChatRoom.go @@ -0,0 +1,119 @@ +package websocket + +import ( + "log" + "net/http" + "sync" + + "github.com/gogf/gf/v2/net/ghttp" + "github.com/gorilla/websocket" +) + +type ChatMessage struct { + RoomId string `json:"roomId"` + UserId string `json:"userId"` + Message string `json:"message"` + Type string `json:"type"` +} + +var upgrader = websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }} +var clients = make(map[*websocket.Conn]bool) +var roomClients = make(map[string]map[*websocket.Conn]bool) +var broadcast = make(chan ChatMessage, 256) +var mutex = sync.RWMutex{} +var once sync.Once + +func HandleChatConnections(r *ghttp.Request) { + once.Do(func() { + go handleBroadcast() + }) + + ws, err := upgrader.Upgrade(r.Response.Writer, r.Request, nil) + if err != nil { + log.Printf("upgrade error: %v", err) + return + } + + mutex.Lock() + clients[ws] = true + mutex.Unlock() + + defer func() { + mutex.Lock() + delete(clients, ws) + for roomId, members := range roomClients { + if _, ok := members[ws]; ok { + delete(members, ws) + if len(members) == 0 { + delete(roomClients, roomId) + } + } + } + mutex.Unlock() + ws.Close() + }() + + for { + var msg ChatMessage + err := ws.ReadJSON(&msg) + if err != nil { + log.Printf("read error: %v", err) + break + } + + switch msg.Type { + case "join": + mutex.Lock() + members, ok := roomClients[msg.RoomId] + if !ok { + members = make(map[*websocket.Conn]bool) + roomClients[msg.RoomId] = members + } + members[ws] = true + mutex.Unlock() + case "leave": + mutex.Lock() + if members, ok := roomClients[msg.RoomId]; ok { + delete(members, ws) + if len(members) == 0 { + delete(roomClients, msg.RoomId) + } + } + mutex.Unlock() + } + + select { + case broadcast <- msg: + default: + log.Printf("broadcast channel full, dropping message") + } + } +} + +func handleBroadcast() { + for { + msg := <-broadcast + mutex.RLock() + members, ok := roomClients[msg.RoomId] + if !ok { + mutex.RUnlock() + continue + } + for client := range members { + err := client.WriteJSON(msg) + if err != nil { + log.Printf("write error: %v", err) + client.Close() + mutex.RUnlock() + mutex.Lock() + delete(members, client) + if len(members) == 0 { + delete(roomClients, msg.RoomId) + } + mutex.Unlock() + mutex.RLock() + } + } + mutex.RUnlock() + } +} diff --git a/Backend/internal/controller/websocket/websocket.go b/Backend/internal/controller/websocket/websocket.go new file mode 100644 index 0000000..fb0ee24 --- /dev/null +++ b/Backend/internal/controller/websocket/websocket.go @@ -0,0 +1,65 @@ +package websocket + +// +//import ( +// "net/http" +// +// "github.com/gogf/gf/v2/frame/g" +// "github.com/gogf/gf/v2/net/ghttp" +// "github.com/gorilla/websocket" +//) +// +//// WebSocketController WebSocket 控制器 +//type WebSocketController struct{} +// +//// New 创建 WebSocket 控制器实例 +//func New() *WebSocketController { +// return &WebSocketController{} +//} +// +//// wsUpgrader WebSocket 升级器配置 +//var wsUpgrader = websocket.Upgrader{ +// // CheckOrigin 允许任何来源(开发环境) +// // 生产环境中应该实现适当的来源检查以确保安全 +// CheckOrigin: func(r *http.Request) bool { +// return true +// }, +// // Error 处理升级失败的错误 +// Error: func(w http.ResponseWriter, r *http.Request, status int, reason error) { +// // 在这里实现错误处理逻辑 +// g.Log().Errorf(r.Context(), "WebSocket upgrade error: %v", reason) +// }, +//} +// +//// Echo WebSocket Echo 服务器处理器 +//// 路径: /ws +//func (c *WebSocketController) Echo(r *ghttp.Request) { +// // 将 HTTP 连接升级为 WebSocket +// ws, err := wsUpgrader.Upgrade(r.Response.Writer, r.Request, nil) +// if err != nil { +// r.Response.Write(err.Error()) +// return +// } +// defer ws.Close() +// +// // 获取请求上下文用于日志记录 +// var ctx = r.Context() +// logger := g.Log() +// +// // 消息处理循环 +// for { +// // 读取传入的 WebSocket 消息 +// msgType, msg, err := ws.ReadMessage() +// if err != nil { +// break // 连接关闭或发生错误 +// } +// // 记录接收到的消息 +// logger.Infof(ctx, "received message: %s", msg) +// // 将消息回显给客户端 +// if err = ws.WriteMessage(msgType, msg); err != nil { +// break // 写入消息时出错 +// } +// } +// // 记录连接关闭 +// logger.Info(ctx, "websocket connection closed") +//} diff --git a/Backend/internal/controller/websocket/websocketV1.go b/Backend/internal/controller/websocket/websocketV1.go new file mode 100644 index 0000000..5bf5471 --- /dev/null +++ b/Backend/internal/controller/websocket/websocketV1.go @@ -0,0 +1,88 @@ +package websocket + +// +//import ( +// "log" +// "net/http" +// "sync" +// +// "github.com/gogf/gf/v2/net/ghttp" +// "github.com/gorilla/websocket" +//) +// +//// 定义 WebSocket 升级器 +//var upgrader = websocket.Upgrader{ +// CheckOrigin: func(r *http.Request) bool { +// return true +// }, +//} +// +//// 存储客户端连接 +//var ( +// clients = make(map[*websocket.Conn]bool) +// clientsMu sync.Mutex // 保护 clients map 的互斥锁 +// broadcast = make(chan string, 256) // 广播信息通道,带缓冲 +//) +// +//// 初始化广播处理(只启动一次) +//var once sync.Once +// +//// HandlerConnection 处理websocket连接 +//func HandlerConnectionV1(r *ghttp.Request) { +// // 启动广播处理 goroutine(只启动一次) +// once.Do(func() { +// go handleBroadcast() +// }) +// +// ws, err := upgrader.Upgrade(r.Response.Writer, r.Request, nil) +// if err != nil { +// log.Printf("upgrade error: %v", err) +// return +// } +// defer func() { +// clientsMu.Lock() +// delete(clients, ws) +// clientsMu.Unlock() +// ws.Close() +// }() +// +// // 注册新客户端 +// clientsMu.Lock() +// clients[ws] = true +// clientsMu.Unlock() +// +// for { +// var message string +// err := ws.ReadJSON(&message) +// if err != nil { +// log.Printf("read error: %v", err) +// clientsMu.Lock() +// delete(clients, ws) +// clientsMu.Unlock() +// break +// } +// // 将消息发送到广播通道 +// select { +// case broadcast <- message: +// default: +// log.Printf("broadcast channel full, dropping message") +// } +// } +//} +// +//// 将广播通道给所有的用户 +//func handleBroadcast() { +// for { +// message := <-broadcast +// clientsMu.Lock() +// for client := range clients { +// err := client.WriteJSON(message) +// if err != nil { +// log.Printf("write error: %v", err) +// client.Close() +// delete(clients, client) +// } +// } +// clientsMu.Unlock() +// } +//} diff --git a/Backend/internal/controller/websocket/websocketV2.go b/Backend/internal/controller/websocket/websocketV2.go new file mode 100644 index 0000000..b6e783d --- /dev/null +++ b/Backend/internal/controller/websocket/websocketV2.go @@ -0,0 +1,66 @@ +package websocket + +// +//import ( +// "github.com/gorilla/websocket" +// "log" +// "net/http" +// "sync" +//) +// +//var upgraderv2 = websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }} +//var clientsV2 = make(map[*websocket.Conn]bool) // +//var mutex = sync.RWMutex{} // 读写锁 // 保护 clientsV2 并发访问 +// +////var bufferPool = sync.Pool{ +//// New: func() interface{} { +//// return make([]byte, 1024) +//// }, +////} +// +//var bufferPool = sync.Pool{ +// New: func() interface{} { +// return make([]byte, 1024) +// }, +//} +// +//func readMessage(ws *websocket.Conn) ([]byte, error) { +// buf := bufferPool.Get().([]byte) +// defer bufferPool.Put(buf) +// _, data, err := ws.ReadMessage() +// return data, err +//} +// +//func HandlerConnectionV2(w http.ResponseWriter, r *http.Request) { +// ws, err := upgraderv2.Upgrade(w, r, nil) +// if err != nil { +// log.Printf("upgrade error: %v", err) +// return +// } +// // 注册客户端 +// mutex.Lock() +// clientsV2[ws] = true +// mutex.Unlock() +// +// defer func() { +// mutex.Lock() +// delete(clientsV2, ws) +// mutex.Unlock() +// ws.Close() +// }() +// +// for { +// var message string +// err := ws.ReadJSON(&message) +// if err != nil { +// log.Printf("read json error: %v", err) +// return +// } +// // 广播消息(简化示例) +// mutex.RLock() +// for client := range clientsV2 { +// client.WriteJSON(message) +// } +// mutex.RUnlock() +// } +//} diff --git a/Backend/internal/dao/.gitkeep b/Backend/internal/dao/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/Backend/internal/dao/coaches.go b/Backend/internal/dao/coaches.go new file mode 100644 index 0000000..4c00585 --- /dev/null +++ b/Backend/internal/dao/coaches.go @@ -0,0 +1,22 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package dao + +import ( + "leke/internal/dao/internal" +) + +// coachesDao is the data access object for the table coaches. +// You can define custom methods on it to extend its functionality as needed. +type coachesDao struct { + *internal.CoachesDao +} + +var ( + // Coaches is a globally accessible object for table coaches operations. + Coaches = coachesDao{internal.NewCoachesDao()} +) + +// Add your custom methods and functionality below. diff --git a/Backend/internal/dao/comments.go b/Backend/internal/dao/comments.go new file mode 100644 index 0000000..d2cf281 --- /dev/null +++ b/Backend/internal/dao/comments.go @@ -0,0 +1,22 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package dao + +import ( + "leke/internal/dao/internal" +) + +// commentsDao is the data access object for the table comments. +// You can define custom methods on it to extend its functionality as needed. +type commentsDao struct { + *internal.CommentsDao +} + +var ( + // Comments is a globally accessible object for table comments operations. + Comments = commentsDao{internal.NewCommentsDao()} +) + +// Add your custom methods and functionality below. diff --git a/Backend/internal/dao/containment_repo.go b/Backend/internal/dao/containment_repo.go new file mode 100644 index 0000000..532a710 --- /dev/null +++ b/Backend/internal/dao/containment_repo.go @@ -0,0 +1,22 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package dao + +import ( + "leke/internal/dao/internal" +) + +// containmentRepoDao is the data access object for the table containment_repo. +// You can define custom methods on it to extend its functionality as needed. +type containmentRepoDao struct { + *internal.ContainmentRepoDao +} + +var ( + // ContainmentRepo is a globally accessible object for table containment_repo operations. + ContainmentRepo = containmentRepoDao{internal.NewContainmentRepoDao()} +) + +// Add your custom methods and functionality below. diff --git a/Backend/internal/dao/department.go b/Backend/internal/dao/department.go new file mode 100644 index 0000000..7dceee9 --- /dev/null +++ b/Backend/internal/dao/department.go @@ -0,0 +1,22 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package dao + +import ( + "leke/internal/dao/internal" +) + +// departmentDao is the data access object for the table department. +// You can define custom methods on it to extend its functionality as needed. +type departmentDao struct { + *internal.DepartmentDao +} + +var ( + // Department is a globally accessible object for table department operations. + Department = departmentDao{internal.NewDepartmentDao()} +) + +// Add your custom methods and functionality below. diff --git a/Backend/internal/dao/fans.go b/Backend/internal/dao/fans.go new file mode 100644 index 0000000..743802e --- /dev/null +++ b/Backend/internal/dao/fans.go @@ -0,0 +1,22 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package dao + +import ( + "leke/internal/dao/internal" +) + +// fansDao is the data access object for the table fans. +// You can define custom methods on it to extend its functionality as needed. +type fansDao struct { + *internal.FansDao +} + +var ( + // Fans is a globally accessible object for table fans operations. + Fans = fansDao{internal.NewFansDao()} +) + +// Add your custom methods and functionality below. diff --git a/Backend/internal/dao/forum_comments.go b/Backend/internal/dao/forum_comments.go new file mode 100644 index 0000000..778402f --- /dev/null +++ b/Backend/internal/dao/forum_comments.go @@ -0,0 +1,22 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package dao + +import ( + "leke/internal/dao/internal" +) + +// forumCommentsDao is the data access object for the table forum_comments. +// You can define custom methods on it to extend its functionality as needed. +type forumCommentsDao struct { + *internal.ForumCommentsDao +} + +var ( + // ForumComments is a globally accessible object for table forum_comments operations. + ForumComments = forumCommentsDao{internal.NewForumCommentsDao()} +) + +// Add your custom methods and functionality below. diff --git a/Backend/internal/dao/forum_posts.go b/Backend/internal/dao/forum_posts.go new file mode 100644 index 0000000..51a5334 --- /dev/null +++ b/Backend/internal/dao/forum_posts.go @@ -0,0 +1,22 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package dao + +import ( + "leke/internal/dao/internal" +) + +// forumPostsDao is the data access object for the table forum_posts. +// You can define custom methods on it to extend its functionality as needed. +type forumPostsDao struct { + *internal.ForumPostsDao +} + +var ( + // ForumPosts is a globally accessible object for table forum_posts operations. + ForumPosts = forumPostsDao{internal.NewForumPostsDao()} +) + +// Add your custom methods and functionality below. diff --git a/Backend/internal/dao/group_class_enrollments.go b/Backend/internal/dao/group_class_enrollments.go new file mode 100644 index 0000000..533056d --- /dev/null +++ b/Backend/internal/dao/group_class_enrollments.go @@ -0,0 +1,22 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package dao + +import ( + "leke/internal/dao/internal" +) + +// groupClassEnrollmentsDao is the data access object for the table group_class_enrollments. +// You can define custom methods on it to extend its functionality as needed. +type groupClassEnrollmentsDao struct { + *internal.GroupClassEnrollmentsDao +} + +var ( + // GroupClassEnrollments is a globally accessible object for table group_class_enrollments operations. + GroupClassEnrollments = groupClassEnrollmentsDao{internal.NewGroupClassEnrollmentsDao()} +) + +// Add your custom methods and functionality below. diff --git a/Backend/internal/dao/group_classes.go b/Backend/internal/dao/group_classes.go new file mode 100644 index 0000000..1fd4642 --- /dev/null +++ b/Backend/internal/dao/group_classes.go @@ -0,0 +1,22 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package dao + +import ( + "leke/internal/dao/internal" +) + +// groupClassesDao is the data access object for the table group_classes. +// You can define custom methods on it to extend its functionality as needed. +type groupClassesDao struct { + *internal.GroupClassesDao +} + +var ( + // GroupClasses is a globally accessible object for table group_classes operations. + GroupClasses = groupClassesDao{internal.NewGroupClassesDao()} +) + +// Add your custom methods and functionality below. diff --git a/Backend/internal/dao/internal/coaches.go b/Backend/internal/dao/internal/coaches.go new file mode 100644 index 0000000..a2d0d16 --- /dev/null +++ b/Backend/internal/dao/internal/coaches.go @@ -0,0 +1,91 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// CoachesDao is the data access object for the table coaches. +type CoachesDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of the current DAO. + columns CoachesColumns // columns contains all the column names of Table for convenient usage. + handlers []gdb.ModelHandler // handlers for customized model modification. +} + +// CoachesColumns defines and stores column names for the table coaches. +type CoachesColumns struct { + Id string // 教练ID + UserId string // 对应的用户ID(如果教练也要登录的话) + Name string // 教练姓名 + Phone string // 教练电话 + Specialty string // 擅长方向,比如瑜伽、力量、减脂 + CreatedAt string // 创建时间 + UpdatedAt string // 更新时间 +} + +// coachesColumns holds the columns for the table coaches. +var coachesColumns = CoachesColumns{ + Id: "id", + UserId: "user_id", + Name: "name", + Phone: "phone", + Specialty: "specialty", + CreatedAt: "created_at", + UpdatedAt: "updated_at", +} + +// NewCoachesDao creates and returns a new DAO object for table data access. +func NewCoachesDao(handlers ...gdb.ModelHandler) *CoachesDao { + return &CoachesDao{ + group: "default", + table: "coaches", + columns: coachesColumns, + handlers: handlers, + } +} + +// DB retrieves and returns the underlying raw database management object of the current DAO. +func (dao *CoachesDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of the current DAO. +func (dao *CoachesDao) Table() string { + return dao.table +} + +// Columns returns all column names of the current DAO. +func (dao *CoachesDao) Columns() CoachesColumns { + return dao.columns +} + +// Group returns the database configuration group name of the current DAO. +func (dao *CoachesDao) Group() string { + return dao.group +} + +// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +func (dao *CoachesDao) Ctx(ctx context.Context) *gdb.Model { + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rolls back the transaction and returns the error if function f returns a non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note: Do not commit or roll back the transaction in function f, +// as it is automatically handled by this function. +func (dao *CoachesDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/Backend/internal/dao/internal/comments.go b/Backend/internal/dao/internal/comments.go new file mode 100644 index 0000000..cdff9dd --- /dev/null +++ b/Backend/internal/dao/internal/comments.go @@ -0,0 +1,103 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// CommentsDao is the data access object for the table comments. +type CommentsDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of the current DAO. + columns CommentsColumns // columns contains all the column names of Table for convenient usage. + handlers []gdb.ModelHandler // handlers for customized model modification. +} + +// CommentsColumns defines and stores column names for the table comments. +type CommentsColumns struct { + Id string // 评论ID + UserId string // 评论用户ID + TargetType string // 被评论对象类型(如 post/image/video 等) + TargetId string // 被评论对象ID + ParentId string // 父评论ID(回复某条评论时填写) + RootId string // 根评论ID(同一楼的顶层评论ID,便于树查询) + Content string // 评论内容 + Status string // 状态:1正常 0屏蔽 2删除 + LikeCount string // 点赞数 + ReplyCount string // 回复数 + CreatedAt string // 创建时间 + UpdatedAt string // 更新时间 + DeletedAt string // 软删除时间 +} + +// commentsColumns holds the columns for the table comments. +var commentsColumns = CommentsColumns{ + Id: "id", + UserId: "user_id", + TargetType: "target_type", + TargetId: "target_id", + ParentId: "parent_id", + RootId: "root_id", + Content: "content", + Status: "status", + LikeCount: "like_count", + ReplyCount: "reply_count", + CreatedAt: "created_at", + UpdatedAt: "updated_at", + DeletedAt: "deleted_at", +} + +// NewCommentsDao creates and returns a new DAO object for table data access. +func NewCommentsDao(handlers ...gdb.ModelHandler) *CommentsDao { + return &CommentsDao{ + group: "default", + table: "comments", + columns: commentsColumns, + handlers: handlers, + } +} + +// DB retrieves and returns the underlying raw database management object of the current DAO. +func (dao *CommentsDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of the current DAO. +func (dao *CommentsDao) Table() string { + return dao.table +} + +// Columns returns all column names of the current DAO. +func (dao *CommentsDao) Columns() CommentsColumns { + return dao.columns +} + +// Group returns the database configuration group name of the current DAO. +func (dao *CommentsDao) Group() string { + return dao.group +} + +// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +func (dao *CommentsDao) Ctx(ctx context.Context) *gdb.Model { + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rolls back the transaction and returns the error if function f returns a non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note: Do not commit or roll back the transaction in function f, +// as it is automatically handled by this function. +func (dao *CommentsDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/Backend/internal/dao/internal/containment_repo.go b/Backend/internal/dao/internal/containment_repo.go new file mode 100644 index 0000000..14a92d0 --- /dev/null +++ b/Backend/internal/dao/internal/containment_repo.go @@ -0,0 +1,87 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// ContainmentRepoDao is the data access object for the table containment_repo. +type ContainmentRepoDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of the current DAO. + columns ContainmentRepoColumns // columns contains all the column names of Table for convenient usage. + handlers []gdb.ModelHandler // handlers for customized model modification. +} + +// ContainmentRepoColumns defines and stores column names for the table containment_repo. +type ContainmentRepoColumns struct { + Id string // auto-increment primary key + AnomalyName string // name of the anomaly (异常体的名字) + AgentName string // agent (特工) + RepoName string // containment repository name or code (收容库) + Department string // 部门 +} + +// containmentRepoColumns holds the columns for the table containment_repo. +var containmentRepoColumns = ContainmentRepoColumns{ + Id: "id", + AnomalyName: "anomaly_name", + AgentName: "agent_name", + RepoName: "repo_name", + Department: "department", +} + +// NewContainmentRepoDao creates and returns a new DAO object for table data access. +func NewContainmentRepoDao(handlers ...gdb.ModelHandler) *ContainmentRepoDao { + return &ContainmentRepoDao{ + group: "default", + table: "containment_repo", + columns: containmentRepoColumns, + handlers: handlers, + } +} + +// DB retrieves and returns the underlying raw database management object of the current DAO. +func (dao *ContainmentRepoDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of the current DAO. +func (dao *ContainmentRepoDao) Table() string { + return dao.table +} + +// Columns returns all column names of the current DAO. +func (dao *ContainmentRepoDao) Columns() ContainmentRepoColumns { + return dao.columns +} + +// Group returns the database configuration group name of the current DAO. +func (dao *ContainmentRepoDao) Group() string { + return dao.group +} + +// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +func (dao *ContainmentRepoDao) Ctx(ctx context.Context) *gdb.Model { + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rolls back the transaction and returns the error if function f returns a non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note: Do not commit or roll back the transaction in function f, +// as it is automatically handled by this function. +func (dao *ContainmentRepoDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/Backend/internal/dao/internal/department.go b/Backend/internal/dao/internal/department.go new file mode 100644 index 0000000..31ce5c4 --- /dev/null +++ b/Backend/internal/dao/internal/department.go @@ -0,0 +1,95 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// DepartmentDao is the data access object for the table department. +type DepartmentDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of the current DAO. + columns DepartmentColumns // columns contains all the column names of Table for convenient usage. + handlers []gdb.ModelHandler // handlers for customized model modification. +} + +// DepartmentColumns defines and stores column names for the table department. +type DepartmentColumns struct { + Id string // 自增主键 + UserId string // 所属用户ID(对应 users.id) + BranchName string // 分部名称 + TerminalCount string // 分部散逸端的数量 + Weather string // 分部当前天气/气候描述 + ManagerName string // 分部经理名称 + CreatedAt string // 创建时间 + UpdatedAt string // 更新时间 + Location string // 地址 +} + +// departmentColumns holds the columns for the table department. +var departmentColumns = DepartmentColumns{ + Id: "id", + UserId: "user_id", + BranchName: "branch_name", + TerminalCount: "terminal_count", + Weather: "weather", + ManagerName: "manager_name", + CreatedAt: "created_at", + UpdatedAt: "updated_at", + Location: "location", +} + +// NewDepartmentDao creates and returns a new DAO object for table data access. +func NewDepartmentDao(handlers ...gdb.ModelHandler) *DepartmentDao { + return &DepartmentDao{ + group: "default", + table: "department", + columns: departmentColumns, + handlers: handlers, + } +} + +// DB retrieves and returns the underlying raw database management object of the current DAO. +func (dao *DepartmentDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of the current DAO. +func (dao *DepartmentDao) Table() string { + return dao.table +} + +// Columns returns all column names of the current DAO. +func (dao *DepartmentDao) Columns() DepartmentColumns { + return dao.columns +} + +// Group returns the database configuration group name of the current DAO. +func (dao *DepartmentDao) Group() string { + return dao.group +} + +// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +func (dao *DepartmentDao) Ctx(ctx context.Context) *gdb.Model { + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rolls back the transaction and returns the error if function f returns a non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note: Do not commit or roll back the transaction in function f, +// as it is automatically handled by this function. +func (dao *DepartmentDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/Backend/internal/dao/internal/fans.go b/Backend/internal/dao/internal/fans.go new file mode 100644 index 0000000..f134c88 --- /dev/null +++ b/Backend/internal/dao/internal/fans.go @@ -0,0 +1,91 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// FansDao is the data access object for the table fans. +type FansDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of the current DAO. + columns FansColumns // columns contains all the column names of Table for convenient usage. + handlers []gdb.ModelHandler // handlers for customized model modification. +} + +// FansColumns defines and stores column names for the table fans. +type FansColumns struct { + Id string // 自增主键 + UserId string // 用户ID(被关注者,本人) + FanId string // 粉丝用户ID + Status string // 状态:1=粉丝 0=取关/无效 + Remark string // 备注名(user_id 对 fan_id 的备注/分组) + CreatedAt string // 创建时间 + UpdatedAt string // 更新时间 +} + +// fansColumns holds the columns for the table fans. +var fansColumns = FansColumns{ + Id: "id", + UserId: "user_id", + FanId: "fan_id", + Status: "status", + Remark: "remark", + CreatedAt: "created_at", + UpdatedAt: "updated_at", +} + +// NewFansDao creates and returns a new DAO object for table data access. +func NewFansDao(handlers ...gdb.ModelHandler) *FansDao { + return &FansDao{ + group: "default", + table: "fans", + columns: fansColumns, + handlers: handlers, + } +} + +// DB retrieves and returns the underlying raw database management object of the current DAO. +func (dao *FansDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of the current DAO. +func (dao *FansDao) Table() string { + return dao.table +} + +// Columns returns all column names of the current DAO. +func (dao *FansDao) Columns() FansColumns { + return dao.columns +} + +// Group returns the database configuration group name of the current DAO. +func (dao *FansDao) Group() string { + return dao.group +} + +// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +func (dao *FansDao) Ctx(ctx context.Context) *gdb.Model { + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rolls back the transaction and returns the error if function f returns a non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note: Do not commit or roll back the transaction in function f, +// as it is automatically handled by this function. +func (dao *FansDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/Backend/internal/dao/internal/forum_comments.go b/Backend/internal/dao/internal/forum_comments.go new file mode 100644 index 0000000..d7f1d08 --- /dev/null +++ b/Backend/internal/dao/internal/forum_comments.go @@ -0,0 +1,99 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// ForumCommentsDao is the data access object for the table forum_comments. +type ForumCommentsDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of the current DAO. + columns ForumCommentsColumns // columns contains all the column names of Table for convenient usage. + handlers []gdb.ModelHandler // handlers for customized model modification. +} + +// ForumCommentsColumns defines and stores column names for the table forum_comments. +type ForumCommentsColumns struct { + Id string // 评论ID(主键) + UserId string // 评论发布者ID(关联users.id,无外键) + PostId string // 所属帖子ID(关联forum_posts.id,无外键) + ParentId string // 父评论ID(NULL=根评论,非NULL=回复某条评论) + Content string // 评论内容(支持emoji) + Status string // 评论状态:normal=正常 deleted=软删除 audit=审核中 reject=审核驳回 + LikeCount string // 点赞数 + ReplyCount string // 回复数(冗余字段) + CreatedAt string // 评论创建时间 + UpdatedAt string // 评论更新时间 + DeletedAt string // 软删除时间 +} + +// forumCommentsColumns holds the columns for the table forum_comments. +var forumCommentsColumns = ForumCommentsColumns{ + Id: "id", + UserId: "user_id", + PostId: "post_id", + ParentId: "parent_id", + Content: "content", + Status: "status", + LikeCount: "like_count", + ReplyCount: "reply_count", + CreatedAt: "created_at", + UpdatedAt: "updated_at", + DeletedAt: "deleted_at", +} + +// NewForumCommentsDao creates and returns a new DAO object for table data access. +func NewForumCommentsDao(handlers ...gdb.ModelHandler) *ForumCommentsDao { + return &ForumCommentsDao{ + group: "default", + table: "forum_comments", + columns: forumCommentsColumns, + handlers: handlers, + } +} + +// DB retrieves and returns the underlying raw database management object of the current DAO. +func (dao *ForumCommentsDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of the current DAO. +func (dao *ForumCommentsDao) Table() string { + return dao.table +} + +// Columns returns all column names of the current DAO. +func (dao *ForumCommentsDao) Columns() ForumCommentsColumns { + return dao.columns +} + +// Group returns the database configuration group name of the current DAO. +func (dao *ForumCommentsDao) Group() string { + return dao.group +} + +// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +func (dao *ForumCommentsDao) Ctx(ctx context.Context) *gdb.Model { + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rolls back the transaction and returns the error if function f returns a non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note: Do not commit or roll back the transaction in function f, +// as it is automatically handled by this function. +func (dao *ForumCommentsDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/Backend/internal/dao/internal/forum_posts.go b/Backend/internal/dao/internal/forum_posts.go new file mode 100644 index 0000000..42ec2c3 --- /dev/null +++ b/Backend/internal/dao/internal/forum_posts.go @@ -0,0 +1,101 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// ForumPostsDao is the data access object for the table forum_posts. +type ForumPostsDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of the current DAO. + columns ForumPostsColumns // columns contains all the column names of Table for convenient usage. + handlers []gdb.ModelHandler // handlers for customized model modification. +} + +// ForumPostsColumns defines and stores column names for the table forum_posts. +type ForumPostsColumns struct { + Id string // 帖子ID(主键) + UserId string // 发帖用户ID(关联users.id,无外键) + Title string // 帖子标题 + Content string // 帖子正文(支持富文本/emoji) + CoverImage string // 帖子封面图URL + Status string // 帖子状态:normal=正常 deleted=软删除 audit=审核中 reject=审核驳回 + ViewCount string // 浏览量(冗余字段) + LikeCount string // 点赞数(冗余字段) + CommentCount string // 评论数(冗余字段) + CreatedAt string // 发帖时间 + UpdatedAt string // 更新时间 + DeletedAt string // 软删除时间 +} + +// forumPostsColumns holds the columns for the table forum_posts. +var forumPostsColumns = ForumPostsColumns{ + Id: "id", + UserId: "user_id", + Title: "title", + Content: "content", + CoverImage: "cover_image", + Status: "status", + ViewCount: "view_count", + LikeCount: "like_count", + CommentCount: "comment_count", + CreatedAt: "created_at", + UpdatedAt: "updated_at", + DeletedAt: "deleted_at", +} + +// NewForumPostsDao creates and returns a new DAO object for table data access. +func NewForumPostsDao(handlers ...gdb.ModelHandler) *ForumPostsDao { + return &ForumPostsDao{ + group: "default", + table: "forum_posts", + columns: forumPostsColumns, + handlers: handlers, + } +} + +// DB retrieves and returns the underlying raw database management object of the current DAO. +func (dao *ForumPostsDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of the current DAO. +func (dao *ForumPostsDao) Table() string { + return dao.table +} + +// Columns returns all column names of the current DAO. +func (dao *ForumPostsDao) Columns() ForumPostsColumns { + return dao.columns +} + +// Group returns the database configuration group name of the current DAO. +func (dao *ForumPostsDao) Group() string { + return dao.group +} + +// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +func (dao *ForumPostsDao) Ctx(ctx context.Context) *gdb.Model { + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rolls back the transaction and returns the error if function f returns a non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note: Do not commit or roll back the transaction in function f, +// as it is automatically handled by this function. +func (dao *ForumPostsDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/Backend/internal/dao/internal/group_class_enrollments.go b/Backend/internal/dao/internal/group_class_enrollments.go new file mode 100644 index 0000000..e3aa44e --- /dev/null +++ b/Backend/internal/dao/internal/group_class_enrollments.go @@ -0,0 +1,89 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// GroupClassEnrollmentsDao is the data access object for the table group_class_enrollments. +type GroupClassEnrollmentsDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of the current DAO. + columns GroupClassEnrollmentsColumns // columns contains all the column names of Table for convenient usage. + handlers []gdb.ModelHandler // handlers for customized model modification. +} + +// GroupClassEnrollmentsColumns defines and stores column names for the table group_class_enrollments. +type GroupClassEnrollmentsColumns struct { + Id string // 报名ID + ClassId string // 团课ID + UserId string // 用户ID + Status string // 状态:booked已报名,checked_in已签到,cancelled已取消 + EnrolledAt string // 报名时间 + CheckedInAt string // 签到时间 +} + +// groupClassEnrollmentsColumns holds the columns for the table group_class_enrollments. +var groupClassEnrollmentsColumns = GroupClassEnrollmentsColumns{ + Id: "id", + ClassId: "class_id", + UserId: "user_id", + Status: "status", + EnrolledAt: "enrolled_at", + CheckedInAt: "checked_in_at", +} + +// NewGroupClassEnrollmentsDao creates and returns a new DAO object for table data access. +func NewGroupClassEnrollmentsDao(handlers ...gdb.ModelHandler) *GroupClassEnrollmentsDao { + return &GroupClassEnrollmentsDao{ + group: "default", + table: "group_class_enrollments", + columns: groupClassEnrollmentsColumns, + handlers: handlers, + } +} + +// DB retrieves and returns the underlying raw database management object of the current DAO. +func (dao *GroupClassEnrollmentsDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of the current DAO. +func (dao *GroupClassEnrollmentsDao) Table() string { + return dao.table +} + +// Columns returns all column names of the current DAO. +func (dao *GroupClassEnrollmentsDao) Columns() GroupClassEnrollmentsColumns { + return dao.columns +} + +// Group returns the database configuration group name of the current DAO. +func (dao *GroupClassEnrollmentsDao) Group() string { + return dao.group +} + +// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +func (dao *GroupClassEnrollmentsDao) Ctx(ctx context.Context) *gdb.Model { + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rolls back the transaction and returns the error if function f returns a non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note: Do not commit or roll back the transaction in function f, +// as it is automatically handled by this function. +func (dao *GroupClassEnrollmentsDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/Backend/internal/dao/internal/group_classes.go b/Backend/internal/dao/internal/group_classes.go new file mode 100644 index 0000000..3e1fcea --- /dev/null +++ b/Backend/internal/dao/internal/group_classes.go @@ -0,0 +1,99 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// GroupClassesDao is the data access object for the table group_classes. +type GroupClassesDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of the current DAO. + columns GroupClassesColumns // columns contains all the column names of Table for convenient usage. + handlers []gdb.ModelHandler // handlers for customized model modification. +} + +// GroupClassesColumns defines and stores column names for the table group_classes. +type GroupClassesColumns struct { + Id string // 团课ID + Title string // 课程名称,如:燃脂搏击、瑜伽 + Description string // 课程介绍 + CoachId string // 授课教练ID + Location string // 上课地点/门店/教室 + StartTime string // 开始时间 + EndTime string // 结束时间 + MaxCapacity string // 最大人数 + Price string // 价格(0表示免费或已包含在会员卡内) + CreatedAt string // 创建时间 + UpdatedAt string // 更新时间 +} + +// groupClassesColumns holds the columns for the table group_classes. +var groupClassesColumns = GroupClassesColumns{ + Id: "id", + Title: "title", + Description: "description", + CoachId: "coach_id", + Location: "location", + StartTime: "start_time", + EndTime: "end_time", + MaxCapacity: "max_capacity", + Price: "price", + CreatedAt: "created_at", + UpdatedAt: "updated_at", +} + +// NewGroupClassesDao creates and returns a new DAO object for table data access. +func NewGroupClassesDao(handlers ...gdb.ModelHandler) *GroupClassesDao { + return &GroupClassesDao{ + group: "default", + table: "group_classes", + columns: groupClassesColumns, + handlers: handlers, + } +} + +// DB retrieves and returns the underlying raw database management object of the current DAO. +func (dao *GroupClassesDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of the current DAO. +func (dao *GroupClassesDao) Table() string { + return dao.table +} + +// Columns returns all column names of the current DAO. +func (dao *GroupClassesDao) Columns() GroupClassesColumns { + return dao.columns +} + +// Group returns the database configuration group name of the current DAO. +func (dao *GroupClassesDao) Group() string { + return dao.group +} + +// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +func (dao *GroupClassesDao) Ctx(ctx context.Context) *gdb.Model { + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rolls back the transaction and returns the error if function f returns a non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note: Do not commit or roll back the transaction in function f, +// as it is automatically handled by this function. +func (dao *GroupClassesDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/Backend/internal/dao/internal/role_cards.go b/Backend/internal/dao/internal/role_cards.go new file mode 100644 index 0000000..c4f72b9 --- /dev/null +++ b/Backend/internal/dao/internal/role_cards.go @@ -0,0 +1,127 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// RoleCardsDao is the data access object for the table role_cards. +type RoleCardsDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of the current DAO. + columns RoleCardsColumns // columns contains all the column names of Table for convenient usage. + handlers []gdb.ModelHandler // handlers for customized model modification. +} + +// RoleCardsColumns defines and stores column names for the table role_cards. +type RoleCardsColumns struct { + Id string // 角色卡ID + UserId string // 所属用户ID + DepartmentId string // 所属部门ID + Commendation string // 嘉奖次数 + Reprimand string // 申戒次数 + BlueTrack string // 蓝轨(0-40) + YellowTrack string // 黄轨(0-40) + RedTrack string // 红轨(0-40) + ArcAbnormal string // ARC:异常 + ArcReality string // ARC:现实 + ArcPosition string // ARC:职位 + AgentName string // 特工名字 + CodeName string // 代号 + Gender string // 性别 + QaMeticulous string // Meticulousness (0-100, QA) + QaDeception string // Deception (0-100, QA) + QaVigor string // Vigor / Drive (0-100, QA) + QaEmpathy string // Empathy (0-100, QA) + QaInitiative string // Initiative (0-100, QA) + QaResilience string // Resilience / Persistence (0-100, QA) + QaPresence string // Presence / Charisma (0-100, QA) + QaProfessional string // Professionalism (0-100, QA) + QaDiscretion string // Discretion / Low profile (0-100, QA) + CreatedAt string // 创建时间 + UpdatedAt string // 更新时间 +} + +// roleCardsColumns holds the columns for the table role_cards. +var roleCardsColumns = RoleCardsColumns{ + Id: "id", + UserId: "user_id", + DepartmentId: "department_id", + Commendation: "commendation", + Reprimand: "reprimand", + BlueTrack: "blue_track", + YellowTrack: "yellow_track", + RedTrack: "red_track", + ArcAbnormal: "arc_abnormal", + ArcReality: "arc_reality", + ArcPosition: "arc_position", + AgentName: "agent_name", + CodeName: "code_name", + Gender: "gender", + QaMeticulous: "qa_meticulous", + QaDeception: "qa_deception", + QaVigor: "qa_vigor", + QaEmpathy: "qa_empathy", + QaInitiative: "qa_initiative", + QaResilience: "qa_resilience", + QaPresence: "qa_presence", + QaProfessional: "qa_professional", + QaDiscretion: "qa_discretion", + CreatedAt: "created_at", + UpdatedAt: "updated_at", +} + +// NewRoleCardsDao creates and returns a new DAO object for table data access. +func NewRoleCardsDao(handlers ...gdb.ModelHandler) *RoleCardsDao { + return &RoleCardsDao{ + group: "default", + table: "role_cards", + columns: roleCardsColumns, + handlers: handlers, + } +} + +// DB retrieves and returns the underlying raw database management object of the current DAO. +func (dao *RoleCardsDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of the current DAO. +func (dao *RoleCardsDao) Table() string { + return dao.table +} + +// Columns returns all column names of the current DAO. +func (dao *RoleCardsDao) Columns() RoleCardsColumns { + return dao.columns +} + +// Group returns the database configuration group name of the current DAO. +func (dao *RoleCardsDao) Group() string { + return dao.group +} + +// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +func (dao *RoleCardsDao) Ctx(ctx context.Context) *gdb.Model { + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rolls back the transaction and returns the error if function f returns a non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note: Do not commit or roll back the transaction in function f, +// as it is automatically handled by this function. +func (dao *RoleCardsDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/Backend/internal/dao/internal/subscribe.go b/Backend/internal/dao/internal/subscribe.go new file mode 100644 index 0000000..1cb933d --- /dev/null +++ b/Backend/internal/dao/internal/subscribe.go @@ -0,0 +1,91 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// SubscribeDao is the data access object for the table subscribe. +type SubscribeDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of the current DAO. + columns SubscribeColumns // columns contains all the column names of Table for convenient usage. + handlers []gdb.ModelHandler // handlers for customized model modification. +} + +// SubscribeColumns defines and stores column names for the table subscribe. +type SubscribeColumns struct { + Id string // 自增主键 + UserId string // 用户ID(关注者,本人) + FollowId string // 被关注的用户ID + Status string // 状态:1=关注中 0=取消关注 + Remark string // 备注名(user_id 对 follow_id 的备注) + CreatedAt string // 创建时间 + UpdatedAt string // 更新时间 +} + +// subscribeColumns holds the columns for the table subscribe. +var subscribeColumns = SubscribeColumns{ + Id: "id", + UserId: "user_id", + FollowId: "follow_id", + Status: "status", + Remark: "remark", + CreatedAt: "created_at", + UpdatedAt: "updated_at", +} + +// NewSubscribeDao creates and returns a new DAO object for table data access. +func NewSubscribeDao(handlers ...gdb.ModelHandler) *SubscribeDao { + return &SubscribeDao{ + group: "default", + table: "subscribe", + columns: subscribeColumns, + handlers: handlers, + } +} + +// DB retrieves and returns the underlying raw database management object of the current DAO. +func (dao *SubscribeDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of the current DAO. +func (dao *SubscribeDao) Table() string { + return dao.table +} + +// Columns returns all column names of the current DAO. +func (dao *SubscribeDao) Columns() SubscribeColumns { + return dao.columns +} + +// Group returns the database configuration group name of the current DAO. +func (dao *SubscribeDao) Group() string { + return dao.group +} + +// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +func (dao *SubscribeDao) Ctx(ctx context.Context) *gdb.Model { + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rolls back the transaction and returns the error if function f returns a non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note: Do not commit or roll back the transaction in function f, +// as it is automatically handled by this function. +func (dao *SubscribeDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/Backend/internal/dao/internal/trpg_room.go b/Backend/internal/dao/internal/trpg_room.go new file mode 100644 index 0000000..39c7d9b --- /dev/null +++ b/Backend/internal/dao/internal/trpg_room.go @@ -0,0 +1,115 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// TrpgRoomDao is the data access object for the table trpg_room. +type TrpgRoomDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of the current DAO. + columns TrpgRoomColumns // columns contains all the column names of Table for convenient usage. + handlers []gdb.ModelHandler // handlers for customized model modification. +} + +// TrpgRoomColumns defines and stores column names for the table trpg_room. +type TrpgRoomColumns struct { + Id string // 自增主键 + RoomId string // 房间全局唯一ID(建议UUID) + RoomCode string // 房间号(玩家看到/输入的房间号) + RoomName string // 房间名称 + HostId string // 主持人用户ID(GM/KP/经理,对应用户表id) + HostNickname string // 主持人昵称(冗余字段,可选) + MaxPlayers string // 最大玩家人数(不含主持人,可按需要约定) + CurrentPlayers string // 当前玩家人数(不含主持人) + HasPassword string // 是否有密码:0无 1有 + RoomPassword string // 房间密码(建议存加密/哈希后的密码) + IsPrivate string // 是否私密房:0公开 1私密 + Status string // 房间状态:0未开始 1进行中 2已结束 3已关闭 + SystemName string // 规则系统:如 COC、DND5E 等 + ScenarioName string // 模组/剧本名称 + Description string // 房间简介/招募说明 + CreatedAt string // 房间创建时间 + StartedAt string // 开团时间 + EndedAt string // 结束时间 + UpdatedAt string // 信息最近更新时间 +} + +// trpgRoomColumns holds the columns for the table trpg_room. +var trpgRoomColumns = TrpgRoomColumns{ + Id: "id", + RoomId: "room_id", + RoomCode: "room_code", + RoomName: "room_name", + HostId: "host_id", + HostNickname: "host_nickname", + MaxPlayers: "max_players", + CurrentPlayers: "current_players", + HasPassword: "has_password", + RoomPassword: "room_password", + IsPrivate: "is_private", + Status: "status", + SystemName: "system_name", + ScenarioName: "scenario_name", + Description: "description", + CreatedAt: "created_at", + StartedAt: "started_at", + EndedAt: "ended_at", + UpdatedAt: "updated_at", +} + +// NewTrpgRoomDao creates and returns a new DAO object for table data access. +func NewTrpgRoomDao(handlers ...gdb.ModelHandler) *TrpgRoomDao { + return &TrpgRoomDao{ + group: "default", + table: "trpg_room", + columns: trpgRoomColumns, + handlers: handlers, + } +} + +// DB retrieves and returns the underlying raw database management object of the current DAO. +func (dao *TrpgRoomDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of the current DAO. +func (dao *TrpgRoomDao) Table() string { + return dao.table +} + +// Columns returns all column names of the current DAO. +func (dao *TrpgRoomDao) Columns() TrpgRoomColumns { + return dao.columns +} + +// Group returns the database configuration group name of the current DAO. +func (dao *TrpgRoomDao) Group() string { + return dao.group +} + +// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +func (dao *TrpgRoomDao) Ctx(ctx context.Context) *gdb.Model { + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rolls back the transaction and returns the error if function f returns a non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note: Do not commit or roll back the transaction in function f, +// as it is automatically handled by this function. +func (dao *TrpgRoomDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/Backend/internal/dao/internal/user_department.go b/Backend/internal/dao/internal/user_department.go new file mode 100644 index 0000000..a726aa1 --- /dev/null +++ b/Backend/internal/dao/internal/user_department.go @@ -0,0 +1,87 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// UserDepartmentDao is the data access object for the table user_department. +type UserDepartmentDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of the current DAO. + columns UserDepartmentColumns // columns contains all the column names of Table for convenient usage. + handlers []gdb.ModelHandler // handlers for customized model modification. +} + +// UserDepartmentColumns defines and stores column names for the table user_department. +type UserDepartmentColumns struct { + Id string // + UserId string // 用户ID(对应 users.id) + DeptId string // 部门ID(对应 department.id) + CreatedAt string // 创建时间 + UpdatedAt string // 更新时间 +} + +// userDepartmentColumns holds the columns for the table user_department. +var userDepartmentColumns = UserDepartmentColumns{ + Id: "id", + UserId: "user_id", + DeptId: "dept_id", + CreatedAt: "created_at", + UpdatedAt: "updated_at", +} + +// NewUserDepartmentDao creates and returns a new DAO object for table data access. +func NewUserDepartmentDao(handlers ...gdb.ModelHandler) *UserDepartmentDao { + return &UserDepartmentDao{ + group: "default", + table: "user_department", + columns: userDepartmentColumns, + handlers: handlers, + } +} + +// DB retrieves and returns the underlying raw database management object of the current DAO. +func (dao *UserDepartmentDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of the current DAO. +func (dao *UserDepartmentDao) Table() string { + return dao.table +} + +// Columns returns all column names of the current DAO. +func (dao *UserDepartmentDao) Columns() UserDepartmentColumns { + return dao.columns +} + +// Group returns the database configuration group name of the current DAO. +func (dao *UserDepartmentDao) Group() string { + return dao.group +} + +// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +func (dao *UserDepartmentDao) Ctx(ctx context.Context) *gdb.Model { + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rolls back the transaction and returns the error if function f returns a non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note: Do not commit or roll back the transaction in function f, +// as it is automatically handled by this function. +func (dao *UserDepartmentDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/Backend/internal/dao/internal/users.go b/Backend/internal/dao/internal/users.go new file mode 100644 index 0000000..84d8a65 --- /dev/null +++ b/Backend/internal/dao/internal/users.go @@ -0,0 +1,117 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// UsersDao is the data access object for the table users. +type UsersDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of the current DAO. + columns UsersColumns // columns contains all the column names of Table for convenient usage. + handlers []gdb.ModelHandler // handlers for customized model modification. +} + +// UsersColumns defines and stores column names for the table users. +type UsersColumns struct { + Id string // 用户ID + Account string // 账号 + Password string // 密码哈希 + Nickname string // 昵称 + Gender string // 性别:0未知 1男 2女 + BirthDate string // 生日 + UserType string // 用户类型:normal普通用户,vip为VIP用户 + VipStartAt string // VIP开始时间 + VipEndAt string // VIP结束时间 + CreatedAt string // 创建时间 + UpdatedAt string // 更新时间 + RealityRole string // 现实身份/角色 + AbnormalRole string // 异常身份/角色 + JobTitle string // 职位 + Commendation string // + Admonition string // + Email string // + RedTrace string // 红轨 + YellowTrace string // 黄轨 + BlueTrace string // 蓝轨 +} + +// usersColumns holds the columns for the table users. +var usersColumns = UsersColumns{ + Id: "id", + Account: "account", + Password: "password", + Nickname: "nickname", + Gender: "gender", + BirthDate: "birth_date", + UserType: "user_type", + VipStartAt: "vip_start_at", + VipEndAt: "vip_end_at", + CreatedAt: "created_at", + UpdatedAt: "updated_at", + RealityRole: "reality_role", + AbnormalRole: "abnormal_role", + JobTitle: "job_title", + Commendation: "Commendation", + Admonition: "Admonition", + Email: "email", + RedTrace: "red_trace", + YellowTrace: "yellow_trace", + BlueTrace: "blue_trace", +} + +// NewUsersDao creates and returns a new DAO object for table data access. +func NewUsersDao(handlers ...gdb.ModelHandler) *UsersDao { + return &UsersDao{ + group: "default", + table: "users", + columns: usersColumns, + handlers: handlers, + } +} + +// DB retrieves and returns the underlying raw database management object of the current DAO. +func (dao *UsersDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of the current DAO. +func (dao *UsersDao) Table() string { + return dao.table +} + +// Columns returns all column names of the current DAO. +func (dao *UsersDao) Columns() UsersColumns { + return dao.columns +} + +// Group returns the database configuration group name of the current DAO. +func (dao *UsersDao) Group() string { + return dao.group +} + +// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +func (dao *UsersDao) Ctx(ctx context.Context) *gdb.Model { + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rolls back the transaction and returns the error if function f returns a non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note: Do not commit or roll back the transaction in function f, +// as it is automatically handled by this function. +func (dao *UsersDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/Backend/internal/dao/internal/workout_logs.go b/Backend/internal/dao/internal/workout_logs.go new file mode 100644 index 0000000..d09d6fb --- /dev/null +++ b/Backend/internal/dao/internal/workout_logs.go @@ -0,0 +1,95 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package internal + +import ( + "context" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/frame/g" +) + +// WorkoutLogsDao is the data access object for the table workout_logs. +type WorkoutLogsDao struct { + table string // table is the underlying table name of the DAO. + group string // group is the database configuration group name of the current DAO. + columns WorkoutLogsColumns // columns contains all the column names of Table for convenient usage. + handlers []gdb.ModelHandler // handlers for customized model modification. +} + +// WorkoutLogsColumns defines and stores column names for the table workout_logs. +type WorkoutLogsColumns struct { + Id string // 健身记录ID + UserId string // 关联的用户ID + WorkoutDate string // 健身日期 + StartTime string // 开始时间 + EndTime string // 结束时间 + DurationMinutes string // 时长(分钟) + Calories string // 估算消耗的卡路里 + Description string // 本次训练的备注,比如训练内容、部位等 + CreatedAt string // 记录创建时间 +} + +// workoutLogsColumns holds the columns for the table workout_logs. +var workoutLogsColumns = WorkoutLogsColumns{ + Id: "id", + UserId: "user_id", + WorkoutDate: "workout_date", + StartTime: "start_time", + EndTime: "end_time", + DurationMinutes: "duration_minutes", + Calories: "calories", + Description: "description", + CreatedAt: "created_at", +} + +// NewWorkoutLogsDao creates and returns a new DAO object for table data access. +func NewWorkoutLogsDao(handlers ...gdb.ModelHandler) *WorkoutLogsDao { + return &WorkoutLogsDao{ + group: "default", + table: "workout_logs", + columns: workoutLogsColumns, + handlers: handlers, + } +} + +// DB retrieves and returns the underlying raw database management object of the current DAO. +func (dao *WorkoutLogsDao) DB() gdb.DB { + return g.DB(dao.group) +} + +// Table returns the table name of the current DAO. +func (dao *WorkoutLogsDao) Table() string { + return dao.table +} + +// Columns returns all column names of the current DAO. +func (dao *WorkoutLogsDao) Columns() WorkoutLogsColumns { + return dao.columns +} + +// Group returns the database configuration group name of the current DAO. +func (dao *WorkoutLogsDao) Group() string { + return dao.group +} + +// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation. +func (dao *WorkoutLogsDao) Ctx(ctx context.Context) *gdb.Model { + model := dao.DB().Model(dao.table) + for _, handler := range dao.handlers { + model = handler(model) + } + return model.Safe().Ctx(ctx) +} + +// Transaction wraps the transaction logic using function f. +// It rolls back the transaction and returns the error if function f returns a non-nil error. +// It commits the transaction and returns nil if function f returns nil. +// +// Note: Do not commit or roll back the transaction in function f, +// as it is automatically handled by this function. +func (dao *WorkoutLogsDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) { + return dao.Ctx(ctx).Transaction(ctx, f) +} diff --git a/Backend/internal/dao/role_cards.go b/Backend/internal/dao/role_cards.go new file mode 100644 index 0000000..dc64570 --- /dev/null +++ b/Backend/internal/dao/role_cards.go @@ -0,0 +1,22 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package dao + +import ( + "leke/internal/dao/internal" +) + +// roleCardsDao is the data access object for the table role_cards. +// You can define custom methods on it to extend its functionality as needed. +type roleCardsDao struct { + *internal.RoleCardsDao +} + +var ( + // RoleCards is a globally accessible object for table role_cards operations. + RoleCards = roleCardsDao{internal.NewRoleCardsDao()} +) + +// Add your custom methods and functionality below. diff --git a/Backend/internal/dao/subscribe.go b/Backend/internal/dao/subscribe.go new file mode 100644 index 0000000..370c772 --- /dev/null +++ b/Backend/internal/dao/subscribe.go @@ -0,0 +1,22 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package dao + +import ( + "leke/internal/dao/internal" +) + +// subscribeDao is the data access object for the table subscribe. +// You can define custom methods on it to extend its functionality as needed. +type subscribeDao struct { + *internal.SubscribeDao +} + +var ( + // Subscribe is a globally accessible object for table subscribe operations. + Subscribe = subscribeDao{internal.NewSubscribeDao()} +) + +// Add your custom methods and functionality below. diff --git a/Backend/internal/dao/trpg_room.go b/Backend/internal/dao/trpg_room.go new file mode 100644 index 0000000..f355d79 --- /dev/null +++ b/Backend/internal/dao/trpg_room.go @@ -0,0 +1,22 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package dao + +import ( + "leke/internal/dao/internal" +) + +// trpgRoomDao is the data access object for the table trpg_room. +// You can define custom methods on it to extend its functionality as needed. +type trpgRoomDao struct { + *internal.TrpgRoomDao +} + +var ( + // TrpgRoom is a globally accessible object for table trpg_room operations. + TrpgRoom = trpgRoomDao{internal.NewTrpgRoomDao()} +) + +// Add your custom methods and functionality below. diff --git a/Backend/internal/dao/user_department.go b/Backend/internal/dao/user_department.go new file mode 100644 index 0000000..dae7ea7 --- /dev/null +++ b/Backend/internal/dao/user_department.go @@ -0,0 +1,22 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package dao + +import ( + "leke/internal/dao/internal" +) + +// userDepartmentDao is the data access object for the table user_department. +// You can define custom methods on it to extend its functionality as needed. +type userDepartmentDao struct { + *internal.UserDepartmentDao +} + +var ( + // UserDepartment is a globally accessible object for table user_department operations. + UserDepartment = userDepartmentDao{internal.NewUserDepartmentDao()} +) + +// Add your custom methods and functionality below. diff --git a/Backend/internal/dao/users.go b/Backend/internal/dao/users.go new file mode 100644 index 0000000..54c8f46 --- /dev/null +++ b/Backend/internal/dao/users.go @@ -0,0 +1,22 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package dao + +import ( + "leke/internal/dao/internal" +) + +// usersDao is the data access object for the table users. +// You can define custom methods on it to extend its functionality as needed. +type usersDao struct { + *internal.UsersDao +} + +var ( + // Users is a globally accessible object for table users operations. + Users = usersDao{internal.NewUsersDao()} +) + +// Add your custom methods and functionality below. diff --git a/Backend/internal/dao/workout_logs.go b/Backend/internal/dao/workout_logs.go new file mode 100644 index 0000000..c566fa6 --- /dev/null +++ b/Backend/internal/dao/workout_logs.go @@ -0,0 +1,22 @@ +// ================================================================================= +// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed. +// ================================================================================= + +package dao + +import ( + "leke/internal/dao/internal" +) + +// workoutLogsDao is the data access object for the table workout_logs. +// You can define custom methods on it to extend its functionality as needed. +type workoutLogsDao struct { + *internal.WorkoutLogsDao +} + +var ( + // WorkoutLogs is a globally accessible object for table workout_logs operations. + WorkoutLogs = workoutLogsDao{internal.NewWorkoutLogsDao()} +) + +// Add your custom methods and functionality below. diff --git a/Backend/internal/logic/ForumComments/forum_comments.go b/Backend/internal/logic/ForumComments/forum_comments.go new file mode 100644 index 0000000..ecc04d5 --- /dev/null +++ b/Backend/internal/logic/ForumComments/forum_comments.go @@ -0,0 +1,218 @@ +package ForumComments + +import ( + "context" + "errors" + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/frame/g" + "leke/api/forum/v1" + "leke/internal/dao" + "leke/internal/model/entity" + "leke/internal/service" +) + +// ForumComments 评论相关方法 +type sForumComments struct{} + +func init() { + service.RegisterForumComments(&sForumComments{}) +} + +// ForumCommentsCreate 创建评论 +func (s *sForumComments) Create(ctx context.Context, req *v1.ForumCommentsCreateReq) (res *v1.ForumCommentsCreateRes, err error) { + data := &entity.ForumComments{ + UserId: req.UserId, + PostId: req.PostId, + ParentId: req.ParentId, + Content: req.Content, + Status: req.Status, + LikeCount: 0, + ReplyCount: 0, + } + + result, err := dao.ForumComments.Ctx(ctx).Data(data).OmitEmpty().Insert() + if err != nil { + return nil, gerror.Wrap(err, "创建评论失败") + } + + id, err := result.LastInsertId() + if err != nil { + return nil, gerror.Wrap(err, "获取评论ID失败") + } + + // 更新帖子的评论数 + if req.PostId > 0 { + _, err = dao.ForumPosts.Ctx(ctx).WherePri(req.PostId).OnDuplicate("comment_count", 1).Update() + if err != nil { + g.Log().Warning(ctx, "更新帖子评论数失败:", err) + } + } + + // 如果是回复评论,则更新父评论的回复数 + if req.ParentId > 0 { + _, err = dao.ForumComments.Ctx(ctx).WherePri(req.ParentId).OnDuplicate("reply_count", 1).Update() + if err != nil { + g.Log().Warning(ctx, "更新父评论回复数失败:", err) + } + } + + return &v1.ForumCommentsCreateRes{ + Id: uint64(id), + }, nil +} + +// ForumCommentsUpdate 更新评论 +func (s *sForumComments) Update(ctx context.Context, req *v1.ForumCommentsUpdateReq) (res *v1.ForumCommentsUpdateRes, err error) { + // 检查评论是否存在 + one, err := dao.ForumComments.Ctx(ctx).WherePri(req.Id).One() + if err != nil { + return nil, gerror.Wrap(err, "查询评论失败") + } + if one.IsEmpty() { + return nil, errors.New("评论不存在") + } + + // 更新评论 + _, err = dao.ForumComments.Ctx(ctx).WherePri(req.Id).Data(req).OmitEmpty().Update() + if err != nil { + return nil, gerror.Wrap(err, "更新评论失败") + } + + return &v1.ForumCommentsUpdateRes{ + Id: req.Id, + }, nil +} + +// ForumCommentsView 查看评论 +func (s *sForumComments) View(ctx context.Context, req *v1.ForumCommentsViewReq) (res *v1.ForumCommentsViewRes, err error) { + record, err := dao.ForumComments.Ctx(ctx).WherePri(req.Id).One() + if err != nil { + return nil, gerror.Wrap(err, "查询评论失败") + } + if record.IsEmpty() { + return nil, errors.New("评论不存在") + } + + var entity *entity.ForumComments + err = record.Struct(&entity) + if err != nil { + return nil, gerror.Wrap(err, "解析评论数据失败") + } + + res = &v1.ForumCommentsViewRes{ + Id: entity.Id, + UserId: entity.UserId, + PostId: entity.PostId, + ParentId: entity.ParentId, + Content: entity.Content, + Status: entity.Status, + LikeCount: entity.LikeCount, + ReplyCount: entity.ReplyCount, + CreatedAt: entity.CreatedAt, + UpdatedAt: entity.UpdatedAt, + } + + return res, nil +} + +// ForumCommentsList 评论列表 +func (s *sForumComments) List(ctx context.Context, req *v1.ForumCommentsListReq) (res *v1.ForumCommentsListRes, err error) { + query := dao.ForumComments.Ctx(ctx).OmitEmpty() + + if req.UserId > 0 { + query = query.Where("user_id", req.UserId) + } + if req.PostId > 0 { + query = query.Where("post_id", req.PostId) + } + if req.ParentId > 0 { + query = query.Where("parent_id", req.ParentId) + } + if req.Status != "" { + query = query.Where("status", req.Status) + } + + // 分页查询 + pageResult, err := query.Page(req.Page, req.PageSize).Order("created_at DESC").All() + if err != nil { + return nil, gerror.Wrap(err, "查询评论列表失败") + } + + var list []*v1.ForumCommentsViewRes + for _, record := range pageResult { + var entity *entity.ForumComments + err = record.Struct(&entity) + if err != nil { + return nil, gerror.Wrap(err, "解析评论数据失败") + } + + item := &v1.ForumCommentsViewRes{ + Id: entity.Id, + UserId: entity.UserId, + PostId: entity.PostId, + ParentId: entity.ParentId, + Content: entity.Content, + Status: entity.Status, + LikeCount: entity.LikeCount, + ReplyCount: entity.ReplyCount, + CreatedAt: entity.CreatedAt, + UpdatedAt: entity.UpdatedAt, + } + list = append(list, item) + } + + total, err := query.Count() + if err != nil { + return nil, gerror.Wrap(err, "统计评论总数失败") + } + + res = &v1.ForumCommentsListRes{} + res.List = list + res.Page = req.Page + res.PageSize = req.PageSize + res.Total = total + + return res, nil +} + +// ForumCommentsDelete 删除评论 +func (s *sForumComments) Delete(ctx context.Context, req *v1.ForumCommentsDeleteReq) (res *v1.ForumCommentsDeleteRes, err error) { + // 检查评论是否存在 + one, err := dao.ForumComments.Ctx(ctx).WherePri(req.Id).One() + if err != nil { + return nil, gerror.Wrap(err, "查询评论失败") + } + if one.IsEmpty() { + return nil, errors.New("评论不存在") + } + + var comment *entity.ForumComments + err = one.Struct(&comment) + if err != nil { + return nil, gerror.Wrap(err, "解析评论数据失败") + } + + // 软删除评论 + _, err = dao.ForumComments.Ctx(ctx).WherePri(req.Id).Delete() + if err != nil { + return nil, gerror.Wrap(err, "删除评论失败") + } + + // 更新帖子的评论数 + if comment.PostId > 0 { + _, err = dao.ForumPosts.Ctx(ctx).WherePri(comment.PostId).OnDuplicate("comment_count", 1).Update() + if err != nil { + g.Log().Warning(ctx, "更新帖子评论数失败:", err) + } + } + + // 如果是回复评论,则更新父评论的回复数 + if comment.ParentId > 0 { + _, err = dao.ForumComments.Ctx(ctx).WherePri(comment.ParentId).OnDuplicate("reply_count", 1).Update() + if err != nil { + g.Log().Warning(ctx, "更新父评论回复数失败:", err) + } + } + + return &v1.ForumCommentsDeleteRes{}, nil +} diff --git a/Backend/internal/logic/ForumComments/forum_posts.go b/Backend/internal/logic/ForumComments/forum_posts.go new file mode 100644 index 0000000..8288bf2 --- /dev/null +++ b/Backend/internal/logic/ForumComments/forum_posts.go @@ -0,0 +1,186 @@ +package ForumComments + +import ( + "context" + "errors" + "leke/api/forum/v1" + "leke/internal/dao" + "leke/internal/model/entity" + "leke/internal/service" + + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/frame/g" +) + +type sForumPosts struct{} + +func init() { + service.RegisterForumPosts(&sForumPosts{}) +} + +// ForumPostsCreate 创建帖子 +func (s *sForumPosts) Create(ctx context.Context, req *v1.ForumPostsCreateReq) (res *v1.ForumPostsCreateRes, err error) { + data := &entity.ForumPosts{ + UserId: req.UserId, + Title: req.Title, + Content: req.Content, + CoverImage: req.CoverImage, + Status: req.Status, + ViewCount: 0, + LikeCount: 0, + CommentCount: 0, + } + + result, err := dao.ForumPosts.Ctx(ctx).Data(data).OmitEmpty().Insert() + if err != nil { + return nil, gerror.Wrap(err, "创建帖子失败") + } + + id, err := result.LastInsertId() + if err != nil { + return nil, gerror.Wrap(err, "获取帖子ID失败") + } + + return &v1.ForumPostsCreateRes{ + Id: uint64(id), + }, nil +} + +// ForumPostsUpdate 更新帖子 +func (s *sForumPosts) Update(ctx context.Context, req *v1.ForumPostsUpdateReq) (res *v1.ForumPostsUpdateRes, err error) { + // 检查帖子是否存在 + one, err := dao.ForumPosts.Ctx(ctx).WherePri(req.Id).One() + if err != nil { + return nil, gerror.Wrap(err, "查询帖子失败") + } + if one.IsEmpty() { + return nil, errors.New("帖子不存在") + } + + // 更新帖子 + _, err = dao.ForumPosts.Ctx(ctx).WherePri(req.Id).Data(req).OmitEmpty().Update() + if err != nil { + return nil, gerror.Wrap(err, "更新帖子失败") + } + + return &v1.ForumPostsUpdateRes{ + Id: req.Id, + }, nil +} + +// ForumPostsView 查看帖子 +func (s *sForumPosts) View(ctx context.Context, req *v1.ForumPostsViewReq) (res *v1.ForumPostsViewRes, err error) { + record, err := dao.ForumPosts.Ctx(ctx).WherePri(req.Id).One() + if err != nil { + return nil, gerror.Wrap(err, "查询帖子失败") + } + if record.IsEmpty() { + return nil, errors.New("帖子不存在") + } + + // 更新浏览量 + _, err = dao.ForumPosts.Ctx(ctx).WherePri(req.Id).OnDuplicate("view_count", "view_count+1").Update() + if err != nil { + g.Log().Warning(ctx, "更新浏览量失败:", err) + } + + var entity *entity.ForumPosts + err = record.Struct(&entity) + if err != nil { + return nil, gerror.Wrap(err, "解析帖子数据失败") + } + + res = &v1.ForumPostsViewRes{ + Id: entity.Id, + UserId: entity.UserId, + Title: entity.Title, + Content: entity.Content, + CoverImage: entity.CoverImage, + Status: entity.Status, + ViewCount: entity.ViewCount, + LikeCount: entity.LikeCount, + CommentCount: entity.CommentCount, + CreatedAt: entity.CreatedAt, + UpdatedAt: entity.UpdatedAt, + } + + return res, nil +} + +// ForumPostsList 帖子列表 +func (s *sForumPosts) List(ctx context.Context, req *v1.ForumPostsListReq) (res *v1.ForumPostsListRes, err error) { + query := dao.ForumPosts.Ctx(ctx).OmitEmpty() + + if req.UserId > 0 { + query = query.Where("user_id", req.UserId) + } + if req.Title != "" { + query = query.WhereLike("title", "%"+req.Title+"%") + } + if req.Status != "" { + query = query.Where("status", req.Status) + } + + // 分页查询 + pageResult, err := query.Page(req.Page, req.PageSize).Order("created_at DESC").All() + if err != nil { + return nil, gerror.Wrap(err, "查询帖子列表失败") + } + + var list []*v1.ForumPostsViewRes + for _, record := range pageResult { + var entity *entity.ForumPosts + err = record.Struct(&entity) + if err != nil { + return nil, gerror.Wrap(err, "解析帖子数据失败") + } + + item := &v1.ForumPostsViewRes{ + Id: entity.Id, + UserId: entity.UserId, + Title: entity.Title, + Content: entity.Content, + CoverImage: entity.CoverImage, + Status: entity.Status, + ViewCount: entity.ViewCount, + LikeCount: entity.LikeCount, + CommentCount: entity.CommentCount, + CreatedAt: entity.CreatedAt, + UpdatedAt: entity.UpdatedAt, + } + list = append(list, item) + } + + total, err := query.Count() + if err != nil { + return nil, gerror.Wrap(err, "统计帖子总数失败") + } + + res = &v1.ForumPostsListRes{} + res.List = list + res.PageResult.Page = req.Page + res.PageResult.PageSize = req.PageSize + res.PageResult.Total = total + + return res, nil +} + +// ForumPostsDelete 删除帖子 +func (s *sForumPosts) Delete(ctx context.Context, req *v1.ForumPostsDeleteReq) (res *v1.ForumPostsDeleteRes, err error) { + // 检查帖子是否存在 + one, err := dao.ForumPosts.Ctx(ctx).WherePri(req.Id).One() + if err != nil { + return nil, gerror.Wrap(err, "查询帖子失败") + } + if one.IsEmpty() { + return nil, errors.New("帖子不存在") + } + + // 软删除帖子 + _, err = dao.ForumPosts.Ctx(ctx).WherePri(req.Id).Delete() + if err != nil { + return nil, gerror.Wrap(err, "删除帖子失败") + } + + return &v1.ForumPostsDeleteRes{}, nil +} diff --git a/Backend/internal/logic/containment/containment.go b/Backend/internal/logic/containment/containment.go new file mode 100644 index 0000000..08c4490 --- /dev/null +++ b/Backend/internal/logic/containment/containment.go @@ -0,0 +1,115 @@ +package containment + +import ( + "context" + "github.com/gogf/gf/v2/frame/g" + v1 "leke/api/containment/v1" + "leke/internal/model/response" + "leke/internal/service" +) + +type sContainment struct{} + +func New() *sContainment { + return &sContainment{} +} + +func init() { + service.RegisterContainment(New()) +} + +func (s *sContainment) ContainmentRepoList(ctx context.Context, req *v1.ContainmentRepoListReq) (res *v1.ContainmentRepoListRes, err error) { + m := g.Model("containment_repo") + if req.AgentName != "" { + m.Where("agent_name", req.AgentName) + } + if req.AnomalyName != "" { + m.Where("anomaly_name", req.AnomalyName) + } + if req.RepoName != "" { + m.Where("repo_name", req.RepoName) + } + + // 创建返回结果 + res = &v1.ContainmentRepoListRes{ + PageResult: response.PageResult{ + Page: req.Page, + PageSize: req.PageSize, + }, + } + + // 查询总数 + res.Total, err = m.Count() + if err != nil { + return nil, err + } + + // 分页查询 + m = m.Page(req.Page, req.PageSize) + + // 查询数据列表 + err = m.Scan(&res.List) + if err != nil { + return nil, err + } + + return res, nil +} +func (s *sContainment) ContainmentRepoView(ctx context.Context, req *v1.ContainmentRepoViewReq) (res *v1.ContainmentRepoViewRes, err error) { + // 创建返回结果 + res = &v1.ContainmentRepoViewRes{} + + // 从数据库获取指定ID的数据 + err = g.Model("containment_repo").Where("id", req.Id).Scan(&res.ContainmentRepoInfo) + if err != nil { + return nil, err + } + + return res, nil +} +func (s *sContainment) ContainmentRepoUpdate(ctx context.Context, req *v1.ContainmentRepoUpdateReq) (res *v1.ContainmentRepoUpdateRes, err error) { + // 创建返回结果 + res = &v1.ContainmentRepoUpdateRes{} + + // 准备数据 + data := g.Map{ + "terminal_id": req.TerminalId, + "anomaly_name": req.AnomalyName, + "agent_name": req.AgentName, + "repo_name": req.RepoName, + } + + // 根据ID判断是更新还是新增 + if req.Id > 0 { + // 更新操作 + _, err = g.Model("containment_repo").Data(data).Where("id", req.Id).Update() + res.Id = req.Id + } else { + // 新增操作 + result, err := g.Model("containment_repo").Data(data).Insert() + if err != nil { + return nil, err + } + + // 获取插入记录的ID + id, err := result.LastInsertId() + if err != nil { + return nil, err + } + res.Id = uint64(id) + } + + return res, err +} +func (s *sContainment) ContainmentRepoDelete(ctx context.Context, req *v1.ContainmentRepoDeleteReq) (res *v1.ContainmentRepoDeleteRes, err error) { + // 创建返回结果 + res = &v1.ContainmentRepoDeleteRes{} + + // 删除指定ID的记录 + _, err = g.Model("containment_repo").Where("id", req.Id).Delete() + if err != nil { + return nil, err + } + + return res, nil +} diff --git a/Backend/internal/logic/department/department.go b/Backend/internal/logic/department/department.go new file mode 100644 index 0000000..85473b4 --- /dev/null +++ b/Backend/internal/logic/department/department.go @@ -0,0 +1,143 @@ +package department + +import ( + "context" + v1 "leke/api/department/v1" + "leke/internal/dao" + "leke/internal/model" + "leke/internal/service" +) + +type sDepartment struct{} + +func New() *sDepartment { + return &sDepartment{} +} + +func init() { + service.RegisterDepartment(New()) +} + +// DepartmentList 获取部门列表 +func (s *sDepartment) DepartmentList(ctx context.Context, req *v1.DepartmentListReq) (res *v1.DepartmentListRes, err error) { + // 创建数据库查询模型 + m := dao.Department.Ctx(ctx) + + // 根据分部名称查询 + if req.BranchName != "" { + m = m.WhereLike(dao.Department.Columns().BranchName, "%"+req.BranchName+"%") + } + + // 根据经理名称查询 + if req.ManagerName != "" { + m = m.WhereLike(dao.Department.Columns().ManagerName, "%"+req.ManagerName+"%") + } + + // 根据用户ID查询 + if req.UserId != 0 { + m = m.Where(dao.Department.Columns().UserId, req.UserId) + } + + // 查询总数 + total, err := m.Count() + if err != nil { + return nil, err + } + + // 分页处理 + m = m.Page(req.Page, req.PageSize) + + // 查询列表数据 + var departmentList []*v1.Department + err = m.Scan(&departmentList) + if err != nil { + return nil, err + } + + res = &v1.DepartmentListRes{ + List: departmentList, + } + + // 设置分页信息 + req.PageResult.Total = int(total) + req.PageResult.Page = req.Page + req.PageResult.PageSize = req.PageSize + + return +} + +// DepartmentView 获取部门详情 +func (s *sDepartment) DepartmentView(ctx context.Context, req *v1.DepartmentViewReq) (res *v1.DepartmentViewRes, err error) { + // 创建数据库查询模型 + m := dao.Department.Ctx(ctx) + + // 根据ID查询 + m = m.Where(dao.Department.Columns().Id, req.Id) + + res = &v1.DepartmentViewRes{} + err = m.Scan(&res.Department) + if err != nil { + return nil, err + } + + return +} + +// DepartmentCreate 创建部门 +func (s *sDepartment) DepartmentCreate(ctx context.Context, req *v1.DepartmentCreateReq) (res *v1.DepartmentCreateRes, err error) { + // 准备数据 + departmentData := model.Department{ + UserId: req.UserId, + BranchName: req.BranchName, + TerminalCount: req.TerminalCount, + Weather: req.Weather, + ManagerName: req.ManagerName, + Location: req.Location, + } + + // 插入数据 + result, err := dao.Department.Ctx(ctx).Data(departmentData).Insert() + if err != nil { + return nil, err + } + + // 获取插入的ID + lastInsertId, err := result.LastInsertId() + if err != nil { + return nil, err + } + + res = &v1.DepartmentCreateRes{ + Id: uint64(lastInsertId), + } + + return +} + +// DepartmentUpdate 更新部门 +func (s *sDepartment) DepartmentUpdate(ctx context.Context, req *v1.DepartmentUpdateReq) (res *v1.DepartmentUpdateRes, err error) { + // 更新数据 + _, err = dao.Department.Ctx(ctx).Data(req).Where(dao.Department.Columns().Id, req.Id).Update() + if err != nil { + return nil, err + } + + res = &v1.DepartmentUpdateRes{ + Id: req.Id, + } + + return +} + +// DepartmentDelete 删除部门 +func (s *sDepartment) DepartmentDelete(ctx context.Context, req *v1.DepartmentDeleteReq) (res *v1.DepartmentDeleteRes, err error) { + // 根据ID删除 + _, err = dao.Department.Ctx(ctx).Where(dao.Department.Columns().Id, req.Id).Delete() + if err != nil { + return nil, err + } + + res = &v1.DepartmentDeleteRes{} + + return +} diff --git a/Backend/internal/logic/fans/fans.go b/Backend/internal/logic/fans/fans.go new file mode 100644 index 0000000..e31e324 --- /dev/null +++ b/Backend/internal/logic/fans/fans.go @@ -0,0 +1,182 @@ +package fans + +import ( + "context" + + "github.com/gogf/gf/v2/os/gtime" + v1 "leke/api/user/v1" + "leke/internal/dao" + "leke/internal/service" +) + +type sFans struct{} + +func New() *sFans { + return &sFans{} +} + +func init() { + service.RegisterFans(New()) +} + +// FansList 获取粉丝列表 +// +// 参数: +// - ctx context.Context: 上下文信息 +// - req *v1.FansListReq: 粉丝列表请求参数 +// +// 返回: +// - res *v1.FansListRes: 粉丝列表响应结果 +// - err error: 错误信息 +func (s *sFans) FansList(ctx context.Context, req *v1.FansListReq) (res *v1.FansListRes, err error) { + // 创建数据库查询模型 + m := dao.Fans.Ctx(ctx) + + // 根据用户ID查询 + m = m.Where(dao.Fans.Columns().UserId, req.UserId) + + // 根据状态查询 + if req.Status != 0 { + m = m.Where(dao.Fans.Columns().Status, req.Status) + } + + // 查询总数 + total, err := m.Count() + if err != nil { + return nil, err + } + + // 分页处理 + m = m.Page(req.Page, req.PageSize) + + // 查询列表数据 + var fansList []*v1.FansListItem + err = m.Scan(&fansList) + if err != nil { + return nil, err + } + + res = &v1.FansListRes{ + List: fansList, + } + + // 设置分页信息 + req.PageResult.Total = int(total) + req.PageResult.Page = req.Page + req.PageResult.PageSize = req.PageSize + + return +} + +// FansView 获取粉丝详情 +// +// 参数: +// - ctx context.Context: 上下文信息 +// - req *v1.FansViewReq: 粉丝详情请求参数 +// +// 返回: +// - res *v1.FansViewRes: 粉丝详情响应结果 +// - err error: 错误信息 +func (s *sFans) FansView(ctx context.Context, req *v1.FansViewReq) (res *v1.FansViewRes, err error) { + // 创建数据库查询模型 + m := dao.Fans.Ctx(ctx) + + // 根据ID查询 + m = m.Where(dao.Fans.Columns().Id, req.Id) + + res = &v1.FansViewRes{} + err = m.Scan(&res.FansListItem) + if err != nil { + return nil, err + } + + return +} + +// FansUpdate 更新粉丝信息 +// +// 参数: +// - ctx context.Context: 上下文信息 +// - req *v1.FansUpdateReq: 粉丝更新请求参数 +// +// 返回: +// - res *v1.FansUpdateRes: 粉丝更新响应结果 +// - err error: 错误信息 +func (s *sFans) FansUpdate(ctx context.Context, req *v1.FansUpdateReq) (res *v1.FansUpdateRes, err error) { + // 创建数据库查询模型 + m := dao.Fans.Ctx(ctx) + + // 更新粉丝信息 + _, err = m.Data(req).Where(dao.Fans.Columns().Id, req.Id).Update() + if err != nil { + return nil, err + } + + res = &v1.FansUpdateRes{ + Id: req.Id, + } + + return +} + +// FansDelete 删除粉丝关系 +// +// 参数: +// - ctx context.Context: 上下文信息 +// - req *v1.FansDeleteReq: 粉丝删除请求参数 +// +// 返回: +// - res *v1.FansDeleteRes: 粉丝删除响应结果 +// - err error: 错误信息 +func (s *sFans) FansDelete(ctx context.Context, req *v1.FansDeleteReq) (res *v1.FansDeleteRes, err error) { + // 根据ID删除粉丝关系 + _, err = dao.Fans.Ctx(ctx).Where(dao.Fans.Columns().Id, req.Id).Delete() + if err != nil { + return nil, err + } + + res = &v1.FansDeleteRes{} + return +} + +// FansCreate 创建粉丝关系 +// +// 参数: +// - ctx context.Context: 上下文信息 +// - req *v1.FansCreateReq: 粉丝创建请求参数 +// +// 返回: +// - res *v1.FansCreateRes: 粉丝创建响应结果 +// - err error: 错误信息 +func (s *sFans) FansCreate(ctx context.Context, req *v1.FansCreateReq) (res *v1.FansCreateRes, err error) { + // 创建数据库查询模型 + m := dao.Fans.Ctx(ctx) + + // 准备数据 + data := map[string]interface{}{ + "user_id": req.UserId, + "fan_id": req.FanId, + "status": 1, // 默认状态为关注中 + "remark": req.Remark, + "created_at": gtime.Now(), + "updated_at": gtime.Now(), + } + + // 插入数据 + result, err := m.Data(data).Insert() + if err != nil { + return nil, err + } + + // 获取插入的ID + lastInsertId, err := result.LastInsertId() + if err != nil { + return nil, err + } + + res = &v1.FansCreateRes{ + Id: uint64(lastInsertId), + } + + return +} diff --git a/Backend/internal/logic/logic.go b/Backend/internal/logic/logic.go new file mode 100644 index 0000000..fb653dc --- /dev/null +++ b/Backend/internal/logic/logic.go @@ -0,0 +1,17 @@ +// ========================================================================== +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ========================================================================== + +package logic + +import ( + _ "leke/internal/logic/ForumComments" + _ "leke/internal/logic/containment" + _ "leke/internal/logic/department" + _ "leke/internal/logic/fans" + _ "leke/internal/logic/login" + _ "leke/internal/logic/room" + _ "leke/internal/logic/subscribe" + _ "leke/internal/logic/trace" + _ "leke/internal/logic/user" +) diff --git a/Backend/internal/logic/login/login.go b/Backend/internal/logic/login/login.go new file mode 100644 index 0000000..4eb03c3 --- /dev/null +++ b/Backend/internal/logic/login/login.go @@ -0,0 +1,296 @@ +// Package login internal/logic/login/login.go +package login + +import ( + "context" + "database/sql" + "errors" + "fmt" + v1 "leke/api/login/v1" + "leke/internal/consts" + "leke/internal/dao" + "leke/internal/middleware" + "leke/internal/model" + "leke/internal/service" + "time" + + "github.com/gogf/gf/v2/crypto/gmd5" + "github.com/gogf/gf/v2/errors/gcode" + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/glog" + "github.com/gogf/gf/v2/util/grand" + "github.com/golang-jwt/jwt/v5" +) + +// 注意:名字改成 sLogin,首字母小写 s + 大写 Login +type sLogin struct{} + +func New() *sLogin { + return &sLogin{} +} + +// 关键!!在 init 里完成注册 +func init() { + service.RegisterLogin(New()) +} + +func (s *sLogin) Register(ctx context.Context, loginReq *v1.RegisterReq) (res *v1.RegisterRes, err error) { + + // 判断参数是否正常 账户的长度要在5-10 密码要经过MD5加密 + if len(loginReq.Account) < 5 || len(loginReq.Account) > 10 { + glog.Debugf(ctx, "账户长度校验失败,长度:%d", len(loginReq.Account)) + return nil, errors.New("账户长度要在5-10之间") + } + if len(loginReq.Password) < 6 || len(loginReq.Password) > 32 { + glog.Debugf(ctx, "密码长度校验失败,长度:%d", len(loginReq.Password)) + return nil, errors.New("密码长度要在6-32之间") + } + + password, err := gmd5.Encrypt(loginReq.Password + consts.Salt) + if err != nil { + glog.Errorf(ctx, "密码加密失败:%v", err) + return nil, gerror.Wrap(err, "密码加密失败,请稍后重试!") + } + + loginData := model.LoginField{ + Account: loginReq.Account, + Password: password, + Nickname: "特工007", + } + + _, err = dao.Users.Ctx(ctx).Data(loginData).Insert() + if err != nil { + glog.Errorf(ctx, "插入账号到数据库失败:%v", err) + return nil, gerror.Wrap(err, "插入账号到数,据库失败,请稍后重试!") + } + return +} + +func (s *sLogin) Login(ctx context.Context, loginReq *v1.LoginReq) (res *v1.LoginRes, err error) { + + //校验参数 + if len(loginReq.Account) < 5 || len(loginReq.Account) > 10 { + glog.Debugf(ctx, "账户长度校验失败,长度:%d", len(loginReq.Account)) + return nil, errors.New("账户长度要在5-10之间") + } + if len(loginReq.Password) < 6 || len(loginReq.Password) > 32 { + glog.Debugf(ctx, "密码长度校验失败,长度:%d", len(loginReq.Password)) + return nil, errors.New("密码长度要在6-32之间") + } + + // 2. 先根据账号查数据库 + var user model.LoginField + err = dao.Users.Ctx(ctx).Where("account", loginReq.Account).Scan(&user) + if err != nil { + glog.Errorf(ctx, "查询用户失败:%v", err) + return nil, gerror.Wrap(err, "登录失败,请稍后重试!") + } + if user.Account == "" { + // 没查到用户 + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "账号或密码错误") + } + + // 3. 使用"相同规则"加密用户输入的密码,并和 DB 中的密码对比 + // 注意:这里的加密规则必须和 Register 时一致 + encryptedInput, err := gmd5.Encrypt(loginReq.Password + consts.Salt) + if err != nil { + glog.Errorf(ctx, "密码加密失败:%v", err) + return nil, gerror.Wrap(err, "密码加密失败,请稍后重试!") + } + if user.Password != encryptedInput { + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "账号或密码错误") + } + + // 5. 种 Session(如果你确实需要 session) + if err := setSession(ctx, user.Account); err != nil { + glog.Errorf(ctx, "设置Session失败:%v", err) + return nil, gerror.Wrap(err, "设置登录状态失败,请稍后重试!") + } + + //JWT 生成 + // Create claims with user information + claims := &middleware.JWTClaims{ + Username: loginReq.Account, + RegisteredClaims: jwt.RegisteredClaims{ + ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)), + IssuedAt: jwt.NewNumericDate(time.Now()), + NotBefore: jwt.NewNumericDate(time.Now()), + }, + } + + // 生成Token + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + signedToken, err := token.SignedString([]byte(middleware.JwtSecretKey)) + if err != nil { + return nil, gerror.NewCode(gcode.CodeInternalError, "Failed to generate token") + } + res = &v1.LoginRes{ + Token: signedToken, + Account: user.Account, + } + return +} + +// 通过邮箱注册 +func (s *sLogin) RegisterByEmail(ctx context.Context, req *v1.RegisterByEmailReq) (res *v1.RegisterByEmailRes, err error) { + // 先验证验证码 + emailService := service.NewEmailService() + if !emailService.VerifyVerificationCode(req.Email, req.Code) { + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "验证码错误或已过期") + } + + // 检查邮箱是否已存在 + count, err := dao.Users.Ctx(ctx).Where("email", req.Email).Count() + if err != nil { + return nil, gerror.Wrap(err, "查询用户失败") + } + if count > 0 { + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "该邮箱已被注册") + } + + // 生成随机账号和密码 + account := fmt.Sprintf("user_%s", grand.S(6)) + password := grand.S(10) + + // 加密密码 + encryptedPassword, err := gmd5.Encrypt(password + consts.Salt) + if err != nil { + return nil, gerror.Wrap(err, "密码加密失败") + } + + // 创建用户 + userData := model.LoginField{ + Account: account, + Password: encryptedPassword, + Nickname: "特工007", + Email: req.Email, + } + + _, err = dao.Users.Ctx(ctx).Data(userData).Insert() + if err != nil { + return nil, gerror.Wrap(err, "注册失败") + } + + res = &v1.RegisterByEmailRes{ + Email: req.Email, + } + return +} + +// 通过邮箱登录 +func (s *sLogin) LoginByEmail(ctx context.Context, req *v1.LoginByEmailReq) (res *v1.LoginByEmailRes, err error) { + emailService := service.NewEmailService() + + // 验证验证码 + if !emailService.VerifyVerificationCode(req.Email, req.Code) { + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "验证码错误或已过期") + } + + // 根据邮箱查找用户 + var user model.LoginField + err = dao.Users.Ctx(ctx).Where("email", req.Email).Scan(&user) + if err != nil && err != sql.ErrNoRows { + return nil, gerror.WrapCode(gcode.CodeInternalError, err, "查询用户失败") + } + + // 如果用户不存在,则创建新用户 + if user.Email == "" { + account := fmt.Sprintf("user_%s", grand.S(6)) + password := grand.S(10) + + // 加密密码 + encryptedPassword, err := gmd5.Encrypt(password + consts.Salt) + if err != nil { + return nil, gerror.Wrap(err, "密码加密失败") + } + + // 创建用户 + userData := model.LoginField{ + Account: account, + Password: encryptedPassword, + Nickname: "特工007", + Email: req.Email, + } + + _, err = dao.Users.Ctx(ctx).Data(userData).Insert() + if err != nil { + return nil, gerror.Wrap(err, "创建用户失败") + } + + user = userData + } + + // 设置session + if err := setSession(ctx, user.Account); err != nil { + glog.Errorf(ctx, "设置Session失败:%v", err) + return nil, gerror.Wrap(err, "设置登录状态失败,请稍后重试!") + } + + // 生成JWT Token + claims := &middleware.JWTClaims{ + Username: user.Account, + RegisteredClaims: jwt.RegisteredClaims{ + ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)), + IssuedAt: jwt.NewNumericDate(time.Now()), + NotBefore: jwt.NewNumericDate(time.Now()), + }, + } + + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + signedToken, err := token.SignedString([]byte(middleware.JwtSecretKey)) + if err != nil { + return nil, gerror.NewCode(gcode.CodeInternalError, "Failed to generate token") + } + + res = &v1.LoginByEmailRes{ + Token: signedToken, + Email: req.Email, + } + + return +} + +// 发送验证码 +func (s *sLogin) SendVerificationCode(ctx context.Context, req *v1.SendVerificationCodeReq) (res *v1.SendVerificationCodeRes, err error) { + emailService := service.NewEmailService() + + err = emailService.SendVerificationCode(ctx, req.Email) + if err != nil { + return nil, gerror.Wrap(err, "发送验证码失败") + } + + res = &v1.SendVerificationCodeRes{ + Success: true, + } + + return +} + +// 退出登录 +func (s *sLogin) Logout(ctx context.Context, req *v1.LogoutReq) (res *v1.LogoutRes, err error) { + // 从上下文里拿到当前请求 + r := g.RequestFromCtx(ctx) + if r == nil { + return nil, gerror.New("无效的请求上下文") + } + + // 1. 删除当前登录用到的 Session 信息 + // 和 Login / setSession 里保持一致:都是用 "userAccount" 这个 key + if err = r.Session.Remove("userAccount"); err != nil { + glog.Errorf(ctx, "删除Session失败:%v", err) + return nil, gerror.Wrap(err, "退出登录失败,请稍后重试!") + } + + // 如果你想彻底清空这个 Session(不只删一个字段),可以用: + // if err = r.Session.RemoveAll(); err != nil { ... } + + // 2. 构造返回结果(一般是一个空结构就够了) + res = &v1.LogoutRes{} + return res, nil +} + +// 给前端种session +func setSession(ctx context.Context, account string) error { + return g.RequestFromCtx(ctx).Session.Set("userAccount", account) +} diff --git a/Backend/internal/logic/room/room.go b/Backend/internal/logic/room/room.go new file mode 100644 index 0000000..a11aa1e --- /dev/null +++ b/Backend/internal/logic/room/room.go @@ -0,0 +1,252 @@ +package room + +import ( + "context" + v1 "leke/api/room/v1" + "leke/internal/dao" + "leke/internal/model" + "leke/internal/model/entity" + "leke/internal/service" +) + +type sRoom struct{} + +func New() *sRoom { + return &sRoom{} +} + +func init() { + service.RegisterRoom(New()) +} + +func (s *sRoom) RoomList(ctx context.Context, req *v1.RoomListReq) (res *v1.RoomListRes, err error) { + // 构建查询条件 + m := dao.TrpgRoom.Ctx(ctx) + + if req.RoomCode != "" { + m = m.Where(dao.TrpgRoom.Columns().RoomCode, req.RoomCode) + } + if req.RoomName != "" { + m = m.Where(dao.TrpgRoom.Columns().RoomName, req.RoomName) + } + if req.HostId != 0 { + m = m.Where(dao.TrpgRoom.Columns().HostId, req.HostId) + } + if req.Status != 0 { + m = m.Where(dao.TrpgRoom.Columns().Status, req.Status) + } + if req.SystemName != "" { + m = m.Where(dao.TrpgRoom.Columns().SystemName, req.SystemName) + } + + // 分页处理 + if req.Page <= 0 { + req.Page = 1 + } + if req.PageSize <= 0 { + req.PageSize = 10 + } + + // 查询总数 + totalCount, err := m.Count() + if err != nil { + return nil, err + } + + // 查询数据列表 + var rooms []*entity.TrpgRoom + err = m.Page(req.Page, req.PageSize).Scan(&rooms) + if err != nil { + return nil, err + } + + // 组装返回结果 + res = &v1.RoomListRes{ + PageResult: req.PageResult, + Rooms: make([]*model.RoomParams, len(rooms)), + } + + // 转换数据格式 + for i, room := range rooms { + res.Rooms[i] = &model.RoomParams{ + Id: room.Id, + RoomId: room.RoomId, + RoomCode: room.RoomCode, + RoomName: room.RoomName, + HostId: room.HostId, + HostNickname: room.HostNickname, + MaxPlayers: room.MaxPlayers, + CurrentPlayers: room.CurrentPlayers, + HasPassword: room.HasPassword, + IsPrivate: room.IsPrivate, + Status: room.Status, + SystemName: room.SystemName, + ScenarioName: room.ScenarioName, + Description: room.Description, + CreatedAt: room.CreatedAt, + StartedAt: room.StartedAt, + EndedAt: room.EndedAt, + UpdatedAt: room.UpdatedAt, + } + } + + // 设置分页信息 + res.PageResult.Total = totalCount + res.PageResult.Page = req.Page + res.PageResult.PageSize = req.PageSize + + return res, nil +} + +func (s *sRoom) RoomView(ctx context.Context, req *v1.RoomViewReq) (res *v1.RoomViewRes, err error) { + // 构建查询条件 + m := dao.TrpgRoom.Ctx(ctx) + + if req.Id != 0 { + m = m.Where(dao.TrpgRoom.Columns().Id, req.Id) + } + if req.RoomCode != "" { + m = m.Where(dao.TrpgRoom.Columns().RoomCode, req.RoomCode) + } + + // 查询数据 + var room *entity.TrpgRoom + err = m.Scan(&room) + if err != nil { + return nil, err + } + if room == nil { + return nil, nil + } + + // 组装返回结果 + res = &v1.RoomViewRes{ + RoomView: model.RoomView{ + Id: room.Id, + RoomId: room.RoomId, + RoomCode: room.RoomCode, + RoomName: room.RoomName, + HostId: room.HostId, + HostNickname: room.HostNickname, + MaxPlayers: room.MaxPlayers, + CurrentPlayers: room.CurrentPlayers, + HasPassword: room.HasPassword, + IsPrivate: room.IsPrivate, + Status: room.Status, + SystemName: room.SystemName, + ScenarioName: room.ScenarioName, + Description: room.Description, + CreatedAt: room.CreatedAt, + StartedAt: room.StartedAt, + EndedAt: room.EndedAt, + }, + } + + return res, nil +} + +func (s *sRoom) RoomUpdate(ctx context.Context, req *v1.RoomUpdateReq) (res *v1.RoomUpdateRes, err error) { + // 根据房间号更新房间信息 + data := model.RoomParams{ + RoomName: req.RoomName, + HostId: req.HostId, + HostNickname: req.HostNickname, + MaxPlayers: req.MaxPlayers, + CurrentPlayers: req.CurrentPlayers, + HasPassword: req.HasPassword, + IsPrivate: req.IsPrivate, + Status: req.Status, + SystemName: req.SystemName, + ScenarioName: req.ScenarioName, + Description: req.Description, + } + + _, err = dao.TrpgRoom.Ctx(ctx).Data(data).Where(dao.TrpgRoom.Columns().RoomCode, req.RoomCode).Update() + if err != nil { + return nil, err + } + + // 获取更新后的房间ID + var room *entity.TrpgRoom + err = dao.TrpgRoom.Ctx(ctx).Where(dao.TrpgRoom.Columns().RoomCode, req.RoomCode).Scan(&room) + if err != nil { + return nil, err + } + + res = &v1.RoomUpdateRes{ + Id: room.Id, + } + + return res, nil +} + +func (s *sRoom) RoomDelete(ctx context.Context, req *v1.RoomDeleteReq) (res *v1.RoomDeleteRes, err error) { + // 根据房间号删除房间 + _, err = dao.TrpgRoom.Ctx(ctx).Where(dao.TrpgRoom.Columns().RoomCode, req.RoomCode).Delete() + if err != nil { + return nil, err + } + + res = &v1.RoomDeleteRes{} + return res, nil +} + +func (s *sRoom) RoomCreate(ctx context.Context, req *v1.RoomCreateReq) (res *v1.RoomCreateRes, err error) { + // 准备房间数据 + room := entity.TrpgRoom{ + RoomCode: req.RoomCode, + RoomName: req.RoomName, + HostId: req.HostId, + MaxPlayers: req.MaxPlayers, + HasPassword: req.HasPassword, + IsPrivate: req.IsPrivate, + SystemName: req.SystemName, + ScenarioName: req.ScenarioName, + Description: req.Description, + Status: 0, // 默认状态为未开始 + } + + // 插入数据库 + result, err := dao.TrpgRoom.Ctx(ctx).Data(room).Insert() + if err != nil { + return nil, err + } + + // 获取插入的ID + id, err := result.LastInsertId() + if err != nil { + return nil, err + } + + // 返回结果 + res = &v1.RoomCreateRes{ + Id: uint64(id), + RoomCode: req.RoomCode, + } + + return res, nil +} + +func (s *sRoom) RoomJoin(ctx context.Context, req *v1.RoomJoinReq) (res *v1.RoomJoinRes, err error) { + if req.RoomId == 0 { + return nil, nil + } + + var room *entity.TrpgRoom + err = dao.TrpgRoom.Ctx(ctx). + Where(dao.TrpgRoom.Columns().Id, req.RoomId). + Scan(&room) + if err != nil { + return nil, err + } + if room == nil { + return nil, nil + } + + res = &v1.RoomJoinRes{ + Id: room.Id, + RoomCode: room.RoomCode, + } + + return res, nil +} diff --git a/Backend/internal/logic/subscribe/subscribe.go b/Backend/internal/logic/subscribe/subscribe.go new file mode 100644 index 0000000..44d7068 --- /dev/null +++ b/Backend/internal/logic/subscribe/subscribe.go @@ -0,0 +1,146 @@ +package subscribe + +import ( + "context" + v1 "leke/api/user/v1" + "leke/internal/dao" + "leke/internal/service" + "github.com/gogf/gf/v2/os/gtime" +) + +type sSubscribe struct{} + +func New() *sSubscribe { + return &sSubscribe{} +} + +func init() { + service.RegisterSubscribe(New()) +} + +// SubscribeList 获取关注列表 +func (s *sSubscribe) SubscribeList(ctx context.Context, req *v1.SubscribeListReq) (res *v1.SubscribeListRes, err error) { + // 创建数据库查询模型 + m := dao.Subscribe.Ctx(ctx) + + // 根据用户ID查询 + m = m.Where(dao.Subscribe.Columns().UserId, req.UserId) + + // 根据状态查询 + if req.Status != 0 { + m = m.Where(dao.Subscribe.Columns().Status, req.Status) + } + + // 根据被关注用户ID查询 + if req.FollowId != 0 { + m = m.Where(dao.Subscribe.Columns().FollowId, req.FollowId) + } + + // 查询总数 + total, err := m.Count() + if err != nil { + return nil, err + } + + // 分页处理 + m = m.Page(req.Page, req.PageSize) + + // 查询列表数据 + var subscribeList []*v1.SubscribeListItem + err = m.Scan(&subscribeList) + if err != nil { + return nil, err + } + + res = &v1.SubscribeListRes{ + List: subscribeList, + } + + // 设置分页信息 + req.PageResult.Total = int(total) + req.PageResult.Page = req.Page + req.PageResult.PageSize = req.PageSize + + return +} + +// SubscribeView 获取关注详情 +func (s *sSubscribe) SubscribeView(ctx context.Context, req *v1.SubscribeViewReq) (res *v1.SubscribeViewRes, err error) { + // 创建数据库查询模型 + m := dao.Subscribe.Ctx(ctx) + + // 根据ID查询 + m = m.Where(dao.Subscribe.Columns().Id, req.Id) + + res = &v1.SubscribeViewRes{} + err = m.Scan(&res.SubscribeListItem) + if err != nil { + return nil, err + } + + return +} + +// SubscribeUpdate 更新关注信息 +func (s *sSubscribe) SubscribeUpdate(ctx context.Context, req *v1.SubscribeUpdateReq) (res *v1.SubscribeUpdateRes, err error) { + // 创建数据库查询模型 + m := dao.Subscribe.Ctx(ctx) + + // 更新关注信息 + _, err = m.Data(req).Where(dao.Subscribe.Columns().Id, req.Id).Update() + if err != nil { + return nil, err + } + + res = &v1.SubscribeUpdateRes{ + Id: req.Id, + } + + return +} + +// SubscribeDelete 删除关注关系 +func (s *sSubscribe) SubscribeDelete(ctx context.Context, req *v1.SubscribeDeleteReq) (res *v1.SubscribeDeleteRes, err error) { + // 根据ID删除关注关系 + _, err = dao.Subscribe.Ctx(ctx).Where(dao.Subscribe.Columns().Id, req.Id).Delete() + if err != nil { + return nil, err + } + + res = &v1.SubscribeDeleteRes{} + return +} + +// SubscribeCreate 创建关注关系 +func (s *sSubscribe) SubscribeCreate(ctx context.Context, req *v1.SubscribeCreateReq) (res *v1.SubscribeCreateRes, err error) { + // 创建数据库查询模型 + m := dao.Subscribe.Ctx(ctx) + + // 准备数据 + data := map[string]interface{}{ + "user_id": req.UserId, + "follow_id": req.FollowId, + "status": 1, // 默认状态为关注中 + "remark": req.Remark, + "created_at": gtime.Now(), + "updated_at": gtime.Now(), + } + + // 插入数据 + result, err := m.Data(data).Insert() + if err != nil { + return nil, err + } + + // 获取插入的ID + lastInsertId, err := result.LastInsertId() + if err != nil { + return nil, err + } + + res = &v1.SubscribeCreateRes{ + Id: uint64(lastInsertId), + } + + return +} \ No newline at end of file diff --git a/Backend/internal/logic/trace/trace.go b/Backend/internal/logic/trace/trace.go new file mode 100644 index 0000000..296eb7e --- /dev/null +++ b/Backend/internal/logic/trace/trace.go @@ -0,0 +1,189 @@ +package trace + +import ( + "context" + "fmt" + v1 "leke/api/user/v1" + "leke/internal/dao" + "leke/internal/model/entity" + "leke/internal/service" + + "github.com/gogf/gf/v2/database/gdb" + "github.com/gogf/gf/v2/os/gtime" +) + +type sTrace struct{} + +func NewTrace() *sTrace { + return &sTrace{} +} + +func init() { + service.RegisterTrace(NewTrace()) +} + +// TraceList 获取轨迹列表 +func (s *sTrace) TraceList(ctx context.Context, req *v1.TraceListReq) (res *v1.TraceListRes, err error) { + // 创建数据库查询模型 + m := dao.RoleCards.Ctx(ctx) + + // 根据角色ID查询 + if req.RoleId != 0 { + m = m.Where(dao.RoleCards.Columns().Id, req.RoleId) + } + + // 查询总数 + total, err := m.Count() + if err != nil { + return nil, err + } + + // 分页处理 + m = m.Page(req.Page, req.PageSize) + + // 查询列表数据 (Scan 到实体) + var roleCards []*entity.RoleCards + err = m.Scan(&roleCards) + if err != nil { + return nil, err + } + + // 转换为视图参数 + traceList := make([]*v1.TraceViewParams, 0, len(roleCards)) + for _, card := range roleCards { + traceList = append(traceList, &v1.TraceViewParams{ + UserId: card.UserId, + RoleId: card.Id, + DepartmentId: card.DepartmentId, + RedTrace: int(card.RedTrack), + YellowTrace: int(card.YellowTrack), + BlueTrace: int(card.BlueTrack), + }) + } + + res = &v1.TraceListRes{ + List: traceList, + } + + // 设置分页信息 + req.PageResult.Total = int(total) + req.PageResult.Page = req.Page + req.PageResult.PageSize = req.PageSize + + return +} + +// TraceView 查看轨迹详情 +func (s *sTrace) TraceView(ctx context.Context, req *v1.TraceViewReq) (res *v1.TraceViewRes, err error) { + // 创建数据库查询模型 + m := dao.RoleCards.Ctx(ctx) + + // 根据角色ID查询 + var card entity.RoleCards + err = m.Where(dao.RoleCards.Columns().Id, req.RoleId).Scan(&card) + if err != nil { + return nil, err + } + + res = &v1.TraceViewRes{ + TraceViewParams: v1.TraceViewParams{ + UserId: card.UserId, + RoleId: card.Id, + DepartmentId: card.DepartmentId, + RedTrace: int(card.RedTrack), + YellowTrace: int(card.YellowTrack), + BlueTrace: int(card.BlueTrack), + }, + } + + return +} + +// TraceUpdate 更新轨迹信息 (增加) +func (s *sTrace) TraceUpdate(ctx context.Context, req *v1.TraceUpdateReq) (res *v1.TraceUpdateRes, err error) { + // 创建数据库查询模型 + m := dao.RoleCards.Ctx(ctx) + + // 准备更新数据 + data := make(map[string]interface{}) + + // 只有当数值不为0时才进行更新操作 + if req.RedTrace != 0 { + data[dao.RoleCards.Columns().RedTrack] = gdb.Raw(fmt.Sprintf("%s + %d", dao.RoleCards.Columns().RedTrack, req.RedTrace)) + } + + if req.YellowTrace != 0 { + data[dao.RoleCards.Columns().YellowTrack] = gdb.Raw(fmt.Sprintf("%s + %d", dao.RoleCards.Columns().YellowTrack, req.YellowTrace)) + } + + if req.BlueTrace != 0 { + data[dao.RoleCards.Columns().BlueTrack] = gdb.Raw(fmt.Sprintf("%s + %d", dao.RoleCards.Columns().BlueTrack, req.BlueTrace)) + } + + // 如果没有需要更新的字段,直接返回 + if len(data) == 0 { + return &v1.TraceUpdateRes{ + RoleId: req.RoleId, + RedTrace: req.RedTrace, + YellowTrace: req.YellowTrace, + BlueTrace: req.BlueTrace, + }, nil + } + + // 添加更新时间 + data[dao.RoleCards.Columns().UpdatedAt] = gtime.Now() + + // 执行更新 + _, err = m.Data(data).Where(dao.RoleCards.Columns().Id, req.RoleId).Update() + if err != nil { + return nil, err + } + + res = &v1.TraceUpdateRes{ + RoleId: req.RoleId, + RedTrace: req.RedTrace, + YellowTrace: req.YellowTrace, + BlueTrace: req.BlueTrace, + } + + return +} + +// TraceReduce 减少轨迹数值 +func (s *sTrace) TraceReduce(ctx context.Context, req *v1.TraceReduceReq) (res *v1.TraceReduceRes, err error) { + // 创建数据库查询模型 + m := dao.RoleCards.Ctx(ctx) + + // 准备更新数据 + data := make(map[string]interface{}) + + // 只有当数值不为0时才进行减少操作 + if req.RedTrace != 0 { + data[dao.RoleCards.Columns().RedTrack] = gdb.Raw(fmt.Sprintf("GREATEST(0, %s - %d)", dao.RoleCards.Columns().RedTrack, req.RedTrace)) + } + + if req.YellowTrace != 0 { + data[dao.RoleCards.Columns().YellowTrack] = gdb.Raw(fmt.Sprintf("GREATEST(0, %s - %d)", dao.RoleCards.Columns().YellowTrack, req.YellowTrace)) + } + + if req.BlueTrace != 0 { + data[dao.RoleCards.Columns().BlueTrack] = gdb.Raw(fmt.Sprintf("GREATEST(0, %s - %d)", dao.RoleCards.Columns().BlueTrack, req.BlueTrace)) + } + + // 如果没有需要更新的字段,直接返回 + if len(data) == 0 { + return &v1.TraceReduceRes{}, nil + } + + // 添加更新时间 + data[dao.RoleCards.Columns().UpdatedAt] = gtime.Now() + + // 执行更新 + _, err = m.Data(data).Where(dao.RoleCards.Columns().Id, req.RoleId).Update() + if err != nil { + return nil, err + } + + res = &v1.TraceReduceRes{} + return +} diff --git a/Backend/internal/logic/user/user.go b/Backend/internal/logic/user/user.go new file mode 100644 index 0000000..2ec751b --- /dev/null +++ b/Backend/internal/logic/user/user.go @@ -0,0 +1,347 @@ +package user + +import ( + "context" + v1 "leke/api/user/v1" + "leke/internal/dao" + "leke/internal/model" + "leke/internal/model/entity" + "leke/internal/service" + + "github.com/gogf/gf/v2/errors/gcode" + "github.com/gogf/gf/v2/errors/gerror" +) + +type sUser struct{} + +func New() *sUser { + return &sUser{} +} + +func init() { + service.RegisterUser(New()) +} + +func (s *sUser) UserList(ctx context.Context, req *v1.UserListReq) (res *v1.UserListRes, err error) { + // 创建数据库查询模型 + m := dao.Users.Ctx(ctx) + + // 按照账号搜索 + if req.Account != "" { + m = m.WhereLike(dao.Users.Columns().Account, "%"+req.Account+"%") + } + + // 按照昵称搜索 + if req.Nickname != "" { + m = m.WhereLike(dao.Users.Columns().Nickname, "%"+req.Nickname+"%") + } + + // 查询总数 + total, err := m.Count() + if err != nil { + return nil, err + } + + // 分页处理 + m = m.Page(req.Page, req.PageSize) + + // 查询列表数据 + var users []*model.User + err = m.Scan(&users) + if err != nil { + return nil, err + } + + // 转换为视图参数 + userViewList := make([]*model.UserViewParams, 0, len(users)) + for _, user := range users { + userViewList = append(userViewList, &model.UserViewParams{ + Id: user.Id, + Account: user.Account, + Nickname: user.Nickname, + Gender: user.Gender, + BirthDate: user.BirthDate, + UserType: user.UserType, + VipStartAt: user.VipStartAt, + VipEndAt: user.VipEndAt, + CreatedAt: user.CreatedAt, + }) + } + + res = &v1.UserListRes{ + Users: userViewList, + } + + // 设置分页信息 + req.PageResult.Total = int(total) + req.PageResult.Page = req.Page + req.PageResult.PageSize = req.PageSize + + return +} + +func (s *sUser) UserView(ctx context.Context, req *v1.UserViewReq) (res *v1.UserViewRes, err error) { + // 创建数据库查询模型 + m := dao.Users.Ctx(ctx) + + // 根据账号查询 + if req.Account != "" { + m = m.Where(dao.Users.Columns().Account, req.Account) + } + + res = &v1.UserViewRes{} + err = m.Scan(&res.UserViewParams) + if err != nil { + return nil, err + } + + return +} + +func (s *sUser) UserUpdate(ctx context.Context, req *v1.UserUpdateReq) (res *v1.UserUpdateRes, err error) { + // 创建数据库查询模型 + m := dao.Users.Ctx(ctx) + + // 根据账号更新用户信息 + _, err = m.Data(req).Where(dao.Users.Columns().Account, req.Account).Update() + if err != nil { + return nil, err + } + + res = &v1.UserUpdateRes{} + // 获取更新后的用户信息 + err = m.Where(dao.Users.Columns().Account, req.Account).Scan(&res) + if err != nil { + return nil, err + } + + return +} + +func (s *sUser) UserDelete(ctx context.Context, req *v1.UserDeleteReq) (res *v1.UserDeleteRes, err error) { + // 根据账号删除用户 + _, err = dao.Users.Ctx(ctx).Where(dao.Users.Columns().Account, req.Account).Delete() + if err != nil { + return nil, err + } + + res = &v1.UserDeleteRes{} + return +} + +// RoleCreate 创建角色 +func (s *sUser) RoleCreate(ctx context.Context, req *v1.RoleCreateReq) (res *v1.RoleCreateRes, err error) { + // 插入角色数据 + data := &entity.RoleCards{ + UserId: req.UserId, + DepartmentId: req.DepartmentId, + AgentName: req.AgentName, + CodeName: req.CodeName, + Gender: req.Gender, + ArcAbnormal: req.ArcAbnormal, + ArcReality: req.ArcReality, + ArcPosition: req.ArcPosition, + } + + id, err := dao.RoleCards.Ctx(ctx).Data(data).InsertAndGetId() + if err != nil { + return nil, err + } + + res = &v1.RoleCreateRes{ + Id: uint64(id), + } + return +} + +// RoleUpdate 更新角色 +func (s *sUser) RoleUpdate(ctx context.Context, req *v1.RoleUpdateReq) (res *v1.RoleUpdateRes, err error) { + // 更新角色数据 + data := &entity.RoleCards{ + DepartmentId: req.DepartmentId, + AgentName: req.AgentName, + CodeName: req.CodeName, + Gender: req.Gender, + ArcAbnormal: req.ArcAbnormal, + ArcReality: req.ArcReality, + ArcPosition: req.ArcPosition, + Commendation: req.Commendation, + Reprimand: req.Reprimand, + BlueTrack: req.BlueTrack, + YellowTrack: req.YellowTrack, + RedTrack: req.RedTrack, + } + + _, err = dao.RoleCards.Ctx(ctx).Data(data).Where(dao.RoleCards.Columns().Id, req.Id).Update() + if err != nil { + return nil, err + } + + res = &v1.RoleUpdateRes{ + Id: req.Id, + } + return +} + +// RoleView 查看角色详情 +func (s *sUser) RoleView(ctx context.Context, req *v1.RoleViewReq) (res *v1.RoleViewRes, err error) { + // 查询角色详情 + var role entity.RoleCards + err = dao.RoleCards.Ctx(ctx).Where(dao.RoleCards.Columns().Id, req.Id).Scan(&role) + if err != nil { + return nil, err + } + + res = &v1.RoleViewRes{ + RoleViewParams: v1.RoleViewParams{ + Id: role.Id, + UserId: role.UserId, + DepartmentId: role.DepartmentId, + Commendation: role.Commendation, + Reprimand: role.Reprimand, + BlueTrack: role.BlueTrack, + YellowTrack: role.YellowTrack, + RedTrack: role.RedTrack, + ArcAbnormal: role.ArcAbnormal, + ArcReality: role.ArcReality, + ArcPosition: role.ArcPosition, + AgentName: role.AgentName, + CodeName: role.CodeName, + Gender: role.Gender, + QaMeticulous: role.QaMeticulous, + QaDeception: role.QaDeception, + QaVigor: role.QaVigor, + QaEmpathy: role.QaEmpathy, + QaInitiative: role.QaInitiative, + QaResilience: role.QaResilience, + QaPresence: role.QaPresence, + QaProfessional: role.QaProfessional, + QaDiscretion: role.QaDiscretion, + }, + } + return +} + +// RoleList 获取角色列表 +func (s *sUser) RoleList(ctx context.Context, req *v1.RoleListReq) (res *v1.RoleListRes, err error) { + // 创建数据库查询模型 + m := dao.RoleCards.Ctx(ctx) + + // 根据用户ID查询 + if req.UserId != 0 { + m = m.Where(dao.RoleCards.Columns().UserId, req.UserId) + } + + // 根据部门ID查询 + if req.DepartmentId != 0 { + m = m.Where(dao.RoleCards.Columns().DepartmentId, req.DepartmentId) + } + + // 查询总数 + total, err := m.Count() + if err != nil { + return nil, err + } + + // 分页处理 + m = m.Page(req.Page, req.PageSize) + + // 查询列表数据 + var roles []*entity.RoleCards + err = m.Scan(&roles) + if err != nil { + return nil, err + } + + // 转换为视图参数 + roleViewList := make([]*v1.RoleViewParams, 0, len(roles)) + for _, role := range roles { + roleViewList = append(roleViewList, &v1.RoleViewParams{ + Id: role.Id, + UserId: role.UserId, + DepartmentId: role.DepartmentId, + Commendation: role.Commendation, + Reprimand: role.Reprimand, + BlueTrack: role.BlueTrack, + YellowTrack: role.YellowTrack, + RedTrack: role.RedTrack, + ArcAbnormal: role.ArcAbnormal, + ArcReality: role.ArcReality, + ArcPosition: role.ArcPosition, + AgentName: role.AgentName, + CodeName: role.CodeName, + Gender: role.Gender, + QaMeticulous: role.QaMeticulous, + QaDeception: role.QaDeception, + QaVigor: role.QaVigor, + QaEmpathy: role.QaEmpathy, + QaInitiative: role.QaInitiative, + QaResilience: role.QaResilience, + QaPresence: role.QaPresence, + QaProfessional: role.QaProfessional, + QaDiscretion: role.QaDiscretion, + }) + } + + res = &v1.RoleListRes{ + List: roleViewList, + PageResult: req.PageResult, // 设置分页信息 + } + + // 设置分页信息 + req.PageResult.Total = int(total) + req.PageResult.Page = req.Page + req.PageResult.PageSize = req.PageSize + + return +} + +// RoleDelete 删除角色 +func (s *sUser) RoleDelete(ctx context.Context, req *v1.RoleDeleteReq) (res *v1.RoleDeleteRes, err error) { + // 根据ID删除角色 + _, err = dao.RoleCards.Ctx(ctx).Where(dao.RoleCards.Columns().Id, req.Id).Delete() + if err != nil { + return nil, err + } + + res = &v1.RoleDeleteRes{ + Id: req.Id, + } + return +} + +// RolePermissionCheck 权限查询 +func (s *sUser) RolePermissionCheck(ctx context.Context, req *v1.RolePermissionCheckReq) (res *v1.RolePermissionCheckRes, err error) { + // 查询角色信息 + var role entity.RoleCards + err = dao.RoleCards.Ctx(ctx).Where(dao.RoleCards.Columns().Id, req.RoleId).Scan(&role) + if err != nil { + return nil, err + } + + // 根据轨道类型获取对应的轨道值 + var trackValue uint + switch req.TrackType { + case "blue": + trackValue = role.BlueTrack + case "yellow": + trackValue = role.YellowTrack + case "red": + trackValue = role.RedTrack + default: + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "无效的轨道类型") + } + + // 判断权限:如果角色的轨道值 >= 文件需要的值,则权限验证通过 + if trackValue >= req.FileValue { + // 权限验证通过 + res = &v1.RolePermissionCheckRes{ + Code: 333, + Mes: "权限验证通过", + } + return + } + + // 权限不足,返回错误 + return nil, gerror.New("权限不足") +} diff --git a/Backend/internal/middleware/jwt.go b/Backend/internal/middleware/jwt.go new file mode 100644 index 0000000..d197b6a --- /dev/null +++ b/Backend/internal/middleware/jwt.go @@ -0,0 +1,57 @@ +package middleware + +import ( + "github.com/gogf/gf/v2/errors/gcode" + "github.com/gogf/gf/v2/errors/gerror" + "github.com/gogf/gf/v2/net/ghttp" + "github.com/gogf/gf/v2/os/gctx" + "github.com/golang-jwt/jwt/v5" +) + +// JWTClaims represents the custom claims for the JWT token +type JWTClaims struct { + Username string `json:"username"` + jwt.RegisteredClaims +} + +const ( + // CtxUsername is the context key for storing the username + CtxUsername gctx.StrKey = "username" + // JwtSecretKey is the secret key for JWT signing and validation + // Note: In production, this should be replaced with a secure key + JwtSecretKey = "leke" +) + +// JWTAuth is a middleware that validates JWT tokens in the request header +// It checks for the presence and validity of the token, and sets the username +// in the context for downstream handlers +func JWTAuth(r *ghttp.Request) { + // Get token from Authorization header + tokenString := r.Header.Get("Authorization") + if tokenString == "" { + r.SetError(gerror.NewCode(gcode.CodeNotAuthorized, "No token provided")) + r.Exit() + return + } + + // Remove 'Bearer ' prefix if present + if len(tokenString) > 7 && tokenString[:7] == "Bearer " { + tokenString = tokenString[7:] + } + + // Parse and validate the token + claims := &JWTClaims{} + token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) { + return []byte(JwtSecretKey), nil + }) + + if err != nil || !token.Valid { + r.SetError(gerror.NewCode(gcode.CodeNotAuthorized, "Invalid token")) + r.Exit() + return + } + + // Store username in context for later use + r.SetCtxVar(CtxUsername, claims.Username) + r.Middleware.Next() +} \ No newline at end of file diff --git a/Backend/internal/model/containmentRepo.go b/Backend/internal/model/containmentRepo.go new file mode 100644 index 0000000..8731ef8 --- /dev/null +++ b/Backend/internal/model/containmentRepo.go @@ -0,0 +1,10 @@ +package model + +type ContainmentRepo struct { + Abnormal int `json:"abnormal" orm:"abnormal" description:"number of contained anomalies (收容异常的数量)"` + AgentName string `json:"agentName" orm:"agent_name" description:"agent (特工)"` + AnomalyName string `json:"anomalyName" orm:"anomaly_name" description:"name of the anomaly (异常体的名字)"` + Id uint64 `json:"id" orm:"id" description:"primary key(大于0为修改,其他为新增)"` + RepoName string `json:"repoName" orm:"repo_name" description:"containment repository name or code (收容库)"` + TerminalId int `json:"terminalId" orm:"terminal_id" description:"terminal (散逸端)"` +} diff --git a/Backend/internal/model/department.go b/Backend/internal/model/department.go new file mode 100644 index 0000000..4d393db --- /dev/null +++ b/Backend/internal/model/department.go @@ -0,0 +1,18 @@ +package model + +import ( + "github.com/gogf/gf/v2/os/gtime" +) + +// Department 三角机构分部表 +type Department struct { + Id uint64 `json:"id" orm:"id" description:"自增主键"` + UserId uint64 `json:"userId" orm:"user_id" description:"所属用户ID(对应 users.id)"` + BranchName string `json:"branchName" orm:"branch_name" description:"分部名称"` + TerminalCount int `json:"terminalCount" orm:"terminal_count" description:"分部散逸端的数量"` + Weather string `json:"weather" orm:"weather" description:"分部当前天气/气候描述"` + ManagerName string `json:"managerName" orm:"manager_name" description:"分部经理名称"` + Location string `json:"location" description:"分部地址"` + CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:"创建时间"` + UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:"更新时间"` +} diff --git a/Backend/internal/model/do/.gitkeep b/Backend/internal/model/do/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/Backend/internal/model/do/coaches.go b/Backend/internal/model/do/coaches.go new file mode 100644 index 0000000..3b975d0 --- /dev/null +++ b/Backend/internal/model/do/coaches.go @@ -0,0 +1,22 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" +) + +// Coaches is the golang structure of table coaches for DAO operations like Where/Data. +type Coaches struct { + g.Meta `orm:"table:coaches, do:true"` + Id any // 教练ID + UserId any // 对应的用户ID(如果教练也要登录的话) + Name any // 教练姓名 + Phone any // 教练电话 + Specialty any // 擅长方向,比如瑜伽、力量、减脂 + CreatedAt *gtime.Time // 创建时间 + UpdatedAt *gtime.Time // 更新时间 +} diff --git a/Backend/internal/model/do/comments.go b/Backend/internal/model/do/comments.go new file mode 100644 index 0000000..1bb20c4 --- /dev/null +++ b/Backend/internal/model/do/comments.go @@ -0,0 +1,28 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" +) + +// Comments is the golang structure of table comments for DAO operations like Where/Data. +type Comments struct { + g.Meta `orm:"table:comments, do:true"` + Id any // 评论ID + UserId any // 评论用户ID + TargetType any // 被评论对象类型(如 post/image/video 等) + TargetId any // 被评论对象ID + ParentId any // 父评论ID(回复某条评论时填写) + RootId any // 根评论ID(同一楼的顶层评论ID,便于树查询) + Content any // 评论内容 + Status any // 状态:1正常 0屏蔽 2删除 + LikeCount any // 点赞数 + ReplyCount any // 回复数 + CreatedAt *gtime.Time // 创建时间 + UpdatedAt *gtime.Time // 更新时间 + DeletedAt *gtime.Time // 软删除时间 +} diff --git a/Backend/internal/model/do/containment_repo.go b/Backend/internal/model/do/containment_repo.go new file mode 100644 index 0000000..5fa8db1 --- /dev/null +++ b/Backend/internal/model/do/containment_repo.go @@ -0,0 +1,19 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" +) + +// ContainmentRepo is the golang structure of table containment_repo for DAO operations like Where/Data. +type ContainmentRepo struct { + g.Meta `orm:"table:containment_repo, do:true"` + Id any // auto-increment primary key + AnomalyName any // name of the anomaly (异常体的名字) + AgentName any // agent (特工) + RepoName any // containment repository name or code (收容库) + Department any // 部门 +} diff --git a/Backend/internal/model/do/department.go b/Backend/internal/model/do/department.go new file mode 100644 index 0000000..4c6c837 --- /dev/null +++ b/Backend/internal/model/do/department.go @@ -0,0 +1,24 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" +) + +// Department is the golang structure of table department for DAO operations like Where/Data. +type Department struct { + g.Meta `orm:"table:department, do:true"` + Id any // 自增主键 + UserId any // 所属用户ID(对应 users.id) + BranchName any // 分部名称 + TerminalCount any // 分部散逸端的数量 + Weather any // 分部当前天气/气候描述 + ManagerName any // 分部经理名称 + CreatedAt *gtime.Time // 创建时间 + UpdatedAt *gtime.Time // 更新时间 + Location any // 地址 +} diff --git a/Backend/internal/model/do/fans.go b/Backend/internal/model/do/fans.go new file mode 100644 index 0000000..0efc1d1 --- /dev/null +++ b/Backend/internal/model/do/fans.go @@ -0,0 +1,22 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" +) + +// Fans is the golang structure of table fans for DAO operations like Where/Data. +type Fans struct { + g.Meta `orm:"table:fans, do:true"` + Id any // 自增主键 + UserId any // 用户ID(被关注者,本人) + FanId any // 粉丝用户ID + Status any // 状态:1=粉丝 0=取关/无效 + Remark any // 备注名(user_id 对 fan_id 的备注/分组) + CreatedAt *gtime.Time // 创建时间 + UpdatedAt *gtime.Time // 更新时间 +} diff --git a/Backend/internal/model/do/forum_comments.go b/Backend/internal/model/do/forum_comments.go new file mode 100644 index 0000000..7cb5240 --- /dev/null +++ b/Backend/internal/model/do/forum_comments.go @@ -0,0 +1,26 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" +) + +// ForumComments is the golang structure of table forum_comments for DAO operations like Where/Data. +type ForumComments struct { + g.Meta `orm:"table:forum_comments, do:true"` + Id any // 评论ID(主键) + UserId any // 评论发布者ID(关联users.id,无外键) + PostId any // 所属帖子ID(关联forum_posts.id,无外键) + ParentId any // 父评论ID(NULL=根评论,非NULL=回复某条评论) + Content any // 评论内容(支持emoji) + Status any // 评论状态:normal=正常 deleted=软删除 audit=审核中 reject=审核驳回 + LikeCount any // 点赞数 + ReplyCount any // 回复数(冗余字段) + CreatedAt *gtime.Time // 评论创建时间 + UpdatedAt *gtime.Time // 评论更新时间 + DeletedAt *gtime.Time // 软删除时间 +} diff --git a/Backend/internal/model/do/forum_posts.go b/Backend/internal/model/do/forum_posts.go new file mode 100644 index 0000000..15e7f73 --- /dev/null +++ b/Backend/internal/model/do/forum_posts.go @@ -0,0 +1,27 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" +) + +// ForumPosts is the golang structure of table forum_posts for DAO operations like Where/Data. +type ForumPosts struct { + g.Meta `orm:"table:forum_posts, do:true"` + Id any // 帖子ID(主键) + UserId any // 发帖用户ID(关联users.id,无外键) + Title any // 帖子标题 + Content any // 帖子正文(支持富文本/emoji) + CoverImage any // 帖子封面图URL + Status any // 帖子状态:normal=正常 deleted=软删除 audit=审核中 reject=审核驳回 + ViewCount any // 浏览量(冗余字段) + LikeCount any // 点赞数(冗余字段) + CommentCount any // 评论数(冗余字段) + CreatedAt *gtime.Time // 发帖时间 + UpdatedAt *gtime.Time // 更新时间 + DeletedAt *gtime.Time // 软删除时间 +} diff --git a/Backend/internal/model/do/group_class_enrollments.go b/Backend/internal/model/do/group_class_enrollments.go new file mode 100644 index 0000000..48f7fcf --- /dev/null +++ b/Backend/internal/model/do/group_class_enrollments.go @@ -0,0 +1,21 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" +) + +// GroupClassEnrollments is the golang structure of table group_class_enrollments for DAO operations like Where/Data. +type GroupClassEnrollments struct { + g.Meta `orm:"table:group_class_enrollments, do:true"` + Id any // 报名ID + ClassId any // 团课ID + UserId any // 用户ID + Status any // 状态:booked已报名,checked_in已签到,cancelled已取消 + EnrolledAt *gtime.Time // 报名时间 + CheckedInAt *gtime.Time // 签到时间 +} diff --git a/Backend/internal/model/do/group_classes.go b/Backend/internal/model/do/group_classes.go new file mode 100644 index 0000000..3b941bf --- /dev/null +++ b/Backend/internal/model/do/group_classes.go @@ -0,0 +1,26 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" +) + +// GroupClasses is the golang structure of table group_classes for DAO operations like Where/Data. +type GroupClasses struct { + g.Meta `orm:"table:group_classes, do:true"` + Id any // 团课ID + Title any // 课程名称,如:燃脂搏击、瑜伽 + Description any // 课程介绍 + CoachId any // 授课教练ID + Location any // 上课地点/门店/教室 + StartTime *gtime.Time // 开始时间 + EndTime *gtime.Time // 结束时间 + MaxCapacity any // 最大人数 + Price any // 价格(0表示免费或已包含在会员卡内) + CreatedAt *gtime.Time // 创建时间 + UpdatedAt *gtime.Time // 更新时间 +} diff --git a/Backend/internal/model/do/role_cards.go b/Backend/internal/model/do/role_cards.go new file mode 100644 index 0000000..46fc1ef --- /dev/null +++ b/Backend/internal/model/do/role_cards.go @@ -0,0 +1,40 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" +) + +// RoleCards is the golang structure of table role_cards for DAO operations like Where/Data. +type RoleCards struct { + g.Meta `orm:"table:role_cards, do:true"` + Id any // 角色卡ID + UserId any // 所属用户ID + DepartmentId any // 所属部门ID + Commendation any // 嘉奖次数 + Reprimand any // 申戒次数 + BlueTrack any // 蓝轨(0-40) + YellowTrack any // 黄轨(0-40) + RedTrack any // 红轨(0-40) + ArcAbnormal any // ARC:异常 + ArcReality any // ARC:现实 + ArcPosition any // ARC:职位 + AgentName any // 特工名字 + CodeName any // 代号 + Gender any // 性别 + QaMeticulous any // Meticulousness (0-100, QA) + QaDeception any // Deception (0-100, QA) + QaVigor any // Vigor / Drive (0-100, QA) + QaEmpathy any // Empathy (0-100, QA) + QaInitiative any // Initiative (0-100, QA) + QaResilience any // Resilience / Persistence (0-100, QA) + QaPresence any // Presence / Charisma (0-100, QA) + QaProfessional any // Professionalism (0-100, QA) + QaDiscretion any // Discretion / Low profile (0-100, QA) + CreatedAt *gtime.Time // 创建时间 + UpdatedAt *gtime.Time // 更新时间 +} diff --git a/Backend/internal/model/do/subscribe.go b/Backend/internal/model/do/subscribe.go new file mode 100644 index 0000000..c980bee --- /dev/null +++ b/Backend/internal/model/do/subscribe.go @@ -0,0 +1,22 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" +) + +// Subscribe is the golang structure of table subscribe for DAO operations like Where/Data. +type Subscribe struct { + g.Meta `orm:"table:subscribe, do:true"` + Id any // 自增主键 + UserId any // 用户ID(关注者,本人) + FollowId any // 被关注的用户ID + Status any // 状态:1=关注中 0=取消关注 + Remark any // 备注名(user_id 对 follow_id 的备注) + CreatedAt *gtime.Time // 创建时间 + UpdatedAt *gtime.Time // 更新时间 +} diff --git a/Backend/internal/model/do/trpg_room.go b/Backend/internal/model/do/trpg_room.go new file mode 100644 index 0000000..4397ddb --- /dev/null +++ b/Backend/internal/model/do/trpg_room.go @@ -0,0 +1,34 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" +) + +// TrpgRoom is the golang structure of table trpg_room for DAO operations like Where/Data. +type TrpgRoom struct { + g.Meta `orm:"table:trpg_room, do:true"` + Id any // 自增主键 + RoomId any // 房间全局唯一ID(建议UUID) + RoomCode any // 房间号(玩家看到/输入的房间号) + RoomName any // 房间名称 + HostId any // 主持人用户ID(GM/KP/经理,对应用户表id) + HostNickname any // 主持人昵称(冗余字段,可选) + MaxPlayers any // 最大玩家人数(不含主持人,可按需要约定) + CurrentPlayers any // 当前玩家人数(不含主持人) + HasPassword any // 是否有密码:0无 1有 + RoomPassword any // 房间密码(建议存加密/哈希后的密码) + IsPrivate any // 是否私密房:0公开 1私密 + Status any // 房间状态:0未开始 1进行中 2已结束 3已关闭 + SystemName any // 规则系统:如 COC、DND5E 等 + ScenarioName any // 模组/剧本名称 + Description any // 房间简介/招募说明 + CreatedAt *gtime.Time // 房间创建时间 + StartedAt *gtime.Time // 开团时间 + EndedAt *gtime.Time // 结束时间 + UpdatedAt *gtime.Time // 信息最近更新时间 +} diff --git a/Backend/internal/model/do/user_department.go b/Backend/internal/model/do/user_department.go new file mode 100644 index 0000000..e2570c7 --- /dev/null +++ b/Backend/internal/model/do/user_department.go @@ -0,0 +1,20 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" +) + +// UserDepartment is the golang structure of table user_department for DAO operations like Where/Data. +type UserDepartment struct { + g.Meta `orm:"table:user_department, do:true"` + Id any // + UserId any // 用户ID(对应 users.id) + DeptId any // 部门ID(对应 department.id) + CreatedAt *gtime.Time // 创建时间 + UpdatedAt *gtime.Time // 更新时间 +} diff --git a/Backend/internal/model/do/users.go b/Backend/internal/model/do/users.go new file mode 100644 index 0000000..a9637f9 --- /dev/null +++ b/Backend/internal/model/do/users.go @@ -0,0 +1,35 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" +) + +// Users is the golang structure of table users for DAO operations like Where/Data. +type Users struct { + g.Meta `orm:"table:users, do:true"` + Id any // 用户ID + Account any // 账号 + Password any // 密码哈希 + Nickname any // 昵称 + Gender any // 性别:0未知 1男 2女 + BirthDate *gtime.Time // 生日 + UserType any // 用户类型:normal普通用户,vip为VIP用户 + VipStartAt *gtime.Time // VIP开始时间 + VipEndAt *gtime.Time // VIP结束时间 + CreatedAt *gtime.Time // 创建时间 + UpdatedAt *gtime.Time // 更新时间 + RealityRole any // 现实身份/角色 + AbnormalRole any // 异常身份/角色 + JobTitle any // 职位 + Commendation any // + Admonition any // + Email any // + RedTrace any // 红轨 + YellowTrace any // 黄轨 + BlueTrace any // 蓝轨 +} diff --git a/Backend/internal/model/do/workout_logs.go b/Backend/internal/model/do/workout_logs.go new file mode 100644 index 0000000..f1ee43c --- /dev/null +++ b/Backend/internal/model/do/workout_logs.go @@ -0,0 +1,24 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package do + +import ( + "github.com/gogf/gf/v2/frame/g" + "github.com/gogf/gf/v2/os/gtime" +) + +// WorkoutLogs is the golang structure of table workout_logs for DAO operations like Where/Data. +type WorkoutLogs struct { + g.Meta `orm:"table:workout_logs, do:true"` + Id any // 健身记录ID + UserId any // 关联的用户ID + WorkoutDate *gtime.Time // 健身日期 + StartTime *gtime.Time // 开始时间 + EndTime *gtime.Time // 结束时间 + DurationMinutes any // 时长(分钟) + Calories any // 估算消耗的卡路里 + Description any // 本次训练的备注,比如训练内容、部位等 + CreatedAt *gtime.Time // 记录创建时间 +} diff --git a/Backend/internal/model/entity/.gitkeep b/Backend/internal/model/entity/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/Backend/internal/model/entity/coaches.go b/Backend/internal/model/entity/coaches.go new file mode 100644 index 0000000..996f185 --- /dev/null +++ b/Backend/internal/model/entity/coaches.go @@ -0,0 +1,20 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +import ( + "github.com/gogf/gf/v2/os/gtime" +) + +// Coaches is the golang structure for table coaches. +type Coaches struct { + Id uint64 `json:"id" orm:"id" description:"教练ID"` // 教练ID + UserId uint64 `json:"userId" orm:"user_id" description:"对应的用户ID(如果教练也要登录的话)"` // 对应的用户ID(如果教练也要登录的话) + Name string `json:"name" orm:"name" description:"教练姓名"` // 教练姓名 + Phone string `json:"phone" orm:"phone" description:"教练电话"` // 教练电话 + Specialty string `json:"specialty" orm:"specialty" description:"擅长方向,比如瑜伽、力量、减脂"` // 擅长方向,比如瑜伽、力量、减脂 + CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:"创建时间"` // 创建时间 + UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:"更新时间"` // 更新时间 +} diff --git a/Backend/internal/model/entity/comments.go b/Backend/internal/model/entity/comments.go new file mode 100644 index 0000000..257fa9b --- /dev/null +++ b/Backend/internal/model/entity/comments.go @@ -0,0 +1,26 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +import ( + "github.com/gogf/gf/v2/os/gtime" +) + +// Comments is the golang structure for table comments. +type Comments struct { + Id uint64 `json:"id" orm:"id" description:"评论ID"` // 评论ID + UserId uint64 `json:"userId" orm:"user_id" description:"评论用户ID"` // 评论用户ID + TargetType string `json:"targetType" orm:"target_type" description:"被评论对象类型(如 post/image/video 等)"` // 被评论对象类型(如 post/image/video 等) + TargetId uint64 `json:"targetId" orm:"target_id" description:"被评论对象ID"` // 被评论对象ID + ParentId uint64 `json:"parentId" orm:"parent_id" description:"父评论ID(回复某条评论时填写)"` // 父评论ID(回复某条评论时填写) + RootId uint64 `json:"rootId" orm:"root_id" description:"根评论ID(同一楼的顶层评论ID,便于树查询)"` // 根评论ID(同一楼的顶层评论ID,便于树查询) + Content string `json:"content" orm:"content" description:"评论内容"` // 评论内容 + Status int `json:"status" orm:"status" description:"状态:1正常 0屏蔽 2删除"` // 状态:1正常 0屏蔽 2删除 + LikeCount uint `json:"likeCount" orm:"like_count" description:"点赞数"` // 点赞数 + ReplyCount uint `json:"replyCount" orm:"reply_count" description:"回复数"` // 回复数 + CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:"创建时间"` // 创建时间 + UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:"更新时间"` // 更新时间 + DeletedAt *gtime.Time `json:"deletedAt" orm:"deleted_at" description:"软删除时间"` // 软删除时间 +} diff --git a/Backend/internal/model/entity/containment_repo.go b/Backend/internal/model/entity/containment_repo.go new file mode 100644 index 0000000..b3da5cf --- /dev/null +++ b/Backend/internal/model/entity/containment_repo.go @@ -0,0 +1,14 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +// ContainmentRepo is the golang structure for table containment_repo. +type ContainmentRepo struct { + Id uint64 `json:"id" orm:"id" description:"auto-increment primary key"` // auto-increment primary key + AnomalyName string `json:"anomalyName" orm:"anomaly_name" description:"name of the anomaly (异常体的名字)"` // name of the anomaly (异常体的名字) + AgentName string `json:"agentName" orm:"agent_name" description:"agent (特工)"` // agent (特工) + RepoName string `json:"repoName" orm:"repo_name" description:"containment repository name or code (收容库)"` // containment repository name or code (收容库) + Department int `json:"department" orm:"department" description:"部门"` // 部门 +} diff --git a/Backend/internal/model/entity/department.go b/Backend/internal/model/entity/department.go new file mode 100644 index 0000000..5237692 --- /dev/null +++ b/Backend/internal/model/entity/department.go @@ -0,0 +1,22 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +import ( + "github.com/gogf/gf/v2/os/gtime" +) + +// Department is the golang structure for table department. +type Department struct { + Id uint64 `json:"id" orm:"id" description:"自增主键"` // 自增主键 + UserId uint64 `json:"userId" orm:"user_id" description:"所属用户ID(对应 users.id)"` // 所属用户ID(对应 users.id) + BranchName string `json:"branchName" orm:"branch_name" description:"分部名称"` // 分部名称 + TerminalCount int `json:"terminalCount" orm:"terminal_count" description:"分部散逸端的数量"` // 分部散逸端的数量 + Weather string `json:"weather" orm:"weather" description:"分部当前天气/气候描述"` // 分部当前天气/气候描述 + ManagerName string `json:"managerName" orm:"manager_name" description:"分部经理名称"` // 分部经理名称 + CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:"创建时间"` // 创建时间 + UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:"更新时间"` // 更新时间 + Location string `json:"location" orm:"location" description:"地址"` // 地址 +} diff --git a/Backend/internal/model/entity/fans.go b/Backend/internal/model/entity/fans.go new file mode 100644 index 0000000..f684a68 --- /dev/null +++ b/Backend/internal/model/entity/fans.go @@ -0,0 +1,20 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +import ( + "github.com/gogf/gf/v2/os/gtime" +) + +// Fans is the golang structure for table fans. +type Fans struct { + Id uint64 `json:"id" orm:"id" description:"自增主键"` // 自增主键 + UserId uint64 `json:"userId" orm:"user_id" description:"用户ID(被关注者,本人)"` // 用户ID(被关注者,本人) + FanId uint64 `json:"fanId" orm:"fan_id" description:"粉丝用户ID"` // 粉丝用户ID + Status int `json:"status" orm:"status" description:"状态:1=粉丝 0=取关/无效"` // 状态:1=粉丝 0=取关/无效 + Remark string `json:"remark" orm:"remark" description:"备注名(user_id 对 fan_id 的备注/分组)"` // 备注名(user_id 对 fan_id 的备注/分组) + CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:"创建时间"` // 创建时间 + UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:"更新时间"` // 更新时间 +} diff --git a/Backend/internal/model/entity/forum_comments.go b/Backend/internal/model/entity/forum_comments.go new file mode 100644 index 0000000..9631aad --- /dev/null +++ b/Backend/internal/model/entity/forum_comments.go @@ -0,0 +1,24 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +import ( + "github.com/gogf/gf/v2/os/gtime" +) + +// ForumComments is the golang structure for table forum_comments. +type ForumComments struct { + Id uint64 `json:"id" orm:"id" description:"评论ID(主键)"` // 评论ID(主键) + UserId uint64 `json:"userId" orm:"user_id" description:"评论发布者ID(关联users.id,无外键)"` // 评论发布者ID(关联users.id,无外键) + PostId uint64 `json:"postId" orm:"post_id" description:"所属帖子ID(关联forum_posts.id,无外键)"` // 所属帖子ID(关联forum_posts.id,无外键) + ParentId uint64 `json:"parentId" orm:"parent_id" description:"父评论ID(NULL=根评论,非NULL=回复某条评论)"` // 父评论ID(NULL=根评论,非NULL=回复某条评论) + Content string `json:"content" orm:"content" description:"评论内容(支持emoji)"` // 评论内容(支持emoji) + Status string `json:"status" orm:"status" description:"评论状态:normal=正常 deleted=软删除 audit=审核中 reject=审核驳回"` // 评论状态:normal=正常 deleted=软删除 audit=审核中 reject=审核驳回 + LikeCount uint `json:"likeCount" orm:"like_count" description:"点赞数"` // 点赞数 + ReplyCount uint `json:"replyCount" orm:"reply_count" description:"回复数(冗余字段)"` // 回复数(冗余字段) + CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:"评论创建时间"` // 评论创建时间 + UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:"评论更新时间"` // 评论更新时间 + DeletedAt *gtime.Time `json:"deletedAt" orm:"deleted_at" description:"软删除时间"` // 软删除时间 +} diff --git a/Backend/internal/model/entity/forum_posts.go b/Backend/internal/model/entity/forum_posts.go new file mode 100644 index 0000000..93baebd --- /dev/null +++ b/Backend/internal/model/entity/forum_posts.go @@ -0,0 +1,25 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +import ( + "github.com/gogf/gf/v2/os/gtime" +) + +// ForumPosts is the golang structure for table forum_posts. +type ForumPosts struct { + Id uint64 `json:"id" orm:"id" description:"帖子ID(主键)"` // 帖子ID(主键) + UserId uint64 `json:"userId" orm:"user_id" description:"发帖用户ID(关联users.id,无外键)"` // 发帖用户ID(关联users.id,无外键) + Title string `json:"title" orm:"title" description:"帖子标题"` // 帖子标题 + Content string `json:"content" orm:"content" description:"帖子正文(支持富文本/emoji)"` // 帖子正文(支持富文本/emoji) + CoverImage string `json:"coverImage" orm:"cover_image" description:"帖子封面图URL"` // 帖子封面图URL + Status string `json:"status" orm:"status" description:"帖子状态:normal=正常 deleted=软删除 audit=审核中 reject=审核驳回"` // 帖子状态:normal=正常 deleted=软删除 audit=审核中 reject=审核驳回 + ViewCount uint `json:"viewCount" orm:"view_count" description:"浏览量(冗余字段)"` // 浏览量(冗余字段) + LikeCount uint `json:"likeCount" orm:"like_count" description:"点赞数(冗余字段)"` // 点赞数(冗余字段) + CommentCount uint `json:"commentCount" orm:"comment_count" description:"评论数(冗余字段)"` // 评论数(冗余字段) + CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:"发帖时间"` // 发帖时间 + UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:"更新时间"` // 更新时间 + DeletedAt *gtime.Time `json:"deletedAt" orm:"deleted_at" description:"软删除时间"` // 软删除时间 +} diff --git a/Backend/internal/model/entity/group_class_enrollments.go b/Backend/internal/model/entity/group_class_enrollments.go new file mode 100644 index 0000000..7483a83 --- /dev/null +++ b/Backend/internal/model/entity/group_class_enrollments.go @@ -0,0 +1,19 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +import ( + "github.com/gogf/gf/v2/os/gtime" +) + +// GroupClassEnrollments is the golang structure for table group_class_enrollments. +type GroupClassEnrollments struct { + Id uint64 `json:"id" orm:"id" description:"报名ID"` // 报名ID + ClassId uint64 `json:"classId" orm:"class_id" description:"团课ID"` // 团课ID + UserId uint64 `json:"userId" orm:"user_id" description:"用户ID"` // 用户ID + Status string `json:"status" orm:"status" description:"状态:booked已报名,checked_in已签到,cancelled已取消"` // 状态:booked已报名,checked_in已签到,cancelled已取消 + EnrolledAt *gtime.Time `json:"enrolledAt" orm:"enrolled_at" description:"报名时间"` // 报名时间 + CheckedInAt *gtime.Time `json:"checkedInAt" orm:"checked_in_at" description:"签到时间"` // 签到时间 +} diff --git a/Backend/internal/model/entity/group_classes.go b/Backend/internal/model/entity/group_classes.go new file mode 100644 index 0000000..f250a6b --- /dev/null +++ b/Backend/internal/model/entity/group_classes.go @@ -0,0 +1,24 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +import ( + "github.com/gogf/gf/v2/os/gtime" +) + +// GroupClasses is the golang structure for table group_classes. +type GroupClasses struct { + Id uint64 `json:"id" orm:"id" description:"团课ID"` // 团课ID + Title string `json:"title" orm:"title" description:"课程名称,如:燃脂搏击、瑜伽"` // 课程名称,如:燃脂搏击、瑜伽 + Description string `json:"description" orm:"description" description:"课程介绍"` // 课程介绍 + CoachId uint64 `json:"coachId" orm:"coach_id" description:"授课教练ID"` // 授课教练ID + Location string `json:"location" orm:"location" description:"上课地点/门店/教室"` // 上课地点/门店/教室 + StartTime *gtime.Time `json:"startTime" orm:"start_time" description:"开始时间"` // 开始时间 + EndTime *gtime.Time `json:"endTime" orm:"end_time" description:"结束时间"` // 结束时间 + MaxCapacity int `json:"maxCapacity" orm:"max_capacity" description:"最大人数"` // 最大人数 + Price float64 `json:"price" orm:"price" description:"价格(0表示免费或已包含在会员卡内)"` // 价格(0表示免费或已包含在会员卡内) + CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:"创建时间"` // 创建时间 + UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:"更新时间"` // 更新时间 +} diff --git a/Backend/internal/model/entity/role_cards.go b/Backend/internal/model/entity/role_cards.go new file mode 100644 index 0000000..1fb262c --- /dev/null +++ b/Backend/internal/model/entity/role_cards.go @@ -0,0 +1,38 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +import ( + "github.com/gogf/gf/v2/os/gtime" +) + +// RoleCards is the golang structure for table role_cards. +type RoleCards struct { + Id uint64 `json:"id" orm:"id" description:"角色卡ID"` // 角色卡ID + UserId uint64 `json:"userId" orm:"user_id" description:"所属用户ID"` // 所属用户ID + DepartmentId uint64 `json:"departmentId" orm:"department_id" description:"所属部门ID"` // 所属部门ID + Commendation uint `json:"commendation" orm:"commendation" description:"嘉奖次数"` // 嘉奖次数 + Reprimand uint `json:"reprimand" orm:"reprimand" description:"申戒次数"` // 申戒次数 + BlueTrack uint `json:"blueTrack" orm:"blue_track" description:"蓝轨(0-40)"` // 蓝轨(0-40) + YellowTrack uint `json:"yellowTrack" orm:"yellow_track" description:"黄轨(0-40)"` // 黄轨(0-40) + RedTrack uint `json:"redTrack" orm:"red_track" description:"红轨(0-40)"` // 红轨(0-40) + ArcAbnormal string `json:"arcAbnormal" orm:"arc_abnormal" description:"ARC:异常"` // ARC:异常 + ArcReality string `json:"arcReality" orm:"arc_reality" description:"ARC:现实"` // ARC:现实 + ArcPosition string `json:"arcPosition" orm:"arc_position" description:"ARC:职位"` // ARC:职位 + AgentName string `json:"agentName" orm:"agent_name" description:"特工名字"` // 特工名字 + CodeName string `json:"codeName" orm:"code_name" description:"代号"` // 代号 + Gender string `json:"gender" orm:"gender" description:"性别"` // 性别 + QaMeticulous uint `json:"qaMeticulous" orm:"qa_meticulous" description:"Meticulousness (0-100, QA)"` // Meticulousness (0-100, QA) + QaDeception uint `json:"qaDeception" orm:"qa_deception" description:"Deception (0-100, QA)"` // Deception (0-100, QA) + QaVigor uint `json:"qaVigor" orm:"qa_vigor" description:"Vigor / Drive (0-100, QA)"` // Vigor / Drive (0-100, QA) + QaEmpathy uint `json:"qaEmpathy" orm:"qa_empathy" description:"Empathy (0-100, QA)"` // Empathy (0-100, QA) + QaInitiative uint `json:"qaInitiative" orm:"qa_initiative" description:"Initiative (0-100, QA)"` // Initiative (0-100, QA) + QaResilience uint `json:"qaResilience" orm:"qa_resilience" description:"Resilience / Persistence (0-100, QA)"` // Resilience / Persistence (0-100, QA) + QaPresence uint `json:"qaPresence" orm:"qa_presence" description:"Presence / Charisma (0-100, QA)"` // Presence / Charisma (0-100, QA) + QaProfessional uint `json:"qaProfessional" orm:"qa_professional" description:"Professionalism (0-100, QA)"` // Professionalism (0-100, QA) + QaDiscretion uint `json:"qaDiscretion" orm:"qa_discretion" description:"Discretion / Low profile (0-100, QA)"` // Discretion / Low profile (0-100, QA) + CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:"创建时间"` // 创建时间 + UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:"更新时间"` // 更新时间 +} diff --git a/Backend/internal/model/entity/subscribe.go b/Backend/internal/model/entity/subscribe.go new file mode 100644 index 0000000..61c3fd7 --- /dev/null +++ b/Backend/internal/model/entity/subscribe.go @@ -0,0 +1,20 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +import ( + "github.com/gogf/gf/v2/os/gtime" +) + +// Subscribe is the golang structure for table subscribe. +type Subscribe struct { + Id uint64 `json:"id" orm:"id" description:"自增主键"` // 自增主键 + UserId uint64 `json:"userId" orm:"user_id" description:"用户ID(关注者,本人)"` // 用户ID(关注者,本人) + FollowId uint64 `json:"followId" orm:"follow_id" description:"被关注的用户ID"` // 被关注的用户ID + Status int `json:"status" orm:"status" description:"状态:1=关注中 0=取消关注"` // 状态:1=关注中 0=取消关注 + Remark string `json:"remark" orm:"remark" description:"备注名(user_id 对 follow_id 的备注)"` // 备注名(user_id 对 follow_id 的备注) + CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:"创建时间"` // 创建时间 + UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:"更新时间"` // 更新时间 +} diff --git a/Backend/internal/model/entity/trpg_room.go b/Backend/internal/model/entity/trpg_room.go new file mode 100644 index 0000000..06848c7 --- /dev/null +++ b/Backend/internal/model/entity/trpg_room.go @@ -0,0 +1,32 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +import ( + "github.com/gogf/gf/v2/os/gtime" +) + +// TrpgRoom is the golang structure for table trpg_room. +type TrpgRoom struct { + Id uint64 `json:"id" orm:"id" description:"自增主键"` // 自增主键 + RoomId string `json:"roomId" orm:"room_id" description:"房间全局唯一ID(建议UUID)"` // 房间全局唯一ID(建议UUID) + RoomCode string `json:"roomCode" orm:"room_code" description:"房间号(玩家看到/输入的房间号)"` // 房间号(玩家看到/输入的房间号) + RoomName string `json:"roomName" orm:"room_name" description:"房间名称"` // 房间名称 + HostId uint64 `json:"hostId" orm:"host_id" description:"主持人用户ID(GM/KP/经理,对应用户表id)"` // 主持人用户ID(GM/KP/经理,对应用户表id) + HostNickname string `json:"hostNickname" orm:"host_nickname" description:"主持人昵称(冗余字段,可选)"` // 主持人昵称(冗余字段,可选) + MaxPlayers uint `json:"maxPlayers" orm:"max_players" description:"最大玩家人数(不含主持人,可按需要约定)"` // 最大玩家人数(不含主持人,可按需要约定) + CurrentPlayers uint `json:"currentPlayers" orm:"current_players" description:"当前玩家人数(不含主持人)"` // 当前玩家人数(不含主持人) + HasPassword int `json:"hasPassword" orm:"has_password" description:"是否有密码:0无 1有"` // 是否有密码:0无 1有 + RoomPassword string `json:"roomPassword" orm:"room_password" description:"房间密码(建议存加密/哈希后的密码)"` // 房间密码(建议存加密/哈希后的密码) + IsPrivate int `json:"isPrivate" orm:"is_private" description:"是否私密房:0公开 1私密"` // 是否私密房:0公开 1私密 + Status int `json:"status" orm:"status" description:"房间状态:0未开始 1进行中 2已结束 3已关闭"` // 房间状态:0未开始 1进行中 2已结束 3已关闭 + SystemName string `json:"systemName" orm:"system_name" description:"规则系统:如 COC、DND5E 等"` // 规则系统:如 COC、DND5E 等 + ScenarioName string `json:"scenarioName" orm:"scenario_name" description:"模组/剧本名称"` // 模组/剧本名称 + Description string `json:"description" orm:"description" description:"房间简介/招募说明"` // 房间简介/招募说明 + CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:"房间创建时间"` // 房间创建时间 + StartedAt *gtime.Time `json:"startedAt" orm:"started_at" description:"开团时间"` // 开团时间 + EndedAt *gtime.Time `json:"endedAt" orm:"ended_at" description:"结束时间"` // 结束时间 + UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:"信息最近更新时间"` // 信息最近更新时间 +} diff --git a/Backend/internal/model/entity/user_department.go b/Backend/internal/model/entity/user_department.go new file mode 100644 index 0000000..c511aae --- /dev/null +++ b/Backend/internal/model/entity/user_department.go @@ -0,0 +1,18 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +import ( + "github.com/gogf/gf/v2/os/gtime" +) + +// UserDepartment is the golang structure for table user_department. +type UserDepartment struct { + Id uint64 `json:"id" orm:"id" description:""` // + UserId uint64 `json:"userId" orm:"user_id" description:"用户ID(对应 users.id)"` // 用户ID(对应 users.id) + DeptId uint64 `json:"deptId" orm:"dept_id" description:"部门ID(对应 department.id)"` // 部门ID(对应 department.id) + CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:"创建时间"` // 创建时间 + UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:"更新时间"` // 更新时间 +} diff --git a/Backend/internal/model/entity/users.go b/Backend/internal/model/entity/users.go new file mode 100644 index 0000000..80db281 --- /dev/null +++ b/Backend/internal/model/entity/users.go @@ -0,0 +1,33 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +import ( + "github.com/gogf/gf/v2/os/gtime" +) + +// Users is the golang structure for table users. +type Users struct { + Id uint64 `json:"id" orm:"id" description:"用户ID"` // 用户ID + Account string `json:"account" orm:"account" description:"账号"` // 账号 + Password string `json:"password" orm:"password" description:"密码哈希"` // 密码哈希 + Nickname string `json:"nickname" orm:"nickname" description:"昵称"` // 昵称 + Gender int `json:"gender" orm:"gender" description:"性别:0未知 1男 2女"` // 性别:0未知 1男 2女 + BirthDate *gtime.Time `json:"birthDate" orm:"birth_date" description:"生日"` // 生日 + UserType string `json:"userType" orm:"user_type" description:"用户类型:normal普通用户,vip为VIP用户"` // 用户类型:normal普通用户,vip为VIP用户 + VipStartAt *gtime.Time `json:"vipStartAt" orm:"vip_start_at" description:"VIP开始时间"` // VIP开始时间 + VipEndAt *gtime.Time `json:"vipEndAt" orm:"vip_end_at" description:"VIP结束时间"` // VIP结束时间 + CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:"创建时间"` // 创建时间 + UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:"更新时间"` // 更新时间 + RealityRole string `json:"realityRole" orm:"reality_role" description:"现实身份/角色"` // 现实身份/角色 + AbnormalRole string `json:"abnormalRole" orm:"abnormal_role" description:"异常身份/角色"` // 异常身份/角色 + JobTitle string `json:"jobTitle" orm:"job_title" description:"职位"` // 职位 + Commendation int `json:"commendation" orm:"Commendation" description:""` // + Admonition int `json:"admonition" orm:"Admonition" description:""` // + Email string `json:"email" orm:"email" description:""` // + RedTrace int `json:"redTrace" orm:"red_trace" description:"红轨"` // 红轨 + YellowTrace int `json:"yellowTrace" orm:"yellow_trace" description:"黄轨"` // 黄轨 + BlueTrace int `json:"blueTrace" orm:"blue_trace" description:"蓝轨"` // 蓝轨 +} diff --git a/Backend/internal/model/entity/workout_logs.go b/Backend/internal/model/entity/workout_logs.go new file mode 100644 index 0000000..1dee44e --- /dev/null +++ b/Backend/internal/model/entity/workout_logs.go @@ -0,0 +1,22 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package entity + +import ( + "github.com/gogf/gf/v2/os/gtime" +) + +// WorkoutLogs is the golang structure for table workout_logs. +type WorkoutLogs struct { + Id uint64 `json:"id" orm:"id" description:"健身记录ID"` // 健身记录ID + UserId uint64 `json:"userId" orm:"user_id" description:"关联的用户ID"` // 关联的用户ID + WorkoutDate *gtime.Time `json:"workoutDate" orm:"workout_date" description:"健身日期"` // 健身日期 + StartTime *gtime.Time `json:"startTime" orm:"start_time" description:"开始时间"` // 开始时间 + EndTime *gtime.Time `json:"endTime" orm:"end_time" description:"结束时间"` // 结束时间 + DurationMinutes int `json:"durationMinutes" orm:"duration_minutes" description:"时长(分钟)"` // 时长(分钟) + Calories int `json:"calories" orm:"calories" description:"估算消耗的卡路里"` // 估算消耗的卡路里 + Description string `json:"description" orm:"description" description:"本次训练的备注,比如训练内容、部位等"` // 本次训练的备注,比如训练内容、部位等 + CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:"记录创建时间"` // 记录创建时间 +} diff --git a/Backend/internal/model/login.go b/Backend/internal/model/login.go new file mode 100644 index 0000000..be33707 --- /dev/null +++ b/Backend/internal/model/login.go @@ -0,0 +1,8 @@ +package model + +type LoginField struct { + Account string `json:"account" v:"required#账号不能为空"` + Password string `json:"password" v:"required#密码不能为空"` + Nickname string `json:"nickname" v:"required#昵称不能为空"` + Email string `json:"email"` +} diff --git a/Backend/internal/model/response/common.go b/Backend/internal/model/response/common.go new file mode 100644 index 0000000..1bb92ee --- /dev/null +++ b/Backend/internal/model/response/common.go @@ -0,0 +1,8 @@ +package response + +// 分页结果 +type PageResult struct { + Page int `json:"page"` + PageSize int `json:"pageSize"` + Total int `json:"total"` +} diff --git a/Backend/internal/model/trpg_room.go b/Backend/internal/model/trpg_room.go new file mode 100644 index 0000000..404fea9 --- /dev/null +++ b/Backend/internal/model/trpg_room.go @@ -0,0 +1,53 @@ +// ================================================================================= +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// ================================================================================= + +package model + +import ( + "github.com/gogf/gf/v2/os/gtime" +) + +// TrpgRoom is the golang structure for table trpg_room. +type RoomParams struct { + Id uint64 `json:"id" orm:"id" description:"自增主键"` // 自增主键 + RoomId string `json:"roomId" orm:"room_id" description:"房间全局唯一ID(建议UUID)"` // 房间全局唯一ID(建议UUID) + RoomCode string `json:"roomCode" orm:"room_code" description:"房间号(玩家看到/输入的房间号)"` // 房间号(玩家看到/输入的房间号) + RoomName string `json:"roomName" orm:"room_name" description:"房间名称"` // 房间名称 + HostId uint64 `json:"hostId" orm:"host_id" description:"主持人用户ID(GM/KP/经理,对应用户表id)"` // 主持人用户ID(GM/KP/经理,对应用户表id) + HostNickname string `json:"hostNickname" orm:"host_nickname" description:"主持人昵称(冗余字段,可选)"` // 主持人昵称(冗余字段,可选) + MaxPlayers uint `json:"maxPlayers" orm:"max_players" description:"最大玩家人数(不含主持人,可按需要约定)"` // 最大玩家人数(不含主持人,可按需要约定) + CurrentPlayers uint `json:"currentPlayers" orm:"current_players" description:"当前玩家人数(不含主持人)"` // 当前玩家人数(不含主持人) + HasPassword int `json:"hasPassword" orm:"has_password" description:"是否有密码:0无 1有"` // 是否有密码:0无 1有 + RoomPassword string `json:"roomPassword" orm:"room_password" description:"房间密码(建议存加密/哈希后的密码)"` // 房间密码(建议存加密/哈希后的密码) + IsPrivate int `json:"isPrivate" orm:"is_private" description:"是否私密房:0公开 1私密"` // 是否私密房:0公开 1私密 + Status int `json:"status" orm:"status" description:"房间状态:0未开始 1进行中 2已结束 3已关闭"` // 房间状态:0未开始 1进行中 2已结束 3已关闭 + SystemName string `json:"systemName" orm:"system_name" description:"规则系统:如 COC、DND5E 等"` // 规则系统:如 COC、DND5E 等 + ScenarioName string `json:"scenarioName" orm:"scenario_name" description:"模组/剧本名称"` // 模组/剧本名称 + Description string `json:"description" orm:"description" description:"房间简介/招募说明"` // 房间简介/招募说明 + CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:"房间创建时间"` // 房间创建时间 + StartedAt *gtime.Time `json:"startedAt" orm:"started_at" description:"开团时间"` // 开团时间 + EndedAt *gtime.Time `json:"endedAt" orm:"ended_at" description:"结束时间"` // 结束时间 + UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:"信息最近更新时间"` // 信息最近更新时间 +} + +type RoomView struct { + Id uint64 `json:"id" orm:"id" description:"自增主键"` // 自增主键 + RoomId string `json:"roomId" orm:"room_id" description:"房间全局唯一ID(建议UUID)"` // 房间全局唯一ID(建议UUID) + RoomCode string `json:"roomCode" orm:"room_code" description:"房间号(玩家看到/输入的房间号)"` // 房间号(玩家看到/输入的房间号) + RoomName string `json:"roomName" orm:"room_name" description:"房间名称"` // 房间名称 + HostId uint64 `json:"hostId" orm:"host_id" description:"主持人用户ID(GM/KP/经理,对应用户表id)"` // 主持人用户ID(GM/KP/经理,对应用户表id) + HostNickname string `json:"hostNickname" orm:"host_nickname" description:"主持人昵称(冗余字段,可选)"` // 主持人昵称(冗余字段,可选) + MaxPlayers uint `json:"maxPlayers" orm:"max_players" description:"最大玩家人数(不含主持人,可按需要约定)"` // 最大玩家人数(不含主持人,可按需要约定) + CurrentPlayers uint `json:"currentPlayers" orm:"current_players" description:"当前玩家人数(不含主持人)"` // 当前玩家人数(不含主持人) + HasPassword int `json:"hasPassword" orm:"has_password" description:"是否有密码:0无 1有"` // 是否有密码:0无 1有 + IsPrivate int `json:"isPrivate" orm:"is_private" description:"是否私密房:0公开 1私密"` // 是否私密房:0公开 1私密 + Status int `json:"status" orm:"status" description:"房间状态:0未开始 1进行中 2已结束 3已关闭"` // 房间状态:0未开始 1进行中 2已结束 3已关闭 + SystemName string `json:"systemName" orm:"system_name" description:"规则系统:如 COC、DND5E 等"` // 规则系统:如 COC、DND5E 等 + ScenarioName string `json:"scenarioName" orm:"scenario_name" description:"模组/剧本名称"` // 模组/剧本名称 + Description string `json:"description" orm:"description" description:"房间简介/招募说明"` // 房间简介/招募说明 + CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:"房间创建时间"` // 房间创建时间 + StartedAt *gtime.Time `json:"startedAt" orm:"started_at" description:"开团时间"` // 开团时间 + EndedAt *gtime.Time `json:"endedAt" orm:"ended_at" description:"结束时间"` // 结束时间 + +} diff --git a/Backend/internal/model/user.go b/Backend/internal/model/user.go new file mode 100644 index 0000000..80aac1e --- /dev/null +++ b/Backend/internal/model/user.go @@ -0,0 +1,32 @@ +package model + +import "github.com/gogf/gf/v2/os/gtime" + +type User struct { + Id uint64 `json:"id" orm:"id" description:"用户ID"` // 用户ID + Account string `json:"account" orm:"account" description:"账号"` // 账号 + Password string `json:"password" orm:"password" description:"密码哈希"` // 密码哈希 + Nickname string `json:"nickname" orm:"nickname" description:"昵称"` // 昵称 + Gender int `json:"gender" orm:"gender" description:"性别:0未知 1男 2女"` // 性别:0未知 1男 2女 + BirthDate *gtime.Time `json:"birthDate" orm:"birth_date" description:"生日"` // 生日 + UserType string `json:"userType" orm:"user_type" description:"用户类型:normal普通用户,vip为VIP用户"` // 用户类型:normal普通用户,vip为VIP用户 + VipStartAt *gtime.Time `json:"vipStartAt" orm:"vip_start_at" description:"VIP开始时间"` // VIP开始时间 + VipEndAt *gtime.Time `json:"vipEndAt" orm:"vip_end_at" description:"VIP结束时间"` // VIP结束时间 + CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:"创建时间"` // 创建时间 + UpdatedAt *gtime.Time `json:"updatedAt" orm:"updated_at" description:"更新时间"` // 更新时间 +} + +type UserViewParams struct { + Id uint64 `json:"id" orm:"id" description:"用户ID"` // 用户ID + Account string `json:"account" orm:"account" description:"账号"` // 账号 + Nickname string `json:"nickname" orm:"nickname" description:"昵称"` // 昵称 + Gender int `json:"gender" orm:"gender" description:"性别:0未知 1男 2女"` // 性别:0未知 1男 2女 + BirthDate *gtime.Time `json:"birthDate" orm:"birth_date" description:"生日"` // 生日 + UserType string `json:"userType" orm:"user_type" description:"用户类型:normal普通用户,vip为VIP用户"` // 用户类型:normal普通用户,vip为VIP用户 + VipStartAt *gtime.Time `json:"vipStartAt" orm:"vip_start_at" description:"VIP开始时间"` // VIP开始时间 + VipEndAt *gtime.Time `json:"vipEndAt" orm:"vip_end_at" description:"VIP结束时间"` // VIP结束时间 + CreatedAt *gtime.Time `json:"createdAt" orm:"created_at" description:"创建时间"` // 创建时间 + RealityRole string `json:"realityRole" orm:"reality_role" description:"现实身份/角色"` // 现实身份/角色 + AbnormalRole string `json:"abnormalRole" orm:"abnormal_role" description:"异常身份/角色"` // 异常身份/角色 + JobTitle string `json:"jobTitle" orm:"job_title" description:"职位"` // 职位 +} diff --git a/Backend/internal/packed/packed.go b/Backend/internal/packed/packed.go new file mode 100644 index 0000000..e20ab1e --- /dev/null +++ b/Backend/internal/packed/packed.go @@ -0,0 +1 @@ +package packed diff --git a/Backend/internal/service/containment.go b/Backend/internal/service/containment.go new file mode 100644 index 0000000..afb9ea9 --- /dev/null +++ b/Backend/internal/service/containment.go @@ -0,0 +1,35 @@ +// ================================================================================ +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// You can delete these comments if you wish manually maintain this interface file. +// ================================================================================ + +package service + +import ( + "context" + v1 "leke/api/containment/v1" +) + +type ( + IContainment interface { + ContainmentRepoList(ctx context.Context, req *v1.ContainmentRepoListReq) (res *v1.ContainmentRepoListRes, err error) + ContainmentRepoView(ctx context.Context, req *v1.ContainmentRepoViewReq) (res *v1.ContainmentRepoViewRes, err error) + ContainmentRepoUpdate(ctx context.Context, req *v1.ContainmentRepoUpdateReq) (res *v1.ContainmentRepoUpdateRes, err error) + ContainmentRepoDelete(ctx context.Context, req *v1.ContainmentRepoDeleteReq) (res *v1.ContainmentRepoDeleteRes, err error) + } +) + +var ( + localContainment IContainment +) + +func Containment() IContainment { + if localContainment == nil { + panic("implement not found for interface IContainment, forgot register?") + } + return localContainment +} + +func RegisterContainment(i IContainment) { + localContainment = i +} diff --git a/Backend/internal/service/department.go b/Backend/internal/service/department.go new file mode 100644 index 0000000..e5131ba --- /dev/null +++ b/Backend/internal/service/department.go @@ -0,0 +1,41 @@ +// ================================================================================ +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// You can delete these comments if you wish manually maintain this interface file. +// ================================================================================ + +package service + +import ( + "context" + v1 "leke/api/department/v1" +) + +type ( + IDepartment interface { + // DepartmentList 获取部门列表 + DepartmentList(ctx context.Context, req *v1.DepartmentListReq) (res *v1.DepartmentListRes, err error) + // DepartmentView 获取部门详情 + DepartmentView(ctx context.Context, req *v1.DepartmentViewReq) (res *v1.DepartmentViewRes, err error) + // DepartmentCreate 创建部门 + DepartmentCreate(ctx context.Context, req *v1.DepartmentCreateReq) (res *v1.DepartmentCreateRes, err error) + // DepartmentUpdate 更新部门 + DepartmentUpdate(ctx context.Context, req *v1.DepartmentUpdateReq) (res *v1.DepartmentUpdateRes, err error) + // DepartmentDelete 删除部门 + DepartmentDelete(ctx context.Context, req *v1.DepartmentDeleteReq) (res *v1.DepartmentDeleteRes, err error) + } +) + +var ( + localDepartment IDepartment +) + +func Department() IDepartment { + if localDepartment == nil { + panic("implement not found for interface IDepartment, forgot register?") + } + return localDepartment +} + +func RegisterDepartment(i IDepartment) { + localDepartment = i +} diff --git a/Backend/internal/service/email.go b/Backend/internal/service/email.go new file mode 100644 index 0000000..76e3ff4 --- /dev/null +++ b/Backend/internal/service/email.go @@ -0,0 +1,139 @@ +package service + +import ( + "bytes" + "context" + "fmt" + "html/template" + "io/ioutil" + "math/rand" + "net/smtp" + "time" + + "github.com/jordan-wright/email" + "github.com/patrickmn/go-cache" +) + +var ( + // 验证码缓存 + // 缓存中的验证代码将在创建后5分钟内有效,且每隔10分钟进行一次清理。 + verificationCodeCache = cache.New(5*time.Minute, 10*time.Minute) +) + +type EmailService interface { + // SendVerificationCode 向用户的邮箱发送验证码 + SendVerificationCode(ctx context.Context, to string) error + // VerifyVerificationCode 验证邮箱的验证码 + VerifyVerificationCode(email string, code string) bool +} + +type emailService struct { +} + +func NewEmailService() EmailService { + return &emailService{} +} + +// SendVerificationCode 向用户的邮箱发送验证码 +func (e *emailService) SendVerificationCode(ctx context.Context, to string) error { + code := generateVerificationCode() + + err := e.sendVerificationCode(to, code) + if err != nil { + return err + } + + // 将验证码存储在缓存中以供后续验证 + verificationCodeCache.Set(to, code, cache.DefaultExpiration) + + return nil +} + +// sendVerificationCode 发送验证代码到指定的邮箱。 +// 参数 to: 邮件接收人的邮箱地址。 +// 参数 code: 需要发送的验证代码。 +// 返回值 error: 发送过程中遇到的任何错误。 +func (e *emailService) sendVerificationCode(to string, code string) error { + // 创建一个新的邮件实例 + em := email.NewEmail() + em.From = "3149026837@qq.com" + em.To = []string{to} + em.Subject = "验证码" + + // 读取HTML模板文件 + htmlFilePath := "resource/public/verification_code.html" + if tmplContent, err := ioutil.ReadFile(htmlFilePath); err == nil { + // 成功读取模板文件,使用模板 + tmpl, err := template.New("verification").Parse(string(tmplContent)) + if err == nil { + var buf bytes.Buffer + data := map[string]string{"Code": code} + if err := tmpl.Execute(&buf, data); err == nil { + em.HTML = buf.Bytes() + } else { + // 模板执行失败,使用默认内容 + em.HTML = []byte(fmt.Sprintf(` +

验证码

+

您的验证码是: %s

+

此验证码将在5分钟后过期。

+ `, code)) + } + } else { + // 模板解析失败,使用默认内容 + em.HTML = []byte(fmt.Sprintf(` +

验证码

+

您的验证码是: %s

+

此验证码将在5分钟后过期。

+ `, code)) + } + } else { + // 读取模板文件失败,使用默认内容 + em.HTML = []byte(fmt.Sprintf(` +

验证码

+

您的验证码是: %s

+

此验证码将在5分钟后过期。

+ `, code)) + } + + // 发送邮件(这里使用QQ进行发送邮件验证码) + _ = em.Send( + "smtp.qq.com:587", + smtp.PlainAuth("", "3149026837@qq.com", "szkgunhouolidghb", "smtp.qq.com"), + ) + + //if err != nil { + // g.Log().Errorf(context.Background(), "send email error: %+v", err) + // return fmt.Errorf("发送邮件失败: %v", err) + //} + + return nil // 邮件发送成功,返回nil +} + +// generateVerificationCode 随机生成一个6位数的验证码。 +func generateVerificationCode() string { + rand.Seed(time.Now().UnixNano()) + code := fmt.Sprintf("%06d", rand.Intn(1000000)) + return code +} + +// VerifyVerificationCode 验证发送给用户的验证码 +func (e *emailService) VerifyVerificationCode(email string, code string) bool { + // 调试代码 + if code == "123456" { + return true + } + + // 从缓存中检索验证码 + cachedCode, found := verificationCodeCache.Get(email) + // 如果没有找到验证码或者验证码过期,返回false + if !found { + return false + } + + // 比较缓存中的代码与提供的代码 + if cachedCode != code { + return false + } + + return true +} diff --git a/Backend/internal/service/fans.go b/Backend/internal/service/fans.go new file mode 100644 index 0000000..584b29c --- /dev/null +++ b/Backend/internal/service/fans.go @@ -0,0 +1,81 @@ +// ================================================================================ +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// You can delete these comments if you wish manually maintain this interface file. +// ================================================================================ + +package service + +import ( + "context" + v1 "leke/api/user/v1" +) + +type ( + IFans interface { + // FansList 获取粉丝列表 + // + // 参数: + // - ctx context.Context: 上下文信息 + // - req *v1.FansListReq: 粉丝列表请求参数 + // + // 返回: + // - res *v1.FansListRes: 粉丝列表响应结果 + // - err error: 错误信息 + FansList(ctx context.Context, req *v1.FansListReq) (res *v1.FansListRes, err error) + // FansView 获取粉丝详情 + // + // 参数: + // - ctx context.Context: 上下文信息 + // - req *v1.FansViewReq: 粉丝详情请求参数 + // + // 返回: + // - res *v1.FansViewRes: 粉丝详情响应结果 + // - err error: 错误信息 + FansView(ctx context.Context, req *v1.FansViewReq) (res *v1.FansViewRes, err error) + // FansUpdate 更新粉丝信息 + // + // 参数: + // - ctx context.Context: 上下文信息 + // - req *v1.FansUpdateReq: 粉丝更新请求参数 + // + // 返回: + // - res *v1.FansUpdateRes: 粉丝更新响应结果 + // - err error: 错误信息 + FansUpdate(ctx context.Context, req *v1.FansUpdateReq) (res *v1.FansUpdateRes, err error) + // FansDelete 删除粉丝关系 + // + // 参数: + // - ctx context.Context: 上下文信息 + // - req *v1.FansDeleteReq: 粉丝删除请求参数 + // + // 返回: + // - res *v1.FansDeleteRes: 粉丝删除响应结果 + // - err error: 错误信息 + FansDelete(ctx context.Context, req *v1.FansDeleteReq) (res *v1.FansDeleteRes, err error) + // FansCreate 创建粉丝关系 + // + // 参数: + // - ctx context.Context: 上下文信息 + // - req *v1.FansCreateReq: 粉丝创建请求参数 + // + // 返回: + // - res *v1.FansCreateRes: 粉丝创建响应结果 + // - err error: 错误信息 + FansCreate(ctx context.Context, req *v1.FansCreateReq) (res *v1.FansCreateRes, err error) + } +) + +var ( + localFans IFans +) + +func Fans() IFans { + if localFans == nil { + panic("implement not found for interface IFans, forgot register?") + } + return localFans +} + +func RegisterFans(i IFans) { + localFans = i +} diff --git a/Backend/internal/service/forum_comments.go b/Backend/internal/service/forum_comments.go new file mode 100644 index 0000000..fea2458 --- /dev/null +++ b/Backend/internal/service/forum_comments.go @@ -0,0 +1,65 @@ +// ================================================================================ +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// You can delete these comments if you wish manually maintain this interface file. +// ================================================================================ + +package service + +import ( + "context" + v1 "leke/api/forum/v1" +) + +type ( + IForumComments interface { + // ForumCommentsCreate 创建评论 + Create(ctx context.Context, req *v1.ForumCommentsCreateReq) (res *v1.ForumCommentsCreateRes, err error) + // ForumCommentsUpdate 更新评论 + Update(ctx context.Context, req *v1.ForumCommentsUpdateReq) (res *v1.ForumCommentsUpdateRes, err error) + // ForumCommentsView 查看评论 + View(ctx context.Context, req *v1.ForumCommentsViewReq) (res *v1.ForumCommentsViewRes, err error) + // ForumCommentsList 评论列表 + List(ctx context.Context, req *v1.ForumCommentsListReq) (res *v1.ForumCommentsListRes, err error) + // ForumCommentsDelete 删除评论 + Delete(ctx context.Context, req *v1.ForumCommentsDeleteReq) (res *v1.ForumCommentsDeleteRes, err error) + } + IForumPosts interface { + // ForumPostsCreate 创建帖子 + Create(ctx context.Context, req *v1.ForumPostsCreateReq) (res *v1.ForumPostsCreateRes, err error) + // ForumPostsUpdate 更新帖子 + Update(ctx context.Context, req *v1.ForumPostsUpdateReq) (res *v1.ForumPostsUpdateRes, err error) + // ForumPostsView 查看帖子 + View(ctx context.Context, req *v1.ForumPostsViewReq) (res *v1.ForumPostsViewRes, err error) + // ForumPostsList 帖子列表 + List(ctx context.Context, req *v1.ForumPostsListReq) (res *v1.ForumPostsListRes, err error) + // ForumPostsDelete 删除帖子 + Delete(ctx context.Context, req *v1.ForumPostsDeleteReq) (res *v1.ForumPostsDeleteRes, err error) + } +) + +var ( + localForumComments IForumComments + localForumPosts IForumPosts +) + +func ForumComments() IForumComments { + if localForumComments == nil { + panic("implement not found for interface IForumComments, forgot register?") + } + return localForumComments +} + +func RegisterForumComments(i IForumComments) { + localForumComments = i +} + +func ForumPosts() IForumPosts { + if localForumPosts == nil { + panic("implement not found for interface IForumPosts, forgot register?") + } + return localForumPosts +} + +func RegisterForumPosts(i IForumPosts) { + localForumPosts = i +} diff --git a/Backend/internal/service/login.go b/Backend/internal/service/login.go new file mode 100644 index 0000000..beec773 --- /dev/null +++ b/Backend/internal/service/login.go @@ -0,0 +1,41 @@ +// ================================================================================ +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// You can delete these comments if you wish manually maintain this interface file. +// ================================================================================ + +package service + +import ( + "context" + v1 "leke/api/login/v1" +) + +type ( + ILogin interface { + Register(ctx context.Context, loginReq *v1.RegisterReq) (res *v1.RegisterRes, err error) + Login(ctx context.Context, loginReq *v1.LoginReq) (res *v1.LoginRes, err error) + // 通过邮箱注册 + RegisterByEmail(ctx context.Context, req *v1.RegisterByEmailReq) (res *v1.RegisterByEmailRes, err error) + // 通过邮箱登录 + LoginByEmail(ctx context.Context, req *v1.LoginByEmailReq) (res *v1.LoginByEmailRes, err error) + // 发送验证码 + SendVerificationCode(ctx context.Context, req *v1.SendVerificationCodeReq) (res *v1.SendVerificationCodeRes, err error) + // 退出登录 + Logout(ctx context.Context, req *v1.LogoutReq) (res *v1.LogoutRes, err error) + } +) + +var ( + localLogin ILogin +) + +func Login() ILogin { + if localLogin == nil { + panic("implement not found for interface ILogin, forgot register?") + } + return localLogin +} + +func RegisterLogin(i ILogin) { + localLogin = i +} diff --git a/Backend/internal/service/room.go b/Backend/internal/service/room.go new file mode 100644 index 0000000..5c85e78 --- /dev/null +++ b/Backend/internal/service/room.go @@ -0,0 +1,37 @@ +// ================================================================================ +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// You can delete these comments if you wish manually maintain this interface file. +// ================================================================================ + +package service + +import ( + "context" + v1 "leke/api/room/v1" +) + +type ( + IRoom interface { + RoomList(ctx context.Context, req *v1.RoomListReq) (res *v1.RoomListRes, err error) + RoomView(ctx context.Context, req *v1.RoomViewReq) (res *v1.RoomViewRes, err error) + RoomUpdate(ctx context.Context, req *v1.RoomUpdateReq) (res *v1.RoomUpdateRes, err error) + RoomDelete(ctx context.Context, req *v1.RoomDeleteReq) (res *v1.RoomDeleteRes, err error) + RoomCreate(ctx context.Context, req *v1.RoomCreateReq) (res *v1.RoomCreateRes, err error) + RoomJoin(ctx context.Context, req *v1.RoomJoinReq) (res *v1.RoomJoinRes, err error) + } +) + +var ( + localRoom IRoom +) + +func Room() IRoom { + if localRoom == nil { + panic("implement not found for interface IRoom, forgot register?") + } + return localRoom +} + +func RegisterRoom(i IRoom) { + localRoom = i +} diff --git a/Backend/internal/service/subscribe.go b/Backend/internal/service/subscribe.go new file mode 100644 index 0000000..6dfa201 --- /dev/null +++ b/Backend/internal/service/subscribe.go @@ -0,0 +1,41 @@ +// ================================================================================ +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// You can delete these comments if you wish manually maintain this interface file. +// ================================================================================ + +package service + +import ( + "context" + v1 "leke/api/user/v1" +) + +type ( + ISubscribe interface { + // SubscribeList 获取关注列表 + SubscribeList(ctx context.Context, req *v1.SubscribeListReq) (res *v1.SubscribeListRes, err error) + // SubscribeView 获取关注详情 + SubscribeView(ctx context.Context, req *v1.SubscribeViewReq) (res *v1.SubscribeViewRes, err error) + // SubscribeUpdate 更新关注信息 + SubscribeUpdate(ctx context.Context, req *v1.SubscribeUpdateReq) (res *v1.SubscribeUpdateRes, err error) + // SubscribeDelete 删除关注关系 + SubscribeDelete(ctx context.Context, req *v1.SubscribeDeleteReq) (res *v1.SubscribeDeleteRes, err error) + // SubscribeCreate 创建关注关系 + SubscribeCreate(ctx context.Context, req *v1.SubscribeCreateReq) (res *v1.SubscribeCreateRes, err error) + } +) + +var ( + localSubscribe ISubscribe +) + +func Subscribe() ISubscribe { + if localSubscribe == nil { + panic("implement not found for interface ISubscribe, forgot register?") + } + return localSubscribe +} + +func RegisterSubscribe(i ISubscribe) { + localSubscribe = i +} diff --git a/Backend/internal/service/trace.go b/Backend/internal/service/trace.go new file mode 100644 index 0000000..47dc809 --- /dev/null +++ b/Backend/internal/service/trace.go @@ -0,0 +1,39 @@ +// ================================================================================ +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// You can delete these comments if you wish manually maintain this interface file. +// ================================================================================ + +package service + +import ( + "context" + v1 "leke/api/user/v1" +) + +type ( + ITrace interface { + // TraceList 获取轨迹列表 + TraceList(ctx context.Context, req *v1.TraceListReq) (res *v1.TraceListRes, err error) + // TraceView 查看轨迹详情 + TraceView(ctx context.Context, req *v1.TraceViewReq) (res *v1.TraceViewRes, err error) + // TraceUpdate 更新轨迹信息 (增加) + TraceUpdate(ctx context.Context, req *v1.TraceUpdateReq) (res *v1.TraceUpdateRes, err error) + // TraceReduce 减少轨迹数值 + TraceReduce(ctx context.Context, req *v1.TraceReduceReq) (res *v1.TraceReduceRes, err error) + } +) + +var ( + localTrace ITrace +) + +func Trace() ITrace { + if localTrace == nil { + panic("implement not found for interface ITrace, forgot register?") + } + return localTrace +} + +func RegisterTrace(i ITrace) { + localTrace = i +} diff --git a/Backend/internal/service/user.go b/Backend/internal/service/user.go new file mode 100644 index 0000000..62d6ffa --- /dev/null +++ b/Backend/internal/service/user.go @@ -0,0 +1,47 @@ +// ================================================================================ +// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT. +// You can delete these comments if you wish manually maintain this interface file. +// ================================================================================ + +package service + +import ( + "context" + v1 "leke/api/user/v1" +) + +type ( + IUser interface { + UserList(ctx context.Context, req *v1.UserListReq) (res *v1.UserListRes, err error) + UserView(ctx context.Context, req *v1.UserViewReq) (res *v1.UserViewRes, err error) + UserUpdate(ctx context.Context, req *v1.UserUpdateReq) (res *v1.UserUpdateRes, err error) + UserDelete(ctx context.Context, req *v1.UserDeleteReq) (res *v1.UserDeleteRes, err error) + // RoleCreate 创建角色 + RoleCreate(ctx context.Context, req *v1.RoleCreateReq) (res *v1.RoleCreateRes, err error) + // RoleUpdate 更新角色 + RoleUpdate(ctx context.Context, req *v1.RoleUpdateReq) (res *v1.RoleUpdateRes, err error) + // RoleView 查看角色详情 + RoleView(ctx context.Context, req *v1.RoleViewReq) (res *v1.RoleViewRes, err error) + // RoleList 获取角色列表 + RoleList(ctx context.Context, req *v1.RoleListReq) (res *v1.RoleListRes, err error) + // RoleDelete 删除角色 + RoleDelete(ctx context.Context, req *v1.RoleDeleteReq) (res *v1.RoleDeleteRes, err error) + // RolePermissionCheck 权限查询 + RolePermissionCheck(ctx context.Context, req *v1.RolePermissionCheckReq) (res *v1.RolePermissionCheckRes, err error) + } +) + +var ( + localUser IUser +) + +func User() IUser { + if localUser == nil { + panic("implement not found for interface IUser, forgot register?") + } + return localUser +} + +func RegisterUser(i IUser) { + localUser = i +} diff --git a/Backend/leke.exe b/Backend/leke.exe new file mode 100644 index 0000000..7bf184f Binary files /dev/null and b/Backend/leke.exe differ diff --git a/Backend/main.go b/Backend/main.go new file mode 100644 index 0000000..d50b38e --- /dev/null +++ b/Backend/main.go @@ -0,0 +1,16 @@ +package main + +import ( + _ "leke/internal/packed" + + _ "leke/internal/logic" + + "leke/internal/cmd" + + _ "github.com/gogf/gf/contrib/drivers/mysql/v2" + "github.com/gogf/gf/v2/os/gctx" +) + +func main() { + cmd.Main.Run(gctx.GetInitCtx()) +} diff --git a/Backend/manifest/deploy/kustomize/base/deployment.yaml b/Backend/manifest/deploy/kustomize/base/deployment.yaml new file mode 100644 index 0000000..28f1d69 --- /dev/null +++ b/Backend/manifest/deploy/kustomize/base/deployment.yaml @@ -0,0 +1,21 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: template-single + labels: + app: template-single +spec: + replicas: 1 + selector: + matchLabels: + app: template-single + template: + metadata: + labels: + app: template-single + spec: + containers: + - name : main + image: template-single + imagePullPolicy: Always + diff --git a/Backend/manifest/deploy/kustomize/base/kustomization.yaml b/Backend/manifest/deploy/kustomize/base/kustomization.yaml new file mode 100644 index 0000000..302d92d --- /dev/null +++ b/Backend/manifest/deploy/kustomize/base/kustomization.yaml @@ -0,0 +1,8 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: +- deployment.yaml +- service.yaml + + + diff --git a/Backend/manifest/deploy/kustomize/base/service.yaml b/Backend/manifest/deploy/kustomize/base/service.yaml new file mode 100644 index 0000000..608771c --- /dev/null +++ b/Backend/manifest/deploy/kustomize/base/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: template-single +spec: + ports: + - port: 80 + protocol: TCP + targetPort: 8000 + selector: + app: template-single + diff --git a/Backend/manifest/deploy/kustomize/overlays/develop/configmap.yaml b/Backend/manifest/deploy/kustomize/overlays/develop/configmap.yaml new file mode 100644 index 0000000..3b1d0af --- /dev/null +++ b/Backend/manifest/deploy/kustomize/overlays/develop/configmap.yaml @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: template-single-configmap +data: + config.yaml: | + server: + address: ":8000" + openapiPath: "/api.json" + swaggerPath: "/swagger" + + logger: + level : "all" + stdout: true diff --git a/Backend/manifest/deploy/kustomize/overlays/develop/deployment.yaml b/Backend/manifest/deploy/kustomize/overlays/develop/deployment.yaml new file mode 100644 index 0000000..04e4851 --- /dev/null +++ b/Backend/manifest/deploy/kustomize/overlays/develop/deployment.yaml @@ -0,0 +1,10 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: template-single +spec: + template: + spec: + containers: + - name : main + image: template-single:develop \ No newline at end of file diff --git a/Backend/manifest/deploy/kustomize/overlays/develop/kustomization.yaml b/Backend/manifest/deploy/kustomize/overlays/develop/kustomization.yaml new file mode 100644 index 0000000..4731c47 --- /dev/null +++ b/Backend/manifest/deploy/kustomize/overlays/develop/kustomization.yaml @@ -0,0 +1,14 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: +- ../../base +- configmap.yaml + +patchesStrategicMerge: +- deployment.yaml + +namespace: default + + + diff --git a/Backend/manifest/docker/Dockerfile b/Backend/manifest/docker/Dockerfile new file mode 100644 index 0000000..d3abe8f --- /dev/null +++ b/Backend/manifest/docker/Dockerfile @@ -0,0 +1,16 @@ +FROM loads/alpine:3.8 + +############################################################################### +# INSTALLATION +############################################################################### + +ENV WORKDIR /app +ADD resource $WORKDIR/ +ADD ./temp/linux_amd64/main $WORKDIR/main +RUN chmod +x $WORKDIR/main + +############################################################################### +# START +############################################################################### +WORKDIR $WORKDIR +CMD ./main diff --git a/Backend/manifest/docker/docker.sh b/Backend/manifest/docker/docker.sh new file mode 100644 index 0000000..ff393f9 --- /dev/null +++ b/Backend/manifest/docker/docker.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# This shell is executed before docker build. + + + + + diff --git a/Backend/manifest/i18n/.gitkeep b/Backend/manifest/i18n/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/Backend/manifest/protobuf/.keep-if-necessary b/Backend/manifest/protobuf/.keep-if-necessary new file mode 100644 index 0000000..e69de29 diff --git a/Backend/resource/public/README.md b/Backend/resource/public/README.md new file mode 100644 index 0000000..9a26f8b --- /dev/null +++ b/Backend/resource/public/README.md @@ -0,0 +1,35 @@ +# WebSocket Echo Server 使用说明 + +## 功能说明 + +这是一个基于 GoFrame 框架实现的 WebSocket Echo 服务器示例。 + +## 使用方法 + +1. 启动服务器: + ```bash + go run main.go + ``` + +2. 访问测试页面: + 打开浏览器访问:`http://localhost:8000/` + +3. WebSocket 连接地址: + - WebSocket: `ws://localhost:8000/ws` + - 如果使用 HTTPS: `wss://localhost:8000/ws` + +## 功能特性 + +- ✅ WebSocket 连接管理 +- ✅ 消息回显(Echo) +- ✅ 连接状态监控 +- ✅ 自动重连提示 +- ✅ 支持文本消息 + +## 测试步骤 + +1. 打开浏览器访问 `http://localhost:8000/` +2. 页面会自动连接到 WebSocket 服务器 +3. 在输入框中输入消息并点击"发送"按钮 +4. 服务器会将消息回显到页面上 + diff --git a/Backend/resource/public/broadcast.html b/Backend/resource/public/broadcast.html new file mode 100644 index 0000000..646d94b --- /dev/null +++ b/Backend/resource/public/broadcast.html @@ -0,0 +1,227 @@ + + + + WebSocket 广播测试 + + + + + + +
+

+ WebSocket 广播测试 + 未连接 +

+
+ 客户端ID: | 连接状态: 等待连接... +
+
+
+
+ + + + +
+ + 提示:打开多个浏览器窗口或标签页,在一个窗口中发送消息,所有窗口都会收到广播消息 + +
+
+ + + + + diff --git a/Backend/resource/public/chatroom.html b/Backend/resource/public/chatroom.html new file mode 100644 index 0000000..b693407 --- /dev/null +++ b/Backend/resource/public/chatroom.html @@ -0,0 +1,359 @@ + + + + TrangleAgent - 聊天室测试 + + + + + + +
+

+ TrangleAgent 聊天室测试 + 未连接 +

+ +
+
+ + +
+
+ + +
+ + +
+ +
+
+
+ [系统] + 等待连接到聊天室... +
+
+ + +
+ +
+ + + + +
+
+ + + + + diff --git a/Backend/resource/public/index.html b/Backend/resource/public/index.html new file mode 100644 index 0000000..04dbae0 --- /dev/null +++ b/Backend/resource/public/index.html @@ -0,0 +1,109 @@ + + + + GoFrame WebSocket Echo Server + + + + + + +
+

GoFrame WebSocket Echo Server

+
+
+
+
+
+
+ + + + + diff --git a/Backend/resource/public/verification_code.html b/Backend/resource/public/verification_code.html new file mode 100644 index 0000000..136deab --- /dev/null +++ b/Backend/resource/public/verification_code.html @@ -0,0 +1,305 @@ + + + + + 三角机构 · 机密通行码 + + + + +
+
+
+
+
+
+
+ TRIANGLE AGENCY · INTERNAL +
+
ACCESS LEVEL: REDACTED
+
+ +
+
+
+ TRINGLE AGNCY +
+
三角机构 · 机密通信单
+
+ +
+
!
+
此邮件包含机密通行密码,仅限机构内部人员查阅。
+
+ +
内部通行码 · 请勿外传
+
+ 下方代码用于解锁指定散逸端终端。请在系统提示时准确输入。 +
+ +
+
CONTAINMENT ACCESS CODE
+
{{.Code}}
+
+ 有效期:5 分钟 · 失效后请向直属经理申请新码。 +
+
+ +
+ 使用前,请再次确认: +
· 您正在使用的是机构提供的终端或安全网络环境; +
· 周围无未授权人员,屏幕不可被旁人窥视; +
· 本通行码只用于当前任务,不得截图、转发或长期存储。 +
+ +
+ 如将本通行码泄露给三角机构外部人员, + 或被系统判定存在高风险共享行为, + 您将被立即要求前往经理办公室领取 + 1 次申戒,并记录在个人档案中。 +
+ +
+ 若您认为本邮件系误发或内容与任务不符,请立即与上级经理或风控部门联系, + 并在完成报告前不要尝试使用该通行码。 +
+ + +
+
+
+ + diff --git a/Backend/utility/.gitkeep b/Backend/utility/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/Backend/utils/Dice.go b/Backend/utils/Dice.go new file mode 100644 index 0000000..3708255 --- /dev/null +++ b/Backend/utils/Dice.go @@ -0,0 +1,48 @@ +package utils + +import ( + "math/rand" + "time" +) + +const ( + Dice4 = 4 + Dice8 = 8 + Dice10 = 10 + Dice12 = 12 + Dice20 = 20 + Dice24 = 24 + Dice30 = 30 + Dice100 = 100 +) + +// 4 8 10 12 20 24 30 100面的骰子 +func Dice(num, side int) []int { + // 初始化随机数种子 + rand.Seed(time.Now().UnixNano()) + + // 创建一个长度为num的切片用于存储结果 + result := make([]int, num) + + // 投掷num次骰子,每次的结果是1到side之间的随机数 + for i := 0; i < num; i++ { + result[i] = rand.Intn(side) + 1 + } + return result +} +func Chaos(dice []int) (sum int) { + //如果dice里面有三个三 sum为0 + trible := 0 + for i := 0; i < len(dice); i++ { + if dice[i] != 3 { + sum += dice[i] + } + if dice[i] == 3 { + trible++ + } + } + if trible == 3 { //三重升华 + return 0 + } + return sum +} diff --git a/Backend/ws/client.go b/Backend/ws/client.go new file mode 100644 index 0000000..8a6d916 --- /dev/null +++ b/Backend/ws/client.go @@ -0,0 +1,61 @@ +package ws + +import ( + "encoding/json" + + chatmodel "leke/ws/model" + + "github.com/gogf/gf/v2/net/ghttp" +) + +// Client 表示一个在线用户的 WebSocket 连接 +// 这里只是框架:先把字段和发送逻辑搭好,读写循环可以后面慢慢加。 +type Client struct { + UserId uint64 // 当前用户ID + Nickname string // 昵称(可选) + Conn *ghttp.Request // 底层 WebSocket 连接 + + // Send 是一个发送队列:其他地方往这里丢 []byte,这个 client 的写协程负责发出去 + Send chan []byte + + // Rooms 记录当前用户加入的房间(简单用 map 做集合) + Rooms map[string]bool +} + +// NewClient 创建一个新的客户端连接对象 +func NewClient(userId uint64, nickname string, conn *ghttp.Request) *Client { + return &Client{ + UserId: userId, + Nickname: nickname, + Conn: conn, + Send: make(chan []byte, 256), // 简单先给一个缓冲,防止轻微阻塞 + Rooms: make(map[string]bool), + } +} + +// EnqueueMessage 将 ChatMessage 编码为 JSON 丢到 Send 队列 +// 真正 WriteMessage 的动作建议在一个单独的 writeLoop 里做。 +func (c *Client) EnqueueMessage(msg *chatmodel.ChatMessage) error { + // 如果需要,补一些默认字段 + if msg.FromUserId == 0 { + msg.FromUserId = c.UserId + } + if msg.FromNickname == "" { + msg.FromNickname = c.Nickname + } + + data, err := json.Marshal(msg) + if err != nil { + return err + } + + select { + case c.Send <- data: + // 正常入队 + default: + // 队列满了,可以考虑:丢弃 / 断开连接 / 打日志 + // 这里简单选择丢弃,并返回错误 + return ErrSendQueueFull + } + return nil +} diff --git a/Backend/ws/dos/readme.md b/Backend/ws/dos/readme.md new file mode 100644 index 0000000..986c84b --- /dev/null +++ b/Backend/ws/dos/readme.md @@ -0,0 +1,26 @@ +### ChatMessage (服务端 → 客户端) + +```json +{ + "type": "world | room | private | system", + "subType": "message | user_join | user_leave | room_created", + "fromUserId": 123, + "fromNickname": "某某", + "roomId": "xxx", + "content": "xxx", + "time": "2025-12-08T12:34:56" +} + +**这里的“放在哪里”** 👉 放在你的项目文档里,用来说明“聊天协议”。 + +--- + +## 总结一句人话版 + +这个 JSON **不是一个要单独存起来的文件**,而是你聊天系统里「**一条消息长什么样**」的**约定**: + +- 在前端:定义成一个 TypeScript interface / JSDoc 类型,用来写 WebSocket 的 `send` 和 `onmessage`。 +- 在后端:定义成一个 Go struct,用来 `json.Unmarshal` / `json.Marshal`。 +- 在文档:写在一个协议文档里,提醒自己和队友“所有聊天消息都按这个格式来”。 + +你下一步如果愿意,我可以帮你把「前端消息类型定义 + 后端 struct + 一条从前端发到后端再广播出去的完整流程图」给你画成一个“数据流思路”,方便你对着实现。 diff --git a/Backend/ws/errors.go b/Backend/ws/errors.go new file mode 100644 index 0000000..d32bf0d --- /dev/null +++ b/Backend/ws/errors.go @@ -0,0 +1,7 @@ +package ws + +import "errors" + +var ( + ErrSendQueueFull = errors.New("客户端队列堵塞") +) diff --git a/Backend/ws/hub.go b/Backend/ws/hub.go new file mode 100644 index 0000000..bc4dfce --- /dev/null +++ b/Backend/ws/hub.go @@ -0,0 +1,132 @@ +package ws + +import ( + "sync" + + chatmodel "leke/ws/model" // 按实际路径修改 +) + +// Hub 聊天中枢:管理所有连接和房间 +type Hub struct { + mu sync.RWMutex + + // 所有在线用户:userId -> Client + clients map[uint64]*Client + + // 房间成员:roomId -> (userId -> Client) + rooms map[string]map[uint64]*Client +} + +// 全局唯一 Hub(简单起见用一个全局变量) +var ChatHub = NewHub() + +// NewHub 创建一个新的 Hub 实例 +func NewHub() *Hub { + return &Hub{ + clients: make(map[uint64]*Client), + rooms: make(map[string]map[uint64]*Client), + } +} + +// Register 注册新连接 +func (h *Hub) Register(c *Client) { + h.mu.Lock() + defer h.mu.Unlock() + + h.clients[c.UserId] = c + // 默认认为所有在线用户都在世界频道,这里你可以根据需要扩展 +} + +// Unregister 移除连接(断线/退出) +func (h *Hub) Unregister(c *Client) { + h.mu.Lock() + defer h.mu.Unlock() + + delete(h.clients, c.UserId) + + // 同时把他从所有房间移除 + for roomId, members := range h.rooms { + if _, ok := members[c.UserId]; ok { + delete(members, c.UserId) + if len(members) == 0 { + delete(h.rooms, roomId) + } + } + } +} + +// JoinRoom 加入房间 +func (h *Hub) JoinRoom(userId uint64, roomId string) { + h.mu.Lock() + defer h.mu.Unlock() + + client, ok := h.clients[userId] + if !ok { + return + } + + members, ok := h.rooms[roomId] + if !ok { + members = make(map[uint64]*Client) + h.rooms[roomId] = members + } + members[userId] = client + client.Rooms[roomId] = true +} + +// LeaveRoom 离开房间 +func (h *Hub) LeaveRoom(userId uint64, roomId string) { + h.mu.Lock() + defer h.mu.Unlock() + + client, ok := h.clients[userId] + if !ok { + return + } + + if members, ok := h.rooms[roomId]; ok { + delete(members, userId) + if len(members) == 0 { + delete(h.rooms, roomId) + } + } + delete(client.Rooms, roomId) +} + +// BroadcastWorld 向所有在线用户发送世界频道消息 +func (h *Hub) BroadcastWorld(msg *chatmodel.ChatMessage) { + h.mu.RLock() + defer h.mu.RUnlock() + + for _, client := range h.clients { + _ = client.EnqueueMessage(msg) + } +} + +// BroadcastRoom 向房间内所有用户发送消息 +func (h *Hub) BroadcastRoom(roomId string, msg *chatmodel.ChatMessage) { + h.mu.RLock() + defer h.mu.RUnlock() + + members, ok := h.rooms[roomId] + if !ok { + return + } + for _, client := range members { + _ = client.EnqueueMessage(msg) + } +} + +// SendPrivate 发送私聊消息 +func (h *Hub) SendPrivate(fromUserId, toUserId uint64, msg *chatmodel.ChatMessage) { + h.mu.RLock() + defer h.mu.RUnlock() + + toClient, ok := h.clients[toUserId] + if !ok { + return // 对方不在线,可以后面扩展:离线消息存DB + } + + msg.FromUserId = fromUserId + _ = toClient.EnqueueMessage(msg) +} diff --git a/Backend/ws/model/chat_message.go b/Backend/ws/model/chat_message.go new file mode 100644 index 0000000..a6464b4 --- /dev/null +++ b/Backend/ws/model/chat_message.go @@ -0,0 +1,34 @@ +package model + +// MessageType 消息的大类:发到哪里 +type MessageType string + +const ( + TypeWorld MessageType = "world" // 世界频道 + TypeRoom MessageType = "room" // 房间 + TypePrivate MessageType = "private" // 私聊 + TypeSystem MessageType = "system" // 系统消息(如通知、提示等) +) + +// MessageAction 消息的动作:干什么事 +type MessageAction string + +const ( + ActionSend MessageAction = "send" // 发送聊天消息 + ActionJoinRoom MessageAction = "join_room" // 加入房间 + ActionLeaveRoom MessageAction = "leave_room" // 离开房间 + ActionCreateRoom MessageAction = "create_room" // 创建房间(也可以走 HTTP) +) + +// ChatMessage WebSocket 收/发的统一结构 +// 前端和后端都按这个结构来编码/解码 JSON。 +type ChatMessage struct { + Type MessageType `json:"type"` // 消息类型:world/room/private/system + Action MessageAction `json:"action"` // 动作:send/join_room/leave_room... + FromUserId uint64 `json:"fromUserId,omitempty"` // 发送者用户ID + FromNickname string `json:"fromNickname,omitempty"` // 发送者昵称 + RoomId string `json:"roomId,omitempty"` // 房间ID(房间消息时使用) + ToUserId uint64 `json:"toUserId,omitempty"` // 目标用户ID(私聊时使用) + Content string `json:"content,omitempty"` // 文本内容 + Time string `json:"time,omitempty"` // 时间(ISO字符串,前期用 string 即可) +} diff --git a/Frontend/.gitignore b/Frontend/.gitignore new file mode 100644 index 0000000..c3c35fc --- /dev/null +++ b/Frontend/.gitignore @@ -0,0 +1,14 @@ +node_modules +dist +.DS_Store +*.log +.vscode +.idea + + + + + + + + diff --git a/Frontend/QUICKSTART.md b/Frontend/QUICKSTART.md new file mode 100644 index 0000000..358c043 --- /dev/null +++ b/Frontend/QUICKSTART.md @@ -0,0 +1,142 @@ +# 快速开始指南 + +## 安装和运行 + +1. **安装依赖** + ```bash + npm install + ``` + +2. **启动开发服务器** + ```bash + npm run dev + ``` + +3. **访问应用** + 浏览器会自动打开 http://localhost:3000 + +## 使用流程 + +### 第一步:注册账号 +1. 访问登录页面,点击"还没有账号?立即注册" +2. 填写账号、密码、确认密码和姓名 +3. 点击注册,自动登录并跳转到主页 + +### 第二步:创建或加入房间 +**创建房间:** +1. 在主页"创建房间"卡片中,输入房间名称 +2. 点击"创建房间"按钮 +3. 自动进入房间,成为主持人 + +**加入房间:** +1. 在主页"加入房间"卡片中,输入房间号 +2. 点击"加入房间"按钮 +3. 自动进入房间,成为玩家 + +### 第三步:使用骰子系统 + +**玩家初始投掷(4个D4):** +1. 进入房间后,玩家会看到"玩家初始投掷(4个D4)"区域 +2. 点击"投掷4个D4"按钮 +3. 查看投掷结果,显示4个骰子的值 +4. 如果3的数量少于3个: + - 可以输入消耗的属性值来增加3的数量 + - 如果最终达到3个3,不会产生混沌值 + - 否则,每个不是3的D4会产生1点混沌值 +5. 点击"确认投掷结果"完成初始投掷 + +**普通投掷:** +- 在"选择骰子类型"区域,点击对应的骰子按钮(D3/D4/D6/D12/D20) +- 投掷结果会显示在投掷历史中 + +**混沌值管理(仅主持人):** +- 混沌值显示在页面顶部,所有人可见 +- 主持人可以点击"消耗混沌值"按钮来消耗混沌值 + +### 第四步:总部/分部管理 + +1. 在主页点击"总部/分部管理" +2. **创建分部**(需要经理权限): + - 点击"创建分部"按钮 + - 填写分部名称和负责人 + - 点击确定 +3. **发布招聘信息**(需要经理权限): + - 在右侧"招聘信息"卡片中,点击"发布招聘" + - 填写标题和内容 + - 点击确定 +4. **查看分部信息**: + - 点击分部列表中的"查看详情" + - 可以看到该分部的天气、散一端等信息 + +### 第五步:异常管理 + +1. 在主页点击"异常管理" +2. **添加异常**: + - 点击"添加异常"按钮 + - 填写异常信息: + - 异常名称 + - 类型(实体/概念/地点/现象) + - 特殊能力 + - 焦点 + - 领域(现实/记忆/时间/空间/生命/死亡/知识/情感) + - 点击确定 +3. **收容异常**: + - 在"未收容异常"列表中,点击"收容"按钮 + - 异常会移动到"已收容异常"列表 +4. **解决异常**: + - 在"已收容异常"列表中,点击"标记为已解决" + - 异常会移动到"已解决异常"列表 +5. **设置天气**: + - 在"天气系统"卡片中,输入天气信息 + - 点击"设置天气"按钮 + +## 功能说明 + +### 混沌值规则(TRTC三角洲机构规则) + +1. **初始投掷规则**: + - 玩家开始时必须投掷4个D4 + - 如果投出的4个D4中,3的数量达到3个(可通过消耗属性值达到),则不会产生混沌值 + - 如果3的数量少于3个,每个不是3的D4会产生1点混沌值 + +2. **混沌值显示**: + - 混沌值显示在房间页面顶部,所有玩家可见 + - 只有主持人可以消耗混沌值 + +3. **普通投掷**: + - 普通投掷不会自动产生混沌值(除非特殊规则) + +### 数据存储 + +- 当前版本使用浏览器的localStorage进行数据存储 +- 所有数据保存在本地浏览器中 +- 刷新页面后数据会保留 +- 清除浏览器数据会丢失所有信息 + +## 注意事项 + +1. **房间号分享**:创建房间后,可以点击"复制房间号"按钮分享给其他玩家 +2. **角色权限**: + - 主持人:可以消耗混沌值 + - 玩家:可以投掷骰子,但不能消耗混沌值 + - 经理:可以创建分部、发布招聘信息 +3. **数据同步**:当前版本是纯前端应用,多个浏览器标签页之间的数据不会实时同步 + +## 常见问题 + +**Q: 如何切换账号?** +A: 点击右上角"退出"按钮,然后使用其他账号登录。 + +**Q: 房间数据会丢失吗?** +A: 数据保存在浏览器localStorage中,除非清除浏览器数据,否则不会丢失。 + +**Q: 可以多人同时使用吗?** +A: 当前版本是纯前端,多人使用时需要各自在浏览器中操作,数据不会实时同步。如需实时同步,需要集成WebSocket或后端API。 + + + + + + + + diff --git a/Frontend/README.md b/Frontend/README.md new file mode 100644 index 0000000..7b13ffb --- /dev/null +++ b/Frontend/README.md @@ -0,0 +1,140 @@ +# 三角洲机构 - TRPG跑团在线工具 + +基于Vue3 + Ant Design Vue构建的TRPG跑团在线工具网站,遵循TRTC三角洲机构规则。 + +## 功能特性 + +### 用户系统 +- 用户注册/登录(账号、密码、姓名) +- 用户信息管理 + +### 房间系统 +- 创建房间,房间号可分享 +- 加入房间(通过房间号) +- 房间内角色:主持人(Host)和玩家(PL) +- 房间信息展示 + +### 骰子系统 +- 支持常见骰子:D3、D4、D6、D12、D20 +- 玩家初始投掷:4个D4 +- **混沌值机制**: + - 每个不是3的D4给主持人加1点混沌值 + - 玩家投出3个3(可通过消耗属性值达到)不会产生混沌值 + - 每个不是3的骰子都会变成混沌值 + - 混沌值所有人可见 + - 主持人可以消耗混沌值 +- 投掷历史记录 + +### 总部/分部管理系统 +- 创建分部 +- 解散分部 +- 发布招聘信息(经理权限) +- 查看招聘信息(所有分部成员) +- 分部信息管理(散一端、天气系统) + +### 异常管理系统 +- 添加异常(名称、类型、特殊能力、焦点、领域) +- 异常收容 +- 异常解决 +- 异常分类:未收容、已收容、已解决 +- 天气系统设置 +- 散一端管理 + +## 技术栈 + +- Vue 3 (Composition API) +- Ant Design Vue 4.x +- Vue Router 4 +- Pinia (状态管理) +- Vite (构建工具) + +## 项目结构 + +``` +src/ +├── views/ # 页面组件 +│ ├── Login.vue # 登录页 +│ ├── Register.vue # 注册页 +│ ├── Home.vue # 主页 +│ ├── Room.vue # 房间页 +│ ├── Organization.vue # 总部/分部管理 +│ └── Anomaly.vue # 异常管理 +├── stores/ # Pinia状态管理 +│ ├── user.js # 用户状态 +│ ├── room.js # 房间状态 +│ ├── organization.js # 组织状态 +│ └── anomaly.js # 异常状态 +├── router/ # 路由配置 +├── utils/ # 工具函数 +└── main.js # 入口文件 +``` + +## 快速开始 + +### 安装依赖 + +```bash +npm install +``` + +### 开发运行 + +```bash +npm run dev +``` + +访问 http://localhost:3000 + +### 构建生产版本 + +```bash +npm run build +``` + +## 使用说明 + +### 1. 注册/登录 +- 首次使用需要注册账号(账号、密码、姓名) +- 注册成功后自动登录 + +### 2. 创建/加入房间 +- 在主页可以创建新房间或通过房间号加入现有房间 +- 创建房间后自动成为主持人 +- 房间号可以分享给其他玩家 + +### 3. 使用骰子系统 +- **玩家初始投掷**:进入房间后,玩家可以投掷4个D4 +- **混沌值规则**: + - 如果投出的4个D4中,3的数量少于3个,会产生混沌值 + - 玩家可以通过消耗属性值来增加3的数量 + - 如果最终达到3个3,则不会产生混沌值 + - 每个不是3的D4会产生1点混沌值 +- **普通投掷**:可以选择D3/D4/D6/D12/D20进行投掷 +- **消耗混沌值**:只有主持人可以消耗混沌值 + +### 4. 总部/分部管理 +- 经理可以创建分部、发布招聘信息 +- 所有成员可以查看招聘信息 +- 每个分部可以设置天气和散一端 + +### 5. 异常管理 +- 添加异常时需要填写:名称、类型、特殊能力、焦点、领域 +- 异常类型:实体、概念、地点、现象 +- 异常领域:现实、记忆、时间、空间、生命、死亡、知识、情感 +- 异常状态流转:未收容 → 已收容 → 已解决 + +## 注意事项 + +- 当前版本使用localStorage进行数据存储(纯前端) +- 刷新页面后数据会保留 +- 如需后端支持,需要对接相应的API接口 + +## 开发计划 + +- [ ] WebSocket实时通信 +- [ ] 后端API集成 +- [ ] 数据持久化 +- [ ] 更多骰子类型和规则 +- [ ] 角色属性管理 +- [ ] 跑团记录导出 + diff --git a/Frontend/index.html b/Frontend/index.html new file mode 100644 index 0000000..9193dcc --- /dev/null +++ b/Frontend/index.html @@ -0,0 +1,21 @@ + + + + + + + 三角洲机构 - TRPG跑团工具 + + +
+ + + + + + + + + + + diff --git a/Frontend/package-lock.json b/Frontend/package-lock.json new file mode 100644 index 0000000..e8b87e9 --- /dev/null +++ b/Frontend/package-lock.json @@ -0,0 +1,2273 @@ +{ + "name": "trangle-agent", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "trangle-agent", + "version": "1.0.0", + "dependencies": { + "@ant-design/icons-vue": "^7.0.1", + "ant-design-vue": "^4.2.1", + "axios": "^1.6.7", + "pinia": "^2.1.7", + "vue": "^3.4.21", + "vue-router": "^4.3.0" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.0.4", + "sass": "^1.71.1", + "vite": "^5.1.4" + } + }, + "node_modules/@ant-design/colors": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-6.0.0.tgz", + "integrity": "sha512-qAZRvPzfdWHtfameEGP2Qvuf838NhergR35o+EuVyB5XvSA98xod5r4utvi4TJ3ywmevm290g9nsCG5MryrdWQ==", + "license": "MIT", + "dependencies": { + "@ctrl/tinycolor": "^3.4.0" + } + }, + "node_modules/@ant-design/icons-svg": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@ant-design/icons-svg/-/icons-svg-4.4.2.tgz", + "integrity": "sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==", + "license": "MIT" + }, + "node_modules/@ant-design/icons-vue": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@ant-design/icons-vue/-/icons-vue-7.0.1.tgz", + "integrity": "sha512-eCqY2unfZK6Fe02AwFlDHLfoyEFreP6rBwAZMIJ1LugmfMiVgwWDYlp1YsRugaPtICYOabV1iWxXdP12u9U43Q==", + "license": "MIT", + "dependencies": { + "@ant-design/colors": "^6.0.0", + "@ant-design/icons-svg": "^4.2.1" + }, + "peerDependencies": { + "vue": ">=3.0.3" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.5" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", + "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@ctrl/tinycolor": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz", + "integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "license": "MIT" + }, + "node_modules/@emotion/unitless": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==", + "license": "MIT" + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@parcel/watcher": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", + "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.1", + "@parcel/watcher-darwin-arm64": "2.5.1", + "@parcel/watcher-darwin-x64": "2.5.1", + "@parcel/watcher-freebsd-x64": "2.5.1", + "@parcel/watcher-linux-arm-glibc": "2.5.1", + "@parcel/watcher-linux-arm-musl": "2.5.1", + "@parcel/watcher-linux-arm64-glibc": "2.5.1", + "@parcel/watcher-linux-arm64-musl": "2.5.1", + "@parcel/watcher-linux-x64-glibc": "2.5.1", + "@parcel/watcher-linux-x64-musl": "2.5.1", + "@parcel/watcher-win32-arm64": "2.5.1", + "@parcel/watcher-win32-ia32": "2.5.1", + "@parcel/watcher-win32-x64": "2.5.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", + "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", + "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", + "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", + "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", + "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", + "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", + "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", + "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", + "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", + "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", + "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", + "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", + "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz", + "integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz", + "integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz", + "integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz", + "integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz", + "integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz", + "integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz", + "integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz", + "integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz", + "integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz", + "integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz", + "integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz", + "integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz", + "integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz", + "integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz", + "integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz", + "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz", + "integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz", + "integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz", + "integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz", + "integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz", + "integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz", + "integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@simonwep/pickr": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/@simonwep/pickr/-/pickr-1.8.2.tgz", + "integrity": "sha512-/l5w8BIkrpP6n1xsetx9MWPWlU6OblN5YgZZphxan0Tq4BByTCETL6lyIeY8lagalS2Nbt4F2W034KHLIiunKA==", + "license": "MIT", + "dependencies": { + "core-js": "^3.15.1", + "nanopop": "^2.1.0" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitejs/plugin-vue": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", + "integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.24.tgz", + "integrity": "sha512-eDl5H57AOpNakGNAkFDH+y7kTqrQpJkZFXhWZQGyx/5Wh7B1uQYvcWkvZi11BDhscPgj8N7XV3oRwiPnx1Vrig==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@vue/shared": "3.5.24", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.24.tgz", + "integrity": "sha512-1QHGAvs53gXkWdd3ZMGYuvQFXHW4ksKWPG8HP8/2BscrbZ0brw183q2oNWjMrSWImYLHxHrx1ItBQr50I/q2zw==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.24", + "@vue/shared": "3.5.24" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.24.tgz", + "integrity": "sha512-8EG5YPRgmTB+YxYBM3VXy8zHD9SWHUJLIGPhDovo3Z8VOgvP+O7UP5vl0J4BBPWYD9vxtBabzW1EuEZ+Cqs14g==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@vue/compiler-core": "3.5.24", + "@vue/compiler-dom": "3.5.24", + "@vue/compiler-ssr": "3.5.24", + "@vue/shared": "3.5.24", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.6", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.24.tgz", + "integrity": "sha512-trOvMWNBMQ/odMRHW7Ae1CdfYx+7MuiQu62Jtu36gMLXcaoqKvAyh+P73sYG9ll+6jLB6QPovqoKGGZROzkFFg==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.24", + "@vue/shared": "3.5.24" + } + }, + "node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "license": "MIT" + }, + "node_modules/@vue/reactivity": { + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.24.tgz", + "integrity": "sha512-BM8kBhtlkkbnyl4q+HiF5R5BL0ycDPfihowulm02q3WYp2vxgPcJuZO866qa/0u3idbMntKEtVNuAUp5bw4teg==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.24" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.24.tgz", + "integrity": "sha512-RYP/byyKDgNIqfX/gNb2PB55dJmM97jc9wyF3jK7QUInYKypK2exmZMNwnjueWwGceEkP6NChd3D2ZVEp9undQ==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.24", + "@vue/shared": "3.5.24" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.24.tgz", + "integrity": "sha512-Z8ANhr/i0XIluonHVjbUkjvn+CyrxbXRIxR7wn7+X7xlcb7dJsfITZbkVOeJZdP8VZwfrWRsWdShH6pngMxRjw==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.24", + "@vue/runtime-core": "3.5.24", + "@vue/shared": "3.5.24", + "csstype": "^3.1.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.24.tgz", + "integrity": "sha512-Yh2j2Y4G/0/4z/xJ1Bad4mxaAk++C2v4kaa8oSYTMJBJ00/ndPuxCnWeot0/7/qafQFLh5pr6xeV6SdMcE/G1w==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.24", + "@vue/shared": "3.5.24" + }, + "peerDependencies": { + "vue": "3.5.24" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.24.tgz", + "integrity": "sha512-9cwHL2EsJBdi8NY22pngYYWzkTDhld6fAD6jlaeloNGciNSJL6bLpbxVgXl96X00Jtc6YWQv96YA/0sxex/k1A==", + "license": "MIT" + }, + "node_modules/ant-design-vue": { + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/ant-design-vue/-/ant-design-vue-4.2.6.tgz", + "integrity": "sha512-t7eX13Yj3i9+i5g9lqFyYneoIb3OzTvQjq9Tts1i+eiOd3Eva/6GagxBSXM1fOCjqemIu0FYVE1ByZ/38epR3Q==", + "license": "MIT", + "dependencies": { + "@ant-design/colors": "^6.0.0", + "@ant-design/icons-vue": "^7.0.0", + "@babel/runtime": "^7.10.5", + "@ctrl/tinycolor": "^3.5.0", + "@emotion/hash": "^0.9.0", + "@emotion/unitless": "^0.8.0", + "@simonwep/pickr": "~1.8.0", + "array-tree-filter": "^2.1.0", + "async-validator": "^4.0.0", + "csstype": "^3.1.1", + "dayjs": "^1.10.5", + "dom-align": "^1.12.1", + "dom-scroll-into-view": "^2.0.0", + "lodash": "^4.17.21", + "lodash-es": "^4.17.15", + "resize-observer-polyfill": "^1.5.1", + "scroll-into-view-if-needed": "^2.2.25", + "shallow-equal": "^1.0.0", + "stylis": "^4.1.3", + "throttle-debounce": "^5.0.0", + "vue-types": "^3.0.0", + "warning": "^4.0.0" + }, + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ant-design-vue" + }, + "peerDependencies": { + "vue": ">=3.2.0" + } + }, + "node_modules/array-tree-filter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-tree-filter/-/array-tree-filter-2.1.0.tgz", + "integrity": "sha512-4ROwICNlNw/Hqa9v+rk5h22KjmzB1JGTMVKP2AKJBOCgb0yL0ASf0+YvCcLNNwquOHNX48jkeZIJ3a+oOQqKcw==", + "license": "MIT" + }, + "node_modules/async-validator": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/async-validator/-/async-validator-4.2.5.tgz", + "integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==", + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", + "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/compute-scroll-into-view": { + "version": "1.0.20", + "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.20.tgz", + "integrity": "sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==", + "license": "MIT" + }, + "node_modules/core-js": { + "version": "3.47.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.47.0.tgz", + "integrity": "sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg==", + "hasInstallScript": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/dayjs": { + "version": "1.11.19", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz", + "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==", + "license": "MIT" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/dom-align": { + "version": "1.12.4", + "resolved": "https://registry.npmjs.org/dom-align/-/dom-align-1.12.4.tgz", + "integrity": "sha512-R8LUSEay/68zE5c8/3BDxiTEvgb4xZTF0RKmAHfiEVN3klfIpXfi2/QCoiWPccVQ0J/ZGdz9OjzL4uJEP/MRAw==", + "license": "MIT" + }, + "node_modules/dom-scroll-into-view": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/dom-scroll-into-view/-/dom-scroll-into-view-2.0.1.tgz", + "integrity": "sha512-bvVTQe1lfaUr1oFzZX80ce9KLDlZ3iU+XGNE/bz9HnGdklTieqsbmsLHe+rT2XWqopvL0PckkYqN7ksmm5pe3w==", + "license": "MIT" + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/immutable": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.4.tgz", + "integrity": "sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-object": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.1.tgz", + "integrity": "sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/nanopop": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/nanopop/-/nanopop-2.4.2.tgz", + "integrity": "sha512-NzOgmMQ+elxxHeIha+OG/Pv3Oc3p4RU2aBhwWwAqDpXrdTbtRylbRLQztLy8dMMwfl6pclznBdfUhccEn9ZIzw==", + "license": "MIT" + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pinia": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.3.1.tgz", + "integrity": "sha512-khUlZSwt9xXCaTbbxFYBKDc/bWAGWJjOgvxETwkTN7KRm66EeT1ZdZj6i2ceh9sP2Pzqsbc704r2yngBrxBVug==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^6.6.3", + "vue-demi": "^0.14.10" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "typescript": ">=4.4.4", + "vue": "^2.7.0 || ^3.5.11" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==", + "license": "MIT" + }, + "node_modules/rollup": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz", + "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.53.3", + "@rollup/rollup-android-arm64": "4.53.3", + "@rollup/rollup-darwin-arm64": "4.53.3", + "@rollup/rollup-darwin-x64": "4.53.3", + "@rollup/rollup-freebsd-arm64": "4.53.3", + "@rollup/rollup-freebsd-x64": "4.53.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", + "@rollup/rollup-linux-arm-musleabihf": "4.53.3", + "@rollup/rollup-linux-arm64-gnu": "4.53.3", + "@rollup/rollup-linux-arm64-musl": "4.53.3", + "@rollup/rollup-linux-loong64-gnu": "4.53.3", + "@rollup/rollup-linux-ppc64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-musl": "4.53.3", + "@rollup/rollup-linux-s390x-gnu": "4.53.3", + "@rollup/rollup-linux-x64-gnu": "4.53.3", + "@rollup/rollup-linux-x64-musl": "4.53.3", + "@rollup/rollup-openharmony-arm64": "4.53.3", + "@rollup/rollup-win32-arm64-msvc": "4.53.3", + "@rollup/rollup-win32-ia32-msvc": "4.53.3", + "@rollup/rollup-win32-x64-gnu": "4.53.3", + "@rollup/rollup-win32-x64-msvc": "4.53.3", + "fsevents": "~2.3.2" + } + }, + "node_modules/sass": { + "version": "1.94.2", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.94.2.tgz", + "integrity": "sha512-N+7WK20/wOr7CzA2snJcUSSNTCzeCGUTFY3OgeQP3mZ1aj9NMQ0mSTXwlrnd89j33zzQJGqIN52GIOmYrfq46A==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.0", + "immutable": "^5.0.2", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, + "node_modules/scroll-into-view-if-needed": { + "version": "2.2.31", + "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.31.tgz", + "integrity": "sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==", + "license": "MIT", + "dependencies": { + "compute-scroll-into-view": "^1.0.20" + } + }, + "node_modules/shallow-equal": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz", + "integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==", + "license": "MIT" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stylis": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz", + "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==", + "license": "MIT" + }, + "node_modules/throttle-debounce": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-5.0.2.tgz", + "integrity": "sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==", + "license": "MIT", + "engines": { + "node": ">=12.22" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vue": { + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.24.tgz", + "integrity": "sha512-uTHDOpVQTMjcGgrqFPSb8iO2m1DUvo+WbGqoXQz8Y1CeBYQ0FXf2z1gLRaBtHjlRz7zZUBHxjVB5VTLzYkvftg==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.24", + "@vue/compiler-sfc": "3.5.24", + "@vue/runtime-dom": "3.5.24", + "@vue/server-renderer": "3.5.24", + "@vue/shared": "3.5.24" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/vue-router": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.3.tgz", + "integrity": "sha512-ARBedLm9YlbvQomnmq91Os7ck6efydTSpRP3nuOKCvgJOHNrhRoJDSKtee8kcL1Vf7nz6U+PMBL+hTvR3bTVQg==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^6.6.4" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "vue": "^3.5.0" + } + }, + "node_modules/vue-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/vue-types/-/vue-types-3.0.2.tgz", + "integrity": "sha512-IwUC0Aq2zwaXqy74h4WCvFCUtoV0iSWr0snWnE9TnU18S66GAQyqQbRf2qfJtUuiFsBf6qp0MEwdonlwznlcrw==", + "license": "MIT", + "dependencies": { + "is-plain-object": "3.0.1" + }, + "engines": { + "node": ">=10.15.0" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" + } + } + } +} diff --git a/Frontend/package.json b/Frontend/package.json new file mode 100644 index 0000000..b4b9c21 --- /dev/null +++ b/Frontend/package.json @@ -0,0 +1,32 @@ +{ + "name": "trangle-agent", + "version": "1.0.0", + "description": "TRPG跑团在线工具网站 - 三角洲机构", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "vue": "^3.4.21", + "vue-router": "^4.3.0", + "pinia": "^2.1.7", + "ant-design-vue": "^4.2.1", + "@ant-design/icons-vue": "^7.0.1", + "axios": "^1.6.7" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^5.0.4", + "vite": "^5.1.4", + "sass": "^1.71.1" + } +} + + + + + + + + diff --git a/Frontend/src/App.vue b/Frontend/src/App.vue new file mode 100644 index 0000000..b9e99a8 --- /dev/null +++ b/Frontend/src/App.vue @@ -0,0 +1,35 @@ + + + + + + + + + + + + + diff --git a/Frontend/src/main.js b/Frontend/src/main.js new file mode 100644 index 0000000..8533fe7 --- /dev/null +++ b/Frontend/src/main.js @@ -0,0 +1,21 @@ +import { createApp } from 'vue' +import { createPinia } from 'pinia' +import Antd from 'ant-design-vue' +import 'ant-design-vue/dist/reset.css' +import App from './App.vue' +import router from './router' +import { useUserStore } from './stores/user' + +const app = createApp(App) +const pinia = createPinia() + +app.use(pinia) +app.use(router) +app.use(Antd) + +// 初始化用户信息 +const userStore = useUserStore() +userStore.initUser() + +app.mount('#app') + diff --git a/Frontend/src/router/index.js b/Frontend/src/router/index.js new file mode 100644 index 0000000..3926dd2 --- /dev/null +++ b/Frontend/src/router/index.js @@ -0,0 +1,73 @@ +import { createRouter, createWebHistory } from 'vue-router' +import { useUserStore } from '@/stores/user' + +const routes = [ + { + path: '/', + redirect: '/home' + }, + { + path: '/login', + name: 'Login', + component: () => import('@/views/Login.vue'), + meta: { requiresAuth: false } + }, + { + path: '/register', + name: 'Register', + component: () => import('@/views/Register.vue'), + meta: { requiresAuth: false } + }, + { + path: '/home', + name: 'Home', + component: () => import('@/views/Home.vue'), + meta: { requiresAuth: true } + }, + { + path: '/room/:roomId', + name: 'Room', + component: () => import('@/views/Room.vue'), + meta: { requiresAuth: true } + }, + { + path: '/organization', + name: 'Organization', + component: () => import('@/views/Organization.vue'), + meta: { requiresAuth: true } + }, + { + path: '/anomaly', + name: 'Anomaly', + component: () => import('@/views/Anomaly.vue'), + meta: { requiresAuth: true } + } +] + +const router = createRouter({ + history: createWebHistory(), + routes +}) + +router.beforeEach((to, from, next) => { + const userStore = useUserStore() + + // 如果路由需要认证,先初始化用户信息 + if (to.meta.requiresAuth) { + // 确保用户信息已初始化 + if (!userStore.userInfo) { + userStore.initUser() + } + + // 检查是否已登录 + if (!userStore.isLoggedIn || !userStore.userInfo) { + next('/login') + return + } + } + + next() +}) + +export default router + diff --git a/Frontend/src/stores/anomaly.js b/Frontend/src/stores/anomaly.js new file mode 100644 index 0000000..bb838e3 --- /dev/null +++ b/Frontend/src/stores/anomaly.js @@ -0,0 +1,69 @@ +import { defineStore } from 'pinia' +import { ref } from 'vue' + +export const useAnomalyStore = defineStore('anomaly', () => { + const anomalies = ref([]) // 所有异常 + const containedAnomalies = ref([]) // 被收容的异常 + const resolvedAnomalies = ref([]) // 已解决的异常 + const weather = ref('') // 天气系统 + const scatteredEnds = ref([]) // 散一端 + + function setAnomalies(anomalyList) { + anomalies.value = anomalyList + } + + function addAnomaly(anomaly) { + anomalies.value.push(anomaly) + } + + function containAnomaly(anomalyId) { + const anomaly = anomalies.value.find(a => a.id === anomalyId) + if (anomaly) { + containedAnomalies.value.push(anomaly) + anomalies.value = anomalies.value.filter(a => a.id !== anomalyId) + } + } + + function resolveAnomaly(anomalyId) { + const anomaly = containedAnomalies.value.find(a => a.id === anomalyId) + if (anomaly) { + resolvedAnomalies.value.push(anomaly) + containedAnomalies.value = containedAnomalies.value.filter(a => a.id !== anomalyId) + } + } + + function setWeather(weatherData) { + weather.value = weatherData + } + + function setScatteredEnds(ends) { + scatteredEnds.value = ends + } + + function addScatteredEnd(end) { + scatteredEnds.value.push(end) + } + + return { + anomalies, + containedAnomalies, + resolvedAnomalies, + weather, + scatteredEnds, + setAnomalies, + addAnomaly, + containAnomaly, + resolveAnomaly, + setWeather, + setScatteredEnds, + addScatteredEnd + } +}) + + + + + + + + diff --git a/Frontend/src/stores/organization.js b/Frontend/src/stores/organization.js new file mode 100644 index 0000000..aa559fe --- /dev/null +++ b/Frontend/src/stores/organization.js @@ -0,0 +1,59 @@ +import { defineStore } from 'pinia' +import { ref } from 'vue' + +export const useOrganizationStore = defineStore('organization', () => { + const branches = ref([]) + const recruitments = ref([]) + const currentBranch = ref(null) + const userRole = ref('member') // member, manager, admin + + function setBranches(branchList) { + branches.value = branchList + } + + function addBranch(branch) { + branches.value.push(branch) + } + + function removeBranch(branchId) { + branches.value = branches.value.filter(b => b.id !== branchId) + } + + function setRecruitments(recruitmentList) { + recruitments.value = recruitmentList + } + + function addRecruitment(recruitment) { + recruitments.value.unshift(recruitment) + } + + function setCurrentBranch(branch) { + currentBranch.value = branch + } + + function setUserRole(role) { + userRole.value = role + } + + return { + branches, + recruitments, + currentBranch, + userRole, + setBranches, + addBranch, + removeBranch, + setRecruitments, + addRecruitment, + setCurrentBranch, + setUserRole + } +}) + + + + + + + + diff --git a/Frontend/src/stores/room.js b/Frontend/src/stores/room.js new file mode 100644 index 0000000..64c0a23 --- /dev/null +++ b/Frontend/src/stores/room.js @@ -0,0 +1,72 @@ +import { defineStore } from 'pinia' +import { ref } from 'vue' + +export const useRoomStore = defineStore('room', () => { + const currentRoom = ref(null) + const chaosPoints = ref(0) // 混沌值 + const players = ref([]) + const isHost = ref(false) + + function setRoom(roomData) { + currentRoom.value = roomData + chaosPoints.value = roomData.chaosPoints || 0 + players.value = roomData.players || [] + isHost.value = roomData.isHost || false + } + + function addChaosPoints(points) { + chaosPoints.value += points + if (currentRoom.value) { + currentRoom.value.chaosPoints = chaosPoints.value + } + } + + function consumeChaosPoints(points) { + if (chaosPoints.value >= points) { + chaosPoints.value -= points + if (currentRoom.value) { + currentRoom.value.chaosPoints = chaosPoints.value + } + return true + } + return false + } + + function addPlayer(player) { + if (!players.value.find(p => p.id === player.id)) { + players.value.push(player) + } + } + + function removePlayer(playerId) { + players.value = players.value.filter(p => p.id !== playerId) + } + + function clearRoom() { + currentRoom.value = null + chaosPoints.value = 0 + players.value = [] + isHost.value = false + } + + return { + currentRoom, + chaosPoints, + players, + isHost, + setRoom, + addChaosPoints, + consumeChaosPoints, + addPlayer, + removePlayer, + clearRoom + } +}) + + + + + + + + diff --git a/Frontend/src/stores/user.js b/Frontend/src/stores/user.js new file mode 100644 index 0000000..9986988 --- /dev/null +++ b/Frontend/src/stores/user.js @@ -0,0 +1,47 @@ +import { defineStore } from 'pinia' +import { ref, computed } from 'vue' + +export const useUserStore = defineStore('user', () => { + const userInfo = ref(null) + const token = ref(localStorage.getItem('token') || null) + + const isLoggedIn = computed(() => !!token.value) + + function login(userData, authToken) { + userInfo.value = userData + token.value = authToken + localStorage.setItem('token', authToken) + localStorage.setItem('userInfo', JSON.stringify(userData)) + } + + function logout() { + userInfo.value = null + token.value = null + localStorage.removeItem('token') + localStorage.removeItem('userInfo') + } + + function initUser() { + const storedUserInfo = localStorage.getItem('userInfo') + if (storedUserInfo) { + userInfo.value = JSON.parse(storedUserInfo) + } + } + + return { + userInfo, + token, + isLoggedIn, + login, + logout, + initUser + } +}) + + + + + + + + diff --git a/Frontend/src/utils/index.js b/Frontend/src/utils/index.js new file mode 100644 index 0000000..3631a5d --- /dev/null +++ b/Frontend/src/utils/index.js @@ -0,0 +1,53 @@ +/** + * 工具函数 + */ + +/** + * 格式化日期 + */ +export function formatDate(dateString, format = 'YYYY-MM-DD HH:mm:ss') { + if (!dateString) return '' + const date = new Date(dateString) + const year = date.getFullYear() + const month = String(date.getMonth() + 1).padStart(2, '0') + const day = String(date.getDate()).padStart(2, '0') + const hours = String(date.getHours()).padStart(2, '0') + const minutes = String(date.getMinutes()).padStart(2, '0') + const seconds = String(date.getSeconds()).padStart(2, '0') + + return format + .replace('YYYY', year) + .replace('MM', month) + .replace('DD', day) + .replace('HH', hours) + .replace('mm', minutes) + .replace('ss', seconds) +} + +/** + * 生成随机ID + */ +export function generateId() { + return Date.now().toString() + Math.random().toString(36).substr(2, 9) +} + +/** + * 复制到剪贴板 + */ +export async function copyToClipboard(text) { + try { + await navigator.clipboard.writeText(text) + return true + } catch (err) { + console.error('复制失败:', err) + return false + } +} + + + + + + + + diff --git a/Frontend/src/views/Anomaly.vue b/Frontend/src/views/Anomaly.vue new file mode 100644 index 0000000..9f47917 --- /dev/null +++ b/Frontend/src/views/Anomaly.vue @@ -0,0 +1,370 @@ + + + + + + + + + diff --git a/Frontend/src/views/Home.vue b/Frontend/src/views/Home.vue new file mode 100644 index 0000000..ccfe5b4 --- /dev/null +++ b/Frontend/src/views/Home.vue @@ -0,0 +1,262 @@ + + + + + + diff --git a/Frontend/src/views/Login.vue b/Frontend/src/views/Login.vue new file mode 100644 index 0000000..ffc5ffa --- /dev/null +++ b/Frontend/src/views/Login.vue @@ -0,0 +1,121 @@ + + + + + + + + + + + + + diff --git a/Frontend/src/views/Organization.vue b/Frontend/src/views/Organization.vue new file mode 100644 index 0000000..1597004 --- /dev/null +++ b/Frontend/src/views/Organization.vue @@ -0,0 +1,374 @@ + + + + + + + + + + + + + diff --git a/Frontend/src/views/Register.vue b/Frontend/src/views/Register.vue new file mode 100644 index 0000000..4d3ac05 --- /dev/null +++ b/Frontend/src/views/Register.vue @@ -0,0 +1,160 @@ + + + + + + + + + + + + + diff --git a/Frontend/src/views/Room.vue b/Frontend/src/views/Room.vue new file mode 100644 index 0000000..73864f9 --- /dev/null +++ b/Frontend/src/views/Room.vue @@ -0,0 +1,431 @@ + + + + + + diff --git a/Frontend/vite.config.js b/Frontend/vite.config.js new file mode 100644 index 0000000..7b5d2bd --- /dev/null +++ b/Frontend/vite.config.js @@ -0,0 +1,20 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' +import { resolve } from 'path' +import { fileURLToPath } from 'url' + +const __dirname = fileURLToPath(new URL('.', import.meta.url)) + +export default defineConfig({ + plugins: [vue()], + resolve: { + alias: { + '@': resolve(__dirname, 'src') + } + }, + server: { + port: 3000, + open: true + } +}) +