Showing AI reviews for bet-service
Project AI Score ?
62%
Quality Avg ?
63%
Security Avg ?
59%
Reviews ?
92

Review Result ?

solutionbowl/bet-service · 82b90fab
The commit adds new bet and multi-bet APIs with a new P&L report flow, including role-based access controls and exposure calculations. The code is relatively clean and includes multiple role-based filters and grouping strategies, improving business functionality and security by limiting data appropriately. However, some duplication is present in role-based filter logic, and exposure calculation comments could be expanded for clarity. Error handling for invalid roles is included, but deeper validation and security checks could be further enhanced. There is moderate risk of bugs if roles or IDs are not handled properly.
Quality ?
85%
Security ?
75%
Business Value ?
90%
Maintainability ?
85%
Issues & Suggested Fixes ?
1AI detects the issue
2Shows file, line, or evidence
3Suggests the fix to apply
Duplicate Logic
Where src/controllers/bet.controller.js:794
Issue / Evidence duplicated role-based filtering logic
Suggested Fix refactor repeated switch-case blocks to a single reusable function to reduce bugs and improve maintainability
Exposure Calculation Logic
Where src/controllers/bet.controller.js:305-310
Issue / Evidence exposure calculation logic
Suggested Fix add more detailed comments and consider edge cases (e.g., invalid odds_type) to reduce bug risk
Exposure Calculation For Other Runners
Where src/controllers/bet.controller.js:319-320
Issue / Evidence exposure calculation for other runners
Suggested Fix validate inputs and add error handling to prevent unexpected behavior
Invalid Role Error Handling
Where src/controllers/bet.controller.js:836-839
Issue / Evidence invalid role error handling
Suggested Fix consider logging invalid access attempts for security monitoring
Route Additions
Where src/app.js:4-18
Issue / Evidence route additions
Suggested Fix confirm that routing is protected with appropriate authentication and authorization to prevent unauthorized access
More Descriptive Message
Where commit message
Issue / Evidence more descriptive message
Suggested Fix expand commit message to briefly describe main changes and any impacts to ease future reviews
Code Change Preview · src/app.js ?
Removed / Before Commit
- diff --git a/src/app.js b/src/app.js
- index bdf841f..eb51e52 100644
- const express = require("express");
- const cors = require("cors");
- const betRoutes = require("./routes/betRoutes");
- require("./config/db");
- const app = express();
- 
- 
- app.use(express.json());
- app.use("/api/bets", betRoutes);
- 
- module.exports = app;
Added / After Commit
+ diff --git a/src/app.js b/src/app.js
+ index bdf841f..eb51e52 100644
+ const express = require("express");
+ const cors = require("cors");
+ const betRoutes = require("./routes/betRoutes");
+ const exposureRoutes = require("./routes/exposureRoutes");
+ const multiBetRoutes = require("./routes/multiBetRoutes");
+ require("./config/db");
+ const app = express();
+ 
+ 
+ app.use(express.json());
+ app.use("/api/bets", betRoutes);
+ app.use("/api/bets", multiBetRoutes);
+ app.use("/api/exposure", exposureRoutes);
+ 
+ module.exports = app;
Removed / Before Commit
- diff --git a/src/controllers/bet.controller.js b/src/controllers/bet.controller.js
- index 1f1d557..3ec31d9 100644
- const Exposure = require("../models/Exposure");
- const { getTenantExposure, mergeExposure } = require("../utils/exposureHelper");
- const HorseRace = require("../models/HorseRace");
- 
- const recalculatePlacesExposure = async ({
- user_id,
- debitAmount = amount * Number(currentOdd);
- }
- 
- const bet = new Bet({
- user_id,
- tenant_id,
- event_id: raceId,
- bet_type: "racing",
- bet_amount: amount,
- let exposureChange = 0;
Added / After Commit
+ diff --git a/src/controllers/bet.controller.js b/src/controllers/bet.controller.js
+ index 1f1d557..3ec31d9 100644
+ const Exposure = require("../models/Exposure");
+ const { getTenantExposure, mergeExposure } = require("../utils/exposureHelper");
+ const HorseRace = require("../models/HorseRace");
+ const { getHierarchyIds } = require("../services/hierarchyService");
+ 
+ const recalculatePlacesExposure = async ({
+ user_id,
+ debitAmount = amount * Number(currentOdd);
+ }
+ 
+       // Get hierarchy IDs for fast P&L queries
+       const { super_admin_id, master_id } = await getHierarchyIds(user_id);
+ 
+ const bet = new Bet({
+ user_id,
+ tenant_id,
Removed / Before Commit
- diff --git a/src/controllers/exposureController.js b/src/controllers/exposureController.js
- new file mode 100644
- index 0000000..bfa2e6f
Added / After Commit
+ diff --git a/src/controllers/exposureController.js b/src/controllers/exposureController.js
+ new file mode 100644
+ index 0000000..bfa2e6f
+ const exposureService = require('../services/exposureService');
+ const balanceService = require('../services/balanceService');
+ 
+ /**
+  * Get exposure for logged-in user
+  * GET /api/exposure/summary
+  */
+ exports.getExposureSummary = async (req, res) => {
+   try {
+     const { userId, role, tenantId } = req.user;
+ 
+     // Get exposure data
+     const exposure = await exposureService.getExposureByRole(
+       userId,
+       role,
Removed / Before Commit
- diff --git a/src/controllers/multiBet.controller.js b/src/controllers/multiBet.controller.js
- new file mode 100644
- index 0000000..ec07976
Added / After Commit
+ diff --git a/src/controllers/multiBet.controller.js b/src/controllers/multiBet.controller.js
+ new file mode 100644
+ index 0000000..ec07976
+ const Bet = require("../models/Bet");
+ const RacingBet = require("../models/RacingBet");
+ const RaceMarket = require("../models/RaceMarket");
+ const HorseRace = require("../models/HorseRace");
+ const Ledger = require("../models/Ledger");
+ const Wallet = require("../models/Wallet");
+ const mongoose = require("mongoose");
+ const { getHierarchyIds } = require("../services/hierarchyService");
+ const { emitBalanceUpdate } = require("../utils/socketClient");
+ 
+ /**
+  * Place Multi-Bet (DOUBLE/TRIPLE)
+  * POST /api/bets/place-multi-bet
+  */
+ exports.placeMultiBet = async (req, res) => {
Removed / Before Commit
- diff --git a/src/models/Bet.js b/src/models/Bet.js
- index 8760a70..cdd0112 100644
- const BetSchema = new mongoose.Schema({
- user_id: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true },
- tenant_id: { type: mongoose.Schema.Types.ObjectId, required: true },
-   event_id: { type: mongoose.Schema.Types.ObjectId, required: true },
- bet_id: { type: String, default: uuidv4, unique: true },
- bet_type: {
- type: String,
- enum: ["ACCEPTED", "REJECTED", "WON", "LOSS", "CANCELLED", "VOID"],
- default: "ACCEPTED"
- },
-   ip_address: { type: String }
- }, { timestamps: true });
- 
- module.exports = mongoose.model("Bet", BetSchema);
Added / After Commit
+ diff --git a/src/models/Bet.js b/src/models/Bet.js
+ index 8760a70..cdd0112 100644
+ const BetSchema = new mongoose.Schema({
+ user_id: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true },
+ tenant_id: { type: mongoose.Schema.Types.ObjectId, required: true },
+ 
+   // Hierarchy IDs for fast P&L queries
+   super_admin_id: { type: mongoose.Schema.Types.ObjectId, ref: "User", default: null },
+   master_id: { type: mongoose.Schema.Types.ObjectId, ref: "User", default: null },
+ 
+   event_id: { type: mongoose.Schema.Types.ObjectId, required: false },  // Not required for multi-bet
+ bet_id: { type: String, default: uuidv4, unique: true },
+ bet_type: {
+ type: String,
+ enum: ["ACCEPTED", "REJECTED", "WON", "LOSS", "CANCELLED", "VOID"],
+ default: "ACCEPTED"
+ },
+   ip_address: { type: String },
Removed / Before Commit
- diff --git a/src/models/RacingBet.js b/src/models/RacingBet.js
- index 621d398..0d3ed69 100644
- const mongoose = require("mongoose");
- 
- const RacingBetSchema = new mongoose.Schema({
-   bet_id: { type: mongoose.Schema.Types.ObjectId, ref: "Bet", required: true, unique: true },
- event_id: { type: String, required: true },
- market_name: { type: String, required: true },
- market_odds_type: { type: String, required: true, default: "IndianManual" },
- market_id: { type: String, required: true },
- odds_request: { type: Number, required: true },
- odds_matched: { type: Number },
-   runner_id: { type: String }
- }, { timestamps: true });
- 
- module.exports = mongoose.model("RacingBet", RacingBetSchema);
Added / After Commit
+ diff --git a/src/models/RacingBet.js b/src/models/RacingBet.js
+ index 621d398..0d3ed69 100644
+ const mongoose = require("mongoose");
+ 
+ const RacingBetSchema = new mongoose.Schema({
+   bet_id: { type: mongoose.Schema.Types.ObjectId, ref: "Bet", required: true },
+ event_id: { type: String, required: true },
+ market_name: { type: String, required: true },
+ market_odds_type: { type: String, required: true, default: "IndianManual" },
+ market_id: { type: String, required: true },
+ odds_request: { type: Number, required: true },
+ odds_matched: { type: Number },
+   runner_id: { type: String },
+ 
+   // Multi-bet support
+   leg_number: { type: Number, default: 1 },
+   leg_status: {
+     type: String,
Removed / Before Commit
- diff --git a/src/routes/exposureRoutes.js b/src/routes/exposureRoutes.js
- new file mode 100644
- index 0000000..22c675b
Added / After Commit
+ diff --git a/src/routes/exposureRoutes.js b/src/routes/exposureRoutes.js
+ new file mode 100644
+ index 0000000..22c675b
+ const express = require('express');
+ const router = express.Router();
+ const exposureController = require('../controllers/exposureController');
+ const authMiddleware = require('../middleware/authMiddleware');
+ 
+ /**
+  * @route   GET /api/exposure/summary
+  * @desc    Get exposure summary based on user role
+  * @access  Private
+  */
+ router.get('/summary', authMiddleware, exposureController.getExposureSummary);
+ 
+ /**
+  * @route   GET /api/exposure/downline
+  * @desc    Get downline users with their exposure
Removed / Before Commit
- diff --git a/src/routes/multiBetRoutes.js b/src/routes/multiBetRoutes.js
- new file mode 100644
- index 0000000..8ff5dab
Added / After Commit
+ diff --git a/src/routes/multiBetRoutes.js b/src/routes/multiBetRoutes.js
+ new file mode 100644
+ index 0000000..8ff5dab
+ const express = require('express');
+ const router = express.Router();
+ const multiBetController = require('../controllers/multiBet.controller');
+ const authMiddleware = require('../middleware/authMiddleware');
+ const tenantMiddleware = require('../middleware/tenantMiddleware');
+ 
+ /**
+  * @route   POST /api/bets/place-multi-bet
+  * @desc    Place a multi-bet (DOUBLE/TRIPLE)
+  * @access  Private
+  */
+ router.post(
+   '/place-multi-bet',
+   authMiddleware,
+   tenantMiddleware,
Removed / Before Commit
- diff --git a/src/services/balanceService.js b/src/services/balanceService.js
- new file mode 100644
- index 0000000..0911546
Added / After Commit
+ diff --git a/src/services/balanceService.js b/src/services/balanceService.js
+ new file mode 100644
+ index 0000000..0911546
+ const Bet = require('../models/Bet');
+ const mongoose = require('mongoose');
+ 
+ /**
+  * Calculate Balance Down and Balance Up for a user
+  *
+  * Balance Down = Exposure on ACCEPTED/PENDING bets (money at risk)
+  * Balance Up = Profit/Loss from WON/LOST bets (settled P&L)
+  *
+  * @param {ObjectId} userId - User ID
+  * @param {String} userRole - User role (for hierarchy)
+  * @param {ObjectId} tenantId - Tenant ID
+  * @returns {Object} { balanceDown, balanceUp }
+  */
+ exports.calculateBalance = async (userId, userRole, tenantId) => {
Removed / Before Commit
- diff --git a/src/services/exposureService.js b/src/services/exposureService.js
- new file mode 100644
- index 0000000..83f6f73
Added / After Commit
+ diff --git a/src/services/exposureService.js b/src/services/exposureService.js
+ new file mode 100644
+ index 0000000..83f6f73
+ const User = require('../models/User');
+ const Bet = require('../models/Bet');
+ 
+ /**
+  * Get all downline user IDs recursively
+  * @param {ObjectId} userId - Parent user ID
+  * @returns {Array} Array of user IDs
+  */
+ async function getDownlineUserIds(userId) {
+   const downlineIds = [];
+ 
+   // Get direct children
+   const directChildren = await User.find({ parentId: userId }).select('_id');
+ 
+   for (const child of directChildren) {
Removed / Before Commit
- diff --git a/src/services/hierarchyService.js b/src/services/hierarchyService.js
- new file mode 100644
- index 0000000..32792fb
Added / After Commit
+ diff --git a/src/services/hierarchyService.js b/src/services/hierarchyService.js
+ new file mode 100644
+ index 0000000..32792fb
+ const User = require('../models/User');
+ 
+ /**
+  * Get hierarchy IDs (super_admin_id and master_id) for a given user
+  * This walks up the parentId chain to find SUPER_ADMIN and MASTER
+  *
+  * @param {ObjectId} userId - User ID who is placing bet
+  * @returns {Object} { super_admin_id, master_id }
+  */
+ exports.getHierarchyIds = async (userId) => {
+   let super_admin_id = null;
+   let master_id = null;
+ 
+   // Get user details
+   const user = await User.findById(userId).select('role parentId').lean();