diff --git a/app/grpc.go b/app/grpc.go index b1f3774..2ce47b6 100644 --- a/app/grpc.go +++ b/app/grpc.go @@ -3,7 +3,7 @@ package app import ( "git.hlsq.asia/mmorpg/service-common/net/grpc/service" "git.hlsq.asia/mmorpg/service-qgdzs/config" - "git.hlsq.asia/mmorpg/service-qgdzs/internal/grpc_server/server" + "git.hlsq.asia/mmorpg/service-qgdzs/internal/grpc_server" ) // ModuleGrpcServer Grpc服务模块 @@ -16,7 +16,7 @@ func (m *ModuleGrpcServer) init() error { } func (m *ModuleGrpcServer) start() error { - m.server = server.NewServer(config.Get().Serve.Grpc.TTL) + m.server = grpc_server.NewServer(config.Get().Serve.Grpc.TTL) m.server.Init(config.Get().Serve.Grpc.Address, config.Get().Serve.Grpc.Port) return nil } diff --git a/config/config.dev.yaml b/config/config.dev.yaml index e9d9593..ca531fc 100644 --- a/config/config.dev.yaml +++ b/config/config.dev.yaml @@ -18,7 +18,7 @@ db: maxIdleConn: 20 connMaxLifetimeSec: 600 connMaxIdleTimeSec: 180 - logLevel: "warn" + logLevel: "info" redis: addr: "47.108.184.184:6379" password: "lQ7aM8oB6lK0iD5k" diff --git a/go.mod b/go.mod index 77ebf49..d6b8bef 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module git.hlsq.asia/mmorpg/service-qgdzs go 1.23.1 require ( - git.hlsq.asia/mmorpg/service-common v0.0.0-20260113095415-34b2e45e3d50 + git.hlsq.asia/mmorpg/service-common v0.0.0-20260114100922-695f5e7b0497 github.com/judwhite/go-svc v1.2.1 google.golang.org/grpc v1.71.1 gorm.io/gen v0.3.27 diff --git a/go.sum b/go.sum index 7a6b9fa..e289616 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,5 @@ 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-20260113095415-34b2e45e3d50 h1:T0JZl2N+HMYRgyifEGFXD2y1MZOuoAS1xjnIg/JLGwM= -git.hlsq.asia/mmorpg/service-common v0.0.0-20260113095415-34b2e45e3d50/go.mod h1:xv6m1I2jUA6mudKVznygpnzMoshBQarthHD1QnkW4qc= github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0= github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= diff --git a/internal/dao/model/records.gen.go b/internal/dao/model/records.gen.go new file mode 100644 index 0000000..f928139 --- /dev/null +++ b/internal/dao/model/records.gen.go @@ -0,0 +1,40 @@ +// 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 TableNameRecord = "records" + +// Record mapped from table +type Record 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"` // 用户-唯一编号 + QuestionSn string `gorm:"column:question_sn;not null;comment:题目-唯一编号" json:"question_sn"` // 题目-唯一编号 + Answer string `gorm:"column:answer;not null;comment:答案" json:"answer"` // 答案 + IsCorrect int32 `gorm:"column:is_correct;not null;comment:是否正确 0 否 1 是" json:"is_correct"` // 是否正确 0 否 1 是 + 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 Record's table name +func (*Record) TableName() string { + return TableNameRecord +} + +// Auto sn +func (m *Record) 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 f9d8ca8..f5d18a1 100644 --- a/internal/dao/query/gen.go +++ b/internal/dao/query/gen.go @@ -20,6 +20,7 @@ func Use(db *gorm.DB, opts ...gen.DOOption) *Query { db: db, Category: newCategory(db, opts...), Question: newQuestion(db, opts...), + Record: newRecord(db, opts...), } } @@ -28,6 +29,7 @@ type Query struct { Category category Question question + Record record } func (q *Query) Available() bool { return q.db != nil } @@ -37,6 +39,7 @@ func (q *Query) clone(db *gorm.DB) *Query { db: db, Category: q.Category.clone(db), Question: q.Question.clone(db), + Record: q.Record.clone(db), } } @@ -53,18 +56,21 @@ func (q *Query) ReplaceDB(db *gorm.DB) *Query { db: db, Category: q.Category.replaceDB(db), Question: q.Question.replaceDB(db), + Record: q.Record.replaceDB(db), } } type queryCtx struct { Category *categoryDo 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), } } diff --git a/internal/dao/query/records.gen.go b/internal/dao/query/records.gen.go new file mode 100644 index 0000000..0da10d5 --- /dev/null +++ b/internal/dao/query/records.gen.go @@ -0,0 +1,359 @@ +// 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 newRecord(db *gorm.DB, opts ...gen.DOOption) record { + _record := record{} + + _record.recordDo.UseDB(db, opts...) + _record.recordDo.UseModel(&model.Record{}) + + tableName := _record.recordDo.TableName() + _record.ALL = field.NewAsterisk(tableName) + _record.ID = field.NewUint64(tableName, "id") + _record.Sn = field.NewString(tableName, "sn") + _record.UserSn = field.NewString(tableName, "user_sn") + _record.QuestionSn = field.NewString(tableName, "question_sn") + _record.Answer = field.NewString(tableName, "answer") + _record.IsCorrect = field.NewInt32(tableName, "is_correct") + _record.CreatedAt = field.NewTime(tableName, "created_at") + _record.UpdatedAt = field.NewTime(tableName, "updated_at") + _record.DeletedAt = field.NewField(tableName, "deleted_at") + + _record.fillFieldMap() + + return _record +} + +type record struct { + recordDo recordDo + + ALL field.Asterisk + ID field.Uint64 + Sn field.String // 业务唯一编号 + UserSn field.String // 用户-唯一编号 + QuestionSn field.String // 题目-唯一编号 + Answer field.String // 答案 + IsCorrect field.Int32 // 是否正确 0 否 1 是 + CreatedAt field.Time + UpdatedAt field.Time + DeletedAt field.Field + + fieldMap map[string]field.Expr +} + +func (r record) Table(newTableName string) *record { + r.recordDo.UseTable(newTableName) + return r.updateTableName(newTableName) +} + +func (r record) As(alias string) *record { + r.recordDo.DO = *(r.recordDo.As(alias).(*gen.DO)) + return r.updateTableName(alias) +} + +func (r *record) updateTableName(table string) *record { + r.ALL = field.NewAsterisk(table) + r.ID = field.NewUint64(table, "id") + r.Sn = field.NewString(table, "sn") + r.UserSn = field.NewString(table, "user_sn") + r.QuestionSn = field.NewString(table, "question_sn") + r.Answer = field.NewString(table, "answer") + r.IsCorrect = field.NewInt32(table, "is_correct") + r.CreatedAt = field.NewTime(table, "created_at") + r.UpdatedAt = field.NewTime(table, "updated_at") + r.DeletedAt = field.NewField(table, "deleted_at") + + r.fillFieldMap() + + return r +} + +func (r *record) WithContext(ctx context.Context) *recordDo { return r.recordDo.WithContext(ctx) } + +func (r record) TableName() string { return r.recordDo.TableName() } + +func (r record) Alias() string { return r.recordDo.Alias() } + +func (r record) Columns(cols ...field.Expr) gen.Columns { return r.recordDo.Columns(cols...) } + +func (r *record) GetFieldByName(fieldName string) (field.OrderExpr, bool) { + _f, ok := r.fieldMap[fieldName] + if !ok || _f == nil { + return nil, false + } + _oe, ok := _f.(field.OrderExpr) + return _oe, ok +} + +func (r *record) fillFieldMap() { + r.fieldMap = make(map[string]field.Expr, 9) + r.fieldMap["id"] = r.ID + r.fieldMap["sn"] = r.Sn + r.fieldMap["user_sn"] = r.UserSn + r.fieldMap["question_sn"] = r.QuestionSn + r.fieldMap["answer"] = r.Answer + r.fieldMap["is_correct"] = r.IsCorrect + r.fieldMap["created_at"] = r.CreatedAt + r.fieldMap["updated_at"] = r.UpdatedAt + r.fieldMap["deleted_at"] = r.DeletedAt +} + +func (r record) clone(db *gorm.DB) record { + r.recordDo.ReplaceConnPool(db.Statement.ConnPool) + return r +} + +func (r record) replaceDB(db *gorm.DB) record { + r.recordDo.ReplaceDB(db) + return r +} + +type recordDo struct{ gen.DO } + +func (r recordDo) Debug() *recordDo { + return r.withDO(r.DO.Debug()) +} + +func (r recordDo) WithContext(ctx context.Context) *recordDo { + return r.withDO(r.DO.WithContext(ctx)) +} + +func (r recordDo) ReadDB() *recordDo { + return r.Clauses(dbresolver.Read) +} + +func (r recordDo) WriteDB() *recordDo { + return r.Clauses(dbresolver.Write) +} + +func (r recordDo) Session(config *gorm.Session) *recordDo { + return r.withDO(r.DO.Session(config)) +} + +func (r recordDo) Clauses(conds ...clause.Expression) *recordDo { + return r.withDO(r.DO.Clauses(conds...)) +} + +func (r recordDo) Returning(value interface{}, columns ...string) *recordDo { + return r.withDO(r.DO.Returning(value, columns...)) +} + +func (r recordDo) Not(conds ...gen.Condition) *recordDo { + return r.withDO(r.DO.Not(conds...)) +} + +func (r recordDo) Or(conds ...gen.Condition) *recordDo { + return r.withDO(r.DO.Or(conds...)) +} + +func (r recordDo) Select(conds ...field.Expr) *recordDo { + return r.withDO(r.DO.Select(conds...)) +} + +func (r recordDo) Where(conds ...gen.Condition) *recordDo { + return r.withDO(r.DO.Where(conds...)) +} + +func (r recordDo) Order(conds ...field.Expr) *recordDo { + return r.withDO(r.DO.Order(conds...)) +} + +func (r recordDo) Distinct(cols ...field.Expr) *recordDo { + return r.withDO(r.DO.Distinct(cols...)) +} + +func (r recordDo) Omit(cols ...field.Expr) *recordDo { + return r.withDO(r.DO.Omit(cols...)) +} + +func (r recordDo) Join(table schema.Tabler, on ...field.Expr) *recordDo { + return r.withDO(r.DO.Join(table, on...)) +} + +func (r recordDo) LeftJoin(table schema.Tabler, on ...field.Expr) *recordDo { + return r.withDO(r.DO.LeftJoin(table, on...)) +} + +func (r recordDo) RightJoin(table schema.Tabler, on ...field.Expr) *recordDo { + return r.withDO(r.DO.RightJoin(table, on...)) +} + +func (r recordDo) Group(cols ...field.Expr) *recordDo { + return r.withDO(r.DO.Group(cols...)) +} + +func (r recordDo) Having(conds ...gen.Condition) *recordDo { + return r.withDO(r.DO.Having(conds...)) +} + +func (r recordDo) Limit(limit int) *recordDo { + return r.withDO(r.DO.Limit(limit)) +} + +func (r recordDo) Offset(offset int) *recordDo { + return r.withDO(r.DO.Offset(offset)) +} + +func (r recordDo) Scopes(funcs ...func(gen.Dao) gen.Dao) *recordDo { + return r.withDO(r.DO.Scopes(funcs...)) +} + +func (r recordDo) Unscoped() *recordDo { + return r.withDO(r.DO.Unscoped()) +} + +func (r recordDo) Create(values ...*model.Record) error { + if len(values) == 0 { + return nil + } + return r.DO.Create(values) +} + +func (r recordDo) CreateInBatches(values []*model.Record, batchSize int) error { + return r.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 (r recordDo) Save(values ...*model.Record) error { + if len(values) == 0 { + return nil + } + return r.DO.Save(values) +} + +func (r recordDo) First() (*model.Record, error) { + if result, err := r.DO.First(); err != nil { + return nil, err + } else { + return result.(*model.Record), nil + } +} + +func (r recordDo) Take() (*model.Record, error) { + if result, err := r.DO.Take(); err != nil { + return nil, err + } else { + return result.(*model.Record), nil + } +} + +func (r recordDo) Last() (*model.Record, error) { + if result, err := r.DO.Last(); err != nil { + return nil, err + } else { + return result.(*model.Record), nil + } +} + +func (r recordDo) Find() ([]*model.Record, error) { + result, err := r.DO.Find() + return result.([]*model.Record), err +} + +func (r recordDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.Record, err error) { + buf := make([]*model.Record, 0, batchSize) + err = r.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 (r recordDo) FindInBatches(result *[]*model.Record, batchSize int, fc func(tx gen.Dao, batch int) error) error { + return r.DO.FindInBatches(result, batchSize, fc) +} + +func (r recordDo) Attrs(attrs ...field.AssignExpr) *recordDo { + return r.withDO(r.DO.Attrs(attrs...)) +} + +func (r recordDo) Assign(attrs ...field.AssignExpr) *recordDo { + return r.withDO(r.DO.Assign(attrs...)) +} + +func (r recordDo) Joins(fields ...field.RelationField) *recordDo { + for _, _f := range fields { + r = *r.withDO(r.DO.Joins(_f)) + } + return &r +} + +func (r recordDo) Preload(fields ...field.RelationField) *recordDo { + for _, _f := range fields { + r = *r.withDO(r.DO.Preload(_f)) + } + return &r +} + +func (r recordDo) FirstOrInit() (*model.Record, error) { + if result, err := r.DO.FirstOrInit(); err != nil { + return nil, err + } else { + return result.(*model.Record), nil + } +} + +func (r recordDo) FirstOrCreate() (*model.Record, error) { + if result, err := r.DO.FirstOrCreate(); err != nil { + return nil, err + } else { + return result.(*model.Record), nil + } +} + +func (r recordDo) FindByPage(offset int, limit int) (result []*model.Record, count int64, err error) { + result, err = r.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 = r.Offset(-1).Limit(-1).Count() + return +} + +func (r recordDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) { + count, err = r.Count() + if err != nil { + return + } + + err = r.Offset(offset).Limit(limit).Scan(result) + return +} + +func (r recordDo) Scan(result interface{}) (err error) { + return r.DO.Scan(result) +} + +func (r recordDo) Delete(models ...*model.Record) (result gen.ResultInfo, err error) { + return r.DO.Delete(models) +} + +func (r *recordDo) withDO(do gen.Dao) *recordDo { + r.DO = *do.(*gen.DO) + return r +} diff --git a/internal/dao/repository/records.go b/internal/dao/repository/records.go new file mode 100644 index 0000000..7a662e2 --- /dev/null +++ b/internal/dao/repository/records.go @@ -0,0 +1,64 @@ +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" + "time" +) + +type RecordDao struct { + ctx context.Context + query *query.Query + cache *redis.CacheClient +} + +func NewRecordDao(ctx context.Context, cache ...*redis.CacheClient) *RecordDao { + dao := &RecordDao{ + ctx: ctx, + query: query.Use(mysql.GetDB(dbName)), + } + if len(cache) > 0 { + dao.cache = cache[0] + } + return dao +} + +func (d *RecordDao) Create(record *model.Record) (*model.Record, error) { + err := d.query.Record.WithContext(d.ctx). + Create(record) + return record, err +} + +type RecordItem struct { + QuestionSn string `gorm:"column:question_sn"` + Question string `gorm:"column:question"` + Difficulty int32 `gorm:"column:difficulty"` + Category string `gorm:"column:category"` + IsCorrect int32 `gorm:"column:is_correct"` + CreatedAt time.Time `gorm:"column:created_at"` +} + +func (d *RecordDao) FindByUSN(usn string, page, pageSize int) ([]*RecordItem, int64, error) { + result := make([]*RecordItem, 0) + count, err := d.query.Record.WithContext(d.ctx). + Select( + d.query.Question.Sn.As("question_sn"), + d.query.Question.Question, + d.query.Question.Difficulty, + d.query.Category.Category, + d.query.Record.IsCorrect, + d.query.Record.CreatedAt, + ). + Where(d.query.Record.UserSn.Eq(usn)). + LeftJoin(d.query.Question, d.query.Question.Sn.EqCol(d.query.Record.QuestionSn)). + LeftJoin(d.query.Category, d.query.Category.Sn.EqCol(d.query.Question.CategorySn)). + Order(d.query.Record.CreatedAt.Desc()). + ScanByPage(&result, (page-1)*pageSize, pageSize) + if err != nil { + return nil, 0, err + } + return result, count, nil +} diff --git a/internal/grpc_server/server/server_question_temp.go b/internal/grpc_server/server/server_question_temp.go deleted file mode 100644 index a7365f7..0000000 --- a/internal/grpc_server/server/server_question_temp.go +++ /dev/null @@ -1,161 +0,0 @@ -package server - -// -//import ( -// "bytes" -// "context" -// "encoding/json" -// "git.hlsq.asia/mmorpg/service-common/log" -// "git.hlsq.asia/mmorpg/service-common/proto/rs/grpc_pb" -// "io" -// "net/http" -//) -// -//var question = []string{ -// "本次对话 数据来源必须:东方财富,巨潮资讯,官网,数据截止:自上市至2025年第3季度\n帮我查询一下京东方科技集团股份有限公司公司的所有财报数量", -// "帮我从东方财富 / 巨潮资讯 / 官网这三个来源整理一下重大事项公告的数量,按照三个维度划分( 并购重组公告披露,关联交易公告,对外投资公告)", -// "帮我从东方财富 / 巨潮资讯 / 官网这三个来源整理一下投资者关系活动,按照三个维度划分( 年度业绩说明次数, 投资者调研次数,接待机构数量)", -// "帮我查询一下ESG与治理的信息各个维度评分评级", -// "帮我查询一下这个公司监管合规信息,综合评估一下本年度监管合规(信披评分,公司治理,内控合规,ESG表现,财务合规,业务运营合规,股东与投资者权益保护合规,合规风险管理)百分制", -// "将本次对话的所有内容总结和完整的装入这个结构体生成json对象数据返回,注意:必须以json(markdown代码)的形式返回,并且只输出json即可不用输出其它说明,下面内容就是结构体: \n// CompanyComplianceScore 公司合规评分\ntype CompanyComplianceScore struct {\n\tInformationDisclosureRating string `json:\"informationDisclosureRating\" bson:\"information_disclosure_rating\" comment:\"信息披露评级\"`//获取交易所的信披评级,如A/B/C等\n\tRegularReportFrequencyCount *RegularReportFrequency `json:\"regularReportFrequency\" bson:\"regular_report_frequency\" comment:\"定期报告披露频次\"`\n\tMajorAnnouncementCount *MajorAnnouncementCount `json:\"majorAnnouncementCount\" bson:\"major_announcement_count\" comment:\"重大事项公告\"`\n\tInvestorRelationsActivity *InvestorRelationsActivity `json:\"InvestorRelationsActivity\" bson:\"investor_relations_activity\" comment:\"投资者关系活动\"`\n\tEsgReport *EsgReport `json:\"esgReport\" bson:\"esg_report\" comment:\"ESG与治理\"`\n\tDisclosureScore float64 `json:\"disclosureScore\" bson:\"disclosure_score\" comment:\"信息披露专项评分\"`\n\tCorporateGovernanceScore float64 `json:\"corporateGovernanceScore\" bson:\"corporate_governance_score\" comment:\"公司治理评分\"`\n\tInternalControlCompliance float64 `json:\"internalControlCompliance\" bson:\"internal_control_compliance\" comment:\"内控合规评分\"`\n\tEsgPerformanceScore float64 `json:\"esgPerformanceScore\" bson:\"esg_performance_score\" comment:\"ESG表现评分\"`\n\tFinancialComplianceScore float64 `json:\"financialComplianceScore\" bson:\"financial_compliance_score\" comment:\"财务合规评分\"`\n\tBusinessOperationCompliance float64 `json:\"businessOperationCompliance\" bson:\"business_operation_compliance\" comment:\"业务运营合规评分\"`\n\tShareholderRightsCompliance float64 `json:\"shareholderRightsCompliance\" bson:\"shareholder_rights_compliance\" comment:\"股东权益保护合规评分\"`\n\tComplianceRiskManagement float64 `json:\"complianceRiskManagement\" bson:\"compliance_risk_management\" comment:\"合规风险管理评分\"`\n\tOverallComprehensiveScore float64 `json:\"overallComprehensiveScore\" bson:\"overall_comprehensive_score\" comment:\"综合评分\"`\n}\n\n// RegularReportFrequency 定期报告披露频次\ntype RegularReportFrequency struct {\n\tAnnualReportCount int `json:\"annualReportCount\" bson:\"annual_report_count\" comment:\"年报数量\"`\n\tSemiAnnualReportCount int `json:\"semiAnnualReportCount\" bson:\"semi_annual_report_count\" comment:\"半年报数量\"`\n\tQuarterlyReportCount int `json:\"quarterlyReportCount\" bson:\"quarterly_report_count\" comment:\"季度报告数量\"`\n\tTotalReportCount int `json:\"totalReportCount\" bson:\"total_report_count\" comment:\"报告总数\"`\n}\n\n// MajorAnnouncementCount 重大事项公告\ntype MajorAnnouncementCount struct {\n\tMnaAnnouncementCount int `json:\"mnaAnnouncementCount\" bson:\"mna_announcement_count\" comment:\"并购重组公告数量\"`\n\tRelatedTransactionCount int `json:\"relatedTransactionCount\" bson:\"related_transaction_count\" comment:\"关联交易公告数量\"`\n\tForeignInvestmentCount int `json:\"foreignInvestmentCount\" bson:\"foreign_investment_count\" comment:\"对外投资公告数量\"`\n}\n\n// InvestorRelationsActivity 投资者关系活动\ntype InvestorRelationsActivity struct {\n\tAnnualPerformanceBriefings int `json:\"annualPerformanceBriefings\" bson:\"annual_performance_briefings\" comment:\"年度业绩说明次数\"`\n\tInvestorResearchCount int `json:\"investorResearchCount\" bson:\"investor_research_count\" comment:\"投资者调研次数\"`\n\tInstitutionsReceivedCount int `json:\"institutionsReceivedCount\" bson:\"institutions_received_count\" comment:\"接待机构数量\"`\n}\n\n// EsgReport ESG与治理\ntype EsgReport struct {\n\tEsgOverallScore float64 `json:\"esgOverallScore\" bson:\"esg_overall_score\" comment:\"ESG综合评分\"`//通过【上海华证指数信息服务有限公司】披露的公司ESG评级,例如BBB,不允许返回分数\n\tEnvironmentalScore float64 `json:\"environmentalScore\" bson:\"environmental_score\" comment:\"环境评分\"`\n\tSocialScore float64 `json:\"socialScore\" bson:\"social_score\" comment:\"社会评分\"`\n\tGovernanceScore float64 `json:\"governanceScore\" bson:\"governance_score\" comment:\"治理评分\"`\n}", -//} -// -//type Message struct { -// Role string `json:"role"` -// Content string `json:"content"` -//} -// -//type Input struct { -// Messages []Message `json:"messages"` -//} -// -//type Parameters struct { -// EnableSearch bool `json:"enable_search"` -// ResultFormat string `json:"result_format"` -// SearchOptions SearchOptions `json:"search_options"` -//} -// -//type SearchOptions struct { -// SearchStrategy string `json:"search_strategy"` -//} -// -//type RequestBody struct { -// Model string `json:"model"` -// Input Input `json:"input"` -// Parameters Parameters `json:"parameters"` -//} -// -//type ResponseBody struct { -// Output struct { -// Choices []struct { -// Message Message `json:"message"` -// FinishReason string `json:"finish_reason"` -// } `json:"choices"` -// } `json:"output"` -// Usage struct { -// InputTokens int32 `json:"input_tokens"` -// OutputTokens int32 `json:"output_tokens"` -// TotalTokens int32 `json:"total_tokens"` -// } `json:"usage"` -//} -// -//func (s *Server) GenerateQuestion(ctx context.Context, req *grpc_pb.GenerateQuestionReq) (*grpc_pb.GenerateQuestionResp, error) { -// inToken, outToken := int32(0), int32(0) -// -// messages := make([]Message, 0) -// for i, step := range question { -// messages = append(messages, newUserMessage(step)) -// -// //chatCompletion, err := client.Chat.Completions.New( -// // context.Background(), openai.ChatCompletionNewParams{ -// // Messages: messages, -// // Model: "qwen3-max", -// // }, -// //) -// //if err != nil { -// // return nil, err -// //} -// r, err := request(messages) -// if err != nil { -// return nil, err -// } -// inToken += r.Usage.InputTokens -// outToken += r.Usage.OutputTokens -// log.Infof("回答 %v:%v", i, r) -// //break -// messages = append(messages, r.Output.Choices[0].Message) -// -// //log.Infof("回答 %v:%v", i, chatCompletion.Choices[0].Message.Content) -// //messages = append(messages, openai.AssistantMessage(chatCompletion.Choices[0].Message.Content)) -// } -// log.Infof("返回:%v", messages[len(messages)-1].Content) -// //log.Infof("消耗token:%v %v, 费用:%v", inToken, outToken, float64(inToken)/1000*0.0032+float64(outToken)/1000*0.0128) -// log.Infof("消耗token:%v %v, 费用:%v", inToken, outToken, float64(inToken)/1000*0.002+float64(outToken)/1000*0.003) -// -// //chatCompletion, err := client.Chat.Completions.New( -// // context.Background(), openai.ChatCompletionNewParams{ -// // Messages: []openai.ChatCompletionMessageParamUnion{ -// // openai.UserMessage("你能做什么"), -// // }, -// // Model: "qwen3-max", -// // }, -// //) -// //if err != nil { -// // return nil, err -// //} -// //for i, choice := range chatCompletion.Choices { -// // log.Infof("chatCompletion %v: %#+v \n", i, choice) -// //} -// //log.Infof("返回:%v", chatCompletion.Choices[0].Message.Content) -// return nil, nil -//} -// -//func newUserMessage(content string) Message { -// return Message{ -// Role: "user", -// Content: content, -// } -//} -// -//func request(messages []Message) (*ResponseBody, error) { -// client := &http.Client{} -// requestBody := RequestBody{ -// //Model: "qwen3-max", -// Model: "deepseek-v3.2", -// Input: Input{ -// Messages: messages, -// }, -// Parameters: Parameters{ -// EnableSearch: true, -// SearchOptions: SearchOptions{ -// SearchStrategy: "max", -// }, -// -// ResultFormat: "message", -// }, -// } -// jsonData, _ := json.Marshal(requestBody) -// -// req, err := http.NewRequest("POST", "https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation", bytes.NewBuffer(jsonData)) -// if err != nil { -// return nil, err -// } -// req.Header.Set("Authorization", "Bearer "+"sk-4daf94ad2fa94288b198d2a1d8924cef") -// req.Header.Set("Content-Type", "application/json") -// -// resp, err := client.Do(req) -// if err != nil { -// return nil, err -// } -// defer resp.Body.Close() -// -// body, err := io.ReadAll(resp.Body) -// if err != nil { -// return nil, err -// } -// var res ResponseBody -// if err = json.Unmarshal(body, &res); err != nil { -// return nil, err -// } -// return &res, nil -//} diff --git a/internal/grpc_server/server/server_init.go b/internal/grpc_server/server_init.go similarity index 96% rename from internal/grpc_server/server/server_init.go rename to internal/grpc_server/server_init.go index 10d33ca..7bd34c1 100644 --- a/internal/grpc_server/server/server_init.go +++ b/internal/grpc_server/server_init.go @@ -1,4 +1,4 @@ -package server +package grpc_server import ( "git.hlsq.asia/mmorpg/service-common/discover/common" diff --git a/internal/grpc_server/server/server_question.go b/internal/grpc_server/server_question.go similarity index 81% rename from internal/grpc_server/server/server_question.go rename to internal/grpc_server/server_question.go index 1cb14e0..0b5d709 100644 --- a/internal/grpc_server/server/server_question.go +++ b/internal/grpc_server/server_question.go @@ -7,7 +7,9 @@ import ( "fmt" "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" @@ -15,23 +17,6 @@ import ( "time" ) -//var prompt = []string{` -//你是一个微信小游戏的内容策划,请生成 %v 道“每日趣味题”,要求: -//- 避免敏感、争议或超纲内容 -//- 每次从 [动物冷知识, 科学趣理, 语言陷阱, 历史轶事, 地理奇观, 数学谜题, 艺术人文, 综合杂学] 中**随机选一个类型** -//- 确保选项有迷惑性,解析有趣 -//- 绝对要避免重复,现在的时间是:%v -//- 输出格式为 JSON 数组,不要任何解释、不要Markdown、不要任何其他字符: -// [{ -// "question": "题目文本", // 简洁,30字以内 -// "options": ["A. 选项1", "B. 选项2", "C. 选项3", "D. 选项4"], // 提供4个选项(A/B/C/D),其中仅1个正确 -// "answer": "C", // 答案 -// "explanation": "解析文本", // 200字以内,尽量幽默有趣 -// "category": "分类", // 只能从上面列出的类型中选择 -// "difficulty": 100, // 难度分 0 - 100 -// }] -//`} - var prompt = []string{` 你是一个微信小游戏的内容策划,我需要生成“每日趣味题”,要求: - 避免敏感、争议或超纲内容 @@ -139,6 +124,7 @@ 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 { @@ -160,6 +146,7 @@ func (s *Server) GetQuestion(ctx context.Context, req *grpc_pb.GetQuestionReq) ( }, 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 { @@ -171,12 +158,26 @@ func (s *Server) AnswerQuestion(ctx context.Context, req *grpc_pb.AnswerQuestion log.Errorf("AnswerQuestion json.Unmarshal error: %v, data: %v", err, question.Options) return nil, err } + // 保存答题记录 + if utils.ShouldBindUsn(ctx, &req.USN) { + isCorrect := int32(0) + if req.Answer == question.Answer { + isCorrect = 1 + } + _, _ = repository.NewRecordDao(ctx).Create(&model.Record{ + UserSn: req.USN, + QuestionSn: question.Sn, + Answer: req.Answer, + IsCorrect: isCorrect, + }) + } 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 { @@ -194,3 +195,30 @@ func (s *Server) GetAllCategory(ctx context.Context, req *grpc_pb.GetAllCategory 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 { + log.Errorf("GetRecord error: %v", err) + return nil, 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, + IsCorrect: record.IsCorrect, + CreateTime: record.CreatedAt.Unix(), + }) + } + return &grpc_pb.GetRecordResp{ + Count: int32(count), + Records: resp, + }, nil +}