fix(BIZ-87): 修复中奖比对算法——按生成时间匹配正确期号
问题:所有生成记录都在比对最新一期开奖,而非根据生成时间匹配对应期号。 修复: - 新增 find_draw_by_gen_time() 函数,采用窗口归属模型 - 每个开奖日 D 的归属窗口为 (D_prev 20:00, D 20:00] - 生成时间落在哪个窗口内 → 与该开奖日 D 比对 - 规则 2.1:生成时间 < 开奖日 20:00 → 与当日开奖比对 - 规则 2.2:生成时间 ≥ 开奖日 20:00 → 与下次开奖日比对 - 超过所有开奖日 → 返回 waiting 状态 修改 api_compare_record() 使用新函数替换 load_latest_draw + find_next_draw_after 逻辑 测试验证: - gen 6/29 10:00 → 6/30 2026074 ✅ - gen 6/28 19:00 → 6/28 2026073 ✅ - gen 6/28 21:00 → 6/30 2026074 ✅ - gen 7/4 18:22 → waiting(7/5 未开奖)✅ Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
@@ -201,6 +201,86 @@ def find_next_draw_after(date_obj):
|
||||
traceback.print_exc()
|
||||
return None
|
||||
|
||||
|
||||
def find_draw_by_gen_time(gen_time):
|
||||
"""根据生成时间找到归属的开奖期。
|
||||
|
||||
比对规则(窗口归属模型):
|
||||
- 每个开奖日 D 有一个归属窗口 (D_prev 20:00, D 20:00]
|
||||
- 生成时间 T 落在哪个窗口内 → 与该开奖日 D 比对
|
||||
- 简化实现:按开奖日升序遍历,找到第一个 D 使得 T ≤ D 20:00
|
||||
- 如果 T 同日且 T < 20:00 → 同日比对(规则 2.1)
|
||||
- 如果 T 在 D 之前某日 → 仍归属 D(窗口归属)
|
||||
- 如果 T 同日且 T ≥ 20:00 → 归属下一个开奖日(规则 2.2)
|
||||
- 如果 T 超过所有开奖日的 20:00 → 等待下一期
|
||||
|
||||
返回 (draw_dict, compare_type) 或 (None, 'waiting')
|
||||
"""
|
||||
try:
|
||||
df = load_history_dataframe()
|
||||
if df is None or df.empty:
|
||||
return None, 'no_data'
|
||||
|
||||
df['_dt'] = pd.to_datetime(df['开奖时间'], errors='coerce')
|
||||
df = df.dropna(subset=['_dt']).sort_values('_dt', ascending=True).reset_index(drop=True)
|
||||
|
||||
if df.empty:
|
||||
return None, 'no_data'
|
||||
|
||||
def row_to_draw(row):
|
||||
s = str(row.get('号码', '')).strip()
|
||||
reds, blue = parse_number_string(s)
|
||||
draw_date_str = str(row.get('开奖时间', '')).strip()
|
||||
draw_date = None
|
||||
try:
|
||||
draw_date = datetime.strptime(draw_date_str[:10], '%Y-%m-%d')
|
||||
except (ValueError, TypeError):
|
||||
try:
|
||||
dt = pd.to_datetime(draw_date_str, errors='coerce')
|
||||
if pd.notna(dt):
|
||||
draw_date = dt.to_pydatetime()
|
||||
except Exception:
|
||||
pass
|
||||
return {
|
||||
'date': draw_date_str,
|
||||
'date_obj': draw_date,
|
||||
'period': str(row.get('期数', '')).strip(),
|
||||
'reds': reds,
|
||||
'blue': blue,
|
||||
'raw': {k: str(row.get(k, '')).strip() for k in ['开奖时间', '期数', '号码', '和值特征', '奇偶比', '大小比', '跨度']}
|
||||
}
|
||||
|
||||
# 遍历所有开奖日(升序),找到第一个 D 使得 gen_time ≤ D 20:00
|
||||
for i in range(len(df)):
|
||||
draw_date = df.iloc[i]['_dt']
|
||||
draw_8pm = draw_date.replace(hour=20, minute=0, second=0, microsecond=0)
|
||||
|
||||
if gen_time <= draw_8pm:
|
||||
# gen_time 落在 D 的窗口内 → 与 D 比对
|
||||
draw = row_to_draw(df.iloc[i])
|
||||
|
||||
# 判断是同日还是窗口归属
|
||||
gen_date_only = gen_time.date()
|
||||
draw_date_only = draw_date.date()
|
||||
|
||||
if gen_date_only == draw_date_only and gen_time < draw_8pm:
|
||||
compare_type = 'current' # 同日,20:00 前
|
||||
elif gen_date_only == draw_date_only and gen_time >= draw_8pm:
|
||||
# 同日 20:00 后 → 应该归属下一期(但上面条件已判断 ≤,所以这种情况不会进到这里)
|
||||
# 这个分支在逻辑上不会到达,但保留防御性代码
|
||||
continue
|
||||
else:
|
||||
compare_type = 'current' # 窗口归属(生成时间在 D 之前某个时间点,归属 D)
|
||||
|
||||
return draw, compare_type
|
||||
|
||||
# gen_time 超过所有开奖日的 20:00 → 等待下一期
|
||||
last_draw = row_to_draw(df.iloc[-1])
|
||||
return last_draw, 'waiting'
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
return None, 'error'
|
||||
|
||||
# ============================================================
|
||||
# 认证装饰器(可选)
|
||||
# ============================================================
|
||||
@@ -510,48 +590,29 @@ def api_compare_record(record_id):
|
||||
if not tickets:
|
||||
return jsonify({'success': False, 'error': '无号码数据可比对'}), 400
|
||||
|
||||
# 加载历史开奖数据,找最近一期
|
||||
latest_draw = load_latest_draw()
|
||||
if not latest_draw or not latest_draw.get('reds'):
|
||||
# 根据生成时间找到归属的开奖期
|
||||
# 规则:生成时间 < 开奖日 20:00 → 与当日开奖比对;>= 20:00 → 与下次开奖日比对
|
||||
draw_to_compare, compare_type = find_draw_by_gen_time(gen_time)
|
||||
|
||||
if draw_to_compare is None:
|
||||
return jsonify({'success': False, 'error': '无法加载开奖数据'}), 500
|
||||
|
||||
# 确定比对的开奖期
|
||||
# 规则:生成时间 < 开奖日 20:00 → 与当日开奖比对;>= 20:00 → 与下次开奖日比对
|
||||
draw_to_compare = None
|
||||
compare_type = ''
|
||||
if not draw_to_compare.get('reds'):
|
||||
return jsonify({'success': False, 'error': '开奖数据号码解析失败'}), 500
|
||||
|
||||
latest_date = latest_draw.get('date_obj')
|
||||
if latest_date:
|
||||
# 当日 20:00 的时间点
|
||||
draw_day_8pm = latest_date.replace(hour=20, minute=0, second=0, microsecond=0)
|
||||
|
||||
if gen_time < draw_day_8pm:
|
||||
# 生成时间在开奖日 20:00 之前 → 与当日(最近一期)比对
|
||||
draw_to_compare = latest_draw
|
||||
compare_type = 'current'
|
||||
else:
|
||||
# 生成时间在开奖日 20:00 之后 → 与下次开奖日比对
|
||||
next_draw = find_next_draw_after(latest_date)
|
||||
if next_draw:
|
||||
draw_to_compare = next_draw
|
||||
compare_type = 'next'
|
||||
else:
|
||||
# 暂无下次开奖数据,返回等待状态
|
||||
if compare_type == 'waiting':
|
||||
# 生成时间超过所有已记录开奖日 → 等待下一期
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'data': {
|
||||
'status': 'waiting',
|
||||
'message': f'生成时间 {gen_time_str} 在最近开奖日 {latest_draw["date"]} 20:00 之后,下次开奖尚未举行,请等待开奖后再次比对。',
|
||||
'message': f'生成时间 {gen_time_str} 超过最近开奖日 {draw_to_compare["date"]} 20:00,下次开奖尚未举行,请等待开奖后再次比对。',
|
||||
'gen_time': gen_time_str,
|
||||
'latest_draw_date': latest_draw['date'],
|
||||
'latest_draw_period': latest_draw['period'],
|
||||
'latest_draw_date': draw_to_compare['date'],
|
||||
'latest_draw_period': draw_to_compare['period'],
|
||||
'compare_rule': '生成时间 ≥ 开奖日 20:00 → 等待下次开奖日比对'
|
||||
}
|
||||
})
|
||||
else:
|
||||
# 无法解析开奖日期,直接用最近一期
|
||||
draw_to_compare = latest_draw
|
||||
compare_type = 'current_fallback'
|
||||
|
||||
# 执行比对
|
||||
draw_reds = set(draw_to_compare['reds'])
|
||||
@@ -607,7 +668,7 @@ def api_compare_record(record_id):
|
||||
'status': 'compared',
|
||||
'gen_time': gen_time_str,
|
||||
'compare_type': compare_type,
|
||||
'compare_rule': '生成时间 < 开奖日 20:00 → 与当日开奖比对' if compare_type == 'current' else '生成时间 ≥ 开奖日 20:00 → 与下次开奖日比对',
|
||||
'compare_rule': f'生成时间 {gen_time_str} 归属开奖期 {draw_to_compare["period"]}({draw_to_compare["date"]})',
|
||||
'draw': {
|
||||
'date': draw_to_compare['date'],
|
||||
'period': draw_to_compare['period'],
|
||||
|
||||
Reference in New Issue
Block a user