Merge branch 'develop' into release

This commit is contained in:
sk 2024-11-11 10:09:24 +08:00
commit efb440d6a9
39 changed files with 2894 additions and 47 deletions

View File

@ -345,7 +345,7 @@ func (this *SceneStateAvengersStart) OnPlayerOp(s *base.Scene, p *base.Player, o
}
if sceneEx.CheckNeedDestroy() && playerEx.freeTimes <= 0 {
//离开有统计
sceneEx.PlayerLeave(playerEx.Player, common.PlayerLeaveReason_OnDestroy, true)
sceneEx.SceneDestroy(true)
return false
}
switch opcode {
@ -991,7 +991,7 @@ func AvengersCheckAndSaveLog(sceneEx *AvengersSceneData, playerEx *AvengersPlaye
playerEx.smallGameWinCoin = 0
if sceneEx.CheckNeedDestroy() && playerEx.freeTimes <= 0 {
sceneEx.PlayerLeave(playerEx.Player, common.PlayerLeaveReason_OnDestroy, true)
sceneEx.SceneDestroy(true)
}
}

View File

@ -351,7 +351,7 @@ func (this *SceneStateCaiShenStart) OnPlayerOp(s *base.Scene, p *base.Player, op
}
if sceneEx.CheckNeedDestroy() && playerEx.freeTimes <= 0 {
//离开有统计
sceneEx.PlayerLeave(playerEx.Player, common.PlayerLeaveReason_OnDestroy, true)
sceneEx.SceneDestroy(true)
return false
}
switch opcode {
@ -1080,7 +1080,7 @@ func CaiShenCheckAndSaveLog(sceneEx *CaiShenSceneData, playerEx *CaiShenPlayerDa
playerEx.smallGameWinCoin = 0
if sceneEx.CheckNeedDestroy() && playerEx.freeTimes <= 0 {
sceneEx.PlayerLeave(playerEx.Player, common.PlayerLeaveReason_OnDestroy, true)
sceneEx.SceneDestroy(true)
}
}

View File

@ -346,7 +346,7 @@ func (this *SceneStateEasterIslandStart) OnPlayerOp(s *base.Scene, p *base.Playe
}
if sceneEx.CheckNeedDestroy() && playerEx.freeTimes <= 0 {
//离开有统计
sceneEx.PlayerLeave(playerEx.Player, common.PlayerLeaveReason_OnDestroy, true)
sceneEx.SceneDestroy(true)
return false
}
switch opcode {
@ -949,7 +949,7 @@ func EasterIslandCheckAndSaveLog(sceneEx *EasterIslandSceneData, playerEx *Easte
playerEx.smallGameWinCoin = 0
if sceneEx.CheckNeedDestroy() && playerEx.freeTimes <= 0 {
sceneEx.PlayerLeave(playerEx.Player, common.PlayerLeaveReason_OnDestroy, true)
sceneEx.SceneDestroy(true)
}
}

View File

@ -559,7 +559,7 @@ func FortuneDragonAndSaveLog(sceneEx *FortuneDragonSceneData, playerEx *FortuneD
playerEx.winCoin = 0
if sceneEx.CheckNeedDestroy() && data.Results[0].FreeNum <= 0 {
sceneEx.PlayerLeave(playerEx.Player, common.PlayerLeaveReason_OnDestroy, true)
sceneEx.SceneDestroy(true)
}
}
func init() {

View File

@ -561,7 +561,7 @@ func FortuneOxAndSaveLog(sceneEx *FortuneOxSceneData, playerEx *FortuneOxPlayerD
playerEx.winCoin = 0
if sceneEx.CheckNeedDestroy() && data.Results[0].FreeNum <= 0 {
sceneEx.PlayerLeave(playerEx.Player, common.PlayerLeaveReason_OnDestroy, true)
sceneEx.SceneDestroy(true)
}
}
func init() {

View File

@ -558,7 +558,7 @@ func FortuneRabbitAndSaveLog(sceneEx *FortuneRabbitSceneData, playerEx *FortuneR
playerEx.winCoin = 0
if sceneEx.CheckNeedDestroy() && data.Results[0].FreeNum <= 0 {
sceneEx.PlayerLeave(playerEx.Player, common.PlayerLeaveReason_OnDestroy, true)
sceneEx.SceneDestroy(true)
}
}
func init() {

View File

@ -308,17 +308,7 @@ func (this *SceneBaseStateFruits) OnTick(s *base.Scene) {
// }
//}
if sceneEx.CheckNeedDestroy() {
for _, player := range sceneEx.players {
if !player.IsRob {
if player.freeTimes == 0 && player.maryFreeTimes == 0 {
//离开有统计
sceneEx.PlayerLeave(player.Player, common.PlayerLeaveReason_OnDestroy, true)
}
}
}
if s.GetRealPlayerCnt() == 0 {
sceneEx.SceneDestroy(true)
}
sceneEx.SceneDestroy(true)
}
}
//if sceneEx, ok := s.GetExtraData().(*FruitsSceneData); ok {

View File

@ -999,7 +999,7 @@ func IceAgeCheckAndSaveLog(sceneEx *IceAgeSceneData, playerEx *IceAgePlayerData)
playerEx.smallGameWinCoin = 0
if sceneEx.CheckNeedDestroy() && playerEx.freeTimes <= 0 {
sceneEx.PlayerLeave(playerEx.Player, common.PlayerLeaveReason_OnDestroy, true)
sceneEx.SceneDestroy(true)
}
}

View File

@ -311,14 +311,7 @@ func (this *SceneBaseStateRichBlessed) OnTick(s *base.Scene) {
// }
//}
if sceneEx.CheckNeedDestroy() {
for _, player := range sceneEx.players {
if !player.IsRob {
sceneEx.PlayerLeave(player.Player, common.PlayerLeaveReason_OnDestroy, true)
}
}
if s.GetRealPlayerCnt() == 0 {
sceneEx.SceneDestroy(true)
}
sceneEx.SceneDestroy(true)
}
}
//if sceneEx, ok := s.GetExtraData().(*RichBlessedSceneData); ok {

View File

@ -326,7 +326,7 @@ func (this *SceneStateTamQuocStart) OnPlayerOp(s *base.Scene, p *base.Player, op
}
if sceneEx.CheckNeedDestroy() && playerEx.freeTimes <= 0 {
//离开有统计
sceneEx.PlayerLeave(playerEx.Player, common.PlayerLeaveReason_OnDestroy, true)
sceneEx.SceneDestroy(true)
return false
}
switch opcode {
@ -835,7 +835,7 @@ func TamQuocCheckAndSaveLog(sceneEx *TamQuocSceneData, playerEx *TamQuocPlayerDa
playerEx.smallGameWinCoin = 0
if sceneEx.CheckNeedDestroy() && playerEx.freeTimes <= 0 {
sceneEx.PlayerLeave(playerEx.Player, common.PlayerLeaveReason_OnDestroy, true)
sceneEx.SceneDestroy(true)
}
}

View File

@ -967,6 +967,7 @@ func NewPlayerData(acc string, name, headUrl string, id int32, channel, platform
AccountType: accountType,
DeviceOS: deviceOS,
ClientVer: clientVer,
GuideData: make(map[int32]int32),
}
pd.InitNewData(params)

3
tools/mongoctl/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
.idea/
*/.DS_Store
.vscode

21
tools/mongoctl/LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 dobyte
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

483
tools/mongoctl/README-zh.md Normal file
View File

@ -0,0 +1,483 @@
# mongoctl
### 1.介绍
mongoctl是一个自动化生成MongoDB数据访问对象Data Access Object的工具。
### 2.优势
* 支持primitive.ObjectID、primitive.DateTime类型的自动填充。
* 支持int、int8、int16、int32、int64、uint、uint8、uint16、uint32、uint64类型的自增长。
* 提供了对数据库字段的统一生成方案,避免了业务代码中随处可见的数据库字段的问题。
* 提供了包括InsertOne、InsertMany、UpdateOne、UpdateOneByID、UpdateMany、FindOne、FindOneByID、FindMany、DeleteOne、DeleteOneByID、DeleteMany、Count、Aggregate等多种数据库操作接口。
* 提供了对数据库操作接口的扩展能力。
* 提供了分包与不分包两种包解决方案。
* 支持目录和文件名风格的自定义。
### 3.安装
```bash
go install
```
### 4.用法
```bash
用法 mongoctl:
mongoctl [flags] -model-dir=. -model-names=T,T -dao-dir=./dao
Flags:
-counter-name string
自增模型名称; 默认是 counter
-dao-dir string
生成代码所在目录; 必需
-dao-pkg-path string
生成代码所在目录的包名; 默认自动生成
-file-style string
代码文件名称风格; 选项: kebab | underscore | lower | camel | pascal; 默认是 underscore (default "underscore")
-model-dir string
模型所在目录; 必需
-model-names string
模型结构体名称; 必需
-model-pkg-alias string
模型包别名
-model-pkg-path string
模型包名; 默认自动生成
-sub-pkg-enable
每个模型创建一个子包; 默认关闭
-sub-pkg-style string
模型子包目录名称风格; 选项: kebab | underscore | lower | camel | pascal; 默认是 kebab (default "kebab")
```
### 5.标签
在模型定义中支持对gen标签的解析目前支持以下标签解析
| 标签名称 | | 示例 | 说明 |
| -------- | ---------------------------------------------------------- | ------------------ | -------------------------- |
| autoFill | primitive.ObjectID、primitive.DateTime | gen:"autoFill" | |
| autoIncr | int、int8、int16、int32、int64、uint、uint8、uint16、uint32、uint64 | gen:"autoIncr:uid" | 该自增为原子操作,会在生成代码时同步生成计数器代码。 |
### 6.示例
###### 6-1.创建模型
model/mail.go
```go
package model
import "go.mongodb.org/mongo-driver/bson/primitive"
//go:generate mongoctl -model-dir=. -model-names=Mail -dao-dir=../dao/
type Mail struct {
ID primitive.ObjectID `bson:"_id" gen:"autoFill"` // 邮件ID
Title string `bson:"title"` // 邮件标题
Content string `bson:"content"` // 邮件内容
Sender int64 `bson:"sender"` // 邮件发送者
Receiver int64 `bson:"receiver"` // 邮件接受者
Status int `bson:"status"` // 邮件状态
SendTime primitive.DateTime `bson:"send_time" gen:"autoFill"` // 发送时间
}
```
###### 6-2.生成dao文件
```bash
go generate ./...
```
###### 6-3.生成的dao文件示例
dao/internal/mail.go
```go
// --------------------------------------------------------------------------------------------
// The following code is automatically generated by the mongo-dao-generator tool.
// Please do not modify this code manually to avoid being overwritten in the next generation.
// For more tool details, please click the link to view https://github.com/dobyte/mongo-dao-generator
// --------------------------------------------------------------------------------------------
package internal
import (
"context"
"errors"
models "github.com/dobyte/mongo-dao-generator/example/model"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"time"
)
type MailFilterFunc func(cols *MailColumns) interface{}
type MailUpdateFunc func(cols *MailColumns) interface{}
type MailPipelineFunc func(cols *MailColumns) interface{}
type MailCountOptionsFunc func(cols *MailColumns) *options.CountOptions
type MailAggregateOptionsFunc func(cols *MailColumns) *options.AggregateOptions
type MailFindOneOptionsFunc func(cols *MailColumns) *options.FindOneOptions
type MailFindManyOptionsFunc func(cols *MailColumns) *options.FindOptions
type MailUpdateOptionsFunc func(cols *MailColumns) *options.UpdateOptions
type MailDeleteOptionsFunc func(cols *MailColumns) *options.DeleteOptions
type MailInsertOneOptionsFunc func(cols *MailColumns) *options.InsertOneOptions
type MailInsertManyOptionsFunc func(cols *MailColumns) *options.InsertManyOptions
type Mail struct {
Columns *MailColumns
Database *mongo.Database
Collection *mongo.Collection
}
type MailColumns struct {
ID string // 邮件ID
Title string // 邮件标题
Content string // 邮件内容
Sender string // 邮件发送者
Receiver string // 邮件接受者
Status string // 邮件状态
SendTime string // 发送时间
}
var mailColumns = &MailColumns{
ID: "_id", // 邮件ID
Title: "title", // 邮件标题
Content: "content", // 邮件内容
Sender: "sender", // 邮件发送者
Receiver: "receiver", // 邮件接受者
Status: "status", // 邮件状态
SendTime: "send_time", // 发送时间
}
func NewMail(db *mongo.Database) *Mail {
return &Mail{
Columns: mailColumns,
Database: db,
Collection: db.Collection("mail"),
}
}
// Count returns the number of documents in the collection.
func (dao *Mail) Count(ctx context.Context, filterFunc MailFilterFunc, optionsFunc ...MailCountOptionsFunc) (int64, error) {
var (
opts *options.CountOptions
filter = filterFunc(dao.Columns)
)
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
return dao.Collection.CountDocuments(ctx, filter, opts)
}
// Aggregate executes an aggregate command against the collection and returns a cursor over the resulting documents.
func (dao *Mail) Aggregate(ctx context.Context, pipelineFunc MailPipelineFunc, optionsFunc ...MailAggregateOptionsFunc) (*mongo.Cursor, error) {
var (
opts *options.AggregateOptions
pipeline = pipelineFunc(dao.Columns)
)
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
return dao.Collection.Aggregate(ctx, pipeline, opts)
}
// InsertOne executes an insert command to insert a single document into the collection.
func (dao *Mail) InsertOne(ctx context.Context, model *models.Mail, optionsFunc ...MailInsertOneOptionsFunc) (*mongo.InsertOneResult, error) {
if model == nil {
return nil, errors.New("model is nil")
}
if err := dao.autofill(ctx, model); err != nil {
return nil, err
}
var opts *options.InsertOneOptions
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
return dao.Collection.InsertOne(ctx, model, opts)
}
// InsertMany executes an insert command to insert multiple documents into the collection.
func (dao *Mail) InsertMany(ctx context.Context, models []*models.Mail, optionsFunc ...MailInsertManyOptionsFunc) (*mongo.InsertManyResult, error) {
if len(models) == 0 {
return nil, errors.New("models is empty")
}
documents := make([]interface{}, 0, len(models))
for i := range models {
model := models[i]
if err := dao.autofill(ctx, model); err != nil {
return nil, err
}
documents = append(documents, model)
}
var opts *options.InsertManyOptions
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
return dao.Collection.InsertMany(ctx, documents, opts)
}
// UpdateOne executes an update command to update at most one document in the collection.
func (dao *Mail) UpdateOne(ctx context.Context, filterFunc MailFilterFunc, updateFunc MailUpdateFunc, optionsFunc ...MailUpdateOptionsFunc) (*mongo.UpdateResult, error) {
var (
opts *options.UpdateOptions
filter = filterFunc(dao.Columns)
update = updateFunc(dao.Columns)
)
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
return dao.Collection.UpdateOne(ctx, filter, update, opts)
}
// UpdateOneByID executes an update command to update at most one document in the collection.
func (dao *Mail) UpdateOneByID(ctx context.Context, id string, updateFunc MailUpdateFunc, optionsFunc ...MailUpdateOptionsFunc) (*mongo.UpdateResult, error) {
objectID, err := primitive.ObjectIDFromHex(id)
if err != nil {
return nil, err
}
return dao.UpdateOne(ctx, func(cols *MailColumns) interface{} {
return bson.M{"_id": objectID}
}, updateFunc, optionsFunc...)
}
// UpdateMany executes an update command to update documents in the collection.
func (dao *Mail) UpdateMany(ctx context.Context, filterFunc MailFilterFunc, updateFunc MailUpdateFunc, optionsFunc ...MailUpdateOptionsFunc) (*mongo.UpdateResult, error) {
var (
opts *options.UpdateOptions
filter = filterFunc(dao.Columns)
update = updateFunc(dao.Columns)
)
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
return dao.Collection.UpdateMany(ctx, filter, update, opts)
}
// FindOne executes a find command and returns a model for one document in the collection.
func (dao *Mail) FindOne(ctx context.Context, filterFunc MailFilterFunc, optionsFunc ...MailFindOneOptionsFunc) (*models.Mail, error) {
var (
opts *options.FindOneOptions
model = &models.Mail{}
filter = filterFunc(dao.Columns)
)
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
err := dao.Collection.FindOne(ctx, filter, opts).Decode(model)
if err != nil {
if err == mongo.ErrNoDocuments {
return nil, nil
}
return nil, err
}
return model, nil
}
// FindOneByID executes a find command and returns a model for one document in the collection.
func (dao *Mail) FindOneByID(ctx context.Context, id string, optionsFunc ...MailFindOneOptionsFunc) (*models.Mail, error) {
objectID, err := primitive.ObjectIDFromHex(id)
if err != nil {
return nil, err
}
return dao.FindOne(ctx, func(cols *MailColumns) interface{} {
return bson.M{"_id": objectID}
}, optionsFunc...)
}
// FindMany executes a find command and returns many models the matching documents in the collection.
func (dao *Mail) FindMany(ctx context.Context, filterFunc MailFilterFunc, optionsFunc ...MailFindManyOptionsFunc) ([]*models.Mail, error) {
var (
opts *options.FindOptions
filter = filterFunc(dao.Columns)
)
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
cur, err := dao.Collection.Find(ctx, filter, opts)
if err != nil {
return nil, err
}
models := make([]*models.Mail, 0)
if err = cur.All(ctx, &models); err != nil {
return nil, err
}
return models, nil
}
// DeleteOne executes a delete command to delete at most one document from the collection.
func (dao *Mail) DeleteOne(ctx context.Context, filterFunc MailFilterFunc, optionsFunc ...MailDeleteOptionsFunc) (*mongo.DeleteResult, error) {
var (
opts *options.DeleteOptions
filter = filterFunc(dao.Columns)
)
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
return dao.Collection.DeleteOne(ctx, filter, opts)
}
// DeleteOneByID executes a delete command to delete at most one document from the collection.
func (dao *Mail) DeleteOneByID(ctx context.Context, id string, optionsFunc ...MailDeleteOptionsFunc) (*mongo.DeleteResult, error) {
objectID, err := primitive.ObjectIDFromHex(id)
if err != nil {
return nil, err
}
return dao.DeleteOne(ctx, func(cols *MailColumns) interface{} {
return bson.M{"_id": objectID}
}, optionsFunc...)
}
// DeleteMany executes a delete command to delete documents from the collection.
func (dao *Mail) DeleteMany(ctx context.Context, filterFunc MailFilterFunc, optionsFunc ...MailDeleteOptionsFunc) (*mongo.DeleteResult, error) {
var (
opts *options.DeleteOptions
filter = filterFunc(dao.Columns)
)
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
return dao.Collection.DeleteMany(ctx, filter, opts)
}
// autofill when inserting data
func (dao *Mail) autofill(ctx context.Context, model *models.Mail) error {
if model.ID.IsZero() {
model.ID = primitive.NewObjectID()
}
if model.SendTime == 0 {
model.SendTime = primitive.NewDateTimeFromTime(time.Now())
}
return nil
}
```
dao/mail.go
```go
package dao
import (
"github.com/dobyte/mongo-dao-generator/example/dao/internal"
"go.mongodb.org/mongo-driver/mongo"
)
type MailColumns = internal.MailColumns
type Mail struct {
*internal.Mail
}
func NewMail(db *mongo.Database) *Mail {
return &Mail{Mail: internal.NewMail(db)}
}
```
###### 6-4.使用生成的dao文件
```go
package main
import (
"context"
"github.com/dobyte/mongo-dao-generator/example/dao"
"github.com/dobyte/mongo-dao-generator/example/model"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/mongo/readpref"
"log"
"time"
)
func main() {
var (
uri = "mongodb://root:12345678@127.0.0.1:27017"
opts = options.Client().ApplyURI(uri)
baseCtx = context.Background()
)
ctx, cancel := context.WithTimeout(baseCtx, 5*time.Second)
client, err := mongo.Connect(ctx, opts)
cancel()
if err != nil {
log.Fatalf("connect mongo server failed: %v", err)
}
ctx, cancel = context.WithTimeout(baseCtx, 5*time.Second)
defer cancel()
err = client.Ping(ctx, readpref.Primary())
cancel()
if err != nil {
log.Fatalf("ping mongo server failed: %v", err)
}
db := client.Database("dao_test")
mailDao := dao.NewMail(db)
_, err = mailDao.InsertOne(baseCtx, &model.Mail{
Title: "mongo-dao-generator introduction",
Content: "the mongo-dao-generator is a tool for automatically generating MongoDB Data Access Object.",
Sender: 1,
Receiver: 2,
Status: 1,
})
if err != nil {
log.Fatalf("failed to insert into mongo database: %v", err)
}
mail, err := mailDao.FindOne(baseCtx, func(cols *dao.MailColumns) interface{} {
return bson.M{cols.Receiver: 2}
})
if err != nil {
log.Fatalf("failed to find a row of data from mongo database: %v", err)
}
log.Printf("%+v", mail)
}
```
运行结果:
```bash
2023/02/17 16:05:31 &{ID:ObjectID("63ef354a4ddc485f0d9c5ea3") Title:mongo-dao-generator introduction Content:the mongo-dao-generator is a tool for automatically generating MongoDB Data Access Object. Sender:1 Receiver:2 Status:1 SendTime:1676621130323}
```

51
tools/mongoctl/counter.go Normal file
View File

@ -0,0 +1,51 @@
package main
import (
"fmt"
"path/filepath"
"strings"
)
type counter struct {
opts *options
modelName string
daoClassName string
daoVariableName string
daoPkgPath string
daoPkgName string
daoOutputDir string
daoOutputFile string
daoPrefixName string
collectionName string
}
func newCounter(opts *options) *counter {
c := &counter{}
c.opts = opts
c.modelName = toPascalCase(opts.counterName)
c.daoClassName = toPascalCase(c.modelName)
c.daoVariableName = toCamelCase(c.modelName)
c.daoOutputFile = fmt.Sprintf("%s.go", toFileName(c.modelName, c.opts.fileNameStyle))
c.collectionName = toUnderscoreCase(c.modelName)
dir := strings.TrimSuffix(opts.daoDir, "/")
if opts.subPkgEnable {
c.daoOutputDir = dir + "/" + toPackagePath(c.modelName, c.opts.subPkgStyle)
} else {
c.daoOutputDir = dir
c.daoPrefixName = toPascalCase(c.modelName)
}
return c
}
func (c *counter) setDaoPkgPath(path string) {
if c.opts.subPkgEnable {
c.daoPkgPath = path + "/" + toPackagePath(c.modelName, c.opts.subPkgStyle)
} else {
c.daoPkgPath = path
}
c.daoPkgName = toPackageName(filepath.Base(c.daoPkgPath))
}

View File

@ -0,0 +1,14 @@
package dao
import (
"go.mongodb.org/mongo-driver/mongo"
"mongo.games.com/mongoctl/example/dao/internal"
)
type Counter struct {
*internal.Counter
}
func NewCounter(db *mongo.Database) *Counter {
return &Counter{Counter: internal.NewCounter(db)}
}

View File

@ -0,0 +1,76 @@
// --------------------------------------------------------------------------------------------------
// The following code is automatically generated by the mongo-dao-generator tool.
// Please do not modify this code manually to avoid being overwritten in the next generation.
// For more tool details, please click the link to view https://github.com/dobyte/mongo-dao-generator
// --------------------------------------------------------------------------------------------------
package internal
import (
"context"
"errors"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
type Counter struct {
Columns *CounterColumns
Database *mongo.Database
Collection *mongo.Collection
}
type CounterModel struct {
ID string `bson:"_id"`
Value int64 `bson:"value"`
}
type CounterColumns struct {
ID string
Value string
}
var counterColumns = &CounterColumns{
ID: "_id",
Value: "value",
}
func NewCounter(db *mongo.Database) *Counter {
return &Counter{
Columns: counterColumns,
Database: db,
Collection: db.Collection("counter"),
}
}
// Incr 自增值
func (dao *Counter) Incr(ctx context.Context, key string, incr ...int) (int64, error) {
var (
upsert = true
returnDocument = options.After
counter = &CounterModel{}
value = 1
)
if len(incr) > 0 {
if incr[0] == 0 {
return 0, errors.New("invalid increment value")
}
value = incr[0]
}
rst := dao.Collection.FindOneAndUpdate(ctx, bson.M{
dao.Columns.ID: key,
}, bson.M{"$inc": bson.M{
dao.Columns.Value: value,
}}, &options.FindOneAndUpdateOptions{
Upsert: &upsert,
ReturnDocument: &returnDocument,
})
if err := rst.Decode(counter); err != nil {
return 0, err
}
return counter.Value, nil
}

View File

@ -0,0 +1,290 @@
// --------------------------------------------------------------------------------------------
// The following code is automatically generated by the mongo-dao-generator tool.
// Please do not modify this code manually to avoid being overwritten in the next generation.
// For more tool details, please click the link to view https://github.com/dobyte/mongo-dao-generator
// --------------------------------------------------------------------------------------------
package internal
import (
"context"
"errors"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
modelpkg "mongo.games.com/mongoctl/example/model"
"time"
)
type MailFilterFunc func(cols *MailColumns) interface{}
type MailUpdateFunc func(cols *MailColumns) interface{}
type MailPipelineFunc func(cols *MailColumns) interface{}
type MailCountOptionsFunc func(cols *MailColumns) *options.CountOptions
type MailAggregateOptionsFunc func(cols *MailColumns) *options.AggregateOptions
type MailFindOneOptionsFunc func(cols *MailColumns) *options.FindOneOptions
type MailFindManyOptionsFunc func(cols *MailColumns) *options.FindOptions
type MailUpdateOptionsFunc func(cols *MailColumns) *options.UpdateOptions
type MailDeleteOptionsFunc func(cols *MailColumns) *options.DeleteOptions
type MailInsertOneOptionsFunc func(cols *MailColumns) *options.InsertOneOptions
type MailInsertManyOptionsFunc func(cols *MailColumns) *options.InsertManyOptions
type Mail struct {
Columns *MailColumns
Database *mongo.Database
Collection *mongo.Collection
}
type MailColumns struct {
ID string // 邮件ID
Title string // 邮件标题
Content string // 邮件内容
Sender string // 邮件发送者
Receiver string // 邮件接受者
Status string // 邮件状态
SendTime string // 发送时间
}
var mailColumns = &MailColumns{
ID: "_id", // 邮件ID
Title: "title", // 邮件标题
Content: "content", // 邮件内容
Sender: "sender", // 邮件发送者
Receiver: "receiver", // 邮件接受者
Status: "status", // 邮件状态
SendTime: "send_time", // 发送时间
}
func NewMail(db *mongo.Database) *Mail {
return &Mail{
Columns: mailColumns,
Database: db,
Collection: db.Collection("mail"),
}
}
// Count returns the number of documents in the collection.
func (dao *Mail) Count(ctx context.Context, filterFunc MailFilterFunc, optionsFunc ...MailCountOptionsFunc) (int64, error) {
var (
opts *options.CountOptions
filter = filterFunc(dao.Columns)
)
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
return dao.Collection.CountDocuments(ctx, filter, opts)
}
// Aggregate executes an aggregate command against the collection and returns a cursor over the resulting documents.
func (dao *Mail) Aggregate(ctx context.Context, pipelineFunc MailPipelineFunc, optionsFunc ...MailAggregateOptionsFunc) (*mongo.Cursor, error) {
var (
opts *options.AggregateOptions
pipeline = pipelineFunc(dao.Columns)
)
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
return dao.Collection.Aggregate(ctx, pipeline, opts)
}
// InsertOne executes an insert command to insert a single document into the collection.
func (dao *Mail) InsertOne(ctx context.Context, model *modelpkg.Mail, optionsFunc ...MailInsertOneOptionsFunc) (*mongo.InsertOneResult, error) {
if model == nil {
return nil, errors.New("model is nil")
}
if err := dao.autofill(ctx, model); err != nil {
return nil, err
}
var opts *options.InsertOneOptions
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
return dao.Collection.InsertOne(ctx, model, opts)
}
// InsertMany executes an insert command to insert multiple documents into the collection.
func (dao *Mail) InsertMany(ctx context.Context, models []*modelpkg.Mail, optionsFunc ...MailInsertManyOptionsFunc) (*mongo.InsertManyResult, error) {
if len(models) == 0 {
return nil, errors.New("models is empty")
}
documents := make([]interface{}, 0, len(models))
for i := range models {
model := models[i]
if err := dao.autofill(ctx, model); err != nil {
return nil, err
}
documents = append(documents, model)
}
var opts *options.InsertManyOptions
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
return dao.Collection.InsertMany(ctx, documents, opts)
}
// UpdateOne executes an update command to update at most one document in the collection.
func (dao *Mail) UpdateOne(ctx context.Context, filterFunc MailFilterFunc, updateFunc MailUpdateFunc, optionsFunc ...MailUpdateOptionsFunc) (*mongo.UpdateResult, error) {
var (
opts *options.UpdateOptions
filter = filterFunc(dao.Columns)
update = updateFunc(dao.Columns)
)
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
return dao.Collection.UpdateOne(ctx, filter, update, opts)
}
// UpdateOneByID executes an update command to update at most one document in the collection.
func (dao *Mail) UpdateOneByID(ctx context.Context, id string, updateFunc MailUpdateFunc, optionsFunc ...MailUpdateOptionsFunc) (*mongo.UpdateResult, error) {
objectID, err := primitive.ObjectIDFromHex(id)
if err != nil {
return nil, err
}
return dao.UpdateOne(ctx, func(cols *MailColumns) interface{} {
return bson.M{"_id": objectID}
}, updateFunc, optionsFunc...)
}
// UpdateMany executes an update command to update documents in the collection.
func (dao *Mail) UpdateMany(ctx context.Context, filterFunc MailFilterFunc, updateFunc MailUpdateFunc, optionsFunc ...MailUpdateOptionsFunc) (*mongo.UpdateResult, error) {
var (
opts *options.UpdateOptions
filter = filterFunc(dao.Columns)
update = updateFunc(dao.Columns)
)
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
return dao.Collection.UpdateMany(ctx, filter, update, opts)
}
// FindOne executes a find command and returns a model for one document in the collection.
func (dao *Mail) FindOne(ctx context.Context, filterFunc MailFilterFunc, optionsFunc ...MailFindOneOptionsFunc) (*modelpkg.Mail, error) {
var (
opts *options.FindOneOptions
model = &modelpkg.Mail{}
filter = filterFunc(dao.Columns)
)
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
err := dao.Collection.FindOne(ctx, filter, opts).Decode(model)
if err != nil {
if err == mongo.ErrNoDocuments {
return nil, nil
}
return nil, err
}
return model, nil
}
// FindOneByID executes a find command and returns a model for one document in the collection.
func (dao *Mail) FindOneByID(ctx context.Context, id string, optionsFunc ...MailFindOneOptionsFunc) (*modelpkg.Mail, error) {
objectID, err := primitive.ObjectIDFromHex(id)
if err != nil {
return nil, err
}
return dao.FindOne(ctx, func(cols *MailColumns) interface{} {
return bson.M{"_id": objectID}
}, optionsFunc...)
}
// FindMany executes a find command and returns many models the matching documents in the collection.
func (dao *Mail) FindMany(ctx context.Context, filterFunc MailFilterFunc, optionsFunc ...MailFindManyOptionsFunc) ([]*modelpkg.Mail, error) {
var (
opts *options.FindOptions
filter = filterFunc(dao.Columns)
)
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
cur, err := dao.Collection.Find(ctx, filter, opts)
if err != nil {
return nil, err
}
models := make([]*modelpkg.Mail, 0)
if err = cur.All(ctx, &models); err != nil {
return nil, err
}
return models, nil
}
// DeleteOne executes a delete command to delete at most one document from the collection.
func (dao *Mail) DeleteOne(ctx context.Context, filterFunc MailFilterFunc, optionsFunc ...MailDeleteOptionsFunc) (*mongo.DeleteResult, error) {
var (
opts *options.DeleteOptions
filter = filterFunc(dao.Columns)
)
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
return dao.Collection.DeleteOne(ctx, filter, opts)
}
// DeleteOneByID executes a delete command to delete at most one document from the collection.
func (dao *Mail) DeleteOneByID(ctx context.Context, id string, optionsFunc ...MailDeleteOptionsFunc) (*mongo.DeleteResult, error) {
objectID, err := primitive.ObjectIDFromHex(id)
if err != nil {
return nil, err
}
return dao.DeleteOne(ctx, func(cols *MailColumns) interface{} {
return bson.M{"_id": objectID}
}, optionsFunc...)
}
// DeleteMany executes a delete command to delete documents from the collection.
func (dao *Mail) DeleteMany(ctx context.Context, filterFunc MailFilterFunc, optionsFunc ...MailDeleteOptionsFunc) (*mongo.DeleteResult, error) {
var (
opts *options.DeleteOptions
filter = filterFunc(dao.Columns)
)
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
return dao.Collection.DeleteMany(ctx, filter, opts)
}
// autofill when inserting data
func (dao *Mail) autofill(ctx context.Context, model *modelpkg.Mail) error {
if model.ID.IsZero() {
model.ID = primitive.NewObjectID()
}
if model.SendTime == 0 {
model.SendTime = primitive.NewDateTimeFromTime(time.Now())
}
return nil
}

View File

@ -0,0 +1,330 @@
// --------------------------------------------------------------------------------------------
// The following code is automatically generated by the mongo-dao-generator tool.
// Please do not modify this code manually to avoid being overwritten in the next generation.
// For more tool details, please click the link to view https://github.com/dobyte/mongo-dao-generator
// --------------------------------------------------------------------------------------------
package internal
import (
"context"
"errors"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
modelpkg "mongo.games.com/mongoctl/example/model"
"time"
)
type UserFilterFunc func(cols *UserColumns) interface{}
type UserUpdateFunc func(cols *UserColumns) interface{}
type UserPipelineFunc func(cols *UserColumns) interface{}
type UserCountOptionsFunc func(cols *UserColumns) *options.CountOptions
type UserAggregateOptionsFunc func(cols *UserColumns) *options.AggregateOptions
type UserFindOneOptionsFunc func(cols *UserColumns) *options.FindOneOptions
type UserFindManyOptionsFunc func(cols *UserColumns) *options.FindOptions
type UserUpdateOptionsFunc func(cols *UserColumns) *options.UpdateOptions
type UserDeleteOptionsFunc func(cols *UserColumns) *options.DeleteOptions
type UserInsertOneOptionsFunc func(cols *UserColumns) *options.InsertOneOptions
type UserInsertManyOptionsFunc func(cols *UserColumns) *options.InsertManyOptions
type User struct {
Columns *UserColumns
Database *mongo.Database
Collection *mongo.Collection
}
type UserColumns struct {
ID string
UID string // 用户ID
Account string // 用户账号
Password string // 用户密码
Salt string // 密码
Mobile string // 用户手机
Email string // 用户邮箱
Nickname string // 用户昵称
Signature string // 用户签名
Gender string // 用户性别
Level string // 用户等级
Experience string // 用户经验
Coin string // 用户金币
Type string // 用户类型
Status string // 用户状态
DeviceID string // 设备ID
ThirdPlatforms string // 第三方平台
RegisterIP string // 注册IP
RegisterTime string // 注册时间
LastLoginIP string // 最近登录IP
LastLoginTime string // 最近登录时间
}
var userColumns = &UserColumns{
ID: "_id",
UID: "uid", // 用户ID
Account: "account", // 用户账号
Password: "password", // 用户密码
Salt: "salt", // 密码
Mobile: "mobile", // 用户手机
Email: "email", // 用户邮箱
Nickname: "nickname", // 用户昵称
Signature: "signature", // 用户签名
Gender: "gender", // 用户性别
Level: "level", // 用户等级
Experience: "experience", // 用户经验
Coin: "coin", // 用户金币
Type: "type", // 用户类型
Status: "status", // 用户状态
DeviceID: "device_id", // 设备ID
ThirdPlatforms: "third_platforms", // 第三方平台
RegisterIP: "register_ip", // 注册IP
RegisterTime: "register_time", // 注册时间
LastLoginIP: "last_login_ip", // 最近登录IP
LastLoginTime: "last_login_time", // 最近登录时间
}
func NewUser(db *mongo.Database) *User {
return &User{
Columns: userColumns,
Database: db,
Collection: db.Collection("user"),
}
}
// Count returns the number of documents in the collection.
func (dao *User) Count(ctx context.Context, filterFunc UserFilterFunc, optionsFunc ...UserCountOptionsFunc) (int64, error) {
var (
opts *options.CountOptions
filter = filterFunc(dao.Columns)
)
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
return dao.Collection.CountDocuments(ctx, filter, opts)
}
// Aggregate executes an aggregate command against the collection and returns a cursor over the resulting documents.
func (dao *User) Aggregate(ctx context.Context, pipelineFunc UserPipelineFunc, optionsFunc ...UserAggregateOptionsFunc) (*mongo.Cursor, error) {
var (
opts *options.AggregateOptions
pipeline = pipelineFunc(dao.Columns)
)
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
return dao.Collection.Aggregate(ctx, pipeline, opts)
}
// InsertOne executes an insert command to insert a single document into the collection.
func (dao *User) InsertOne(ctx context.Context, model *modelpkg.User, optionsFunc ...UserInsertOneOptionsFunc) (*mongo.InsertOneResult, error) {
if model == nil {
return nil, errors.New("model is nil")
}
if err := dao.autofill(ctx, model); err != nil {
return nil, err
}
var opts *options.InsertOneOptions
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
return dao.Collection.InsertOne(ctx, model, opts)
}
// InsertMany executes an insert command to insert multiple documents into the collection.
func (dao *User) InsertMany(ctx context.Context, models []*modelpkg.User, optionsFunc ...UserInsertManyOptionsFunc) (*mongo.InsertManyResult, error) {
if len(models) == 0 {
return nil, errors.New("models is empty")
}
documents := make([]interface{}, 0, len(models))
for i := range models {
model := models[i]
if err := dao.autofill(ctx, model); err != nil {
return nil, err
}
documents = append(documents, model)
}
var opts *options.InsertManyOptions
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
return dao.Collection.InsertMany(ctx, documents, opts)
}
// UpdateOne executes an update command to update at most one document in the collection.
func (dao *User) UpdateOne(ctx context.Context, filterFunc UserFilterFunc, updateFunc UserUpdateFunc, optionsFunc ...UserUpdateOptionsFunc) (*mongo.UpdateResult, error) {
var (
opts *options.UpdateOptions
filter = filterFunc(dao.Columns)
update = updateFunc(dao.Columns)
)
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
return dao.Collection.UpdateOne(ctx, filter, update, opts)
}
// UpdateOneByID executes an update command to update at most one document in the collection.
func (dao *User) UpdateOneByID(ctx context.Context, id string, updateFunc UserUpdateFunc, optionsFunc ...UserUpdateOptionsFunc) (*mongo.UpdateResult, error) {
objectID, err := primitive.ObjectIDFromHex(id)
if err != nil {
return nil, err
}
return dao.UpdateOne(ctx, func(cols *UserColumns) interface{} {
return bson.M{"_id": objectID}
}, updateFunc, optionsFunc...)
}
// UpdateMany executes an update command to update documents in the collection.
func (dao *User) UpdateMany(ctx context.Context, filterFunc UserFilterFunc, updateFunc UserUpdateFunc, optionsFunc ...UserUpdateOptionsFunc) (*mongo.UpdateResult, error) {
var (
opts *options.UpdateOptions
filter = filterFunc(dao.Columns)
update = updateFunc(dao.Columns)
)
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
return dao.Collection.UpdateMany(ctx, filter, update, opts)
}
// FindOne executes a find command and returns a model for one document in the collection.
func (dao *User) FindOne(ctx context.Context, filterFunc UserFilterFunc, optionsFunc ...UserFindOneOptionsFunc) (*modelpkg.User, error) {
var (
opts *options.FindOneOptions
model = &modelpkg.User{}
filter = filterFunc(dao.Columns)
)
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
err := dao.Collection.FindOne(ctx, filter, opts).Decode(model)
if err != nil {
if err == mongo.ErrNoDocuments {
return nil, nil
}
return nil, err
}
return model, nil
}
// FindOneByID executes a find command and returns a model for one document in the collection.
func (dao *User) FindOneByID(ctx context.Context, id string, optionsFunc ...UserFindOneOptionsFunc) (*modelpkg.User, error) {
objectID, err := primitive.ObjectIDFromHex(id)
if err != nil {
return nil, err
}
return dao.FindOne(ctx, func(cols *UserColumns) interface{} {
return bson.M{"_id": objectID}
}, optionsFunc...)
}
// FindMany executes a find command and returns many models the matching documents in the collection.
func (dao *User) FindMany(ctx context.Context, filterFunc UserFilterFunc, optionsFunc ...UserFindManyOptionsFunc) ([]*modelpkg.User, error) {
var (
opts *options.FindOptions
filter = filterFunc(dao.Columns)
)
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
cur, err := dao.Collection.Find(ctx, filter, opts)
if err != nil {
return nil, err
}
models := make([]*modelpkg.User, 0)
if err = cur.All(ctx, &models); err != nil {
return nil, err
}
return models, nil
}
// DeleteOne executes a delete command to delete at most one document from the collection.
func (dao *User) DeleteOne(ctx context.Context, filterFunc UserFilterFunc, optionsFunc ...UserDeleteOptionsFunc) (*mongo.DeleteResult, error) {
var (
opts *options.DeleteOptions
filter = filterFunc(dao.Columns)
)
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
return dao.Collection.DeleteOne(ctx, filter, opts)
}
// DeleteOneByID executes a delete command to delete at most one document from the collection.
func (dao *User) DeleteOneByID(ctx context.Context, id string, optionsFunc ...UserDeleteOptionsFunc) (*mongo.DeleteResult, error) {
objectID, err := primitive.ObjectIDFromHex(id)
if err != nil {
return nil, err
}
return dao.DeleteOne(ctx, func(cols *UserColumns) interface{} {
return bson.M{"_id": objectID}
}, optionsFunc...)
}
// DeleteMany executes a delete command to delete documents from the collection.
func (dao *User) DeleteMany(ctx context.Context, filterFunc UserFilterFunc, optionsFunc ...UserDeleteOptionsFunc) (*mongo.DeleteResult, error) {
var (
opts *options.DeleteOptions
filter = filterFunc(dao.Columns)
)
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
return dao.Collection.DeleteMany(ctx, filter, opts)
}
// autofill when inserting data
func (dao *User) autofill(ctx context.Context, model *modelpkg.User) error {
if model.ID.IsZero() {
model.ID = primitive.NewObjectID()
}
if model.UID == 0 {
if id, err := NewCounter(dao.Database).Incr(ctx, "uid"); err != nil {
return err
} else {
model.UID = int32(id)
}
}
if model.RegisterTime == 0 {
model.RegisterTime = primitive.NewDateTimeFromTime(time.Now())
}
if model.LastLoginTime == 0 {
model.LastLoginTime = primitive.NewDateTimeFromTime(time.Now())
}
return nil
}

View File

@ -0,0 +1,22 @@
package dao
import (
"go.mongodb.org/mongo-driver/mongo"
"mongo.games.com/mongoctl/example/dao/internal"
)
type MailColumns = internal.MailColumns
type Mail struct {
*internal.Mail
}
func NewMail(db *mongo.Database, c *mongo.Collection) *Mail {
v := internal.NewMail(nil)
v.Database = db
v.Collection = c
panic("创建索引")
//c.Indexes().CreateOne()
//c.Indexes().CreateMany()
return &Mail{Mail: v}
}

View File

@ -0,0 +1,22 @@
package dao
import (
"go.mongodb.org/mongo-driver/mongo"
"mongo.games.com/mongoctl/example/dao/internal"
)
type UserColumns = internal.UserColumns
type User struct {
*internal.User
}
func NewUser(db *mongo.Database, c *mongo.Collection) *User {
v := internal.NewUser(nil)
v.Database = db
v.Collection = c
panic("创建索引")
//c.Indexes().CreateOne()
//c.Indexes().CreateMany()
return &User{User: v}
}

View File

@ -0,0 +1,62 @@
package main
import (
"context"
"log"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/mongo/readpref"
"mongo.games.com/mongoctl/example/dao"
"mongo.games.com/mongoctl/example/model"
)
func main() {
var (
uri = "mongodb://root:12345678@127.0.0.1:27017"
opts = options.Client().ApplyURI(uri)
baseCtx = context.Background()
)
ctx, cancel := context.WithTimeout(baseCtx, 5*time.Second)
client, err := mongo.Connect(ctx, opts)
cancel()
if err != nil {
log.Fatalf("connect mongo server failed: %v", err)
}
ctx, cancel = context.WithTimeout(baseCtx, 5*time.Second)
defer cancel()
err = client.Ping(ctx, readpref.Primary())
cancel()
if err != nil {
log.Fatalf("ping mongo server failed: %v", err)
}
db := client.Database("dao_test")
mailDao := dao.NewMail(db, db.Collection("mail"))
_, err = mailDao.InsertOne(baseCtx, &model.Mail{
Title: "mongo-dao-generator introduction",
Content: "the mongo-dao-generator is a tool for automatically generating MongoDB Data Access Object.",
Sender: 1,
Receiver: 2,
Status: 1,
})
if err != nil {
log.Fatalf("failed to insert into mongo database: %v", err)
}
mail, err := mailDao.FindOne(baseCtx, func(cols *dao.MailColumns) interface{} {
return bson.M{cols.Receiver: 2}
})
if err != nil {
log.Fatalf("failed to find a row of data from mongo database: %v", err)
}
log.Printf("%+v", mail)
}

View File

@ -0,0 +1,16 @@
package model
import (
"go.mongodb.org/mongo-driver/bson/primitive"
)
//go:generate mongoctl -model-dir=. -model-names=Mail -dao-dir=../dao/
type Mail struct {
ID primitive.ObjectID `bson:"_id" gen:"autoFill"` // 邮件ID
Title string `bson:"title"` // 邮件标题
Content string `bson:"content"` // 邮件内容
Sender int64 `bson:"sender"` // 邮件发送者
Receiver int64 `bson:"receiver"` // 邮件接受者
Status int `bson:"status"` // 邮件状态
SendTime primitive.DateTime `bson:"send_time" gen:"autoFill"` // 发送时间
}

View File

@ -0,0 +1,61 @@
package model
import "go.mongodb.org/mongo-driver/bson/primitive"
type Gender int
const (
GenderUnknown Gender = iota // 未知
GenderMale // 男性
GenderFemale // 女性
)
// Type 用户类型
type Type int
const (
TypeRobot Type = 0 // 机器人用户
TypeGuest Type = 1 // 游客用户
TypeGeneral Type = 2 // 普通用户
TypeSystem Type = 3 // 系统用户
)
// Status 用户状态
type Status int
const (
StatusNormal Status = iota // 正常
StatusForbidden // 封禁
)
//go:generate mongoctl -model-dir=. -model-names=User -dao-dir=../dao/
type User struct {
ID primitive.ObjectID `bson:"_id" gen:"autoFill"`
UID int32 `bson:"uid" gen:"autoIncr:uid"` // 用户ID
Account string `bson:"account"` // 用户账号
Password string `bson:"password"` // 用户密码
Salt string `bson:"salt"` // 密码
Mobile string `bson:"mobile"` // 用户手机
Email string `bson:"email"` // 用户邮箱
Nickname string `bson:"nickname"` // 用户昵称
Signature string `bson:"signature"` // 用户签名
Gender Gender `bson:"gender"` // 用户性别
Level int `bson:"level"` // 用户等级
Experience int `bson:"experience"` // 用户经验
Coin int `bson:"coin"` // 用户金币
Type Type `bson:"type"` // 用户类型
Status Status `bson:"status"` // 用户状态
DeviceID string `bson:"device_id"` // 设备ID
ThirdPlatforms ThirdPlatforms `bson:"third_platforms"` // 第三方平台
RegisterIP string `bson:"register_ip"` // 注册IP
RegisterTime primitive.DateTime `bson:"register_time" gen:"autoFill"` // 注册时间
LastLoginIP string `bson:"last_login_ip"` // 最近登录IP
LastLoginTime primitive.DateTime `bson:"last_login_time" gen:"autoFill"` // 最近登录时间
}
// ThirdPlatforms 第三方平台
type ThirdPlatforms struct {
Wechat string `bson:"wechat"` // 微信登录openid
Google string `bson:"google"` // 谷歌登录userid
Facebook string `bson:"facebook"` // 脸书登录userid
}

393
tools/mongoctl/generator.go Normal file
View File

@ -0,0 +1,393 @@
package main
import (
"fmt"
"go/ast"
"go/token"
"golang.org/x/tools/go/packages"
"log"
"mongo.games.com/mongoctl/template"
"os"
"path/filepath"
"reflect"
"strings"
)
const (
symbolBacktick = "`"
)
const (
symbolBacktickKey = "SymbolBacktick"
)
const (
varPackagesKey = "VarPackages"
varModelClassNameKey = "VarModelClassName"
varModelPackageNameKey = "VarModelPackageName"
varModelPackagePathKey = "VarModelPackagePath"
varModelVariableNameKey = "VarModelVariableName"
varModelColumnsDefineKey = "VarModelColumnsDefine"
varModelColumnsInstanceKey = "VarModelColumnsInstance"
varDaoClassNameKey = "VarDaoClassName"
varDaoVariableNameKey = "VarDaoVariableName"
varDaoPackageNameKey = "VarDaoPackageName"
varDaoPackagePathKey = "VarDaoPackagePath"
varDaoPrefixNameKey = "VarDaoPrefixName"
varCollectionNameKey = "VarCollectionName"
varAutofillCodeKey = "VarAutofillCode"
)
const defaultCounterName = "Counter"
type options struct {
modelDir string //
modelPkgPath string //
modelPkgAlias string //
modelNames []string //
daoDir string
daoPkgPath string
subPkgEnable bool
subPkgStyle style
counterName string
fileNameStyle style
}
type generator struct {
opts *options
counter *counter
modelNames map[string]struct{}
}
func newGenerator(opts *options) *generator {
modelNames := make(map[string]struct{}, len(opts.modelNames))
for _, modelName := range opts.modelNames {
if isExportable(modelName) { // 是否导出字段
modelNames[modelName] = struct{}{}
}
}
if len(modelNames) == 0 {
log.Fatalf("error: %d model type names found", len(modelNames))
}
if opts.counterName == "" {
opts.counterName = defaultCounterName
}
return &generator{
opts: opts,
counter: newCounter(opts),
modelNames: modelNames,
}
}
func (g *generator) makeDao() {
models := g.parseModels()
for _, m := range models {
g.makeModelInternalDao(m)
g.makeModelExternalDao(m)
fmt.Printf("%s's dao file generated successfully\n", m.modelName)
if !m.isDependCounter {
continue
}
g.makeCounterInternalDao()
g.makeCounterExternalDao()
}
}
// generate an internal dao file based on model
func (g *generator) makeModelInternalDao(m *model) {
replaces := make(map[string]string)
replaces[varModelClassNameKey] = m.modelClassName
replaces[varModelPackageNameKey] = m.modelPkgName
replaces[varModelPackagePathKey] = m.modelPkgPath
replaces[varModelVariableNameKey] = m.modelVariableName
replaces[varDaoPrefixNameKey] = m.daoPrefixName
replaces[varDaoClassNameKey] = m.daoClassName
replaces[varDaoVariableNameKey] = m.daoVariableName
replaces[varCollectionNameKey] = m.collectionName
replaces[varModelColumnsDefineKey] = m.modelColumnsDefined()
replaces[varModelColumnsInstanceKey] = m.modelColumnsInstance()
replaces[varAutofillCodeKey] = m.autoFillCode()
replaces[varPackagesKey] = m.packages()
file := m.daoOutputDir + "/internal/" + m.daoOutputFile
err := doWrite(file, template.InternalTemplate, replaces)
if err != nil {
log.Fatal(err)
}
}
// generate an external dao file based on model
func (g *generator) makeModelExternalDao(m *model) {
file := m.daoOutputDir + "/" + m.daoOutputFile
_, err := os.Stat(file)
if err != nil {
switch {
case os.IsNotExist(err):
// ignore
case os.IsExist(err):
return
default:
log.Fatal(err)
}
} else {
return
}
replaces := make(map[string]string)
replaces[varDaoClassNameKey] = m.daoClassName
replaces[varDaoPrefixNameKey] = m.daoPrefixName
replaces[varDaoPackageNameKey] = m.daoPkgName
replaces[varDaoPackagePathKey] = m.daoPkgPath
err = doWrite(file, template.ExternalTemplate, replaces)
if err != nil {
log.Fatal(err)
}
}
// generate an internal dao file based on counter model
func (g *generator) makeCounterInternalDao() {
replaces := make(map[string]string)
replaces[varDaoClassNameKey] = g.counter.daoClassName
replaces[varDaoPrefixNameKey] = g.counter.daoPrefixName
replaces[varDaoVariableNameKey] = g.counter.daoVariableName
replaces[varCollectionNameKey] = g.counter.collectionName
replaces[symbolBacktickKey] = symbolBacktick
file := g.counter.daoOutputDir + "/internal/" + g.counter.daoOutputFile
err := doWrite(file, template.CounterInternalTemplate, replaces)
if err != nil {
log.Fatal(err)
}
}
// generate an external dao file based on counter model
func (g *generator) makeCounterExternalDao() {
file := g.counter.daoOutputDir + "/" + g.counter.daoOutputFile
_, err := os.Stat(file)
if err != nil {
switch {
case os.IsNotExist(err):
// ignore
case os.IsExist(err):
return
default:
log.Fatal(err)
}
} else {
return
}
replaces := make(map[string]string)
replaces[varDaoClassNameKey] = g.counter.daoClassName
replaces[varDaoPackageNameKey] = g.counter.daoPkgName
replaces[varDaoPackagePathKey] = g.counter.daoPkgPath
err = doWrite(file, template.CounterExternalTemplate, replaces)
if err != nil {
log.Fatal(err)
}
}
// parse multiple models from the go file
func (g *generator) parseModels() []*model {
var (
pkg = g.loadPackage()
models = make([]*model, 0, len(pkg.Syntax))
daoPkgPath = g.opts.daoPkgPath
modelPkgPath = g.opts.modelPkgPath
modelPkgName = g.opts.modelPkgAlias
)
if g.opts.daoPkgPath == "" && pkg.Module != nil {
outPath, err := filepath.Abs(g.opts.daoDir)
if err != nil {
log.Fatal(err)
}
daoPkgPath = pkg.Module.Path + outPath[len(pkg.Module.Dir):]
}
daoPkgPath = strings.ReplaceAll(daoPkgPath, `\`, `/`)
g.counter.setDaoPkgPath(daoPkgPath)
for _, file := range pkg.Syntax {
if g.opts.modelPkgPath == "" && pkg.Module != nil && pkg.Fset != nil {
filePath := filepath.Dir(pkg.Fset.Position(file.Package).Filename)
modelPkgPath = pkg.Module.Path + filePath[len(pkg.Module.Dir):]
}
modelPkgPath = strings.ReplaceAll(modelPkgPath, `\`, `/`)
modelPkgName = file.Name.Name
ast.Inspect(file, func(node ast.Node) bool {
decl, ok := node.(*ast.GenDecl)
if !ok || decl.Tok != token.TYPE {
return true
}
for _, s := range decl.Specs {
spec, ok := s.(*ast.TypeSpec)
if !ok {
continue
}
_, ok = g.modelNames[spec.Name.Name]
if !ok {
continue
}
st, ok := spec.Type.(*ast.StructType)
if !ok {
continue
}
model := newModel(g.opts)
model.setModelName(spec.Name.Name)
model.setModelPkg(modelPkgName, modelPkgPath)
model.setDaoPkgPath(daoPkgPath)
for _, item := range st.Fields.List {
name := item.Names[0].Name
if !isExportable(name) {
continue
}
field := &field{name: name, column: name}
if item.Tag != nil && len(item.Tag.Value) > 2 {
runes := []rune(item.Tag.Value)
if runes[0] != '`' || runes[len(runes)-1] != '`' {
continue
}
tag := reflect.StructTag(runes[1 : len(runes)-1])
if column := tag.Get("bson"); column != "" {
field.column = column
}
val, ok := tag.Lookup("gen")
if ok {
parts := strings.Split(val, ";")
for _, part := range parts {
if part == "" {
continue
}
switch eles := strings.SplitN(part, ":", 2); eles[0] {
case "autoFill":
expr, ok := item.Type.(*ast.SelectorExpr)
if !ok {
continue
}
switch fmt.Sprintf("%s.%s", expr.X.(*ast.Ident).Name, expr.Sel.Name) {
case "primitive.ObjectID":
field.autoFill = objectID
model.addImport(pkg3)
case "primitive.DateTime":
field.autoFill = dateTime
model.addImport(pkg1)
model.addImport(pkg3)
}
case "autoIncr":
if len(eles) != 2 || eles[1] == "" {
continue
}
expr, ok := item.Type.(*ast.Ident)
if !ok {
continue
}
switch expr.Name {
case "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64":
field.autoFill = autoIncr
field.autoIncrFieldName = eles[1]
if g.opts.subPkgEnable {
model.addImport(g.counter.daoPkgPath)
}
switch expr.Name {
case "int":
field.autoIncrFieldKind = reflect.Int
case "int8":
field.autoIncrFieldKind = reflect.Int8
case "int16":
field.autoIncrFieldKind = reflect.Int16
case "int32":
field.autoIncrFieldKind = reflect.Int32
case "int64":
field.autoIncrFieldKind = reflect.Int64
case "uint":
field.autoIncrFieldKind = reflect.Uint
case "uint8":
field.autoIncrFieldKind = reflect.Uint8
case "uint16":
field.autoIncrFieldKind = reflect.Uint16
case "uint32":
field.autoIncrFieldKind = reflect.Uint32
case "uint64":
field.autoIncrFieldKind = reflect.Uint64
}
}
}
}
}
}
if item.Doc != nil {
field.documents = make([]string, 0, len(item.Doc.List))
for _, doc := range item.Doc.List {
field.documents = append(field.documents, doc.Text)
}
}
if item.Comment != nil {
field.comment = item.Comment.List[0].Text
}
model.addFields(field)
}
models = append(models, model)
}
return true
})
}
return models
}
func (g *generator) loadPackage() *packages.Package {
cfg := &packages.Config{
Mode: packages.NeedName | packages.NeedFiles | packages.NeedCompiledGoFiles | packages.NeedImports | packages.NeedTypes | packages.NeedTypesSizes | packages.NeedSyntax | packages.NeedTypesInfo | packages.NeedModule,
Tests: false,
}
pkgs, err := packages.Load(cfg, g.opts.modelDir)
if err != nil {
log.Fatal(err)
}
if len(pkgs) != 1 {
log.Fatalf("error: %d packages found", len(pkgs))
}
return pkgs[0]
}

22
tools/mongoctl/go.mod Normal file
View File

@ -0,0 +1,22 @@
module mongo.games.com/mongoctl
go 1.22.5
require (
go.mongodb.org/mongo-driver v1.17.1
golang.org/x/tools v0.26.0
)
require (
github.com/golang/snappy v0.0.4 // indirect
github.com/klauspost/compress v1.13.6 // indirect
github.com/montanaflynn/stats v0.7.1 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.2 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
golang.org/x/crypto v0.26.0 // indirect
golang.org/x/mod v0.21.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/text v0.17.0 // indirect
)

54
tools/mongoctl/go.sum Normal file
View File

@ -0,0 +1,54 @@
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/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.mongodb.org/mongo-driver v1.17.1 h1:Wic5cJIwJgSpBhe3lx3+/RybR5PiYRMpVFgO7cOHyIM=
go.mongodb.org/mongo-driver v1.17.1/go.mod h1:wwWm/+BuOddhcq3n68LKRmgk2wXzmF6s0SFOa0GINL4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@ -0,0 +1,36 @@
package main
import (
"fmt"
"testing"
)
func TestModule(t *testing.T) {
m := newModel(&options{
modelDir: "./model",
modelPkgPath: "./modelpkgpath",
modelPkgAlias: "test",
modelNames: []string{"names"},
daoDir: "./dao",
daoPkgPath: "./daopkgpath",
subPkgEnable: false,
subPkgStyle: lowerCase,
counterName: "",
fileNameStyle: lowerCase,
})
m.setModelName("mymodel")
m.setModelPkg("mypkg", "mypath")
m.setDaoPkgPath("daopath")
fmt.Println(m.packages())
fmt.Println(m.modelColumnsInstance())
fmt.Println(m.modelColumnsDefined())
fmt.Println(m.autoFillCode())
}
func TestToName(t *testing.T) {
var f func()
f()
}

66
tools/mongoctl/main.go Normal file
View File

@ -0,0 +1,66 @@
package main
import (
"flag"
"fmt"
"log"
"os"
"strings"
)
var (
modelDir = flag.String("model-dir", "", "模型所在目录; 必需")
modelNames = flag.String("model-names", "", "模型结构体名称; 必需")
modelPkgPath = flag.String("model-pkg-path", "", "模型包名; 默认自动生成")
modelPkgAlias = flag.String("model-pkg-alias", "", "模型包别名")
daoDir = flag.String("dao-dir", "", "生成代码所在目录; 必需")
daoPkgPath = flag.String("dao-pkg-path", "", "生成代码所在目录的包名; 默认自动生成")
subPkgEnable = flag.Bool("sub-pkg-enable", false, "每个模型创建一个子包; 默认关闭")
subPkgStyle = flag.String("sub-pkg-style", "kebab", "模型子包目录名称风格; 选项: kebab | underscore | lower | camel | pascal; 默认是 kebab")
counterName = flag.String("counter-name", "", "自增模型名称; 默认是 counter")
fileNameStyle = flag.String("file-style", "underscore", "代码文件名称风格; 选项: kebab | underscore | lower | camel | pascal; 默认是 underscore")
)
// Usage is a replacement usage function for the flags package.
func usage() {
fmt.Fprintf(os.Stderr, "用法 mongoctl:\n")
fmt.Fprintf(os.Stderr, "\tmongoctl [flags] -model-dir=. -model-names=T,T -dao-dir=./dao\n")
fmt.Fprintf(os.Stderr, "Flags:\n")
flag.PrintDefaults()
}
//go:generate mongoctl -type=Mail,User
func main() {
log.SetFlags(0)
log.SetPrefix("mongoctl: ")
flag.Usage = usage
flag.Parse()
if len(*modelDir) == 0 {
flag.Usage()
os.Exit(2)
}
if len(*modelNames) == 0 {
flag.Usage()
os.Exit(2)
}
if len(*daoDir) == 0 {
flag.Usage()
os.Exit(2)
}
newGenerator(&options{
daoDir: *daoDir,
daoPkgPath: *daoPkgPath,
modelDir: *modelDir,
modelNames: strings.Split(*modelNames, ","),
modelPkgPath: *modelPkgPath,
modelPkgAlias: *modelPkgAlias,
subPkgEnable: *subPkgEnable,
subPkgStyle: style(*subPkgStyle),
counterName: *counterName,
fileNameStyle: style(*fileNameStyle),
}).makeDao()
}

273
tools/mongoctl/model.go Normal file
View File

@ -0,0 +1,273 @@
package main
import (
"fmt"
"path/filepath"
"reflect"
"sort"
"strings"
)
const (
defaultModelPkgAlias = "modelpkg"
defaultModelVariableName = "model"
)
type autoFill int
const (
objectID autoFill = iota + 1 // primitive.NewObjectID()
dateTime // primitive.NewDateTimeFromTime(time.Now())
autoIncr // auto-increment
)
const (
pkg1 = "time"
pkg2 = "context"
pkg3 = "go.mongodb.org/mongo-driver/bson/primitive"
pkg4 = "go.mongodb.org/mongo-driver/mongo"
pkg5 = "go.mongodb.org/mongo-driver/mongo/options"
pkg6 = "errors"
pkg7 = "go.mongodb.org/mongo-driver/bson"
)
type field struct {
name string
column string
comment string
documents []string
autoFill autoFill
autoIncrFieldName string
autoIncrFieldKind reflect.Kind
}
type model struct {
opts *options
fields []*field
imports map[string]string
modelName string
modelClassName string
modelVariableName string
modelPkgPath string
modelPkgName string
daoClassName string
daoVariableName string
daoPkgPath string
daoPkgName string
daoOutputDir string
daoOutputFile string
daoPrefixName string
collectionName string
fieldNameMaxLen int
fieldComplexMaxLen int
isDependCounter bool
}
func newModel(opts *options) *model {
m := &model{
opts: opts,
fields: make([]*field, 0),
imports: make(map[string]string, 8),
}
m.addImport(pkg2)
m.addImport(pkg4)
m.addImport(pkg5)
m.addImport(pkg6)
m.addImport(pkg7)
return m
}
func (m *model) setModelName(name string) {
m.modelName = name
m.modelClassName = toPascalCase(m.modelName)
m.modelVariableName = toCamelCase(m.modelName)
m.daoClassName = toPascalCase(m.modelName)
m.daoVariableName = toCamelCase(m.modelName)
m.daoOutputFile = fmt.Sprintf("%s.go", toFileName(m.modelName, m.opts.fileNameStyle))
m.collectionName = toUnderscoreCase(m.modelName)
dir := strings.TrimSuffix(m.opts.daoDir, "/")
if m.opts.subPkgEnable {
m.daoOutputDir = dir + "/" + toPackagePath(m.modelName, m.opts.subPkgStyle)
} else {
m.daoOutputDir = dir
m.daoPrefixName = toPascalCase(m.modelName)
}
}
func (m *model) setModelPkg(name, path string) {
m.modelPkgPath = path
if m.opts.modelPkgAlias != "" {
m.modelPkgName = m.opts.modelPkgAlias
m.addImport(m.modelPkgPath, m.modelPkgName)
} else {
m.modelPkgName = name
m.addImport(m.modelPkgPath)
}
if m.modelPkgName == defaultModelVariableName {
m.modelPkgName = defaultModelPkgAlias
m.addImport(m.modelPkgPath, m.modelPkgName)
}
}
func (m *model) setDaoPkgPath(path string) {
if m.opts.subPkgEnable {
m.daoPkgPath = path + "/" + toPackagePath(m.modelName, m.opts.subPkgStyle)
} else {
m.daoPkgPath = path
}
m.daoPkgName = toPackageName(filepath.Base(m.daoPkgPath))
}
func (m *model) addImport(pkg string, alias ...string) {
if len(alias) > 0 {
m.imports[pkg] = alias[0]
} else {
m.imports[pkg] = ""
}
}
func (m *model) addFields(fields ...*field) {
for _, f := range fields {
if l := len(f.name); l > m.fieldNameMaxLen {
m.fieldNameMaxLen = l
}
if l := len(f.name) + len(f.column) + 5; l > m.fieldComplexMaxLen {
m.fieldComplexMaxLen = l
}
if f.autoFill == autoIncr {
m.isDependCounter = true
}
}
m.fields = append(m.fields, fields...)
}
func (m *model) modelColumnsDefined() (str string) {
for i, f := range m.fields {
str += fmt.Sprintf("\t%s%s%s %s", f.name, strings.Repeat(" ", m.fieldNameMaxLen-len(f.name)+1), "string", f.comment)
if i != len(m.fields)-1 {
str += "\n"
}
}
str = strings.TrimPrefix(str, "\t")
return
}
func (m *model) modelColumnsInstance() (str string) {
for i, f := range m.fields {
s := fmt.Sprintf("%s:%s\"%s\",", f.name, strings.Repeat(" ", m.fieldNameMaxLen-len(f.name)+1), f.column)
s += strings.Repeat(" ", m.fieldComplexMaxLen-len(s)+1) + f.comment
str += "\t" + s
if i != len(m.fields)-1 {
str += "\n"
}
}
str = strings.TrimLeft(str, "\t")
return
}
func (m *model) packages() (str string) {
packages := make([]string, 0, len(m.imports))
for pkg := range m.imports {
packages = append(packages, pkg)
}
sort.Slice(packages, func(i, j int) bool {
return packages[i] < packages[j]
})
for _, pkg := range packages {
if alias := m.imports[pkg]; alias != "" {
str += fmt.Sprintf("\t%s \"%s\"\n", alias, pkg)
} else {
str += fmt.Sprintf("\t\"%s\"\n", pkg)
}
}
str = strings.TrimPrefix(str, "\t")
str = strings.TrimSuffix(str, "\n")
return
}
func (m *model) autoFillCode() (str string) {
var (
counterName = toPascalCase(m.opts.counterName)
counterPkgPrefix string
)
if m.opts.subPkgEnable {
counterPkgPrefix = fmt.Sprintf("%s.", toPackageName(counterName))
}
for _, f := range m.fields {
if f.autoFill == 0 {
continue
}
if str != "" {
str += "\n\n"
}
switch f.autoFill {
case objectID:
str += fmt.Sprintf("\tif model.%s.IsZero() {\n", f.name)
str += fmt.Sprintf("\t\tmodel.%s = primitive.NewObjectID()\n", f.name)
str += "\t}"
case dateTime:
str += fmt.Sprintf("\tif model.%s == 0 {\n", f.name)
str += fmt.Sprintf("\t\tmodel.%s = primitive.NewDateTimeFromTime(time.Now())\n", f.name)
str += "\t}"
case autoIncr:
str += fmt.Sprintf("\tif model.%s == 0 {\n", f.name)
str += fmt.Sprintf("\t\tif id, err := %sNew%s(dao.Database).Incr(ctx, \"%s\"); err != nil {\n", counterPkgPrefix, counterName, f.autoIncrFieldName)
str += "\t\t\treturn err\n"
str += "\t\t} else {\n"
switch f.autoIncrFieldKind {
case reflect.Int:
str += fmt.Sprintf("\t\t\tmodel.%s = int(id)\n", f.name)
case reflect.Int8:
str += fmt.Sprintf("\t\t\tmodel.%s = int8(id)\n", f.name)
case reflect.Int16:
str += fmt.Sprintf("\t\t\tmodel.%s = int16(id)\n", f.name)
case reflect.Int32:
str += fmt.Sprintf("\t\t\tmodel.%s = int32(id)\n", f.name)
case reflect.Int64:
str += fmt.Sprintf("\t\t\tmodel.%s = id\n", f.name)
case reflect.Uint:
str += fmt.Sprintf("\t\t\tmodel.%s = uint(id)\n", f.name)
case reflect.Uint8:
str += fmt.Sprintf("\t\t\tmodel.%s = uint8(id)\n", f.name)
case reflect.Uint16:
str += fmt.Sprintf("\t\t\tmodel.%s = uint16(id)\n", f.name)
case reflect.Uint32:
str += fmt.Sprintf("\t\t\tmodel.%s = uint32(id)\n", f.name)
case reflect.Uint64:
str += fmt.Sprintf("\t\t\tmodel.%s = uint64(id)\n", f.name)
}
str += "\t\t}\n"
str += "\t}"
}
}
if str != "" {
str += "\n\n"
}
str += "\treturn nil"
str = strings.TrimPrefix(str, "\t")
return
}

View File

@ -0,0 +1,97 @@
package template
const CounterExternalTemplate = `
package ${VarDaoPackageName}
import (
"go.mongodb.org/mongo-driver/mongo"
"${VarDaoPackagePath}/internal"
)
type ${VarDaoClassName} struct {
*internal.${VarDaoClassName}
}
func New${VarDaoClassName}(db *mongo.Database) *${VarDaoClassName} {
return &${VarDaoClassName}{${VarDaoClassName}: internal.New${VarDaoClassName}(db)}
}
`
const CounterInternalTemplate = `
// --------------------------------------------------------------------------------------------------
// The following code is automatically generated by the mongo-dao-generator tool.
// Please do not modify this code manually to avoid being overwritten in the next generation.
// For more tool details, please click the link to view https://github.com/dobyte/mongo-dao-generator
// --------------------------------------------------------------------------------------------------
package internal
import (
"context"
"errors"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
type ${VarDaoClassName} struct {
Columns *${VarDaoPrefixName}Columns
Database *mongo.Database
Collection *mongo.Collection
}
type ${VarDaoPrefixName}Model struct {
ID string ${SymbolBacktick}bson:"_id"${SymbolBacktick}
Value int64 ${SymbolBacktick}bson:"value"${SymbolBacktick}
}
type ${VarDaoPrefixName}Columns struct {
ID string
Value string
}
var ${VarDaoVariableName}Columns = &${VarDaoPrefixName}Columns{
ID: "_id",
Value: "value",
}
func New${VarDaoClassName}(db *mongo.Database) *${VarDaoClassName} {
return &${VarDaoClassName}{
Columns: ${VarDaoVariableName}Columns,
Database: db,
Collection: db.Collection("${VarCollectionName}"),
}
}
// Incr 自增值
func (dao *${VarDaoClassName}) Incr(ctx context.Context, key string, incr ...int) (int64, error) {
var (
upsert = true
returnDocument = options.After
counter = &${VarDaoPrefixName}Model{}
value = 1
)
if len(incr) > 0 {
if incr[0] == 0 {
return 0, errors.New("invalid increment value")
}
value = incr[0]
}
rst := dao.Collection.FindOneAndUpdate(ctx, bson.M{
dao.Columns.ID: key,
}, bson.M{"$inc": bson.M{
dao.Columns.Value: value,
}}, &options.FindOneAndUpdateOptions{
Upsert: &upsert,
ReturnDocument: &returnDocument,
})
if err := rst.Decode(counter); err != nil {
return 0, err
}
return counter.Value, nil
}
`

View File

@ -0,0 +1,292 @@
package template
const ExternalTemplate = `
package ${VarDaoPackageName}
import (
"go.mongodb.org/mongo-driver/mongo"
"${VarDaoPackagePath}/internal"
)
type ${VarDaoPrefixName}Columns = internal.${VarDaoPrefixName}Columns
type ${VarDaoClassName} struct {
*internal.${VarDaoClassName}
}
func New${VarDaoClassName}(db *mongo.Database, c *mongo.Collection) *${VarDaoClassName} {
v := internal.New${VarDaoClassName}(nil)
v.Database = db
v.Collection = c
panic("创建索引")
//c.Indexes().CreateOne()
//c.Indexes().CreateMany()
return &${VarDaoClassName}{${VarDaoClassName}: v}
}
`
const InternalTemplate = `
// --------------------------------------------------------------------------------------------
// The following code is automatically generated by the mongo-dao-generator tool.
// Please do not modify this code manually to avoid being overwritten in the next generation.
// For more tool details, please click the link to view https://github.com/dobyte/mongo-dao-generator
// --------------------------------------------------------------------------------------------
package internal
import (
${VarPackages}
)
type ${VarDaoPrefixName}FilterFunc func(cols *${VarDaoPrefixName}Columns) interface{}
type ${VarDaoPrefixName}UpdateFunc func(cols *${VarDaoPrefixName}Columns) interface{}
type ${VarDaoPrefixName}PipelineFunc func(cols *${VarDaoPrefixName}Columns) interface{}
type ${VarDaoPrefixName}CountOptionsFunc func(cols *${VarDaoPrefixName}Columns) *options.CountOptions
type ${VarDaoPrefixName}AggregateOptionsFunc func(cols *${VarDaoPrefixName}Columns) *options.AggregateOptions
type ${VarDaoPrefixName}FindOneOptionsFunc func(cols *${VarDaoPrefixName}Columns) *options.FindOneOptions
type ${VarDaoPrefixName}FindManyOptionsFunc func(cols *${VarDaoPrefixName}Columns) *options.FindOptions
type ${VarDaoPrefixName}UpdateOptionsFunc func(cols *${VarDaoPrefixName}Columns) *options.UpdateOptions
type ${VarDaoPrefixName}DeleteOptionsFunc func(cols *${VarDaoPrefixName}Columns) *options.DeleteOptions
type ${VarDaoPrefixName}InsertOneOptionsFunc func(cols *${VarDaoPrefixName}Columns) *options.InsertOneOptions
type ${VarDaoPrefixName}InsertManyOptionsFunc func(cols *${VarDaoPrefixName}Columns) *options.InsertManyOptions
type ${VarDaoClassName} struct {
Columns *${VarDaoPrefixName}Columns
Database *mongo.Database
Collection *mongo.Collection
}
type ${VarDaoPrefixName}Columns struct {
${VarModelColumnsDefine}
}
var ${VarDaoVariableName}Columns = &${VarDaoPrefixName}Columns{
${VarModelColumnsInstance}
}
func New${VarDaoClassName}(db *mongo.Database) *${VarDaoClassName} {
return &${VarDaoClassName}{
Columns: ${VarDaoVariableName}Columns,
Database: db,
Collection: db.Collection("${VarCollectionName}"),
}
}
// Count returns the number of documents in the collection.
func (dao *${VarDaoClassName}) Count(ctx context.Context, filterFunc ${VarDaoPrefixName}FilterFunc, optionsFunc ...${VarDaoPrefixName}CountOptionsFunc) (int64, error) {
var (
opts *options.CountOptions
filter = filterFunc(dao.Columns)
)
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
return dao.Collection.CountDocuments(ctx, filter, opts)
}
// Aggregate executes an aggregate command against the collection and returns a cursor over the resulting documents.
func (dao *${VarDaoClassName}) Aggregate(ctx context.Context, pipelineFunc ${VarDaoPrefixName}PipelineFunc, optionsFunc ...${VarDaoPrefixName}AggregateOptionsFunc) (*mongo.Cursor, error) {
var (
opts *options.AggregateOptions
pipeline = pipelineFunc(dao.Columns)
)
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
return dao.Collection.Aggregate(ctx, pipeline, opts)
}
// InsertOne executes an insert command to insert a single document into the collection.
func (dao *${VarDaoClassName}) InsertOne(ctx context.Context, model *${VarModelPackageName}.${VarModelClassName}, optionsFunc ...${VarDaoPrefixName}InsertOneOptionsFunc) (*mongo.InsertOneResult, error) {
if model == nil {
return nil, errors.New("model is nil")
}
if err := dao.autofill(ctx, model); err != nil {
return nil, err
}
var opts *options.InsertOneOptions
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
return dao.Collection.InsertOne(ctx, model, opts)
}
// InsertMany executes an insert command to insert multiple documents into the collection.
func (dao *${VarDaoClassName}) InsertMany(ctx context.Context, models []*${VarModelPackageName}.${VarModelClassName}, optionsFunc ...${VarDaoPrefixName}InsertManyOptionsFunc) (*mongo.InsertManyResult, error) {
if len(models) == 0 {
return nil, errors.New("models is empty")
}
documents := make([]interface{}, 0, len(models))
for i := range models {
model := models[i]
if err := dao.autofill(ctx, model); err != nil {
return nil, err
}
documents = append(documents, model)
}
var opts *options.InsertManyOptions
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
return dao.Collection.InsertMany(ctx, documents, opts)
}
// UpdateOne executes an update command to update at most one document in the collection.
func (dao *${VarDaoClassName}) UpdateOne(ctx context.Context, filterFunc ${VarDaoPrefixName}FilterFunc, updateFunc ${VarDaoPrefixName}UpdateFunc, optionsFunc ...${VarDaoPrefixName}UpdateOptionsFunc) (*mongo.UpdateResult, error) {
var (
opts *options.UpdateOptions
filter = filterFunc(dao.Columns)
update = updateFunc(dao.Columns)
)
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
return dao.Collection.UpdateOne(ctx, filter, update, opts)
}
// UpdateOneByID executes an update command to update at most one document in the collection.
func (dao *${VarDaoClassName}) UpdateOneByID(ctx context.Context, id string, updateFunc ${VarDaoPrefixName}UpdateFunc, optionsFunc ...${VarDaoPrefixName}UpdateOptionsFunc) (*mongo.UpdateResult, error) {
objectID, err := primitive.ObjectIDFromHex(id)
if err != nil {
return nil, err
}
return dao.UpdateOne(ctx, func(cols *${VarDaoPrefixName}Columns) interface{} {
return bson.M{"_id": objectID}
}, updateFunc, optionsFunc...)
}
// UpdateMany executes an update command to update documents in the collection.
func (dao *${VarDaoClassName}) UpdateMany(ctx context.Context, filterFunc ${VarDaoPrefixName}FilterFunc, updateFunc ${VarDaoPrefixName}UpdateFunc, optionsFunc ...${VarDaoPrefixName}UpdateOptionsFunc) (*mongo.UpdateResult, error) {
var (
opts *options.UpdateOptions
filter = filterFunc(dao.Columns)
update = updateFunc(dao.Columns)
)
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
return dao.Collection.UpdateMany(ctx, filter, update, opts)
}
// FindOne executes a find command and returns a model for one document in the collection.
func (dao *${VarDaoClassName}) FindOne(ctx context.Context, filterFunc ${VarDaoPrefixName}FilterFunc, optionsFunc ...${VarDaoPrefixName}FindOneOptionsFunc) (*${VarModelPackageName}.${VarModelClassName}, error) {
var (
opts *options.FindOneOptions
model = &${VarModelPackageName}.${VarModelClassName}{}
filter = filterFunc(dao.Columns)
)
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
err := dao.Collection.FindOne(ctx, filter, opts).Decode(model)
if err != nil {
if err == mongo.ErrNoDocuments {
return nil, nil
}
return nil, err
}
return model, nil
}
// FindOneByID executes a find command and returns a model for one document in the collection.
func (dao *${VarDaoClassName}) FindOneByID(ctx context.Context, id string, optionsFunc ...${VarDaoPrefixName}FindOneOptionsFunc) (*${VarModelPackageName}.${VarModelClassName}, error) {
objectID, err := primitive.ObjectIDFromHex(id)
if err != nil {
return nil, err
}
return dao.FindOne(ctx, func(cols *${VarDaoPrefixName}Columns) interface{} {
return bson.M{"_id": objectID}
}, optionsFunc...)
}
// FindMany executes a find command and returns many models the matching documents in the collection.
func (dao *${VarDaoClassName}) FindMany(ctx context.Context, filterFunc ${VarDaoPrefixName}FilterFunc, optionsFunc ...${VarDaoPrefixName}FindManyOptionsFunc) ([]*${VarModelPackageName}.${VarModelClassName}, error) {
var (
opts *options.FindOptions
filter = filterFunc(dao.Columns)
)
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
cur, err := dao.Collection.Find(ctx, filter, opts)
if err != nil {
return nil, err
}
models := make([]*${VarModelPackageName}.${VarModelClassName}, 0)
if err = cur.All(ctx, &models); err != nil {
return nil, err
}
return models, nil
}
// DeleteOne executes a delete command to delete at most one document from the collection.
func (dao *${VarDaoClassName}) DeleteOne(ctx context.Context, filterFunc ${VarDaoPrefixName}FilterFunc, optionsFunc ...${VarDaoPrefixName}DeleteOptionsFunc) (*mongo.DeleteResult, error) {
var (
opts *options.DeleteOptions
filter = filterFunc(dao.Columns)
)
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
return dao.Collection.DeleteOne(ctx, filter, opts)
}
// DeleteOneByID executes a delete command to delete at most one document from the collection.
func (dao *${VarDaoClassName}) DeleteOneByID(ctx context.Context, id string, optionsFunc ...${VarDaoPrefixName}DeleteOptionsFunc) (*mongo.DeleteResult, error) {
objectID, err := primitive.ObjectIDFromHex(id)
if err != nil {
return nil, err
}
return dao.DeleteOne(ctx, func(cols *${VarDaoPrefixName}Columns) interface{} {
return bson.M{"_id": objectID}
}, optionsFunc...)
}
// DeleteMany executes a delete command to delete documents from the collection.
func (dao *${VarDaoClassName}) DeleteMany(ctx context.Context, filterFunc ${VarDaoPrefixName}FilterFunc, optionsFunc ...${VarDaoPrefixName}DeleteOptionsFunc) (*mongo.DeleteResult, error) {
var (
opts *options.DeleteOptions
filter = filterFunc(dao.Columns)
)
if len(optionsFunc) > 0 {
opts = optionsFunc[0](dao.Columns)
}
return dao.Collection.DeleteMany(ctx, filter, opts)
}
// autofill when inserting data
func (dao *${VarDaoClassName}) autofill(ctx context.Context, model *${VarModelPackageName}.${VarModelClassName}) error {
${VarAutofillCode}
}
`

160
tools/mongoctl/util.go Normal file
View File

@ -0,0 +1,160 @@
package main
import (
"os"
"path/filepath"
"strings"
"unicode"
"unicode/utf8"
)
type style string
const (
kebabCase style = "kebab" // 全小写中划线
underscoreCase style = "underscore" // 小写下划线
camelCase style = "camel" // 小写字母开头驼峰
pascalCase style = "pascal" // 大写字母开头驼峰
lowerCase style = "lower" // 全小写
)
// convert to underscore style, example: UserProfile > user_profile
func toUnderscoreCase(s string) string {
return toLowerCase(s, 95)
}
// convert to kebab style, example: UserProfile > user-profile
func toKebabCase(s string) string {
return toLowerCase(s, 45)
}
// convert to camel style, example: user-profile > userProfile
func toCamelCase(s string) string {
chars := make([]rune, 0, len(s))
upper := false
first := true
for i := 0; i < len(s); i++ {
switch {
case s[i] >= 65 && s[i] <= 90:
if first {
chars = append(chars, rune(s[i]+32))
} else {
chars = append(chars, rune(s[i]))
}
first = false
upper = false
case s[i] >= 97 && s[i] <= 122:
if upper && !first {
chars = append(chars, rune(s[i]-32))
} else {
chars = append(chars, rune(s[i]))
}
first = false
upper = false
case s[i] == 45:
upper = true
case s[i] == 95:
upper = true
}
}
return string(chars)
}
// convert to pascal style, example: user-profile > UserProfile
func toPascalCase(s string) string {
s = toCamelCase(s)
return strings.ToUpper(string(s[0])) + s[1:]
}
func toLowerCase(s string, c rune) string {
chars := make([]rune, 0)
for i := 0; i < len(s); i++ {
if s[i] >= 65 && s[i] <= 90 {
if i == 0 {
chars = append(chars, rune(s[i]+32))
} else {
chars = append(chars, c, rune(s[i]+32))
}
} else {
chars = append(chars, rune(s[i]))
}
}
return string(chars)
}
func toPackageName(s string) string {
chars := make([]rune, 0, len(s))
for i := 0; i < len(s); i++ {
switch {
case s[i] >= 65 && s[i] <= 90:
chars = append(chars, rune(s[i]+32))
case s[i] >= 97 && s[i] <= 122:
chars = append(chars, rune(s[i]))
}
}
return string(chars)
}
func toPackagePath(s string, style style) string {
switch style {
case kebabCase:
return toKebabCase(s)
case underscoreCase:
return toUnderscoreCase(s)
case camelCase:
return toCamelCase(s)
case pascalCase:
return toPascalCase(s)
case lowerCase:
return toPackageName(s)
default:
return toKebabCase(s)
}
}
func toFileName(s string, style style) string {
switch style {
case kebabCase:
return toKebabCase(s)
case underscoreCase:
return toUnderscoreCase(s)
case camelCase:
return toCamelCase(s)
case pascalCase:
return toPascalCase(s)
case lowerCase:
return toPackageName(s)
default:
return toUnderscoreCase(s)
}
}
func doWrite(file string, tpl string, replaces map[string]string) error {
s := os.Expand(tpl, func(s string) string {
switch {
case len(s) >= 3 && s[:3] == "Var":
return replaces[s]
case len(s) >= 6 && s[:6] == "Symbol":
return replaces[s]
default:
return "$" + s
}
})
if err := os.MkdirAll(filepath.Dir(file), os.ModePerm); err != nil {
return err
}
return os.WriteFile(file, []byte(strings.TrimPrefix(s, "\n")), os.ModePerm)
}
// 大写字母开头的字段才是导出的
func isExportable(s string) bool {
r, _ := utf8.DecodeRuneInString(s)
return unicode.IsUpper(r)
}

View File

@ -1,6 +1,8 @@
package main
import (
"fmt"
"strings"
"time"
"mongo.games.com/goserver/core/logger"
@ -13,7 +15,7 @@ type SaveTaskHandler interface {
var SaverSliceNumber = 600
var DbSaver_Inst = &DbSaver{
var DbSaveInst = &DbSaver{
Tick: int32(SaverSliceNumber),
index: 0,
init: false,
@ -31,6 +33,18 @@ type DbSaver struct {
pool map[SaveTaskHandler]*SaverArray
}
func (this *DbSaver) String() string {
buf := strings.Builder{}
buf.WriteString("DbSaver:\n")
buf.WriteString(fmt.Sprintf("Tick: %v\n", this.Tick))
buf.WriteString(fmt.Sprintf("List: %v\n", len(this.list)))
buf.WriteString(fmt.Sprintf("Queue: %v\n", len(this.queue)))
for k, v := range this.queue {
buf.WriteString(fmt.Sprintf("q%v: %v\n", k, len(v.queue)))
}
return buf.String()
}
// pushBalanceSaverArray 向队列中添加SaveTaskHandler
func (this *DbSaver) pushBalanceSaverArray(sth SaveTaskHandler) {
if sth == nil {
@ -64,8 +78,8 @@ func (this *DbSaver) RegisterDbSaverTask(i interface{}) {
}
}
// UnregisteDbSaveTask 从队列中移除SaveTaskHandler
func (this *DbSaver) UnregisteDbSaveTask(i interface{}) {
// UnregisterDbSaveTask 从队列中移除SaveTaskHandler
func (this *DbSaver) UnregisterDbSaveTask(i interface{}) {
if sth, ok := i.(SaveTaskHandler); ok {
if arr, exist := this.pool[sth]; exist {
delete(this.pool, sth)
@ -144,5 +158,5 @@ func (this *DbSaver) Shutdown() {
// 注册模块
func init() {
module.RegisteModule(DbSaver_Inst, time.Second, 0)
module.RegisteModule(DbSaveInst, time.Second, 0)
}

View File

@ -176,6 +176,9 @@ func (this *Player) init() bool {
if this.VCardCost < 0 {
this.VCardCost = 0
}
if this.GuideData == nil {
this.GuideData = make(map[int32]int32)
}
this.InitRolesAndPets()
return true
}
@ -1376,7 +1379,7 @@ func (this *Player) OnLogouted() {
// 更新数据库
logger.Logger.Tracef("###%v unmount from DBSaver[DelPlayer]", this.Name)
DbSaver_Inst.UnregisteDbSaveTask(this)
DbSaveInst.UnregisterDbSaveTask(this)
this.Save(true)
}
@ -1415,7 +1418,8 @@ func (this *Player) UnmarshalData(data []byte, scene *Scene) {
// PlayerInfo 同步
info := PlayerInfoMgrSingle.Players[this.SnId]
if info == nil {
PlayerInfoMgrSingle.Players[this.SnId] = &PlayerInfo{}
info = &PlayerInfo{}
PlayerInfoMgrSingle.Players[this.SnId] = info
}
info.GameData = pd.GameData
@ -4534,7 +4538,7 @@ func (this *Player) GetSkillAdd(id int32) int32 {
func (this *Player) SCGuide() {
cfg := PlatformMgrSingleton.GetConfig(this.Platform).GuideConfig
pack := &playerproto.SCGuideConfig{}
for _, data := range cfg.Info {
for _, data := range cfg.GetInfo() {
var awards []*playerproto.ItemInfo
for _, award := range data.Awards {
item := &playerproto.ItemInfo{

View File

@ -207,7 +207,7 @@ func (c *PlayerCacheMgr) Update() {
c.DbSaver.Update()
for _, p := range c.playerWaitClr {
delete(c.playerMap, p.SnId)
c.UnregisteDbSaveTask(p)
c.UnregisterDbSaveTask(p)
// 释放玩家数据
if PlayerMgrSington.GetPlayerBySnId(p.SnId) == nil {

View File

@ -144,9 +144,9 @@ func (this *PlayerMgr) AddPlayer(sid int64, playerInfo *model.PlayerData, s *net
logger.Logger.Tracef("###%v mount to DBSaver[AddPlayer]", player.Name)
if oldp != nil { //删除旧的玩家
DbSaver_Inst.UnregisteDbSaveTask(oldp)
DbSaveInst.UnregisterDbSaveTask(oldp)
}
DbSaver_Inst.RegisterDbSaverTask(player)
DbSaveInst.RegisterDbSaverTask(player)
niceIdMgr.NiceIdCheck(player.SnId)
} else {
player.NiceId = niceIdMgr.PopNiceId(player.SnId)

View File

@ -1095,8 +1095,8 @@ func (this *ShopMgr) Exchange(param *ExchangeParam) {
}
BagMgrSingleton.AddItemsOffline(&model.AddItemParam{
Platform: p.Platform,
SnId: p.SnId,
Platform: param.Platform,
SnId: param.SnId,
Change: itemInfo2,
GainWay: common.GainWay_Exchange,
Operator: "system",

View File

@ -2750,8 +2750,8 @@ func init() {
} else {
pack.Tag = webapiproto.TagCode_SUCCESS
pack.Msg = "登录成功"
pack.Platform = ls.acc.Platform
pack.SnId = ls.acc.SnId
pack.Platform = acc.Platform
pack.SnId = acc.SnId
}
}
@ -2795,10 +2795,10 @@ func init() {
// 离线获取
var address []string
task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
address, _ = model.GetPlayerAddress(msg.GetPlatform(), msg.GetSnId())
address, err = model.GetPlayerAddress(msg.GetPlatform(), msg.GetSnId())
return nil
}), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) {
if address == nil {
if err != nil {
pack.Tag = webapiproto.TagCode_FAILED
pack.Msg = "获取失败"
} else {
@ -2970,6 +2970,7 @@ func init() {
CallBackFunc: func(code shop.OpResultCode) {
pack.Tag = webapiproto.ExchangeCreateCode(code)
pack.Id = msg.GetId()
logger.Logger.Tracef("/api/game/exchange_create return %v", pack)
tNode.TransRep.RetFiels = pack
tNode.Resume()
},