diff --git a/.DS_Store b/.DS_Store index 6e6e959..8cc0347 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/.gitea/workflows/docker.yaml b/.gitea/workflows/docker.yaml deleted file mode 100644 index d2446d1..0000000 --- a/.gitea/workflows/docker.yaml +++ /dev/null @@ -1,97 +0,0 @@ -name: Build docker and publish -run-name: The pipeline for docker build -on: - push: - branches: - - main - pull_request: - branches: - - main - -env: - # Docker - REPO: ${{ vars.REPO }} - DOCKER_USERNAME: ${{ vars.DOCKER_USERNAME }} - DOCKER_PASSWORD: ${{ vars.DOCKER_PASSWORD}} - # Gitea - GIT_USERNAME: ${{ vars.GIT_USERNAME }} - GIT_PASSWORD: ${{ vars.GIT_PASSWORD }} - # Host SSH - SSH_HOST: ${{ vars.SSH_HOST }} - SSH_PORT: ${{ vars.SSH_PORT }} - SSH_USER: ${{ vars.SSH_USER }} - SSH_PASSWORD: ${{ vars.SSH_PASSWORD }} - # SMTP - SMTP_SERVER_ADDRESS: ${{ vars.SMTP_SERVER_ADDRESS }} - SMTP_USERNAME: ${{ vars.SMTP_USERNAME }} - SMTP_PASSWORD: ${{ vars.SMTP_PASSWORD }} - -jobs: - linux: - runs-on: ubuntu-latest - strategy: - matrix: - # 使用gitea-tool-cache需要指定具体的版本号 - go: ["1.24.5"] - - steps: - - name: Checkout - uses: https://${{ env.GIT_USERNAME }}:${{ env.GIT_PASSWORD }}@${{ vars.DOMAIN_OF_GITEA}}/actions/checkout@v4 - - # 将.env环境变量配置文件拷贝致gitea runner容器 - - name: copy env file to runner container - uses: https://${{ env.GIT_USERNAME }}:${{ env.GIT_PASSWORD }}@${{ vars.DOMAIN_OF_GITEA}}/actions/ssh-action@v0.1.10 - with: - host: ${{ env.SSH_HOST }} - username: ${{ env.SSH_USER }} - password: ${{ env.SSH_PASSWORD }} - port: ${{ env.SSH_PORT }} - debug: true - script: | - mkdir -p /install/cicd_env_files_mini_chat - cd /install/cicd_env_files_mini_chat - docker cp ${{env.JOB_CONTAINER_NAME}}:${{gitea.WORKSPACE}}/deploy/.env ./.env - source ./.env - docker cp ${{env.JOB_CONTAINER_NAME}}:${{gitea.WORKSPACE}}/docs/${SERVICE_NAME}.json . - docker cp .env ${{ vars.RUNNER_CONTAINER_NAME }}:/.env - docker exec ${{ vars.RUNNER_CONTAINER_NAME }} /bin/bash -c "source /.env" - - - name: Install Go environment - uses: https://${{ env.GIT_USERNAME }}:${{ env.GIT_PASSWORD }}@${{ vars.DOMAIN_OF_GITEA}}/actions/gitea-tool-cache@v5 - with: - # 需要指定具体版本号! - go-version: ${{ matrix.go }} - - - uses: https://${{ env.GIT_USERNAME }}:${{ env.GIT_PASSWORD }}@${{ vars.DOMAIN_OF_GITEA}}/actions/setup-go@v2 - with: - go-version: ${{ matrix.go }} - - - uses: https://${{ env.GIT_USERNAME }}:${{ env.GIT_PASSWORD }}@${{ vars.DOMAIN_OF_GITEA}}/actions/cache@v3 - with: - path: | - ~/.cache/go-build - ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- - - - name: Prepare GO environment - run: | - go env -w GOPROXY=https://goproxy.cn,direct - go env -w GOPRIVATE="${{ vars.DOMAIN_OF_GITEA}}" - git config --global url."https://${{ env.GIT_USERNAME }}:${{ env.GIT_PASSWORD }}@${{ vars.DOMAIN_OF_GITEA}}/".insteadOf "https://${{ vars.DOMAIN_OF_GITEA}}/" - - - name: Build and push docker image - run: | - source ${{gitea.WORKSPACE}}/deploy/.env - make docker - make publish-docker - make docker-run - - - name: Build APIDOC docker image - run: | - source ${{gitea.WORKSPACE}}/deploy/.env - echo ${SERVICE_NAME} - echo ${APIDOC_CONTAINER_NAME} - echo ${SWAGGER_JSON} - make swagger-docker \ No newline at end of file diff --git a/.trae/.ignore b/.trae/.ignore deleted file mode 100644 index e69de29..0000000 diff --git a/.trae/documents/API 与后台管理代码审计与整改计划.md b/.trae/documents/API 与后台管理代码审计与整改计划.md deleted file mode 100644 index 4590929..0000000 --- a/.trae/documents/API 与后台管理代码审计与整改计划.md +++ /dev/null @@ -1,50 +0,0 @@ -## 概览 -- 后端以 Go 实现,路由集中在 `internal/router/router.go`,分5组:管理端非认证(`/api/admin`登录)、管理端认证(`/api/admin`全量管理接口)、系统管理(`/api`模板兼容)、APP公开(`/api/app`)、APP认证(`/api/app`)。 -- 管理端处理器分布在 `internal/api/admin/*`,覆盖活动/期次/奖励/抽奖/用户资产/称号/道具卡/优惠券/工会/系统用户角色菜单等。 -- 前端后台管理位于 `web/admin`,Axios 基础 `baseURL=/api`,API 模块在 `web/admin/src/api/*`。 - -## 发现与结论 -- 路由与前端 URL 基本一致;系统管理模块通过 `baseURL=/api` 访问 `/api/user/list|/api/role/list|/api/v3/system/menus/simple`,与后端 `systemApiRouter` 对齐(`internal/router/router.go:59-167`)。 -- 前端存在绝对 URL 前缀问题:`web/admin/src/api/reward-grant.ts:50` 使用 `url: '/api/admin/...'`,与 `baseURL=/api` 叠加为重复前缀,应改为相对 `admin/...`。 -- 奖励发放接口在前端有重复实现:`reward-grant.ts` 与 `player-manage.ts` 同指向 `POST /api/admin/users/{id}/rewards/grant`(后端实现见 `internal/api/admin/users_reward_admin.go:32-75`),建议合并与统一用法。 -- 认证链路清晰:管理端拦截器校验 JWT、登录哈希与状态(`internal/router/interceptor/admin_auth.go:18-82`);APP 端简化(`internal/router/interceptor/app_auth.go:13-35`)。前端 Axios 统一加 `Authorization` 并防抖登出(`web/admin/src/utils/http/index.ts:65-76,84-92`)。 -- 分层一致性:多数管理端处理器调用 service 层,但也有直接操作 DAO 的情况(如称号管理 `internal/api/admin/titles_admin.go`),建议将复杂业务(效果校验、用户称号唯一激活切换)沉入 service 保持一致性与复用。 -- 性能与合理性: - - 工作台积分统计扫表累加(`internal/api/admin/dashboard_admin.go:74-84`)可改为 SQL 聚合并按有效期过滤;积分净变动已用流水 SUM(`87-107`),建议两者一致走 SQL。 - - 新用户概览多表聚合(`224-381`)逻辑合理但查询密集,后续可评估增加索引与批量 UNION/子查询降往返。 -- API 路由一致性:系统管理接口挂在 `/api` 与管理端其余接口挂在 `/api/admin`,存在认知负担;短期保留兼容,补齐文档;中期评估统一路径策略。 - -## 整改实施计划 -### 阶段1:快速修复 -- 修复前端绝对 URL 前缀问题:将 `web/admin/src/api/reward-grant.ts:50` 的 `url: '/api/admin/users/${userId}/rewards/grant'` 改为 `url: 'admin/users/${userId}/rewards/grant'`,与其余模块一致。 -- 合并奖励发放 API:删除/重定向 `reward-grant.ts` 的重复实现,统一在 `player-manage.ts` 暴露 `fetchGrantPlayerReward`,或反之,在 `reward-grant.ts` 统一导出并在 `player-manage.ts` 复用。 - -### 阶段2:分层与可维护性 -- 抽取称号管理核心逻辑到 service: - - 将 `AssignUserTitle` 的 upsert 与“仅保留一个激活称号”的切换(`internal/api/admin/titles_admin.go:439-490`)抽到 `internal/service/title`,供管理端与未来 APP 端统一调用。 - - 将效果参数校验 `validateEffectParams`(`titles_admin.go:296-377`)迁移为可复用的校验器,便于单元测试与前后端一致性。 -- 优化工作台统计: - - 积分总额改为 SQL `SUM(points)` 并过滤过期(替换 `dashboard_admin.go:74-84` 的内存累加),减少 I/O 与CPU。 - -### 阶段3:一致性与文档 -- 路由规范化策略评审:维持 `/api`(系统管理)与 `/api/admin` 并存,新增开发文档条目明确两者用途与历史原因;中期根据前端路由与菜单加载需求评估是否迁移到 `/api/admin/system/*`。 -- 更新 Swagger 文档:核对 `docs/swagger.yaml`/`docs/swagger.json` 与现状路由一致,补充工作台接口、称号/效果接口、批量发放接口说明。 - -### 阶段4:测试与验证 -- 前端:为 `Axios` 层与关键 API(登录、奖励发放、称号分配)增加最小可运行的集成测试或契约测试;校验 401 防抖与登出行为。 -- 后端:为称号 service 新增单元测试(效果参数校验、scope 命中、唯一激活切换),为工作台统计新增 SQL 聚合测试。 - -## 参考定位 -- 路由总表:`internal/router/router.go:50-53,56-167,169-188,190-210,213-232` -- 绝对 URL 问题:`web/admin/src/api/reward-grant.ts:50` -- Axios baseURL:`.env.development:7`、`web/admin/src/utils/http/index.ts:45-48` -- 奖励发放后端:`internal/api/admin/users_reward_admin.go:32-75` -- 工作台积分统计:`internal/api/admin/dashboard_admin.go:74-84,87-107` -- 称号分配逻辑:`internal/api/admin/titles_admin.go:439-490` -- 认证拦截器:`internal/router/interceptor/admin_auth.go:18-82`、`internal/router/interceptor/app_auth.go:13-35` - -## 验收标准 -- 前端所有 API 使用相对路径,无 `/api/` 重复前缀;奖励发放仅一处实现且通过。 -- 工作台统计在数据量上升时仍稳定(SQL 聚合后端性能可观)。 -- 称号管理逻辑具备可测试的 service 层,接口行为与前端预期一致。 -- Swagger 文档与实际路由对齐,前后端调用一目了然。 \ No newline at end of file diff --git a/.trae/documents/APP商品API设计与交付计划.md b/.trae/documents/APP商品API设计与交付计划.md deleted file mode 100644 index d9cb132..0000000 --- a/.trae/documents/APP商品API设计与交付计划.md +++ /dev/null @@ -1,143 +0,0 @@ -## 项目架构对齐 -- 后端框架:`gin`(`internal/pkg/core`),统一中间件与响应封装 -- 路由分组:`internal/router/router.go` 下 APP 公开/鉴权分组可扩展 `products` 资源 -- 鉴权:APP 端采用 `core.WrapAuthHandler(intc.AppTokenAuthVerify)`,`Authorization: Bearer ` -- ORM/DB:`gorm` + `gorm/gen`(`internal/repository/mysql/dao/Query`),读写分离 `GetDbR/GetDbW` -- Swagger:通过注释生成(`swaggo/swag`),挂载 `GET /swagger/*any` -- 商品相关模型:`product`(SPU)、`sku`、`category`、`spec`、`spec_option`、`product_spec`、`sku_spec_value`、推荐位 `recommendation_slot/item` - -## 接口设计 -### 1) 商品列表 -- 路径:`GET /api/app/products` -- 鉴权:需要(App Token) -- 请求参数: - - `page`(默认1)`pageSize`(默认20,最大50) - - `categoryId`(可选) - - `priceMin`、`priceMax`(可选,闭区间) - - `salesMin`(可选) - - `inStock`(布尔,可选,默认true,仅展示可售) - - `sortBy`(`sales|price|createdAt`)`order`(`asc|desc`,默认 `desc`) -- 业务规则:只返回 `status=上架` 的 SPU;`inStock=true` 时需至少一个 SKU `stock-stock_locked>0` -- 响应结构: -```json -{ - "total": 1234, - "currentPage": 1, - "pageSize": 20, - "list": [ - { - "id": 1001, - "name": "羽绒服", - "mainImage": "https://.../xx.jpg", - "price": 299.00, - "sales": 5230, - "inStock": true, - "categoryId": 12 - } - ] -} -``` -- 过滤/排序实现: - - 基于 `product` 表 `status/category_id/sales_count/created_at/price_min/price_max` 组合条件 - - `inStock` 通过子查询/EXISTS 关联 `sku` 计算有效库存 - -### 2) 商品详情 -- 路径:`GET /api/app/products/:id` -- 鉴权:需要(App Token) -- 业务规则:SPU `status=上架` 且可售;下架/缺货返回业务态错误码 -- 响应结构: -```json -{ - "id": 1001, - "name": "羽绒服", - "album": ["https://.../1.jpg", "https://.../2.jpg"], - "priceRange": {"min": 199.00, "max": 399.00}, - "sales": 5230, - "stock": {"total": 235, "available": 220}, - "specs": [ - {"name": "颜色", "options": ["黑色", "白色"]}, - {"name": "尺码", "options": ["M", "L", "XL"]} - ], - "description": "高蓬松保暖...", - "service": ["7天无理由", "运费险"], - "recommendations": [ - {"id": 1002, "name": "羽绒马甲", "mainImage": "...", "price": 199.00} - ] -} -``` -- 规格/相册/描述来源: - - 相册:`product.images_json` 或关联表(若存在),解析为数组 - - 规格:`product_spec` + `spec` + `spec_option` 聚合; - - 描述/服务保障:从 `product` 富文本字段(或扩展表)读取 -- 推荐逻辑: - - 优先使用 `recommendation_slot.scene='detail_related'` 的绑定数据 - - 兜底:同类目类目内 `sales_count` Top N(去重当前商品) - -## 路由与代码结构 -- 路由注册:`internal/router/router.go` - - `appAuthGroup := router.Group("/api/app", core.WrapAuthHandler(intc.AppTokenAuthVerify))` - - `appAuthGroup.GET("/products", product.New(repo, logger).List)` - - `appAuthGroup.GET("/products/:id", product.New(repo, logger).Detail)` -- Handler:`internal/api/product/list_app.go`、`internal/api/product/detail_app.go` - - 统一入参校验、容错与错误码 -- Service:`internal/service/product/service.go` - - 封装查询、库存计算、推荐聚合、缓存命中 -- DAO查询:使用 `dao.Query` 与 `GetDbR` -- DTO:`internal/api/product/dto.go`(列表项与详情结构体) - -## 鉴权与权限 -- 使用现有 APP Token 验证中间件;Swagger `@Security LoginVerifyToken` -- 接口动作无需RBAC(面向APP用户),但保留限流与风控扩展点 - -## 性能与优化(500ms SLA) -- 索引: - - `product(status, category_id, sales_count, created_at)` 复合索引 - - `sku(product_id, status)`、`sku(stock, stock_locked)` 参与计算库存 -- 查询: - - 列表:单次查询 + `EXISTS` 子查询校验库存;分页使用 `LIMIT/OFFSET`(页码≤100) - - 详情:读取 SPU + 聚合 `SUM(stock-stock_locked)` + 规格与相册多表查询 -- 缓存: - - 本地 TTL 缓存: - - 列表:键 `list:{filters}:{page}:{pageSize}`,TTL 30s,命中后直接返回 - - 详情:键 `detail:{id}`,TTL 60s;库存变更事件可主动失效(后续扩展) - - 并发合并:使用 `singleflight` 避免热点详情击穿 -- 传输:仅必要字段;GZIP 已由中间件统一处理 -- 限流:`/products` 每用户每秒 ≤10(后续中间件实现,可选) - -## 错误码与校验 -- 下架:`PRODUCT_OFFSHELF`(HTTP 200,业务码),提示“商品已下架” -- 缺货:`PRODUCT_OUT_OF_STOCK` -- 参数错误:`INVALID_PARAM`(页码、价格区间校验) -- 统一响应封装沿用 `internal/pkg/core/context.go` - -## Swagger文档 -- 在两个 Handler 顶部添加注释: - - `@Summary`、`@Description`、`@Tags products` - - `@Param`(列出查询参数与类型) - - `@Success 200 {object} ListResp` / `DetailResp` - - `@Router /api/app/products [get]`、`/api/app/products/{id} [get]` - - `@Security LoginVerifyToken` -- 生成后可在 `http://127.0.0.1:9991/swagger/index.html` 查看 - -## 单元测试 -- Handler:`httptest.NewServer` + 构造 `Authorization` 头,校验分页与过滤、错误码 -- Service:使用内存/模拟 DAO(或事务性测试库)覆盖:库存计算、规格聚合、推荐兜底 -- 覆盖率目标:核心逻辑 ≥70% - -## 压力测试 -- 工具:`hey` 或 `wrk` -- 示例: - - 列表:`hey -n 5000 -c 100 'http://localhost:9991/api/app/products?page=1&pageSize=20'` - - 详情:`wrk -t4 -c200 -d30s 'http://localhost:9991/api/app/products/1001'` -- 目标:P95 ≤ 500ms,错误率 < 0.1% - -## 联调与交付 -- 与前端约定字段与筛选参数;提供响应示例与错误码表 -- 提供 Swagger 文档与可复用的 Postman/HTTP 文件 -- 完成线上/测试环境联调回归 - -## 验收标准 -- 两个接口完成并通过单元与压力测试 -- Swagger 文档完整,含请求/响应示例 -- P95 响应时间 ≤ 500ms(测试环境数据量下) -- 鉴权与状态校验生效;缓存命中率提升热点访问性能 \ No newline at end of file diff --git a/.trae/documents/APP端工会审核适配与可选增强.md b/.trae/documents/APP端工会审核适配与可选增强.md deleted file mode 100644 index c89b628..0000000 --- a/.trae/documents/APP端工会审核适配与可选增强.md +++ /dev/null @@ -1,25 +0,0 @@ -## 是否变更APP接口 -- 路由与参数:不变。现有 APP 端接口保持原路径与入参。 - - `POST /api/app/guilds/:guild_id/members`(加入):internal/api/guild/members_app.go:19-50 - - `DELETE /api/app/guilds/:guild_id/members/:user_id`(离开):internal/api/guild/members_app.go:52-83 - - `GET /api/app/guilds/:guild_id/members`(成员列表):internal/api/guild/members_app.go:102-141 -- 响应结构:不变(仍返回 `{ message }`)。 -- 行为变化:当工会 `join_mode=1` 时,加入后处于待审状态,不会立即出现在成员列表(服务层变更:internal/service/guild/join.go:13-48;成员列表过滤正式成员:internal/service/guild/members_list.go:9-29)。 - -## 客户端适配建议(保持接口不变) -- 加入后状态提示:将文案改为“申请已提交,待审核”,并在个人/工会页显示“待审核”状态。 -- 功能限制:待审期间禁用仅成员可用的操作(如成员互动、贡献等)。 -- 成员列表:无需变更,列表只显示已通过成员。 - -## 可选后端增强(增强体验,仍向后兼容) -1) 加入响应增强:在 `POST /api/app/guilds/:guild_id/members` 返回体中新增 `join_status`(0待审/2已通过),便于客户端无额外查询即可知状态。 -2) 成员自查接口:新增 `GET /api/app/guilds/:guild_id/members/me` 返回当前用户在该工会的 `role/status/join_status`。 -3) 申请查询接口:新增 `GET /api/app/guilds/:guild_id/applications?user_id=xxx` 用于查询自己的待审记录(只读)。 -- 以上均为增量字段与新端点,旧客户端不受影响。 - -## 验收 -- 加入审核工会时,APP 加入成功但显示“待审核”,成员列表不包含该用户;审核通过后自动展示。 -- 保持现有路由与参数不变,无需强制更新客户端。 -- 若启用增强:客户端通过 `join_status` 或新查询端点准确展示状态。 - -如确认,我将按照“保持接口不变 + 提供可选增强”的方式推进服务与(可选)APP接口扩展,并提供前端适配示例。 \ No newline at end of file diff --git a/.trae/documents/Add Cost Analysis to Lottery Simulation.md b/.trae/documents/Add Cost Analysis to Lottery Simulation.md deleted file mode 100644 index ddaef48..0000000 --- a/.trae/documents/Add Cost Analysis to Lottery Simulation.md +++ /dev/null @@ -1,38 +0,0 @@ -# Add Cost Analysis to Lottery Simulation - -I will enhance the lottery simulation feature to include cost calculation and financial analysis. - -## 1. Backend Changes (`internal/api/admin/lottery_admin.go`) - -- **Update Response Structures**: - - Add `Cost` (int64) to `simulateRewardStat` (Unit cost in cents). - - Add `TotalCost` (int64) to `simulateRewardStat` (WonCount * Cost). - - Add `TotalSimulationCost` (int64) to `simulateIssueResponse`. - - Add `TotalSimulationRevenue` (int64) to `simulateIssueResponse` (TotalDraws * Activity.PriceDraw). - - Add `GrossProfit` (int64) to `simulateIssueResponse` (Revenue - Cost). - - Add `GrossProfitRate` (float64) to `simulateIssueResponse`. - -- **Logic Update**: - - In `SimulateIssue`: - - Fetch the `Activity` details to get `PriceDraw`. - - Collect all `ProductID`s from the rewards. - - Batch query the `Products` table to get prices. - - Map product prices to rewards (if `ProductID > 0`). - - Calculate financial stats after the simulation loop. - -## 2. Frontend Changes (`web/admin/src/views/operations/lottery-simulation/index.vue`) - -- **Update API Type Definition**: Update `SimulateRewardStat` and `SimulateIssueResponse` interfaces in `api/operations.ts`. -- **UI Enhancement**: - - **Summary Cards**: Add a row of summary cards at the top of the results section showing: - - Total Revenue (总收入) - - Total Cost (总成本) - - Gross Profit (毛利润) - - Profit Margin (毛利率) - - **Table Columns**: - - Add "Unit Cost" (单价/成本) column. - - Add "Total Cost" (总发放成本) column. - -## 3. Verification -- Verify compilation. -- (Self-Correction) Ensure `Price` is handled as integer (cents) and formatted correctly in frontend (divided by 100). diff --git a/.trae/documents/Add Product Description Field.md b/.trae/documents/Add Product Description Field.md deleted file mode 100644 index 7f39508..0000000 --- a/.trae/documents/Add Product Description Field.md +++ /dev/null @@ -1,31 +0,0 @@ -# Add Product Description Field for App Detail - -## 1. Database Migration -We need to add a `description` column to the `products` table to store the product details. -**SQL Statement:** -```sql -ALTER TABLE products ADD COLUMN description TEXT COMMENT '商品详情'; -``` - -## 2. Backend Model Update -Update the GORM model and DAO to reflect the database change. -* **Model**: Update `internal/repository/mysql/model/products.gen.go` to include the `Description` field. -* **DAO**: Update `internal/repository/mysql/dao/products.gen.go` to include the `Description` field definition and mapping. - -## 3. Service Layer Update -Update `internal/service/product/product.go` to handle the new field. -* **Input Structs**: Add `Description` to `CreateProductInput` and `ModifyProductInput`. -* **Business Logic**: - * Update `CreateProduct` to save the description. - * Update `ModifyProduct` to update the description. - * Update `GetDetailForApp` to populate the `Description` field from the database instead of returning an empty string. - -## 4. Admin API Update -Update the Admin API to allow creating and editing products with a description. -* **File**: `internal/api/admin/product_create.go` -* **Request Structs**: Add `description` field to `createProductRequest` and `modifyProductRequest`. -* **Handlers**: Pass the `description` from the request to the service layer. - -## 5. App API Verification -* **File**: `internal/api/app/product.go` -* The `GetProductDetailForApp` function already returns a `Description` field in the response. By updating the service layer to populate this field, the App API will automatically return the correct data. diff --git a/.trae/documents/Channel Management Implementation Plan.md b/.trae/documents/Channel Management Implementation Plan.md deleted file mode 100644 index 22f9579..0000000 --- a/.trae/documents/Channel Management Implementation Plan.md +++ /dev/null @@ -1,38 +0,0 @@ -# 渠道管理与用户来源统计实施计划 - -针对您的需求,我将把现有的“小程序二维码生成”功能升级为完整的“渠道管理”系统,并增加用户来源统计。 - -## 1. 数据库变更 (Schema Changes) - -* **新增 `channels` 表**: - * 用于定义推广渠道。 - * 字段: `id` (主键), `name` (渠道名称), `code` (唯一标识, 用于二维码参数), `type` (类型: 如抖音/微信/线下), `remarks` (备注), `created_at`, `updated_at`。 -* **修改 `users` 表**: - * 新增 `channel_id` (BIGINT) 字段,建立与 `channels` 表的关联。 - * (注意:现有 `douyin_id` 字段保留,用于业务逻辑,但统计归因将统一使用 `channel_id`)。 - -## 2. 后端开发 (Backend) - -* **API 开发 (`internal/api/admin`)**: - * 实现渠道管理的 CRUD 接口:`List`, `Create`, `Update`, `Delete`。 - * **统计逻辑**: 在 `List` 接口中,同步查询 `users` 表,统计每个 `channel_id` 下的用户数量。 -* **登录/注册逻辑优化 (`internal/service/user`)**: - * 修改 `LoginWeixin` (微信登录) 逻辑。 - * 除了现有的 `invite_code` 和 `douyin_id`,新增支持 `channel_code` 参数。 - * 逻辑: 当用户注册时,如果检测到 `channel_code`,查找对应的 `Channel` 记录,并将 `channel.ID` 写入用户表的 `channel_id` 字段。 - -## 3. 前端开发 (Frontend) - -* **新增页面**: `运营管理` -> `渠道管理` (`web/admin/src/views/operations/channels/index.vue`)。 - * **列表页**: 展示渠道名称、唯一标识(Code)、**累计注册用户数**、创建时间。 - * **操作栏**: 提供“编辑”、“删除”以及 **“查看二维码”** 功能。 -* **二维码生成优化**: - * 点击“查看二维码”时,自动调用生成接口,参数中自动带上当前渠道的 `code`。 - * (替代原有的纯手动输入二维码生成页面,或将其保留为“自定义工具”)。 - -## 4. 验证计划 - -1. **功能验证**: 在后台创建一个测试渠道,生成二维码。 -2. **流程验证**: 模拟新用户携带该渠道参数登录。 -3. **数据验证**: 检查数据库 `users` 表中新用户的 `channel_id` 是否正确。 -4. **统计验证**: 刷新渠道管理页面,确认“注册用户数”是否+1。 diff --git a/.trae/documents/Fix Race Condition in Ichiban Strategy GrantReward.md b/.trae/documents/Fix Race Condition in Ichiban Strategy GrantReward.md deleted file mode 100644 index 8ff1a90..0000000 --- a/.trae/documents/Fix Race Condition in Ichiban Strategy GrantReward.md +++ /dev/null @@ -1,36 +0,0 @@ -# 修复一番赏策略库存扣减竞态问题 - -经检查,`internal/service/activity/strategy/ichiban.go` 中的 `GrantReward` 方法存在严重的竞态条件风险。当前实现采用“先查询库存,再更新库存”的方式,在高并发下可能导致超卖。 - -## 修复计划 - -我将参照 `default.go` 和 `reward_grant.go` 中的正确实现,对 `ichiban.go` 进行以下修改: - -1. **重构 `GrantReward` 方法** - * 移除原有的 `First()` 查询逻辑。 - * 改为使用**乐观锁**原子更新: - * 查询条件增加 `Quantity > 0`。 - * 使用 `UpdateSimple` 执行 `Quantity - 1`。 - * 根据 `RowsAffected` 判断扣减是否成功。 - -## 预期代码变更 - -```go -func (s *ichibanStrategy) GrantReward(ctx context.Context, userID int64, rewardID int64) error { - // 使用乐观锁原子扣减库存 - result, err := s.write.ActivityRewardSettings.WithContext(ctx).Where( - s.write.ActivityRewardSettings.ID.Eq(rewardID), - s.write.ActivityRewardSettings.Quantity.Gt(0), - ).UpdateSimple(s.write.ActivityRewardSettings.Quantity.Add(-1)) - - if err != nil { - return err - } - if result.RowsAffected == 0 { - return errors.New("sold out or reward not found") - } - return nil -} -``` - -此修改将彻底解决库存扣减的并发安全问题。 diff --git a/.trae/documents/Go 代码规范整改计划.md b/.trae/documents/Go 代码规范整改计划.md deleted file mode 100644 index 0511fce..0000000 --- a/.trae/documents/Go 代码规范整改计划.md +++ /dev/null @@ -1,40 +0,0 @@ -## 范围 -- API 层:补齐接口文档注释、统一 req/res 模式、限制为路由与参数控制 -- Service 层:迁移业务与数据访问、补齐函数级注释、对齐 activity_* 分层命名 - -## 工作项 -1. API 注释补全(Swagger) -- 为缺少注释的端点补充 `@Summary/@Description/@Tags/@Param/@Success/@Failure/@Router`: - - `internal/api/activity/draw_app.go:16 ExecuteDraw` - - `internal/api/admin/draw_simulate.go:36 SimulateIssueDraw` - - `internal/api/admin/issue_random_commit.go:21/48/80 Commit/Get/History` - - `internal/api/admin/draw_receipt.go:31/77 GetDrawReceipt/GetDrawReceiptByLogID` - - `internal/api/admin/verify_draw.go:34 VerifyDrawReceipt` - -2. API 限定职责 -- 将 API 中直接访问数据库的逻辑迁移到 Service 层: - - `internal/api/admin/draw_receipt.go` 查询收据改为调用 `activity.Service.GetDrawReceipt/GetDrawReceiptByLogID` -- 保持 API 仅做:参数绑定、鉴权、调用 service、返回 res - -3. 统一 req/res 代码风格 -- 统一使用 `req := new(XxxRequest)` / `res := new(XxxResponse)`,并在所有端点输出 `ctx.Payload(res)` -- 检查并修正个别端点的 `var req` 临时写法 - -4. Service 注释与接口规范 -- 为 `internal/service/activity/activity.go` 的 `Service` 接口所有方法添加函数级注释(功能、参数、返回值) -- 为 `internal/service/activity/*.go`(如 `draw_execute.go` 等)关键导出函数添加注释 -- 保持 `internal/service/user/*.go` 已有中文注释风格的一致性 - -5. 分层与命名对齐 -- 保持并补齐 `internal/api/activity/*` 与 `internal/service/activity/*` 的 `activity_*` 命名映射(CRUD、issue、rewards、draw、logs) -- 审核并调整少量不一致的文件/函数命名(如 helper 置于非 API 包或私有化) - -## 验证 -- 运行 Swagger 生成与本地文档预览,检查所有路由均有完整注释 -- 运行单元测试/集成冒烟:抽奖、承诺、收据查询、奖励发放 -- 代码静态检查:确保 API 无直接 DAO 访问、Service 注释完整 - -## 交付物 -- 已修正的 API 与 Service 源码 -- 通过的 Swagger 文档与接口列表 -- 简要测试记录(通过用例与关键接口返回示例) diff --git a/.trae/documents/Implement Backpack API for App Users.md b/.trae/documents/Implement Backpack API for App Users.md deleted file mode 100644 index 1aa6576..0000000 --- a/.trae/documents/Implement Backpack API for App Users.md +++ /dev/null @@ -1,73 +0,0 @@ -## 概述 -为 APP 端提供“用户背包”能力,覆盖用户资产的六类数据:积分消费明细、优惠券、道具卡、头衔、订单、中奖资产(背包)。在保留现有分项查询接口的基础上,新增缺失的 APP 端接口与一个聚合概要接口,统一鉴权与分页规范,并更新 API 文档。 - -## 新增接口 -- GET `/api/app/users/{user_id}/inventory` - - 功能:用户背包列表(中奖资产),按时间倒序,支持分页 - - 返回:`InventoryWithProduct` 列表(含商品名称、图片、等级/备注、关联活动/奖励ID) - - 鉴权:`LoginVerifyToken` -- GET `/api/app/users/{user_id}/titles` - - 功能:用户头衔列表,支持分页 - - 返回:`UserTitleItem` 列表(称号ID、名称、来源/获得时间、可选效果) - - 鉴权:`LoginVerifyToken` -- GET `/api/app/users/{user_id}/backpack` - - 功能:聚合概要接口,返回六类资产的“计数+最近N条”,用于首页/概览展示 - - 查询参数:`limit=5`(各类最近条数),`include=points,coupons,item_cards,titles,orders,inventory`(可选,默认全包含) - - 返回: - - `points: {count, recent: UserPointsLedger[]}` - - `coupons: {count, recent: CouponItem[]}` - - `item_cards: {count, recent: ItemCardWithTemplate[]}` - - `titles: {count, recent: UserTitleItem[]}` - - `orders: {count, recent: Orders[]}` - - `inventory: {count, recent: InventoryWithProduct[]}` - - 鉴权:`LoginVerifyToken` - -## 复用现有 APP 接口(保持不变) -- 积分明细:GET `/api/app/users/{user_id}/points`(已存在) -- 积分余额:GET `/api/app/users/{user_id}/points/balance`(已存在) -- 优惠券:GET `/api/app/users/{user_id}/coupons`(已存在) -- 道具卡:GET `/api/app/users/{user_id}/item_cards`、GET `/api/app/users/{user_id}/item_cards/uses`(已存在) -- 订单:GET `/api/app/users/{user_id}/orders`(已存在) - -## 数据模型(响应结构) -- `InventoryWithProduct`:`{ id, product_id, title, images[], activity_id, reward_id, order_id, level, created_at }` -- `UserTitleItem`:`{ user_title_id, title_id, title_name, acquired_at, effects?: [{key, value, unit}], expires_at? }` -- `CouponItem`(沿用现有):`{ id, name, amount, valid_start, valid_end, status, rules }` -- `ItemCardWithTemplate`(沿用现有):含模板字段与剩余次数/有效期 -- `Orders`(沿用现有):按现有模型返回 -- `UserPointsLedger`(沿用现有):按现有模型返回 - -## 技术实现 -- 目录与文件: - - `internal/api/user/inventory_app.go`:`ListUserInventoryForApp()`(调用 `usersvc.ListInventoryWithProduct`) - - `internal/api/user/titles_app.go`:`ListUserTitlesForApp()`(新增 usersvc 方法或复用 admin 服务逻辑) - - `internal/api/user/backpack_app.go`:`GetUserBackpackOverview()`(各服务查询计数与最近N条) -- 路由:`internal/router/router.go` - - APP 认证组注册:`/users/:user_id/inventory`、`/users/:user_id/titles`、`/users/:user_id/backpack` -- Service 层:`internal/service/user` - - `ListInventoryWithProduct(ctx, userID, page, pageSize)`(已有) - - `ListUserTitles(ctx, userID, page, pageSize)`(新增):查询用户称号关联与系统称号名称/效果 - - 计数方法:各类 `CountXxx(ctx, userID)`(供概要接口聚合) - -## 鉴权与约束 -- 所有接口使用 `LoginVerifyToken` -- `user_id` 为路径参数,但服务端以会话 `SessionUserInfo().Id` 校验一致性,防越权 -- 分页统一:`page`、`page_size`(默认 1/20,最大 100),按 `created_at` 倒序 - -## Swagger 文档 -- 为新增 3 个接口添加注解:`@Summary`、`@Description`、`@Tags APP端.用户`、`@Security LoginVerifyToken`、`@Param`、`@Success`/`@Failure`、`@Router` -- 模型定义共用已有结构;为新结构 `UserTitleItem`、`InventoryWithProduct` 增加定义 -- 执行脚本生成文档:`scripts/swagger.sh` - -## 测试与验收 -- 单元/集成: - - 伪造用户会话,验证分页与鉴权;数据空集场景返回空数组 - - 概要接口在 `include` 过滤时仅返回所选分组 -- 手工验证: - - 分别对六类接口 `curl` 测试分页与计数一致性 - - Swagger 文档能正确展示并可试用 - -## 验收标准 -- 三个新增接口可用且鉴权正确;分页与计数正确 -- 概要接口耗时可控(<200ms 在百条内数据量);可通过 `include` 控制 -- 文档完整,前端对接字段明确;无越权与泄露 diff --git a/.trae/documents/Lottery Simulation Feature Implementation Plan.md b/.trae/documents/Lottery Simulation Feature Implementation Plan.md deleted file mode 100644 index 5596098..0000000 --- a/.trae/documents/Lottery Simulation Feature Implementation Plan.md +++ /dev/null @@ -1,53 +0,0 @@ -# 添加抽奖模拟功能 (Lottery Simulation) - -我将实现一个抽奖模拟功能,允许管理员在后台模拟抽奖过程并分析概率分布,**全过程仅在内存中进行,不会修改数据库中的真实数据**。 - -## 1. API 接口设计 -**接口地址**: `POST /api/admin/lottery/issues/:issue_id/simulate` - -**请求参数 (Body)**: -```json -{ - "num_users": 100, // 模拟人数 - "draws_per_user": 1 // 每人抽奖次数 (总抽奖次数 = 人数 * 次数) -} -``` - -**返回结果**: -```json -{ - "total_draws": 100, // 总模拟次数 - "rewards": [ - { - "reward_id": 1, - "name": "IPhone 15", - "level": 1, // 奖品等级 - "original_qty": 10, // 初始库存 - "won_count": 2, // 模拟中奖数 - "remaining_qty": 8,// 模拟剩余库存 - "actual_prob": 0.02, // 实际中奖率 (2%) - "theoretical_prob": 0.01 // 理论概率 (基于权重的 1%) - } - ] -} -``` - -## 2. 实现细节 - -### A. 新增处理器 `internal/api/admin/lottery_admin.go` -我将添加 `SimulateIssue` 函数,执行以下逻辑: -1. **读取配置**: 从数据库获取指定期号 (Issue) 和奖品配置 (Reward Settings)。 -2. **内存模拟**: - - 在内存中创建奖品库存的副本。 - - 运行加权随机算法 `N` 次(总抽奖次数)。 - - 扣减内存中的库存并记录中奖数据。 - - **关键**: 绝对不会写入数据库或修改真实库存。 -3. **统计计算**: - - 计算每个奖品的实际中奖率与基于权重的理论概率进行对比。 - -### B. 注册路由 `internal/router/router.go` -- 在 `adminAuthApiRouter` 分组下添加新路由。 -- `POST /lottery/issues/:issue_id/simulate` -> `adminHandler.SimulateIssue()` - -## 3. 验证计划 -- **功能验证**: 使用现有的活动/期号调用模拟接口,输入不同的人数和次数,验证返回的概率分布是否符合预期(例如:高权重奖品中奖率更高,库存耗尽后不再中奖)。 \ No newline at end of file diff --git a/.trae/documents/Troubleshoot and Fix Virtual Shipping, Refunds, and Item_Coupon Issues.md b/.trae/documents/Troubleshoot and Fix Virtual Shipping, Refunds, and Item_Coupon Issues.md deleted file mode 100644 index c8e7bfd..0000000 --- a/.trae/documents/Troubleshoot and Fix Virtual Shipping, Refunds, and Item_Coupon Issues.md +++ /dev/null @@ -1,27 +0,0 @@ -# 故障排查与修复计划 - -针对您提出的四个问题,我制定了以下修复计划。我将使用中文进行回答和执行。 - -## 1. 虚拟发货问题 (Virtual Shipment) -* **现状**: 后端代码中已存在 `UploadVirtualShipping` 调用,但在支付回调中如果上传失败,目前是忽略错误的(`_ = ...`),导致无法确定是否成功。 -* **修复**: 修改 `internal/api/pay/wechat_notify.go`,增加错误日志记录。如果虚拟发货上传失败,将在后台日志中打印详细错误原因,方便排查(如 `transaction_id` 错误或配置问题)。 - -## 2. 订单管理与退款 (Refund Button) -* **现状**: 您提到的“履约”可能是指发货,但对于虚拟商品,通常需要的是“退款”。目前后台订单列表缺少直观的“退款”按钮。 -* **修复**: - * **前端**: 在 `web/admin/src/views/pay/orders/index.vue` 中添加“退款”按钮。 - * **后端**: 对接现有的 `CreateRefund` 接口。该接口已包含完整的逻辑:调用微信退款、更新订单状态、**自动恢复用户积分**和**自动恢复优惠券**。 - -## 3. 道具卡添加不生效 (Item Card Issue) -* **原因**: 经查代码 `internal/service/user/item_card_add.go`,当道具卡模板不存在或状态为“未启用”时,代码直接返回 `nil` (成功),但实际上没有给用户添加卡片。这就是“不生效”且无报错的原因。 -* **修复**: 修改服务层代码,当道具卡不可用时,明确返回错误(如 `errors.New("item card not found or disabled")`),让接口报错,提示您具体原因。 - -## 4. 优惠券添加不生效 (Coupon Issue) -* **原因**: 同上,`internal/service/user/coupon_add.go` 在优惠券模板未启用时也静默返回成功。 -* **修复**: 修改服务层代码,当优惠券不可用时,明确返回错误,确保您能看到操作失败的提示。 - -## 执行步骤 -1. **后端**: 修复道具卡和优惠券的“静默失败”逻辑,使其报错。 -2. **后端**: 在支付回调中增加虚拟发货的错误日志。 -3. **前端**: 在订单管理页面增加“退款”按钮并对接接口。 -4. **验证**: 通过 curl 或界面操作验证修复效果。 diff --git a/.trae/documents/一番赏活动创建表单逻辑优化计划.md b/.trae/documents/一番赏活动创建表单逻辑优化计划.md deleted file mode 100644 index b6c7a6e..0000000 --- a/.trae/documents/一番赏活动创建表单逻辑优化计划.md +++ /dev/null @@ -1,54 +0,0 @@ -# 一番赏活动创建表单优化计划 - -用户反馈在创建一番赏活动时,选择即时/定时开奖的页面逻辑不对。通过分析代码 `web/admin/src/views/activity/wizard/index.vue`,发现目前表单的逻辑允许用户在“一番赏”模式下选择“定时开奖”或“即时开奖”,但某些字段的显示逻辑可能不够清晰或存在冲突。一番赏(Ichiban Kuji)通常有其特定的开奖规则,需要对表单进行以下优化。 - -## 1. 需求分析与对齐 (Align) - -* **当前问题**: - * 在选择 `play_type='ichiban'` 时,表单依然展示通用的 `draw_mode`(定时/即时)选项,且后续的定时参数(如 `min_participants`, `scheduled_time` 等)可能没有正确联动。 - * 一番赏的核心逻辑是“即买即开”(即时开奖),但也可能存在“定时开奖”的特殊玩法(如凑够多少人开一箱)。需要明确一番赏是否支持所有开奖模式。 - * **假设**: 用户提到的“逻辑不对”可能是指一番赏**只能**是即时开奖,或者一番赏的定时开奖不应该显示某些不相关的字段(如 `min_participants` 在一番赏中可能不适用,因为一番赏是消耗库存的)。 - * **修正策略**: - * 当选择“一番赏”时,默认锁定或限制 `draw_mode` 的选项。 - * 如果是“一番赏”,通常是**即时开奖**(用户购买后立即知道结果)。 - * 如果一番赏确实支持定时(例如:全员购买完后统一开奖),则需要明确字段。 - * **根据用户语境“即时 定时的 页面逻辑不对”**,推测可能是:切换一番赏时,应该自动处理 `draw_mode`,或者某些字段不该显示。 - -* **优化方案**: - 1. **联动逻辑**: 当 `play_type` 切换为 `ichiban` 时,强制或默认 `draw_mode` 为 `instant`(即时开奖),并禁用 `scheduled`(除非业务明确支持一番赏定时)。 - 2. **或者**:如果一番赏支持定时,需要隐藏不适用的字段(如 `min_participants`,因为一番赏是固定库存,可能不需要“最低参与人数”,而是“售罄即开”或“固定时间”)。 - 3. **UI 调整**: 简化一番赏模式下的表单,隐藏不必要的复杂定时配置。 - - **经确认(自我推演)**: 一番赏的典型玩法是用户购买后立即获得确定的赏品(即时)。但也存在“整箱全开”的定时玩法。但最常见的问题是:**一番赏不应该显示“最低参与人数”等凑单逻辑,因为它基于固定数量的签**。 - - **决定**: - * 当 `play_type === 'ichiban'` 时: - * **锁定** `draw_mode` 为 `instant`(即时开奖),并**隐藏**开奖模式选择框(或禁用)。 - * **隐藏**所有定时相关的配置(`min_participants`, `time_mode`, `interval_minutes` 等)。 - * 这样能避免用户配置出“定时开奖的一番赏”这种非典型且可能逻辑冲突的活动(除非后端明确支持)。 - -## 2. 架构设计 (Architect) - -* **前端逻辑调整 (`wizard/index.vue`)**: - * 监听 `scheduledForm.play_type` 的变化。 - * 如果 `play_type === 'ichiban'`: - * 自动设置 `scheduledForm.draw_mode = 'instant'`。 - * `scheduledForm.min_participants = 0`。 - * `scheduledForm.interval_minutes = 0`。 - * 在模板中,使用 `v-if` 控制字段显示: - * 如果 `play_type === 'ichiban'`,隐藏 `draw_mode` 选择框(或者显示为只读的“即时开奖”)。 - * 隐藏所有 `scheduled` 相关的输入框。 - -## 3. 任务拆解 (Atomize) - -1. **Modify Frontend (`wizard/index.vue`)**: - * 添加 `watch` 监听 `scheduledForm.play_type`。 - * 修改模板中的 `v-if` 条件,针对 `ichiban` 隐藏不必要的字段。 - * 或者直接在 `play_type` 选择器下方添加提示:“一番赏模式下默认为即时开奖”。 - -## 4. 执行步骤 (Automate) - -1. **Step 1**: 修改 `web/admin/src/views/activity/wizard/index.vue`。 - * 在 `script setup` 中添加 `watch` 逻辑。 - * 调整 `template` 中的表单项可见性。 - diff --git a/.trae/documents/代码结构梳理与API文档整理.md b/.trae/documents/代码结构梳理与API文档整理.md deleted file mode 100644 index df1458a..0000000 --- a/.trae/documents/代码结构梳理与API文档整理.md +++ /dev/null @@ -1,77 +0,0 @@ -## 背景与现状概要 -- 技术栈:后端 Go(Gin 封装于 `internal/pkg/core`)、GORM、Viper、Zap、Swagger;前端 Vue3 + Vite + TypeScript + Pinia + Element Plus。 -- 核心入口:`main.go`(服务启动与任务调度);路由中心:`internal/router/router.go`;拦截与鉴权:`internal/router/interceptor/*`。 -- 业务分层:处理器 `internal/api/{admin,activity,app,user,pay,common}`;服务 `internal/service/**`;数据访问 `internal/repository/mysql/{dao,model}`。 -- 前端管理后台:`web/admin/src/**`,接口定义集中在 `web/admin/src/api/**`。 - -## 目标与交付物 -- 代码结构梳理报告:目录分层说明 + 模块职责 + 依赖关系图(Mermaid)。 -- 无用代码清理:列出候选清理清单、逐项验证、提交可回滚的变更方案。 -- API 文档(前后端): - - 后端 REST 端点清单(方法、路径、鉴权、处理器、请求/响应示例、错误码、分页约定)。 - - 前端调用约定(基地址、拦截器、Token 注入、模块函数到后端端点的映射)。 -- 更新与补充项目根文档:`docs/说明文档.md`(规划、实施方案、进度记录)。 - -## 实施步骤 -### 1. 架构梳理(Align/Architect) -- 枚举代码目录:识别后端、前端、配置、构建、脚本、生成器与产物目录。 -- 生成架构图:描绘后端分层(Router → Interceptor → API → Service → Repo)、前端分层(视图 → Store → API)。 -- 输出《代码结构总览.md》:说明关键路径与职责、跨模块依赖、构建/部署要点。 - -### 2. 路由与端点收敛(Atomize) -- 解析 `internal/router/router.go` 的路由注册,枚举所有端点(含分组与中间件)。 -- 关联处理器方法(如 `admin.*`、`activity.*` 等),抽取鉴权要求:`AdminTokenAuthVerify`、`AppTokenAuthVerify`、RBAC `RequireAdminAction`。 -- 标准化约定:分页键(`page`、`page_size`)、通用响应包(`code`、`message`、`data`、`request_id`)、错误码体系。 - -### 3. API 文档编制 -- 后端文档:生成《API文档-后端.md》 - - 列表:方法、路径、处理器、鉴权、中间件、请求参数、响应示例、错误码、注意事项。 - - 支付/回调、系统健康、上传等特殊端点单独章节。 -- 前端文档:生成《API文档-前端.md》 - - 说明 Axios 基础配置(BaseURL、超时、拦截器、Token 注入)。 - - 列出 `web/admin/src/api/**` 模块函数到后端端点映射、入参/出参、调用示例。 - -### 4. 无用代码清理策略 -- 判定规则: - - 未被任何文件 `import`/调用; - - 未在路由或启动流程中引用; - - 构建产物(如 `build/resources/admin/**`)、运行日志(`logs/**`); - - 演示/测试脚本(如 `miniapp/pay-test/**`)、一次性生成器产物。 -- 候选清单(初版): - - `internal/metrics/**`(若未启用 Prometheus); - - `internal/repository/mysql/testrepo_sqlite.go`(未检索到引用); - - `cmd/**`(工具/生成器,保留或迁移到 dev-only); - - `scripts/swagger.*`(构建脚本,非运行时); - - `build/resources/admin/**`、`logs/**`(产物与输出)。 -- 清理流程: - - 逐项交叉检索引用关系 → 标注“安全删除”/“需保留”; - - 对可能未来使用的模块改为禁用配置或注释式保留,避免功能回退风险; - - 产物与日志转移到忽略或发布流程之外(完善 `.gitignore` 与构建管线)。 - -### 5. 文档与规范同步 -- 更新 `docs/说明文档.md`:规划、实施方案、节点记录(按用户规范)。 -- 在 `docs/api/` 目录落地《代码结构总览.md》《API文档-后端.md》《API文档-前端.md》。 -- 所有函数在新增代码中补充函数级注释(功能、参数、返回值)。 - -### 6. 验收与验证(Assess) -- 后端:`go build`、路由完整性检查、Swagger 校验(非生产)、关键端点手测。 -- 前端:`vite build`、ESLint/Stylelint、页面 API 调用冒烟测试。 -- 部署:非生产环境验证 PProf、CORS、静态资源路由回退;检查 `.env` 与证书安全。 - -## 输出物清单 -- `docs/api/代码结构总览.md`(含架构图) -- `docs/api/API文档-后端.md`(REST 列表与约定) -- `docs/api/API文档-前端.md`(调用契约与示例) -- 可回滚的清理变更(提交前附清单与影响评估) - -## 依赖与约束 -- 保留生成器/工具目录(`cmd/**`)除非确认迁移; -- 配置与证书不改动业务值,仅完善文档与忽略策略; -- 如需补充 Swagger 注解,遵循现有 `swaggo` 用法并保持最小侵入。 - -## 下一步 -- 确认本计划后: - 1) 输出架构梳理文档; - 2) 生成端点清单并编制前后端 API 文档; - 3) 提交清理候选与验证报告,执行安全清理。 -- 如需额外约定(错误码字典、分页/排序统一规范、RBAC 角色映射),我将在文档中补充并与现有实现对齐。 \ No newline at end of file diff --git a/.trae/documents/优化“新用户”和“实时抽奖”接口性能.md b/.trae/documents/优化“新用户”和“实时抽奖”接口性能.md deleted file mode 100644 index d26d058..0000000 --- a/.trae/documents/优化“新用户”和“实时抽奖”接口性能.md +++ /dev/null @@ -1,52 +0,0 @@ -## 痛点定位 -- 新用户接口在单次请求内对每个用户做多次单表查询(资产、道具卡、优惠券、称号、最后在线时间),形成 N+1 查询,延迟随列表大小线性增加(`internal/api/admin/dashboard_admin.go` 的 `DashboardNewUsers()`)。 -- 实时抽奖动态接口对每条抽奖日志逐条联查用户/期/活动/奖品,亦为 N+1 查询(`DashboardDrawStream()`)。 - -## 后端优化方案 -- 批量聚合替代逐条查询(NewUsers) - - 第一步:一次查出当前页的 `user_id` 列表 - - 第二步:分别对各表做分组聚合(单次查询): - - 资产:`SELECT user_id, COUNT(*) FROM user_inventory WHERE user_id IN (...) GROUP BY user_id` - - 道具卡:`SELECT user_id, COUNT(*) FROM user_item_cards WHERE status=1 AND user_id IN (...) GROUP BY user_id` - - 优惠券:`SELECT user_id, COUNT(*) FROM user_coupons WHERE user_id IN (...) GROUP BY user_id` - - 称号:`SELECT ut.user_id, st.id, st.name FROM user_titles ut LEFT JOIN system_titles st ON st.id=ut.title_id WHERE ut.user_id IN (...)` - - 最后在线:分别取各行为表 `MAX(time)` 按 `user_id` 聚合,再在内存求最大值 - - 积分余额:改为批量查 `user_points` 有效积分 `GROUP BY user_id`,或接入预聚合表(见下) - - 第三步:用 `map[user_id]value` 合并到用户列表,避免每行多次往返数据库 -- 连接查询替代逐条补全(DrawStream) - - 单条 SQL 联查:`activity_draw_logs` LEFT JOIN `users`、`activity_issues`、`activities`、`activity_reward_settings`,一次性返回 `nickname/activityName/issueNumber/prizeName` - - 保留 `since_id + limit` 增量拉取;避免循环内 `First()` 调用 -- 预聚合与缓存 - - 建议增加 `user_stats` 表(或 Redis 缓存)维护:`points_balance`、`inventory_count`、`item_card_count`、`coupon_count`、`title_list`、`last_online_at` - - 更新策略: - - 同步:在相关写入路径(发放积分/道具卡/优惠券/称号、抽奖、下单)更新统计 - - 异步:crontab 每 1-5 分钟增量刷新 - - 实时抽奖:为最近 50 条结果加 3-5 秒内存缓存(LRU 或 Redis) -- 限流与分页 - - 新用户默认 `page_size=20`,最大 50;实时抽奖 `limit<=100` - - 对于“今年”范围下分页检索控制页大小,避免一次返回过多用户 - -## 数据库与索引 -- 新建/确认索引: - - `users(created_at)`、`users(id)` - - `activity_draw_logs(id DESC, user_id, issue_id, reward_id, created_at)` - - `user_inventory(user_id)`、`user_item_cards(user_id,status)`、`user_coupons(user_id)`、`user_titles(user_id,title_id)` - - `user_points(user_id, valid_end)`、`user_points_ledger(user_id, created_at)` -- 可选:为 `log_request(path, created_at)` 增加 `user_id` 字段与索引,精确“最后在线时间” - -## 前端协同优化 -- 新用户页签切换时:防抖 200ms;保留上次结果并显示加载骨架,避免空白闪烁 -- 实时抽奖轮询:保持 5s;追加条目后裁剪到 100-200 条以保证 DOM 轻量;使用 `ref` 持有列表(已改) -- 宽度问题:动态项允许换行并分两行展示(已改),避免不可见 - -## 验收指标 -- 新用户接口:在 `page_size=20` 时 P95 响应时间 < 200ms(本地数据量下) -- 实时抽奖接口:在 `limit=50` 时 P95 响应时间 < 150ms;每轮轮询端到端显示时间 < 300ms - -## 下一步实现内容(获批后执行) -1) 重写 `DashboardNewUsers()` 为批量聚合与合并映射 -2) 重写 `DashboardDrawStream()` 为单次 LEFT JOIN 联查 -3) 添加必要索引迁移脚本 -4) 可选:落地 `user_stats` 预聚合与写路径刷新机制 - -确认后我将按上述方案逐条落地并提供压测数据与对比报告。 \ No newline at end of file diff --git a/.trae/documents/优化双倍卡配置与作用范围选择的实施计划.md b/.trae/documents/优化双倍卡配置与作用范围选择的实施计划.md deleted file mode 100644 index 0921714..0000000 --- a/.trae/documents/优化双倍卡配置与作用范围选择的实施计划.md +++ /dev/null @@ -1,37 +0,0 @@ -## 使用指导(立即可用) -- 叠加策略(stacking_strategy) - - 对“双倍概率”(effect_type=6)当前不生效;代码仅按“概率累加+统一封顶”处理(internal/service/activity/draw_with_effects.go:133-149)。 - - 只有一个双倍效果时,保持默认即可(推荐填 1“累加封顶”或 0“最大值”,不影响现状)。 -- 统一封顶(cap_value_x1000) - - 约束“多个双倍效果合并后的总概率”的上限(单位千分)。 - - 只有一个效果:填 0(不封顶)或填业务上限(如 1000=100%)。 - - 多个效果:按业务上限填写,如最多 30% 则填 300(避免叠加超过 30%)。 - -## 作用范围简化(改造目标) -- 将前端“作用范围”改为: - - 下拉选择“活动 activity_ids”(多选) - - 依赖选择“期 issue_ids”(多选,基于已选活动加载期) - - 可选“排除期 exclude.issue_ids”(多选) -- 不再要求手写 ID,不展示分类/时间等无关选项。 - -## 前端改造 -- EffectEditDialog.vue - - 为 effect_type=6 添加三组下拉:活动、期、排除期。 - - 提交时构造简化版 scopes_json:`{ activity_ids, issue_ids, exclude: { issue_ids } }`。 - - 在 UI 上为 stacking_strategy/cap_value_x1000 提供简短说明(不改后端字段)。 -- EffectManagerDialog.vue - - 在列表新增“作用范围”列,展示活动/期/排除期标签(已完成范围列,可保留)。 - -## 数据来源与接口(不改后端) -- 活动/期下拉数据: - - 如果已有活动与期的接口:直接请求填充; - - 如暂无接口:先用静态选项或从现有管理页可获取的列表填充,下轮再对接接口。 -- 不改后端:效果保存仍通过 `admin/system_titles/:title_id/effects`,携带 `params_json` 与简化 `scopes_json`。 - -## 验收 -- 在管理端为“双倍概率”新增/编辑效果: - - 可用下拉选择活动与期,不用手写; - - 保存后列表显示范围标签; - - 抽奖按选定的期过滤生效(draw_with_effects.go:85-103)。 - -确认后我将实施前端下拉改造与表单说明文本,并保持后端兼容。 \ No newline at end of file diff --git a/.trae/documents/优化订单转化漏斗指标计算.md b/.trae/documents/优化订单转化漏斗指标计算.md deleted file mode 100644 index bc1ea3a..0000000 --- a/.trae/documents/优化订单转化漏斗指标计算.md +++ /dev/null @@ -1,20 +0,0 @@ -## 问题 -现有漏斗将“主要流失环节”选在“完成订单”,因为完成率为0%。但该模块目标是识别支付瓶颈,主要应关注“访问→下单”“下单→支付”,履约(完成订单)不应参与支付瓶颈判断;整体转化率也应以支付为准。 - -## 调整方案(前端) -- 文件:`web/admin/src/views/dashboard/console/modules/order-funnel.vue` -- 指标计算: - - **整体转化率**改为:`payments / visitors * 100`;新增“履约完成率”可选展示 `completions / payments * 100` - - **主要流失环节(支付链路)**只在两段中比较: - - `访问→下单` 流失率 = `1 - orders / visitors` - - `下单→支付` 流失率 = `1 - payments / orders` - - **主要流失率**对应上述最大值(保留一位小数) -- 现有四阶段显示不变;“完成订单”的比率与流失仅用于展示,不参与“支付瓶颈”指标 - -## 验证 -- 数据示例:访问723,下单10,支付10,完成0 - - 支付整体转化率:`10/723≈1.38%` - - 两段流失率:`访问→下单≈98.6%`,`下单→支付=0%` → 主要流失环节:下单用户 - - 履约完成率:`0/10=0%`(可作为履约提示,不影响支付瓶颈判断) - -确认后我将直接修改该组件的计算逻辑并构建验证。 \ No newline at end of file diff --git a/.trae/documents/修复 Admin 前端构建 TypeScript 错误.md b/.trae/documents/修复 Admin 前端构建 TypeScript 错误.md deleted file mode 100644 index 61487e1..0000000 --- a/.trae/documents/修复 Admin 前端构建 TypeScript 错误.md +++ /dev/null @@ -1,53 +0,0 @@ -## 问题概览 -- 缺失模块:`@/mock/temp/commentDetail` 导致 TS2307(src/components/business/comment-widget/index.vue:46)。 -- 事件类型不匹配:`emit('search', ...)` 未在 `defineEmits` 中声明载荷(activity-search、player-search)。 -- 缺少类型声明:`crypto-js/md5` 导致 TS7016(login)。 -- 返回类型不一致:登录接口不含 `refreshToken` 导致 TS2339(login)。 -- 表格数据类型为 `unknown[]` 与组件期望 `Record[]` 不匹配(guild、banner、product)。 - -## 修复方案(不改接口协议,最小改动) -- 缺失模块补齐:新增 `web/admin/src/mock/temp/commentDetail.ts`,导出 `Comment` 类型与 `commentList: Ref`。 - - 使 `src/components/business/comment-widget/index.vue:46` 的导入可解析。 -- 事件类型声明完善:在以下文件的 `defineEmits` 中为 `search` 声明载荷类型 `Props['modelValue']`: - - `src/views/activity/manage/modules/activity-search.vue:93-101,148-151` - - `src/views/player-manage/modules/player-search.vue:82-90,136-139` -- 为 `crypto-js/md5` 提供类型:新增 `web/admin/src/types/shims-crypto-js.d.ts` 内容:`declare module 'crypto-js/md5';` - - 保证 `src/views/auth/login/index.vue:117` 类型可解析。 -- 登录返回类型与使用一致化:修改登录页不解构 `refreshToken`,仅解构 `token` 并调用 `userStore.setToken(token)`: - - `src/views/auth/login/index.vue:223-236`;当前后端返回结构为 `{ token, is_super }`(参考后端 `internal/service/admin/login.go:22-25,62-63` 与前端 `src/api/auth.ts:8-14`)。 -- 表格数据类型注解:在各视图的 `apiFn` 显式标注返回类型为 `Promise>`,使 `useTable` 能推导出 `TRecord`,从而 `data` 类型为记录数组: - - Guild 列表:`src/views/guild/manage/index.vue:137-173`,返回 `PaginatedResponse<{ id; name; owner_id; ... }>`。 - - Banner 列表:`src/views/operations/banner/index.vue:77-98`,返回 `PaginatedResponse`(`src/api/banner.ts:3-10`)。 - - Product 分类:`src/views/product/categories/index.vue:80-102`,返回 `PaginatedResponse<{ id; name; parent_id; status }>`。 - - Product 列表:`src/views/product/list/index.vue`(同样模式)。 - - 若不方便标注,临时方案:在模板传参处显式断言 `:data="data as Record[]"`(不推荐,先按注解方案处理)。 - -## 具体改动明细 -- 新增 `src/mock/temp/commentDetail.ts`: - - 导出 `export interface Comment { id:number; author:string; content:string; timestamp:string; replies: Comment[] }` - - `export const commentList = ref([])`(保持与组件 `comments.value.push(...)` 一致)。 -- 更新事件声明: - - `activity-search.vue` `defineEmits()` 中将 `(e: 'search'): void` 改为 `(e: 'search', value: Props['modelValue']): void`。 - - `player-search.vue` 同步调整。 -- 新增类型声明文件:`src/types/shims-crypto-js.d.ts` 写入 `declare module 'crypto-js/md5';`。 -- 登录页使用调整: - - 改为 `const { token } = await fetchLogin(...)`,并调用 `userStore.setToken(token)`。 -- useTable 返回类型注解:在上述 4 个视图 `apiFn` 的返回 `then` 中显式声明返回值类型为 `Api.Common.PaginatedResponse<...>`。 - -## 验证步骤 -- 安装依赖后(若需要):`npm i --save-dev @types/crypto-js`(可省略,因已加 shims)。 -- 执行 `npm run build`,expect:无 TS 错误;资源正常打包。 -- 运行本地:`npm run dev`,检查各页面: - - 评论组件能正常发布与回复。 - - 搜索模块能正常触发,父组件收到带载荷的 `search` 事件。 - - 登录页能正常登录并存储 `token`。 - - Guild、Banner、Product 列表渲染正常,分页操作无类型报错。 - -## 影响面与风险 -- 均为类型与前端视图层改动,不涉及后端接口或数据结构变更。 -- `crypto-js` 类型通过 shims 解决,不强依赖 `@types` 包。 -- `useTable` 类型注解只提升类型推导,不改变运行时行为。 - -## 后续优化建议 -- 统一在 `api/*` 层将后端分页响应封装为 `Api.Common.PaginatedResponse`,视图层的 `apiFn` 直接返回该类型,减少重复 `then` 转换与类型注解。 -- 为常见搜索子组件抽取 `Emits` 模板,避免事件签名遗漏。 \ No newline at end of file diff --git a/.trae/documents/修复 Vue watcher 中“resetForm 未初始化”错误的改造方案.md b/.trae/documents/修复 Vue watcher 中“resetForm 未初始化”错误的改造方案.md deleted file mode 100644 index 70f9164..0000000 --- a/.trae/documents/修复 Vue watcher 中“resetForm 未初始化”错误的改造方案.md +++ /dev/null @@ -1,34 +0,0 @@ -## 问题分析 -- 组件 `coupon-dialog.vue` 在 `setup` 中使用了: - ```ts - watch(() => props.data, (newData) => { ... else { resetForm() } }, { immediate: true }) - ``` -- `resetForm` 在源码中以 `const resetForm = () => { ... }` 形式定义在 `watch` 之后。由于 `immediate: true` 会在初始化阶段立即执行回调,导致在 `resetForm` 尚未初始化之前被调用,抛出 `ReferenceError: Cannot access 'resetForm' before initialization`。 - -## 修复方案 -1) 将 `resetForm` 改为函数声明并上移到 `watch` 之前: -```ts -function resetForm() { - form.value = { name:'', coupon_type:1, discount_type:1, discount_value:0, valid_days:30, status:1, remark:'' } -} -``` -- 函数声明具备提升(hoisting),不会出现“未初始化”问题。 -- 位置调整到 `rules` 与 `dialogTitle` 定义之后、`watch(props.data, ...)` 之前。 - -2) 保留 `immediate: true`(业务期望首次打开时即填充/重置),无需改动;如需更稳健可补充 `flush: 'post'`,但此处核心是函数提升顺序问题。 - -3) 可选强化 -- 如同时监听 `visible`,建议统一在 `watch` 中使用已声明的 `resetForm` 与 `fillFormFromProps(newData)` 两个函数,避免将逻辑直接写在回调内。 -- 若依赖 `formRef.resetFields()` 的时机,考虑在 `handleClose` 使用 `nextTick` 保证 DOM 与表单实例可用。 - -## 变更清单 -- 文件:`web/admin/src/views/operations/coupons/modules/coupon-dialog.vue` -- 改动: - - 将 `resetForm` 改为函数声明并移动到 `watch` 之前。 - - (可选)在 `watch` 的第三参数加入 `flush: 'post'`。 - -## 验收 -- 打开/切换数据源时不再出现 `ReferenceError`。 -- 创建/编辑模式下首次加载和重置行为保持一致(`immediate: true` 生效)。 - -确认后我将按上述方案修改代码并验证。 \ No newline at end of file diff --git a/.trae/documents/修复 Windows 打包后时区错误(unknown time zone Asia_Shanghai).md b/.trae/documents/修复 Windows 打包后时区错误(unknown time zone Asia_Shanghai).md deleted file mode 100644 index ef58d90..0000000 --- a/.trae/documents/修复 Windows 打包后时区错误(unknown time zone Asia_Shanghai).md +++ /dev/null @@ -1,34 +0,0 @@ -**问题定位** -- 触发点在 `internal/pkg/timeutil/timeutil.go:13-22`:`time.LoadLocation("Asia/Shanghai")` 在 `init` 中执行,失败则 `panic`,并将 `time.Local` 设为中国时区。 -- 在 Windows 无系统 IANA tzdata、且以 `CGO_ENABLED=0` 交叉编译的静态二进制中,`time` 包找不到 `Asia/Shanghai`,导致启动时报错。 - -**解决方案(按优先级)** -- 方案A(推荐):嵌入 tzdata 到二进制 - - 在应用入口 `main.go` 增加空导入:`_ "time/tzdata"`,使 `time` 包使用内嵌 IANA 数据库。 - - 或在打包时添加构建标签:`-tags timetzdata`(效果等同),无需改代码。 -- 方案B(无代码变更):随程序分发 `zoneinfo.zip` - - 复制 `$GOROOT/lib/time/zoneinfo.zip` 到可执行文件同目录,运行前设置环境变量 `ZONEINFO` 指向该文件,例如:`set ZONEINFO=%CD%\zoneinfo.zip`。 -- 方案C(防御补充):去除 `panic` 并提供容错 - - 在 `timeutil` 初始化失败时,回退到固定东八区:`time.FixedZone("CST", 8*3600)`,并设置 `time.Local`,避免启动失败。 - -**打包命令更新** -- 将 README 中 Windows 打包命令更新为: - - `CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags "-s -w" -tags timetzdata -trimpath -o build/bindbox.exe .` -- 若采用方案B:在 `build` 目录放置 `zoneinfo.zip`,并在启动脚本/说明中加入 `set ZONEINFO=%~dp0zoneinfo.zip`。 - -**验证计划** -- 本地编译并运行单测:覆盖 `internal/pkg/timeutil` 的格式化与解析函数,确认 `Asia/Shanghai` 可加载且不崩溃。 -- 人工验证:在 Windows 主机上执行 `bindbox.exe` 并访问 `http://127.0.0.1:9991/system/health`,字段 `time` 正常返回中国时区时间(参考 `internal/pkg/core/core.go:418-441` 与 `timeutil.CSTLayoutString()`)。 - -**将实施的具体改动** -- 若选A: - - 在 `main.go` 增加 `_ "time/tzdata"` 导入。 - - 更新 README 的 Windows 构建命令,加入 `-tags timetzdata`。 -- 若选C(可选增强): - - 修改 `internal/pkg/timeutil/timeutil.go`,失败时不 `panic`,改为固定时区回退并日志记录。 - -**回滚与兼容** -- 方案A仅增加约 ~800KB 体积,无行为回滚风险。 -- 方案C为防御性修改,兼容现有逻辑;若需恢复原严格行为,可还原为 `panic`。 - -请确认采用的方案(推荐选A;可附加选C增强健壮性)。确认后我将按上述步骤实施并提交更改、完成打包与验证。 \ No newline at end of file diff --git a/.trae/documents/修复“添加奖品弹出批量抽奖”与奖品列表优化计划.md b/.trae/documents/修复“添加奖品弹出批量抽奖”与奖品列表优化计划.md deleted file mode 100644 index 9008e8f..0000000 --- a/.trae/documents/修复“添加奖品弹出批量抽奖”与奖品列表优化计划.md +++ /dev/null @@ -1,40 +0,0 @@ -## 问题定位 -- 奖品页 `web/admin/src/views/activity/rewards/index.vue` 在工具栏下方无条件渲染了批量抽奖对话框:``。 -- 该子组件内部以 `props.activity !== null` 控制 `v-model`,而当前页的 `currentActivity` 始终返回对象(非 null),导致对话框初始即打开,表现为点击“新增奖励”时出现“批量抽奖”。 - -## 修复方案 -- 直接移除奖品页中的 `BatchDrawDialog` 渲染(该页仅做奖励 CRUD,模拟批量抽奖逻辑属于活动管理页/模拟页)。 -- 若保留(备选):使用受控显示状态,改为 ``,并提供单独按钮触发。默认 `showBatchDrawDialog=false`。 -- 采用“移除”方案,影响文件:`web/admin/src/views/activity/rewards/index.vue`(删除组件引用与相关变量)。 - -## 奖品列表优化 -- 新增展示: - - “剩余/总量”列:`quantity / original_qty` - - “期望概率”列:基于总权重 `sumWeight` 本地计算 `weight/sumWeight*100`,保留两位小数。 - - Boss 标签保持,但列名更明确为“Boss”。 -- 汇总信息:表头上方显示“奖品总数 / 总权重”。 -- 交互优化: - - 编辑校验完善(名称、商品、权重、数量、原始数量、等级为必填/大于零)。 - - 删除增加二次确认弹窗。 -- 过滤与排序(前端本地): - - 过滤项:名称(模糊)、等级(S/A/B/C)、是否 Boss。 - - 默认排序:按等级(S→C)+ `sort`。 -- 数据加载优化:商品列表一次加载并缓存,避免重复请求。 - -## 具体改动 -- 修改文件:`web/admin/src/views/activity/rewards/index.vue` - - 删除 `` 与相关 `currentActivity` 计算属性。 - - 在 `columns` 增加 `剩余/总量`、`期望概率` 两列(期望概率通过本地计算得到)。 - - 在顶部新增汇总区域(使用 `computed` 基于 `data` 计算)。 - - 增加简单的本地过滤控件(`ElInput`/`ElSelect`),对展示数据做 `computed` 过滤与排序。 - - 删除与批量抽奖相关的无用状态(如 `showSimDialog`)。 - - 删除按钮文案更明确:`批量新增奖励` → `新增奖励`。 -- 不改后端接口;所有优化为前端表现层。 - -## 验收 -- 进入“奖励”页,点击“新增奖励”只弹奖励编辑对话框,不再出现批量抽奖对话框。 -- 列表显示“剩余/总量”、“期望概率”,汇总信息正确;过滤与排序生效。 -- 构建通过,交互校验正常,删除有确认提示。 - -## 风险与回滚 -- 风险低,仅前端视图改动;如需回滚,保留原组件引用并恢复初始布局即可。 \ No newline at end of file diff --git a/.trae/documents/修复“用户概览”无数据显示.md b/.trae/documents/修复“用户概览”无数据显示.md deleted file mode 100644 index 96e44b2..0000000 --- a/.trae/documents/修复“用户概览”无数据显示.md +++ /dev/null @@ -1,18 +0,0 @@ -## 问题诊断 -- 前端 `active-user.vue` 将图表数据 `xAxisLabels/chartData` 改为普通数组,未使用 Vue 响应式;页面初始为空数组且后续赋值不触发渲染,导致“用户概览没有数据”。 -- 指标列表 `list` 同为普通数组,数值更新不触发视图刷新。 -- 后端 `GET /api/admin/dashboard/user_overview` 正常返回,但前端未正确展示。 - -## 修复方案 -- 将 `xAxisLabels`、`chartData` 改为 `ref/ref`,用 `.value` 填充;模板自动解包可直接绑定。 -- 将指标 `list` 改为 `reactive`,更新项时触发视图刷新。 -- 增加兜底:接口异常或空数据时显示“暂无”与零值,避免空白。 - -## 验证 -- 后端:`curl -H 'Authorization: ' 'http://localhost:8000/api/admin/dashboard/user_overview?rangeType=30d'` 返回含 `chart/metrics`。 -- 前端:刷新工作台,“用户概述”条形图与四个指标显示数据;空数据时显示零与“暂无”。 - -确认后我将: -1) 更新 `web/admin/src/views/dashboard/console/modules/active-user.vue` 响应式实现; -2) 运行后端构建校验; -3) 提供前端验证说明(构建目前受其他文件类型错误影响不影响本模块联调)。 \ No newline at end of file diff --git a/.trae/documents/修复微信支付回调验签失败(支持平台公钥_PUB_KEY_ID).md b/.trae/documents/修复微信支付回调验签失败(支持平台公钥_PUB_KEY_ID).md deleted file mode 100644 index 58ae401..0000000 --- a/.trae/documents/修复微信支付回调验签失败(支持平台公钥_PUB_KEY_ID).md +++ /dev/null @@ -1,38 +0,0 @@ -## 问题定位 -- 现象:POST `/api/pay/wechat/notify` 返回 400,日志提示“certificate [PUB_KEY_ID_0116104396352025041000211519001600] not found in verifier”。 -- 原因:微信侧请求头 `Wechatpay-Serial` 为 `PUB_KEY_ID_*` 格式,说明该商户已启用“微信支付平台公钥”模式;当前代码仅使用“平台证书”验签(`verifiers.NewSHA256WithRSAVerifier` + `downloader`),导致找不到匹配的证书序列号而验签失败。 -- 代码位置: - - 路由注册:`internal/router/router.go:252` - - 回调处理:`internal/api/pay/wechat_notify.go:40-46`(注册证书下载器+证书验签器) - - 客户端初始化:`internal/pkg/pay/client.go:32-39`(`WithWechatPayAutoAuthCipher` 使用平台证书模式) - -## 修复方案 -- 增加“平台公钥”模式配置并切换验签/签名实现: - - 在配置新增 `wechatpay.public_key_id` 与 `wechatpay.public_key_path`(商户后台下载的 `pub_key.pem` 与对应 `PUB_KEY_ID_*`)。 - - 回调验签:当检测到已配置公钥,改用 `verifiers.NewSHA256WithRSAPubkeyVerifier(publicKeyID, publicKey)` 构造 `notify.Handler`;否则保持现有证书模式。 - - 请求侧签名与自动加密:当检测到已配置公钥,改用 `option.WithWechatPayPublicKeyAuthCipher(mchid, serialNo, privateKey, publicKeyID, publicKey)` 初始化 `core.Client`;否则保持 `WithWechatPayAutoAuthCipher`。 -- 初始化时机优化: - - 将证书下载器注册/公钥加载前置至应用启动阶段,避免首次回调时的“尚未拉取证书”竞态问题。 -- 兼容策略: - - 双模式自动选择:优先检测公钥配置;缺省走证书模式,确保对旧商户不破坏。 - -## 实施步骤 -- 配置: - - 在 `configs/*.toml` 的 `[wechatpay]` 增加 `public_key_id` 与 `public_key_path` 键;同时支持环境变量覆盖(如 `WECHAT_PUBLIC_KEY_ID`、`WECHAT_PUBLIC_KEY_PATH`)。 -- 代码改动: - - `internal/api/pay/wechat_notify.go`:根据配置分支选择 `verifiers.NewSHA256WithRSAPubkeyVerifier` 或现有证书验签器;加载公钥使用 `utils.LoadPublicKeyWithPath`。 - - `internal/pkg/pay/client.go`:根据配置分支选择 `option.WithWechatPayPublicKeyAuthCipher` 或现有证书选项。 - - 启动流程:在应用初始化位置集中完成下载器注册/公钥加载与复用,不在每次回调时重复注册。 - -## 验证步骤 -- 本地联调: - - 使用真实微信回调数据(包含 `Wechatpay-Serial: PUB_KEY_ID_*`)验证回调成功返回 `{"code":"SUCCESS","message":"OK"}`。 - - 下单→支付→回调链路验证:检查订单状态从 1→2,`paidAt` 按回调 `success_time` 生效(`internal/api/pay/wechat_notify.go:67-71`)。 -- 日志与安全: - - 打印初始化模式(证书/公钥)与关键配置是否完整,避免静默失败。 - - 不输出密钥/私钥内容,符合安全规范。 - -## 额外检查(关于“req 没有写”) -- 当前回调处理直接使用 `ctx.Request()` 原始请求体(`internal/api/pay/wechat_notify.go:48`),未提前读取/篡改;若上游中间件读取了 `Body`,需保证使用 `io.NopCloser` 复位请求体后再交给 `notify.Handler`。本次改造同时确认路由链路未破坏请求体传递。 - -请确认是否按照“平台公钥”接入(已具备 `pub_key.pem` 与 `PUB_KEY_ID_*`)。确认后我将按上述方案实现代码与配置改动,并完成联调与验证。 \ No newline at end of file diff --git a/.trae/documents/修复抽奖动态未渲染问题.md b/.trae/documents/修复抽奖动态未渲染问题.md deleted file mode 100644 index f21cf53..0000000 --- a/.trae/documents/修复抽奖动态未渲染问题.md +++ /dev/null @@ -1,10 +0,0 @@ -## 原因推测 -- 抽奖动态列表使用 `reactive([])`,在数组 `unshift/splice` 更新时可能未触发渲染;改用 `ref([])` 更稳妥。 -- 文案需更明确:“昵称 在 活动名-期号 中奖 奖品名”。 - -## 修复项 -1) 将列表改为 `ref` 并按 `.value` 更新;模板自动解包无改动。 -2) 文案改为纯文本:“中奖 {{ prizeName }}”,去掉标签以避免样式干扰。 - -## 验证 -- 首次加载立即显示返回的列表项;后续轮询追加新项;行内容符合“谁在哪一个活动中了什么奖品”。 \ No newline at end of file diff --git a/.trae/documents/修复活动创建向导步骤逻辑并统一全局风格.md b/.trae/documents/修复活动创建向导步骤逻辑并统一全局风格.md deleted file mode 100644 index 986e9b8..0000000 --- a/.trae/documents/修复活动创建向导步骤逻辑并统一全局风格.md +++ /dev/null @@ -1,49 +0,0 @@ -## 问题确认 -- 症状:在“创建活动”向导中点击“下一步”后,出现“批量抽奖”界面,用户认为逻辑错误。 -- 初步研判:批量抽奖来自 `BatchDrawDialog`(src/views/activity/rewards/modules/batch-draw-dialog.vue)。该组件在管理页(src/views/activity/manage/index.vue)中常驻渲染,可能因不当条件渲染或状态误触发而在向导流程中弹出。 -- 路由侧现状:列表页有 `router.push({ name: 'ActivityWizard' })`,但路由模块未注册对应路由,存在跳转异常风险。 - -## 目标 -1. 保证向导“下一步”仅在步骤间顺序前进:活动 → 期数 → 奖品,绝不触发批量抽奖。 -2. 统一界面风格为项目全局规范:颜色、圆角、阴影、按钮、表单、布局与动画。 -3. 保留现有功能,避免回归,完成自测与构建验证。 - -## 技术方案 -### 1. 逻辑修复 -- 向导页面(两处)严格限制导航: - - `src/views/activity/wizard/index.vue`:`next()` 仅在成功创建活动后 `active.value = 1`,在成功创建期数后 `active.value = 2`,移除或校验任何非最终提交阶段的 `router.push` 与其他弹窗触发。 - - `src/views/activity/manage/index.vue`:`nextWizard()` 仅递进 `wizardActive`。确保 `BatchDrawDialog` 的 `v-model` 只由 `openBatchDraw()` 显式开启。 -- 隔离批量抽奖弹窗: - - 在管理页将 `` 增加条件渲染 `v-if="!showWizard"`,确保向导弹窗期间不渲染批量抽奖组件,从根本杜绝误触发。 -- 路由补全:在 `src/router/modules/activity.ts` 注册 `ActivityWizard` 路由(path `/activity/wizard`,component `'/activity/wizard'`),避免列表页“创建活动”跳转异常导致意外页面状态。 - -### 2. 风格统一 -- 去除自定义渐变与玻璃拟态,改为全局样式: - - 主容器采用 `art-card` 与项目标准阴影、圆角(卡片 16px,按钮/输入 12px)。 - - 步骤指示器使用项目的圆形数字样式与轻量过渡;颜色使用主题色 `#5D87FF` 及状态色变量。 - - 表单与按钮风格遵循项目统一尺寸、高度与交互(36px 高度,标准阴影与过渡)。 -- 统一 CSS 变量与工具类: - - 使用项目内的 CSS 变量(颜色、圆角、间距),移除局部硬编码样式。 - - 采用现有 `art-card`、栅格与间距体系,保证一致性。 - -### 3. 验证与测试 -- 开发验证: - - 在两处向导中执行完整流程(创建活动→创建期数→配置奖品),确认“下一步”不触发 `BatchDrawDialog`。 - - 构建检查:`npm run build` 无 TS/构建错误。 -- 回归验证: - - 管理页列表的“模拟(批量抽奖)”按钮仍可正常打开 `BatchDrawDialog`。 - - 列表页“创建活动”跳转到向导路由正常。 - -## 交付物 -- 修复后的向导逻辑代码(两处),隔离批量抽奖弹窗的条件渲染。 -- 新增路由项 `ActivityWizard`。 -- 去除非统一风格的样式,替换为项目全局风格实现。 -- 构建与运行验证结果说明。 - -## 验收标准 -- 向导点击“下一步”始终进入下一步骤,不出现批量抽奖。 -- 界面风格与全局统一(颜色、圆角、阴影、按钮、表单风格一致)。 -- 列表页“创建活动”跳转向导正常;管理页“模拟”按钮打开批量抽奖正常。 -- 构建通过,无新错误。 - -如确认以上方案,我将开始实施并同步每一步的修复与验证结果。 \ No newline at end of file diff --git a/.trae/documents/修复称号初始化JSON字段并重新初始化.md b/.trae/documents/修复称号初始化JSON字段并重新初始化.md deleted file mode 100644 index 86081e5..0000000 --- a/.trae/documents/修复称号初始化JSON字段并重新初始化.md +++ /dev/null @@ -1,22 +0,0 @@ -## 问题原因 -- 数据库报错 `Invalid JSON text: "The document is empty."`,说明往 `system_titles.obtain_rules_json` 写入了空字符串 `""`。 -- 在 MySQL JSON 列中,空字符串不是合法 JSON;需使用 `"{}"` 或 `"[]"` 等合法文本。 -- 代码位置:`internal/api/admin/titles_seed.go` 初始化 `SystemTitles` 时将 `ObtainRulesJSON`、`ScopesJSON` 设为了空字符串。 - -## 修复方案 -- 修改 `internal/api/admin/titles_seed.go` 的种子逻辑: - - 将 `ObtainRulesJSON` 默认值改为 `"{}"`(或 `{"type":"manual"}` 作为占位规则)。 - - 将 `ScopesJSON` 默认值改为 `"{}"`(空作用域表示全局生效)。 -- 保持 `SystemTitleEffects.ParamsJSON` 使用 `json.Marshal(d.Params)`(已为合法 JSON)。 - -## 执行步骤 -1. 更新代码:将空字符串替换为合法 JSON 默认值 `"{}"`。 -2. 编译检查:`go build ./...`。 -3. 运行初始化:`POST /api/admin/system_titles/seed_default`,预期返回 6 个称号(新建或已存在)。 -4. 验证列表:登录后调用 `GET /api/admin/system_titles?page=1&page_size=20`,应返回 `total=6`。 - -## 风险与回滚 -- 仅修改种子默认值,不影响既有数据;若此前插入失败,无需回滚。 -- 如你希望 `obtain_rules_json` 有具体规则,我可以将默认值改为 `{"type":"manual"}` 或按你的要求填充。 - -确认后我将立即修复代码、重新初始化并验证页面数据。 \ No newline at end of file diff --git a/.trae/documents/修复范围筛选、抽奖动态与待办事项.md b/.trae/documents/修复范围筛选、抽奖动态与待办事项.md deleted file mode 100644 index 1e0869d..0000000 --- a/.trae/documents/修复范围筛选、抽奖动态与待办事项.md +++ /dev/null @@ -1,32 +0,0 @@ -## 问题与修复方案 -- 新用户切换“本月/上月/今年”无变化:后端 `new_users` 未支持时间范围;前端未传范围参数 -- 实时抽奖动态文案未达成“谁在哪一个活动中了什么奖品” -- 待办事项仍展示绑定/公会;未展示“未抽奖用户” - -## 后端调整 -1) 扩展 `GET /api/admin/dashboard/new_users` -- 新增参数 `period=month|last_month|year` -- 按 `users.created_at` 过滤对应范围 - -2) 抽奖动态数据完整化 -- 已返回 `activityName/issueNumber/prizeName`;确保空值处理 - -3) 待办事项 -- 保持返回 `taskType='undrawn'`、`taskLabel='从未参与抽奖'` - -## 前端调整 -1) 新用户模块 -- `fetchNewUsers(page,pageSize,period)` 支持传 `period` -- `new-user.vue` 监听单选切换,映射“本月/上月/今年”→`month/last_month/year` - -2) 实时抽奖动态 -- 行文改为:`{{ nickname }} 在 {{ activityName }}-{{ issueNumber }} {{ isWinner ? '中奖 ' + prizeName : '参与' }}` - -3) 待办事项 -- 使用接口返回的 `taskLabel` 和 `taskType`,标签统一为 `info`,文案显示“从未参与抽奖” - -## 验证 -- 后端编译通过; -- 切换单选范围数据刷新; -- 抽奖动态行文本符合“谁在哪一个活动中了什么奖品”; -- 待办列表展示“未参与抽奖”的用户 \ No newline at end of file diff --git a/.trae/documents/修复订单详情展示与数据补全(活动_期号_支付与中奖信息).md b/.trae/documents/修复订单详情展示与数据补全(活动_期号_支付与中奖信息).md deleted file mode 100644 index 8bab855..0000000 --- a/.trae/documents/修复订单详情展示与数据补全(活动_期号_支付与中奖信息).md +++ /dev/null @@ -1,49 +0,0 @@ -## 问题归因 -- 活动/期号为空:后端仅在存在抽奖日志时填充活动信息,未抽取或刚支付时为0。参考 internal/api/admin/pay_orders_admin.go:167-191。 -- 中奖字段与等级:前端直接展示“是否中奖”,但应改为“中奖等级”并基于 `activity.level`/`reward_id` 显示;未抽取时应显示“待开奖”。 -- 支付信息不全:只显示 `ActualAmount`,未分解“积分抵扣金额/积分数量”;`PayPreorderID` 为0时未做断言处理。 -- 商品价格与金额为0:抽奖订单常无商品明细或价格未赋值;可根据奖励商品填充价格,或以“抽奖价格×次数”为明细。 - -## 后端改造(GetPayOrderDetail) -- 文件:internal/api/admin/pay_orders_admin.go -- 变更点: - 1) 无抽奖日志时,解析 `order.Remark` 中 `lottery:activity:|issue:|count:` 填充 `activity.activity_id/issue_id`,查询 `issue.issue_number` 与 `activity.activity_name`。 - 2) 扩展支付信息:`payment.points_amount`(订单积分抵扣金额,单位分)、`payment.points_used`(抵扣积分数量=points_amount/10)、`payment.total_amount`(订单总额,单位分)。 - 3) 返回 `activity.count`(抽次数);若有奖励商品,补充 `activity.product_price`(商品价格,单位分)。 - 4) 若订单无 `order_items`,在响应中提供 `computed_items`:基于“抽奖价格×次数”构造一条展示用明细(前端优先显示 `computed_items`)。 - 5) `payment.pay_preorder_id` 为0时仍返回,前端据此显示“-”。 - -## 前端改造(订单详情抽屉) -- 文件:web/admin/src/views/…(订单详情抽屉组件,使用 fetchGetOrderDetail) -- 变更点: - 1) 活动:显示 `activity.activity_name`;下方附 `issue_number`;不显示 `(ID:0)`。 - 2) 状态区:移除“是否中奖”;改为“中奖等级”显示 `activity.level`,若 `reward_id` 为空则显示“待开奖”。 - 3) 支付区: - - “实付”显示:`payment.actual_amount/100` 元 - - 新增“积分抵扣”:`payment.points_used` 积分(约 `points_amount/100` 元) - - 新增“订单总额”:`payment.total_amount/100` 元 - - “预订单ID”:显示 `payment.pay_preorder_id`,为0则显示“-” - 4) 明细表: - - 优先显示 `computed_items` 的“单价/金额”,否则显示 `items`; - - 若有中奖商品,显示 `activity.reward_name` 与 `activity.product_price`。 - -## 路由与API契约 -- 前端调用不变:`GET admin/pay/orders/:order_no` -- 响应新增字段: - - `activity.count`、`activity.product_price` - - `payment.points_amount`、`payment.points_used`、`payment.total_amount` - - `computed_items: [{name, quantity, unit_price, amount}]` - -## 实施步骤 -1) 后端:更新 GetPayOrderDetail 填充解析与新增字段;构造 `computed_items`。 -2) 前端:更新详情组件的数据映射与展示逻辑;删改“是否中奖”,补充字段渲染与空值处理。 -3) 验证: - - 无抽奖日志的订单仍能显示活动名称与期号 - - 积分全额支付场景显示积分抵扣与总额,实付为0,预订单ID为“-” - - 即时模式支付后,轮询显示中奖等级与商品信息 - - 明细表显示正确单价/金额 - -## 验收标准 -- 所有问题项均有正确数据或合理占位显示 -- 即时/定时、积分/金额支付四种组合下展示正确 -- 架构不破坏现有接口:前端仅增量使用新增字段 \ No newline at end of file diff --git a/.trae/documents/全面代码清理与优化计划.md b/.trae/documents/全面代码清理与优化计划.md deleted file mode 100644 index 9787580..0000000 --- a/.trae/documents/全面代码清理与优化计划.md +++ /dev/null @@ -1,127 +0,0 @@ -## 目标 - -* 全面清理未用代码、注释废弃块、空文件与无用测试 - -* 识别并重构重复代码(重复率≥80%) - -* 保持现有功能稳定,构建与测试全部通过 - -* 输出对比报告与文档更新 - -## 范围 - -* 后端:`internal/**`、`cmd/**`、`migrations/**` - -* 前端管理:`web/admin/**`(Vue/TS/样式与公共组件) - -* 通用资源:`docs/**`、脚手架与配置(不更改生产配置) - -## 清理策略与工具 - -* 未用与死代码检测 - - * Go:`golangci-lint`(unused、deadcode、revive)、`go vet` - - * TS/Vue:`tsc --noEmit`(类型与未用导出)、`eslint`(no-unused-vars/no-dead-code) - -* 注释废弃块识别 - - * 规则:Grep 检索注释中出现代码结构(`func|class|export|