package modules import ( "context" "database/sql" "encoding/json" "github.com/heroiclabs/nakama-common/runtime" ) // RegisterDisciple 注册弟子/领地相关 RPC。 func RegisterDisciple(initializer runtime.Initializer) error { if err := initializer.RegisterRpc("DiscipleService/DispatchDisciple", dispatchDisciple); err != nil { return err } if err := initializer.RegisterRpc("DiscipleService/ClaimMissionOutput", claimMissionOutput); err != nil { return err } if err := initializer.RegisterRpc("DiscipleService/BidTerritory", bidTerritory); err != nil { return err } return nil } type dispatchReq struct { DiscipleID string `json:"disciple_id"` TargetType string `json:"target_type"` TargetID string `json:"target_id"` DurationMinutes int32 `json:"duration_minutes"` InsuranceItemID string `json:"insurance_item_id"` } type missionIDReq struct { MissionID string `json:"mission_id"` } type territoryBidReq struct { GuildID string `json:"guild_id"` TerritoryID string `json:"territory_id"` BidAmount string `json:"bid_amount"` IsRenewal bool `json:"is_renewal"` } type discipleMissionData struct { MissionID string `json:"mission_id"` DiscipleID string `json:"disciple_id"` TargetType string `json:"target_type"` StartedAt string `json:"started_at"` EndedAt string `json:"ended_at"` Status string `json:"status"` } type missionRewardData struct { MissionID string `json:"mission_id"` Result string `json:"result"` Rewards interface{} `json:"rewards"` } type territoryBidData struct { BidID string `json:"bid_id"` TerritoryID string `json:"territory_id"` BidAmount string `json:"bid_amount"` DepositLocked string `json:"deposit_locked"` WindowEndAt string `json:"window_end_at"` } func dispatchDisciple(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, payload string) (string, error) { traceID := newTraceID() uid := userIDFromCtx(ctx) if uid == "" { return errResp(1001, "missing token", traceID) } var req dispatchReq if err := json.Unmarshal([]byte(payload), &req); err != nil { return errResp(9001, "invalid payload", traceID) } // 获取角色ID var charID string err := hhdbPool.QueryRow(ctx, ` SELECT id FROM characters WHERE player_id = $1 AND status = 'active' ORDER BY created_at DESC LIMIT 1 `, uid).Scan(&charID) if err != nil { return errResp(4002, "character not found", traceID) } // 检查弟子是否存在且属于该角色 var discipleStatus string var discipleQuality string err = hhdbPool.QueryRow(ctx, ` SELECT status, quality FROM disciples WHERE id = $1 AND owner_id = $2 `, req.DiscipleID, charID).Scan(&discipleStatus, &discipleQuality) if err != nil { return errResp(9002, "disciple not found", traceID) } if discipleStatus != "active" { return errResp(9003, "disciple not active", traceID) } // 检查弟子是否已在任务中 var activeMissions int32 err = hhdbPool.QueryRow(ctx, ` SELECT COUNT(*) FROM disciple_missions WHERE disciple_id = $1 AND status IN ('pending', 'active') `, req.DiscipleID).Scan(&activeMissions) if err == nil && activeMissions > 0 { return errResp(9004, "disciple already on mission", traceID) } // 创建任务 var missionID string err = hhdbPool.QueryRow(ctx, ` INSERT INTO disciple_missions (disciple_id, owner_id, mission_type, difficulty, success_rate, death_rate, status) VALUES ($1, $2, $3, 1, 0.8, 0.03, 'pending') RETURNING id::text `, req.DiscipleID, charID, req.TargetType).Scan(&missionID) if err != nil { logger.Error("create disciple mission failed: %v", err) return errResp(9002, "internal error", traceID) } logger.Info("DiscipleService/DispatchDisciple success: mission_id=%s disciple=%s", missionID, req.DiscipleID) return okResp(discipleMissionData{ MissionID: missionID, DiscipleID: req.DiscipleID, TargetType: req.TargetType, Status: "dispatched", }, traceID) } func claimMissionOutput(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, payload string) (string, error) { traceID := newTraceID() var req missionIDReq if err := json.Unmarshal([]byte(payload), &req); err != nil { return errResp(9006, "invalid payload", traceID) } // 查询任务信息 var missionStatus string var ownerID string err := hhdbPool.QueryRow(ctx, ` SELECT status, owner_id FROM disciple_missions WHERE id = $1 `, req.MissionID).Scan(&missionStatus, &ownerID) if err != nil { return errResp(9007, "mission not found", traceID) } if missionStatus != "completed" { return errResp(9008, "mission not completed", traceID) } // 结算奖励(简化版) rewards := map[string]interface{}{ "spirit_stones": 100, "materials": []string{"草药", "矿石"}, } logger.Info("DiscipleService/ClaimMissionOutput success: mission_id=%s", req.MissionID) return okResp(missionRewardData{ MissionID: req.MissionID, Result: "success", Rewards: rewards, }, traceID) } func bidTerritory(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, payload string) (string, error) { traceID := newTraceID() uid := userIDFromCtx(ctx) if uid == "" { return errResp(1001, "missing token", traceID) } var req territoryBidReq if err := json.Unmarshal([]byte(payload), &req); err != nil { return errResp(9009, "invalid payload", traceID) } // 获取帮派ID var charID string var guildID string err := hhdbPool.QueryRow(ctx, ` SELECT c.id, gm.guild_id FROM characters c JOIN guild_members gm ON c.id = gm.character_id WHERE c.player_id = $1 AND c.status = 'active' AND gm.role = 'leader' ORDER BY c.created_at DESC LIMIT 1 `, uid).Scan(&charID, &guildID) if err != nil { return errResp(7003, "character not found or not leader", traceID) } // 校验竞标窗口期(简化版:假设当前在窗口期内) // 实际实现应检查竞标周期 logger.Info("DiscipleService/BidTerritory success: guild=%s territory=%s", guildID, req.TerritoryID) return okResp(territoryBidData{ BidID: genUUID(), TerritoryID: req.TerritoryID, BidAmount: req.BidAmount, }, traceID) }