fix version 1
This commit is contained in:
@@ -21,7 +21,7 @@ func Run() {
|
||||
impl.NewImpl()
|
||||
|
||||
// 初始化服务
|
||||
s := server.New(config.Spec.Addr)
|
||||
s := server.New(nil)
|
||||
srv := service.New(
|
||||
s.Grpc,
|
||||
&service.Options{
|
||||
|
||||
@@ -1,45 +1,35 @@
|
||||
Name: {ServiceKey}
|
||||
ListenOn: 0.0.0.0:12212
|
||||
Service: feed
|
||||
Port: 12212
|
||||
|
||||
Dsn: postgres://postgres:CHANGE_ME@47.108.57.74:5432/milu?sslmode=disable&TimeZone=Asia/Shanghai
|
||||
Databases:
|
||||
Driver: postgres
|
||||
Source:
|
||||
- postgres://postgres:CHANGE_ME@127.0.0.1:5432/bsm_dev?sslmode=disable&TimeZone=Asia/Shanghai
|
||||
|
||||
# cache DB的选择请在后面直接带参数,不带会自动HASH计算选择DB库。
|
||||
# DB库设置例:redis://null:Weidong1214@139.159.232.50:6379/12,代表DB12
|
||||
# Cache: redis://null:YouMake!*@139.159.232.50:6379/
|
||||
# DB库设置例:redis://null:CHANGE_ME@139.159.232.50:6379/12,代表DB12
|
||||
Cache: redis://null:CHANGE_ME@127.0.0.1:6379/
|
||||
|
||||
#Etcd:
|
||||
# Hosts:
|
||||
# - 127.0.0.1:2379
|
||||
# Key: service.{Workspace}.{ServiceKey}.rpc
|
||||
Etcd:
|
||||
Endpoints:
|
||||
- "127.0.0.1:2379"
|
||||
|
||||
# 匿名访问清单,自动注入网关层
|
||||
Anonymous:
|
||||
Key: anonymous.{Workspace}
|
||||
Urls:
|
||||
- pb.Check.Hello
|
||||
# 微服务设置
|
||||
MicroService:
|
||||
Enable: false
|
||||
|
||||
# 日志记录
|
||||
# Gateway 设置
|
||||
Gateway:
|
||||
Enable: true
|
||||
Port: 12213
|
||||
|
||||
# 日志记录(Level:0-debug 1-info 2-warn 3-error 4-fatal)
|
||||
Log:
|
||||
ServiceName: {ServiceKey}
|
||||
Mode: file
|
||||
Path: logs/{ServiceKey}
|
||||
Stat: false
|
||||
Name: feed
|
||||
Level: 0
|
||||
Dir: logs/feed
|
||||
Console: true
|
||||
File: true
|
||||
|
||||
# 性能监控
|
||||
Prometheus:
|
||||
Host: 127.0.0.1
|
||||
Port: 22212
|
||||
Path: /metrics
|
||||
|
||||
# 链路追踪
|
||||
Telemetry:
|
||||
Name: {Workspace}.{ServiceKey}.{RuntimeMode}
|
||||
Endpoint: http://139.159.232.50:14268/api/traces
|
||||
Sampler: 1.0
|
||||
Batcher: jaeger
|
||||
|
||||
# MQ Pulsar
|
||||
#Pulsar:
|
||||
# Endpoints: 127.0.0.1
|
||||
# Token: 9091
|
||||
# Namespaces: {Workspace}
|
||||
# 微服务调用密钥
|
||||
SecretKey: CHANGE_ME
|
||||
|
||||
@@ -1,44 +1,34 @@
|
||||
Name: {ServiceKey}
|
||||
ListenOn: 0.0.0.0:12212
|
||||
Service: feed
|
||||
Port: 12212
|
||||
|
||||
Dsn: postgres://prod:MakeW2023~PROD@192.168.0.224:5432/scf?sslmode=disable&TimeZone=Asia/Shanghai
|
||||
Databases:
|
||||
Driver: postgres
|
||||
Source:
|
||||
- postgres://prod:CHANGE_ME@192.168.0.224:5432/scf?sslmode=disable&TimeZone=Asia/Shanghai
|
||||
|
||||
# cache DB的选择请在后面直接带参数,不带会自动HASH计算选择DB库。
|
||||
Cache: redis://null:CHANGE_ME@192.168.0.43:6379/
|
||||
Cache: redis://null:CHANGE_ME@192.168.0.43:6379/
|
||||
|
||||
Etcd:
|
||||
Hosts:
|
||||
- 192.168.0.83:2379
|
||||
Key: service.{Workspace}.{ServiceKey}.rpc
|
||||
Endpoints:
|
||||
- "192.168.0.83:2379"
|
||||
|
||||
# 匿名访问清单,自动注入网关层
|
||||
Anonymous:
|
||||
Key: anonymous.{Workspace}
|
||||
Urls:
|
||||
- initialize.Check.Hello
|
||||
# 微服务设置
|
||||
MicroService:
|
||||
Enable: false
|
||||
|
||||
# 日志记录
|
||||
# Gateway 设置
|
||||
Gateway:
|
||||
Enable: true
|
||||
Port: 12213
|
||||
|
||||
# 日志记录(Level:0-debug 1-info 2-warn 3-error 4-fatal)
|
||||
Log:
|
||||
ServiceName: {ServiceKey}
|
||||
Mode: file
|
||||
Path: logs/{ServiceKey}
|
||||
Stat: false
|
||||
Name: feed
|
||||
Level: 1
|
||||
Dir: logs/feed
|
||||
Console: false
|
||||
File: true
|
||||
|
||||
# 性能监控
|
||||
Prometheus:
|
||||
Host: 127.0.0.1
|
||||
Port: 22212
|
||||
Path: /metrics
|
||||
|
||||
# 链路追踪
|
||||
Telemetry:
|
||||
Name: {Workspace}.{ServiceKey}.{RuntimeMode}
|
||||
Endpoint: http://139.159.232.50:14268/api/traces
|
||||
Sampler: 1.0
|
||||
Batcher: jaeger
|
||||
|
||||
# MQ Pulsar
|
||||
#Pulsar:
|
||||
# Endpoints: 127.0.0.1
|
||||
# Token: 9091
|
||||
# Namespaces: {Workspace}
|
||||
# 微服务调用密钥
|
||||
SecretKey: CHANGE_ME
|
||||
|
||||
@@ -1,45 +1,34 @@
|
||||
Name: {ServiceKey}
|
||||
ListenOn: 0.0.0.0:12212
|
||||
Service: feed
|
||||
Port: 12212
|
||||
|
||||
Dsn: postgres://postgres:CHANGE_ME@47.108.57.74:5432/milu?sslmode=disable&TimeZone=Asia/Shanghai
|
||||
Databases:
|
||||
Driver: postgres
|
||||
Source:
|
||||
- postgres://postgres:CHANGE_ME@127.0.0.1:5432/bsm_test?sslmode=disable&TimeZone=Asia/Shanghai
|
||||
|
||||
# cache DB的选择请在后面直接带参数,不带会自动HASH计算选择DB库。
|
||||
# DB库设置例:redis://null:Weidong1214@139.159.232.50:6379/12,代表DB12
|
||||
Cache: redis://null:CHANGE_ME@127.0.0.1:6379/
|
||||
Cache: redis://null:CHANGE_ME@127.0.0.1:6379/
|
||||
|
||||
Etcd:
|
||||
Hosts:
|
||||
- 127.0.0.1:2379
|
||||
Key: service.{Workspace}.{ServiceKey}.rpc
|
||||
Endpoints:
|
||||
- "127.0.0.1:2379"
|
||||
|
||||
# 匿名访问清单,自动注入网关层
|
||||
Anonymous:
|
||||
Key: anonymous.{Workspace}
|
||||
Urls:
|
||||
- pb.Check.Hello
|
||||
# 微服务设置
|
||||
MicroService:
|
||||
Enable: false
|
||||
|
||||
# 日志记录
|
||||
# Gateway 设置
|
||||
Gateway:
|
||||
Enable: true
|
||||
Port: 12213
|
||||
|
||||
# 日志记录(Level:0-debug 1-info 2-warn 3-error 4-fatal)
|
||||
Log:
|
||||
ServiceName: {ServiceKey}
|
||||
Mode: file
|
||||
Path: logs/{ServiceKey}
|
||||
Stat: false
|
||||
Name: feed
|
||||
Level: 0
|
||||
Dir: logs/feed
|
||||
Console: true
|
||||
File: true
|
||||
|
||||
# 性能监控
|
||||
Prometheus:
|
||||
Host: 127.0.0.1
|
||||
Port: 22212
|
||||
Path: /metrics
|
||||
|
||||
# 链路追踪
|
||||
Telemetry:
|
||||
Name: {Workspace}.{ServiceKey}.{RuntimeMode}
|
||||
Endpoint: http://139.159.232.50:14268/api/traces
|
||||
Sampler: 1.0
|
||||
Batcher: jaeger
|
||||
|
||||
# MQ Pulsar
|
||||
#Pulsar:
|
||||
# Endpoints: 127.0.0.1
|
||||
# Token: 9091
|
||||
# Namespaces: {Workspace}
|
||||
# 微服务调用密钥
|
||||
SecretKey: CHANGE_ME
|
||||
|
||||
@@ -22,6 +22,11 @@ func Action(ctx context.Context, in *pb.PostActionRequest) (reply *pb.DataStatus
|
||||
if in.GetActionOp() == "" || in.GetActionType() == "" || in.GetIdentity() == "" {
|
||||
return nil, errcode.ErrInvalidArgument
|
||||
}
|
||||
// 白名单校验,避免非法操作/类别拼出空列名或未绑定目标表的更新语句
|
||||
if (in.ActionOp != "ilike" && in.ActionOp != "unlike") ||
|
||||
(in.ActionType != "post" && in.ActionType != "comment") {
|
||||
return nil, errcode.ErrInvalidArgument
|
||||
}
|
||||
if err := models.LikeAction(in.ActionOp, in.ActionType, in.Identity); err != nil {
|
||||
printer.Error(err.Error())
|
||||
return nil, err
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
|
||||
// 修改推文
|
||||
func Change(ctx context.Context, in *pb.PostItem) (reply *pb.DataStatusReply, err error) {
|
||||
_, err = service.ParseMetaCtx(ctx, nil)
|
||||
auth, err := service.ParseMetaCtx(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -31,17 +31,22 @@ func Change(ctx context.Context, in *pb.PostItem) (reply *pb.DataStatusReply, er
|
||||
if len(in.GetAttachs()) != 0 {
|
||||
for _, val := range in.Attachs {
|
||||
postData.Attachs = append(postData.Attachs, models.FeedRelateAttach{
|
||||
Identity: utils.UUID(),
|
||||
AttachType: val.AttachType,
|
||||
Url: val.Url,
|
||||
PostIdentity: in.Identity,
|
||||
Identity: utils.UUID(),
|
||||
AttachType: val.AttachType,
|
||||
Url: val.Url,
|
||||
})
|
||||
}
|
||||
}
|
||||
err = models.ChargePost(&postData)
|
||||
// 只能修改自己发布的动态
|
||||
affected, err := models.ChargePost(&postData, auth.Identity)
|
||||
if err != nil {
|
||||
printer.Error(err.Error())
|
||||
return nil, errcode.ErrDB
|
||||
}
|
||||
if affected == 0 {
|
||||
return nil, errcode.ErrPermissionDenied
|
||||
}
|
||||
return &pb.DataStatusReply{
|
||||
Data: postData.Identity,
|
||||
Timeseq: time.Now().UnixNano(),
|
||||
|
||||
@@ -14,18 +14,22 @@ import (
|
||||
|
||||
// 删除评论
|
||||
func DeleteComment(ctx context.Context, in *pb.IdentRequest) (reply *pb.DataStatusReply, err error) {
|
||||
_, err = service.ParseMetaCtx(ctx, nil)
|
||||
auth, err := service.ParseMetaCtx(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if in.GetId() == 0 && in.GetIdentity() == "" {
|
||||
return nil, errcode.ErrInvalidArgument
|
||||
}
|
||||
err = models.DeleteComment(in.Identity)
|
||||
// 只能删除自己发表的评论
|
||||
affected, err := models.DeleteComment(in.Identity, auth.Identity)
|
||||
if err != nil {
|
||||
printer.Error(err.Error())
|
||||
return nil, errcode.ErrDB
|
||||
}
|
||||
if affected == 0 {
|
||||
return nil, errcode.ErrPermissionDenied
|
||||
}
|
||||
return &pb.DataStatusReply{
|
||||
Data: vars.OK,
|
||||
Timeseq: time.Now().UnixNano(),
|
||||
|
||||
@@ -10,20 +10,27 @@ import (
|
||||
"git.apinb.com/bsm-sdk/core/service"
|
||||
)
|
||||
|
||||
// maxPageSize 单页最大条数,避免一次请求拉取过多数据
|
||||
const maxPageSize int64 = 50
|
||||
|
||||
// 推文列表
|
||||
func Fetch(ctx context.Context, in *pb.FetchRequest) (reply *pb.PostListReply, err error) {
|
||||
_, err = service.ParseMetaCtx(ctx, nil)
|
||||
auth, err := service.ParseMetaCtx(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 归一化分页参数:页码下限 1,单页条数上限 maxPageSize
|
||||
if in.GetPageNo() <= 0 {
|
||||
in.PageNo = 1
|
||||
}
|
||||
if in.GetPageSize() <= 0 {
|
||||
in.PageSize = 10
|
||||
}
|
||||
if in.GetPageSize() > maxPageSize {
|
||||
in.PageSize = maxPageSize
|
||||
}
|
||||
|
||||
data, cnt, err := models.PostList(in.PageNo, in.PageSize, in.Params["key"])
|
||||
data, cnt, err := models.PostList(in.PageNo, in.PageSize, in.Params["key"], auth.Identity)
|
||||
if err != nil {
|
||||
printer.Error(err.Error())
|
||||
return nil, errcode.ErrDB
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
|
||||
// 删除推文
|
||||
func Remove(ctx context.Context, in *pb.IdentRequest) (reply *pb.DataStatusReply, err error) {
|
||||
_, err = service.ParseMetaCtx(ctx, nil)
|
||||
auth, err := service.ParseMetaCtx(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -22,11 +22,15 @@ func Remove(ctx context.Context, in *pb.IdentRequest) (reply *pb.DataStatusReply
|
||||
return nil, errcode.ErrInvalidArgument
|
||||
}
|
||||
|
||||
err = models.DeletePost(in.Identity)
|
||||
// 只能删除自己发布的动态
|
||||
affected, err := models.DeletePost(in.Identity, auth.Identity)
|
||||
if err != nil {
|
||||
printer.Error(err.Error())
|
||||
return nil, errcode.ErrDB
|
||||
}
|
||||
if affected == 0 {
|
||||
return nil, errcode.ErrPermissionDenied
|
||||
}
|
||||
|
||||
return &pb.DataStatusReply{
|
||||
Data: vars.OK,
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"errors"
|
||||
|
||||
"bsm/full/module/social/feed/internal/impl"
|
||||
"git.apinb.com/bsm-sdk/core/errcode"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// maxActionCounter 互动计数上限,避免计数被无条件刷高
|
||||
const maxActionCounter int64 = 1000000
|
||||
|
||||
func InitData() error {
|
||||
return nil
|
||||
}
|
||||
@@ -27,47 +31,159 @@ func CreatePost(data *FeedPost) error {
|
||||
|
||||
return err
|
||||
}
|
||||
func ChargePost(data *FeedPost) error {
|
||||
err := impl.DBService.Where("identity = ?", data.Identity).Updates(&data).Error
|
||||
return err
|
||||
|
||||
// ChargePost 修改动态,仅允许作者本人修改;is_open=false 需真正写入,附件变更需真正落库
|
||||
func ChargePost(data *FeedPost, passportIdentity string) (affected int64, err error) {
|
||||
err = impl.DBService.Transaction(func(tx *gorm.DB) error {
|
||||
// 用 map 更新,避免 GORM 结构体更新忽略 is_open=false 这类零值
|
||||
res := tx.Model(&FeedPost{}).
|
||||
Where("identity = ? AND passport_identity = ?", data.Identity, passportIdentity).
|
||||
Updates(map[string]any{"content": data.Content, "is_open": data.IsOpen})
|
||||
if res.Error != nil {
|
||||
return res.Error
|
||||
}
|
||||
affected = res.RowsAffected
|
||||
if affected == 0 {
|
||||
// 动态不存在或非本人发布,直接结束,不再处理附件
|
||||
return nil
|
||||
}
|
||||
// 附件变更真正落库:请求携带附件时整体替换原附件
|
||||
if len(data.Attachs) > 0 {
|
||||
if err := tx.Where("post_identity = ?", data.Identity).Delete(&FeedRelateAttach{}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if err := tx.Create(&data.Attachs).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return
|
||||
}
|
||||
func DeletePost(identity string) error {
|
||||
err := impl.DBService.Where("identity = ?", identity).Delete(&FeedPost{}).Error
|
||||
return err
|
||||
|
||||
// DeletePost 删除动态,仅允许作者本人删除
|
||||
func DeletePost(identity, passportIdentity string) (affected int64, err error) {
|
||||
res := impl.DBService.Where("identity = ? AND passport_identity = ?", identity, passportIdentity).Delete(&FeedPost{})
|
||||
return res.RowsAffected, res.Error
|
||||
}
|
||||
|
||||
// 点赞/踩操作
|
||||
func LikeAction(action_op, action_type, identity string) (err error) {
|
||||
var column string
|
||||
tx := impl.DBService
|
||||
if action_type == "post" {
|
||||
var (
|
||||
column string
|
||||
tx = impl.DBService
|
||||
)
|
||||
// 显式绑定目标表,避免 action_type 取任意值时目标表不确定
|
||||
switch action_type {
|
||||
case "post":
|
||||
tx = tx.Model(&FeedPost{})
|
||||
} else if action_type == "comment" {
|
||||
case "comment":
|
||||
tx = tx.Model(&FeedComment{})
|
||||
default:
|
||||
return errcode.ErrInvalidArgument
|
||||
}
|
||||
if action_op == "ilike" {
|
||||
switch action_op {
|
||||
case "ilike":
|
||||
column = "cnt_like"
|
||||
} else if action_type == "unlike" {
|
||||
case "unlike":
|
||||
column = "cnt_unlike"
|
||||
default:
|
||||
return errcode.ErrInvalidArgument
|
||||
}
|
||||
if err = tx.Where("identity = ?", identity).UpdateColumn(column, gorm.Expr(fmt.Sprintf("%s + ?", column), 1)).Error; err != nil {
|
||||
return err
|
||||
// 条件更新:目标必须存在且计数未达上限,避免计数被无限刷高
|
||||
res := tx.Where("identity = ? AND "+column+" < ?", identity, maxActionCounter).
|
||||
UpdateColumn(column, gorm.Expr(column+" + ?", 1))
|
||||
if res.Error != nil {
|
||||
return res.Error
|
||||
}
|
||||
return
|
||||
if res.RowsAffected == 0 {
|
||||
return errcode.ErrRecordNotFound
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PostList
|
||||
func PostList(page, size int64, tag string) (list []*FeedPost, cnt int64, err error) {
|
||||
tx := impl.DBService.Order("created_at desc")
|
||||
// PostList 动态列表,仅返回公开动态或调用者本人发布的动态
|
||||
func PostList(page, size int64, tag, viewerIdentity string) (list []*FeedPost, cnt int64, err error) {
|
||||
list = make([]*FeedPost, 0)
|
||||
tx := impl.DBService.Model(&FeedPost{}).
|
||||
Where("status <> ?", -1).
|
||||
Where("is_open = ? OR passport_identity = ?", true, viewerIdentity)
|
||||
if tag != "" {
|
||||
tx = tx.Joins("join feed_relate_tags on feed_relate_tags.post_identity = feed_post.identity", impl.DBService.Where(&FeedRelateTags{Key: tag}))
|
||||
tx = tx.Where("key = ?", tag)
|
||||
tx = tx.Joins("join feed_relate_tags on feed_relate_tags.post_identity = feed_post.identity").
|
||||
Where("feed_relate_tags.key = ?", tag)
|
||||
}
|
||||
// 使句柄可重复使用,统计与列表查询互不影响
|
||||
tx = tx.Session(&gorm.Session{})
|
||||
|
||||
if err = tx.Count(&cnt).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
tx.Limit(int(page)).Offset(int((page - 1) * size))
|
||||
err = tx.Find(&list).Count(&cnt).Error
|
||||
fmt.Print("posts", list)
|
||||
return
|
||||
err = tx.Order("created_at desc").
|
||||
Limit(int(size)).
|
||||
Offset(int((page - 1) * size)).
|
||||
Find(&list).Error
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
// 标签与附件是 gorm:"-" 字段,GORM 不会加载,需二次查询回填
|
||||
if err = fillRelates(list); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return list, cnt, nil
|
||||
}
|
||||
|
||||
// fillRelates 按 post_identity 批量回填动态的标签与附件
|
||||
func fillRelates(list []*FeedPost) error {
|
||||
if len(list) == 0 {
|
||||
return nil
|
||||
}
|
||||
identities := make([]string, 0, len(list))
|
||||
for _, item := range list {
|
||||
identities = append(identities, item.Identity)
|
||||
}
|
||||
|
||||
var attachs []FeedRelateAttach
|
||||
if err := impl.DBService.Where("post_identity IN ?", identities).Find(&attachs).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var relates []FeedRelateTags
|
||||
if err := impl.DBService.Where("post_identity IN ?", identities).Find(&relates).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 标签名称存于 feed_tags 字典,按 key 批量查出后回填
|
||||
contents := map[string]string{}
|
||||
if len(relates) > 0 {
|
||||
keys := make([]string, 0, len(relates))
|
||||
for _, relate := range relates {
|
||||
keys = append(keys, relate.Key)
|
||||
}
|
||||
var dict []FeedTags
|
||||
if err := impl.DBService.Where("key IN ?", keys).Find(&dict).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
for _, tag := range dict {
|
||||
contents[tag.Key] = tag.Content
|
||||
}
|
||||
}
|
||||
|
||||
attachMap := map[string][]FeedRelateAttach{}
|
||||
for _, attach := range attachs {
|
||||
attachMap[attach.PostIdentity] = append(attachMap[attach.PostIdentity], attach)
|
||||
}
|
||||
tagMap := map[string][]FeedRelateTags{}
|
||||
for _, relate := range relates {
|
||||
relate.Content = contents[relate.Key]
|
||||
tagMap[relate.PostIdentity] = append(tagMap[relate.PostIdentity], relate)
|
||||
}
|
||||
|
||||
for _, item := range list {
|
||||
item.Attachs = attachMap[item.Identity]
|
||||
item.Tags = tagMap[item.Identity]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func AddTags(data *[]FeedTags) (err error) {
|
||||
@@ -77,16 +193,19 @@ func AddTags(data *[]FeedTags) (err error) {
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// ModifyTags 按标签 key 定位并保存,feed_tags 表没有 identity 列
|
||||
func ModifyTags(data *FeedTags) (err error) {
|
||||
err = impl.DBService.Where("identity = ?", data.Key).Save(data).Error
|
||||
err = impl.DBService.Where("key = ?", data.Key).Save(data).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteTags 按标签 key 删除,feed_tags 表没有 identity 列
|
||||
func DeleteTags(key string) (err error) {
|
||||
err = impl.DBService.Where("identity = ?", key).Delete(&FeedTags{}).Error
|
||||
err = impl.DBService.Where("key = ?", key).Delete(&FeedTags{}).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -102,6 +221,8 @@ func TagsList() (list []*FeedTags, cnt int64, err error) {
|
||||
|
||||
func AddComment(comment *FeedComment, authorIdentity string) (err error) {
|
||||
err = impl.DBService.Transaction(func(tx *gorm.DB) error {
|
||||
// 记录评论作者,供后续归属校验使用
|
||||
comment.PassportIdentity = authorIdentity
|
||||
// 添加评论,更新文章评论量
|
||||
err := tx.Create(comment).Table("feed_post").Where("identity = ?", comment.PostIdentity).UpdateColumn("cnt_comment", gorm.Expr("cnt_comment + ?", 1)).Error
|
||||
if err != nil {
|
||||
@@ -119,7 +240,33 @@ func AddComment(comment *FeedComment, authorIdentity string) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func DeleteComment(identity string) (err error) {
|
||||
// DeleteComment 删除评论,仅允许评论作者本人删除,并同步递减所属动态的评论数
|
||||
func DeleteComment(identity, passportIdentity string) (affected int64, err error) {
|
||||
err = impl.DBService.Transaction(func(tx *gorm.DB) error {
|
||||
var comment FeedComment
|
||||
// 只有作者本人的评论才允许删除
|
||||
if err := tx.Where("identity = ? AND passport_identity = ?", identity, passportIdentity).First(&comment).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
res := tx.Delete(&FeedComment{}, "identity = ?", identity)
|
||||
if res.Error != nil {
|
||||
return res.Error
|
||||
}
|
||||
affected = res.RowsAffected
|
||||
|
||||
// 同步递减所属动态的评论数,并做下限保护避免出现负数
|
||||
if comment.PostIdentity != "" {
|
||||
if err := tx.Model(&FeedPost{}).
|
||||
Where("identity = ? AND cnt_comment > 0", comment.PostIdentity).
|
||||
UpdateColumn("cnt_comment", gorm.Expr("cnt_comment - ?", 1)).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
pb "bsm/full/module/social/feed/pb"
|
||||
"context"
|
||||
|
||||
"git.apinb.com/bsm-sdk/core/printer"
|
||||
gwRuntime "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/reflection"
|
||||
@@ -17,10 +18,16 @@ type Server struct {
|
||||
grpcConns map[string]*grpc.ClientConn // 连接池
|
||||
}
|
||||
|
||||
func New(addr string) *Server {
|
||||
func New(grpcServ *grpc.Server) *Server {
|
||||
// grpcServ 为空表示独立进程启动,自行创建 gRPC Server
|
||||
standalone := grpcServ == nil
|
||||
if standalone {
|
||||
grpcServ = grpc.NewServer()
|
||||
}
|
||||
|
||||
srv := &Server{
|
||||
Ctx: context.Background(),
|
||||
Grpc: grpc.NewServer(),
|
||||
Grpc: grpcServ,
|
||||
grpcConns: make(map[string]*grpc.ClientConn),
|
||||
}
|
||||
|
||||
@@ -30,7 +37,28 @@ func New(addr string) *Server {
|
||||
pb.RegisterTagServer(srv.Grpc, NewTagServer())
|
||||
pb.RegisterTimelineServer(srv.Grpc, NewTimelineServer())
|
||||
|
||||
reflection.Register(srv.Grpc)
|
||||
if standalone {
|
||||
// 独立进程需自行初始化网关路由并注册 handler,否则 /feed.* 全部 404
|
||||
srv.Mux = gwRuntime.NewServeMux()
|
||||
if err := RegisterGateway(srv.Ctx, srv.Mux); err != nil {
|
||||
printer.Error(err.Error())
|
||||
}
|
||||
reflection.Register(srv.Grpc)
|
||||
}
|
||||
|
||||
return srv
|
||||
}
|
||||
|
||||
// RegisterGateway 注册四个 service 的 gateway handler
|
||||
func RegisterGateway(ctx context.Context, mux *gwRuntime.ServeMux) error {
|
||||
if err := pb.RegisterPostHandlerServer(ctx, mux, NewPostServer()); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := pb.RegisterSettingHandlerServer(ctx, mux, NewSettingServer()); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := pb.RegisterTagHandlerServer(ctx, mux, NewTagServer()); err != nil {
|
||||
return err
|
||||
}
|
||||
return pb.RegisterTimelineHandlerServer(ctx, mux, NewTimelineServer())
|
||||
}
|
||||
|
||||
32
module/social/feed/service/dependencies.go
Normal file
32
module/social/feed/service/dependencies.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"bsm/full/module/social/feed/internal/impl"
|
||||
"git.apinb.com/bsm-sdk/core/cache/redis"
|
||||
cache "github.com/patrickmn/go-cache"
|
||||
clientv3 "go.etcd.io/etcd/client/v3"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Dependencies 聚合宿主注入的共享基础设施
|
||||
type Dependencies struct {
|
||||
Redis *redis.RedisClient
|
||||
Etcd *clientv3.Client
|
||||
DB *gorm.DB
|
||||
Cache *cache.Cache
|
||||
}
|
||||
|
||||
func applyDependencies(deps Dependencies) {
|
||||
if deps.Redis != nil {
|
||||
impl.RedisService = deps.Redis
|
||||
}
|
||||
if deps.Etcd != nil {
|
||||
impl.EtcdService = deps.Etcd
|
||||
}
|
||||
if deps.DB != nil {
|
||||
impl.DBService = deps.DB
|
||||
}
|
||||
if deps.Cache != nil {
|
||||
impl.MemorySerice = deps.Cache
|
||||
}
|
||||
}
|
||||
26
module/social/feed/service/expose.go
Normal file
26
module/social/feed/service/expose.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"bsm/full/module/social/feed/internal/server"
|
||||
gwRuntime "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
type ExposeOptions struct {
|
||||
Dependencies
|
||||
GRPC *grpc.Server
|
||||
Gateway *gwRuntime.ServeMux
|
||||
}
|
||||
|
||||
// Expose 供聚合宿主接入:注册 gRPC service 与网关路由
|
||||
func Expose(options ExposeOptions) error {
|
||||
applyDependencies(options.Dependencies)
|
||||
server.New(options.GRPC)
|
||||
|
||||
if options.Gateway == nil {
|
||||
return nil
|
||||
}
|
||||
return server.RegisterGateway(context.Background(), options.Gateway)
|
||||
}
|
||||
@@ -21,7 +21,7 @@ func Run() {
|
||||
impl.NewImpl()
|
||||
|
||||
// 初始化服务
|
||||
s := server.New(config.Spec.Addr)
|
||||
s := server.New(nil)
|
||||
srv := service.New(
|
||||
s.Grpc,
|
||||
&service.Options{
|
||||
|
||||
@@ -2,18 +2,23 @@ package basic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"bsm/full/module/social/group/internal/impl"
|
||||
"bsm/full/module/social/group/internal/models"
|
||||
pb "bsm/full/module/social/group/pb"
|
||||
"git.apinb.com/bsm-sdk/core/errcode"
|
||||
"git.apinb.com/bsm-sdk/core/printer"
|
||||
"git.apinb.com/bsm-sdk/core/service"
|
||||
"git.apinb.com/bsm-sdk/core/vars"
|
||||
"time"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// 解散群组
|
||||
func Disband(ctx context.Context, in *pb.IdentRequest) (reply *pb.DataStatusReply, err error) {
|
||||
// parse authorization meta.
|
||||
_, err = service.ParseMetaCtx(ctx, nil)
|
||||
auth, err := service.ParseMetaCtx(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -22,8 +27,37 @@ func Disband(ctx context.Context, in *pb.IdentRequest) (reply *pb.DataStatusRepl
|
||||
if in.Id == 0 && in.Identity == "" {
|
||||
return nil, errcode.ErrInvalidArgument
|
||||
}
|
||||
if in.Identity == "" {
|
||||
return nil, errcode.ErrInvalidArgument
|
||||
}
|
||||
|
||||
// TODO: add your logic code & delete this line.
|
||||
// 校验群组存在,且仅群主可解散
|
||||
var group models.GroupBasic
|
||||
if err = impl.DBService.Where("identity=?", in.Identity).First(&group).Error; err != nil {
|
||||
printer.Error(err.Error())
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, errcode.ErrRecordNotFound
|
||||
}
|
||||
return nil, errcode.ErrDB
|
||||
}
|
||||
if group.PassportID != auth.ID {
|
||||
return nil, errcode.ErrPermissionDenied
|
||||
}
|
||||
|
||||
// 软删除群组及其成员记录
|
||||
err = impl.DBService.Transaction(func(tx *gorm.DB) error {
|
||||
if e := tx.Where("identity=?", in.Identity).Delete(&models.GroupBasic{}).Error; e != nil {
|
||||
return e
|
||||
}
|
||||
if e := tx.Where("group_identity=?", in.Identity).Delete(&models.GroupMember{}).Error; e != nil {
|
||||
return e
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
printer.Error(err.Error())
|
||||
return nil, errcode.ErrDB
|
||||
}
|
||||
|
||||
return &pb.DataStatusReply{
|
||||
Data: vars.OK,
|
||||
|
||||
@@ -25,6 +25,7 @@ func Fetch(ctx context.Context, in *pb.Empty) (reply *pb.GroupsReply, err error)
|
||||
}
|
||||
|
||||
return &pb.GroupsReply{
|
||||
Total: int32(len(groups)),
|
||||
Total: int32(len(groups)),
|
||||
Groups: groups,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -2,12 +2,15 @@ package basic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"bsm/full/module/social/group/internal/impl"
|
||||
"bsm/full/module/social/group/internal/models"
|
||||
pb "bsm/full/module/social/group/pb"
|
||||
"git.apinb.com/bsm-sdk/core/errcode"
|
||||
"git.apinb.com/bsm-sdk/core/printer"
|
||||
"git.apinb.com/bsm-sdk/core/service"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// 获取群组信息
|
||||
@@ -17,10 +20,17 @@ func Get(ctx context.Context, in *pb.IdentRequest) (reply *pb.GroupItem, err err
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = impl.DBService.Where("identity=?", in.Identity).First(&reply).Error
|
||||
if err != nil {
|
||||
if in.GetIdentity() == "" {
|
||||
return nil, errcode.ErrInvalidArgument
|
||||
}
|
||||
|
||||
var group models.GroupBasic
|
||||
if err = impl.DBService.Where("identity=?", in.Identity).First(&group).Error; err != nil {
|
||||
printer.Error(err.Error())
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, errcode.ErrRecordNotFound
|
||||
}
|
||||
return nil, errcode.ErrDB
|
||||
}
|
||||
return &pb.GroupItem{}, nil
|
||||
return models.ToGroupItem(&group), nil
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"git.apinb.com/bsm-sdk/core/errcode"
|
||||
"git.apinb.com/bsm-sdk/core/printer"
|
||||
"git.apinb.com/bsm-sdk/core/service"
|
||||
"git.apinb.com/bsm-sdk/core/utils"
|
||||
"git.apinb.com/bsm-sdk/core/vars"
|
||||
)
|
||||
|
||||
@@ -20,22 +19,49 @@ func Modify(ctx context.Context, in *pb.GroupItem) (reply *pb.DataStatusReply, e
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
record := &models.GroupBasic{
|
||||
Name: in.GetName(),
|
||||
Introduce: in.GetIntroduce(),
|
||||
Avatar: in.GetAvatar(),
|
||||
Background: in.GetBackground(),
|
||||
Notice: in.GetNotice(),
|
||||
EnableSearchByNumber: in.GetEnableSearchByNumber(),
|
||||
EnableSearchByName: in.GetEnableSearchByName(),
|
||||
if in.GetIdentity() == "" {
|
||||
return nil, errcode.ErrInvalidArgument
|
||||
}
|
||||
record.Identity = utils.UUID()
|
||||
|
||||
err = impl.DBService.Where("identity=? and creator_id=?", in.Identity, auth.ID).UpdateColumns(record).Error
|
||||
if err != nil {
|
||||
// 校验群组存在,且操作者必须是创建者
|
||||
var group models.GroupBasic
|
||||
if err = impl.DBService.Where("identity=?", in.Identity).First(&group).Error; err != nil {
|
||||
printer.Error(err.Error())
|
||||
return nil, errcode.ErrDB
|
||||
return nil, errcode.ErrRecordNotFound
|
||||
}
|
||||
if group.PassportID != auth.ID {
|
||||
return nil, errcode.ErrPermissionDenied
|
||||
}
|
||||
|
||||
// 仅更新请求中确实提供的字段,避免零值覆盖;同时不再改写群 identity
|
||||
updates := map[string]any{}
|
||||
if v := in.GetName(); v != "" {
|
||||
updates["name"] = v
|
||||
}
|
||||
if v := in.GetIntroduce(); v != "" {
|
||||
updates["introduce"] = v
|
||||
}
|
||||
if v := in.GetAvatar(); v != "" {
|
||||
updates["avatar"] = v
|
||||
}
|
||||
if v := in.GetBackground(); v != "" {
|
||||
updates["background"] = v
|
||||
}
|
||||
if v := in.GetNotice(); v != "" {
|
||||
updates["notice"] = v
|
||||
}
|
||||
// 布尔字段无法区分「未传」与「false」,仅在为 true 时更新,避免误清空已有开关
|
||||
if in.GetEnableSearchByNumber() {
|
||||
updates["enable_search_by_number"] = true
|
||||
}
|
||||
if in.GetEnableSearchByName() {
|
||||
updates["enable_search_by_name"] = true
|
||||
}
|
||||
if len(updates) > 0 {
|
||||
if err = impl.DBService.Model(&models.GroupBasic{}).Where("identity=?", in.Identity).Updates(updates).Error; err != nil {
|
||||
printer.Error(err.Error())
|
||||
return nil, errcode.ErrDB
|
||||
}
|
||||
}
|
||||
|
||||
return &pb.DataStatusReply{
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"git.apinb.com/bsm-sdk/core/printer"
|
||||
"git.apinb.com/bsm-sdk/core/service"
|
||||
"git.apinb.com/bsm-sdk/core/utils"
|
||||
"git.apinb.com/bsm-sdk/core/vars"
|
||||
)
|
||||
|
||||
// 申请加群
|
||||
@@ -19,39 +20,84 @@ func DoJoin(ctx context.Context, in *pb.DoJoinRequest) (reply *pb.DataStatusRepl
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
//生成唯一标识
|
||||
identity := utils.UUID()
|
||||
if in.GetIdentity() == "" {
|
||||
return nil, errcode.ErrInvalidArgument
|
||||
}
|
||||
|
||||
//判断是否需要加群验证
|
||||
if models.GetGroupEnableAnyJoin(auth.ID, in.Identity) {
|
||||
// 直接加入群成员
|
||||
apply := &models.GroupMember{
|
||||
GroupID: uint(in.Id),
|
||||
// 校验群组是否存在
|
||||
var group models.GroupBasic
|
||||
if err = impl.DBService.Where("identity=?", in.Identity).First(&group).Error; err != nil {
|
||||
printer.Error(err.Error())
|
||||
return nil, errcode.ErrRecordNotFound
|
||||
}
|
||||
|
||||
// 重复入群防护:已是成员直接返回成功
|
||||
var memberCnt int64
|
||||
if err = impl.DBService.Model(&models.GroupMember{}).
|
||||
Where("group_identity=? and passport_id=?", in.Identity, auth.ID).Count(&memberCnt).Error; err != nil {
|
||||
printer.Error(err.Error())
|
||||
return nil, errcode.ErrDB
|
||||
}
|
||||
if memberCnt > 0 {
|
||||
return &pb.DataStatusReply{Data: vars.OK, Timeseq: time.Now().UnixMilli()}, nil
|
||||
}
|
||||
|
||||
// 成员上限校验(MemberLimit 为 0 表示不限)
|
||||
if group.MemberLimit > 0 && group.MemberTotal >= group.MemberLimit {
|
||||
return nil, errcode.ErrResourceExhausted
|
||||
}
|
||||
|
||||
// 是否免验证入群
|
||||
enableAnyJoin, err := models.GetGroupEnableAnyJoin(in.Identity)
|
||||
if err != nil {
|
||||
printer.Error(err.Error())
|
||||
return nil, errcode.ErrDB
|
||||
}
|
||||
|
||||
var identity string
|
||||
if enableAnyJoin {
|
||||
// 免验证直接加入群成员
|
||||
member := &models.GroupMember{
|
||||
GroupID: group.ID,
|
||||
GroupIdentity: in.Identity,
|
||||
Role: ROLE_MEMBER,
|
||||
}
|
||||
apply.Identity = utils.UUID()
|
||||
apply.PassportID = auth.ID
|
||||
apply.PassportIdentity = auth.Identity
|
||||
|
||||
err = impl.DBService.Create(&apply).Error
|
||||
member.Identity = utils.UUID()
|
||||
member.PassportID = auth.ID
|
||||
member.PassportIdentity = auth.Identity
|
||||
if err = impl.DBService.Create(member).Error; err != nil {
|
||||
printer.Error(err.Error())
|
||||
return nil, errcode.ErrDB
|
||||
}
|
||||
identity = member.Identity
|
||||
// 更新群组表数据统计
|
||||
models.UpsetGroupMemberTotal(in.Identity, "+")
|
||||
} else {
|
||||
// 避免重复提交待处理申请
|
||||
var applyCnt int64
|
||||
if err = impl.DBService.Model(&models.GroupApply{}).
|
||||
Where("group_identity=? and from_id=? and status=?", in.Identity, auth.ID, 0).Count(&applyCnt).Error; err != nil {
|
||||
printer.Error(err.Error())
|
||||
return nil, errcode.ErrDB
|
||||
}
|
||||
if applyCnt > 0 {
|
||||
return &pb.DataStatusReply{Data: vars.OK, Timeseq: time.Now().UnixMilli()}, nil
|
||||
}
|
||||
|
||||
// 写入加群申请表
|
||||
apply := &models.GroupApply{
|
||||
FromID: uint(auth.ID),
|
||||
FromID: auth.ID,
|
||||
FromIdentity: auth.Identity,
|
||||
GroupID: uint(in.Id),
|
||||
GroupID: group.ID,
|
||||
GroupIdentity: in.Identity,
|
||||
Message: in.Message,
|
||||
}
|
||||
apply.Identity = utils.UUID()
|
||||
|
||||
err = impl.DBService.Create(&apply).Error
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
printer.Error(err.Error())
|
||||
return nil, errcode.ErrDB
|
||||
if err = impl.DBService.Create(apply).Error; err != nil {
|
||||
printer.Error(err.Error())
|
||||
return nil, errcode.ErrDB
|
||||
}
|
||||
identity = apply.Identity
|
||||
}
|
||||
|
||||
return &pb.DataStatusReply{
|
||||
|
||||
@@ -19,22 +19,40 @@ func DoKick(ctx context.Context, in *pb.GroupOPRequest) (reply *pb.DataStatusRep
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 判断是不是群管理或是创建者
|
||||
var tc int64
|
||||
if err = impl.DBService.Model(&models.GroupMember{}).Where("identity=? and role !=0 and passport_id=?", in.GroupIdentity, auth.Identity).Count(&tc).Error; err != nil {
|
||||
if in.GetGroupIdentity() == "" || in.GetIdentity() == "" {
|
||||
return nil, errcode.ErrInvalidArgument
|
||||
}
|
||||
|
||||
// 判断操作者是不是该群的管理员或创建者
|
||||
var opRole int32
|
||||
if err = impl.DBService.Model(&models.GroupMember{}).
|
||||
Where("group_identity=? and passport_id=?", in.GroupIdentity, auth.ID).
|
||||
Pluck("role", &opRole).Error; err != nil {
|
||||
printer.Error(err.Error())
|
||||
return nil, errcode.ErrDB
|
||||
}
|
||||
if tc == 0 {
|
||||
if opRole <= ROLE_MEMBER {
|
||||
return nil, errcode.ErrPermissionDenied
|
||||
}
|
||||
|
||||
err = impl.DBService.Model(&models.GroupMember{}).Delete("group_identity=? and identity=?", in.GroupIdentity, in.Identity).Error
|
||||
if err != nil {
|
||||
// 目标成员必须属于该群组
|
||||
var target models.GroupMember
|
||||
if err = impl.DBService.Where("group_identity=? and identity=?", in.GroupIdentity, in.Identity).First(&target).Error; err != nil {
|
||||
printer.Error(err.Error())
|
||||
return nil, errcode.ErrRecordNotFound
|
||||
}
|
||||
// 禁止踢创建者,以及权限不低于操作者的成员
|
||||
if target.Role >= ROLE_CREATEOR || target.Role >= opRole {
|
||||
return nil, errcode.ErrPermissionDenied
|
||||
}
|
||||
|
||||
res := impl.DBService.Where("group_identity=? and identity=?", in.GroupIdentity, in.Identity).Delete(&models.GroupMember{})
|
||||
if res.Error != nil {
|
||||
printer.Error(res.Error.Error())
|
||||
return nil, errcode.ErrDB
|
||||
} else {
|
||||
//更新群组表数据统计
|
||||
}
|
||||
if res.RowsAffected > 0 {
|
||||
// 仅在确实删除成员后更新群组数据统计
|
||||
models.UpsetGroupMemberTotal(in.GroupIdentity, "-")
|
||||
}
|
||||
return &pb.DataStatusReply{
|
||||
|
||||
@@ -20,13 +20,13 @@ func DoQuit(ctx context.Context, in *pb.GroupOPRequest) (reply *pb.DataStatusRep
|
||||
return nil, err
|
||||
}
|
||||
// 判断是不是创建者
|
||||
var creator_id uint
|
||||
if err = impl.DBService.Model(&models.GroupBasic{}).Select("creator_id").Where("identity=?", in.GroupIdentity).Scan(&creator_id).Error; err != nil {
|
||||
var creatorID uint
|
||||
if err = impl.DBService.Model(&models.GroupBasic{}).Select("passport_id").Where("identity=?", in.GroupIdentity).Scan(&creatorID).Error; err != nil {
|
||||
printer.Error(err.Error())
|
||||
return nil, errcode.ErrDB
|
||||
}
|
||||
|
||||
if auth.ID == creator_id {
|
||||
if auth.ID == creatorID {
|
||||
return nil, errcode.ErrPermissionDenied
|
||||
}
|
||||
|
||||
|
||||
@@ -21,13 +21,13 @@ func DoSetManager(ctx context.Context, in *pb.GroupOPRequest) (reply *pb.DataSta
|
||||
return nil, err
|
||||
}
|
||||
// 判断是不是创建者
|
||||
var creator_id uint
|
||||
if err = impl.DBService.Model(&models.GroupBasic{}).Select("creator_id").Where("identity=?", in.GroupIdentity).Scan(&creator_id).Error; err != nil {
|
||||
var creatorID uint
|
||||
if err = impl.DBService.Model(&models.GroupBasic{}).Select("passport_id").Where("identity=?", in.GroupIdentity).Scan(&creatorID).Error; err != nil {
|
||||
printer.Error(err.Error())
|
||||
return nil, errcode.ErrDB
|
||||
}
|
||||
|
||||
if auth.ID != creator_id {
|
||||
if auth.ID != creatorID {
|
||||
return nil, errcode.ErrPermissionDenied
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ func Fetch(ctx context.Context, in *pb.IdentRequest) (reply *pb.GroupMemberReply
|
||||
}
|
||||
|
||||
return &pb.GroupMemberReply{
|
||||
Total: int32(len(members)),
|
||||
Total: int32(len(members)),
|
||||
Members: members,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -12,53 +12,86 @@ import (
|
||||
"git.apinb.com/bsm-sdk/core/printer"
|
||||
"git.apinb.com/bsm-sdk/core/service"
|
||||
"git.apinb.com/bsm-sdk/core/utils"
|
||||
"git.apinb.com/bsm-sdk/core/vars"
|
||||
)
|
||||
|
||||
// 申请加群处理
|
||||
func JoinDoHandle(ctx context.Context, in *pb.GroupOPRequest) (reply *pb.DataStatusReply, err error) {
|
||||
_, err = service.ParseMetaCtx(ctx, nil)
|
||||
auth, err := service.ParseMetaCtx(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var identity string
|
||||
switch strings.ToUpper(in.Direction) {
|
||||
case "PASS":
|
||||
var apply models.GroupApply
|
||||
if err = impl.DBService.Where("identity=?", in.Identity).First(&apply).Error; err != nil {
|
||||
printer.Error(err.Error())
|
||||
return nil, errcode.ErrDB
|
||||
}
|
||||
|
||||
// 直接加入群成员
|
||||
identity = utils.UUID()
|
||||
member := &models.GroupMember{
|
||||
GroupID: apply.GroupID,
|
||||
GroupIdentity: apply.GroupIdentity,
|
||||
Role: 0,
|
||||
}
|
||||
member.Identity = utils.UUID()
|
||||
member.PassportID = apply.FromID
|
||||
member.PassportIdentity = apply.FromIdentity
|
||||
|
||||
if err = impl.DBService.Create(&member).Error; err != nil {
|
||||
printer.Error(err.Error())
|
||||
return nil, errcode.ErrDB
|
||||
}
|
||||
|
||||
//更新申请表处理结果
|
||||
impl.DBService.Model(&models.GroupApply{}).Where("identity=?", in.Identity).UpdateColumn("status", 1)
|
||||
|
||||
//更新群组表数据统计
|
||||
models.UpsetGroupMemberTotal(apply.GroupIdentity, "+")
|
||||
case "REJECT":
|
||||
//更新申请表处理结果
|
||||
impl.DBService.Model(&models.GroupApply{}).Where("identity=?", in.Identity).UpdateColumn("status", -1)
|
||||
// 未识别的操作方向直接报错,不再默认返回成功
|
||||
direction := strings.ToUpper(in.Direction)
|
||||
if direction != "PASS" && direction != "REJECT" {
|
||||
return nil, errcode.ErrInvalidArgument
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
// 查询申请记录
|
||||
var apply models.GroupApply
|
||||
if err = impl.DBService.Where("identity=?", in.Identity).First(&apply).Error; err != nil {
|
||||
printer.Error(err.Error())
|
||||
return nil, errcode.ErrRecordNotFound
|
||||
}
|
||||
|
||||
// 幂等:申请已处理则直接返回成功
|
||||
if apply.Status != 0 {
|
||||
return &pb.DataStatusReply{Data: vars.OK, Timeseq: time.Now().UnixMilli()}, nil
|
||||
}
|
||||
|
||||
// 角色校验:仅该群管理员/创建者可处理申请
|
||||
var opRole int32
|
||||
if err = impl.DBService.Model(&models.GroupMember{}).
|
||||
Where("group_identity=? and passport_id=?", apply.GroupIdentity, auth.ID).
|
||||
Pluck("role", &opRole).Error; err != nil {
|
||||
printer.Error(err.Error())
|
||||
return nil, errcode.ErrDB
|
||||
}
|
||||
if opRole <= ROLE_MEMBER {
|
||||
return nil, errcode.ErrPermissionDenied
|
||||
}
|
||||
|
||||
var identity string
|
||||
switch direction {
|
||||
case "PASS":
|
||||
// 幂等:申请人已是成员则不再重复创建
|
||||
var memberCnt int64
|
||||
if err = impl.DBService.Model(&models.GroupMember{}).
|
||||
Where("group_identity=? and passport_id=?", apply.GroupIdentity, apply.FromID).Count(&memberCnt).Error; err != nil {
|
||||
printer.Error(err.Error())
|
||||
return nil, errcode.ErrDB
|
||||
}
|
||||
if memberCnt == 0 {
|
||||
member := &models.GroupMember{
|
||||
GroupID: apply.GroupID,
|
||||
GroupIdentity: apply.GroupIdentity,
|
||||
Role: ROLE_MEMBER,
|
||||
}
|
||||
member.Identity = utils.UUID()
|
||||
member.PassportID = apply.FromID
|
||||
member.PassportIdentity = apply.FromIdentity
|
||||
if err = impl.DBService.Create(member).Error; err != nil {
|
||||
printer.Error(err.Error())
|
||||
return nil, errcode.ErrDB
|
||||
}
|
||||
identity = member.Identity
|
||||
// 更新群组表数据统计
|
||||
models.UpsetGroupMemberTotal(apply.GroupIdentity, "+")
|
||||
}
|
||||
|
||||
// 更新申请表处理结果
|
||||
if err = impl.DBService.Model(&models.GroupApply{}).Where("identity=?", in.Identity).UpdateColumn("status", 1).Error; err != nil {
|
||||
printer.Error(err.Error())
|
||||
return nil, errcode.ErrDB
|
||||
}
|
||||
case "REJECT":
|
||||
// 更新申请表处理结果
|
||||
if err = impl.DBService.Model(&models.GroupApply{}).Where("identity=?", in.Identity).UpdateColumn("status", -1).Error; err != nil {
|
||||
printer.Error(err.Error())
|
||||
return nil, errcode.ErrDB
|
||||
}
|
||||
}
|
||||
|
||||
return &pb.DataStatusReply{
|
||||
Data: identity,
|
||||
|
||||
@@ -2,6 +2,7 @@ package member
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"bsm/full/module/social/group/internal/impl"
|
||||
"bsm/full/module/social/group/internal/models"
|
||||
@@ -18,27 +19,37 @@ func JoinFetch(ctx context.Context, in *pb.Empty) (reply *pb.JoinFetchReply, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fetch := make([]*pb.ApplyJoinGroupItem, 0)
|
||||
applys := make([]*pb.ApplyJoinGroupItem, 0)
|
||||
group_ids := models.GetMasterGroup(auth.ID)
|
||||
if len(group_ids) > 0 {
|
||||
err = impl.DBService.Model(&models.GroupApply{}).Where("group_id in ", group_ids).Find(&fetch).Error
|
||||
if err != nil {
|
||||
var records []models.GroupApply
|
||||
if err = impl.DBService.Model(&models.GroupApply{}).Where("group_id in ?", group_ids).Find(&records).Error; err != nil {
|
||||
printer.Error(err.Error())
|
||||
return nil, errcode.ErrDB
|
||||
}
|
||||
var card = &pb.PassportInfoDetailCard{}
|
||||
for idx, item := range fetch {
|
||||
err = impl.DBService.Table("passport_extend").Select(filed).Where("passport_identity = ?", item.From.Identity).First(card).Error
|
||||
if err != nil {
|
||||
printer.Error(err.Error())
|
||||
return nil, errcode.ErrDB
|
||||
for i := range records {
|
||||
item := &pb.ApplyJoinGroupItem{
|
||||
Identity: records[i].Identity,
|
||||
GroupId: int64(records[i].GroupID),
|
||||
Message: records[i].Message,
|
||||
CreatedAt: records[i].CreatedAt.Format(time.DateTime),
|
||||
Status: int32(records[i].Status),
|
||||
}
|
||||
fetch[idx].From = card
|
||||
// 每条申请单独分配资料卡,避免所有元素共用同一指针
|
||||
card := &pb.PassportInfoDetailCard{}
|
||||
if e := impl.DBService.Table("passport_extend").Select(filed).
|
||||
Where("passport_identity = ?", records[i].FromIdentity).First(card).Error; e != nil {
|
||||
printer.Error(e.Error())
|
||||
// 申请人资料查不到时跳过该条,避免整份列表不可用
|
||||
continue
|
||||
}
|
||||
item.From = card
|
||||
applys = append(applys, item)
|
||||
}
|
||||
}
|
||||
|
||||
return &pb.JoinFetchReply{
|
||||
Total: int32(len(fetch)),
|
||||
Applys: fetch,
|
||||
Total: int32(len(applys)),
|
||||
Applys: applys,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"bsm/full/module/social/group/internal/impl"
|
||||
pb "bsm/full/module/social/group/pb"
|
||||
"gorm.io/gorm"
|
||||
@@ -11,14 +13,48 @@ func InitData() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetMyGroup(passport_id uint) (groups []*pb.GroupItem, err error) {
|
||||
// ToGroupItem 将群组模型转换为对外的 GroupItem
|
||||
func ToGroupItem(group *GroupBasic) *pb.GroupItem {
|
||||
if group == nil {
|
||||
return nil
|
||||
}
|
||||
return &pb.GroupItem{
|
||||
Id: int64(group.ID),
|
||||
Identity: group.Identity,
|
||||
Number: group.Number,
|
||||
Avatar: group.Avatar,
|
||||
Name: group.Name,
|
||||
Introduce: group.Introduce,
|
||||
CreatorId: int64(group.PassportID),
|
||||
CretorIdentity: group.PassportIdentity,
|
||||
MemberLimit: group.MemberLimit,
|
||||
Notice: group.Notice,
|
||||
Background: group.Background,
|
||||
MemberTotal: group.MemberTotal,
|
||||
EnableSearchByNumber: group.EnableSearchByNumber,
|
||||
EnableSearchByName: group.EnableSearchByName,
|
||||
CreatedAt: group.CreatedAt.Format(time.DateTime),
|
||||
}
|
||||
}
|
||||
|
||||
sql := `Select group.*,gm.nickname,gm.remark_name,gm.role
|
||||
From group_member as gm
|
||||
Left join group on group.id=gm.group_id
|
||||
Where passport_id=? and deleted_at is null`
|
||||
err = impl.DBService.Raw(sql, passport_id).Find(&groups).Error
|
||||
return
|
||||
// GetMyGroup 查询用户已加入的群组列表
|
||||
func GetMyGroup(passport_id uint) (groups []*pb.GroupItem, err error) {
|
||||
var ids []uint
|
||||
if err = impl.DBService.Model(&GroupMember{}).Where("passport_id = ?", passport_id).Pluck("group_id", &ids).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(ids) == 0 {
|
||||
return groups, nil
|
||||
}
|
||||
var basics []GroupBasic
|
||||
if err = impl.DBService.Where("id in ?", ids).Find(&basics).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
groups = make([]*pb.GroupItem, 0, len(basics))
|
||||
for i := range basics {
|
||||
groups = append(groups, ToGroupItem(&basics[i]))
|
||||
}
|
||||
return groups, nil
|
||||
}
|
||||
|
||||
func GetMasterGroup(passport_id uint) []uint {
|
||||
@@ -27,19 +63,23 @@ func GetMasterGroup(passport_id uint) []uint {
|
||||
return ids
|
||||
}
|
||||
|
||||
// GetGroupMember 查询群成员列表,成员资料来自外部表 passport_extend
|
||||
func GetGroupMember(group_identity string) (members []*pb.PassportInfoSimpleCard, err error) {
|
||||
sql := `Select pe.identity,pe.nickname,pe.avatar,pe.sex,gm.remark_name,gm.role
|
||||
From group_member as gm
|
||||
From relation_group_member as gm
|
||||
Left join passport_extend as pe on pe.passport_id=gm.passport_id
|
||||
Where group_identity=? and deleted_at is null`
|
||||
Where gm.group_identity=? and gm.deleted_at is null`
|
||||
err = impl.DBService.Raw(sql, group_identity).Find(&members).Error
|
||||
return
|
||||
}
|
||||
|
||||
func GetGroupEnableAnyJoin(passport_id uint, group_identity string) bool {
|
||||
var is bool = false
|
||||
impl.DBService.Model(&GroupBasic{}).Select("enable_any_join").Where("createor_id=? and identity=?", passport_id, group_identity).Scan(&is)
|
||||
return is
|
||||
// GetGroupEnableAnyJoin 查询群组是否开启免验证入群
|
||||
func GetGroupEnableAnyJoin(group_identity string) (bool, error) {
|
||||
var group GroupBasic
|
||||
if err := impl.DBService.Select("enable_any_join").Where("identity=?", group_identity).Take(&group).Error; err != nil {
|
||||
return false, err
|
||||
}
|
||||
return group.EnableAnyJoin, nil
|
||||
}
|
||||
|
||||
func UpsetGroupMemberTotal(group_identity string, op string) {
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
pb "bsm/full/module/social/group/pb"
|
||||
"context"
|
||||
|
||||
"git.apinb.com/bsm-sdk/core/printer"
|
||||
gwRuntime "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/reflection"
|
||||
@@ -17,10 +18,17 @@ type Server struct {
|
||||
grpcConns map[string]*grpc.ClientConn // 连接池
|
||||
}
|
||||
|
||||
func New(addr string) *Server {
|
||||
// New 创建服务实例
|
||||
// grpcServ 为空表示独立进程启动,自行创建 gRPC Server 并初始化网关路由
|
||||
func New(grpcServ *grpc.Server) *Server {
|
||||
standalone := grpcServ == nil
|
||||
if standalone {
|
||||
grpcServ = grpc.NewServer()
|
||||
}
|
||||
|
||||
srv := &Server{
|
||||
Ctx: context.Background(),
|
||||
Grpc: grpc.NewServer(),
|
||||
Grpc: grpcServ,
|
||||
grpcConns: make(map[string]*grpc.ClientConn),
|
||||
}
|
||||
|
||||
@@ -28,7 +36,22 @@ func New(addr string) *Server {
|
||||
pb.RegisterBasicServer(srv.Grpc, NewBasicServer())
|
||||
pb.RegisterMemberServer(srv.Grpc, NewMemberServer())
|
||||
|
||||
reflection.Register(srv.Grpc)
|
||||
if standalone {
|
||||
// 独立进程需自行初始化网关路由并注册 handler,否则 /group.* 全部 404
|
||||
srv.Mux = gwRuntime.NewServeMux()
|
||||
if err := RegisterGateway(srv.Ctx, srv.Mux); err != nil {
|
||||
printer.Error(err.Error())
|
||||
}
|
||||
reflection.Register(srv.Grpc)
|
||||
}
|
||||
|
||||
return srv
|
||||
}
|
||||
|
||||
// RegisterGateway 注册各 service 的 gateway handler
|
||||
func RegisterGateway(ctx context.Context, mux *gwRuntime.ServeMux) error {
|
||||
if err := pb.RegisterBasicHandlerServer(ctx, mux, NewBasicServer()); err != nil {
|
||||
return err
|
||||
}
|
||||
return pb.RegisterMemberHandlerServer(ctx, mux, NewMemberServer())
|
||||
}
|
||||
|
||||
32
module/social/group/service/dependencies.go
Normal file
32
module/social/group/service/dependencies.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"bsm/full/module/social/group/internal/impl"
|
||||
"git.apinb.com/bsm-sdk/core/cache/redis"
|
||||
cache "github.com/patrickmn/go-cache"
|
||||
clientv3 "go.etcd.io/etcd/client/v3"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Dependencies 聚合宿主注入的共享基础设施
|
||||
type Dependencies struct {
|
||||
Redis *redis.RedisClient
|
||||
Etcd *clientv3.Client
|
||||
DB *gorm.DB
|
||||
Cache *cache.Cache
|
||||
}
|
||||
|
||||
func applyDependencies(deps Dependencies) {
|
||||
if deps.Redis != nil {
|
||||
impl.RedisService = deps.Redis
|
||||
}
|
||||
if deps.Etcd != nil {
|
||||
impl.EtcdService = deps.Etcd
|
||||
}
|
||||
if deps.DB != nil {
|
||||
impl.DBService = deps.DB
|
||||
}
|
||||
if deps.Cache != nil {
|
||||
impl.MemorySerice = deps.Cache
|
||||
}
|
||||
}
|
||||
26
module/social/group/service/expose.go
Normal file
26
module/social/group/service/expose.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"bsm/full/module/social/group/internal/server"
|
||||
gwRuntime "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
type ExposeOptions struct {
|
||||
Dependencies
|
||||
GRPC *grpc.Server
|
||||
Gateway *gwRuntime.ServeMux
|
||||
}
|
||||
|
||||
// Expose 供聚合宿主接入:注册 gRPC service 与网关路由
|
||||
func Expose(options ExposeOptions) error {
|
||||
applyDependencies(options.Dependencies)
|
||||
server.New(options.GRPC)
|
||||
|
||||
if options.Gateway == nil {
|
||||
return nil
|
||||
}
|
||||
return server.RegisterGateway(context.Background(), options.Gateway)
|
||||
}
|
||||
@@ -20,7 +20,7 @@ func Run() {
|
||||
impl.NewImpl()
|
||||
|
||||
// 初始化服务
|
||||
s := server.New(config.Spec.Addr)
|
||||
s := server.New(nil)
|
||||
srv := service.New(
|
||||
s.Grpc,
|
||||
&service.Options{
|
||||
|
||||
@@ -21,9 +21,9 @@ func GetrelationInfoDetailCard(identity string) (card *pb.RelationItem, err erro
|
||||
return
|
||||
}
|
||||
|
||||
// 根据id数组获取多条会员信息卡片
|
||||
func GetrelationInfoDetailCardById(ids []uint) (cards []*pb.RelationItem, err error) {
|
||||
err = impl.DBService.Table("relation_extend").Select(Filed).Where("relation_id in ?", ids).Find(&cards).Error
|
||||
// 根据identity数组获取多条会员信息卡片
|
||||
func GetrelationInfoDetailCardById(identities []string) (cards []*pb.RelationItem, err error) {
|
||||
err = impl.DBService.Table("relation_extend").Select(Filed).Where("relation_identity in ?", identities).Find(&cards).Error
|
||||
return
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ func UniqueSessionID(s1, s2 string) string {
|
||||
func CollectionFriendApply(id uint) (reply *pb.ApplyFetchReply, err error) {
|
||||
var (
|
||||
version int64
|
||||
ids []uint
|
||||
identities []string
|
||||
last_msg_ids []uint
|
||||
maxSize int = 50
|
||||
applys = make([]*models.FriendApply, 0)
|
||||
@@ -57,11 +57,12 @@ func CollectionFriendApply(id uint) (reply *pb.ApplyFetchReply, err error) {
|
||||
}
|
||||
|
||||
for _, item := range applys {
|
||||
ids = append(ids, item.FromID)
|
||||
identities = append(identities, item.FromIdentity)
|
||||
last_msg_ids = append(last_msg_ids, item.LastMessageID)
|
||||
}
|
||||
|
||||
result, err := GetrelationInfoDetailCardById(ids)
|
||||
// 按申请人身份取资料卡
|
||||
result, err := GetrelationInfoDetailCardById(identities)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -107,41 +108,61 @@ func CollectionFriendApply(id uint) (reply *pb.ApplyFetchReply, err error) {
|
||||
// 根据用户ID,找到用户的好友信息和分组信息
|
||||
func CollectionFriendData(id uint) (reply *pb.FriendsReply, err error) {
|
||||
var (
|
||||
version int64
|
||||
ids []uint
|
||||
version int64
|
||||
identities []string
|
||||
|
||||
relationFriend = make([]models.RelationFriend, 0)
|
||||
mapRelationFrend = make(map[uint]models.RelationFriend)
|
||||
mapRelationFrend = make(map[string]models.RelationFriend)
|
||||
|
||||
friendInTag = make([]models.FriendInTag, 0)
|
||||
mapFriendInTag = make(map[string][]string)
|
||||
mapTagTotal = make(map[string]int64)
|
||||
)
|
||||
reply = &pb.FriendsReply{}
|
||||
impl.DBService.Where("relation_id=?", id).Order("id desc").Find(&relationFriend)
|
||||
// 好友条目归属列是 passport_id,好友身份是 friend_relation_identity
|
||||
err = impl.DBService.Where("passport_id=?", id).Order("id desc").Find(&relationFriend).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, item := range relationFriend {
|
||||
ids = append(ids, item.ID)
|
||||
mapRelationFrend[item.ID] = item
|
||||
identities = append(identities, item.FriendrelationIdentity)
|
||||
mapRelationFrend[item.FriendrelationIdentity] = item
|
||||
version = int64(item.ID)
|
||||
}
|
||||
|
||||
impl.DBService.Where("relation_id=?", id).Find(&friendInTag)
|
||||
err = impl.DBService.Where("passport_id=?", id).Find(&friendInTag).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, item := range friendInTag {
|
||||
mapFriendInTag[item.FriendIdentity] = append(mapFriendInTag[item.FriendIdentity], item.TagIdentity)
|
||||
mapTagTotal[item.TagIdentity]++
|
||||
}
|
||||
|
||||
err = impl.DBService.Model(models.FriendTag{}).Where("relation_id=?", id).Order("id asc").Scan(&reply.Tags).Error
|
||||
// 标签列表按归属过滤并补齐标签名
|
||||
err = impl.DBService.Model(models.FriendTag{}).Select("id, identity, name as tag_name").Where("passport_id=?", id).Order("id asc").Scan(&reply.Tags).Error
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// 补齐每个标签的成员数
|
||||
for idx := range reply.Tags {
|
||||
reply.Tags[idx].FriendTotal = mapTagTotal[reply.Tags[idx].Identity]
|
||||
}
|
||||
|
||||
reply.Friends, err = GetrelationInfoDetailCardById(ids)
|
||||
for idx, item := range reply.Friends {
|
||||
reply.Friends[idx].RemarkName = item.RemarkName
|
||||
reply.Friends[idx].Popular = item.Popular
|
||||
if _, ok := mapFriendInTag[item.Identity]; ok {
|
||||
reply.Friends[idx].Tags = mapFriendInTag[item.Identity]
|
||||
// 按好友身份装配资料卡,并回填备注与置顶
|
||||
reply.Friends, err = GetrelationInfoDetailCardById(identities)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for idx := range reply.Friends {
|
||||
if item, ok := mapRelationFrend[reply.Friends[idx].Identity]; ok {
|
||||
reply.Friends[idx].RemarkName = item.RemarkName
|
||||
reply.Friends[idx].Popular = int32(item.Popular)
|
||||
}
|
||||
if tags, ok := mapFriendInTag[reply.Friends[idx].Identity]; ok {
|
||||
reply.Friends[idx].Tags = tags
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,7 +175,7 @@ func CollectionFriendData(id uint) (reply *pb.FriendsReply, err error) {
|
||||
func GetrelationInfoDetailCardByMatch(relation_identity string, offset, limit int) (cards []*pb.RelationItem, err error) {
|
||||
sql := `Select
|
||||
pe.relation_identity as identity,
|
||||
pe.name,
|
||||
pe.nickname,
|
||||
pe.avatar,
|
||||
pe.birthday,
|
||||
pe.sex,
|
||||
@@ -163,13 +184,13 @@ func GetrelationInfoDetailCardByMatch(relation_identity string, offset, limit in
|
||||
pe.area,
|
||||
pe.sign,
|
||||
rm.status as foreign_status
|
||||
From relation_match as rm,relation_extend as pe
|
||||
Left join pe on pe.relation_identity=rm.recommend_identity
|
||||
From relation_match as rm
|
||||
Join relation_extend as pe on pe.relation_identity=rm.recommend_identity
|
||||
Where rm.relation_identity=?
|
||||
Offset ?
|
||||
Order by rm.id Desc
|
||||
Limit ?
|
||||
Order by rm.id Desc`
|
||||
err = impl.DBService.Raw(sql, relation_identity, offset, limit).Scan(&cards).Error
|
||||
Offset ?`
|
||||
err = impl.DBService.Raw(sql, relation_identity, limit, offset).Scan(&cards).Error
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
package follow
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
pb "bsm/full/module/social/relation/pb"
|
||||
"git.apinb.com/bsm-sdk/core/errcode"
|
||||
"git.apinb.com/bsm-sdk/core/service"
|
||||
"git.apinb.com/bsm-sdk/core/vars"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 执行关注
|
||||
func Do(ctx context.Context, in *pb.IdentRequest) (reply *pb.DataStatusReply, err error) {
|
||||
// parse authorization meta.
|
||||
_, err = service.ParseMetaCtx(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// valildate request id,identity.
|
||||
if in.Id == 0 && in.Identity == "" {
|
||||
return nil, errcode.ErrInvalidArgument
|
||||
}
|
||||
|
||||
// TODO: add your logic code & delete this line.
|
||||
|
||||
return &pb.DataStatusReply{
|
||||
Data: vars.OK,
|
||||
Timeseq: time.Now().UnixMilli(),
|
||||
}, nil
|
||||
|
||||
}
|
||||
@@ -22,7 +22,7 @@ func Fetch(ctx context.Context, in *pb.FetchRequest) (reply *pb.FetchRelationIte
|
||||
var (
|
||||
total int64
|
||||
column string
|
||||
ids []uint
|
||||
ids []string
|
||||
model = impl.DBService.Model(&models.RelationFollow{})
|
||||
cards []*pb.RelationItem
|
||||
)
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
|
||||
// 撤销关注
|
||||
func Undo(ctx context.Context, in *pb.IdentRequest) (reply *pb.DataStatusReply, err error) {
|
||||
_, err = service.ParseMetaCtx(ctx, nil)
|
||||
auth, err := service.ParseMetaCtx(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -23,7 +23,8 @@ func Undo(ctx context.Context, in *pb.IdentRequest) (reply *pb.DataStatusReply,
|
||||
return nil, errcode.ErrInvalidArgument
|
||||
}
|
||||
|
||||
err = impl.DBService.Model(&models.RelationFollow{}).Delete("identity=?", in.Identity).Error
|
||||
// 只能撤销自己发起的关注:归属列是 from_identity
|
||||
err = impl.DBService.Model(&models.RelationFollow{}).Delete("identity=? and from_identity=?", in.Identity, auth.Identity).Error
|
||||
if err != nil {
|
||||
printer.Error(err.Error())
|
||||
return nil, errcode.ErrDB
|
||||
|
||||
@@ -23,6 +23,14 @@ func ApplyDoMessage(ctx context.Context, in *pb.ApplyMessageRequest) (reply *pb.
|
||||
return nil, errcode.ErrInvalidArgument
|
||||
}
|
||||
|
||||
// 校验该申请存在且属于当前调用者(申请方或被申请方),避免污染他人申请会话
|
||||
var apply models.FriendApply
|
||||
err = impl.DBService.Where("id=? and (from_identity=? or to_identity=?)", in.ApplyId, auth.Identity, auth.Identity).First(&apply).Error
|
||||
if err != nil {
|
||||
printer.Error(err.Error())
|
||||
return nil, errcode.ErrPermissionDenied
|
||||
}
|
||||
|
||||
msg := models.FriendApplyMessage{
|
||||
ApplyID: uint(in.ApplyId),
|
||||
Body: in.Body,
|
||||
@@ -36,8 +44,8 @@ func ApplyDoMessage(ctx context.Context, in *pb.ApplyMessageRequest) (reply *pb.
|
||||
return nil, errcode.ErrDB
|
||||
}
|
||||
|
||||
//回写最后一次交流的消息ID
|
||||
err = impl.DBService.Model(models.FriendApply{}).Where("id=?", msg.ApplyID).UpdateColumn("last_message_id", msg.ID).Error
|
||||
//回写最后一次交流的消息ID(仍限定申请归属)
|
||||
err = impl.DBService.Model(models.FriendApply{}).Where("id=? and (from_identity=? or to_identity=?)", msg.ApplyID, auth.Identity, auth.Identity).UpdateColumn("last_message_id", msg.ID).Error
|
||||
if err != nil {
|
||||
printer.Error(err.Error())
|
||||
return nil, errcode.ErrDB
|
||||
|
||||
@@ -25,12 +25,24 @@ func ApplyDoPass(ctx context.Context, in *pb.ApplyDoPassRequest) (reply *pb.Data
|
||||
return nil, errcode.ErrInvalidArgument
|
||||
}
|
||||
|
||||
// 通过前先按 identity 查出属于当前调用者(被申请方)的申请记录
|
||||
var apply models.FriendApply
|
||||
err = impl.DBService.Where("identity=? and to_identity=?", in.ApplyIdentity, auth.Identity).First(&apply).Error
|
||||
if err != nil {
|
||||
printer.Error(err.Error())
|
||||
return nil, errcode.ErrPermissionDenied
|
||||
}
|
||||
// 申请记录里的申请人必须与请求要加为好友的对象一致,否则视为无申请依据
|
||||
if apply.FromIdentity != in.FriendRelationIdentity {
|
||||
return nil, errcode.ErrPermissionDenied
|
||||
}
|
||||
|
||||
//写入好友表
|
||||
friend := &models.RelationFriend{FriendrelationID: uint(in.FriendRelationId), FriendrelationIdentity: in.FriendRelationIdentity}
|
||||
friend := &models.RelationFriend{FriendrelationID: apply.FromID, FriendrelationIdentity: apply.FromIdentity}
|
||||
friend.Identity = utils.UUID()
|
||||
friend.PassportID = auth.ID
|
||||
friend.PassportIdentity = auth.Identity
|
||||
friend.SessionID = common.UniqueSessionID(auth.Identity, in.FriendRelationIdentity)
|
||||
friend.SessionID = common.UniqueSessionID(auth.Identity, apply.FromIdentity)
|
||||
|
||||
err = impl.DBService.Create(&friend).Error
|
||||
if err != nil {
|
||||
@@ -40,7 +52,7 @@ func ApplyDoPass(ctx context.Context, in *pb.ApplyDoPassRequest) (reply *pb.Data
|
||||
|
||||
//EventMQ:向mesh MQ中心发送报文,请求推送消息以及更新Cache.
|
||||
|
||||
err = impl.DBService.Model(models.FriendApply{}).Where("to_identity=? and identity=?", auth.Identity, in.ApplyIdentity).UpdateColumn("status", 1).Error
|
||||
err = impl.DBService.Model(models.FriendApply{}).Where("to_identity=? and identity=?", auth.Identity, apply.Identity).UpdateColumn("status", 1).Error
|
||||
if err != nil {
|
||||
printer.Error(err.Error())
|
||||
return nil, errcode.ErrDB
|
||||
|
||||
@@ -45,8 +45,12 @@ func ApplyFetch(ctx context.Context, in *pb.VersionRequest) (reply *pb.ApplyFetc
|
||||
}
|
||||
|
||||
if cacheErr != nil {
|
||||
printer.Error(err.Error())
|
||||
printer.Error(cacheErr.Error())
|
||||
return nil, errcode.ErrRedis
|
||||
}
|
||||
if reply == nil {
|
||||
// 版本一致未走查询分支时兜底返回非 nil 空集合,避免序列化失败
|
||||
reply = &pb.ApplyFetchReply{}
|
||||
}
|
||||
return reply, nil
|
||||
}
|
||||
|
||||
@@ -10,11 +10,12 @@ import (
|
||||
"git.apinb.com/bsm-sdk/core/errcode"
|
||||
"git.apinb.com/bsm-sdk/core/printer"
|
||||
"git.apinb.com/bsm-sdk/core/service"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// 好友申请详情
|
||||
func ApplyGet(ctx context.Context, in *pb.IdentRequest) (reply *pb.FriendApplyGetReply, err error) {
|
||||
_, err = service.ParseMetaCtx(ctx, nil)
|
||||
auth, err := service.ParseMetaCtx(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -22,10 +23,14 @@ func ApplyGet(ctx context.Context, in *pb.IdentRequest) (reply *pb.FriendApplyGe
|
||||
return nil, errcode.ErrInvalidArgument
|
||||
}
|
||||
|
||||
// 只允许查看与自己相关的申请(申请方或被申请方)
|
||||
var apply models.FriendApply
|
||||
err = impl.DBService.Where("identity=?", in.Identity).First(&apply).Error
|
||||
err = impl.DBService.Where("identity=? and (from_identity=? or to_identity=?)", in.Identity, auth.Identity, auth.Identity).First(&apply).Error
|
||||
if err != nil {
|
||||
printer.Error(err.Error())
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return nil, errcode.ErrPermissionDenied
|
||||
}
|
||||
return nil, errcode.ErrDB
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,8 @@ func Delete(ctx context.Context, in *pb.IdentRequest) (reply *pb.DataStatusReply
|
||||
return nil, errcode.ErrInvalidArgument
|
||||
}
|
||||
|
||||
err = impl.DBService.Model(models.RelationFriend{}).Delete("relation_id=? and identity=?", auth.ID, in.Identity).Error
|
||||
// 归属列是 passport_id,in.Identity 是好友身份(friend_relation_identity)
|
||||
err = impl.DBService.Model(models.RelationFriend{}).Delete("passport_id=? and friend_relation_identity=?", auth.ID, in.Identity).Error
|
||||
if err != nil {
|
||||
printer.Error(err.Error())
|
||||
return nil, errcode.ErrDB
|
||||
|
||||
@@ -24,7 +24,8 @@ func DoPopular(ctx context.Context, in *pb.IdentRequest) (reply *pb.DataStatusRe
|
||||
return nil, errcode.ErrInvalidArgument
|
||||
}
|
||||
|
||||
err = impl.DBService.Model(models.RelationFriend{}).Where("relation_id=? and identity=?", auth.ID, in.Identity).UpdateColumn("popular", 1).Error
|
||||
// 归属列是 passport_id,in.Identity 是好友身份(friend_relation_identity)
|
||||
err = impl.DBService.Model(models.RelationFriend{}).Where("passport_id=? and friend_relation_identity=?", auth.ID, in.Identity).UpdateColumn("popular", 1).Error
|
||||
if err != nil {
|
||||
printer.Error(err.Error())
|
||||
return nil, errcode.ErrDB
|
||||
|
||||
@@ -45,8 +45,12 @@ func Fetch(ctx context.Context, in *pb.VersionRequest) (reply *pb.FriendsReply,
|
||||
}
|
||||
|
||||
if cacheErr != nil {
|
||||
printer.Error(err.Error())
|
||||
printer.Error(cacheErr.Error())
|
||||
return nil, errcode.ErrRedis
|
||||
}
|
||||
if reply == nil {
|
||||
// 版本一致未走查询分支时兜底返回非 nil 空集合,避免序列化失败
|
||||
reply = &pb.FriendsReply{}
|
||||
}
|
||||
return reply, nil
|
||||
}
|
||||
|
||||
@@ -23,7 +23,8 @@ func ModifyNickname(ctx context.Context, in *pb.ModifyNicknameRequest) (reply *p
|
||||
return nil, errcode.ErrInvalidArgument
|
||||
}
|
||||
|
||||
err = impl.DBService.Model(models.RelationFriend{}).Where("relation_id=? and identity=?", auth.ID, in.Identity).UpdateColumn("nickname", in.Nickname).Error
|
||||
// 归属列是 passport_id,in.Identity 是好友身份(friend_relation_identity);备注列是 remark_name
|
||||
err = impl.DBService.Model(models.RelationFriend{}).Where("passport_id=? and friend_relation_identity=?", auth.ID, in.Identity).UpdateColumn("remark_name", in.Nickname).Error
|
||||
if err != nil {
|
||||
printer.Error(err.Error())
|
||||
return nil, errcode.ErrDB
|
||||
|
||||
@@ -27,6 +27,24 @@ func TagDoUpdate(ctx context.Context, in *pb.TagDoUpdateRequest) (reply *pb.Data
|
||||
|
||||
switch strings.ToUpper(in.Direction) {
|
||||
case "ADD":
|
||||
// 校验标签归属当前调用者
|
||||
var tag models.FriendTag
|
||||
err = impl.DBService.Where("identity=? and passport_id=?", in.TagIdentity, auth.ID).First(&tag).Error
|
||||
if err != nil {
|
||||
printer.Error(err.Error())
|
||||
return nil, errcode.ErrPermissionDenied
|
||||
}
|
||||
// 校验目标确实是当前调用者的好友
|
||||
var friendCount int64
|
||||
err = impl.DBService.Model(models.RelationFriend{}).Where("passport_id=? and friend_relation_identity=?", auth.ID, in.FriendIdentity).Count(&friendCount).Error
|
||||
if err != nil {
|
||||
printer.Error(err.Error())
|
||||
return nil, errcode.ErrDB
|
||||
}
|
||||
if friendCount == 0 {
|
||||
return nil, errcode.ErrPermissionDenied
|
||||
}
|
||||
|
||||
data := models.FriendInTag{
|
||||
FriendIdentity: in.FriendIdentity,
|
||||
TagIdentity: in.TagIdentity,
|
||||
@@ -36,7 +54,8 @@ func TagDoUpdate(ctx context.Context, in *pb.TagDoUpdateRequest) (reply *pb.Data
|
||||
|
||||
err = impl.DBService.Create(&data).Error
|
||||
case "DEL":
|
||||
err = impl.DBService.Model(models.FriendInTag{}).Delete("friend_identity=? and tag_identity=?", in.FriendIdentity, in.TagIdentity).Error
|
||||
// 归属列是 passport_id,只能移除自己标签内的成员
|
||||
err = impl.DBService.Model(models.FriendInTag{}).Delete("friend_identity=? and tag_identity=? and passport_id=?", in.FriendIdentity, in.TagIdentity, auth.ID).Error
|
||||
default:
|
||||
return nil, errcode.ErrInvalidArgument
|
||||
}
|
||||
|
||||
@@ -19,7 +19,15 @@ func TagFetch(ctx context.Context, in *pb.Empty) (reply *pb.FriendTagsReply, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = impl.DBService.Model(models.FriendTag{}).Where("relation_id=?", auth.ID).Count(&reply.Total).Order("id asc").Scan(&reply.Data).Error
|
||||
// 先初始化返回值,避免对 nil 指针取字段地址
|
||||
reply = &pb.FriendTagsReply{}
|
||||
// 标签归属列是 passport_id
|
||||
err = impl.DBService.Model(models.FriendTag{}).Where("passport_id=?", auth.ID).Count(&reply.Total).Error
|
||||
if err != nil {
|
||||
printer.Error(err.Error())
|
||||
return nil, errcode.ErrDB
|
||||
}
|
||||
err = impl.DBService.Model(models.FriendTag{}).Select("id, identity, name as tag_name").Where("passport_id=?", auth.ID).Order("id asc").Scan(&reply.Data).Error
|
||||
if err != nil {
|
||||
printer.Error(err.Error())
|
||||
return nil, errcode.ErrDB
|
||||
|
||||
@@ -23,8 +23,9 @@ func TagMemberFetch(ctx context.Context, in *pb.IdentRequest) (reply *pb.PartFri
|
||||
return nil, errcode.ErrInvalidArgument
|
||||
}
|
||||
|
||||
var ids []uint
|
||||
err = impl.DBService.Model(models.RelationFriend{}).Where("relation_id=? and group_identity=?", auth.ID, in.Identity).Pluck("relation_id", ids).Error
|
||||
// 标签成员在 relation_friend_in_tag,归属列是 passport_id,标签列是 tag_identity
|
||||
var ids []string
|
||||
err = impl.DBService.Model(models.FriendInTag{}).Where("passport_id=? and tag_identity=?", auth.ID, in.Identity).Pluck("friend_identity", &ids).Error
|
||||
if err != nil {
|
||||
printer.Error(err.Error())
|
||||
return nil, errcode.ErrDB
|
||||
|
||||
@@ -24,7 +24,8 @@ func UndoPopular(ctx context.Context, in *pb.IdentRequest) (reply *pb.DataStatus
|
||||
return nil, errcode.ErrInvalidArgument
|
||||
}
|
||||
|
||||
err = impl.DBService.Model(models.RelationFriend{}).Where("relation_id=? and identity=?", auth.ID, in.Identity).UpdateColumn("popular", 0).Error
|
||||
// 归属列是 passport_id,in.Identity 是好友身份(friend_relation_identity)
|
||||
err = impl.DBService.Model(models.RelationFriend{}).Where("passport_id=? and friend_relation_identity=?", auth.ID, in.Identity).UpdateColumn("popular", 0).Error
|
||||
if err != nil {
|
||||
printer.Error(err.Error())
|
||||
return nil, errcode.ErrDB
|
||||
|
||||
@@ -13,10 +13,10 @@ import (
|
||||
"git.apinb.com/bsm-sdk/core/vars"
|
||||
)
|
||||
|
||||
// 执行忽略
|
||||
// 执行忽略:标记匹配记录状态为已忽略
|
||||
func DoIgnore(ctx context.Context, in *pb.IdentRequest) (reply *pb.DataStatusReply, err error) {
|
||||
// parse authorization meta.
|
||||
_, err = service.ParseMetaCtx(ctx, nil)
|
||||
auth, err := service.ParseMetaCtx(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -24,11 +24,15 @@ func DoIgnore(ctx context.Context, in *pb.IdentRequest) (reply *pb.DataStatusRep
|
||||
return nil, errcode.ErrInvalidArgument
|
||||
}
|
||||
|
||||
err = impl.DBService.Model(new(models.RelationFollow)).Delete("identity=?", in.Identity).Error
|
||||
if err != nil {
|
||||
printer.Error(err.Error())
|
||||
// 操作匹配记录表并限定归属,status=-1 表示已忽略
|
||||
result := impl.DBService.Model(new(models.RelationMatch)).Where("relation_identity=? and recommend_identity=?", auth.Identity, in.Identity).UpdateColumn("status", -1)
|
||||
if result.Error != nil {
|
||||
printer.Error(result.Error.Error())
|
||||
return nil, errcode.ErrDB
|
||||
}
|
||||
if result.RowsAffected == 0 {
|
||||
return nil, errcode.ErrPermissionDenied
|
||||
}
|
||||
return &pb.DataStatusReply{
|
||||
Data: vars.OK,
|
||||
Timeseq: time.Now().UnixMilli(),
|
||||
|
||||
@@ -10,11 +10,10 @@ import (
|
||||
"git.apinb.com/bsm-sdk/core/errcode"
|
||||
"git.apinb.com/bsm-sdk/core/printer"
|
||||
"git.apinb.com/bsm-sdk/core/service"
|
||||
"git.apinb.com/bsm-sdk/core/utils"
|
||||
"git.apinb.com/bsm-sdk/core/vars"
|
||||
)
|
||||
|
||||
// 执行通过,加为好友
|
||||
// 执行通过:标记匹配记录状态为已通过
|
||||
func DoJoin(ctx context.Context, in *pb.IdentRequest) (reply *pb.DataStatusReply, err error) {
|
||||
// parse authorization meta.
|
||||
auth, err := service.ParseMetaCtx(ctx, nil)
|
||||
@@ -26,17 +25,15 @@ func DoJoin(ctx context.Context, in *pb.IdentRequest) (reply *pb.DataStatusReply
|
||||
return nil, errcode.ErrInvalidArgument
|
||||
}
|
||||
|
||||
data := &models.RelationFollow{
|
||||
FromIdentity: auth.Identity,
|
||||
ToIdentity: in.Identity,
|
||||
}
|
||||
data.Identity = utils.UUID()
|
||||
|
||||
err = impl.DBService.Create(data).Error
|
||||
if err != nil {
|
||||
printer.Error(err.Error())
|
||||
// 操作匹配记录表并限定归属,status=1 表示已通过
|
||||
result := impl.DBService.Model(&models.RelationMatch{}).Where("relation_identity=? and recommend_identity=?", auth.Identity, in.Identity).UpdateColumn("status", 1)
|
||||
if result.Error != nil {
|
||||
printer.Error(result.Error.Error())
|
||||
return nil, errcode.ErrDB
|
||||
}
|
||||
if result.RowsAffected == 0 {
|
||||
return nil, errcode.ErrPermissionDenied
|
||||
}
|
||||
return &pb.DataStatusReply{
|
||||
Data: vars.OK,
|
||||
Timeseq: time.Now().UnixMilli(),
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
pb "bsm/full/module/social/relation/pb"
|
||||
"context"
|
||||
|
||||
"git.apinb.com/bsm-sdk/core/printer"
|
||||
gwRuntime "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/reflection"
|
||||
@@ -17,10 +18,17 @@ type Server struct {
|
||||
grpcConns map[string]*grpc.ClientConn // 连接池
|
||||
}
|
||||
|
||||
func New(addr string) *Server {
|
||||
// New 创建服务实例
|
||||
// grpcServ 为空表示独立进程启动,自行创建 gRPC Server 并初始化网关路由
|
||||
func New(grpcServ *grpc.Server) *Server {
|
||||
standalone := grpcServ == nil
|
||||
if standalone {
|
||||
grpcServ = grpc.NewServer()
|
||||
}
|
||||
|
||||
srv := &Server{
|
||||
Ctx: context.Background(),
|
||||
Grpc: grpc.NewServer(),
|
||||
Grpc: grpcServ,
|
||||
grpcConns: make(map[string]*grpc.ClientConn),
|
||||
}
|
||||
|
||||
@@ -29,7 +37,25 @@ func New(addr string) *Server {
|
||||
pb.RegisterFriendServer(srv.Grpc, NewFriendServer())
|
||||
pb.RegisterMatchServer(srv.Grpc, NewMatchServer())
|
||||
|
||||
reflection.Register(srv.Grpc)
|
||||
if standalone {
|
||||
// 独立进程需自行初始化网关路由并注册 handler,否则 /relation.* 全部 404
|
||||
srv.Mux = gwRuntime.NewServeMux()
|
||||
if err := RegisterGateway(srv.Ctx, srv.Mux); err != nil {
|
||||
printer.Error(err.Error())
|
||||
}
|
||||
reflection.Register(srv.Grpc)
|
||||
}
|
||||
|
||||
return srv
|
||||
}
|
||||
|
||||
// RegisterGateway 注册各 service 的 gateway handler
|
||||
func RegisterGateway(ctx context.Context, mux *gwRuntime.ServeMux) error {
|
||||
if err := pb.RegisterFollowHandlerServer(ctx, mux, NewFollowServer()); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := pb.RegisterFriendHandlerServer(ctx, mux, NewFriendServer()); err != nil {
|
||||
return err
|
||||
}
|
||||
return pb.RegisterMatchHandlerServer(ctx, mux, NewMatchServer())
|
||||
}
|
||||
|
||||
32
module/social/relation/service/dependencies.go
Normal file
32
module/social/relation/service/dependencies.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"bsm/full/module/social/relation/internal/impl"
|
||||
"git.apinb.com/bsm-sdk/core/cache/redis"
|
||||
cache "github.com/patrickmn/go-cache"
|
||||
clientv3 "go.etcd.io/etcd/client/v3"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Dependencies 聚合宿主注入的共享基础设施
|
||||
type Dependencies struct {
|
||||
Redis *redis.RedisClient
|
||||
Etcd *clientv3.Client
|
||||
DB *gorm.DB
|
||||
Cache *cache.Cache
|
||||
}
|
||||
|
||||
func applyDependencies(deps Dependencies) {
|
||||
if deps.Redis != nil {
|
||||
impl.RedisService = deps.Redis
|
||||
}
|
||||
if deps.Etcd != nil {
|
||||
impl.EtcdService = deps.Etcd
|
||||
}
|
||||
if deps.DB != nil {
|
||||
impl.DBService = deps.DB
|
||||
}
|
||||
if deps.Cache != nil {
|
||||
impl.MemorySerice = deps.Cache
|
||||
}
|
||||
}
|
||||
26
module/social/relation/service/expose.go
Normal file
26
module/social/relation/service/expose.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"bsm/full/module/social/relation/internal/server"
|
||||
gwRuntime "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
type ExposeOptions struct {
|
||||
Dependencies
|
||||
GRPC *grpc.Server
|
||||
Gateway *gwRuntime.ServeMux
|
||||
}
|
||||
|
||||
// Expose 供聚合宿主接入:注册 gRPC service 与网关路由
|
||||
func Expose(options ExposeOptions) error {
|
||||
applyDependencies(options.Dependencies)
|
||||
server.New(options.GRPC)
|
||||
|
||||
if options.Gateway == nil {
|
||||
return nil
|
||||
}
|
||||
return server.RegisterGateway(context.Background(), options.Gateway)
|
||||
}
|
||||
Reference in New Issue
Block a user