Files
2026-04-21 23:02:15 +08:00

14 KiB
Raw Permalink Blame History

phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
phase plan type wave depends_on files_modified autonomous requirements must_haves
01 01 execute 1
application/admin/controller/History.php
application/admin/model/History.php
application/admin/lang/zh-cn/history.php
true
OMIT-04
OMIT-05
truths artifacts key_links
后端接口能接收 periods 参数并返回遗漏号码列表
返回数据包含每个遗漏号码的 num、omit(遗漏期数)、color(波色)
结果按遗漏期数 omit 从大到小排序
path provides exports
application/admin/controller/History.php missingNum() 控制器方法,处理 AJAX 请求
missingNum
path provides exports
application/admin/model/History.php getMissingNumbers($periods) 模型方法,执行遗漏计算
getMissingNumbers
calcOmitCount
path provides contains
application/admin/lang/zh-cn/history.php 遗漏号码相关 i18n 文本 'Missing Number Analysis'
from to via pattern
application/admin/controller/History.php application/admin/model/History.php $this->model->getMissingNumbers($periods) getMissingNumbers
from to via pattern
application/admin/model/History.php fa_history Db::name('history') 查询 num1~num7 Db::name('history')
from to via pattern
application/admin/model/History.php fa_num Db::name('num') 查询波色映射 Db::name('num')
实现遗漏号码查询的后端逻辑:History 控制器新增 missingNum() AJAX 接口,History 模型新增 getMissingNumbers() 计算方法,支持查询最近 X 期开奖数据并计算 1-49 中未出现的号码及其遗漏期数和波色,结果按遗漏期数降序返回。

Purpose: 为前端弹窗提供遗漏号码数据源(per D-02: 遗漏计算在后端完成) Output: 可用的 history/missingNum AJAX 端点 + i18n 文本

<execution_context> @D:/code/php/amlhc/.claude/get-shit-done/workflows/execute-plan.md @D:/code/php/amlhc/.claude/get-shit-done/templates/summary.md </execution_context>

@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/01-omitted-number-analysis/01-RESEARCH.md @D:/code/php/amlhc/application/admin/controller/History.php @D:/code/php/amlhc/application/admin/model/History.php @D:/code/php/amlhc/application/admin/lang/zh-cn/history.php

From application/admin/controller/History.php:

namespace app\admin\controller;
use app\common\controller\Backend;
class History extends Backend {
    protected $model = null;
    public function _initialize();  // sets $this->model = new \app\admin\model\History
}
// Inherits: $this->success($msg, $data), $this->error($msg) from Backend trait
// Inherits: $this->request->isAjax(), $this->request->get() from think\Controller

From application/admin/model/History.php:

namespace app\admin\model;
use think\Model;
class History extends Model {
    protected $name = 'history';       // maps to fa_history table
    protected $autoWriteTimestamp = false;
    // Fields: expect (int, PK), num1~num7 (int), openTime (datetime)
}

From application/admin/controller/Num.php::getColorMap() pattern:

public function getColorMap() {
    $list = $this->model->field('num,color')->select();
    $map = [];
    foreach ($list as $item) { $map[$item['num']] = $item['color']; }
    $this->success($map);
}
// Returns: {code: 1, msg: {"1":"红波","2":"蓝波",...}, data: null}

Expected response format from missingNum():

