邹方成 1ab39d2f5a
Some checks failed
Build docker and publish / linux (1.24.5) (push) Failing after 25s
refactor: 重构项目结构并重命名模块
feat(admin): 新增工会管理功能
feat(activity): 添加活动管理相关服务
feat(user): 实现用户道具卡和积分管理
feat(guild): 新增工会成员管理功能

fix: 修复数据库连接配置
fix: 修正jwtoken导入路径
fix: 解决端口冲突问题

style: 统一代码格式和注释风格
style: 更新项目常量命名

docs: 添加项目框架和开发规范文档
docs: 更新接口文档注释

chore: 移除无用代码和文件
chore: 更新Makefile和配置文件
chore: 清理日志文件

test: 添加道具卡测试脚本
2025-11-14 21:10:00 +08:00

463 lines
10 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package core
import (
"bytes"
stdctx "context"
"fmt"
"io/ioutil"
"mime/multipart"
"net/http"
"net/url"
"path"
"strings"
"sync"
"bindbox-game/internal/pkg/logger"
"bindbox-game/internal/pkg/trace"
"bindbox-game/internal/proposal"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
)
type HandlerFunc func(c Context)
type Trace = trace.T
const (
_Alias = "_alias_"
_TraceName = "_trace_"
_LoggerName = "_logger_"
_BodyName = "_body_"
_PayloadName = "_payload_"
_SessionUserInfo = "_session_user_info"
_AbortErrorName = "_abort_error_"
_IsRecordMetrics = "_is_record_metrics_"
)
var contextPool = &sync.Pool{
New: func() interface{} {
return new(context)
},
}
func newContext(ctx *gin.Context) Context {
context := contextPool.Get().(*context)
context.ctx = ctx
return context
}
func releaseContext(ctx Context) {
c := ctx.(*context)
c.ctx = nil
contextPool.Put(c)
}
var _ Context = (*context)(nil)
type Context interface {
init()
// ShouldBindQuery 反序列化 querystring
// tag: `form:"xxx"` (注:不要写成 query)
ShouldBindQuery(obj interface{}) error
// ShouldBindPostForm 反序列化 postform (querystring会被忽略)
// tag: `form:"xxx"`
ShouldBindPostForm(obj interface{}) error
// ShouldBindForm 同时反序列化 querystring 和 postform;
// 当 querystring 和 postform 存在相同字段时postform 优先使用。
// tag: `form:"xxx"`
ShouldBindForm(obj interface{}) error
// ShouldBindJSON 反序列化 postjson
// tag: `json:"xxx"`
ShouldBindJSON(obj interface{}) error
// ShouldBindXML 反序列化 xml
// tag: `xml:"xxx"`
ShouldBindXML(obj interface{}) error
// ShouldBindURI 反序列化 path 参数(如路由路径为 /user/:name)
// tag: `uri:"xxx"`
ShouldBindURI(obj interface{}) error
// Redirect 重定向
Redirect(code int, location string)
// Trace 获取 Trace 对象
Trace() Trace
setTrace(trace Trace)
disableTrace()
// Logger 获取 Logger 对象
Logger() logger.CustomLogger
setLogger(logger logger.CustomLogger)
// Payload 正确返回
Payload(payload interface{})
getPayload() interface{}
// File 文件下载
File(filePath string)
// HTML 返回界面
HTML(name string, obj interface{})
// String 返回字符串
String(str string)
// XML 返回 XML
XML(obj interface{})
// ExcelData 返回二进制数据
ExcelData(filename string, byteData []byte)
// FormFile 通过表单上传文件
FormFile(name string) (*multipart.FileHeader, error)
// SaveUploadedFile 将表单文件上传到本地
SaveUploadedFile(file *multipart.FileHeader, dst string) error
// AbortWithError 错误返回
AbortWithError(err BusinessError)
abortError() BusinessError
// Header 获取 Header 对象
Header() http.Header
// GetHeader 获取 Header
GetHeader(key string) string
// SetHeader 设置 Header
SetHeader(key, value string)
// SessionUserInfo 当前用户信息
SessionUserInfo() proposal.SessionUserInfo
setSessionUserInfo(info proposal.SessionUserInfo)
// Alias 设置路由别名 for metrics path
Alias() string
setAlias(path string)
// disableRecordMetrics 设置禁止记录指标
disableRecordMetrics()
ableRecordMetrics()
isRecordMetrics() bool
// RequestInputParams 获取所有参数
RequestInputParams() url.Values
// RequestPostFormParams 获取 PostForm 参数
RequestPostFormParams() url.Values
// RequestPathParams 获取路由参数的值
RequestPathParams(key string) string
// Request 获取 Request 对象
Request() *http.Request
// RawData 获取 Request.Body
RawData() []byte
// Method 获取 Request.Method
Method() string
// Host 获取 Request.Host
Host() string
// Path 获取 请求的路径 Request.URL.Path (不附带 querystring)
Path() string
// URI 获取 unescape 后的 Request.URL.RequestURI()
URI() string
// RequestContext 获取请求的 context (当 client 关闭后,会自动 canceled)
RequestContext() StdContext
// ResponseWriter 获取 ResponseWriter 对象
ResponseWriter() gin.ResponseWriter
// Param 获取 URL 参数
Param(key string) string
}
type context struct {
ctx *gin.Context
}
type StdContext struct {
stdctx.Context
Trace
logger.CustomLogger
}
func (c *context) init() {
body, err := c.ctx.GetRawData()
if err != nil {
panic(err)
}
c.ctx.Set(_BodyName, body) // cache body是为了trace使用
c.ctx.Request.Body = ioutil.NopCloser(bytes.NewBuffer(body)) // re-construct req body
}
// ShouldBindQuery 反序列化querystring
// tag: `form:"xxx"` (注不要写成query)
func (c *context) ShouldBindQuery(obj interface{}) error {
return c.ctx.ShouldBindWith(obj, binding.Query)
}
// ShouldBindPostForm 反序列化 postform (querystring 会被忽略)
// tag: `form:"xxx"`
func (c *context) ShouldBindPostForm(obj interface{}) error {
return c.ctx.ShouldBindWith(obj, binding.FormPost)
}
// ShouldBindForm 同时反序列化querystring和postform;
// 当querystring和postform存在相同字段时postform优先使用。
// tag: `form:"xxx"`
func (c *context) ShouldBindForm(obj interface{}) error {
return c.ctx.ShouldBindWith(obj, binding.Form)
}
// ShouldBindJSON 反序列化postjson
// tag: `json:"xxx"`
func (c *context) ShouldBindJSON(obj interface{}) error {
return c.ctx.ShouldBindWith(obj, binding.JSON)
}
// ShouldBindXML 反序列化xml
// tag: `xml:"xxx"`
func (c *context) ShouldBindXML(obj interface{}) error {
return c.ctx.ShouldBindWith(obj, binding.XML)
}
// ShouldBindURI 反序列化path参数(如路由路径为 /user/:name)
// tag: `uri:"xxx"`
func (c *context) ShouldBindURI(obj interface{}) error {
return c.ctx.ShouldBindUri(obj)
}
// Redirect 重定向
func (c *context) Redirect(code int, location string) {
c.ctx.Redirect(code, location)
}
func (c *context) Trace() Trace {
t, ok := c.ctx.Get(_TraceName)
if !ok || t == nil {
return nil
}
return t.(Trace)
}
func (c *context) setTrace(trace Trace) {
c.ctx.Set(_TraceName, trace)
}
func (c *context) disableTrace() {
c.setTrace(nil)
}
func (c *context) Logger() logger.CustomLogger {
getLogger, ok := c.ctx.Get(_LoggerName)
if !ok {
return nil
}
return getLogger.(logger.CustomLogger)
}
func (c *context) setLogger(logger logger.CustomLogger) {
c.ctx.Set(_LoggerName, logger)
}
func (c *context) getPayload() interface{} {
if payload, ok := c.ctx.Get(_PayloadName); ok != false {
return payload
}
return nil
}
func (c *context) Payload(payload interface{}) {
c.ctx.Set(_PayloadName, payload)
}
func (c *context) File(filePath string) {
c.ctx.Writer.Header().Add("Content-Disposition", fmt.Sprintf("attachment; filename=%s", path.Base(filePath)))
c.ctx.Writer.Header().Add("Content-Type", "application/octet-stream")
c.ctx.File(filePath)
}
func (c *context) HTML(name string, obj interface{}) {
c.ctx.HTML(200, name+".html", obj)
}
func (c *context) String(str string) {
c.ctx.String(200, str)
}
func (c *context) XML(obj interface{}) {
c.ctx.XML(200, obj)
}
func (c *context) ExcelData(fileName string, data []byte) {
c.ctx.Writer.Header().Add("Content-Disposition", fmt.Sprintf("attachment; filename*=UTF-8''%s", url.QueryEscape(fileName)))
c.ctx.Writer.Header().Add("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
c.ctx.Data(http.StatusOK, "application/vnd.ms-excel", data)
}
func (c *context) FormFile(name string) (*multipart.FileHeader, error) {
return c.ctx.FormFile(name)
}
func (c *context) SaveUploadedFile(file *multipart.FileHeader, dst string) error {
return c.ctx.SaveUploadedFile(file, dst)
}
func (c *context) Header() http.Header {
header := c.ctx.Request.Header
clone := make(http.Header, len(header))
for k, v := range header {
value := make([]string, len(v))
copy(value, v)
clone[k] = value
}
return clone
}
func (c *context) GetHeader(key string) string {
return c.ctx.GetHeader(key)
}
func (c *context) SetHeader(key, value string) {
c.ctx.Header(key, value)
}
func (c *context) SessionUserInfo() proposal.SessionUserInfo {
val, ok := c.ctx.Get(_SessionUserInfo)
if !ok {
return proposal.SessionUserInfo{}
}
return val.(proposal.SessionUserInfo)
}
func (c *context) setSessionUserInfo(info proposal.SessionUserInfo) {
c.ctx.Set(_SessionUserInfo, info)
}
func (c *context) AbortWithError(err BusinessError) {
if err != nil {
httpCode := err.HTTPCode()
if httpCode == 0 {
httpCode = http.StatusInternalServerError
}
c.ctx.AbortWithStatus(httpCode)
c.ctx.Set(_AbortErrorName, err)
}
}
func (c *context) abortError() BusinessError {
err, _ := c.ctx.Get(_AbortErrorName)
return err.(BusinessError)
}
func (c *context) Alias() string {
path, ok := c.ctx.Get(_Alias)
if !ok {
return ""
}
return path.(string)
}
func (c *context) setAlias(path string) {
if path = strings.TrimSpace(path); path != "" {
c.ctx.Set(_Alias, path)
}
}
func (c *context) isRecordMetrics() bool {
isRecordMetrics, ok := c.ctx.Get(_IsRecordMetrics)
if !ok {
return false
}
return isRecordMetrics.(bool)
}
func (c *context) ableRecordMetrics() {
c.ctx.Set(_IsRecordMetrics, true)
}
func (c *context) disableRecordMetrics() {
c.ctx.Set(_IsRecordMetrics, false)
}
// RequestInputParams 获取所有参数
func (c *context) RequestInputParams() url.Values {
_ = c.ctx.Request.ParseForm()
return c.ctx.Request.Form
}
// RequestPostFormParams 获取 PostForm 参数
func (c *context) RequestPostFormParams() url.Values {
_ = c.ctx.Request.ParseForm()
return c.ctx.Request.PostForm
}
// RequestPathParams 获取路由参数的值
func (c *context) RequestPathParams(key string) string {
return c.ctx.Param(key)
}
// Request 获取 Request
func (c *context) Request() *http.Request {
return c.ctx.Request
}
func (c *context) RawData() []byte {
body, ok := c.ctx.Get(_BodyName)
if !ok {
return nil
}
return body.([]byte)
}
// Method 请求的method
func (c *context) Method() string {
return c.ctx.Request.Method
}
// Host 请求的host
func (c *context) Host() string {
return c.ctx.Request.Host
}
// Path 请求的路径(不附带querystring)
func (c *context) Path() string {
return c.ctx.Request.URL.Path
}
// URI unescape后的uri
func (c *context) URI() string {
uri, _ := url.QueryUnescape(c.ctx.Request.URL.RequestURI())
return uri
}
// RequestContext (包装 Trace + Logger) 获取请求的 context (当client关闭后会自动canceled)
func (c *context) RequestContext() StdContext {
return StdContext{
c.ctx.Request.Context(),
c.Trace(),
c.Logger(),
}
}
// ResponseWriter 获取 ResponseWriter
func (c *context) ResponseWriter() gin.ResponseWriter {
return c.ctx.Writer
}
// Param 获取 URL 参数
func (c *context) Param(key string) string {
return c.ctx.Param(key)
}