From d6dd5b032daaa4239dee7445138667cbec43754b Mon Sep 17 00:00:00 2001 From: ZhangAo Date: Fri, 7 Nov 2025 10:10:10 +0800 Subject: [PATCH] u --- .../base-app/src/app/auth/login.tsx | 38 +++-- .../base-app/src/app/auth/register.tsx | 3 +- .../base-app/src/request/api/auth.ts | 13 +- .../base-app/src/request/index.ts | 2 +- code/base-project/base-app/src/store/user.ts | 1 - .../base-go-v2/internal/api/auth/index.go | 11 +- .../base-go-v2/internal/db/query.go | 134 +++++++++--------- .../base-go-v2/internal/logx/log.go | 19 +++ .../base-go-v2/internal/reponse/response.go | 35 +++-- .../base-go-v2/internal/router/context.go | 16 +-- .../base-go-v2/internal/router/error.go | 42 ++++++ 11 files changed, 202 insertions(+), 112 deletions(-) create mode 100644 code/base-project/base-go-v2/internal/router/error.go diff --git a/code/base-project/base-app/src/app/auth/login.tsx b/code/base-project/base-app/src/app/auth/login.tsx index cd76ced3..a034b58e 100644 --- a/code/base-project/base-app/src/app/auth/login.tsx +++ b/code/base-project/base-app/src/app/auth/login.tsx @@ -1,10 +1,11 @@ +import { useRouter } from 'expo-router' import { useState } from 'react' import { Image, Text, View } from 'react-native' import { AButton, ACheckbox, AForm, AFormItem, AInput } from '@/components/common/ao' import { IconQq, IconWeixin, IconZhiwen } from '@/components/iconfont' import { Divider } from '@/components/ui/divider' import useFormValidation from '@/hooks/useFormValidation' -import alovaInstance from '@/request' +import { loginDefaultApi } from '@/request/api/auth' import { toast } from '@/utils/toast' interface LoginForm { @@ -15,6 +16,9 @@ interface LoginForm { } export default function AuthLogin() { + const router = useRouter() + + const [loading, setLoading] = useState(false) const [form, setForm] = useState({ username: '', password: '', @@ -25,7 +29,6 @@ export default function AuthLogin() { setForm(prev => ({ ...prev, [field]: value })) } - // 验证规则 const rules = { username: { required: true, message: '账号是必填项' }, password: { required: true, message: '密码是必填项' }, @@ -34,17 +37,24 @@ export default function AuthLogin() { const { isValid } = useFormValidation(form, rules) const loginDefault = () => { - alovaInstance.Post('/auth/test', { - title: 'foo', - body: 'bar', - userId: 1, - }) - .then((res) => { - console.log('成功返回:', res) - }) - .catch((err) => { - console.log('请求失败或未授权:', err) - }) + setLoading(true) + // loginDefaultApi({ + // account: form.username, + // password: form.password, + // }).then((res) => { + // if (res.isSuccess) { + // router.replace('/') + // } + // else { + // toast.show({ + // title: res.message || '登录失败', + // }) + // } + // }).catch((_err) => { + + // }).finally(() => { + // setLoading(false) + // }) } const loginQq = () => { @@ -92,7 +102,7 @@ export default function AuthLogin() { 忘记密码? - + 登录 diff --git a/code/base-project/base-app/src/app/auth/register.tsx b/code/base-project/base-app/src/app/auth/register.tsx index dcb7b292..95b989a3 100644 --- a/code/base-project/base-app/src/app/auth/register.tsx +++ b/code/base-project/base-app/src/app/auth/register.tsx @@ -39,11 +39,10 @@ export default function AuthRegister() { password: form.password, }).then((res) => { if (res.isSuccess) { - console.log(res) toast.show({ title: '注册成功', }) - // router.replace('/auth/login') + router.replace('/auth/login') } else { toast.show({ diff --git a/code/base-project/base-app/src/request/api/auth.ts b/code/base-project/base-app/src/request/api/auth.ts index 57f4942e..92ac44ea 100644 --- a/code/base-project/base-app/src/request/api/auth.ts +++ b/code/base-project/base-app/src/request/api/auth.ts @@ -1,10 +1,21 @@ import alovaInstance from '../index' +const API_PREFIX = '/auth' + interface RegisterDefaultParams { account: string password: string } export function registerDefaultApi(params: RegisterDefaultParams) { - return alovaInstance.Post>('/auth/register/default', params) + return alovaInstance.Post>(`${API_PREFIX}/register/default`, params) +} + +interface LoginDefaultParams { + account: string + password: string +} + +export function loginDefaultApi(params: LoginDefaultParams) { + return alovaInstance.Post>(`${API_PREFIX}/login/default`, params) } diff --git a/code/base-project/base-app/src/request/index.ts b/code/base-project/base-app/src/request/index.ts index c8463a29..bed3e94e 100644 --- a/code/base-project/base-app/src/request/index.ts +++ b/code/base-project/base-app/src/request/index.ts @@ -30,7 +30,7 @@ function isWhiteListed(url: string) { } const alovaInstance = createAlova({ - baseURL: 'http://192.168.5.6:8085/app', + baseURL: 'http://192.168.1.136:8085/app', requestAdapter: axiosRequestAdapter(), timeout: 5000, responded: { diff --git a/code/base-project/base-app/src/store/user.ts b/code/base-project/base-app/src/store/user.ts index 9e220419..d5b8e96a 100644 --- a/code/base-project/base-app/src/store/user.ts +++ b/code/base-project/base-app/src/store/user.ts @@ -28,7 +28,6 @@ const useUserStore = create()( ), ) -// 在组件外使用 const userStore = useUserStore export { userStore, useUserStore } diff --git a/code/base-project/base-go-v2/internal/api/auth/index.go b/code/base-project/base-go-v2/internal/api/auth/index.go index 3f8b4f76..65e65dcb 100644 --- a/code/base-project/base-go-v2/internal/api/auth/index.go +++ b/code/base-project/base-go-v2/internal/api/auth/index.go @@ -60,8 +60,7 @@ func loginDefault(c *router.Context) error { // 获取请求体并校验必填字段 bodyData, err := c.GetBodyWithRequired("account", "password") if err != nil { - c.AddError(err) - return err + return c.AddError(err) } account := bodyData.GetString("account") @@ -74,7 +73,6 @@ func loginDefault(c *router.Context) error { ) user, err := db.FindOne("user_info", userQuery) if err != nil { - c.AddError(err) return response.Success(c).Message("查询用户失败,请稍后重试").Send() } @@ -116,7 +114,7 @@ func loginDefault(c *router.Context) error { func registerDefault(c *router.Context) error { bodyData, err := c.GetBodyWithRequired("account", "password") if err != nil { - return err + return c.AddError(err) } account := bodyData.GetString("account") password := bodyData.GetString("password") @@ -124,12 +122,12 @@ func registerDefault(c *router.Context) error { // 构造查询条件,检查账号是否已存在 accountQuery := mapx.New().SetKV("account", account) - existingUsers, err := db.Find("user_info", accountQuery) + existingUsers, err := db.FindOne("user_info", accountQuery) if err != nil { return c.AddError(err) } - if len(existingUsers) > 0 { + if existingUsers != nil { return response.Fail(c).Message("该账号已被注册").Send() } @@ -143,7 +141,6 @@ func registerDefault(c *router.Context) error { // 插入新用户 insertedRows, err := db.InsertOne("user_info", newUserData) if err != nil { - return c.AddError(err) } if insertedRows != 1 { return response.Fail(c).Message("注册失败").Send() diff --git a/code/base-project/base-go-v2/internal/db/query.go b/code/base-project/base-go-v2/internal/db/query.go index 315bc499..904626ef 100644 --- a/code/base-project/base-go-v2/internal/db/query.go +++ b/code/base-project/base-go-v2/internal/db/query.go @@ -8,47 +8,47 @@ import ( // ---------------- 通用查询方法 ---------------- -// GetOne 根据主键查询单条记录,返回 mapx.M -func GetOne(table string, pkColumn string, pkValue interface{}) (mapx.M, error) { - query := fmt.Sprintf("SELECT * FROM %s WHERE %s=$1", table, pkColumn) - - row := mapx.New() - err := DB.Get(&row, query, pkValue) +// 执行查询,返回多条记录 +func queryMaps(query string, args []interface{}) ([]mapx.M, error) { + rows, err := DB.Queryx(query, args...) if err != nil { return nil, err } - return row, nil + defer rows.Close() + + var result []mapx.M + for rows.Next() { + row := mapx.New() + if err := rows.MapScan(row); err != nil { + return nil, err + } + result = append(result, row) + } + + return result, nil } -// GetBatch 根据主键批量查询,返回 []mapx.M -func GetBatch(table string, pkColumn string, pkValues []interface{}) ([]mapx.M, error) { - if len(pkValues) == 0 { - return nil, nil - } - - var rows []mapx.M - - // 构建 IN 占位符 - placeholders := make([]string, len(pkValues)) - for i := range pkValues { - placeholders[i] = fmt.Sprintf("$%d", i+1) - } - - query := fmt.Sprintf("SELECT * FROM %s WHERE %s IN (%s)", table, pkColumn, strings.Join(placeholders, ",")) - err := DB.Select(&rows, query, pkValues...) +// 执行查询,返回单条记录 +func queryMap(query string, args []interface{}) (mapx.M, error) { + rows, err := DB.Queryx(query, args...) if err != nil { return nil, err } + defer rows.Close() - return rows, nil -} - -// Find 根据条件查询多条记录,可选排序,返回 []mapx.M -func Find(table string, conditions map[string]interface{}, orderBy ...string) ([]mapx.M, error) { - if len(conditions) == 0 { - return nil, fmt.Errorf("查询条件不能为空") + if rows.Next() { + row := mapx.New() + if err := rows.MapScan(row); err != nil { + return nil, err + } + return row, nil } + return nil, nil +} + +// 构建 WHERE 条件 +func buildWhere(conditions map[string]interface{}) (string, []interface{}) { var whereParts []string var values []interface{} i := 1 @@ -57,8 +57,40 @@ func Find(table string, conditions map[string]interface{}, orderBy ...string) ([ values = append(values, v) i++ } + return strings.Join(whereParts, " AND "), values +} - query := fmt.Sprintf("SELECT * FROM %s WHERE %s", table, strings.Join(whereParts, " AND ")) +// ---------------- 公共查询方法 ---------------- + +// GetOne 根据主键查询单条记录 +func GetOne(table string, pkColumn string, pkValue interface{}) (mapx.M, error) { + query := fmt.Sprintf("SELECT * FROM %s WHERE %s=$1", table, pkColumn) + return queryMap(query, []interface{}{pkValue}) +} + +// GetBatch 根据主键批量查询 +func GetBatch(table string, pkColumn string, pkValues []interface{}) ([]mapx.M, error) { + if len(pkValues) == 0 { + return nil, nil + } + + placeholders := make([]string, len(pkValues)) + for i := range pkValues { + placeholders[i] = fmt.Sprintf("$%d", i+1) + } + + query := fmt.Sprintf("SELECT * FROM %s WHERE %s IN (%s)", table, pkColumn, strings.Join(placeholders, ",")) + return queryMaps(query, pkValues) +} + +// Find 根据条件查询多条记录,可选排序 +func Find(table string, conditions map[string]interface{}, orderBy ...string) ([]mapx.M, error) { + if len(conditions) == 0 { + return nil, fmt.Errorf("查询条件不能为空") + } + + where, values := buildWhere(conditions) + query := fmt.Sprintf("SELECT * FROM %s WHERE %s", table, where) order := "id DESC" if len(orderBy) > 0 && orderBy[0] != "" { @@ -66,16 +98,10 @@ func Find(table string, conditions map[string]interface{}, orderBy ...string) ([ } query += " ORDER BY " + order - var rows []mapx.M - err := DB.Select(&rows, query, values...) - if err != nil { - return nil, err - } - - return rows, nil + return queryMaps(query, values) } -// FindAll 查询整个表,可选排序,返回 []mapx.M +// FindAll 查询整个表,可选排序 func FindAll(table string, orderBy ...string) ([]mapx.M, error) { query := fmt.Sprintf("SELECT * FROM %s", table) @@ -85,31 +111,17 @@ func FindAll(table string, orderBy ...string) ([]mapx.M, error) { } query += " ORDER BY " + order - var rows []mapx.M - err := DB.Select(&rows, query) - if err != nil { - return nil, err - } - - return rows, nil + return queryMaps(query, nil) } -// FindOne 根据条件查询单条记录,返回 mapx.M +// FindOne 根据条件查询单条记录,可选排序 func FindOne(table string, conditions map[string]interface{}, orderBy ...string) (mapx.M, error) { if len(conditions) == 0 { return nil, fmt.Errorf("查询条件不能为空") } - var whereParts []string - var values []interface{} - i := 1 - for k, v := range conditions { - whereParts = append(whereParts, fmt.Sprintf("%s=$%d", k, i)) - values = append(values, v) - i++ - } - - query := fmt.Sprintf("SELECT * FROM %s WHERE %s", table, strings.Join(whereParts, " AND ")) + where, values := buildWhere(conditions) + query := fmt.Sprintf("SELECT * FROM %s WHERE %s", table, where) order := "id DESC" if len(orderBy) > 0 && orderBy[0] != "" { @@ -117,11 +129,5 @@ func FindOne(table string, conditions map[string]interface{}, orderBy ...string) } query += " ORDER BY " + order + " LIMIT 1" - row := mapx.New() - err := DB.Get(&row, query, values...) - if err != nil { - return nil, err - } - - return row, nil + return queryMap(query, values) } diff --git a/code/base-project/base-go-v2/internal/logx/log.go b/code/base-project/base-go-v2/internal/logx/log.go index 986718de..4fba5172 100644 --- a/code/base-project/base-go-v2/internal/logx/log.go +++ b/code/base-project/base-go-v2/internal/logx/log.go @@ -180,6 +180,14 @@ func newConsoleEncoder(enableColor bool) zapcore.Encoder { pid := os.Getpid() enc.AppendString(fmt.Sprintf("[%d] [%d] %s", pid, routinex.CurGID(), caller.TrimmedPath())) }, + EncodeDuration: func(d time.Duration, enc zapcore.PrimitiveArrayEncoder) { + ms := d.Milliseconds() + if ms < 1000 { + enc.AppendString(fmt.Sprintf("%dms", ms)) + } else { + enc.AppendString(fmt.Sprintf("%.2fs", d.Seconds())) + } + }, } return zapcore.NewConsoleEncoder(cfg) } @@ -192,6 +200,17 @@ func newJSONEncoder() zapcore.Encoder { } cfg.EncodeLevel = zapcore.CapitalLevelEncoder cfg.EncodeCaller = zapcore.ShortCallerEncoder + + // 智能 duration 输出 + cfg.EncodeDuration = func(d time.Duration, enc zapcore.PrimitiveArrayEncoder) { + ms := d.Milliseconds() + if ms < 1000 { + enc.AppendString(fmt.Sprintf("%dms", ms)) + } else { + enc.AppendString(fmt.Sprintf("%.2fs", d.Seconds())) + } + } + return zapcore.NewJSONEncoder(cfg) } diff --git a/code/base-project/base-go-v2/internal/reponse/response.go b/code/base-project/base-go-v2/internal/reponse/response.go index 8324d2f7..5ba82eac 100644 --- a/code/base-project/base-go-v2/internal/reponse/response.go +++ b/code/base-project/base-go-v2/internal/reponse/response.go @@ -8,6 +8,7 @@ import ( "net/http" ) +// Result 响应结构 type Result struct { Code int `json:"code"` Message string `json:"message"` @@ -15,6 +16,7 @@ type Result struct { TrackId string `json:"trackId,omitempty"` } +// HttpCode 响应状态码 var HttpCode = struct { Success int Unauthorized int @@ -27,31 +29,37 @@ var HttpCode = struct { RefreshToken: 402, } +// Builder 响应构建器 type Builder struct { - c *router.Context - result Result + c *router.Context + result Result + statusCode int // 可自定义 HTTP 状态码 } +// Success 构造成功响应 func Success(c *router.Context) *Builder { return &Builder{ - c: c, - result: Result{ - Code: HttpCode.Success, - Message: "请求成功", - }, + c: c, + result: Result{Code: HttpCode.Success, Message: "请求成功"}, + statusCode: http.StatusOK, } } +// Fail 构造失败响应 func Fail(c *router.Context) *Builder { return &Builder{ - c: c, - result: Result{ - Code: HttpCode.Fail, - Message: "请求失败", - }, + c: c, + result: Result{Code: HttpCode.Fail, Message: "请求失败"}, + statusCode: http.StatusOK, } } +// Status 设置自定义 HTTP 状态码 +func (b *Builder) Status(code int) *Builder { + b.statusCode = code + return b +} + func (b *Builder) Code(code int) *Builder { b.result.Code = code return b @@ -67,6 +75,7 @@ func (b *Builder) Data(data interface{}) *Builder { return b } +// Send 发送响应 func (b *Builder) Send() error { if b.result.Data == nil { b.result.Data = "" @@ -74,5 +83,5 @@ func (b *Builder) Send() error { if trackId := routinex.Get(logx.TrackID); trackId != nil { b.result.TrackId = strutil.ToString(trackId) } - return b.c.JSON(http.StatusOK, b.result) + return b.c.JSON(b.statusCode, b.result) } diff --git a/code/base-project/base-go-v2/internal/router/context.go b/code/base-project/base-go-v2/internal/router/context.go index 7b33d54e..2148a21a 100644 --- a/code/base-project/base-go-v2/internal/router/context.go +++ b/code/base-project/base-go-v2/internal/router/context.go @@ -1,13 +1,11 @@ package router import ( - "base-go-v2/internal/errs" "base-go-v2/internal/utils/mapx" "base-go-v2/internal/utils/validate" "encoding/json" - "sync" - "github.com/valyala/fasthttp" + "sync" ) // BodyCache 缓存 fasthttp 请求体 @@ -167,23 +165,23 @@ func (c *Context) AddError(err error) error { } if _, exists := c.keys["errors"]; !exists { - c.keys["errors"] = []*errs.StackError{} + c.keys["errors"] = []*StackError{} } // 统一生成堆栈 - se := errs.WrapWithStack(err) - c.keys["errors"] = append(c.keys["errors"].([]*errs.StackError), se) + se := WrapWithStack(err) + c.keys["errors"] = append(c.keys["errors"].([]*StackError), se) return err } // Errors 返回 []*utils.StackError -func (c *Context) Errors() []*errs.StackError { +func (c *Context) Errors() []*StackError { if c.keys == nil { return nil } - if err, exists := c.keys["errors"]; exists { - return err.([]*errs.StackError) + if errs, exists := c.keys["errors"]; exists { + return errs.([]*StackError) } return nil } diff --git a/code/base-project/base-go-v2/internal/router/error.go b/code/base-project/base-go-v2/internal/router/error.go new file mode 100644 index 00000000..f23b8f49 --- /dev/null +++ b/code/base-project/base-go-v2/internal/router/error.go @@ -0,0 +1,42 @@ +package router + +import ( + "errors" + "runtime/debug" +) + +// StackError 保存原始错误和堆栈 +type StackError struct { + err error + stack string +} + +// 实现 error 接口 +func (s *StackError) Error() string { + if s.err == nil { + return "" + } + return s.err.Error() +} + +// StackTrace 返回堆栈信息 +func (s *StackError) StackTrace() string { + return s.stack +} + +// WrapWithStack 给错误附加堆栈,如果已经是 StackError 则直接返回 +func WrapWithStack(err error) *StackError { + if err == nil { + return nil + } + + var se *StackError + if errors.As(err, &se) { + return se + } + + return &StackError{ + err: err, + stack: string(debug.Stack()), + } +}