{
  "code": 1,
  "msg": "查询成功",
  "data": [
    {"num": 7, "omit": 50, "color": "绿波"},
    {"num": 13, "omit": 32, "color": "红波"}
  ]
}
Task 1: 在 History 模型中添加 getMissingNumbers() 和 calcOmitCount() 方法 application/admin/model/History.php - application/admin/model/History.php(当前模型结构) - application/admin/model/Num.php(参考波色查询模式) - sql/amlhc.sql line 471-482fa_history 表结构,确认字段名和类型) 在 application/admin/model/History.php 中添加两个方法:
  1. getMissingNumbers($periods = 10) — 公共方法,计算遗漏号码:

    • 使用 Db::name('history')->field('expect,num1,num2,num3,num4,num5,num6,num7')->order('openTime', 'desc')->limit($periods)->select() 获取最近 $periods 期数据
    • 遍历结果,将出现的号码收集到 $appeared 数组(key 为号码 intvalue 为 true
    • 遍历 1-49,找出未在 $appeared 中的号码,收集到 $missing 数组
    • 查询更多历史数据用于计算遗漏期数:Db::name('history')->field('num1,num2,num3,num4,num5,num6,num7')->order('openTime', 'desc')->limit(500)->select()
    • 查询波色映射:Db::name('num')->column('color', 'num') 获取 $colorMap
    • 对每个遗漏号码调用 calcOmitCount($num, $allHistory) 计算遗漏期数
    • 组装结果:['num' => $num, 'omit' => $omitCount, 'color' => $colorMap[$num] ?? '—']
    • 使用 usort($result, function($a, $b) { return $b['omit'] - $a['omit']; }) 按 omit 降序排序
    • 返回 $result 数组
  2. calcOmitCount($num, $allHistory) — 私有方法,计算某个号码的遗漏期数:

    • 遍历 $allHistory(已按 openTime DESC 排序),对每行检查 num1~num7 是否等于 $num
    • 一旦找到,返回当前索引 $idx(即该号码最后一次出现距今多少期)
    • 如果遍历完都没找到,返回 count($allHistory)(表示 500 期内都未出现)

关键实现细节:

  • 必须使用 use think\facade\Db;\think\Db::name() 来执行数据库查询(检查当前文件命名空间,如果模型已继承 think\Model,可直接用 self::field(...)->select()Db::name()
  • 号码比较必须用 (int) 转换,因为数据库返回的 num1~num7 是 int 类型(schema 显示为 int(11)),但要确保一致
  • $periods 参数范围校验:1-100,但模型层不校验(由控制器层校验),模型只负责计算
  • 波色缺失时返回 '—' 字符串作为兜底

添加 use think\facade\Db; 到文件顶部(如果尚未存在)。 <acceptance_criteria> - application/admin/model/History.php 包含 public function getMissingNumbers($periods = 10) - application/admin/model/History.php 包含 private function calcOmitCount($num, $allHistory) - getMissingNumbers 方法体包含 Db::name('history')self:: 查询 - getMissingNumbers 方法体包含 Db::name('num') 查询波色映射 - getMissingNumbers 方法体包含 usort 按 omit 降序排序 - 方法返回数组结构为 [['num' => int, 'omit' => int, 'color' => string], ...] - 文件顶部有 use think\facade\Db; 或使用 \think\Db:: 完整命名空间 </acceptance_criteria> grep -c "getMissingNumbers|calcOmitCount" application/admin/model/History.php | grep "2" History 模型有 getMissingNumbers($periods) 和 calcOmitCount($num, $allHistory) 两个方法,能查询 fa_history 表计算遗漏号码并返回按遗漏期数降序的 [{num, omit, color}] 数组

Task 2: 在 History 控制器中添加 missingNum() AJAX 接口方法 application/admin/controller/History.php - application/admin/controller/History.php(当前控制器结构) - application/admin/controller/Num.php(参考 getColorMap 方法的响应模式) - application/common/controller/Backend.php(确认 $noNeedRight 用法) 在 application/admin/controller/History.php 中添加 missingNum() 方法:
  1. 在类中添加权限控制属性(在 _initialize() 方法上方或下方):

    // 无需额外权限检查(但仍在 admin 模块内,需要 admin 登录)
    protected $noNeedRight = ['missingNum'];
    
  2. 添加 missingNum() 方法:

    /**
     * 查询遗漏号码
     * @return void
     */
    public function missingNum()
    {
        if ($this->request->isAjax()) {
            $periods = $this->request->get('periods', 10, 'intval');
            if ($periods < 1 || $periods > 100) {
                $this->error('期数范围必须在 1-100 之间');
            }
            $result = $this->model->getMissingNumbers($periods);
            $this->success('查询成功', $result);
        }
    }
    

关键实现细节:

  • 使用 $this->request->isAjax() 判断是否为 AJAX 请求
  • 使用 $this->request->get('periods', 10, 'intval') 获取参数,默认值 10,强制转为 int
  • 参数校验:$periods < 1 或 > 100 时调用 $this->error() 返回错误
  • 调用 $this->model->getMissingNumbers($periods) 获取结果
  • 使用 $this->success('查询成功', $result) 返回标准 FastAdmin 响应格式 {code: 1, msg: '查询成功', data: [...]}
  • 方法无返回值(void),通过 $this->success/error 输出 JSON

不要使用 protected $noNeedRight = ['*'],只放开 missingNum 一个方法即可(最小权限原则)。 <acceptance_criteria> - application/admin/controller/History.php 包含 protected $noNeedRight = ['missingNum'] - application/admin/controller/History.php 包含 public function missingNum() - missingNum 方法体包含 $this->request->isAjax() 判断 - missingNum 方法体包含 $this->request->get('periods', 10, 'intval') - missingNum 方法体包含 $periods < 1 || $periods > 100 范围校验 - missingNum 方法体包含 $this->model->getMissingNumbers($periods) 调用 - missingNum 方法体包含 $this->success('查询成功', $result) 响应 - missingNum 方法体包含 $this->error(...) 错误响应 </acceptance_criteria> grep -c "missingNum|noNeedRight" application/admin/controller/History.php | awk '$1 >= 2' History 控制器有 missingNum() 方法,接受 AJAX GET 请求,校验 periods 参数 1-100,调用模型 getMissingNumbers() 返回标准 JSON 响应

Task 3: 添加遗漏号码相关 i18n 文本到语言文件 application/admin/lang/zh-cn/history.php - application/admin/lang/zh-cn/history.php(当前语言文件) - application/admin/lang/zh-cn/num.php(参考格式,如果存在) 修改 application/admin/lang/zh-cn/history.php,在现有返回值数组中添加遗漏号码相关的语言键:

当前内容:

return [
    'Expect' => '期号',
    'OpenTime' => '时间',
    'Num7' => '特码',
];

修改为:

return [
    'Expect' => '期号',
    'OpenTime' => '时间',
    'Num7' => '特码',
    'Missing Number Analysis' => '遗漏号码分析',
    'Query Periods' => '查询期数',
    'Missing' => '遗漏',
    'periods' => '期',
    'No missing numbers found' => '最近所有号码均出现过',
    'Query failed' => '查询失败',
    'Loading' => '查询中...',
];

保持与现有格式一致:单引号包裹 key 和 value,逗号结尾,缩进 4 空格。 <acceptance_criteria> - application/admin/lang/zh-cn/history.php 包含键 'Missing Number Analysis',值为 '遗漏号码分析' - application/admin/lang/zh-cn/history.php 包含键 'Query Periods',值为 '查询期数' - application/admin/lang/zh-cn/history.php 包含键 'Missing',值为 '遗漏' - application/admin/lang/zh-cn/history.php 包含键 'periods',值为 '期' - 文件格式保持 return [ ... ]; 结构,使用单引号 </acceptance_criteria> grep -c "Missing Number Analysis|Query Periods|Missing" application/admin/lang/zh-cn/history.php | awk '$1 >= 3' 语言文件包含遗漏号码相关的所有中文翻译键值对

<threat_model>

Trust Boundaries

Boundary Description
Browser → Controller (AJAX GET) 用户输入 periods 参数,可能注入非数值
Controller → Model 内部调用,信任已建立
Model → Database (fa_history, fa_num) ORM 查询,无原始 SQL 拼接

STRIDE Threat Register

Threat ID Category Component Disposition Mitigation Plan
T-01-01 Tampering History::missingNum() - periods 参数 mitigate 使用 $this->request->get('periods', 10, 'intval') 强制类型转换 + 范围校验 `$periods < 1
T-01-02 Tampering History::getMissingNumbers() - SQL 查询 mitigate 使用 ThinkPHP ORM Db::name() 参数化查询,无原始 SQL 字符串拼接
T-01-03 Elevation of Privilege History::missingNum() 权限绕过 mitigate 使用 $noNeedRight = ['missingNum'](非 $noNeedLogin),admin 登录仍然必需,仅跳过权限规则检查
T-01-04 Denial of Service 超大 periods 值导致查询慢 mitigate 参数上限 100,模型层 LIMIT 500 查询历史,确保 O(500*7) 时间复杂度可接受
</threat_model>
- 通过浏览器访问 admin history 页面,在地址栏直接请求 `history/missingNum?periods=10`,返回 JSON 且 code=1data 为非空数组 - 请求 `history/missingNum?periods=0` 返回错误(code!=1 - 请求 `history/missingNum?periods=200` 返回错误(code!=1 - 返回数据中每个元素包含 numint 1-49)、omitint >=0)、colorstring - 返回数组按 omit 字段降序排列

<success_criteria>

  • History 模型有 getMissingNumbers() 和 calcOmitCount() 方法
  • History 控制器有 missingNum() AJAX 端点,带 periods 参数校验
  • 请求返回标准 FastAdmin 格式 {code: 1, msg: '查询成功', data: [{num, omit, color}, ...]}
  • 结果按遗漏期数 omit 从大到小排序
  • 波色映射从 fa_num 表获取,缺失时显示 '—'
  • 语言文件包含遗漏号码相关 i18n 键值对
  • missingNum 方法仅需 admin 登录即可访问($noNeedRight 非 $noNeedLogin </success_criteria>
After completion, create `.planning/phases/01-omitted-number-analysis/01-01-SUMMARY.md`