feat: complete contract-driven business development and MySQL integration

This commit is contained in:
2026-09-24 21:56:04 +08:00
parent f9afb8f8dd
commit f5478cdc76
108 changed files with 6557 additions and 550 deletions

105
contract/policy.go Normal file
View File

@@ -0,0 +1,105 @@
package contract
import (
"context"
"errors"
"fmt"
"io"
"unicode/utf8"
)
// RequestPolicy describes protocol strictness, independently of business
// fields. Empty values inherit App defaults, then the historical binder policy.
type RequestPolicy struct {
UnknownQuery string `json:"unknownQuery"` // ignore or reject
UTF8 string `json:"utf8"` // replace or reject
Charset string `json:"charset"` // any or utf8
Body string `json:"body"` // auto, required, optional, none, emptyObject
}
func (p RequestPolicy) Validate() error {
for _, item := range []struct {
name, value string
allowed []string
}{
{"unknownQuery", p.UnknownQuery, []string{"ignore", "reject"}},
{"utf8", p.UTF8, []string{"replace", "reject"}},
{"charset", p.Charset, []string{"any", "utf8"}},
{"body", p.Body, []string{"auto", "required", "optional", "none", "emptyObject"}},
} {
if item.value == "" {
continue
}
ok := false
for _, value := range item.allowed {
ok = ok || value == item.value
}
if !ok {
return fmt.Errorf("request policy: invalid %s", item.name)
}
}
return nil
}
func MergePolicy(base, override RequestPolicy) RequestPolicy {
if override.UnknownQuery != "" {
base.UnknownQuery = override.UnknownQuery
}
if override.UTF8 != "" {
base.UTF8 = override.UTF8
}
if override.Charset != "" {
base.Charset = override.Charset
}
if override.Body != "" {
base.Body = override.Body
}
return base
}
type policyKey struct{}
func WithRequestPolicy(ctx context.Context, p RequestPolicy) context.Context {
if ctx == nil {
ctx = context.Background()
}
return context.WithValue(ctx, policyKey{}, p)
}
func RequestPolicyFromContext(ctx context.Context) RequestPolicy {
if ctx == nil {
return RequestPolicy{}
}
p, _ := ctx.Value(policyKey{}).(RequestPolicy)
return p
}
// utf8Reader checks original bytes in one pass, including runes crossing Read
// boundaries. It never replaces invalid bytes or buffers the complete body.
type utf8Reader struct {
r io.Reader
pending [4]byte
n int
}
var errUTF8 = errors.New("invalid UTF-8 in JSON body")
func (r *utf8Reader) Read(p []byte) (int, error) {
n, err := r.r.Read(p)
for _, b := range p[:n] {
if r.n == 0 && b < utf8.RuneSelf {
continue
}
r.pending[r.n] = b
r.n++
if utf8.FullRune(r.pending[:r.n]) {
ch, size := utf8.DecodeRune(r.pending[:r.n])
if ch == utf8.RuneError && size == 1 {
return n, errUTF8
}
r.n = 0
}
}
if err == io.EOF && r.n != 0 {
return n, errUTF8
}
return n, err
}