diff --git a/app/app.go b/app/app.go index ec22b74..102b372 100644 --- a/app/app.go +++ b/app/app.go @@ -21,7 +21,7 @@ func (p *Program) Init(_ svc.Environment) error { return err } p.moduleList = append(p.moduleList, base) - p.moduleList = append(p.moduleList, (&module.DB{}).Bind(config.Get().DB)) + p.moduleList = append(p.moduleList, (&module.DB{}).Bind(config.Get().DB, config.Get().App.Name)) p.moduleList = append(p.moduleList, (&module.Grpc{}).Bind(grpc_server.NewServer(config.Get().Serve.Grpc))) p.moduleList = append(p.moduleList, &ModuleTimer{}) p.moduleList = append(p.moduleList, (&module.Prometheus{}).Bind(config.Get().Metric)) diff --git a/config/config.dev.yaml b/config/config.dev.yaml index 53976ef..19bb03a 100644 --- a/config/config.dev.yaml +++ b/config/config.dev.yaml @@ -31,7 +31,6 @@ db: password: "lQ7aM8oB6lK0iD5k" db: 0 kafka: - groupID: "qgdzs" brokers: [ "127.0.0.1:9092" ] serve: diff --git a/go.mod b/go.mod index db4ee1d..e8f9d06 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,7 @@ module git.hlsq.asia/mmorpg/service-qgdzs go 1.24.0 require ( - git.hlsq.asia/mmorpg/service-common v0.0.0-20260125091651-f22d22cbdc57 - github.com/IBM/sarama v1.46.3 + git.hlsq.asia/mmorpg/service-common v0.0.0-20260130025300-427fca7ed19f github.com/judwhite/go-svc v1.2.1 github.com/robfig/cron/v3 v3.0.1 google.golang.org/grpc v1.77.0 @@ -15,6 +14,7 @@ require ( require ( filippo.io/edwards25519 v1.1.0 // indirect + github.com/IBM/sarama v1.46.3 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bwmarrin/snowflake v0.3.0 // indirect github.com/bytedance/gopkg v0.1.3 // indirect diff --git a/go.sum b/go.sum index e786cfb..dcd759a 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= -git.hlsq.asia/mmorpg/service-common v0.0.0-20260125091651-f22d22cbdc57 h1:u+BKY8YAm1gA9EL3Tw3KfzKRmZ3TgBMkFQurltLl438= -git.hlsq.asia/mmorpg/service-common v0.0.0-20260125091651-f22d22cbdc57/go.mod h1:mMhZcumphj6gaVTppVYsMTkd+5HupmQgAc53Pd4MH9I= +git.hlsq.asia/mmorpg/service-common v0.0.0-20260130025300-427fca7ed19f h1:U0HnB8i3ZTKrg3XBQpwFcI64LB6mraj02yNAqV3MBws= +git.hlsq.asia/mmorpg/service-common v0.0.0-20260130025300-427fca7ed19f/go.mod h1:mMhZcumphj6gaVTppVYsMTkd+5HupmQgAc53Pd4MH9I= github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0= github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/IBM/sarama v1.46.3 h1:njRsX6jNlnR+ClJ8XmkO+CM4unbrNr/2vB5KK6UA+IE= diff --git a/internal/dao/model/point_card.gen.go b/internal/dao/model/point_card.gen.go new file mode 100644 index 0000000..96dd357 --- /dev/null +++ b/internal/dao/model/point_card.gen.go @@ -0,0 +1,38 @@ +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. + +package model + +import ( + "time" + + "git.hlsq.asia/mmorpg/service-common/utils" + "gorm.io/gorm" +) + +const TableNamePointCard = "point_card" + +// PointCard mapped from table +type PointCard struct { + ID uint64 `gorm:"column:id;primaryKey;autoIncrement:true" json:"id"` + Sn string `gorm:"column:sn;not null;comment:业务唯一编号" json:"sn"` // 业务唯一编号 + UserSn string `gorm:"column:user_sn;not null;comment:用户-唯一编号" json:"user_sn"` // 用户-唯一编号 + Point int64 `gorm:"column:point;not null;comment:积分" json:"point"` // 积分 + CreatedAt time.Time `gorm:"column:created_at;not null" json:"created_at"` + UpdatedAt time.Time `gorm:"column:updated_at;not null" json:"updated_at"` + DeletedAt gorm.DeletedAt `gorm:"column:deleted_at" json:"deleted_at"` +} + +// TableName PointCard's table name +func (*PointCard) TableName() string { + return TableNamePointCard +} + +// Auto sn +func (m *PointCard) BeforeCreate(_ *gorm.DB) error { + if m.Sn == "" { + m.Sn = utils.SnowflakeInstance().Generate().String() + } + return nil +} diff --git a/internal/dao/model/point_records.gen.go b/internal/dao/model/point_records.gen.go new file mode 100644 index 0000000..08ca56b --- /dev/null +++ b/internal/dao/model/point_records.gen.go @@ -0,0 +1,46 @@ +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. + +package model + +import ( + "time" + + "git.hlsq.asia/mmorpg/service-common/utils" + "gorm.io/gorm" +) + +const TableNamePointRecord = "point_records" + +// PointRecord mapped from table +type PointRecord struct { + ID uint64 `gorm:"column:id;primaryKey;autoIncrement:true" json:"id"` + Sn string `gorm:"column:sn;not null;comment:业务唯一编号" json:"sn"` // 业务唯一编号 + UserSn string `gorm:"column:user_sn;not null;comment:用户-唯一编号" json:"user_sn"` // 用户-唯一编号 + /* + 来源: + 0. 未知 + 1. 随机答题 + 2. 类目答题 + 3. 限时答题 + */ + Source int32 `gorm:"column:source;not null;comment:来源:\n0. 未知\n1. 随机答题\n2. 类目答题\n3. 限时答题" json:"source"` + Point int64 `gorm:"column:point;not null;comment:积分" json:"point"` // 积分 + CreatedAt time.Time `gorm:"column:created_at;not null" json:"created_at"` + UpdatedAt time.Time `gorm:"column:updated_at;not null" json:"updated_at"` + DeletedAt gorm.DeletedAt `gorm:"column:deleted_at" json:"deleted_at"` +} + +// TableName PointRecord's table name +func (*PointRecord) TableName() string { + return TableNamePointRecord +} + +// Auto sn +func (m *PointRecord) BeforeCreate(_ *gorm.DB) error { + if m.Sn == "" { + m.Sn = utils.SnowflakeInstance().Generate().String() + } + return nil +} diff --git a/internal/dao/query/gen.go b/internal/dao/query/gen.go index f5d18a1..5836690 100644 --- a/internal/dao/query/gen.go +++ b/internal/dao/query/gen.go @@ -17,29 +17,35 @@ import ( func Use(db *gorm.DB, opts ...gen.DOOption) *Query { return &Query{ - db: db, - Category: newCategory(db, opts...), - Question: newQuestion(db, opts...), - Record: newRecord(db, opts...), + db: db, + Category: newCategory(db, opts...), + PointCard: newPointCard(db, opts...), + PointRecord: newPointRecord(db, opts...), + Question: newQuestion(db, opts...), + Record: newRecord(db, opts...), } } type Query struct { db *gorm.DB - Category category - Question question - Record record + Category category + PointCard pointCard + PointRecord pointRecord + Question question + Record record } func (q *Query) Available() bool { return q.db != nil } func (q *Query) clone(db *gorm.DB) *Query { return &Query{ - db: db, - Category: q.Category.clone(db), - Question: q.Question.clone(db), - Record: q.Record.clone(db), + db: db, + Category: q.Category.clone(db), + PointCard: q.PointCard.clone(db), + PointRecord: q.PointRecord.clone(db), + Question: q.Question.clone(db), + Record: q.Record.clone(db), } } @@ -53,24 +59,30 @@ func (q *Query) WriteDB() *Query { func (q *Query) ReplaceDB(db *gorm.DB) *Query { return &Query{ - db: db, - Category: q.Category.replaceDB(db), - Question: q.Question.replaceDB(db), - Record: q.Record.replaceDB(db), + db: db, + Category: q.Category.replaceDB(db), + PointCard: q.PointCard.replaceDB(db), + PointRecord: q.PointRecord.replaceDB(db), + Question: q.Question.replaceDB(db), + Record: q.Record.replaceDB(db), } } type queryCtx struct { - Category *categoryDo - Question *questionDo - Record *recordDo + Category *categoryDo + PointCard *pointCardDo + PointRecord *pointRecordDo + Question *questionDo + Record *recordDo } func (q *Query) WithContext(ctx context.Context) *queryCtx { return &queryCtx{ - Category: q.Category.WithContext(ctx), - Question: q.Question.WithContext(ctx), - Record: q.Record.WithContext(ctx), + Category: q.Category.WithContext(ctx), + PointCard: q.PointCard.WithContext(ctx), + PointRecord: q.PointRecord.WithContext(ctx), + Question: q.Question.WithContext(ctx), + Record: q.Record.WithContext(ctx), } } diff --git a/internal/dao/query/point_card.gen.go b/internal/dao/query/point_card.gen.go new file mode 100644 index 0000000..32aa3d0 --- /dev/null +++ b/internal/dao/query/point_card.gen.go @@ -0,0 +1,353 @@ +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. + +package query + +import ( + "context" + + "gorm.io/gorm" + "gorm.io/gorm/clause" + "gorm.io/gorm/schema" + + "gorm.io/gen" + "gorm.io/gen/field" + + "gorm.io/plugin/dbresolver" + + "git.hlsq.asia/mmorpg/service-qgdzs/internal/dao/model" +) + +func newPointCard(db *gorm.DB, opts ...gen.DOOption) pointCard { + _pointCard := pointCard{} + + _pointCard.pointCardDo.UseDB(db, opts...) + _pointCard.pointCardDo.UseModel(&model.PointCard{}) + + tableName := _pointCard.pointCardDo.TableName() + _pointCard.ALL = field.NewAsterisk(tableName) + _pointCard.ID = field.NewUint64(tableName, "id") + _pointCard.Sn = field.NewString(tableName, "sn") + _pointCard.UserSn = field.NewString(tableName, "user_sn") + _pointCard.Point = field.NewInt64(tableName, "point") + _pointCard.CreatedAt = field.NewTime(tableName, "created_at") + _pointCard.UpdatedAt = field.NewTime(tableName, "updated_at") + _pointCard.DeletedAt = field.NewField(tableName, "deleted_at") + + _pointCard.fillFieldMap() + + return _pointCard +} + +type pointCard struct { + pointCardDo pointCardDo + + ALL field.Asterisk + ID field.Uint64 + Sn field.String // 业务唯一编号 + UserSn field.String // 用户-唯一编号 + Point field.Int64 // 积分 + CreatedAt field.Time + UpdatedAt field.Time + DeletedAt field.Field + + fieldMap map[string]field.Expr +} + +func (p pointCard) Table(newTableName string) *pointCard { + p.pointCardDo.UseTable(newTableName) + return p.updateTableName(newTableName) +} + +func (p pointCard) As(alias string) *pointCard { + p.pointCardDo.DO = *(p.pointCardDo.As(alias).(*gen.DO)) + return p.updateTableName(alias) +} + +func (p *pointCard) updateTableName(table string) *pointCard { + p.ALL = field.NewAsterisk(table) + p.ID = field.NewUint64(table, "id") + p.Sn = field.NewString(table, "sn") + p.UserSn = field.NewString(table, "user_sn") + p.Point = field.NewInt64(table, "point") + p.CreatedAt = field.NewTime(table, "created_at") + p.UpdatedAt = field.NewTime(table, "updated_at") + p.DeletedAt = field.NewField(table, "deleted_at") + + p.fillFieldMap() + + return p +} + +func (p *pointCard) WithContext(ctx context.Context) *pointCardDo { + return p.pointCardDo.WithContext(ctx) +} + +func (p pointCard) TableName() string { return p.pointCardDo.TableName() } + +func (p pointCard) Alias() string { return p.pointCardDo.Alias() } + +func (p pointCard) Columns(cols ...field.Expr) gen.Columns { return p.pointCardDo.Columns(cols...) } + +func (p *pointCard) GetFieldByName(fieldName string) (field.OrderExpr, bool) { + _f, ok := p.fieldMap[fieldName] + if !ok || _f == nil { + return nil, false + } + _oe, ok := _f.(field.OrderExpr) + return _oe, ok +} + +func (p *pointCard) fillFieldMap() { + p.fieldMap = make(map[string]field.Expr, 7) + p.fieldMap["id"] = p.ID + p.fieldMap["sn"] = p.Sn + p.fieldMap["user_sn"] = p.UserSn + p.fieldMap["point"] = p.Point + p.fieldMap["created_at"] = p.CreatedAt + p.fieldMap["updated_at"] = p.UpdatedAt + p.fieldMap["deleted_at"] = p.DeletedAt +} + +func (p pointCard) clone(db *gorm.DB) pointCard { + p.pointCardDo.ReplaceConnPool(db.Statement.ConnPool) + return p +} + +func (p pointCard) replaceDB(db *gorm.DB) pointCard { + p.pointCardDo.ReplaceDB(db) + return p +} + +type pointCardDo struct{ gen.DO } + +func (p pointCardDo) Debug() *pointCardDo { + return p.withDO(p.DO.Debug()) +} + +func (p pointCardDo) WithContext(ctx context.Context) *pointCardDo { + return p.withDO(p.DO.WithContext(ctx)) +} + +func (p pointCardDo) ReadDB() *pointCardDo { + return p.Clauses(dbresolver.Read) +} + +func (p pointCardDo) WriteDB() *pointCardDo { + return p.Clauses(dbresolver.Write) +} + +func (p pointCardDo) Session(config *gorm.Session) *pointCardDo { + return p.withDO(p.DO.Session(config)) +} + +func (p pointCardDo) Clauses(conds ...clause.Expression) *pointCardDo { + return p.withDO(p.DO.Clauses(conds...)) +} + +func (p pointCardDo) Returning(value interface{}, columns ...string) *pointCardDo { + return p.withDO(p.DO.Returning(value, columns...)) +} + +func (p pointCardDo) Not(conds ...gen.Condition) *pointCardDo { + return p.withDO(p.DO.Not(conds...)) +} + +func (p pointCardDo) Or(conds ...gen.Condition) *pointCardDo { + return p.withDO(p.DO.Or(conds...)) +} + +func (p pointCardDo) Select(conds ...field.Expr) *pointCardDo { + return p.withDO(p.DO.Select(conds...)) +} + +func (p pointCardDo) Where(conds ...gen.Condition) *pointCardDo { + return p.withDO(p.DO.Where(conds...)) +} + +func (p pointCardDo) Order(conds ...field.Expr) *pointCardDo { + return p.withDO(p.DO.Order(conds...)) +} + +func (p pointCardDo) Distinct(cols ...field.Expr) *pointCardDo { + return p.withDO(p.DO.Distinct(cols...)) +} + +func (p pointCardDo) Omit(cols ...field.Expr) *pointCardDo { + return p.withDO(p.DO.Omit(cols...)) +} + +func (p pointCardDo) Join(table schema.Tabler, on ...field.Expr) *pointCardDo { + return p.withDO(p.DO.Join(table, on...)) +} + +func (p pointCardDo) LeftJoin(table schema.Tabler, on ...field.Expr) *pointCardDo { + return p.withDO(p.DO.LeftJoin(table, on...)) +} + +func (p pointCardDo) RightJoin(table schema.Tabler, on ...field.Expr) *pointCardDo { + return p.withDO(p.DO.RightJoin(table, on...)) +} + +func (p pointCardDo) Group(cols ...field.Expr) *pointCardDo { + return p.withDO(p.DO.Group(cols...)) +} + +func (p pointCardDo) Having(conds ...gen.Condition) *pointCardDo { + return p.withDO(p.DO.Having(conds...)) +} + +func (p pointCardDo) Limit(limit int) *pointCardDo { + return p.withDO(p.DO.Limit(limit)) +} + +func (p pointCardDo) Offset(offset int) *pointCardDo { + return p.withDO(p.DO.Offset(offset)) +} + +func (p pointCardDo) Scopes(funcs ...func(gen.Dao) gen.Dao) *pointCardDo { + return p.withDO(p.DO.Scopes(funcs...)) +} + +func (p pointCardDo) Unscoped() *pointCardDo { + return p.withDO(p.DO.Unscoped()) +} + +func (p pointCardDo) Create(values ...*model.PointCard) error { + if len(values) == 0 { + return nil + } + return p.DO.Create(values) +} + +func (p pointCardDo) CreateInBatches(values []*model.PointCard, batchSize int) error { + return p.DO.CreateInBatches(values, batchSize) +} + +// Save : !!! underlying implementation is different with GORM +// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values) +func (p pointCardDo) Save(values ...*model.PointCard) error { + if len(values) == 0 { + return nil + } + return p.DO.Save(values) +} + +func (p pointCardDo) First() (*model.PointCard, error) { + if result, err := p.DO.First(); err != nil { + return nil, err + } else { + return result.(*model.PointCard), nil + } +} + +func (p pointCardDo) Take() (*model.PointCard, error) { + if result, err := p.DO.Take(); err != nil { + return nil, err + } else { + return result.(*model.PointCard), nil + } +} + +func (p pointCardDo) Last() (*model.PointCard, error) { + if result, err := p.DO.Last(); err != nil { + return nil, err + } else { + return result.(*model.PointCard), nil + } +} + +func (p pointCardDo) Find() ([]*model.PointCard, error) { + result, err := p.DO.Find() + return result.([]*model.PointCard), err +} + +func (p pointCardDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.PointCard, err error) { + buf := make([]*model.PointCard, 0, batchSize) + err = p.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error { + defer func() { results = append(results, buf...) }() + return fc(tx, batch) + }) + return results, err +} + +func (p pointCardDo) FindInBatches(result *[]*model.PointCard, batchSize int, fc func(tx gen.Dao, batch int) error) error { + return p.DO.FindInBatches(result, batchSize, fc) +} + +func (p pointCardDo) Attrs(attrs ...field.AssignExpr) *pointCardDo { + return p.withDO(p.DO.Attrs(attrs...)) +} + +func (p pointCardDo) Assign(attrs ...field.AssignExpr) *pointCardDo { + return p.withDO(p.DO.Assign(attrs...)) +} + +func (p pointCardDo) Joins(fields ...field.RelationField) *pointCardDo { + for _, _f := range fields { + p = *p.withDO(p.DO.Joins(_f)) + } + return &p +} + +func (p pointCardDo) Preload(fields ...field.RelationField) *pointCardDo { + for _, _f := range fields { + p = *p.withDO(p.DO.Preload(_f)) + } + return &p +} + +func (p pointCardDo) FirstOrInit() (*model.PointCard, error) { + if result, err := p.DO.FirstOrInit(); err != nil { + return nil, err + } else { + return result.(*model.PointCard), nil + } +} + +func (p pointCardDo) FirstOrCreate() (*model.PointCard, error) { + if result, err := p.DO.FirstOrCreate(); err != nil { + return nil, err + } else { + return result.(*model.PointCard), nil + } +} + +func (p pointCardDo) FindByPage(offset int, limit int) (result []*model.PointCard, count int64, err error) { + result, err = p.Offset(offset).Limit(limit).Find() + if err != nil { + return + } + + if size := len(result); 0 < limit && 0 < size && size < limit { + count = int64(size + offset) + return + } + + count, err = p.Offset(-1).Limit(-1).Count() + return +} + +func (p pointCardDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) { + count, err = p.Count() + if err != nil { + return + } + + err = p.Offset(offset).Limit(limit).Scan(result) + return +} + +func (p pointCardDo) Scan(result interface{}) (err error) { + return p.DO.Scan(result) +} + +func (p pointCardDo) Delete(models ...*model.PointCard) (result gen.ResultInfo, err error) { + return p.DO.Delete(models) +} + +func (p *pointCardDo) withDO(do gen.Dao) *pointCardDo { + p.DO = *do.(*gen.DO) + return p +} diff --git a/internal/dao/query/point_records.gen.go b/internal/dao/query/point_records.gen.go new file mode 100644 index 0000000..9e941e0 --- /dev/null +++ b/internal/dao/query/point_records.gen.go @@ -0,0 +1,364 @@ +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. + +package query + +import ( + "context" + + "gorm.io/gorm" + "gorm.io/gorm/clause" + "gorm.io/gorm/schema" + + "gorm.io/gen" + "gorm.io/gen/field" + + "gorm.io/plugin/dbresolver" + + "git.hlsq.asia/mmorpg/service-qgdzs/internal/dao/model" +) + +func newPointRecord(db *gorm.DB, opts ...gen.DOOption) pointRecord { + _pointRecord := pointRecord{} + + _pointRecord.pointRecordDo.UseDB(db, opts...) + _pointRecord.pointRecordDo.UseModel(&model.PointRecord{}) + + tableName := _pointRecord.pointRecordDo.TableName() + _pointRecord.ALL = field.NewAsterisk(tableName) + _pointRecord.ID = field.NewUint64(tableName, "id") + _pointRecord.Sn = field.NewString(tableName, "sn") + _pointRecord.UserSn = field.NewString(tableName, "user_sn") + _pointRecord.Source = field.NewInt32(tableName, "source") + _pointRecord.Point = field.NewInt64(tableName, "point") + _pointRecord.CreatedAt = field.NewTime(tableName, "created_at") + _pointRecord.UpdatedAt = field.NewTime(tableName, "updated_at") + _pointRecord.DeletedAt = field.NewField(tableName, "deleted_at") + + _pointRecord.fillFieldMap() + + return _pointRecord +} + +type pointRecord struct { + pointRecordDo pointRecordDo + + ALL field.Asterisk + ID field.Uint64 + Sn field.String // 业务唯一编号 + UserSn field.String // 用户-唯一编号 + /* + 来源: + 0. 未知 + 1. 随机答题 + 2. 类目答题 + 3. 限时答题 + */ + Source field.Int32 + Point field.Int64 // 积分 + CreatedAt field.Time + UpdatedAt field.Time + DeletedAt field.Field + + fieldMap map[string]field.Expr +} + +func (p pointRecord) Table(newTableName string) *pointRecord { + p.pointRecordDo.UseTable(newTableName) + return p.updateTableName(newTableName) +} + +func (p pointRecord) As(alias string) *pointRecord { + p.pointRecordDo.DO = *(p.pointRecordDo.As(alias).(*gen.DO)) + return p.updateTableName(alias) +} + +func (p *pointRecord) updateTableName(table string) *pointRecord { + p.ALL = field.NewAsterisk(table) + p.ID = field.NewUint64(table, "id") + p.Sn = field.NewString(table, "sn") + p.UserSn = field.NewString(table, "user_sn") + p.Source = field.NewInt32(table, "source") + p.Point = field.NewInt64(table, "point") + p.CreatedAt = field.NewTime(table, "created_at") + p.UpdatedAt = field.NewTime(table, "updated_at") + p.DeletedAt = field.NewField(table, "deleted_at") + + p.fillFieldMap() + + return p +} + +func (p *pointRecord) WithContext(ctx context.Context) *pointRecordDo { + return p.pointRecordDo.WithContext(ctx) +} + +func (p pointRecord) TableName() string { return p.pointRecordDo.TableName() } + +func (p pointRecord) Alias() string { return p.pointRecordDo.Alias() } + +func (p pointRecord) Columns(cols ...field.Expr) gen.Columns { return p.pointRecordDo.Columns(cols...) } + +func (p *pointRecord) GetFieldByName(fieldName string) (field.OrderExpr, bool) { + _f, ok := p.fieldMap[fieldName] + if !ok || _f == nil { + return nil, false + } + _oe, ok := _f.(field.OrderExpr) + return _oe, ok +} + +func (p *pointRecord) fillFieldMap() { + p.fieldMap = make(map[string]field.Expr, 8) + p.fieldMap["id"] = p.ID + p.fieldMap["sn"] = p.Sn + p.fieldMap["user_sn"] = p.UserSn + p.fieldMap["source"] = p.Source + p.fieldMap["point"] = p.Point + p.fieldMap["created_at"] = p.CreatedAt + p.fieldMap["updated_at"] = p.UpdatedAt + p.fieldMap["deleted_at"] = p.DeletedAt +} + +func (p pointRecord) clone(db *gorm.DB) pointRecord { + p.pointRecordDo.ReplaceConnPool(db.Statement.ConnPool) + return p +} + +func (p pointRecord) replaceDB(db *gorm.DB) pointRecord { + p.pointRecordDo.ReplaceDB(db) + return p +} + +type pointRecordDo struct{ gen.DO } + +func (p pointRecordDo) Debug() *pointRecordDo { + return p.withDO(p.DO.Debug()) +} + +func (p pointRecordDo) WithContext(ctx context.Context) *pointRecordDo { + return p.withDO(p.DO.WithContext(ctx)) +} + +func (p pointRecordDo) ReadDB() *pointRecordDo { + return p.Clauses(dbresolver.Read) +} + +func (p pointRecordDo) WriteDB() *pointRecordDo { + return p.Clauses(dbresolver.Write) +} + +func (p pointRecordDo) Session(config *gorm.Session) *pointRecordDo { + return p.withDO(p.DO.Session(config)) +} + +func (p pointRecordDo) Clauses(conds ...clause.Expression) *pointRecordDo { + return p.withDO(p.DO.Clauses(conds...)) +} + +func (p pointRecordDo) Returning(value interface{}, columns ...string) *pointRecordDo { + return p.withDO(p.DO.Returning(value, columns...)) +} + +func (p pointRecordDo) Not(conds ...gen.Condition) *pointRecordDo { + return p.withDO(p.DO.Not(conds...)) +} + +func (p pointRecordDo) Or(conds ...gen.Condition) *pointRecordDo { + return p.withDO(p.DO.Or(conds...)) +} + +func (p pointRecordDo) Select(conds ...field.Expr) *pointRecordDo { + return p.withDO(p.DO.Select(conds...)) +} + +func (p pointRecordDo) Where(conds ...gen.Condition) *pointRecordDo { + return p.withDO(p.DO.Where(conds...)) +} + +func (p pointRecordDo) Order(conds ...field.Expr) *pointRecordDo { + return p.withDO(p.DO.Order(conds...)) +} + +func (p pointRecordDo) Distinct(cols ...field.Expr) *pointRecordDo { + return p.withDO(p.DO.Distinct(cols...)) +} + +func (p pointRecordDo) Omit(cols ...field.Expr) *pointRecordDo { + return p.withDO(p.DO.Omit(cols...)) +} + +func (p pointRecordDo) Join(table schema.Tabler, on ...field.Expr) *pointRecordDo { + return p.withDO(p.DO.Join(table, on...)) +} + +func (p pointRecordDo) LeftJoin(table schema.Tabler, on ...field.Expr) *pointRecordDo { + return p.withDO(p.DO.LeftJoin(table, on...)) +} + +func (p pointRecordDo) RightJoin(table schema.Tabler, on ...field.Expr) *pointRecordDo { + return p.withDO(p.DO.RightJoin(table, on...)) +} + +func (p pointRecordDo) Group(cols ...field.Expr) *pointRecordDo { + return p.withDO(p.DO.Group(cols...)) +} + +func (p pointRecordDo) Having(conds ...gen.Condition) *pointRecordDo { + return p.withDO(p.DO.Having(conds...)) +} + +func (p pointRecordDo) Limit(limit int) *pointRecordDo { + return p.withDO(p.DO.Limit(limit)) +} + +func (p pointRecordDo) Offset(offset int) *pointRecordDo { + return p.withDO(p.DO.Offset(offset)) +} + +func (p pointRecordDo) Scopes(funcs ...func(gen.Dao) gen.Dao) *pointRecordDo { + return p.withDO(p.DO.Scopes(funcs...)) +} + +func (p pointRecordDo) Unscoped() *pointRecordDo { + return p.withDO(p.DO.Unscoped()) +} + +func (p pointRecordDo) Create(values ...*model.PointRecord) error { + if len(values) == 0 { + return nil + } + return p.DO.Create(values) +} + +func (p pointRecordDo) CreateInBatches(values []*model.PointRecord, batchSize int) error { + return p.DO.CreateInBatches(values, batchSize) +} + +// Save : !!! underlying implementation is different with GORM +// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values) +func (p pointRecordDo) Save(values ...*model.PointRecord) error { + if len(values) == 0 { + return nil + } + return p.DO.Save(values) +} + +func (p pointRecordDo) First() (*model.PointRecord, error) { + if result, err := p.DO.First(); err != nil { + return nil, err + } else { + return result.(*model.PointRecord), nil + } +} + +func (p pointRecordDo) Take() (*model.PointRecord, error) { + if result, err := p.DO.Take(); err != nil { + return nil, err + } else { + return result.(*model.PointRecord), nil + } +} + +func (p pointRecordDo) Last() (*model.PointRecord, error) { + if result, err := p.DO.Last(); err != nil { + return nil, err + } else { + return result.(*model.PointRecord), nil + } +} + +func (p pointRecordDo) Find() ([]*model.PointRecord, error) { + result, err := p.DO.Find() + return result.([]*model.PointRecord), err +} + +func (p pointRecordDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.PointRecord, err error) { + buf := make([]*model.PointRecord, 0, batchSize) + err = p.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error { + defer func() { results = append(results, buf...) }() + return fc(tx, batch) + }) + return results, err +} + +func (p pointRecordDo) FindInBatches(result *[]*model.PointRecord, batchSize int, fc func(tx gen.Dao, batch int) error) error { + return p.DO.FindInBatches(result, batchSize, fc) +} + +func (p pointRecordDo) Attrs(attrs ...field.AssignExpr) *pointRecordDo { + return p.withDO(p.DO.Attrs(attrs...)) +} + +func (p pointRecordDo) Assign(attrs ...field.AssignExpr) *pointRecordDo { + return p.withDO(p.DO.Assign(attrs...)) +} + +func (p pointRecordDo) Joins(fields ...field.RelationField) *pointRecordDo { + for _, _f := range fields { + p = *p.withDO(p.DO.Joins(_f)) + } + return &p +} + +func (p pointRecordDo) Preload(fields ...field.RelationField) *pointRecordDo { + for _, _f := range fields { + p = *p.withDO(p.DO.Preload(_f)) + } + return &p +} + +func (p pointRecordDo) FirstOrInit() (*model.PointRecord, error) { + if result, err := p.DO.FirstOrInit(); err != nil { + return nil, err + } else { + return result.(*model.PointRecord), nil + } +} + +func (p pointRecordDo) FirstOrCreate() (*model.PointRecord, error) { + if result, err := p.DO.FirstOrCreate(); err != nil { + return nil, err + } else { + return result.(*model.PointRecord), nil + } +} + +func (p pointRecordDo) FindByPage(offset int, limit int) (result []*model.PointRecord, count int64, err error) { + result, err = p.Offset(offset).Limit(limit).Find() + if err != nil { + return + } + + if size := len(result); 0 < limit && 0 < size && size < limit { + count = int64(size + offset) + return + } + + count, err = p.Offset(-1).Limit(-1).Count() + return +} + +func (p pointRecordDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) { + count, err = p.Count() + if err != nil { + return + } + + err = p.Offset(offset).Limit(limit).Scan(result) + return +} + +func (p pointRecordDo) Scan(result interface{}) (err error) { + return p.DO.Scan(result) +} + +func (p pointRecordDo) Delete(models ...*model.PointRecord) (result gen.ResultInfo, err error) { + return p.DO.Delete(models) +} + +func (p *pointRecordDo) withDO(do gen.Dao) *pointRecordDo { + p.DO = *do.(*gen.DO) + return p +} diff --git a/internal/dao/repository/categories.go b/internal/dao/repository/categories.go index ddcc8ad..c79730c 100644 --- a/internal/dao/repository/categories.go +++ b/internal/dao/repository/categories.go @@ -2,7 +2,6 @@ package repository import ( "context" - "git.hlsq.asia/mmorpg/service-common/db/mysql" "git.hlsq.asia/mmorpg/service-common/db/redis" "git.hlsq.asia/mmorpg/service-qgdzs/internal/dao/model" "git.hlsq.asia/mmorpg/service-qgdzs/internal/dao/query" @@ -14,10 +13,10 @@ type CategoryDao struct { cache *redis.CacheClient } -func NewCategoryDao(ctx context.Context, cache ...*redis.CacheClient) *CategoryDao { +func NewCategoryDao(ctx context.Context, query *query.Query, cache ...*redis.CacheClient) *CategoryDao { dao := &CategoryDao{ ctx: ctx, - query: query.Use(mysql.GetDB(dbName)), + query: query, } if len(cache) > 0 { dao.cache = cache[0] diff --git a/internal/dao/repository/define.go b/internal/dao/repository/define.go index b330a69..5fc0be5 100644 --- a/internal/dao/repository/define.go +++ b/internal/dao/repository/define.go @@ -3,6 +3,7 @@ package repository import ( "fmt" "git.hlsq.asia/mmorpg/service-common/db/mysql" + "git.hlsq.asia/mmorpg/service-qgdzs/internal/dao/query" ) var dbName mysql.DBName = "qgdzs_db" @@ -11,6 +12,10 @@ var ( cacheBySn = "c:%v:s:%v" ) +func Query() *query.Query { + return query.Use(mysql.GetDB(dbName)) +} + func keyCacheBySn(sn string, tableName string) string { return fmt.Sprintf(cacheBySn, tableName, sn) } diff --git a/internal/dao/repository/point_card.go b/internal/dao/repository/point_card.go new file mode 100644 index 0000000..3686cf2 --- /dev/null +++ b/internal/dao/repository/point_card.go @@ -0,0 +1,52 @@ +package repository + +import ( + "context" + "errors" + "git.hlsq.asia/mmorpg/service-common/db/redis" + "git.hlsq.asia/mmorpg/service-common/utils" + "git.hlsq.asia/mmorpg/service-qgdzs/internal/dao/model" + "git.hlsq.asia/mmorpg/service-qgdzs/internal/dao/query" +) + +type PointCardDao struct { + ctx context.Context + query *query.Query + cache *redis.CacheClient +} + +func NewPointCardDao(ctx context.Context, query *query.Query, cache ...*redis.CacheClient) *PointCardDao { + dao := &PointCardDao{ + ctx: ctx, + query: query, + } + if len(cache) > 0 { + dao.cache = cache[0] + } + return dao +} + +func (d *PointCardDao) Create(pointCard *model.PointCard) (*model.PointCard, error) { + err := d.query.PointCard.WithContext(d.ctx). + Create(pointCard) + return pointCard, err +} + +func (d *PointCardDao) IncrPointCard(usn string, point int64) error { + info, err := d.query.PointCard.WithContext(d.ctx). + Where(d.query.PointCard.UserSn.Eq(usn)). + UpdateSimple(d.query.PointCard.Point.Add(point)) + if err != nil { + return utils.ErrorsWrap(err) + } + if info.RowsAffected == 0 { + return utils.ErrorsWrap(errors.New("user not found")) + } + return nil +} + +func (d *PointCardDao) FindByUserSn(usn string) (*model.PointCard, error) { + return d.query.PointCard.WithContext(d.ctx). + Where(d.query.PointCard.UserSn.Eq(usn)). + First() +} diff --git a/internal/dao/repository/point_records.go b/internal/dao/repository/point_records.go new file mode 100644 index 0000000..3ccf86f --- /dev/null +++ b/internal/dao/repository/point_records.go @@ -0,0 +1,58 @@ +package repository + +import ( + "context" + "errors" + "git.hlsq.asia/mmorpg/service-common/db/redis" + "git.hlsq.asia/mmorpg/service-common/utils" + "git.hlsq.asia/mmorpg/service-qgdzs/internal/dao/model" + "git.hlsq.asia/mmorpg/service-qgdzs/internal/dao/query" + "gorm.io/gorm" +) + +type PointRecordsDao struct { + ctx context.Context + query *query.Query + cache *redis.CacheClient +} + +func NewPointRecordsDao(ctx context.Context, query *query.Query, cache ...*redis.CacheClient) *PointRecordsDao { + dao := &PointRecordsDao{ + ctx: ctx, + query: query, + } + if len(cache) > 0 { + dao.cache = cache[0] + } + return dao +} + +func (d *PointRecordsDao) CreateAndIncrPointCard(pointRecord *model.PointRecord) error { + return d.query.Transaction(func(tx *query.Query) error { + if err := tx.PointRecord.WithContext(d.ctx). + Create(pointRecord); err != nil { + return err + } + + pcd := NewPointCardDao(d.ctx, tx) + if _, err := pcd.FindByUserSn(pointRecord.UserSn); err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + _, err = pcd.Create(&model.PointCard{ + UserSn: pointRecord.UserSn, + Point: pointRecord.Point, + }) + if err != nil { + return utils.ErrorsWrap(err) + } + } else { + return utils.ErrorsWrap(err) + } + } else { + if err = pcd.IncrPointCard(pointRecord.UserSn, pointRecord.Point); err != nil { + return utils.ErrorsWrap(err) + } + } + + return nil + }) +} diff --git a/internal/dao/repository/questions.go b/internal/dao/repository/questions.go index 702ef0f..3e81612 100644 --- a/internal/dao/repository/questions.go +++ b/internal/dao/repository/questions.go @@ -2,7 +2,6 @@ package repository import ( "context" - "git.hlsq.asia/mmorpg/service-common/db/mysql" "git.hlsq.asia/mmorpg/service-common/db/redis" "git.hlsq.asia/mmorpg/service-common/utils" "git.hlsq.asia/mmorpg/service-qgdzs/internal/dao/model" @@ -17,10 +16,10 @@ type QuestionDao struct { cache *redis.CacheClient } -func NewQuestionDao(ctx context.Context, cache ...*redis.CacheClient) *QuestionDao { +func NewQuestionDao(ctx context.Context, query *query.Query, cache ...*redis.CacheClient) *QuestionDao { dao := &QuestionDao{ ctx: ctx, - query: query.Use(mysql.GetDB(dbName)), + query: query, } if len(cache) > 0 { dao.cache = cache[0] @@ -73,23 +72,3 @@ func (d *QuestionDao) FindBySn(sn string) (*model.Question, error) { } return first, nil } - -func (d *QuestionDao) FindByCategory(categorySn string) (*model.Question, error) { - count, err := d.query.Question.WithContext(d.ctx). - Where(d.query.Question.CategorySn.Eq(categorySn)). - Count() - if err != nil { - return nil, err - } - if count == 0 { - return nil, gorm.ErrRecordNotFound - } - first, err := d.query.Question.WithContext(d.ctx). - Where(d.query.Question.CategorySn.Eq(categorySn)). - Offset(utils.RandInt(0, int(count-1))). - First() - if err != nil { - return nil, err - } - return first, nil -} diff --git a/internal/dao/repository/records.go b/internal/dao/repository/records.go index fe0d1b7..3b17048 100644 --- a/internal/dao/repository/records.go +++ b/internal/dao/repository/records.go @@ -2,7 +2,6 @@ package repository import ( "context" - "git.hlsq.asia/mmorpg/service-common/db/mysql" "git.hlsq.asia/mmorpg/service-common/db/redis" "git.hlsq.asia/mmorpg/service-qgdzs/internal/dao/model" "git.hlsq.asia/mmorpg/service-qgdzs/internal/dao/query" @@ -15,10 +14,10 @@ type RecordDao struct { cache *redis.CacheClient } -func NewRecordDao(ctx context.Context, cache ...*redis.CacheClient) *RecordDao { +func NewRecordDao(ctx context.Context, query *query.Query, cache ...*redis.CacheClient) *RecordDao { dao := &RecordDao{ ctx: ctx, - query: query.Use(mysql.GetDB(dbName)), + query: query, } if len(cache) > 0 { dao.cache = cache[0] diff --git a/internal/grpc_server/server_category.go b/internal/grpc_server/server_category.go new file mode 100644 index 0000000..ef9344f --- /dev/null +++ b/internal/grpc_server/server_category.go @@ -0,0 +1,79 @@ +package grpc_server + +import ( + "context" + "encoding/json" + "git.hlsq.asia/mmorpg/service-common/db/kafka" + "git.hlsq.asia/mmorpg/service-common/proto/rs/grpc_pb" + "git.hlsq.asia/mmorpg/service-common/utils" + "git.hlsq.asia/mmorpg/service-qgdzs/internal/dao/repository" + "git.hlsq.asia/mmorpg/service-qgdzs/internal/timer" +) + +// 玩法 - 类目答题 + +// GetAllCategory 获取所有类目 +func (s *Server) GetAllCategory(ctx context.Context, req *grpc_pb.GetAllCategoryReq) (*grpc_pb.GetAllCategoryResp, error) { + categoryList, err := repository.NewCategoryDao(ctx, s.query).FindAll() + if err != nil { + return nil, utils.ErrorsWrap(err) + } + categories := make([]*grpc_pb.GetAllCategoryItem, 0) + for _, category := range categoryList { + categories = append(categories, &grpc_pb.GetAllCategoryItem{ + Sn: category.Sn, + Category: category.Category, + }) + } + return &grpc_pb.GetAllCategoryResp{ + Categories: categories, + }, nil +} + +// CategoryGetQuestion 获取题目 +func (s *Server) CategoryGetQuestion(ctx context.Context, req *grpc_pb.CategoryGetQuestionReq) (*grpc_pb.CategoryGetQuestionResp, error) { + question, err := repository.NewQuestionDao(ctx, s.query).FindByRandom(req.CategorySn) + if err != nil { + return nil, utils.ErrorsWrap(err) + } + options := make([]string, 0) + if err = json.Unmarshal([]byte(question.Options), &options); err != nil { + return nil, utils.ErrorsWrapF(err, "data: %v", question.Options) + } + category, _ := repository.NewCategoryDao(ctx, s.query).FindNameBySn(question.CategorySn) + return &grpc_pb.CategoryGetQuestionResp{ + Sn: question.Sn, + Question: question.Question, + Options: options, + Category: category, + Difficulty: question.Difficulty, + }, nil +} + +// CategoryAnswerQuestion 回答题目 +func (s *Server) CategoryAnswerQuestion(ctx context.Context, req *grpc_pb.CategoryAnswerQuestionReq) (*grpc_pb.CategoryAnswerQuestionResp, error) { + utils.ShouldBindUsn(ctx, &req.USN) + + question, err := repository.NewQuestionDao(ctx, s.query).FindBySn(req.Sn) + if err != nil { + return nil, utils.ErrorsWrap(err) + } + options := make([]string, 0) + if err = json.Unmarshal([]byte(question.Options), &options); err != nil { + return nil, utils.ErrorsWrapF(err, "data: %v", question.Options) + } + // 保存答题记录 + if req.USN != "" { + kafka.NewProducer().Produce(ctx, &timer.TopicQuestionAnswer{ + MessageSn: utils.SnowflakeInstance().Generate().String(), + USN: req.USN, + QuestionSn: question.Sn, + QuestionAnswer: question.Answer, + Answer: req.Answer, + }) + } + return &grpc_pb.CategoryAnswerQuestionResp{ + Answer: question.Answer, + Explanation: question.Explanation, + }, nil +} diff --git a/internal/grpc_server/server_init.go b/internal/grpc_server/server_init.go index f7b4f88..025315b 100644 --- a/internal/grpc_server/server_init.go +++ b/internal/grpc_server/server_init.go @@ -5,12 +5,15 @@ import ( "git.hlsq.asia/mmorpg/service-common/discover/common" "git.hlsq.asia/mmorpg/service-common/net/grpc/service" "git.hlsq.asia/mmorpg/service-common/proto/rs/grpc_pb" + "git.hlsq.asia/mmorpg/service-qgdzs/internal/dao/query" + "git.hlsq.asia/mmorpg/service-qgdzs/internal/dao/repository" "google.golang.org/grpc" ) type Server struct { grpc_pb.UnimplementedQgdzsServer service.Base + query *query.Query } func NewServer(cfg *config.GrpcConfig) *Server { @@ -33,6 +36,7 @@ func (s *Server) OnCustomGrpcServerOption() []grpc.ServerOption { func (s *Server) OnInit(serve *grpc.Server) { grpc_pb.RegisterQgdzsServer(serve, s) + s.query = repository.Query() } func (s *Server) OnClose() { diff --git a/internal/grpc_server/server_question.go b/internal/grpc_server/server_question.go index 2f486fb..ef97845 100644 --- a/internal/grpc_server/server_question.go +++ b/internal/grpc_server/server_question.go @@ -5,16 +5,13 @@ import ( "encoding/json" "errors" "fmt" - "git.hlsq.asia/mmorpg/service-common/db/kafka" "git.hlsq.asia/mmorpg/service-common/db/redis" "git.hlsq.asia/mmorpg/service-common/log" - "git.hlsq.asia/mmorpg/service-common/net/http/http_resp" "git.hlsq.asia/mmorpg/service-common/proto/rs/grpc_pb" "git.hlsq.asia/mmorpg/service-common/utils" "git.hlsq.asia/mmorpg/service-qgdzs/internal/ai" "git.hlsq.asia/mmorpg/service-qgdzs/internal/dao/model" "git.hlsq.asia/mmorpg/service-qgdzs/internal/dao/repository" - "git.hlsq.asia/mmorpg/service-qgdzs/internal/timer" "gorm.io/gorm" "time" ) @@ -30,7 +27,7 @@ var prompt = []string{` "question": "题目文本", // 简洁,30字以内 "options": ["A. 选项1", "B. 选项2", "C. 选项3", "D. 选项4"], // 提供4个选项(A/B/C/D),其中仅1个正确 "answer": "C", // 答案 - "explanation": "解析文本", // 200字以内,尽量幽默有趣 + "explanation": "解析文本", // 200字以内,尽量幽默有趣,不要在文本中说正确和错误,因为不知道用户选的哪个 "category": "分类", // 尽量从上述分类中选择,你也可以增加,但是命名风格要类似 "difficulty": 100, // 难度分 0 - 100 }] @@ -57,16 +54,20 @@ type Question struct { } func (s *Server) GenerateQuestion(ctx context.Context, req *grpc_pb.GenerateQuestionReq) (*grpc_pb.GenerateQuestionResp, error) { - categoryDao := repository.NewCategoryDao(ctx, redis.GetCacheClient()) + categoryDao := repository.NewCategoryDao(ctx, s.query, redis.GetCacheClient()) category, err := categoryDao.FindAll() if err != nil { return nil, utils.ErrorsWrap(err) } + categoryName := make([]string, 0) + for _, c := range category { + categoryName = append(categoryName, c.Category) + } question := make([]*Question, 0) err = ai.NewAIClient(false, "", 0.9). RequestAI( []string{ - fmt.Sprintf(prompt[0], time.Now().Format("2006-01-02 15:04:05"), category), + fmt.Sprintf(prompt[0], time.Now().Format("2006-01-02 15:04:05"), categoryName), fmt.Sprintf(prompt[1], req.Num), fmt.Sprintf(prompt[2], req.Num), fmt.Sprintf(prompt[3], req.Num), @@ -89,7 +90,7 @@ func (s *Server) GenerateQuestion(ctx context.Context, req *grpc_pb.GenerateQues return nil, utils.ErrorsWrap(err) } - questionDao := repository.NewQuestionDao(ctx, redis.GetCacheClient()) + questionDao := repository.NewQuestionDao(ctx, s.query, redis.GetCacheClient()) for _, q := range question { marshal, _ := json.Marshal(q.Options) categorySn, err := categoryDao.FindSnByName(q.Category) @@ -121,116 +122,3 @@ func (s *Server) GenerateQuestion(ctx context.Context, req *grpc_pb.GenerateQues } return nil, nil } - -// GetQuestion 获取题目 -func (s *Server) GetQuestion(ctx context.Context, req *grpc_pb.GetQuestionReq) (*grpc_pb.GetQuestionResp, error) { - question, err := repository.NewQuestionDao(ctx).FindByRandom(req.CategorySn) - if err != nil { - return nil, utils.ErrorsWrap(err) - } - options := make([]string, 0) - if err = json.Unmarshal([]byte(question.Options), &options); err != nil { - return nil, utils.ErrorsWrapF(err, "data: %v", question.Options) - } - category, _ := repository.NewCategoryDao(ctx).FindNameBySn(question.CategorySn) - return &grpc_pb.GetQuestionResp{ - Sn: question.Sn, - Question: question.Question, - Options: options, - Category: category, - Difficulty: question.Difficulty, - }, nil -} - -// GetQuestionInfo 获取具体的题目 -func (s *Server) GetQuestionInfo(ctx context.Context, req *grpc_pb.GetQuestionInfoReq) (*grpc_pb.GetQuestionInfoResp, error) { - question, err := repository.NewQuestionDao(ctx).FindBySn(req.QuestionSn) - if err != nil { - return nil, utils.ErrorsWrap(err) - } - options := make([]string, 0) - if err = json.Unmarshal([]byte(question.Options), &options); err != nil { - return nil, utils.ErrorsWrapF(err, "data: %v", question.Options) - } - category, _ := repository.NewCategoryDao(ctx).FindNameBySn(question.CategorySn) - return &grpc_pb.GetQuestionInfoResp{ - Question: question.Question, - Options: options, - Category: category, - Difficulty: question.Difficulty, - Explanation: question.Explanation, - }, nil -} - -// AnswerQuestion 回答题目 -func (s *Server) AnswerQuestion(ctx context.Context, req *grpc_pb.AnswerQuestionReq) (*grpc_pb.AnswerQuestionResp, error) { - question, err := repository.NewQuestionDao(ctx).FindBySn(req.Sn) - if err != nil { - return nil, utils.ErrorsWrap(err) - } - options := make([]string, 0) - if err = json.Unmarshal([]byte(question.Options), &options); err != nil { - return nil, utils.ErrorsWrapF(err, "data: %v", question.Options) - } - // 保存答题记录 - if utils.ShouldBindUsn(ctx, &req.USN) { - data := &timer.TopicQuestionAnswer{ - MessageSn: utils.SnowflakeInstance().Generate().String(), - USN: req.USN, - QuestionSn: question.Sn, - QuestionAnswer: question.Answer, - Answer: req.Answer, - } - marshal, _ := json.Marshal(data) - kafka.NewProducer().Produce(ctx, "qgdzs.question.answer", string(marshal)) - } - return &grpc_pb.AnswerQuestionResp{ - Answer: question.Answer, - Explanation: question.Explanation, - }, nil -} - -// GetAllCategory 获取所有类目 -func (s *Server) GetAllCategory(ctx context.Context, req *grpc_pb.GetAllCategoryReq) (*grpc_pb.GetAllCategoryResp, error) { - categoryList, err := repository.NewCategoryDao(ctx).FindAll() - if err != nil { - return nil, utils.ErrorsWrap(err) - } - categories := make([]*grpc_pb.GetAllCategoryItem, 0) - for _, category := range categoryList { - categories = append(categories, &grpc_pb.GetAllCategoryItem{ - Sn: category.Sn, - Category: category.Category, - }) - } - return &grpc_pb.GetAllCategoryResp{ - Categories: categories, - }, nil -} - -// GetRecord 获取答题记录 -func (s *Server) GetRecord(ctx context.Context, req *grpc_pb.GetRecordReq) (*grpc_pb.GetRecordResp, error) { - if !utils.ShouldBindUsn(ctx, &req.USN) { - return nil, http_resp.ParamError - } - records, count, err := repository.NewRecordDao(ctx).FindByUSN(req.USN, int(req.Page), int(req.PageSize)) - if err != nil { - return nil, utils.ErrorsWrap(err) - } - resp := make([]*grpc_pb.GetRecordItem, 0) - for _, record := range records { - resp = append(resp, &grpc_pb.GetRecordItem{ - QuestionSn: record.QuestionSn, - Question: record.Question, - Difficulty: record.Difficulty, - Category: record.Category, - QuestionAnswer: record.QuestionAnswer, - Answer: record.Answer, - CreateTime: record.CreatedAt.Unix(), - }) - } - return &grpc_pb.GetRecordResp{ - Count: int32(count), - Records: resp, - }, nil -} diff --git a/internal/grpc_server/server_quickly.go b/internal/grpc_server/server_quickly.go new file mode 100644 index 0000000..15f65d6 --- /dev/null +++ b/internal/grpc_server/server_quickly.go @@ -0,0 +1,61 @@ +package grpc_server + +import ( + "context" + "encoding/json" + "git.hlsq.asia/mmorpg/service-common/db/kafka" + "git.hlsq.asia/mmorpg/service-common/proto/rs/grpc_pb" + "git.hlsq.asia/mmorpg/service-common/utils" + "git.hlsq.asia/mmorpg/service-qgdzs/internal/dao/repository" + "git.hlsq.asia/mmorpg/service-qgdzs/internal/timer" +) + +// 玩法 - 随机答题 + +// QuicklyGetQuestion 获取题目 +func (s *Server) QuicklyGetQuestion(ctx context.Context, req *grpc_pb.QuicklyGetQuestionReq) (*grpc_pb.QuicklyGetQuestionResp, error) { + question, err := repository.NewQuestionDao(ctx, s.query).FindByRandom("") + if err != nil { + return nil, utils.ErrorsWrap(err) + } + options := make([]string, 0) + if err = json.Unmarshal([]byte(question.Options), &options); err != nil { + return nil, utils.ErrorsWrapF(err, "data: %v", question.Options) + } + category, _ := repository.NewCategoryDao(ctx, s.query).FindNameBySn(question.CategorySn) + return &grpc_pb.QuicklyGetQuestionResp{ + Sn: question.Sn, + Question: question.Question, + Options: options, + Category: category, + Difficulty: question.Difficulty, + }, nil +} + +// QuicklyAnswerQuestion 回答题目 +func (s *Server) QuicklyAnswerQuestion(ctx context.Context, req *grpc_pb.QuicklyAnswerQuestionReq) (*grpc_pb.QuicklyAnswerQuestionResp, error) { + utils.ShouldBindUsn(ctx, &req.USN) + + question, err := repository.NewQuestionDao(ctx, s.query).FindBySn(req.Sn) + if err != nil { + return nil, utils.ErrorsWrap(err) + } + options := make([]string, 0) + if err = json.Unmarshal([]byte(question.Options), &options); err != nil { + return nil, utils.ErrorsWrapF(err, "data: %v", question.Options) + } + // 保存答题记录 + if req.USN != "" { + kafka.NewProducer().Produce(ctx, &timer.TopicQuestionAnswer{ + MessageSn: utils.SnowflakeInstance().Generate().String(), + USN: req.USN, + QuestionSn: question.Sn, + QuestionAnswer: question.Answer, + Answer: req.Answer, + }) + } + return &grpc_pb.QuicklyAnswerQuestionResp{ + Answer: question.Answer, + Explanation: question.Explanation, + }, nil +} diff --git a/internal/grpc_server/server_random.go b/internal/grpc_server/server_random.go new file mode 100644 index 0000000..5797461 --- /dev/null +++ b/internal/grpc_server/server_random.go @@ -0,0 +1,71 @@ +package grpc_server + +import ( + "context" + "encoding/json" + "git.hlsq.asia/mmorpg/service-common/db/kafka" + "git.hlsq.asia/mmorpg/service-common/proto/rs/grpc_pb" + "git.hlsq.asia/mmorpg/service-common/utils" + "git.hlsq.asia/mmorpg/service-qgdzs/internal/dao/repository" + "git.hlsq.asia/mmorpg/service-qgdzs/internal/timer" +) + +// 玩法 - 随机答题 + +// RandomGetQuestion 获取题目 +func (s *Server) RandomGetQuestion(ctx context.Context, req *grpc_pb.RandomGetQuestionReq) (*grpc_pb.RandomGetQuestionResp, error) { + question, err := repository.NewQuestionDao(ctx, s.query).FindByRandom("") + if err != nil { + return nil, utils.ErrorsWrap(err) + } + options := make([]string, 0) + if err = json.Unmarshal([]byte(question.Options), &options); err != nil { + return nil, utils.ErrorsWrapF(err, "data: %v", question.Options) + } + category, _ := repository.NewCategoryDao(ctx, s.query).FindNameBySn(question.CategorySn) + return &grpc_pb.RandomGetQuestionResp{ + Sn: question.Sn, + Question: question.Question, + Options: options, + Category: category, + Difficulty: question.Difficulty, + }, nil +} + +// RandomAnswerQuestion 回答题目 +func (s *Server) RandomAnswerQuestion(ctx context.Context, req *grpc_pb.RandomAnswerQuestionReq) (*grpc_pb.RandomAnswerQuestionResp, error) { + utils.ShouldBindUsn(ctx, &req.USN) + + question, err := repository.NewQuestionDao(ctx, s.query).FindBySn(req.Sn) + if err != nil { + return nil, utils.ErrorsWrap(err) + } + options := make([]string, 0) + if err = json.Unmarshal([]byte(question.Options), &options); err != nil { + return nil, utils.ErrorsWrapF(err, "data: %v", question.Options) + } + // 保存答题记录 + if req.USN != "" { + kafka.NewProducer().Produce(ctx, &timer.TopicQuestionAnswer{ + MessageSn: utils.SnowflakeInstance().Generate().String(), + USN: req.USN, + QuestionSn: question.Sn, + QuestionAnswer: question.Answer, + Answer: req.Answer, + }) + + // 随机答题正确给10分,错误不得分 + if req.Answer == question.Answer { + kafka.NewProducer().Produce(ctx, &timer.TopicAddPoint{ + MessageSn: utils.SnowflakeInstance().Generate().String(), + Source: timer.AddPointSourceRandom, + USN: req.USN, + Point: 10, + }) + } + } + return &grpc_pb.RandomAnswerQuestionResp{ + Answer: question.Answer, + Explanation: question.Explanation, + }, nil +} diff --git a/internal/grpc_server/server_record.go b/internal/grpc_server/server_record.go new file mode 100644 index 0000000..11cb1e1 --- /dev/null +++ b/internal/grpc_server/server_record.go @@ -0,0 +1,59 @@ +package grpc_server + +import ( + "context" + "encoding/json" + "git.hlsq.asia/mmorpg/service-common/net/http/http_resp" + "git.hlsq.asia/mmorpg/service-common/proto/rs/grpc_pb" + "git.hlsq.asia/mmorpg/service-common/utils" + "git.hlsq.asia/mmorpg/service-qgdzs/internal/dao/repository" +) + +// 模块 - 答题记录 + +// GetRecord 获取答题记录 +func (s *Server) GetRecord(ctx context.Context, req *grpc_pb.GetRecordReq) (*grpc_pb.GetRecordResp, error) { + if !utils.ShouldBindUsn(ctx, &req.USN) { + return nil, http_resp.ParamError + } + records, count, err := repository.NewRecordDao(ctx, s.query).FindByUSN(req.USN, int(req.Page), int(req.PageSize)) + if err != nil { + return nil, utils.ErrorsWrap(err) + } + resp := make([]*grpc_pb.GetRecordItem, 0) + for _, record := range records { + resp = append(resp, &grpc_pb.GetRecordItem{ + QuestionSn: record.QuestionSn, + Question: record.Question, + Difficulty: record.Difficulty, + Category: record.Category, + QuestionAnswer: record.QuestionAnswer, + Answer: record.Answer, + CreateTime: record.CreatedAt.Unix(), + }) + } + return &grpc_pb.GetRecordResp{ + Count: int32(count), + Records: resp, + }, nil +} + +// GetQuestionInfo 获取具体的题目 +func (s *Server) GetQuestionInfo(ctx context.Context, req *grpc_pb.GetQuestionInfoReq) (*grpc_pb.GetQuestionInfoResp, error) { + question, err := repository.NewQuestionDao(ctx, s.query).FindBySn(req.QuestionSn) + if err != nil { + return nil, utils.ErrorsWrap(err) + } + options := make([]string, 0) + if err = json.Unmarshal([]byte(question.Options), &options); err != nil { + return nil, utils.ErrorsWrapF(err, "data: %v", question.Options) + } + category, _ := repository.NewCategoryDao(ctx, s.query).FindNameBySn(question.CategorySn) + return &grpc_pb.GetQuestionInfoResp{ + Question: question.Question, + Options: options, + Category: category, + Difficulty: question.Difficulty, + Explanation: question.Explanation, + }, nil +} diff --git a/internal/timer/consumer.go b/internal/timer/consumer.go index 01d7242..29f8c7e 100644 --- a/internal/timer/consumer.go +++ b/internal/timer/consumer.go @@ -2,7 +2,6 @@ package timer import ( "context" - "encoding/json" "errors" "git.hlsq.asia/mmorpg/service-common/db/kafka" "git.hlsq.asia/mmorpg/service-common/log" @@ -12,6 +11,14 @@ import ( "gorm.io/gorm" ) +// !!!消费者必须做幂等!!! +func startConsumer() { + go kafka.NewConsumer().Consume([]kafka.Topic{ + &TopicQuestionAnswer{}, + &TopicAddPoint{}, + }) +} + type TopicQuestionAnswer struct { MessageSn string `json:"messageSn"` USN string `json:"usn"` @@ -24,6 +31,34 @@ func (t *TopicQuestionAnswer) Name() string { return "qgdzs.question.answer" } +func (t *TopicQuestionAnswer) OnMessage(ctx context.Context) error { + log.Infof("Kafka consume topic: %v: %#+v", t.Name(), t) + if t.USN == "" || t.QuestionSn == "" { + return utils.ErrorsWrap(errors.New("invalid data")) + } + + // 答题记录 + isCorrect := int32(0) + if t.QuestionAnswer == t.Answer { + isCorrect = 1 + } + _, err := repository.NewRecordDao(ctx, repository.Query()).Create(&model.Record{ + Sn: t.MessageSn, + UserSn: t.USN, + QuestionSn: t.QuestionSn, + Answer: t.Answer, + IsCorrect: isCorrect, + }) + if err != nil { + if errors.Is(err, gorm.ErrDuplicatedKey) { + return nil + } + return utils.ErrorsWrap(err) + } + + return nil +} + type TopicAddPoint struct { MessageSn string `json:"messageSn"` Source AddPointSource `json:"source"` @@ -44,65 +79,25 @@ func (t *TopicAddPoint) Name() string { return "qgdzs.user.point" } -// !!!消费者必须做幂等!!! -func startConsumer() { - go kafka.NewConsumer().Consume(map[kafka.Topic]kafka.Handler{ - &TopicQuestionAnswer{}: func(ctx context.Context, msg []byte) error { - data := &TopicQuestionAnswer{} - if err := json.Unmarshal(msg, data); err != nil { - return err - } - log.Infof("Kafka consume topic: %v: %#+v", data.Name(), data) - if data.USN == "" || data.QuestionSn == "" { - return utils.ErrorsWrap(errors.New("invalid data")) - } +func (t *TopicAddPoint) OnMessage(ctx context.Context) error { + log.Infof("Kafka consume topic: %v: %#+v", t.Name(), t) + if t.USN == "" || t.Point == 0 { + return utils.ErrorsWrap(errors.New("invalid data")) + } - // 答题记录 - isCorrect := int32(0) - if data.QuestionAnswer == data.Answer { - isCorrect = 1 - } - _, err := repository.NewRecordDao(ctx, repository.Query()).Create(&model.Record{ - Sn: data.MessageSn, - UserSn: data.USN, - QuestionSn: data.QuestionSn, - Answer: data.Answer, - IsCorrect: isCorrect, - }) - if err != nil { - if errors.Is(err, gorm.ErrDuplicatedKey) { - return nil - } - return utils.ErrorsWrap(err) - } - - return nil - }, - &TopicAddPoint{}: func(ctx context.Context, msg []byte) error { - data := &TopicAddPoint{} - if err := json.Unmarshal(msg, data); err != nil { - return err - } - log.Infof("Kafka consume topic: %v: %#+v", data.Name(), data) - if data.USN == "" || data.Point == 0 { - return utils.ErrorsWrap(errors.New("invalid data")) - } - - // 积分记录 & 加分 - err := repository.NewPointRecordsDao(ctx, repository.Query()).CreateAndIncrPointCard(&model.PointRecord{ - Sn: data.MessageSn, - UserSn: data.USN, - Source: int32(data.Source), - Point: data.Point, - }) - if err != nil { - if errors.Is(err, gorm.ErrDuplicatedKey) { - return nil - } - return utils.ErrorsWrap(err) - } - - return nil - }, + // 积分记录 & 加分 + err := repository.NewPointRecordsDao(ctx, repository.Query()).CreateAndIncrPointCard(&model.PointRecord{ + Sn: t.MessageSn, + UserSn: t.USN, + Source: int32(t.Source), + Point: t.Point, }) + if err != nil { + if errors.Is(err, gorm.ErrDuplicatedKey) { + return nil + } + return utils.ErrorsWrap(err) + } + + return nil }