Compare commits

..

32 Commits

Author SHA1 Message Date
wwweww 7a12a9e702 fix(k8s): add Harbor registry prefix to all service image references 2026-05-04 13:22:25 +08:00
wwweww fe84f68648 fix(jenkins): use curl --get --data-urlencode to avoid shell glob, no python3 required 2026-05-04 12:53:29 +08:00
wwweww a3c7c013c5 fix(jenkins): replace curl with python3 urllib to avoid shell glob and timeout issues 2026-05-04 12:44:44 +08:00
wwweww c523ebcf89 fix(jenkins): pass URL via shell variable to avoid glob expansion 2026-05-04 12:25:54 +08:00
wwweww bbccb3e05e fix(jenkins): quote URL with query params to prevent shell glob expansion 2026-05-04 12:19:57 +08:00
wwweww 4bdd040e91 fix(jenkins): poll from Harbor API instead of k8s yaml, add FORCE_DEPLOY param 2026-05-04 02:55:26 +08:00
wwweww 590e5b0cd2 Merge pull request '添加jenkins' (#3) from jenkins/init into main
Reviewed-on: http://103.236.53.208:3000/juwan/juwan-backend/pulls/3
2026-05-03 18:52:13 +00:00
wwweww ad5bff534c Merge branch 'main' into jenkins/init 2026-05-03 14:53:31 +00:00
wwweww 13eb299316 添加jenkins 2026-05-03 22:41:40 +08:00
zetaloop a3518d20f1 docs: 在接口文档补全 title 与 desc 并标注 ID 序列化形式 2026-05-03 20:58:28 +08:00
zetaloop 9654147054 chore: 更新前端仓库 2026-05-03 19:03:56 +08:00
zetaloop 44c73e787f docs: 重新生成 swagger 接口文档 2026-05-03 19:03:09 +08:00
zetaloop 164f98cf33 docs: 重写 dev README 对齐 per-domain 拓扑与前端集成 2026-05-03 19:00:58 +08:00
zetaloop 60a3530609 docs: 归档基于旧版 k8s 的 jwt 加密部署文档 2026-05-03 18:59:44 +08:00
zetaloop d19cb53f24 docs: 清理过时的部署文档与模板残留 2026-05-03 18:59:11 +08:00
zetaloop b08a3a51e0 feat: 接入 frontend 子模块并集成 envoy 2026-05-03 09:22:29 +08:00
zetaloop 9a32850030 feat: admin 使用固定 ID 并补全全角色权限和演示数据 2026-05-03 08:21:28 +08:00
zetaloop 776ecc479f fix(users-rpc): 移除未使用的 DataSource 配置字段 2026-05-03 07:57:19 +08:00
zetaloop 631469a713 fix: 给有 FK 依赖的建表脚本加数字前缀以保证字母序就是依赖序 2026-05-03 07:57:19 +08:00
zetaloop 22c7c4e7d9 refactor: 用 per-domain fixture 替代 seed.py 2026-05-03 07:57:19 +08:00
zetaloop d1031f48b3 refactor: 拆分 postgres 为 per-domain 数据库 2026-05-03 07:57:19 +08:00
zetaloop 429a1df32a refactor: 启用 conf.UseEnv 并将 etc 配置统一为 env 占位 2026-05-03 07:57:19 +08:00
zetaloop 87bf86a410 fix: 重命名 objectStorge 为 objectstory 并修复 service ports 缩进 2026-05-03 07:57:19 +08:00
zetaloop 227f5814df feat(player): 新增 GET /players/me 查询当前用户打手资料 2026-05-03 06:18:01 +08:00
zetaloop cf639f1bca fix: int64 ID 字段序列化为 JSON string 2026-05-03 05:58:21 +08:00
wwweww 83fa982749 补齐各个服务的k8s配置清单 2026-05-02 11:57:07 +08:00
zetaloop 41890ddd33 fix: 修复 player 详情 completionRate 和 k8s 公开路由
同步 k8s Envoy 中已放行公开接口的实际路由,避免落到泛用 user API;同时规范 dev API 测试脚本的响应体解析,使新增负向断言通过静态检查。
2026-04-29 23:42:46 +08:00
zetaloop 2341284f6c Merge branch 'main-merge-base-a' 2026-04-29 23:29:12 +08:00
wwweww 585417c07a delete chat rpc service 2026-04-25 10:29:46 +08:00
wwweww fbb4d12f63 add go mod cn proxy 2026-04-25 10:29:08 +08:00
wwweww cdc85ce7dd Modify Configuration 2026-04-25 10:28:42 +08:00
wwweww bd07075cfe add wt test 2026-04-25 10:27:53 +08:00
217 changed files with 10703 additions and 17985 deletions
+3
View File
@@ -0,0 +1,3 @@
[submodule "frontend"]
path = frontend
url = http://103.236.53.208:3000/juwan/juwan-frontend.git
+1 -1
View File
@@ -20,7 +20,7 @@ func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
conf.MustLoad(*configFile, &c, conf.UseEnv())
svcCtx := svc.NewServiceContext(c)
handler := chathandler.NewHandler(svcCtx)
+6 -6
View File
@@ -17,8 +17,8 @@ Hybrid:
Wt:
Addr: :8443
Path: /wt/chat
CertFile: /etc/certs/tls.crt
KeyFile: /etc/certs/tls.key
CertFile: "${CHAT_WT_CERT_FILE}"
KeyFile: "${CHAT_WT_KEY_FILE}"
Auth:
Enabled: true
FallbackStrategy: auto
@@ -29,18 +29,18 @@ Hybrid:
WsHeaderName: x-auth-user-id
WtTokenSource: cookie
WtTokenName: JToken
WtJWTSecret: MGUyMWE3ZDhjMTQ5ZDg1MWViOWU0MGM3OTE2NWVkYTBlOTE5ZWRkZDU1YjYzOGJjOWRiNzM0NTc4NDIyMjlkZQ
WtJWTSecret: "${JWT_SECRET_KEY}"
Stateless:
PollInterval: 100ms
BatchSize: 100
Mongo:
URI: mongodb://mongo:27017
Database: juwan_chat
URI: "${MONGO_URI}"
Database: "${MONGO_DATABASE}"
Redis:
Addr: redis:6379
Addr: "${REDIS_HOST}:${REDIS_PORT}"
Log:
Level: info
-114
View File
@@ -1,114 +0,0 @@
package chatservice
import (
"context"
"juwan-backend/app/chat/rpc/pb"
"github.com/zeromicro/go-zero/zrpc"
"google.golang.org/grpc"
)
type (
ChatSessions = pb.ChatSessions
AddChatSessionsReq = pb.AddChatSessionsReq
AddChatSessionsResp = pb.AddChatSessionsResp
UpdateChatSessionsReq = pb.UpdateChatSessionsReq
UpdateChatSessionsResp = pb.UpdateChatSessionsResp
DelChatSessionsReq = pb.DelChatSessionsReq
DelChatSessionsResp = pb.DelChatSessionsResp
GetChatSessionsByIdReq = pb.GetChatSessionsByIdReq
GetChatSessionsByIdResp = pb.GetChatSessionsByIdResp
SearchChatSessionsReq = pb.SearchChatSessionsReq
SearchChatSessionsResp = pb.SearchChatSessionsResp
AddParticipantReq = pb.AddParticipantReq
AddParticipantResp = pb.AddParticipantResp
RemoveParticipantReq = pb.RemoveParticipantReq
RemoveParticipantResp = pb.RemoveParticipantResp
ChatMessages = pb.ChatMessages
AddChatMessagesReq = pb.AddChatMessagesReq
AddChatMessagesResp = pb.AddChatMessagesResp
DelChatMessagesReq = pb.DelChatMessagesReq
DelChatMessagesResp = pb.DelChatMessagesResp
GetChatMessagesByIdReq = pb.GetChatMessagesByIdReq
GetChatMessagesByIdResp = pb.GetChatMessagesByIdResp
SearchChatMessagesReq = pb.SearchChatMessagesReq
SearchChatMessagesResp = pb.SearchChatMessagesResp
ChatService interface {
AddChatSessions(ctx context.Context, in *AddChatSessionsReq, opts ...grpc.CallOption) (*AddChatSessionsResp, error)
UpdateChatSessions(ctx context.Context, in *UpdateChatSessionsReq, opts ...grpc.CallOption) (*UpdateChatSessionsResp, error)
DelChatSessions(ctx context.Context, in *DelChatSessionsReq, opts ...grpc.CallOption) (*DelChatSessionsResp, error)
GetChatSessionsById(ctx context.Context, in *GetChatSessionsByIdReq, opts ...grpc.CallOption) (*GetChatSessionsByIdResp, error)
SearchChatSessions(ctx context.Context, in *SearchChatSessionsReq, opts ...grpc.CallOption) (*SearchChatSessionsResp, error)
AddParticipant(ctx context.Context, in *AddParticipantReq, opts ...grpc.CallOption) (*AddParticipantResp, error)
RemoveParticipant(ctx context.Context, in *RemoveParticipantReq, opts ...grpc.CallOption) (*RemoveParticipantResp, error)
AddChatMessages(ctx context.Context, in *AddChatMessagesReq, opts ...grpc.CallOption) (*AddChatMessagesResp, error)
DelChatMessages(ctx context.Context, in *DelChatMessagesReq, opts ...grpc.CallOption) (*DelChatMessagesResp, error)
GetChatMessagesById(ctx context.Context, in *GetChatMessagesByIdReq, opts ...grpc.CallOption) (*GetChatMessagesByIdResp, error)
SearchChatMessages(ctx context.Context, in *SearchChatMessagesReq, opts ...grpc.CallOption) (*SearchChatMessagesResp, error)
}
defaultChatService struct {
cli zrpc.Client
}
)
func NewChatService(cli zrpc.Client) ChatService {
return &defaultChatService{cli: cli}
}
func (m *defaultChatService) AddChatSessions(ctx context.Context, in *AddChatSessionsReq, opts ...grpc.CallOption) (*AddChatSessionsResp, error) {
client := pb.NewChatServiceClient(m.cli.Conn())
return client.AddChatSessions(ctx, in, opts...)
}
func (m *defaultChatService) UpdateChatSessions(ctx context.Context, in *UpdateChatSessionsReq, opts ...grpc.CallOption) (*UpdateChatSessionsResp, error) {
client := pb.NewChatServiceClient(m.cli.Conn())
return client.UpdateChatSessions(ctx, in, opts...)
}
func (m *defaultChatService) DelChatSessions(ctx context.Context, in *DelChatSessionsReq, opts ...grpc.CallOption) (*DelChatSessionsResp, error) {
client := pb.NewChatServiceClient(m.cli.Conn())
return client.DelChatSessions(ctx, in, opts...)
}
func (m *defaultChatService) GetChatSessionsById(ctx context.Context, in *GetChatSessionsByIdReq, opts ...grpc.CallOption) (*GetChatSessionsByIdResp, error) {
client := pb.NewChatServiceClient(m.cli.Conn())
return client.GetChatSessionsById(ctx, in, opts...)
}
func (m *defaultChatService) SearchChatSessions(ctx context.Context, in *SearchChatSessionsReq, opts ...grpc.CallOption) (*SearchChatSessionsResp, error) {
client := pb.NewChatServiceClient(m.cli.Conn())
return client.SearchChatSessions(ctx, in, opts...)
}
func (m *defaultChatService) AddParticipant(ctx context.Context, in *AddParticipantReq, opts ...grpc.CallOption) (*AddParticipantResp, error) {
client := pb.NewChatServiceClient(m.cli.Conn())
return client.AddParticipant(ctx, in, opts...)
}
func (m *defaultChatService) RemoveParticipant(ctx context.Context, in *RemoveParticipantReq, opts ...grpc.CallOption) (*RemoveParticipantResp, error) {
client := pb.NewChatServiceClient(m.cli.Conn())
return client.RemoveParticipant(ctx, in, opts...)
}
func (m *defaultChatService) AddChatMessages(ctx context.Context, in *AddChatMessagesReq, opts ...grpc.CallOption) (*AddChatMessagesResp, error) {
client := pb.NewChatServiceClient(m.cli.Conn())
return client.AddChatMessages(ctx, in, opts...)
}
func (m *defaultChatService) DelChatMessages(ctx context.Context, in *DelChatMessagesReq, opts ...grpc.CallOption) (*DelChatMessagesResp, error) {
client := pb.NewChatServiceClient(m.cli.Conn())
return client.DelChatMessages(ctx, in, opts...)
}
func (m *defaultChatService) GetChatMessagesById(ctx context.Context, in *GetChatMessagesByIdReq, opts ...grpc.CallOption) (*GetChatMessagesByIdResp, error) {
client := pb.NewChatServiceClient(m.cli.Conn())
return client.GetChatMessagesById(ctx, in, opts...)
}
func (m *defaultChatService) SearchChatMessages(ctx context.Context, in *SearchChatMessagesReq, opts ...grpc.CallOption) (*SearchChatMessagesResp, error) {
client := pb.NewChatServiceClient(m.cli.Conn())
return client.SearchChatMessages(ctx, in, opts...)
}
-5
View File
@@ -1,5 +0,0 @@
Name: pb.rpc
ListenOn: 0.0.0.0:8080
Log:
Level: debug
-7
View File
@@ -1,7 +0,0 @@
package config
import "github.com/zeromicro/go-zero/zrpc"
type Config struct {
zrpc.RpcServerConf
}
@@ -1,69 +0,0 @@
package logic
import (
"context"
"errors"
"juwan-backend/app/chat/rpc/internal/svc"
"juwan-backend/app/chat/rpc/pb"
"github.com/zeromicro/go-zero/core/logx"
)
type AddChatMessagesLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewAddChatMessagesLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AddChatMessagesLogic {
return &AddChatMessagesLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logx.WithContext(ctx),
}
}
func (l *AddChatMessagesLogic) AddChatMessages(in *pb.AddChatMessagesReq) (*pb.AddChatMessagesResp, error) {
if in.GetSessionId() <= 0 {
return nil, errors.New("sessionId is required")
}
if in.GetSenderId() <= 0 {
return nil, errors.New("senderId is required")
}
if in.GetContent() == "" {
return nil, errors.New("content is required")
}
store := l.svcCtx.Store
store.Mu.Lock()
defer store.Mu.Unlock()
if _, ok := store.Sessions[in.GetSessionId()]; !ok {
return nil, errors.New("session not found")
}
now := nowUnix(0)
msgType := in.GetType()
if msgType == "" {
msgType = "text"
}
msg := &pb.ChatMessages{
Id: store.NextMessage(),
SessionId: in.GetSessionId(),
SenderId: in.GetSenderId(),
Type: msgType,
Content: in.GetContent(),
CreatedAt: now,
}
store.Messages[msg.Id] = msg
store.SessionMessages[in.GetSessionId()] = append(store.SessionMessages[in.GetSessionId()], msg.Id)
session := store.Sessions[in.GetSessionId()]
session.LastMessage = in.GetContent()
session.LastMessageAt = now
session.UpdatedAt = now
return &pb.AddChatMessagesResp{Id: msg.Id}, nil
}
@@ -1,64 +0,0 @@
package logic
import (
"context"
"errors"
"juwan-backend/app/chat/rpc/internal/svc"
"juwan-backend/app/chat/rpc/pb"
"github.com/zeromicro/go-zero/core/logx"
)
type AddChatSessionsLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewAddChatSessionsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AddChatSessionsLogic {
return &AddChatSessionsLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logx.WithContext(ctx),
}
}
func (l *AddChatSessionsLogic) AddChatSessions(in *pb.AddChatSessionsReq) (*pb.AddChatSessionsResp, error) {
if in.GetType() == "" {
return nil, errors.New("type is required")
}
if in.GetCreatorId() <= 0 {
return nil, errors.New("creatorId is required")
}
store := l.svcCtx.Store
store.Mu.Lock()
defer store.Mu.Unlock()
now := nowUnix(0)
participants := append([]int64(nil), in.GetParticipants()...)
hasCreator := false
for _, p := range participants {
if p == in.GetCreatorId() {
hasCreator = true
break
}
}
if !hasCreator {
participants = append(participants, in.GetCreatorId())
}
session := &pb.ChatSessions{
Id: store.NextSession(),
Type: in.GetType(),
Name: in.GetName(),
CreatorId: in.GetCreatorId(),
Participants: participants,
CreatedAt: now,
UpdatedAt: now,
}
store.Sessions[session.Id] = session
return &pb.AddChatSessionsResp{Id: session.Id}, nil
}
@@ -1,46 +0,0 @@
package logic
import (
"context"
"errors"
"juwan-backend/app/chat/rpc/internal/svc"
"juwan-backend/app/chat/rpc/pb"
"github.com/zeromicro/go-zero/core/logx"
)
type AddParticipantLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewAddParticipantLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AddParticipantLogic {
return &AddParticipantLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logx.WithContext(ctx),
}
}
func (l *AddParticipantLogic) AddParticipant(in *pb.AddParticipantReq) (*pb.AddParticipantResp, error) {
store := l.svcCtx.Store
store.Mu.Lock()
defer store.Mu.Unlock()
session, ok := store.Sessions[in.GetSessionId()]
if !ok {
return nil, errors.New("session not found")
}
for _, p := range session.Participants {
if p == in.GetUserId() {
return &pb.AddParticipantResp{}, nil
}
}
session.Participants = append(session.Participants, in.GetUserId())
session.UpdatedAt = nowUnix(0)
return &pb.AddParticipantResp{}, nil
}
@@ -1,45 +0,0 @@
package logic
import (
"context"
"juwan-backend/app/chat/rpc/internal/svc"
"juwan-backend/app/chat/rpc/pb"
"github.com/zeromicro/go-zero/core/logx"
)
type DelChatMessagesLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewDelChatMessagesLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DelChatMessagesLogic {
return &DelChatMessagesLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logx.WithContext(ctx),
}
}
func (l *DelChatMessagesLogic) DelChatMessages(in *pb.DelChatMessagesReq) (*pb.DelChatMessagesResp, error) {
store := l.svcCtx.Store
store.Mu.Lock()
defer store.Mu.Unlock()
msg, ok := store.Messages[in.GetId()]
if ok {
ids := store.SessionMessages[msg.SessionId]
filtered := make([]int64, 0, len(ids))
for _, id := range ids {
if id != in.GetId() {
filtered = append(filtered, id)
}
}
store.SessionMessages[msg.SessionId] = filtered
}
delete(store.Messages, in.GetId())
return &pb.DelChatMessagesResp{}, nil
}
@@ -1,35 +0,0 @@
package logic
import (
"context"
"juwan-backend/app/chat/rpc/internal/svc"
"juwan-backend/app/chat/rpc/pb"
"github.com/zeromicro/go-zero/core/logx"
)
type DelChatSessionsLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewDelChatSessionsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DelChatSessionsLogic {
return &DelChatSessionsLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logx.WithContext(ctx),
}
}
func (l *DelChatSessionsLogic) DelChatSessions(in *pb.DelChatSessionsReq) (*pb.DelChatSessionsResp, error) {
store := l.svcCtx.Store
store.Mu.Lock()
defer store.Mu.Unlock()
delete(store.Sessions, in.GetId())
delete(store.SessionMessages, in.GetId())
return &pb.DelChatSessionsResp{}, nil
}
@@ -1,38 +0,0 @@
package logic
import (
"context"
"errors"
"juwan-backend/app/chat/rpc/internal/svc"
"juwan-backend/app/chat/rpc/pb"
"github.com/zeromicro/go-zero/core/logx"
)
type GetChatMessagesByIdLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewGetChatMessagesByIdLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetChatMessagesByIdLogic {
return &GetChatMessagesByIdLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logx.WithContext(ctx),
}
}
func (l *GetChatMessagesByIdLogic) GetChatMessagesById(in *pb.GetChatMessagesByIdReq) (*pb.GetChatMessagesByIdResp, error) {
store := l.svcCtx.Store
store.Mu.RLock()
defer store.Mu.RUnlock()
msg, ok := store.Messages[in.GetId()]
if !ok {
return nil, errors.New("message not found")
}
return &pb.GetChatMessagesByIdResp{ChatMessages: msg}, nil
}
@@ -1,38 +0,0 @@
package logic
import (
"context"
"errors"
"juwan-backend/app/chat/rpc/internal/svc"
"juwan-backend/app/chat/rpc/pb"
"github.com/zeromicro/go-zero/core/logx"
)
type GetChatSessionsByIdLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewGetChatSessionsByIdLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetChatSessionsByIdLogic {
return &GetChatSessionsByIdLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logx.WithContext(ctx),
}
}
func (l *GetChatSessionsByIdLogic) GetChatSessionsById(in *pb.GetChatSessionsByIdReq) (*pb.GetChatSessionsByIdResp, error) {
store := l.svcCtx.Store
store.Mu.RLock()
defer store.Mu.RUnlock()
session, ok := store.Sessions[in.GetId()]
if !ok {
return nil, errors.New("session not found")
}
return &pb.GetChatSessionsByIdResp{ChatSessions: session}, nil
}
-10
View File
@@ -1,10 +0,0 @@
package logic
import "time"
func nowUnix(ts int64) int64 {
if ts > 0 {
return ts
}
return time.Now().Unix()
}
@@ -1,47 +0,0 @@
package logic
import (
"context"
"errors"
"juwan-backend/app/chat/rpc/internal/svc"
"juwan-backend/app/chat/rpc/pb"
"github.com/zeromicro/go-zero/core/logx"
)
type RemoveParticipantLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewRemoveParticipantLogic(ctx context.Context, svcCtx *svc.ServiceContext) *RemoveParticipantLogic {
return &RemoveParticipantLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logx.WithContext(ctx),
}
}
func (l *RemoveParticipantLogic) RemoveParticipant(in *pb.RemoveParticipantReq) (*pb.RemoveParticipantResp, error) {
store := l.svcCtx.Store
store.Mu.Lock()
defer store.Mu.Unlock()
session, ok := store.Sessions[in.GetSessionId()]
if !ok {
return nil, errors.New("session not found")
}
filtered := make([]int64, 0, len(session.Participants))
for _, p := range session.Participants {
if p != in.GetUserId() {
filtered = append(filtered, p)
}
}
session.Participants = filtered
session.UpdatedAt = nowUnix(0)
return &pb.RemoveParticipantResp{}, nil
}
@@ -1,58 +0,0 @@
package logic
import (
"context"
"juwan-backend/app/chat/rpc/internal/svc"
"juwan-backend/app/chat/rpc/pb"
"github.com/zeromicro/go-zero/core/logx"
)
type SearchChatMessagesLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewSearchChatMessagesLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SearchChatMessagesLogic {
return &SearchChatMessagesLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logx.WithContext(ctx),
}
}
func (l *SearchChatMessagesLogic) SearchChatMessages(in *pb.SearchChatMessagesReq) (*pb.SearchChatMessagesResp, error) {
store := l.svcCtx.Store
store.Mu.RLock()
defer store.Mu.RUnlock()
msgIDs := store.SessionMessages[in.GetSessionId()]
var results []*pb.ChatMessages
for _, id := range msgIDs {
msg, ok := store.Messages[id]
if !ok {
continue
}
if in.SenderId != nil && msg.SenderId != *in.SenderId {
continue
}
results = append(results, msg)
}
limit := in.GetLimit()
if limit <= 0 {
limit = 20
}
offset := in.GetPage() * limit
if offset >= int64(len(results)) {
return &pb.SearchChatMessagesResp{}, nil
}
end := offset + limit
if end > int64(len(results)) {
end = int64(len(results))
}
return &pb.SearchChatMessagesResp{ChatMessages: results[offset:end]}, nil
}
@@ -1,65 +0,0 @@
package logic
import (
"context"
"juwan-backend/app/chat/rpc/internal/svc"
"juwan-backend/app/chat/rpc/pb"
"github.com/zeromicro/go-zero/core/logx"
)
type SearchChatSessionsLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewSearchChatSessionsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SearchChatSessionsLogic {
return &SearchChatSessionsLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logx.WithContext(ctx),
}
}
func (l *SearchChatSessionsLogic) SearchChatSessions(in *pb.SearchChatSessionsReq) (*pb.SearchChatSessionsResp, error) {
store := l.svcCtx.Store
store.Mu.RLock()
defer store.Mu.RUnlock()
var results []*pb.ChatSessions
for _, s := range store.Sessions {
if in.Type != nil && s.Type != *in.Type {
continue
}
if in.UserId != nil {
found := false
for _, p := range s.Participants {
if p == *in.UserId {
found = true
break
}
}
if !found {
continue
}
}
results = append(results, s)
}
limit := in.GetLimit()
if limit <= 0 {
limit = 20
}
offset := in.GetPage() * limit
if offset >= int64(len(results)) {
return &pb.SearchChatSessionsResp{}, nil
}
end := offset + limit
if end > int64(len(results)) {
end = int64(len(results))
}
return &pb.SearchChatSessionsResp{ChatSessions: results[offset:end]}, nil
}
@@ -1,49 +0,0 @@
package logic
import (
"context"
"errors"
"juwan-backend/app/chat/rpc/internal/svc"
"juwan-backend/app/chat/rpc/pb"
"github.com/zeromicro/go-zero/core/logx"
)
type UpdateChatSessionsLogic struct {
ctx context.Context
svcCtx *svc.ServiceContext
logx.Logger
}
func NewUpdateChatSessionsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateChatSessionsLogic {
return &UpdateChatSessionsLogic{
ctx: ctx,
svcCtx: svcCtx,
Logger: logx.WithContext(ctx),
}
}
func (l *UpdateChatSessionsLogic) UpdateChatSessions(in *pb.UpdateChatSessionsReq) (*pb.UpdateChatSessionsResp, error) {
store := l.svcCtx.Store
store.Mu.Lock()
defer store.Mu.Unlock()
session, ok := store.Sessions[in.GetId()]
if !ok {
return nil, errors.New("session not found")
}
if in.Name != nil {
session.Name = *in.Name
}
if in.LastMessage != nil {
session.LastMessage = *in.LastMessage
}
if in.LastMessageAt != nil {
session.LastMessageAt = *in.LastMessageAt
}
session.UpdatedAt = nowUnix(0)
return &pb.UpdateChatSessionsResp{}, nil
}
@@ -1,75 +0,0 @@
package server
import (
"context"
"juwan-backend/app/chat/rpc/internal/logic"
"juwan-backend/app/chat/rpc/internal/svc"
"juwan-backend/app/chat/rpc/pb"
)
type ChatServiceServer struct {
svcCtx *svc.ServiceContext
pb.UnimplementedChatServiceServer
}
func NewChatServiceServer(svcCtx *svc.ServiceContext) *ChatServiceServer {
return &ChatServiceServer{
svcCtx: svcCtx,
}
}
func (s *ChatServiceServer) AddChatSessions(ctx context.Context, in *pb.AddChatSessionsReq) (*pb.AddChatSessionsResp, error) {
l := logic.NewAddChatSessionsLogic(ctx, s.svcCtx)
return l.AddChatSessions(in)
}
func (s *ChatServiceServer) UpdateChatSessions(ctx context.Context, in *pb.UpdateChatSessionsReq) (*pb.UpdateChatSessionsResp, error) {
l := logic.NewUpdateChatSessionsLogic(ctx, s.svcCtx)
return l.UpdateChatSessions(in)
}
func (s *ChatServiceServer) DelChatSessions(ctx context.Context, in *pb.DelChatSessionsReq) (*pb.DelChatSessionsResp, error) {
l := logic.NewDelChatSessionsLogic(ctx, s.svcCtx)
return l.DelChatSessions(in)
}
func (s *ChatServiceServer) GetChatSessionsById(ctx context.Context, in *pb.GetChatSessionsByIdReq) (*pb.GetChatSessionsByIdResp, error) {
l := logic.NewGetChatSessionsByIdLogic(ctx, s.svcCtx)
return l.GetChatSessionsById(in)
}
func (s *ChatServiceServer) SearchChatSessions(ctx context.Context, in *pb.SearchChatSessionsReq) (*pb.SearchChatSessionsResp, error) {
l := logic.NewSearchChatSessionsLogic(ctx, s.svcCtx)
return l.SearchChatSessions(in)
}
func (s *ChatServiceServer) AddParticipant(ctx context.Context, in *pb.AddParticipantReq) (*pb.AddParticipantResp, error) {
l := logic.NewAddParticipantLogic(ctx, s.svcCtx)
return l.AddParticipant(in)
}
func (s *ChatServiceServer) RemoveParticipant(ctx context.Context, in *pb.RemoveParticipantReq) (*pb.RemoveParticipantResp, error) {
l := logic.NewRemoveParticipantLogic(ctx, s.svcCtx)
return l.RemoveParticipant(in)
}
func (s *ChatServiceServer) AddChatMessages(ctx context.Context, in *pb.AddChatMessagesReq) (*pb.AddChatMessagesResp, error) {
l := logic.NewAddChatMessagesLogic(ctx, s.svcCtx)
return l.AddChatMessages(in)
}
func (s *ChatServiceServer) DelChatMessages(ctx context.Context, in *pb.DelChatMessagesReq) (*pb.DelChatMessagesResp, error) {
l := logic.NewDelChatMessagesLogic(ctx, s.svcCtx)
return l.DelChatMessages(in)
}
func (s *ChatServiceServer) GetChatMessagesById(ctx context.Context, in *pb.GetChatMessagesByIdReq) (*pb.GetChatMessagesByIdResp, error) {
l := logic.NewGetChatMessagesByIdLogic(ctx, s.svcCtx)
return l.GetChatMessagesById(in)
}
func (s *ChatServiceServer) SearchChatMessages(ctx context.Context, in *pb.SearchChatMessagesReq) (*pb.SearchChatMessagesResp, error) {
l := logic.NewSearchChatMessagesLogic(ctx, s.svcCtx)
return l.SearchChatMessages(in)
}
@@ -1,15 +0,0 @@
package svc
import "juwan-backend/app/chat/rpc/internal/config"
type ServiceContext struct {
Config config.Config
Store *ChatStore
}
func NewServiceContext(c config.Config) *ServiceContext {
return &ServiceContext{
Config: c,
Store: NewChatStore(),
}
}
-38
View File
@@ -1,38 +0,0 @@
package svc
import (
"sync"
"juwan-backend/app/chat/rpc/pb"
)
type ChatStore struct {
Mu sync.RWMutex
nextSessionID int64
nextMessageID int64
Sessions map[int64]*pb.ChatSessions
Messages map[int64]*pb.ChatMessages
SessionMessages map[int64][]int64
}
func NewChatStore() *ChatStore {
return &ChatStore{
nextSessionID: 1000,
nextMessageID: 1000,
Sessions: make(map[int64]*pb.ChatSessions),
Messages: make(map[int64]*pb.ChatMessages),
SessionMessages: make(map[int64][]int64),
}
}
func (s *ChatStore) NextSession() int64 {
s.nextSessionID++
return s.nextSessionID
}
func (s *ChatStore) NextMessage() int64 {
s.nextMessageID++
return s.nextMessageID
}
-39
View File
@@ -1,39 +0,0 @@
package main
import (
"flag"
"fmt"
"juwan-backend/app/chat/rpc/internal/config"
"juwan-backend/app/chat/rpc/internal/server"
"juwan-backend/app/chat/rpc/internal/svc"
"juwan-backend/app/chat/rpc/pb"
"github.com/zeromicro/go-zero/core/conf"
"github.com/zeromicro/go-zero/core/service"
"github.com/zeromicro/go-zero/zrpc"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
)
var configFile = flag.String("f", "etc/pb.yaml", "the config file")
func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
ctx := svc.NewServiceContext(c)
s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) {
pb.RegisterChatServiceServer(grpcServer, server.NewChatServiceServer(ctx))
if c.Mode == service.DevMode || c.Mode == service.TestMode {
reflection.Register(grpcServer)
}
})
defer s.Stop()
fmt.Printf("Starting rpc server at %s...\n", c.ListenOn)
s.Start()
}
-175
View File
@@ -1,175 +0,0 @@
package pb
import (
"context"
"google.golang.org/grpc"
)
type ChatServiceServer interface {
AddChatSessions(context.Context, *AddChatSessionsReq) (*AddChatSessionsResp, error)
UpdateChatSessions(context.Context, *UpdateChatSessionsReq) (*UpdateChatSessionsResp, error)
DelChatSessions(context.Context, *DelChatSessionsReq) (*DelChatSessionsResp, error)
GetChatSessionsById(context.Context, *GetChatSessionsByIdReq) (*GetChatSessionsByIdResp, error)
SearchChatSessions(context.Context, *SearchChatSessionsReq) (*SearchChatSessionsResp, error)
AddParticipant(context.Context, *AddParticipantReq) (*AddParticipantResp, error)
RemoveParticipant(context.Context, *RemoveParticipantReq) (*RemoveParticipantResp, error)
AddChatMessages(context.Context, *AddChatMessagesReq) (*AddChatMessagesResp, error)
DelChatMessages(context.Context, *DelChatMessagesReq) (*DelChatMessagesResp, error)
GetChatMessagesById(context.Context, *GetChatMessagesByIdReq) (*GetChatMessagesByIdResp, error)
SearchChatMessages(context.Context, *SearchChatMessagesReq) (*SearchChatMessagesResp, error)
mustEmbedUnimplementedChatServiceServer()
}
type UnimplementedChatServiceServer struct{}
func (UnimplementedChatServiceServer) AddChatSessions(context.Context, *AddChatSessionsReq) (*AddChatSessionsResp, error) {
return nil, grpc.Errorf(12, "method AddChatSessions not implemented")
}
func (UnimplementedChatServiceServer) UpdateChatSessions(context.Context, *UpdateChatSessionsReq) (*UpdateChatSessionsResp, error) {
return nil, grpc.Errorf(12, "method UpdateChatSessions not implemented")
}
func (UnimplementedChatServiceServer) DelChatSessions(context.Context, *DelChatSessionsReq) (*DelChatSessionsResp, error) {
return nil, grpc.Errorf(12, "method DelChatSessions not implemented")
}
func (UnimplementedChatServiceServer) GetChatSessionsById(context.Context, *GetChatSessionsByIdReq) (*GetChatSessionsByIdResp, error) {
return nil, grpc.Errorf(12, "method GetChatSessionsById not implemented")
}
func (UnimplementedChatServiceServer) SearchChatSessions(context.Context, *SearchChatSessionsReq) (*SearchChatSessionsResp, error) {
return nil, grpc.Errorf(12, "method SearchChatSessions not implemented")
}
func (UnimplementedChatServiceServer) AddParticipant(context.Context, *AddParticipantReq) (*AddParticipantResp, error) {
return nil, grpc.Errorf(12, "method AddParticipant not implemented")
}
func (UnimplementedChatServiceServer) RemoveParticipant(context.Context, *RemoveParticipantReq) (*RemoveParticipantResp, error) {
return nil, grpc.Errorf(12, "method RemoveParticipant not implemented")
}
func (UnimplementedChatServiceServer) AddChatMessages(context.Context, *AddChatMessagesReq) (*AddChatMessagesResp, error) {
return nil, grpc.Errorf(12, "method AddChatMessages not implemented")
}
func (UnimplementedChatServiceServer) DelChatMessages(context.Context, *DelChatMessagesReq) (*DelChatMessagesResp, error) {
return nil, grpc.Errorf(12, "method DelChatMessages not implemented")
}
func (UnimplementedChatServiceServer) GetChatMessagesById(context.Context, *GetChatMessagesByIdReq) (*GetChatMessagesByIdResp, error) {
return nil, grpc.Errorf(12, "method GetChatMessagesById not implemented")
}
func (UnimplementedChatServiceServer) SearchChatMessages(context.Context, *SearchChatMessagesReq) (*SearchChatMessagesResp, error) {
return nil, grpc.Errorf(12, "method SearchChatMessages not implemented")
}
func (UnimplementedChatServiceServer) mustEmbedUnimplementedChatServiceServer() {}
type UnsafeChatServiceServer interface {
mustEmbedUnimplementedChatServiceServer()
}
type ChatServiceClient interface {
AddChatSessions(ctx context.Context, in *AddChatSessionsReq, opts ...grpc.CallOption) (*AddChatSessionsResp, error)
UpdateChatSessions(ctx context.Context, in *UpdateChatSessionsReq, opts ...grpc.CallOption) (*UpdateChatSessionsResp, error)
DelChatSessions(ctx context.Context, in *DelChatSessionsReq, opts ...grpc.CallOption) (*DelChatSessionsResp, error)
GetChatSessionsById(ctx context.Context, in *GetChatSessionsByIdReq, opts ...grpc.CallOption) (*GetChatSessionsByIdResp, error)
SearchChatSessions(ctx context.Context, in *SearchChatSessionsReq, opts ...grpc.CallOption) (*SearchChatSessionsResp, error)
AddParticipant(ctx context.Context, in *AddParticipantReq, opts ...grpc.CallOption) (*AddParticipantResp, error)
RemoveParticipant(ctx context.Context, in *RemoveParticipantReq, opts ...grpc.CallOption) (*RemoveParticipantResp, error)
AddChatMessages(ctx context.Context, in *AddChatMessagesReq, opts ...grpc.CallOption) (*AddChatMessagesResp, error)
DelChatMessages(ctx context.Context, in *DelChatMessagesReq, opts ...grpc.CallOption) (*DelChatMessagesResp, error)
GetChatMessagesById(ctx context.Context, in *GetChatMessagesByIdReq, opts ...grpc.CallOption) (*GetChatMessagesByIdResp, error)
SearchChatMessages(ctx context.Context, in *SearchChatMessagesReq, opts ...grpc.CallOption) (*SearchChatMessagesResp, error)
}
type chatServiceClient struct {
cc grpc.ClientConnInterface
}
func NewChatServiceClient(cc grpc.ClientConnInterface) ChatServiceClient {
return &chatServiceClient{cc}
}
func (c *chatServiceClient) AddChatSessions(ctx context.Context, in *AddChatSessionsReq, opts ...grpc.CallOption) (*AddChatSessionsResp, error) {
out := new(AddChatSessionsResp)
err := c.cc.Invoke(ctx, "/pb.chatService/AddChatSessions", in, out, opts...)
return out, err
}
func (c *chatServiceClient) UpdateChatSessions(ctx context.Context, in *UpdateChatSessionsReq, opts ...grpc.CallOption) (*UpdateChatSessionsResp, error) {
out := new(UpdateChatSessionsResp)
err := c.cc.Invoke(ctx, "/pb.chatService/UpdateChatSessions", in, out, opts...)
return out, err
}
func (c *chatServiceClient) DelChatSessions(ctx context.Context, in *DelChatSessionsReq, opts ...grpc.CallOption) (*DelChatSessionsResp, error) {
out := new(DelChatSessionsResp)
err := c.cc.Invoke(ctx, "/pb.chatService/DelChatSessions", in, out, opts...)
return out, err
}
func (c *chatServiceClient) GetChatSessionsById(ctx context.Context, in *GetChatSessionsByIdReq, opts ...grpc.CallOption) (*GetChatSessionsByIdResp, error) {
out := new(GetChatSessionsByIdResp)
err := c.cc.Invoke(ctx, "/pb.chatService/GetChatSessionsById", in, out, opts...)
return out, err
}
func (c *chatServiceClient) SearchChatSessions(ctx context.Context, in *SearchChatSessionsReq, opts ...grpc.CallOption) (*SearchChatSessionsResp, error) {
out := new(SearchChatSessionsResp)
err := c.cc.Invoke(ctx, "/pb.chatService/SearchChatSessions", in, out, opts...)
return out, err
}
func (c *chatServiceClient) AddParticipant(ctx context.Context, in *AddParticipantReq, opts ...grpc.CallOption) (*AddParticipantResp, error) {
out := new(AddParticipantResp)
err := c.cc.Invoke(ctx, "/pb.chatService/AddParticipant", in, out, opts...)
return out, err
}
func (c *chatServiceClient) RemoveParticipant(ctx context.Context, in *RemoveParticipantReq, opts ...grpc.CallOption) (*RemoveParticipantResp, error) {
out := new(RemoveParticipantResp)
err := c.cc.Invoke(ctx, "/pb.chatService/RemoveParticipant", in, out, opts...)
return out, err
}
func (c *chatServiceClient) AddChatMessages(ctx context.Context, in *AddChatMessagesReq, opts ...grpc.CallOption) (*AddChatMessagesResp, error) {
out := new(AddChatMessagesResp)
err := c.cc.Invoke(ctx, "/pb.chatService/AddChatMessages", in, out, opts...)
return out, err
}
func (c *chatServiceClient) DelChatMessages(ctx context.Context, in *DelChatMessagesReq, opts ...grpc.CallOption) (*DelChatMessagesResp, error) {
out := new(DelChatMessagesResp)
err := c.cc.Invoke(ctx, "/pb.chatService/DelChatMessages", in, out, opts...)
return out, err
}
func (c *chatServiceClient) GetChatMessagesById(ctx context.Context, in *GetChatMessagesByIdReq, opts ...grpc.CallOption) (*GetChatMessagesByIdResp, error) {
out := new(GetChatMessagesByIdResp)
err := c.cc.Invoke(ctx, "/pb.chatService/GetChatMessagesById", in, out, opts...)
return out, err
}
func (c *chatServiceClient) SearchChatMessages(ctx context.Context, in *SearchChatMessagesReq, opts ...grpc.CallOption) (*SearchChatMessagesResp, error) {
out := new(SearchChatMessagesResp)
err := c.cc.Invoke(ctx, "/pb.chatService/SearchChatMessages", in, out, opts...)
return out, err
}
var ChatService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "pb.chatService",
HandlerType: (*ChatServiceServer)(nil),
Methods: []grpc.MethodDesc{
{MethodName: "AddChatSessions", Handler: _ChatService_AddChatSessions_Handler},
{MethodName: "UpdateChatSessions", Handler: _ChatService_UpdateChatSessions_Handler},
{MethodName: "DelChatSessions", Handler: _ChatService_DelChatSessions_Handler},
{MethodName: "GetChatSessionsById", Handler: _ChatService_GetChatSessionsById_Handler},
{MethodName: "SearchChatSessions", Handler: _ChatService_SearchChatSessions_Handler},
{MethodName: "AddParticipant", Handler: _ChatService_AddParticipant_Handler},
{MethodName: "RemoveParticipant", Handler: _ChatService_RemoveParticipant_Handler},
{MethodName: "AddChatMessages", Handler: _ChatService_AddChatMessages_Handler},
{MethodName: "DelChatMessages", Handler: _ChatService_DelChatMessages_Handler},
{MethodName: "GetChatMessagesById", Handler: _ChatService_GetChatMessagesById_Handler},
{MethodName: "SearchChatMessages", Handler: _ChatService_SearchChatMessages_Handler},
},
Streams: []grpc.StreamDesc{},
Metadata: "chat.proto",
}
func RegisterChatServiceServer(s grpc.ServiceRegistrar, srv ChatServiceServer) {
s.RegisterService(&ChatService_ServiceDesc, srv)
}
-161
View File
@@ -1,161 +0,0 @@
package pb
import (
"context"
"google.golang.org/grpc"
)
func _ChatService_AddChatSessions_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AddChatSessionsReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ChatServiceServer).AddChatSessions(ctx, in)
}
info := &grpc.UnaryServerInfo{Server: srv, FullMethod: "/pb.chatService/AddChatSessions"}
return interceptor(ctx, in, info, func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ChatServiceServer).AddChatSessions(ctx, req.(*AddChatSessionsReq))
})
}
func _ChatService_UpdateChatSessions_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(UpdateChatSessionsReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ChatServiceServer).UpdateChatSessions(ctx, in)
}
info := &grpc.UnaryServerInfo{Server: srv, FullMethod: "/pb.chatService/UpdateChatSessions"}
return interceptor(ctx, in, info, func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ChatServiceServer).UpdateChatSessions(ctx, req.(*UpdateChatSessionsReq))
})
}
func _ChatService_DelChatSessions_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DelChatSessionsReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ChatServiceServer).DelChatSessions(ctx, in)
}
info := &grpc.UnaryServerInfo{Server: srv, FullMethod: "/pb.chatService/DelChatSessions"}
return interceptor(ctx, in, info, func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ChatServiceServer).DelChatSessions(ctx, req.(*DelChatSessionsReq))
})
}
func _ChatService_GetChatSessionsById_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetChatSessionsByIdReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ChatServiceServer).GetChatSessionsById(ctx, in)
}
info := &grpc.UnaryServerInfo{Server: srv, FullMethod: "/pb.chatService/GetChatSessionsById"}
return interceptor(ctx, in, info, func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ChatServiceServer).GetChatSessionsById(ctx, req.(*GetChatSessionsByIdReq))
})
}
func _ChatService_SearchChatSessions_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SearchChatSessionsReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ChatServiceServer).SearchChatSessions(ctx, in)
}
info := &grpc.UnaryServerInfo{Server: srv, FullMethod: "/pb.chatService/SearchChatSessions"}
return interceptor(ctx, in, info, func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ChatServiceServer).SearchChatSessions(ctx, req.(*SearchChatSessionsReq))
})
}
func _ChatService_AddParticipant_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AddParticipantReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ChatServiceServer).AddParticipant(ctx, in)
}
info := &grpc.UnaryServerInfo{Server: srv, FullMethod: "/pb.chatService/AddParticipant"}
return interceptor(ctx, in, info, func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ChatServiceServer).AddParticipant(ctx, req.(*AddParticipantReq))
})
}
func _ChatService_RemoveParticipant_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(RemoveParticipantReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ChatServiceServer).RemoveParticipant(ctx, in)
}
info := &grpc.UnaryServerInfo{Server: srv, FullMethod: "/pb.chatService/RemoveParticipant"}
return interceptor(ctx, in, info, func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ChatServiceServer).RemoveParticipant(ctx, req.(*RemoveParticipantReq))
})
}
func _ChatService_AddChatMessages_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(AddChatMessagesReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ChatServiceServer).AddChatMessages(ctx, in)
}
info := &grpc.UnaryServerInfo{Server: srv, FullMethod: "/pb.chatService/AddChatMessages"}
return interceptor(ctx, in, info, func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ChatServiceServer).AddChatMessages(ctx, req.(*AddChatMessagesReq))
})
}
func _ChatService_DelChatMessages_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(DelChatMessagesReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ChatServiceServer).DelChatMessages(ctx, in)
}
info := &grpc.UnaryServerInfo{Server: srv, FullMethod: "/pb.chatService/DelChatMessages"}
return interceptor(ctx, in, info, func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ChatServiceServer).DelChatMessages(ctx, req.(*DelChatMessagesReq))
})
}
func _ChatService_GetChatMessagesById_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(GetChatMessagesByIdReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ChatServiceServer).GetChatMessagesById(ctx, in)
}
info := &grpc.UnaryServerInfo{Server: srv, FullMethod: "/pb.chatService/GetChatMessagesById"}
return interceptor(ctx, in, info, func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ChatServiceServer).GetChatMessagesById(ctx, req.(*GetChatMessagesByIdReq))
})
}
func _ChatService_SearchChatMessages_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(SearchChatMessagesReq)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ChatServiceServer).SearchChatMessages(ctx, in)
}
info := &grpc.UnaryServerInfo{Server: srv, FullMethod: "/pb.chatService/SearchChatMessages"}
return interceptor(ctx, in, info, func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ChatServiceServer).SearchChatMessages(ctx, req.(*SearchChatMessagesReq))
})
}
-179
View File
@@ -1,179 +0,0 @@
package pb
type ChatMessages struct {
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
SessionId int64 `protobuf:"varint,2,opt,name=sessionId,proto3" json:"sessionId,omitempty"`
SenderId int64 `protobuf:"varint,3,opt,name=senderId,proto3" json:"senderId,omitempty"`
Type string `protobuf:"bytes,4,opt,name=type,proto3" json:"type,omitempty"`
Content string `protobuf:"bytes,5,opt,name=content,proto3" json:"content,omitempty"`
CreatedAt int64 `protobuf:"varint,6,opt,name=createdAt,proto3" json:"createdAt,omitempty"`
}
func (x *ChatMessages) GetId() int64 {
if x != nil {
return x.Id
}
return 0
}
func (x *ChatMessages) GetSessionId() int64 {
if x != nil {
return x.SessionId
}
return 0
}
func (x *ChatMessages) GetSenderId() int64 {
if x != nil {
return x.SenderId
}
return 0
}
func (x *ChatMessages) GetType() string {
if x != nil {
return x.Type
}
return ""
}
func (x *ChatMessages) GetContent() string {
if x != nil {
return x.Content
}
return ""
}
func (x *ChatMessages) GetCreatedAt() int64 {
if x != nil {
return x.CreatedAt
}
return 0
}
type AddChatMessagesReq struct {
SessionId int64 `protobuf:"varint,1,opt,name=sessionId,proto3" json:"sessionId,omitempty"`
SenderId int64 `protobuf:"varint,2,opt,name=senderId,proto3" json:"senderId,omitempty"`
Type string `protobuf:"bytes,3,opt,name=type,proto3" json:"type,omitempty"`
Content string `protobuf:"bytes,4,opt,name=content,proto3" json:"content,omitempty"`
}
func (x *AddChatMessagesReq) GetSessionId() int64 {
if x != nil {
return x.SessionId
}
return 0
}
func (x *AddChatMessagesReq) GetSenderId() int64 {
if x != nil {
return x.SenderId
}
return 0
}
func (x *AddChatMessagesReq) GetType() string {
if x != nil {
return x.Type
}
return ""
}
func (x *AddChatMessagesReq) GetContent() string {
if x != nil {
return x.Content
}
return ""
}
type AddChatMessagesResp struct {
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
}
func (x *AddChatMessagesResp) GetId() int64 {
if x != nil {
return x.Id
}
return 0
}
type DelChatMessagesReq struct {
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
}
func (x *DelChatMessagesReq) GetId() int64 {
if x != nil {
return x.Id
}
return 0
}
type DelChatMessagesResp struct{}
type GetChatMessagesByIdReq struct {
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
}
func (x *GetChatMessagesByIdReq) GetId() int64 {
if x != nil {
return x.Id
}
return 0
}
type GetChatMessagesByIdResp struct {
ChatMessages *ChatMessages `protobuf:"bytes,1,opt,name=chatMessages,proto3" json:"chatMessages,omitempty"`
}
func (x *GetChatMessagesByIdResp) GetChatMessages() *ChatMessages {
if x != nil {
return x.ChatMessages
}
return nil
}
type SearchChatMessagesReq struct {
Page int64 `protobuf:"varint,1,opt,name=page,proto3" json:"page,omitempty"`
Limit int64 `protobuf:"varint,2,opt,name=limit,proto3" json:"limit,omitempty"`
SessionId int64 `protobuf:"varint,3,opt,name=sessionId,proto3" json:"sessionId,omitempty"`
SenderId *int64 `protobuf:"varint,4,opt,name=senderId,proto3,oneof" json:"senderId,omitempty"`
}
func (x *SearchChatMessagesReq) GetPage() int64 {
if x != nil {
return x.Page
}
return 0
}
func (x *SearchChatMessagesReq) GetLimit() int64 {
if x != nil {
return x.Limit
}
return 0
}
func (x *SearchChatMessagesReq) GetSessionId() int64 {
if x != nil {
return x.SessionId
}
return 0
}
func (x *SearchChatMessagesReq) GetSenderId() *int64 {
if x != nil {
return x.SenderId
}
return nil
}
type SearchChatMessagesResp struct {
ChatMessages []*ChatMessages `protobuf:"bytes,1,rep,name=chatMessages,proto3" json:"chatMessages,omitempty"`
}
func (x *SearchChatMessagesResp) GetChatMessages() []*ChatMessages {
if x != nil {
return x.ChatMessages
}
return nil
}
-282
View File
@@ -1,282 +0,0 @@
package pb
type ChatSessions struct {
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"`
Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"`
CreatorId int64 `protobuf:"varint,4,opt,name=creatorId,proto3" json:"creatorId,omitempty"`
Participants []int64 `protobuf:"varint,5,rep,packed,name=participants,proto3" json:"participants,omitempty"`
LastMessage string `protobuf:"bytes,6,opt,name=lastMessage,proto3" json:"lastMessage,omitempty"`
LastMessageAt int64 `protobuf:"varint,7,opt,name=lastMessageAt,proto3" json:"lastMessageAt,omitempty"`
CreatedAt int64 `protobuf:"varint,8,opt,name=createdAt,proto3" json:"createdAt,omitempty"`
UpdatedAt int64 `protobuf:"varint,9,opt,name=updatedAt,proto3" json:"updatedAt,omitempty"`
}
func (x *ChatSessions) GetId() int64 {
if x != nil {
return x.Id
}
return 0
}
func (x *ChatSessions) GetType() string {
if x != nil {
return x.Type
}
return ""
}
func (x *ChatSessions) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *ChatSessions) GetCreatorId() int64 {
if x != nil {
return x.CreatorId
}
return 0
}
func (x *ChatSessions) GetParticipants() []int64 {
if x != nil {
return x.Participants
}
return nil
}
func (x *ChatSessions) GetLastMessage() string {
if x != nil {
return x.LastMessage
}
return ""
}
func (x *ChatSessions) GetLastMessageAt() int64 {
if x != nil {
return x.LastMessageAt
}
return 0
}
func (x *ChatSessions) GetCreatedAt() int64 {
if x != nil {
return x.CreatedAt
}
return 0
}
func (x *ChatSessions) GetUpdatedAt() int64 {
if x != nil {
return x.UpdatedAt
}
return 0
}
type AddChatSessionsReq struct {
Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
CreatorId int64 `protobuf:"varint,3,opt,name=creatorId,proto3" json:"creatorId,omitempty"`
Participants []int64 `protobuf:"varint,4,rep,packed,name=participants,proto3" json:"participants,omitempty"`
}
func (x *AddChatSessionsReq) GetType() string {
if x != nil {
return x.Type
}
return ""
}
func (x *AddChatSessionsReq) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *AddChatSessionsReq) GetCreatorId() int64 {
if x != nil {
return x.CreatorId
}
return 0
}
func (x *AddChatSessionsReq) GetParticipants() []int64 {
if x != nil {
return x.Participants
}
return nil
}
type AddChatSessionsResp struct {
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
}
func (x *AddChatSessionsResp) GetId() int64 {
if x != nil {
return x.Id
}
return 0
}
type UpdateChatSessionsReq struct {
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
Name *string `protobuf:"bytes,2,opt,name=name,proto3,oneof" json:"name,omitempty"`
LastMessage *string `protobuf:"bytes,3,opt,name=lastMessage,proto3,oneof" json:"lastMessage,omitempty"`
LastMessageAt *int64 `protobuf:"varint,4,opt,name=lastMessageAt,proto3,oneof" json:"lastMessageAt,omitempty"`
}
func (x *UpdateChatSessionsReq) GetId() int64 {
if x != nil {
return x.Id
}
return 0
}
func (x *UpdateChatSessionsReq) GetName() *string {
if x != nil {
return x.Name
}
return nil
}
func (x *UpdateChatSessionsReq) GetLastMessage() *string {
if x != nil {
return x.LastMessage
}
return nil
}
func (x *UpdateChatSessionsReq) GetLastMessageAt() *int64 {
if x != nil {
return x.LastMessageAt
}
return nil
}
type UpdateChatSessionsResp struct{}
type DelChatSessionsReq struct {
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
}
func (x *DelChatSessionsReq) GetId() int64 {
if x != nil {
return x.Id
}
return 0
}
type DelChatSessionsResp struct{}
type GetChatSessionsByIdReq struct {
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
}
func (x *GetChatSessionsByIdReq) GetId() int64 {
if x != nil {
return x.Id
}
return 0
}
type GetChatSessionsByIdResp struct {
ChatSessions *ChatSessions `protobuf:"bytes,1,opt,name=chatSessions,proto3" json:"chatSessions,omitempty"`
}
func (x *GetChatSessionsByIdResp) GetChatSessions() *ChatSessions {
if x != nil {
return x.ChatSessions
}
return nil
}
type SearchChatSessionsReq struct {
Page int64 `protobuf:"varint,1,opt,name=page,proto3" json:"page,omitempty"`
Limit int64 `protobuf:"varint,2,opt,name=limit,proto3" json:"limit,omitempty"`
UserId *int64 `protobuf:"varint,3,opt,name=userId,proto3,oneof" json:"userId,omitempty"`
Type *string `protobuf:"bytes,4,opt,name=type,proto3,oneof" json:"type,omitempty"`
}
func (x *SearchChatSessionsReq) GetPage() int64 {
if x != nil {
return x.Page
}
return 0
}
func (x *SearchChatSessionsReq) GetLimit() int64 {
if x != nil {
return x.Limit
}
return 0
}
func (x *SearchChatSessionsReq) GetUserId() *int64 {
if x != nil {
return x.UserId
}
return nil
}
func (x *SearchChatSessionsReq) GetType() *string {
if x != nil {
return x.Type
}
return nil
}
type SearchChatSessionsResp struct {
ChatSessions []*ChatSessions `protobuf:"bytes,1,rep,name=chatSessions,proto3" json:"chatSessions,omitempty"`
}
func (x *SearchChatSessionsResp) GetChatSessions() []*ChatSessions {
if x != nil {
return x.ChatSessions
}
return nil
}
type AddParticipantReq struct {
SessionId int64 `protobuf:"varint,1,opt,name=sessionId,proto3" json:"sessionId,omitempty"`
UserId int64 `protobuf:"varint,2,opt,name=userId,proto3" json:"userId,omitempty"`
}
func (x *AddParticipantReq) GetSessionId() int64 {
if x != nil {
return x.SessionId
}
return 0
}
func (x *AddParticipantReq) GetUserId() int64 {
if x != nil {
return x.UserId
}
return 0
}
type AddParticipantResp struct{}
type RemoveParticipantReq struct {
SessionId int64 `protobuf:"varint,1,opt,name=sessionId,proto3" json:"sessionId,omitempty"`
UserId int64 `protobuf:"varint,2,opt,name=userId,proto3" json:"userId,omitempty"`
}
func (x *RemoveParticipantReq) GetSessionId() int64 {
if x != nil {
return x.SessionId
}
return 0
}
func (x *RemoveParticipantReq) GetUserId() int64 {
if x != nil {
return x.UserId
}
return 0
}
type RemoveParticipantResp struct{}
+2
View File
@@ -3,6 +3,8 @@ FROM golang:1.25-alpine AS builder
RUN apk add --no-cache git
WORKDIR /build
ENV GOTOOLCHAIN=auto
ENV GOPROXY=https://goproxy.cn,direct
COPY go-wst/ go-wst/
COPY juwan-backend/go.mod juwan-backend/go.sum juwan-backend/
-18
View File
@@ -1,18 +0,0 @@
FROM golang:1.25-alpine AS builder
RUN apk add --no-cache git
WORKDIR /build
COPY go-wst/ go-wst/
COPY juwan-backend/go.mod juwan-backend/go.sum juwan-backend/
WORKDIR /build/juwan-backend
RUN go mod download
COPY juwan-backend/ /build/juwan-backend/
RUN CGO_ENABLED=0 go build -o /chat-rpc ./app/chat/rpc/
FROM alpine:latest
COPY --from=builder /chat-rpc /chat-rpc
COPY juwan-backend/app/chat/rpc/etc/pb.yaml /etc/pb.yaml
CMD ["/chat-rpc", "-f", "/etc/pb.yaml"]
+8 -1
View File
@@ -7,13 +7,20 @@ Hybrid:
Protocol: auto
Ws:
Name: chat-ws
Addr: :28888
Addr: :28889
Path: /ws/chat
MaxConnections: 10000
Auth:
Enabled: true
Source: envoy-header
HeaderName: x-auth-user-id
Wt:
Addr: :28443
Path: /wt/chat
CertFile: /Users/asadz/code/juwan/juwan-backend/app/chat/test/certs/tls.crt
KeyFile: /Users/asadz/code/juwan/juwan-backend/app/chat/test/certs/tls.key
Auth:
Enabled: true
FallbackStrategy: auto
MaxRetries: 3
MaxConnections: 10000
+2
View File
@@ -21,6 +21,8 @@ Hybrid:
KeyFile: /etc/certs/tls.key
Auth:
Enabled: true
Auth:
Enabled: true
FallbackStrategy: auto
MaxRetries: 3
MaxConnections: 10000
-5
View File
@@ -1,5 +0,0 @@
Name: pb.rpc
ListenOn: 0.0.0.0:28080
Log:
Level: debug
+35 -7
View File
@@ -12,7 +12,6 @@ echo ""
cleanup() {
echo ""
echo "=== Collecting container logs ==="
docker compose -f "$SCRIPT_DIR/docker-compose.yml" logs chat-rpc > "$LOG_DIR/chat-rpc.log" 2>&1 || true
docker compose -f "$SCRIPT_DIR/docker-compose.yml" logs chat-api > "$LOG_DIR/chat-api.log" 2>&1 || true
echo "=== Stopping containers ==="
docker compose -f "$SCRIPT_DIR/docker-compose.yml" down --remove-orphans 2>/dev/null || true
@@ -23,6 +22,22 @@ echo "=== Step 1: Building Docker images ==="
docker compose -f "$SCRIPT_DIR/docker-compose.yml" build 2>&1 | tee "$LOG_DIR/build.log"
echo ""
echo "=== Step 1.5: Building test tools ==="
(cd "$SCRIPT_DIR" && GOTOOLCHAIN=local GOPROXY=https://goproxy.cn,direct go build -o "$SCRIPT_DIR/wt_client" ./wt_client.go 2>&1) | tee -a "$LOG_DIR/build.log"
(cd "$SCRIPT_DIR" && GOTOOLCHAIN=local GOPROXY=https://goproxy.cn,direct go build -o "$SCRIPT_DIR/gen_token" ./gen_token.go 2>&1) | tee -a "$LOG_DIR/build.log"
echo ""
echo "=== Step 1.6: Generating JWT tokens ==="
JWT_SECRET="test-secret"
TOKEN_1001=$("$SCRIPT_DIR/gen_token" "$JWT_SECRET" 1001)
TOKEN_1002=$("$SCRIPT_DIR/gen_token" "$JWT_SECRET" 1002)
TOKEN_2001=$("$SCRIPT_DIR/gen_token" "$JWT_SECRET" 2001)
TOKEN_2002=$("$SCRIPT_DIR/gen_token" "$JWT_SECRET" 2002)
TOKEN_9001=$("$SCRIPT_DIR/gen_token" "$JWT_SECRET" 9001)
echo " user 1001 token: ${TOKEN_1001:0:30}..."
echo " user 1002 token: ${TOKEN_1002:0:30}..."
echo ""
echo "=== Step 2: Starting services ==="
docker compose -f "$SCRIPT_DIR/docker-compose.yml" up -d 2>&1 | tee -a "$LOG_DIR/build.log"
echo ""
@@ -39,24 +54,37 @@ sleep 3
echo "Services should be ready."
echo ""
VENV_PYTHON="$SCRIPT_DIR/.venv/bin/python3"
if [ ! -f "$VENV_PYTHON" ]; then
echo "=== Creating Python venv ==="
python3 -m venv "$SCRIPT_DIR/.venv"
"$VENV_PYTHON" -m pip install websockets -q
fi
echo "=== Step 4: Running WebSocket tests ==="
cd "$SCRIPT_DIR"
python3 test_ws.py 2>&1 | tee "$LOG_DIR/ws_test_stdout.log"
ALL_PROXY= HTTP_PROXY= HTTPS_PROXY= NO_PROXY=* "$VENV_PYTHON" test_ws.py 2>&1 | tee "$LOG_DIR/ws_test_stdout.log"
WS_RC=${PIPESTATUS[0]}
echo ""
echo "=== Step 5: Running WebTransport fallback tests ==="
python3 test_wt.py 2>&1 | tee "$LOG_DIR/wt_test_stdout.log"
WT_RC=${PIPESTATUS[0]}
echo "=== Step 5: Running WebTransport fallback tests (WS) ==="
ALL_PROXY= HTTP_PROXY= HTTPS_PROXY= NO_PROXY=* "$VENV_PYTHON" test_wt.py 2>&1 | tee "$LOG_DIR/wt_test_stdout.log"
WT_FALLBACK_RC=${PIPESTATUS[0]}
echo ""
echo "=== Step 6: Running WebTransport native tests (QUIC) ==="
ALL_PROXY= HTTP_PROXY= HTTPS_PROXY= NO_PROXY=* "$SCRIPT_DIR/wt_client" "https://localhost:28443/wt/chat?token=${TOKEN_9001}" 2>&1 | tee "$LOG_DIR/wt_native_stdout.log"
WT_NATIVE_RC=${PIPESTATUS[0]}
echo ""
echo "=== Test Summary ==="
echo "WebSocket test: $([ $WS_RC -eq 0 ] && echo 'PASSED' || echo 'FAILED')"
echo "WebTransport test: $([ $WT_RC -eq 0 ] && echo 'PASSED' || echo 'FAILED')"
echo "WT fallback test (WS): $([ $WT_FALLBACK_RC -eq 0 ] && echo 'PASSED' || echo 'FAILED')"
echo "WT native test (QUIC): $([ $WT_NATIVE_RC -eq 0 ] && echo 'PASSED' || echo 'FAILED')"
echo ""
echo "Logs saved to: $LOG_DIR/"
ls -la "$LOG_DIR/"
if [ $WS_RC -ne 0 ] || [ $WT_RC -ne 0 ]; then
if [ $WS_RC -ne 0 ] || [ $WT_FALLBACK_RC -ne 0 ] || [ $WT_NATIVE_RC -ne 0 ]; then
exit 1
fi
+1 -1
View File
@@ -22,7 +22,7 @@ func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
conf.MustLoad(*configFile, &c, conf.UseEnv())
server := rest.MustNewServer(c.RestConf)
server.Use(middlewares.NewHeaderExtractorMiddleware().Handle)
+6 -9
View File
@@ -7,16 +7,13 @@ Prometheus:
Port: 4001
Path: /metrics
# ===== PROC CONFIG =====
#CommunityRpcConf:
# Target: k8s://juwan/community-rpc-svc.juwan:8080
#UsercenterRpcConf:
# Target: k8s://juwan/users-rpc-svc.juwan:8080
# ===== DEV CONFIG =====
CommunityRpcConf:
Endpoints:
- community-rpc:8080
- "${COMMUNITY_RPC_TARGET}"
UsercenterRpcConf:
Endpoints:
- user-rpc:8080
- "${USER_RPC_TARGET}"
Log:
Level: debug
+3 -3
View File
@@ -4,7 +4,7 @@
package types
type Comment struct {
Id int64 `json:"id"`
Id int64 `json:"id,string"`
Content string `json:"content"`
Author UserProfile `json:"author"`
LikeCount int64 `json:"likeCount"`
@@ -54,12 +54,12 @@ type PathId struct {
}
type Post struct {
Id int64 `json:"id"`
Id int64 `json:"id,string"`
Title string `json:"title"`
Content string `json:"content"`
Images []string `json:"images"`
Tags []string `json:"tags"`
LinkedOrderId int64 `json:"linkedOrderId,optional"`
LinkedOrderId int64 `json:"linkedOrderId,string,optional"`
Pinned bool `json:"pinned"`
LikeCount int64 `json:"likeCount"`
CommentCount int64 `json:"commentCount"`
+4 -22
View File
@@ -6,36 +6,18 @@ Prometheus:
Port: 4001
Path: /metrics
# ===== PROC CONF =====
#SnowflakeRpcConf:
# Target: k8s://juwan/snowflake-svc:8080
#
#DB:
# Master: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@user-db-rw.juwan:${DB_PORT}/${DB_NAME}?sslmode=disable"
# Slave: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@user-db-ro.juwan:${DB_PORT}/${DB_NAME}?sslmode=disable"
#
#CacheConf:
# - Host: "${REDIS_M_HOST}"
# Type: node
# Pass: "${REDIS_PASSWORD}"
# User: "default"
# - Host: "${REDIS_S_HOST}"
# Type: node
# Pass: "${REDIS_PASSWORD}"
# User: "default"
# ===== DEV CONF =====
SnowflakeRpcConf:
Endpoints:
- snowflake:8080
- "${SNOWFLAKE_RPC_TARGET}"
DB:
Master: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@postgres:${DB_PORT}/${DB_NAME}?sslmode=disable"
Slaves: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@postgres:${DB_PORT}/${DB_NAME}?sslmode=disable"
Master: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}?sslmode=disable"
Slaves: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@${DB_HOST_RO}:${DB_PORT}/${DB_NAME}?sslmode=disable"
CacheConf:
- Host: "${REDIS_HOST}:${REDIS_PORT}"
Type: node
Pass: "${REDIS_PASSWORD}"
Log:
Level: debug
+1 -1
View File
@@ -22,7 +22,7 @@ func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
conf.MustLoad(*configFile, &c, conf.UseEnv())
server := rest.MustNewServer(c.RestConf)
server.Use(middlewares.NewHeaderExtractorMiddleware().Handle)
+8 -4
View File
@@ -7,13 +7,17 @@ Prometheus:
Port: 4001
Path: /metrics
# ===== DEV CONFIG =====
DisputeRpcConf:
Endpoints:
- dispute-rpc:8080
- "${DISPUTE_RPC_TARGET}"
OrderRpcConf:
Endpoints:
- order-rpc:8080
- "${ORDER_RPC_TARGET}"
PlayerRpcConf:
Endpoints:
- player-rpc:8080
- "${PLAYER_RPC_TARGET}"
Log:
Level: debug
+5 -5
View File
@@ -15,11 +15,11 @@ type CreateDisputeReq struct {
}
type Dispute struct {
Id int64 `json:"id"`
OrderId int64 `json:"orderId"`
InitiatorId int64 `json:"initiatorId"`
Id int64 `json:"id,string"`
OrderId int64 `json:"orderId,string"`
InitiatorId int64 `json:"initiatorId,string"`
InitiatorName string `json:"initiatorName"`
RespondentId int64 `json:"respondentId"`
RespondentId int64 `json:"respondentId,string"`
Reason string `json:"reason"`
Evidence []string `json:"evidence"`
Status string `json:"status"`
@@ -28,7 +28,7 @@ type Dispute struct {
RespondentEvidence []string `json:"respondentEvidence"`
AppealReason string `json:"appealReason,optional"`
AppealedAt string `json:"appealedAt,optional"`
ResolvedBy int64 `json:"resolvedBy,optional"`
ResolvedBy int64 `json:"resolvedBy,string,optional"`
ResolvedAt string `json:"resolvedAt,optional"`
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
+4 -4
View File
@@ -6,18 +6,18 @@ Prometheus:
Port: 4001
Path: /metrics
# ===== DEV CONF =====
SnowflakeRpcConf:
Endpoints:
- snowflake:8080
- "${SNOWFLAKE_RPC_TARGET}"
DB:
Master: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@postgres:${DB_PORT}/${DB_NAME}?sslmode=disable"
Slaves: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@postgres:${DB_PORT}/${DB_NAME}?sslmode=disable"
Master: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}?sslmode=disable"
Slaves: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@${DB_HOST_RO}:${DB_PORT}/${DB_NAME}?sslmode=disable"
CacheConf:
- Host: "${REDIS_HOST}:${REDIS_PORT}"
Type: node
Pass: "${REDIS_PASSWORD}"
Log:
Level: debug
+4 -12
View File
@@ -7,21 +7,10 @@ Prometheus:
Port: 4001
Path: /metrics
# ===== PROC CONFIG =====
#CacheConf:
# - Host: "${REDIS_M_HOST}"
# Type: node
# Pass: "${REDIS_PASSWORD}"
# User: "default"
# - Host: "${REDIS_S_HOST}"
# Type: node
# Pass: "${REDIS_PASSWORD}"
# User: "default"
# ===== DEV CONFIG =====
CacheConf:
- Host: "${REDIS_HOST}:${REDIS_PORT}"
Type: node
Pass: "${REDIS_PASSWORD}"
Kmq:
Name: email-api
@@ -29,3 +18,6 @@ Kmq:
- "${KAFKA_BROKER}"
Group: "email-api-group"
Topic: "email-task"
Log:
Level: debug
+2 -12
View File
@@ -30,15 +30,5 @@ Mail:
InsecureSkipVerify: false
ReplyTo: "${EMAIL_REPLY_TO}"
# Mail:
# Enabled: true
# Host: "smtp.163.com"
# Port: 465
# Username: "churong2646@163.com"
# Password: "GTv6C6qNbv5urAiD"
# FromAddress: "churong2646@163.com"
# FromName: "聚玩"
# UseSSL: true
# UseStartTLS: false
# InsecureSkipVerify: false
# ReplyTo: ""
Log:
Level: debug
+1 -6
View File
@@ -7,14 +7,9 @@ Prometheus:
Port: 4001
Path: /metrics
# ===== PROC CONF =====
#GameRpcConf:
# Target: k8s://juwan/game-rpc-svc:8080
# ===== DEV CONF =====
GameRpcConf:
Endpoints:
- game-rpc:8080
- "${GAME_RPC_TARGET}"
Log:
Level: debug
+1 -1
View File
@@ -22,7 +22,7 @@ func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
conf.MustLoad(*configFile, &c, conf.UseEnv())
server := rest.MustNewServer(c.RestConf)
server.Use(middlewares.NewRequestMiddleware().Handle)
+1 -1
View File
@@ -1,5 +1,5 @@
// Code generated by goctl. DO NOT EDIT.
// goctl 1.9.2
// goctl 1.10.1
package handler
+1 -1
View File
@@ -7,7 +7,7 @@ type EmptyResp struct {
}
type Game struct {
Id int64 `json:"id,optional"`
Id int64 `json:"id,string,optional"`
Name string `json:"name"`
Icon string `json:"icon"`
Category string `json:"category"`
+4 -26
View File
@@ -6,40 +6,18 @@ Prometheus:
Port: 4001
Path: /metrics
# ===== PROC CONF =====
#DB:
# Master: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@user-db-rw.juwan:${DB_PORT}/${DB_NAME}?sslmode=disable"
# Slaves: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@user-db-ro.juwan:${DB_PORT}/${DB_NAME}?sslmode=disable"
#
#
#SnowflakeRpcConf:
# Target: k8s://juwan/snowflake-svc:8080
#
#CacheConf:
# - Host: "${REDIS_M_HOST}"
# Type: node
# Pass: "${REDIS_PASSWORD}"
# User: "default"
# - Host: "${REDIS_S_HOST}"
# Type: node
# Pass: "${REDIS_PASSWORD}"
# User: "default"
#
#Log:
# Level: info
# ===== DEV CONF =====
SnowflakeRpcConf:
Endpoints:
- snowflake:8080
- "${SNOWFLAKE_RPC_TARGET}"
DB:
Master: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@postgres:${DB_PORT}/${DB_NAME}?sslmode=disable"
Slaves: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@postgres:${DB_PORT}/${DB_NAME}?sslmode=disable"
Master: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}?sslmode=disable"
Slaves: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@${DB_HOST_RO}:${DB_PORT}/${DB_NAME}?sslmode=disable"
CacheConf:
- Host: "${REDIS_HOST}:${REDIS_PORT}"
Type: node
Pass: "${REDIS_PASSWORD}"
Log:
Level: debug
+4 -2
View File
@@ -7,7 +7,9 @@ Prometheus:
Port: 4001
Path: /metrics
# ===== DEV CONFIG =====
NotificationRpcConf:
Endpoints:
- notification-rpc:8080
- "${NOTIFICATION_RPC_TARGET}"
Log:
Level: debug
+1 -1
View File
@@ -7,7 +7,7 @@ type EmptyResp struct {
}
type Notification struct {
Id int64 `json:"id"`
Id int64 `json:"id,string"`
Type string `json:"type"`
Title string `json:"title"`
Content string `json:"content"`
+1 -1
View File
@@ -22,7 +22,7 @@ func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
conf.MustLoad(*configFile, &c, conf.UseEnv())
server := rest.MustNewServer(c.RestConf)
server.Use(middlewares.NewHeaderExtractorMiddleware().Handle)
+4 -4
View File
@@ -6,18 +6,18 @@ Prometheus:
Port: 4001
Path: /metrics
# ===== DEV CONF =====
SnowflakeRpcConf:
Endpoints:
- snowflake:8080
- "${SNOWFLAKE_RPC_TARGET}"
DB:
Master: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@postgres:${DB_PORT}/${DB_NAME}?sslmode=disable"
Slaves: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@postgres:${DB_PORT}/${DB_NAME}?sslmode=disable"
Master: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}?sslmode=disable"
Slaves: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@${DB_HOST_RO}:${DB_PORT}/${DB_NAME}?sslmode=disable"
CacheConf:
- Host: "${REDIS_HOST}:${REDIS_PORT}"
Type: node
Pass: "${REDIS_PASSWORD}"
Log:
Level: debug
+1 -13
View File
@@ -7,21 +7,9 @@ Prometheus:
Port: 4001
Path: /metrics
# ===== PROC CONF =====
#FileRpcConf:
# Target: k8s://juwan/objectstory-rpc-svc:8080
#
#Log:
# Level: info
# ===== DEV CONF =====
FileRpcConf:
Endpoints:
- objectstory-rpc:8080
- "${OBJECTSTORY_RPC_TARGET}"
Log:
Level: debug
# k8s://juwan/<service name>:8080
+1 -1
View File
@@ -22,7 +22,7 @@ func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
conf.MustLoad(*configFile, &c, conf.UseEnv())
server := rest.MustNewServer(c.RestConf)
server.Use(middlewares.NewRequestMiddleware().Handle)
+5 -28
View File
@@ -6,36 +6,13 @@ Prometheus:
Port: 4001
Path: /metrics
# Target: k8s://juwan/<service name>.<namespace>:8080
#S3Conf:
# Endpoint: "${S3_ENDPOINT}"
# AccessKey: "${S3_ACCESS_KEY}"
# SecretKey: "${S3_SECRET_KEY}"
# Bucket: "${S3_BUCKET_NAME}"
# Region: "${S3_REGION}"
S3Conf:
Endpoint: "https://cn-nb1.rains3.com"
AccessKey: "mfgGnaAcUDP2zYAi"
SecretKey: "ZfKkbhUvsAchiKlxzIXrDHrSyskyRj"
Bucket: "juwan-dev-image-zj"
Region: auto
Endpoint: "${S3_ENDPOINT}"
AccessKey: "${S3_ACCESS_KEY}"
SecretKey: "${S3_SECRET_KEY}"
Bucket: "${S3_BUCKET_NAME}"
Region: "${S3_REGION}"
UsePathStyle: true
Log:
Level: debug
#DB:
# Master: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@user-db-rw.juwan:${DB_PORT}/${DB_NAME}?sslmode=disable"
# Slaves: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@user-db-ro.juwan:${DB_PORT}/${DB_NAME}?sslmode=disable"
#
#CacheConf:
# - Host: "${REDIS_M_HOST}"
# Type: node
# Pass: "${REDIS_PASSWORD}"
# User: "default"
# - Host: "${REDIS_S_HOST}"
# Type: node
# Pass: "${REDIS_PASSWORD}"
# User: "default"
#
+1 -1
View File
@@ -22,7 +22,7 @@ func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
conf.MustLoad(*configFile, &c, conf.UseEnv())
ctx := svc.NewServiceContext(c)
s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) {
+6 -15
View File
@@ -7,26 +7,17 @@ Prometheus:
Port: 4001
Path: /metrics
# k8s://juwan/<service name>:8080
# ===== PROC CONF =====
#OrderRpcConf:
# Target: k8s://juwan/order-rpc-svc:8080
#
#PlayerRpcConf:
# Target: k8s://juwan/player-rpc-svc:8080
#
#ShopRpcConf:
# Target: k8s://juwan/shop-rpc-svc:8080
# ===== DEV CONF ====
OrderRpcConf:
Endpoints:
- order-rpc:8080
- "${ORDER_RPC_TARGET}"
PlayerRpcConf:
Endpoints:
- player-rpc:8080
- "${PLAYER_RPC_TARGET}"
ShopRpcConf:
Endpoints:
- shop-rpc:8080
- "${SHOP_RPC_TARGET}"
Log:
Level: debug
+9 -9
View File
@@ -4,9 +4,9 @@
package types
type CreateOrderReq struct {
PlayerId int64 `json:"playerId"`
ShopId int64 `json:"shopId,optional"`
ServiceId int64 `json:"serviceId"`
PlayerId int64 `json:"playerId,string"`
ShopId int64 `json:"shopId,string,optional"`
ServiceId int64 `json:"serviceId,string"`
Quantity int `json:"quantity"`
Note string `json:"note,optional"`
}
@@ -20,10 +20,10 @@ type EmptyResp struct {
}
type Order struct {
Id int64 `json:"id"`
ConsumerId int64 `json:"consumerId"`
Id int64 `json:"id,string"`
ConsumerId int64 `json:"consumerId,string"`
PlayerId string `json:"playerId"`
ShopId int64 `json:"shopId,optional"`
ShopId int64 `json:"shopId,string,optional"`
Service PlayerService `json:"service"`
Status string `json:"status"`
TotalPrice float64 `json:"totalPrice"`
@@ -60,9 +60,9 @@ type PathId struct {
}
type PlayerService struct {
Id int64 `json:"id"`
PlayerId int64 `json:"playerId"`
GameId int64 `json:"gameId"`
Id int64 `json:"id,string"`
PlayerId int64 `json:"playerId,string"`
GameId int64 `json:"gameId,string"`
GameName string `json:"gameName"`
Title string `json:"title"`
Description string `json:"description"`
+1 -1
View File
@@ -22,7 +22,7 @@ func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
conf.MustLoad(*configFile, &c, conf.UseEnv())
server := rest.MustNewServer(c.RestConf)
server.Use(middlewares.NewRequestMiddleware().Handle)
+4 -35
View File
@@ -1,54 +1,23 @@
Name: pb.rpc
ListenOn: 0.0.0.0:8080
Prometheus:
Host: 0.0.0.0
Port: 4001
Path: /metrics
# tcd:
# Hosts:
# - 127.0.0.1:2379
# Key: pb.rpc
# Target: k8s://juwan/<service name>.<namespace>:8080
# ==== PROC CONF ====
#SnowflakeRpcConf:
# Target: k8s://juwan/snowflake-svc:8080
#
#
#DB:
# Master: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@user-db-rw.juwan:${DB_PORT}/${DB_NAME}?sslmode=disable"
# Slaves: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@user-db-ro.juwan:${DB_PORT}/${DB_NAME}?sslmode=disable"
#
#
#CacheConf:
# - Host: "${REDIS_M_HOST}"
# Type: node
# Pass: "${REDIS_PASSWORD}"
# User: "default"
# - Host: "${REDIS_S_HOST}"
# Type: node
# Pass: "${REDIS_PASSWORD}"
# User: "default"
#
#Log:
# Level: info
# ==== DEV CONF ====
SnowflakeRpcConf:
Endpoints:
- snowflake:8080
- "${SNOWFLAKE_RPC_TARGET}"
DB:
Master: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@postgres:${DB_PORT}/${DB_NAME}?sslmode=disable"
Slaves: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@postgres:${DB_PORT}/${DB_NAME}?sslmode=disable"
Master: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}?sslmode=disable"
Slaves: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@${DB_HOST_RO}:${DB_PORT}/${DB_NAME}?sslmode=disable"
CacheConf:
- Host: "${REDIS_HOST}:${REDIS_PORT}"
Type: node
Pass: "${REDIS_PASSWORD}"
Log:
Level: debug
+6 -8
View File
@@ -7,15 +7,13 @@ Prometheus:
Port: 4001
Path: /metrics
# k8s://juwan/<service name>:8080
# ===== PROC CONF =====
#PlayerRpcConf:
# Target: k8s://juwan/player-rpc-svc:8080
# ===== DEV CONF ====
PlayerRpcConf:
Endpoints:
- player-rpc:8080
- "${PLAYER_RPC_TARGET}"
UsercenterRpcConf:
Endpoints:
- user-rpc:8080
- "${USER_RPC_TARGET}"
Log:
Level: debug
@@ -0,0 +1,32 @@
// Code scaffolded by goctl. Safe to edit.
// goctl 1.10.1
package player
import (
"net/http"
"github.com/zeromicro/go-zero/rest/httpx"
"juwan-backend/app/player/api/internal/logic/player"
"juwan-backend/app/player/api/internal/svc"
"juwan-backend/app/player/api/internal/types"
)
// 获取当前用户的打手资料
func GetMyPlayerHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.EmptyResp
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := player.NewGetMyPlayerLogic(r.Context(), svcCtx)
resp, err := l.GetMyPlayer(&req)
if err != nil {
httpx.ErrorCtx(r.Context(), w, err)
} else {
httpx.OkJsonCtx(r.Context(), w, resp)
}
}
}
@@ -57,6 +57,12 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
Path: "/players/me",
Handler: player.InitPlayerHandler(serverCtx),
},
{
// 获取当前用户的打手资料
Method: http.MethodGet,
Path: "/players/me",
Handler: player.GetMyPlayerHandler(serverCtx),
},
{
// 更新接单状态
Method: http.MethodPut,
@@ -45,11 +45,16 @@ func (l *GetPlayerLogic) GetPlayer(req *types.GetPlayerReq) (resp *types.PlayerP
if player == nil {
return nil, errors.New("player not found")
}
completionRate := 0.0
if player.TotalOrders > 0 {
completionRate = float64(player.CompletedOrders) / float64(player.TotalOrders)
}
resp = &types.PlayerProfile{
Id: player.Id,
Rating: player.Rating,
TotalOrders: player.TotalOrders,
CompletionRate: completionRate,
Status: player.Status,
Gender: player.Gender,
Services: []types.PlayerService{},
@@ -0,0 +1,126 @@
// Code scaffolded by goctl. Safe to edit.
// goctl 1.10.1
package player
import (
"context"
"encoding/json"
"errors"
"strconv"
"time"
"juwan-backend/app/player/api/internal/svc"
"juwan-backend/app/player/api/internal/types"
"juwan-backend/app/player/rpc/pb"
"juwan-backend/app/player/rpc/playerservice"
"juwan-backend/app/users/rpc/usercenter"
"juwan-backend/common/utils/contextj"
"github.com/zeromicro/go-zero/core/logx"
"google.golang.org/grpc/status"
)
type GetMyPlayerLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
// 获取当前用户的打手资料
func NewGetMyPlayerLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetMyPlayerLogic {
return &GetMyPlayerLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *GetMyPlayerLogic) GetMyPlayer(req *types.EmptyResp) (resp *types.PlayerProfile, err error) {
userID, err := contextj.UserIDFrom(l.ctx)
if err != nil {
return nil, err
}
playerResp, err := l.svcCtx.PlayerRpc.GetPlayerByUserId(l.ctx, &playerservice.SearchPlayersReq{UserId: &userID})
if err != nil {
st, _ := status.FromError(err)
if st.Code().String() == "NotFound" {
return nil, errors.New("player not found")
}
return nil, err
}
player := playerResp.GetPlayers()
if player == nil {
return nil, errors.New("player not found")
}
userResp, err := l.svcCtx.UserRpc.GetUsersById(l.ctx, &usercenter.GetUsersByIdReq{Id: userID})
if err != nil {
return nil, err
}
user := userResp.GetUsers()
if user == nil {
return nil, errors.New("user not found")
}
games := make([]string, 0, len(player.Games))
for _, gameID := range player.Games {
games = append(games, strconv.FormatInt(gameID, 10))
}
verificationStatus := map[string]string{}
if user.VerificationStatus != "" {
_ = json.Unmarshal([]byte(user.VerificationStatus), &verificationStatus)
}
resp = &types.PlayerProfile{
Id: player.Id,
User: types.UserProfile{
Id: strconv.FormatInt(user.Id, 10),
Username: user.Username,
Nickname: user.Nickname,
Avatar: user.Avatar,
Role: user.CurrentRole,
VerifiedRoles: append([]string{}, user.VerifiedRoles...),
VerificationStatus: verificationStatus,
Phone: user.Phone,
Bio: user.Bio,
CreatedAt: time.Unix(user.CreatedAt, 0).Format(time.DateTime),
},
Rating: player.Rating,
TotalOrders: player.TotalOrders,
Status: player.Status,
Games: games,
Services: []types.PlayerService{},
Gender: player.Gender,
Tags: append([]string{}, player.Tags...),
}
if player.ShopId != 0 {
resp.ShopId = strconv.FormatInt(player.ShopId, 10)
}
svcResp, svcErr := l.svcCtx.PlayerRpc.SearchPlayerServices(l.ctx, &pb.SearchPlayerServicesReq{
PlayerId: player.Id,
Limit: 100,
})
if svcErr != nil {
logx.Errorf("GetMyPlayer SearchPlayerServices player=%d err: %v", player.Id, svcErr)
} else {
for _, s := range svcResp.PlayerServices {
resp.Services = append(resp.Services, types.PlayerService{
Id: s.Id,
PlayerId: s.PlayerId,
GameId: s.GameId,
Title: s.Title,
Description: s.Description,
Price: s.Price,
Unit: s.Unit,
RankRange: s.RankRange,
Availability: s.Availability,
})
}
}
return resp, nil
}
+6 -6
View File
@@ -4,7 +4,7 @@
package types
type CreateServiceReq struct {
GameId int64 `json:"gameId,optional"`
GameId int64 `json:"gameId,string,optional"`
Title string `json:"title,optional"`
Description string `json:"description,optional"`
Price float64 `json:"price"`
@@ -56,7 +56,7 @@ type PlayerListResp struct {
}
type PlayerProfile struct {
Id int64 `json:"id"`
Id int64 `json:"id,string"`
User UserProfile `json:"user"`
Rating float64 `json:"rating"`
TotalOrders int64 `json:"totalOrders"`
@@ -71,9 +71,9 @@ type PlayerProfile struct {
}
type PlayerService struct {
Id int64 `json:"id,optional"`
PlayerId int64 `json:"playerId"`
GameId int64 `json:"gameId"`
Id int64 `json:"id,string,optional"`
PlayerId int64 `json:"playerId,string"`
GameId int64 `json:"gameId,string"`
GameName string `json:"gameName"`
Title string `json:"title"`
Description string `json:"description"`
@@ -100,7 +100,7 @@ type UpdatePlayerStatusReq struct {
type UpdateServiceReq struct {
Id int64 `path:"id"`
GameId *int64 `json:"gameId,optional"`
GameId *int64 `json:"gameId,string,optional"`
Title *string `json:"title,optional"`
Description *string `json:"description,optional"`
Price *float64 `json:"price,optional"`
+1 -1
View File
@@ -22,7 +22,7 @@ func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
conf.MustLoad(*configFile, &c, conf.UseEnv())
server := rest.MustNewServer(c.RestConf)
server.Use(middlewares.NewHeaderExtractorMiddleware().Handle)
+4 -29
View File
@@ -1,48 +1,23 @@
Name: pb.rpc
ListenOn: 0.0.0.0:8080
Prometheus:
Host: 0.0.0.0
Port: 4001
Path: /metrics
# ===== PROC CONF =====
#SnowflakeRpcConf:
# Target: k8s://juwan/snowflake-svc:8080
#DB:
# Master: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@user-db-rw.juwan:${DB_PORT}/${DB_NAME}?sslmode=disable"
# Slaves: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@user-db-ro.juwan:${DB_PORT}/${DB_NAME}?sslmode=disable"
#
#CacheConf:
# - Host: "${REDIS_M_HOST}"
# Type: node
# Pass: "${REDIS_PASSWORD}"
# User: "default"
# - Host: "${REDIS_S_HOST}"
# Type: node
# Pass: "${REDIS_PASSWORD}"
# User: "default"
#
#Log:
# Level: info
# ===== DEV CONF =====
SnowflakeRpcConf:
Endpoints:
- snowflake:8080
- "${SNOWFLAKE_RPC_TARGET}"
DB:
Master: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@postgres:${DB_PORT}/${DB_NAME}?sslmode=disable"
Slaves: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@postgres:${DB_PORT}/${DB_NAME}?sslmode=disable"
Master: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}?sslmode=disable"
Slaves: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@${DB_HOST_RO}:${DB_PORT}/${DB_NAME}?sslmode=disable"
CacheConf:
- Host: "${REDIS_HOST}:${REDIS_PORT}"
Type: node
Pass: "${REDIS_PASSWORD}"
Log:
Level: debug
+8 -4
View File
@@ -7,13 +7,17 @@ Prometheus:
Port: 4001
Path: /metrics
# ===== DEV CONFIG =====
ReviewRpcConf:
Endpoints:
- review-rpc:8080
- "${REVIEW_RPC_TARGET}"
OrderRpcConf:
Endpoints:
- order-rpc:8080
- "${ORDER_RPC_TARGET}"
PlayerRpcConf:
Endpoints:
- player-rpc:8080
- "${PLAYER_RPC_TARGET}"
Log:
Level: debug
+3 -3
View File
@@ -27,9 +27,9 @@ type PageReq struct {
}
type Review struct {
Id int64 `json:"id"`
OrderId int64 `json:"orderId"`
FromUserId int64 `json:"fromUserId"`
Id int64 `json:"id,string"`
OrderId int64 `json:"orderId,string"`
FromUserId int64 `json:"fromUserId,string"`
FromUserName string `json:"fromUserName"`
Rating int `json:"rating"`
Content string `json:"content"`
+1 -1
View File
@@ -22,7 +22,7 @@ func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
conf.MustLoad(*configFile, &c, conf.UseEnv())
server := rest.MustNewServer(c.RestConf)
server.Use(middlewares.NewHeaderExtractorMiddleware().Handle)
+4 -4
View File
@@ -6,18 +6,18 @@ Prometheus:
Port: 4001
Path: /metrics
# ===== DEV CONF =====
SnowflakeRpcConf:
Endpoints:
- snowflake:8080
- "${SNOWFLAKE_RPC_TARGET}"
DB:
Master: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@postgres:${DB_PORT}/${DB_NAME}?sslmode=disable"
Slaves: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@postgres:${DB_PORT}/${DB_NAME}?sslmode=disable"
Master: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}?sslmode=disable"
Slaves: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@${DB_HOST_RO}:${DB_PORT}/${DB_NAME}?sslmode=disable"
CacheConf:
- Host: "${REDIS_HOST}:${REDIS_PORT}"
Type: node
Pass: "${REDIS_PASSWORD}"
Log:
Level: debug
+4 -2
View File
@@ -7,7 +7,9 @@ Prometheus:
Port: 4001
Path: /metrics
# ===== DEV CONFIG =====
SearchRpcConf:
Endpoints:
- search-rpc:8080
- "${SEARCH_RPC_TARGET}"
Log:
Level: debug
+1 -1
View File
@@ -22,7 +22,7 @@ func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
conf.MustLoad(*configFile, &c, conf.UseEnv())
server := rest.MustNewServer(c.RestConf)
server.Use(middlewares.NewHeaderExtractorMiddleware().Handle)
+4 -4
View File
@@ -6,18 +6,18 @@ Prometheus:
Port: 4001
Path: /metrics
# ===== DEV CONF =====
SnowflakeRpcConf:
Endpoints:
- snowflake:8080
- "${SNOWFLAKE_RPC_TARGET}"
DB:
Master: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@postgres:${DB_PORT}/${DB_NAME}?sslmode=disable"
Slaves: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@postgres:${DB_PORT}/${DB_NAME}?sslmode=disable"
Master: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}?sslmode=disable"
Slaves: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@${DB_HOST_RO}:${DB_PORT}/${DB_NAME}?sslmode=disable"
CacheConf:
- Host: "${REDIS_HOST}:${REDIS_PORT}"
Type: node
Pass: "${REDIS_PASSWORD}"
Log:
Level: debug
+6 -10
View File
@@ -7,17 +7,13 @@ Prometheus:
Port: 4001
Path: /metrics
# k8s://juwan/<service name>:8080
# ===== PROC CONFIG =====
#ShopRpcConf:
# Target: k8s://juwan/shop-rpc-svc.juwan:8080
# ===== DEV CONFIG ====
ShopRpcConf:
Endpoints:
- shop-rpc:8080
- "${SHOP_RPC_TARGET}"
PlayerRpcConf:
Endpoints:
- player-rpc:8080
- "${PLAYER_RPC_TARGET}"
Log:
Level: debug
+5 -5
View File
@@ -37,7 +37,7 @@ type IncomeStatsResp struct {
type InvitationReq struct {
Id int64 `path:"id"`
PlayerId int64 `json:"playerId"`
PlayerId int64 `json:"playerId,string"`
}
type PageMeta struct {
@@ -61,11 +61,11 @@ type ShopIdReq struct {
}
type ShopInvitation struct {
Id int64 `json:"id"`
ShopId int64 `json:"shopId"`
PlayerId int64 `json:"playerId"`
Id int64 `json:"id,string"`
ShopId int64 `json:"shopId,string"`
PlayerId int64 `json:"playerId,string"`
Status string `json:"status"`
InvitedBy int64 `json:"invitedBy"`
InvitedBy int64 `json:"invitedBy,string"`
CreatedAt int64 `json:"createdAt"`
RespondedAt int64 `json:"respondedAt,optional"`
}
+1 -1
View File
@@ -22,7 +22,7 @@ func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
conf.MustLoad(*configFile, &c, conf.UseEnv())
server := rest.MustNewServer(c.RestConf)
server.Use(middlewares.NewHeaderExtractorMiddleware().Handle)
+7 -38
View File
@@ -6,53 +6,22 @@ Prometheus:
Port: 4001
Path: /metrics
# tcd:
# Hosts:
# - 127.0.0.1:2379
# Key: pb.rpc
# ===== PROC Config =====
#SnowflakeRpcConf:
# Target: k8s://juwan/snowflake-svc:8080
#UsersRpcConf:
# Target: k8s://juwan/user-rpc-svc:8080
#DB:
# Master: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@{DB_HOST_RW}.juwan:${DB_PORT}/${DB_NAME}?sslmode=disable"
# Slaves: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@{BD_HOST_RO}.juwan:${DB_PORT}/${DB_NAME}?sslmode=disable"
#
#
#CacheConf:
# - Host: "${REDIS_M_HOST}"
# Type: node
# Pass: "${REDIS_PASSWORD}"
# User: "default"
# - Host: "${REDIS_S_HOST}"
# Type: node
# Pass: "${REDIS_PASSWORD}"
# User: "default"
# ===== DEV Config =====
SnowflakeRpcConf:
Endpoints:
- snowflake:8080
- "${SNOWFLAKE_RPC_TARGET}"
UsersRpcConf:
Endpoints:
- user-rpc:8080
- "${USER_RPC_TARGET}"
DB:
Master: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@postgres:${DB_PORT}/${DB_NAME}?sslmode=disable"
Slaves: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@postgres:${DB_PORT}/${DB_NAME}?sslmode=disable"
Master: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}?sslmode=disable"
Slaves: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@${DB_HOST_RO}:${DB_PORT}/${DB_NAME}?sslmode=disable"
CacheConf:
- Host: "${REDIS_HOST}:${REDIS_PORT}"
Type: node
Pass: "${REDIS_PASSWORD}"
Log:
Level: info
# Target: k8s://juwan/<service name>.<namespace>:8080
#DB:
# Master: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@user-db-rw.juwan:${DB_PORT}/${DB_NAME}?sslmode=disable"
# Slaves: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@user-db-ro.juwan:${DB_PORT}/${DB_NAME}?sslmode=disable"
Level: debug
+1 -1
View File
@@ -22,7 +22,7 @@ func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
conf.MustLoad(*configFile, &c, conf.UseEnv())
ctx := svc.NewServiceContext(c)
s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) {
+10 -28
View File
@@ -1,45 +1,27 @@
Name: pb.rpc
ListenOn: 0.0.0.0:8080
Prometheus:
Host: 0.0.0.0
Port: 4001
Path: /metrics
# ===== PROC Config =====
#SnowflakeRpcConf:
# Target: k8s://juwan/snowflake-svc.juwan:8080
#UserRpcConf:
# Target: k8s://juwan/user-rpc-svc.juwan:8080
#
#DB:
# Master: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@user-db-rw.juwan:${DB_PORT}/${DB_NAME}?sslmode=disable"
# Slave: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@user-db-ro.juwan:${DB_PORT}/${DB_NAME}?sslmode=disable"
#
#CacheConf:
# - Host: "${REDIS_M_HOST}"
# Type: node
# Pass: "${REDIS_PASSWORD}"
# User: "default"
# - Host: "${REDIS_S_HOST}"
# Type: node
# Pass: "${REDIS_PASSWORD}"
# User: "default"
#
#Log:
# Level: info
# ===== DEV Config =====
SnowflakeRpcConf:
Endpoints:
- snowflake:8080
- "${SNOWFLAKE_RPC_TARGET}"
UserRpcConf:
Endpoints:
- user-rpc:8080
- "${USER_RPC_TARGET}"
DB:
Master: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@postgres:${DB_PORT}/${DB_NAME}?sslmode=disable"
Slave: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@postgres:${DB_PORT}/${DB_NAME}?sslmode=disable"
Master: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}?sslmode=disable"
Slave: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@${DB_HOST_RO}:${DB_PORT}/${DB_NAME}?sslmode=disable"
CacheConf:
- Host: "${REDIS_HOST}:${REDIS_PORT}"
Type: node
Pass: "${REDIS_PASSWORD}"
Log:
Level: debug
+3 -9
View File
@@ -7,19 +7,13 @@ Prometheus:
Port: 4001
Path: /metrics
# ===== PROC CONFIG =====
#UsercenterRpcConf:
# Target: k8s://juwan/user-rpc-svc:8080
#UserVerificationRpc:
# Target: k8s://juwan/user_verifications-svc:8080
# ===== DEV CONFIG ====
UserVerificationRpc:
Endpoints:
- user-verifications-rpc:8080
- "${USER_VERIFICATIONS_RPC_TARGET}"
UsercenterRpcConf:
Endpoints:
- user-rpc:8080
- "${USER_RPC_TARGET}"
Log:
Level: debug
+2 -2
View File
@@ -125,8 +125,8 @@ type VerificationIdReq struct {
}
type VerificationItem struct {
Id int64 `json:"id"` // 认证记录ID (主键,用于管理员操作)
UserId int64 `json:"userId"` // 申请人ID (外键)
Id int64 `json:"id,string"` // 认证记录ID (主键,用于管理员操作)
UserId int64 `json:"userId,string"` // 申请人ID (外键)
UserNickname string `json:"userNickname"` // 冗余显示,方便前端展示
Role string `json:"role"` // 申请角色: player, owner
Status string `json:"status"` // pending, approved, rejected
+1 -1
View File
@@ -22,7 +22,7 @@ func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
conf.MustLoad(*configFile, &c, conf.UseEnv())
server := rest.MustNewServer(c.RestConf)
server.Use(middlewares.NewHeaderExtractorMiddleware().Handle)
+35 -11
View File
@@ -6,14 +6,28 @@ import (
"strings"
"time"
"juwan-backend/app/snowflake/rpc/snowflake"
"juwan-backend/app/users/rpc/internal/models/schema"
"juwan-backend/app/users/rpc/internal/models/users"
"juwan-backend/app/users/rpc/internal/svc"
"juwan-backend/common/utils/pwdUtils"
"juwan-backend/pkg/types"
"github.com/zeromicro/go-zero/core/logx"
)
const adminUserID int64 = 100000
var adminVerifiedRoles = types.TextArray{
Elements: []string{"consumer", "player", "owner", "admin"},
Valid: true,
}
var adminVerificationStatus = schema.VerificationStatusStruct{
Consumer: "approved",
Player: "approved",
Owner: "approved",
}
func initAdmin(svcCtx *svc.ServiceContext) {
username := strings.TrimSpace(os.Getenv("ADMIN_USERNAME"))
password := strings.TrimSpace(os.Getenv("ADMIN_PASSWORD"))
@@ -24,8 +38,9 @@ func initAdmin(svcCtx *svc.ServiceContext) {
go func() {
ctx := context.Background()
var existing bool
for i := range 30 {
exists, err := svcCtx.UsersModelRW.Users.Query().Where(users.UsernameEQ(username)).Exist(ctx)
ok, err := svcCtx.UsersModelRW.Users.Query().Where(users.UsernameEQ(username)).Exist(ctx)
if err != nil {
if i < 29 {
time.Sleep(time.Second)
@@ -34,10 +49,23 @@ func initAdmin(svcCtx *svc.ServiceContext) {
logx.Errorf("check admin user: %v", err)
return
}
if exists {
existing = ok
break
}
if existing {
err := svcCtx.UsersModelRW.Users.Update().
Where(users.UsernameEQ(username)).
SetIsAdmin(true).
SetVerifiedRoles(adminVerifiedRoles).
SetVerificationStatus(adminVerificationStatus).
Exec(ctx)
if err != nil {
logx.Errorf("reconcile admin user: %v", err)
return
}
break
logx.Infof("reconciled admin user: %s", username)
return
}
hashedPassword, err := pwdUtils.HashPassword(password)
@@ -46,14 +74,8 @@ func initAdmin(svcCtx *svc.ServiceContext) {
return
}
resp, err := svcCtx.Snowflake.NextId(ctx, &snowflake.NextIdReq{})
if err != nil {
logx.Errorf("generate admin user ID: %v", err)
return
}
_, err = svcCtx.UsersModelRW.Users.Create().
SetID(resp.Id).
SetID(adminUserID).
SetUsername(username).
SetPasswordHash(hashedPassword).
SetEmail(email).
@@ -63,6 +85,8 @@ func initAdmin(svcCtx *svc.ServiceContext) {
SetCurrentRole("consumer").
SetNickname(username).
SetIsAdmin(true).
SetVerifiedRoles(adminVerifiedRoles).
SetVerificationStatus(adminVerificationStatus).
Save(ctx)
if err != nil {
logx.Errorf("create admin user: %v", err)
+5 -30
View File
@@ -6,46 +6,21 @@ Prometheus:
Port: 4001
Path: /metrics
DataSource: "${DB_URI}?sslmode=disable"
# ===== PROC CONFIG =====
#SnowflakeRpcConf:
# Target: k8s://juwan/snowflake-svc:8080
#
#DB:
# Master: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@user-db-rw.juwan:${DB_PORT}/${DB_NAME}?sslmode=disable"
# Slave: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@user-db-ro.juwan:${DB_PORT}/${DB_NAME}?sslmode=disable"
#
#CacheConf:
# - Host: "${REDIS_M_HOST}"
# Type: node
# Pass: "${REDIS_PASSWORD}"
# User: "default"
# - Host: "${REDIS_S_HOST}"
# Type: node
# Pass: "${REDIS_PASSWORD}"
# User: "default"
#
#Jwt:
# SecretKey: "${JWT_SECRET_KEY}"
# Issuer: "juwan-user-rpc"
# ===== DEV CONFIG =====
SnowflakeRpcConf:
Endpoints:
- snowflake:8080
- "${SNOWFLAKE_RPC_TARGET}"
DB:
Master: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@postgres:${DB_PORT}/${DB_NAME}?sslmode=disable"
Slave: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@postgres:${DB_PORT}/${DB_NAME}?sslmode=disable"
Master: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}?sslmode=disable"
Slave: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@${DB_HOST_RO}:${DB_PORT}/${DB_NAME}?sslmode=disable"
CacheConf:
- Host: "${REDIS_HOST}:${REDIS_PORT}"
Type: node
Pass: "${REDIS_PASSWORD}"
Jwt:
SecretKey: "MGUyMWE3ZDhjMTQ5ZDg1MWViOWU0MGM3OTE2NWVkYTBlOTE5ZWRkZDU1YjYzOGJjOWRiNzM0NTc4NDIyMjlkZQ"
SecretKey: "${JWT_SECRET_KEY}"
Issuer: "juwan-user-rpc"
Log:
-1
View File
@@ -12,7 +12,6 @@ type JwtConfig struct {
type Config struct {
zrpc.RpcServerConf
DataSource string `json:"dataSource"`
DB struct {
Master string
Slave string
+3 -10
View File
@@ -7,16 +7,9 @@ Prometheus:
Port: 4001
Path: /metrics
# ===== PROC CONF =====
#WalletRpcConf:
# Target: k8s://juwan/wallet-rpc:8080
# ===== DEV CONF =====
WalletRpcConf:
Endpoints:
- wallet-rpc:8080
# k8s://juwan/<service name>:8080
- "${WALLET_RPC_TARGET}"
Log:
Level: debug
+1 -1
View File
@@ -1,5 +1,5 @@
// Code generated by goctl. DO NOT EDIT.
// goctl 1.9.2
// goctl 1.10.1
package handler
+2 -2
View File
@@ -1,5 +1,5 @@
// Code generated by goctl. DO NOT EDIT.
// goctl 1.9.2
// goctl 1.10.1
package types
@@ -29,7 +29,7 @@ type TopupReq struct {
}
type Transaction struct {
Id int64 `json:"id"`
Id int64 `json:"id,string"`
Type string `json:"type"`
Amount string `json:"amount"`
Description string `json:"description"`
+1 -1
View File
@@ -22,7 +22,7 @@ func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
conf.MustLoad(*configFile, &c, conf.UseEnv())
server := rest.MustNewServer(c.RestConf)
server.Use(middlewares.NewRequestMiddleware().Handle)
+4 -36
View File
@@ -1,55 +1,23 @@
Name: pb.rpc
ListenOn: 0.0.0.0:8080
Prometheus:
Host: 0.0.0.0
Port: 4001
Path: /metrics
# tcd:
# Hosts:
# - 127.0.0.1:2379
# Key: pb.rpc
# Target: k8s://juwan/<service name>.<namespace>:8080
# ===== PROC CONF =====
#SnowflakeRpcConf:
# Target: k8s://juwan/snowflake-svc:8080
#
#
#DB:
# Master: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@user-db-rw.juwan:${DB_PORT}/${DB_NAME}?sslmode=disable"
# Slaves: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@user-db-ro.juwan:${DB_PORT}/${DB_NAME}?sslmode=disable"
#
#
#CacheConf:
# - Host: "${REDIS_M_HOST}"
# Type: node
# Pass: "${REDIS_PASSWORD}"
# User: "default"
# - Host: "${REDIS_S_HOST}"
# Type: node
# Pass: "${REDIS_PASSWORD}"
# User: "default"
#
#Log:
# Level: info
# ===== DEV CONFIG =====
SnowflakeRpcConf:
Endpoints:
- snowflake:8080
- "${SNOWFLAKE_RPC_TARGET}"
DB:
Master: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@postgres:${DB_PORT}/${DB_NAME}?sslmode=disable"
Slaves: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@postgres:${DB_PORT}/${DB_NAME}?sslmode=disable"
Master: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}?sslmode=disable"
Slaves: "postgresql://${PD_USERNAME}:${DB_PASSWORD}@${DB_HOST_RO}:${DB_PORT}/${DB_NAME}?sslmode=disable"
CacheConf:
- Host: "${REDIS_HOST}:${REDIS_PORT}"
Type: node
Pass: "${REDIS_PASSWORD}"
Log:
Level: debug
+29 -3
View File
@@ -3,18 +3,44 @@ PD_USERNAME=postgres
DB_PASSWORD=123456
DB_PORT=5432
DB_NAME=app
DB_URI=postgresql://postgres:123456@postgres:5432/app
DB_HOST=postgres
DB_HOST_RO=postgres
# Redis
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_M_HOST=redis:6379
REDIS_S_HOST=redis:6379
# Mongo (chat)
MONGO_URI=mongodb://mongo:27017
MONGO_DATABASE=juwan_chat
# Kafka
KAFKA_BROKER=kafka:9092
# JWT
JWT_SECRET_KEY=MGUyMWE3ZDhjMTQ5ZDg1MWViOWU0MGM3OTE2NWVkYTBlOTE5ZWRkZDU1YjYzOGJjOWRiNzM0NTc4NDIyMjlkZQ
# RPC targets (compose DNS)
SNOWFLAKE_RPC_TARGET=snowflake:8080
USER_RPC_TARGET=user-rpc:8080
USER_VERIFICATIONS_RPC_TARGET=user-verifications-rpc:8080
PLAYER_RPC_TARGET=player-rpc:8080
GAME_RPC_TARGET=game-rpc:8080
SHOP_RPC_TARGET=shop-rpc:8080
ORDER_RPC_TARGET=order-rpc:8080
WALLET_RPC_TARGET=wallet-rpc:8080
COMMUNITY_RPC_TARGET=community-rpc:8080
REVIEW_RPC_TARGET=review-rpc:8080
DISPUTE_RPC_TARGET=dispute-rpc:8080
NOTIFICATION_RPC_TARGET=notification-rpc:8080
SEARCH_RPC_TARGET=search-rpc:8080
OBJECTSTORY_RPC_TARGET=objectstory-rpc:8080
# Chat WebTransport TLS (dev self-signed; chat service reads OptionalValue with empty fallback)
CHAT_WT_CERT_FILE=/etc/certs/tls.crt
CHAT_WT_KEY_FILE=/etc/certs/tls.key
# Email (placeholder)
EMAIL_SMTP_HOST=smtp.example.com
EMAIL_SMTP_PORT=465
+35 -42
View File
@@ -4,63 +4,54 @@
- Docker(需要 buildx
- Python 3(构建脚本)
- Git(含 submodule:首次需 `git submodule update --init --recursive`
## 使用
```bash
cd deploy/dev
# 1. 构建所有微服务镜像
# 默认 8 并行,可通过环境变量 BAKE_BATCH_SIZE 调整
# 1. 构建所有镜像(默认 8 并行,可通过 BAKE_BATCH_SIZE 调整)
python3 build.py
# 2. 启动
docker compose up -d
# 3. 查看状态
docker compose ps
# 4. 通过网关访问
curl http://127.0.0.1:18080/healthz
# 5. 停止
docker compose down
# 3. 通过网关访问
open http://127.0.0.1:18080
```
构建脚本扫描 `app/` 下所有 `api``rpc``mq``adapter` 入口,通过 `docker buildx bake` 并行构建所有服务镜像,生成 `juwan/<service>-<type>:dev`
构建脚本扫描 `app/` 下所有 `api``rpc``mq``adapter` 入口`frontend/`,通过 `docker buildx bake` 并行构建所有镜像,生成 `juwan/<service>-<type>:dev``juwan/frontend:dev`
端到端接口测试走网关 `http://127.0.0.1:18080``18801-18814` 是各服务的直连端口,不经过认证链路
前端作为 submodule 接入 compose,通过 envoy 的同源 fallback 路由向浏览器提供,无需独立端口
Chat WebSocket 通过网关 `ws://127.0.0.1:18080/ws/chat` 访问。WebTransport 使用 `18443/udp``/wt/chat` 入口
如需只启动部分服务:
```bash
docker compose up -d postgres redis snowflake player-rpc player-api
```
Chat WebSocket 通过 `ws://127.0.0.1:18080/ws/chat` 访问。WebTransport 使用 `18443/udp``/wt/chat`
## 端口映射
| 服务 | 宿主机端口 |
| ---------------- | ---------------- |
| PostgreSQL | 15432 |
| Redis | 16379 |
| Kafka | 19092 |
| Envoy Gateway | 18080 |
| users-api | 18801 |
| player-api | 18802 |
| game-api | 18803 |
| shop-api | 18804 |
| order-api | 18805 |
| wallet-api | 18806 |
| community-api | 18807 |
| objectstory-api | 18808 |
| email-api | 18809 |
| chat-api | 18810, 18443/udp |
| review-api | 18811 |
| dispute-api | 18812 |
| notification-api | 18813 |
| search-api | 18814 |
| 服务 | 宿主机端口 | 说明 |
| ---------------- | ---------------- | ---------------------------------- |
| Envoy Gateway | 18080 | 浏览器入口,`/api/*``/ws/*`、前端静态都从这里出 |
| Redis | 16379 | 共享会话与验证码 |
| MongoDB | 27017 | chat 消息持久化 |
| Kafka | 19092 | email-mq 任务队列 |
| ratelimit | 18081, 16070 | 限流服务 |
| users-api | 18801 | 直连调试入口,不经认证链路 |
| player-api | 18802 | |
| game-api | 18803 | |
| shop-api | 18804 | |
| order-api | 18805 | |
| wallet-api | 18806 | |
| community-api | 18807 | |
| objectstory-api | 18808 | |
| email-api | 18809 | |
| chat-api | 18810, 18889, 18443/udp | |
| review-api | 18811 | |
| dispute-api | 18812 | |
| notification-api | 18813 | |
| search-api | 18814 | |
11 个 per-domain PostgreSQL`users-db``player-db``game-db``shop-db``order-db``wallet-db``community-db``review-db``dispute-db``notification-db``search-db`)和 `frontend` 容器不暴露宿主端口,仅在 compose 内网通过 DNS 互访。
## 环境变量
@@ -74,17 +65,19 @@ docker compose up -d postgres redis snowflake player-rpc player-api
| ADMIN_PASSWORD | 管理员密码 | admin123 |
| ADMIN_EMAIL | 管理员邮箱 | admin@juwan.dev |
默认 admin 固定 ID `100000`,拥有消费者、打手、店主三种身份全部开通,并预置了店铺、服务、钱包、帖子等演示数据,可直接用于完整链路联调。
## 认证
登录和注册通过 `users-api` 下发 `JToken` Cookie。`envoy-gateway` 负责 JWT 校验并注入认证头,`authz-adapter` 做会话态二次校验,后端服务只消费 `x-auth-user-id` 等头。
写接口需要先 `GET /healthz` 领取 `XSRF-TOKEN``XSRF-GUARD`,再在请求头带上 `xsrf-token`
写接口需要先 `GET /healthz` 领取 `__Host-XSRF-TOKEN``__Host-XSRF-GUARD` cookie,再在请求头带上 `xsrf-token`
注册和密码重置都需要先调验证码接口拿到 `requestId`,再把它放到 `X-Request-Id` 请求头里发后续请求。
## 数据库初始化
## 数据库初始化
首次启动时 PostgreSQL 会自动执行 `desc/sql/` 下的建表语句。如需重新初始化,删除 volume 后重启
每个 per-domain PostgreSQL 首次启动时,通过挂载 `desc/sql/<domain>/``deploy/dev/fixture/<domain>.sql` 自动完成建表与演示数据导入。如需完全重置
```bash
docker compose down -v
+14
View File
@@ -91,9 +91,23 @@ def discover_targets():
return targets
def frontend_target():
frontend_root = ROOT_DIR / "frontend"
if not (frontend_root / "Dockerfile").exists():
return None
return {
"context": "frontend",
"dockerfile": "Dockerfile",
"tags": [f"{IMAGE_PREFIX}/frontend:{IMAGE_TAG}"],
}
def main():
os.chdir(ROOT_DIR)
targets = discover_targets()
ft = frontend_target()
if ft is not None:
targets["frontend"] = ft
if not targets:
print("No targets found")
sys.exit(1)
+214 -30
View File
@@ -1,19 +1,10 @@
services:
# ==================== 基础设施 ====================
postgres:
x-postgres-base: &postgres-base
image: postgres:17-bookworm
container_name: juwan-postgres
restart: unless-stopped
environment:
environment: &postgres-env
POSTGRES_USER: ${PD_USERNAME}
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: ${DB_NAME}
ports:
- "15432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
- ./init-db.sh:/docker-entrypoint-initdb.d/init-db.sh:ro
- ../../desc/sql:/docker-entrypoint-initdb.d/sql:ro
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${PD_USERNAME} -d ${DB_NAME}"]
interval: 5s
@@ -21,6 +12,140 @@ services:
start_period: 30s
retries: 10
services:
# ==================== 基础设施 ====================
users-db:
<<: *postgres-base
container_name: juwan-users-db
environment:
<<: *postgres-env
POSTGRES_DOMAIN: users
volumes:
- users-pgdata:/var/lib/postgresql/data
- ./init-domain.sh:/docker-entrypoint-initdb.d/00-init-domain.sh:ro
- ../../desc/sql:/docker-entrypoint-initdb.d/sql:ro
- ./fixture:/docker-entrypoint-initdb.d/fixture:ro
game-db:
<<: *postgres-base
container_name: juwan-game-db
environment:
<<: *postgres-env
POSTGRES_DOMAIN: game
volumes:
- game-pgdata:/var/lib/postgresql/data
- ./init-domain.sh:/docker-entrypoint-initdb.d/00-init-domain.sh:ro
- ../../desc/sql:/docker-entrypoint-initdb.d/sql:ro
- ./fixture:/docker-entrypoint-initdb.d/fixture:ro
player-db:
<<: *postgres-base
container_name: juwan-player-db
environment:
<<: *postgres-env
POSTGRES_DOMAIN: player
volumes:
- player-pgdata:/var/lib/postgresql/data
- ./init-domain.sh:/docker-entrypoint-initdb.d/00-init-domain.sh:ro
- ../../desc/sql:/docker-entrypoint-initdb.d/sql:ro
- ./fixture:/docker-entrypoint-initdb.d/fixture:ro
shop-db:
<<: *postgres-base
container_name: juwan-shop-db
environment:
<<: *postgres-env
POSTGRES_DOMAIN: shop
volumes:
- shop-pgdata:/var/lib/postgresql/data
- ./init-domain.sh:/docker-entrypoint-initdb.d/00-init-domain.sh:ro
- ../../desc/sql:/docker-entrypoint-initdb.d/sql:ro
- ./fixture:/docker-entrypoint-initdb.d/fixture:ro
order-db:
<<: *postgres-base
container_name: juwan-order-db
environment:
<<: *postgres-env
POSTGRES_DOMAIN: order
volumes:
- order-pgdata:/var/lib/postgresql/data
- ./init-domain.sh:/docker-entrypoint-initdb.d/00-init-domain.sh:ro
- ../../desc/sql:/docker-entrypoint-initdb.d/sql:ro
- ./fixture:/docker-entrypoint-initdb.d/fixture:ro
wallet-db:
<<: *postgres-base
container_name: juwan-wallet-db
environment:
<<: *postgres-env
POSTGRES_DOMAIN: wallet
volumes:
- wallet-pgdata:/var/lib/postgresql/data
- ./init-domain.sh:/docker-entrypoint-initdb.d/00-init-domain.sh:ro
- ../../desc/sql:/docker-entrypoint-initdb.d/sql:ro
- ./fixture:/docker-entrypoint-initdb.d/fixture:ro
community-db:
<<: *postgres-base
container_name: juwan-community-db
environment:
<<: *postgres-env
POSTGRES_DOMAIN: community
volumes:
- community-pgdata:/var/lib/postgresql/data
- ./init-domain.sh:/docker-entrypoint-initdb.d/00-init-domain.sh:ro
- ../../desc/sql:/docker-entrypoint-initdb.d/sql:ro
- ./fixture:/docker-entrypoint-initdb.d/fixture:ro
review-db:
<<: *postgres-base
container_name: juwan-review-db
environment:
<<: *postgres-env
POSTGRES_DOMAIN: review
volumes:
- review-pgdata:/var/lib/postgresql/data
- ./init-domain.sh:/docker-entrypoint-initdb.d/00-init-domain.sh:ro
- ../../desc/sql:/docker-entrypoint-initdb.d/sql:ro
- ./fixture:/docker-entrypoint-initdb.d/fixture:ro
dispute-db:
<<: *postgres-base
container_name: juwan-dispute-db
environment:
<<: *postgres-env
POSTGRES_DOMAIN: dispute
volumes:
- dispute-pgdata:/var/lib/postgresql/data
- ./init-domain.sh:/docker-entrypoint-initdb.d/00-init-domain.sh:ro
- ../../desc/sql:/docker-entrypoint-initdb.d/sql:ro
- ./fixture:/docker-entrypoint-initdb.d/fixture:ro
notification-db:
<<: *postgres-base
container_name: juwan-notification-db
environment:
<<: *postgres-env
POSTGRES_DOMAIN: notification
volumes:
- notification-pgdata:/var/lib/postgresql/data
- ./init-domain.sh:/docker-entrypoint-initdb.d/00-init-domain.sh:ro
- ../../desc/sql:/docker-entrypoint-initdb.d/sql:ro
- ./fixture:/docker-entrypoint-initdb.d/fixture:ro
search-db:
<<: *postgres-base
container_name: juwan-search-db
environment:
<<: *postgres-env
POSTGRES_DOMAIN: search
volumes:
- search-pgdata:/var/lib/postgresql/data
- ./init-domain.sh:/docker-entrypoint-initdb.d/00-init-domain.sh:ro
- ../../desc/sql:/docker-entrypoint-initdb.d/sql:ro
- ./fixture:/docker-entrypoint-initdb.d/fixture:ro
redis:
image: redis:8-alpine
container_name: juwan-redis
@@ -166,6 +291,14 @@ services:
rl-redis:
condition: service_started
frontend:
image: juwan/frontend:dev
container_name: juwan-frontend
restart: unless-stopped
environment:
NODE_ENV: production
PORT: "3000"
# ==================== RPC 层 ====================
user-rpc:
image: juwan/users-rpc:dev
@@ -173,11 +306,13 @@ services:
restart: unless-stopped
env_file: .env
environment:
DB_HOST: users-db
DB_HOST_RO: users-db
ADMIN_USERNAME: ${ADMIN_USERNAME}
ADMIN_PASSWORD: ${ADMIN_PASSWORD}
ADMIN_EMAIL: ${ADMIN_EMAIL}
depends_on:
postgres:
users-db:
condition: service_healthy
redis:
condition: service_healthy
@@ -189,8 +324,11 @@ services:
container_name: juwan-user-verifications-rpc
restart: unless-stopped
env_file: .env
environment:
DB_HOST: users-db
DB_HOST_RO: users-db
depends_on:
postgres:
users-db:
condition: service_healthy
redis:
condition: service_healthy
@@ -204,8 +342,11 @@ services:
container_name: juwan-player-rpc
restart: unless-stopped
env_file: .env
environment:
DB_HOST: player-db
DB_HOST_RO: player-db
depends_on:
postgres:
player-db:
condition: service_healthy
redis:
condition: service_healthy
@@ -217,8 +358,11 @@ services:
container_name: juwan-game-rpc
restart: unless-stopped
env_file: .env
environment:
DB_HOST: game-db
DB_HOST_RO: game-db
depends_on:
postgres:
game-db:
condition: service_healthy
redis:
condition: service_healthy
@@ -230,8 +374,11 @@ services:
container_name: juwan-shop-rpc
restart: unless-stopped
env_file: .env
environment:
DB_HOST: shop-db
DB_HOST_RO: shop-db
depends_on:
postgres:
shop-db:
condition: service_healthy
redis:
condition: service_healthy
@@ -245,8 +392,11 @@ services:
container_name: juwan-order-rpc
restart: unless-stopped
env_file: .env
environment:
DB_HOST: order-db
DB_HOST_RO: order-db
depends_on:
postgres:
order-db:
condition: service_healthy
redis:
condition: service_healthy
@@ -258,8 +408,11 @@ services:
container_name: juwan-wallet-rpc
restart: unless-stopped
env_file: .env
environment:
DB_HOST: wallet-db
DB_HOST_RO: wallet-db
depends_on:
postgres:
wallet-db:
condition: service_healthy
redis:
condition: service_healthy
@@ -271,19 +424,17 @@ services:
container_name: juwan-community-rpc
restart: unless-stopped
env_file: .env
environment:
DB_HOST: community-db
DB_HOST_RO: community-db
depends_on:
postgres:
community-db:
condition: service_healthy
redis:
condition: service_healthy
snowflake:
condition: service_started
chat-rpc:
image: juwan/chat-rpc:dev
container_name: juwan-chat-rpc
restart: unless-stopped
objectstory-rpc:
image: juwan/objectstory-rpc:dev
container_name: juwan-objectstory-rpc
@@ -295,8 +446,11 @@ services:
container_name: juwan-review-rpc
restart: unless-stopped
env_file: .env
environment:
DB_HOST: review-db
DB_HOST_RO: review-db
depends_on:
postgres:
review-db:
condition: service_healthy
redis:
condition: service_healthy
@@ -308,8 +462,11 @@ services:
container_name: juwan-dispute-rpc
restart: unless-stopped
env_file: .env
environment:
DB_HOST: dispute-db
DB_HOST_RO: dispute-db
depends_on:
postgres:
dispute-db:
condition: service_healthy
redis:
condition: service_healthy
@@ -321,8 +478,11 @@ services:
container_name: juwan-notification-rpc
restart: unless-stopped
env_file: .env
environment:
DB_HOST: notification-db
DB_HOST_RO: notification-db
depends_on:
postgres:
notification-db:
condition: service_healthy
redis:
condition: service_healthy
@@ -334,8 +494,11 @@ services:
container_name: juwan-search-rpc
restart: unless-stopped
env_file: .env
environment:
DB_HOST: search-db
DB_HOST_RO: search-db
depends_on:
postgres:
search-db:
condition: service_healthy
redis:
condition: service_healthy
@@ -456,6 +619,7 @@ services:
image: juwan/chat-api:dev
container_name: juwan-chat-api
restart: unless-stopped
env_file: .env
ports:
- "18810:8888"
- "18889:8889"
@@ -531,8 +695,28 @@ services:
condition: service_healthy
volumes:
pgdata:
name: juwan-pgdata
users-pgdata:
name: juwan-users-pgdata
game-pgdata:
name: juwan-game-pgdata
player-pgdata:
name: juwan-player-pgdata
shop-pgdata:
name: juwan-shop-pgdata
order-pgdata:
name: juwan-order-pgdata
wallet-pgdata:
name: juwan-wallet-pgdata
community-pgdata:
name: juwan-community-pgdata
review-pgdata:
name: juwan-review-pgdata
dispute-pgdata:
name: juwan-dispute-pgdata
notification-pgdata:
name: juwan-notification-pgdata
search-pgdata:
name: juwan-search-pgdata
redisdata:
name: juwan-redisdata
mongodata:
+37 -4
View File
@@ -116,6 +116,15 @@ static_resources:
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
disabled: true
- match:
path: /api/v1/players/me
headers:
- name: ":method"
exact_match: GET
route:
cluster: player_api_cluster
timeout: 30s
- match:
prefix: /api/v1/players
headers:
@@ -418,10 +427,13 @@ static_resources:
- match:
prefix: /
direct_response:
status: 404
body:
inline_string: gateway route not found
route:
cluster: frontend_cluster
timeout: 30s
typed_per_filter_config:
envoy.filters.http.ext_authz:
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
disabled: true
access_log:
- name: envoy.access_loggers.stdout
@@ -616,6 +628,13 @@ static_resources:
headers:
- name: ":method"
exact_match: GET
- match:
path: /api/v1/players/me
headers:
- name: ":method"
exact_match: GET
requires:
provider_name: juwan_user_jwt
- match:
prefix: /api/v1/players
headers:
@@ -959,6 +978,20 @@ static_resources:
address: ratelimit
port_value: 8081
- name: frontend_cluster
connect_timeout: 2s
type: STRICT_DNS
lb_policy: ROUND_ROBIN
load_assignment:
cluster_name: frontend_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: frontend
port_value: 3000
admin:
access_log_path: /tmp/admin.log
address:
+71
View File
@@ -0,0 +1,71 @@
INSERT INTO posts (id, author_id, author_role, title, content, tags, like_count, comment_count) VALUES
(100000, 100000, 'admin', '新赛季代练活动上线:首单立减 20',
'新赛季开启,聚玩新一轮活动来了:
活动期间:
- 新用户首单立减 20 元,最低 19.9 起
- 老用户推荐新人,双方各得 30 元代金券
- 周末下单额外送一局陪玩体验
所有活动可在下单时自动生效,有任何问题可在社区留言或私信我。祝大家上分顺利!',
ARRAY['活动','公告','新赛季'], 20, 1),
(100001, 100001, 'player', '新赛季中单法师强度排行',
'新赛季中单法师的强度有了不少变化,这里给大家分析一下当前版本的T0-T2梯队。
T0: 阿狸、辛德拉
T1: 维克托、泽丽
T2: 佐伊、妮蔻
大家觉得还有哪些英雄被低估了?',
ARRAY['英雄联盟','攻略','中单'], 12, 3),
(100002, 100003, 'player', 'VALORANT 新地图攻略分享',
'新地图的几个关键烟点和架枪位分享给大家。决斗位在这张图上优势很大,特别是A点的peek角度。',
ARRAY['VALORANT','攻略','地图'], 8, 1),
(100003, 100004, 'owner', '星辰工作室招募公告',
'星辰代练工作室现招募以下段位打手:
- LOL 大师及以上
- 王者荣耀 王者50星以上
待遇优厚,佣金比例可谈。有意者私信联系。',
ARRAY['招募','工作室'], 5, 2),
(100004, 100006, 'consumer', '求推荐靠谱的LOL代练',
'钻石卡了好久上不去,想找个靠谱的代练帮忙上大师。有推荐的吗?最好是有保障的工作室。',
ARRAY['英雄联盟','求推荐'], 3, 2),
(100005, 100002, 'player', '上分心得:如何从钻石到大师',
'很多人卡在钻石上不去,其实关键在于几个点:
1. 英雄池不要太广,精通2-3个就够
2. 对线期不要浪,稳住发育
3. 多看小地图,把握团战时机
希望对大家有帮助!',
ARRAY['英雄联盟','心得','上分'], 15, 4);
INSERT INTO comments (id, post_id, author_id, content, like_count) VALUES
(100000, 100000, 100001, '这波活动真香,已经下单了', 5),
(100001, 100001, 100002, '阿狸确实强,ban率太高了', 3),
(100002, 100001, 100006, '维克托感觉也是T0级别的', 1),
(100003, 100001, 100003, '法师版本确实舒服', 2),
(100004, 100002, 100001, '新地图确实好玩,决斗位优势大', 1),
(100005, 100003, 100001, '有兴趣,已私信', 0),
(100006, 100003, 100002, '星辰工作室不错,之前合作过', 2),
(100007, 100004, 100001, '可以看看我的主页,钻石到大师是我的强项', 1),
(100008, 100004, 100004, '欢迎来星辰工作室了解,我们有专业的代练团队', 0),
(100009, 100005, 100006, '写得好,学到了', 2),
(100010, 100005, 100007, '确实,英雄池精了比广好', 1),
(100011, 100005, 100001, '补充一点:心态也很重要,连跪就休息', 3),
(100012, 100005, 100003, '赞同,我也是靠精通少数英雄上的不朽', 1);
INSERT INTO post_likes (id, post_id, user_id) VALUES
(109100, 100000, 100001),(109099, 100000, 100002),(109098, 100000, 100006),(109097, 100000, 100007),(109096, 100000, 100008),
(109101, 100001, 100002),(109102, 100001, 100003),(109103, 100001, 100006),(109104, 100001, 100007),
(109105, 100002, 100001),(109106, 100002, 100006),
(109107, 100003, 100001),(109108, 100003, 100002),
(109109, 100004, 100001),(109110, 100004, 100004),
(109111, 100005, 100001),(109112, 100005, 100003),(109113, 100005, 100006),(109114, 100005, 100007);
INSERT INTO comment_likes (id, comment_id, user_id) VALUES
(109201, 100001, 100001),(109202, 100001, 100006),(109203, 100001, 100007),
(109204, 100003, 100001),(109205, 100003, 100006),
(109206, 100006, 100001),(109207, 100006, 100004),
(109208, 100009, 100001),(109209, 100009, 100007),
(109210, 100011, 100002),(109211, 100011, 100006),(109212, 100011, 100007);
+9
View File
@@ -0,0 +1,9 @@
INSERT INTO games (id, name, icon, category, sort_order) VALUES
(100001, '英雄联盟', '🎮', 'MOBA', 1),
(100002, '王者荣耀', '👑', 'MOBA', 2),
(100003, 'VALORANT', '🔫', 'FPS', 3),
(100004, '永劫无间', '⚔️', 'ACT', 4),
(100005, '原神', '🌟', 'RPG', 5),
(100006, 'CS2', '💣', 'FPS', 6),
(100007, '绝地求生', '🪖', 'FPS', 7),
(100008, '和平精英', '🎯', 'FPS', 8);
+34
View File
@@ -0,0 +1,34 @@
INSERT INTO orders (id, consumer_id, player_id, shop_id, service_snapshot, status, total_price, note) VALUES
(100001, 100006, 100001, 100001,
'{"serviceId":100001,"title":"LOL 钻石上分","price":99.00,"unit":"段","gameName":"英雄联盟"}'::jsonb,
'completed', 99.00, '希望快点上分'),
(100002, 100006, 100002, 100001,
'{"serviceId":100004,"title":"LOL 大师冲击","price":199.00,"unit":"段","gameName":"英雄联盟"}'::jsonb,
'in_progress', 199.00, '大师冲宗师'),
(100003, 100007, 100003, 100002,
'{"serviceId":100006,"title":"VALORANT 不朽上分","price":150.00,"unit":"段","gameName":"VALORANT"}'::jsonb,
'pending_accept', 150.00, null),
(100004, 100007, 100001, 100001,
'{"serviceId":100002,"title":"LOL 排位陪玩","price":30.00,"unit":"局","gameName":"英雄联盟"}'::jsonb,
'completed', 30.00, '陪玩两局'),
(100005, 100006, 100004, 100003,
'{"serviceId":100010,"title":"原神深渊代打","price":80.00,"unit":"次","gameName":"原神"}'::jsonb,
'pending_payment', 80.00, '深渊满星');
INSERT INTO order_state_logs (id, order_id, from_status, to_status, action, actor_id, actor_role) VALUES
(100001, 100001, null, 'pending_payment', 'create', 100006, 'consumer'),
(100002, 100001, 'pending_payment', 'pending_accept', 'pay', 100006, 'consumer'),
(100003, 100001, 'pending_accept', 'in_progress', 'accept', 100001, 'player'),
(100004, 100001, 'in_progress', 'pending_close', 'finish', 100001, 'player'),
(100005, 100001, 'pending_close', 'completed', 'confirm', 100006, 'consumer'),
(100006, 100002, null, 'pending_payment', 'create', 100006, 'consumer'),
(100007, 100002, 'pending_payment', 'pending_accept', 'pay', 100006, 'consumer'),
(100008, 100002, 'pending_accept', 'in_progress', 'accept', 100002, 'player'),
(100009, 100003, null, 'pending_payment', 'create', 100007, 'consumer'),
(100010, 100003, 'pending_payment', 'pending_accept', 'pay', 100007, 'consumer'),
(100011, 100004, null, 'pending_payment', 'create', 100007, 'consumer'),
(100012, 100004, 'pending_payment', 'pending_accept', 'pay', 100007, 'consumer'),
(100013, 100004, 'pending_accept', 'in_progress', 'accept', 100001, 'player'),
(100014, 100004, 'in_progress', 'pending_close', 'finish', 100001, 'player'),
(100015, 100004, 'pending_close', 'completed', 'confirm', 100007, 'consumer'),
(100016, 100005, null, 'pending_payment', 'create', 100006, 'consumer');
+21
View File
@@ -0,0 +1,21 @@
INSERT INTO players (id, user_id, status, rating, total_orders, completed_orders, gender, tags, games, shop_id) VALUES
(100000, 100000, 'available', 5.00, 0, 0, false, ARRAY['LOL王者','VALORANT不朽','多游戏'], ARRAY[100001,100002,100003,100005]::bigint[], 100000),
(100001, 100001, 'available', 4.85, 128, 120, false, ARRAY['中单','法师','上分快'], ARRAY[100001,100002]::bigint[], 100001),
(100002, 100002, 'available', 4.72, 95, 88, true, ARRAY['上单','刺客','高端局'], ARRAY[100001]::bigint[], 100001),
(100003, 100003, 'busy', 4.90, 67, 65, false, ARRAY['决斗','不朽段位'], ARRAY[100003]::bigint[], 100002),
(100004, 100008, 'available', 4.60, 42, 38, true, ARRAY['全能','多游戏'], ARRAY[100001,100003,100005]::bigint[], 100003);
INSERT INTO player_services (id, player_id, game_id, title, description, price, unit, rank_range, availability) VALUES
(100001, 100001, 100001, 'LOL 钻石上分', '钻石到大师,稳定上分', 99.00, '', '钻石→大师', ARRAY['周一至周五','晚间']),
(100002, 100001, 100001, 'LOL 排位陪玩', '钻石段位陪玩,轻松愉快', 30.00, '', '钻石', ARRAY['全天']),
(100003, 100001, 100002, '王者荣耀 星耀上分', '星耀到王者,快速安全', 68.00, '', '星耀→王者', ARRAY['全天']),
(100004, 100002, 100001, 'LOL 大师冲击', '大师到宗师,高端局专精', 199.00, '', '大师→宗师', ARRAY['周末','晚间']),
(100005, 100002, 100001, 'LOL 定位赛代打', '10局定位赛,保底铂金', 50.00, '10局','定位赛', ARRAY['全天']),
(100006, 100003, 100003, 'VALORANT 不朽上分', '永恒到不朽,决斗位专精', 150.00, '', '永恒→不朽', ARRAY['晚间','周末']),
(100007, 100003, 100003, 'VALORANT 陪玩', '不朽段位陪玩,教学向', 40.00, '', '不朽', ARRAY['全天']),
(100008, 100004, 100001, 'LOL 黄金上分', '黄金到铂金,新手友好', 39.00, '', '黄金→铂金', ARRAY['全天']),
(100009, 100004, 100003, 'VALORANT 白银上分', '白银到黄金,基础教学', 45.00, '', '白银→黄金', ARRAY['全天']),
(100010, 100004, 100005, '原神深渊代打', '深渊12层满星', 80.00, '', '深渊12层', ARRAY['全天']),
(100011, 100000, 100001, 'LOL 全段位上分', '铂金到王者全段位接单,经验丰富', 88.00, '', '铂金→王者', ARRAY['全天']),
(100012, 100000, 100003, 'VALORANT 高端陪玩','不朽段位陪练,教学向,耐心稳重', 50.00, '', '不朽', ARRAY['晚间','周末']),
(100013, 100000, 100002, '王者荣耀 巅峰赛带飞','巅峰赛 2300+ 老玩家,稳定上分', 66.00, '', '星耀→王者', ARRAY['全天']);

Some files were not shown because too many files have changed in this diff Show More