# Project Context ## 2026-10-24 恢复测试账号与正式短信登录并存 - 用户明确要求三个测试账号继续使用固定验证码直接登录,正式手机号走短信,短信验证码两分钟过期。10-23 的 `0018bae` 将所有号码统一送至短信接口且只接受6位码,导致5位测试码 `66666` 无法使用;不能再次统一成纯短信登录。 - `water-mini/src/utils/login-flow.ts` 按三个指定号码分流到 `/auth/test-phone`,其他号码走 `/auth/send-sms` 与 `/auth/phone-login`。页面分别校验 `test_phone_login`、`sms_login`,任一开启即显示手机号入口;测试号不发送短信、不启动发送倒计时,固定码由后端验证,正式号不能使用测试固定码。 - `SmsCode` 有效期改为120秒;60秒重发间隔独立保留。重发事务内作废旧码,只校验最新一条记录;最新码过期/消费或重发失败均不能回退使用旧码。存量5分钟验证码也受创建后120秒上限约束。 - 本轮没有改动 `APP_ENV=local`、测试开关、来源白名单、已有账号预绑定手机号规则或业务授权。线上公开 `/config` 只读检查返回短信和测试登录均开启,未验证线上固定码登录或真实短信发送。 - 验证:小程序36项、后端短信51项、测试手机号25项、架构126项、契约911项通过;后端测试使用记录型短信发送器且全部数据回滚。API说明、OpenAPI及其公开副本已同步。 - 类型检查、微信与H5构建通过;微信产物已确认包含测试登录分流及 `lazyCodeLoading=requiredComponents`。本轮未部署线上后端、未上传微信,也未发布H5预览。 ## 2026-10-21 短信验证码登录(方案A:手机号预绑定到已有账号) - 起因:`login-flow.ts` 早就引用了 `/auth/send-sms` 与 `/auth/phone-login`,但两个接口从未实装,非测试号用户点「获取验证码」得到的是失败文案。微信登录侧 AppID 又在 `5e3ff2e` 里被误改为 `wx8ebee994ea7c5af3`(真实账号是 `wxb53276de7129d025`),本地源码与 `.env` 一致地错、反而更难排查。 - 方案:用户选定 A —— 短信作为**已有账号的第二种凭据**,手机号须先由超级管理员在后台绑定;不新建账号。这样 `user_id`、业务身份、权限与同一账号的微信登录完全一致,且不与 `IdentityManagement::grant()` 的「1 账号 1 身份」冲突。B(自动建号)会造出零身份账号,登录后只能停在 `/pages/account/identities` 空页;C(按手机号反查业务档案自动授权)因 water 业务档案上根本没有 phone/mobile 字段而未采用。 - 新增 `POST /auth/send-sms`、`POST /auth/phone-login`(公开组,与其他登录入口共用 `LoginThrottle`)、`POST /core/water/mini-user-phone` 后台绑定。`SystemService` 新增 `capabilities.sms_login`。`AuthService` 抽出 `issueForExisting()`:原 `issue()` 会为新身份建号,短信登录必须登录到已存在的行,两条路径共用 `sessionFor()`。未改动 `grant()`。 - **防枚举**:已绑定与未绑定手机号返回完全一致的 `{sent:true,expires_in:300}`,未绑定时不发短信也不落库。否则该接口可直接用于枚举哪些手机号注册过本系统。 - 验证码 6 位 `random_int`、5 分钟过期、单次有效、只存 HMAC-SHA256(密钥 `MINI_SMS_CODE_PEPPER`,留空回退 `MINI_SMS_PASSWORD`)。6 位空间小,裸 SHA-256 可离线穷举,加盐是必需的。明文不进库不进日志。行锁保证并发下同一个码不会被用两次。 - **实现期发现并修掉的真实缺陷**:`consume()` 原先在事务内 `throw`,导致一次输错把刚累加的失败次数一起回滚,5 次锁定形同虚设。改为事务内只判定、提交后再抛。 - 限流全部发生在发短信之前:同号 60s、单号每日 10 条、单 IP 每小时 30 条。短信宝返回码(0 成功,-1 参数不全,-2 账号密码错误,-3/-4 余额,30 间隔太短)统一转 50302,不透传;日志只记状态码,请求 URL 含密码故从不记录。短信下发失败立即 `discard()` 该验证码,未送达的码不会被事后使用。 - 迁移 `scripts/migrate_water_mini_sms.php --apply` 已在本机执行并复跑验证幂等:新增 `sa_water_mini_sms_code`、给 `sa_water_mini_user` 加 `phone` 及唯一索引。MySQL 唯一索引不约束 NULL,存量无手机号账号不受影响。`.env` 已写入短信宝账号(gitignore),`.env.example` 为占位。 - 验证:新测试 `mini_sms_login_test.php` 39 项全过(注入记录型 sender,全程无短信外发,全部数据回滚);契约 911 项/53 操作、架构 126 项、框架 47 项、HTTP 110 项、测试手机号 25 项、ClientIp 16 项、setup 5 项均过。全量 34 个后端测试 29 通过、5 失败,失败清单与上一轮完全一致(`mini_identity_management_test.php`、`water_mini_api_test.php` 旧断言,`mini_protection_*` 本机 Redis 未启动,`water_ledger_test.php:18`),已用 `git stash` 在还原后的干净树复现确认非本次引入。实测 HTTP:未绑定号下发返回 200 且不发短信、未绑定登录 401、格式非法/多余字段/非常用号段均 422。前端 `vue-tsc` 通过、25 项测试通过、mp-weixin 重新编译通过。 - 未做:未与短信宝真实联调发送(本轮验证全部走注入的假 sender,用户真实号码未收到短信);未提供专用解绑接口;未加 AppID 前后端一致性自检(`PROJECT_CONTEXT` 早已记录该缺口);`sa_water_mini_sms_code` 靠 `issue()` 时的机会式清理(删除 1 天前记录)收敛,没有独立定时任务。 - 提醒:短信宝账号密码是在对话里明文给出的,只落在 gitignore 的 `server/.env`;若该对话会被留存或分享,建议在短信宝后台重置一次。 ## 2026-10-21 小程序登录加固:代理来源、多身份锁死、微信 errcode - 起因:`AuthController` 与 `RequestProfile::ip()` 都直接用 `$request->getRemoteIp()`。已确认 webman 该方法只返回 TCP 对端地址、完全不识别反向代理(`webman-framework/src/Http/Request.php:180`)。本机 8787 前若挂 nginx/宝塔网关,所有调用方会共用一个来源:登录限流按单一 60 次/分钟 桶计算;更严重的是 `TestPhoneLogin::assertSourceAllowed()` 会拿代理 IP 比对,而回环 `127.0.0.1` 正是默认白名单,等于对所有来源放行本地测试手机号登录。 - 新增 `app/miniapi/http/ClientIp.php`:`from(peer, forwardedFor, trustedProxies)` 是纯函数以便单测,`resolve(Request)` 读配置后委托。只有直连对端在白名单内才解析 `X-Forwarded-For`,并从右向左跳过受信任的代理跳;不可解析、缺头或整链皆为受信任地址时回落到对端地址。兼容 `1.2.3.4:port` 与 `[::1]:port`,配置项与请求值统一经 inet_pton/ntop 规范化,避免配置写法差异导致静默失效。 - 新增配置 `MINI_TRUSTED_PROXIES`,默认 `127.0.0.1,::1`(回环不可从网络到达)。不配置等价于维持原行为;反向代理部署须列出每一跳代理地址,否则聚合限流与测试来源放行问题依旧存在。`server/.env.example` 已补说明。 - `AuthController::login`、`AuthController::testPhone`、`RequestProfile::ip()` 改用 `ClientIp::resolve()`。testPhone 中限流与来源白名单共用同一个解析结果,避免两者判定不一致。登录不再信任任意来源的 `X-Forwarded-For` 这一原有前提保持不变。 - `WechatIdentityProvider`:微信对凭据类问题返回 HTTP 200 + errcode,原实现只判 `openid` 是否存在,导致 code 过期(40029)、code 已用(40163)、频率限制(40226/45009/45011)、AppID 无效(40013/40125) 全部塌缩为同一句 40102 且日志无痕。现按 errcode 分流,并以 `Log::warning('mini_api.wechat_login', ['errcode'=>...])` 留痕;只记录错误码,不记录 session_key/unionid/errmsg。复用既有 40102/42901/50301,未新增错误码,OpenAPI 与前端类型无需改动。响应体无法解析时由原来的 40102 改为更准确的 50302。 - 多身份锁死:`Access::identities()` 在多个生效身份时抛 40903,该调用位于 `AuthService::issue()` 事务内会整体回滚,用户既拿不到 Token 也没有自助恢复路径(后台可逐条停用,但无盘点手段,且存量数据无清理入口)。新增 `scripts/audit_mini_identity_conflict.php`:默认只读,复用 `Access::resourceModel()` 复核授权指向的企业/司机/代理是否仍启用,区分「指向已删除或停用档案(Access 不计入,不影响登录)」与「真正冲突」;`--apply` 在事务内停用除最新一条外的生效授权并打印被停用编号,回滚方式为把这些编号的 status 改回 1。不删除记录,不改动用户与业务档案。 - 验证:`php -l` 6 个改动文件全部通过。新增 `tests/mini_client_ip_test.php` 16 项全过,覆盖非信任对端忽略伪造头、可信代理解析、多跳从右向左、端口与 IPv6 括号形式、不可解析回落、配置项规范化。`mini_api_architecture_test` 118 项、`mini_api_contract_test` 883 项 51 操作、`mini_test_phone_test` 25 项、`water_mini_http_test` 110 项实测 HTTP 均通过。全量 33 个后端测试 28 通过、5 失败。 - 5 项失败全部为改动前既有失败,已用 `git stash` 在干净树上逐一复现确认:`mini_identity_management_test.php`、`water_mini_api_test.php`(PROJECT_CONTEXT 已记录的「同一账号可绑定多身份」旧断言)、`mini_protection_test.php` 与 `mini_protection_http_test.php`(本机 Redis 未启动、保护开关未启用)、`water_ledger_test.php:18`。本轮未修改认证规则、未掩盖这些失败。 - 本机当前数据为 3 条生效授权、0 冲突、0 失效引用,无需执行 `--apply`。php-cs-fixer 未安装在本环境,格式按 `.php-cs-fixer.mini.php` 规则人工核对(PSR12、concat_space、blank_line_before_statement return、no_unused_imports 等)。 - 未做:`sa_water_mini_session` 无过期清理,长期累积;Token 无滑动续期,7 天硬过期后前端静默清会话;`LoginThrottle` 仍是文件锁单机限流,多机部署需换共享限流器;`water-mini/src/utils/login-flow.ts` 引用的 `/auth/send-sms`、`/auth/phone-login` 仍未实装,非测试号用户点「获取验证码」得到的是 404 类文案;前后端 appid 仍是两份文件无一致性校验。均未在本轮处理。 - 文档同步:`server/docs/WATER_MINI_API.md` 更新调用方地址解析规则、测试手机号来源白名单的同源判定,以及新增「微信登录失败分流」小节(含 errcode 对照表)。 - 提醒:`server/.env` 的 `MINI_WECHAT_SECRET` 明文曾在排查过程中输出到会话记录,需在微信公众平台重置该 AppSecret 并同步更新 `server/.env`。 ## 2026-10-19 企业邀请推广赠水第一版 - 用户选择先实现推广奖励;按企业邀请企业、首购后签收、财务人工核实后赠水权益实现。奖励桶数/规格可配置,默认关闭,无现金发放、无自动送水。 - 新增 `server/plugin/saiadmin/app/service/promotion/`,三张 `sa_water_promotion_*` 表、本机已执行可复跑的增量迁移;没有回填历史奖励。平台规则在绑定时快照;禁止自邀、共用管理员及循环绑定,首购前绑定一次。购水共用入口 `WaterPurchaseReceipt` 生成待审核奖励,组合结算按整笔金额判断,首购前已有配送不能领奖。审核发放与权益、流水同事务,重复请求不重复发奖。 - 后台入口:财务结算中心 → 推广赠水奖励,沿用 finance:read/write;小程序“我的 → 奖励与推广”支持邀请码、复制/微信页面分享、显式绑定和真实分页记录,移除旧测试奖励作为实际返回。个人中心显示累计赠水桶数,权益明细增加推广赠水;小程序不披露客户付款凭据、金额或内部审核说明。 - OpenAPI 69个Schema,51个操作(含HEAD);文档及类型已同步。使用、迁移、API、约束和回退见 `server/docs/WATER_PROMOTION.md`;本轮执行结果见 `server/docs/WATER_PROMOTION_RESULTS.md`。 - 验证:9组后端共1445项通过(新增推广56);小程序25项通过(新增页面4),两端类型检查、PHP语法/格式、前端格式/ESLint/Stylelint及后台/H5/微信构建通过。业务夹具全部回滚。后台4173及H5预览已更新,HTML及入口资源哈希与构建一致;实际本机登录读取rewards得到water/initialized=true/enabled=false/demo=false/0桶,随后退出测试会话。无真实奖励发放、未改活动启用状态、未上传微信或发布生产。 - 已有 `water_mini_api_test.php:331`、`mini_identity_management_test.php:133` 仍假设同一账号能同时绑定多个业务身份,实际 IdentityManagement.php:72 已禁止,两个旧测试失败;本轮未修改认证规则或掩盖失败。浏览器点击/截图、微信真机分享和完整端到端验收未执行。 - 正式退款/已使用赠水扣回未实现;首购后的对应规格签收不能证明组合订单所有权益已全部配送,必须人工核实后审核。关闭活动停止新绑定,已有承诺按快照审核;不能通过删除表或回滚代码撤销已生效权益。 ## 2026-10-09 后台 P0/P1 待补做收尾 - 完成四项真待补做,删残留 TODO、清理误导性 catch、实装注册端点、实装会员开通/续费。 - **T1 删 TODO**:`views/system/role/modules/permission-dialog.vue:141` 的 `// TODO: 调用保存权限接口` 已删除。后端 `SystemRoleController::menuPermission` 早已实现,仅拖调一次 `api.menuPermission` 的注释残留。 - **T2 整批清理 catch**:17 个 dialog 文件中的 `console.log('表单验证失败:', error)` 改为留空 catch + 注释(说明 ElForm 自带校验提示 + axios 拦截器自动 `ElMessage.error`);`register/index.vue` catch 保留但改文案;`login/index.vue` 注释化的 `console.log(error.code)` 删除。本轮未触动含 `loading.value = false` 的重置代码。 - **T3 注册 API**:`POST /core/register` 新增,免登录;`RegisterController` 继承 `OpenController`,`RegisterValidate` 含 username(3-20 字母数字唯一)/password(6-16)/confirmPassword(确认);密码走 `password_hash($password, PASSWORD_DEFAULT)`,默认 `is_super=0 status=1 dashboard=work`,不分配任何角色。前端 `api/auth.ts` 新增 `fetchRegister`,`register/index.vue` 替换 `setTimeout` 模拟为真实调用 + catch 留 `loading.value = false` finally。 - **T4 会员开通/续费**:`POST /core/user-management/users/member-activation`,权限 `core:user:list:memberActivation`;`MemberActivationValidate` 校验 card_type∈{0,1,2,3}、pay_amount≥0、expire_time>now;`RentalUserLogic::activateMember` 事务内 INSERT `sa_member_activation_record`(source=3,manage_agent_id=operator) + UPDATE `sa_rental_user.member_expire_at/member_card_type`;复用 `accessibleManagerIds()` + `assertUserAccessible()` 鉴权。SQL `20261009_add_rental_user_member_fields.sql` 加字段 + 从历史 activation 记录回填最新有效到期(仅 status=1 且未过期);`20261009_add_member_activation_button_permission.sql` 在 UserList 菜单下挂按钮并自动赋给有 UserList 权限的角色;`scripts/migrate_rental_user_member_fields.php` 等效合并两个 SQL 的可执行脚本,`--apply` 跑。前端 `api/userManagement/rentalUser.ts` 新增 `activateMember` + `MemberActivationPayload/Result` + `MEMBER_CARD_TYPE_OPTIONS` + `RentalUser` 加 `member_expire_at/member_card_type/member_card_type_name/member_active`;`user-list/index.vue` 新增「会员状态」列、`openMemberActivation` 改按钮 click、`MemberActivationDialog` 含卡类型/金额/到期/备注字段(默认到期 +30 天,过往日期禁用);表格「储值卡赠额」列后插入会员状态列。 - **验证**:`php -l` 6 个后端文件全部通过;`vue-tsc --noEmit` exit=0 0 错误;ESLint 检查 23 个改动文件 0 错误(`--fix` 处理 6 处 prettier/格式问题);原 `showPendingAction` 占位函数已删除(无引用);注册端点 + 会员开通端点的冒烟脚本 `tests/register_and_member_activation_test.php` 已写好(本次未在 webman 容器中运行,留待下次启动后跑)。 - **未做**:退款/撤销会员流程(等下一期);多种会员等级(沿用 card_type 0-3);自动续费/到期提醒;`MemberActivationRecordLogic` 中 1-4 月/季/半年/年卡的常量与 `MemberActivationValidate::card_type` 0-3 一致(原系统 DEVICE_NAMES 已包含设备类型 100+ 的命名,与卡类型无关,无需调整)。 ## 2026-10-09 昨日一致性修复接续 - 当前后台4173、小程序API及H5预览8787可访问;本机配置为企业余额支付/测试手机号登录开启,微信登录/真实微信支付关闭。正式企业余额充值退款、岗位权限和会计口径仍缺业务定义,不根据旧租赁钱包推断。 - 后端订单快照、套餐删除、小程序业务、财务、预警、企业、调度、履约、契约和架构10组共1589项通过,业务夹具回滚。小程序21项测试、两端类型检查、后台道路/套餐删除脚本通过。 - 后台和H5的入口HTML及2/3个入口JS/CSS资源SHA256与现有构建一致;本轮未重构建、发布、迁移、重启或修改业务数据。 - 修正 `water-mini/README.md` 与子项目上下文中的余额未开放、可开票、按名称猜分类、Schema数量和旧验证状态。浏览器工具因 `windows sandbox failed: orchestrator_helper_exit_nonzero` 无法启动,点击/截图/真机验收仍未完成;详细记录见 `server/docs/ADMIN_MINI_REPAIR_RESULTS.md`。 ## 2026-10-08 后台与小程序一致性修复 - CheckoutService 按持久化订单快照结算,WaterPurchaseReceipt 共用收款/权益逻辑;扣款、流水、权益和支付状态同事务。套餐修改/下架/删除不影响 30 分钟内的原订单,原 SKU 或企业停用仍拒绝支付。创建时锁全部套餐再锁 SKU;支付统一 SKU → 企业 → 余额/权益。quote_revision 校验价格、规格和桶数变化,新小程序提交,旧客户端可省略。 - 财务列表/导出通过实际余额扣款流水与已支付 checkout 明细识别企业余额购水;小程序用户由 MiniAudit/MiniBalanceLedger 追踪,供水记录 created_by=0。未迁移、未自动修补历史差异。 - 小程序每次进入套餐页刷新,购物车使用最新报价、失败禁止提交、金额保留分;去除无实现的独立卡/到期扣减/电子开票承诺和名称猜分类。多地址接口文档/地址类型已补齐。后台预警、权益、企业统计按企业+SKU+有效任务关联,兼容新旧 active_key。 - waterAmap 同页请求共享 1100ms 间隔和最多 3 次 QPS 退避;定向成本缓存 60 秒、256 条。不是跨用户或跨服务的 Key 总量限流。H5 本地发布先复制资源再原子切换 HTML,保留旧哈希资产。 - 已通过后端 1653 项、小程序 21 项、后台删除/道路脚本、类型/语法/格式与后台/H5/微信生产构建;后台 4173 和小程序 8787/water-mini/index.html 的入口 HTML 与资源哈希已核对为新构建。业务夹具回滚,未上传微信或验证真机点击。 - 企业余额正式入账与退款、权限岗位细分、套餐分类/设备/有效期配置、财务会计口径仍待业务定义;不可把旧租赁钱包直接当供水企业余额。详细记录:server/docs/ADMIN_MINI_REPAIR_RESULTS.md。 ## 2026-09-19 uni-app 小程序第一版 - 用户明确开始制作移动前端,覆盖此前“只做接口”范围;根 AGENTS.md 已更新。独立 `water-mini` 使用官方 uni-app Vue3模板对应的 DCloud 3.0.0-5020420260813003、Vue3.4.21、TypeScript,依赖锁定 package-lock.json。对照 `D:\水后台UI\mobile\M01–M18.png` 实现28个页面,复用组件/会话/请求/分页/命令服务,59个TS接口类型由既有OpenAPI生成。 - 企业端:首页/真实套餐/组合报价/待支付意向及取消/地址版本编辑/权益流水与预约/配送跟踪/私有照片/设备用量;司机端:工作台、任务/路线、出发、GCJ02打卡、SN归属核对、拍照、草稿、签收及异常;代理端:客户筛选与真实收款统计。微信登录与身份选择共用现有独立Token,首次登录等管理员绑定。未实现的手机号登录、支付、佣金提现、推广、硬件绑定/GPS明确关闭,无模拟账户或伪造业务数据。 - 登录状态与请求解耦;401/退出/身份切换清空私有页面数据,旧响应不能覆盖新身份。写请求先持久化用户+路径下的完整payload和request_key,双击锁定,网络/5xx重试沿用原请求;未知结果期间修改内容会拒绝并提供重试按钮。照片用带Token的下载请求,不在URL泄露令牌;上传压缩后前端限制约600KB以适应微信单键存储重试,签收选择1–4张照片;临时照片退出/页面销毁清理。 - 已通过类型检查、12项关键逻辑测试(受控uni网络替身,不写业务数据库)、Prettier、微信与H5生产构建;28个微信WXML均存在,编译包约203KB。本机H5发布在 `server/public/water-mini`,地址 `http://127.0.0.1:8787/water-mini/index.html`;页面/主资源HTTP200,公开套餐接口实际返回3条。后端未修改、无迁移、无Redis变更、无服务重启或微信平台上传。 - 运行/维护/边界见 `water-mini/README.md`。微信开发者工具导入 `water-mini/dist/build/mp-weixin`;实际AppID需填写 `src/manifest.json` 的 mp-weixin.appid,API域名由 `.env.local` 的 VITE_API_BASE_URL 配置,真机需HTTPS及合法域名。微信后端凭据仍未配置,真实登录/三端授权业务端到端、拍照/位置实机、浏览器点击验收未完成;浏览器工具仍报 unsupported Codex auth method: apikey。H5仅预览公开页面/登录提示,不提供模拟登录或手填Token入口。 ## 2026-09-19 小程序接口文档网页版 - 新增 `server/public/docs/mini/index.html`,本机访问 `http://127.0.0.1:8787/docs/mini/index.html`。Swagger UI 5.33.0 静态资源来自官方 npm 包,下载后验证 SHA-512,保留 LICENSE/NOTICE 和来源记录;页面支持45个操作的分类、筛选、Schema、示例、Bearer授权和在线调试。手机号登录明确标注仍为设计,未新增对应业务接口。 - `server/scripts/build_mini_docs.php` 将唯一维护源 `docs/openapi/water-mini-v1.json` 同步到公开目录,`--check` 检查一致性。页面强制使用当前来源的 `/api/mini/v1`,不持久化Token、不使用外部CDN或第三方校验器;配置状态实时读取 `/config`。 - `MINI_ALLOWED_ORIGINS` 支持逗号分隔精确来源,默认仅本机8787的127.0.0.1/localhost;部署需覆盖为实际HTTPS来源。业务鉴权不变;接入文档、维护说明和.env.example同步更新。无数据库/Redis/业务接口变更,无手工重启或生产部署。 - 验证:页面/脚本/样式/OpenAPI/Swagger资源HTTP200,框架含HTTP64项、接口契约770项(45操作)通过,PHP/JS语法与文档同步检查通过;实际带本机Origin的GET200、OPTIONS204、空登录POST42201,未登录会话40101,未知来源40301。浏览器工具持续报 unsupported Codex auth method: apikey,未做浏览器渲染/点击验收。PowerShell首次读取异常响应体为空导致断言失败,改用curl确认实际错误码正确。 ## 2026-09-18 小程序规则整改与前端接口文档 - 用户明确授权按规则修改代码并给前端接口文档;按照 AI_RULES 的 IMPLEMENT / MODE-3+MODE-4、L3 执行前置方案和分阶段验证。AI_RULES 仅93–118节,项目内未找到其引用的 PROJECT_RULES.md;实际架构要求见根 AGENTS.md。 - 地址修改:锁定企业后,WaterDelivery 模型 lockForUpdate 当前读检查 active_key,修复 REPEATABLE READ 授权快照漏掉并发后台预约的问题。新双连接测试在企业锁前交错提交真实预约,证实普通读仍看不到任务而新逻辑返回40902。 - 新 PhoneNumber 公共格式策略经 Fields::phone/WaterPresenter 复用;接受7–15位国内/国际电话、去除格式符号,拒绝纯符号/掩码/非法格式;格式不明的历史号码统一****,不再原样暴露。 - AgentQuery::customers 用现有 orders/devices 关联 withSum/withMax/withExists/withCount,消除每客户4次查询,保持返回字段及排除未来收款的口径;测试确认增加客户行数不会增加查询次数。 - 前端交付:server/docs/WATER_MINI_FRONTEND.md(登录、身份、逐接口目录、字段语义、调用顺序、幂等、照片、错误码);server/docs/openapi/water-mini-v1.json(OpenAPI3.0.3,45个操作含2个HEAD,59个Schema)。维护说明 WATER_MINI_API.md 更新;变更验证记录 WATER_MINI_REVIEW_FIXES.md。 - 实测:小程序业务161(含32组返回Schema)、地址双连接12、架构97、框架含HTTP64、真实HTTP112;原业务64/调度42/履约59/代理59/企业64通过。契约检查实际路由/鉴权/引用/示例全部通过;PHP lint57文件、格式化dry-run64文件0差异。OpenAPI未在Apifox界面实际导入;项目测试校验器只覆盖本项目使用的Schema子集。 - 普通业务夹具回滚,HTTP临时身份清理;并发测试需提交独立企业/SKU库存/权益/授权夹具供第二连接读取,finally按ID清理。无迁移、无持久业务种子、未重启或部署。微信凭据未配置,真实外部登录未联调;支付/佣金/提现等尚未开放的能力保持关闭。 ## 2026-09-18 小程序 UI 对应基础接口与分层重构 - 用户要求仅做接口,UI 依据 `D:\水后台UI\mobile\M01.png`–`M18.png`。用户反复强调简洁、可读、可维护、公共逻辑集中、设计模式、控制器/服务分层、模型查询;已写入根 `AGENTS.md`,后续持续遵守。 - `server/app/miniapi` 由基础 health/session 扩展为系统、认证、套餐、企业、订单、司机、代理、照片独立控制器;公共 BaseController/Access/Input/Fields/Pagination/WaterPresenter/Commands 复用。控制器不查询数据库;业务读写使用 Eloquent 模型,复用 WaterBusiness/WaterFulfillment 的库存与权益事务。套餐和配送列表使用模型关联预加载。 - 登录采用 IdentityProvider→WechatIdentityProvider 适配器;司机动作采用 TaskAction 命令接口+TaskActionFactory 简单工厂;InTransitAction 模板方法复用状态校验,SaveFulfillment 共用暂存/签收验证。服务不保存跨请求身份。96项架构约束检查防止控制器查库、服务依赖HTTP或绕过模型。 - 微信 code 登录只创建无授权小程序用户;独立随机令牌只存摘要,每请求校验过期/撤销/停用及当前角色。超级管理员使用 `/core/water/mini-users`、`/core/water/mini-grants` 绑定/撤销企业管理员/成员、司机、代理身份,与后台 JWT/管理员ID隔离。单机多进程登录限流有文件锁,多机需共享限流。 - 企业接口涵盖真实首页人群、启用套餐、服务器报价、待支付意向、地址版本校验修改、汇总权益/流水、预约/取消、配送跟踪、设备用量;司机涵盖任务/路线、出发、打卡、照片、暂存、实际签收、异常记录、已绑定SN核对;代理仅查询授权自身/有效下级的客户与真实收款统计。写操作统一事务、幂等、撤权复核及独立小程序审计。 - 已执行 `server/scripts/migrate_water_mini_api.php --apply`:8张 sa_water_mini_* 表,不植入身份/支付/财务数据。照片JPEG重编码及时间/上报位置水印,专用私有表按任务归属读取。真实支付、分卡有效期、多站点、佣金提现、邀请奖励、实时GPS、硬件绑定/联网检测仍未实现,接口明确能力关闭;没有把UI样例比例写成业务规则。 - 当前 `/api/mini/v1/config` 报告微信登录未配置;需补充 `.env` 的 MINI_WECHAT_APPID/SECRET 等,未做微信外部联调。原收款登记仍是实际权益入口,下单意向不产生权益。代理业绩按企业当前归属,不能当佣金账本。 - 已通过 PHP 语法61文件、格式化检查、业务接口93项、架构96项、含HTTP基础64项、真实HTTP112项;旧业务64/调度42/履约59/代理59/内容72项回归通过。业务夹具事务回滚,HTTP临时无授权用户finally清理。无前端改动。 - 接口说明、M01–M18映射、请求示例、模式扩展及限制见 `server/docs/WATER_MINI_API.md`;格式化配置 `.php-cs-fixer.mini.php`,本机工具 runtime/tools/php-cs-fixer.phar(3.88.2)。本次实际HTTP已加载新接口;Windows不要用重复启动windows.php替代重载。 ## 2026-09-17 菜单管理导航入口 - 用户要求把菜单操作加到导航;按添加“菜单管理”入口实现。原系统菜单id8及core:menu:index/read/save/update/destroy早已存在,供水mergeWaterMenus原先隐藏全部旧系统导航,导致入口不可见。 - router/modules/water.ts从服务端已授权菜单树提取/system/menu,保留原Menu路由名称、组件、可见标记与权限,放在角色与权限后、操作审计前,移除原树重复节点;其他旧菜单仍隐藏但保留路由。无授权不创建入口,不更改数据库菜单、角色授权或业务数据。 - views/index/index.vue让/system/menu沿用供水外观。复用现有system/menu页面及CRUD接口,原新增/编辑/删除按钮权限不变;补货阈值策略现有权限仍是water:alerts:write(菜单名称“管理补货预警”)。 - Prettier/ESLint/vue-tsc通过;导航运行验证覆盖排序、原始树不变、未授权不补发、无重复、无供水菜单回退;真实HTTP确认/core/system/menu返回唯一可见Menu页面,/core/menu/index包含全部既有菜单操作权限。浏览器仍有已知apikey认证阻塞,未做点击验收。 - Vite3335模块生产构建通过,dist更新;4173首页和包含导航修改的主JS资源SHA256一致。 ## 2026-09-17 平台阈值策略编辑 - 用户进一步明确保留“阈值策略”入口,用于编辑平台统一参数;不支持逐企业覆盖。已恢复AlertPolicyDialog:补货预警桶数alert_qty(默认5,严格小于)、每人每桶天数days_per_barrel(默认3)、紧急桶数urgent_qty(默认2,不高于)。紧急阈值须小于补货阈值,天数范围1–365,整数校验。 - 已执行并幂等复跑 scripts/migrate_water_alert_setting.php --apply,新增InnoDB单行sa_water_alert_setting;默认5/3/2,保存不重置。AlertSettings命令服务以行锁+revision防止覆盖过期修改,策略与platform:alerts审计同事务。首次发现本机新表默认MyISAM,已转换为InnoDB并精确恢复测试参数,最终快照回滚验证通过。 - GET alerts/policy要求alerts:read;POST alerts action=policy要求alerts:write,仅允许平台参数,旧id/threshold_qty等账户覆盖拒绝。保存后前端刷新列表、统计、规则和预计用水。SQL/逐行计算、企业补货状态、账户预测共用每次查询的策略快照;无跨请求缓存,后台保存后后续查询立即生效。 - 公司人数employee_count沿用本轮已新增可空字段及企业编辑/详情。日均升数=规格升数×人数÷配置天数,预计天数=满桶×配置天数÷人数。未登记人数仅按桶数预警;多规格分别估算。旧账户threshold_qty/daily_liters不参与算法,不改写历史值。 - 不按预计用量扣库存、不自动建单,现场桶数仍由盘点/换桶/签收维护。配送沿用原冻结、权限及批次回滚。当前默认29账户/28预警(24紧急、4普通),17已有任务,无业务数据变更。 - 新平台策略43项+预警118项+业务64项+企业68项共293项通过(含HTTP);所有夹具及策略/业务快照回滚。PHP lint、前端Prettier/ESLint/Stylelint/vue-tsc通过。详细规则与迁移见server/docs/WATER_ALERTS.md。浏览器仍有已知apikey认证故障,未做点击/截图验收。 - 生产构建成功(3335模块),dist已更新;4173首页与含“保存并生效”的策略资源SHA256一致。仅保留既有system/menu导入警告。 ## 2026-09-17 首页内容配置独立页面 - `/water-content` 按新参考图实现左侧轮播列表/筛选/8条分页/全量上线数量与右侧手机预览;新建/编辑独立表单支持图片及活动链接、人群、区域、起止时间、排序、启停和草稿实时预览。顶部可模拟购水状态、管理员身份与区域,查看有效轮播和真实启用套餐,图片失败回退标题。 - `WaterContentDashboard` 应用查询服务 + `content/ContentInput/ContentPolicy` 校验及状态策略,Eloquent访问原内容/套餐模型。新增GET content/dashboard与content/preview(content:read),写入复用POST content(content:write)和WaterBusiness事务/审计;原通用内容列表同用状态策略。 - 状态按上海时间及精确秒包含边界;全量上线计数不受筛选分页影响。区域按完整名称匹配(留空/全国为全域);模拟管理员身份与购水状态可组合。实际移动端接口/登录人群推导尚未接入,不能把后台预览参数当成真实授权;代理权限仍由角色管理。 - 无数据库迁移,无持久种子数据;当前内容库0条,显示空状态,不填截图样例。参考图已保存到 `server/docs/references/water-content-reference-20260917.png`,契约见 `server/docs/WATER_CONTENT.md`。 - 内容75项(含HTTP)及原业务69项通过,所有夹具和审计回滚;相关PHP lint及前端Prettier/ESLint/Stylelint/vue-tsc通过。浏览器认证故障仍限制实际点击/截图验收。 - Vite生产构建3331模块通过,dist更新;4173首页及内容页资源SHA256与本次构建一致,可访问 `http://127.0.0.1:4173/#/water-content`。 ## 2026-09-17 待审核提现测试数据 - 用户要求增加测试申请,已执行并复跑 `server/scripts/migrate_water_withdrawal_demo.php --apply` 与 `seed_water_withdrawal_demo.php --apply --http`,固定批次 `TEST-WATER-WITHDRAWAL-20260917`,ID1–4。星澜商务5760元、青禾推广用户860元、云帆企业服务3280元、森屿商贸2460元,共4笔12360元,名称均带【提现测试】,日期固定09-17。 - 新增独立 `sa_water_withdrawal_demo` 表,仅存展示夹具,不是真实提现账本;Eloquent `WaterWithdrawalDemo` + `finance/WithdrawalDemo` 返回看板独立 `withdrawal_demo` 字段。真实待提现金额仍null、capabilities.withdrawals仍false,审核/打款未启用;原收款、权益、库存、配送和代理快照不变,仅追加种子审计。 - 前端待审核区域显示申请方、模拟渠道、金额、日期和待审核(测试);卡片显示明确标记的测试金额/笔数。按所选月份和all/demo范围显示,regular排除;流水关键词和分页不影响申请,不生成退款或资金流水。 - PHP语法、首次写入/幂等复跑、范围/月界/时间/原数据校验、4组真实HTTP核对及98项财务回归通过;前端ESLint/Prettier/Stylelint/vue-tsc和Vite构建通过,dist更新。浏览器认证仍是既有限制。 ## 2026-09-17 财务结算页面接续完成 - `/water-finance` 已从通用表格改为参考图布局:四卡、资金趋势/月汇总、未开通提现区、流水搜索/8条分页/详情/全量对账导出,支持月份与测试范围切换、错误重试和窄屏单列。 - 完成 `src/api/water/finance.ts`、`finance/index.vue`、`ReceiptDialog.vue`、`finance.scss`;复用共用卡片/图表/下载工具。登记收款沿用 finance purchase 凭据幂等与权益事务,远程选择企业/套餐、预览金额桶数并核对后提交,失败保留表单。 - 后端昨日草稿已验证,补上月份格式严格检查,防空字节等非法日期;范围、时间、统计口径见 `server/docs/WATER_FINANCE.md`。实际路由文件是 `server/plugin/saiadmin/config/route.php`。无数据库迁移,无新增持久业务数据。 - 财务98项(含真实HTTP/完整XLSX/50,000条上限/收款幂等)、原供水69项通过,全部测试夹具回滚;PHP lint、前端vue-tsc/ESLint/Prettier/Stylelint和Vite3323模块构建通过,dist已更新。 - 本地 `http://127.0.0.1:4173/#/water-finance` 预览已提供本次产物:首页内容及财务资源SHA256与dist一致。 - 退款、佣金、奖励与提现的规则仍未确定,本次按昨天记录保持未开通,金额为null/“—”,不生成财务账本或启用审核。 - 浏览器工具复测仍报 `unsupported Codex auth method: apikey`,本次未完成点击、截图及窄屏视觉验收;这是唯一尚未执行的页面验收环节,不应误记为项目登录问题。 ## 2026-09-16 财务结算页面(历史交接,已于09-17接续) - 最新需求:按用户提供的“财务结算中心”参考图调整 `/water-finance`,延续薄控制器、业务服务和值对象/策略分层,数据经Eloquent模型处理。参考图保存在 `server/docs/references/water-finance-reference-20260916.png`:四张收款/退款/提现/佣金卡片、左侧资金趋势及月汇总、右侧提现审核、底部最近资金流水,顶部对账下载与批量审核。 - 已核对现状:供水财务仅有 `WaterBusiness::purchase()` 的线下收款登记与套餐权益闭环,`WaterOrder` 记录订单;旧 `DepositRefundLogic` 属于租赁押金,不是供水退款,不应混入。供水退款、推广奖励、佣金与提现尚无账本或规则。 - 已向用户提出可选澄清:先完成收款/趋势/对账,其他区域显示暂未开通,或补充佣金比例、退款规则及提现审核后的打款方式;截至暂停未收到该问题答复。当前实现方向为先接入已有收款,缺失金额返回null/显示“—”,禁用无业务支撑的提现审核,不伪造截图数值或财务流程。 - **已落盘但尚未验证的后端代码**:新增 `server/plugin/saiadmin/app/service/finance/FinanceFilters.php`、`service/WaterFinanceDashboard.php`;`model/water/WaterOrder.php` 添加 financeSource/financePeriod/financeDetails 查询scope;`controller/water/WaterController.php` 添加 financeDashboard/financeExport;`config/route.php` 添加 GET `/core/water/finance/dashboard` 和 POST `/core/water/finance/export`,均校验 `water:finance:read`。未新增数据库结构,未写财务数据。 - 后端草稿契约:month默认为上海时间本月(2000年至本月),source支持all/regular/demo,keyword仅筛流水,page/limit分页;今日统计固定当天,月统计和补零日趋势按所选月份/数据范围,全量统计不受关键词与分页影响。排除未来记录,日期截至当前时间。测试识别暂以凭据 `TEST-` 前缀或套餐名称含“测试”为准,regular仅表示排除已标记测试,不能声称已银行对账。未开通退款/佣金/提现返回null及capabilities=false。XLSX全量匹配导出上限50000条并防公式注入。 - **前端尚未落盘**:整批patch因对 `finance/index.vue` 同时Delete/Add而校验失败,实查 `src/api/water/finance.ts`、`views/water/finance/ReceiptDialog.vue`、`finance.scss` 均不存在,`finance/index.vue` 仍是原 ``。明天应使用Update修改现有文件,不要认为之前前端草稿已应用。 - 下一步:先审查并验证后端草稿(PHP语法、月份边界、今日/月汇总、测试范围、关键词、分页、权限、XLSX全量及特殊文本),再创建独立前端看板、月份切换、数据范围和流水搜索、详情/分页/导出、收款弹窗;复用 `WaterMetricCards.vue`、`WaterChart.vue`、`@/utils/waterDownload`,收款沿用POST finance action=purchase及原凭据幂等机制。保留错误/加载/空状态及窄屏布局。退款/净收率显示未开通/不可计算,不当成0。 - **验证状态**:本轮财务新增代码尚未运行PHP lint、业务测试、HTTP、前端检查或构建,不能标记完成。需完成相关测试及vue-tsc/ESLint/Stylelint/Vite后更新记录;不要混用此前代理模块通过的检查结果。用户要求暂停后只更新交接记录,不继续实现。 - 浏览器环境:最近复测仍为 `unsupported Codex auth method: apikey`;备用node_repl因 `codex-windows-sandbox-setup.exe` 启动路径解析失败,实际组件已确认存在,重置未恢复。用户知道需处理登录并重启,但未确认已完成;明天需要UI验收时先复测,不将工具认证故障当作项目登录故障。 ## 2026-09-16 代理业绩排行测试数据 - 用户要求增加排行测试,执行并复跑 `server/scripts/seed_water_agent_ranking_demo.php --apply --http`,固定批次 `TEST-WATER-AGENT-RANKING-20260916`;新增订单32–36,复用5家启用的 `【代理测试0916】` 客户。本月排行:李策划2000元、西瓜商贸1500元、测试商贸600元。 - 通过WaterBusiness购水流程增加4100元模拟订单和205桶权益;测试SKU15及5个套餐完成后停用,流水明确无实际收款。旧记录快照不变,没有库存入库或配送任务。PHP语法、事务内金额/权益/旧数据校验、HTTP排行及幂等复跑通过;下月不自动生成业绩。详见 `server/docs/WATER_AGENTS.md`。 ## 2026-09-16 代理商管理独立页面 - `saiadmin-artd/src/views/water/agents` 替代通用表格:统计卡、代理/手机/等级/授权筛选、本月业绩排行、管理抽屉、代理档案/地图辖区编辑、超级管理员账号开通。真实供水订单按客户当前代理归属统计,未配置佣金显示空值;不混用旧租赁分润。 - `WaterAgentDashboard` 查询服务、`WaterAgentManagement` 命令服务和 `agent/AgentFilters/AgentHierarchyPolicy/AgentAccessPolicy` 值对象/策略,均通过Eloquent模型处理数据。新增 dashboard/detail/open-account 接口;原 agents save 委托管理服务。 - 已执行并复跑 `server/scripts/migrate_water_agent_hierarchy.php --apply`,仅增可空parent_id及索引;NULL未设置、0一级,支持最多三级,拒绝循环及子树超深。旧等级不推断回填。 - 账号开通仅ID=1可执行,事务建立关联,不自动授予角色、部门或全量权限;无默认密码。已授权取真实有效供水权限,档案启用不等于授权。WaterController逐请求拒绝合作停用的关联代理账号;不修改旧系统角色及非供水权限。辖区/团队关系不是代理数据隔离;当前仍是平台入口。 - 代理63项(含HTTP)、企业68项和原业务69项检查通过,夹具全部回滚;前端vue-tsc/ESLint/Stylelint及Vite生产构建通过,dist已更新。浏览器认证工具错误仍阻止视觉验收。详见 `server/docs/WATER_AGENTS.md`。 ## 2026-09-16 代理客户测试档案 - 执行 `server/scripts/seed_water_agent_customers_demo.php --apply`,固定批次 `TEST-WATER-AGENT-CUSTOMERS-20260916`;新增企业79–84,名称前缀 `【代理测试0916】`。李策划(id=1)、西瓜商贸(id=6)、测试商贸(id=7)各关联2家;5家启用、远航服务停用。地址/电话明确为测试内容,无配送坐标。 - 仅新增客户档案与审计,不添加收款、套餐权益、库存、设备或配送任务;旧客户及非目标业务模型数据快照校验不变。列表、代理/状态筛选、详情关联验证通过,复跑 `replayed=true` 不重复插入也不覆盖后续用户修改。 ## 2026-09-16 企业客户管理独立页面 - `saiadmin-artd/src/views/water/enterprises` 替代该页通用 WaterWorkspace,按参考图提供四张统计卡、客户筛选/分页、权益与库存/设备/最近订单/补货状态、增长与转化图、详情、编辑及批量启停。编辑保留地图选点;代理归属复用既有 agent_id,无迁移。 - WaterController 为薄控制器,新增 dashboard/detail/export/batch-status 入口;`WaterEnterpriseDashboard` 应用服务配合 `enterprise/EnterpriseFilters` 值对象和 `EnterpriseReplenishmentPolicy` 策略,数据通过 Eloquent 模型访问。多规格/设备先聚合避免重复累计;批量启停事务化且追加审计,不变更权益或配送。 - 全量统计独立于列表筛选:活跃取真实用水或换桶记录;复购按本月购水企业中历史累计至少两单的比例;漏斗只取本月新增企业这一批次,暂无线索阶段,不伪造数据。业务口径及接口见 `server/docs/WATER_ENTERPRISES.md`。 - 企业68项(含本地HTTP)和原业务69项检查通过,测试事务回滚;前端vue-tsc/ESLint/Stylelint及Vite生产构建通过,dist已更新。浏览器工具当前阻塞为 `unsupported Codex auth method: apikey`,未做截图级验收。 ## 2026-09-11 代理商服务区域地图选择 - 代理商新增/编辑的 `region` 字段改为 `region-map` 类型,通过 `WaterRegionField` 打开高德地图,搜索或点选后反向识别省/市/区县,确认回填区域名称,保存代理商后写入。取消保留旧值,兼容原区域文本,无数据库迁移。 - 原配送选点提取为共用 `WaterLocationPicker`,支持point/region两种模式;`DispatchLocationPicker`保留原open/selected接口。`waterAmap.findMapRegion`共用现有高德加载器,处理直辖市空city/重复省市、识别失败和配额错误;过期请求不会覆盖新选择。 - 服务区域仍是行政区名称,不是多区域集合或地图多边形范围;地图确认不直接写库。区域解析脚本与既有道路规划回归、ESLint、vue-tsc、Vite构建验证;实际高德浏览器交互仍受既有工具启动故障限制。 ## 2026-09-11 导航偶发白色修复 - 根因:`.water-layout` 导航样式原位于部分业务页按需导入的 `views/water/modules/water.scss`;独立库存/设备页没有该依赖,冷启动直达会显示默认白色,先访问总览等页面后才恢复深色。 - 将导航和主区域背景规则原样拆至 `saiadmin-artd/src/views/index/water-layout.scss`,由公共布局 `views/index/index.vue` 直接导入。页面样式仍留在原water.scss;保留原 `/water-` 范围判断。 - Prettier、ESLint、vue-tsc与Vite构建3291模块通过,构建清单验证布局静态依赖包含导航CSS,不依赖业务页懒加载。浏览器启动工具故障仍是既有限制,本轮未声称视觉验收。 - 2026-09-11 工具排查更正:“Node运行时缺失”尚无证据。已确认 Codex cua_node 的 b58ca2eaa616c2da / e7fe122ad3cbcd58 两个版本 node.exe 均存在且可运行(v24.20.0);重置 CUA 会话后仍报 `failed to start Node runtime ... os error 3`,具体失效启动路径未定位。此故障不能归因于项目依赖或Node文件被删除。 ## 2026-09-11 配送异常及返仓闭环 - 用户要求兼顾可用性、数据维护和设计模式,新增 `WaterFulfillment` 应用服务、`fulfillment/DeliveryState` 状态策略、`DeliverySettlement` 纯账务计算及 Eloquent `WaterDeliveryEvent` 追加事件。原 complete 结算统一委托新服务,事件保存请求哈希、操作人和快照,禁止覆盖已登记结果。 - 后台支持实收/部分签收/拒收 → 退回核对(完好入仓、破损隔离)→ 关联重送;实收才扣权益,未送达余量退回后释放冻结。回收空桶先记车载,路线返仓时分规格核对实交及短少,结算后才释放配送员。库存页增加隔离满桶及累计空桶损失,任务详情增加履约记录和重送关系。 - 已执行并复跑 `server/scripts/migrate_water_fulfillment.php --apply`,仅增量字段/表及唯一重送索引;旧在途任务10/11补充 LEGACY-FULFILL 路线关联及迁移事件,原状态、库存/权益/收款不变。新返回 returning/returned 已纳入跨日待办和总览,已结算时间及全范围返仓待办有独立入口。 - 新增 dispatch action receive / return_receive / redeliver / route_settle;仓库退回及路线结算额外要求 water:inventory:write。GET dispatch/history提供履约时间线。测试实际数据仍17单103桶,未推进保留演示任务。 - 后端59项新增闭环、原69业务/42调度/30流水/30设备/24策略模型共254项通过;另19项本地HTTP完整路径核对通过,临时夹具清理,正常操作审计保留。前端验证及工具限制详见 `server/docs/WATER_FULFILLMENT.md`;浏览器工具Node运行时缺失,本轮未做视觉验收。 - 当前闭环是后台人员登记;移动端接单/客户签字照片、GPS、自动设备核验、支付退款仍未实现。破损转可用/报废和已签收冲正需后续可审计补偿流程。新状态投入使用后不能直接回退旧代码或删除字段。 - 最终前端vue-tsc、ESLint/Prettier及Vite构建通过(3268 modules),已有道路规划脚本检查通过;4173预览读取最新构建,8787已实测新API。最终数据仍4片区17单103桶,9条正式未结算路线(旧任务10/11的归档路线35/36);未操作保留演示任务的签收或结算。 ## 2026-09-11 罗湖、福田、宝安配送测试 - 用户要求增加三个片区数据,已执行 `server/scripts/seed_water_district_dispatch_demo.php --apply`,固定批次 `TEST-WATER-DISTRICTS-20260911`;幂等复跑 `replayed=true`,不覆盖用户后续操作。 - 新增6个【测试0911】企业及任务77–82:罗湖2单10桶(路线17)、福田2单12桶(路线18)、宝安2单14桶(路线19),每区一位已排班测试配送员、各一条两站已派单路线。共同起点为南油第四工业区4栋;送达点为各区内人工模拟坐标,地址明确虚构企业、模拟坐标、请勿实际配送,未声称核实门牌。 - 新增36桶模拟入库和权益,无收款;原测试SKU可用库存和在途不变、锁定增加36。事务内校验原任务/账户/收款订单不变,新路线每条2站、片区/日期/冻结权益一致。 - PHP lint、实际写入校验、三区看板筛选及幂等复跑通过。当前看板4个片区、17单103桶(南山11单67桶,罗湖2单10桶,福田2单12桶,宝安2单14桶),状态待派单3、已派单11、配送中3;本轮未做浏览器或高德道路返回验收。 ## 2026-09-11 今日配送测试数据 - 用户要求新增今日任务供测试,已执行 `server/scripts/seed_water_today_dispatch_demo.php --apply`,固定批次 `TEST-WATER-TODAY-20260911`;首次仅允许当天执行,复跑返回 `replayed=true`,不覆盖后续操作。 - 新增6个【测试0911】企业及今日任务71–76,共33桶:晨光办公4桶、青禾研发6桶待派单;星海科技5桶、云帆商务7桶、绿洲创意3桶组成路线15(三站已派单);远景服务8桶为路线16(配送中)。新增3位已排班测试配送员,其中“合单体验配送员”空闲可用于待派单合单测试。 - 沿用南油第四工业区4栋起点及此前高德确认的中洲控股中心、科技园、生态园坐标;园区位置明确标为园区级,企业为虚构测试名称、地址含请勿实际配送。三站设置不同送达时限,方便测试路线优化。 - 仅增加33桶模拟仓库入库及33桶模拟权益,无新增收款;原测试SKU可用库存不变,锁定增加25、在途增加8。事务内确认原任务、原账户和收款订单全部不变,新增状态、日期、权益冻结一致。 - PHP语法检查、实际写入校验及幂等复跑通过。今日看板服务返回11单67桶(待派单3、已派单5、配送中3),其中包含此前5条未完成任务;本轮未重新进行浏览器验收。 ## Identity - Name: SaiAdmin 6.x / 当前前端品牌名为 `云泉智配` - Purpose: 基于 Webman 的后台管理系统,包含权限管理、系统配置、代码生成、日志/缓存/服务器管理,以及当前定制的商户、设备、订单、资金、运营和小程序业务。 - Root: `D:\phpstudy_pro\WWW\saiadmin6.x` - Layout: 前后端同仓库,前端在 `saiadmin-artd`,PHP 后端在 `server`。 - Status: 开发中;工作树存在大量用户已有修改和未跟踪业务文件,处理任务时不要 reset、checkout 或覆盖这些改动。 ## 当前供水业务合并(2026-09-09) - 用户以 `D:\水后台UI\desktop\P01.png`–`P09.png` 为新后台参考,授权合并。新主线为企业购水套餐、权益、实物库存、补货和配送。下文旧项目映射仅作为旧模块参考,冲突时以新供水规则为准。 - 新后端:`server/plugin/saiadmin/app/service/WaterBusiness.php`、`app/controller/water/WaterController.php`(均在 saiadmin 插件内),接口 `/core/water/*`。前端:`saiadmin-artd/src/views/water`。入口 `/#/water-overview`。 - 迁移:`php scripts/migrate_water_business.php --apply`,已执行并复跑,新增 9 张 `sa_water_*` 表及 12 个菜单,未转换或覆盖旧业务数据。 - 新菜单存在时旧前端一级菜单隐藏,但旧路由继续注册。新供水权限显式授权,不自动继承给旧代理。代理、角色与审计新入口先复用旧页面。 - 已落地基础闭环:登记已核实线下套餐收款 → 发放权益 → 建单冻结权益/库存 → 派单出库 → 签收扣权益/增加现场满桶 → 回收空桶;未出库可取消释放。权益与实物分别记录,收款凭据和有效配送任务防重复。 - 当前一企业一个供水地址;没有实际遥测数据时不显示预测或虚构在线状态。尚未完成地图合单、设备饮水协议、线上支付退款、代理自动佣金/提现及移动端内容投放。完整边界见 `server/docs/WATER_MERGE.md`。 - 验证:`php tests/water_business_test.php` 36 项检查通过,测试数据事务回滚;相关 PHP 语法、前端 vue-tsc、ESLint、Vite production build 通过(3245 modules)。浏览器已确认新总览、企业、库存、内容手机预览及企业必填校验,旧审计页面可从新菜单访问。 ## Stack and commands ### 废弃表清理(2026-09-10) - 用户确认后仅删除water库中的 `sa_device_operation_profile`(0行)、`sa_article`(8行)、`sa_article_banner`(3行)、`sa_article_category`(4行);63表减为59表,非目标表行数全部不变,供水测试数据保留。 - 完整备份63表且4张表经过临时表恢复/逐行比对;备份目录见 `server/docs/unused-tables-backup-location.txt`,完整结果及恢复命令见 `server/docs/DATABASE_CLEANUP_REVIEW.md`。备份文件未放入网站公开目录或仓库。 - `server/scripts/cleanup_unused_water_tables.php` 固定4表白名单,清理前检查数据库名、备份SHA256、数据变化和代码/数据库对象依赖;默认检查,`--apply`删除,`--restore --apply`只恢复缺失表且拒绝覆盖。 - 69项供水、30项设备接入、56项旧设备运营检查全部通过并回滚。15条预警、10+5分页、2595元测试收款核对通过。 ### 补货搜索核对(2026-09-10) - 按用户要求再添加10条不同名称测试预警:星河科技、青禾设计、远航物流、锦程制造、晨曦教育、百川商贸、蓝湾酒店、瑞丰农业、华庭物业、博雅文化,名称均以“(测试)”结尾;分布东/西/南/北城区,使用山泉水/纯净水两种测试规格。脚本 `server/scripts/seed_water_alert_search_demo.php --apply` 使用固定批次标记防重复,现场库存经盘点入口建立账户,未增加收款或权益。 - 当前预警15条,补货页默认每页10条(其他共享页面仍20条),第一页10条、第二页5条;已验证搜索星河1条、东城区3条、山泉水5条,重跑不重复增加数据,今日收款保持2595元。新增账户权益为0,仅用于预警、搜索和分页;建单仍受权益/仓库库存校验。 - 本次页大小调整通过vue-tsc、ESLint、Prettier及Vite构建(3247 modules);浏览器验证第一页10条、下一页5条,从第二页搜索“星河”返回第一页1条,重置恢复15条,已停留在第一页供用户测试。 - 搜索已接入 `GET /core/water/alerts?keyword=...`,后端模糊匹配企业名称、区域和商品规格。新增本轮10条数据前,原5条演示企业名均含0910、规格含18.9,搜索“1”会全部命中,并非查询未执行;浏览器搜索“云泉科技”返回1条。 - `WaterWorkspace.vue` 已修正补货页搜索提示,明确搜索范围及点击查询/回车触发;增加重置(清空关键词、恢复仅预警账户及第1页)、120字限制和无匹配说明。前端搜索去除首尾空格,保持后端原有筛选口径。 - 验证:vue-tsc、相关 ESLint、Prettier、Vite构建通过(3247 modules)。4173新版浏览器中,带空格的“云泉科技”查询返回1条且去掉空格;回车查询“不存在的企业”返回0条并显示无匹配提示;重置恢复5条。 ### 设备协议接入预留(2026-09-10) - 按用户要求先预留,等有设备后再提供协议。新增 `POST /iot/water/report`,以及原 `mqtt.message` 的供水适配事件;`config/water_device.php` 两个开关默认关闭,`adapter=null`,未启用外部连接或修改现有 MQTT Topic。 - `app/service/water/DeviceProtocolAdapter.php` 保留 HTTP/MQTT 协议转换接口;`DeviceIngress.php` 检查开关、HTTP 独立 Bearer 凭据及报文大小。具体厂商适配器尚未实现,因此不能宣称已完成真实设备接入。 - `DeviceReport.php` 仅更新已有供水设备的 `last_seen` 和当日累计用量快照;最近5分钟的报告时间作为心跳,重复/较旧心跳不刷新。用量同日取累计最大值、跨日重置,库存权益及账户日均不变。不支持直接接收增量用量。 - `php tests/water_device_ingress_test.php` 30项通过,PHP lint通过,所有设备测试回滚;接口格式、未来厂商鉴权/重放/Topic绑定要求见 `server/docs/WATER_DEVICE_INTEGRATION.md`。 - 原有69项供水业务检查回归通过;路由清单已确认,HTTP子进程已重新加载,实际POST设备入口返回503(保持关闭)。MQTT进程未启用,未来联调时需重启加载新事件配置。 ### 总览收款测试数据(2026-09-10) - 后续按用户要求新增配送/预警演示:`php scripts/seed_water_dispatch_demo.php --apply`,固定批次 `TEST-WATER-DISPATCH-20260910`。任务 ID 9/10/11 分别待派单8桶、已派单6桶、配送中10桶,复用云泉科技/智配办公/清泉产业园;均为模拟任务。 - 测试仓库入库100桶后为可用76、锁定14、在途10;新增“【测试0910】待补货体验中心”(满桶1/阈值2,紧急)、“【测试0910】待补货商务楼”(满桶3/阈值5,普通),各有10桶可用权益且尚未建单。总预警5条,其中3条已关联有效配送;无模拟遥测。 - 两个新测试企业各登记299元模拟套餐,因此当日总览测试收款合计从1997增至2595元。脚本整批事务,以 `demo_seed` 流水作为已完成标记,复跑不会重置用户后续操作或重复入库/建单/收款;已复跑核对上述数量和金额不变。 - 用户要求保留少量测试数据,已执行 `server/scripts/seed_water_receipts_demo.php --apply`。新增 3 个 `【测试0910】` 企业、1 个 18.9L 测试规格、3 个套餐和 3 笔模拟收款;订单 ID 3/4/5,金额 699/299/999 元,共 1997 元、90 桶权益。 - 固定收款凭据 `TEST-WATER-RECEIPTS-20260910-A/B/C`,流水备注明确“未发生真实收款”,地址标明“请勿配送”。未生成配送单、实物库存或设备遥测。 - 当日总览收款从 0 增至 1997 元;重复执行返回原订单,不重复入账、不修改登记日期。此批为保留的测试业务数据,不能作为实际经营收款依据。 ### 补货待办接续(2026-09-10) - 已补齐账户数量阈值编辑和补货批量建单。入口 `/#/water-alerts`,“全部启用账户 / 策略维护”可查看未预警账户;停用企业/规格不参与补货列表。 - `POST /core/water/alerts` 的 `action=policy` 仅更新 `threshold_qty`,权限 `water:alerts:write`,记录修改前后阈值及操作人,不修改日均、库存或权益。天数规则仍为既有 5 天预警、3 天紧急。 - `POST /core/water/dispatch` 的 `action=batch_create` 接收 `items:[{account_id,quantity}]`(1–50 条),权限 `water:dispatch:write`。服务端根据账户解析企业和规格,复用单笔冻结规则,按规格/企业排序加锁;任一条失败整批回滚,已有有效任务禁止重复创建。 - 前端 `BatchDeliveryDialog.vue` 逐条输入桶数并确认企业、规格及地址;只勾选当前页有可用权益、未建单的预警账户,换页/查询清空勾选。失败保留填写内容供核对。 - 验证:69 项数据库回滚检查通过,包含阈值即时生效、非法值、合计库存不足、后续行失败回滚、停用企业、重复建单和权限拒绝。PHP lint、vue-tsc、相关 ESLint、Vite build 通过(3247 modules)。浏览器确认新按钮、预警桶数列和范围切换入口,当前零业务数据,未通过浏览器生成实际配送单。 - 已重启本项目 HTTP 子进程并确认 8787 返回 HTTP 200;前端 4173 预览加载本次构建。无需新增数据库迁移。 - 下一步建议:配送异常/拒收退回流程;地图合单、设备饮水协议、线上支付退款、佣金和真实移动端投放仍未实现,详见 `server/docs/WATER_MERGE.md`。 ### Frontend (`saiadmin-artd`) - Vue 3 + TypeScript + Vite 7 + Element Plus。 - Pinia、Vue Router 4、VueUse、Vue I18n、Axios。 - Tailwind CSS 4、Sass、ECharts、XLSX、WangEditor 等业务组件。 - Package manager: pnpm;版本要求见 `saiadmin-artd/package.json`:Node `>=20.19.0`、pnpm `>=8.8.0`。 - 当前环境 Node `v22.21.0` 可用,但 `pnpm` 未加入 PATH;项目 `node_modules` 已存在,可直接使用 `node_modules/.bin` 中的工具。 - Development: `pnpm dev`;端口由 `.env` 的 `VITE_PORT` 配置为 `3006`。 - Build: `pnpm build`(`vue-tsc --noEmit && vite build`)。 - Lint: `pnpm lint`;格式化/样式检查脚本见 `package.json`。 - Development API: `.env.development` 使用 `VITE_API_URL=/api`,Vite 代理到 `http://127.0.0.1:8787`。 - Production API: 当前本地部署使用 `VITE_API_URL=http://127.0.0.1:8787` 直连 Webman;如果部署到正式域名,应由反向代理把 API 前缀转发到 Webman,而不能继续指向 `D:\phpstudy_pro\WWW\api` 等其他项目。 ### Backend (`server`) - PHP `>=8.1`,当前 CLI PHP `8.2.29`;Composer 当前 `2.10.2`。 - Webman/Workerman,`workerman/webman-framework ^2.1`;Think ORM、SaiAdmin 6.x、SaiPackage 6.x。 - 数据库默认 MySQL,配置入口为 `server/config/database.php`,值来自 `server/.env`。 - 缓存支持 file/Redis;当前 `server/.env` 为 `CACHE_MODE=file`。 - JWT、Session、文件存储、Webman 插件机制;当前增加了 `workerman/mqtt ^2.2`。 - Development server: `server/windows.php`(Windows 入口);Webman HTTP 进程配置在 `server/config/process.php`,监听 `http://0.0.0.0:8787`。 - Unix-style entry: `server/start.php`。 - PHP syntax check: `php -l `;本次已核对启动、进程、Bootstrap、MQTT subscriber、登录中间件等关键文件,均无语法错误。 ## Structure and entry points ### Frontend - `saiadmin-artd/src/main.ts`: 创建 Vue 应用,初始化 Pinia、Router、全局指令、错误处理和国际化。 - `saiadmin-artd/src/App.vue`: Element Plus 配置、主题初始化、存储兼容性检查和系统升级检查。 - `saiadmin-artd/src/router/index.ts`: Hash 路由;静态路由在 `src/router/routes/staticRoutes.ts`,登录后动态路由来自 `src/router/routes/asyncRoutes.ts`。 - `saiadmin-artd/src/router/guards/beforeEach.ts`: 登录状态、用户信息、字典、后端菜单和动态路由初始化。 - `saiadmin-artd/src/router/core/MenuProcessor.ts`: 前端/后端菜单模式选择、角色过滤、空菜单过滤和路径规范化;后端模式通过 `/core/system/menu` 获取菜单。 - `saiadmin-artd/src/utils/http/index.ts`: Axios 统一封装;从 Pinia 读取 access token,添加 Bearer Authorization,处理统一响应和 401。 - `saiadmin-artd/src/api`: 按功能组织 API;目前约 31 个 TypeScript API 文件。 - `saiadmin-artd/src/views`: 约 132 个视图文件,包含系统、安全维护、工具和当前定制业务模块。 - `saiadmin-artd/SAIADMIN.md`: 前端功能目录约定,新增业务通常按模块/业务/表页面组织。 ### Backend - `server/start.php` / `server/windows.php`: Webman 启动入口。 - `server/config/process.php`: HTTP、MQTT subscriber、文件监控进程配置。 - `server/app`: 项目层控制器、模型、服务和进程;当前包含 `app/process/MqttSubscriber.php`、`app/service/MqttPublisher.php`、`app/service/MqttTopic.php`。 - `server/plugin/saiadmin`: SaiAdmin 核心插件;按 controller、logic、model、validate、middleware、utils、config 分层。 - `server/plugin/saiadmin/config/route.php`: `/core` 和 `/tool` 路由,`fastRoute()` 为控制器常用 CRUD 路由生成器。 - `server/plugin/saiadmin/app/middleware`: 登录检查、权限检查、跨域和系统日志中间件。 - `server/plugin/saipackage`: 在线插件/安装相关功能。 - `server/config/plugin`: tinywan JWT/storage、Webman channel/console/event 等插件配置。 ## Architecture and integrations - 浏览器先加载前端静态应用;前端通过 Axios 请求 `/api`,开发时由 Vite 代理到 Webman `8787`。 - 生产构建当前直连 `http://127.0.0.1:8787`;后端 SaiAdmin 插件已启用 `CrossDomain` 中间件以支持前端站点跨端口请求。 - 登录页调用 `GET /core/captcha`,登录调用 `POST /core/login`;成功后保存 access/refresh token,再获取用户信息、字典和菜单。 - 前端动态菜单支持 backend 模式和 frontend 模式。当前 `.env` 的 `VITE_ACCESS_MODE=backend`,因此以服务端菜单为主;`MenuProcessor` 会补齐缺失的内置 `MiniProgram` 和 `Merchant` 一级菜单。 - 后端 SaiAdmin 插件通过控制器属性/反射缓存实现免登录接口和权限标记;登录与验证码接口在 `LoginController::$noNeedLogin` 中放行。 - MQTT 由 `app\process\MqttSubscriber` 作为独立 Webman 进程运行,业务发布入口位于 `MqttPublisher` 和 `/tool/mqtt/publish`。 - 前端路由模块目前包括 dashboard、system、miniProgram、agent、merchant、device、operations、result、exception;定制业务视图包括 agent、device、fund-management、management、merchant、mini-program、operation-management、order、user-management。 ### 旧业务项目参照映射(后续开发必须复用) - 业务规则权威参考项目为 `D:\phpstudy_pro\WWW\api`(ThinkPHP 5)。后续实现设备、商户、代理、订单、资金等旧业务页面时,应先追踪该项目的 controller → model/service → 写入/查询表链路,再将业务语义翻译到 SaiAdmin;不直接让新前端调用旧项目接口。 - 铺货记录旧入口为 `api/application/agentapi/controller/Merchant.php::bind_device_records()`,分页查询位于 `api/application/common/model/MarketZuoRelyRecord.php::queryPageList()`。旧逻辑关联 `market_zuo_rely_record`、`market_goods`、`store` 和 `admin_user_tier`,按记录时间倒序并限制当前代理本人/下级可见范围。 - 铺货流水字段映射:旧 `market_zuo_rely_record` → 新 `sa_merchant_device_binding_record`;`market_good_id` → `device_id`;`goods_sn` → `device_sn`;`rely_store_id` → `merchant_id`;`distribute_type` → `action_type`(1 铺货、2 解绑);`add_date` → `action_time`;`depend_type` 保留为设备类型码。 - 关联表映射:旧 `market_goods` → `sa_device`,旧 `device_id`/二维码搜索 → 新 `device_code`,设备 SN 使用 `goods_sn`;旧 `store.store_name` → `sa_merchant.name`;操作人使用 `sa_system_user` 的 `realname`/`username` 和 `phone`。 - 铺货记录筛选映射:旧 `distribute_type`、`search_store_name`、`search_device_id`、`search_goods_sn`、`search_depend_type` → 新 `action_type`、`merchant`、`qr_code`、`device_sn`、`goods_type`。旧分页 `start` 是从 0 开始的页号,新接口统一使用 `page`(从 1 开始)和 `limit`。 - 当前项目尚未完整迁移旧 `admin_user_tier` 代理层级树,因此设备类数据权限暂统一沿用 `sa_device.manage_agent_id`:管理员 ID 1 查看全部,其他账号只查看其当前管理设备及对应流水。未来迁移代理层级后,再按旧项目“本人 + 合法下级”规则扩展。 - 设备分配旧链路位于 `api/application/agentapi/controller/Sttuf.php`:`distribute_agent_devices()` 查询当前代理管理库存,`save_distribute_agent_devices()` 执行下发,`my_agent_stock_devices()` 查询下级未铺货库存,`save_recover_agent_devices()` 执行回收,`agent_distribute_log()` 查询双方分配/回收流水;旧路由分别为 `/agentapi/distribute_agent_devices`、`/agentapi/save_distribute_agent_devices`、`/agentapi/my_agent_stock_devices`、`/agentapi/save_recover_agent_devices`、`/agentapi/agent_distribute_log`。 - 设备管理权映射:旧 `market_goods.manage_aid` → 新 `sa_device.manage_agent_id`,旧 `market_goods.belong_aid` → 新 `sa_device.belong_agent_id`(分配只转移管理权,不改变所有权)。旧 `device_manager_record` → 新 `sa_device_manager_record`;`goods_id/goods_sn/goods_type` → `device_id/device_sn/goods_type`,`last_aid/now_aid` → `last_agent_id/now_agent_id`。新 `action_type=1` 表示分配、`action_type=2` 表示回收,对应旧 `flow_type=0/1`。 - 分配安全规则沿用旧语义并收紧为库存操作:目标必须是当前账号有权访问且启用的下级代理;设备必须存在、当前由操作者管理、且尚未铺货到商户;单次最多 100 台。成功后在同一事务内更新 `sa_device.manage_agent_id` 并写 `sa_device_manager_record`,不存在、非当前管理、已在目标代理、已铺货分别返回独立结果集合。回收执行相反的管理权转移,同样禁止回收已铺货设备。 - 代理修改旧链路位于 `api/application/agentapi/controller/Sttuf.php::edit_agent()` / `save_edit_agent()`。新系统保留 `sa_system_user.realname/phone` 作为账号基础资料,并使用一对一 `sa_agent_profile` 保存运营模式、产品、分润、业务权限、运营区域和公司地址;开通类型继续由部门层级推导且编辑时只读,登录账号和密码均不在此页面修改。 ## Conventions and constraints - 2026-09-08 用户确认的维护要求:可配置的业务类型、产品编码及其映射优先由数据库统一维护,前后端读取同一来源,不在多个类或页面重复写死映射数组。固定协议值与业务配置须区分,不能把可变业务目录当作代码常量。 - 业务代码按数据查询、规则计算和页面展示拆分职责,使用明确的方法名;注释说明业务来源、公式和边界条件,避免大方法中混杂查询、计算及兼容判断。实现旧业务前复用已验证的项目记录,不另创手工流程代替缺失的数据关联。 - 新增旧业务功能前,先查询上述 `D:\phpstudy_pro\WWW\api` 对应控制器、模型/服务及数据写入路径;`PROJECT_CONTEXT.md` 已记录过的映射直接复用,只分析尚未覆盖的业务差异。 - 前端优先遵循 `SAIADMIN.md` 的模块目录结构,并使用已有 API、类型、组件和 store 模式。 - 路由组件路径使用项目现有的 `AppRouteRecord` 结构及 `@views`/`@utils`/`@stores`/`@styles` 等别名。 - 后端新增 SaiAdmin 业务通常配套 controller、logic、model、validate 和 `config/route.php` 路由。 - 修改前先检查当前工作树;截至本次分析,前后端均有未提交变更,不能把这些变更当作基线或擅自清理。 - 不在项目记忆中保存 `.env` 的密码、Token、JWT 密钥或其他凭据,只记录变量名、用途和非敏感默认值。 ## 设备运营实现与旧业务映射(2026-09-08) - 用户明确要求:设备运营是截图中的只读统计列表,不另建手工运营参数流程;业务类型及产品映射从数据库读取,代码要按职责拆分、便于维护。之前的参数表方案已撤下,不能恢复“待配置+手填分成”作为业务迁移替代方案。 - 权威旧链路:api/application/agentapi/controller/Statistics.php::business_device_data() → common/model/MarketGoods.php::getMarketGoodsDetailList() / common/service/Statistics.php::getDeviceStatData()。原表头“归属代理”实际显示设备当前管理代理,继续按管理权筛选。 - 新实现分工:DeviceOperationLogic 负责范围校验、批量订单聚合、排序分页;DeviceProductCatalog 读取数据库产品/类型字典;DeviceOperationShares 计算既有代理、商户分成;DeviceOperationMetrics 计算客单价、盈利、增长率、回本和评级。不得在这些类及页面另写设备编号→产品编码的固定数组。 - 数据字典:系统管理/数据字典中 device_product(设备产品:标签为名称,键值为 sa_agent_profile.profit_rates 的产品编码);device_type_product(设备类型产品映射:标签为产品编码,键值为 sa_device.goods_type 编号)。支持多个设备类型归同一产品,按状态和软删除过滤;重复编号或重复产品编码报错,禁止静默覆盖。接口每次请求读取字典,修改立即生效,前端类别选项和默认选中项由接口返回。 - 成本直接读取 sa_device.cost_price(新增可空字段,来源旧 market_goods.cost_price)。比例读取 sa_agent_profile.profit_rates 和 sa_merchant.profit_method/profit_rate;代理为占总交易额比例,商户为相对直属代理比例。例如代理80%、商户相对25%,商户有效20%、直属管理人净60%。多级管理人扣除当前商户分支第一个下级的比例;部门同层多人等无法唯一确定关系时保持未知,不猜分成代理。运营统计不修改支付分账。 - sa_device_operation_profile 不再参与任何查询,参数写入接口和页面入口已移除;该空表已于2026-09-10经用户确认、备份后删除,core:operation:device:update 权限已停用并软删除。仅保留 GET /core/operation-management/device-operations/index(core:operation:device:index),响应附带 categories/category;类别使用数据库产品编码,不再使用旧页面别名 k-battery/bluetooth 等。 - 本人与下级范围互斥,本人只查当前管理人,下级沿用部门后代(管理员1查询其他账号);设备与订单同时限制管理范围。净交易额为实付减退款且不小于零,扣款失败无收入;总订单数包含未支付订单,客单价除以有效已付订单数。今日/昨日优先 return_at,缺失依次 update_time/rent_at/create_time;每日收入累加。 - 盈利按商户有效分成;旧“盈利率”是净交易额÷设备成本;今日表现按昨日交易额÷投放期间日均交易额及旧阈值评级,无交易默认为“最差”。回本按当前铺货时间后的管理人有效收入累计,取首次达到成本的日期;昨日金额0时增长率为 NULL;未知成本/分成的相关指标为 NULL,前端显示“--”,但交易额0时收益可确定为0。 - 前端:saiadmin-artd/src/views/operation-management/device-operations/index.vue,API:src/api/operationManagement/deviceOperation.ts。保留截图20列、条纹行、设备码换行、居中分页,默认10条;金额、订单数、客单价和盈利率在数据库聚合后排序分页;过滤快速切换时忽略过期响应。没有操作列、运营参数表单或手工比例保存。 - 新迁移:server/plugin/saiadmin/db/20260908_use_device_operation_sources.sql、20260908_add_device_product_dictionary.sql。运行 server 目录下 php scripts/migrate_device_operation_sources.php --apply --legacy-schema=api;不带 --apply 为检查模式,--legacy-schema 可省略。成本只回填唯一 SN、设备类型及已有 legacy_id 一致且新库成本为空的设备,不覆盖现有成本;运行时报表不访问旧库。 - 本地数据现状:新库为15条测试设备,与 api.xs_market_goods 匹配SN为0,故没有迁入旧设备成本;现有代理档案也不完整。真实台账及档案未迁入时不能宣称经营指标数据齐全,更不能复制截图数值充当真实数据。 - 验证:上述迁移重复执行成功;php server/tests/device_operation_test.php 通过56项检查(所有数据库测试事务回滚),包括退款、每日累计、回本、全局排序分页、管理范围、档案修改即时计算、多级分成、新增数据库类型驱动类别与分成、禁用/重复字典映射。PHP lint、vue-tsc、ESLint、Prettier、Stylelint、Vite构建通过;浏览器验证真实列表、10条分页、数据库类别切换,无JavaScript错误。Vite保留既有系统菜单动态/静态导入警告。 - 最终页面验收(2026-09-08):在 1920×900 桌面视口下,统计表与表头宽度均为 1649px,截图对应的20列完整显示,分页居中;无运营参数按钮或操作列。验收结束后已恢复浏览器原始视口。当前代码修正和本地构建已完成;真实设备成本及缺失代理档案的迁入仍按上述数据现状跟进。 ## Known issues and follow-ups - **Next session (2026-09-05):** 退押管理的真实分页与 XLSX 导出已经完成,明天从“正式退押任务写入链路”继续:先追踪新版订单完结及微信/支付宝退款回调,在生成退押任务时写入 `sa_deposit_refund_record`,退款结果回调时更新 `status`、`refund_time` 和异常备注。当前新系统尚无完整支付退款执行链路,不得用 `sa_rental_order.refund_amount` 猜测押金、计划退款时间或退款路径。可复用本地 `20260904_seed_deposit_refund_test_data.sql` 的 3 条 `TEST-ORDER-*` 数据做页面与导出回归。 1. **登录验证码图片 MIME 和生产 API 缓存/路由问题已修复。** - 前端登录页在 `saiadmin-artd/src/views/auth/login/index.vue` 使用 `img :src="captcha"`,接口为 `GET /core/captcha`。 - 本次直接请求本地后端得到 HTTP 200、业务 `code=200`,返回了 `uuid` 和图片数据;图片数据前缀为 `data:image/png;base64,/9j/...`。 - `/9j/` 表示实际内容是 JPEG,而 `server/plugin/saiadmin/utils/Captcha.php` 使用的 `Webman\Captcha\CaptchaBuilder::get()` 在当前 vendor 实现中通过 `imagejpeg()` 输出。已将返回值改为 `data:image/jpeg;base64,`,并重启 Webman 进程使修改生效。 - 前端 `refreshCaptcha()` 已增加异常捕获,接口失败时会在控制台记录 `[Login] Failed to load captcha`。 - 当前 `server/.env` 为 `CAPTCHA_MODE=cache`、`CACHE_MODE=file`,本次接口请求成功,缓存配置不是当前“接口完全不可用”的首要证据。修复前应保留现有用户改动并先确认浏览器 Network/Console。 - 生产环境曾请求 `/prod/core/captcha` 并收到 `304 Not Modified`;实测该路径没有进入本项目 Webman:端口 `8787` 下 `/prod/core/captcha` 为 404,端口 80 下则被转到另一个 `D:\phpstudy_pro\WWW\api` 项目并出现 PHP 语法错误。已改为生产构建直连 `http://127.0.0.1:8787`,并给验证码 GET 增加时间戳参数防止缓存复用。 2. **登录跨域预检问题已修复。** - 直连 `127.0.0.1:8787` 后,JSON 登录会先发送 `OPTIONS /core/login`;原先返回 404,浏览器因此阻止后续 POST。 - 已增加全局 `CrossDomain` 中间件、通用 OPTIONS 路由,并保留 SaiAdmin 业务路由上的跨域中间件,确保预检及登录成功/失败响应都带跨域头。 - 复测结果:OPTIONS HTTP 200;POST `/core/login` 在无效验证码下正常返回业务错误且包含 `Access-Control-Allow-Origin`,说明登录请求链路已打通。 3. **工作树不是干净基线。** - 已修改文件包括前端锁文件、品牌配置、中文文案、菜单处理/路由,以及后端 Composer、缓存、ORM、进程和环境示例配置。 - 未跟踪内容包括前端商户/小程序 API 与页面、代理/设备/订单/资金等业务页面,以及后端 MQTT、服务层、插件和 Composer lock。处理后续任务时应区分用户已有改动和新改动。 4. **需要审查配置中的敏感材料。** - `server/config/plugin/tinywan/jwt/app.php` 存在看起来像密钥配置的内容;记忆不保存具体值。若这些值用于真实环境,应移出代码库、改用环境变量并轮换。 5. **编码显示异常。** - 多个源码注释和 README 在当前 PowerShell 输出中显示为乱码,可能是终端编码问题,也可能是部分文件编码不一致;修改中文内容前应确认文件实际编码,避免无意重写整文件。 6. **商户统计页面直链问题已修复。** - `merchant.ts` 路由模块已注册商户列表、添加商户和数据统计页面。 - 后端菜单模式缺少商户路由时,`MenuProcessor` 会补充并合并内置商户路由;已验证 `/merchant/statistics?id=73` 正常渲染。 7. **商户列表的营业时间已接入并完成迁移,免费店员仍待业务定义。** - 设备绑定、提现更新、删除商户、密码重置、商户统计和营业时间已有前后端接口。 - 营业时间使用旧系统兼容字段 `business_time_des`,迁移脚本为 `server/plugin/saiadmin/db/20260826_add_merchant_business_time.sql`;2026-08-27 已执行并确认字段存在。 - “免费店员”在当前新系统没有对应数据表、用户关联或明确业务规则,暂未猜测实现,前端仍保留提示入口。 8. **Windows 后端启动脚本不解析 `status`、`restart` 等参数。** - `server/windows.php` 每次执行都会再启动一整套 Workerman 进程;不要用 `php windows.php restart -d`,否则多个进程会通过端口复用同时监听 8787,新增路由可能随机命中旧进程并返回 404。 - 2026-08-26 已清理本项目重复进程树并只保留一套 8787 服务;后续重载应先精确停止属于 `saiadmin6.x/server/windows.php` 的进程树,再启动一次。 9. **车载设备供电/断电依赖独立硬件指令服务。** - 新接口沿用旧系统 `POST /cdy/tasks` 协议,发送 `uid`、`task_type=switch` 与 `value=1/0`;实际网关地址通过 `DEVICE_COMMAND_ENDPOINT` 配置。 - 配置为空时接口会拒绝执行;不要用本地字段更新或模拟成功替代真实网关响应。二维码扫描内容可选配 `DEVICE_QR_BASE_URL`,为空时仅编码设备机器码。 ## Decisions and change notes - 2026-08-25: 建立本项目独立记忆文件;不回退当前工作树。 - 2026-08-25: 记录当前定制方向为 WaterAdmin + 商户/设备/订单/资金/运营/小程序业务,并包含 MQTT 集成。 - 2026-08-25: 修复登录验证码 JPEG/PNG MIME 不一致问题,重启后端并复测 `/core/captcha` 返回 `data:image/jpeg`,前端重新构建。 - 2026-08-25: 修复生产 `/prod` API 指向错误项目和验证码缓存问题;生产构建改用 Webman `8787`,启用跨域中间件并为验证码请求增加防缓存参数。 - 2026-08-25: 修复直连后端导致的登录 OPTIONS 预检 404;增加全局跨域支持和通用 OPTIONS 路由,复测登录 POST 已能正常到达后端。 - 2026-08-26: 按旧系统语义新增商户营业时间读写接口、数据库迁移和前端编辑弹窗;免费店员功能因缺少业务规则暂缓。 - 2026-08-26: 核对商户数据统计页面已具备日期查询、汇总、趋势图、支付方式、每日明细和 Excel 导出全链路;执行现有统计演示数据脚本,写入 8 个演示商户共 480 条最近 60 天统计记录,并通过 8787 HTTP 接口验证查询和导出。 - 2026-08-26: 完成添加商户前后端链路;前端表单通过 `/core/merchant/save` 提交,后端执行字段校验、直属代理/分店归属校验、手机号唯一校验和密码哈希,详情接口不返回密码;添加页补充可编辑的 0-100 分润比例字段。 - 2026-08-26: 完成“我的设备”真实列表、后端分页搜索/分类/状态汇总和 Excel 导出;数据按 `sa_device.manage_agent_id` 隔离,超级管理员 ID 1 可查看全部,缺少真实业务字段时使用 `--` 或 `0`,不生成虚假订单/金额数据。 - 2026-08-26: 用户指定后续旧业务逻辑统一参照 `D:\phpstudy_pro\WWW\api`。本次完成铺货记录 controller/model/表字段和权限范围映射并沉淀到项目记忆,后续不重复分析已确认部分。 - 2026-08-26: “去铺货”沿用旧系统“先选择商户、再绑定设备”的交互语义,并按用户截图直接复用商户经营数据宽表;提交端统一复用 `MerchantLogic::bindDevices()`,不新增第二套绑定事务。 - 2026-08-27: 完成代理设备管理权分配/回收链路;分配只转移 `sa_device.manage_agent_id`,不改变 `belong_agent_id`,已铺货设备继续禁止分配或回收,操作与流水写入保持同一事务。 - 2026-08-27: 执行商户营业时间幂等迁移,`sa_merchant.business_time_des` 已落库;“免费店员”仍因缺少数据模型和明确业务规则而保持待定义状态。 - 2026-08-27: 完成代理“修改信息”页及 `GET /core/agent/read`、`PUT /core/agent/update`;产品分润动态联动,修改范围受直属层级权限约束,手机号变更时检查冲突。列表“更多设置”中的合同管理已接入真实页面与读写接口。 - 2026-08-27: 代理列表“更多设置 → 删除代理”已改为二次确认后调用 `POST /core/agent/destroy`。按旧系统语义删除仅禁用账号而不物理删除历史;后端会校验操作者可见范围,并拒绝仍有关联设备、商户或下级代理的账号。权限迁移 `20260827_add_agent_destroy_permission.sql` 已执行并确认新增 `core:agent:destroy` POST 权限。 - 2026-08-27: 代理列表的“广告权限”占位入口已移除;“商户管理”改为跳转 `/merchant/subordinate-merchant?agent_id=<代理ID>`。下级商户页会同步路由中的代理 ID 到筛选条件,后端继续按当前账号可见范围校验该参数。 - 2026-08-27: 代理列表“更多设置 → 设备绑定”已接入精简编号输入弹窗,交互按参考图使用英文逗号分隔设备编号;提交复用既有 `POST /core/agent/device/assign`,因此沿用代理可见范围、当前管理权、未铺货及单次 100 台的后端限制。相关文件已通过 ESLint、`vue-tsc --noEmit` 和 Vite 生产构建(3183 modules;仅保留既有系统菜单动态/静态导入警告)。 ## Latest change notes - 2026-09-02: 完成“用户管理 → 用户投诉 / 用户反馈”真实业务链路。用户投诉复用 `sa_wechat_mini_program` 的微信支付配置,通过 API v3 提供配置选择、最近 31 天分页查询、详情、敏感手机号解密、协商记录、回复和完结;配置错误会明确返回,不伪造投诉数据。用户反馈新增 `sa_user_feedback`、`sa_user_feedback_reply`,支持组合筛选、状态数量、详情、回复流水、后台备注、状态处理和 XLSX 导出;普通管理员按本人及递归下级代理范围查询,超管查看全部。迁移 `server/plugin/saiadmin/db/20260902_add_user_complaint_feedback.sql` 已连续执行两次并清除菜单缓存,11 个接口权限已落库。 - 2026-09-02: 完成“用户管理 → 用户列表 → 余额充值”管理端代充链路。`sa_rental_user` 已有 `wallet_balance` 列无需加列;新建资金账本表 `sa_rental_user_wallet_log`(为 card/card_gift 预留 `balance_type`,本轮仅支持 wallet)。新增 `POST /core/user-management/users/recharge`,由 `RentalUserController::recharge` → `RentalUserLogic::recharge` 在事务里锁定用户行 → 增量更新 `wallet_balance` → 写流水;操作人 ID/姓名来自 `BaseController::$this->adminId/adminInfo`。扣减时禁止减为负数(余额不足则抛 `ApiException`),amount=0/缺字段均抛业务错误。前端 `user-list/index.vue` 弹窗**保持原状**(仅金额输入框),`confirmBalanceRecharge` 改为真实 `await rentalUserApi.recharge(...)`,成功后刷新列表;`showPendingAction` 仍保留给“设置会员”和“储值卡充值”按钮。后端接口允许 `balance_type` 缺省(默认 wallet)和 `remark` 缺省(默认空串)——前端不暴露任何新 UI 字段。可见范围复用现有 `accessibleManagerIds()` + `assertUserAccessible()`,谁能看谁就能充。新增菜单 `RentalUserRecharge`(slug `core:user:list:recharge`,method POST,sort 100),按 `UserList` 角色传播;当前尚无角色持有 `UserList`,故本次新增菜单未挂在普通角色上,超管 ID 1 不受影响。迁移脚本 `server/plugin/saiadmin/db/20260902_add_rental_user_wallet_log.sql` 已执行两次(幂等)。 - 2026-09-04: 完成“用户列表 → 储值卡充值”。按旧系统 `Business::invest_user_accout(type=0)` 语义,管理端只调整储值卡赠额:旧 `wash_gift_balance` 映射到新 `card_gift_balance`;`card_balance` 代表用户实付本金,不开放人工修改。前端新增显示目标用户、当前赠额、正负金额和备注的真实弹窗,调用现有 `POST /core/user-management/users/recharge` 并传 `balance_type=card_gift`。后端用显式白名单将 `wallet/card_gift` 映射到余额列,保留事务、行锁、可见范围和操作人流水,并新增金额最多两位小数、DECIMAL 上限、0 元拒绝、超额扣减拒绝和备注长度校验。新项目未发现可复用的 NFC 卡同步服务;若恢复实体卡绑定,应新增事务提交后的异步同步,不在数据库事务内调用外部厂商。 - 2026-09-04: 完成“资金管理 → 退押管理”真实列表与 XLSX 导出。旧系统语义来自 `agentapi/Order::withdaw_deposit_orders()`、`common/model/UserOrder::queryDepositOrders()` 和 `common/service/Task::addDepositOrderTask()`:退押是独立任务,退款额为押金减消费,计划退款时间、退款路径和任务状态不能从普通订单退款字段猜测。新系统新增 `sa_deposit_refund_record`(迁移 `20260904_add_deposit_refund_record.sql`),新增 `GET /core/fund-management/refund-management/index` 与 `POST /core/fund-management/refund-management/export`,支持状态、来源、退款类型、订单号、用户 ID 和申请日期筛选,并按“本人 + 递归下级管理代理”隔离数据。导出最多 50000 条,文本字段做 Excel 公式注入防护。原静态页面已替换为真实分页、汇总和下载。权限迁移 `20260904_add_deposit_refund_permissions.sql` 已重复执行验证幂等,菜单 ID 227/228 已落库并清理菜单/接口权限缓存。测试数据脚本 `20260904_seed_deposit_refund_test_data.sql` 仅关联既有 `TEST-ORDER-20260901-*` 订单;正式订单完结/支付回调后续必须按该表字段契约写入退押任务,本轮未伪造尚不存在的支付退款执行链路。 - 2026-09-04: 完成“资金管理 → 充值记录”真实列表与 XLSX 导出。新增 `GET /core/fund-management/recharge-records/index` 和 `POST /core/fund-management/recharge-records/export`,列表与导出共用充值类型、变动方向、用户 ID、昵称及日期范围筛选,并复用用户列表的“本人 + 递归下级”订单用户可见范围。导出不受分页限制,超过 50000 条要求缩小筛选,文本字段做 Excel 公式注入防护。页面已移除静态演示数据,展示增加、扣减、净变动汇总、变动前后余额、操作人和备注。当前新库只有已提交的后台余额流水 `sa_rental_user_wallet_log`,且无新版用户支付单表,因此本页暂不展示微信/支付宝未支付充值单;待迁移实付充值下单与回调链路后再合并。权限迁移 `20260904_add_recharge_record_api_permissions.sql` 已重复执行验证幂等,菜单 ID 224/225 已落库并清理菜单/接口权限缓存;当前普通角色未持有充值记录页权限,故角色继承关联为 0。 - 2026-09-01: 用户决定暂缓商户登录体系设计。当前添加商户会把手机号和密码哈希保存到 `sa_merchant`,但 `/core/login` 只认证 `sa_system_user`,因此商户暂时没有登录入口;在明确“独立商户端”或“共用管理后台”前不要同步创建后台账号,也不要新增临时登录接口。 - 2026-09-01: “用户管理 → 用户列表”正在由静态演示改为真实数据链路,旧业务参照 `api/application/agentapi/controller/Search.php::query_users()` 和 `common/model/User.php::getPageUserList()`:用户资料独立存储,租借次数与消费金额从订单汇总。已新增 `sa_rental_user` 模型/逻辑/控制器、`GET /core/user-management/users/index`、`GET /core/user-management/users/orders`、前端 API 与真实分页页面;支持微信/支付宝、用户 ID、昵称、手机号筛选及用户订单记录。非超级管理员范围按“本人 + 递归下级”的 `manage_agent_id` 订单关系限制。资金充值和会员设置涉及资金账本、支付日志与商户会员规则,本轮未伪造实现,原静态操作入口已从用户列表移除。 - 2026-09-02: “用户管理 → 用户列表”数据库与运行时验证已完成。已在本地数据库依次执行并重复验证 `20260901_add_rental_user.sql`、`20260901_add_rental_user_permissions.sql`、`20260901_seed_rental_users_from_test_orders.sql`;`sa_rental_user` 共 19 列,30 个测试订单用户全部同步,重复执行未产生重复数据或权限。使用管理员令牌真实请求用户分页、用户订单以及来源、用户 ID、昵称、手机号筛选均返回业务 `code=200`;微信/支付宝各 15 人。`UserList` 当前没有普通角色关联,因此新增订单权限也没有可继承的角色关联;超级管理员可正常使用,给普通角色开放页面时需同时授权 `core:user:list:index` 与 `core:user:list:orders`。前端 `vue-tsc --noEmit` 与 Vite 生产构建通过(3193 modules),仅保留既有 system menu 动静态导入提示。 - 2026-09-01: “下级订单”已由 `` 静态占位改为真实分页页面,新增 `GET /core/order/subordinate/index` 与 `/read`。权限范围与下级设备保持一致:超级管理员查询所有非自身管理人订单;普通账号根据 `sa_system_dept.level` 查询递归后代部门账号,并以 `sa_rental_order.manage_agent_id` 限定列表和详情。页面支持设备分类、状态汇总、代理 ID、订单、用户、商户、设备、交易号、来源、支付和日期筛选,并展示管理代理信息。权限迁移 `20260901_add_subordinate_order_api_permissions.sql` 已在本地 `water` 库执行;30 条测试订单当前分布为管理人 ID 1 共 6 条、ID 3 共 12 条、ID 10 共 12 条,便于验证本人/下级隔离。相关 PHP 文件通过语法检查,前端通过 Prettier、`vue-tsc --noEmit`,生产构建产物已更新。 - 2026-09-01: 完成“订单管理 → 我的订单”真实数据基础链路。旧业务参照 `api/application/agentapi/controller/OrderSearch.php::query_orders_new()` 与 `Order.php::user_order_detail()`;新系统只查询 `sa_rental_order`,不调用旧项目接口。新增 `GET /core/order/my/index` 和 `/read`,支持设备分类、状态、订单号、用户、商户、设备、交易单号、来源、支付和日期筛选,并按 `manage_agent_id` 隔离(管理员 ID 1 查看全部)。迁移 `20260901_add_rental_order.sql`、`20260901_add_my_order_api_permissions.sql` 已执行于本地 `water` 库并确认订单表存在。测试脚本 `20260901_seed_rental_order_test_data.sql` 已执行,写入 6 个页面分类各 5 条、共 30 条可重复清理的 `TEST-ORDER-20260901-` 前缀订单;不包含真实订单数据。 - 2026-08-31: 本轮设备模块完成记录:下级设备页已由静态演示改为按部门后代范围的真实列表,完成归属详情、车载供电/断电、二维码预览/下载、解绑以及单台/批量回收。下级设备接口为 `/core/device/subordinate/index`、`/read`、`/power`、`/unbind`;解绑权限迁移已在本地 `water` 库执行。回收复用既有 `/core/agent/device/recover`,只回收未绑定商户设备。已写入本地测试数据脚本 `20260831_seed_subordinate_device_test_data.sql`,前缀为 `TEST-SUBDEV-`;初始可回收设备为 ID 13、15,已绑定解绑测试设备为 ID 14。我的设备工具栏新增“设备二维码”,二维码页面已接入当前账号管理的真实设备,支持筛选、预览及单个/批量 SVG 下载;二维码内容遵循 `DEVICE_QR_BASE_URL + device_code`(未配置时直接使用机器码)。 - 2026-08-31: 完成“下级设备”真实数据链路:新增 `GET /core/device/subordinate/index`、`GET /core/device/subordinate/read` 和 `POST /core/device/subordinate/power`。可见范围通过 `sa_system_dept.level` 的后代部门计算,并仅返回这些代理当前由 `sa_device.manage_agent_id` 管理的设备;设备归属 `belong_agent_id` 只用于展示,不作为越权依据。页面已移除静态演示数据,归属弹窗展示归属代理、当前管理者与商户绑定信息;供电/断电仅对 `goods_type=107` 车载设备显示,沿用既有 `/cdy/tasks` 指令协议和 `sa_device_command_log` 审计记录。权限迁移位于 `server/plugin/saiadmin/db/20260831_add_subordinate_device_operations.sql`,需在目标数据库执行后非超管角色才能调用新接口。 - 2026-08-31: 已在本地 `water` 数据库执行 `20260831_add_subordinate_device_operations.sql`,并复核三条下级设备接口权限均已写入。当前 `SubordinateDevice` 菜单没有角色关联,因此没有可继承的非超管角色授权;超级管理员不受该角色映射影响。 - 2026-08-31: 下级设备页补充二维码与解绑:二维码复用 `subordinate/read` 返回的 `qr_content` 并用 `qrcode.vue` 渲染;解绑使用新接口 `POST /core/device/subordinate/unbind`,按下级设备范围校验后复用原有解绑事务、解绑流水和商户设备数同步。已在本地 `water` 库执行补充权限迁移 `20260831_add_subordinate_device_unbind_permission.sql`;当前已绑定测试设备为 `TEST-SUBDEV-POWER-001`(ID 14),可用于解绑验证,重新执行测试数据脚本即可恢复该绑定。 - 2026-08-31: 下级设备页补充单台/批量回收,前端复用既有 `POST /core/agent/device/recover` 和 `AgentLogic::recoverDevices()`,按设备当前管理代理分组回收。回收只转移 `manage_agent_id` 回当前账号并写 `sa_device_manager_record`;已绑定商户的设备被前端排除且后端仍会强制拒绝。无需新建回收事务或迁移;接口权限 `core:agent:device:recover` 已由既有代理设备分配迁移提供。测试设备 ID 13、15 可回收,ID 14 因已绑定不能回收。 - 2026-08-31: 完成“我的设备 → 设备二维码”真实页面:`/device/qrcode` 复用 `GET /core/device/index`,继承当前管理权范围和设备类型/绑定/二维码/SN 筛选;列表显示真实机器码、商户和二维码内容,并支持预览、单个及批量 SVG 下载。`DeviceLogic::formatDevice()` 统一返回 `qr_content`,规则与设备详情一致:配置 `DEVICE_QR_BASE_URL` 时拼接机器码,未配置时直接使用机器码。我的设备工具栏已增加“设备二维码”入口;无新增数据库迁移。 - 2026-08-25: Added `saiadmin-artd/src/router/modules/merchant.ts` with routes for `/merchant/my-merchant`, `/merchant/subordinate-merchant`, `/merchant/add-merchant`, and `/merchant/statistics`. - 2026-08-25: Registered the merchant route module and made backend-menu mode supplement/merge these routes when the server menu has not been synchronized yet. This fixes direct navigation to `/merchant/statistics?id=73` being redirected to the dashboard. - 2026-08-25: Verified `http://localhost:4173/#/merchant/statistics?id=73` remains on the requested URL and renders the `数据统计` page with merchant data. - 2026-08-25: 商户列表接口新增 `status`、`profit_method` 筛选和代理类型 `summary` 统计;下级商户页面已接入代理 ID、状态、分成方式搜索,分类切换和关键词搜索均复测成功。 - 2026-08-25: 重启 8787 Webman 进程加载商户列表后端改动;浏览器实测下级商户分类显示全部 65、城市合伙人 22、代理商 22、业务员 21,名称搜索可缩小到 1 条结果。 - 2026-08-26: 商户添加/编辑页地图由 OpenStreetMap iframe 替换为高德 Web JS API;支持地址搜索定位、地图点击逆地理编码、地址/地区回填,以及编辑时按已保存地址回显。高德凭据仅放在被 Git 忽略的本地 `.env.local` 中。 - 2026-08-26: 修复高德地图加载失败:高德脚本 URL 补充 `AMap.Geocoder` 插件,避免初始化地理编码器时报错。 - 2026-08-26: 新增 `/core/device/index`、`/core/device/export`、设备 API 封装和 `views/device/my-devices/index.vue` 真实数据页面;执行设备测试数据和权限 SQL,并通过商户绑定接口将 `TEST-VEHICLE-0001` 绑定到演示商户,用于验证商户搜索及绑定状态筛选。 - 2026-08-26: 参照旧项目 `bind_device_records` 完成铺货记录页:新增 `/core/device/distribution-record/index`、`/core/device/distribution-record/export`、独立前端 API/页面、设备菜单及导出权限;“我的设备”和“下级设备”的铺货记录按钮改为打开真实页面。 - 2026-08-26: 完成“去铺货”页面 `/device/distribute`。旧入口 `GET agentapi/add_bind_store`、`POST agentapi/save_bind_store`、`Store::bind_device_store()` / `save_bind_store()` 与 `Store::bindZuo2Store()`,在新项目分别映射为商户经营数据列表、`DeviceBindingDialog`、`POST /core/merchant/device/bind` 和 `MerchantLogic::bindDevices()`。页面按旧系统截图展示门头照、商户名称/地址、金额、订单量、设备数、使用率、客单价、上级代理、分润人、分成方式及右侧操作区;点击“设备绑定”输入或扫码 SN 完成铺货。 - 2026-08-26: 去铺货入口已接入“我的设备”和“下级设备”。从“我的设备”勾选未绑定设备进入时通过查询参数预填 SN;不勾选时可在商户行点击“设备绑定”后手工录入。商户候选复用 `GET /core/merchant/index`,当前绑定列表复用 `GET /core/merchant/devices`,绑定结果继续区分成功、已绑定当前商户、已绑定其他商户、设备不存在和无管理权。 - 2026-08-26: 新增并执行幂等菜单迁移 `server/plugin/saiadmin/db/20260826_add_device_distribution_page.sql`,`DistributeDevice` 菜单唯一;新增前端设备路由模块,并在后端菜单尚未同步时由 `MenuProcessor` 合并设备子路由。 - 2026-08-27: 接续完成代理设备分配半成品:`AgentLogic`/`AgentController` 提供代理列表、导出、可分配库存、分配、回收和流水接口;代理列表改用真实 API,接入 `DeviceAssignmentDialog` 与 `AssignmentRecordsDialog`;新增 `/device/assign` 页面,并让“我的设备 → 去分配”携带已勾选未铺货设备 SN。 - 2026-08-27: 已执行幂等迁移 `server/plugin/saiadmin/db/20260826_add_agent_device_assignment.sql`,数据库现有 `sa_device_manager_record`、唯一 `AssignDevice` 页面菜单及 6 个代理相关接口权限。 - 2026-08-27: 新增并执行两次幂等迁移 `server/plugin/saiadmin/db/20260827_add_agent_profile.sql`,数据库已有 `sa_agent_profile`、隐藏 `AgentEdit` 页面及 `core:agent:read/update` 权限。前端新增 `/agent/edit-agent?id=...`,后端菜单模式会合并代理内置路由。 - 2026-08-27: 完成代理合同管理迁移:新增 `/agent/contract?id=...` 隐藏页、`GET/PUT /core/agent/contract`、`sa_agent_contract` 一对一附件表及幂等权限迁移 `server/plugin/saiadmin/db/20260827_add_agent_contract.sql`。三组图片字段分别为 `contract_urls`、`identity_urls`、`extra_urls`,每组最多 20 张;读写均复用代理可见范围校验。 ## Verification - 2026-09-04: 退押管理通过 PHP 语法和 Webman 路由加载检查;本地库 3 条测试任务覆盖待退、已退、退款异常,汇总应退 43.90、已退 12.00、未退 31.90,已退状态筛选返回 1 条,管理员 ID 3 的自身范围返回 2 条。导出生成 5331-byte XLSX,文件签名 `PK 03 04` 正确且包含“订单号”“退款金额”表头,临时导出文件已清理。前端文件通过 Prettier、ESLint、Stylelint、`vue-tsc --noEmit` 和 Vite production build(3197 modules,仅既有 system menu 动静态导入提示)。 - 2026-09-04: 充值记录列表/导出通过 PHP 语法和 Webman 路由加载检查;真实库 7 条流水查询为 wallet 7、card_gift 0,汇总为增加 160.00、扣减 20.00、净变动 140.00,最新记录的用户、操作人和前后余额映射正确;按 wallet 筛选生成 5437-byte XLSX,文件签名 `PK 03 04` 正确,临时导出文件已清理。前端文件通过 Prettier、ESLint、Stylelint、`vue-tsc --noEmit` 和 Vite production build(3197 modules,仅既有 system menu 动静态导入提示)。 - 2026-09-04: 储值卡赠额充值改动通过 3 个 PHP 文件 `php -l`,后端 `php webman route:list` 确认 `POST /core/user-management/users/recharge` 正常加载;前端相关文件通过 Prettier、ESLint、Stylelint 和 `vue-tsc --noEmit`,Vite production build 通过(3196 modules,仅保留既有 system menu 动静态导入提示)。本次未对真实用户执行入账,未产生测试资金流水。 - 2026-09-02: 投诉/反馈新增 PHP 文件及路由全部通过 `php -l`;前端相关文件通过 Prettier、ESLint、`vue-tsc --noEmit` 和 Vite production build(3196 modules)。真实 HTTP 回归:反馈筛选列表、详情、状态更新、回复均返回 200,回复流水写入且状态一致;导出返回 HTTP 200、XLSX MIME、4835 bytes;测试数据和回复已清理。投诉配置接口返回 200,当前测试占位私钥查询投诉时返回明确的“微信支付商户私钥格式错误”,验证配置校验和错误透传生效。 - 2026-09-02: 余额充值回归测试通过:表 `sa_rental_user_wallet_log` 12 列 5 索引落库,菜单 `RentalUserRecharge`(slug `core:user:list:recharge`,method POST,sort 100,id 212)落库,迁移两次幂等。`RentalUserController` / `RentalUserLogic` / `WalletRechargeValidate` 通过 `php -l`;前端 `vue-tsc --noEmit` 通过;Vite production build 通过(39.08s,仅保留既有 system menu 动静态导入提示)。真实 HTTP 自测:测试用户 900005(balance 100)→ POST +50 → 150 → POST -20 → 130 → POST +50 → 180(首轮带 remark 字段)→ 回滚后再 POST 不带 balance_type/remark +30 → 210 → POST 带 balance_type=wallet+remark +10 → 220;列表查询 `wallet_balance=220` 复核;流水表 5 行 direction 1/-1/1/1/1、before/after 全程一致、operator_id=1/operator_name=admin、source=admin_recharge;负数超额扣款返回“余额不足,无法扣减”,余额未变;amount=0 返回“充值金额不能为 0”;balance_type=card 返回“账户类型不支持”;user_id=0 返回“请选择有效用户”;缺 user_id 返回“请选择用户”;**回滚后不带 balance_type 仍 200**,证明 balance_type 已从必填改为可选。前端 user-list/index.vue 弹窗模板回滚到原状,仅金额输入框,未增加任何字段。Webman 清理 supervisor (PID 29800 / 28492 / 3832) 后启动单实例(PID 15804)加载新代码(Windows 不启用文件监控);自测过程未污染正式用户密码或凭据,仅复用现有 admin 令牌。 - 2026-09-02: 用户列表三份幂等迁移已执行两次并通过结构、权限和数据数量复核;真实 HTTP 验证用户总数 30、首页 10 条、首个用户订单明细 1 条,用户 ID/昵称/手机号筛选均精确返回 1 条,来源筛选微信/支付宝各 15 条且行数据来源一致。相关 PHP 文件与路由通过 `php -l`;前端 `vue-tsc --noEmit` 和 Vite production build 通过(3193 modules transformed)。 - 2026-09-02: 用户列表页面按参考截图完成视觉调整:筛选栏新增“用户画像”,用户 ID 与昵称同行显示,移除截图中不存在的状态列,金额统一为押金两位小数及其他金额带 `¥` 的格式;操作区恢复“订单记录/设置会员/余额充值/储值卡充值”四个按钮并按两列排列。订单记录沿用真实接口,用户画像跳转 `/user-management/avatars`,会员与充值按钮暂提示尚未接入,避免虚构资金业务;页面已通过 Prettier、ESLint、`vue-tsc --noEmit` 与 Vite production build(3193 modules transformed)。 - 2026-08-31: 下级设备改动已通过 `php -l`(DeviceLogic、DeviceController、route.php)、项目内 `vue-tsc --noEmit` 和 Vite 生产构建(3188 modules transformed)。Vite 仅报告既有 system menu 编辑弹窗的动静态导入提示;未直接执行数据库迁移或向真实设备网关下发指令。 - 2026-08-31: 二维码/解绑增量已再次通过相关 PHP 文件 `php -l` 与项目内 `vue-tsc --noEmit`;本地数据库已确认解绑权限菜单 ID 208 和测试设备 ID 14 的有效商户绑定。 - 2026-08-31: 回收功能前端已通过项目内 `vue-tsc --noEmit` 与 Vite 生产构建;既有 `AgentLogic.php` 再次通过 `php -l`。 - 2026-08-31: 二维码页改动已通过 `DeviceLogic.php` 的 `php -l` 和项目内 `vue-tsc --noEmit`;Vite 构建已启动并完成模块转换,继续保留项目既有 system menu 动静态导入提示。 - Last checked: 2026-09-02 - Frontend `vue-tsc --noEmit`: passed via the project-local binary. - Frontend Vite production build: passed; 3152 modules transformed. - Merchant PHP controller/logic: `php -l` passed after list summary and filter changes. - Browser verification: `/merchant/subordinate-merchant` loaded without console errors; agent-type tab switching and merchant-name search returned backend-filtered results. - Backend selected PHP files: `php -l` passed. - 2026-08-26: 营业时间相关 PHP 控制器、逻辑、验证器和路由 `php -l` 均通过;前端使用项目内 `vue-tsc` Node 入口执行类型检查无输出错误。由于当前 8787 未监听,新增接口尚未进行运行时请求验证;数据库迁移也尚未执行。 - 2026-08-26: 数据统计接口验证成功:`GET /core/merchant/statistics?id=2&start_date=2026-08-01&end_date=2026-08-03` 返回业务 `code=200`、3 天明细、交易额 `375.90`、收益 `67.66`、订单 48;`POST /core/merchant/statistics/export` 返回 HTTP 200,导出内容 5064 bytes。 - Local `GET http://127.0.0.1:8787/core/captcha`: HTTP 200, business code 200, image payload returned with matching JPEG MIME/signature after the fix. - Local cross-origin captcha request with an `Origin` header: HTTP 200 with `Access-Control-Allow-Origin` and JPEG data. - Local login preflight: OPTIONS HTTP 200; invalid-code POST HTTP 200 with the expected business error and CORS header. - `pnpm` command: not available on PATH, so package scripts were not invoked directly. - 2026-08-26: Real HTTP self-test passed: POST `/core/merchant/save` created temporary merchant ID 74; GET `/core/merchant/read` returned submitted fields without `password`; name-filtered GET `/core/merchant/index` found 1 row; duplicate phone was rejected; DELETE `/core/merchant/destroy` removed the temporary record and the follow-up list query returned 0 rows. - 2026-08-26: Frontend `vue-tsc --noEmit` passed after adding the profit-rate input. - 2026-08-26: Frontend Vite production build passed after the add-merchant form update; 3155 modules transformed. Vite reported the existing static/dynamic import warning for the system menu edit dialog. - 2026-08-26: AMap map integration passed `vue-tsc --noEmit` and Vite production build (3155 modules). Browser automation verification was unavailable because the local Node runtime is v22.21.0 while the browser tool requires v22.22.0 or newer. - 2026-08-26: Confirmed local `.env.local` contains both AMap variables without recording their values; `vue-tsc --noEmit` passed and Vite build completed successfully after the Geocoder plugin fix. - 2026-08-26: “我的设备”PHP 控制器、逻辑和路由均通过 `php -l`;前端项目本地 `vue-tsc --noEmit` 和 Vite production build 均通过(3156 modules,保留既有 system menu 动静态导入警告)。 - 2026-08-26: 真实 HTTP 自测通过:全量 12 条、车载 2 条、充电设备 10 条、在线 7 条、离线 5 条、SN/二维码/类型搜索均命中预期;绑定 1 台测试设备后,已绑定 1 条、未绑定 11 条、商户名称搜索 1 条。`POST /core/device/export` 返回 HTTP 200、正确 XLSX MIME 和 5984 bytes 文件。 - 2026-08-26: 铺货记录 PHP 控制器、逻辑、路由通过 `php -l`,前端 `vue-tsc --noEmit` 和 Vite production build 通过(3160 modules,只有既有 system menu 动静态导入警告)。 - 2026-08-26: 铺货记录真实 HTTP 自测通过:测试流水共 4 条(铺货 3、解绑 1),操作类型筛选分别返回 3/1 条;第二家商户、`PB000001` 二维码、`TEST-POWER-0001` SN、设备类型 101 均精确返回 1 条;分页和空结果正确。解绑筛选导出返回 HTTP 200、XLSX MIME、4941 bytes。当前 12 台测试设备中 2 台已绑定、10 台未绑定。 - 2026-08-26: 去铺货真实 HTTP 自测通过:候选商户和未绑定设备列表可用;将 `TEST-PASSBT-0002` 成功铺货到商户 ID 75 后,重复提交归入 `has_binded_sn`,改投其他商户归入 `binded_other_store_sn`,不存在 SN 归入 `not_find_sn`;通过直接调用同一业务逻辑并注入代理 ID 2 验证无管理权设备归入 `not_owned_sn`。成功后未绑定候选从 10 降为 9,商户 `device_count` 从 0 增至 1、`distributed=1`,且新增一条 `action_type=1` 流水。当前 12 台测试设备中 3 台已绑定、9 台未绑定。 - 2026-08-26: 去铺货页面改为用户截图对应的商户经营数据宽表后,前端 `vue-tsc --noEmit` 通过,Vite production build 通过(3163 modules);仅保留既有 system menu 动静态导入警告。浏览器自动化仍因本机 Node v22.21.0 低于工具要求的 v22.22.0 未执行。 - Files examined include root README, frontend package/env/router/login/http/config files, backend composer/env/start/config/process/session/captcha/login/route files, and current Git status/diff summaries. - 2026-08-27: 代理相关 PHP 控制器、逻辑、验证器和路由均通过 `php -l`;代理分配相关前端文件通过 ESLint,`vue-tsc --noEmit` 通过;Vite production build 通过(3173 modules),仅保留既有 system menu 动静态导入警告。 - 2026-08-27: 真实 HTTP 自测通过:代理列表 7 条、初始可分配库存 9 台;1 台测试设备分配成功,额外不存在 SN 正确归入 `not_find_sn`,分配流水可查询;随后回收成功且设备重新回到当前账号的可分配库存。数据库最终仍有 12 台设备由管理员 ID 1 管理;自测保留 1 条分配和 1 条回收审计流水。 - 2026-08-27: 自测使用的临时 Webman 8787 进程已停止,验证结束后端口未监听。 - 2026-08-27: 执行 `20260826_add_merchant_business_time.sql` 后通过 `information_schema.columns` 复核 `sa_merchant.business_time_des` 唯一存在。 - 2026-08-27: 代理修改相关 PHP 逻辑、控制器、验证器和路由通过 `php -l`;相关前端文件通过 Prettier、ESLint、`vue-tsc --noEmit`,Vite production build 通过(3177 modules,仅保留既有 system menu 动静态导入警告)。 - 2026-08-27: 代理修改真实 HTTP 自测通过:读取代理 101,临时将运营模式改为 employee 并写入测试公司地址,二次读取确认持久化,再恢复为原始 agent/空地址;读取当前登录人 ID 1 返回“代理不存在或无权操作”。当前 Webman 版本对 PUT JSON 存在 `post()` 为空的运行时情况,`AgentController::update()` 已增加 `rawBody()` JSON 兼容读取。 - 2026-08-27: 合同管理相关 PHP 控制器、逻辑、验证器和路由通过 `php -l`;前端相关文件通过 Prettier、ESLint、`vue-tsc --noEmit`,Vite production build 通过(3180 modules,仅保留既有 system menu 动静态导入警告)。迁移已执行并确认 `sa_agent_contract`、隐藏页和两个权限菜单存在;浏览器实际加载 `/agent/contract?id=101` 成功,显示代理信息、三组上传区和提交按钮,无页面错误。 - 2026-08-28: 参照 `https://5.catpay.com.cn/equipment/index` 的“我的设备”操作区,完成设备管理、车载设备供电/断电、已铺货设备解绑和二维码:详情展示归属代理、当前管理者及商户绑定信息;供断电仅对 `goods_type=107` 显示并按旧 `/cdy/tasks` switch 协议下发;解绑保留绑定流水并同步商户设备数;二维码由项目已有 `qrcode.vue` 按真实机器码/扫描前缀生成。迁移 `server/plugin/saiadmin/db/20260828_add_device_operations.sql` 已执行,创建 `sa_device_command_log` 并补齐 `core:device:read`、`core:device:power`、`core:device:unbind` 权限。 ## 2026-09-11 设备测试批次 - 用户要求添加设备测试数据,执行 `server/scripts/seed_water_device_dashboard_demo.php --apply`;固定批次 `TEST-WATER-DEVICES-20260911`,已复跑确认不重复写入、不刷新测试心跳或覆盖编辑。 - 新增12台【测试0911】设备(ID26–37),南山/罗湖/福田/宝安各3台,复用既有标记测试企业及SKU。写入时6在线、2离线(其中1台超过24小时)、2告警、2未接入,版本1.1.0-test/1.2.0-test/1.3.0-test及未登记。 - 新增298条明确归属测试设备的模拟日用量,覆盖30天;包含零用量和无当日数据,可验证8+4分页、组合筛选、趋势及异常类型。不是实际设备遥测;测试心跳会按15分钟规则自然转离线,不后台自动续期。 - 脚本事务内确认原设备、库存、权益、配送和收款不变,并核对状态/区域/分页/历史数量。PHP语法、实际写入校验及幂等复跑通过。 ## 2026-09-11 智能设备页改版 - `/water-devices` 按参考图改为独立看板:统计、SN/企业/状态/区域/版本组合筛选、8条分页卡片、设备详情/维护、版本分布、30日真实用水趋势和当前异常表。组件、查询组合函数和状态/筛选后端策略分层,格式化代码。 - `WaterDeviceDashboard` + `device/DeviceFilters` + `DeviceStatusPolicy` 统一读取和导出,`GET devices/dashboard`、`POST devices/export` 需设备读取权限;写入复用原 WaterBusiness。故障优先,15分钟未上报离线,从未上报未接入;全量统计不随分页/筛选变化。 - 已执行并复跑 `migrate_water_device_usage.php --apply`,新增按设备/日期唯一用水汇总;复制已有明确日期快照0条。DeviceReport原事务/设备锁内保存日累计最大值,跨日留存、重复不累加;无报告日期为null。没有虚构历史、OTA或故障工单,固件入口只管理版本档案。 - 45项设备看板(含真实HTTP)及原30项接入检查通过,夹具全部回滚;PHP语法、Prettier/ESLint/vue-tsc、Vite3290模块构建通过。浏览器工具仍报os error3,本次未完成视觉验收。详见 `server/docs/WATER_DEVICE_DASHBOARD.md`。 ## 2026-09-11 库存页参考图改版 - 库存入口改为独立看板:四项全量统计、分规格表格、七日出入库趋势、库存构成环图,保留查询分页、规格维护、入库、盘点、现场换桶和流水;不使用参考图示例数据。 - `WaterInventory` 读模型新增 dashboard / export 接口,均需库存读取权限;全量统计不受搜索/分页影响,导出包含所有匹配规格。写入复用 WaterBusiness 事务和凭据去重。 - 空桶含仓库、企业现场、车载待交接;总满桶库存为可用+锁定+在途。预警按启用规格 available<=safety_stock,趋势入库含完好退回、出库按发车,不混入盘点。 - 50项库存验证(含真实HTTP)和69项业务回归通过,测试数据回滚;PHP语法、前端ESLint/类型/构建检查通过。浏览器工具启动报 os error 3,视觉验收未完成。细节见 `server/docs/WATER_INVENTORY.md`。 ## 2026-09-10 库存流水查询与导出 - 库存管理的“查看库存流水”使用独立组件 `WaterLedgerDrawer.vue`,不再限制最近 100 条;默认每页 10 条,可选 20/50/100 条,支持页码跳转、关键词、操作类型、起止日期、重置和空结果提示。 - `WaterLedger` 服务统一列表/导出筛选口径:企业名、商品名、关联凭据、说明,日期包含结束日全天;操作类型及中文名称由后端提供。保留原库存/配送九种流水,档案、收款等其他审计不混入。 - `GET /core/water/ledger` 返回分页及 `kinds`,`POST /core/water/ledger/export` 接收相同筛选,导出当前已查询条件的全部 XLSX(忽略 page/limit),超过 50,000 条明确拒绝。两接口均要求库存读取权限;临时文件位于系统临时目录,响应生成后删除。 - 验证:PHP lint、ESLint、vue-tsc、Vite build(3250 modules)通过;`php tests/water_ledger_test.php` 30 项、原 `water_business_test.php` 69 项通过,测试数据全部回滚。浏览器现有 20 条流水分成两页;第二页搜索星河返回第一页 1 条,下载 XLSX 解析确认 1 条星河数据加表头,空结果禁用导出,重置恢复 20 条。 - 2026-09-10:库存流水开始日期、结束日期改为两个独立的单日期选择框,可只填一端或选择跨日范围;查询前提示开始晚于结束,列表与导出继续复用 start_date/end_date。 - 日期拆分验证:ESLint、vue-tsc、Vite build 通过;浏览器分别选择 2026-09-01 和 2026-09-10 后查询成功,两个日期保持独立。 ## 2026-09-10 配送调度中心 - `/water-dispatch` 已从 WaterWorkspace 普通列表换为独立调度看板:真实片区任务池、日期/关键词/片区筛选、配送员状态、站点地图、路线摘要/完整路线、任务状态分页及当前页 Excel 导出。原企业/库存/预警页面保持共享组件,调度详情跳转带入关键词。 - 后端新增 `WaterDispatch.php`:看板、配送员档案、每日排班、车辆容量、待派单任务日期/时限/位置、区域和时限优先合单预览及原子确认。任务、库存、权益仍复用 WaterBusiness;完整路线单独查询,避免关键词/日期过滤漏站。 - 已执行可重入增量迁移 `php scripts/migrate_water_dispatch.php --apply`:新增 driver/driver_shift/route 三张表,企业经纬度和配送计划/路线字段。旧任务姓名电话关联到 2 位既有测试配送员,未推断排班,当前容量使用可编辑默认 100 桶;3 单 24 桶、待派单 1/配送中 2 的状态未改变。 - `GET /core/water/dispatch/board`、`GET /core/water/dispatch/route` 需要配送读取权限,`GET /core/water/dispatch/plan` 和 POST dispatch 新 action 需要管理权限。合单按容量及相同片区校验,原始自由文本 HTTP 派单不再使用,单笔派单也生成经校验的路线。 - 地图使用现有高德配置,仅展示登记过的坐标和站序连线。当前模拟企业无坐标,显示真实空状态;没有道路优化、预计到达时间、GPS轨迹或配送员在线心跳。之前“地图合单未实现”的描述由本条取代:区域/容量/时限合单和坐标地图已实现,道路导航优化仍未接入。 - 后端41项调度测试、69项供水业务、30项库存流水、30项设备接入共170项通过,新增测试记录全部回滚;ESLint、vue-tsc、Vite构建通过。浏览器确认3单24桶,搜索智配办公后任务池/路线/明细同为1单6桶,排班档案和新增表单可用,无候选方案禁用合单确认。细节与回退见 `server/docs/WATER_DISPATCH.md`。 - 最终浏览器补充验证:配送员新增表单包含姓名、电话、片区、车辆容量;任务详情跳转补货预警自动带入智配办公,显示1条账户且关联已有配送。最终页面保持原供水侧栏样式,显示当日未排班2人和当前页Excel导出入口。 ## 2026-09-10 高德地图实际接入 - 配送地图不再因无任务坐标提前退出:始终初始化真实高德底图,无坐标默认全国视野,有坐标按路线展示编号站点及顺序连线,含比例尺、缩放、查看全部站点、点选任务和失败重试。 - `src/utils/waterAmap.ts` 统一供水地图和选点弹窗的脚本/插件加载,复用既有高德脚本标识与本地 VITE_AMAP_KEY/VITE_AMAP_SECURITY_CODE,配置值不记入文档。 - `DispatchLocationPicker.vue` 为配送计划增加地址搜索及地图选点,确认回填后保存才写入配送任务坐标;保存后切换到对应任务组展示。后端沿用已有 schedule 校验,企业地址和库存/权益不受影响。 - 浏览器实际确认全国高德底图、比例尺/缩放正常;上海市浦东新区世纪大道1号地址解析和标记正常,坐标回填成功;验证后取消计划,未把验证坐标保存进模拟任务。不存在道路导航或实时GPS轨迹接入。 - 最终 ESLint、vue-tsc 和 Vite production build 均通过;已刷新4173预览加载高德地图版本。 ## 2026-09-10 深圳地图测试坐标 - 用户要求以南油第四工业区4栋出发、送往中洲大厦;目的地按南山区海德一道中洲控股中心处理(已询问具体大厦,当前按此默认)。两个地址经项目高德 Geocoder 查询:出发 GCJ-02 113.924140,22.522806;送达 113.935124,22.516070。 - 已执行 `php scripts/seed_water_shenzhen_coordinates.php --apply`,仅给任务9/10/11及其对应3个【测试0910】企业更新地址、深圳南山区片区和坐标;任务新增 origin_address/origin_longitude/origin_latitude 记录出发点,结构由原 dispatch 增量迁移扩展。 - 三条测试任务使用同一出发点及目的地,保留原8/6/10桶、待派单/配送中/配送中状态,未额外建单或修改库存、权益、收款。脚本事务内校验业务表和非位置字段保持一致,固定批次标记复跑不覆盖后续用户修改;原测试行快照位于 server/runtime/water-shenzhen-map-before.json。 - 地图新增橙色“起”标记,单任务起点至送达点及同路线按站序绘制示意连线;路线卡和详情显示出发地址。未调用道路导航服务。 - 脚本 PHP lint 和41项配送回归检查通过,脚本已复跑确认 replayed=true。 - 深圳坐标最终验证:ESLint、vue-tsc、Vite构建通过;浏览器确认深圳南山区3单24桶、橙色起点与蓝色送达点均显示,起止示意连线及出发地址正常。 ## 2026-09-10 高德驾车线路规划 - 已替换 DispatchMap 的直线示意:单任务或同一真实路线具有唯一出发点及完整送达坐标时,通过 AMap.Driving 按既有站序绘制实际道路,显示预计公里/分钟(不含配送停留),支持重新规划。此前“未接入道路导航”的记录由本条更新;尚无实时GPS及各站承诺到达时间。 - waterAmap.ts 独立加载驾车插件,每次最多16个途经点,超长路线分段并汇总,保留全部站序;连续重复位置去重,部分失败不展示不完整线路。缺坐标、多起点、未成路线的多任务组明确提示;切换时丢弃旧结果,真实路线等待完整站点接口后才规划。 - 验证:ESLint、vue-tsc、Vite build 通过;node scripts/test-water-driving.mjs 覆盖分段不漏站、汇总、重复坐标、旧请求及服务失败。浏览器实测南油第四工业区4栋至中洲控股中心道路约2.0公里,初次11分钟、重新规划10分钟,用时随路况变化。本次未更改订单或库存等业务数据。 ## 2026-09-10 新增两条深圳测试路线 - 执行 server/scripts/seed_water_shenzhen_routes.php --apply,固定批次 TEST-WATER-SZ-ROUTES-20260910;重跑 replayed=true,不覆盖用户后续变更。新增任务69/路线13(科技园4桶)、任务70/路线14(生态园10栋506室6桶),状态已派单,分别关联独立测试企业和已排班测试配送员。 - 同一起点为南油第四工业区4栋 GCJ-02 113.924140,22.522806。高德 Geocoder 查询科技园113.946040,22.544610;生态园113.952743,22.530351为园区级返回值,地址明确标注暂定位园区,未声称精确到10栋或506室。 - 仅新增10桶测试权益,无收款订单;调用原业务创建配送与路线提交,测试SKU可用库存减10、锁定加10,测试账户权益同步冻结。事务校验原任务/收款不变、路线及权益一致。当前看板5单34桶(待派单1/已派单2/配送中2)。PHP lint、实际写入校验及幂等复跑通过。 - 浏览器验证两条独立路线均返回实际道路:科技园约5.3公里/28分钟,生态园园区约4.8公里/17分钟(当次路况估计);关键词搜索对应1单,已恢复全部列表。 ## 2026-09-10 配送优化策略与Eloquent模型 - 用户明确要求最短距离、最快送达、时限优先,以及模型数据访问。本次供水业务整体切换Eloquent:model/water独立模型及关联,WaterBusiness/WaterDispatch/WaterLedger/WaterRoutePlanning、供水权限与选项读取、设备报告均不再使用Think ORM或Db::table;框架原登录和其他模块不迁移。测试及调用业务服务的种子脚本统一Eloquent事务连接。 - service/routing 的 RouteStrategy 与三个具体策略,以有向高德道路矩阵计算固定批次站序;最多8站穷举,固定共同出发点、不返仓。最短距离按总里程,最快送达按总完成用时,时限策略按超时站数→超时总秒数→总用时。含可配置每站配送停留,无时限时按用时比较,不代表跨车队全局最优或未来路况预测。 - 配送页面新增“线路优化”,合单预览各批新增“选择策略并优化”;可查看优化前后指标、每站到达/完成时间、超时和道路地图。仅尚未出库的已派单路线可应用;待派单批次在合单确认时应用。配送计划补齐出发地址与坐标选点。 - POST dispatch新增 optimize / optimize_apply,plan_commit各组支持optimization_token。后端重新读取模型校验坐标并执行策略,预览绑定操作员、任务指纹,10分钟过期;保存时再次校验状态、站序、日期,行锁及原库存/权益逻辑保持一致,重复提交幂等。路线optimization保存策略及估计,地图以保存策略重新调用高德。 - 已执行 scripts/migrate_water_routing.php --apply,仅新增sa_water_route.optimization和sa_water_route_preview。道路成本由浏览器JS API提供,后端校验格式与任务匹配,不独立核验高德来源;仅作为调度估计,不驱动扣款或库存数量。 - 本机PHP需原生mbstring。Windows supervisor已用项目脚本重启并启用扩展;scripts/start_water.ps1及windows.php向子进程传递config/php/mbstring.ini,不修改全局php.ini。CLI回归使用php -d extension=mbstring tests/文件名。 - 194项后端回归通过(业务69、调度41、流水30、设备30、策略/模型24),测试数据全部回滚;前端ESLint、vue-tsc、道路矩阵与取消/配额错误测试通过。浏览器实测三种策略并给生态园测试路线14保存时限优先,仍5单34桶,状态不变。现有测试路线为单站,多站不同排序由三站集成测试覆盖。详细契约及限制见server/docs/WATER_ROUTING_STRATEGIES.md。 - 最终Vite构建通过,浏览器刷新后显示路线14已保存“时限优先”,仍5单34桶;Windows原windows.php启动也已补充项目级mbstring子进程配置。 ## 2026-09-19 uni-app 底部导航修复 - water-mini/src/components/BottomNav.vue 统一控制固定定位:移除 app.css 对 bottom-nav 的 H5 位移覆盖,使用左右贴边、max-width:480px 和自动外边距居中,解决 scoped left/right 与全局 transform 冲突。统一四等分按钮、图标和文字间距,页面底部预留安全区。 - 企业导航按设计恢复首页/订单/水卡/我的,水卡仍展示真实汇总权益;司机为工作台/任务/地图/我的,地图入口沿用配送路线及站点地图;代理为工作台/用户/订单/佣金,佣金页补导航,账户入口移至代理工作台,账户详情不显示无选中项的代理导航。 - 点击当前菜单不再 reLaunch;切换进行中忽略重复点击,失败提示可重试。未改变 API、数据库、Redis、身份认证或佣金业务。 - vue-tsc、现有12项测试、微信小程序及H5构建通过;已更新本地 http://127.0.0.1:8787/water-mini/index.html,入口和新资源 HTTP 200。编译后的 H5/微信 CSS 已核对;浏览器工具因 unsupported Codex auth method: apikey 不可用,视觉截图及实际点击验收 NOT RUN。 ## 2026-09-19 本机测试手机号登录及微信导入修正 - 用户明确指定 13012345678 / 66666 测试功能。新增 POST /api/mini/v1/auth/test-phone,经 TestPhoneLogin 服务和 TestPhoneIdentityProvider 适配器验证,仅 APP_ENV=local 且 MINI_TEST_PHONE_LOGIN=true 并且 TCP 来源回环地址可登录。默认生产环境关闭;不验证真实手机号所有权,不与微信账号合并。SessionVerifier 在开关关闭时拒绝既有测试会话。 - 本机 server/.env 已开启测试,Windows monitor 自动重新加载,实际 config 已返回 test_phone_login=true。通过幂等 CLI seed_mini_test_phone.php 初始化用户5,并绑定仅三个既有测试资源:企业49【测试0910】科技园配送测试、司机13【测试】地图配送员-KJY、代理7测试商贸。未创建业务库存、收款、订单;脚本不恢复已停用账号/授权。 - water-mini 登录页新增手机号/验证码表单与本地测试标记,H5和微信编译端共用真实会话及身份选择。正式短信登录仍待接入。源目录 project.config.json 指向 dist/build/mp-weixin,修复直接导入源码找不到 app.json 的问题;此本机入口使用 touristappid、urlCheck=false,生产或真实微信登录须配置真实AppID并启用域名校验。 - 小程序接口默认地址在 water-mini/src/api/client.ts,可用 water-mini/.env.local 的 VITE_API_BASE_URL 覆盖并重新编译;后端8787监听在 server/config/process.php。测试手机号仅支持电脑本机网页/开发者工具;手机真机的127.0.0.1不是电脑。 - 验证:18项测试登录、161项业务、786项契约、100项架构、47项框架检查通过,测试数据回滚;前端类型和12项测试、MP/H5构建通过。实际HTTP验证10个身份/业务读取接口、错误验证码401、跨企业403、注销后401。API文档已同步46项操作,H5预览更新并验证资源200。UI实际点击验收受浏览器工具认证错误限制,NOT RUN。