Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
F
fc-minigame
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
陈冲
fc-minigame
Commits
a1fb5c7a
Commit
a1fb5c7a
authored
Jun 22, 2026
by
陈冲
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 已上榜玩家当天重玩已上榜的游戏不再显示
parent
aa23cfba
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
121 additions
and
13 deletions
+121
-13
game.sql
backend/deploy/game.sql
+9
-0
game_mysql.sql
backend/deploy/game_mysql.sql
+9
-0
game.rs
backend/src/db/models/game.rs
+11
-5
mod.rs
backend/src/db/models/mod.rs
+1
-0
room.rs
backend/src/db/models/room.rs
+37
-0
socket_io.rs
backend/src/protocols/socket_io.rs
+54
-8
No files found.
backend/deploy/game.sql
View file @
a1fb5c7a
drop
table
if
exists
tb_room
;
create
table
tb_room
(
batch_no
VARCHAR
(
50
)
PRIMARY
KEY
,
-- 批次
item_num
int
not
null
check
(
item_num
between
1
and
6
),
-- 游戏项目(1到6)
player_count
int
not
null
default
0
,
-- 房间人数
create_date
timestamptz
-- 创建时间
);
drop
table
if
exists
tb_game
;
create
table
tb_game
(
...
...
backend/deploy/game_mysql.sql
View file @
a1fb5c7a
DROP
TABLE
IF
EXISTS
tb_room
;
CREATE
TABLE
tb_room
(
batch_no
VARCHAR
(
50
)
PRIMARY
KEY
,
item_num
INT
NOT
NULL
,
player_count
INT
NOT
NULL
DEFAULT
0
,
create_date
DATETIME
);
DROP
TABLE
IF
EXISTS
tb_game
;
CREATE
TABLE
tb_game
(
...
...
backend/src/db/models/game.rs
View file @
a1fb5c7a
...
...
@@ -2,7 +2,7 @@ use crate::{
commons
::
utils
::
CONFIG
as
config
,
db
::
models
::{
get_i32
,
get_string
,
log
::
Log
},
};
use
chrono
::{
DateTime
,
NaiveDate
,
Utc
};
use
chrono
::{
DateTime
,
Utc
};
use
serde_json
::
json
;
pub
struct
Game
{
...
...
@@ -65,16 +65,22 @@ impl Game {
}
}
}
pub
async
fn
get_rank
(
wechat_id
:
&
str
)
->
i32
{
pub
async
fn
get_rank
(
wechat_id
:
&
str
,
item_num
:
i32
)
->
i32
{
let
Some
(
_
)
=
&
config
.db
else
{
println!
(
"没有配置数据库, get_rank return 0;"
);
return
0
;
};
let
res
=
crate
::
db
::
db_query
(
r
#
"SELECT COUNT(1) AS rank_count FROM tb_game WHERE wechat_id = ?
AND game_rank <= 10"
#
,
vec!
[
json!
(
&
wechat_id
)],
r
#
"SELECT COUNT(1) AS rank_count
FROM tb_game g
INNER JOIN tb_room r ON r.batch_no = g.batch_no
WHERE g.wechat_id = ?
AND g.item_num = ?
AND r.item_num = ?
AND g.game_rank BETWEEN 1 AND 10
AND DATE(r.create_date) = CURRENT_DATE"
#
,
vec!
[
json!
(
&
wechat_id
),
json!
(
item_num
),
json!
(
item_num
)],
)
.await
;
match
res
{
...
...
backend/src/db/models/mod.rs
View file @
a1fb5c7a
...
...
@@ -3,6 +3,7 @@ use serde_json::Value;
pub
mod
game
;
pub
mod
log
;
pub
mod
manager
;
pub
mod
room
;
pub
fn
get_i32
(
row
:
&
Value
,
key
:
&
str
)
->
Result
<
i32
,
String
>
{
row
.get
(
key
)
...
...
backend/src/db/models/room.rs
0 → 100644
View file @
a1fb5c7a
use
crate
::
commons
::
utils
::
CONFIG
as
config
;
use
chrono
::{
DateTime
,
Utc
};
use
serde_json
::
json
;
pub
struct
GameRoom
{
pub
batch_no
:
String
,
pub
item_num
:
i32
,
pub
player_count
:
i32
,
pub
create_date
:
Option
<
DateTime
<
Utc
>>
,
}
impl
GameRoom
{
pub
async
fn
insert
(
&
self
)
->
bool
{
let
Some
(
_
)
=
&
config
.db
else
{
println!
(
"没有配置数据库, room insert return true;"
);
return
true
;
};
let
res
=
crate
::
db
::
db_query
(
r
#
"insert into tb_room(batch_no,item_num,player_count,create_date) values(?,?,?,now());"
#
,
vec!
[
json!
(
&
self
.batch_no
),
json!
(
&
self
.item_num
),
json!
(
&
self
.player_count
),
],
)
.await
;
match
res
{
Ok
(
_
)
=>
true
,
Err
(
err
)
=>
{
println!
(
"room insert failed: {err}"
);
false
}
}
}
}
backend/src/protocols/socket_io.rs
View file @
a1fb5c7a
...
...
@@ -2,6 +2,7 @@ use crate::commons::utils::{self, CONFIG as config};
use
crate
::
db
::
models
::
game
::
Game
;
use
crate
::
db
::
models
::
log
::
Log
;
use
crate
::
db
::
models
::
manager
::
*
;
use
crate
::
db
::
models
::
room
::
GameRoom
;
use
bytes
::
Bytes
;
use
dashmap
::
DashMap
;
use
dashmap
::
mapref
::
entry
::
Entry
;
...
...
@@ -95,6 +96,7 @@ struct RoomState {
players
:
HashMap
<
String
,
RoomPlayer
>
,
// 玩家列表,key 是 userid
next_entry_order
:
usize
,
current_game_id
:
Option
<
String
>
,
current_item_num
:
i32
,
batch_no
:
String
,
round_started_at
:
Option
<
Instant
>
,
}
...
...
@@ -262,6 +264,8 @@ impl RoomState {
self
.players
.clear
();
self
.next_entry_order
=
0
;
self
.current_game_id
=
None
;
self
.current_item_num
=
0
;
self
.batch_no
.clear
();
self
.round_started_at
=
None
;
}
}
...
...
@@ -320,6 +324,15 @@ fn valid_user(userid: &str, validuser: &str) -> bool {
}
}
fn
parse_item_num
(
game_id
:
&
str
)
->
i32
{
game_id
.trim
()
.strip_prefix
(
"game"
)
.unwrap_or_else
(||
game_id
.trim
())
.parse
::
<
i32
>
()
.unwrap_or
(
0
)
}
fn
decode_msgpack
(
data
:
&
Data
<
Bytes
>
)
->
Result
<
Value
,
rmp_serde
::
decode
::
Error
>
{
rmp_serde
::
from_slice
(
data
.
0
.as_ref
())
}
...
...
@@ -677,6 +690,7 @@ async fn handle_room_command(
}
state
.reset
();
state
.current_game_id
=
Some
(
game_id
);
state
.current_item_num
=
index
as
i32
;
state
.status
=
RoomStatus
::
Loading
;
// let snapshot = state.snapshot();
drop
(
state
);
...
...
@@ -712,10 +726,8 @@ async fn handle_room_command(
.and_then
(|
value
|
value
.as_str
())
.unwrap_or
(
""
)
.to_string
();
let
already_top_ranked
=
CLIENT_TOP_RANKED_MAP
.get
(
userid
)
.map
(|
value
|
*
value
)
.unwrap_or
(
false
);
let
already_top_ranked
=
Game
::
get_rank
(
userid
,
parse_item_num
(
&
gameId
))
.await
>
0
;
CLIENT_TOP_RANKED_MAP
.insert
(
userid
.to_string
(),
already_top_ranked
);
if
let
Ok
(
mut
state
)
=
ROOM_STATE
.lock
()
{
// 当前h5进入的不是大屏开启的游戏房间
...
...
@@ -809,7 +821,7 @@ async fn handle_room_command(
return
;
}
let
game_index
=
data
.as_str
()
.unwrap_or
(
""
);
if
let
Ok
(
mut
state
)
=
ROOM_STATE
.lock
()
{
let
start_info
=
if
let
Ok
(
mut
state
)
=
ROOM_STATE
.lock
()
{
if
state
.players
.is_empty
()
{
drop
(
state
);
emit_msgpack
(
&
socket
,
evt
,
&
err_resp
(
"房间中没有玩家"
,
Some
(
json!
(
1
))));
...
...
@@ -823,16 +835,46 @@ async fn handle_room_command(
state
.status
=
RoomStatus
::
Running
;
state
.current_game_id
=
Some
(
game_index
.to_string
());
if
state
.current_item_num
<=
0
{
state
.current_item_num
=
parse_item_num
(
game_index
);
}
state
.round_started_at
=
Some
(
Instant
::
now
());
let
round_id
=
GAME_ROUND_ID
.fetch_add
(
1
,
Ordering
::
SeqCst
)
+
1
;
// let snapshot = state.snapshot();
let
play_len
=
state
.players
.len
();
let
item_num
=
state
.current_item_num
;
//每次开始房间时记录当前批次
let
batch_no
=
format!
(
"{}-{}"
,
utils
::
get_current_timestamp
(),
play_len
);
state
.batch_no
=
batch_no
.clone
();
drop
(
state
);
Some
((
round_id
,
batch_no
,
item_num
,
play_len
))
//通知所有玩家倒计时60秒,实际上还有3秒倒计时,以前2秒统计结果,所以是65
}
else
{
None
};
let
Some
((
round_id
,
batch_no
,
item_num
,
play_len
))
=
start_info
else
{
return
;
};
{
let
room
=
GameRoom
{
batch_no
:
batch_no
.clone
(),
item_num
,
player_count
:
play_len
as
i32
,
create_date
:
None
,
};
if
!
room
.insert
()
.await
{
if
let
Ok
(
mut
state
)
=
ROOM_STATE
.lock
()
{
if
state
.batch_no
==
batch_no
{
state
.status
=
RoomStatus
::
Loading
;
state
.batch_no
.clear
();
state
.round_started_at
=
None
;
}
}
emit_msgpack
(
&
socket
,
evt
,
&
err_resp
(
"房间批次保存失败"
,
None
));
return
;
}
spawn_game_timeout
(
round_id
,
GAME_START_COUNTDOWN_SECS
+
GAME_RUNNING_SECS
+
GAME_RESULT_WAIT_SECS
,
...
...
@@ -989,6 +1031,11 @@ async fn handle_room_command(
if
game
.insert
()
.await
{
if
!
already_top_ranked
&&
(
1
..=
ROOM_SCORE_RANK_LIMIT
as
i64
)
.contains
(
&
rank
)
{
CLIENT_TOP_RANKED_MAP
.insert
(
userid
.to_string
(),
true
);
if
let
Ok
(
mut
state
)
=
ROOM_STATE
.lock
()
{
if
let
Some
(
player
)
=
state
.players
.get_mut
(
userid
)
{
player
.already_top_ranked
=
true
;
}
}
}
let
mut
payload
=
json!
({
...
...
@@ -1126,8 +1173,7 @@ pub async fn on_connect(_io: SocketIo, socket: SocketRef, Data(data): Data<Value
}
}
}
else
{
let
already_top_ranked
=
Game
::
get_rank
(
&
userid
)
.await
>
0
;
CLIENT_TOP_RANKED_MAP
.insert
(
userid
.clone
(),
already_top_ranked
);
CLIENT_TOP_RANKED_MAP
.insert
(
userid
.clone
(),
false
);
if
get_admin_count
()
==
0
{
match
CLIENT_PLAYER_MAP
.entry
(
userid
.clone
())
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment