# huaka **Repository Path**: Moxyu/huaka ## Basic Information - **Project Name**: huaka - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-10-15 - **Last Updated**: 2026-10-16 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 理发店会员管理 给一家理发店做的会员记账系统,替代原来在纸质会员卡上划道的做法。店主年纪较大、小学文化、只会用智能手机做简单操作,所以从数据模型到界面都围着「还剩几次」和「今天收了多少」两个数字转,不追求功能多。 ## 目录结构 | 目录 | 说明 | |------------|---------------------------------------------| | `server/` | Laravel 13 接口后端,同时服务后台与手机端,规则见 `server/AGENTS.md` | | `admin/` | Vue 3 + Ant Design Vue 4 管理后台,规则见 `admin/AGENTS.md` | | `mobile/` | uni-app 手机端,给店主本人用,规则见 `mobile/AGENTS.md` | ## 技术栈 - 后端:Laravel 13、PHP 8.3+、MySQL 8.0+、tymon/jwt-auth 2.x、gregwar/captcha、overtrue/easy-sms、phpoffice/phpspreadsheet - 管理后台:Vue 3、Vite、Ant Design Vue 4、Pinia、Vue Router、Axios、Sass - 手机端:uni-app(Vue 3)、普通 CSS,可打包 Android App,也可跑 H5 ## 业务功能 **会员与账目** - 会员以手机号作身份,称呼可以留空,列表自动显示手机号后四位,避免因为不会打字就卡住 - 一张卡同时记「剩余次数」与「余额」,两者独立增减 - 办卡充值时实收金额与余额变动分开记,充 500 送 100 时营业额按实收的 500 算 - 散客收款只记一笔收入,不碰任何会员账目 - 记错了用撤销,补一条反向流水让账目自动抵消,原记录保留不删 - 每笔变动都留变动前后的次数与余额快照,客人有疑问时可查 **管理后台** - 会员列表支持手机号后四位与称呼搜索,行内可直接剪完了、充钱、调整 - 消费流水按类型、收款方式、时间筛选,并显示这段时间的收入合计 - 回访提醒只捞卡里还有次数或余额却长期未到店的会员,按剩余次数倒序排列 - 仪表盘展示今日收入、今日到店、会员总数、待回访与最近消费流水 - 服务项目维护价格与「是否可用次卡抵扣」 **手机端** - 开单首页:今日收入与到店人数、找人、最近到店的大按钮、新人办卡、散客收款 - 会员卡:剩余次数与余额用超大数字,两个主按钮「剪完了」「充钱」 - 剪完了有确认弹层,把「8 → 7」写大,点错了能取消 - 账本可按今天或这个月查看并撤销;我的里有回访提醒,点一下直接拨号 **顾客自助页** - 店里墙上贴一张二维码,顾客扫码就能查自己的卡还剩几次、还有多少钱,不用等店主腾出手 - 没办过卡的可以自己登记,登记记录带「顾客自己扫码登记」备注,方便和手工录入的区分 - 后台「系统管理 → 扫码办卡」里生成二维码,可改地址、可直接打印 - 页面不登录谁都能开,所以只显示姓氏加星号与打码手机号;查卡与登记都挂了限速,防止有人拿号码段挨个试 ## 脚手架内置能力 **后端** - 后台双通道登录:账号密码 + 图形验证码、手机号 + 短信验证码,JWT 签发与无痛刷新 - RBAC:管理员、角色、权限三级模型,权限菜单由 `config/permission.php` 全量定义 - 中间件:`CorsMiddleware` 跨域、`AdminOperationCheckMiddleware` 接口级鉴权、`AdminOperationLogMiddleware` 操作留痕 - 统一响应 `ResponseService`、统一表单请求基类 `BaseRequest`、统一异常注册 `ExceptionRegisterService` - 基础控制器 `BaseController` 提供 `getCurrentAdmin`、`paginate`、`exportData`(Excel 导出) - 登录日志、操作日志、短信日志、运行环境查看 - 站点配置中心(分组键值配置,支持批量更新) - 通用服务:`CaptchaService` 验证码、`SmsService` 短信、`RateLimitService` 限流、`RsaService` 加解密签名、`AMapService` 地图与IP定位、`MenuBuilderService` 菜单树 - 命令 `php artisan rbac:reset` 一键重置权限数据 **前端** - 请求封装:自动携带 token、按后端 `show` 字段弹提示、401 无痛刷新并重放排队请求 - 动态菜单与面包屑:菜单由后端权限接口下发,按 `path` 高亮与展开 - 布局 `default.vue`:侧栏折叠、主题色切换、暗色模式、全屏、锁屏、个人下拉 - 通用组件:`Crud.vue` 列表页脚手架、单/多图上传与附件上传、富文本、图标选择、日期与日期区间、颜色选择、地图选点、倒计时、文本省略、验证码按钮 - 页面:登录、仪表板、个人中心、管理员、角色、权限、配置、登录日志、操作日志、运行日志、短信日志、404 ## 快速开始 后端: ```bash cd server cp .env.example .env # 若已存在 .env 则跳过,凭据已随模板提供 # 只需补齐 DB_USERNAME 与 DB_PASSWORD php artisan migrate --seed ``` 前端: ```bash cd admin cp .env.example .env # 接口地址默认 http://127.0.0.1:8000/api pnpm install ``` 手机端: ```bash cd mobile pnpm install pnpm dev:h5 # 浏览器调试;真机调试用 pnpm dev:app ``` 手机端的接口地址走环境变量:`.env` 是本地调试用的,`.env.production` 是打包上线用的,两个都已备好。 真机连电脑调试时要把 `.env` 里的地址换成电脑在局域网里的 IP——手机上的 `127.0.0.1` 指的是手机自己,写它连不上后端。 初始账号 `admin / admin888`,登录后请立即修改密码与手机号。 ## 服务管理 根目录 `manage.sh` 统一管理后端、管理后台与手机端三个服务: ```bash ./manage.sh start 启动全部服务 ./manage.sh stop 停止全部服务 ./manage.sh restart 重启全部服务 ./manage.sh status 查看全部服务状态 ./manage.sh start admin 只启动管理后台 ./manage.sh start mobile 只启动手机端 H5 调试服务 ./manage.sh stop server 只停止后端 ./manage.sh -h 查看脚本帮助 ``` | 服务 | 目录 | 端口 | 启动命令 | |----------|------------|------|-------------------------------------------------| | `server` | `server/` | 8000 | `php artisan octane:start --host=0.0.0.0` | | `admin` | `admin/` | 5173 | `pnpm dev` | | `mobile` | `mobile/` | 5174 | `pnpm dev:h5 --strictPort` | 日志写入 `.runtime/logs/服务名.log`,超过 10MB 时在下次启动自动轮转为 `.log.1`;PID 写入 `.runtime/pids/服务名.pid`。启动后会等待端口就绪,30 秒仍未就绪判定失败并回滚清理;启动前会释放固定端口,端口被其他进程占用时该进程会被结束。 后端默认以 Octane + swoole 常驻运行,环境未装 swoole 时把脚本里 `COMMANDS[server]` 改成 `php artisan serve --host=0.0.0.0 --port=8000` 即可。 手机端跑 H5 只是为了在浏览器里调试,真正装到手机上是打包成 App:`pnpm dev:app` 真机调试、`pnpm build:app` 打包。 各前端的端口写在各自项目的 `vite.config.js` 里并开了 `strictPort`,端口被占时直接失败而不是偷偷换一个——否则 `manage.sh` 会一直等一个根本没在监听的端口,最后误判成启动失败。 根目录 `deploy.sh` 为按需本机保留的部署脚本,不随仓库分发,首次使用前需填写配置区的 SSH 信息、线上目录与重启命令,未填写时会直接拒绝执行。 ## 测试 后端内置冒烟测试,使用内存 SQLite,不依赖本地 MySQL: ```bash cd server php artisan test ``` 覆盖内容: - `tests/Feature/PermissionCoverageTest.php` 权限表与路由一致性、权限定义合法性 - `tests/Feature/AdminAuthTest.php` 登录发令牌、菜单下发、接口鉴权 401/403、失败日志落库 - `tests/Feature/AdminSecurityTest.php` 操作日志密码脱敏、角色停用与删除后不再授权、删除有下级的权限被拦下 - `tests/Feature/RouteErrorTest.php` 接口不存在返回 404、方法不对返回 405、不带 JSON 头也返回 401 - `tests/Feature/CardPageTest.php` 顾客自助页查卡、短信验证码校验、验证码一次性、短信限速、重复登记不建第二张卡、姓名与手机号打码 - `tests/Feature/QrCodeTest.php` 二维码结构合法性(反解矩阵并做格式信息 BCH 校验)、地址可达性判断、权限校验 - `tests/Feature/MemberAccountTest.php` 办卡记账、扣次、扣余额、次数与余额不足拦截、撤销与重复撤销、防重提交、删除保护、散客不碰会员账、回访筛选口径、手机端首页数据 ## 新增一个业务模块的步骤 1. 后端补 `app/Http/Controllers/Admin/XxxController.php`、`app/Http/Requests/Admin/XxxRequest.php`、`app/Models/Xxx.php` 与迁移文件 2. 在 `routes/api/admin.php` 的鉴权路由组内注册接口 3. 在 `config/permission.php` 追加菜单与接口权限,`id` 递增,`path` 必须与路由、前端路由完全一致 4. 后台补 `admin/src/api/xxx.js`、`admin/src/views/<模块>/<页面>.vue`,并在 `admin/src/router/pages.js` 注册 5. 手机端如要用,补 `mobile/src/api/xxx.js` 与页面,并在 `mobile/src/pages.json` 注册 6. 执行 `php artisan rbac:reset` 重新灌入权限数据 ## 开发约定 - 任何接口改动都要同步维护权限表,`config/permission.php` 漏登记会导致超管之外的角色返回 403 - 前端路由路径、页面物理路径、后端菜单 `path` 三者必须逐级一致 - 接口成功、失败提示统一走 `ResponseService`,文案由后端维护,前端按 `show` 字段决定是否弹出 ## 环境变量 - `.env` 不入库,只放本机运行配置 - `.env.example` 与 `.env.prod` 入库随仓库分发,第三方服务凭据(腾讯云短信、七牛云对象存储、高德地图、钉钉机器人)与各环境独立的 `APP_KEY`、`JWT_SECRET` 已直接写入模板,克隆后补齐数据库配置即可运行 - `QCLOUD_SMS_CARD_TEMPLATE_ID` 是顾客自助登记用的短信模板,默认沿用登录模板,**正式给顾客用之前要在腾讯云控制台申请一个登记验证码的模板并替换**,否则顾客收到的文案是登录提示 - 数据库凭据是唯一例外,`DB_USERNAME`、`DB_PASSWORD` 在所有环境文件中均留空,由部署方自行补齐 - `composer.json`、`package.json` 的依赖清单覆盖脚手架的完整能力面,个别库当前未直接使用(如 `jaeger/querylist`、`chuoke/rmb-capital`、`overtrue/pinyin`、`intervention/image`),确认无引用后可按需移除