# fish_services **Repository Path**: 296803441/fish_services ## Basic Information - **Project Name**: fish_services - **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-06-13 - **Last Updated**: 2026-08-06 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 钓鱼服务端 钓鱼 App 服务端基础骨架,基于 Go 开发,支持 App 接口服务和后台管理服务双服务域。 ## 快速开始 1. 启动基础依赖: - `docker compose -f deploy/docker-compose.yaml up -d` 2. 安装依赖: - `go mod tidy` 3. 启动服务: - `make run` 4. 检查健康状态: - `GET http://localhost:8081/api/v1/health` 5. 查看在线 API 文档: - `http://localhost:8081/swagger/index.html` ## 服务域划分 - App 接口服务: - 路由前缀:`/api/v1` - 面向移动 App、小程序、H5 等 C 端客户端 - 当前示例接口: - `GET http://localhost:8081/api/v1/health` - `GET http://localhost:8081/api/v1/ping` - `GET http://localhost:8081/api/v1/weather/minutely` - `GET http://localhost:8081/api/v1/weather/hourly` - `GET http://localhost:8081/api/v1/weather/daily` - `GET http://localhost:8081/api/v1/weather/forecast` - `GET http://localhost:8081/api/v1/fishing-spots` - `GET http://localhost:8081/api/v1/fishing-spots/nearby` - `GET http://localhost:8081/api/v1/fishing-spots/search` - `GET http://localhost:8081/api/v1/fishing-spots/report/poi-search` - `POST http://localhost:8081/api/v1/fishing-spots/report-submissions` - `POST http://localhost:8081/api/v1/auth/send-code` - `POST http://localhost:8081/api/v1/auth/register` - `POST http://localhost:8081/api/v1/auth/login` - `POST http://localhost:8081/api/v1/auth/password/reset` - `GET http://localhost:8081/api/v1/users/profile` - `PATCH http://localhost:8081/api/v1/users/profile` - `POST http://localhost:8081/api/v1/fishing-sessions/sync` - `GET http://localhost:8081/api/v1/fishing-sessions` - `GET http://localhost:8081/api/v1/fishing-sessions/:id` - 后台管理服务: - 路由前缀:`/admin/v1` - 面向运营后台、内容审核、订单管理、用户管理等后台场景 - 当前示例接口: - `POST http://localhost:8081/admin/v1/auth/login` - `GET http://localhost:8081/admin/v1/profile` - `GET http://localhost:8081/admin/v1/ping` - `GET http://localhost:8081/admin/v1/fishing-spots/poi-search` - `POST http://localhost:8081/admin/v1/fishing-spots` - `GET http://localhost:8081/admin/v1/fishing-spot-submissions` - `POST http://localhost:8081/admin/v1/fishing-spot-submissions/:id/review` ## API 文档 - 在线 Swagger UI: - `http://localhost:8081/swagger/index.html` - 原始 OpenAPI 文件: - `http://localhost:8081/docs/swagger.yaml` - 错误码说明文档: - `docs/错误码说明.md` ## 统一响应与错误码 - 成功响应统一格式: - `code`: 固定为 `0` - `message`: 固定为 `success` - `request_id`: 请求追踪 ID - `data`: 业务数据 - 失败响应统一格式: - `code`: 稳定业务错误码,不再等于 HTTP 状态码 - `message`: 面向开发与联调的错误语义 - `user_message`: 面向 C 端用户展示的提示文案 - `request_id`: 请求追踪 ID - 错误码分层约定: - `1xxxxx`:通用请求错误 - `2xxxxx`:认证与用户相关错误 - `3xxxxx`:钓场相关错误 - `4xxxxx`:作钓记录相关错误 - `5xxxxx`:品牌相关错误 - `6xxxxx`:系统枚举相关错误 - `7xxxxx`:外部依赖服务错误 - `9xxxxx`:系统内部错误 - 完整错误码清单见: - `docs/错误码说明.md` ## 后台快速验证 1. 启动服务,系统会根据实体自动同步数据库表结构,并初始化默认后台数据 2. 使用默认管理员账号登录: - 用户名:`admin` - 密码:`Admin@123456` 3. 调用登录接口: - `POST http://localhost:8081/admin/v1/auth/login` 4. 使用返回的 JWT 访问受保护接口: - `GET http://localhost:8081/admin/v1/profile` - `GET http://localhost:8081/admin/v1/ping` ## App 用户快速验证 1. 启动服务,系统会根据实体自动同步数据库表结构 2. 默认会自动初始化一个 App 测试账号: - 手机号:`13800138000` - 密码:`App@123456` 3. 也可以先发送验证码: - `POST http://localhost:8081/api/v1/auth/send-code` 4. 使用手机号 + 验证码注册: - `POST http://localhost:8081/api/v1/auth/register` 5. 使用手机号 + 密码或手机号 + 验证码登录: - `POST http://localhost:8081/api/v1/auth/login` 6. 使用手机号 + 验证码找回密码: - `POST http://localhost:8081/api/v1/auth/password/reset` 7. 使用返回的 JWT 访问 App 用户信息接口: - `GET http://localhost:8081/api/v1/users/profile` 8. 调用资料完善接口更新昵称、头像、性别: - `PATCH http://localhost:8081/api/v1/users/profile` 9. 调用作钓记录同步接口上传一次完整作钓会话: - `POST http://localhost:8081/api/v1/fishing-sessions/sync` 10. 调用作钓记录列表接口查询历史记录: - `GET http://localhost:8081/api/v1/fishing-sessions` 11. 调用作钓记录详情接口查询单次完整记录: - `GET http://localhost:8081/api/v1/fishing-sessions/:id` ## 天气预报模块 - 分钟级接口:`GET http://localhost:8081/api/v1/weather/minutely` - 小时级接口:`GET http://localhost:8081/api/v1/weather/hourly` - 天级接口:`GET http://localhost:8081/api/v1/weather/daily` - 统一聚合接口:`GET http://localhost:8081/api/v1/weather/forecast` - 当前 provider:彩云天气 - 抽象设计: - `internal/pkg/weather`:统一气象领域模型、Provider 接口、Aggregator 聚合入口 - `internal/pkg/weather/caiyun`:彩云原始实体、HTTP 请求与统一模型映射 - `internal/service/app/weather_service.go`:App 层参数校验与统一查询入口 - `internal/handler/app/weather_handler.go`:天气 HTTP 接口 - 模型约定: - 内部统一返回结构以彩云分钟级、小时级、天级字段语义为基准 - App 可按页面需要单独请求分钟级、小时级、天级接口,避免一次查询拿全量数据 - 后续对接其他天气供应商时,只需要实现 `weather.Provider` 并映射到统一 `weather.Forecast` - `weather.Aggregator` 已预留多供应商聚合与 fallback 能力 - 当前默认状态: - `configs/config.yaml` 已提供 `weather` 配置示例 - 未配置有效 `weather.caiyun.token` 时不会真正请求彩云接口 ### 天气配置 ```yaml weather: enabled: true default_source: "caiyun" timeout_seconds: 8 caiyun: enabled: true token: "你的彩云 Token" base_url: "https://api.caiyunapp.com" ``` - 支持环境变量覆盖: - `FISH_WEATHER_ENABLED` - `FISH_WEATHER_DEFAULT_SOURCE` - `FISH_WEATHER_TIMEOUT_SECONDS` - `FISH_WEATHER_CAIYUN_ENABLED` - `FISH_WEATHER_CAIYUN_TOKEN` - `FISH_WEATHER_CAIYUN_BASE_URL` ### 天气调用示例 ```bash curl "http://localhost:8081/api/v1/weather/minutely?longitude=101.6656&latitude=39.2072" ``` ```bash curl "http://localhost:8081/api/v1/weather/hourly?longitude=101.6656&latitude=39.2072&hourly_steps=24" ``` ```bash curl "http://localhost:8081/api/v1/weather/daily?longitude=101.6656&latitude=39.2072&daily_steps=3" ``` ```bash curl "http://localhost:8081/api/v1/weather/forecast?longitude=101.6656&latitude=39.2072&hourly_steps=24&daily_steps=3" ``` - 查询参数: - `longitude`:经度,范围 `-180 ~ 180` - `latitude`:纬度,范围 `-90 ~ 90` - `hourly_steps`:仅小时级和统一聚合接口使用,范围 `1 ~ 360`,默认 `24` - `daily_steps`:仅天级和统一聚合接口使用,范围 `1 ~ 15`,默认 `3` - 统一返回包含: - `result.minutely`:分钟级预报 - `result.hourly`:小时级预报 - `result.daily`:天级预报 ## 钓场模块 - 后台人工添加流程: - 先调用 `GET /admin/v1/fishing-spots/poi-search` 检索高德门店候选 - 再调用 `GET /admin/v1/fishing-spots/poi-detail` 查询门店详情和照片 - 再调用 `POST /admin/v1/fishing-spots` 人工确认并入库 - 确认入库时,服务端会根据 `amap_poi_id` 自动拉取高德详情,并把评分、消费、停车信息、图片等快照存进 `fishing_spots` - App 用户上报流程: - 用户先调用 `GET /api/v1/fishing-spots/report/poi-search` 查询高德门店候选 - 用户可调用 `GET /api/v1/fishing-spots/report/poi-detail` 查看门店详情和照片 - 用户选中后调用 `POST /api/v1/fishing-spots/report-submissions` 提交待审核门店 - 后台调用 `GET /admin/v1/fishing-spot-submissions` 查看待审核列表 - 后台调用 `POST /admin/v1/fishing-spot-submissions/:id/review` 执行通过或驳回 - App 查询接口: - `GET /api/v1/fishing-spots`:门店列表,支持最近排序和钓场类型筛选 - `GET /api/v1/fishing-spots/nearby`:根据当前位置检索附近钓场 - `GET /api/v1/fishing-spots/search`:根据城市和关键字分页搜索钓场 - 抽象设计: - `internal/model/fishing_spot.go`:钓场实体 - `internal/model/fishing_spot_submission.go`:用户上报待审核实体 - `internal/pkg/poisearch`:POI 搜索统一抽象 - `internal/pkg/poisearch/amap`:高德 POI 搜索与详情实现 - `internal/service/admin/fishing_spot_service.go`:后台人工确认创建流程 - `internal/service/app/fishing_spot_service.go`:App 列表、附近、筛选、用户上报 - 数据维护: - `fishing_spots` 已接入 GORM `AutoMigrate` - `fishing_spot_submissions` 已接入 GORM `AutoMigrate` - 后台确认创建或审核通过时,会自动把高德 POI 详情同步到 `fishing_spots` 表 - 服务启动时会自动同步表结构,无需手工创建该表 ### 钓场类型 - `0`:未知 - `1`:休闲塘 - `2`:黑坑 - `3`:炮坑 - `4`:船钓场 ### 高德配置 ```yaml amap: enabled: true key: "你的高德Web服务Key" base_url: "https://restapi.amap.com" timeout_seconds: 8 search_page_size: 10 ``` - 支持环境变量覆盖: - `FISH_AMAP_ENABLED` - `FISH_AMAP_KEY` - `FISH_AMAP_BASE_URL` - `FISH_AMAP_TIMEOUT_SECONDS` - `FISH_AMAP_SEARCH_PAGE_SIZE` ### 钓场调用示例 ```bash curl "http://localhost:8081/api/v1/fishing-spots?sort_by=nearest&longitude=113.32452&latitude=23.099994&spot_type=1&page=1&page_size=20" ``` ```bash curl "http://localhost:8081/api/v1/fishing-spots/nearby?longitude=113.32452&latitude=23.099994&radius_meters=5000&limit=20" ``` ```bash curl "http://localhost:8081/api/v1/fishing-spots/search?city=%E5%B9%BF%E5%B7%9E%E5%B8%82&keyword=%E6%B0%B4%E5%BA%93&page=1&page_size=20" ``` ```bash curl -H "Authorization: Bearer " "http://localhost:8081/admin/v1/fishing-spots/poi-search?keywords=%E5%A4%A9%E6%B2%B3%E9%92%93%E9%B1%BC%E5%9C%BA®ion=%E5%B9%BF%E5%B7%9E%E5%B8%82&page_num=1&page_size=10&city_limit=true" ``` ```bash curl -H "Authorization: Bearer " "http://localhost:8081/admin/v1/fishing-spots/poi-detail?poi_id=B0FFG1ABCD" ``` ```bash curl -H "Authorization: Bearer " "http://localhost:8081/api/v1/fishing-spots/report/poi-search?keywords=%E5%A4%A9%E6%B2%B3%E9%92%93%E9%B1%BC%E5%9C%BA®ion=%E5%B9%BF%E5%B7%9E%E5%B8%82&page_num=1&page_size=10" ``` ```bash curl -H "Authorization: Bearer " "http://localhost:8081/api/v1/fishing-spots/report/poi-detail?poi_id=B0FFG1ABCD" ``` ```bash curl -X POST "http://localhost:8081/api/v1/fishing-spots/report-submissions" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "name": "龙湖钓场", "province": "广东省", "city": "广州市", "district": "天河区", "address": "天河区龙湖路88号", "longitude": 113.32452, "latitude": 23.099994, "amap_poi_id": "B0FFG1ABCD", "amap_type": "体育休闲服务;运动场馆;垂钓园", "amap_type_code": "110304", "spot_type": 1 }' ``` ```bash curl -X POST "http://localhost:8081/admin/v1/fishing-spots" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "name": "龙湖钓场", "alias": "龙湖垂钓园", "province": "广东省", "city": "广州市", "district": "天河区", "city_code": "020", "ad_code": "440106", "address": "天河区龙湖路88号", "longitude": 113.32452, "latitude": 23.099994, "amap_poi_id": "B0FFG1ABCD", "amap_type": "体育休闲服务;运动场馆;垂钓园", "amap_type_code": "110304", "contact_phone": "020-88886666", "business_hours": "周一至周日 08:00-22:00", "introduction": "适合台钓和路亚,支持夜钓。", "spot_type": 1, "status": 1 }' ``` ```bash curl -H "Authorization: Bearer " "http://localhost:8081/admin/v1/fishing-spot-submissions?review_status=1&page=1&page_size=20" ``` ```bash curl -X POST "http://localhost:8081/admin/v1/fishing-spot-submissions/1/review" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "action": "approve", "review_note": "信息核对通过", "spot_type": 1 }' ``` ## App 认证说明 - 注册方式:手机号 + 短信验证码 + 密码 - 登录方式: - 手机号 + 密码 - 手机号 + 短信验证码 - 找回密码方式:手机号 + 短信验证码 + 新密码 - 用户资料完善:支持更新昵称、头像地址、性别、年龄、钓龄、喜欢钓的鱼种、经常去的区域 - 当前短信能力已做抽象预埋: - `internal/pkg/sms`:短信发送接口抽象 - `internal/pkg/verifycode`:验证码存储抽象 - `internal/pkg/ratelimit`:发送频控与防刷存储抽象 - 当前仅支持腾讯云短信发送,启动时会按 `sms.provider=tencent` 初始化真实发送器 - 若腾讯云密钥、`sdk_app_id` 或供应商配置错误,服务会直接启动失败,避免误走模拟发送 - 后续接入其他供应商时,只需要新增 `sms.ProviderSender` 实现并扩展启动工厂 - 测试环境验证码方案: - 当 `server.mode != release` 且 `sms.debug_enabled=true` 时,发送验证码会优先使用固定验证码 - 默认固定验证码为 `123456` - 当 `sms.debug_return_code=true` 时,`/api/v1/auth/send-code` 响应会直接返回 `debug_code` - 生产环境 `release` 模式下会自动禁用该能力 ## 作钓记录同步 - 同步接口:`POST http://localhost:8081/api/v1/fishing-sessions/sync` - 列表接口:`GET http://localhost:8081/api/v1/fishing-sessions?page=1&page_size=20` - 详情接口:`GET http://localhost:8081/api/v1/fishing-sessions/:id` - 设计约束: - 必须携带 App 用户 JWT,服务端会自动绑定当前登录用户 - 使用 `local_session_id` 作为用户维度幂等键 - 同步时会整体替换该次会话下的轨迹点、鱼获记录、天气快照 - 列表与详情查询都严格按当前用户隔离 ## 系统枚举 - App 获取接口: - `GET http://localhost:8081/api/v1/system-enums` - `GET http://localhost:8081/api/v1/system-enums/:type` - 后台维护接口: - `GET http://localhost:8081/admin/v1/system-enum-types` - `GET http://localhost:8081/admin/v1/system-enums` - `POST http://localhost:8081/admin/v1/system-enums` - `PUT http://localhost:8081/admin/v1/system-enums/:id` - `DELETE http://localhost:8081/admin/v1/system-enums/:id` - 当前内置枚举类型: - `fish_type`:鱼种 - `technique`:操作手法 - `water_layer`:水层 - `structure`:结构 - `rig`:钓组 - 使用约束: - 服务启动时通过 `AutoMigrate` 自动建表,并自动补齐默认枚举数据 - App 端只消费启用状态的枚举项 - App 端分组 key 使用 `fish_type / technique / water_layer / structure / rig` - 后台内部存储仍保留统一枚举类型定义,其中鱼种对应内部类型 `fish_species` - 后台前端可通过管理接口动态增删改查,App 端下次拉取即可生效 ## 验证码频控策略 - 单手机号发送间隔:默认 `60 秒` - 单手机号每日发送上限:默认 `10 次` - 单 IP 每日发送上限:默认 `30 次` - 默认优先使用 Redis 做频控与验证码存储;如果未启用 Redis,会回退到内存实现 - 相关配置项: - `sms.send_interval_seconds` - `sms.send_max_per_day_per_mobile` - `sms.send_max_per_day_per_ip` - `sms.debug_enabled` - `sms.debug_fixed_code` - `sms.debug_return_code` ## 日志说明 - 日志同时输出到标准输出和文件,默认文件路径:`./logs/app.log` - 当前已覆盖的日志类型: - 服务启动与优雅停机日志 - HTTP 请求访问日志 - Panic 恢复日志 - MySQL / Redis 初始化与异常日志 - GORM 慢 SQL 与错误日志 - 短信发送、验证码、限流相关日志 - App 用户注册、登录、找回密码、资料更新日志 - 天气查询日志 - 后台管理员登录日志 - 日志配置项: - `log.level` - `log.file_path` - `log.max_size_mb` - `log.max_backups` - `log.max_age_days` - `log.compress` ## 目录结构 - `cmd/api`:服务启动入口 - `internal/handler/app`:App 接口控制器 - `internal/handler/admin`:后台管理控制器 - `internal/service/app`:App 业务服务 - `internal/service/admin`:后台业务服务 - `internal/repository/app`:App 数据访问层 - `internal/repository/admin`:后台数据访问层 - `internal/model`:数据库模型 - `internal/middleware`:通用中间件 - `docs`:接口文档 ## 数据库维护 - 当前默认使用 GORM `AutoMigrate` 根据实体自动同步表结构 - 主要实体定义位于: - `internal/model/user.go` - `internal/model/admin.go` - `internal/model/fishing_spot.go` - 表结构同步入口位于: - `internal/bootstrap/migrate.go` - 服务启动时会自动处理: - `users` - `admins` - `roles` - `permissions` - `admin_roles` - `role_permissions` - `audit_logs` - `fishing_spots` - `fishing_sessions` - `fishing_session_track_points` - `fishing_session_catch_records` - `fishing_session_weather_snapshots` - 默认后台管理员与基础 RBAC 数据也会由代码自动初始化 - 默认 App 测试账号也会由代码自动初始化;若手机号已存在则跳过,不会覆盖已有用户密码 - 默认 App 账号配置位于: - `seed.app_user.enabled` - `seed.app_user.mobile` - `seed.app_user.password` - `seed.app_user.nickname` - 支持环境变量覆盖: - `FISH_SEED_APP_USER_ENABLED` - `FISH_SEED_APP_USER_MOBILE` - `FISH_SEED_APP_USER_PASSWORD` - `FISH_SEED_APP_USER_NICKNAME` ## 当前能力 - Gin HTTP 服务 - YAML 配置加载,支持 `FISH_` 环境变量覆盖 - 文件与标准输出结构化日志 - MySQL 与 Redis 启动接入 - Trace ID 与异常恢复中间件 - App API 与后台管理 API 分域组织 - App 用户验证码发送、注册、登录、找回密码、个人信息接口 - App 用户资料完善接口 - App 用户作钓记录同步、列表、详情接口 - App 系统枚举获取接口 - 后台系统枚举管理接口 - 后台人工检索高德门店并确认创建钓场 - App 端附近钓场检索与按城市搜索 - 统一天气预报接口,已按彩云字段语义抽象内部模型 - 验证码发送频控与基础防刷 - 基于 JWT 的后台鉴权 - 基于 JWT 的 App 用户鉴权 - 本地 Docker Compose 基础环境