PROJECT_CONTEXT.md 132 KB

Project Context

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_watermini* 表,不植入身份/支付/财务数据。照片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 仍是原 <WaterWorkspace module="finance" />。明天应使用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 的 b58ca2eaa6 / e7fe122ad3 两个版本 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 <file>;本次已核对启动、进程、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: “下级订单”已由 <OrderList empty /> 静态占位改为真实分页页面,新增 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。