diff --git a/cmd/gormgen/README.md b/cmd/gormgen/README.md index b33018a..11276d7 100644 --- a/cmd/gormgen/README.md +++ b/cmd/gormgen/README.md @@ -34,6 +34,6 @@ eg : ```shell # 根目录下执行 -go run cmd/gormgen/main.go -dsn "root:api2api..@tcp(sh-cynosdbmysql-grp-88th45wy.sql.tencentcdb.com:28555)/mini_chat?charset=utf8mb4&parseTime=True&loc=Local" -tables "log_operation,log_request,admin,app_keyword,app_keyword_reply,app_user,app_message_log,mini_program" +go run cmd/gormgen/main.go -dsn "root:api2api..@tcp(sh-cynosdbmysql-grp-88th45wy.sql.tencentcdb.com:28555)/mini_chat?charset=utf8mb4&parseTime=True&loc=Local" -tables "log_operation,log_request,admin,app_keyword,app_keyword_reply,app_user,app_message_log,mini_program,mini_program_access_token" ``` diff --git a/internal/pkg/miniprogram/access_token.go b/internal/pkg/miniprogram/access_token.go new file mode 100644 index 0000000..f48d0f4 --- /dev/null +++ b/internal/pkg/miniprogram/access_token.go @@ -0,0 +1,39 @@ +package miniprogram + +import ( + "fmt" + + "github.com/go-resty/resty/v2" +) + +type AccessTokenResponse struct { + AccessToken string `json:"access_token"` + ExpiresIn int `json:"expires_in"` +} + +// GetAccessToken 获取微信 access_token +// DOC: https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-access-token/getAccessToken.html +func GetAccessToken(appID, appSecret string, resultStruct interface{}) error { + requestData := map[string]string{} + requestData["grant_type"] = "client_credential" + requestData["appid"] = appID + requestData["secret"] = appSecret + + response, err := resty.New().R(). + ForceContentType("application/json"). + SetHeader("Content-Type", "application/json"). + SetQueryParams(requestData). + SetResult(resultStruct). + Get("https://api.weixin.qq.com/cgi-bin/token") + + if err != nil { + return err + } + + // 检查响应状态码 + if response.IsError() { + return fmt.Errorf("服务异常(%d)", response.StatusCode()) + } + + return nil +} diff --git a/internal/pkg/miniprogram/access_token_test.go b/internal/pkg/miniprogram/access_token_test.go new file mode 100644 index 0000000..9eb9da3 --- /dev/null +++ b/internal/pkg/miniprogram/access_token_test.go @@ -0,0 +1,16 @@ +package miniprogram + +import ( + "testing" +) + +func TestGetAccessToken(t *testing.T) { + res := new(AccessTokenResponse) + err := GetAccessToken("wx26ad074017e1e63f", "026c19ce4f3bb090c56573024c59a8be", res) + if err != nil { + t.Error(err) + } + + t.Logf("access_token: %s", res.AccessToken) + t.Logf("expires_in: %d", res.ExpiresIn) +} diff --git a/internal/pkg/miniprogram/subscribe.go b/internal/pkg/miniprogram/subscribe.go new file mode 100644 index 0000000..7a5bb1a --- /dev/null +++ b/internal/pkg/miniprogram/subscribe.go @@ -0,0 +1,58 @@ +package miniprogram + +import ( + "fmt" + + "github.com/go-resty/resty/v2" +) + +type SendSubscribeMessageResponse struct { + Errcode int `json:"errcode"` + Errmsg string `json:"errmsg"` +} + +type SendSubscribeMessageRequest struct { + Touser string `json:"touser"` + TemplateID string `json:"template_id"` + Page string `json:"page"` + MiniprogramState string `json:"miniprogram_state"` + Lang string `json:"lang"` + Data struct { + Thing1 struct { + Value string `json:"value"` + } `json:"thing1"` + Time2 struct { + Value string `json:"value"` + } `json:"time2"` + Thing4 struct { + Value string `json:"value"` + } `json:"thing4"` + } `json:"data"` +} + +// SendSubscribeMessage 发送订阅消息 +// DOC: https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-message-management/subscribe-message/sendMessage.html +func SendSubscribeMessage(accessToken string, requestData *SendSubscribeMessageRequest, resultStruct interface{}) error { + response, err := resty.New().R(). + ForceContentType("application/json"). + SetHeader("Content-Type", "application/json"). + SetBody(requestData). + SetResult(resultStruct). + Post(fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=%s", accessToken)) + + if err != nil { + return err + } + + // 检查响应状态码 + if response.IsError() { + return fmt.Errorf("服务异常(%d)", response.StatusCode()) + } + + // 检查响应结果 + if response.Result().(*SendSubscribeMessageResponse).Errcode != 0 { + return fmt.Errorf("服务异常(%d): %s", response.Result().(*SendSubscribeMessageResponse).Errcode, response.Result().(*SendSubscribeMessageResponse).Errmsg) + } + + return nil +} diff --git a/internal/pkg/miniprogram/subscribe_test.go b/internal/pkg/miniprogram/subscribe_test.go new file mode 100644 index 0000000..40a308b --- /dev/null +++ b/internal/pkg/miniprogram/subscribe_test.go @@ -0,0 +1,32 @@ +package miniprogram + +import ( + "testing" +) + +func TestSendSubscribeMessage(t *testing.T) { + res := new(AccessTokenResponse) + err := GetAccessToken("wx26ad074017e1e63f", "026c19ce4f3bb090c56573024c59a8be", res) + if err != nil { + t.Errorf("获取 access_token 错误: %s", err.Error()) + } + + sendSubscribeMessageRequest := new(SendSubscribeMessageRequest) + sendSubscribeMessageRequest.Touser = "onjlN4wrsGe09SdRrpl4D_FWBh0I" + sendSubscribeMessageRequest.TemplateID = "9dCV3z7vRPBGm8iMtSXsD7ZVyUjld46w7HTH9zLnzWw" + sendSubscribeMessageRequest.Page = "pages/contact/index" + sendSubscribeMessageRequest.MiniprogramState = "trial" + sendSubscribeMessageRequest.Lang = "zh_CN" + sendSubscribeMessageRequest.Data.Thing1.Value = "测试(CC)" + sendSubscribeMessageRequest.Data.Time2.Value = "2025-10-27" + sendSubscribeMessageRequest.Data.Thing4.Value = "测试(CC)" + + sendSubscribeMessageResponse := new(SendSubscribeMessageResponse) + err = SendSubscribeMessage(res.AccessToken, sendSubscribeMessageRequest, sendSubscribeMessageResponse) + if err != nil { + t.Error(err) + } + + t.Logf("Errcode: %d", sendSubscribeMessageResponse.Errcode) + t.Logf("Errmsg: %s", sendSubscribeMessageResponse.Errmsg) +} diff --git a/internal/repository/mysql/dao/app_message_log.gen.go b/internal/repository/mysql/dao/app_message_log.gen.go index e98d9a2..73db85b 100644 --- a/internal/repository/mysql/dao/app_message_log.gen.go +++ b/internal/repository/mysql/dao/app_message_log.gen.go @@ -35,8 +35,8 @@ func newAppMessageLog(db *gorm.DB, opts ...gen.DOOption) appMessageLog { _appMessageLog.ReceiverID = field.NewString(tableName, "receiver_id") _appMessageLog.MsgType = field.NewInt32(tableName, "msg_type") _appMessageLog.Content = field.NewString(tableName, "content") - _appMessageLog.IsRead = field.NewInt32(tableName, "is_read") _appMessageLog.CreatedAt = field.NewTime(tableName, "created_at") + _appMessageLog.IsRead = field.NewInt32(tableName, "is_read") _appMessageLog.fillFieldMap() @@ -56,8 +56,8 @@ type appMessageLog struct { ReceiverID field.String // 接收人ID MsgType field.Int32 // 消息类型(1:文本 2:图片) Content field.String // 消息内容 - IsRead field.Int32 // 是否已读(0:未读 1:已读) CreatedAt field.Time // 创建时间 + IsRead field.Int32 // 是否已读(0:未读 1:已读) fieldMap map[string]field.Expr } @@ -83,6 +83,7 @@ func (a *appMessageLog) updateTableName(table string) *appMessageLog { a.MsgType = field.NewInt32(table, "msg_type") a.Content = field.NewString(table, "content") a.CreatedAt = field.NewTime(table, "created_at") + a.IsRead = field.NewInt32(table, "is_read") a.fillFieldMap() @@ -108,8 +109,8 @@ func (a *appMessageLog) fillFieldMap() { a.fieldMap["receiver_id"] = a.ReceiverID a.fieldMap["msg_type"] = a.MsgType a.fieldMap["content"] = a.Content - a.fieldMap["is_read"] = a.IsRead a.fieldMap["created_at"] = a.CreatedAt + a.fieldMap["is_read"] = a.IsRead } func (a appMessageLog) clone(db *gorm.DB) appMessageLog { diff --git a/internal/repository/mysql/dao/gen.go b/internal/repository/mysql/dao/gen.go index 371c10f..3d78192 100644 --- a/internal/repository/mysql/dao/gen.go +++ b/internal/repository/mysql/dao/gen.go @@ -25,6 +25,7 @@ var ( LogOperation *logOperation LogRequest *logRequest MiniProgram *miniProgram + MiniProgramAccessToken *miniProgramAccessToken ) func SetDefault(db *gorm.DB, opts ...gen.DOOption) { @@ -37,48 +38,52 @@ func SetDefault(db *gorm.DB, opts ...gen.DOOption) { LogOperation = &Q.LogOperation LogRequest = &Q.LogRequest MiniProgram = &Q.MiniProgram + MiniProgramAccessToken = &Q.MiniProgramAccessToken } func Use(db *gorm.DB, opts ...gen.DOOption) *Query { return &Query{ - db: db, - Admin: newAdmin(db, opts...), - AppKeyword: newAppKeyword(db, opts...), - AppKeywordReply: newAppKeywordReply(db, opts...), - AppMessageLog: newAppMessageLog(db, opts...), - AppUser: newAppUser(db, opts...), - LogOperation: newLogOperation(db, opts...), - LogRequest: newLogRequest(db, opts...), - MiniProgram: newMiniProgram(db, opts...), + db: db, + Admin: newAdmin(db, opts...), + AppKeyword: newAppKeyword(db, opts...), + AppKeywordReply: newAppKeywordReply(db, opts...), + AppMessageLog: newAppMessageLog(db, opts...), + AppUser: newAppUser(db, opts...), + LogOperation: newLogOperation(db, opts...), + LogRequest: newLogRequest(db, opts...), + MiniProgram: newMiniProgram(db, opts...), + MiniProgramAccessToken: newMiniProgramAccessToken(db, opts...), } } type Query struct { db *gorm.DB - Admin admin - AppKeyword appKeyword - AppKeywordReply appKeywordReply - AppMessageLog appMessageLog - AppUser appUser - LogOperation logOperation - LogRequest logRequest - MiniProgram miniProgram + Admin admin + AppKeyword appKeyword + AppKeywordReply appKeywordReply + AppMessageLog appMessageLog + AppUser appUser + LogOperation logOperation + LogRequest logRequest + MiniProgram miniProgram + MiniProgramAccessToken miniProgramAccessToken } func (q *Query) Available() bool { return q.db != nil } func (q *Query) clone(db *gorm.DB) *Query { return &Query{ - db: db, - Admin: q.Admin.clone(db), - AppKeyword: q.AppKeyword.clone(db), - AppKeywordReply: q.AppKeywordReply.clone(db), - AppMessageLog: q.AppMessageLog.clone(db), - AppUser: q.AppUser.clone(db), - LogOperation: q.LogOperation.clone(db), - LogRequest: q.LogRequest.clone(db), - MiniProgram: q.MiniProgram.clone(db), + db: db, + Admin: q.Admin.clone(db), + AppKeyword: q.AppKeyword.clone(db), + AppKeywordReply: q.AppKeywordReply.clone(db), + AppMessageLog: q.AppMessageLog.clone(db), + AppUser: q.AppUser.clone(db), + LogOperation: q.LogOperation.clone(db), + LogRequest: q.LogRequest.clone(db), + MiniProgram: q.MiniProgram.clone(db), + MiniProgramAccessToken: q.MiniProgramAccessToken.clone(db), } } @@ -92,49 +97,52 @@ func (q *Query) WriteDB() *Query { func (q *Query) ReplaceDB(db *gorm.DB) *Query { return &Query{ - db: db, - Admin: q.Admin.replaceDB(db), - AppKeyword: q.AppKeyword.replaceDB(db), - AppKeywordReply: q.AppKeywordReply.replaceDB(db), - AppMessageLog: q.AppMessageLog.replaceDB(db), - AppUser: q.AppUser.replaceDB(db), - LogOperation: q.LogOperation.replaceDB(db), - LogRequest: q.LogRequest.replaceDB(db), - MiniProgram: q.MiniProgram.replaceDB(db), + db: db, + Admin: q.Admin.replaceDB(db), + AppKeyword: q.AppKeyword.replaceDB(db), + AppKeywordReply: q.AppKeywordReply.replaceDB(db), + AppMessageLog: q.AppMessageLog.replaceDB(db), + AppUser: q.AppUser.replaceDB(db), + LogOperation: q.LogOperation.replaceDB(db), + LogRequest: q.LogRequest.replaceDB(db), + MiniProgram: q.MiniProgram.replaceDB(db), + MiniProgramAccessToken: q.MiniProgramAccessToken.replaceDB(db), } } type queryCtx struct { - Admin *adminDo - AppKeyword *appKeywordDo - AppKeywordReply *appKeywordReplyDo - AppMessageLog *appMessageLogDo - AppUser *appUserDo - LogOperation *logOperationDo - LogRequest *logRequestDo - MiniProgram *miniProgramDo + Admin *adminDo + AppKeyword *appKeywordDo + AppKeywordReply *appKeywordReplyDo + AppMessageLog *appMessageLogDo + AppUser *appUserDo + LogOperation *logOperationDo + LogRequest *logRequestDo + MiniProgram *miniProgramDo + MiniProgramAccessToken *miniProgramAccessTokenDo } func (q *Query) WithContext(ctx context.Context) *queryCtx { return &queryCtx{ - Admin: q.Admin.WithContext(ctx), - AppKeyword: q.AppKeyword.WithContext(ctx), - AppKeywordReply: q.AppKeywordReply.WithContext(ctx), - AppMessageLog: q.AppMessageLog.WithContext(ctx), - AppUser: q.AppUser.WithContext(ctx), - LogOperation: q.LogOperation.WithContext(ctx), - LogRequest: q.LogRequest.WithContext(ctx), - MiniProgram: q.MiniProgram.WithContext(ctx), + Admin: q.Admin.WithContext(ctx), + AppKeyword: q.AppKeyword.WithContext(ctx), + AppKeywordReply: q.AppKeywordReply.WithContext(ctx), + AppMessageLog: q.AppMessageLog.WithContext(ctx), + AppUser: q.AppUser.WithContext(ctx), + LogOperation: q.LogOperation.WithContext(ctx), + LogRequest: q.LogRequest.WithContext(ctx), + MiniProgram: q.MiniProgram.WithContext(ctx), + MiniProgramAccessToken: q.MiniProgramAccessToken.WithContext(ctx), } } func (q *Query) Transaction(fc func(tx *Query) error, opts ...*sql.TxOptions) error { - return q.db.Transaction(func(tx *gorm.DB) error { return fc(q.ReplaceDB(tx)) }, opts...) + return q.db.Transaction(func(tx *gorm.DB) error { return fc(q.clone(tx)) }, opts...) } func (q *Query) Begin(opts ...*sql.TxOptions) *QueryTx { tx := q.db.Begin(opts...) - return &QueryTx{Query: q.ReplaceDB(tx), Error: tx.Error} + return &QueryTx{Query: q.clone(tx), Error: tx.Error} } type QueryTx struct { diff --git a/internal/repository/mysql/dao/mini_program_access_token.gen.go b/internal/repository/mysql/dao/mini_program_access_token.gen.go new file mode 100644 index 0000000..b7bea45 --- /dev/null +++ b/internal/repository/mysql/dao/mini_program_access_token.gen.go @@ -0,0 +1,336 @@ +// 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 dao + +import ( + "context" + + "gorm.io/gorm" + "gorm.io/gorm/clause" + "gorm.io/gorm/schema" + + "gorm.io/gen" + "gorm.io/gen/field" + + "gorm.io/plugin/dbresolver" + + "mini-chat/internal/repository/mysql/model" +) + +func newMiniProgramAccessToken(db *gorm.DB, opts ...gen.DOOption) miniProgramAccessToken { + _miniProgramAccessToken := miniProgramAccessToken{} + + _miniProgramAccessToken.miniProgramAccessTokenDo.UseDB(db, opts...) + _miniProgramAccessToken.miniProgramAccessTokenDo.UseModel(&model.MiniProgramAccessToken{}) + + tableName := _miniProgramAccessToken.miniProgramAccessTokenDo.TableName() + _miniProgramAccessToken.ALL = field.NewAsterisk(tableName) + _miniProgramAccessToken.ID = field.NewInt32(tableName, "id") + _miniProgramAccessToken.AppID = field.NewString(tableName, "app_id") + _miniProgramAccessToken.AccessToken = field.NewString(tableName, "access_token") + _miniProgramAccessToken.CreatedAt = field.NewTime(tableName, "created_at") + _miniProgramAccessToken.ExpiredAt = field.NewTime(tableName, "expired_at") + + _miniProgramAccessToken.fillFieldMap() + + return _miniProgramAccessToken +} + +// miniProgramAccessToken 小程序 access_token 表 +type miniProgramAccessToken struct { + miniProgramAccessTokenDo + + ALL field.Asterisk + ID field.Int32 // 主键 + AppID field.String // app_id + AccessToken field.String // access_token + CreatedAt field.Time // 创建时间 + ExpiredAt field.Time // 过期时间 + + fieldMap map[string]field.Expr +} + +func (m miniProgramAccessToken) Table(newTableName string) *miniProgramAccessToken { + m.miniProgramAccessTokenDo.UseTable(newTableName) + return m.updateTableName(newTableName) +} + +func (m miniProgramAccessToken) As(alias string) *miniProgramAccessToken { + m.miniProgramAccessTokenDo.DO = *(m.miniProgramAccessTokenDo.As(alias).(*gen.DO)) + return m.updateTableName(alias) +} + +func (m *miniProgramAccessToken) updateTableName(table string) *miniProgramAccessToken { + m.ALL = field.NewAsterisk(table) + m.ID = field.NewInt32(table, "id") + m.AppID = field.NewString(table, "app_id") + m.AccessToken = field.NewString(table, "access_token") + m.CreatedAt = field.NewTime(table, "created_at") + m.ExpiredAt = field.NewTime(table, "expired_at") + + m.fillFieldMap() + + return m +} + +func (m *miniProgramAccessToken) GetFieldByName(fieldName string) (field.OrderExpr, bool) { + _f, ok := m.fieldMap[fieldName] + if !ok || _f == nil { + return nil, false + } + _oe, ok := _f.(field.OrderExpr) + return _oe, ok +} + +func (m *miniProgramAccessToken) fillFieldMap() { + m.fieldMap = make(map[string]field.Expr, 5) + m.fieldMap["id"] = m.ID + m.fieldMap["app_id"] = m.AppID + m.fieldMap["access_token"] = m.AccessToken + m.fieldMap["created_at"] = m.CreatedAt + m.fieldMap["expired_at"] = m.ExpiredAt +} + +func (m miniProgramAccessToken) clone(db *gorm.DB) miniProgramAccessToken { + m.miniProgramAccessTokenDo.ReplaceConnPool(db.Statement.ConnPool) + return m +} + +func (m miniProgramAccessToken) replaceDB(db *gorm.DB) miniProgramAccessToken { + m.miniProgramAccessTokenDo.ReplaceDB(db) + return m +} + +type miniProgramAccessTokenDo struct{ gen.DO } + +func (m miniProgramAccessTokenDo) Debug() *miniProgramAccessTokenDo { + return m.withDO(m.DO.Debug()) +} + +func (m miniProgramAccessTokenDo) WithContext(ctx context.Context) *miniProgramAccessTokenDo { + return m.withDO(m.DO.WithContext(ctx)) +} + +func (m miniProgramAccessTokenDo) ReadDB() *miniProgramAccessTokenDo { + return m.Clauses(dbresolver.Read) +} + +func (m miniProgramAccessTokenDo) WriteDB() *miniProgramAccessTokenDo { + return m.Clauses(dbresolver.Write) +} + +func (m miniProgramAccessTokenDo) Session(config *gorm.Session) *miniProgramAccessTokenDo { + return m.withDO(m.DO.Session(config)) +} + +func (m miniProgramAccessTokenDo) Clauses(conds ...clause.Expression) *miniProgramAccessTokenDo { + return m.withDO(m.DO.Clauses(conds...)) +} + +func (m miniProgramAccessTokenDo) Returning(value interface{}, columns ...string) *miniProgramAccessTokenDo { + return m.withDO(m.DO.Returning(value, columns...)) +} + +func (m miniProgramAccessTokenDo) Not(conds ...gen.Condition) *miniProgramAccessTokenDo { + return m.withDO(m.DO.Not(conds...)) +} + +func (m miniProgramAccessTokenDo) Or(conds ...gen.Condition) *miniProgramAccessTokenDo { + return m.withDO(m.DO.Or(conds...)) +} + +func (m miniProgramAccessTokenDo) Select(conds ...field.Expr) *miniProgramAccessTokenDo { + return m.withDO(m.DO.Select(conds...)) +} + +func (m miniProgramAccessTokenDo) Where(conds ...gen.Condition) *miniProgramAccessTokenDo { + return m.withDO(m.DO.Where(conds...)) +} + +func (m miniProgramAccessTokenDo) Order(conds ...field.Expr) *miniProgramAccessTokenDo { + return m.withDO(m.DO.Order(conds...)) +} + +func (m miniProgramAccessTokenDo) Distinct(cols ...field.Expr) *miniProgramAccessTokenDo { + return m.withDO(m.DO.Distinct(cols...)) +} + +func (m miniProgramAccessTokenDo) Omit(cols ...field.Expr) *miniProgramAccessTokenDo { + return m.withDO(m.DO.Omit(cols...)) +} + +func (m miniProgramAccessTokenDo) Join(table schema.Tabler, on ...field.Expr) *miniProgramAccessTokenDo { + return m.withDO(m.DO.Join(table, on...)) +} + +func (m miniProgramAccessTokenDo) LeftJoin(table schema.Tabler, on ...field.Expr) *miniProgramAccessTokenDo { + return m.withDO(m.DO.LeftJoin(table, on...)) +} + +func (m miniProgramAccessTokenDo) RightJoin(table schema.Tabler, on ...field.Expr) *miniProgramAccessTokenDo { + return m.withDO(m.DO.RightJoin(table, on...)) +} + +func (m miniProgramAccessTokenDo) Group(cols ...field.Expr) *miniProgramAccessTokenDo { + return m.withDO(m.DO.Group(cols...)) +} + +func (m miniProgramAccessTokenDo) Having(conds ...gen.Condition) *miniProgramAccessTokenDo { + return m.withDO(m.DO.Having(conds...)) +} + +func (m miniProgramAccessTokenDo) Limit(limit int) *miniProgramAccessTokenDo { + return m.withDO(m.DO.Limit(limit)) +} + +func (m miniProgramAccessTokenDo) Offset(offset int) *miniProgramAccessTokenDo { + return m.withDO(m.DO.Offset(offset)) +} + +func (m miniProgramAccessTokenDo) Scopes(funcs ...func(gen.Dao) gen.Dao) *miniProgramAccessTokenDo { + return m.withDO(m.DO.Scopes(funcs...)) +} + +func (m miniProgramAccessTokenDo) Unscoped() *miniProgramAccessTokenDo { + return m.withDO(m.DO.Unscoped()) +} + +func (m miniProgramAccessTokenDo) Create(values ...*model.MiniProgramAccessToken) error { + if len(values) == 0 { + return nil + } + return m.DO.Create(values) +} + +func (m miniProgramAccessTokenDo) CreateInBatches(values []*model.MiniProgramAccessToken, batchSize int) error { + return m.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 (m miniProgramAccessTokenDo) Save(values ...*model.MiniProgramAccessToken) error { + if len(values) == 0 { + return nil + } + return m.DO.Save(values) +} + +func (m miniProgramAccessTokenDo) First() (*model.MiniProgramAccessToken, error) { + if result, err := m.DO.First(); err != nil { + return nil, err + } else { + return result.(*model.MiniProgramAccessToken), nil + } +} + +func (m miniProgramAccessTokenDo) Take() (*model.MiniProgramAccessToken, error) { + if result, err := m.DO.Take(); err != nil { + return nil, err + } else { + return result.(*model.MiniProgramAccessToken), nil + } +} + +func (m miniProgramAccessTokenDo) Last() (*model.MiniProgramAccessToken, error) { + if result, err := m.DO.Last(); err != nil { + return nil, err + } else { + return result.(*model.MiniProgramAccessToken), nil + } +} + +func (m miniProgramAccessTokenDo) Find() ([]*model.MiniProgramAccessToken, error) { + result, err := m.DO.Find() + return result.([]*model.MiniProgramAccessToken), err +} + +func (m miniProgramAccessTokenDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.MiniProgramAccessToken, err error) { + buf := make([]*model.MiniProgramAccessToken, 0, batchSize) + err = m.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 (m miniProgramAccessTokenDo) FindInBatches(result *[]*model.MiniProgramAccessToken, batchSize int, fc func(tx gen.Dao, batch int) error) error { + return m.DO.FindInBatches(result, batchSize, fc) +} + +func (m miniProgramAccessTokenDo) Attrs(attrs ...field.AssignExpr) *miniProgramAccessTokenDo { + return m.withDO(m.DO.Attrs(attrs...)) +} + +func (m miniProgramAccessTokenDo) Assign(attrs ...field.AssignExpr) *miniProgramAccessTokenDo { + return m.withDO(m.DO.Assign(attrs...)) +} + +func (m miniProgramAccessTokenDo) Joins(fields ...field.RelationField) *miniProgramAccessTokenDo { + for _, _f := range fields { + m = *m.withDO(m.DO.Joins(_f)) + } + return &m +} + +func (m miniProgramAccessTokenDo) Preload(fields ...field.RelationField) *miniProgramAccessTokenDo { + for _, _f := range fields { + m = *m.withDO(m.DO.Preload(_f)) + } + return &m +} + +func (m miniProgramAccessTokenDo) FirstOrInit() (*model.MiniProgramAccessToken, error) { + if result, err := m.DO.FirstOrInit(); err != nil { + return nil, err + } else { + return result.(*model.MiniProgramAccessToken), nil + } +} + +func (m miniProgramAccessTokenDo) FirstOrCreate() (*model.MiniProgramAccessToken, error) { + if result, err := m.DO.FirstOrCreate(); err != nil { + return nil, err + } else { + return result.(*model.MiniProgramAccessToken), nil + } +} + +func (m miniProgramAccessTokenDo) FindByPage(offset int, limit int) (result []*model.MiniProgramAccessToken, count int64, err error) { + result, err = m.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 = m.Offset(-1).Limit(-1).Count() + return +} + +func (m miniProgramAccessTokenDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) { + count, err = m.Count() + if err != nil { + return + } + + err = m.Offset(offset).Limit(limit).Scan(result) + return +} + +func (m miniProgramAccessTokenDo) Scan(result interface{}) (err error) { + return m.DO.Scan(result) +} + +func (m miniProgramAccessTokenDo) Delete(models ...*model.MiniProgramAccessToken) (result gen.ResultInfo, err error) { + return m.DO.Delete(models) +} + +func (m *miniProgramAccessTokenDo) withDO(do gen.Dao) *miniProgramAccessTokenDo { + m.DO = *do.(*gen.DO) + return m +} diff --git a/internal/repository/mysql/model/app_message_log.gen.go b/internal/repository/mysql/model/app_message_log.gen.go index f0b9fee..b8dc38d 100644 --- a/internal/repository/mysql/model/app_message_log.gen.go +++ b/internal/repository/mysql/model/app_message_log.gen.go @@ -20,8 +20,8 @@ type AppMessageLog struct { ReceiverID string `gorm:"column:receiver_id;not null;comment:接收人ID" json:"receiver_id"` // 接收人ID MsgType int32 `gorm:"column:msg_type;not null;comment:消息类型(1:文本 2:图片)" json:"msg_type"` // 消息类型(1:文本 2:图片) Content string `gorm:"column:content;not null;comment:消息内容" json:"content"` // 消息内容 - IsRead int32 `gorm:"column:is_read;not null;default:0;comment:是否已读(0:未读 1:已读)" json:"is_read"` // 是否已读(0:未读 1:已读) CreatedAt time.Time `gorm:"column:created_at;not null;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间 + IsRead int32 `gorm:"column:is_read;comment:是否已读(0:未读 1:已读)" json:"is_read"` // 是否已读(0:未读 1:已读) } // TableName AppMessageLog's table name diff --git a/internal/repository/mysql/model/mini_program_access_token.gen.go b/internal/repository/mysql/model/mini_program_access_token.gen.go new file mode 100644 index 0000000..866b56d --- /dev/null +++ b/internal/repository/mysql/model/mini_program_access_token.gen.go @@ -0,0 +1,25 @@ +// 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" +) + +const TableNameMiniProgramAccessToken = "mini_program_access_token" + +// MiniProgramAccessToken 小程序 access_token 表 +type MiniProgramAccessToken struct { + ID int32 `gorm:"column:id;primaryKey;autoIncrement:true;comment:主键" json:"id"` // 主键 + AppID string `gorm:"column:app_id;not null;comment:app_id" json:"app_id"` // app_id + AccessToken string `gorm:"column:access_token;not null;comment:access_token" json:"access_token"` // access_token + CreatedAt time.Time `gorm:"column:created_at;not null;default:CURRENT_TIMESTAMP;comment:创建时间" json:"created_at"` // 创建时间 + ExpiredAt time.Time `gorm:"column:expired_at;not null;default:CURRENT_TIMESTAMP;comment:过期时间" json:"expired_at"` // 过期时间 +} + +// TableName MiniProgramAccessToken's table name +func (*MiniProgramAccessToken) TableName() string { + return TableNameMiniProgramAccessToken +} diff --git a/internal/services/services_miniprogram/miniprogram.go b/internal/services/services_miniprogram/miniprogram.go new file mode 100644 index 0000000..26db2e6 --- /dev/null +++ b/internal/services/services_miniprogram/miniprogram.go @@ -0,0 +1,32 @@ +package services_miniprogram + +import ( + "mini-chat/internal/pkg/core" + "mini-chat/internal/pkg/logger" + "mini-chat/internal/repository/mysql" + "mini-chat/internal/repository/mysql/dao" +) + +var _ Service = (*service)(nil) + +type Service interface { + i() + + GetAccessToken(appID, appSecret string, ctx core.Context) (string, error) +} + +type service struct { + logger logger.CustomLogger + writeDB *dao.Query + readDB *dao.Query +} + +func New(logger logger.CustomLogger, db mysql.Repo) Service { + return &service{ + logger: logger, + writeDB: dao.Use(db.GetDbW()), + readDB: dao.Use(db.GetDbR()), + } +} + +func (s *service) i() {} diff --git a/internal/services/services_miniprogram/miniprogram_access_token.go b/internal/services/services_miniprogram/miniprogram_access_token.go new file mode 100644 index 0000000..5e6098a --- /dev/null +++ b/internal/services/services_miniprogram/miniprogram_access_token.go @@ -0,0 +1,53 @@ +package services_miniprogram + +import ( + "fmt" + "time" + + "mini-chat/internal/pkg/core" + "mini-chat/internal/pkg/miniprogram" + "mini-chat/internal/repository/mysql/model" + + "go.uber.org/zap" + "gorm.io/gorm" +) + +func (s *service) GetAccessToken(appID, appSecret string, ctx core.Context) (string, error) { + info, err := s.readDB.MiniProgramAccessToken.WithContext(ctx.RequestContext()). + Where(s.readDB.MiniProgramAccessToken.AppID.Eq(appID)). + Where(s.readDB.MiniProgramAccessToken.ExpiredAt.Gte(time.Now())).First() + if err != nil && err != gorm.ErrRecordNotFound { + return "", err + } + + accessToken := "" + + if err == gorm.ErrRecordNotFound { + // 生成 access_token + accessTokenResponse := new(miniprogram.AccessTokenResponse) + if err := miniprogram.GetAccessToken(appID, appSecret, accessTokenResponse); err != nil { + s.logger.Error("GetAccessToken failed", zap.Error(err)) + return "", err + } + + if accessTokenResponse.AccessToken == "" { + return "", fmt.Errorf("access_token is empty") + } + + // 保存 access_token + accessTokenData := new(model.MiniProgramAccessToken) + accessTokenData.AppID = appID + accessTokenData.AccessToken = accessTokenResponse.AccessToken + accessTokenData.CreatedAt = time.Now() + accessTokenData.ExpiredAt = time.Now().Add(time.Duration(accessTokenResponse.ExpiresIn) * time.Second) + if err := s.writeDB.MiniProgramAccessToken.WithContext(ctx.RequestContext()).Create(accessTokenData); err != nil { + return "", err + } + + accessToken = accessTokenResponse.AccessToken + } else { + accessToken = info.AccessToken + } + + return accessToken, nil +}