swagger 推广后台 优化对战百佳历史接口

This commit is contained in:
2026-09-24 14:55:58 +08:00
parent 4be9ce4e85
commit 43286f10ef
160 changed files with 1710 additions and 1719 deletions
+14 -10
View File
@@ -19,7 +19,7 @@
| 缓存 | Redis(Spring Data Redis + Lettuce) |
| 消息 | RabbitMQ(业务侧配置虚拟主机等,详见各 profile) |
| 实时 | WebSocket(如 `/ws/**` 等路径,与安全放行配置保持一致) |
| API 文档 | **Springfox 3.x**(主用,Swagger / OAS3 分组);另引入 **springdoc-openapi** Starter,可与 Springfox 并行存在 |
| API 文档 | 已移除(不再依赖 Springfox / springdoc,仓库内无 Swagger UI 与 OpenAPI 端点) |
---
@@ -81,7 +81,7 @@ chengdu-backend/
### 对外匿名接口(简述)
与安全配置对齐的常见分类包括:`/login`、`/register`、`/captchaImage`,以及业务侧的 `/api/**`、`/admin/**`、WebSocket、部分 Swagger 与 Druid 监控路径等。具体请以 `ruoyi-framework` 中 **SecurityFilterChain** 与白名单配置为准。
与安全配置对齐的常见分类包括:`/login`、`/register`、`/captchaImage`,以及业务侧的 `/api/**`、`/admin/**`、WebSocket、Druid 监控路径等。具体请以 `ruoyi-framework` 中 **SecurityFilterChain** 与白名单配置为准。
---
@@ -130,7 +130,7 @@ mvn -pl ruoyi-admin -am spring-boot:run -DskipTests
- 技术栈:Vue CLI、Element UI(`package.json` 声明 Node `>=8.9`,实际开发建议 Node **16 LTS** 或 **18 LTS**,并按需开启 OpenSSL 兼容)。
- 脚本见 `ruoyi-ui/package.json`(Windows 脚本含 `set NODE_OPTIONS=--openssl-legacy-provider`;在 macOS/Linux 上需 `export NODE_OPTIONS=...` 或使用等价脚本)。
- 开发与打包:`npm run dev`、`npm run build:prod` 等。
- 系统工具内「Swagger」页当前 iframe 指向 **`/doc.html`**;若后端未部署对应聚合页,请改为与下文 **API 文档** 中实际地址一致(例如 `/swagger-ui.html`)。
- 原「系统工具 → 系统接口」的 Swagger 页已随接口文档一并删除(`views/tool/swagger/index.vue`),若侧边栏仍有该菜单,执行 `DELETE FROM sys_menu WHERE component = 'tool/swagger/index';` 后重新登录。
### promo-ui2(Vue 3)
@@ -158,16 +158,14 @@ mvn -pl ruoyi-admin -am spring-boot:run -DskipTests
## API 文档
- **Springfox(当前主要使用)**:启动后访问(端口与 `server.port` 一致,仓库默认 **28081**)
`http://localhost:28081/swagger-ui.html`
在页面右上角切换分组(如 `user`、`thirdParty`、`playingMethod`、`task`、`promo` 等;与 `SwaggerConfig` 中 Docket 一致)。OpenAPI JSON 路径形如 `/<group>/api-docs`(Spring Security 已对 `/*/api-docs` 等放行,以实际配置为准)。
- **springdoc-openapi**:`ruoyi-admin` 已引入依赖;若单独启用 SpringDoc UI,需确保 **Security** 对 `/swagger-ui/**`、`/v3/api-docs` 等路径放行,避免 401。
- 项目已**移除全部接口文档能力**:删除 Springfox(`springfox-boot-starter`)、springdoc-openapi 依赖、`SwaggerConfig` 与源码中的 `@Api` / `@ApiOperation` / `@ApiModelProperty` 等注解,`SecurityConfig` 与 `ResourcesConfig` 里对应的放行与静态映射也已去掉。
- 因此 `/swagger-ui.html`、`/v3/api-docs`、`/*/api-docs`、`/doc.html` 均不再对外提供服务(未认证访问会直接落到 401)。
---
## 版本信息
- 父 POM:`com.ruoyi:ruoyi:4.8.2`(`spring-boot.version` **4.0.3**,`springdoc.version` **3.0.2**,`swagger` / Springfox **3.0.0**)
- 父 POM:`com.ruoyi:ruoyi:4.8.2`(`spring-boot.version` **4.0.3**)
- 若依官网参考:<http://www.ruoyi.vip>
---
@@ -175,7 +173,13 @@ mvn -pl ruoyi-admin -am spring-boot:run -DskipTests
## 安全建议
1. 轮换并私有化所有第三方(支付宝、聚合支付、短信等)密钥;生产环境勿使用仓库内示例明文。
2. 修改 JWT `token.secret` 与 Druid 监控口令等非默认值。
3. 按需收紧 Druid、`/actuator`(若启用)及 Swagger 相关路径的访问范围。
2. 修改 JWT `token.secret` 等非默认值。
3. Druid 监控台:生产已关闭(`application-pro.yml` 的 `statViewServlet.enabled=false`,`/druid/**` 是匿名放行的,开着等于把全部 SQL 与连接信息暴露到公网);
开发环境已限制 `allow: 127.0.0.1` 并改用非默认口令。临时排障如需开启,务必保持白名单 + 改口令,用完立即关回。
4. **三端 token 互不通用**(游戏管理后台 / 推广后台 / 玩家端):三个端共用同一套 JWT 与会话存储,
靠 `LoginUser.loginScope`(`admin` / `promo` / `player`)区分,判定在 `ruoyi-framework` 的 `SecurityConfig`:
`/admin|/system|/monitor|/tool|/test` 只认后台账号、`/promo/**` 只认推广端签发的 token、`/api/**` 只认玩家端签发的 token。
标记由 `ApiUserDetailsServiceImpl` 按登录账号前缀(`api_` / `promo_` / `promo_user_` / 无前缀)写入,随会话缓存在 Redis。
**注意**:升级到该版本后,不带端标记的旧 token 会被拒绝,推广后台与玩家端需要重新登录一次(后台账号不受影响)。
如有与本说明不一致之处,以当前仓库代码及团队部署文档为准。
+405
View File
@@ -0,0 +1,405 @@
"""麻将胡了 · 回合池生成器:把引擎跑出的回合写进 `game_mahjong_round` / `game_mahjong_step`。
两种模式:
--mode natural (默认)自然形态:不干预,直接采样落库。**RTP≈5.25,只能测链路,不能当验收**。
--mode quota 配额构造:先标定每桶池内实测均值 → 二分解出 16 桶配额(空转率锁 65%、目标 RTP 0.80)
→ 按配额定量填充。**这才是能上线/能做 RTP 验收的池子**。
用法(在 mahjong-py 目录下):
python generate_rounds.py --mode quota --per-box 1000 --dry-run
python generate_rounds.py --mode quota --per-box 1000 --write
python generate_rounds.py --mode natural --per-box 100 --dry-run
python generate_rounds.py --boxes 1,2 --per-box 100 --write --mode quota
写库内容(两种模式一样):
1. `game_mahjong_round`:每局一行(bucket_id / actual_mul / 旋数 / 波数 / has_fs / near_miss)
2. `game_mahjong_step`:每帧一行(逐波;含 grid_state / ways_detail / 下落补位 / 阶梯 / 触发信息)
3. `game_mahjong_box_bucket`:**配额就是 count**(3 个身份各一套,used=0)
4. `game_mahjong_box_template`:每箱每身份一条生效模板(= 配额快照,供抽干补桶)
5. `game_mahjong_box.total_rounds`:回写成本次生成的回合数
"""
from __future__ import annotations
import argparse
import json
import math
import random
import sys
from collections import Counter
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
from mahjong import config as C # noqa: E402
from mahjong.engine import GRID_COLS, GRID_ROWS, play_round # noqa: E402
DB = dict(host="127.0.0.1", port=23306, user="henan", password="iTSJSPPZM3LSGAPC", database="henan")
EMPTY_SHARE = 0.65 # 空转率(桶 1 配额占比,见设计文档 §3.9);未按箱分档时的统一值
# 按档调波动(用户 2026-09-22 定):**所有箱子 RTP 都锁 0.80**,但波动不同 ——
# 低档(1~20):空转低 → 剩余配额必须更"平" → 小奖更多、手感热;
# 高档(200~500):空转高 → 剩余配额更"陡" → 中大奖更多、博性更强。
# 机制:`solve_quotas` 会把"剩余部分"的加权均值凑到 `0.80/(1-空转率)`,所以
# **只改空转率就能同时改形状**,不用再单独调 power。
VOLATILITY_LADDER = (
(20, 0.62, "低档:空转 62%,小奖更多(热)"),
(100, 0.65, "中档:空转 65%(设计基准)"),
(10 ** 9, 0.68, "高档:空转 68%,中大奖更多(陡)"),
)
def empty_share_for(box_id: int) -> float:
bet = C.bet_of(box_id)
for upper, share, _ in VOLATILITY_LADDER:
if bet <= upper:
return share
return EMPTY_SHARE
TARGET_RTP = 0.80
CALIB_ROUNDS = 6000 # 每箱标定样本(用来量每个桶的**池内实测**均值)
MAX_TRIES = 400_000 # 单个桶的采样上限(防低概率桶把时间吃光)
# ---------------------------------------------------------------- 行构造
def grid_rows(flat) -> list:
"""扁平 20 格 → 4 行 × 5 列(接口用的 gridState 形状)。"""
return [list(flat[r * GRID_COLS:(r + 1) * GRID_COLS]) for r in range(GRID_ROWS)]
def build_steps(round_, round_id: int):
"""把一局摊成逐帧行。"""
rows, step_seq = [], 0
spin_win = 0.0
for spin in round_.spins:
spin_win_before = spin_win
acc_sym = Counter()
winners = []
rotations = spin.rotations
frames = rotations or (None,) # 空旋也要发一帧(落定盘面),否则客户端无从渲染
for i, rot in enumerate(frames, start=1):
step_seq += 1
if rot is None:
grid_flat = spin.landing
ways, removed, drops, fills, conv, gold = [], [], [], [], [], []
base_mul, rot_mul = 0.0, C.ladder_mul(1, spin.is_fs)
else:
grid_flat = rot.grid_before
gold = [{"pos": p, "symbol": rot.grid_before[p - 1]} for p in rot.golden_before]
conv = [{"pos": p, "fromSymbol": s} for p, s in rot.converted]
ways = [{"symbol": w.symbol, "reelCount": w.run, "counts": list(w.counts),
"loc": list(w.loc), "ways": w.ways, "payBase": w.pay_base,
"rotationMul": rot.rotation_mul,
"win": round(w.pay_base * rot.rotation_mul, 4)}
for w in rot.wins]
seen = set()
removed = [p for w in rot.wins for p in w.loc if not (p in seen or seen.add(p))]
drops = [{"from": a, "to": b, "symbol": s} for a, b, s in rot.drops]
fills = [{"pos": p, "symbol": s} for p, s in rot.fills]
base_mul, rot_mul = rot.base_mul, rot.rotation_mul
for w in rot.wins:
acc_sym[w.symbol] += len(w.loc)
winners.append({"symbol": w.symbol, "count": w.run,
"win": round(w.pay_base * rot.rotation_mul, 4)})
spin_win = spin_win_before + sum(r.win_mul for r in rotations[:i])
is_last = (i == len(frames))
scat = ({"scatterCount": spin.scatters, "freeSpins": spin.fs_awarded, "extraSpins": 0}
if spin.index == 0 and i == 1 and spin.fs_awarded else None)
retri = ({"scatterCount": spin.scatters, "extraSpins": C.fs_award(spin.scatters)}
if spin.index > 0 and i == 1 and spin.retrigger else None)
rows.append(dict(
id=round_id * 100 + step_seq,
round_id=round_id,
reel_spin_id=spin.index,
rotation_seq=i,
grid_state=json.dumps(grid_rows(grid_flat), ensure_ascii=False) if grid_flat else None,
golden_detail=json.dumps(gold, ensure_ascii=False) if gold else None,
wild_converted=json.dumps(conv, ensure_ascii=False) if conv else None,
ways_detail=json.dumps(ways, ensure_ascii=False) if ways else None,
accumulated_symbols=json.dumps(dict(acc_sym), ensure_ascii=False),
spin_winners=json.dumps(winners, ensure_ascii=False),
removed_positions=json.dumps(removed),
drop_data=json.dumps(drops, ensure_ascii=False) if drops else None,
fill_data=json.dumps(fills, ensure_ascii=False) if fills else None,
scatter_trigger=json.dumps(scat, ensure_ascii=False) if scat else None,
retrigger_info=json.dumps(retri, ensure_ascii=False) if retri else None,
base_score=round(base_mul, 4),
win_amount=round(sum(r.win_mul for r in rotations[:i]), 4),
rotation_mul=rot_mul,
spin_win=round(spin_win, 4),
win_capped=1 if (round_.capped and is_last and spin is round_.spins[-1]) else 0,
bigwin_level=None,
is_final=1 if is_last else 0,
))
spin_win = spin_win_before + sum(r.win_mul for r in rotations)
return rows
def make_round(box_id: int, seq: int, rng: random.Random):
r = play_round(rng, box_id=box_id)
round_id = box_id * 1_000_000 + seq
steps = build_steps(r, round_id=round_id)
row = dict(round_id=round_id, box_id=box_id, bucket_id=C.bucket_of(r.actual_mul),
actual_mul=round(r.actual_mul, 4), free_spin_count=r.free_spins,
total_rotations=sum(len(s.rotations) for s in r.spins),
has_fs=1 if r.has_fs else 0, near_miss=1 if r.near_miss else 0)
return row, steps
# ---------------------------------------------------------------- 配额构造
def calibrate(box_id: int, n: int, seed: int):
"""小样本标定:每个桶的**池内实测均值**(自然均值与名义 expect_mul 差很多,必须实测)。"""
rng = random.Random(seed)
tot, cnt = Counter(), Counter()
for _ in range(n):
r = play_round(rng, box_id=box_id)
b = C.bucket_of(r.actual_mul)
tot[b] += r.actual_mul
cnt[b] += 1
means = {}
for b in C.BUCKETS:
means[b.id] = (tot[b.id] / cnt[b.id]) if cnt[b.id] else b.expect_mul
return means, cnt
def _quota_from_power(means, power: float, empty_share: float):
"""w_b = 30×0.6^((b-1)×power)(b≥2),桶 1 锁 empty_share;返回 16 个配额(下标 1..16)。"""
ws = {b.id: 30.0 * (0.6 ** ((b.id - 1) * power)) for b in C.BUCKETS if b.id >= 2}
s = sum(ws.values())
q = {b.id: 0.0 for b in C.BUCKETS}
q[1] = empty_share
for b_id, w in ws.items():
q[b_id] = (1.0 - empty_share) * w / s
return q
def solve_quotas(means, target_rtp=TARGET_RTP, empty_share=EMPTY_SHARE):
"""二分 power,使 Σ q_b × mean_b = target_rtp。power 越大越偏向低倍桶(期望越低)。"""
def expected(power):
q = _quota_from_power(means, power, empty_share)
return sum(q[b.id] * means[b.id] for b in C.BUCKETS)
lo, hi = 0.05, 8.0
# expected(power) 随 power **递减**:power 越大越偏向低倍桶
for _ in range(80):
mid = (lo + hi) / 2
if expected(mid) > target_rtp:
lo = mid # 期望偏高 → 还要更大的 power
else:
hi = mid
q = _quota_from_power(means, (lo + hi) / 2, empty_share)
return q, expected((lo + hi) / 2)
def _fill(box_id: int, targets: dict, seed: int, max_tries=MAX_TRIES):
"""按桶目标条数填充:落进目标桶才收。返回 (pool, bucket_cnt, short)。"""
rng = random.Random(seed)
pool, tries, short = [], Counter(), []
for b in C.BUCKETS:
got = 0
while got < targets[b.id] and tries[b.id] < max_tries:
tries[b.id] += 1
row, steps = make_round(box_id, len(pool) + 1, rng)
if row["bucket_id"] == b.id:
pool.append((row, steps))
got += 1
if got < targets[b.id]:
short.append(f"桶{b.id} {got}/{targets[b.id]}")
for i, (row, steps) in enumerate(pool, start=1):
rid = box_id * 1_000_000 + i
row["round_id"] = rid
for j, st in enumerate(steps, start=1):
st["round_id"] = rid
st["id"] = rid * 100 + j
cnt = Counter(row["bucket_id"] for row, _ in pool)
return pool, cnt, short
def quota_targets(per_box: int, quotas: dict):
targets = {b.id: int(round(per_box * quotas[b.id])) for b in C.BUCKETS}
diff = per_box - sum(targets.values())
if diff:
targets[1] += diff
return targets
def _sample_bucket(box_id: int, bucket_id: int, rng: random.Random, max_tries=MAX_TRIES):
"""采一条落进指定桶的回合(低概率桶会多试几次)。"""
for _ in range(max_tries):
row, steps = make_round(box_id, 0, rng)
if row["bucket_id"] == bucket_id:
return row, steps
return None
def _tune(box_id: int, pool: list, target_rtp: float, per_box: int, seed: int, verbose=True):
"""整数可执行的定向微调:配额算出来是小数,四舍五入会把「0.4 条」的尾部桶抹成 0 条,
于是它本该贡献的期望就丢了(实测池内均值会系统性偏低 5~8pt)。
做法:拿空转局(桶 1,倍率 0)去换中高桶的回合,每换一条按**真实倍率**结账,
直到池内均值落进目标 ±0.005 以内。空转率只会动几条(<0.5pt)。
"""
rng = random.Random(seed + 31337)
cur = sum(r["actual_mul"] for r, _ in pool)
need = target_rtp * per_box - cur # 还差多少(单位:倍率总和)
empties = [i for i, (r, _) in enumerate(pool) if r["bucket_id"] == 1]
ordered = sorted((x for x in C.BUCKETS if x.id >= 2), key=lambda x: -x.expect_mul)
swaps = 0
while need > 0.6 and empties and ordered:
cand = next((b for b in ordered if b.expect_mul <= need), None)
if cand is None:
break
got = None
for _ in range(300): # 只收"不超过缺口"的回合,保证不会冲过头
s = _sample_bucket(box_id, cand.id, rng)
if s and s[0]["actual_mul"] <= need:
got = s
break
if got is None: # 这一档试不出来,退一档
ordered = [b for b in ordered if b.id != cand.id]
continue
row, steps = got
pool[empties.pop()] = (row, steps)
need -= row["actual_mul"]
swaps += 1
rng = random.Random(rng.random()) # 让后续采样换流
if verbose and swaps:
print(f" 微调:换入 {swaps} 条中高桶回合(空转率随之动 {swaps / per_box:.2%})")
cnt = Counter(r["bucket_id"] for r, _ in pool)
rtp = sum(r["actual_mul"] for r, _ in pool) / max(1, len(pool))
if verbose:
print(f" 微调后池内实测 RTP {rtp:.4f}(与目标差 {abs(rtp - target_rtp):.4f})")
return pool, cnt
def generate_quota(box_id: int, per_box: int, seed: int, empty_share: float, target_rtp: float,
verbose=True):
"""解配额 → 定条数 → 填充 → 整数微调 → 重排 id。
`empty_share`:桶 1 的配额占比。CLI 不传时按 `empty_share_for(box_id)` 取**按档波动**的值
(低档 62% / 中档 65% / 高档 68%),这样各箱形状不同、但 RTP 都锁在 `target_rtp`。
为什么填完还要微调:运行时按库存消耗(一个 cycle 每条回合正好播一次)⇒ `cycle RTP = 池内均值`,
而配额是连续解出来的小数,四舍五入会抹掉尾部桶(见 `_tune` 的注释)。
"""
means, _ = calibrate(box_id, CALIB_ROUNDS, seed)
quotas, exp_rtp = solve_quotas(means, target_rtp, empty_share)
targets = quota_targets(per_box, quotas)
if verbose:
print(f" 标定:配额理论期望 {exp_rtp:.4f}(目标 {target_rtp});桶1 配额 {quotas[1]:.2%};"
f"桶条数(1/2/3/12/16) = {targets[1]}/{targets[2]}/{targets[3]}/{targets[12]}/{targets[16]}")
pool, cnt, short = _fill(box_id, targets, seed + 999 + box_id * 7919)
pool, cnt = _tune(box_id, pool, target_rtp, per_box, seed + box_id, verbose)
for i, (row, steps) in enumerate(pool, start=1): # 重排 id(采样与微调都打乱了顺序)
rid = box_id * 1_000_000 + i
row["round_id"] = rid
for j, st in enumerate(steps, start=1):
st["round_id"] = rid
st["id"] = rid * 100 + j
return pool, quotas, means, exp_rtp, short
# ---------------------------------------------------------------- 写库
def write(conn, pool_by_box, per_box, audiences=C.AUDIENCES):
cur = conn.cursor()
total_r = total_s = 0
for box, (pool, quotas, bucket_cnt) in pool_by_box.items():
rounds = [row for row, _ in pool]
steps = [st for _, sts in pool for st in sts]
lo, hi = box * 1_000_000 + 1, box * 1_000_000 + per_box
cur.execute(f"DELETE FROM game_mahjong_step WHERE round_id BETWEEN {lo} AND {hi}")
cur.execute(f"DELETE FROM game_mahjong_round WHERE box_id={box}")
cur.executemany(
"INSERT INTO game_mahjong_round (round_id,box_id,bucket_id,actual_mul,free_spin_count,"
"total_rotations,has_fs,near_miss) VALUES (%(round_id)s,%(box_id)s,%(bucket_id)s,"
"%(actual_mul)s,%(free_spin_count)s,%(total_rotations)s,%(has_fs)s,%(near_miss)s)", rounds)
cols = ("id", "round_id", "reel_spin_id", "rotation_seq", "grid_state", "golden_detail",
"wild_converted", "ways_detail", "accumulated_symbols", "spin_winners",
"removed_positions", "drop_data", "fill_data", "scatter_trigger", "retrigger_info",
"base_score", "win_amount", "rotation_mul", "spin_win", "win_capped",
"bigwin_level", "is_final")
cur.executemany(
f"INSERT INTO game_mahjong_step ({','.join(cols)}) "
f"VALUES ({','.join('%(' + c + ')s' for c in cols)})", steps)
for aud in audiences:
cur.execute("DELETE FROM game_mahjong_box_bucket WHERE box_id=%s AND audience=%s", (box, aud))
cur.executemany(
"INSERT INTO game_mahjong_box_bucket (box_id,bucket_id,audience,count,used) "
"VALUES (%s,%s,%s,%s,0)",
[(box, b.id, aud, bucket_cnt.get(b.id, 0)) for b in C.BUCKETS])
items = json.dumps([{"bucketId": b.id, "count": bucket_cnt.get(b.id, 0)} for b in C.BUCKETS],
ensure_ascii=False)
cur.execute("DELETE FROM game_mahjong_box_template WHERE box_id=%s AND audience=%s", (box, aud))
cur.execute(
"INSERT INTO game_mahjong_box_template (box_id,audience,name,total_rounds,items_json,is_active) "
"VALUES (%s,%s,%s,%s,%s,1)",
(box, aud, f"配额池-{per_box}局", per_box, items))
cur.execute("UPDATE game_mahjong_box SET total_rounds=%s WHERE box_id=%s", (per_box, box))
conn.commit()
total_r += len(rounds)
total_s += len(steps)
rtp = sum(r["actual_mul"] for r in rounds) / max(1, len(rounds))
print(f" 箱 {box:2d}(赌注 {C.bet_of(box):>3}):回合 {len(rounds):>5} 帧 {len(steps):>6} "
f"**池内实测 RTP {rtp:.4f}** 空转 {bucket_cnt.get(1,0)/max(1,len(rounds)):.1%} "
f"FS 局 {sum(r['has_fs'] for r in rounds)}")
print(f"合计:回合 {total_r},帧 {total_s}")
return total_r, total_s
def build_natural(box_id, per_box, seed):
rng = random.Random(seed + box_id)
pool, cnt = [], Counter()
for i in range(1, per_box + 1):
row, steps = make_round(box_id, i, rng)
pool.append((row, steps))
cnt[row["bucket_id"]] += 1
return pool, cnt
def main() -> None:
ap = argparse.ArgumentParser()
ap.add_argument("--mode", choices=("natural", "quota"), default="natural")
ap.add_argument("--per-box", type=int, default=1000)
ap.add_argument("--boxes", default="")
ap.add_argument("--seed", type=int, default=20260922)
ap.add_argument("--empty-share", type=float, default=None,
help="空转率(桶 1 配额占比);不传=按档取(低档 0.62 / 中档 0.65 / 高档 0.68)")
ap.add_argument("--target-rtp", type=float, default=TARGET_RTP)
ap.add_argument("--write", action="store_true")
ap.add_argument("--dry-run", action="store_true")
ap.add_argument("--host", default=DB["host"]); ap.add_argument("--port", type=int, default=DB["port"])
ap.add_argument("--user", default=DB["user"]); ap.add_argument("--password", default=DB["password"])
ap.add_argument("--database", default=DB["database"])
args = ap.parse_args()
boxes = [int(x) for x in args.boxes.split(",") if x.strip()] or list(C.BOX_IDS)
print(f"==== {args.mode} 模式:{len(boxes)} 箱 × {args.per_box} 局"
f"{'' if args.write and not args.dry_run else '(干跑,不写库)'} ====")
pool_by_box = {}
for box in boxes:
if args.mode == "natural":
pool, cnt = build_natural(box, args.per_box, args.seed)
quotas = None
pool_by_box[box] = (pool, quotas, cnt)
else:
share = args.empty_share if args.empty_share is not None else empty_share_for(box)
pool, quotas, means, exp_rtp, short = generate_quota(
box, args.per_box, args.seed, share, args.target_rtp)
cnt = Counter(row["bucket_id"] for row, _ in pool)
if short:
print(f" 未填满:{';'.join(short)}")
pool_by_box[box] = (pool, quotas, cnt)
if not (args.write and not args.dry_run):
rows = [r for r, _ in pool_by_box[box][0]]
rtp = sum(r["actual_mul"] for r in rows) / max(1, len(rows))
print(f" 箱 {box:2d}(赌注 {C.bet_of(box):>3}):回合 {len(rows):>5} "
f"**池内实测 RTP {rtp:.4f}** 空转 {cnt.get(1,0)/max(1,len(rows)):.1%} "
f"FS 局 {sum(r['has_fs'] for r in rows)}")
if not (args.write and not args.dry_run):
return
import pymysql
conn = pymysql.connect(host=args.host, port=args.port, user=args.user,
password=args.password, database=args.database, charset="utf8mb4")
write(conn, pool_by_box, args.per_box)
conn.close()
if __name__ == "__main__":
main()
-22
View File
@@ -24,7 +24,6 @@
<druid.version>1.2.28</druid.version>
<yauaa.version>8.1.0</yauaa.version>
<bitwalker.version>1.21</bitwalker.version>
<swagger.version>3.0.0</swagger.version>
<kaptcha.version>2.3.3</kaptcha.version>
<tianai-captcha.version>1.5.5</tianai-captcha.version>
<pagehelper.boot.version>2.1.1</pagehelper.boot.version>
@@ -35,7 +34,6 @@
<poi.version>4.1.2</poi.version>
<velocity.version>2.3</velocity.version>
<jwt.version>0.9.1</jwt.version>
<springdoc.version>3.0.2</springdoc.version>
</properties>
<!-- 依赖声明 -->
@@ -149,26 +147,6 @@
<version>${oshi.version}</version>
</dependency>
<!-- spring-doc -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>${springdoc.version}</version>
</dependency>
<!-- Swagger3依赖 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>${swagger.version}</version>
<!-- <exclusions>-->
<!-- <exclusion>-->
<!-- <groupId>io.swagger</groupId>-->
<!-- <artifactId>swagger-models</artifactId>-->
<!-- </exclusion>-->
<!-- </exclusions>-->
</dependency>
<!-- io常用工具类 -->
<dependency>
<groupId>commons-io</groupId>
+15 -5
View File
@@ -1,14 +1,10 @@
import request from '@/axios'
import type { RegisterUserType, SmsLoginType, UserLoginType, UserType } from './types'
import type { RegisterUserType, SmsLoginType, UserType } from './types'
interface RoleParams {
roleName: string
}
export const loginApi = (data: UserLoginType): Promise<IResponse<UserType>> => {
return request.post({ url: '/promo/login', data })
}
export const smsLoginApi = (data: SmsLoginType): Promise<IResponse<UserType>> => {
return request.post({ url: '/promo/smsLogin', data })
}
@@ -34,6 +30,20 @@ export const getCodeApi = (
})
}
/** 点「获取验证码」时的账号前置校验:无推广后台权限的号在这一步就被拒绝,不用先拖滑块 */
export const precheckPhoneApi = (
phoneNumber: string,
parentInvitationCode?: string
): Promise<IResponse<UserType>> => {
return request.post({
url: '/promo/sms/precheck',
data: {
phoneNumber: phoneNumber,
parentInvitationCode
}
})
}
export const loginOutApi = (): Promise<IResponse> => {
return request.get({ url: '/mock/user/loginOut' })
}
@@ -1,5 +1,5 @@
<script setup lang="tsx">
import { getAdminRoleApi, getCodeApi, smsLoginApi } from '@/api/login'
import { getAdminRoleApi, getCodeApi, precheckPhoneApi, smsLoginApi } from '@/api/login'
import type { SmsLoginType, UserType } from '@/api/login/types'
import { getMyMenus } from '@/api/permission'
import { PATH_URL } from '@/axios/service'
@@ -35,6 +35,7 @@ const loading = ref(false)
const redirect = ref('')
const getCodeTime = ref(60)
const getCodeLoading = ref(false)
const captchaCode = ref('')
let tacInstance: any = null
let codeTimer: ReturnType<typeof setInterval> | undefined
@@ -107,8 +108,17 @@ const schema = reactive<FormSchema[]>([
if (event.key === 'Enter') signIn()
}}
/>
<ElButton class="login-code-btn" disabled={unref(getCodeLoading)} onClick={getCode}>
{unref(getCodeLoading) ? `${unref(getCodeTime)}s` : '获取验证码'}
<ElButton
class="login-code-btn"
disabled={unref(getCodeLoading)}
loading={unref(getCodeLoading) && !unref(captchaCode)}
onClick={getCode}
>
{unref(getCodeLoading)
? unref(captchaCode)
? `${unref(getCodeTime)}s`
: '发送中'
: '获取验证码'}
</ElButton>
</div>
)
@@ -165,6 +175,32 @@ onBeforeUnmount(() => {
if (codeTimer) clearInterval(codeTimer)
})
// 点下按钮就开始倒计时:短信网关往返通常 1~3 秒,等接口返回再倒计时会像卡死
const startCountdown = () => {
if (codeTimer) clearInterval(codeTimer)
captchaCode.value = ''
getCodeTime.value = 60
getCodeLoading.value = true
codeTimer = setInterval(() => {
getCodeTime.value--
if (getCodeTime.value <= 0) {
clearInterval(codeTimer)
codeTimer = undefined
getCodeTime.value = 60
getCodeLoading.value = false
}
}, 1000)
}
// 发送失败(限流/验证码失效/发送异常):解禁按钮,让用户能立刻重试
const resetCountdown = () => {
if (codeTimer) clearInterval(codeTimer)
codeTimer = undefined
getCodeTime.value = 60
getCodeLoading.value = false
captchaCode.value = ''
}
const getCode = async () => {
if (getCodeLoading.value) return
const { username, parentInvitationCode } = await getFormData<SmsLoginType>()
@@ -172,6 +208,10 @@ const getCode = async () => {
ElMessage.error('请输入正确的手机号')
return
}
// 先验账号再弹滑块:不是推广后台用户(与后台账号校验同一份规则)直接拒绝,
// 避免用户白拖一次滑块才被告知没权限;接口失败/被限流时放行走原流程,发送时后端还会再验一次
const phoneCheck = await precheckPhoneApi(username, parentInvitationCode).catch(() => null)
if (!phoneCheck) return
if (typeof window.TAC === 'undefined' || typeof window.CaptchaConfig === 'undefined') {
ElMessage.error('滑块验证码未加载,请刷新页面')
return
@@ -187,19 +227,18 @@ const getCode = async () => {
validSuccess: (_res: unknown, _config: unknown, tac: any) => {
tac.destroyWindow()
tacInstance = null
getCodeApi(username, captchaId, '2', parentInvitationCode).then(() => {
ElMessage.success('验证码已发送')
getCodeLoading.value = true
codeTimer = setInterval(() => {
getCodeTime.value--
if (getCodeTime.value <= 0) {
clearInterval(codeTimer)
codeTimer = undefined
getCodeTime.value = 60
getCodeLoading.value = false
startCountdown()
getCodeApi(username, captchaId, '2', parentInvitationCode)
.then((res: any) => {
// 业务失败(限流/验证码失效)响应拦截器已弹过错误提示,这里只释放倒计时
if (res?.code !== 0 && res?.code !== 200) {
resetCountdown()
return
}
}, 1000)
captchaCode.value = captchaId
ElMessage.success('验证码已发送')
})
.catch(() => resetCountdown())
},
validFail: (_res: unknown, _config: unknown, tac: any) => {
ElMessage.error('滑块验证失败,请重试')
@@ -10,7 +10,7 @@ import { RegisterUserType } from '@/api/login/types'
import { useUserStore } from '@/store/modules/user'
import { completeLogin } from '../completeLogin'
import { useRouter } from 'vue-router'
import { registerApi, getCodeApi } from '@/api/login'
import { registerApi, getCodeApi, precheckPhoneApi } from '@/api/login'
import { PATH_URL } from '@/axios/service'
declare global {
@@ -38,12 +38,38 @@ const { required } = useValidator()
const getCodeTime = ref(60)
const getCodeLoading = ref(false)
let tacInstance: any = null
let codeTimer: ReturnType<typeof setInterval> | undefined
// 点下按钮就开始倒计时:短信网关往返通常 1~3 秒,等接口返回再倒计时会像卡死
const startCountdown = () => {
if (codeTimer) clearInterval(codeTimer)
getCodeTime.value = 60
getCodeLoading.value = true
codeTimer = setInterval(() => {
getCodeTime.value--
if (getCodeTime.value <= 0) {
clearInterval(codeTimer)
codeTimer = undefined
getCodeTime.value = 60
getCodeLoading.value = false
}
}, 1000)
}
// 发送失败(限流/验证码失效/发送异常):解禁按钮,让用户能立刻重试
const resetCountdown = () => {
if (codeTimer) clearInterval(codeTimer)
codeTimer = undefined
getCodeTime.value = 60
getCodeLoading.value = false
}
onBeforeUnmount(() => {
if (tacInstance) {
tacInstance.destroyWindow()
tacInstance = null
}
if (codeTimer) clearInterval(codeTimer)
})
const getCode = async () => {
@@ -52,6 +78,12 @@ const getCode = async () => {
ElMessage.error('手机号不能为空')
return
}
// 先验账号再弹滑块,与登录页同一份规则(注册页无账号时靠招商邀请码放行);接口失败/被限流时放行走原流程
const phoneCheck = await precheckPhoneApi(
formData.username,
(formData as any).parentInvitationCode
).catch(() => null)
if (!phoneCheck) return
// 判断TAC是否加载
if (typeof window.TAC === 'undefined' || typeof window.CaptchaConfig === 'undefined') {
@@ -71,23 +103,18 @@ const getCode = async () => {
tac.destroyWindow()
tacInstance = null
const captchaId = window._captchaId
// 滑块验证成功,发送短信验证码
// 滑块验证成功,立刻进入倒计时,再等短信接口返回
startCountdown()
getCodeApi(formData.username, captchaId)
.then(() => {
ElMessage.success('获取验证码成功')
getCodeLoading.value = true
const timer = setInterval(() => {
getCodeTime.value--
if (getCodeTime.value <= 0) {
clearInterval(timer)
getCodeTime.value = 60
getCodeLoading.value = false
.then((res: any) => {
// 业务失败(限流/验证码失效)响应拦截器已弹过错误提示,这里只释放倒计时
if (res?.code !== 0 && res?.code !== 200) {
resetCountdown()
return
}
}, 1000)
})
.catch(() => {
ElMessage.error('获取验证码失败')
ElMessage.success('获取验证码成功')
})
.catch(() => resetCountdown())
},
validFail: (res: any, c: any, tac: any) => {
ElMessage.error('滑块验证失败,请重试')
+5 -2
View File
@@ -255,12 +255,15 @@
<ElTableColumn prop="id" label="ID" width="60" />
<ElTableColumn prop="templateName" label="模板名称" min-width="150" />
<ElTableColumn prop="ornamentCount" label="饰品数" width="80" />
<ElTableColumn prop="createBy" label="创建人" width="100" />
<ElTableColumn prop="createBy" label="创建人" width="100">
<template #default="{ row }">{{ row.createBy || '其他主播' }}</template>
</ElTableColumn>
<ElTableColumn prop="createTime" label="创建时间" width="170" />
<ElTableColumn label="操作" width="140">
<template #default="{ row }">
<ElButton type="primary" link size="small" @click="handleLoadTemplate(row)">加载</ElButton>
<ElButton type="danger" link size="small" @click="handleDeleteTemplate(row.id)">删除</ElButton>
<!-- 后端只允许创建者本人或超管删除;共享模板 createBy 会被置空,这里不给别人显示删除按钮 -->
<ElButton v-if="row.createBy" type="danger" link size="small" @click="handleDeleteTemplate(row.id)">删除</ElButton>
</template>
</ElTableColumn>
</ElTable>
-6
View File
@@ -29,12 +29,6 @@
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional> <!-- 表示依赖不会传递 -->
</dependency>
<!-- spring-doc -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
</dependency>
<!-- Mysql驱动包 -->
<dependency>
<groupId>com.mysql</groupId>
@@ -4,8 +4,6 @@ import java.util.ArrayList;
import java.util.List;
import jakarta.servlet.http.HttpServletResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
@@ -33,7 +31,6 @@ import com.ruoyi.system.service.ISysDictTypeService;
*
* @author ruoyi
*/
@Api(tags = "管理端 字段值管理")
@RestController
@RequestMapping("/system/dict/data")
public class SysDictDataController extends BaseController
@@ -44,7 +41,6 @@ public class SysDictDataController extends BaseController
@Autowired
private ISysDictTypeService dictTypeService;
@ApiOperation("获取字典值")
@PreAuthorize("@ss.hasPermi('system:dict:list')")
@GetMapping("/list")
public TableDataInfo list(SysDictData dictData)
@@ -91,7 +87,6 @@ public class SysDictDataController extends BaseController
/**
* 新增字典类型
*/
@ApiOperation("新增字典值")
@PreAuthorize("@ss.hasPermi('system:dict:add')")
@Log(title = "字典数据", businessType = BusinessType.INSERT)
@PostMapping
@@ -104,7 +99,6 @@ public class SysDictDataController extends BaseController
/**
* 修改保存字典类型
*/
@ApiOperation("修改字典值")
@PreAuthorize("@ss.hasPermi('system:dict:edit')")
@Log(title = "字典数据", businessType = BusinessType.UPDATE)
@PutMapping
@@ -3,7 +3,6 @@ package com.ruoyi.web.controller.system;
import java.util.List;
import java.util.Set;
import io.swagger.annotations.Api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@@ -24,7 +23,6 @@ import com.ruoyi.system.service.ISysMenuService;
*
* @author ruoyi
*/
@Api(tags = "管理端认证api")
@RestController
public class SysLoginController
{
@@ -11,11 +11,10 @@ import java.util.List;
import java.util.Map;
/**
* swagger 用户测试方法
* 用户测试方法(若依自带示例,未对外使用)
*
* @author ruoyi
*/
//@Api(tags = "用户信息管理")
@RestController
@RequestMapping("/test/user")
public class TestController extends BaseController
@@ -26,7 +25,6 @@ public class TestController extends BaseController
users.put(2, new UserEntity(2, "ry", "admin123", "15666666666"));
}
// @ApiOperation("获取用户列表")
@GetMapping("/list")
public R<List<UserEntity>> userList()
{
@@ -34,8 +32,6 @@ public class TestController extends BaseController
return R.ok(userList);
}
// @ApiOperation("获取用户详细")
// @ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path", dataTypeClass = Integer.class)
@GetMapping("/{userId}")
public R<UserEntity> getUser(@PathVariable Integer userId)
{
@@ -49,13 +45,6 @@ public class TestController extends BaseController
}
}
// @ApiOperation("新增用户")
// @ApiImplicitParams({
// @ApiImplicitParam(name = "userId", value = "用户id", dataType = "Integer", dataTypeClass = Integer.class),
// @ApiImplicitParam(name = "username", value = "用户名称", dataType = "String", dataTypeClass = String.class),
// @ApiImplicitParam(name = "password", value = "用户密码", dataType = "String", dataTypeClass = String.class),
// @ApiImplicitParam(name = "mobile", value = "用户手机", dataType = "String", dataTypeClass = String.class)
// })
@PostMapping("/save")
public R<String> save(UserEntity user)
{
@@ -67,7 +56,6 @@ public class TestController extends BaseController
return R.ok();
}
// @ApiOperation("更新用户")
@PutMapping("/update")
public R<String> update(@RequestBody UserEntity user)
{
@@ -84,8 +72,6 @@ public class TestController extends BaseController
return R.ok();
}
// @ApiOperation("删除用户信息")
// @ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path", dataTypeClass = Integer.class)
@DeleteMapping("/{userId}")
public R<String> delete(@PathVariable Integer userId)
{
@@ -101,19 +87,14 @@ public class TestController extends BaseController
}
}
//@ApiModel(value = "UserEntity", description = "用户实体")
class UserEntity
{
// @ApiModelProperty("用户ID")
private Integer userId;
// @ApiModelProperty("用户名称")
private String username;
// @ApiModelProperty("用户密码")
private String password;
// @ApiModelProperty("用户手机")
private String mobile;
public UserEntity()
@@ -1,170 +0,0 @@
package com.ruoyi.web.core.config;
import io.swagger.annotations.ApiOperation;
import io.swagger.models.auth.In;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.service.SecurityScheme;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import java.util.ArrayList;
import java.util.List;
@Configuration
public class SwaggerConfig {
/**
* 后台接口分组
*/
// @Bean
public Docket adminDocket() {
return new Docket(DocumentationType.OAS_30)
.select()
// 扫描注解
.apis(RequestHandlerSelectors.basePackage("com.ruoyi.admin.controller"))
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
// 扫描所有.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
.groupName("admin")
// 设置安全模式,访问token
.securitySchemes(securitySchemes())
.securityContexts(securityContexts());
}
/**
* 用户业务接口分组
*/
@Bean
public Docket userDocket() {
return new Docket(DocumentationType.OAS_30)
.select()
.apis(RequestHandlerSelectors.basePackage("com.ruoyi.user.controller"))
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
.paths(PathSelectors.any())
.build()
.groupName("user")
.securitySchemes(securitySchemes())
.securityContexts(securityContexts());
}
/**
* 第三方业务接口分组
*/
@Bean
public Docket thirdPartyDocket() {
return new Docket(DocumentationType.OAS_30)
.select()
.apis(RequestHandlerSelectors.basePackage("com.ruoyi.thirdparty"))
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
.paths(PathSelectors.any())
.build()
.groupName("thirdParty")
.securitySchemes(securitySchemes())
.securityContexts(securityContexts());
}
/**
* 玩法业务接口分组
*/
@Bean
public Docket playingMethodDocket() {
return new Docket(DocumentationType.OAS_30)
.select()
.apis(RequestHandlerSelectors.basePackage("com.ruoyi.playingmethod.controller"))
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
.paths(PathSelectors.any())
.build()
.groupName("playingMethod")
.securitySchemes(securitySchemes())
.securityContexts(securityContexts());
}
/**
* 糖果玩法业务接口分组
*/
@Bean
public Docket gameSugarMethodDocket() {
return new Docket(DocumentationType.OAS_30)
.select()
.apis(RequestHandlerSelectors.basePackage("com.ruoyi.game.sugar.controller"))
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
.paths(PathSelectors.any())
.build()
.groupName("gameSugarMethod")
.securitySchemes(securitySchemes())
.securityContexts(securityContexts());
}
/**
* 任务业务接口分组
*/
@Bean
public Docket taskDocket() {
return new Docket(DocumentationType.OAS_30)
.select()
.apis(RequestHandlerSelectors.basePackage("com.ruoyi.task.controller"))
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
.paths(PathSelectors.any())
.build()
.groupName("task")
.securitySchemes(securitySchemes())
.securityContexts(securityContexts());
}
/**
* 推广后台接口分组
*/
@Bean
public Docket promoDocket() {
return new Docket(DocumentationType.OAS_30)
.select()
.apis(RequestHandlerSelectors.basePackage("com.ruoyi.promo.controller"))
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
.paths(PathSelectors.any())
.build()
.groupName("promo")
.securitySchemes(securitySchemes())
.securityContexts(securityContexts());
}
/**
* 安全模式,这里指定token通过Authorization请求头传递
*/
private List<SecurityScheme> securitySchemes() {
List<SecurityScheme> apiKeyList = new ArrayList<SecurityScheme>();
apiKeyList.add(new ApiKey("Authorization", "Authorization", In.HEADER.toValue()));
return apiKeyList;
}
/**
* 安全上下文
*/
private List<SecurityContext> securityContexts() {
List<SecurityContext> securityContexts = new ArrayList<>();
securityContexts.add(SecurityContext.builder()
.securityReferences(defaultAuth())
.operationSelector(o -> o.requestMappingPattern().matches("/.*"))
.build());
return securityContexts;
}
/**
* 默认的安全引用
*/
private List<SecurityReference> defaultAuth() {
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
List<SecurityReference> securityReferences = new ArrayList<>();
securityReferences.add(new SecurityReference("Authorization", authorizationScopes));
return securityReferences;
}
}
@@ -70,12 +70,12 @@ spring:
enabled: true
statViewServlet:
enabled: true
# 设置白名单,不填则允许所有访问
allow:
# 白名单:只允许本机访问(原来留空 = 任意 IP 都能打开控制台)
allow: 127.0.0.1
url-pattern: /druid/*
# 控制台管理用户名和密码
login-username: ruoyi
login-password: 123456
# 控制台管理用户名和密码(河南单独一套,勿用常州/默认值)
login-username: druidhn
login-password: Hn@Druid#7t4Wq9
filter:
stat:
enabled: true
@@ -69,13 +69,15 @@ spring:
webStatFilter:
enabled: true
statViewServlet:
enabled: true
# 设置白名单,不填则允许所有访问
allow:
# 生产关闭 Druid 控制台:/druid/** 是匿名放行的,开着等于把全部 SQL 语句、URI 统计、连接信息挂到公网
# 需要临时排障时改成 true,并保持 allow 白名单 + 非默认口令,用完立刻关回
enabled: false
# 白名单:只允许本机访问(原来留空 = 任意 IP)
allow: 127.0.0.1
url-pattern: /druid/*
# 控制台管理用户名和密码
login-username: ruoyi
login-password: 123456
# 控制台管理用户名和密码(河南单独一套,临时开启时使用,勿改回默认值)
login-username: druidhn
login-password: Hn@Druid#7t4Wq9
filter:
stat:
enabled: true
@@ -0,0 +1,125 @@
package com.ruoyi.framework.config;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.entity.UserData;
import com.ruoyi.common.core.domain.model.LoginUser;
import org.junit.jupiter.api.Test;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import java.util.Collections;
import java.util.function.Supplier;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* 三端 token 隔离规则验证(不启动 Spring,直接测 SecurityConfig 里的三个判定方法):
* <ul>
* <li>后台面 /admin|/system|/monitor|/tool|/test → 只认后台 sys_user 账号签发的 token</li>
* <li>推广面 /promo/** → 只认推广后台签发的 token(loginScope = promo)</li>
* <li>玩家面 /api/** → 只认玩家端签发的 token(loginScope = player)</li>
* </ul>
* 关键点:主播(01)/招商(04)/卡商(05) 是同一张 tt_user 表、两端都能登录,LoginUser 长得一模一样,
* 只有登录端标记能区分,所以「主播的玩家端 token」不能进推广接口,「主播的推广 token」也不能进玩家接口。
*/
public class SecurityRulesTest {
private final SecurityConfig config = new SecurityConfig();
/** 后台账号登录(sys_user) */
private static LoginUser adminLogin() {
LoginUser loginUser = new LoginUser();
loginUser.setUser(new SysUser());
loginUser.setLoginScope(LoginUser.SCOPE_ADMIN);
return loginUser;
}
/** 推广后台登录(超管 99 / 主播 01 / 招商 04 / 卡商 05) */
private static LoginUser promoLogin(String userType) {
LoginUser loginUser = new LoginUser();
loginUser.setUserData(UserData.builder().userType(userType).build());
loginUser.setLoginScope(LoginUser.SCOPE_PROMO);
return loginUser;
}
/** 玩家端登录(通用用户 00 / 主播 01 / 招商 04 / 卡商 05) */
private static LoginUser playerLogin(String userType) {
LoginUser loginUser = new LoginUser();
loginUser.setUserData(UserData.builder().userType(userType).build());
loginUser.setLoginScope(LoginUser.SCOPE_PLAYER);
return loginUser;
}
/** 本次改造前签发的老 token:没有登录端标记 */
private static LoginUser legacyLogin(String userType) {
LoginUser loginUser = new LoginUser();
loginUser.setUserData(UserData.builder().userType(userType).build());
return loginUser;
}
/** 已认证的令牌:与 JwtAuthenticationTokenFilter 一致,用三参构造(两参构造是"未认证"状态) */
private static Supplier<Authentication> auth(LoginUser principal) {
return principal == null ? () -> null
: () -> new UsernamePasswordAuthenticationToken(principal, null, Collections.emptyList());
}
/** 未认证的令牌:两参构造,isAuthenticated() 为 false */
private static Supplier<Authentication> unauthenticated(LoginUser principal) {
return () -> new UsernamePasswordAuthenticationToken(principal, null);
}
private boolean adminAllowed(LoginUser principal) {
return config.checkAdminUser(auth(principal), null).isGranted();
}
private boolean promoAllowed(LoginUser principal) {
return config.checkPromoUser(auth(principal), null).isGranted();
}
private boolean playerAllowed(LoginUser principal) {
return config.checkPlayerUser(auth(principal), null).isGranted();
}
@Test
public void adminEndOnlyAcceptsBackendAccount() {
assertTrue(adminAllowed(adminLogin()), "后台账号应能访问后台接口");
assertFalse(adminAllowed(promoLogin("99")), "推广超管 token 不能访问后台接口");
assertFalse(adminAllowed(promoLogin("01")), "推广 token 不能访问后台接口");
assertFalse(adminAllowed(playerLogin("00")), "玩家 token 不能访问后台接口");
assertFalse(adminAllowed(playerLogin("01")), "主播的玩家端 token 不能访问后台接口");
assertFalse(adminAllowed(null), "未登录不能访问后台接口");
}
@Test
public void promoEndOnlyAcceptsPromoToken() {
for (String userType : new String[]{"99", "04", "01", "05"}) {
assertTrue(promoAllowed(promoLogin(userType)), "推广后台 token(" + userType + ")应能访问推广接口");
}
assertFalse(promoAllowed(playerLogin("01")), "主播的玩家端 token 不能访问推广接口");
assertFalse(promoAllowed(playerLogin("00")), "玩家 token 不能访问推广接口");
assertFalse(promoAllowed(adminLogin()), "后台 token 不能访问推广接口");
assertFalse(promoAllowed(legacyLogin("01")), "本次改造前的老 token 没有端标记,必须重新登录");
assertFalse(promoAllowed(null), "未登录不能访问推广接口");
}
@Test
public void playerEndOnlyAcceptsPlayerToken() {
for (String userType : new String[]{"00", "01", "04", "05"}) {
assertTrue(playerAllowed(playerLogin(userType)), "玩家端 token(" + userType + ")应能访问 /api/**");
}
assertFalse(playerAllowed(promoLogin("01")), "主播的推广 token 不能访问 /api/**");
assertFalse(playerAllowed(promoLogin("99")), "推广超管 token 不能访问 /api/**");
assertFalse(playerAllowed(adminLogin()), "后台 token 不能访问 /api/**");
assertFalse(playerAllowed(legacyLogin("00")), "本次改造前的老 token 没有端标记,必须重新登录");
assertFalse(playerAllowed(null), "未登录不能访问 /api/**");
}
@Test
public void unauthenticatedTokenIsDeniedEverywhere() {
Supplier<Authentication> notAuthenticated = unauthenticated(adminLogin());
assertFalse(config.checkAdminUser(notAuthenticated, null).isGranted());
assertFalse(config.checkPromoUser(notAuthenticated, null).isGranted());
assertFalse(config.checkPlayerUser(notAuthenticated, null).isGranted());
}
}
-7
View File
@@ -192,13 +192,6 @@
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<!-- Swagger3依赖 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>${swagger.version}</version>
</dependency>
</dependencies>
</project>
@@ -22,6 +22,15 @@ public class LoginUser implements UserDetails
{
private static final long serialVersionUID = 1L;
/** 登录端:游戏管理后台(sys_user 账号) */
public static final String SCOPE_ADMIN = "admin";
/** 登录端:推广后台(promo_admin 超管 / 主播 01 / 招商 04 / 卡商 05) */
public static final String SCOPE_PROMO = "promo";
/** 登录端:玩家端(H5 / App,api_ 前缀登录) */
public static final String SCOPE_PLAYER = "player";
/**
* 用户ID
*/
@@ -79,6 +88,13 @@ public class LoginUser implements UserDetails
private UserData userData;
/**
* 登录端标记(admin / promo / player):同一个 tt_user 既能登录玩家端也能登录推广后台,
* 两端签发的 token 必须互不通用,鉴权时按这个标记区分(见 SecurityConfig 的三条规则)。
* 签发时在 ApiUserDetailsServiceImpl 按账号前缀写入,随会话缓存在 Redis 里。
*/
private String loginScope;
public LoginUser(Long userId, UserData userData) {
this.userId = userId;
this.userData = userData;
@@ -30,7 +30,9 @@ public class MimeTypeUtils
// 图片
"bmp", "gif", "jpg", "jpeg", "png",
// word excel powerpoint
"doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt",
// 2026-09-23 去掉 html/htm:上传目录 /profile/** 是匿名可访问的静态目录,
// 放行 html/htm 等于允许任意登录用户往自己域名下丢页面(.html 被当静态资源打开就是 XSS/钓鱼面)
"doc", "docx", "xls", "xlsx", "ppt", "pptx", "txt",
// 压缩文件
"rar", "zip", "gz", "bz2",
// 视频格式
@@ -5,14 +5,12 @@ import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.CacheControl;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
@@ -54,11 +52,6 @@ public class ResourcesConfig implements WebMvcConfigurer
registry.addResourceHandler("/upload/**")
.addResourceLocations("file:" + RuoYiConfig.getProfile() + "/upload/");
/** swagger配置 */
registry.addResourceHandler("/swagger-ui/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/")
.setCacheControl(CacheControl.maxAge(5, TimeUnit.HOURS).cachePublic());
}
/**
@@ -36,6 +36,7 @@ import java.util.function.Supplier;
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig {
@Qualifier("apiUserDetailsServiceImpl")
@Autowired
private UserDetailsService apiUserDetailsService;
@@ -101,13 +102,21 @@ public class SecurityConfig {
// 静态资源
.requestMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html",
"/**/*.css", "/**/*.js", "/profile/**", "/uploads/**", "/upload/**").permitAll()
// Swagger & Druid
.requestMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
// Druid 监控台:生产已在 application-pro.yml 关闭 statViewServlet(enabled=false)
// 并限制 allow=127.0.0.1 + 非默认口令;这里保留放行是为了本地开发能打开控制台
.requestMatchers("/druid/**").permitAll()
// WebSocket
.requestMatchers("/api/websocket/skins").permitAll()
.requestMatchers("/ws/**").permitAll()
// 后台接口:只认「后台 sys_user 账号」的 token(详见 checkAdminUser)
.requestMatchers("/admin/**").access(this::checkAdminUser)
// 后台接口(游戏管理后台 + 框架自带系统管理/监控/代码生成/测试接口):
// 只认「后台 sys_user 账号」的 token,推广 / 玩家 token 一律 403(详见 checkAdminUser)
// /common/** 是文件上传下载(只有后台 ruoyi-ui 在用),不放进三端前缀里会对所有端开放
.requestMatchers("/admin/**", "/system/**", "/monitor/**", "/tool/**", "/test/**", "/common/**")
.access(this::checkAdminUser)
// 推广接口:只认推广身份的 token,玩家(00)与后台账号一律 403(详见 checkPromoUser)
.requestMatchers("/promo/**").access(this::checkPromoUser)
// 玩家接口:只认玩家端身份的 token,推广超管与后台账号一律 403(详见 checkPlayerUser)
.requestMatchers("/api/**").access(this::checkPlayerUser)
// 其他所有请求都需要认证
.anyRequest().authenticated();
})
@@ -138,7 +147,7 @@ public class SecurityConfig {
* → LoginUser.userData 是 UserData,LoginUser.user 为 null → 拒绝(403)。
* 这样即使某个后台 Controller 忘了标 @PreAuthorize,也不会被玩家 token 调用。
*/
private AuthorizationDecision checkAdminUser(Supplier<? extends Authentication> authentication,
AuthorizationDecision checkAdminUser(Supplier<? extends Authentication> authentication,
RequestAuthorizationContext context) {
Authentication auth = authentication.get();
if (auth == null || !auth.isAuthenticated()) {
@@ -150,6 +159,48 @@ public class SecurityConfig {
}
/**
* /promo/** 的访问判定:只允许「推广后台签发」的 token(loginScope = promo)。
*
* 同一个 tt_user(主播 01 / 招商 04 / 卡商 05)既能登录玩家端也能登录推广后台,
* 两端拿到的 LoginUser(userId + userData)完全一样,光看 userType 区分不了,
* 所以这里必须按登录端标记卡:玩家端签发的 token 即使账号是主播,也进不来推广接口。
*/
AuthorizationDecision checkPromoUser(Supplier<? extends Authentication> authentication,
RequestAuthorizationContext context) {
LoginUser loginUser = loginUserOf(authentication);
if (loginUser == null || loginUser.getUserData() == null) {
return new AuthorizationDecision(false);
}
return new AuthorizationDecision(LoginUser.SCOPE_PROMO.equals(loginUser.getLoginScope()));
}
/**
* /api/** 的访问判定:只允许「玩家端签发」的 token(loginScope = player)。
*
* 推广后台 token(哪怕底层账号就是 tt_user)一律拒绝;后台账号(sys_user)没有 userData,也进不来。
*/
AuthorizationDecision checkPlayerUser(Supplier<? extends Authentication> authentication,
RequestAuthorizationContext context) {
LoginUser loginUser = loginUserOf(authentication);
if (loginUser == null || loginUser.getUserData() == null) {
return new AuthorizationDecision(false);
}
return new AuthorizationDecision(LoginUser.SCOPE_PLAYER.equals(loginUser.getLoginScope()));
}
/**
* 取出已认证的 LoginUser,其余情况(匿名 / 未认证 / 非本框架主体)返回 null
*/
private LoginUser loginUserOf(Supplier<? extends Authentication> authentication) {
Authentication auth = authentication.get();
if (auth == null || !auth.isAuthenticated()) {
return null;
}
return auth.getPrincipal() instanceof LoginUser loginUser ? loginUser : null;
}
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
@@ -61,6 +61,8 @@ public class UserDetailsServiceImpl implements UserDetailsService
public UserDetails createLoginUser(SysUser user)
{
return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user));
LoginUser loginUser = new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user));
loginUser.setLoginScope(LoginUser.SCOPE_ADMIN);
return loginUser;
}
}
@@ -45,6 +45,11 @@ public class WsTokenService {
if (loginUser == null) {
return null;
}
// 只认玩家端 token:推广/后台 token 即使 userId 撞上某个 tt_user 的 id 也不能连玩家 WS
// (与 SecurityConfig.checkPlayerUser 口径一致;本次改造前签发的老 token 无端标记,同样要重新登录)
if (!LoginUser.SCOPE_PLAYER.equals(loginUser.getLoginScope())) {
return null;
}
return loginUser.getUserId().equals(expectedUserId) ? uuid : null;
} catch (Exception e) {
log.warn("WS Token 校验异常, userId={}", expectedUserId, e);
+11 -11
View File
@@ -246,11 +246,11 @@
v-model="item.value"
maxlength="11"
clearable
placeholder="留空则自动生成随机手机号"
placeholder="请输入 11 位手机号(必填)"
/>
</el-form-item>
<div style="font-size:12px;color:#7a664e;padding-left:90px;">
<i class="el-icon-info"></i> 手机号选填:填写后作为登录账号,留空则系统随机生成
<i class="el-icon-info"></i> 手机号必填:该号即推广后台登录账号,登录要用它接收短信验证码
</div>
</el-form>
<div slot="footer" class="dialog-footer">
@@ -292,11 +292,11 @@
v-model="item.value"
maxlength="11"
clearable
placeholder="留空则自动生成随机手机号"
placeholder="请输入 11 位手机号(必填)"
/>
</el-form-item>
<div style="font-size:12px;color:#7a664e;padding-left:90px;">
<i class="el-icon-info"></i> 手机号选填:填写后作为登录账号,留空则系统随机生成
<i class="el-icon-info"></i> 手机号必填:该号即推广后台登录账号,登录要用它接收短信验证码
</div>
</el-form>
<div slot="footer" class="dialog-footer">
@@ -399,9 +399,7 @@
</el-col>
<el-col :span="12">
<el-form-item label="用户类型:" prop="userType">
<span v-if="form.userType === '00'">系统用户</span>
<span v-if="form.userType === '01'">主播</span>
<span v-if="form.userType === '02'">普通用户</span>
<span v-for="dict in dict.type.user_type" v-if="dict.value === form.userType" :key="dict.value">{{ dict.label }}</span>
</el-form-item>
<el-form-item label="用户状态:">
<span v-if="form.status === '0'">正常</span>
@@ -484,7 +482,7 @@ export default {
addNewUser: false,
addRobot: false,
addCardMerchant: false,
// 招商账号-自定义手机号输入列表(每项 { value },留空则后端随机生成)
// 招商账号-手机号输入列表(每项 { value },必填,作为推广后台登录账号)
businessPhones: [{ value: "" }],
// 卡商账号-自定义手机号输入列表
cardPhones: [{ value: "" }],
@@ -675,14 +673,17 @@ export default {
onCardNumberChange(val) {
this.cardPhones = this.buildPhoneInputs(val, this.cardPhones);
},
/* 校验并提取自定义手机号:合法返回数组(留空占位为""),非法返回 null */
/* 校验并提取手机号:全部必填,合法返回数组,非法返回 null */
extractCustomPhones(phones) {
const result = [];
const seen = new Set();
const reg = /^1[3-9]\d{9}$/;
for (let i = 0; i < phones.length; i++) {
const v = (phones[i].value || "").trim();
if (v) {
if (!v) {
this.$modal.msgError(`请填写第 ${i + 1} 个手机号`);
return null;
}
if (!reg.test(v)) {
this.$modal.msgError(`第 ${i + 1} 个手机号格式不正确,请输入 11 位有效手机号`);
return null;
@@ -692,7 +693,6 @@ export default {
return null;
}
seen.add(v);
}
result.push(v);
}
return result;
-17
View File
@@ -1,17 +0,0 @@
<template>
<i-frame :src="url" />
</template>
<script>
import iFrame from "@/components/iFrame/index";
export default {
name: "Swagger",
components: { iFrame },
data() {
return {
// url: process.env.VUE_APP_BASE_API + "/swagger-ui/index.html"
url: process.env.VUE_APP_BASE_API + "/doc.html"
};
},
};
</script>
@@ -1,6 +1,5 @@
package com.ruoyi.domain.dto.packSack;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@@ -12,7 +11,6 @@ import java.util.List;
@Data
public class PackSackCondition {
@ApiModelProperty("背包类型(1我的库存 2提货背包 3福利箱子)")
private Integer packType;
private List<Integer> uidList;
@@ -27,9 +25,7 @@ public class PackSackCondition {
private String endTime;
@ApiModelProperty("价格排序方向(1升序 2降序,null不排序)")
private Integer priceSort;
@ApiModelProperty("时间排序方向(1升序 2降序,null不排序)")
private Integer timeSort;
}
@@ -1,25 +1,19 @@
package com.ruoyi.domain.dto.share;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 分享记录列表查询参数
*/
@Data
@ApiModel("分享记录列表查询参数")
public class ShareRecordQueryDTO {
/** 可选;按被邀请人用户ID过滤 */
@ApiModelProperty("下级用户ID(可选)")
private Long subUserId;
/** 页码(由前端传入) */
@ApiModelProperty(value = "页码", required = true, example = "1")
private Integer pageNum;
/** 每页大小(由前端传入) */
@ApiModelProperty(value = "每页大小", required = true, example = "10")
private Integer pageSize;
}
@@ -1,6 +1,5 @@
package com.ruoyi.domain.other;
import io.swagger.annotations.ApiParam;
import lombok.Data;
import jakarta.validation.constraints.Min;
@@ -9,28 +8,20 @@ import java.math.BigDecimal;
@Data
public class ApiShoppingBody {
@ApiParam("商品名称")
private String name;
@ApiParam("类型(0全部饰品 1匕首 2手枪 3步枪 4微型冲锋枪 5重型武器 6手套 7印花 8其它)")
private String type;
@ApiParam("品质(1普通 2StatTrak™ 3纪念品 4★ 5★ StatTrak™ 6闪耀 7金色 8全息 9闪亮)")
private String quality;
@ApiParam("稀有度")
private String rarity;
@ApiParam("外观(1崭新出厂 2略有磨损 3久经沙场 4残损不堪 5战痕累累 6无涂装)")
private String exterior;
@ApiParam("最高价格")
private BigDecimal maxPrice;
@ApiParam("最低价格")
private BigDecimal minPrice = BigDecimal.ZERO;
@ApiParam("1价格升序 2价格降序 3更新时间升序 4更新时间降序")
private Integer sortBy;
@Min(value = 1, message = "每页长度最小为1")
@@ -39,7 +30,6 @@ public class ApiShoppingBody {
@Min(value = 1, message = "页码最小为1")
private Integer pageNum;
@ApiParam("是否可兑换")
private Boolean isExchange;
}
@@ -1,6 +1,5 @@
package com.ruoyi.domain.other;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import jakarta.validation.constraints.Max;
@@ -12,15 +11,12 @@ import java.util.Map;
@Data
public class CreateFightBody {
@ApiModelProperty("模式(0欧皇 1非酋)")
@NotBlank(message = "模式不能为空")
private String model;
@ApiModelProperty("玩家人数")
@Max(value = 12, message = "最大值12")
private Integer playerNumber;
@ApiModelProperty("组队模式(0或空=个人对战 1=2v2组队,座位0/1为一队、座位2/3为二队)")
private Integer teamMode;
// 所有宝箱
@@ -1,6 +1,5 @@
package com.ruoyi.domain.other;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.NonNull;
@@ -21,12 +20,9 @@ public class CreateOrderParam {
@Min(value = 1, message = "最小值1")
private Integer goodsNum;
@ApiModelProperty("对接qs支付新增:支付方式(alipay/wxpay)")
private String payType;
@ApiModelProperty("1:原有支付,2:图片支付")
private Integer payWay = 1;
@ApiModelProperty("百度推广bdvid")
private String bdVid;
}
@@ -1,17 +1,13 @@
package com.ruoyi.domain.other;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
public class FightBoutData {
@ApiModelProperty("对战ID")
private Integer fightId;
@ApiModelProperty("对战回合号")
private Integer boutNum;
@ApiModelProperty("过期时间")
private Integer expirationTime;
}
@@ -23,6 +23,6 @@ public class PromoWebConfig implements WebMvcConfigurer {
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(promoMaintenanceInterceptor)
.addPathPatterns("/promo/**")
.excludePathPatterns("/promo/login", "/promo/smsLogin", "/promo/register");
.excludePathPatterns("/promo/smsLogin", "/promo/register");
}
}
@@ -19,8 +19,6 @@ import com.ruoyi.domain.entity.sys.PromoAdmin;
import com.ruoyi.domain.entity.sys.TtUser;
import com.ruoyi.promo.domain.entity.PromoCardKey;
import com.ruoyi.promo.mapper.PromoCardKeyMapper;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@@ -32,7 +30,6 @@ import java.util.stream.Collectors;
/**
* 超级管理员专属管理接口
*/
@Api(tags = "超管管理")
@RestController
@RequestMapping("/promo/adminManage")
public class PromoAdminManageController extends BaseController {
@@ -63,7 +60,6 @@ public class PromoAdminManageController extends BaseController {
/**
* 招商管理:查看所有招商及其下属主播汇总数据
*/
@ApiOperation("招商管理列表")
@GetMapping("/businessList")
public R<List<Map<String, Object>>> getBusinessList() {
if (!isSuperAdmin()) {
@@ -207,7 +203,6 @@ public class PromoAdminManageController extends BaseController {
/**
* 主播管理:查看所有主播及其下属用户汇总数据
*/
@ApiOperation("主播管理列表")
@GetMapping("/anchorList")
public R<List<Map<String, Object>>> getAnchorList() {
if (!isSuperAdmin()) {
@@ -305,7 +300,6 @@ public class PromoAdminManageController extends BaseController {
/**
* 卡商管理:查看所有卡商及其卡密记录
*/
@ApiOperation("卡商管理列表")
@GetMapping("/cardMerchantList")
public R<List<Map<String, Object>>> getCardMerchantList() {
if (!isSuperAdmin()) {
@@ -420,7 +414,6 @@ public class PromoAdminManageController extends BaseController {
// ==================== 推广后台管理员账号管理(仅超管) ====================
@ApiOperation("管理员列表")
@GetMapping("/adminList")
public R<List<PromoAdmin>> adminList() {
if (!isSuperAdmin()) {
@@ -432,7 +425,6 @@ public class PromoAdminManageController extends BaseController {
return R.ok(list);
}
@ApiOperation("新增/修改管理员")
@PostMapping("/saveAdmin")
public R<Void> saveAdmin(@RequestBody PromoAdmin body) {
if (!isSuperAdmin()) {
@@ -511,7 +503,6 @@ public class PromoAdminManageController extends BaseController {
.count() > 0;
}
@ApiOperation("删除管理员")
@PostMapping("/deleteAdmin/{id}")
public R<Void> deleteAdmin(@PathVariable("id") Integer id) {
if (!isSuperAdmin()) {
@@ -10,8 +10,6 @@ import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.promo.model.PromoCaptchaCheckRequest;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
@@ -26,7 +24,6 @@ import java.util.concurrent.TimeUnit;
* POST /promo/captcha/gen - 生成验证码
* POST /promo/captcha/check - 校验验证码
*/
@Api(tags = "推广后台滑块验证码")
@Slf4j
@RestController
@RequestMapping("/promo/captcha")
@@ -44,7 +41,6 @@ public class PromoCaptchaController extends BaseController {
/**
* 生成滑块验证码
*/
@ApiOperation("生成滑块验证码")
@Anonymous
@PostMapping("/gen")
public R<Object> genCaptcha() {
@@ -74,7 +70,6 @@ public class PromoCaptchaController extends BaseController {
* 校验滑块验证码
* 通过后在 Redis 写入 promo:captcha:verified:{phoneNumber} = captchaId,有效期 5 分钟
*/
@ApiOperation("校验滑块验证码")
@Anonymous
@PostMapping("/check")
public R<Object> checkCaptcha(@RequestBody PromoCaptchaCheckRequest request) {
@@ -10,8 +10,6 @@ import com.ruoyi.promo.domain.entity.PromoCardKey;
import com.ruoyi.promo.service.PromoCardKeyService;
import com.ruoyi.promo.service.PromoLoginService;
import com.ruoyi.domain.entity.TtRechargeProd;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@@ -20,7 +18,6 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@Api(tags = "卡商卡密管理")
@RestController
@RequestMapping("/promo/cardKey")
public class PromoCardKeyController extends BaseController {
@@ -37,7 +34,6 @@ public class PromoCardKeyController extends BaseController {
/**
* 获取卡商信息(名称、余额)
*/
@ApiOperation("获取卡商信息")
@GetMapping("/merchantInfo")
public R<Map<String, Object>> getMerchantInfo() {
var loginUser = getLoginUser();
@@ -56,7 +52,6 @@ public class PromoCardKeyController extends BaseController {
/**
* 获取充值列表(金额选项)
*/
@ApiOperation("获取充值金额列表")
@GetMapping("/rechargeList")
public R<List<TtRechargeProd>> getRechargeList() {
List<TtRechargeProd> list = promoLoginService.queryList();
@@ -66,7 +61,6 @@ public class PromoCardKeyController extends BaseController {
/**
* 生成卡密
*/
@ApiOperation("生成卡密")
@PostMapping("/generate")
public R<List<String>> generateCardKeys(@RequestBody Map<String, Object> request) {
var loginUser = getLoginUser();
@@ -112,7 +106,6 @@ public class PromoCardKeyController extends BaseController {
/**
* 销毁未使用卡密(软删除并按面值退回卡商余额;已使用的不可销毁)
*/
@ApiOperation("销毁卡密")
@PostMapping("/destroy")
public R<String> destroyCardKey(@RequestBody Map<String, Object> request) {
var loginUser = getLoginUser();
@@ -140,7 +133,6 @@ public class PromoCardKeyController extends BaseController {
/**
* 获取卡密列表
*/
@ApiOperation("获取卡密列表")
@GetMapping("/list")
public R<IPage<PromoCardKey>> getCardKeyList(
@RequestParam(required = false) String usedByName,
@@ -9,8 +9,6 @@ import com.ruoyi.domain.common.constant.UserType;
import com.ruoyi.promo.domain.entity.PromoCdkey;
import com.ruoyi.promo.domain.entity.PromoCdkeyRedeem;
import com.ruoyi.promo.service.PromoCdkeyService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@@ -23,7 +21,6 @@ import java.util.concurrent.TimeUnit;
/**
* 推广CDKEY管理
*/
@Api(tags = "推广CDKEY管理")
@RestController
@RequestMapping("/promo/cdkey")
public class PromoCdkeyController extends BaseController {
@@ -37,7 +34,6 @@ public class PromoCdkeyController extends BaseController {
/**
* 生成CDKEY(仅主播账号可操作)
*/
@ApiOperation("生成CDKEY")
@PostMapping("/generate")
public R<List<String>> generate(@RequestBody Map<String, Object> form) {
var loginUser = getLoginUser();
@@ -70,7 +66,6 @@ public class PromoCdkeyController extends BaseController {
/**
* 获取创建CDKEY记录列表
*/
@ApiOperation("获取创建CDKEY记录列表")
@GetMapping("/createList")
public R<List<PromoCdkey>> createList(
@RequestParam(required = false) String startDate,
@@ -91,7 +86,6 @@ public class PromoCdkeyController extends BaseController {
/**
* 获取兑换CDKEY记录列表
*/
@ApiOperation("获取兑换CDKEY记录列表")
@GetMapping("/redeemList")
public R<List<PromoCdkeyRedeem>> redeemList(
@RequestParam(required = false) String startDate,
@@ -113,7 +107,6 @@ public class PromoCdkeyController extends BaseController {
/**
* 创建CDKEY日报
*/
@ApiOperation("创建CDKEY日报")
@GetMapping("/createDaily")
public R<List<Map<String, Object>>> createDaily(
@RequestParam(required = false) String startDate,
@@ -133,7 +126,6 @@ public class PromoCdkeyController extends BaseController {
/**
* 兑换CDKEY日报
*/
@ApiOperation("兑换CDKEY日报")
@GetMapping("/redeemDaily")
public R<List<Map<String, Object>>> redeemDaily(
@RequestParam(required = false) String startDate,
@@ -20,15 +20,12 @@ import com.ruoyi.admin.config.RedisConstants;
import com.ruoyi.admin.mapper.PromoAdminMapper;
import com.ruoyi.admin.mapper.TtUserMapper;
import com.ruoyi.common.redis.config.RedisLock;
import com.ruoyi.domain.other.ApiLoginBody;
import com.ruoyi.domain.other.ApiVerificationCodeLoginBody;
import com.ruoyi.promo.contract.request.RegisterRequest;
import com.ruoyi.promo.contract.request.UpdateInvitationCodeRequest;
import com.ruoyi.promo.contract.response.GetShareDomainResponse;
import com.ruoyi.promo.service.PromoLoginService;
import com.ruoyi.system.service.ISysConfigService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@@ -40,7 +37,6 @@ import java.util.Map;
import java.util.LinkedHashMap;
import java.util.concurrent.TimeUnit;
@Api(tags = "推广后台用户认证")
@RestController
@RequestMapping("/promo")
public class PromoLoginController extends BaseController {
@@ -63,7 +59,6 @@ public class PromoLoginController extends BaseController {
@Autowired
private RedisLock redisLock;
@ApiOperation("获取推广后台用户信息")
@PostMapping("/getInfo")
public R<Map<String, Object>> getPromoInfo() {
var loginUser = getLoginUser();
@@ -134,7 +129,6 @@ public class PromoLoginController extends BaseController {
return R.ok(info);
}
@ApiOperation("修改个人资料(昵称/手机号/邮箱)")
@PostMapping("/updateProfile")
public R<Void> updateProfile(@RequestBody Map<String, String> body) {
var loginUser = getLoginUser();
@@ -186,7 +180,6 @@ public class PromoLoginController extends BaseController {
return R.ok();
}
@ApiOperation("修改头像(超管,前端裁剪后的 base64)")
@PostMapping("/updateAvatar")
public R<Void> updateAvatar(@RequestBody Map<String, String> body) {
var loginUser = getLoginUser();
@@ -216,7 +209,6 @@ public class PromoLoginController extends BaseController {
return R.ok();
}
@ApiOperation("修改登录密码")
@PostMapping("/updatePassword")
public R<Void> updatePassword(@RequestBody Map<String, String> body) {
var loginUser = getLoginUser();
@@ -263,21 +255,6 @@ public class PromoLoginController extends BaseController {
return R.ok();
}
@ApiOperation("推广后台用户登录")
@Anonymous
@PostMapping("/login")
public AjaxResult login(@RequestBody ApiLoginBody apiLoginBody) {
if ("1".equals(configService.selectConfigByKey("promoMaintenance"))) {
return AjaxResult.error("系统维护中,请稍后再试");
}
AjaxResult ajax = AjaxResult.success();
String token = promoLoginService.login(apiLoginBody.getUsername(), apiLoginBody.getPassword());
ajax.put(Constants.TOKEN, token);
ajax.put("user", null);
return ajax;
}
@ApiOperation("推广后台短信验证码登录")
@Anonymous
@PostMapping("/smsLogin")
public AjaxResult smsLogin(@RequestBody ApiVerificationCodeLoginBody body) {
@@ -294,7 +271,6 @@ public class PromoLoginController extends BaseController {
}
}
@ApiOperation("推广后台用户注册")
@Anonymous
@PostMapping("/register")
public AjaxResult register(@RequestBody RegisterRequest request) {
@@ -311,27 +287,23 @@ public class PromoLoginController extends BaseController {
return ajax;
}
@ApiOperation("推广后台用户修改推广码")
@PostMapping("/updateInvitationCode")
public R updateInvitationCode(@RequestBody @Validated UpdateInvitationCodeRequest request) {
return promoLoginService.updateInvitationCode(request);
}
@ApiOperation("获取主播/招商推广域名")
@GetMapping("/getsharedomain")
public R<GetShareDomainResponse> getShareDomain() {
return promoLoginService.getShareDomain();
}
@ApiOperation("获取充值卡列表")
@GetMapping("/card/list")
public PageDataInfo<TtRechargeProd> cardList() {
List<TtRechargeProd> list = promoLoginService.queryList();
return getPageData(list);
}
@ApiOperation("生成充值卡")
@PostMapping("/generateCard/{rechargeListId}/{num}")
@UpdateUserCache
public R<List<String>> generateCard(@PathVariable("rechargeListId") Integer rechargeListId,
@@ -5,14 +5,11 @@ import com.ruoyi.common.core.domain.R;
import com.ruoyi.domain.common.constant.UserType;
import com.ruoyi.promo.domain.entity.PromoMenuConfig;
import com.ruoyi.promo.service.PromoMenuService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.*;
@Api(tags = "推广后台菜单权限管理")
@RestController
@RequestMapping("/promo/menu")
public class PromoMenuController extends BaseController {
@@ -23,7 +20,6 @@ public class PromoMenuController extends BaseController {
/**
* 获取当前登录用户的菜单权限(根据用户类型)
*/
@ApiOperation("获取当前用户菜单权限")
@GetMapping("/getMenus")
public R<List<String>> getMenus() {
var loginUser = getLoginUser();
@@ -38,7 +34,6 @@ public class PromoMenuController extends BaseController {
/**
* 获取所有可分配的菜单配置(从数据库读取,前端权限页用)
*/
@ApiOperation("获取所有可分配菜单")
@GetMapping("/allMenuConfigs")
public R<List<PromoMenuConfig>> getAllMenuConfigs() {
var loginUser = getLoginUser();
@@ -55,7 +50,6 @@ public class PromoMenuController extends BaseController {
/**
* 超管:获取某角色类型的菜单配置
*/
@ApiOperation("获取角色菜单配置(超管用)")
@GetMapping("/getRoleMenus/{roleType}")
public R<List<String>> getRoleMenus(@PathVariable("roleType") String roleType) {
var loginUser = getLoginUser();
@@ -72,7 +66,6 @@ public class PromoMenuController extends BaseController {
/**
* 超管:保存某角色类型的菜单权限
*/
@ApiOperation("保存角色菜单权限(超管用)")
@PostMapping("/saveRoleMenus")
public R<Void> saveRoleMenus(@RequestBody Map<String, Object> request) {
var loginUser = getLoginUser();
@@ -13,13 +13,12 @@ import com.ruoyi.domain.entity.TtBoxRecords;
import com.ruoyi.domain.entity.TtOrnament;
import com.ruoyi.domain.entity.roll.*;
import com.ruoyi.domain.entity.sys.TtUser;
import com.ruoyi.domain.common.constant.UserType;
import com.ruoyi.domain.common.constant.TtAccountRecordSource;
import com.ruoyi.domain.common.constant.TtboxRecordSource;
import com.ruoyi.admin.service.TtUserService;
import com.ruoyi.admin.mapper.TtUserMapper;
import com.ruoyi.promo.mapper.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
@@ -31,7 +30,6 @@ import java.util.*;
/**
* 推广 ROLL 房管理
*/
@Api(tags = "推广 ROLL 房管理")
@RestController
@RequestMapping("/promo/roll")
@Slf4j
@@ -70,11 +68,24 @@ public class PromoRollController extends BaseController {
/**
* 获取奖池列表(下拉选择用)
*/
@ApiOperation("获取奖池列表")
@GetMapping("/jackpotList")
public R<List<TtRollJackpot>> jackpotList(
@RequestParam(value = "jackpotName", required = false) String jackpotName) {
// 归属校验:只返回自己 Roll 房关联的奖池(此前无过滤,任何推广账号都能看到别人的全部奖池)
List<Integer> myJackpotIds = new ArrayList<>();
for (TtRoll roll : new LambdaQueryChainWrapper<>(promoRollMapper)
.eq(TtRoll::getUserId, getUserId().intValue())
.isNotNull(TtRoll::getJackpotId)
.list()) {
if (!myJackpotIds.contains(roll.getJackpotId())) {
myJackpotIds.add(roll.getJackpotId());
}
}
if (myJackpotIds.isEmpty()) {
return R.ok(new ArrayList<>());
}
LambdaQueryWrapper<TtRollJackpot> wrapper = Wrappers.lambdaQuery();
wrapper.in(TtRollJackpot::getJackpotId, myJackpotIds);
if (StringUtils.isNotEmpty(jackpotName)) {
wrapper.like(TtRollJackpot::getJackpotName, jackpotName);
}
@@ -85,7 +96,6 @@ public class PromoRollController extends BaseController {
/**
* 搜索可用饰品(用于添加到奖池)
*/
@ApiOperation("搜索可用饰品")
@GetMapping("/ornaments")
public R<List<TtOrnament>> searchOrnaments(
@RequestParam(required = false) String name,
@@ -105,7 +115,6 @@ public class PromoRollController extends BaseController {
/**
* 按价格区间搜索饰品(用于模板选择,价格升序)
*/
@ApiOperation("按价格区间搜索饰品(分页)")
@GetMapping("/ornamentsByPrice")
public R<Map<String, Object>> searchOrnamentsByPrice(
@RequestParam(required = false) BigDecimal minPrice,
@@ -142,7 +151,6 @@ public class PromoRollController extends BaseController {
/**
* 获取固定金币饰品列表(唯一Hash名以 jinbi- 开头,按面值升序)
*/
@ApiOperation("获取固定金币饰品列表")
@GetMapping("/coinOrnaments")
public R<List<TtOrnament>> coinOrnaments() {
LambdaQueryWrapper<TtOrnament> wrapper = Wrappers.lambdaQuery();
@@ -154,9 +162,12 @@ public class PromoRollController extends BaseController {
/**
* 获取奖池饰品列表
*/
@ApiOperation("获取奖池饰品列表")
@GetMapping("/jackpotOrnaments/{jackpotId}")
public R<List<TtRollJackpotOrnaments>> jackpotOrnaments(@PathVariable("jackpotId") Integer jackpotId) {
R<String> ownerCheck = checkJackpotOwner(jackpotId);
if (ownerCheck != null) {
return R.fail(ownerCheck.getMsg());
}
List<TtRollJackpotOrnaments> list = new LambdaQueryChainWrapper<>(promoJackpotOrnamentsMapper)
.eq(TtRollJackpotOrnaments::getJackpotId, jackpotId)
.list();
@@ -166,7 +177,6 @@ public class PromoRollController extends BaseController {
/**
* 向奖池添加饰品
*/
@ApiOperation("向奖池添加饰品")
@PostMapping("/addOrnaments")
@Transactional(rollbackFor = Exception.class)
public R<String> addOrnaments(@RequestBody Map<String, Object> body) {
@@ -231,7 +241,6 @@ public class PromoRollController extends BaseController {
/**
* 从奖池移除饰品
*/
@ApiOperation("从奖池移除饰品")
@DeleteMapping("/removeOrnament/{id}")
@Transactional(rollbackFor = Exception.class)
public R<String> removeOrnament(@PathVariable("id") Integer id) {
@@ -266,7 +275,6 @@ public class PromoRollController extends BaseController {
/**
* 更新奖池饰品数量
*/
@ApiOperation("更新奖池饰品数量")
@PutMapping("/updateOrnamentNum")
@Transactional(rollbackFor = Exception.class)
public R<String> updateOrnamentNum(@RequestBody Map<String, Object> body) {
@@ -410,7 +418,6 @@ public class PromoRollController extends BaseController {
/**
* Roll房列表
*/
@ApiOperation("Roll房列表")
@GetMapping("/list")
public R<List<TtRoll>> list(
@RequestParam(required = false) Integer id,
@@ -433,9 +440,13 @@ public class PromoRollController extends BaseController {
/**
* Roll房详情
*/
@ApiOperation("Roll房详情")
@GetMapping("/{rollId}")
public R<TtRoll> getInfo(@PathVariable("rollId") Integer rollId) {
// 归属校验:只能看自己的 Roll 房(此前传任意 id 都能看到别人房间配置)
R<String> ownerCheck = checkRollOwner(rollId);
if (ownerCheck != null) {
return R.fail(ownerCheck.getMsg());
}
TtRoll roll = promoRollMapper.selectById(rollId);
if (roll == null) return R.fail("Roll房不存在");
return R.ok(roll);
@@ -444,7 +455,6 @@ public class PromoRollController extends BaseController {
/**
* 新增Roll房
*/
@ApiOperation("新增Roll房")
@PostMapping
@RepeatSubmit(interval = 3000, message = "Roll房创建中,请勿重复提交")
@Transactional(rollbackFor = Exception.class)
@@ -601,7 +611,6 @@ public class PromoRollController extends BaseController {
/**
* 修改Roll房
*/
@ApiOperation("修改Roll房")
@PutMapping
public R<String> edit(@RequestBody TtRoll ttRoll) {
if (ttRoll.getId() == null) return R.fail("Roll房ID不能为空");
@@ -639,7 +648,6 @@ public class PromoRollController extends BaseController {
/**
* 删除 Roll 房
*/
@ApiOperation("删除 Roll 房")
@DeleteMapping("/{rollId}")
@Transactional(rollbackFor = Exception.class)
public R<String> remove(@PathVariable("rollId") Integer rollId) {
@@ -688,7 +696,6 @@ public class PromoRollController extends BaseController {
/**
* 指定奖池饰品获奖用户
*/
@ApiOperation("指定奖池饰品获奖用户")
@PutMapping("/designateOrnamentUser")
public R<String> designateOrnamentUser(@RequestBody Map<String, Object> body) {
Integer id = body.get("id") != null ? ((Number) body.get("id")).intValue() : null;
@@ -773,7 +780,6 @@ public class PromoRollController extends BaseController {
/**
* Roll房用户列表
*/
@ApiOperation("Roll 房用户列表")
@GetMapping("/users/{rollId}")
public R<List<TtRollUser>> users(@PathVariable("rollId") Integer rollId) {
R<String> ownerCheck = checkRollOwner(rollId);
@@ -793,7 +799,6 @@ public class PromoRollController extends BaseController {
* Roll房中奖名单(参与用户中了什么饰品)
* 数据口径:tt_box_records 中 source=roll房(3) 且 rollId 匹配的记录,为开奖后实际发放的饰品
*/
@ApiOperation("Roll 房中奖名单")
@GetMapping("/winners/{rollId}")
public R<List<Map<String, Object>>> winners(@PathVariable("rollId") Integer rollId) {
R<String> ownerCheck = checkRollOwner(rollId);
@@ -859,36 +864,77 @@ public class PromoRollController extends BaseController {
return null;
}
/**
* 校验奖池归属:奖池必须关联当前登录用户自己的 Roll 房
* (读接口用,不要求"未开奖"——已开奖的房间本人仍然要看自己的奖池)
*/
private R<String> checkJackpotOwner(Integer jackpotId) {
TtRoll roll = findRollByJackpot(jackpotId);
if (roll == null) return R.fail("奖池未关联Roll房");
if (roll.getUserId() == null || !roll.getUserId().equals(getUserId().intValue())) {
return R.fail("无权查看他人奖池");
}
return null;
}
/**
* 奖项模板归属校验:只有创建者本人或推广超管可以改/删
*/
private R<String> checkTemplateOwner(TtRollPrizeTemplate tpl) {
if (tpl == null) return R.fail("模板不存在");
if (isSuperAdmin() || isTemplateCreator(tpl)) return null;
return R.fail("无权操作他人奖项模板");
}
/** 模板是共享库(建房间时可选用任意模板),但 createBy 是创建人手机号,非本人/超管一律置空 */
private void hideTemplateCreatorIfNotMine(List<TtRollPrizeTemplate> list) {
for (TtRollPrizeTemplate tpl : list) {
hideTemplateCreatorIfNotMine(tpl);
}
}
private void hideTemplateCreatorIfNotMine(TtRollPrizeTemplate tpl) {
if (tpl == null || isSuperAdmin() || isTemplateCreator(tpl)) return;
tpl.setCreateBy(null);
}
private boolean isTemplateCreator(TtRollPrizeTemplate tpl) {
var loginUser = getLoginUser();
String currentUserName = (loginUser != null && loginUser.getUserData() != null)
? loginUser.getUserData().getUserName() : null;
return currentUserName != null && currentUserName.equals(tpl.getCreateBy());
}
private boolean isSuperAdmin() {
var loginUser = getLoginUser();
return loginUser != null && loginUser.getUserData() != null
&& UserType.SUPER_ADMIN.getCode().equals(loginUser.getUserData().getUserType());
}
// ==================== 奖项模板管理 ====================
/**
* 获取奖项模板列表
*/
@ApiOperation("获取奖项模板列表")
@GetMapping("/prizeTemplates")
public R<List<TtRollPrizeTemplate>> prizeTemplateList() {
List<TtRollPrizeTemplate> list = new LambdaQueryChainWrapper<>(promoRollPrizeTemplateMapper)
.orderByDesc(TtRollPrizeTemplate::getCreateTime)
.list();
// 补充每个模板的饰品数量
for (TtRollPrizeTemplate tpl : list) {
int count = new LambdaQueryChainWrapper<>(promoRollPrizeTemplateDetailMapper)
.eq(TtRollPrizeTemplateDetail::getTemplateId, tpl.getId())
.count().intValue();
// 用 description 字段临时存数量(不加新字段)
tpl.setCreateBy(tpl.getCreateBy());
}
// 模板是共享库(创建房间时可以选择任意模板),但 createBy 存的是 tt_user.user_name(手机号),
// 只有本人和超管能看到,其他人置空,避免泄露他人手机号
hideTemplateCreatorIfNotMine(list);
return R.ok(list);
}
/**
* 获取奖项模板详情(含饰品列表)
*/
@ApiOperation("获取奖项模板详情")
@GetMapping("/prizeTemplate/{templateId}")
public R<Map<String, Object>> prizeTemplateDetail(@PathVariable("templateId") Integer templateId) {
TtRollPrizeTemplate tpl = promoRollPrizeTemplateMapper.selectById(templateId);
if (tpl == null) return R.fail("模板不存在");
hideTemplateCreatorIfNotMine(tpl);
List<TtRollPrizeTemplateDetail> details = new LambdaQueryChainWrapper<>(promoRollPrizeTemplateDetailMapper)
.eq(TtRollPrizeTemplateDetail::getTemplateId, templateId)
.orderByDesc(TtRollPrizeTemplateDetail::getPrice)
@@ -902,7 +948,6 @@ public class PromoRollController extends BaseController {
/**
* 创建奖项模板
*/
@ApiOperation("创建奖项模板")
@PostMapping("/prizeTemplate")
public R<TtRollPrizeTemplate> createPrizeTemplate(@RequestBody Map<String, Object> body) {
String templateName = body.get("templateName") != null ? body.get("templateName").toString() : null;
@@ -933,7 +978,6 @@ public class PromoRollController extends BaseController {
/**
* 修改奖项模板
*/
@ApiOperation("修改奖项模板")
@PutMapping("/prizeTemplate")
public R<String> updatePrizeTemplate(@RequestBody Map<String, Object> body) {
Integer id = body.get("id") != null ? ((Number) body.get("id")).intValue() : null;
@@ -941,6 +985,9 @@ public class PromoRollController extends BaseController {
TtRollPrizeTemplate existing = promoRollPrizeTemplateMapper.selectById(id);
if (existing == null) return R.fail("模板不存在");
// 归属校验:只有创建者本人或推广超管能改别人的奖项模板
R<String> ownerCheck = checkTemplateOwner(existing);
if (ownerCheck != null) return ownerCheck;
// 校验模板总价值不低于200金币
BigDecimal total = calcTemplateTotal(body);
@@ -967,9 +1014,13 @@ public class PromoRollController extends BaseController {
/**
* 删除奖项模板
*/
@ApiOperation("删除奖项模板")
@DeleteMapping("/prizeTemplate/{templateId}")
public R<String> deletePrizeTemplate(@PathVariable("templateId") Integer templateId) {
TtRollPrizeTemplate existing = promoRollPrizeTemplateMapper.selectById(templateId);
if (existing == null) return R.fail("模板不存在");
// 归属校验:只有创建者本人或推广超管能删别人的奖项模板
R<String> ownerCheck = checkTemplateOwner(existing);
if (ownerCheck != null) return ownerCheck;
promoRollPrizeTemplateMapper.deleteById(templateId);
new LambdaUpdateChainWrapper<>(promoRollPrizeTemplateDetailMapper)
.eq(TtRollPrizeTemplateDetail::getTemplateId, templateId)
@@ -1,20 +1,15 @@
package com.ruoyi.promo.controller;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.ruoyi.admin.mapper.PromoAdminMapper;
import com.ruoyi.admin.service.TtUserService;
import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.domain.common.constant.UserType;
import com.ruoyi.domain.entity.sys.TtUser;
import com.ruoyi.promo.model.PromoPhoneRequest;
import com.ruoyi.promo.service.PromoAccountService;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.thirdparty.common.model.ApiSmsBody;
import com.ruoyi.thirdparty.common.service.ApiSmsService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
@@ -22,39 +17,72 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
/**
* 推广后台短信验证码控制器
* POST /promo/sms/getVerifyCode - 获取短信验证码(需要滑块验证)
* POST /promo/sms/precheck - 点「获取验证码」时的账号前置校验(不弹滑块)
* POST /promo/sms/getVerifyCode - 获取短信验证码(需要滑块验证,此处再兜底校验一次账号)
*/
@Api(tags = "推广后台短信")
@Slf4j
@RestController
@RequestMapping("/promo/sms")
public class PromoSmsController extends BaseController {
/** 前置校验节流:同一手机号 10 分钟最多 10 次,避免被拿来批量探测哪些手机号有推广后台账号 */
private static final int PRECHECK_MAX_COUNT = 10;
private static final int PRECHECK_MINUTES = 10;
private final ISysConfigService sysConfigService;
private final ApiSmsService apiSmsService;
private final PromoAdminMapper promoAdminMapper;
private final PromoAccountService promoAccountService;
@Autowired
private RedisCache redisCache;
@Autowired
private TtUserService userService;
public PromoSmsController(ISysConfigService sysConfigService,
ApiSmsService apiSmsService,
PromoAdminMapper promoAdminMapper) {
PromoAccountService promoAccountService) {
this.sysConfigService = sysConfigService;
this.apiSmsService = apiSmsService;
this.promoAdminMapper = promoAdminMapper;
this.promoAccountService = promoAccountService;
}
/**
* 取验证码前的账号校验:没权限的号直接在这儿被拦住,不用先拖一次滑块再被拒
*/
@Anonymous
@PostMapping("/precheck")
public R<Object> precheck(@RequestBody PromoPhoneRequest request) {
String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance");
if ("1".equals(websiteMaintenance)) {
return R.fail("网站维护中......");
}
String phoneNumber = request.getPhoneNumber();
if (StringUtils.isEmpty(phoneNumber)) {
return R.fail("手机号不能为空");
}
String throttleKey = "promo:sms:precheck:" + phoneNumber;
Long count = redisCache.redisTemplate.opsForValue().increment(throttleKey);
if (count != null && count == 1L) {
redisCache.expire(throttleKey, PRECHECK_MINUTES, TimeUnit.MINUTES);
}
if (count != null && count > PRECHECK_MAX_COUNT) {
return R.fail("操作过于频繁,请" + PRECHECK_MINUTES + "分钟后再试");
}
String denyReason = promoAccountService.checkLoginPermission(phoneNumber, request.getParentInvitationCode());
if (denyReason != null) {
return R.fail(denyReason);
}
return R.ok("校验通过");
}
/**
* 获取短信验证码(推广后台版本)
* 需要先完成/promo/captcha/check 滑块验证
*/
@ApiOperation("获取短信验证码")
@Anonymous
@PostMapping("/getVerifyCode")
public R<Object> getVerifyCode(@RequestBody ApiSmsBody smsBody) {
@@ -69,23 +97,10 @@ public class PromoSmsController extends BaseController {
return R.fail("验证码类型错误");
}
if ("2".equals(smsBody.getType())) {
TtUser user = new LambdaQueryChainWrapper<>(userService.getBaseMapper())
.eq(TtUser::getPhoneNumber, phoneNumber)
.eq(TtUser::getDelFlag, "0")
.eq(TtUser::getStatus, "0")
.one();
boolean hasPromoRole = user != null
&& (UserType.ANCHOR.getCode().equals(user.getUserType())
|| UserType.BUSINESS_USER.getCode().equals(user.getUserType())
|| UserType.CARD_MERCHANT.getCode().equals(user.getUserType()));
// 超级管理员账号(promo_admin.username / phone_number)也可以是手机号,同样允许短信登录
boolean isSuperAdmin = !hasPromoRole && promoAdminMapper.findByAccount(phoneNumber) != null;
// 手机号没注册过:带有效招商邀请码时允许发码(登录即自动注册为主播)
boolean canAutoRegister = !hasPromoRole && !isSuperAdmin
&& StringUtils.isNotEmpty(smsBody.getParentInvitationCode())
&& findBusinessByInvitationCode(smsBody.getParentInvitationCode()) != null;
if (!hasPromoRole && !isSuperAdmin && !canAutoRegister) {
return R.fail("该手机号无推广后台登录权限");
// 兜底:前置校验可以被绕过(直接打接口),真正发短信前必须再验一次
String denyReason = promoAccountService.checkLoginPermission(phoneNumber, smsBody.getParentInvitationCode());
if (denyReason != null) {
return R.fail(denyReason);
}
}
@@ -100,6 +115,8 @@ public class PromoSmsController extends BaseController {
if (StringUtils.isEmpty(storedCaptchaId) || !storedCaptchaId.equals(captchaId)) {
return R.fail("滑块验证已过期或无效,请重新验证");
}
// 一次性消费:同一个 captchaId 在 5 分钟有效期内只能换一条短信,否则可重放刷短信
redisCache.deleteObject(verifiedKey);
String msg = apiSmsService.getVerifyCode(smsBody);
if (StringUtils.isNotEmpty(msg)) {
@@ -113,19 +130,4 @@ public class PromoSmsController extends BaseController {
log.info("[推广后台短信发送成功] phone={}", phoneNumber);
return R.ok("获取验证码成功,请注意短信通知!");
}
/**
* 按邀请码找招商账号(必须是 04 招商)
*/
private TtUser findBusinessByInvitationCode(String invitationCode) {
TtUser parent = new LambdaQueryChainWrapper<>(userService.getBaseMapper())
.eq(TtUser::getInvitationCode, invitationCode.trim().toUpperCase())
.eq(TtUser::getDelFlag, "0")
.eq(TtUser::getStatus, "0")
.one();
if (parent == null || !UserType.BUSINESS_USER.getCode().equals(parent.getUserType())) {
return null;
}
return parent;
}
}
@@ -31,8 +31,6 @@ import com.ruoyi.promo.domain.param.AnchorDayTurnoverParam;
import com.ruoyi.promo.domain.vo.AnchorDayTurnoverVO;
import com.ruoyi.promo.mapper.PromoTurnoverMapper;
import com.ruoyi.promo.service.PromoTurnoverService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -46,7 +44,6 @@ import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
@Api(tags = "推广流水")
@RestController
@RequestMapping("/promo/turnover")
public class PromoTurnoverController extends BaseController {
@@ -66,28 +63,24 @@ public class PromoTurnoverController extends BaseController {
@Autowired
private TtUserBlendErcashMapper ttUserBlendErcashMapper;
@ApiOperation("获取实时数据")
@GetMapping("/getRealTimeData")
public AjaxResult getRealTimeData() {
Long userId = SecurityUtils.getUserId();
return promoTurnoverService.getRealTimeData(userId.intValue());
}
@ApiOperation("获取首页游戏返利看板数据")
@GetMapping("/getGameCommissionData")
public AjaxResult getGameCommissionData() {
Long userId = SecurityUtils.getUserId();
return promoTurnoverService.getGameCommissionData(userId.intValue());
}
@ApiOperation("获取近10天推广数据")
@GetMapping("getLast10DaysPromotionData")
public AjaxResult getLast10DaysPromotionData() {
Long userId = SecurityUtils.getUserId();
return promoTurnoverService.getLast10DaysPromotionData(userId.intValue());
}
@ApiOperation("获取主播推广数据")
@GetMapping("/getAnchorPromotionData")
public TableDataInfo getAnchorPromotionData(AnchorDayTurnoverParam param) {
if (param.getPageNum() == null || param.getPageNum() <= 0) {
@@ -176,7 +169,6 @@ public class PromoTurnoverController extends BaseController {
return pageDataInfo;
}
@ApiOperation("获取下级历史数据")
@GetMapping("/getSubBranches")
public AjaxResult getSubBranches(GetSubBranchesRequest request) {
Long userId = getUserId();
@@ -185,22 +177,28 @@ public class PromoTurnoverController extends BaseController {
return AjaxResult.success(response);
}
@ApiOperation("获取日流水记录")
@GetMapping("/getPurchaseByUserId/{userId}")
public TableDataInfo getPurchaseByUserId(GetPurchaseByUserIdRequest request) {
return promoTurnoverService.getPurchaseByUserId(request);
}
@ApiOperation("获取佣金比例")
@GetMapping("/getCommissionRateByUserId/{userId}")
public AjaxResult getCommissionRateByUserId(@PathVariable("userId") Integer userId) {
// 归属校验:只能查自己或自己直属下级的返佣比例(此前传任意 userId 都能查到别人的比例)
Long currentUserId = getUserId();
if (!userId.equals(currentUserId.intValue())) {
TtUser targetUser = userMapper.selectById(userId);
if (targetUser == null || targetUser.getParentId() == null
|| !targetUser.getParentId().equals(currentUserId.intValue())) {
return AjaxResult.error("无权查看该用户的佣金比例");
}
}
BigDecimal commissionRate = promoTurnoverService.getCommissionRateByUserId(userId);
Map<String, Object> map = new HashMap<>();
map.put("commissionRate", commissionRate);
return AjaxResult.success(map);
}
@ApiOperation("获取佣金比例")
@PutMapping("/updateCommissionRate")
public AjaxResult updateCommissionRate(@RequestBody TtUser ttUser) {
Integer userId = ttUser.getUserId();
@@ -229,7 +227,6 @@ public class PromoTurnoverController extends BaseController {
return row >= 1 ? AjaxResult.success("修改成功") : AjaxResult.error("修改失败");
}
@ApiOperation("获取佣金列表")
@GetMapping("/getCommissionList")
public AjaxResult getCommissionList() {
Long userId = getUserId();
@@ -237,7 +234,6 @@ public class PromoTurnoverController extends BaseController {
return AjaxResult.success(commissionList);
}
@ApiOperation("我的下级列表")
@GetMapping("/mySubordinates")
public AjaxResult mySubordinates(@RequestParam(required = false) String account) {
Long userId = getUserId();
@@ -266,7 +262,6 @@ public class PromoTurnoverController extends BaseController {
return AjaxResult.success(result);
}
@ApiOperation("解除下级关系")
@PostMapping("/removeSubordinate")
public AjaxResult removeSubordinate(@RequestParam("subUserId") Integer subUserId) {
Long currentUserId = getUserId();
@@ -288,7 +283,6 @@ public class PromoTurnoverController extends BaseController {
return success ? AjaxResult.success("解除成功") : AjaxResult.error("解除失败");
}
@ApiOperation("获取已结算佣金总额")
@GetMapping("/settledTotalCommission")
public AjaxResult settledTotalCommission(@RequestParam(required = false) String startDate,
@RequestParam(required = false) String endDate) {
@@ -317,7 +311,6 @@ public class PromoTurnoverController extends BaseController {
return AjaxResult.success(result);
}
@ApiOperation("下级流水记录")
@GetMapping("/flowRecord")
public AjaxResult flowRecord(@RequestParam(required = false) Integer userId,
@RequestParam(required = false) String startDate,
@@ -538,7 +531,6 @@ public class PromoTurnoverController extends BaseController {
}
}
@ApiOperation("招商每日佣金(默认本周,支持 startDate/endDate 自定义范围)")
@GetMapping("/weeklyDailyCommission")
public AjaxResult weeklyDailyCommission(@RequestParam(required = false) String startDate,
@RequestParam(required = false) String endDate) {
@@ -822,7 +814,6 @@ public class PromoTurnoverController extends BaseController {
return dateObj.toString();
}
@ApiOperation("获取主播推广奖励列表")
@GetMapping("/queryPromoteWelfare")
public AjaxResult queryPromoteWelfare(QueryPromoteWelfareRequest request) {
Long userId = getUserId();
@@ -830,7 +821,6 @@ public class PromoTurnoverController extends BaseController {
return AjaxResult.success(response);
}
@ApiOperation("佣金明细记录")
@GetMapping("/commissionDetail")
public AjaxResult commissionDetail(
@RequestParam(required = false) String date,
@@ -896,7 +886,6 @@ public class PromoTurnoverController extends BaseController {
return AjaxResult.success(list);
}
@ApiOperation("佣金日报")
@GetMapping("/commissionDaily")
public AjaxResult commissionDaily(
@RequestParam(required = false) String date,
@@ -945,7 +934,6 @@ public class PromoTurnoverController extends BaseController {
return AjaxResult.success(list);
}
@ApiOperation("佣金汇总")
@GetMapping("/commissionSummary")
public AjaxResult commissionSummary(
@RequestParam(required = false) String startDate,
@@ -1010,7 +998,6 @@ public class PromoTurnoverController extends BaseController {
return userIds;
}
@ApiOperation("下级充值报表")
@GetMapping("/subordinateRecharge")
public AjaxResult subordinateRecharge(
@RequestParam(required = false) String month,
@@ -0,0 +1,14 @@
package com.ruoyi.promo.model;
import lombok.Data;
/**
* 手机号入参(取验证码前置校验用)
*/
@Data
public class PromoPhoneRequest {
private String phoneNumber;
private String parentInvitationCode;
}
@@ -0,0 +1,85 @@
package com.ruoyi.promo.service;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.ruoyi.admin.mapper.PromoAdminMapper;
import com.ruoyi.admin.service.TtUserService;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.domain.common.constant.UserType;
import com.ruoyi.domain.entity.sys.TtUser;
import org.springframework.stereotype.Service;
/**
* 推广后台登录账号校验。
* 点「获取验证码」时先跑一遍(不弹滑块),发短信时再跑一遍兜底,两处必须是同一份规则。
*/
@Service
public class PromoAccountService {
private final TtUserService userService;
private final PromoAdminMapper promoAdminMapper;
public PromoAccountService(TtUserService userService, PromoAdminMapper promoAdminMapper) {
this.userService = userService;
this.promoAdminMapper = promoAdminMapper;
}
/**
* @return null 表示有权登录;否则是给用户看的拒绝原因
*/
public String checkLoginPermission(String phoneNumber, String parentInvitationCode) {
TtUser user = new LambdaQueryChainWrapper<>(userService.getBaseMapper())
.eq(TtUser::getPhoneNumber, phoneNumber)
.eq(TtUser::getDelFlag, "0")
.eq(TtUser::getStatus, "0")
.one();
boolean hasPromoRole = user != null
&& (UserType.ANCHOR.getCode().equals(user.getUserType())
|| UserType.BUSINESS_USER.getCode().equals(user.getUserType())
|| UserType.CARD_MERCHANT.getCode().equals(user.getUserType()));
if (hasPromoRole) {
return null;
}
// 超级管理员账号(promo_admin.username / phone_number)也可以是手机号,同样允许短信登录
if (promoAdminMapper.findByAccount(phoneNumber) != null) {
return null;
}
// 手机号从未注册过 + 有效招商邀请码:允许发码,登录时自动注册为主播(01)。
// 手机号已被占用(普通用户02 / 机器人 / 白号 / 已停用已删除的老号)时不能走自动注册:
// 自动注册会因手机号已存在而跳过,最后只在建 token 时报「用户不存在」,得在这里拦住。
boolean canAutoRegister = !phoneRegistered(phoneNumber)
&& StringUtils.isNotEmpty(parentInvitationCode)
&& findBusinessByInvitationCode(parentInvitationCode) != null;
if (canAutoRegister) {
return null;
}
// 统一提示「无权限」:能走到这里的要么是本来就没推广后台权限的号(含已注册的普通用户),
// 要么是邀请码没填/填错。不区分「已注册」,避免把某个手机号的注册状态暴露出去。
return "该手机号无推广后台登录权限";
}
/**
* 手机号在 tt_user 中是否已存在(含停用/软删除)。
* 必须与 PromoLoginServiceImpl.autoRegisterAnchorIfNeeded 的存在性口径一致,
* 否则两处判断不一致时会出现「发得出验证码但登录报用户不存在」。
*/
private boolean phoneRegistered(String phoneNumber) {
return new LambdaQueryChainWrapper<>(userService.getBaseMapper())
.eq(TtUser::getPhoneNumber, phoneNumber)
.count() > 0;
}
/**
* 按邀请码找招商账号(必须是 04 招商)
*/
private TtUser findBusinessByInvitationCode(String invitationCode) {
TtUser parent = new LambdaQueryChainWrapper<>(userService.getBaseMapper())
.eq(TtUser::getInvitationCode, invitationCode.trim().toUpperCase())
.eq(TtUser::getDelFlag, "0")
.eq(TtUser::getStatus, "0")
.one();
if (parent == null || !UserType.BUSINESS_USER.getCode().equals(parent.getUserType())) {
return null;
}
return parent;
}
}
@@ -11,10 +11,8 @@ import java.util.List;
public interface PromoLoginService {
String login(String username, String password);
/**
* 推广后台短信验证码登录;手机号未注册时,带上级邀请码则自动注册为主播
* 推广后台登录(仅手机号 + 4 位短信验证码);手机号未注册时,带上级邀请码则自动注册为主播
*/
String smsLogin(String phoneNumber, String code, String parentInvitationCode);
@@ -26,7 +26,6 @@ import com.ruoyi.common.utils.uuid.UUID;
import com.ruoyi.domain.common.constant.TtAccountRecordSource;
import com.ruoyi.domain.common.constant.UserType;
import com.ruoyi.domain.entity.TtRechargeProd;
import com.ruoyi.domain.entity.sys.PromoAdmin;
import com.ruoyi.domain.entity.sys.TtUser;
import com.ruoyi.domain.other.TtRedPacket;
import com.ruoyi.domain.other.TtUserAvatar;
@@ -35,6 +34,7 @@ import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.promo.contract.request.RegisterRequest;
import com.ruoyi.promo.contract.request.UpdateInvitationCodeRequest;
import com.ruoyi.promo.contract.response.GetShareDomainResponse;
import com.ruoyi.promo.service.PromoAccountService;
import com.ruoyi.promo.service.PromoLoginService;
import com.ruoyi.system.domain.SysConfig;
import com.ruoyi.system.mapper.SysConfigMapper;
@@ -53,6 +53,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.concurrent.TimeUnit;
@Service
@Slf4j
@@ -83,6 +84,9 @@ public class PromoLoginServiceImpl implements PromoLoginService {
@Autowired
private ApiSmsService apiSmsService;
@Autowired
private PromoAccountService promoAccountService;
@Autowired
private UserIdGenerator userIdGenerator;
@@ -104,110 +108,76 @@ public class PromoLoginServiceImpl implements PromoLoginService {
/** 推广码已修改标记 Redis Key 前缀 */
private static final String INVITATION_CODE_MODIFIED_KEY = "promo:invitation_code_modified:";
/** 登录失败计数:账号维度 / IP 维度,窗口内失败满 10 次锁定 10 分钟 */
private static final String LOGIN_FAIL_ACCOUNT_KEY = "promo:login_fail:account:";
private static final String LOGIN_FAIL_IP_KEY = "promo:login_fail:ip:";
private static final int LOGIN_FAIL_MAX = 10;
private static final int LOGIN_FAIL_LOCK_MINUTES = 10;
/**
* 推广后台账号密码登录(短信未接入前统一走这里):
* ① 超管 → promo_admin(username 或 phone_number)
* ② 招商(04)/主播(01)/卡商(05) → tt_user(账号为手机号)
*/
@Override
public String login(String username, String password) {
sysLoginService.loginPreCheck(username, password);
String account = username.trim();
// ① 超级管理员
PromoAdmin promoAdmin = promoAdminMapper.findByAccount(account);
if (promoAdmin != null) {
if (!SecurityUtils.matchesPassword(password, promoAdmin.getPassword())) {
throw new IllegalArgumentException("账号或密码错误");
}
// 走 promo_ 前缀,确保拿到的是超管身份(promo_admin 表)
LoginUser loginUser = sysLoginService.createLoginUser("promo_" + promoAdmin.getUsername(), password);
return tokenService.createToken(loginUser);
}
// ② 招商/主播/卡商
TtUser user = new LambdaQueryChainWrapper<>(userService.getBaseMapper())
.and(w -> w.eq(TtUser::getPhoneNumber, account).or().eq(TtUser::getUserName, account))
.one();
if (user == null) {
throw new IllegalArgumentException("账号或密码错误");
}
if (!UserType.ANCHOR.getCode().equals(user.getUserType())
&& !UserType.BUSINESS_USER.getCode().equals(user.getUserType())
&& !UserType.CARD_MERCHANT.getCode().equals(user.getUserType())) {
throw new IllegalArgumentException("该账号无推广后台登录权限");
}
if (!SecurityUtils.matchesPassword(password, user.getPassword())) {
throw new IllegalArgumentException("账号或密码错误");
}
return createUserToken(user.getPhoneNumber());
}
/**
* 这个框既能填 4 位短信验证码,也能直接填密码:
* ① 非 4 位数字 → 按密码处理(超管 promo_admin / 招商·主播·卡商 tt_user)
* ② 4 位数字 → 按短信验证码处理
* 推广后台登录:只认「手机号 + 4 位短信验证码」。
* 账号密码登录已下线(/promo/login 一并删除),避免用密码绕过短信校验被撞库。
*/
@Override
public String smsLogin(String phoneNumber, String code, String parentInvitationCode) {
String account = StringUtils.trim(phoneNumber);
String value = StringUtils.trim(code);
PromoAdmin promoAdmin = StringUtils.isNotEmpty(account) ? promoAdminMapper.findByAccount(account) : null;
TtUser ttUser = StringUtils.isNotEmpty(account) ? findUserByAccount(account) : null;
boolean hasPromoRole = ttUser != null
&& !"1".equals(ttUser.getDelFlag())
&& !"1".equals(ttUser.getStatus())
&& (UserType.ANCHOR.getCode().equals(ttUser.getUserType())
|| UserType.BUSINESS_USER.getCode().equals(ttUser.getUserType())
|| UserType.CARD_MERCHANT.getCode().equals(ttUser.getUserType()));
if (promoAdmin == null && ttUser == null && !Validator.isMobile(account)) {
throw new IllegalArgumentException("手机号格式错误,请检查手机号是否输入正确!");
}
boolean isCode = NumberUtil.isNumber(value) && value.length() == 4;
// ① 密码登录
if (!isCode && StringUtils.isNotEmpty(value)) {
if (promoAdmin != null && StringUtils.isNotEmpty(promoAdmin.getPassword())
&& SecurityUtils.matchesPassword(value, promoAdmin.getPassword())) {
// 走 promo_ 前缀,确保拿到的是超管身份
LoginUser loginUser = sysLoginService.createLoginUser("promo_" + promoAdmin.getUsername(), value);
return tokenService.createToken(loginUser);
}
if (hasPromoRole && StringUtils.isNotEmpty(ttUser.getPassword())
&& SecurityUtils.matchesPassword(value, ttUser.getPassword())) {
return createUserToken(ttUser.getPhoneNumber());
}
if (promoAdmin != null || hasPromoRole) {
throw new IllegalArgumentException("密码错误");
}
if (ttUser != null) {
throw new IllegalArgumentException("该账号无推广后台登录权限");
}
throw new IllegalArgumentException("验证码错误");
}
// ② 短信验证码
if (!isCode) {
checkLoginNotLocked(account);
try {
// 非 4 位数字一律按验证码错误处理,不再兼容「填密码」
if (!NumberUtil.isNumber(value) || value.length() != 4) {
throw new IllegalArgumentException("验证码错误");
}
String validateCaptcha = apiSmsService.validateCaptcha(value, "ApiLogin_" + account);
if (!"success".equals(validateCaptcha)) {
throw new IllegalArgumentException(validateCaptcha);
}
// 发码时校验过一次,这里再兜一次:App 登录短信与推广后台共用同一个验证码 key
// (ApiLogin_<手机号>),普通用户拿 App 的码来打本接口时,没有这一步就会走到
// 建 token 才报「用户不存在」。两处必须是同一份口径。
String denyReason = promoAccountService.checkLoginPermission(account, parentInvitationCode);
if (denyReason != null) {
throw new IllegalArgumentException(denyReason);
}
// 手机号没注册过:带上级邀请码则自动注册为主播(口径与 App 的 verificationCodeLoginOrRegister 一致)
autoRegisterAnchorIfNeeded(account, parentInvitationCode);
return createUserToken(account);
String token = createUserToken(account);
clearLoginFail(account);
return token;
} catch (IllegalArgumentException e) {
recordLoginFail(account);
throw e;
}
}
/**
* 按手机号或用户名查 tt_user
* 登录失败计数(账号 + IP 双维度):窗口内失败达到阈值直接拒绝,避免无限撞验证码/刷账号
*/
private TtUser findUserByAccount(String account) {
return new LambdaQueryChainWrapper<>(userService.getBaseMapper())
.and(w -> w.eq(TtUser::getPhoneNumber, account).or().eq(TtUser::getUserName, account))
.one();
private void checkLoginNotLocked(String account) {
if (loginFailCount(LOGIN_FAIL_ACCOUNT_KEY + account) >= LOGIN_FAIL_MAX
|| loginFailCount(LOGIN_FAIL_IP_KEY + IpUtils.getIpAddr()) >= LOGIN_FAIL_MAX) {
throw new IllegalArgumentException("失败次数过多,请 " + LOGIN_FAIL_LOCK_MINUTES + " 分钟后再试");
}
}
private void recordLoginFail(String account) {
bumpLoginFail(LOGIN_FAIL_ACCOUNT_KEY + account);
bumpLoginFail(LOGIN_FAIL_IP_KEY + IpUtils.getIpAddr());
}
private void bumpLoginFail(String key) {
redisCache.setCacheObject(key, loginFailCount(key) + 1, LOGIN_FAIL_LOCK_MINUTES, TimeUnit.MINUTES);
}
/** 登录成功只清账号维度;IP 维度留给它自然过期,避免同一 IP 靠一次成功把失败记录洗掉 */
private void clearLoginFail(String account) {
redisCache.deleteObject(LOGIN_FAIL_ACCOUNT_KEY + account);
}
private int loginFailCount(String key) {
Integer count = redisCache.getCacheObject(key);
return count == null ? 0 : count;
}
/**
@@ -32,8 +32,6 @@ import com.ruoyi.game.mahjong.mapper.MahjongRoundMapper;
import com.ruoyi.game.mahjong.repository.MahjongCycleRepository;
import com.ruoyi.game.mahjong.repository.MahjongRewardPoolCacheRepository;
import com.ruoyi.game.mahjong.service.MahjongDistributionService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
@@ -49,7 +47,6 @@ import java.util.stream.Collectors;
* 与玫瑰女郎逐行同构,差异:只有 normal 一个模式、桶 16 档、封顶 500x、game_type = MAHJONG。
*/
@Slf4j
@Api(tags = "麻将胡了后台")
@RestController
@RequestMapping("/admin/mahjong")
public class MahjongAdminController extends BaseController {
@@ -71,7 +68,6 @@ public class MahjongAdminController extends BaseController {
// ==================== 箱子配置 ====================
@ApiOperation("箱子列表(含各身份目标/实时 RTP 与一轮总回合)")
@GetMapping("/box/list")
public R boxList(@RequestParam(defaultValue = "1") int pageNum,
@RequestParam(defaultValue = "10") int pageSize,
@@ -154,7 +150,6 @@ public class MahjongAdminController extends BaseController {
}
/** 某箱某身份的桶分布:含 convergeCount(收敛账本) + fixedRounds/convergeRounds(按该身份分布反推) */
@ApiOperation("某箱某身份的倍率桶分布")
@GetMapping("/box/buckets/{boxId}")
public R boxBuckets(@PathVariable Long boxId,
@RequestParam(required = false, defaultValue = "common") String audience) {
@@ -174,7 +169,6 @@ public class MahjongAdminController extends BaseController {
}
@Log(title = "箱子配置", action = "保存", businessType = BusinessType.UPDATE)
@ApiOperation("保存某箱某身份的倍率桶分布(items 为固定池回合,反推回写总回合)")
@PostMapping("/box/buckets/save")
@Transactional(rollbackFor = Exception.class)
public R boxBucketsSave(@RequestBody Map<String, Object> params) {
@@ -189,7 +183,6 @@ public class MahjongAdminController extends BaseController {
}
@Log(title = "箱子配置", action = "重算分布", businessType = BusinessType.UPDATE)
@ApiOperation("按目标 RTP 重算某箱某身份分布")
@PostMapping("/box/buckets/calc")
public R calcDistribution(@RequestBody Map<String, Object> params) {
Long boxId = asLong(params.get("boxId"));
@@ -19,8 +19,6 @@ import com.ruoyi.game.mahjong.mapper.MahjongPrizeOrnamentMapper;
import com.ruoyi.game.mahjong.mapper.MahjongRecordMapper;
import com.ruoyi.game.mahjong.service.MahjongGameService;
import com.ruoyi.system.service.ISysConfigService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@@ -30,7 +28,6 @@ import java.util.stream.Collectors;
/** 客户端接口:Base = {host}/api/mahjong,全部 POST + JSON + token 鉴权(对齐设计方案 §7) */
@Slf4j
@Api(tags = "麻将胡了游戏")
@RestController
@RequestMapping("/api/mahjong")
public class MahjongClientController extends BaseController {
@@ -44,7 +41,6 @@ public class MahjongClientController extends BaseController {
/** 进行中的局不展示:结算时 settle_source=start,播完末帧才改 finish;超过单局缓存 TTL(30分钟) 视为已结束兜底 */
private static final long IN_PROGRESS_MS = 30 * 60 * 1000L;
@ApiOperation("获取箱子列表")
@PostMapping("/boxes")
public R boxes() {
List<MahjongBox> list = boxMapper.selectList(new LambdaQueryWrapper<MahjongBox>()
@@ -65,7 +61,6 @@ public class MahjongClientController extends BaseController {
return R.ok(Map.of("list", boxes));
}
@ApiOperation("执行旋转(新游戏或继续游戏)")
@PostMapping("/dospin")
public R dospin(@RequestBody SpinRequest req) {
Long userId = getUserId();
@@ -91,7 +86,6 @@ public class MahjongClientController extends BaseController {
}
}
@ApiOperation("查询游戏记录(分页;scope: 0全服/1我的,默认1我的;只含已播完的局)")
@PostMapping("/records")
public R records(@RequestBody(required = false) RecordPageRequest req) {
Long userId = getUserId();
@@ -169,7 +163,6 @@ public class MahjongClientController extends BaseController {
return exterior == null || exterior.isEmpty() ? name : name + " (" + exterior + ")";
}
@ApiOperation("查询是否有进行中的游戏(断线重连)")
@PostMapping("/hasActive")
public R hasActive() {
Long userId = getUserId();
@@ -15,8 +15,6 @@ import com.ruoyi.game.wheel.constants.WheelPoolType;
import com.ruoyi.game.wheel.service.AdminWheelService;
import com.ruoyi.game.wheel.vo.WheelBetRecordVO;
import com.ruoyi.game.wheel.vo.WheelPoolAdjustmentVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
@@ -27,7 +25,6 @@ import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
@Api(tags = "疯狂转盘")
@Slf4j
@RestController
@RequestMapping("/admin/wheel")
@@ -57,20 +54,17 @@ public class AdminWheelController extends BaseController {
@Autowired
private GameWheelPoolAdjustmentMapper poolAdjustmentMapper;
@ApiOperation("获取疯狂转盘游戏配置")
@GetMapping("/gameconfig")
public AjaxResult getGameConfig() {
return adminWheelService.getGameConfig();
}
@Log(title = "游戏配置", action = "保存", businessType = BusinessType.UPDATE)
@ApiOperation("更新疯狂转盘游戏配置")
@PostMapping("/gameconfig")
public AjaxResult updateGameConfig(@RequestBody String body) {
return adminWheelService.updateGameConfig(body);
}
@ApiOperation("回合记录列表")
@GetMapping("/round/list")
public TableDataInfo roundList(
@RequestParam(required = false) String date,
@@ -88,7 +82,6 @@ public class AdminWheelController extends BaseController {
return getDataTable(list);
}
@ApiOperation("投注记录列表")
@GetMapping("/bet/list")
public TableDataInfo betList(
@RequestParam(required = false) Long roundId,
@@ -101,7 +94,6 @@ public class AdminWheelController extends BaseController {
// ======================== 奖励矩阵 CRUD ========================
@ApiOperation("奖励矩阵列表")
@GetMapping("/matrix/list")
public AjaxResult matrixList(
@RequestParam(required = false) Integer betAmount,
@@ -120,7 +112,6 @@ public class AdminWheelController extends BaseController {
}
@Log(title = "奖励矩阵", action = "保存", businessType = BusinessType.UPDATE)
@ApiOperation("保存奖励矩阵行")
@PostMapping("/matrix/save")
public AjaxResult matrixSave(@RequestBody GameWheelRewardMatrix matrix) {
GameWheelRewardMatrix exist = matrix.getId() == null ? null : rewardMatrixMapper.selectById(matrix.getId());
@@ -145,7 +136,6 @@ public class AdminWheelController extends BaseController {
}
@Log(title = "奖励矩阵", action = "删除", businessType = BusinessType.DELETE)
@ApiOperation("删除奖励矩阵行(软删:置 status=0)")
@DeleteMapping("/matrix/delete/{id}")
public AjaxResult matrixDelete(@PathVariable Long id) {
rewardMatrixMapper.update(null, new LambdaUpdateWrapper<GameWheelRewardMatrix>()
@@ -156,7 +146,6 @@ public class AdminWheelController extends BaseController {
// ======================== 奖池统计 ========================
@ApiOperation("奖池统计")
@GetMapping("/pool/stats")
public AjaxResult poolStats() {
return AjaxResult.success(adminWheelService.buildPoolStats());
@@ -165,7 +154,6 @@ public class AdminWheelController extends BaseController {
// ======================== 奖池调节 ========================
@Log(title = "游戏配置", action = "调整奖池", businessType = BusinessType.GRANT)
@ApiOperation("奖池调整(注水/抽水)")
@PostMapping("/pool/adjust")
@RepeatSubmit(interval = 3000)
public AjaxResult adjustPool(@RequestBody Map<String, Object> params) {
@@ -173,7 +161,6 @@ public class AdminWheelController extends BaseController {
}
@Log(title = "游戏配置", action = "重置奖池", businessType = BusinessType.CLEAN)
@ApiOperation("重置奖池(累计统计与余额一起归零)")
@PostMapping("/pool/reset")
public AjaxResult resetPool(@RequestBody(required = false) Map<String, Object> params) {
Map<String, Object> p = params == null ? Collections.emptyMap() : params;
@@ -182,7 +169,6 @@ public class AdminWheelController extends BaseController {
return adminWheelService.resetPool(poolType, reason, getUsername());
}
@ApiOperation("奖池调整记录")
@GetMapping("/pool/adjustLog")
public TableDataInfo adjustLog(@RequestParam(required = false) String poolType) {
startPage();
@@ -211,7 +197,6 @@ public class AdminWheelController extends BaseController {
return target;
}
@ApiOperation("获取奖池余额(含人工调整)")
@GetMapping("/pool/balance")
public AjaxResult getPoolBalance(@RequestParam(required = false) String poolType) {
if (StringUtils.isEmpty(poolType)) {
@@ -18,8 +18,6 @@ import com.ruoyi.game.wheel.contract.api.response.GetRoundResultsResponse;
import com.ruoyi.game.wheel.service.ApiWheelService;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.user.service.ApiUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
@@ -29,7 +27,6 @@ import org.springframework.web.bind.annotation.RequestMapping;
import java.util.Map;
import org.springframework.web.bind.annotation.RestController;
@Api(tags = "疯狂转盘")
@Slf4j
@RestController
@RequestMapping("/api/wheel")
@@ -55,7 +52,6 @@ public class ApiWheelController extends BaseController {
}
}
@ApiOperation("投注")
@PostMapping("/dobet")
// 这里**不加** @RepeatSubmit:一局允许同一组合重复下注(最多 50 注),连点两次 5 元 = 两注,
// 是正常玩法而不是重复提交;加了会把第二次合法下注挡成"不允许重复提交"。
@@ -82,7 +78,6 @@ public class ApiWheelController extends BaseController {
return apiWheelService.doBet(request, userId);
}
@ApiOperation("获取最新一期游戏状态")
@PostMapping("/last_round_info")
public R<GetRoundInfoResponse> getLastRoundInfo(@RequestBody GetLastRoundInfoRequest request) {
R loginCheck = checkLogin();
@@ -91,7 +86,6 @@ public class ApiWheelController extends BaseController {
return apiWheelService.getLastRoundInfo(userId);
}
@ApiOperation("获取玩家的游戏历史记录")
@PostMapping("/history_bet_info")
public R<GetRoundHistoryResponse> getHistoryBetInfo(@RequestBody GetHistoryBetRequest request) {
R loginCheck = checkLogin();
@@ -100,7 +94,6 @@ public class ApiWheelController extends BaseController {
return apiWheelService.getHistoryBetInfo(request, userId);
}
@ApiOperation("获取疯狂转盘游戏历史记录")
@PostMapping("/round_history_bet_info")
public R<GetRoundHistoryResponse> getRoundHistoryBetInfo(@RequestBody GetRoundHistoryRequest request) {
R loginCheck = checkLogin();
@@ -109,7 +102,6 @@ public class ApiWheelController extends BaseController {
return apiWheelService.getRoundHistoryInfo(request);
}
@ApiOperation("获取历史开奖结果(每一轮的开奖符号)")
@PostMapping("/round_results")
public R<GetRoundResultsResponse> getRoundResults(@RequestBody GetRoundResultsRequest request) {
R loginCheck = checkLogin();
-13
View File
@@ -41,19 +41,6 @@
<version>${ruoyi.version}</version>
</dependency>
<!-- Swagger3依赖 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>${swagger.version}</version>
<!-- <exclusions>-->
<!-- <exclusion>-->
<!-- <groupId>io.swagger</groupId>-->
<!-- <artifactId>swagger-models</artifactId>-->
<!-- </exclusion>-->
<!-- </exclusions>-->
</dependency>
</dependencies>
</project>
@@ -5,15 +5,12 @@ import com.ruoyi.reward.contract.admin.request.AdjustRewardPoolRequest;
import com.ruoyi.reward.contract.admin.response.QueryGlobalRewardConfig;
import com.ruoyi.reward.model.GloalRewardConfig;
import com.ruoyi.reward.service.GlobalRewardPoolService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
@Api(tags = "全局奖池配置")
@Slf4j
@RestController
@RequestMapping("/admin/globalrewardpool")
@@ -21,7 +18,6 @@ public class AdminGlobalRewardPoolController {
@Autowired
private GlobalRewardPoolService globalRewardPoolService;
@ApiOperation("获取奖池配置和状态")
@GetMapping("/config")
public R<QueryGlobalRewardConfig> getGameConfig() {
var config = globalRewardPoolService.getGameConfig();
@@ -37,7 +33,6 @@ public class AdminGlobalRewardPoolController {
return R.ok(result);
}
@ApiOperation("更新奖池配置")
@PostMapping("/config")
public R<Void> updateGameConfig(@RequestBody GloalRewardConfig params) {
String msg = globalRewardPoolService.updateGameConfig(params);
@@ -46,14 +41,12 @@ public class AdminGlobalRewardPoolController {
return R.ok();
}
@ApiOperation("重置奖池缓存")
@PostMapping("/reset")
public R<?> resetRewardPool() {
globalRewardPoolService.resetCache();
return R.ok(null, "奖池缓存已清空");
}
@ApiOperation("调整奖池(抽水/放水)")
@PostMapping("/adjust")
public R<?> adjustRewardPool(@RequestBody AdjustRewardPoolRequest req) {
if (req.getAmount() == null || req.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
@@ -14,7 +14,6 @@ import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.domain.entity.TtBlankCardKey;
import com.ruoyi.domain.entity.TtRechargeProd;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@@ -40,7 +39,6 @@ public class AdminBlankCardKeyController extends BaseController {
/**
* 获取充值产品列表(金额选项)
*/
@ApiOperation("获取充值产品列表")
@GetMapping("/rechargeList")
public R<List<TtRechargeProd>> getRechargeList() {
List<TtRechargeProd> list = rechargeProdMapper.selectList(
@@ -52,7 +50,6 @@ public class AdminBlankCardKeyController extends BaseController {
/**
* 生成空白卡密(管理后台,不扣余额)
*/
@ApiOperation("生成空白卡密")
@PostMapping("/generate")
public R<List<String>> generate(@RequestBody java.util.Map<String, Object> request) {
Integer rechargeListId = (Integer) request.get("rechargeListId");
@@ -99,7 +96,6 @@ public class AdminBlankCardKeyController extends BaseController {
/**
* 获取空白卡密列表
*/
@ApiOperation("获取空白卡密列表")
@GetMapping("/list")
public R<List<TtBlankCardKey>> list(
@RequestParam(required = false) String usedByName,
@@ -9,8 +9,6 @@ import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.page.PageDataInfo;
import com.ruoyi.domain.entity.TtLossRebate;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
@@ -22,7 +20,6 @@ import java.util.List;
* 数据由定时任务 lossRebateService.generateHalfMonthLossRebate() 每半月写入,
* 本页只做「查看 + 对超过单期上限的用户人工发放CDK」。
*/
@Api(tags = "管理端 亏损返水")
@RestController
@RequestMapping("/admin/lossRebate")
public class LossRebateController extends BaseController {
@@ -33,7 +30,6 @@ public class LossRebateController extends BaseController {
this.lossRebateService = lossRebateService;
}
@ApiOperation("亏损返水结算明细列表")
@GetMapping("/list")
public PageDataInfo<TtLossRebate> list(@RequestParam(required = false) String period,
@RequestParam(required = false) Integer userId,
@@ -45,21 +41,18 @@ public class LossRebateController extends BaseController {
@Log(title = "亏损返水", action = "发放CDK", businessType = BusinessType.GRANT,
entityClass = TtLossRebate.class, mapperClass = TtLossRebateMapper.class, idField = "id")
@ApiOperation("人工发放CDK(超过单期上限/自动发放失败的记录)")
@PostMapping("/grant")
public R<String> grant(@RequestBody GrantRequest request) {
if (request == null || request.getId() == null) return R.fail("缺少明细ID");
return lossRebateService.grantManually(request.getId(), request.getAmount(), getUsername());
}
@ApiOperation("读取单期返水上限")
@GetMapping("/config")
public AjaxResult getConfig() {
return AjaxResult.success(lossRebateService.getMaxAmount());
}
@Log(title = "亏损返水", action = "保存上限", businessType = BusinessType.UPDATE)
@ApiOperation("保存单期返水上限(返水超过该金额不自动发放,转人工;0=不设上限)")
@PostMapping("/config")
public AjaxResult saveConfig(@RequestParam(required = false) BigDecimal maxAmount) {
lossRebateService.saveMaxAmount(maxAmount);
@@ -12,15 +12,12 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.page.PageDataInfo;
import com.ruoyi.common.utils.StringUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
import java.util.List;
import java.util.stream.Collectors;
@Api(tags = "管理端 商城管理")
@RestController
@RequestMapping("/admin/shopping")
public class ShoppingController extends BaseController {
@@ -34,7 +31,6 @@ public class ShoppingController extends BaseController {
this.ornamentsService = ornamentsService;
}
@ApiOperation("商品列表")
@GetMapping("/list")
public PageDataInfo<ShoppingDataVO> list(ShoppingBody shoppingBody) {
startPage();
@@ -65,7 +61,6 @@ public class ShoppingController extends BaseController {
}
@Log(title = "商城列表", action = "一键上架", businessType = BusinessType.UPDATE)
@ApiOperation("一键上架所有商品")
@PostMapping("/putAwayAll")
public R<Boolean> putAwayAll() {
LambdaUpdateWrapper<TtOrnament> updateWrapper = new LambdaUpdateWrapper<>();
@@ -82,7 +77,6 @@ public class ShoppingController extends BaseController {
}
@Log(title = "商城列表", action = "一键下架", businessType = BusinessType.UPDATE)
@ApiOperation("一键下架所有商品")
@PostMapping("/soldOutAll")
public R<Boolean> soldOutAll() {
LambdaUpdateWrapper<TtOrnament> updateWrapper = new LambdaUpdateWrapper<>();
@@ -20,8 +20,6 @@ import com.ruoyi.domain.other.TtBoxBody;
import com.ruoyi.domain.vo.BoxPoolStatisticsVO;
import com.ruoyi.domain.vo.TtBoxDataVO;
import com.ruoyi.domain.vo.box.TtBoxUserVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
@@ -38,7 +36,6 @@ import java.util.Arrays;
import java.util.Date;
import java.util.List;
@Api(tags = "管理端 宝箱管理")
@RestController
@RequestMapping("/admin/box")
public class TtBoxController extends BaseController {
@@ -57,7 +54,6 @@ public class TtBoxController extends BaseController {
return success();
}
@ApiOperation("获取宝箱列表")
@GetMapping("/list")
public PageDataInfo<TtBoxDataVO> list(TtBoxBody ttBoxBody) {
startPage();
@@ -80,14 +76,12 @@ public class TtBoxController extends BaseController {
}
@Log(title = "宝箱列表", action = "修改", businessType = BusinessType.UPDATE)
@ApiOperation("修改宝箱")
@PutMapping
public AjaxResult edit(@RequestBody TtBoxDataVO ttBoxDataVO) {
String msg = boxService.updateTtBoxById(ttBoxDataVO);
return StringUtils.isEmpty(msg) ? AjaxResult.success() : AjaxResult.error(msg);
}
@ApiOperation("清空当前奖池")
@GetMapping("clearPrizePool/{boxId}")
public R clearPrizePool(@PathVariable("boxId") Integer boxId) {
@@ -110,27 +104,23 @@ public class TtBoxController extends BaseController {
return AjaxResult.success();
}
@ApiOperation("宝箱统计(按奖池/开箱人类型拆分)")
@GetMapping("/statisticsBoxData/{boxId}")
public R<List<BoxPoolStatisticsVO>> statisticsBoxData(@PathVariable Integer boxId, @RequestParam(required = false) Date date) {
List<BoxPoolStatisticsVO> poolStatistics = boxService.statisticsBoxData(boxId, date);
return R.ok(poolStatistics);
}
@ApiOperation("添加宝箱三级爆率玩家范围")
@PostMapping("/createTtBoxThirdExplosiveUsers")
public AjaxResult createTtBoxThirdExplosiveUsers(@RequestBody TtBoxUserVO ttBoxUserVO) {
String msg = boxService.createTtBoxThirdExplosiveUsers(ttBoxUserVO);
return StringUtils.isEmpty(msg) ? AjaxResult.success() : AjaxResult.error(msg);
}
@ApiOperation("查询宝箱三级爆率玩家范围")
@GetMapping("/queryTtBoxThirdExplosiveUsers/{boxId}")
public AjaxResult queryBoxUsers(@PathVariable Integer boxId) {
return boxService.queryTtBoxThirdExplosiveUsers(boxId);
}
@ApiOperation("删除宝箱三级爆率玩家范围")
@PostMapping("/deleteTtBoxThirdExplosiveUsers")
public AjaxResult deleteTtBoxThirdExplosiveUsers(@RequestBody TtBoxUserVO ttBoxUserVO) {
return boxService.deleteTtBoxThirdExplosiveUsers(ttBoxUserVO);
@@ -143,13 +133,11 @@ public class TtBoxController extends BaseController {
util.exportExcel(response, list.getRows(), "宝箱列表");
}
@ApiOperation("查询开箱临时爆率队列")
@GetMapping("/mockresult/list")
public R<List<BoxMockCache.MockItem>> getBoxMockResult(@RequestParam("uid") Integer uid) {
return R.ok(boxMockService.getMockResultList(uid));
}
@ApiOperation("设置开箱临时爆率队列")
@PostMapping("/mockresult/list")
public R<List<BoxMockCache.MockItem>> updateBoxMockResult(@RequestBody BoxMockRequest request) {
return R.ok(boxMockService.updateMockResultList(request));
@@ -12,8 +12,6 @@ import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.page.PageDataInfo;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
@@ -26,7 +24,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Api(tags = "管理端 宝箱物品")
@RestController
@RequestMapping("/admin/boxOrnaments")
public class TtBoxOrnamentsController extends BaseController {
@@ -37,7 +34,6 @@ public class TtBoxOrnamentsController extends BaseController {
this.boxOrnamentsService = boxOrnamentsService;
}
@ApiOperation("宝箱物品详情")
@GetMapping("/list/{boxId}")
public PageDataInfo<TtBoxOrnamentsDataVO> list(@PathVariable("boxId") Integer boxId) {
// 在分页之前算总数(避免PageHelper干扰百分比计算)
@@ -48,7 +44,6 @@ public class TtBoxOrnamentsController extends BaseController {
return getPageData(list);
}
@ApiOperation("宝箱统计数据")
@GetMapping("/globalData/{boxId}")
public R globalData(@PathVariable("boxId") Integer boxId) {
return boxOrnamentsService.globalData(boxId);
@@ -123,7 +118,6 @@ public class TtBoxOrnamentsController extends BaseController {
}
@Log(title = "宝箱列表", action = "批量填货", businessType = BusinessType.INSERT)
// 宝箱填货
@ApiOperation("宝箱填货")
@PostMapping("/batchAdd")
public AjaxResult batchAdd(@RequestBody batchAddParam param) {
return boxOrnamentsService.batchAdd(param);
@@ -20,7 +20,6 @@ import com.ruoyi.domain.common.constant.UserType;
import com.ruoyi.domain.entity.TtUserBlendErcash;
import com.ruoyi.domain.entity.sys.TtUser;
import com.ruoyi.domain.vo.user.TtUserVo;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
@@ -48,7 +47,6 @@ public class TtCardMerchantController extends BaseController {
@Autowired
private RedisLock redisLock;
@ApiOperation("获取卡商列表")
@GetMapping("/list")
public PageDataInfo<TtUserVo> cardMerchantList(
@RequestParam(required = false) Integer userId,
@@ -86,7 +84,6 @@ public class TtCardMerchantController extends BaseController {
@Log(title = "卡商充值", action = "确认充值", businessType = BusinessType.GRANT,
entityClass = TtUser.class, mapperClass = TtUserMapper.class, idField = "userId")
@ApiOperation("卡商充值(管理员给卡商增加余额)")
@PostMapping("/recharge")
public AjaxResult cardMerchantRecharge(@RequestBody Map<String, Object> params) {
Integer userId = (Integer) params.get("userId");
@@ -154,7 +151,6 @@ public class TtCardMerchantController extends BaseController {
@Log(title = "卡商充值", action = "确认修改", businessType = BusinessType.UPDATE,
entityClass = TtUser.class, mapperClass = TtUserMapper.class, idField = "userId")
@ApiOperation("修改卡商余额(直接设为指定值,按差额写审计流水)")
@PostMapping("/updateBalance")
@Transactional(rollbackFor = Exception.class)
public AjaxResult cardMerchantUpdateBalance(@RequestBody Map<String, Object> params) {
@@ -234,7 +230,6 @@ public class TtCardMerchantController extends BaseController {
}
}
@ApiOperation("卡商充值记录")
@GetMapping("/rechargeLog")
public PageDataInfo<TtUserBlendErcash> cardMerchantRechargeLog(@RequestParam(required = false) Integer userId) {
LambdaQueryChainWrapper<TtUserBlendErcash> wrapper = new LambdaQueryChainWrapper<>(blendErcashMapper);
@@ -11,13 +11,10 @@ import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.page.PageDataInfo;
import com.ruoyi.common.utils.StringUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Api(tags = "管理端 发货管理")
@RestController
@RequestMapping("/admin/deliverGoods")
public class TtDeliveryRecordController extends BaseController {
@@ -28,7 +25,6 @@ public class TtDeliveryRecordController extends BaseController {
this.deliveryRecordService = deliveryRecordService;
}
@ApiOperation("发货申请列表")
@GetMapping("/getDeliveryApplyList")
public PageDataInfo<DeliveryApplyVO> getDeliveryApplyList(TtDeliveryApplyBody deliveryApplyBody) {
startPage();
@@ -36,7 +32,6 @@ public class TtDeliveryRecordController extends BaseController {
return getPageData(list);
}
@ApiOperation("退回发货申请")
@PostMapping("/deliveryFail")
public R<Boolean> deliveryFail(@RequestParam("deliveryRecordId") Integer deliveryRecordId, @RequestParam("message") String message) {
String msg = deliveryRecordService.deliveryFail(deliveryRecordId, message);
@@ -44,14 +39,12 @@ public class TtDeliveryRecordController extends BaseController {
}
@Log(title = "发货记录", action = "确认发货", businessType = BusinessType.GRANT)
@ApiOperation("审核通过发货申请")
@PostMapping("/deliveryApprove")
public R<Boolean> deliveryApprove(@RequestParam("deliveryRecordId") Integer deliveryRecordId) {
String msg = deliveryRecordService.deliveryApprove(deliveryRecordId);
return StringUtils.isEmpty(msg) ? R.ok(true, "操作成功!") : R.fail(false, msg);
}
@ApiOperation("发货记录申请")
@GetMapping("/getDeliveryRecordList")
public PageDataInfo<TtDeliveryRecordDataVO> getDeliveryRecordList(TtDeliveryRecordBody deliveryRecordBody) {
startPage();
@@ -13,13 +13,10 @@ import com.ruoyi.common.core.page.PageDataInfo;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.domain.entity.roll.TtOfficialRollConfig;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Api(tags = "管理端 官方周期Roll配置")
@RestController
@RequestMapping("/admin/officialRollConfig")
public class TtOfficialRollConfigController extends BaseController {
@@ -30,7 +27,6 @@ public class TtOfficialRollConfigController extends BaseController {
this.configService = configService;
}
@ApiOperation("配置列表")
@GetMapping("/list")
public PageDataInfo<TtOfficialRollConfig> list(
@RequestParam(value = "configName", required = false) String configName,
@@ -46,7 +42,6 @@ public class TtOfficialRollConfigController extends BaseController {
return getPageData(configList);
}
@ApiOperation("配置详情")
@GetMapping("/{id}")
public R<TtOfficialRollConfig> getInfo(@PathVariable("id") Integer id) {
return R.ok(configService.getById(id));
@@ -54,7 +49,6 @@ public class TtOfficialRollConfigController extends BaseController {
@Log(title = "官方周期Roll配置", action = "新增", businessType = BusinessType.INSERT,
entityClass = TtOfficialRollConfig.class, mapperClass = TtOfficialRollConfigMapper.class)
@ApiOperation("新增配置")
@PostMapping
public AjaxResult add(@RequestBody TtOfficialRollConfig config) {
config.setCreateBy(getUsername());
@@ -64,7 +58,6 @@ public class TtOfficialRollConfigController extends BaseController {
@Log(title = "官方周期Roll配置", action = "修改", businessType = BusinessType.UPDATE,
entityClass = TtOfficialRollConfig.class, mapperClass = TtOfficialRollConfigMapper.class)
@ApiOperation("编辑配置")
@PutMapping
public AjaxResult edit(@RequestBody TtOfficialRollConfig config) {
config.setUpdateBy(getUsername());
@@ -73,13 +66,11 @@ public class TtOfficialRollConfigController extends BaseController {
}
@Log(title = "官方周期Roll配置", action = "删除", businessType = BusinessType.DELETE)
@ApiOperation("删除配置")
@DeleteMapping("/{id}")
public AjaxResult remove(@PathVariable("id") Integer id) {
return configService.removeById(id) ? AjaxResult.success() : AjaxResult.error("删除失败");
}
@ApiOperation("启用/停用配置")
@PutMapping("/changeStatus")
public AjaxResult changeStatus(@RequestBody TtOfficialRollConfig config) {
if (config.getId() == null) return AjaxResult.error("配置ID不能为空");
@@ -6,14 +6,11 @@ import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.page.PageDataInfo;
import com.ruoyi.domain.dto.sys.OrderQueryCondition;
import com.ruoyi.domain.entity.TtOrder;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Api(tags = "管理端 订单管理")
@RestController
@RequestMapping("/admin/order")
public class TtOrderController extends BaseController {
@@ -24,7 +21,6 @@ public class TtOrderController extends BaseController {
this.ttOrderService = ttOrderService;
}
@ApiOperation("网站支付订单查询")
@PostMapping("/list")
public R list(@RequestBody @Validated OrderQueryCondition condition) {
return ttOrderService.adminList(condition);
@@ -15,8 +15,6 @@ import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.domain.other.TtOrnamentsPrice;
import com.ruoyi.domain.vo.TtOrnamentVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@@ -24,7 +22,6 @@ import org.springframework.web.bind.annotation.*;
import jakarta.servlet.http.HttpServletResponse;
import java.util.List;
@Api(tags = "管理端 饰品管理")
@RestController
@RequestMapping("/admin/ornaments")
public class TtOrnamentsController extends BaseController {
@@ -36,7 +33,6 @@ public class TtOrnamentsController extends BaseController {
private TtOrnamentService ttOrnamentService;
// 饰品列表多条件筛选
@ApiOperation("饰品列表多条件筛选")
@GetMapping("/list")
public R list(TtOrnamentsBody param) {
Page<TtOrnamentVO> ttOrnamentVOPage = ttOrnamentService.listByParam(param);
@@ -59,7 +55,6 @@ public class TtOrnamentsController extends BaseController {
}
@Log(title = "饰品列表", action = "发放饰品", businessType = BusinessType.GRANT)
@ApiOperation("饰品发放")
@PostMapping("/grantOrnaments/{userId}/{ornamentsId}/{num}")
public AjaxResult grantOrnaments(@PathVariable("userId") Integer userId,
@PathVariable("ornamentsId") Long ornamentsId,
@@ -75,7 +70,6 @@ public class TtOrnamentsController extends BaseController {
@Log(title = "饰品列表", action = "新增饰品", businessType = BusinessType.INSERT,
entityClass = TtOrnament.class, mapperClass = TtOrnamentMapper.class)
@ApiOperation("新增饰品")
@PostMapping("/addTtOrnament")
public AjaxResult add(@RequestBody TtOrnament ttOrnament) {
if (ttOrnament.getId() == null) {
@@ -86,14 +80,12 @@ public class TtOrnamentsController extends BaseController {
@Log(title = "饰品列表", action = "编辑", businessType = BusinessType.UPDATE,
entityClass = TtOrnament.class, mapperClass = TtOrnamentMapper.class)
@ApiOperation("修改饰品")
@PostMapping("/editTtOrnament")
public AjaxResult edit(@RequestBody TtOrnament ttOrnament) {
return toAjax(ornamentsService.updateById(ttOrnament));
}
@Log(title = "饰品列表", action = "删除", businessType = BusinessType.DELETE)
@ApiOperation("删除饰品(支持批量)")
@DeleteMapping("/{ids}")
public AjaxResult delete(@PathVariable("ids") List<Long> ids) {
if (ids == null || ids.isEmpty()) {
@@ -10,13 +10,10 @@ import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.page.PageDataInfo;
import com.ruoyi.common.utils.StringUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Api(tags = "管理端 饰品等级信息")
@RestController
@RequestMapping("/admin/ornamentsLevel")
public class TtOrnamentsLevelController extends BaseController {
@@ -27,7 +24,6 @@ public class TtOrnamentsLevelController extends BaseController {
this.ornamentsLevelService = ornamentsLevelService;
}
@ApiOperation("获取所有饰品等级背景")
@GetMapping("/list")
public PageDataInfo<TtOrnamentsLevel> list() {
startPage();
@@ -8,7 +8,6 @@ import com.ruoyi.common.core.page.PageDataInfo;
import com.ruoyi.domain.param.promotion.AnchorDailyTurnoverParam;
import com.ruoyi.domain.param.promotion.AnchorRechargeParam;
import com.ruoyi.domain.vo.promotion.AnchorDailyTurnoverVo;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@@ -22,7 +21,6 @@ public class TtPromoTurnoverController extends BaseController {
@Autowired
private TtPromoTurnoverService ttPromoTurnoverService;
@ApiOperation("获取主播推广数据")
@GetMapping("/getAnchorPromotionData")
public PageDataInfo<AnchorDailyTurnoverVo> getAnchorPromotionData(AnchorDailyTurnoverParam param) {
List<AnchorDailyTurnoverVo> list = ttPromoTurnoverService.getAnchorDailyTurnover(param);
@@ -38,7 +36,6 @@ public class TtPromoTurnoverController extends BaseController {
return pageDataInfo;
}
@ApiOperation("主播充值")
@PostMapping("/recharge")
public AjaxResult recharge(@RequestBody AnchorRechargeParam param) {
return ttPromoTurnoverService.recharge(param);
@@ -13,14 +13,11 @@ import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.page.PageDataInfo;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
@Api(tags = "管理端 红包")
@RestController
@RequestMapping("/admin/redPacket")
public class TtRedPacketController extends BaseController {
@@ -45,7 +42,6 @@ public class TtRedPacketController extends BaseController {
}
@Log(title = "CDK口令", action = "新增", businessType = BusinessType.INSERT)
@ApiOperation("生成红包")
@PostMapping
public AjaxResult add(@RequestBody TtRedPacketDataVO ttRedPacketDataVO) {
List<String> passwordList = redPacketService.insertRedPacket(ttRedPacketDataVO);
@@ -24,8 +24,6 @@ import com.ruoyi.domain.entity.roll.TtRollUser;
import com.ruoyi.domain.entity.sys.TtUser;
import com.ruoyi.domain.vo.TtRollPrizeDataVO;
import com.ruoyi.domain.vo.roll.RollJackpotOrnamentsByPageVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.Data;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@@ -35,7 +33,6 @@ import jakarta.validation.constraints.NotNull;
import java.util.List;
import java.util.Map;
@Api(tags = "管理端 roll房")
@RestController
@RequestMapping("/admin/roll")
public class TtRollController extends BaseController {
@@ -77,7 +74,6 @@ public class TtRollController extends BaseController {
@Log(title = "Roll房列表", action = "新增", businessType = BusinessType.INSERT,
entityClass = TtRoll.class, mapperClass = TtRollMapper.class)
// 创建roll房
@ApiOperation("创建roll房")
@PostMapping
public AjaxResult add(@RequestBody TtRoll ttRoll) {
@@ -88,7 +84,6 @@ public class TtRollController extends BaseController {
}
// roll房拉人
@ApiOperation("roll房拉人")
@PostMapping("/inviteRollUser")
public R inviteRollUser(@RequestBody InviteRollUser InviteRollUser) {
@@ -97,7 +92,6 @@ public class TtRollController extends BaseController {
}
// 获取roll房成员信息
@ApiOperation("获取roll房成员信息")
@GetMapping("getRollUsers/{rollId}")
public AjaxResult getRollUsers(@PathVariable("rollId") Integer rollId) {
return rollService.getRollUsers(rollId);
@@ -106,7 +100,6 @@ public class TtRollController extends BaseController {
@Log(title = "Roll房列表", action = "修改", businessType = BusinessType.UPDATE,
entityClass = TtRoll.class, mapperClass = TtRollMapper.class)
@ApiOperation("编辑roll房")
@PutMapping
public AjaxResult edit(@RequestBody TtRoll ttRoll) {
@@ -117,14 +110,12 @@ public class TtRollController extends BaseController {
}
@Log(title = "Roll房列表", action = "删除", businessType = BusinessType.DELETE)
@ApiOperation("删除roll房")
@DeleteMapping("/remove/{rollId}")
public AjaxResult remove(@PathVariable Long rollId) {
return rollService.removeRollById(rollId, getUsername());
}
// @ApiOperation("roll房奖品列表")
// @PostMapping("/getRollPrizeList")
// public R<List<TtRollPrizeDataVO>> getRollPrizeList(@RequestBody RollPrizesCondition condition) {
//
@@ -133,7 +124,6 @@ public class TtRollController extends BaseController {
// return R.ok(list);
//
// }
@ApiOperation("roll房奖品列表")
@GetMapping("/getRollPrizeList/{rollId}")
public R<List<TtRollPrizeDataVO>> getRollPrizeList(@PathVariable("rollId") Integer rollId) {
@@ -143,40 +133,34 @@ public class TtRollController extends BaseController {
}
@ApiOperation("获取 roll房奖池详情")
@PostMapping("/getRollPrizePool")
public R<RollJackpotOrnamentsByPageVO> getRollPrizePool(@RequestBody @Validated GetRollPrizePool param) {
return rollService.getRollPrizePool(param);
}
@ApiOperation("roll房的指定获奖人名单")
@GetMapping("/rollWinners/{rollId}")
public R rollWinners(@PathVariable Integer rollId) {
return rollUserService.rollWinners(rollId);
}
@ApiOperation("roll房实际中奖记录(开奖后参与用户中了什么饰品)")
@GetMapping("/winningRecords/{rollId}")
public R<List<Map<String, Object>>> winningRecords(@PathVariable("rollId") Integer rollId) {
return rollService.winningRecords(rollId);
}
@Log(title = "装备列表", action = "指定获奖者", businessType = BusinessType.INSERT)
@ApiOperation("指定roll房winner")
@PostMapping("/namedWinner")
public AjaxResult namedWinner(@RequestBody @Validated TtRollPrizeDataVO param) {
return rollService.namedWinner(param);
}
@Log(title = "装备列表", action = "取消奖品", businessType = BusinessType.DELETE)
@ApiOperation("取消指定roll房奖品")
@DeleteMapping("/cancelNamedWinner")
public R cancelNamedWinner(@RequestBody List<Integer> rollUserPrizeIds) {
return rollService.cancelNamedWinner(rollUserPrizeIds);
}
@ApiOperation("获取ROLL房用户")
@GetMapping("/getRollUser/{rollId}")
public AjaxResult getRollUser(@PathVariable("rollId") Integer rollId) {
List<TtUser> rollUserList = rollService.getRollUser(rollId);
@@ -184,13 +168,11 @@ public class TtRollController extends BaseController {
}
@Log(title = "房间用户", action = "加入机器人", businessType = BusinessType.INSERT)
@ApiOperation("加入机器人")
@PostMapping("/addRobot")
public AjaxResult addRobot(@RequestBody AddRobotBody addRobotBody) {
return rollService.addRobot(addRobotBody);
}
@Log(title = "房间用户", action = "加入用户", businessType = BusinessType.INSERT)
@ApiOperation("强制加入普通用户")
@PostMapping("/addUser")
public AjaxResult addUser(@RequestBody AddUserBody addUserBody) {
return rollService.addUser(addUserBody);
@@ -13,14 +13,11 @@ import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.page.PageDataInfo;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
import java.util.List;
@Api(tags = "管理端 roll奖池")
@RestController
@RequestMapping("/admin/rollJackpot")
public class TtRollJackpotController extends BaseController {
@@ -31,7 +28,6 @@ public class TtRollJackpotController extends BaseController {
this.rollJackpotService = rollJackpotService;
}
@ApiOperation("奖池列表")
@GetMapping("/list")
public PageDataInfo<TtRollJackpot> list(@RequestParam(value = "jackpotName", required = false) String jackpotName) {
startPage();
@@ -51,7 +47,6 @@ public class TtRollJackpotController extends BaseController {
@Log(title = "奖池列表", action = "新增", businessType = BusinessType.INSERT,
entityClass = TtRollJackpot.class, mapperClass = TtRollJackpotMapper.class)
@ApiOperation("添加奖池")
@PostMapping
public AjaxResult add(@RequestBody TtRollJackpot ttRollJackpot) {
ttRollJackpot.setCreateBy(getUsername());
@@ -16,14 +16,11 @@ import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.page.PageDataInfo;
import com.ruoyi.common.utils.StringUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
@Api(tags = "管理端 roll奖池物品管理")
@RestController
@RequestMapping("/admin/rollJackpotOrnaments")
public class TtRollJackpotOrnamentsController extends BaseController {
@@ -37,7 +34,6 @@ public class TtRollJackpotOrnamentsController extends BaseController {
this.rollJackpotOrnamentsService = rollJackpotOrnamentsService;
}
@ApiOperation("物品列表")
@GetMapping("/list")
public PageDataInfo<TtRollJackpotOrnamentsDataVO> list(TtRollJackpotOrnamentsBody rollJackpotOrnamentsBody) {
startPage();
@@ -61,7 +57,6 @@ public class TtRollJackpotOrnamentsController extends BaseController {
@Log(title = "奖池列表", action = "修改", businessType = BusinessType.UPDATE,
entityClass = TtRollJackpotOrnaments.class, mapperClass = TtRollJackpotOrnamentsMapper.class)
@ApiOperation("修改奖池道具")
@PutMapping
public AjaxResult edit(@RequestBody TtRollJackpotOrnaments rollJOEdit) {
String msg = rollJackpotOrnamentsService.updateRollJackpotOrnamentsById(rollJOEdit);
@@ -82,7 +77,6 @@ public class TtRollJackpotOrnamentsController extends BaseController {
return toAjax(ok);
}
@ApiOperation("奖池批量填货")
@PostMapping("/batchAdd/{rollJackpotId}/{OrnamentsIds}")
public AjaxResult batchAdd(@PathVariable Integer rollJackpotId, @PathVariable Long[] OrnamentsIds) {
String msg = rollJackpotOrnamentsService.batchAdd(rollJackpotId, Arrays.asList(OrnamentsIds));
@@ -11,14 +11,11 @@ import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.domain.other.TransferApplyBody;
import com.ruoyi.domain.vo.transfer.TransferApplyBatchVO;
import com.ruoyi.domain.vo.transfer.TransferApplyVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.concurrent.TimeUnit;
@Api(tags = "管理端 转赠申请管理")
@RestController
@RequestMapping("/admin/transferApply")
public class TtTransferApplyController extends BaseController {
@@ -32,7 +29,6 @@ public class TtTransferApplyController extends BaseController {
this.redisLock = redisLock;
}
@ApiOperation("转赠申请列表(按饰品逐条)")
@GetMapping("/list")
public PageDataInfo<TransferApplyVO> list(TransferApplyBody body) {
startPage();
@@ -40,7 +36,6 @@ public class TtTransferApplyController extends BaseController {
return getPageData(list);
}
@ApiOperation("转赠申请批次列表(按批次聚合,含饰品明细)")
@GetMapping("/batchList")
public PageDataInfo<TransferApplyBatchVO> batchList(TransferApplyBody body) {
startPage();
@@ -48,7 +43,6 @@ public class TtTransferApplyController extends BaseController {
return getPageData(list);
}
@ApiOperation("审核通过(单条,兼容旧接口)")
@Log(title = "转赠申请", action = "同意", businessType = BusinessType.GRANT)
@PostMapping("/approve")
public R<Boolean> approve(@RequestParam("id") Integer id) {
@@ -64,7 +58,6 @@ public class TtTransferApplyController extends BaseController {
}
}
@ApiOperation("审核驳回(单条,兼容旧接口)")
@Log(title = "转赠申请", action = "驳回", businessType = BusinessType.GRANT)
@PostMapping("/reject")
public R<Boolean> reject(@RequestParam("id") Integer id,
@@ -81,7 +74,6 @@ public class TtTransferApplyController extends BaseController {
}
}
@ApiOperation("批次审核通过(整个批次一起通过)")
@Log(title = "转赠申请", action = "确定同意", businessType = BusinessType.GRANT)
@PostMapping("/approveBatch")
public R<Boolean> approveBatch(@RequestParam("batchId") String batchId) {
@@ -97,7 +89,6 @@ public class TtTransferApplyController extends BaseController {
}
}
@ApiOperation("批次审核驳回(整个批次一起驳回)")
@Log(title = "转赠申请", action = "驳回", businessType = BusinessType.GRANT)
@PostMapping("/rejectBatch")
public R<Boolean> rejectBatch(@RequestParam("batchId") String batchId,
@@ -9,9 +9,6 @@ import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.PageDataInfo;
import com.ruoyi.common.utils.StringUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@@ -25,7 +22,6 @@ import java.util.Arrays;
import java.util.List;
import java.util.Map;
@Api(tags = "管理端 失败物品奖励")
@RestController
@RequestMapping("/admin/upgradeFailOrnaments")
public class TtUpgradeFailOrnamentsController extends BaseController {
@@ -43,7 +39,6 @@ public class TtUpgradeFailOrnamentsController extends BaseController {
@NotNull(message = "参与升级物品id不能为空")
private Integer upgradeOrnamentId;
}
@ApiOperation("失败奖励列表")
@PostMapping("/list")
public PageDataInfo<TtUpgradeFailOrnamentsDataVO> list(@RequestBody listParam param) {
startPage();
@@ -67,7 +62,6 @@ public class TtUpgradeFailOrnamentsController extends BaseController {
private Boolean useAllFlag;
}
@Log(title = "失败奖励", action = "新增", businessType = BusinessType.INSERT)
@ApiOperation("批量添加失败奖励")
@PostMapping("/batchAdd")
public AjaxResult batchAdd(@RequestBody @Validated BatchAddParam param) {
return upgradeFailOrnamentsService.batchAdd(param);
@@ -14,8 +14,6 @@ import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.page.PageDataInfo;
import com.ruoyi.common.utils.StringUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
@@ -24,7 +22,6 @@ import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Api(tags = "管理端 幸运升级")
@RestController
@RequestMapping("/admin/upgradeOrnaments")
public class TtUpgradeOrnamentsController extends BaseController {
@@ -38,7 +35,6 @@ public class TtUpgradeOrnamentsController extends BaseController {
this.upgradeOrnamentsService = upgradeOrnamentsService;
}
@ApiOperation("获取参与幸运升级的物品")
@GetMapping("/list")
public PageDataInfo<TtUpgradeOrnamentsDataVO> list(TtUpgradeOrnamentsBody ttUpgradeOrnamentsBody){
startPage();
@@ -53,7 +49,6 @@ public class TtUpgradeOrnamentsController extends BaseController {
}
@Log(title = "饰品列表", action = "新增", businessType = BusinessType.INSERT)
@ApiOperation("批量填货")
@PostMapping("/batchAdd/{ornamentsIds}")
public AjaxResult batchAdd(@PathVariable Long[] ornamentsIds) {
String msg = upgradeOrnamentsService.batchAdd(Arrays.asList(ornamentsIds));
@@ -10,14 +10,11 @@ import com.ruoyi.domain.other.TtUpgradeRecordBody;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.page.PageDataInfo;
import com.ruoyi.domain.vo.upgrade.UpgradeRecordVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Api(tags = "管理端 幸运升级日志")
@RestController
@RequestMapping("/admin/upgradeRecord")
public class TtUpgradeRecordController extends BaseController {
@@ -28,7 +25,6 @@ public class TtUpgradeRecordController extends BaseController {
this.upgradeRecordService = upgradeRecordService;
}
@ApiOperation("幸运升级日志")
@PostMapping("/list")
public R<Page<UpgradeRecordVO>> list(@RequestBody @Validated UpgradeCondition param){
return upgradeRecordService.adminGetLog(param);
@@ -6,8 +6,6 @@ import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.domain.other.UpgradeWatermarkSettingBody;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@@ -21,7 +19,6 @@ import java.util.Map;
* 后台只配一个「目标水位(%)」:水位高于它 → 随机数中心下移(放水);低于它 → 上移(收紧),
* 长期自动收敛回目标值。随机数的取值范围仍由每个饰品自己的区间限定。
*/
@Api(tags = "幸运升级·奖池水位")
@RestController
@RequestMapping("/admin/upgradeWatermark")
public class TtUpgradeWatermarkController extends BaseController {
@@ -29,13 +26,11 @@ public class TtUpgradeWatermarkController extends BaseController {
@Autowired
private UpgradeWatermarkService watermarkService;
@ApiOperation("当前水位快照 + 目标水位策略 + 按目标水位推算的随机数中心")
@GetMapping("/info")
public R<Map<String, Object>> info() {
return R.ok(watermarkService.info());
}
@ApiOperation("保存目标水位策略(目标水位 + 灵敏度)")
@Log(title = "幸运升级水位策略", action = "保存", businessType = BusinessType.UPDATE)
@PostMapping("/setting")
public R<Map<String, Object>> saveSetting(@RequestBody UpgradeWatermarkSettingBody body) {
@@ -46,7 +41,6 @@ public class TtUpgradeWatermarkController extends BaseController {
return R.ok(watermarkService.info());
}
@ApiOperation("注水/抽水(type 1注水 2抽水;调整原因必填)")
@Log(title = "幸运升级水位", action = "注水/抽水", businessType = BusinessType.GRANT)
@PostMapping("/adjust")
public R<Map<String, Object>> adjust(@RequestBody Map<String, Object> body) {
@@ -60,7 +54,6 @@ public class TtUpgradeWatermarkController extends BaseController {
}
}
@ApiOperation("重置人工调整(插入抵消日志,SUM(delta) 归零,历史保留)")
@Log(title = "幸运升级水位", action = "重置调整", businessType = BusinessType.CLEAN)
@PostMapping("/adjustReset")
public R<Map<String, Object>> adjustReset(@RequestBody(required = false) Map<String, Object> body) {
@@ -72,14 +65,12 @@ public class TtUpgradeWatermarkController extends BaseController {
}
}
@ApiOperation("调整历史(分页)")
@GetMapping("/adjustLog")
public R<Map<String, Object>> adjustLog(@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int size) {
return R.ok(watermarkService.pageLog(page, size));
}
@ApiOperation("按历史记录全量重算累计下注/返奖(纠正 Redis 计数漂移)")
@Log(title = "幸运升级水位", action = "重算累计量", businessType = BusinessType.CLEAN)
@PostMapping("/recalc")
public R<Map<String, Object>> recalc() {
@@ -9,8 +9,6 @@ import com.ruoyi.system.config.ArchiveDaysConfig;
import com.ruoyi.domain.other.TtUserAmountRecordsBody;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.page.PageDataInfo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
@@ -22,7 +20,6 @@ import java.util.List;
import java.util.Map;
@Slf4j
@Api(tags = "管理端 金币账户")
@RestController
@RequestMapping("/admin/userAmountRecords")
public class TtUserAmountRecordsController extends BaseController {
@@ -38,7 +35,6 @@ public class TtUserAmountRecordsController extends BaseController {
this.userAmountRecordsService = userAmountRecordsService;
}
@ApiOperation("获取列表")
@PostMapping("/list")
public R list(@RequestBody @Validated TtUserAmountRecordsBody ttUserAmountRecordsBody) {
return userAmountRecordsService.queryList(ttUserAmountRecordsBody);
@@ -55,7 +51,6 @@ public class TtUserAmountRecordsController extends BaseController {
* - userId 排序 → 快速模式(先分页用户再聚合当页数据,Java内存排序)
* - 聚合字段排序 → 全局排序模式(先聚合所有用户→SQL排序→分页),确保全局排序正确
*/
@ApiOperation("用户流水统计")
@GetMapping("/userTurnover")
public R userTurnover(
@RequestParam(required = false) Integer userId,
@@ -5,8 +5,6 @@ import com.ruoyi.admin.service.TtUserBlendErcashService;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.domain.dto.userRecord.BlendErcashCondition;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
@@ -15,7 +13,6 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Api(tags = "管理端 收支明细")
@RestController
@RequestMapping("/admin/blendErcash")
@Slf4j
@@ -24,13 +21,11 @@ public class TtUserBlendErcashController extends BaseController {
@Autowired
private TtUserBlendErcashService ttUserBlendErcashService;
@ApiOperation("收支明细")
@PostMapping("/list")
public R list(@RequestBody @Validated BlendErcashCondition condition) {
return ttUserBlendErcashService.byCondition(condition);
}
@ApiOperation("收支明细-仅归档表")
@PostMapping("/archive/list")
public R archiveList(@RequestBody @Validated BlendErcashCondition condition) {
return ttUserBlendErcashService.byConditionArchive(condition);
@@ -27,7 +27,6 @@ import com.ruoyi.domain.other.TtUserBody;
import com.ruoyi.domain.other.TtUserPackSackBody;
import com.ruoyi.domain.vo.TtUserPackSackDataVO;
import com.ruoyi.domain.vo.user.TtUserVo;
import io.swagger.annotations.ApiOperation;
import jakarta.servlet.http.HttpServletResponse;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
@@ -89,14 +88,12 @@ public class TtUserController extends BaseController {
}
@Log(title = "用户列表", action = "生成招商账号", businessType = BusinessType.INSERT)
@ApiOperation("生成招商账号")
@PostMapping("/generateAccount")
public void generateAccount(HttpServletResponse response, @RequestBody GenerateAccountBody body) {
List<TtUser> userList = userService.getAccountList(body.getNum(), body.getCustomPhoneNumbers());
userService.generateAccount(response, userList);
}
@ApiOperation("生成账号")
@GetMapping("/generateAccountByPhoneNum")
public R generateAccountByPhoneNum(HttpServletResponse response, @RequestParam("phoneNum") String phoneNum,
@RequestParam("password") String password) {
@@ -105,7 +102,6 @@ public class TtUserController extends BaseController {
}
@Log(title = "用户列表", action = "生成机器人", businessType = BusinessType.INSERT)
@ApiOperation("生成机器人")
@PostMapping("/generateRobot/{num}")
public void generateRobot(HttpServletResponse response, @PathVariable("num") Integer num) {
List<TtUser> userList = userService.getRobotList(num);
@@ -113,7 +109,6 @@ public class TtUserController extends BaseController {
}
@Log(title = "用户列表", action = "生成白号", businessType = BusinessType.INSERT)
@ApiOperation("生成白号")
@PostMapping("/generateBlankAccount")
public void generateBlankAccount(HttpServletResponse response, @RequestBody GenerateAccountBody body) {
List<TtUser> userList = userService.getBlankAccountList(body.getNum(), body.getCustomPhoneNumbers());
@@ -121,7 +116,6 @@ public class TtUserController extends BaseController {
}
@Log(title = "用户列表", action = "生成卡商账号", businessType = BusinessType.INSERT)
@ApiOperation("生成卡商账号")
@PostMapping("/generateCardMerchant")
public void generateCardMerchant(HttpServletResponse response, @RequestBody GenerateAccountBody body) {
List<TtUser> userList = userService.getCardMerchantList(body.getNum(), body.getCustomPhoneNumbers());
@@ -130,7 +124,6 @@ public class TtUserController extends BaseController {
@Log(title = "用户列表", action = "修改", businessType = BusinessType.UPDATE,
entityClass = TtUser.class, mapperClass = TtUserMapper.class)
@ApiOperation("修改用户信息")
@PutMapping
public AjaxResult edit(@RequestBody TtUser ttUser) {
ttUser.setUpdateBy(getUsername());
@@ -146,7 +139,6 @@ public class TtUserController extends BaseController {
@Log(title = "用户列表", action = "修改邀请码", businessType = BusinessType.UPDATE,
entityClass = TtUser.class, mapperClass = TtUserMapper.class)
@ApiOperation("修改邀请码")
@PostMapping("/editInvitationCode")
public AjaxResult editInvitationCode(@RequestBody TtUser ttUser) {
ttUser.setUpdateBy(getUsername());
@@ -225,7 +217,6 @@ public class TtUserController extends BaseController {
@Log(title = "用户列表", action = "重置余额积分", businessType = BusinessType.CLEAN,
entityClass = TtUser.class, mapperClass = TtUserMapper.class, idField = "userId")
@ApiOperation("重置用户余额和积分")
@PostMapping("/resetBalance/{userId}")
public AjaxResult resetUserBalance(@PathVariable("userId") Integer userId){
TtUser ttUser = new TtUser();
@@ -239,7 +230,6 @@ public class TtUserController extends BaseController {
}
@Log(title = "用户列表", action = "真删除", businessType = BusinessType.FORCE)
@ApiOperation("真删除用户(数据库删除,不可恢复)")
@DeleteMapping("/forceDelete/{userId}")
public AjaxResult forceDelete(@PathVariable("userId") Integer userId) {
// 0. 先清登录态:物理删除后 tt_user 查不到该用户,若不清 Redis Token,
@@ -18,8 +18,6 @@ import com.ruoyi.domain.entity.TtOrnament;
import com.ruoyi.domain.entity.lottery.TtTurnoverLottery;
import com.ruoyi.domain.entity.lottery.TtTurnoverLotteryPrize;
import com.ruoyi.domain.entity.lottery.TtTurnoverLotteryRecord;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@@ -31,7 +29,6 @@ import java.util.concurrent.TimeUnit;
/**
* 后台管理 - 流水抽奖配置
*/
@Api(tags = "后台流水抽奖管理")
@RestController
@RequestMapping("/admin/turnoverLottery")
public class TurnoverLotteryAdminController extends BaseController {
@@ -57,7 +54,6 @@ public class TurnoverLotteryAdminController extends BaseController {
/**
* 获取抽奖活动列表
*/
@ApiOperation("获取抽奖活动列表")
@GetMapping("/list")
public PageDataInfo<TtTurnoverLottery> list(
@RequestParam(required = false) String lotteryName,
@@ -92,7 +88,6 @@ public class TurnoverLotteryAdminController extends BaseController {
/**
* 保存抽奖奖品配置
*/
@ApiOperation("保存抽奖奖品")
@PostMapping("/save/{lotteryId}")
public R<String> savePrizes(
@PathVariable("lotteryId") Long lotteryId,
@@ -112,7 +107,6 @@ public class TurnoverLotteryAdminController extends BaseController {
/**
* 更新抽奖活动状态
*/
@ApiOperation("更新抽奖状态")
@PostMapping("/toggleStatus")
public R<String> toggleStatus(@RequestBody Map<String, Object> body) {
Long lotteryId = body.get("lotteryId") != null ? ((Number) body.get("lotteryId")).longValue() : null;
@@ -139,7 +133,6 @@ public class TurnoverLotteryAdminController extends BaseController {
/**
* 创建新的抽奖活动
*/
@ApiOperation("创建抽奖活动")
@PostMapping("/create")
public R<TtTurnoverLottery> create(@RequestBody Map<String, Object> body) {
String lotteryName = body.get("lotteryName") != null ? body.get("lotteryName").toString() : null;
@@ -170,7 +163,6 @@ public class TurnoverLotteryAdminController extends BaseController {
/**
* 删除抽奖活动
*/
@ApiOperation("删除抽奖活动")
@DeleteMapping("/delete/{lotteryId}")
public R<String> delete(@PathVariable("lotteryId") Long lotteryId) {
lotteryMapper.deleteById(lotteryId);
@@ -180,7 +172,6 @@ public class TurnoverLotteryAdminController extends BaseController {
/**
* 搜索饰品 (用于奖品配置时调用)
*/
@ApiOperation("搜索饰品")
@GetMapping("/searchOrnaments")
public R<List<TtOrnament>> searchOrnaments(
@RequestParam(value = "name", required = false) String name,
@@ -205,7 +196,6 @@ public class TurnoverLotteryAdminController extends BaseController {
/**
* 更新抽奖活动基础信息
*/
@ApiOperation("更新抽奖活动")
@PostMapping("/update")
public R<String> update(@RequestBody Map<String, Object> body) {
Long lotteryId = body.get("lotteryId") != null ? ((Number) body.get("lotteryId")).longValue() : null;
@@ -18,8 +18,6 @@ import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.domain.entity.lottery.TtTurnoverLottery;
import com.ruoyi.domain.entity.lottery.TtTurnoverLotteryPrize;
import com.ruoyi.domain.other.TtOrnamentsLevel;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@@ -33,7 +31,6 @@ import java.util.stream.Collectors;
/**
* 流水抽奖奖品管理 (仿宝箱饰品管理 boxOrnaments)
*/
@Api(tags = "流水抽奖奖品管理")
@RestController
@RequestMapping("/admin/turnoverLotteryPrize")
public class TurnoverLotteryPrizeController extends BaseController {
@@ -56,7 +53,6 @@ public class TurnoverLotteryPrizeController extends BaseController {
/**
* 获取奖品列表 (分页,含爆率百分比计算)
*/
@ApiOperation("奖品列表")
@GetMapping("/list/{lotteryId}")
public PageDataInfo<TtTurnoverLotteryPrize> list(@PathVariable("lotteryId") Long lotteryId) {
// 先算总数(避免 PageHelper 干扰百分比计算)
@@ -108,7 +104,6 @@ public class TurnoverLotteryPrizeController extends BaseController {
/**
* 获取单个奖品详情
*/
@ApiOperation("奖品详情")
@GetMapping("/{id}")
public R<TtTurnoverLotteryPrize> getInfo(@PathVariable("id") Long id) {
return R.ok(prizeMapper.selectById(id));
@@ -118,7 +113,6 @@ public class TurnoverLotteryPrizeController extends BaseController {
/**
* 新增奖品
*/
@ApiOperation("新增奖品")
@PostMapping
public AjaxResult add(@RequestBody TtTurnoverLotteryPrize prize) {
if (prize == null || prize.getLotteryId() == null) {
@@ -165,7 +159,6 @@ public class TurnoverLotteryPrizeController extends BaseController {
/**
* 修改奖品
*/
@ApiOperation("修改奖品")
@PutMapping
public AjaxResult edit(@RequestBody TtTurnoverLotteryPrize prize) {
Long lotteryId = prize.getLotteryId();
@@ -211,7 +204,6 @@ public class TurnoverLotteryPrizeController extends BaseController {
/**
* 删除奖品
*/
@ApiOperation("删除奖品")
@DeleteMapping("/{lotteryId}/{ids}")
public AjaxResult remove(@PathVariable Long lotteryId, @PathVariable Long[] ids) {
String lockKey = RedisConstants.TURNOVER_LOTTERY_DRAW_BOX_LOCK + lotteryId;
@@ -9,8 +9,6 @@ import com.ruoyi.common.core.page.PageDataInfo;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.domain.entity.lottery.TtTurnoverLotteryRecord;
import com.ruoyi.domain.entity.lottery.TtTurnoverLottery;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@@ -19,7 +17,6 @@ import java.util.List;
/**
* 后台管理 - 抽奖记录查询
*/
@Api(tags = "后台抽奖记录管理")
@RestController
@RequestMapping("/admin/turnoverLotteryRecord")
public class TurnoverLotteryRecordController extends BaseController {
@@ -33,7 +30,6 @@ public class TurnoverLotteryRecordController extends BaseController {
/**
* 查询抽奖记录列表
*/
@ApiOperation("查询抽奖记录")
@GetMapping("/list")
public PageDataInfo<TtTurnoverLotteryRecord> list(
@RequestParam(value = "userId", required = false) Long userId,
@@ -58,7 +54,6 @@ public class TurnoverLotteryRecordController extends BaseController {
/**
* 获取所有抽奖活动(用于下拉选择)
*/
@ApiOperation("获取抽奖活动选项")
@GetMapping("/lotteryOptions")
public R<List<TtTurnoverLottery>> lotteryOptions() {
LambdaQueryWrapper<TtTurnoverLottery> wrapper = new LambdaQueryWrapper<>();
@@ -6,8 +6,6 @@ import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.redis.config.RedisLock;
import com.ruoyi.common.utils.StringUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@@ -18,7 +16,6 @@ import java.util.concurrent.TimeUnit;
/**
* 用户端 - 流水抽奖
*/
@Api(tags = "用户流水抽奖")
@RestController
@RequestMapping("/api/turnoverLottery")
public class TurnoverLotteryUserController extends BaseController {
@@ -32,7 +29,6 @@ public class TurnoverLotteryUserController extends BaseController {
/**
* 获取用户今日流水统计和可参与的抽奖宝箱
*/
@ApiOperation("获取抽奖信息")
@PostMapping("/myStats")
public R<Map<String, Object>> getMyStats() {
Integer userId = getUserId().intValue();
@@ -42,7 +38,6 @@ public class TurnoverLotteryUserController extends BaseController {
/**
* 执行抽奖
*/
@ApiOperation("执行抽奖")
@PostMapping("/draw")
public R<Map<String, Object>> draw(@RequestBody Map<String, Object> params) {
Integer userId = getUserId().intValue();
@@ -82,7 +77,6 @@ public class TurnoverLotteryUserController extends BaseController {
/**
* 查询我的抽奖记录(分页)
*/
@ApiOperation("我的抽奖记录")
@PostMapping("/myRecords")
public R<Map<String, Object>> myRecords(@RequestBody(required = false) Map<String, Object> params) {
Integer userId = getUserId().intValue();
@@ -109,7 +103,6 @@ public class TurnoverLotteryUserController extends BaseController {
/**
* 查询全服中奖记录(分页,已排除白号/机器人/封禁/已删除用户)
*/
@ApiOperation("全服中奖记录")
@PostMapping("/globalRecords")
public R<Map<String, Object>> globalRecords(@RequestBody(required = false) Map<String, Object> params) {
Long lotteryId = null;
@@ -13,15 +13,12 @@ import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.page.PageDataInfo;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.domain.vo.upgrade.SimpleOrnamentVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
@Api(tags = "管理端 道具管理")
@RestController
@RequestMapping("/admin/websiteProperty")
@Slf4j
@@ -36,7 +33,6 @@ public class WebsitePropertyController extends BaseController {
this.ornamentsService = ornamentsService;
}
// @ApiOperation("获取道具列表")
// @GetMapping("/list")
// public PageDataInfo<WebsitePropertyDataVO> list() {
// startPage();
@@ -44,7 +40,6 @@ public class WebsitePropertyController extends BaseController {
// return getPageData(Arrays.asList());
// }
@ApiOperation("获取道具列表")
@GetMapping("/list")
public TableDataInfo list(OrnamentCondition condition) {
startPage();
@@ -7,7 +7,6 @@ import com.ruoyi.domain.entity.TtOrnament;
import com.ruoyi.domain.entity.TtOrnamentsYY;
import com.ruoyi.domain.vo.WebsitePropertyDataVO;
import com.ruoyi.domain.vo.upgrade.SimpleOrnamentVO;
import io.swagger.models.auth.In;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
@@ -78,7 +78,7 @@ public class ApiUserDetailsServiceImpl implements UserDetailsService {
} else {
userData.setParentInvitationCode(ParentUser.getInvitationCode());
}
return createLoginUser(userData);
return createLoginUser(userData, LoginUser.SCOPE_PLAYER);
} else if (username.startsWith("promo_user_")) {
String phoneNumber = username.substring(11);
TtUser user = new LambdaQueryChainWrapper<>(ttUserService.getBaseMapper())
@@ -93,7 +93,7 @@ public class ApiUserDetailsServiceImpl implements UserDetailsService {
if (hasPromoRole) {
UserData userData = UserData.builder().build();
BeanUtils.copyBeanProp(userData, user);
return createLoginUser(userData);
return createLoginUser(userData, LoginUser.SCOPE_PROMO);
}
// 超级管理员账号(promo_admin.username / phone_number)也可以是手机号
PromoAdmin promoAdmin = promoAdminMapper.findByAccount(phoneNumber);
@@ -105,7 +105,7 @@ public class ApiUserDetailsServiceImpl implements UserDetailsService {
.userType(UserType.SUPER_ADMIN.getCode())
.password(promoAdmin.getPassword())
.build();
return createLoginUser(userData);
return createLoginUser(userData, LoginUser.SCOPE_PROMO);
}
// 以下报错语义与原逻辑保持一致
if (StringUtils.isNull(user)) {
@@ -137,7 +137,7 @@ public class ApiUserDetailsServiceImpl implements UserDetailsService {
.userType(UserType.SUPER_ADMIN.getCode())
.password(promoAdmin.getPassword())
.build();
return createLoginUser(userData);
return createLoginUser(userData, LoginUser.SCOPE_PROMO);
} else {
SysUser user = userService.selectUserByUserName(username);
if (StringUtils.isNull(user)) {
@@ -157,11 +157,19 @@ public class ApiUserDetailsServiceImpl implements UserDetailsService {
}
}
private UserDetails createLoginUser(UserData userData) {
return new LoginUser(userData.getUserId().longValue(), userData);
/**
* 玩家 / 推广端登录:账号前缀决定登录端标记,token 只能用于对应端的接口
*/
private UserDetails createLoginUser(UserData userData, String loginScope) {
LoginUser loginUser = new LoginUser(userData.getUserId().longValue(), userData);
loginUser.setLoginScope(loginScope);
return loginUser;
}
/** 后台账号登录(sys_user) */
private UserDetails createLoginUser(SysUser user) {
return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user));
LoginUser loginUser = new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user));
loginUser.setLoginScope(LoginUser.SCOPE_ADMIN);
return loginUser;
}
}
@@ -12,7 +12,9 @@ public interface TtFightResultService extends IService<TtFightResult> {
FightResultVO getFightResult(Integer fightId);
/**
* 批量获取多个对战的详细结果(替代循环调用 getFightResult 的 N+1 问题)
* 批量获取多个对战的结果(替代循环调用 getFightResult 的 N+1 问题)。
* 只算卡片要展示的金额,开箱明细(fightResult/winnerPrize/endGamePrize)不组装,
* 客户端点卡片进观战/回顾时自己请求 getFightResult 取权威明细。
*
* @param fightIds 对战ID列表
* @return 按入参顺序返回的 FightResultVO 列表(跳过不存在的 fightId)
@@ -38,9 +38,7 @@ public interface TtUserService extends IService<TtUser> {
void generateAccount(HttpServletResponse response, List<TtUser> accountList);
List<TtUser> getAccountList(Integer num);
// 生成招商账号:支持按位置传入自定义手机号(为空的位置随机生成)
// 生成招商账号:手机号必填(推广后台靠手机号收短信验证码登录,随机号收不到码)
List<TtUser> getAccountList(Integer num, List<String> customPhoneNumbers);
TtUser getAccountList(String phoneNumber, String password);
@@ -52,9 +50,7 @@ public interface TtUserService extends IService<TtUser> {
// 生成白号:支持按位置传入自定义手机号(为空的位置随机生成)
List<TtUser> getBlankAccountList(Integer num, List<String> customPhoneNumbers);
List<TtUser> getCardMerchantList(Integer num);
// 生成卡商账号:支持按位置传入自定义手机号(为空的位置随机生成)
// 生成卡商账号:手机号必填(推广后台靠手机号收短信验证码登录,随机号收不到码)
List<TtUser> getCardMerchantList(Integer num, List<String> customPhoneNumbers);
boolean checkUserNameUnique(TtUser user);
@@ -306,7 +306,7 @@ public class TtFightResultServiceImpl extends ServiceImpl<TtFightResultMapper, T
Map<Integer, TtFight> fightMap = fights.stream()
.collect(Collectors.toMap(TtFight::getId, f -> f, (a, b) -> a));
// 2. 批量查询所有座位玩家的 vipLevel
// 2. 批量查询座位玩家 vipLevel + 房主实时昵称(一次查询覆盖两类用户)
List<Integer> allPlayerIds = fights.stream()
.filter(f -> f.getSeats() != null)
.flatMap(f -> f.getSeats().stream())
@@ -314,30 +314,42 @@ public class TtFightResultServiceImpl extends ServiceImpl<TtFightResultMapper, T
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
Map<Integer, Integer> vipMap = new HashMap<>();
if (!allPlayerIds.isEmpty()) {
List<Integer> allUserIds = new ArrayList<>(allPlayerIds);
fights.stream().map(TtFight::getUserId).filter(Objects::nonNull).forEach(allUserIds::add);
allUserIds = allUserIds.stream().distinct().collect(Collectors.toList());
Map<Integer, TtUser> userMap = new HashMap<>();
if (!allUserIds.isEmpty()) {
List<TtUser> users = new LambdaQueryChainWrapper<>(ttUserMapper)
.in(TtUser::getUserId, allPlayerIds)
.select(TtUser::getUserId, TtUser::getVipLevel)
.in(TtUser::getUserId, allUserIds)
.select(TtUser::getUserId, TtUser::getVipLevel, TtUser::getNickName)
.list();
vipMap = users.stream()
.collect(Collectors.toMap(TtUser::getUserId,
u -> u.getVipLevel() != null && u.getVipLevel() > 0 ? u.getVipLevel() - 1 : 0, (a, b) -> a));
userMap = users.stream().filter(Objects::nonNull)
.collect(Collectors.toMap(TtUser::getUserId, u -> u, (a, b) -> a));
}
for (TtFight fight : fights) {
if (fight.getSeats() != null) {
for (FightSeat seat : fight.getSeats()) {
if (seat.getPlayerId() != null) {
seat.setVipLevel(vipMap.getOrDefault(seat.getPlayerId(), 0));
TtUser user = userMap.get(seat.getPlayerId());
Integer vipLevel = user != null ? user.getVipLevel() : null;
seat.setVipLevel(vipLevel != null && vipLevel > 0 ? vipLevel - 1 : 0);
}
}
}
// 用户存在时用实时昵称覆盖;用户被物理删除时保留入库的 ownerNickName
TtUser owner = userMap.get(fight.getUserId());
if (owner != null && owner.getNickName() != null) {
fight.setOwnerNickName(owner.getNickName());
}
}
// 3. 批量查询所有 TtBoxRecords(按 fight_id 分组,只取本场玩家的记录)
// 3. 批量查询所有 TtBoxRecords(按 fight_id 分组,只取本场玩家的记录;只查算金额需要的列)
LambdaQueryWrapper<TtBoxRecords> boxRecordsQuery = new LambdaQueryWrapper<>();
boxRecordsQuery.in(TtBoxRecords::getFightId, fightIds)
.in(TtBoxRecords::getUserId, allPlayerIds);
.in(TtBoxRecords::getUserId, allPlayerIds)
.select(TtBoxRecords::getFightId, TtBoxRecords::getUserId, TtBoxRecords::getHolderUserId,
TtBoxRecords::getOrnamentId, TtBoxRecords::getOrnamentsPrice,
TtBoxRecords::getFightRoundNumber, TtBoxRecords::getCreateTime);
List<TtBoxRecords> allBoxRecords = boxRecordsService.list(boxRecordsQuery);
// 过滤旧数据:回合数不超过对战实际总回合数 + 时间过滤
@@ -377,25 +389,7 @@ public class TtFightResultServiceImpl extends ServiceImpl<TtFightResultMapper, T
.collect(Collectors.toMap(TtBox::getBoxId, b -> b.getBoxName() != null ? b.getBoxName() : "", (a, b) -> a));
}
// 5. 批量查询所有饰品 exteriorName
List<Long> allOrnamentIds = allBoxRecords.stream()
.filter(r -> r.getOrnamentId() != null)
.map(TtBoxRecords::getOrnamentId)
.distinct()
.collect(Collectors.toList());
Map<Long, String> exteriorNameMap = new HashMap<>();
if (!allOrnamentIds.isEmpty()) {
List<TtOrnament> ornaments = new LambdaQueryChainWrapper<>(ttOrnamentMapper)
.in(TtOrnament::getId, allOrnamentIds)
.select(TtOrnament::getId, TtOrnament::getExteriorName)
.list();
exteriorNameMap = ornaments.stream()
.collect(Collectors.toMap(TtOrnament::getId,
o -> normalizeExterior(o.getExteriorName()),
(a, b) -> a));
}
// 6. 逐个构建 FightResultVO
// 5. 逐个构建结果(只算金额,不组装开箱明细列表)
List<FightResultVO> result = new ArrayList<>();
for (Integer fightId : fightIds) {
TtFight fight = fightMap.get(fightId);
@@ -420,46 +414,23 @@ public class TtFightResultServiceImpl extends ServiceImpl<TtFightResultMapper, T
}
}
// 构建 fightBoxVOList
// 箱图只用 boxImg01(boxImg02 客户端零读取);价格/箱名回填到 boxData,客户端按 boxId 取用
Map<String, FightBoxVO> boxData = fight.getBoxData();
ArrayList<FightBoxVO> fightBoxVOList = new ArrayList<>();
if (boxData != null) {
final Map<Integer, BigDecimal> finalBoxPriceMap = boxPriceMap;
final Map<Integer, String> finalBoxNameMap = batchBoxNameMap;
boxData.keySet().stream()
.sorted(Comparator.comparingInt(Integer::parseInt))
.forEach(boxId -> {
Object value = boxData.get(boxId);
if (value instanceof LinkedHashMap) {
Map<String, Object> map = (LinkedHashMap) value;
FightBoxVO vo = new FightBoxVO(
map.get("boxId") != null ? Integer.valueOf(map.get("boxId").toString()) : null,
map.get("number") != null ? Integer.valueOf(map.get("number").toString()) : null,
map.get("boxImg01") != null ? map.get("boxImg01").toString() : null,
map.get("boxImg02") != null ? map.get("boxImg02").toString() : null);
vo.setPrice(finalBoxPriceMap.getOrDefault(vo.getBoxId(), BigDecimal.ZERO));
vo.setBoxName(finalBoxNameMap.getOrDefault(vo.getBoxId(), ""));
fightBoxVOList.add(vo);
} else {
FightBoxVO vo = boxData.get(boxId);
vo.setPrice(finalBoxPriceMap.getOrDefault(vo.getBoxId(), BigDecimal.ZERO));
vo.setBoxName(finalBoxNameMap.getOrDefault(vo.getBoxId(), ""));
fightBoxVOList.add(vo);
for (Map.Entry<String, FightBoxVO> entry : boxData.entrySet()) {
FightBoxVO box = resolveFightBoxVO(entry.getValue());
if (box == null) {
continue;
}
box.setPrice(boxPriceMap.getOrDefault(box.getBoxId(), BigDecimal.ZERO));
box.setBoxName(batchBoxNameMap.getOrDefault(box.getBoxId(), ""));
box.setBoxImg02(null);
entry.setValue(box);
}
});
}
// 分离正常回合结果和赛后奖励
List<TtBoxRecords> roundResults = fightBoxRecords.stream()
.filter(r -> r.getFightRoundNumber() != null && r.getFightRoundNumber() > 0)
.collect(Collectors.toList());
List<TtBoxRecords> endGameRecords = fightBoxRecords.stream()
.filter(r -> r.getFightRoundNumber() == null || r.getFightRoundNumber() <= 0)
.collect(Collectors.toList());
// 胜利方获得的回合饰品(排除赛后奖励)
// 胜利方:DB winner_ids 为空时,从 Redis 对战状态快照兜底(单场处理,异常不影响批量流程)
List<Integer> winnerIds = fight.getWinnerIds() != null ? fight.getWinnerIds() : new ArrayList<>();
// 数据库 winner_ids 为空时,从 Redis 对战状态快照兜底(单个 fight 处理,异常不影响批量流程)
if (winnerIds.isEmpty()) {
try {
FightState state = redisCache.getCacheObject("fight_state:" + fightId);
@@ -471,63 +442,57 @@ public class TtFightResultServiceImpl extends ServiceImpl<TtFightResultMapper, T
log.warn("对战{} 从Redis获取winnerIds兜底失败: {}", fightId, e.getMessage());
}
}
final List<Integer> finalWinnerIds = winnerIds;
List<TtBoxRecords> winnerPrize = roundResults.stream()
.filter(r -> r.getHolderUserId() != null && finalWinnerIds.contains(r.getHolderUserId()))
.filter(r -> r.getOrnamentId() != null)
.collect(Collectors.toList());
// 平分奖:赛后奖励中持有人为赢家的记录,计入赢家所得
winnerPrize.addAll(endGameRecords.stream()
.filter(r -> r.getHolderUserId() != null && finalWinnerIds.contains(r.getHolderUserId()))
.filter(r -> r.getOrnamentId() != null)
.collect(Collectors.toList()));
// 失败方安慰奖
List<TtBoxRecords> endGamePrize = endGameRecords.stream()
.filter(r -> r.getHolderUserId() != null && !finalWinnerIds.contains(r.getHolderUserId()))
.collect(Collectors.toList());
// 填充 exteriorName
List<TtBoxRecords> allPrizeRecords = new ArrayList<>();
allPrizeRecords.addAll(winnerPrize);
allPrizeRecords.addAll(endGamePrize);
final Map<Long, String> finalExteriorNameMap = exteriorNameMap;
allPrizeRecords.forEach(r -> {
if (r.getOrnamentId() != null) {
r.setExteriorName(finalExteriorNameMap.getOrDefault(r.getOrnamentId(), "崭新出厂"));
// 明细列表(fightResult/winnerPrize/endGamePrize)不组装,直接把口径累加成金额:
// 赢家所得 = 持有人为赢家的全部记录(回合饰品 + 赛后平分奖),只算真实饰品(ornamentId 非空);
// 失败方安慰奖 = 赛后记录(回合数<=0)中持有人非赢家的部分。
BigDecimal winnerTotalPrice = BigDecimal.ZERO;
BigDecimal loserTotalPrice = BigDecimal.ZERO;
BigDecimal totalOrnamentsPrice = BigDecimal.ZERO;
for (TtBoxRecords record : fightBoxRecords) {
BigDecimal price = record.getOrnamentsPrice();
if (price == null) {
continue;
}
totalOrnamentsPrice = totalOrnamentsPrice.add(price);
Integer holderUserId = record.getHolderUserId();
if (holderUserId == null) {
continue;
}
if (winnerIds.contains(holderUserId)) {
if (record.getOrnamentId() != null) {
winnerTotalPrice = winnerTotalPrice.add(price);
}
} else if (record.getFightRoundNumber() == null || record.getFightRoundNumber() <= 0) {
loserTotalPrice = loserTotalPrice.add(price);
}
}
});
// 获得的饰品按价格降序,空价格放最后
roundResults.sort(ORNAMENT_PRICE_DESC);
winnerPrize.sort(ORNAMENT_PRICE_DESC);
endGamePrize.sort(ORNAMENT_PRICE_DESC);
// 计算金额
BigDecimal winnerTotalPrice = winnerPrize.stream()
.map(r -> r.getOrnamentsPrice() != null ? r.getOrnamentsPrice() : BigDecimal.ZERO)
.reduce(BigDecimal.ZERO, BigDecimal::add);
BigDecimal loserTotalPrice = endGamePrize.stream()
.map(r -> r.getOrnamentsPrice() != null ? r.getOrnamentsPrice() : BigDecimal.ZERO)
.reduce(BigDecimal.ZERO, BigDecimal::add);
BigDecimal totalOrnamentsPrice = fightBoxRecords.stream()
.filter(r -> r.getOrnamentsPrice() != null)
.map(TtBoxRecords::getOrnamentsPrice)
.reduce(BigDecimal.ZERO, BigDecimal::add);
FightResultVO resultVO = FightResultVO.builder()
result.add(FightResultVO.builder()
.fight(fight)
.winnerIds(winnerIds)
.fightResult(roundResults)
.winnerPrize(winnerPrize)
.endGamePrize(endGamePrize)
.winnerTotalPrice(winnerTotalPrice)
.loserTotalPrice(loserTotalPrice)
.totalOrnamentsPrice(totalOrnamentsPrice)
.fightBoxVOList(fightBoxVOList)
.build();
result.add(resultVO);
.build());
}
return result;
}
/**
* boxData 反序列化后可能是 FightBoxVO,也可能是原始 Map(历史脏数据),统一取成 FightBoxVO。
*/
private FightBoxVO resolveFightBoxVO(Object value) {
if (value == null) return null;
if (value instanceof FightBoxVO) return (FightBoxVO) value;
if (value instanceof LinkedHashMap) {
Map<String, Object> map = (LinkedHashMap) value;
return new FightBoxVO(
map.get("boxId") != null ? Integer.valueOf(map.get("boxId").toString()) : null,
map.get("number") != null ? Integer.valueOf(map.get("number").toString()) : null,
map.get("boxImg01") != null ? map.get("boxImg01").toString() : null,
map.get("boxImg02") != null ? map.get("boxImg02").toString() : null);
}
return null;
}
}
@@ -473,11 +473,6 @@ public class TtUserServiceImpl extends ServiceImpl<TtUserMapper, TtUser> impleme
}
}
@Override
public List<TtUser> getAccountList(Integer num) {
return getAccountList(num, null);
}
@Override
@Transactional(rollbackFor = Exception.class)
public List<TtUser> getAccountList(Integer num, List<String> customPhoneNumbers) {
@@ -490,8 +485,8 @@ public class TtUserServiceImpl extends ServiceImpl<TtUserMapper, TtUser> impleme
.list();
List<TtUser> userList = new ArrayList<>();
for (int i = 0; i < num; i++) {
// 优先使用第 i 个自定义手机号,为空则随机生成
String phoneNumber = getCustomPhoneOrRandom(customPhoneNumbers, i);
// 招商账号必须逐个指定手机号,不随机生成
String phoneNumber = getRequiredPhone(customPhoneNumbers, i, "招商账号");
String nickName = RandomUtils.getRandomName(new Random().nextInt(2));
String password = generateMixedPassword(10);
String invitationCode = getInvitationCode();
@@ -523,7 +518,7 @@ public class TtUserServiceImpl extends ServiceImpl<TtUserMapper, TtUser> impleme
}
/**
* 取第 index 个自定义手机号;为空或越界时返回随机生成的手机号
* 取第 index 个自定义手机号;为空或越界时返回随机生成的手机号(仅白号使用)
*/
private String getCustomPhoneOrRandom(List<String> customPhoneNumbers, int index) {
if (customPhoneNumbers != null && index < customPhoneNumbers.size()
@@ -533,6 +528,22 @@ public class TtUserServiceImpl extends ServiceImpl<TtUserMapper, TtUser> impleme
return getRandomPhoneNumber();
}
/**
* 取第 index 个手机号(必填)。招商/卡商账号是推广后台的登录账号,
* 登录只认「手机号 + 短信验证码」,随机号收不到码就等于发一个登不进去的号,所以不兜底随机生成。
*/
private String getRequiredPhone(List<String> customPhoneNumbers, int index, String label) {
String phone = customPhoneNumbers != null && index < customPhoneNumbers.size()
? StringUtils.trim(customPhoneNumbers.get(index)) : null;
if (StringUtils.isBlank(phone)) {
throw new ServiceException("请填写第 " + (index + 1) + " 个" + label + "的手机号");
}
if (!phone.matches("^1[3-9]\\d{9}$")) {
throw new ServiceException("第 " + (index + 1) + " 个手机号格式不正确,请输入 11 位有效手机号");
}
return phone;
}
/**
* 校验自定义手机号:批次内不可重复,且不可与库中已存在(用户名/手机号)冲突。
* 忽略 del_flag,连软删除记录也拦截,避免直接 insert 触发唯一键冲突。
@@ -722,11 +733,6 @@ public class TtUserServiceImpl extends ServiceImpl<TtUserMapper, TtUser> impleme
return userList;
}
@Override
public List<TtUser> getCardMerchantList(Integer num) {
return getCardMerchantList(num, null);
}
@Override
@Transactional(rollbackFor = Exception.class)
public List<TtUser> getCardMerchantList(Integer num, List<String> customPhoneNumbers) {
@@ -739,8 +745,8 @@ public class TtUserServiceImpl extends ServiceImpl<TtUserMapper, TtUser> impleme
.list();
List<TtUser> userList = new ArrayList<>();
for (int i = 0; i < num; i++) {
// 优先使用第 i 个自定义手机号,为空则随机生成
String phoneNumber = getCustomPhoneOrRandom(customPhoneNumbers, i);
// 卡商账号必须逐个指定手机号,不随机生成
String phoneNumber = getRequiredPhone(customPhoneNumbers, i, "卡商账号");
String nickName = RandomUtils.getRandomName(new Random().nextInt(2));
String password = generateMixedPassword(10);
String invitationCode = getInvitationCode();
@@ -1,7 +1,5 @@
package com.ruoyi.playingmethod.contract.api.request;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
@@ -18,25 +16,20 @@ import jakarta.validation.constraints.Min;
* - boxId 为空:查询全部箱子的记录
* - boxId 不为空:仅查询当前选中箱子的记录
*/
@ApiModel("经典开箱记录查询参数")
@AllArgsConstructor
@NoArgsConstructor
@Data
@Builder
public class ClassicBoxRecordPageRequest {
@ApiModelProperty("页码,默认 1")
@Min(value = 1, message = "页码最小 1")
private Integer page = 1;
@ApiModelProperty("每页条数,默认 20")
@Range(min = 1, max = 50, message = "每页条数 1-50")
private Integer size = 20;
@ApiModelProperty("选中箱子 ID:为空则查询全部箱子的记录,不为空则仅查询该箱子")
private Integer boxId;
@ApiModelProperty("排序方式:0-时间降序,1-时间升序,2-价格降序,默认 0")
@Builder.Default
private Integer orderByFie = 0;
}
@@ -1,6 +1,5 @@
package com.ruoyi.playingmethod.contract.api.request;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@@ -15,11 +14,9 @@ public class GetRollListParamRequest {
private Integer rollId;
// 0官方 1主播
@ApiModelProperty("0官方 1主播")
private Integer rollType;
// 0全部 1可参与 2我的 3已结束
@ApiModelProperty("0全部 1可参与 2我的 3已结束")
private Integer rollStatus;
private Integer userId;
@@ -33,10 +33,6 @@ import com.ruoyi.playingmethod.contract.api.request.ClassicBoxRecordPageRequest;
import com.ruoyi.playingmethod.service.ApiBindBoxService;
import com.ruoyi.playingmethod.service.ApiBoxRecordsService;
import com.ruoyi.system.service.ISysConfigService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
@@ -46,7 +42,6 @@ import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
@Api(tags = "玩转盲盒模式")
@Slf4j
@RestController
@RequestMapping("/api/bindbox")
@@ -79,7 +74,6 @@ public class ApiBindBoxController extends BaseController {
@Autowired
private RedisLock redisLock;
@ApiOperation("获取宝箱信息")
@Anonymous
@PostMapping("/boxData")
public R<TtBoxA> getBoxData(@RequestBody BoxIdRequest request) {
@@ -95,17 +89,12 @@ public class ApiBindBoxController extends BaseController {
return R.ok(boxData);
}
@ApiOperation("获取补偿箱信息")
@PostMapping("/getCompBoxData")
// [停用-前端零调用] @PostMapping("/getCompBoxData")
public AjaxResult getCompBoxData(@RequestBody BoxIdRequest request) {
List<TtOrnamentsA> compBoxData = bindBoxService.getCompBoxData(request.getBoxId());
return success(compBoxData);
}
@ApiOperation("获取宝箱列表")
@ApiImplicitParams({
@ApiImplicitParam(name = "boxType", value = "宝箱类型(1普通箱 2对战箱 3补偿箱 4九宫格)", dataType = "String", dataTypeClass = String.class)
})
@Anonymous
@PostMapping("/getBoxList")
public R<List<TtBoxVO>> getBoxList(@RequestBody(required=false) GetBoxListRequest request) {
@@ -123,7 +112,6 @@ public class ApiBindBoxController extends BaseController {
/**
* 盲盒开箱模式开箱
*/
@ApiOperation("盲盒开箱")
@UpdateUserCache
@RateLimiter(time = 60, count = 30, limitType = LimitType.IP)
@PostMapping("/openBox")
@@ -182,8 +170,7 @@ public class ApiBindBoxController extends BaseController {
}
}
@ApiOperation("获取历史开箱信息")
@PostMapping("/getBindBoxHistory")
// [停用-前端零调用] @PostMapping("/getBindBoxHistory")
public R getBindBoxHistory(@RequestBody @Validated queryCondition param) {
if (param.getStatus().equals(1) || param.getStatus().equals(10)) {
return R.fail("非法的状态参数。");
@@ -193,7 +180,6 @@ public class ApiBindBoxController extends BaseController {
return R.ok(ttBoxRecordsVOS);
}
@ApiOperation("获取个人历史开箱记录")
@PostMapping("/myBoxHistory")
public R<List<TtBoxRecordsVO>> myBoxHistory(@RequestBody queryCondition param) {
Long userId = getUserId();
@@ -217,8 +203,7 @@ public class ApiBindBoxController extends BaseController {
return R.ok(list);
}
@ApiOperation("获取当前补偿值")
@PostMapping("/getCompValue")
// [停用-前端零调用] @PostMapping("/getCompValue")
public AjaxResult getCompValue(@RequestBody BoxIdRequest request) {
Long userId = getUserId();
TtUser ttUser = userService.getById(userId);
@@ -230,8 +215,7 @@ public class ApiBindBoxController extends BaseController {
return success(bindBoxService.getCompValue(ttUser, ttBox));
}
@ApiOperation("盲盒top")
@PostMapping("/top")
// [停用-前端零调用] @PostMapping("/top")
public R<List<TtBoxRecordsVO>> topBlinkBox() {
Long userId = getUserId();
if (ObjectUtil.isNull(userId)) {
@@ -253,8 +237,7 @@ public class ApiBindBoxController extends BaseController {
return R.ok(ttBoxRecordsVOS);
}
@ApiOperation("十佳掉落")
@PostMapping("/tenTop")
// [停用-前端零调用] @PostMapping("/tenTop")
public R<List<TtBoxRecordsVO>> tenTop() {
TenTopQuery param = new TenTopQuery();
param.setSource(Collections.singletonList(TtboxRecordSource.BLIND_BOX.getCode()));
@@ -266,7 +249,6 @@ public class ApiBindBoxController extends BaseController {
return R.ok(ttBoxRecordsVOS);
}
@ApiOperation("今日百佳")
@PostMapping("/todayTopHundred")
public R<List<TtBoxRecordsVO>> todayTopHundred() {
List<Integer> status = Arrays.asList(
@@ -283,8 +265,7 @@ public class ApiBindBoxController extends BaseController {
return R.ok(list);
}
@ApiOperation("历史百佳")
@PostMapping("/historyTopHundred")
// [停用-前端零调用] @PostMapping("/historyTopHundred")
public R<List<TtBoxRecordsVO>> historyTopHundred() {
List<Integer> status = Arrays.asList(
TtboxRecordStatus.IN_PACKSACK_ON.getCode(),
@@ -300,7 +281,6 @@ public class ApiBindBoxController extends BaseController {
return R.ok(list);
}
@ApiOperation("实时 - 所有人历史开箱按时间排序")
@Anonymous
@PostMapping("/realtime")
public R<List<TtBoxRecordsVO>> realtime(@RequestBody @Validated BoxRecordPageRequest request) {
@@ -324,7 +304,6 @@ public class ApiBindBoxController extends BaseController {
return R.ok(list);
}
@ApiOperation("欧皇 - 所有人开箱按最高价格排序")
@Anonymous
@PostMapping("/luckyKing")
public R<List<TtBoxRecordsVO>> luckyKing(@RequestBody @Validated BoxRecordPageRequest request) {
@@ -357,7 +336,6 @@ public class ApiBindBoxController extends BaseController {
* - 全服全部箱子:不传参数 / 不登录 → 查看所有用户的所有箱子的开箱记录
* - 全服当前箱子:不传 userId + 传 boxId / 不登录 → 查看指定箱子的全服记录
*/
@ApiOperation("经典开箱记录")
@Anonymous
@PostMapping("/classicRecords")
public R<List<TtBoxRecordsVO>> classicRecords(@RequestBody @Validated ClassicBoxRecordPageRequest request) {
@@ -6,8 +6,6 @@ import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.domain.dto.boxRecords.queryCondition;
import com.ruoyi.domain.vo.boxRecords.TtBoxRecordsVO;
import com.ruoyi.playingmethod.service.ApiBoxRecordsService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
@@ -17,7 +15,6 @@ import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@Api(tags = "出货记录")
@RestController
@RequestMapping("/api/boxRecords")
public class ApiBoxRecordsController {
@@ -25,8 +22,7 @@ public class ApiBoxRecordsController {
@Autowired
private ApiBoxRecordsService apiBoxRecordsService;
@ApiOperation("获取历史出货记录")
@PostMapping("/historyByCondition")
// [停用-前端零调用] @PostMapping("/historyByCondition")
public R getBindBoxHistory(@RequestBody @Validated queryCondition param) {
if (param.getStatus().contains(1) || param.getStatus().contains(10)) {
return R.fail("非法的状态参数。");
@@ -8,13 +8,10 @@ import com.ruoyi.common.core.page.PageDataInfo;
import com.ruoyi.playingmethod.service.ApiCompoundService;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.domain.vo.UserPackSackDataVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Api(tags = "合成")
@RestController
@RequestMapping("/api/compound")
public class ApiCompoundController extends BaseController {
@@ -31,8 +28,7 @@ public class ApiCompoundController extends BaseController {
this.compoundService = compoundService;
}
@ApiOperation("合成")
@PostMapping("/compound")
// [停用-前端零调用] @PostMapping("/compound")
public R<UserPackSackDataVO> compound(@RequestBody List<Long> packSackIds) {
String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance");
if ("1".equals(websiteMaintenance)) {
@@ -46,8 +42,7 @@ public class ApiCompoundController extends BaseController {
return compoundService.compound(packSackIds, ttUser);
}
@ApiOperation("获取用户合成记录")
@PostMapping("/getUserCompoundRecord")
// [停用-前端零调用] @PostMapping("/getUserCompoundRecord")
public PageDataInfo<UserPackSackDataVO> getUserCompoundRecord() {
startPage();
List<UserPackSackDataVO> userCompoundRecord = compoundService.getUserCompoundRecord(getUserId().intValue());
@@ -19,10 +19,6 @@ import com.ruoyi.playingmethod.contract.api.request.BoxIdRequest;
import com.ruoyi.playingmethod.contract.api.request.OpenBoxRequest;
import com.ruoyi.playingmethod.mapper.ApiBindBoxMapper;
import com.ruoyi.playingmethod.service.ApiExponentService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@@ -34,7 +30,6 @@ import java.util.Map;
import java.util.concurrent.TimeUnit;
@Api(tags = "指数盲盒")
@RestController
@RequestMapping("/api/exponent")
public class ApiExponentController extends BaseController {
@@ -57,10 +52,9 @@ public class ApiExponentController extends BaseController {
@Autowired
private RedisLock redisLock;
@ApiOperation("加入指数盲盒")
@Anonymous
@UpdateUserCache
@PostMapping("/join")
// [停用-前端零调用] @PostMapping("/join")
public R join(@RequestBody @Validated ExponentJoinParam param) {
TtUser ttUser = ttUserService.getById(getUserId());
if ("0".equals(ttUser.getIsRealCheck())) {
@@ -95,53 +89,44 @@ public class ApiExponentController extends BaseController {
}
}
@ApiOperation("获取宝箱列表")
@ApiImplicitParams({
@ApiImplicitParam(name = "boxType", value = "宝箱类型(1普通箱 2对战箱 3补偿箱 4九宫格 5指数盲盒)", dataType = "String", dataTypeClass = String.class)
})
@Anonymous
@PostMapping("/box")
// [停用-前端零调用] @PostMapping("/box")
public R box(@RequestBody BoxIdRequest request) {
TtUser ttUser = ttUserService.getById(getUserId());
return apiExponentService.box(ttUser, request.getBoxId());
}
@ApiOperation("最近十期结果")
@Anonymous
@PostMapping("/recenttenperiod")
// [停用-前端零调用] @PostMapping("/recenttenperiod")
public R recenttenperiod() {
PageUtils.clearPage();
return apiExponentService.recenttenperiod();
}
@ApiOperation("结果信息")
@Anonymous
@PostMapping("/info")
// [停用-前端零调用] @PostMapping("/info")
public R info() {
PageUtils.clearPage();
TtUser ttUser = ttUserService.getById(getUserId());
return apiExponentService.info(ttUser);
}
@ApiOperation("指数记录")
@Anonymous
@PostMapping("/records")
// [停用-前端零调用] @PostMapping("/records")
public R records() {
startPage();
TtUser ttUser = ttUserService.getById(getUserId());
return apiExponentService.records(ttUser);
}
@ApiOperation("我的箱子")
@Anonymous
@PostMapping("/mybox")
// [停用-前端零调用] @PostMapping("/mybox")
public R mybox() {
TtUser ttUser = ttUserService.getById(getUserId());
return apiExponentService.mybox(ttUser);
}
@ApiOperation("盲盒开箱")
@UpdateUserCache
@PostMapping("/openBox")
// [停用-前端零调用] @PostMapping("/openBox")
public R openBox(@RequestBody OpenBoxRequest request) {
TtUser ttUser = ttUserService.getById(getUserId());
if ("0".equals(ttUser.getIsRealCheck())) {
@@ -34,16 +34,15 @@ import com.ruoyi.domain.other.TtBoxVO;
import com.ruoyi.domain.vo.ApiFightListDataVO;
import com.ruoyi.domain.vo.FightResultDataVO;
import com.ruoyi.domain.vo.fight.FightBoxVO;
import com.ruoyi.domain.vo.fight.FightResultVO;
import com.ruoyi.domain.vo.upgrade.SimpleOrnamentVO;
import com.ruoyi.playingmethod.model.vo.ApiFightCardVo;
import com.ruoyi.playingmethod.model.vo.ApiFightRankingVO;
import com.ruoyi.playingmethod.model.vo.ApiFightResultVo;
import com.ruoyi.playingmethod.service.ApiBindBoxService;
import com.ruoyi.playingmethod.service.ApiFightService;
import com.ruoyi.playingmethod.contract.api.request.BoxIdRequest;
import com.ruoyi.domain.other.TtBoxA;
import com.ruoyi.system.service.ISysConfigService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
@@ -58,13 +57,11 @@ import java.util.*;
import java.util.stream.Collectors;
import java.util.concurrent.TimeUnit;
@Api(tags = "对战模式")
@Slf4j
@RestController
@RequestMapping("/api/fight")
public class ApiFightController extends BaseController {
private final TtFightResultService fightResultService;
private final ISysConfigService sysConfigService;
private final TtUserService userService;
private final ApiFightService apiFightService;
@@ -85,12 +82,10 @@ public class ApiFightController extends BaseController {
@Autowired
private TtUserMapper ttUserMapper;
public ApiFightController(TtFightResultService fightResultService,
ISysConfigService sysConfigService,
public ApiFightController(ISysConfigService sysConfigService,
TtUserService userService,
ApiFightService apiFightService,
RedisCache redisCache) {
this.fightResultService = fightResultService;
this.sysConfigService = sysConfigService;
this.userService = userService;
this.apiFightService = apiFightService;
@@ -117,7 +112,6 @@ public class ApiFightController extends BaseController {
/**
* Step1 创建对战房间
*/
@ApiOperation("创建对战房间")
@UpdateUserCache
@PostMapping("/createFight")
public R createFight(@Validated @RequestBody CreateFightBody createFightParam) {
@@ -157,7 +151,6 @@ public class ApiFightController extends BaseController {
/**
* 复刻对战房间:复制目标房间的宝箱配置、人数、组队模式等,创建一个新房间,当前用户成为房主
*/
@ApiOperation("复刻对战房间")
@UpdateUserCache
@PostMapping("/copyFight")
public R copyFight(@RequestBody FightDetailParam param) {
@@ -196,7 +189,6 @@ public class ApiFightController extends BaseController {
/**
* Step2 加入房间(占座),先行HTTP
*/
@ApiOperation("加入对战房间")
@PostMapping("/joinFightRoom")
public R joinFightRoom(@RequestBody FightDetailParam param) {
Integer fightId = param.getFightId();
@@ -234,8 +226,7 @@ public class ApiFightController extends BaseController {
/**
* 退出房间
*/
@ApiOperation("退出房间")
@PostMapping("/fightRoomExit")
// [停用-前端零调用] @PostMapping("/fightRoomExit")
public R fightRoomExit(@RequestBody FightDetailParam param) {
Integer fightId = param.getFightId();
String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance");
@@ -255,7 +246,6 @@ public class ApiFightController extends BaseController {
/**
* Step3 玩家准备游戏
*/
@ApiOperation("玩家准备游戏")
@UpdateUserCache
@PostMapping("/seatrReady")
public R seatrReady(@RequestBody FightDetailParam param) {
@@ -281,7 +271,6 @@ public class ApiFightController extends BaseController {
/**
* Step4 开始游戏(前端满人自动开始)
*/
@ApiOperation("开始游戏")
@PostMapping("/fightBegin")
public R fightBegin(@RequestBody FightDetailParam param) {
String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance");
@@ -305,7 +294,6 @@ public class ApiFightController extends BaseController {
/**
* 游戏结束标记
*/
@ApiOperation("游戏结束标记")
@PostMapping("/fightEnd")
public R fightEnd(@RequestBody FightDetailParam param) {
Integer fightId = param.getFightId();
@@ -323,7 +311,6 @@ public class ApiFightController extends BaseController {
/**
* 抽奖机测试
*/
// @ApiOperation("抽奖机测试")
// @GetMapping("LotteryMachineTest")
public R LotteryMachineTest() {
R checkLogin = checkLogin();
@@ -337,7 +324,6 @@ public class ApiFightController extends BaseController {
return R.ok(ornamentId);
}
@ApiOperation("我参与的历史对战记录")
@PostMapping("/fightOnMyOwn")
public R fightOnMyOwn(@RequestBody FightOnMyOwnParam param) {
Long userId = getUserId();
@@ -345,7 +331,7 @@ public class ApiFightController extends BaseController {
param.setPlayerId(userId.intValue());
List<ApiFightListDataVO> list = apiFightService.getFightList(param);
List<ApiFightCardVo> list = apiFightService.getFightList(param);
return R.ok(list);
}
@@ -353,7 +339,6 @@ public class ApiFightController extends BaseController {
* 快速加入对战房间 - 在本阵营(model 0=欧皇 1=非酋)内返回等待中且有空座位、
* 总价不超过用户余额的最高价房间 id(不落座,需登录)
*/
@ApiOperation("快速加入对战房间")
@UpdateUserCache
@PostMapping("/quickJoinFightRoom")
public R<Long> quickJoinFightRoom(@RequestBody QuickJoinFightParam param) {
@@ -365,26 +350,22 @@ public class ApiFightController extends BaseController {
return apiFightService.quickJoinFightRoom(userId, param != null ? param.getModel() : null);
}
@ApiOperation("全服历史对战记录")
@PostMapping("/fightOnAll")
public R fightOnAll(@RequestBody FightOnMyOwnParam param) {
List<ApiFightListDataVO> list = apiFightService.getFightList(param);
List<ApiFightCardVo> list = apiFightService.getFightList(param);
return R.ok(list);
}
@ApiOperation("历史对战详情")
@PostMapping("/fightDetail")
public R fightDetail(@RequestBody FightDetailParam param) {
return apiFightService.fightDetail(param);
}
@ApiOperation("获取指定记录更早的历史对战")
@PostMapping("/earlierHistory")
public R earlierHistory(@RequestBody FightDetailParam param) {
return apiFightService.earlierHistory(param);
}
@ApiOperation("获取对战宝箱详情")
@PostMapping("/simpleBoxDetail")
public PageDataInfo<SimpleOrnamentVO> simpleBoxDetail(@RequestBody Map<String, Integer> param) {
startPage();
@@ -393,7 +374,6 @@ public class ApiFightController extends BaseController {
return getPageData(list);
}
@ApiOperation("获取对战宝箱")
@PostMapping("/getFightBoxList")
@Anonymous
public R<List<TtBoxVO>> getFightBoxList(@RequestBody(required = false) Map<String, Integer> param) {
@@ -403,7 +383,6 @@ public class ApiFightController extends BaseController {
return R.ok(boxData);
}
@ApiOperation("获取对战列表")
@Anonymous
@PostMapping("/getFightList")
public PageDataInfo<ApiFightListDataVO> getFightList(@RequestBody(required = false) Map<String, Object> param) {
@@ -415,7 +394,6 @@ public class ApiFightController extends BaseController {
return getPageData(list);
}
@ApiOperation("获取对战大厅房间列表(HTTP)")
@Anonymous
@PostMapping("/hall")
public R<Map<String, Object>> fightHall(@RequestBody(required = false) Map<String, Object> param) {
@@ -509,28 +487,24 @@ public class ApiFightController extends BaseController {
return R.ok(result, "所有对战房间");
}
@ApiOperation("获取对战数据")
@PostMapping("/getFightData")
public R<ApiFightListDataVO> getFightData(@RequestBody FightDetailParam param) {
ApiFightListDataVO apiFightListDataVO = apiFightService.getFightList(null, null, null, param.getFightId()).get(0);
return R.ok(apiFightListDataVO);
}
// @ApiOperation("获取对战结果")
// @GetMapping("/getFightResult/{fightId}")
// public R<FightResultDataVO> getFightResult(@PathVariable("fightId") Integer fightId) {
// FightResultDataVO fightResultDataVO = fightResultService.getFightResult(fightId);
// return R.ok(fightResultDataVO);
// }
@ApiOperation("获取对战结果")
@PostMapping("/getFightResult")
public R<FightResultVO> getFightResult(@RequestBody FightDetailParam param) {
FightResultVO fightResultVO = fightResultService.getFightResult(param.getFightId());
public R<ApiFightResultVo> getFightResult(@RequestBody FightDetailParam param) {
ApiFightResultVo fightResultVO = apiFightService.getFightResultForClient(param.getFightId());
return R.ok(fightResultVO);
}
@ApiOperation("存储对战回合数据")
@PostMapping("/saveFightBoutData")
public R<Boolean> saveFightBoutData(@RequestBody FightBoutData fightBoutData) {
R checkLogin = checkLogin();
@@ -540,7 +514,6 @@ public class ApiFightController extends BaseController {
return R.ok();
}
@ApiOperation("获取对战回合数")
@PostMapping("/getFightBoutNum")
public R<Integer> getFightBoutNum(@RequestBody FightDetailParam param) {
Integer fightId = param.getFightId();
@@ -580,7 +553,6 @@ public class ApiFightController extends BaseController {
return R.ok(fightResultDataVO);
}
@ApiOperation("获取对战排行榜")
@Anonymous
@PostMapping("/fightRanking")
public AjaxResult getFightRanking() {
@@ -605,14 +577,12 @@ public class ApiFightController extends BaseController {
return AjaxResult.success(map);
}
@ApiOperation("十佳对战, 1:最大金额,2:最大增长,3:最大倍数")
@Anonymous
@PostMapping("/tenTopFight")
public AjaxResult tenTopFight(@RequestBody BaiJiaFightParam param) {
return AjaxResult.success(apiFightService.tenTopFight(param.getMaxType(), param.getModel(), param.getMinPrice(), param.getMaxPrice(), param.getKeyword()));
}
@ApiOperation("邀请机器人加入对战房间")
@PostMapping({"/joinFightRoomForRobot"})
public R joinFightRoomForRobot(@RequestBody FightDetailParam param) {
R checkLogin = checkLogin();
@@ -653,7 +623,6 @@ public class ApiFightController extends BaseController {
}
}
@ApiOperation("对战宝箱详情")
@Anonymous
@PostMapping("/boxData")
public R<TtBoxA> getFightBoxData(@RequestBody BoxIdRequest request) {
@@ -18,8 +18,6 @@ import com.ruoyi.domain.vo.fight.FightBoxVO;
import com.ruoyi.domain.vo.fight.FightResultVO;
import com.ruoyi.playingmethod.contract.api.request.FightRoomInfoRequest;
import com.ruoyi.playingmethod.service.ApiFightService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
@@ -39,7 +37,6 @@ import java.util.stream.Collectors;
/**
* 对战房间 HTTP 接口(原 WsFightRoom WebSocket 接口的 HTTP 替代)
*/
@Api(tags = "对战房间HTTP接口")
@Slf4j
@RestController
@RequestMapping("/api/fight/room")
@@ -60,8 +57,7 @@ public class ApiFightRoomController extends BaseController {
@Autowired
private RedisCache redisCache;
@ApiOperation("获取对战房间信息/结果")
@PostMapping("/info")
// [停用-前端零调用] @PostMapping("/info")
public R<Object> info(@RequestBody FightRoomInfoRequest request) {
Long userId = getUserId();
Integer fightId = request.getFightId();
@@ -21,8 +21,6 @@ import com.ruoyi.playingmethod.service.ApiLuckyUpgradeService;
import com.ruoyi.playingmethod.service.ApiUpgradeRecordService;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.user.service.ApiUserPackSackService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
@@ -32,7 +30,6 @@ import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
@Api(tags = "幸运升级模式")
@RestController
@RequestMapping("/api/luckyUpgrade")
public class ApiLuckyUpgradeController extends BaseController {
@@ -66,7 +63,6 @@ public class ApiLuckyUpgradeController extends BaseController {
}
}
@ApiOperation("获取可升级的饰品列表")
@Anonymous
@PostMapping("/getOrnamentsList")
public R<List<ApiLuckyOrnamentsDataVO>> getOrnamentsList(@RequestBody ApiLuckyUpgradeBody apiLuckyUpgradeBody){
@@ -89,7 +85,6 @@ public class ApiLuckyUpgradeController extends BaseController {
return R.ok(list);
}
@ApiOperation("幸运升级")
@UpdateUserCache
@PostMapping("/upgrade")
public R upgrade(@RequestBody @Validated UpgradeBodyA upgradeParam){
@@ -109,14 +104,12 @@ public class ApiLuckyUpgradeController extends BaseController {
return apiLuckyUpgradeService.upgrade(ttUser, upgradeParam);
}
@ApiOperation("获取幸运升级历史记录")
@PostMapping("/getUpgradeRecord")
// [停用-前端零调用] @PostMapping("/getUpgradeRecord")
public R getUpgradeRecord(@RequestBody @Validated UpgradeCondition param){
return upgradeRecordService.historyDetail(param);
}
@ApiOperation("获取幸运升级十佳记录")
@PostMapping("/tenTopUpgradeRecord")
// [停用-前端零调用] @PostMapping("/tenTopUpgradeRecord")
public R tenTopUpgradeRecord(@RequestBody(required = false) TenTopUpgradeRequest request){
Boolean isVictory = (request == null) ? null : request.getIsVictory();
R result = upgradeRecordService.tenTopDetail(isVictory);
@@ -134,8 +127,7 @@ public class ApiLuckyUpgradeController extends BaseController {
return result;
}
@ApiOperation("获取个人幸运升级历史记录列表")
@PostMapping("/myHistoryRecord")
// [停用-前端零调用] @PostMapping("/myHistoryRecord")
public R myHistoryRecord(@RequestBody(required = false) @Validated MyUpgradeRecordRequest request){
R check = checkLogin();
if (!check.getCode().equals(200)) {
@@ -148,7 +140,6 @@ public class ApiLuckyUpgradeController extends BaseController {
return upgradeRecordService.myHistoryRecord(userId, request);
}
@ApiOperation("获取当前饰品最近100条拉取记录(含用户头像、概率、饰品全名、磨损度、品质图、图片、价格)")
@PostMapping("/recentPulls")
public R<List<ApiLuckyUpgradeRecentPullVO>> recentPulls(@RequestBody @Validated UpgradeCondition param) {
if (param.getUpgradeRecordId() == null) {
@@ -158,7 +149,6 @@ public class ApiLuckyUpgradeController extends BaseController {
return R.ok(list);
}
@ApiOperation("幸运升级记录分页(scope: 0全服/1我的;itemScope: 0全部饰品/1当前饰品;均需登录)")
@PostMapping("/recordPage")
public R recordPage(@RequestBody @Validated UpgradeRecordPageRequest req) {
// 记录页无论全服/我的均需登录
@@ -15,8 +15,6 @@ import com.ruoyi.playingmethod.contract.api.request.GetRedPackRequest;
import com.ruoyi.playingmethod.service.ApiRedPackService;
import com.ruoyi.thirdparty.wechat.domain.PtLoginUser;
import com.ruoyi.thirdparty.wechat.service.ApiTokenService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@@ -29,7 +27,6 @@ import java.util.List;
@Deprecated
@RestController
@RequestMapping("/api/redPackData")
@Api(tags = "红包")
public class ApiRedPackContoller extends BaseController {
@Autowired
@@ -42,7 +39,6 @@ public class ApiRedPackContoller extends BaseController {
// @PostMapping("/getRedPackList")
@UserPermission
@NoRepeatSubmit
@ApiOperation(value = "获取红包列表")
public TableDataInfo getRedPackList(HttpServletRequest request, @RequestBody GetRedPackListRequest getRedPackListRequest) {
PtLoginUser user = tokenService.getLoginUser(request);
@@ -57,7 +53,6 @@ public class ApiRedPackContoller extends BaseController {
// @PostMapping("/getRedPack")
@UserPermission
@NoRepeatSubmit
@ApiOperation(value = "领取红包")
public AjaxResult getRedPack(@RequestBody GetRedPackRequest request) {
Long userId = getUserId();
return redPackService.getRedPack(userId, request.getRedPackId());
@@ -67,7 +62,6 @@ public class ApiRedPackContoller extends BaseController {
// @PostMapping("/getRedPackByKey")
@UserPermission
@NoRepeatSubmit
@ApiOperation(value = "根据红包口令兑换红包")
public AjaxResult getRedPackByKey(@RequestBody GetRedPackByKeyRequest request) {
LoginUser loginUser = getLoginUser();
if (request.getRedPackKey() == null || "".equals(request.getRedPackKey())) {
@@ -80,7 +74,6 @@ public class ApiRedPackContoller extends BaseController {
// @PostMapping("/getActivityDataHis")
@UserPermission
@NoRepeatSubmit
@ApiOperation(value = "查询参与的红包活动记录")
public TableDataInfo getBoxDataHis(HttpServletRequest request) {
PtLoginUser user = tokenService.getLoginUser(request);
startPage();
@@ -93,7 +86,6 @@ public class ApiRedPackContoller extends BaseController {
// @PostMapping("/getRedPackDataDetail")
@UserPermission
@NoRepeatSubmit
@ApiOperation(value = "查询历史记录")
public AjaxResult getRedPackDataDetail(HttpServletRequest request, @RequestBody GetRedPackDetailRequest getRedPackDetailRequest) {
PtLoginUser user = tokenService.getLoginUser(request);
return redPackService.getRedPackDataDetail(user, getRedPackDetailRequest.getRecordId());
@@ -7,8 +7,6 @@ import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.playingmethod.model.body.SynthesizeRequest;
import com.ruoyi.playingmethod.service.IApiReplacementRecordService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@@ -26,15 +24,13 @@ import jakarta.servlet.http.HttpServletRequest;
*/
@RestController
@RequestMapping("/api/replacementRecord")
@Api(tags = "汰换记录")
public class ApiReplacementRecordController extends BaseController {
@Autowired
private IApiReplacementRecordService ttReplacementRecordService;
@PostMapping("/synthesizeItems")
// [停用-前端零调用] @PostMapping("/synthesizeItems")
@UserPermission
@ApiOperation("汰换饰品")
public AjaxResult synthesizeItems(@RequestBody SynthesizeRequest synthesizeRequest) {
LoginUser loginUser = getLoginUser();
return ttReplacementRecordService.synthesizeItems(loginUser, synthesizeRequest.getBoIds());
@@ -24,9 +24,6 @@ import com.ruoyi.playingmethod.service.ApiRollService;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.domain.vo.RollDetailsDataVO;
import com.ruoyi.domain.vo.RollListDataVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@@ -42,7 +39,6 @@ import jakarta.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.List;
@Api(tags = "ROLL房模式")
@RestController
@RequestMapping("/api/roll")
public class ApiRollController extends BaseController {
@@ -69,7 +65,6 @@ public class ApiRollController extends BaseController {
private String rollPassword;
}
@ApiOperation("加入 ROLL 房")
@RepeatSubmit(message = "操作过于频繁,请稍后重试!")
@PostMapping("/joinRoll")
public R joinRoll(@RequestBody @Validated JoinRollParam param) {
@@ -91,7 +86,6 @@ public class ApiRollController extends BaseController {
}
@ApiOperation("获取ROLL房列表")
@Anonymous
@PostMapping("/getRollList")
public TableDataInfo getRollList(@RequestBody GetRollListParamRequest param) {
@@ -119,33 +113,28 @@ public class ApiRollController extends BaseController {
return getPageData(list);
}
@ApiOperation("获取ROLL房详情")
@PostMapping("/getRollDetails")
public R<RollDetailsDataVO> getRollDetails(@RequestBody RollIdRequest request) {
Long userId = getUserId();
return apiRollService.getRollDetails(request.getRollId(), userId);
}
@ApiOperation("获取ROLL房参与人员详情")
@PostMapping("/getRollPlayers")
public R<List<RollUserVO>> getRollPlayers(@RequestBody @Validated GetRollPlayersParam param) {
return apiRollService.getRollPlayers(param);
}
@ApiOperation("获取ROLL房奖池详情")
@PostMapping("/getRollPrizePool")
public R<List<RollJackpotOrnamentsVO>> getRollPrizePool(@RequestBody @Validated GetRollPrizePool param) {
return apiRollService.getRollPrizePool(param);
}
@ApiOperation("获取ROLL开奖详情")
@PostMapping("/getRollOpenPrize")
public R<List<TtBoxRecordsVO>> getRollOpenPrize(@RequestBody @Validated GetRollOpenPrizeParam param) {
return apiRollService.getRollOpenPrize(param);
}
@ApiOperation("开奖测试,正式环境删除")
@PostMapping("/endRoll")
// [停用-前端零调用] @PostMapping("/endRoll")
public R endRoll(@RequestBody RollIdRequest request){
// TODO: 移除或添加管理员权限校验
return apiRollService.endROLL(request.getRollId());
@@ -176,8 +165,7 @@ public class ApiRollController extends BaseController {
return "1";
}
@ApiOperation("roll房间号搜索")
@PostMapping("/searchRollId")
// [停用-前端零调用] @PostMapping("/searchRollId")
public TableDataInfo searchRoll(@RequestBody RollIdRequest request){
GetRollListParamRequest param = new GetRollListParamRequest();
param.setRollId(request.getRollId());
@@ -4,7 +4,6 @@ import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.playingmethod.entity.TtAttendanceRecord;
import com.ruoyi.playingmethod.service.IApiAttendanceRecordService;
import io.swagger.annotations.Api;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -12,7 +11,6 @@ import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@Api(tags = "签到记录")
@RestController
@RequestMapping("/api/attendanceRecord")
public class ApiTtAttendanceRecordController extends BaseController {
@@ -5,14 +5,11 @@ import com.ruoyi.admin.service.TtUserAvatarService;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.domain.other.TtUserAvatar;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.stream.Collectors;
@Api(tags = "用户头像")
@RestController
@RequestMapping("/api/userAvatar")
public class ApiUserAvatarController extends BaseController {
@@ -23,8 +20,7 @@ public class ApiUserAvatarController extends BaseController {
this.userAvatarService = userAvatarService;
}
@ApiOperation("用户头像列表")
@PostMapping("/list")
// [停用-前端零调用] @PostMapping("/list")
public R<List<TtUserAvatar>> list() {
LambdaQueryWrapper<TtUserAvatar> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(TtUserAvatar::getStatus, "0")
@@ -33,7 +29,6 @@ public class ApiUserAvatarController extends BaseController {
return R.ok(list);
}
@ApiOperation("获取头像图片资源路径列表")
@PostMapping("/images")
public R<List<String>> getAvatarImages() {
LambdaQueryWrapper<TtUserAvatar> wrapper = new LambdaQueryWrapper<>();
@@ -15,16 +15,12 @@ import com.ruoyi.playingmethod.contract.api.request.OpenWelfareBoxRequest;
import com.ruoyi.playingmethod.model.ApiWelfare;
import com.ruoyi.playingmethod.service.ApiWelfareService;
import com.ruoyi.system.service.ISysConfigService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.*;
@Api(tags = "福利")
@RestController
@RequestMapping("/api/welfare")
public class ApiWelfareController extends BaseController {
@@ -41,10 +37,7 @@ public class ApiWelfareController extends BaseController {
@Autowired
private TtUserService userService;
@ApiOperation("获取福利列表")
@ApiResponse(code = 200, message = "eligible【是否具备领取条件(0否 1是)】" +
"<br>claimStatus【领取状态(0未领取 1已领取)】")
@PostMapping("/getWelfareList")
// [停用-前端零调用] @PostMapping("/getWelfareList")
public TableDataInfo getWelfareList() {
startPage();
Long userId = getUserId();
@@ -52,8 +45,7 @@ public class ApiWelfareController extends BaseController {
return getDataTable(list);
}
@ApiOperation("领取福利")
@PostMapping("/claimWelfare")
// [停用-前端零调用] @PostMapping("/claimWelfare")
public AjaxResult claimWelfare(@RequestBody ClaimWelfareRequest request) {
Long userId = getUserId();
// 检查福利是否存在
@@ -82,8 +74,7 @@ public class ApiWelfareController extends BaseController {
return AjaxResult.success(openBoxVO);
}
@ApiOperation("每日领取福利箱子")
@PostMapping("/claimDailyBox")
// [停用-前端零调用] @PostMapping("/claimDailyBox")
public R<?> claimDailyBox() {
Long userId = getUserId();
try {
@@ -94,8 +85,7 @@ public class ApiWelfareController extends BaseController {
}
}
@ApiOperation("批量开启福利箱子(1-15个)")
@PostMapping("/openBoxes")
// [停用-前端零调用] @PostMapping("/openBoxes")
public R<?> openBoxes(@RequestBody @Validated OpenWelfareBoxRequest request) {
Long userId = getUserId();
String bindBoxMaintenance = sysConfigService.selectConfigByKey("bindBoxMaintenance");

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