diff --git a/code/base-project/base-app/test2.html b/code/base-project/base-app/test2.html
index 749b1d68..f47df07b 100644
--- a/code/base-project/base-app/test2.html
+++ b/code/base-project/base-app/test2.html
@@ -2,78 +2,195 @@
-DataGrid 单行编辑 + wdatepicker 示例
-
-
-
+DataGrid 多选删除 + 单行编辑示例
+
-
+
+
+
+
-DataGrid 单行编辑 + wdatepicker
+DataGrid 编辑 + 多选删除 + 彻底删除 示例
-
+ |
ID |
- Name |
- 出生日期 |
- Score |
+ 姓名 |
+ 出生日期 |
+ 成绩 |
+
+// 生成随机数据
+function createMockData(count = 100){
+ let data = [];
+ for(let i = 1; i <= count; i++){
+ data.push({
+ id: i,
+ name: "用户" + i,
+ birth: randomDate("1980-01-01", "2005-12-31"),
+ score: Math.floor(Math.random() * 41) + 60
+ });
+ }
+ return data;
+}
+
+// 生成随机日期
+function randomDate(start, end){
+ let startTime = new Date(start).getTime();
+ let endTime = new Date(end).getTime();
+ let randomTime = startTime + Math.random() * (endTime - startTime);
+ let d = new Date(randomTime);
+
+ return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`;
+}
+
+
+// 批量删除(不立即提交)
+function removeRows() {
+ let rows = $('#dg').datagrid('getChecked');
+ if(rows.length === 0){
+ $.messager.alert('提示', '请先勾选要删除的行');
+ return;
+ }
+
+ // 倒序删除避免索引错乱
+ let indexes = rows.map(row => $('#dg').datagrid('getRowIndex', row))
+ .sort((a,b) => b - a);
+
+ indexes.forEach(index => $('#dg').datagrid('deleteRow', index));
+
+
+ $('#dg').datagrid('clearSelections');
+ $('#dg').datagrid('clearChecked');
+}
+
+
+// 获取真正删除的数据
+function confirmDelete(){
+ let deleted = $('#dg').datagrid('getChanges','deleted');
+ console.log("真正需要提交到后台删除的数据:", deleted);
+ let updated = $('#dg').datagrid('getChanges','updated');
+ console.log("updated:", updated);
+ let inserted = $('#dg').datagrid('getChanges','inserted');
+ console.log("inserted:", inserted);
+}
+
+let currentCell = { rowIndex: null, field: null };
+
+// 监听编辑器加载,记录焦点单元格
+$('#dg').datagrid({
+ onBeginEdit: function (rowIndex, rowData) {
+
+ let editors = $(this).datagrid('getEditors', rowIndex);
+
+ editors.forEach(e => {
+
+ $(e.target).off('focus').on('focus', function () {
+ currentCell.rowIndex = rowIndex;
+ currentCell.field = e.field;
+
+ // 获取所有列 field
+ let fields = $('#dg').datagrid('getColumnFields', true) // true = 冻结列
+ .concat($('#dg').datagrid('getColumnFields', false)); // false = 普通列
+
+ let colIndex = fields.indexOf(e.field);
+
+ console.log(`👉 聚焦位置:行=${currentCell.rowIndex}, 列索引=${colIndex}, field=${currentCell.field}`);
+ });
+
+
+ // 监听粘贴事件
+ $(e.target).off('paste').on('paste', function (event) {
+ setTimeout(() => { // 延迟确保粘贴内容已写入 input
+ let pastedValue = $(this).val();
+ console.log(`📌 粘贴事件触发:行=${currentCell.rowIndex}, 列=${currentCell.field}, 内容=${pastedValue}`);
+
+ handlePaste(currentCell.rowIndex, currentCell.field, pastedValue);
+ }, 10);
+ });
+ });
+ }
+});
+
+
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 7d6be2cf..a29ec86f 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
@@ -10,6 +10,8 @@ import (
"base-go-v2/internal/utils/jwtx"
"base-go-v2/internal/utils/mapx"
"base-go-v2/internal/utils/timex"
+ "fmt"
+ "time"
)
func InitAuthRouter(r *router.Router) {
@@ -69,10 +71,14 @@ func loginDefault(c *router.Context) error {
"account", account,
"password", password,
)
+ start := time.Now()
+
user, err := db.SelectOne("user_info", db.BuildWhereString(userQuery), "")
if err != nil {
return response.Success(c).Message("查询用户失败,请稍后重试").Send()
}
+ duration := time.Since(start) // 耗时
+ fmt.Printf("登录查询: (耗时: %v)\n", duration)
if user == nil {
// 账号或密码错误
diff --git a/code/base-project/base-go-v3/biz/handler/auth/login_service.go b/code/base-project/base-go-v3/biz/handler/auth/login_service.go
index 1579c74d..7d04de9b 100644
--- a/code/base-project/base-go-v3/biz/handler/auth/login_service.go
+++ b/code/base-project/base-go-v3/biz/handler/auth/login_service.go
@@ -8,6 +8,8 @@ import (
"cn/oneao/base-go/utils/jwtx"
"cn/oneao/base-go/utils/response"
"context"
+ "fmt"
+ "time"
auth "cn/oneao/base-go/biz/model/auth"
@@ -25,13 +27,18 @@ func LoginDefault(ctx context.Context, c *app.RequestContext) {
c.String(consts.StatusBadRequest, err.Error())
return
}
- // 处理登录
+ // 假设 ctx 已有,req.Account / req.Password 已有
+ start := time.Now()
+
loginUser, err := db.Client.UserInfo.
Query().
Where(userinfo.AccountEQ(req.Account),
userinfo.PasswordEQ(req.Password)).
Only(ctx)
+ duration := time.Since(start) // 耗时
+ fmt.Printf("登录查询: (耗时: %v)\n", duration)
+
if err != nil {
c.String(consts.StatusInternalServerError, "账号或密码错误")
return
diff --git a/code/base-project/base-go-v3/biz/router/middleware/error.go b/code/base-project/base-go-v3/biz/router/middleware/error.go
index 6b05f9cd..e4fe505f 100644
--- a/code/base-project/base-go-v3/biz/router/middleware/error.go
+++ b/code/base-project/base-go-v3/biz/router/middleware/error.go
@@ -13,7 +13,7 @@ func Error() app.HandlerFunc {
if err := c.Errors.Last(); err != nil {
// 未知错误 -> 500
- c.JSON(500, ErrorResponse{500, "Internal Server Error"})
+ //c.JSON(500, ErrorResponse{500, "Internal Server Error"})
}
}
}
diff --git a/code/base-project/base-go-v3/biz/router/middleware/log.go b/code/base-project/base-go-v3/biz/router/middleware/log.go
new file mode 100644
index 00000000..4621b2cc
--- /dev/null
+++ b/code/base-project/base-go-v3/biz/router/middleware/log.go
@@ -0,0 +1,32 @@
+package middleware
+
+import (
+ "context"
+ "time"
+
+ "github.com/cloudwego/hertz/pkg/app"
+ "github.com/cloudwego/hertz/pkg/common/hlog"
+ "github.com/hertz-contrib/requestid"
+ "go.uber.org/zap"
+)
+
+func Log() app.HandlerFunc {
+ return func(c context.Context, ctx *app.RequestContext) {
+ start := time.Now() // 记录开始时间
+
+ // 执行后续处理
+ ctx.Next(c)
+
+ // 计算耗时
+ duration := time.Since(start).Milliseconds()
+
+ hlog.Info("请求完成",
+ zap.String("RequestID", requestid.Get(ctx)),
+ zap.String("IP", ctx.ClientIP()),
+ zap.String("Method", string(ctx.Method())),
+ zap.String("Path", string(ctx.Path())),
+ zap.Int("Status", ctx.GetResponse().StatusCode()),
+ zap.Int64("Duration", duration),
+ )
+ }
+}
diff --git a/code/base-project/base-go-v3/log/log.go b/code/base-project/base-go-v3/log/log.go
index b9c70029..cb5b5b73 100644
--- a/code/base-project/base-go-v3/log/log.go
+++ b/code/base-project/base-go-v3/log/log.go
@@ -1,49 +1,105 @@
package log
import (
- "log"
+ "fmt"
"os"
- "path"
+ "sync"
"time"
"github.com/cloudwego/hertz/pkg/common/hlog"
- hertzzap "github.com/hertz-contrib/logger/zap"
"go.uber.org/zap"
+ "go.uber.org/zap/zapcore"
"gopkg.in/natefinch/lumberjack.v2"
)
func InitLog() {
- // Customizable output directory.
- var logFilePath string
- dir := "./hlog"
- logFilePath = dir + "/logs/"
- if err := os.MkdirAll(logFilePath, 0o777); err != nil {
- log.Println(err.Error())
- return
- }
+ dynamicLevel := zap.NewAtomicLevel()
- // Set filename to date
- logFileName := time.Now().Format("2006-01-02") + ".log"
- fileName := path.Join(logFilePath, logFileName)
- if _, err := os.Stat(fileName); err != nil {
- if _, err := os.Create(fileName); err != nil {
- log.Println(err.Error())
- return
- }
- }
+ dynamicLevel.SetLevel(zap.DebugLevel)
- logger := hertzzap.NewLogger(hertzzap.WithCores([]hertzzap.CoreConfig{}))
-
- lumberjackLogger := &lumberjack.Logger{
- Filename: fileName,
- MaxSize: 20, // A file can be up to 20M.
- MaxBackups: 5, // Save up to 5 files at the same time.
- MaxAge: 10, // A file can exist for a maximum of 10 days.
- Compress: true, // Compress with gzip.
- }
-
- logger.SetOutput(lumberjackLogger)
- logger.SetLevel(hlog.LevelDebug)
+ logger := NewLogger(
+ WithCores([]CoreConfig{
+ {
+ Enc: zapcore.NewJSONEncoder(humanEncoderConfig()),
+ Ws: zapcore.AddSync(os.Stdout),
+ Lvl: dynamicLevel,
+ },
+ {
+ Enc: zapcore.NewJSONEncoder(humanEncoderConfig()),
+ Ws: getWriteSyncer("./logs/debug"),
+ Lvl: zap.LevelEnablerFunc(func(lev zapcore.Level) bool {
+ return lev == zap.DebugLevel
+ }),
+ },
+ {
+ Enc: zapcore.NewJSONEncoder(humanEncoderConfig()),
+ Ws: getWriteSyncer("./logs/info"),
+ Lvl: zap.LevelEnablerFunc(func(lev zapcore.Level) bool {
+ return lev == zap.InfoLevel
+ }),
+ },
+ {
+ Enc: zapcore.NewJSONEncoder(humanEncoderConfig()),
+ Ws: getWriteSyncer("./logs/warn"),
+ Lvl: zap.LevelEnablerFunc(func(lev zapcore.Level) bool {
+ return lev == zap.WarnLevel
+ }),
+ },
+ {
+ Enc: zapcore.NewJSONEncoder(humanEncoderConfig()),
+ Ws: getWriteSyncer("./logs/error"),
+ Lvl: zap.LevelEnablerFunc(func(lev zapcore.Level) bool {
+ return lev >= zap.ErrorLevel
+ }),
+ },
+ }...),
+ )
+ defer logger.Sync()
hlog.SetLogger(logger)
}
+
+// humanEncoderConfig copy from zap
+func humanEncoderConfig() zapcore.EncoderConfig {
+ return zapcore.EncoderConfig{
+ MessageKey: "Message",
+ LevelKey: "Level",
+ NameKey: "Name",
+ TimeKey: "Ts",
+ CallerKey: "Caller",
+ FunctionKey: "Func",
+ StacktraceKey: "Stacktrace",
+ LineEnding: "\n",
+ EncodeTime: zapcore.ISO8601TimeEncoder,
+ EncodeLevel: zapcore.LowercaseLevelEncoder,
+ EncodeDuration: zapcore.SecondsDurationEncoder,
+ EncodeCaller: zapcore.ShortCallerEncoder,
+ }
+}
+
+var (
+ mu sync.Mutex
+ currentDay = make(map[string]string)
+ currentSync = make(map[string]zapcore.WriteSyncer)
+)
+
+func getWriteSyncer(basePath string) zapcore.WriteSyncer {
+ mu.Lock()
+ defer mu.Unlock()
+
+ today := time.Now().Format("2006-01-02")
+ if day, ok := currentDay[basePath]; !ok || day != today {
+ currentDay[basePath] = today
+ file := fmt.Sprintf("%s/%s.log", basePath, today)
+
+ currentSync[basePath] = zapcore.AddSync(&lumberjack.Logger{
+ Filename: file,
+ MaxSize: 10,
+ MaxBackups: 50000,
+ MaxAge: 5,
+ Compress: true,
+ LocalTime: true,
+ })
+ }
+ return currentSync[basePath]
+}
diff --git a/code/base-project/base-go-v3/log/logger.go b/code/base-project/base-go-v3/log/logger.go
new file mode 100644
index 00000000..29e14215
--- /dev/null
+++ b/code/base-project/base-go-v3/log/logger.go
@@ -0,0 +1,268 @@
+package log
+
+import (
+ "context"
+ "fmt"
+ "io"
+
+ "github.com/cloudwego/hertz/pkg/common/hlog"
+ "go.uber.org/zap"
+ "go.uber.org/zap/zapcore"
+)
+
+var _ hlog.FullLogger = (*Logger)(nil)
+
+type Logger struct {
+ l *zap.Logger
+ config *config
+}
+
+func NewLogger(opts ...Option) *Logger {
+ config := defaultConfig()
+
+ // apply options
+ for _, opt := range opts {
+ opt.apply(config)
+ }
+
+ cores := make([]zapcore.Core, 0, len(config.coreConfigs))
+ for _, coreConfig := range config.coreConfigs {
+ cores = append(cores, zapcore.NewCore(coreConfig.Enc, coreConfig.Ws, coreConfig.Lvl))
+ }
+
+ logger := zap.New(
+ zapcore.NewTee(cores[:]...),
+ config.zapOpts...)
+
+ return &Logger{
+ l: logger,
+ config: config,
+ }
+}
+
+// GetExtraKeys get extraKeys from logger config
+func (l *Logger) GetExtraKeys() []ExtraKey {
+ return l.config.extraKeys
+}
+
+// PutExtraKeys add extraKeys after init
+func (l *Logger) PutExtraKeys(keys ...ExtraKey) {
+ for _, k := range keys {
+ if !InArray(k, l.config.extraKeys) {
+ l.config.extraKeys = append(l.config.extraKeys, k)
+ }
+ }
+}
+
+func (l *Logger) Log(level hlog.Level, kvs ...interface{}) {
+ if len(kvs) == 0 {
+ return
+ }
+
+ // 第一个参数作为 msg
+ msg := fmt.Sprint(kvs[0])
+
+ // 后续参数作为 zap.Field
+ fields := make([]zap.Field, 0, len(kvs)-1)
+ for _, v := range kvs[1:] {
+ if f, ok := v.(zap.Field); ok {
+ fields = append(fields, f)
+ }
+ }
+
+ switch level {
+ case hlog.LevelTrace, hlog.LevelDebug:
+ l.l.Debug(msg, fields...)
+ case hlog.LevelInfo:
+ l.l.Info(msg, fields...)
+ case hlog.LevelNotice, hlog.LevelWarn:
+ l.l.Warn(msg, fields...)
+ case hlog.LevelError:
+ l.l.Error(msg, fields...)
+ case hlog.LevelFatal:
+ l.l.Fatal(msg, fields...)
+ default:
+ l.l.Warn(msg, fields...)
+ }
+}
+
+func (l *Logger) Logf(level hlog.Level, format string, kvs ...interface{}) {
+ logger := l.l.Sugar().With()
+ switch level {
+ case hlog.LevelTrace, hlog.LevelDebug:
+ logger.Debugf(format, kvs...)
+ case hlog.LevelInfo:
+ logger.Infof(format, kvs...)
+ case hlog.LevelNotice, hlog.LevelWarn:
+ logger.Warnf(format, kvs...)
+ case hlog.LevelError:
+ logger.Errorf(format, kvs...)
+ case hlog.LevelFatal:
+ logger.Fatalf(format, kvs...)
+ default:
+ logger.Warnf(format, kvs...)
+ }
+}
+
+func (l *Logger) CtxLogf(level hlog.Level, ctx context.Context, format string, kvs ...interface{}) {
+ zLevel := hLevelToZapLevel(level)
+ if !l.config.coreConfigs[0].Lvl.Enabled(zLevel) {
+ return
+ }
+ zapLogger := l.l
+ if len(l.config.extraKeys) > 0 {
+ for _, k := range l.config.extraKeys {
+ if l.config.extraKeyAsStr {
+ v := ctx.Value(string(k))
+ if v != nil {
+ zapLogger = zapLogger.With(zap.Any(string(k), v))
+ }
+ } else {
+ v := ctx.Value(k)
+ if v != nil {
+ zapLogger = zapLogger.With(zap.Any(string(k), v))
+ }
+ }
+ }
+ }
+ log := zapLogger.Sugar()
+ switch level {
+ case hlog.LevelDebug, hlog.LevelTrace:
+ log.Debugf(format, kvs...)
+ case hlog.LevelInfo:
+ log.Infof(format, kvs...)
+ case hlog.LevelNotice, hlog.LevelWarn:
+ log.Warnf(format, kvs...)
+ case hlog.LevelError:
+ log.Errorf(format, kvs...)
+ case hlog.LevelFatal:
+ log.Fatalf(format, kvs...)
+ default:
+ log.Warnf(format, kvs...)
+ }
+}
+
+func (l *Logger) Trace(v ...interface{}) {
+ l.Log(hlog.LevelTrace, v...)
+}
+
+func (l *Logger) Debug(v ...interface{}) {
+ l.Log(hlog.LevelDebug, v...)
+}
+
+func (l *Logger) Info(v ...interface{}) {
+ l.Log(hlog.LevelInfo, v...)
+}
+
+func (l *Logger) Notice(v ...interface{}) {
+ l.Log(hlog.LevelNotice, v...)
+}
+
+func (l *Logger) Warn(v ...interface{}) {
+ l.Log(hlog.LevelWarn, v...)
+}
+
+func (l *Logger) Error(v ...interface{}) {
+ l.Log(hlog.LevelError, v...)
+}
+
+func (l *Logger) Fatal(v ...interface{}) {
+ l.Log(hlog.LevelFatal, v...)
+}
+
+func (l *Logger) Tracef(format string, v ...interface{}) {
+ l.Logf(hlog.LevelTrace, format, v...)
+}
+
+func (l *Logger) Debugf(format string, v ...interface{}) {
+ l.Logf(hlog.LevelDebug, format, v...)
+}
+
+func (l *Logger) Infof(format string, v ...interface{}) {
+ l.Logf(hlog.LevelInfo, format, v...)
+}
+
+func (l *Logger) Noticef(format string, v ...interface{}) {
+ l.Logf(hlog.LevelWarn, format, v...)
+}
+
+func (l *Logger) Warnf(format string, v ...interface{}) {
+ l.Logf(hlog.LevelWarn, format, v...)
+}
+
+func (l *Logger) Errorf(format string, v ...interface{}) {
+ l.Logf(hlog.LevelError, format, v...)
+}
+
+func (l *Logger) Fatalf(format string, v ...interface{}) {
+ l.Logf(hlog.LevelFatal, format, v...)
+}
+
+func (l *Logger) CtxTracef(ctx context.Context, format string, v ...interface{}) {
+ l.CtxLogf(hlog.LevelDebug, ctx, format, v...)
+}
+
+func (l *Logger) CtxDebugf(ctx context.Context, format string, v ...interface{}) {
+ l.CtxLogf(hlog.LevelDebug, ctx, format, v...)
+}
+
+func (l *Logger) CtxInfof(ctx context.Context, format string, v ...interface{}) {
+ l.CtxLogf(hlog.LevelInfo, ctx, format, v...)
+}
+
+func (l *Logger) CtxNoticef(ctx context.Context, format string, v ...interface{}) {
+ l.CtxLogf(hlog.LevelWarn, ctx, format, v...)
+}
+
+func (l *Logger) CtxWarnf(ctx context.Context, format string, v ...interface{}) {
+ l.CtxLogf(hlog.LevelWarn, ctx, format, v...)
+}
+
+func (l *Logger) CtxErrorf(ctx context.Context, format string, v ...interface{}) {
+ l.CtxLogf(hlog.LevelError, ctx, format, v...)
+}
+
+func (l *Logger) CtxFatalf(ctx context.Context, format string, v ...interface{}) {
+ l.CtxLogf(hlog.LevelFatal, ctx, format, v...)
+}
+
+func (l *Logger) SetLevel(level hlog.Level) {
+ lvl := hLevelToZapLevel(level)
+
+ l.config.coreConfigs[0].Lvl = lvl
+
+ cores := make([]zapcore.Core, 0, len(l.config.coreConfigs))
+ for _, coreConfig := range l.config.coreConfigs {
+ cores = append(cores, zapcore.NewCore(coreConfig.Enc, coreConfig.Ws, coreConfig.Lvl))
+ }
+
+ logger := zap.New(
+ zapcore.NewTee(cores[:]...),
+ l.config.zapOpts...)
+
+ l.l = logger
+}
+
+func (l *Logger) SetOutput(writer io.Writer) {
+ l.config.coreConfigs[0].Ws = zapcore.AddSync(writer)
+
+ cores := make([]zapcore.Core, 0, len(l.config.coreConfigs))
+ for _, coreConfig := range l.config.coreConfigs {
+ cores = append(cores, zapcore.NewCore(coreConfig.Enc, coreConfig.Ws, coreConfig.Lvl))
+ }
+
+ logger := zap.New(
+ zapcore.NewTee(cores[:]...),
+ l.config.zapOpts...)
+
+ l.l = logger
+}
+
+// Logger is used to return an instance of *zap.Logger for custom fields, etc.
+func (l *Logger) Logger() *zap.Logger {
+ return l.l
+}
+
+func (l *Logger) Sync() {
+ _ = l.l.Sync()
+}
diff --git a/code/base-project/base-go-v3/log/utils.go b/code/base-project/base-go-v3/log/utils.go
index 3faf01ca..2c439e81 100644
--- a/code/base-project/base-go-v3/log/utils.go
+++ b/code/base-project/base-go-v3/log/utils.go
@@ -1,17 +1,3 @@
-// Copyright 2022 CloudWeGo Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
package log
import (
diff --git a/code/base-project/base-go-v3/main.go b/code/base-project/base-go-v3/main.go
index 79fcafd4..61235d72 100644
--- a/code/base-project/base-go-v3/main.go
+++ b/code/base-project/base-go-v3/main.go
@@ -1,42 +1,34 @@
package main
import (
+ "cn/oneao/base-go/biz/router/middleware"
"cn/oneao/base-go/config"
"cn/oneao/base-go/db"
"cn/oneao/base-go/log"
"cn/oneao/base-go/utils/idgen"
+
"github.com/cloudwego/hertz/pkg/app/server"
- "github.com/cloudwego/hertz/pkg/common/hlog"
"github.com/hertz-contrib/requestid"
)
func main() {
-
// 初始化雪花id
options := idgen.NewIdGeneratorOptions(config.App.Snowflake.WorkerID)
idgen.SetIdGenerator(options)
// 初始化数据库
db.InitDB()
+ // 日志
+ log.InitLog()
h := server.Default(
server.WithBasePath(config.App.Server.BaseUrl),
server.WithHostPorts(config.App.Server.Port),
)
- // 请求 id
+ // 全局中间件
h.Use(requestid.New())
-
- log.InitLog()
-
- type User struct {
- Name string
- Age int
- }
-
- u := User{"tom", 20}
-
- hlog.Info(u)
+ h.Use(middleware.Log())
register(h)
h.Spin()