# 积分规则与开发契约 记录日期:2026-10-01。版本:points-backend-20261001-v2。状态:**后端及真实HTTP隔离开发检查完成,业务规则未定稿,默认不入账,原表未迁移、未部署**。归属既有 POINT-001—004,不新增任务编号。POINT-002保持开发中;DEC-POINT/POINT-001仍待确认,POINT-003未实施,POINT-004页面尚未接真实积分。 本文件作为积分实施入口;数据库具体增量见[积分数据库方案](D:/AAdesk/202609/airz/database/points-design.md)。接口源码与真实HTTP隔离检查已完成,未部署;页面仍是本机体验签到。未获规则确认时不填真实奖励,配置默认false/null。 ## 0. 最新协议与隔离开发检查 87个真实HTTP请求/1079项检查通过,经过实际ThinkPHP路由、Redis Token鉴权和积分控制器。覆盖匿名/无效/停用/删除/撤销、Cookie/旧Token头拒绝、允许Origin的匿名OPTIONS、JSON/原始query/错误方法、草案/发布/暂停/坏规则、跨日/版本变化、实际丢弃已提交成功响应后的原键确认、本人快照/隐私/旧NULL与0流水、余额边界、坏回执和Redis不可用。 新脚本tests/points_http.php仅取明确私密环境的本机3309连接,创建独立合成数据库、临时ROOT/RUNTIME、随机ENV_PREFIX和Redis DB15随机Token作用域;不读取正式.env,不启动worker或调用模型。成功后自建数据库、Token、临时服务/文件已清理,共享fa_user/fa_user_score_log结构/数据及原Redis队列摘要保持。所有权标记限制目录清理;清理失败返回非0并保留受限目录及安全定位,不能声称资源已移除。 主服务238项、HTTP输入190项纯检查通过。原表23项只读预检通过,仍4增量、14类质量计数各项0,applied=false。迁移新增非NULL历史时间质量计数,正UINT流水ID并入invalidLedgerIntegers;合法NULL历史时间保留,非NULL时间范围0—253402271999,确保上海ISO8601四位年份。首次快照坏主键固定409/POINT_LEDGER_INVALID。当前业务配置保持false/null,页面、原表迁移及部署未完成。 迁移纯110项、独立合成库apply/备份/Windows ACL/恢复89项通过。共享隔离库只复制结构,第一新库合成2会员4流水;apply4增量、重复为0,完整双表schema+NDJSON恢复第二新库并逐列摘要一致。目录3文件ACL及读写probe通过,修正Windows原生绝对路径及父级路径校验,纯路径边界覆盖;两新库、私密INI/备份/probe已清理,源表摘要保持。安全verification保留于C:/Users/JUN/AppData/Local/Temp/airz-points-migration-20261001/apply-20261001-115749-d50a5cd87fd5a84a/verification.json。仅为开发演练,未执行共享原表DDL或生产操作。 ### 0.1 历史:0.48后端基础检查 已新增 PointsRules、UserPoints、v1 Points 控制器、五条路由、默认未发布配置、显式迁移脚本及测试。通用后台score/level、通用模型更新与旧User::score均已拒绝写入;新服务直接在账户锁内限定更新score并保存完整流水,不重算level。新会员0分、数据库默认0级及Auth注册1级初始化保持。schema.sql和ScoreLog中文注释已同步目标结构,不能据此声称目标库已经增加字段。 纯检查237项服务、98项写保护、84项迁移通过。隔离MySQL3309随机前缀合成表45项检查通过,包含同键/异键双PHP并发、锁等待跨日和真实流水insert故障回滚;测试表及临时文件已精确清理。原fa_user/fa_user_score_log结构和数据摘要保持。另22项只读迁移入口检查通过,重复预检仍为4增量、13类质量计数均0,applied=false;本轮修复PHP7.4 PDO不追踪原始START事务导致预检commit失败,改PDO事务接口与RR只读快照。 没有迁移原表、运行apply/备份/Windows ACL、调用模型/worker/Redis/HTTP、编译页面或部署。提交结果未知和连接故障由纯替身覆盖,不是实际断网或生产到账证据;页面状态、真实HTTP、迁移应用与恢复仍须后续开发检查。 ## 1. 首阶段建议与待确认项 建议先实施 POINT-002 的账户余额、流水、每日签到,再接 POINT-004 对应页面部分。POINT-003 邀请关系和可信奖励独立实施;不因完成签到而标记整个积分模块完成。 | 项目 | 首阶段建议 | 状态与理由 | | --- | --- | --- | | 每日签到 | 已登录且状态正常的会员,每个业务日一次,每次 10 分 | **待确认数额**;沿用既有页面建议,不表示已批准真实奖励 | | 业务日 | 服务端 Asia/Shanghai,00:00 至次日 00:00,不提供补签 | 待确认;设备时间、时区及登录连续天数不作为依据 | | 连续签到加成 | 不启用,始终固定当期奖励 | 待确认;不需要新增连续签到表 | | 完善资料奖励 | 首阶段关闭,奖励数额为 null | 必须后续确定完整标准、可信事件和一次性范围;进入档案或编辑资料不发奖 | | 邀请奖励 | 首阶段关闭,奖励数额为 null | 有效邀请条件、双方奖励、次数和归因期限待定;复制、分享、绑定码不直接发奖 | | 积分用途 | 首阶段只提供余额与流水,不启用提现、现金兑换、AI 次数抵扣或贷款优惠 | **待确认权益范围**;不能把积分说成授信、信用分、放款条件 | | 有效期 | 首阶段建议不自动过期 | **待确认**;若需要到期扣减,须先设计到期批次/消费抵扣,不靠定时清零余额实现 | | 会员等级 | 新签到服务首阶段建议仅变更 score,不新增等级或权益联动 | **待确认**;旧 User::score 会按余额改 level,需要明确兼容处理后实施 | | 历史体验积分 | 不导入、不补发真实积分 | 本机记录可篡改,匿名也可签到;正式余额只取服务端既有 score | | 历史服务端数据 | 原 score、流水、日期、备注、业务键保留 | 不按流水合计重建余额、不自动清零、不擅自修复差异 | 首阶段的业务确认应至少覆盖“每天 10 分、上海每日一次、不补签”“资料/邀请奖励关闭”“权益、有效期及等级处理”。在明确确认前,设计状态保持 draft;未来配置的安全默认值必须为未发布、停止新入账,奖励数额不得用页面示例兜底。 ## 2. 已核实的源码现状 | 位置 | 当前能力或风险 | 实施处理 | | --- | --- | --- | | [积分页面](../packages/account/points/points.uvue) | 余额为本机签到日期数乘 10;设备日期;固定体验码 AI2680;没有积分 API | 接真实 API 后移除该余额算法及固定码,清理体验缓存但不换算奖励 | | [账户服务](../packages/account/services/account.uts) | 已有登录、账户变化防护;资料 DTO 无 score/share_code | 新增同分包积分服务,复用会话而不扩充无关资料字段 | | [请求基础](../services/api.uts) | 有 Token 变化及 401 防护;当前错误仅保留 message | 实施时保留现有调用兼容,增加可识别的 HTTP 状态/errorCode,区别确定失败与结果未知 | | [数据库结构](D:/AAdesk/202609/airz/database/schema.sql) | fa_user.score 为有符号 INT;流水已具业务键、事件唯一及用户时间索引 | 复用余额和流水;目标库实际结构仍须迁移前只读核实 | | [通用积分方法](D:/AAdesk/202609/airz/application/common/model/User.php) | 旧score()已明确拒绝,通用模型不能更新score/level | 使用UserPoints领域服务,不再吞异常或随积分重算等级 | | [后台会员模型](D:/AAdesk/202609/airz/application/admin/model/User.php) | 模型事件、后台增改/批量及编辑表单已保护score/level | 积分/等级只读;任意SQL仍不经过模型守卫,新增写方须遵守共同账户锁 | 最初设计依据源码和schema.sql;本轮仅对显式隔离库读取实际结构/数据并在随机合成表检查事务。正式库未连接,隔离原积分表仍缺两个新列和两个索引,不能把合成表或结构文件当作正式迁移证据。 ## 3. 规则发布与事件身份 已增加application/extra/points.php,独立于模型调用额度。默认 `enabled=false`,rule_version/check_in_reward/expiry_policy/level_policy/benefits_policy均null;API映射ruleVersion/checkInReward。发布必须提供完整版本、1—1000的规范整数奖励及none/unchanged/none三项策略;这一范围是配置校验上限,不是批准的奖励额。enabled=false且保留完整发布内容表示paused,不删历史回执。损坏/不完整配置固定POINT_RULE_UNAVAILABLE。 规则版本格式为 2—64 位小写 ASCII,`^[a-z][a-z0-9-]{1,63}$`;拟首版名 `points-checkin-20261001-v1`。同一版本永久对应同一奖励额、日期和参与语义;变更必须新版本,不改历史流水。暂停活动不删除成功回执;启用配置编辑后台不属于首阶段,先使用受控部署配置。 每日签到的账本事件固定: ```text business_type = checkin business_id = 服务端上海业务日 YYYY-MM-DD unique = user_id + business_type + business_id ``` 事件键不包含规则版本,因此同一天发布新版本也不能重复奖励。新请求只能为当前业务日入账;设备日期和客户端字段不能指定补签。旧已成功请求允许在下一日、规则暂停或新版本发布后返回原回执,但仍要求当前账户有效且拥有该流水。 ## 4. 接口源码契约 统一前缀 `/api/v1`,全部要求显式合法Bearer及当前正常会员,不接受Cookie/旧Token头回退。沿用v1 Base的code/msg/data与data.errorCode、Cache-Control: no-store,v1框架错误亦补该缓存头。由认证身份取得userId;未知参数、客户端余额/奖励/业务完成状态/其他用户ID均拒绝。五条精确白名单路径已实现并通过HTTP开发检查,原表迁移/规则发布/页面尚未完成,不视为线上能力。 签到要求application/json,可带UTF-8 charset;正文由原生JSON解码及领域校验,必须三个唯一字符串字段,重复键和Unicode等价重复键拒绝。原始QUERY_STRING校验唯一解码键、白名单及PHP解析一致;NUL、坏编码、重复和空片段拒绝。原始/有效HTTP方法须与接口方法一致,覆盖头存在即拒绝;错误方法405,未知路由404。Common既有入口在鉴权前处理允许Origin的匿名OPTIONS,不重复实现CORS。 | 方法与路径 | 严格输入 | data 主要字段 | | --- | --- | --- | | GET /points | 无业务参数 | balance:int、serverDate、timezone、signedToday、checkInEnabled、ruleVersion:string/null、checkInReward:int/null、balanceState:normal/anomaly | | GET /points/rules | 无业务参数 | status:draft/published/paused、ruleVersion:string/null、checkInReward:int/null、timezone、publishedRules;未发布规则不显示为已生效 | | GET /points/ledger | limit 默认20/最大50;后续页 beforeId + snapshotId | items、snapshotId、nextBeforeId;本人流水,ID 字符串 | | POST /points/check-in | requestId、expectedRuleVersion、expectedDate,必须恰好三个字段 | ledgerId、delta:int、balanceAfter:int、signedDate、ruleVersion、replayed:bool | | GET /points/tasks | 无业务参数 | 首阶段只返回已发布签到任务;未实施资料/邀请奖励明确 unavailable,reward=null | 流水源码采用与现有消息/内容一致的有界游标,替代旧page/pageSize目标草案;没有已发布积分API,不涉及接口兼容迁移。tasks实际返回serverDate/timezone/items,签到任务按已发布规则和今日状态显示,资料/邀请始终unavailable且reward=null。邀请/invitations和/invitations/bind保留后续阶段目标,不返回固定码或假的奖励进度。 ### 4.1 签到请求及回执 `requestId` 为 `pts_` 加 32 位小写十六进制,客户端用安全随机数生成,同一用户命名空间内唯一。`expectedRuleVersion` 为页面实际读取的已发布版本;`expectedDate` 为同一次读取返回的 serverDate,是操作预期条件,**不是指定奖励日期**。日期须为真实有效的 YYYY-MM-DD,拒绝非法日历日期、空值、数组和宽松类型转换。 示例仅描述获批发布后的协议形状,当前无这样的运行规则: ```json { "requestId": "pts_0123456789abcdef0123456789abcdef", "expectedRuleVersion": "points-checkin-20261001-v1", "expectedDate": "2026-10-01" } ``` 入账回执与原流水绑定;重放中 `replayed=true`,其余业务字段保持原值。`balanceAfter` 是这笔入账后的历史余额,不是当前余额;页面必须随后重新 GET /points,不能用旧回执覆盖新余额。不同请求键同日重复签到返回明确的 `POINT_ALREADY_SIGNED`,不写第二条零分流水,也不把新的请求键附到原流水。 ### 4.2 余额与流水读取 余额、今日签到状态与规则须来自一次一致的读取:先按当前会员事务读取,固定一个服务端业务日,再完成对应状态查询,避免日界/入账途中混合视图。日期和规则必须一起提供,客户端不能用本机时钟补齐。服务不可读时固定失败,不把错误或未知余额转成 0。 流水DTO为ledgerId/delta/balanceBefore/balanceAfter/businessType/businessDate/ruleVersion/createdAt。时间为ISO8601带时区,历史未知时间保留null;日期为上海YYYY-MM-DD。ID为字符串,支持现有无符号INT至4294967295,服务/迁移要求64位PHP。历史未识别事件统一legacy,业务日/版本可null;不返回原memo、原业务标识或请求键。历史真实0分记录如实展示,不因新服务禁止零分占位而删除。 首次列表取得本会员最大 ID 作为 snapshotId,空为字符串 "0";后续固定上界并按 `id B[锁账户并查成功请求] B -->|同意图命中| C[返回原流水回执] B -->|未命中| D[锁后日期校验] D --> E[检查当日事件 再校验规则与积分边界] E --> F[余额和一条流水同事务提交] D -->|日期或版本变化| G[固定冲突并刷新] E -->|已签到| G ``` ## 6. 首阶段数据库与写路径 schema.sql及显式迁移脚本已定义可空request_id/rule_version、唯一(user_id,request_id)和ID游标索引(user_id,id);原表未应用,旧流水保持。已有业务唯一和用户时间索引不重复添加;用户时间索引不能直接保证按ID排序。成功流水保存日唯一和请求回执,不建独立签到/请求/连续签到表。具体字段、中文注释与预检见[数据库增量方案](D:/AAdesk/202609/airz/database/points-design.md)。 本轮已盘点并封闭通用后台绝对值编辑、批量、User::score和模型score/level更新;仅新增服务加锁不能防其他旧写方覆盖余额。受控管理员积分调整独立排期,当前只读;任意SQL不经过模型守卫,后续插件/导入必须重新审查。会员删除的既有历史策略本轮保持,可能有孤儿流水;已删除/停用账户不能通过新服务读取或重放。 新服务拒绝外层事务,确保成功意味着本次真正提交;不回滚调用方已有事务。数据库break_reconnect必须严格false,防断线自动重连使单条SQL意外自动提交。事务竞争只在确认本人事务回滚后映射409,未知提交返回503保留原键,不进行自动SQL重试。 后续邀请关系需独立表,受邀人唯一绑定、禁止自邀、邀请码唯一且历史冲突先人工处理。资料奖励以永久任务事件去重,不能按每次资料修订发奖。只有服务端可信业务事件可触发奖励;不能用客户端声称“资料已完成/好友已注册”发奖。 ## 7. 页面实施边界 - 登录后恢复积分页需为现有登录白名单增加固定 `resumePoints` 目标,不接受任意回跳 URL。 - 初次/刷新分别处理加载、失败、空流水、未发布规则、已签到;失败清掉不可信旧状态,不显示假的 0 分或成功。 - 发送中锁定签到按钮;请求结果未知时保留原 requestId/date/version 和账户身份,只允许原请求确认。不能因刷新、关闭提示或跨日自动生成另一签到请求。 - 原回执仅确认原业务日;回执后重新读取当前余额和 serverDate。要签到新一天,必须由用户在刷新后明确点击。 - 离页/换账号拒绝迟到响应,未确认请求随账户作用域隔离;未知请求保留范围先按当前页面会话,不跨退出/换账号迁移。 - 从 API 的 ruleVersion/reward 显示规则与奖励,不再写死 10、AI2680 或本机积分。未接邀请前不显示“真实个人邀请码”或未发生的邀请奖励。 - 清理体验签到缓存不产生服务器写入;原匿名/本机记录不影响服务器 signedToday。 ## 8. 后续开发检查清单与顺序 以下为完整开发检查范围;已覆盖部分见第0节。HTTP后端隔离检查已完成,页面和原表迁移/正式发布仍未完成: | 检查 | 关键反例 | | --- | --- | | 并发入账 | 同日同键/异键双进程;只有一笔奖励和流水,余额正确 | | 历史重放 | 第一天成功、第二天原键仍原回执;新日期复用成功键拒绝 | | 日期边界 | 锁等待跨午夜;昨日未执行请求不能在新一天到账;客户端时区/时钟篡改无效 | | 规则边界 | 规则变化/暂停/未发布;成功旧回执仍可读,未成功新请求拒绝 | | 原子回滚 | 流水 insert、余额 update、commit 故障;无“加分成功但无流水” | | 数据边界 | 负数、INT 上限、坏元数据、NULL 历史事件、非规范业务键不被误认 | | 所有权与隐私 | 匿名/禁用/跨账户、流水游标和自由备注;只本人读写 | | 老写方 | 后台伪造 score/level、legacy 方法/插件调用;不能覆盖新账本 | | 页面状态 | 实际响应丢失、原键确认、换账号、登录恢复、加载/空/错、迟到回包 | | 迁移和恢复 | 原表只读预检;新合成库apply4增量/重复0、完整备份/ACL/第二库恢复、旧数据摘要一致及精确清理已通过 | 当前后端可在规则未定稿时保持默认关闭。下一开发子项可实施积分页面真实读取、登录恢复及原键确认流程;规则未发布时签到不可用,不补造奖励。首阶段业务规则定稿、原表迁移和发布另行安排;邀请资格/奖励规则与关系方案留POINT-003,完整POINT-004后续推进。发布参数在确认前保持空值。 正式迁移、上线、真机/多设备、权益运营和真实业务验收后续按实际范围安排,不复用历史生产授权。本轮为源码、纯测试及显式隔离开发检查,不是生产签到到账或积分页面完成证据。