Compare commits

..
19 Commits
Author SHA1 Message Date
dev 18973882ea chore: seed migration objects batch 19 2026-10-18 13:27:32 +08:00
dev 502f01283f chore: seed migration objects batch 18 2026-10-18 13:27:25 +08:00
dev 8f19d5fd1f chore: seed migration objects batch 17 2026-10-18 13:27:18 +08:00
dev e6d020bd8c chore: seed migration objects batch 16 2026-10-18 13:27:05 +08:00
dev f640d124e7 chore: seed migration objects batch 15 2026-10-18 13:26:51 +08:00
dev 762951ebb0 chore: seed migration objects batch 14 2026-10-18 13:26:43 +08:00
dev 28abf26295 chore: seed migration objects batch 13 2026-10-18 13:26:35 +08:00
dev 5f4f96b64d chore: seed migration objects batch 12 2026-10-18 13:26:29 +08:00
dev 40f2c25199 chore: seed migration objects batch 11 2026-10-18 13:26:04 +08:00
dev 659f4089f4 chore: seed migration objects batch 10 2026-10-18 13:25:34 +08:00
dev e703bbf72d chore: seed migration objects batch 9 2026-10-18 13:25:05 +08:00
dev 97a0b33ba0 chore: seed migration objects batch 8 2026-10-18 13:24:49 +08:00
dev 9a7f9ece7e chore: seed migration objects batch 7 2026-10-18 13:24:42 +08:00
dev be3c43f416 chore: seed migration objects batch 6 2026-10-18 13:24:10 +08:00
dev 2ad6e04417 chore: seed migration objects batch 5 2026-10-18 13:23:15 +08:00
dev 18e59f51ba chore: seed migration objects batch 4 2026-10-18 13:22:52 +08:00
dev 3bbf5387ab chore: seed migration objects batch 3 2026-10-18 13:22:40 +08:00
dev c8ef50e59c chore: seed migration objects batch 2 2026-10-18 13:22:00 +08:00
dev a03db4294f chore: seed migration objects batch 1 2026-10-18 13:21:20 +08:00
373 changed files with 670 additions and 8813 deletions
-16
View File
@@ -1,16 +0,0 @@
.git
.gitea
.idea
.vscode
**/.DS_Store
web/node_modules
web/dist
server/temp
server/logs
server/storage/cache
server/storage/diskqueue
server/resource/public/admin
docs
tools
*.docx
*.pdf
-84
View File
@@ -1,84 +0,0 @@
name: Deploy olons-admin production
on:
push:
branches:
- pro
workflow_dispatch:
concurrency:
group: production-olons-admin
cancel-in-progress: false
permissions:
contents: read
env:
REGISTRY: git.olns.shop
IMAGE: git.olns.shop/pro/olons_admin
SERVICE: olons-admin
DEPLOY_HOST: 145.223.69.9
DEPLOY_USER: olns-deploy
jobs:
deploy:
runs-on: deploy-host
timeout-minutes: 60
steps:
- uses: actions/checkout@v4
- name: Log in to Gitea Container Registry
env:
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
run: |
set -euo pipefail
printf '%s' "$REGISTRY_TOKEN" | docker login "$REGISTRY" -u admin --password-stdin
- name: Build and push image
env:
GITHUB_SHA: ${{ github.sha }}
run: |
set -euo pipefail
SHORT_SHA="$(printf '%.12s' "$GITHUB_SHA")"
docker build -t "${IMAGE}:pro" -t "${IMAGE}:${SHORT_SHA}" .
docker push "$IMAGE:pro"
docker push "$IMAGE:$SHORT_SHA"
- name: Deploy Compose service
env:
DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
DEPLOY_KNOWN_HOSTS: ${{ secrets.DEPLOY_KNOWN_HOSTS }}
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
run: |
set -euo pipefail
install -d -m 700 "$HOME/.ssh"
KEY_FILE="$(mktemp "$HOME/.ssh/olns-deploy.XXXXXX")"
cleanup() {
shred -u "$KEY_FILE" 2>/dev/null || true
}
trap cleanup EXIT
printf '%s\n' "$DEPLOY_SSH_KEY" > "$KEY_FILE"
chmod 600 "$KEY_FILE"
printf '%s\n' "$DEPLOY_KNOWN_HOSTS" > "$HOME/.ssh/known_hosts"
chmod 600 "$HOME/.ssh/known_hosts"
SSH_ARGS=(-i "$KEY_FILE" -o BatchMode=yes -o StrictHostKeyChecking=yes)
for attempt in 1 2 3; do
if printf '%s' "$REGISTRY_TOKEN" | ssh "${SSH_ARGS[@]}" "$DEPLOY_USER@$DEPLOY_HOST" \
"docker login git.olns.shop -u admin --password-stdin"; then
break
fi
if [[ "$attempt" -eq 3 ]]; then
exit 1
fi
sleep "$((attempt * 5))"
done
ssh "${SSH_ARGS[@]}" "$DEPLOY_USER@$DEPLOY_HOST" \
'cd /opt/olns && for attempt in 1 2 3; do
if timeout 180 docker compose pull olons-admin; then
break
fi
if [ "$attempt" -eq 3 ]; then
exit 1
fi
sleep $((attempt * 5))
done && docker compose up -d --no-deps --wait olons-admin'
-36
View File
@@ -1,36 +0,0 @@
FROM node:20-alpine AS web-build
WORKDIR /web
RUN corepack enable && corepack prepare pnpm@10.18.3 --activate
COPY web/package.json web/pnpm-lock.yaml web/pnpm-workspace.yaml ./
RUN pnpm install --frozen-lockfile
COPY web/ ./
RUN pnpm run build
FROM golang:1.24-alpine AS server-build
WORKDIR /src
COPY server/go.mod server/go.sum ./
RUN go mod download
RUN go install github.com/gogf/gf/cmd/gf/v2@v2.10.0
COPY server/ ./
COPY --from=web-build /web/dist/ ./resource/public/admin/
RUN gf pack resource internal/packed/packed.go --keepPath=true \
&& CGO_ENABLED=0 GOOS=linux go build -trimpath -ldflags="-s -w" -o /out/hotgo .
FROM alpine:3.22
RUN apk add --no-cache ca-certificates tzdata \
&& addgroup -S hotgo \
&& adduser -S -G hotgo hotgo
WORKDIR /app
COPY --from=server-build /out/hotgo /app/hotgo
COPY --from=server-build /src/manifest /app/manifest
COPY --from=server-build /src/resource /app/resource
COPY --from=web-build /web/dist/ /app/resource/public/admin/
RUN mkdir -p /app/storage /app/logs \
&& chown -R hotgo:hotgo /app
USER hotgo
EXPOSE 8000
ENTRYPOINT ["/app/hotgo"]
CMD ["all"]
@@ -1,864 +0,0 @@
# Olons 全功能测试用例
> 文档版本:V1.1
> 编制日期:2026-10-16
> 覆盖范围:Olons C 端、HotGo 管理后台、业务 API、数据库一致性、客服 IM 及跨端业务闭环
> 代码基线:`olons_server` 与 `olons_admin` 当前工作区实现
> 用途:测试执行、联调验收、发布前回归和缺陷复测
## 1. 测试目标与范围
本用例集以当前代码路由、服务端校验、页面按钮和后台菜单为准,重点验证以下内容:
- C 端注册登录、首页、领取、订单、商城、储入、取出、个人中心、内容页和在线客服。
- 后台首页、门户、会员、等级、任务大厅、交易、商品、任务、组织、权限、系统应用和系统设置。
- 后台配置、审核及上下架操作对 C 端展示和业务状态的实时影响。
- 金额、字符长度、文件大小、分页、时间窗口、次数限制等边界值。
- 未登录、越权、重复提交、并发、超时、断网、服务异常及脏数据场景。
- 钱包余额、冻结额、业务单、账变流水、审核记录和状态机的一致性。
- Olons 与 118IM 之间的账号创建、好友关系、会话、文本及媒体消息链路。
本轮不包含第三方真实支付清算、短信供应商实发、压力容量定标和渗透测试,但保留相应接口降级与安全验收项。
当前共 548 条用例,其中页面逐项验收 188 条(通用页面 8 条、C 端页面 90 条、后台页面 90 条),其余 360 条用于补充业务规则、接口、安全、数据一致性和跨端闭环。执行时应先完成第 4 章页面用例,再执行对应模块的深层用例。
## 2. 测试依据
| 类型 | 依据 |
| --- | --- |
| C 端路由 | `olons_server/web/src/router/index.ts` |
| C 端接口 | `olons_server/internal/httpapi/` |
| C 端业务规则 | `olons_server/internal/accounts`、`finance`、`tasks`、`mall`、`support`、`attachments` |
| 后台页面 | `olons_admin/web/src/views/` |
| 后台接口 | `olons_admin/server/api/admin/`、`server/internal/logic/sys/` |
| 菜单与初始化 | `olons_admin/server/storage/data/migrations/`、`seeds/` |
| 产品基线 | `后台管理系统需求文档.md`、`后台管理系统概要设计.md`、`后台功能与数据库表映射.md` |
| IM 对接 | `olons_server/docs/IM-前后端对接文档.md`、`Olons-客服IM系统对接设计.md` |
## 3. 执行约定
### 3.1 优先级
| 优先级 | 定义 | 发布要求 |
| --- | --- | --- |
| P0 | 登录、资金、权限、订单结算、数据泄露等核心链路 | 必须 100% 通过 |
| P1 | 主要业务功能及重要异常分支 | 不得存在阻断性缺陷 |
| P2 | 次要功能、显示、兼容和易用性 | 可评估后带已知问题发布 |
### 3.2 通用前置条件
- 浏览器时间、服务端时间和数据库时间同步,业务时区按 Asia/Shanghai 验证。
- 准备独立测试租户,禁止在生产真实会员上执行资金、删除、禁用和权限用例。
- 每个资金用例执行前记录钱包 `available_amount`、`frozen_amount`、`version` 和最近流水。
- 每个配置联动用例记录配置版本号、发布时间及 C 端接口响应,执行后恢复原配置。
- 所有 API 写请求记录 `request_id`;幂等接口记录幂等键、业务单号及返回结果。
### 3.3 测试账号与数据矩阵
| 数据代号 | 准备要求 |
| --- | --- |
| U-NORMAL | 正常真人会员,已设置登录密码和交易密码,已绑已验证收款账户,余额充足 |
| U-NEW | 新注册会员,无交易密码、无收款账户、无订单 |
| U-LOW | 可用余额低于领取/提现下限 |
| U-LIMIT | 当日领取次数或提现次数已达到等级上限 |
| U-PENDING | 存在匹配中或待处理订单 |
| U-DISABLED | 会员状态禁用或钱包状态禁用 |
| U-FAKE | 假人类别会员,用于统计隔离验证 |
| U-MANUAL | 派单模式为手动 |
| U-AUTO | 派单模式为自动 |
| URL-ENABLED | 地址池中的启用地址,域名未绑定或已绑定少量会员 |
| URL-INVALID | 地址池中的失效地址,保留历史绑定会员 |
| URL-UNKNOWN | 未录入地址池的注册域名,用于验证注册成功但不建立绑定 |
| XLS-URL | 地址池导入文件,包含合法、重复、非法 URL、空值和超长数据 |
| A-SUPER | 超级管理员,拥有全部菜单和按钮权限 |
| A-PARENT | 父角色管理员,只有部分业务菜单 |
| A-CHILD | A-PARENT 的子角色,权限必须为父角色子集 |
| A-CS/A-FIN/A-OPS | 客服、财务、运营最小权限账号 |
| P-ON/P-OFF | 已发布商品/已下架商品 |
| T-ON/T-OFF | 已发布任务/已下线任务 |
| IM-3 | 客服依依、客服琪琪、客服琳琳,均已绑定 IM 用户及头像 |
## 4. 页面逐项验收主用例
本章是测试人员的主要执行入口,按照“进入页面 → 检查默认展示 → 操作页面控件 → 检查弹窗 → 验证成功/失败反馈 → 返回或刷新”的顺序执行。后续章节用于补充接口、状态机、并发和数据一致性,不应替代本章的页面操作。
### 4.1 每个页面都必须执行的八项检查
| ID | 优先级 | 页面操作 | 预期结果 |
| --- | --- | --- | --- |
| UI-COM-001 | P1 | 从菜单、快捷入口或站内链接进入页面 | 路由、页面标题、面包屑和菜单高亮正确 |
| UI-COM-002 | P1 | 首次进入页面并观察加载过程 | 有加载反馈;加载结束后不会永久转圈或闪烁 |
| UI-COM-003 | P1 | 检查页面全部文字、金额、时间、图片、状态标签 | 无错别字、乱码、单位错误、破图或状态空白 |
| UI-COM-004 | P1 | 逐个点击页面可见按钮、链接、图标和表格行 | 每个控件均有响应,目标页面或弹窗正确 |
| UI-COM-005 | P1 | 打开弹窗后测试右上角关闭、取消、遮罩和 Esc | 按约定关闭;未保存内容不产生后台写入 |
| UI-COM-006 | P1 | 对页面输入框执行空值、合法值、边界值和非法值 | 校验位置与文案明确,错误输入不能提交 |
| UI-COM-007 | P1 | 模拟空数据、接口失败、超时和无权限 | 分别显示空态、错误态、重试或无权限提示 |
| UI-COM-008 | P2 | 刷新、后退、前进、切换标签页和改变窗口宽度 | 页面不白屏;数据和布局按约定恢复 |
### 4.2 C 端页面逐项测试
#### 4.2.1 登录与注册页 `/app/auth`
| ID | 优先级 | 页面操作 | 预期结果 |
| --- | --- | --- | --- |
| UI-C-AUTH-001 | P1 | 打开登录页 | 仅显示账号登录/注册业务控件,不出现微信、抖音、手机号登录和演示角色入口 |
| UI-C-AUTH-002 | P1 | 点击“账号登录”“注册账号”“已有账户,马上登录” | 登录与注册表单正确切换,已有输入按页面约定保留或清空 |
| UI-C-AUTH-003 | P1 | 登录模式检查手机号、密码框和密码显隐图标 | 占位符、输入类型、显隐切换和键盘类型正确 |
| UI-C-AUTH-004 | P0 | 输入合法账号并点击“登录” | 按钮显示“请稍候”并禁用,成功进入原目标页 |
| UI-C-AUTH-005 | P1 | 输入错误账号并点击“登录” | 页面停留,错误提示清楚,密码不明文残留在日志或 URL |
| UI-C-AUTH-006 | P1 | 注册模式检查姓名、手机号、密码、邀请码、协议勾选框 | 字段与当前注册规则一致,邀请码和协议为必填 |
| UI-C-AUTH-007 | P0 | 未勾选协议点击“注册” | 页面阻止注册并定位协议区域 |
| UI-C-AUTH-008 | P1 | 点击隐私协议/用户协议链接后返回 | 内容页可读,返回后注册表单状态合理保留 |
#### 4.2.2 首页 `/app/home`
| ID | 优先级 | 页面操作 | 预期结果 |
| --- | --- | --- | --- |
| UI-C-HOME-001 | P1 | 打开首页并等待加载完成 | 顶部信息、Banner、快捷功能、滚动信息、内容入口和底部导航完整显示 |
| UI-C-HOME-002 | P1 | 左右滑动 Banner、点击圆点和 Banner 本体 | 切换流畅;有效跳转正确;无跳转 Banner 不误跳 |
| UI-C-HOME-003 | P1 | 点击搜索框 | 进入商城或约定搜索页面,返回首页位置正确 |
| UI-C-HOME-004 | P0 | 依次点击“领取、储入、取出、商城” | 分别进入领取、储入、取出、商城页面 |
| UI-C-HOME-005 | P1 | 后台关闭四个功能后检查对应入口 | 置灰且无法进入;置灰文字仍可辨识 |
| UI-C-HOME-006 | P1 | 点击平台介绍、规则说明、公司资质、代理合作 | 均进入正确内容页,标题和正文相符 |
| UI-C-HOME-007 | P1 | 点击滚动公告/弹窗通知 | 展开对应详情,不跳到错误会员或订单页面 |
| UI-C-HOME-008 | P1 | 点击底部“首页、记录、领取、客服、我的” | 五个页面均可达,当前项高亮正确 |
#### 4.2.3 领取页 `/app/claim`
| ID | 优先级 | 页面操作 | 预期结果 |
| --- | --- | --- | --- |
| UI-C-CLAIM-001 | P1 | 打开领取页 | 总资产、昨日佣金、今日佣金、今日领取数和领取主按钮完整显示 |
| UI-C-CLAIM-002 | P0 | 满足条件时点击领取主按钮 | 按钮进入加载态,不能重复点击;随后展示匹配结果 |
| UI-C-CLAIM-003 | P1 | 不满足余额/等级/营业时间/次数条件时点击领取 | 弹层显示对应原因,不出现笼统“失败” |
| UI-C-CLAIM-004 | P1 | 有在途订单时点击“查看任务” | 进入该订单详情,不新建第二张订单 |
| UI-C-CLAIM-005 | P1 | 匹配中观察进度、等待和倒计时区域 | 状态能刷新,离开再返回仍能恢复当前订单 |
| UI-C-CLAIM-006 | P1 | 结果失败时点击“重新领取” | 重新检查资格;按钮加载态和结果更新正确 |
| UI-C-CLAIM-007 | P1 | 点击“稍后去记录查看”“查看记录”“返回首页” | 分别进入订单列表、订单详情或首页 |
#### 4.2.4 订单列表与详情 `/app/orders`、`/app/orders/:id`
| ID | 优先级 | 页面操作 | 预期结果 |
| --- | --- | --- | --- |
| UI-C-ORDER-001 | P1 | 打开订单列表 | 默认标签、订单卡片、状态、金额、商品图和时间正确 |
| UI-C-ORDER-002 | P1 | 依次点击“全部、待处理、已完成” | 列表只显示对应订单;无数据时显示空态 |
| UI-C-ORDER-003 | P1 | 点击任一订单卡片 | 进入该订单详情,订单号和列表所选记录一致 |
| UI-C-ORDER-004 | P1 | 详情页检查商品、金额、佣金、状态、时间线和流水 | 字段完整,长文本/图片不撑破布局 |
| UI-C-ORDER-005 | P0 | 可提交的手动订单点击“提交确认” | 打开二次确认弹窗,金额和提示与当前订单一致 |
| UI-C-ORDER-006 | P1 | 在确认弹窗点击“暂不提交”、关闭和遮罩 | 弹窗关闭,订单和余额不变 |
| UI-C-ORDER-007 | P0 | 点击“确认提交” | 按钮防重复;成功提示、状态和余额立即更新 |
| UI-C-ORDER-008 | P1 | 余额不足时查看按钮并尝试提交 | 按钮显示“余额不足,无法提交”或引导储入,不错误扣款 |
| UI-C-ORDER-009 | P1 | 冻结、超时、取消、失败订单进入详情 | 不显示不允许的提交按钮,提供客服入口或明确说明 |
| UI-C-ORDER-010 | P1 | 点击详情返回按钮和浏览器后退 | 返回原列表及原标签位置 |
#### 4.2.5 商城列表与详情 `/app/mall`、`/app/mall/:id`
| ID | 优先级 | 页面操作 | 预期结果 |
| --- | --- | --- | --- |
| UI-C-MALL-001 | P1 | 打开商城 | 搜索框、搜索按钮、推荐任务/限时任务标签和商品卡片显示完整 |
| UI-C-MALL-002 | P1 | 输入关键词后点击“搜索”及清空关键词重搜 | 结果按关键词过滤,清空后恢复全部 |
| UI-C-MALL-003 | P1 | 切换“推荐任务”“限时任务” | 商品集合和按钮文字同步变化,无重复或串页 |
| UI-C-MALL-004 | P1 | 点击商品图片、名称、“领取/抢单” | 均进入同一商品详情 |
| UI-C-MALL-005 | P1 | 详情页检查图片、名称、店铺、价格、说明、时限 | 与后台发布内容一致,富文本安全显示 |
| UI-C-MALL-006 | P1 | 商品已下架或过期时刷新详情 | 显示不可用状态,不允许继续领取 |
#### 4.2.6 储入页 `/app/recharge`
| ID | 优先级 | 页面操作 | 预期结果 |
| --- | --- | --- | --- |
| UI-C-RC-001 | P1 | 打开储入页 | 储入方式、金额、姓名和提交按钮完整显示 |
| UI-C-RC-002 | P1 | 展开“储入方式”下拉并逐项选择 | 只列启用渠道;所选值和渠道提示同步更新 |
| UI-C-RC-003 | P1 | 金额留空、越界、输入字母后点击提交 | 页面在金额字段提示,不发起有效订单 |
| UI-C-RC-004 | P1 | 姓名留空或输入超长内容 | 阻止提交并显示字段错误 |
| UI-C-RC-005 | P0 | 填写合法数据点击“提交” | 按钮显示提交中并禁用;成功提示后跳转客服中心 |
| UI-C-RC-006 | P1 | 接口失败后观察页面 | 不跳客服;表单可修改并重新提交 |
#### 4.2.7 取出页 `/app/profile/withdrawal`
| ID | 优先级 | 页面操作 | 预期结果 |
| --- | --- | --- | --- |
| UI-C-WD-001 | P1 | 打开取出页 | 可用余额、金额输入、全部提现、收款账户和申请按钮显示完整 |
| UI-C-WD-002 | P1 | 点击“全部提现” | 输入框填入当前可用余额;如超过渠道上限,报价时明确提示而不创建取出单 |
| UI-C-WD-003 | P1 | 查看收款账户区域并点击 | 有已核验账户时显示银行及尾号;无账户时进入绑定信息页 |
| UI-C-WD-004 | P1 | 输入合法金额点击“申请提现” | 出现确认弹窗,申请金额、手续费和预计到账金额与报价一致 |
| UI-C-WD-005 | P1 | 未设置支付密码点击“去设置支付密码”,填写 6 位数字密码和正确登录密码后点击“确认设置” | 原弹窗切换为设置表单;设置成功返回确认申请界面并保留报价,不自动提交取出单 |
| UI-C-WD-006 | P1 | 分别在确认取出和设置支付密码界面点击右上角关闭叉号;重新打开后点击“返回确认申请” | 两种界面均可关闭;密码输入被清空、未创建取出单;返回确认时保留报价且仍需再次点击“确认申请” |
| UI-C-WD-007 | P0 | 输入支付密码并点击“确认申请” | 加载态防重复;成功关闭弹层并更新余额/记录 |
| UI-C-WD-008 | P1 | 密码错误、次数受限、报价过期 | 分别显示明确提示,弹窗状态可恢复或重新报价 |
| UI-C-WD-009 | P1 | 未设置支付密码时查看“确认申请”,或设置时输入非 6 位密码/错误登录密码 | 未设置时确认按钮不可提交;格式错误在本地提示,接口失败后保留设置界面且不创建取出单 |
| UI-C-WD-010 | P1 | 设置或提交请求进行中连续点击关闭叉号与主按钮 | 请求期间关闭和重复提交被禁止;请求结束后可按状态继续操作 |
#### 4.2.8 我的页面 `/app/profile`
| ID | 优先级 | 页面操作 | 预期结果 |
| --- | --- | --- | --- |
| UI-C-ME-001 | P1 | 打开“我的” | 用户名、邀请码、账户余额及功能菜单完整显示 |
| UI-C-ME-002 | P2 | 点击邀请码复制按钮 | 显示复制成功,剪贴板内容为当前邀请码 |
| UI-C-ME-003 | P0 | 点击“立即取出”“立即储入” | 分别进入取出和储入页面 |
| UI-C-ME-004 | P1 | 点击个人信息、储入记录、取出记录、账户明细、弹窗明细 | 五个入口分别进入正确页面 |
| UI-C-ME-005 | P1 | 点击“退出登录”后点击取消 | 弹窗关闭并保持登录 |
| UI-C-ME-006 | P0 | 点击“退出登录”后点击确定 | 清除会话并进入登录页,后退不能看到有效敏感页面 |
#### 4.2.9 个人信息、银行卡与密码页面
| ID | 优先级 | 页面操作 | 预期结果 |
| --- | --- | --- | --- |
| UI-C-FORM-001 | P1 | 打开个人信息页 | 头像、本人手机号、取出信息、密码管理、支付密码管理按原型分组展示;头像行和三个入口均可点击 |
| UI-C-FORM-002 | P1 | 点击头像行选择合法图片,等待上传完成并刷新页面 | 文件选择器可打开;图片先上传附件再更新本人头像,成功提示且刷新后仍显示新头像 |
| UI-C-FORM-003 | P1 | 打开绑定信息页 | 已绑定账户先显示本人原始信息;新增表单的姓名、身份证、卡号、开户行、选填数字人民币编码、微信码、支付宝码字段齐全 |
| UI-C-FORM-004 | P1 | 点击两处“点击上传”并分别选图 | 上传中、预览和失败重试状态正确,两张图不会串位 |
| UI-C-FORM-005 | P0 | 填完银行卡信息点击“确认绑定” | 按钮防重复;成功后从个人信息进入的返回个人信息,从取出进入的返回取出;本人绑定信息页显示原文,通用账户列表仍脱敏 |
| UI-C-FORM-006 | P1 | 打开修改登录密码页并点击“确定修改” | 当前密码、新密码、确认密码校验正确,成功后给出明确反馈 |
| UI-C-FORM-007 | P1 | 新密码和确认密码不一致 | 页面本地拦截,不请求修改接口 |
| UI-C-FORM-008 | P1 | 打开支付密码页 | 新支付密码、登录密码、已设置时的旧支付密码展示逻辑正确 |
| UI-C-FORM-009 | P0 | 填写合法信息点击“确定” | 按钮防重复,成功后返回且支付密码状态更新 |
| UI-C-FORM-010 | P0 | 点击个人信息中的“取出信息”查看已绑定账户 | 本人姓名、身份证号、银行名、完整银行卡号、已填的数字人民币编码和两张收款码图片正确展示,不以星号替代原值 |
| UI-C-FORM-011 | P1 | 无绑定账户或有多张账户时打开取出信息 | 无账户时可直接新增;多账户逐条对应展示,不串姓名、卡号和收款码 |
| UI-C-FORM-012 | P1 | 数字人民币编码留空后提交绑卡 | 页面不把该字段当必填;成功后详情不出现伪造的编码,其他必填项仍正常校验 |
| UI-C-FORM-013 | P1 | 头像选择 GIF、伪装扩展名、超过 5MB 图片,或模拟上传/更新接口失败 | 明确提示失败;旧头像不变,可重新选择文件重试 |
#### 4.2.10 记录、账变、通知和内容页
| ID | 优先级 | 页面操作 | 预期结果 |
| --- | --- | --- | --- |
| UI-C-REC-001 | P1 | 从“储入记录”进入并切换记录标签 | 默认定位储入,状态、金额和时间正确 |
| UI-C-REC-002 | P1 | 从“取出记录”进入并切换记录标签 | 默认定位取出,手续费、实到和原因正确 |
| UI-C-REC-003 | P1 | 点击记录行查看详情 | 进入对应订单详情或展开详情,记录 ID 不串行 |
| UI-C-REC-004 | P1 | 账户明细筛选收入/支出并翻页 | 筛选和分页正确,前后余额可读 |
| UI-C-REC-005 | P1 | 弹窗明细打开未读通知并返回 | 正文可见,已读状态更新,列表位置合理保留 |
| UI-C-REC-006 | P1 | 平台介绍、规则、代理、资质和通用内容页点击返回 | 标题、正文和返回路径正确 |
#### 4.2.11 客服列表与聊天页 `/app/support`、`/app/support/:id`
| ID | 优先级 | 页面操作 | 预期结果 |
| --- | --- | --- | --- |
| UI-C-CS-001 | P1 | 打开客服列表 | 三名客服名称、头像、在线/忙碌状态完整显示 |
| UI-C-CS-002 | P1 | 依次点击三个客服卡片并返回 | 分别进入对应会话,标题和头像不串客服 |
| UI-C-CS-003 | P1 | 连接失败时点击“重新连接” | 显示连接过程,成功恢复输入区;失败可再次操作 |
| UI-C-CS-004 | P1 | 输入文字,检查发送按钮显隐并点击 | 有文本时显示清晰的发送按钮;发送中禁用 |
| UI-C-CS-005 | P1 | 输入框为空时点击“+” | 展开表情、照片、视频工具;再次点击按约定收起 |
| UI-C-CS-006 | P1 | 点击“表情”并切换最近/表情/手势/物品 | 面板紧凑显示,选中表情插入输入框 |
| UI-C-CS-007 | P1 | 点击“照片”“视频”并取消系统文件选择 | 不发送空消息,工具区恢复可用 |
| UI-C-CS-008 | P0 | 选择合法照片或视频 | 显示上传进度,发送后本方气泡和客服端均可见 |
| UI-C-CS-009 | P1 | 点击图片、播放视频、滚动历史消息 | 预览/播放正常,滚动不遮挡底部输入区 |
| UI-C-CS-010 | P1 | 检查双方头像、左右气泡、时间和发送状态 | 客服在左、本人在右;头像及已发送/失败状态清楚 |
### 4.3 后台页面逐项测试
#### 4.3.1 后台登录与全局框架
| ID | 优先级 | 页面操作 | 预期结果 |
| --- | --- | --- | --- |
| UI-A-FRAME-001 | P1 | 打开后台登录页 | 标题为“企业办公管理系统”,只显示账号登录表单 |
| UI-A-FRAME-002 | P0 | 填写账号密码点击登录 | 加载态防重复;成功进入首页,失败留在登录页 |
| UI-A-FRAME-003 | P1 | 逐个展开一级菜单 | 二级菜单、图标、顺序和折叠状态正确 |
| UI-A-FRAME-004 | P1 | 依次点击主页、会员、交易、商品、任务下全部二级菜单 | 每个菜单可打开,面包屑和标签页标题正确 |
| UI-A-FRAME-005 | P1 | 点击已打开标签、关闭单个、关闭其他、刷新标签 | 标签行为正确,不关闭固定首页 |
| UI-A-FRAME-006 | P1 | 点击全局菜单搜索并搜索页面名称 | 结果正确,点击进入目标页面 |
| UI-A-FRAME-007 | P1 | 点击右上角头像/个人中心/退出 | 个人中心可达;退出确认后回登录页 |
#### 4.3.2 首页与门户管理
| ID | 优先级 | 页面操作 | 预期结果 |
| --- | --- | --- | --- |
| UI-A-HOME-001 | P1 | 打开主控台,逐个检查指标卡、图表和刷新 | 数据、单位、时间范围、刷新时间正确 |
| UI-A-HOME-002 | P1 | 点击可跳转指标卡 | 进入对应业务列表并携带正确筛选条件 |
| UI-A-SITE-001 | P1 | 打开网站设置,检查所有分组和默认值 | 品牌、交易、佣金、时段、门槛和开关字段完整回显 |
| UI-A-SITE-002 | P1 | 修改字段后点击保存/取消/重置 | 保存有反馈;取消/重置不写入;刷新结果符合选择 |
| UI-A-SITE-003 | P1 | 点击历史、提交、发布、回滚入口 | 对应列表/弹窗完整,按钮只在合法状态可用 |
| UI-A-TEXT-001 | P1 | 打开文本管理,使用查询、重置和分页 | 仅显示文本字段和 12 条初始化数据,内容标识列隐藏 |
| UI-A-TEXT-002 | P1 | 点击新增/编辑,检查弹窗和所有字段 | 弹窗足够宽,无图片字段,标题和正文可编辑 |
| UI-A-TEXT-003 | P1 | 点击确定、取消、关闭、发布、下线、删除 | 每个动作提示和列表刷新正确,危险动作有确认 |
| UI-A-BANNER-001 | P1 | 打开 Banner 设置并测试查询、添加、编辑 | 图片、位置、链接、时段、排序和状态字段齐全 |
| UI-A-BANNER-002 | P1 | 测试上传、预览、更换和移除图片 | 预览正确,取消不保存,失败可重试 |
| UI-A-BANNER-003 | P1 | 测试发布/下线/删除及确认弹窗 | 状态更新且 C 端同步,取消确认无副作用 |
| UI-A-MANUAL-001 | P1 | 打开手工商城并测试查询、添加、编辑 | 名称、价格、展示图片和时间字段完整 |
| UI-A-MANUAL-002 | P1 | 测试发布/下架/删除和分页 | 按钮状态与商品状态匹配,列表刷新正确 |
#### 4.3.3 会员管理页面
| ID | 优先级 | 页面操作 | 预期结果 |
| --- | --- | --- | --- |
| UI-A-MEMBER-001 | P1 | 逐项填写会员查询条件并点击查询/重置 | 每项条件有效,重置清空并回第一页 |
| UI-A-MEMBER-002 | P1 | 拖动表格横向滚动条和操作列独立滚动条 | 所有数据列和操作按钮可达,固定列不漂移 |
| UI-A-MEMBER-003 | P1 | 点击添加、详情、编辑 | 对应弹窗字段完整,详情只读,编辑正确回显 |
| UI-A-MEMBER-004 | P1 | 点击“更多”或操作弹层 | 银行卡、地址、团队、账变、弹幕、奖励、真人/假人、启禁用、删除均可见 |
| UI-A-MEMBER-005 | P1 | 逐个打开银行卡、地址、团队弹窗并关闭 | 标题、表格、按钮和关闭行为正确 |
| UI-A-MEMBER-006 | P0 | 点击授权查看明文,未勾选确认或未填用途 | 确认按钮不可提交或明确校验 |
| UI-A-MEMBER-007 | P1 | 打开账变、奖励金、弹幕、状态弹窗 | 金额/原因/内容/范围字段和风险提示完整 |
| UI-A-MEMBER-008 | P1 | 点击设为真人/假人、启用/禁用、删除并取消确认 | 取消后列表状态不变 |
| UI-A-MEMBER-009 | P1 | 点击分页、每页条数和跳页 | 总数、页码、选中行和滚动位置合理更新 |
| UI-A-MEMBER-010 | P1 | 输入完整或部分注册域名查询,并组合域名状态筛选 | 支持模糊匹配;待绑定、启用域名、失效域名结果准确 |
| UI-A-MEMBER-011 | P1 | 查看会员列表和详情中的注册域名、域名状态、绑定时间 | 三项信息一致;未绑定显示“待绑定”,不显示伪造域名 |
| UI-A-MEMBER-012 | P1 | 检查会员列表标题栏和编辑弹窗 | 后台不显示新增会员入口;只允许编辑已有会员 |
| UI-A-LEVEL-001 | P1 | 打开会员等级,点击新增和编辑 | 等级、金额、次数、佣金、状态字段完整回显 |
| UI-A-LEVEL-002 | P1 | 点击保存、取消、停用/启用、删除 | 每个动作反馈正确,删除显示引用限制 |
| UI-A-HALL-001 | P1 | 打开任务大厅,测试查询、新增、编辑和等级选择 | 字段、图标、比例和等级选择正确 |
| UI-A-HALL-002 | P1 | 测试单删、勾选多行、批量删除和取消 | 选中态与成功/失败数量提示准确 |
| UI-A-URL-001 | P1 | 打开 URL 地址池页面 | 汇总卡、查询栏、列表及操作栏完整;列表按累计绑定次数升序、ID 升序显示 |
| UI-A-URL-002 | P1 | 按地址名称、域名、状态和创建时间查询及重置 | 每项筛选有效,域名支持模糊匹配,重置恢复第一页 |
| UI-A-URL-003 | P1 | 点击新增/编辑,填写地址名称、注册入口 URL、状态和备注 | URL 被标准化并正确回显;非法协议、查询参数、片段或重复域名不能保存 |
| UI-A-URL-004 | P1 | 点击“详情”,查询绑定会员并翻页 | 显示累计/当前绑定次数、最后绑定时间及全部绑定会员;手机号脱敏 |
| UI-A-URL-005 | P1 | 点击“设为失效”或“启用”,再点击复制链接 | 状态立即更新;复制内容为标准化注册入口;页面无删除按钮 |
| UI-A-URL-006 | P1 | 打开 Excel 批量导入弹窗并检查说明、关闭和取消 | 明确必填表头、格式、大小和 5000 条限制;关闭不上传文件 |
| UI-A-URL-007 | P1 | 分别导入合法、重复和含错误行的 XLS-URL | 合法行导入且均为启用;重复跳过;错误行按 Excel 行号展示;汇总与列表刷新 |
| UI-A-MLOGIN-001 | P1 | 打开会员登录日志,按会员、方式、结果、域名、IP 和时间查询 | 筛选、重置、分页和空态正确,不与后台管理员登录日志混用 |
| UI-A-MLOGIN-002 | P1 | 查看成功和失败日志详情 | 请求 ID、会员、方式、结果、脱敏手机/IP、地区、域名、系统和浏览器完整准确 |
| UI-A-MLOGIN-003 | P1 | 按当前筛选条件导出会员登录日志 | 文件可打开,条数和字段与筛选结果一致,无 IP 密文、Token 或密码 |
#### 4.3.4 交易管理页面
| ID | 优先级 | 页面操作 | 预期结果 |
| --- | --- | --- | --- |
| UI-A-ORDER-001 | P1 | 订单列表逐项测试筛选、查询、重置和分页 | 条件生效,状态和金额显示正确 |
| UI-A-ORDER-002 | P1 | 点击详情 | 订单、会员、商品、任务、金额、时间线和流水信息完整 |
| UI-A-ORDER-003 | P0 | 点击“匹配订单”并搜索、清理筛选、选择商品、取消 | 弹窗列表和选中态正确;取消不修改订单 |
| UI-A-ORDER-004 | P0 | 选择商品后点击确定 | 加载态防重复,成功关闭并刷新订单 |
| UI-A-ORDER-005 | P1 | 点击“更改金额”,测试输入、取消和确定 | 金额校验清楚,取消无副作用,成功后刷新 |
| UI-A-ORDER-006 | P0 | 点击“强制付款”“冻结订单”并取消/确认 | 风险文案清晰,取消无副作用,确认结果正确 |
| UI-A-ORDER-007 | P1 | 点击“审核任务”或等待会员付款禁用按钮 | 前者跳会员任务审核,后者不可误点 |
| UI-A-RC-001 | P1 | 充值列表测试筛选、汇总卡、详情和凭证预览 | 筛选准确,汇总与列表一致,凭证可查看 |
| UI-A-RC-002 | P0 | 点击“通过”,填写审核说明,取消/确认 | 取消不入账;确认加载态防重复并刷新状态 |
| UI-A-RC-003 | P0 | 点击“驳回”,测试空原因、合法原因和确认 | 必填校验有效,确认后状态和原因正确 |
| UI-A-WD-001 | P1 | 提现列表测试筛选、详情和收款信息展示 | 金额、手续费、实到、状态和脱敏信息正确 |
| UI-A-WD-002 | P0 | 点击通过/驳回,测试确认弹窗 | 按钮仅在合法状态显示,确认后刷新 |
| UI-A-WD-003 | P0 | 处理中记录点击完成并填写外部单号 | 必填校验、加载态和完成结果正确 |
| UI-A-CS-001 | P1 | 客服管理测试查询、新增和编辑 | 名称、头像、工作时间、在线状态、IM 绑定、排序和状态完整 |
| UI-A-CS-002 | P1 | 点击启用/停用 | 开关与状态标签同步,C 端列表同步 |
| UI-A-CS-003 | P1 | 点击删除,测试空原因、取消和确认 | 空原因阻止;取消无副作用;停用记录可删除 |
#### 4.3.5 商品与任务管理页面
| ID | 优先级 | 页面操作 | 预期结果 |
| --- | --- | --- | --- |
| UI-A-PROD-001 | P1 | 商品列表填写名称/分类并点击查询 | 列表过滤正确,字段及总数完整 |
| UI-A-PROD-002 | P1 | 点击添加/编辑并逐项检查表单 | 商品、分类、价格、店铺、图片、状态、推荐等字段正确 |
| UI-A-PROD-003 | P1 | 点击发布/下架并取消/确认 | 弹窗文案对应当前状态,成功后按钮文字反转 |
| UI-A-PROD-004 | P1 | 点击推荐/不推荐 | 状态和按钮立即更新,不影响发布状态 |
| UI-A-PROD-005 | P1 | 点击删除或勾选后批量删除 | 已引用限制提示清楚,成功/失败数量准确 |
| UI-A-PROD-006 | P2 | 检查操作按钮换行和不同窗口宽度 | 编辑、发布/下架、推荐、删除始终可见可点 |
| UI-A-TASK-001 | P1 | 任务列表测试查询、新增、编辑 | 商品、等级、佣金、详情、时段等字段完整 |
| UI-A-TASK-002 | P1 | 点击发布/下线/删除并取消/确认 | 状态限制和引用提示正确,列表刷新 |
| UI-A-SUB-001 | P1 | 会员任务审核测试查询和详情 | 会员、订单、任务、证据、佣金、状态完整 |
| UI-A-SUB-002 | P0 | 待审记录点击审核,切换通过/驳回 | 提示文字和意见字段随结论变化 |
| UI-A-SUB-003 | P0 | 点击确认审核并连续点击 | 按钮防重复;成功关闭、刷新且只结算一次 |
| UI-A-SUB-004 | P1 | 已审核记录点击详情 | 只读展示结果和意见,不再显示可提交审核按钮 |
#### 4.3.6 组织、权限、系统应用和系统设置页面
| ID | 优先级 | 页面操作 | 预期结果 |
| --- | --- | --- | --- |
| UI-A-ORG-001 | P1 | 后台用户页测试查询、新增、编辑、启停、重置密码、删除 | 每个按钮按权限出现,弹窗和确认流程完整 |
| UI-A-ORG-002 | P1 | 部门页展开/收起树,新增同级/下级、编辑、删除 | 树层级和父节点选择正确 |
| UI-A-ORG-003 | P1 | 岗位页测试查询、新增、编辑、启停、删除 | 表单和状态操作正确 |
| UI-A-MENU-001 | P1 | 菜单页展开树,点击新增、编辑、删除 | 目录/菜单/按钮字段随类型正确显示 |
| UI-A-MENU-002 | P1 | 菜单图标选择、路由、组件、权限标识校验 | 图标预览和字段校验正确 |
| UI-A-ROLE-001 | P1 | 角色页新增/编辑/启停/删除 | 角色树、上级角色和基本字段正确 |
| UI-A-ROLE-002 | P0 | 点击“分配菜单”,勾选父节点、子节点、全选和取消 | 级联/半选正确,保存后重新打开保持一致 |
| UI-A-ROLE-003 | P0 | 子角色打开分配菜单弹窗 | 只能操作父角色权限子集,范围外不可选 |
| UI-A-ROLE-004 | P1 | 点击数据权限并切换范围 | 选项和部门树正确,保存后查询范围生效 |
| UI-A-APP-001 | P1 | 打开系统应用各二级菜单和个人中心 | 页面均可达,个人设置可保存和取消 |
| UI-A-DICT-001 | P1 | 字典类型和字典数据页测试查询、新增、编辑、启停、删除 | 类型与数据联动,弹窗和确认正确 |
| UI-A-CONFIG-001 | P1 | 系统配置逐个切换分组并保存 | 各分组字段无错位,保存反馈和重新加载正确 |
| UI-A-CRON-001 | P1 | 定时任务测试查询、新增、编辑、启停、执行、日志 | 每个按钮可达,执行结果和日志正确 |
| UI-A-BLACK-001 | P1 | 黑名单测试查询、新增、编辑、启停、删除 | 表单、确认、状态和列表刷新正确 |
| UI-A-LOG-001 | P1 | 操作/登录/短信/邮件/服务日志逐页查询、重置、详情 | 筛选、分页、详情和脱敏正确 |
| UI-A-ATT-001 | P1 | 附件页测试预览、下载、筛选和删除确认 | 图片/文件预览正确,被引用附件删除受限 |
## 5. 通用与基础能力测试
| ID | 优先级 | 场景与步骤 | 预期结果 |
| --- | --- | --- | --- |
| GEN-001 | P0 | 未登录直接访问任一需要登录的 C 端路由 | 跳转账号登录页,登录后回到原目标路由 |
| GEN-002 | P0 | 未登录调用任一 C 端受保护 API | 返回 401,响应不包含业务数据 |
| GEN-003 | P0 | 未登录直接访问后台业务路由 | 跳转后台登录页,不短暂显示受保护页面 |
| GEN-004 | P0 | 使用失效、伪造、被注销的 Token 调用接口 | 统一拒绝;前端清理会话并引导重新登录 |
| GEN-005 | P1 | 连续快速点击新增、保存、提交、审核按钮 | 按钮加载中禁用;只产生一次有效写入 |
| GEN-006 | P1 | 刷新列表、切换分页、返回上一页 | 查询条件、分页和数据状态符合页面约定,无重复行 |
| GEN-007 | P1 | 请求返回空数组 | 显示明确空状态,不报错、不展示演示假数据 |
| GEN-008 | P1 | API 返回 400/403/404/409/422/429/500 | 展示可理解提示;500 类保留 request_id;页面可重试 |
| GEN-009 | P1 | 请求中断、离线、超时后恢复网络 | 加载态可结束,可重新请求,不产生幽灵提交 |
| GEN-010 | P1 | 输入前后空格、全角字符、Emoji、HTML/脚本片段 | 按字段规则规范化或拒绝;展示时不执行脚本 |
| GEN-011 | P1 | 两个浏览器同时编辑同一带版本号记录 | 后提交者收到版本冲突并要求刷新,不静默覆盖 |
| GEN-012 | P1 | 租户 A 携带租户 B 的资源 ID 请求详情或编辑 | 返回无权限或不存在,绝不返回跨租户数据 |
| GEN-013 | P1 | 列表页横向滚动、窄屏及操作列按钮较多 | 所有按钮可达;操作列不遮挡,滚动条可操作 |
| GEN-014 | P2 | 页面刷新、前进后退、复制带参数 URL 打开 | 路由、标题、筛选参数正确,无白屏 |
| GEN-015 | P2 | 动态资源发版后旧页面加载过期 chunk | 最多自动刷新一次并恢复,10 秒内不循环刷新 |
## 6. C 端功能测试
### 6.1 注册、登录与会话
| ID | 优先级 | 场景与步骤 | 预期结果 |
| --- | --- | --- | --- |
| C-AUTH-001 | P0 | 用合法姓名、11 位手机号、合法密码、6 位邀请码并勾选协议注册 | 注册成功并建立会话;会员、钱包及上下级关系正确创建 |
| C-AUTH-002 | P1 | 姓名分别输入 0、1、10、11 个字符 | 1~10 个合法文字通过;0 和 11 拒绝 |
| C-AUTH-003 | P1 | 姓名含汉字、字母、组合音标及间隔点“·” | 允许;数字、空格、符号和脚本字符拒绝 |
| C-AUTH-004 | P1 | 手机号测试空值、10 位、11 位非 1 开头、第二位 0/2、含字母、合法 13~19 段 | 仅 `1[3-9]` 开头的 11 位数字通过 |
| C-AUTH-005 | P1 | 密码测试 5 个字符、6 个字符、72 字节、73 字节和多字节字符 | 少于 6 个字符或超过 72 字节拒绝;边界值按规则处理 |
| C-AUTH-006 | P1 | 邀请码测试空值、5 位、6 位、7 位、含字母及不存在的邀请码 | 仅存在的 6 位数字邀请码通过 |
| C-AUTH-007 | P0 | 未勾选隐私协议提交注册 | 注册被拒绝,数据库无会员和钱包残留 |
| C-AUTH-008 | P0 | 使用已注册手机号再次注册 | 明确提示已存在,不新增重复会员 |
| C-AUTH-009 | P0 | 正确手机号密码登录 | 登录成功,记录登录时间/日志,会话 Token 可访问受保护接口 |
| C-AUTH-010 | P0 | 正确手机号配错误密码、未注册手机号登录 | 统一凭证错误提示,不泄露手机号是否存在 |
| C-AUTH-011 | P0 | 同一手机号 15 分钟内连续错误登录 10 次,再试正确密码 | 第 10 次阈值后限流;返回 429;窗口后或策略解除后恢复 |
| C-AUTH-012 | P1 | 错误数未达阈值时成功登录,再次输错 | 成功登录清除此前失败计数 |
| C-AUTH-013 | P0 | 登出后使用旧 Token 请求 `/v1/me` | 旧 Token 失效;返回登录页 |
| C-AUTH-014 | P1 | 会话自然过期时停留在业务页继续操作 | 不提交业务;提示登录失效并跳转登录页 |
| C-AUTH-015 | P2 | 身份验证/短信找回入口点击获取验证码 | 当前未开放时明确提示暂未开放,不显示伪成功、不修改密码 |
| C-AUTH-016 | P0 | 使用 URL-ENABLED 的注册入口完成注册 | 注册成功;会员、钱包和 `hg_member_url_binding` 在同一事务创建,返回绑定成功及标准化域名 |
| C-AUTH-017 | P0 | 从 URL-UNKNOWN 域名或缺少可识别来源的页面注册 | 会员和钱包仍创建成功,不生成域名绑定,不因地址池无匹配记录阻断注册 |
| C-AUTH-018 | P0 | 从 URL-INVALID 的域名注册 | 注册成功但不建立绑定;失效地址累计绑定次数和最后绑定时间不变化 |
| C-AUTH-019 | P1 | 使用大小写域名、末尾点、默认 80/443 端口注册 | 域名按统一规则规范化并命中同一地址,不产生重复地址或重复绑定 |
| C-AUTH-020 | P0 | 同一注册请求重复提交或事务中模拟绑定写入失败 | 不重复创建会员/钱包/绑定;绑定写入失败时整个注册事务回滚 |
| C-AUTH-021 | P0 | 账号密码登录成功 | 更新会员最后登录时间、IP 密文、IP 脱敏值和地区,并新增一条成功会员登录日志 |
| C-AUTH-022 | P1 | 使用错误密码、禁用会员或风控会员登录 | 登录失败且不更新会员主表最后登录字段;分别记录脱敏的失败日志和可理解原因 |
| C-AUTH-023 | P1 | 手机验证码登录成功、验证码错误或过期 | 成功/失败均按手机登录方式记录独立日志;成功时更新会员最后登录信息 |
### 6.2 首页、内容、通知与排行榜
| ID | 优先级 | 场景与步骤 | 预期结果 |
| --- | --- | --- | --- |
| C-HOME-001 | P1 | 打开首页 | Banner、用户摘要、快捷入口、公告/滚动信息及导航正常加载 |
| C-HOME-002 | P1 | 后台关闭领取、储入、取出、商城开关后刷新首页 | 对应入口置灰且不可点击,其他入口不受影响 |
| C-HOME-003 | P1 | 重新开启功能开关 | 对应入口恢复可点击并进入正确页面 |
| C-HOME-004 | P1 | Banner 未到生效时间、已过期、已下线 | C 端不展示;生效期内按排序展示 |
| C-HOME-005 | P1 | Banner 无跳转、内部路由、合法 HTTPS 外链 | 分别不跳转、站内跳转、受控打开外链;非法协议不执行 |
| C-HOME-006 | P2 | Banner 图片失效或加载失败 | 显示占位,不导致页面布局塌陷 |
| C-HOME-007 | P1 | 后台发布平台介绍、规则、代理合作、公司资质文本 | C 端对应内容页显示最新已发布版本 |
| C-HOME-008 | P1 | 后台保存草稿但不发布 | C 端继续显示旧已发布版本 |
| C-HOME-009 | P1 | 下线内容或内容不存在 | C 端显示友好空状态,不泄露草稿正文 |
| C-HOME-010 | P1 | 发布全局通知和指定会员通知 | 目标会员可见;非目标会员不可见;已过有效期不展示 |
| C-HOME-011 | P1 | 标记通知已读并刷新 | 已读状态保持;未读数量正确减少,不影响其他会员 |
| C-HOME-012 | P1 | 排行榜混合真人、假人、禁用用户和同分用户 | 按产品口径展示;禁用/应排除数据不出现;同分排序稳定 |
### 6.3 领取、订单与任务状态机
| ID | 优先级 | 场景与步骤 | 预期结果 |
| --- | --- | --- | --- |
| C-TASK-001 | P0 | 正常会员在营业时间进入领取页 | 总资产、昨日/今日佣金、今日领取数与数据库一致 |
| C-TASK-002 | P0 | 后台关闭任务功能后领取 | 返回 `TASK_DISABLED`,不创建订单 |
| C-TASK-003 | P1 | 在营业开始前 1 秒、开始时刻、结束时刻、结束后 1 秒领取 | 两端点允许,窗口外拒绝 |
| C-TASK-004 | P1 | 营业时段配置为跨午夜,如 22:00~02:00 | 22:00~次日 02:00 可领取,其余时间拒绝 |
| C-TASK-005 | P0 | U-AUTO 余额为最低余额减 1 分、等于最低余额、加 1 分 | 小于阈值拒绝;等于和大于阈值通过 |
| C-TASK-006 | P1 | U-MANUAL 余额低于最低余额进入领取资格检查 | 按手动派单规则返回;不错误套用自动派单最低余额校验 |
| C-TASK-007 | P0 | 当日领取数为上限减 1、等于上限 | 前者可领取;等于上限返回 `DAILY_LIMIT_REACHED` |
| C-TASK-008 | P0 | 存在匹配中、待处理、审核中或冻结订单时再次领取 | 返回 `PENDING_ORDER_EXISTS`,并提供查看原订单入口 |
| C-TASK-009 | P1 | 当前会员等级不属于任务大厅允许等级 | 返回 `LEVEL_NOT_ELIGIBLE`,不创建订单 |
| C-TASK-010 | P0 | 禁用会员或禁用钱包领取 | 返回账户受限,不创建订单及流水 |
| C-TASK-011 | P0 | 连续双击领取或并发发起两次领取 | 最多产生一个在途订单,次数只增加一次 |
| C-TASK-012 | P1 | 自动派单延迟配置为 0、正常值、负值、最大值小于最小值 | 0 立即调度;负值归零;反向区间归一;不 panic |
| C-TASK-013 | P0 | 自动派单匹配已发布、CNY、价格在配置区间内的商品 | 匹配成功,订单快照、商品、金额和佣金正确 |
| C-TASK-014 | P0 | 候选商品为下架、删除、非 CNY 或价格越界 | 不被匹配;无候选时订单保持可解释状态 |
| C-TASK-015 | P0 | 手动派单后会员余额足够并确认提交 | 扣除可用余额、结算佣金、订单完成、生成对应流水 |
| C-TASK-016 | P0 | 手动派单后后台改价导致会员余额不足 | 提交被拒并引导储入;余额、冻结额和订单状态不被错误修改 |
| C-TASK-017 | P0 | 同一订单重复点击确认或并发确认 | 只扣一次、只发一次佣金、只生成一组有效流水 |
| C-TASK-018 | P1 | 订单超过截止时间后确认 | 状态转已超时或拒绝提交,不扣款、不结算 |
| C-TASK-019 | P1 | 查询订单列表 `page=0`、`page=1`、`page_size=1/100/101` | 0 使用默认页;1 和 100 有效;101 拒绝 |
| C-TASK-020 | P1 | 使用 all、matching、pending、processing、completed、expired、frozen、cancelled、failed 过滤 | 返回对应状态;未知状态返回校验错误 |
| C-TASK-021 | P1 | 查看本人订单详情和他人订单 ID | 本人可见完整时间线;他人订单返回不存在/无权限 |
| C-TASK-022 | P1 | 提交凭证正文 0、2000、2001 字符 | 0 和 2000 按当前规则通过;2001 拒绝 |
| C-TASK-023 | P1 | 提交附件 0、9、10 个,或包含 0/负数/重复 ID | 0 和最多 9 个合法唯一 ID 通过;其余拒绝 |
| C-TASK-024 | P0 | 使用其他会员上传的附件 ID 提交任务 | 服务端拒绝,不能越权引用附件 |
| C-TASK-025 | P0 | 提交成功后核对订单、任务提交、钱包和流水 | 状态、佣金、前后余额、业务单号及时间在同一事务内一致 |
| C-TASK-026 | P1 | 后台下线任务后查看已领取订单 | 已领取订单仍可按原快照完成;不能新领取该任务 |
### 6.4 储入、收款账户、取出与账变
| ID | 优先级 | 场景与步骤 | 预期结果 |
| --- | --- | --- | --- |
| C-FIN-001 | P1 | 打开储入页 | 只展示启用渠道,显示渠道最小/最大金额和手续费信息 |
| C-FIN-002 | P0 | 储入金额测试 0、负数、最小值减 1 分、最小值、最大值、最大值加 1 分 | 仅渠道区间内金额可提交 |
| C-FIN-003 | P1 | 渠道最大值为 0 | 按无上限处理,但仍校验正数和数据库金额范围 |
| C-FIN-004 | P1 | 金额输入空值、字母、多小数位、科学计数法、超大数 | 前端不产生错误分值;服务端拒绝非法或溢出输入 |
| C-FIN-005 | P1 | 储入备注 500 和 501 个字符 | 500 通过,501 拒绝 |
| C-FIN-006 | P1 | 储入凭证附件 0、9、10 个 | 0~9 个合法附件通过;10 个拒绝 |
| C-FIN-007 | P0 | 凭证 ID 含重复、0、负数或他人附件 | 拒绝,订单不创建 |
| C-FIN-008 | P0 | 正常提交储入 | 只创建一张待处理充值单,随后跳转客服中心 |
| C-FIN-009 | P1 | 储入提交失败 | 保留输入并展示错误,不跳客服、不产生半成品订单 |
| C-FIN-010 | P1 | 查看储入记录全部状态 | 待支付、审核中、成功、失败、过期、取消文案及金额正确 |
| C-FIN-011 | P1 | 待支付订单创建后 30 分钟边界 | 过期时间显示正确,状态由服务端规则处理 |
| C-FIN-012 | P0 | 创建收款账户,输入合法姓名、身份证、卡号、开户行、两张不同收款码 | 创建成功;通用账户列表仅返回脱敏值,本人详情接口可还原原始字段 |
| C-FIN-013 | P1 | 持有人姓名长度 1、2、64、65,或含控制字符/尖括号 | 2~64 且无危险字符通过,其余拒绝 |
| C-FIN-014 | P1 | 身份证输入 15 位、18 位、末位 X、小写 x、空格或连字符 | 合法格式规范化后通过;非法位数和首位 0 拒绝 |
| C-FIN-015 | P1 | 银行名长度 1、2、100、101 | 2~100 通过,其余拒绝 |
| C-FIN-016 | P1 | 银行卡号长度 7、8、32、33 或含非数字 | 8~32 位数字通过,其余拒绝 |
| C-FIN-017 | P1 | 数字人民币编码空值、5、6、32、33 位或含非数字 | 空值或 6~32 位数字通过,其余拒绝 |
| C-FIN-018 | P0 | 微信码、支付宝码缺一个或使用同一附件 ID | 拒绝;必须是两个不同的合法图片附件 |
| C-FIN-019 | P0 | 上传收款码:空文件、JPEG/PNG/WebP、GIF、伪造扩展名、5MB、5MB+1B | 仅真实 JPEG/PNG/WebP 且不超过 5MB 通过 |
| C-FIN-020 | P0 | 使用其他会员的附件设置头像/收款账户 | 服务端拒绝附件越权 |
| C-FIN-021 | P1 | 未绑卡点击取出 | 引导添加银行卡,不进入无账户确认页 |
| C-FIN-022 | P1 | 账户待验证、已验证、已拒绝时申请取出 | 仅已验证账户可报价和申请 |
| C-FIN-023 | P0 | 后台关闭取出开关后报价和提交 | 均拒绝,不能只在前端置灰 |
| C-FIN-024 | P0 | 取出金额测试渠道最小值±1 分、最大值±1 分、可用余额±1 分 | 严格按渠道和余额边界处理 |
| C-FIN-025 | P1 | 取出手续费为固定、比例、固定+比例三种 | 手续费向上取整规则正确,实到金额=申请金额-手续费 |
| C-FIN-026 | P0 | 手续费小于 0、费率超过 10000、手续费大于等于金额 | 渠道视为无效,禁止提交 |
| C-FIN-027 | P0 | 未设置交易密码申请取出 | 引导在确认弹窗内设置 6 位数字交易密码;设置成功不自动取出,用户输入新密码并再次点击确认申请后才创建订单 |
| C-FIN-028 | P1 | 新交易密码测试 5/6/7 位、非数字、与旧密码相同 | 仅 6 位数字且不同于旧密码通过 |
| C-FIN-029 | P0 | 交易密码错误 4 次、第 5 次及限流后正确输入 | 第 5 次达到阈值后 15 分钟限流;正确密码不能绕过 |
| C-FIN-030 | P0 | 当日提现次数为上限减 1、等于上限 | 前者可申请,等于上限拒绝 |
| C-FIN-031 | P0 | 同一提现请求使用同一 8~80 位幂等键重复提交 | 返回同一业务结果,余额只冻结一次 |
| C-FIN-032 | P1 | 幂等键长度 7、8、80、81 或含非法字符 | 8~80 且仅允许字母数字及 `._:-` 通过 |
| C-FIN-033 | P0 | 两个不同幂等键并发提取超过总余额 | 最多一单成功;钱包不可出现负余额 |
| C-FIN-034 | P0 | 取出成功创建后核对钱包 | 可用余额减少、冻结额增加、版本递增并生成成对流水 |
| C-FIN-035 | P1 | 查看取出记录所有状态 | 待审核、审核中、处理中、成功、失败、驳回、取消正确显示 |
| C-FIN-036 | P1 | 账变筛选 income、expense、all、未知值 | 前三者正确;未知值拒绝 |
| C-FIN-037 | P1 | 账变 `biz_type` 长度 32 和 33 | 32 通过,33 拒绝 |
| C-FIN-038 | P0 | 查看账变前后余额 | 每笔 `after = before ± amount`;相邻流水首尾连续 |
| C-FIN-039 | P0 | 本人登录后请求 `GET /v1/payment-accounts/details` | 只返回当前租户、当前会员的完整姓名、身份证号、银行卡号、已填的数字人民币编码及本人收款码 URL;无账户返回空数组 |
| C-FIN-040 | P0 | 使用未登录会话、另一会员或另一租户访问本人取出信息详情 | 未登录被拒绝;其他身份只能看到各自绑定信息,不能通过请求参数指定目标会员或租户 |
| C-FIN-041 | P0 | 将微信/支付宝附件换成他人附件、失效附件,或两张附件对应关系交叉 | 详情接口不返回非本人或失效图片 URL;页面不把微信码、支付宝码串位 |
| C-FIN-042 | P0 | 数据库中某账户的必填密文损坏后请求本人详情 | 安全失败,不返回部分原始字段,不输出密文、解密错误细节或堆栈 |
| C-FIN-043 | P1 | 同一账户分别请求通用列表和本人详情 | 通用列表的姓名、身份证、卡号、数字人民币编码保持脱敏;仅本人详情返回原值 |
| C-FIN-044 | P1 | 数字人民币编码留空创建账户,随后读取详情 | 保存时不生成该字段的密文、哈希或掩码;详情返回空值,银行卡及收款码仍正常使用 |
### 6.5 商城、个人中心与安全设置
| ID | 优先级 | 场景与步骤 | 预期结果 |
| --- | --- | --- | --- |
| C-MY-001 | P1 | 商城查看推荐和限时标签 | 仅已发布、分类启用、有效期内的手工商城商品出现 |
| C-MY-002 | P1 | 搜索商品名称,输入不存在关键词、前后空格及大小写 | 搜索结果正确;无结果显示空状态 |
| C-MY-003 | P1 | 打开本人可见商品详情及不存在/下架商品 ID | 有效商品正常;无效商品返回友好不存在 |
| C-MY-004 | P1 | 限时商品在开始前、开始时、结束时、结束后 | 仅有效窗口允许领取/展示可用动作 |
| C-MY-005 | P1 | 个人中心展示账号、邀请码、等级、余额 | 与 `/v1/me` 一致,金额单位正确 |
| C-MY-006 | P2 | 点击复制邀请码 | 剪贴板内容准确;失败时给出替代提示 |
| C-MY-007 | P0 | 修改登录密码:旧密码错误、空、新密码 5/6 字符、超过 72 字节、与旧密码相同 | 仅旧密码正确且新密码满足规则并不同于旧密码时成功 |
| C-MY-008 | P0 | 修改登录密码成功后用新旧密码登录 | 新密码可登录,旧密码不可登录;既有会话按当前安全策略处理 |
| C-MY-009 | P1 | 首次设置交易密码时填写旧交易密码 | 拒绝;首次设置不应要求或接受旧交易密码 |
| C-MY-010 | P1 | 已有交易密码时不填/填错旧交易密码 | 分别提示必填/错误;不更新密码 |
| C-MY-011 | P1 | 更换头像上传合法图片并刷新 | 头像更新且只关联本人附件;首页、我的、IM 头像一致 |
| C-MY-012 | P1 | 财务记录、账户明细为空和多页数据 | 空态、分页、金额、状态和时间正确 |
| C-MY-013 | P0 | U-DISABLED 打开个人资料及发起交易 | 可按策略查看必要信息,但所有受限写操作被服务端拒绝 |
| C-MY-014 | P1 | 点击退出并取消/确认 | 取消保留会话;确认清除会话并返回登录页 |
| C-MY-015 | P1 | 从个人信息页修改头像后返回“我的”、首页和客服会话 | 新头像在重新读取资料后保持一致;失败时保留旧头像,不出现仅本页临时预览成功的假象 |
### 6.6 在线客服与 118IM
| ID | 优先级 | 场景与步骤 | 预期结果 |
| --- | --- | --- | --- |
| C-IM-001 | P1 | 打开客服列表 | 展示客服依依、客服琪琪、客服琳琳的名称、女性头像及在线状态 |
| C-IM-002 | P0 | 首次点击客服 | 自动创建/恢复会员 IM 身份、建立好友关系和 P2P 会话 |
| C-IM-003 | P0 | 客服未绑定 IM 用户或租户代码不一致 | 明确提示客服暂不可用,不进入可发送假状态 |
| C-IM-004 | P1 | 好友关系为 none、friend、pending、denied | none 发起申请;friend 进入会话;pending/denied 给出对应提示 |
| C-IM-005 | P0 | 本地保存的 IM 密码失效 | 服务端重置并保存凭据后重试一次,成功恢复或明确失败 |
| C-IM-006 | P0 | IM 登录返回的用户 ID 与本地身份不一致 | 拒绝建立会话,防止串号 |
| C-IM-007 | P1 | 会话打开时加载历史消息 | 历史与实时消息分界明确,顺序、发送人、时间正确 |
| C-IM-008 | P1 | 发送空文本、1 字符、1000 字符、1001 字符 | 空文本不发送;1~1000 可发送;超过输入上限不可提交 |
| C-IM-009 | P0 | 快速连续发送文本或网络抖动 | 不丢失、不重复;发送中/失败/成功状态可见 |
| C-IM-010 | P1 | 断开 WebSocket 后点击重新连接 | 恢复会话并续收消息,不重复展示历史消息 |
| C-IM-011 | P1 | 打开表情面板,切换最近/表情/手势/物品并选择 | 分类与布局正确;表情插入输入框,可随文本发送 |
| C-IM-012 | P1 | 上传 JPEG/PNG/WebP/GIF 图片,大小为 10MB 和 10MB+1B | 合法且不超过 10MB 可发送;超限前端拒绝 |
| C-IM-013 | P1 | 上传 MP4/WebM/QuickTime 视频,大小为 50MB 和 50MB+1B | 合法且不超过 50MB 可发送;超限拒绝 |
| C-IM-014 | P0 | 伪造文件后缀或上传脚本/可执行文件 | 客户端和服务端均拒绝,不能作为可执行内容访问 |
| C-IM-015 | P1 | 大文件上传过程取消、断网或接口 500 | 进度结束并提示失败;输入区恢复可用,可重新选择 |
| C-IM-016 | P0 | 会员发图后客服 Web 端查看 | 接收方拥有附件读取权限,图片正常显示,不返回 403 |
| C-IM-017 | P0 | 客服发图后会员端查看 | 会员可读取;无关第三人不能读取附件 |
| C-IM-018 | P0 | 历史绝对地址和同源相对地址媒体混合加载 | 均能通过授权地址显示;不得因 CORS 修复而绕过 ACL |
| C-IM-019 | P1 | 头像 URL 为空、失效、跨域受限 | 使用稳定占位头像;不影响消息发送 |
| C-IM-020 | P1 | 会话 Token 过期后发送 | 自动重连或明确提示重新连接;消息不静默丢失 |
## 7. 管理后台功能测试
### 7.1 后台登录、菜单、组织与 RBAC
| ID | 优先级 | 场景与步骤 | 预期结果 |
| --- | --- | --- | --- |
| A-RBAC-001 | P0 | 正确/错误后台账号密码登录 | 正确进入首页;错误统一提示并记录登录日志 |
| A-RBAC-002 | P0 | 登录页检查其他登录方式、手机号登录、注册、演示角色 | 这些入口均不显示,只保留账号登录 |
| A-RBAC-003 | P2 | 检查登录页系统名 | 显示“企业办公管理系统” |
| A-RBAC-004 | P1 | 展开组织管理、权限管理、系统应用、系统设置 | 二级菜单均有合适图标且可点击 |
| A-RBAC-005 | P1 | 检查系统应用下个人中心 | 菜单可见并能进入个人设置 |
| A-RBAC-006 | P0 | 为 A-CHILD 分配菜单 | 弹窗只显示 A-PARENT 已有菜单范围或范围外禁选 |
| A-RBAC-007 | P0 | 通过构造请求给子角色分配父角色没有的菜单 | 服务端拒绝,数据库不写入越权关系 |
| A-RBAC-008 | P1 | 勾选一级菜单 | 可按设计级联勾选全部可选子菜单和按钮权限 |
| A-RBAC-009 | P1 | 取消一级菜单或部分子菜单 | 父子半选状态正确,不残留不可达按钮权限 |
| A-RBAC-010 | P0 | 无页面权限用户直接输入 URL | 页面不可访问;对应接口同样返回 403 |
| A-RBAC-011 | P0 | 有页面权限但无新增/编辑/删除按钮权限 | 按钮不显示;直接调用写接口仍被拒绝 |
| A-RBAC-012 | P1 | 新增、编辑、停用部门和岗位 | 树关系、排序、状态和成员关联正确 |
| A-RBAC-013 | P1 | 删除存在下级或在用成员的部门/角色 | 被阻止并提示依赖关系 |
| A-RBAC-014 | P1 | 修改管理员角色后刷新其会话 | 菜单和接口权限按系统策略及时生效 |
| A-RBAC-015 | P0 | 非超管尝试编辑超管或给自己提权 | 服务端拒绝并记录操作日志 |
| A-RBAC-016 | P1 | 菜单新增、编辑、排序、隐藏、外链和按钮权限 | 路由树刷新正确,隐藏菜单可按直接路由策略访问 |
| A-RBAC-017 | P1 | 字典增删改、启停并刷新业务页面 | 使用字典的下拉和标签同步更新,不在前端残留硬编码 |
| A-RBAC-018 | P1 | 修改个人资料和密码 | 校验正确;密码不回显;修改记录可追踪 |
### 7.2 首页、网站设置、文本、Banner 与手工商城
| ID | 优先级 | 场景与步骤 | 预期结果 |
| --- | --- | --- | --- |
| A-PORTAL-001 | P1 | 打开首页看板 | 会员、订单、充值、提现、任务等指标与筛选口径一致 |
| A-PORTAL-002 | P1 | 切换日期、刷新、点击指标卡 | 数据区间正确;跳转明细时继承筛选条件 |
| A-PORTAL-003 | P1 | 无业务数据或统计接口失败 | 分别显示 0/空态或错误提示,不混淆 |
| A-PORTAL-004 | P0 | 网站设置读取、修改并保存 | 字段完整回显;保存生成新版本并立即按当前实现生效 |
| A-PORTAL-005 | P1 | 最小金额大于最大金额、反向时间、负次数、非法 URL | 前后端拒绝并定位字段 |
| A-PORTAL-006 | P1 | 营业时间配置正常窗口、全天和跨午夜 | 保存后 C 端按相同语义执行 |
| A-PORTAL-007 | P0 | 两人同时编辑同一网站配置版本 | 后提交者发生版本冲突,不覆盖先提交者 |
| A-PORTAL-008 | P1 | 提交、发布、回滚网站配置 | 状态流转合法;回滚形成可审计新版本,不篡改历史 |
| A-PORTAL-009 | P1 | 切换领取/储入/取出/商城开关 | C 端入口和服务端行为同步变化 |
| A-PORTAL-010 | P1 | 文本列表初始化数据 | 仅约定的 12 条原始文本;列表不显示“内容标识”列 |
| A-PORTAL-011 | P1 | 新增/编辑文本,正文为空、超长、含脚本和恶意链接 | 必填及长度校验有效;危险 HTML 被拒绝或净化 |
| A-PORTAL-012 | P1 | 文本发布、下线、删除 | C 端只显示已发布版本;有历史引用时保留历史 |
| A-PORTAL-013 | P1 | 编辑文本弹窗 | 宽度适合正文编辑,标题为文本管理语义,不出现商品图片字段 |
| A-PORTAL-014 | P1 | Banner 新增、编辑、排序、发布、下线 | C 端按状态、有效期和排序展示 |
| A-PORTAL-015 | P1 | Banner 上传非图片、超限、异常比例或失效 URL | 阻止或明确提示;列表不出现破损数据 |
| A-PORTAL-016 | P1 | 删除已发布 Banner | 按规则先下线或软删除;C 端立即不可见 |
| A-PORTAL-017 | P1 | 手工商城新增/编辑名称、价格、图片、展示时间 | 保存成功并正确回显,`product_type=2` |
| A-PORTAL-018 | P1 | 手工商品发布/下架/删除 | C 端商城展示同步;已关联历史保留快照 |
| A-PORTAL-019 | P0 | 租户 A 管理门户资源 | 不能查看或修改租户 B 的配置、内容、Banner、商品 |
| A-PORTAL-020 | P1 | 查看配置和内容历史 | 操作者、版本、时间、状态和差异可追溯 |
### 7.3 会员、等级与任务大厅
| ID | 优先级 | 场景与步骤 | 预期结果 |
| --- | --- | --- | --- |
| A-MEM-001 | P1 | 会员列表按账号、手机号、时间、状态、派单模式、类别查询 | 筛选准确,重置恢复默认条件 |
| A-MEM-002 | P1 | 列表检查派单模式和类别 | 派单模式显示自动/手动;类别显示真人/假人 |
| A-MEM-003 | P1 | 操作列在 180px 宽度内横向滚动 | 按钮靠左、可滚动查看全部;细滚动条可拖动 |
| A-MEM-004 | P1 | 操作弹层查看编辑、删除、银行卡、地址、团队、账变、弹幕、奖励、类别、状态 | 按权限显示,按钮尺寸和换行不遮挡 |
| A-MEM-005 | P0 | 新增会员使用重复手机号/邀请码冲突 | 拒绝重复;不产生孤立钱包或资料 |
| A-MEM-006 | P1 | 编辑会员基础资料 | 普通编辑不能直接改钱包余额;敏感字段不明文回显 |
| A-MEM-007 | P0 | 切换自动/手动派单和真人/假人 | 保存后 C 端领取规则及统计口径同步更新 |
| A-MEM-008 | P0 | 授权查看会员、银行卡、地址明文 | 必须填写用途并确认;有单独权限;生成敏感访问日志 |
| A-MEM-009 | P0 | 无敏感权限构造明文查看请求 | 返回 403,日志与错误响应不泄露明文 |
| A-MEM-010 | P1 | 新增/编辑/删除银行卡和地址 | 脱敏展示、归属校验、默认项及软删除正确 |
| A-MEM-011 | P1 | 团队查询层级 1、10、11 | 1~10 可查;超过 10 被限制;上下级关系不串租户 |
| A-MEM-012 | P0 | 封禁/解封个人或团队 | 审批、执行、状态日志完整;C 端权限即时受限/恢复 |
| A-MEM-013 | P0 | 同一状态审批重复执行 | 只执行一次,重复请求不重复写状态日志 |
| A-MEM-014 | P0 | 发起账变后未审批直接执行 | 被拒绝;余额和流水不变化 |
| A-MEM-015 | P0 | 审批通过后执行加款/扣款 | 钱包、版本和不可变流水一致,不能扣成负数(除明确授权场景) |
| A-MEM-016 | P0 | 使用相同幂等键重复执行账变/奖励金 | 仅入账一次 |
| A-MEM-017 | P1 | 创建、查看和撤回会员通知/弹幕 | 目标、来源、有效期正确;撤回后 C 端不再展示 |
| A-MEM-018 | P0 | 删除有余额、在途订单或历史流水的会员 | 禁止物理删除;按规则注销/软删除并保留台账 |
| A-MEM-019 | P1 | 会员等级新增/编辑 | 名称、金额、日订单数、佣金率、日提现数校验正确 |
| A-MEM-020 | P1 | 等级金额为负、次数为小数、佣金率越界 | 保存被拒绝,提示具体字段 |
| A-MEM-021 | P1 | 等级编辑、启停、删除 | 操作列仅编辑/停启用/删除;在用等级不能直接删除 |
| A-MEM-022 | P1 | 等级规则变更后检查在途订单 | 在途订单继续使用规则快照,新订单使用新规则 |
| A-MEM-023 | P1 | 任务大厅新增、编辑、启停、单删和批删 | 等级关联、排序和 C 端入口同步;失败项逐条提示 |
| A-MEM-024 | P1 | 删除被任务或历史订单引用的任务大厅 | 被阻止或仅下线,不破坏历史 |
| A-MEM-025 | P1 | 隐藏菜单“资金调账、会员通知” | 左侧菜单不显示,但会员操作入口和授权接口仍正常 |
| A-MEM-026 | P1 | 检查后台会员列表及 `/appMember/edit` | 页面无新增会员按钮;无 ID 的创建请求被拒绝,不创建会员 |
| A-MEM-027 | P1 | 按注册域名输入完整值、前缀、中间片段和大小写关键词 | 均按标准化域名模糊查询,结果不串租户 |
| A-MEM-028 | P1 | 组合待绑定、启用域名、失效域名状态及其他会员条件查询 | 列表、总数、详情的域名状态一致,地址启停后状态实时变化 |
| A-URL-001 | P1 | 新增 HTTP/HTTPS 地址,省略路径或使用自定义端口 | 省略路径时补 `/register`;保留非默认端口;默认 80/443 端口被规范化 |
| A-URL-002 | P1 | 新增 ftp、带账号、查询参数、片段、空域名或超过 512 字符的入口 | 服务端逐项拒绝并返回明确错误,不写入半成品地址 |
| A-URL-003 | P0 | 新增大小写、默认端口或尾点不同但标准化后相同的域名 | 唯一约束和业务校验均阻止重复地址 |
| A-URL-004 | P1 | 编辑未绑定地址的名称、URL、状态和备注 | 合法字段保存;域名哈希、协议和基础 URL 同步更新 |
| A-URL-005 | P0 | 编辑已有绑定会员的地址并尝试更换域名 | 拒绝更换域名;允许修改名称、状态和备注;历史绑定保持不变 |
| A-URL-006 | P1 | 地址池混合 0、1、相同及较大累计绑定次数后查询 | 始终按使用次数升序、ID 升序稳定排列,分页边界不重复不漏行 |
| A-URL-007 | P0 | 将启用地址设为失效后从该域名注册,再重新启用后注册 | 失效期间注册成功但不绑定;重新启用后新会员正常绑定,历史绑定不删除 |
| A-URL-008 | P0 | 两个会员并发从同一启用域名注册 | 每个会员只产生一个绑定;累计绑定次数精确增加,最后绑定时间更新 |
| A-URL-009 | P1 | 查看地址详情并按会员关键词、绑定时间筛选 | 返回该地址全部绑定会员且手机号脱敏;其他地址会员不混入 |
| A-URL-010 | P1 | 导入合法 `.xlsx`/`.xlsm`,表头为中文或支持的英文别名 | 所有合法地址批量写入且状态统一为启用,创建/更新人及租户正确 |
| A-URL-011 | P1 | 导入缺少必填表头、空文件、非 Excel、超过 10MB 或超过 5000 条 | 整体拒绝并给出明确原因,不发生部分未知写入 |
| A-URL-012 | P1 | 导入文件内重复域名、数据库已有域名和多行格式错误 | 重复项计入跳过数;合法行导入;失败数及最多 100 条行号明细准确 |
| A-MLOGIN-001 | P0 | 分别执行账号成功、账号失败、手机成功和手机失败登录 | `hg_app_member_login_log` 每次各写一条,登录方式、结果、会员 ID 和时间准确 |
| A-MLOGIN-002 | P0 | 检查会员登录日志的账号、手机、IP、请求头及错误信息 | 手机和 IP 仅存/展示脱敏值;原始 IP 仅加密保存;密码、验证码和 Token 不落库 |
| A-MLOGIN-003 | P1 | 登录请求携带 Origin、仅 Host、大小写域名或无有效域名 | 优先记录标准化 Origin 域名;回退 Host;无合法来源时域名为空且不影响日志 |
| A-MLOGIN-004 | P1 | IP 地区解析失败或日志队列不可用时登录 | 登录主流程不被地区解析阻断;队列失败降级同步落库并记录警告 |
| A-MLOGIN-005 | P1 | 按会员、账号、脱敏手机、登录方式、结果、域名、IP 和时间组合查询 | 列表总数、分页、详情及倒序排列准确,不能看到其他租户日志 |
| A-MLOGIN-006 | P1 | 导出大于一页的当前筛选结果 | 导出遵循当前筛选和分页约定;字段脱敏,不包含 `login_ip_cipher` |
### 7.4 交易管理
| ID | 优先级 | 场景与步骤 | 预期结果 |
| --- | --- | --- | --- |
| A-TRADE-001 | P1 | 订单列表按单号、会员、状态、派单方式、时间查询 | 结果准确,金额和状态字典正确 |
| A-TRADE-002 | P1 | 查看订单详情 | 商品/任务快照、时间线、会员、金额、流水完整且脱敏 |
| A-TRADE-003 | P0 | 打开匹配商品弹窗 | 只显示已发布、未删除、CNY、符合价格规则的商品 |
| A-TRADE-004 | P1 | 新商品保存为草稿/下架后搜索匹配 | 不出现;发布后立即可选 |
| A-TRADE-005 | P0 | 给手动订单匹配商品 | 订单进入待会员处理;快照和金额正确,不提前扣款 |
| A-TRADE-006 | P0 | 对同一订单并发执行两次匹配 | 仅一次成功,后一次状态冲突 |
| A-TRADE-007 | P0 | 更改手动订单金额为 0、负数、余额边界及超大值 | 非法值拒绝;合法值更新快照和后续校验 |
| A-TRADE-008 | P0 | 强制付款导致余额为负 | 仅有专门权限可执行;明确二次确认;流水标记强制操作 |
| A-TRADE-009 | P0 | 冻结订单 | 状态变冻结,C 端不可继续提交并提示联系客服 |
| A-TRADE-010 | P0 | 普通任务订单尝试调用手动订单专用操作 | 服务端按订单类型和状态拒绝 |
| A-TRADE-011 | P0 | 提交人工处理审批并由无权限/同一人执行 | 按审批规则拒绝越权或不合规执行 |
| A-TRADE-012 | P1 | 充值列表和金额汇总 | 总额、已支付、未支付、人工审核、人工驳回口径与明细一致 |
| A-TRADE-013 | P1 | 查看充值凭证 | 仅授权人员可见;附件归属和可访问性正确 |
| A-TRADE-014 | P0 | 审核通过待审充值 | 状态成功,钱包仅增加一次,生成充值流水和审核记录 |
| A-TRADE-015 | P0 | 驳回充值 | 状态失败/驳回,余额不增加,原因回显 C 端 |
| A-TRADE-016 | P0 | 重复点击充值通过或并发审核 | 第二次返回已处理;不重复入账 |
| A-TRADE-017 | P0 | 审核已过期、取消或已成功充值单 | 拒绝非法状态迁移 |
| A-TRADE-018 | P1 | 充值审核说明为空、超长、含脚本 | 按决策必填规则校验;展示时转义 |
| A-TRADE-019 | P1 | 提现列表查看脱敏收款账户和风险信息 | 默认脱敏;明文访问单独授权并留痕 |
| A-TRADE-020 | P0 | 通过待审核提现 | 进入处理中;冻结资金保持,审核记录完整 |
| A-TRADE-021 | P0 | 驳回提现 | 冻结额释放回可用余额,只生成一次冲正流水 |
| A-TRADE-022 | P0 | 完成处理中提现 | 状态成功,冻结额扣除,外部单号及完成时间保存 |
| A-TRADE-023 | P0 | 完成失败的提现或重复完成 | 拒绝非法状态;不重复扣冻结额 |
| A-TRADE-024 | P0 | 审核过程中会员钱包并发变化 | 使用锁/版本控制,余额守恒且不丢更新 |
| A-TRADE-025 | P1 | 新增客服,填写名称、头像、工作时间、在线状态、IM 租户和用户 | 保存成功并在 C 端展示 |
| A-TRADE-026 | P1 | 客服启用/停用 | 停用客服不再出现在 C 端新列表,历史会话不被删除 |
| A-TRADE-027 | P1 | 删除启用客服 | 被阻止;停用并填写删除原因后方可软删除 |
| A-TRADE-028 | P0 | 客服 IM 用户重复绑定或绑定不存在用户 | 拒绝或明确报错,不能产生不可连接配置 |
| A-TRADE-029 | P1 | 交易操作权限分别授予客服、财务、运营 | 只显示职责内操作;财务动作不对运营开放 |
| A-TRADE-030 | P0 | 所有资金审核完成后核对台账 | 业务单状态、钱包、流水、审核和审计记录可相互追溯 |
### 7.5 商品、任务与会员任务审核
| ID | 优先级 | 场景与步骤 | 预期结果 |
| --- | --- | --- | --- |
| A-PT-001 | P1 | 商品列表查询、新增和编辑 | 名称、编号、分类、价格、币种、图片、推荐、有效期正确保存 |
| A-PT-002 | P1 | 商品价格测试 0、负数、1 分、超大值、小数精度 | 按分存储;非法或溢出拒绝,页面元/分转换准确 |
| A-PT-003 | P1 | 商品发布/下架按钮 | 状态切换成功;文案、颜色及二次确认正确 |
| A-PT-004 | P0 | 商品发布后打开订单匹配弹窗 | 商品可见;下架后立即不可用于新匹配 |
| A-PT-005 | P1 | 推荐/取消推荐 | 列表状态和 C 端推荐区域同步 |
| A-PT-006 | P1 | 删除未引用商品 | 软删除成功,列表及 C 端不再显示 |
| A-PT-007 | P0 | 删除已被订单引用商品 | 禁止删除,只允许下架;历史订单仍显示商品快照 |
| A-PT-008 | P1 | 批量删除混合可删和被引用商品 | 可删项成功,失败项保留并汇总提示 |
| A-PT-009 | P2 | 商品操作列在常见分辨率下显示 | 按钮自动换行,编辑/发布下架/推荐/删除均可达 |
| A-PT-010 | P1 | 任务新增/编辑并绑定商品和会员等级 | 规则、佣金、详情、有效期和等级关系正确 |
| A-PT-011 | P1 | 任务佣金金额、佣金万分比和数量边界 | 非负、精度和上限校验正确,不发生单位误读 |
| A-PT-012 | P1 | 发布/下线任务 | 发布后目标等级可领取;下线不影响已领取订单 |
| A-PT-013 | P1 | 删除未引用/已引用任务 | 未引用软删除;已引用仅下线并保留历史 |
| A-PT-014 | P1 | 会员任务审核列表按会员、订单、状态筛选 | 返回准确,能从交易订单带条件跳转 |
| A-PT-015 | P1 | 查看提交详情 | 正文、附件、任务/订单、佣金、提交时间完整 |
| A-PT-016 | P0 | 审核通过待审提交 | 在同一事务内更新提交和订单并发放一次佣金 |
| A-PT-017 | P0 | 审核驳回且允许重提 | 保存原因和证据,C 端显示原因并按规则允许重提 |
| A-PT-018 | P1 | 驳回时不填补充说明、通过时不填审核意见 | 页面按当前必填规则阻止提交 |
| A-PT-019 | P0 | 两名管理员并发审核同一提交 | 只有一人成功;佣金只发一次 |
| A-PT-020 | P0 | 已结算提交再次审核或更改结论 | 服务端拒绝,历史结果不可覆盖 |
| A-PT-021 | P0 | 提交所含附件已删除或无权限 | 审核页安全降级,不泄露其他会员附件 |
| A-PT-022 | P1 | 等级规则发布后新旧任务订单 | 老订单使用快照,新订单使用新版本 |
| A-PT-023 | P1 | 任务/商品有效期端点测试 | 开始和结束端点语义统一,前后台结果一致 |
| A-PT-024 | P0 | 将租户 B 商品 ID 绑定到租户 A 任务 | 服务端拒绝跨租户关系 |
| A-PT-025 | P1 | 发布缺商品、缺等级或无有效佣金规则的任务 | 阻止发布并提示缺失项 |
### 7.6 HotGo 系统设置与运维功能
| ID | 优先级 | 场景与步骤 | 预期结果 |
| --- | --- | --- | --- |
| A-SYS-001 | P1 | 系统配置各分组读取、编辑、保存 | 保存成功并按缓存策略生效,敏感密钥不明文回显 |
| A-SYS-002 | P1 | 上传设置限制类型、大小和存储驱动 | 前端提示与服务端实际限制一致 |
| A-SYS-003 | P1 | 黑名单新增、启停、删除并从目标 IP 访问 | 生效范围正确,不误封管理员当前地址 |
| A-SYS-004 | P1 | 定时任务新增、修改 cron、启停、手动执行 | 规则校验、执行日志和状态正确 |
| A-SYS-005 | P1 | 配置非法 cron 表达式 | 保存被拒绝,不影响现有任务 |
| A-SYS-006 | P1 | 附件列表预览、下载、删除 | 权限、MIME 和引用检查正确;被引用附件不盲删 |
| A-SYS-007 | P1 | 通知公告新增、发布、撤回和阅读 | 目标后台用户可见,阅读状态正确 |
| A-SYS-008 | P1 | 操作日志按用户、模块、结果和时间筛选 | 可定位本用例产生的关键写操作 |
| A-SYS-009 | P0 | 日志检查密码、Token、银行卡、身份证和密钥 | 不记录明文敏感数据 |
| A-SYS-010 | P1 | 登录日志检查成功、失败、IP 和设备 | 记录准确且符合脱敏规则 |
| A-SYS-011 | P1 | 在线用户强制下线 | 目标会话失效,不影响其他用户 |
| A-SYS-012 | P1 | 地区数据树查询 | 层级、编码和父子关系正确 |
| A-SYS-013 | P1 | 插件管理页面在无操作权限下访问 | 只读或拒绝,不能安装/卸载插件 |
| A-SYS-014 | P1 | 服务/Redis/数据库短暂不可用 | 页面展示可诊断错误,恢复后无需清库即可继续 |
| A-SYS-015 | P2 | 切换主题、语言、侧栏折叠和标签页 | 状态保持,业务表格和弹窗不变形 |
## 8. 跨端端到端业务场景
| ID | 优先级 | 场景与步骤 | 预期结果 |
| --- | --- | --- | --- |
| E2E-001 | P0 | 后台建等级→建大厅→建商品并发布→建任务并发布→会员领取完成 | 全链路可用,规则和金额一致 |
| E2E-002 | P0 | C 端提交储入→后台通过→C 端查看余额和记录 | 余额增加一次,状态和流水一致,提交后可进入客服 |
| E2E-003 | P0 | C 端申请取出→后台通过→完成 | 可用转冻结再扣除,状态和三阶段时间正确 |
| E2E-004 | P0 | C 端申请取出→后台驳回 | 冻结资金完整退回,生成冲正流水 |
| E2E-005 | P0 | 手动会员领取→客服匹配商品→会员确认 | 匹配、余额校验、扣款和结算闭环正确 |
| E2E-006 | P0 | 自动会员领取→延迟自动匹配 | 在配置延迟内完成,商品金额不超过余额及配置区间 |
| E2E-007 | P0 | 会员提交任务→后台审核通过 | 订单、提交、钱包和佣金流水一致且幂等 |
| E2E-008 | P1 | 后台发布/下线 Banner、文本、手工商品 | C 端仅展示当前有效发布版本 |
| E2E-009 | P1 | 后台封禁会员→会员继续领取/储入/取出/客服 | 所有受限动作按策略拒绝且已有数据可追溯 |
| E2E-010 | P1 | 后台切换功能开关→C 端首页和直连接口测试 | UI 与服务端双重生效,不能绕过置灰入口 |
| E2E-011 | P0 | 会员点击客服→发文字、表情、图片、视频→客服回复 | 双端实时可见,头像、历史、附件 ACL 均正确 |
| E2E-012 | P0 | 子角色仅有充值查看权限登录后台 | 只见允许菜单和查看能力,不能审核或访问其他业务 API |
| E2E-013 | P0 | 后台新增启用地址→会员从该域名注册→查看会员和地址详情 | 注册成功并建立唯一绑定;会员详情、地址绑定会员和累计次数一致 |
| E2E-014 | P1 | 后台将已绑定地址设为失效→旧会员登录→新会员注册 | 旧会员绑定历史保留且显示失效;登录正常记日志;新会员注册成功但不绑定 |
| E2E-015 | P1 | Excel 导入地址→从其中一个域名注册→按域名筛会员→导出登录日志 | 导入地址为启用,注册绑定、会员筛选和登录日志链路完整可追溯 |
| E2E-016 | P0 | 会员个人信息页绑定银行卡(数字人民币留空)→查看本人原始取出信息→打开取出报价→在弹窗设置支付密码→再次确认申请 | 绑卡、本人详情、收款码、报价和密码状态一致;设置密码本身不创建取出单,仅最后一次明确确认产生一张订单 |
## 9. API、安全、数据一致性与异常测试
| ID | 优先级 | 场景与步骤 | 预期结果 |
| --- | --- | --- | --- |
| API-001 | P0 | 请求体增加未知 JSON 字段 | C 端 API 按严格解码返回 400,不忽略可疑字段 |
| API-002 | P0 | 使用 GET 调写接口或 POST 调只读接口 | 返回 405,不执行副作用 |
| API-003 | P0 | 修改请求中的 member_id、tenant_id、operator_id | 服务端以会话身份为准,忽略或拒绝越权字段 |
| API-004 | P0 | 数值传字符串、浮点、null、超出 int64 | 返回字段校验错误,不截断、不溢出 |
| API-005 | P1 | 分页 page=-1、page_size=-1/0/101、极大页码 | 非法值拒绝或按明确默认;极大页返回空数组 |
| API-006 | P0 | 将他人订单、充值、提现、附件、会话 ID 逐项替换 | 均不可读取或修改 |
| API-007 | P0 | 重放充值审核、提现执行、任务结算、账变请求 | 状态机/幂等保护生效,不重复记账 |
| API-008 | P0 | 在事务中途模拟数据库错误 | 整个事务回滚,不出现订单成功但无流水等半状态 |
| API-009 | P0 | 高并发更新同一钱包 | 乐观锁/行锁生效,最终余额守恒,版本连续 |
| API-010 | P0 | 校验每个资金业务单的流水唯一键 | 唯一且可反查业务单;冲正指向原流水 |
| API-011 | P0 | 访问上传文件使用路径穿越、双扩展名和恶意 MIME | 被拒绝或安全下载,不可执行服务器文件 |
| API-012 | P0 | 富文本插入 script、onerror、javascript URL、未知 iframe | 存储或渲染前净化,CSP 下不执行 |
| API-013 | P0 | 搜索框输入 SQL 元字符及超长条件 | 参数化查询,无 SQL 注入、无 500 |
| API-014 | P0 | 检查 API、浏览器缓存和导出中的敏感字段 | 通用列表、导出中的身份证和卡号默认脱敏;仅已认证的本人取出信息详情可返回原值且禁缓存;密码、密钥永不返回 |
| API-015 | P0 | 跨域站点调用带凭证业务接口 | CORS 只允许配置源;不使用通配凭证策略 |
| API-016 | P1 | 同一接口连续触发限流 | 返回 429 和合理重试时间,不永久锁死 |
| API-017 | P1 | 数据库中制造孤立引用后打开列表/详情 | 页面可降级并提示,不 panic;提供数据修复线索 |
| API-018 | P1 | Redis 短暂不可用时登录、限流、资金提交 | 安全失败,不绕过认证/限流/幂等;恢复后可继续 |
| API-019 | P1 | IM 服务不可用、慢响应、证书错误 | 客服页可重试并给备用提示,不影响其他业务页面 |
| API-020 | P1 | 检查所有错误响应 | 结构统一包含 code/reason/message/request_id,生产不返回堆栈和 SQL |
| API-021 | P0 | 无 `/urlPool/import`、`/urlPool/status` 或会员登录日志权限直接调用对应接口 | 返回 403;不能通过隐藏按钮、构造请求或导出绕过权限 |
| API-022 | P0 | 跨租户传入地址池 ID、绑定会员 ID 或会员登录日志 ID | 统一返回无权限或不存在,不泄露域名、会员及登录信息 |
| API-023 | P0 | 未登录请求 `GET /v1/payment-accounts/details`,或使用其他 HTTP 方法调用 | 未登录拒绝、非 GET 返回 405;响应不包含任何原始绑定信息 |
| API-024 | P0 | 登录后请求本人取出信息详情并检查响应头、代理缓存、浏览器缓存和访问日志 | 响应设置 `Cache-Control: no-store`;原始身份证、卡号及数字人民币编码不进入缓存或日志 |
## 10. 兼容性、易用性与可恢复性
| ID | 优先级 | 场景与步骤 | 预期结果 |
| --- | --- | --- | --- |
| UX-001 | P1 | iOS Safari、Android Chrome、桌面 Chrome/Edge 最新两个主版本执行冒烟 | 核心流程一致,无白屏和不可点击控件 |
| UX-002 | P1 | C 端宽度 320、375、390、430px | 无横向溢出;底部导航、弹窗和键盘区域可用 |
| UX-003 | P1 | 后台 1366×768、1440×900、1920×1080 | 表格、固定操作列、弹窗和滚动条可操作 |
| UX-004 | P2 | 浏览器缩放 80%、100%、125%、150% | 关键按钮和文字不被裁切 |
| UX-005 | P2 | 键盘 Tab、Enter、Esc 操作表单和弹窗 | 焦点顺序合理,危险操作不被误触 |
| UX-006 | P2 | 图片慢加载、长用户名、长商品名、长错误信息 | 截断/换行合理,详情仍可查看完整内容 |
| UX-007 | P1 | 提交后刷新或返回 | 已成功业务不重复提交;未成功表单按安全策略保留或清空 |
| UX-008 | P1 | 服务发布期间页面打开旧版本 | 静态资源恢复机制有效,API 兼容或给出刷新提示 |
## 11. 发布前最小回归集
每次发布至少执行以下 P0 链路:
1. 正常登录、错误登录限流、退出后 Token 失效。
2. 首页四个功能开关与服务端直连接口同时生效。
3. 自动领取、手动匹配、余额不足、重复领取和重复确认。
4. 储入提交、后台通过、余额及流水核对。
5. 取出报价、未设支付密码时的弹窗设置与关闭、再次确认申请、幂等提交、后台通过/驳回/完成及余额核对。
6. 商品发布后可匹配、下架后不可匹配、已引用商品不可删除。
7. 任务发布、会员提交、后台审核及佣金只结算一次。
8. 子角色菜单不超过父角色;无按钮权限时接口也不可调用。
9. 客服会话首开、断线重连、文本、表情、图片、视频及双端附件显示。
10. 跨租户、跨会员 IDOR、通用列表脱敏、本人取出信息原文与禁缓存、操作日志检查。
11. 启用域名注册绑定、失效/未知域名注册不绑定,以及会员注册域名筛选。
12. 地址池新增、启停、详情、使用次数排序、Excel 导入及会员登录日志查询导出。
13. 个人信息页头像选图更新、取出信息入口、数字人民币选填和本人绑定信息原文展示。
## 12. 准入、准出与缺陷判定
### 12.1 测试准入
- 前后端构建通过,数据库迁移和种子数据在空库及存量库各验证一次。
- C 端、后台、MySQL、Redis、上传目录及 IM 环境可访问。
- 产品配置、测试账号、支付/提现渠道、商品和任务数据已准备。
- 本次变更清单、数据库脚本、回滚方案和发布版本号明确。
### 12.2 发布准出
- P0 用例 100% 通过,P1 通过率不低于 98%。
- 不存在未关闭的阻断、资金错误、越权、数据泄露或不可逆数据损坏缺陷。
- 所有失败用例均有缺陷单、影响范围、临时措施和修复计划。
- 资金抽样满足:业务单金额 = 钱包变化 = 流水金额;重复请求不重复入账。
- 核心业务日志包含 request_id、业务单号和操作者,且无敏感明文。
### 12.3 缺陷记录模板
| 字段 | 内容 |
| --- | --- |
| 缺陷标题 | `[端/模块][严重级别] 简明现象` |
| 环境与版本 | 前端版本、后端版本、数据库迁移版本、浏览器/设备 |
| 前置数据 | 租户、账号代号、订单号、配置版本;不得粘贴真实敏感信息 |
| 复现步骤 | 可重复的最小步骤 |
| 实际结果 | 页面、接口状态码、reason、request_id、必要截图 |
| 预期结果 | 引用本文件用例 ID |
| 数据影响 | 涉及表、记录数、金额、是否可回滚 |
| 附件 | 脱敏截图、HAR、日志片段、SQL 核对结果 |
## 13. 执行记录模板
| 用例 ID | 执行人 | 日期 | 环境/版本 | 结果(通过/失败/阻塞) | 缺陷号 | 备注 |
| --- | --- | --- | --- | --- | --- | --- |
| 示例:C-FIN-031 | | | | | | |
---
说明:配置型金额、次数和时间不在本文写死具体业务数值。执行时应读取当前已发布配置,并使用“下限减 1、下限、下限加 1;上限减 1、上限、上限加 1”的方法验证。代码固定限制(例如分页最大 100、图片 5MB/10MB、视频 50MB、备注 500 字、任务正文 2000 字、附件最多 9 个)已在对应测试用例中明确列出。
@@ -1,349 +0,0 @@
# Olons 功能测试执行报告(2026-10-16)
## 1. 执行说明
- 测试依据:`Olons全功能测试用例.md`(用户补充后的 548 条版本)
- 执行重点:优先从页面真实操作验证,其次执行构建与后端自动化检查
- C 端环境:`https://olns-web.118im.com/admin/`
- 管理后台环境:`https://olons-admin.118im.com/admin`
- 执行策略:第一轮仅做不破坏现有业务数据的页面检查;2026-10-16 用户随后授权使用专用测试数据执行新增、删除、审核、资金及配置变更测试
- 状态含义:`通过`、`部分通过`、`失败`、`阻塞`、`未执行`
## 2. 第一轮结论
第一轮已完成 C 端主要页面的真实加载、数据回显及主要入口检查,并执行了两套前端生产构建和后台 Go 测试。当前发现 1 个明确的页面功能缺陷、1 个测试工程问题;管理后台逐按钮测试因执行机器锁屏暂时中断,锁屏问题不计入产品缺陷。
| 类别 | 结果 |
|---|---|
| C 端生产构建 | 通过 |
| 管理后台生产构建 | 通过(需绕过 pnpm 的自动安装检查,直接使用现有依赖构建) |
| 后台 Go 全量测试 | 失败:Casbin、TCP、存储器用例依赖运行环境或测试数据,出现 panic |
| C 端页面冒烟 | 首页、领取、记录、商城、储入、取出、我的、个人信息、客服中心均可加载 |
| 明确产品缺陷 | 1 个:已有收款账户未在“绑定信息”页回显 |
| 后台页面逐按钮 | 阻塞:执行机器进入锁屏,待解锁续测 |
## 3. C 端页面执行记录
| 页面/关联用例 | 状态 | 实际结果 |
|---|---|---|
| 首页 `/app/home`;UI-C-HOME-001、UI-C-HOME-003、UI-C-HOME-004 | 部分通过 | 页面正常显示会员信息、余额、领取/储入/取出/商城四个入口、排行、平台介绍、规则说明、公司资质、代理合作及底部导航;本轮未执行会产生业务状态变化的领取动作 |
| 领取 `/app/claim`;UI-C-CLAIM-001、UI-C-CLAIM-005 | 通过 | 页面正常显示总资产、昨日佣金、今日佣金、今日领取数;当前会员存在未完成订单,页面正确提示“存在未完成订单”并提供“查看任务”入口 |
| 订单 `/app/orders`;UI-C-ORDER-001、UI-C-ORDER-002 | 部分通过 | 全部、待处理、已完成标签及订单卡片正常显示;本轮未对订单执行提交或取消 |
| 商城 `/app/mall`;UI-C-MALL-001、UI-C-MALL-002 | 部分通过 | 搜索、推荐、限时区域及商品卡片正常显示;“验收临时商品-商品页已编辑”可见 |
| 储入 `/app/recharge`;UI-C-RECHARGE-001、UI-C-RECHARGE-002 | 部分通过 | 等级说明、微信/支付宝/银行卡/人工审核渠道、金额、姓名及提交入口正常显示;未执行真实提交 |
| 取出 `/app/profile/withdrawal`;UI-C-WITHDRAW-001、UI-C-WITHDRAW-002 | 部分通过 | 可用余额、全部提现、已绑定账户 `icbc 6655******4555` 及申请提现入口正常显示;未执行真实提现 |
| 我的 `/app/profile`;UI-C-PROFILE-001、UI-C-PROFILE-002 | 通过 | 会员 potter、邀请码 100005、余额及个人中心入口正常显示 |
| 个人信息 `/app/profile/info`;UI-C-FORM-001、UI-C-FORM-002 | 通过 | 头像、手机号、取出信息、密码管理、支付密码管理入口正常显示 |
| 绑定信息 `/app/profile/bank-card`;UI-C-FORM-003、UI-C-FORM-010、C-FIN-039 | 失败 | 该会员在取出页已有银行卡,但绑定信息页仍显示空白新增表单,没有回显本人原始账户信息 |
| 客服中心 `/app/support`;UI-C-SUPPORT-001 | 通过 | 客服依依、客服琪琪、客服琳琳均显示;在线/离线状态、工作时间和联系电话正常显示 |
## 4. 缺陷记录
### DEF-C-001 已绑定收款账户未在“绑定信息”页回显
| 项目 | 内容 |
|---|---|
| 严重级别 | P1 |
| 关联用例 | UI-C-FORM-003、UI-C-FORM-010、UI-C-FORM-011、C-FIN-039 |
| 前置条件 | 会员已登录,且已有收款账户 |
| 复现步骤 | 1. 打开“我的 → 取出”,确认页面显示 `icbc 6655******4555`;2. 打开“我的 → 个人信息 → 取出信息/绑定信息” |
| 预期结果 | 页面显示本人姓名、身份证号、银行名、完整卡号,以及已上传的微信/支付宝收款码;允许按业务规则编辑或继续新增 |
| 实际结果 | 页面只显示空白的新增绑定表单,已有账户信息未回显 |
| 影响 | 用户无法从个人中心核对已有收款资料,容易重复绑定或误判资料丢失 |
| 建议检查 | 检查页面是否调用 `/v1/payment-accounts/details`;确认接口返回账户列表后是否被表单初始化逻辑覆盖;多账户场景需逐条展示并保持收款码对应关系 |
补充诊断:
- 已核对线上 C 端静态资源,当前发布包包含 `/v1/payment-accounts/details`、`已绑定的取出信息` 和 `添加银行卡`,不是前端漏发版。
- 已只读核对测试库:会员 `potter`(member_id=9、tenant_id=0)确有 1 条未删除的 `icbc` 收款账户,脱敏卡号为 `6655******4555`,密文字段及微信/支付宝附件 ID 均非空。
- 本地代码的详情接口会解密敏感字段;只要服务端 finance key 未配置、与写入时密钥不一致,或详情接口返回异常,页面就会保留空数组并显示新增表单。下一步应抓取该登录会话下 `/v1/payment-accounts/details` 的真实响应,重点检查 `OLONS_SERVER_FINANCE_MASTER_KEY` 与该账户写入时使用的密钥是否一致。
## 5. 构建与自动化检查
### 5.1 C 端前端构建
- 目录:`olons_server/web`
- 命令:`pnpm run build`
- 结果:通过
- 构建结果:4186 个模块完成转换,生产包成功生成
### 5.2 管理后台前端构建
- 目录:`olons_admin/web`
- 常规 `pnpm run build`:被 pnpm 的依赖脚本安全检查阻断,提示 `ERR_PNPM_IGNORED_BUILDS`
- 使用已有依赖直接执行 Vite 与 HotGo postBuild:通过
- 构建结果:6728 个模块完成转换,`dist/app.config.js` 正常生成
- 说明:这是本机 pnpm 依赖脚本策略问题,不是业务代码编译失败;CI 应固定 pnpm 版本及 `onlyBuiltDependencies/approve-builds` 配置,避免发布时临时阻断
### 5.3 后台 Go 测试
- 目录:`olons_admin/server`
- 命令:`go test ./...`
- 结果:失败
- 失败 1:`internal/library/casbin.TestNew`,错误为 `casbin model file does not exist:manifest/config/casbin.conf`
- 失败 2:`internal/library/network/tcp_test.TestSendMsg`,依赖的 `127.0.0.1:8002` 未启动;连接失败后仍调用空连接的 `Send`,触发空指针 panic
- 失败 3:`internal/library/storager.TestMergePartFile`,测试数据/参数无效后仍继续使用空对象,触发空指针 panic
- 判断:上述均属于测试工程对当前目录、外部服务或本地文件的隐式依赖,同时缺少失败后的安全返回;本轮 `internal/logic/sys` 等核心业务包测试通过
- 建议:Casbin 测试使用可确定的项目根路径或 `testdata`;TCP 测试自建临时监听服务;存储测试自建临时文件;所有前置失败后立即 `t.Fatal` 或返回,避免二次 panic
### 5.4 线上可用性与未登录鉴权
| 检查项 | 实际结果 | 结论 |
|---|---|---|
| `GET https://olns-web.118im.com/api/health` | HTTP 200 | 通过 |
| 未登录访问 `GET /v1/me` | HTTP 401 | 通过 |
| 未登录访问 `GET /v1/payment-accounts/details` | HTTP 401 | 通过 |
| 管理后台入口 | HTTP 200 | 通过 |
## 6. 阻塞与待执行项
1. 管理后台 URL 地址池、会员登录日志及其他菜单逐按钮验收:执行机器锁屏,待解锁后续测。
2. 新增、编辑、删除、发布/下架、审核、充值、提现、任务提交、客服发消息等写操作:需要专用测试数据并确认允许产生或清理测试记录后执行。
3. URL 地址池 Excel 合法、重复、错误行导入:需要使用用例中的 XLS-URL 文件执行,会产生数据库写入,暂未提交。
4. 多账户详情、收款码图片一致性、敏感信息展示权限:需在专用测试会员上执行,避免暴露或修改真实会员资料。
## 7. 下一轮计划
1. 解锁后从后台菜单真实点击进入 URL 地址池,依次验证查询、重置、新增/编辑弹窗、详情、启用/失效、复制链接、Excel 导入弹窗及无删除按钮。
2. 验证会员登录日志查询、分页、详情和导出入口。
3. 按菜单顺序完成交易、商品、任务、组织、权限、系统应用、系统设置页面的所有可逆操作。
4. 建立专用测试会员、商品、订单和 URL 地址数据,再执行会改变业务状态的 P0/P1 用例。
5. 修复 DEF-C-001 后,执行绑定信息与提现账户关联回归。
## 8. 第二轮后台页面执行记录(解锁后)
### 8.1 URL 地址池与会员登录日志
| 页面/关联用例 | 状态 | 实际结果 |
|---|---|---|
| URL 地址池;UI-A-URL-001、002 | 通过 | 汇总卡、筛选、展开/收起、查询、重置、分页和空状态正常;重置可清空条件 |
| URL 地址池新增;UI-A-URL-003 | 部分通过 | 新增弹窗包含地址名称、注册入口 URL、状态、备注及保存/取消;未提交写库 |
| URL 地址池详情/启停/复制;UI-A-URL-004、005 | 阻塞 | 当前列表 0 条,无可操作数据 |
| URL Excel 导入;UI-A-URL-006 | 部分失败 | 格式说明、关闭入口、`.xlsx/.xlsm`、5000 条限制、重复跳过和错误行说明完整,但弹窗没有“取消”按钮 |
| 会员登录日志;UI-A-MLOGIN 系列 | 部分通过 | 会员、方式、状态、域名、IP、时间筛选齐全;查询、重置、展开/收起和导出均可用;当前列表 0 条,详情无法验证 |
### 8.2 交易管理
| 页面/关联用例 | 状态 | 实际结果 |
|---|---|---|
| 订单列表;UI-A-ORDER-001 | 部分失败 | 单号、用户名、手机号、状态、时间筛选可用,但缺少用例要求的“派单方式”筛选 |
| 订单详情;UI-A-ORDER-002、A-TRADE-002 | 失败 | 仅显示订单号、会员、商品/任务、等级、金额和规则快照;缺少时间线、资金流水等追溯信息 |
| 匹配订单;UI-A-ORDER-003、A-TRADE-003 | 通过 | 弹窗可按商品名和价格筛选;发布商品可见,保时捷跑车、新手体验商品等均能查询;清理筛选和取消正常 |
| 充值列表;UI-A-RC-001 | 部分失败 | 汇总卡、列表、通过/驳回入口正常;多条真实记录的“真实姓名”列为空,仅在备注中出现登录名 |
| 充值通过/驳回;UI-A-RC-002、003 | 部分通过 | 通过弹窗包含实付金额、外部支付单号和审核说明;驳回弹窗包含原因与说明;取消无副作用,未执行最终审核 |
| 提现列表;UI-A-WD-001 | 部分通过 | 金额、手续费、实到、状态和银行卡脱敏正常;当前 potter 记录的支付宝/微信收款码均显示“-”,需结合账户附件继续核对 |
| 提现通过/驳回;UI-A-WD-002 | 通过 | 合法状态显示按钮,二次确认文案正确,取消无副作用;未执行最终审核 |
| 客服管理;UI-A-CS-001、002 | 部分通过 | 查询、新增/编辑表单、IM 账号和备用联系方式完整;启停操作即时生效,测试后已恢复原状态;未执行删除 |
### 8.3 商品、任务和权限
| 页面/关联用例 | 状态 | 实际结果 |
|---|---|---|
| 商品列表;UI-A-PROD-001、006 | 通过 | 名称/分类筛选、添加、批量删除入口完整;编辑、发布/下架、推荐/不推荐、删除均可见且操作列未截断 |
| 商品表单;UI-A-PROD-002 | 部分通过 | 名称、分类、店铺、价格、图片、详情字段完整;未保存写库 |
| 任务列表;UI-A-TASK-001 | 失败 | 列表、状态操作和任务基本字段正常,但新增任务弹窗没有“商品”选择字段,与用例和任务关联设计不一致 |
| 会员任务审核;UI-A-SUB-001、004 | 失败 | 列表“任务标题”全部为空;详情中的“任务”也为空,并且没有订单信息,不能完成会员—订单—任务追溯 |
| 角色菜单权限;UI-A-ROLE-002、003 | 部分通过 | 弹窗明确限制为父角色权限子集;主页、组织管理、系统应用、系统设置等根节点均可勾选;勾选组织管理后“后台用户”自动选中,父子级联正常;未提交保存 |
| 系统应用菜单;UI-A-APP-001 | 通过 | 日志管理、附件管理、个人中心入口均存在,个人中心已恢复可见 |
## 9. 第二轮新增缺陷
### DEF-A-001 URL 地址池 Excel 导入弹窗缺少取消按钮
- 严重级别:P2
- 关联用例:UI-A-URL-006
- 实际结果:弹窗仅提供右上角关闭,没有底部取消按钮。
- 影响:与后台其他弹窗交互不一致,触屏或窄屏下关闭入口不够明显。
### DEF-A-002 订单详情缺少完整业务追溯信息
- 严重级别:P1
- 关联用例:UI-A-ORDER-002、A-TRADE-002、A-TRADE-030
- 实际结果:详情只展示基础字段和 JSON 规则快照,没有订单状态时间线、付款/冻结/佣金流水、审核记录。
- 影响:客服和财务无法从单一页面核对订单状态与资金变化。
- 代码定位:`TradeOrderViewModel` 只在列表模型上补充 `taskId/productId/memberLevelId/ruleSnapshot`,`trade_order.go` 的 `View` 也只组装这些字段;前端详情弹窗没有请求或渲染资金流水、审批动作和状态时间线。
### DEF-A-003 多个后台交易页面直接显示完整手机号
> 处理结论(2026-10-16):产品方确认会员列表、详情及相关业务页面允许显示完整手机号,本项改判为“已接受设计”,不再作为缺陷,不实施手机号脱敏改造。
- 严重级别:P1
- 涉及页面:会员列表/详情、订单列表/详情、充值列表、会员任务审核列表/详情
- 实际结果:显示 `13311111111`、`15122421111` 等完整手机号。
- 预期结果:默认脱敏;只有具备单独敏感信息权限的人员才能查看明文,且需留痕。
- 代码定位:订单和任务审核查询直接选择 `hg_app_member.mobile`,前端表格直接绑定 `mobile`;当前链路没有脱敏 DTO,也没有敏感字段权限判断。
- 补测结果:会员详情明确提供“授权查看明文”按钮,但未授权前手机号已经完整显示,说明当前授权入口只停留在 UI,未形成真正的数据隔离。
### DEF-A-004 订单列表缺少派单方式筛选
- 严重级别:P1
- 关联用例:UI-A-ORDER-001、A-TRADE-001
- 实际结果:高级筛选只有用户名、手机号、订单状态和下单时间,没有自动/手动派单方式。
- 代码定位:前端 `trade/order/model.ts` 的 `schemas` 和后端 `TradeOrderListInp` 均没有派单方式查询字段,属于前后端共同缺项。
### DEF-A-005 新增任务弹窗缺少商品字段
- 严重级别:P1
- 关联用例:UI-A-TASK-001
- 实际结果:弹窗包含任务大厅、等级、佣金、数量、周期、封面和详情,但没有关联商品选择。
- 影响:无法按设计建立任务与商品的明确关联。
- 代码定位:`hg_task` 对应实体和 `TaskEditInp` 均没有 `product_id`,不是单纯前端漏控件,需要先确认任务是否应直接关联商品,再同步迁移、实体、接口和页面。
### DEF-A-006 会员任务记录缺少任务标题和订单关联
- 严重级别:P1
- 关联用例:UI-A-SUB-001、UI-A-SUB-004
- 实际结果:列表的任务标题为空;详情的任务字段为空;详情未显示订单。
- 影响:审核记录无法追溯到具体任务和订单,已结算记录也难以审计。
- 数据与代码定位:只读核对最新记录,`task_id` 均为 `0`,但 `trade_order_id` 有值;`task_submission.go` 只按 `s.task_id = t.id` 关联任务,没有通过交易订单/商品兜底,因此标题必然为空。审核逻辑还会再次按 `task_id` 查任务,待审核记录可能直接报“关联任务不存在”。
### DEF-A-007 充值列表真实姓名缺失
- 严重级别:P1
- 关联用例:UI-A-RC-001、A-TRADE-012
- 实际结果:多条真实充值记录的“真实姓名”列为空,备注中仅显示 `姓名:potter` 等登录名。
- 影响:人工审核无法直接核对付款人真实姓名,容易造成误审。
## 10. 第三轮后台页面执行记录
### 10.1 主页与门户配置
| 页面/关联用例 | 状态 | 实际结果 |
|---|---|---|
| 商城统计首页;UI-A-HOME-001、002 | 失败 | 今日默认区间无汇总;切换近 30 日后能显示指标和微信/支付宝/银行卡渠道数据,但数据截止时间为 `2026-10-11 13:46:46`,页面提示延迟约 107 小时,汇总任务明显停滞 |
| 网站设置;UI-A-SITE-001、002 | 部分失败 | 表单字段和保存入口完整,但线上已保存配置中隐私网址为空,大量标记“必填”的交易阈值、次数、佣金和自动派单值均为 0;字段“未订单的支付等待时长”文案不通顺,疑为“未支付订单” |
| 文本管理;UI-A-CONTENT-001~004 | 通过 | 初始化数据共 12 条,与原型数量一致;列表已隐藏内容标识;新增、编辑、版本、预览、上下线和删除入口完整;本轮仅打开新增、版本和预览并取消,未写库 |
| Banner 设置;UI-A-BANNER-001、002 | 失败 | 6 条记录状态与操作入口可见,但列表图片列全部为空;打开编辑后,必填 Banner 图片也未回显,现有演示记录不满足接口对图片必填的约束 |
| 手工商城;UI-A-MANUAL-001~003 | 失败 | 4 条记录可查询、编辑和删除,但图片列全部为空;编辑弹窗的必填展示图片未回显;页面没有发布/下架入口,也不显示发布状态 |
### 10.2 会员管理补测
| 页面/关联用例 | 状态 | 实际结果 |
|---|---|---|
| 会员列表;UI-A-MEMBER-001、002 | 部分失败 | 17 条会员可查询,类别与派单模式显示正确,操作列横向滚动条可用;但页面声明“会员资料默认脱敏展示”,列表和详情仍直接显示完整手机号 |
| 会员详情敏感信息;UI-A-MEMBER-003 | 失败 | 详情存在“授权查看明文”按钮,但尚未授权时手机号已经是明文,授权入口没有起到敏感数据隔离作用 |
| 会员银行卡信息;UI-A-MEMBER-004、C-FIN-039 | 失败 | 查询 `potter` 后打开银行卡信息,只有银行名 `icbc` 回显;开户人、开户电话、卡号、支付宝/微信收款码全部为空,与提现页已存在的账户及数据库记录不一致 |
| 会员等级;UI-A-LEVEL-001~003 | 部分通过 | 7 个初始化等级、价格、赠送金额、接单/提现次数、佣金比例、会员数和编辑/启停/删除入口正常;图标列均为空;删除有二次确认并提示仅无会员且未被任务大厅引用时可删,本轮取消未提交 |
| 任务大厅;UI-A-HALL-001~003 | 失败 | 6 条初始化记录可编辑/删除,但图标列为空;后端已有发布状态和状态接口,页面却不显示状态,也没有发布/下架操作 |
| 后台用户;UI-A-ADMIN-001、002 | 部分通过 | 菜单真实路径为 `/org/user`,新增表单字段完整;空表单提交能阻止保存并提示填写用户名/密码;当前角色范围列表为 0 条,编辑、状态和删除无法继续验证 |
| 管理后台登录日志;UI-A-LOG-001~003 | 通过 | 共 30 条记录,查询、重置、展开、导出、详情、单条/批量删除入口完整;本轮未删除日志 |
### 10.3 本轮操作安全说明
- 文本新增、Banner 编辑、手工商城编辑仅打开弹窗查看字段后取消,没有保存。
- 首页只切换统计周期,没有触发交易、审核或资金操作。
- 角色权限上一轮的勾选联动验证未提交;客服状态验证后已恢复原状态。
## 11. 第三轮新增缺陷
### DEF-A-008 首页汇总任务停滞,统计数据延迟 107 小时
- 严重级别:P1
- 关联用例:UI-A-HOME-001、UI-A-HOME-002
- 实际结果:近 30 日指标可以加载,但数据截至 `2026-10-11 13:46:46`,在 `2026-10-16` 验收时页面提示延迟约 107 小时。
- 影响:首页经营指标无法反映当前订单、充值、提现和余额情况,不能作为运营决策依据。
- 代码定位:仓库只有 `hg_dashboard_daily_stat` 的读取逻辑和演示数据 seed,未检索到生产汇总表的写入/刷新任务;需确认是否由仓库外 ETL 承担,如无则缺少汇总任务实现。
### DEF-A-009 网站设置存在不完整的线上配置
- 严重级别:P1
- 关联用例:UI-A-SITE-001、UI-A-SITE-002
- 实际结果:隐私网址为空;多个必填业务参数为 0;网站名称仍为“演示站点-3”。
- 影响:隐私协议入口、订单等待、交易限制、佣金和自动派单等 C 端行为可能失效或采用异常零值。
- 建议:发布前建立配置完整性校验和默认值迁移;加载历史无效配置时给出显著告警,不允许无提示继续运行。
### DEF-A-010 Banner 与手工商城初始化记录缺少必填图片
- 严重级别:P1
- 关联用例:UI-A-BANNER-001、UI-A-BANNER-002、UI-A-MANUAL-001、UI-A-MANUAL-002
- 实际结果:两个列表的图片列均为空,编辑弹窗也没有回显图片;后端过滤逻辑却要求 Banner `imageUrl`、手工商品 `coverUrl` 必填。
- 影响:C 端可能出现空白横幅/商品图;旧记录打开后可能无法在不重新上传图片的情况下保存。
- 建议:修正初始化数据并补数据校验脚本;列表为空图时显示明确占位和“图片缺失”告警。
### DEF-A-011 手工商城发布状态功能未接入页面
- 严重级别:P1
- 关联用例:UI-A-MANUAL-001、UI-A-MANUAL-003
- 实际结果:后端已有 `ManualProductStatus` 接口和发布状态字段,但列表不显示状态、没有发布/下架按钮;编辑接口还会把商品直接改为已发布。
- 影响:运营无法在手工商城页面控制上下架,编辑草稿或已下架商品会绕过明确确认直接发布。
- 建议:列表增加状态列和发布/下架操作;编辑仅保存内容,不隐式改变发布状态。
### DEF-A-012 会员银行卡编辑页未回显已有敏感账户资料
- 严重级别:P0
- 关联用例:UI-A-MEMBER-004、UI-C-FORM-003、C-FIN-039、DEF-C-001
- 复现步骤:会员列表查询 `potter`,点击“银行卡信息”。
- 实际结果:仅开户银行 `icbc` 回显;开户人、开户电话、银行卡号、支付宝与微信收款码均为空。
- 预期结果:经过敏感信息授权后完整回显;未授权时至少显示脱敏值和已有附件占位,且禁止空值覆盖密文。
- 影响:后台人员无法核对已有账户,直接保存存在把已有敏感字段和附件清空的高风险;同时解释了 C 端绑定信息页为何显示空表单。
- 建议:优先检查 finance key 与历史数据加密密钥是否一致、详情接口的解密错误是否被吞掉;编辑接口必须采用“未传不更新”语义,附件 ID 不得因前端未回显而被置空。
### DEF-A-013 任务大厅发布状态功能未接入页面
- 严重级别:P1
- 关联用例:UI-A-HALL-001、UI-A-HALL-003
- 实际结果:列表不显示发布状态,仅有编辑和删除;草稿、待审、已发布、已下线、已归档等状态只能从演示名称猜测。
- 代码定位:后端 `task_hall.go` 已实现 `Status`,前端 API 也定义了 `/taskHall/status`,但 `app-member/task-hall/index.vue` 未导入或调用 `Status`,操作列只有编辑/删除;新增大厅还会直接设为已发布。
- 影响:运营无法进行清晰的发布/下架流程,新增内容绕过发布确认直接对 C 端生效。
- 建议:增加状态列和发布/下架按钮;新增与编辑只保存内容,发布必须走单独状态接口并保留审计记录。
## 12. 当前阶段结论与修复优先级
- 当前累计登记 14 项,其中 13 个有效缺陷(P0 1 个、P1 11 个、P2 1 个)和 1 项已接受设计;C 端收款账户回显缺陷与 P0 后台问题属于同一根因链,合并修复与回归。
- 第一优先级:DEF-A-012 / DEF-C-001,先恢复历史收款账户的安全解密与回显,并防止空表单覆盖密文和附件。
- 第二优先级:DEF-A-006、DEF-A-008,修复任务审核关联和首页汇总任务,否则审核与运营数据均不可信。
- 发布前必须处理:网站配置完整性、Banner/商品图片缺失、手工商城与任务大厅发布状态入口。
## 13. 缺陷修复实施记录
| 编号 | 处理结果 | 修复内容 |
|---|---|---|
| DEF-C-001 / DEF-A-012 | 已修复,待发布回归 | C 端详情接口在历史密钥不可用时回退脱敏字段,不再因单条解密失败让整页为空;后台回显姓名、身份证、手机、账号、数字人民币和双收款码脱敏信息;敏感字段和附件采用“未传不更新”语义,并保留授权查看入口 |
| DEF-A-001 | 已修复 | URL 地址 Excel 导入弹窗增加底部取消按钮 |
| DEF-A-002 | 已修复 | 订单详情增加状态、创建/更新时间、状态时间线、资金流水和审批动作 |
| DEF-A-003 | 已接受设计 | 产品方确认允许展示完整手机号,不修改 |
| DEF-A-004 | 已修复 | 订单列表新增自动/手动派单模式筛选,后端同步按规则快照查询 |
| DEF-A-005 | 已修复 | `hg_task` 增加 `product_id` 迁移并重新生成 DAO/实体;任务新增/编辑增加商品选择、校验和列表回显 |
| DEF-A-006 | 已修复 | 审核列表通过交易订单兜底任务标题并显示订单号/商品;`task_id=0` 时按订单归属校验后允许审核,不再误报任务不存在 |
| DEF-A-007 | 已修复 | 充值真实姓名优先取实名资料,缺失时回退默认银行卡开户人脱敏值 |
| DEF-A-008 | 已修复 | 增加 `dashboard_daily_summary` 汇总任务并注册到 HotGo 定时任务管理;默认每 10 分钟重算今天和昨天,可在后台启停、改表达式和手动执行 |
| DEF-A-009 | 已修复 | 后端强制校验隐私地址、交易余额、等待时长、次数、最低充值和自动派单参数;迁移补齐历史零值并修正文案 |
| DEF-A-010 | 已修复 | 增加随服务部署的 Banner/商品占位资源,迁移只补齐历史空图片记录 |
| DEF-A-011 | 已修复 | 手工商城显示发布状态并增加发布/下架;新增默认草稿,编辑不再隐式发布 |
| DEF-A-013 | 已修复 | 任务大厅显示发布状态并增加发布/下线;新增默认草稿 |
### 13.1 修复后自动化验证
- 管理后台:`go test ./internal/logic/sys ./internal/crons ./internal/model/input/sysin` 通过。
- 管理后台前端:`npm run build` 生产构建通过,6728 个模块完成转换。
- C 端:`go test ./internal/finance ./internal/store` 通过;新增“历史密钥不可用时回退脱敏数据”回归用例。
- C 端前端:`npm run build` 通过,4186 个模块完成转换。
- 新增迁移已在本地 MySQL 8.4 验证;定时任务记录成功初始化,Banner 和商品空图片数量均变为 0。
- 页面回归暂未完成:执行机器再次进入锁屏状态,无法继续通过浏览器点击;服务与前端均已在本地启动,解锁后可直接续测。
## 14. 测试环境发布后回归(2026-10-16)
### 14.1 已通过
- URL 地址池 Excel 导入弹窗已显示“取消”,点击后正常关闭。
- 订单列表已显示自动/手动派单模式,展开查询条件后可见派单模式筛选。
- 订单详情已显示状态、冻结状态、创建/更新时间、状态时间线、资金流水和审批记录。
- 会员任务审核列表已回显任务/商品名称和订单号,历史数据不再空白。
- 会员银行卡信息弹窗已回显开户人、身份证、银行卡、数字钱包和两类收款码;C 端“绑定信息”页也正常显示历史账户。
- 手工商城和任务大厅已显示发布状态及发布/下架(下线)操作。
- Banner 和商品列表的现有图片资源均能正常加载。
- 网站设置已回显站点名称、HTTPS 隐私地址、交易余额、等待时长、次数、最低充值和自动派单范围。
- 经营看板用同一套 `RefreshDaily` 逻辑手工触发后,显示 17 会员、9 商品、余额与昨日订单/充值数据,数据延迟 14 秒。
### 14.2 发布过程中已补齐
- 首次进入任务列表时接口报错,根因为测试库未执行 `hg_task.product_id` 迁移;补执后页面恢复。
- 已在测试库初始化 `dashboard_daily_summary` 定时任务,并补齐站点配置及空图片默认值。
- 站点配置迁移原先把数字写成 JSON 字符串,已改为强制写入 JSON 数值,页面回显正常。
### 14.3 需再发布后复测
- 新增任务弹窗请求商品时使用了 `perPage=500`,超过后端上限 200;已改为 200。
- 部分历史充值单没有实名档案和收款账户,但备注中存在“姓名:xxx”;已增加从备注回退显示姓名。
- 定时任务记录是在当前服务进程启动后插入的,现进程尚未加载该任务;再次发布/重启后需等待一个 10 分钟周期,确认 `calculated_at` 自动推进。
- 当前测试账号 `potter`(系统管理员角色)没有“定时任务”菜单权限,无法在页面手动执行;需使用超级管理员或在角色权限中授予该菜单。
@@ -1,952 +0,0 @@
# URL 地址池与会员注册域名绑定设计方案
> 文档状态:方案设计
> 方案版本:v2.1
> 最后更新:2026-10-15
> 适用项目:Olons Admin / HotGo
> 关联模块:会员注册、会员登录、会员管理、URL 地址池管理、会员登录日志
## 1. 方案变更说明
原方案采用“会员注册时从 URL 地址池轮询取出一个 URL”的模式。现调整为“会员通过哪个域名打开注册页面,就与哪个域名建立绑定关系”。
新流程如下:
```text
管理员或推广人员先向用户提供一个注册链接
→ 用户通过该链接打开注册页面
→ 注册页面获取浏览器地址栏中的域名
→ 用户提交注册
→ 后端校验本次请求的真实注册域名
→ 创建会员
→ 建立会员与注册域名的绑定关系
```
本方案不再使用 URL 轮询顺序、分配游标和自动轮询算法。URL 地址池的作用调整为:
1. 维护允许用于会员注册的域名;
2. 管理域名的启用、失效状态;
3. 统计每个域名带来的注册会员;
4. 查询会员与注册域名的绑定关系。
每个地址同时维护累计绑定次数。地址池列表默认把绑定次数较少的地址排在前面;点击地址详情可以查看当前绑定该地址的全部会员。
## 2. 设计目标
1. 用户使用某个域名注册时,自动与该域名绑定。
2. 注册域名由后端根据可信请求信息确认,不能只信任前端传入值。
3. 每个会员只能存在一个当前绑定域名。
4. 域名未配置或已失效时,会员仍允许注册成功,但保持“待绑定”。
5. 后台不提供创建会员、手动绑定或重新绑定域名的入口。
6. 域名失效后不再接受新的自动绑定,但保留原有绑定用于审计。
7. 支持同域部署、前后端跨域部署以及 CDN/反向代理部署。
8. 所有注册自动绑定和地址状态变更都有业务审计记录。
9. 每个地址记录累计绑定次数,地址池列表默认按绑定次数升序排列。
10. 后台可以手动将启用地址切换为失效,也可以将失效地址重新启用。
11. C 端会员登录成功后更新会员最后登录时间、IP脱敏信息和地区。
12. C 端会员每次登录成功或失败均记录独立日志,并可在后台查询、查看和导出。
## 3. 核心概念
### 3.1 注册 URL
管理员或推广人员提供给用户的完整注册链接,例如:
```text
https://register-a.example.com/register?inviteCode=ABC123
```
完整 URL 中可能包含路径、邀请码或其他查询参数,但会员绑定只取域名部分:
```text
register-a.example.com
```
默认端口不作为域名的一部分:
```text
https://register-a.example.com:443 → register-a.example.com
http://register-a.example.com:80 → register-a.example.com
```
非默认端口的测试环境可以保留端口:
```text
http://127.0.0.1:5173 → 127.0.0.1:5173
```
### 3.2 URL 地址池
URL 地址池是允许用于注册绑定的域名白名单。地址池中的域名分为:
| 值 | 状态 | 说明 |
|---:|---|---|
| 1 | 启用 | 允许新注册会员自动绑定 |
| 2 | 失效 | 不允许新注册会员自动绑定,保留原绑定关系 |
### 3.3 会员域名绑定
会员注册成功后,如果识别出的域名在地址池中存在且为启用状态,则创建绑定记录。
一个会员最多有一个绑定域名。绑定只允许在 C 端会员注册事务中自动建立,后台只能查看,不能手动绑定、解除绑定或重新绑定。
### 3.4 待绑定会员
以下情况会员注册成功但不建立域名绑定:
- 请求中无法识别有效注册域名;
- 注册域名没有配置在地址池中;
- 注册域名已经失效;
没有绑定记录的会员统一显示为“待绑定”。
## 4. 业务规则
### 4.1 正常注册绑定
```text
用户打开 https://register-a.example.com/register
→ 前端读取 window.location.origin
→ 用户提交注册
→ 后端确认真实来源域名为 register-a.example.com
→ 查询地址池
→ 地址存在且启用
→ 创建会员及钱包
→ 创建会员与 register-a.example.com 的绑定
→ 注册成功
```
### 4.2 地址池没有该域名
```text
用户通过 unknown.example.com 注册
→ 后端未在地址池找到该域名
→ 会员和钱包正常创建
→ 不创建域名绑定记录
→ 记录“未知注册域名”业务审计
→ 会员显示为待绑定
```
### 4.3 域名已经失效
```text
用户通过已失效域名注册
→ 会员正常创建
→ 不建立自动绑定
→ 记录“通过失效域名注册”业务审计
→ 会员显示为待绑定
```
### 4.4 重复注册请求
同一会员创建请求因网络原因重复提交时,应通过现有手机号、用户名唯一约束和注册幂等机制防止重复创建。
对已经存在的会员重复执行域名绑定:
- 已绑定同一域名:直接返回成功;
- 已绑定其他域名:不能由注册接口覆盖,也不提供后台改绑入口;
- 既有会员尚未绑定:保持待绑定,注册接口不做事后补绑。
## 5. 注册域名识别
### 5.1 前端采集
注册页面提交以下辅助字段:
```ts
const registerOrigin = window.location.origin;
const registerHost = window.location.host;
```
请求示例:
```json
{
"username": "demo_user",
"mobile": "13800000000",
"password": "******",
"registerOrigin": "https://register-a.example.com",
"registerHost": "register-a.example.com"
}
```
前端字段只用于辅助校验和问题排查,不能作为后端绑定的唯一依据,因为普通 HTTP 客户端可以伪造请求参数。
禁止保存完整 `window.location.href`,避免把邀请码、临时令牌或其他查询参数写入数据库和日志。
### 5.2 后端可信来源
后端按照部署方式解析真实注册域名。
#### 同域部署
注册页面和 API 使用同一个域名时,优先读取:
```text
HTTP Host
```
#### 前后端跨域部署
例如:
```text
注册页面:https://register-a.example.com
注册接口:https://api.example.com/api/member/register
```
此时 API 的 `Host` 是 `api.example.com`,不能用于会员绑定。浏览器跨域请求通常携带:
```text
Origin: https://register-a.example.com
```
后端应从经过 CORS 白名单校验的 `Origin` 中提取注册域名。
#### CDN 或反向代理部署
后端可能收到:
```text
X-Forwarded-Host
X-Forwarded-Proto
Forwarded
```
只有请求来自配置的可信代理 IP 时,才允许使用这些请求头。外部客户端直接传入的 `X-Forwarded-Host` 不可信。
### 5.3 建议解析优先级
```text
1. 已通过 CORS 白名单校验的 Origin 域名
2. 来自可信代理的 X-Forwarded-Host / Forwarded
3. HTTP Host
4. 前端 registerHost,仅用于一致性校验,不单独作为可信值
```
当可信来源域名与前端 `registerHost` 不一致时:
- 会员仍可注册;
- 不自动绑定域名;
- 写入安全警告和业务审计;
- 后台显示“注册域名校验异常”。
### 5.4 域名标准化
后端统一执行:
- 转换为小写;
- 移除首尾空格;
- 移除末尾的点;
- 国际化域名转换为 Punycode;
- 移除默认端口 `80` 和 `443`;
- 拒绝包含路径、用户名、密码、Fragment 的值;
- 开发环境允许配置带非默认端口的 Host;
- 生产环境建议只允许 HTTPS 注册入口。
## 6. 数据模型
域名绑定方案使用两张业务表,不再需要轮询游标表。会员登录审计另增一张独立日志表。
```text
hg_url_pool
│ 1
│
│ N
hg_member_url_binding ─── N:1 ─── hg_app_member
hg_app_member ─── 1:N ─── hg_app_member_login_log
```
### 6.1 URL 地址池表 `hg_url_pool`
```sql
CREATE TABLE IF NOT EXISTS `hg_url_pool` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
`tenant_id` bigint unsigned NOT NULL DEFAULT 0 COMMENT '租户ID',
`name` varchar(100) NOT NULL DEFAULT '' COMMENT '地址名称',
`scheme` varchar(10) NOT NULL DEFAULT 'https' COMMENT '协议:http或https',
`domain` varchar(255) NOT NULL COMMENT '标准化域名,可含非默认端口',
`domain_hash` char(64) NOT NULL COMMENT '标准化域名SHA256',
`base_url` varchar(512) NOT NULL COMMENT '注册入口基础地址',
`status` tinyint NOT NULL DEFAULT 1 COMMENT '状态:1启用,2失效',
`bind_count` bigint unsigned NOT NULL DEFAULT 0 COMMENT '累计成功绑定次数',
`last_bound_at` datetime(3) DEFAULT NULL COMMENT '最后绑定时间',
`remark` varchar(500) NOT NULL DEFAULT '' COMMENT '备注',
`created_by` bigint unsigned NOT NULL DEFAULT 0 COMMENT '创建者ID',
`updated_by` bigint unsigned NOT NULL DEFAULT 0 COMMENT '更新者ID',
`deleted_by` bigint unsigned NOT NULL DEFAULT 0 COMMENT '删除者ID',
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
`updated_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3)
ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
`deleted_at` datetime(3) DEFAULT NULL COMMENT '删除时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_url_pool_domain` (`tenant_id`,`domain_hash`),
KEY `idx_url_pool_list` (`tenant_id`,`bind_count`,`id`,`deleted_at`),
KEY `idx_url_pool_status` (`tenant_id`,`status`,`bind_count`,`deleted_at`),
KEY `idx_url_pool_deleted` (`deleted_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
COMMENT='URL地址池';
```
字段说明:
| 字段 | 说明 |
|---|---|
| `scheme` | 注册入口协议,生产环境一般为 HTTPS |
| `domain` | 绑定和匹配使用的标准化域名 |
| `domain_hash` | 用于唯一索引,避免域名大小写或格式差异导致重复 |
| `base_url` | 后台展示、复制链接时使用,例如 `https://a.example.com/register` |
| `bind_count` | C 端注册成功并实际建立域名绑定的累计次数 |
| `last_bound_at` | 最近一次成功建立绑定的时间 |
地址一旦产生会员绑定,禁止直接修改 `domain`。需要使用新域名时应新增地址;已有会员继续保留原绑定关系。
`bind_count` 是累计使用次数。当前绑定会员数通过 `hg_member_url_binding` 按 `url_pool_id` 实时统计。正常情况下两者一致;保留累计字段便于快速排序和审计校验。
### 6.2 会员域名绑定表 `hg_member_url_binding`
```sql
CREATE TABLE IF NOT EXISTS `hg_member_url_binding` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
`tenant_id` bigint unsigned NOT NULL DEFAULT 0 COMMENT '租户ID',
`member_id` bigint unsigned NOT NULL COMMENT '会员ID',
`url_pool_id` bigint unsigned NOT NULL COMMENT 'URL地址池ID',
`domain_snapshot` varchar(255) NOT NULL COMMENT '绑定时域名快照',
`origin_snapshot` varchar(512) NOT NULL DEFAULT '' COMMENT '绑定时Origin快照',
`bound_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '绑定时间',
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
`updated_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3)
ON UPDATE CURRENT_TIMESTAMP(3) COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_member_url_binding` (`tenant_id`,`member_id`),
KEY `idx_member_url_pool` (`tenant_id`,`url_pool_id`,`member_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
COMMENT='会员注册域名绑定';
```
设计说明:
- 未绑定会员没有该表记录;
- `member_id` 唯一约束保证一个会员只有一个当前绑定;
- `domain_snapshot` 用于保留绑定时的域名;
- `origin_snapshot` 只保存 Scheme 和 Host,不保存路径及查询参数;
- 绑定记录由 C 端注册事务创建,后台仅提供只读查询。
### 6.3 会员登录日志表 `hg_app_member_login_log`
```sql
CREATE TABLE IF NOT EXISTS `hg_app_member_login_log` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '日志ID',
`tenant_id` bigint unsigned NOT NULL DEFAULT 0 COMMENT '租户ID',
`req_id` varchar(64) NOT NULL DEFAULT '' COMMENT '请求ID',
`member_id` bigint unsigned NOT NULL DEFAULT 0 COMMENT '会员ID,未识别会员时为0',
`username` varchar(64) NOT NULL DEFAULT '' COMMENT '用户名快照',
`mobile_mask` varchar(32) NOT NULL DEFAULT '' COMMENT '手机号脱敏值',
`login_type` tinyint NOT NULL DEFAULT 1 COMMENT '登录方式:1账号密码,2手机验证码',
`status` tinyint NOT NULL DEFAULT 1 COMMENT '登录状态:1成功,2失败',
`err_msg` varchar(255) NOT NULL DEFAULT '' COMMENT '失败原因',
`login_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '登录时间',
`login_ip_cipher` text COMMENT '登录IP密文',
`login_ip_mask` varchar(64) NOT NULL DEFAULT '' COMMENT '登录IP脱敏值',
`login_region` varchar(128) NOT NULL DEFAULT '' COMMENT '登录地区',
`login_domain` varchar(255) NOT NULL DEFAULT '' COMMENT '登录页面标准化域名',
`user_agent` varchar(512) NOT NULL DEFAULT '' COMMENT '浏览器UA',
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间',
PRIMARY KEY (`id`),
KEY `idx_member_login_time` (`tenant_id`,`member_id`,`login_at`),
KEY `idx_member_login_status` (`tenant_id`,`status`,`login_at`),
KEY `idx_member_login_username` (`tenant_id`,`username`,`login_at`),
KEY `idx_member_login_domain` (`tenant_id`,`login_domain`,`login_at`),
KEY `idx_member_login_req` (`req_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
COMMENT='C端会员登录日志';
```
设计说明:
- 登录成功和失败都必须写入日志;
- 登录失败且无法识别会员时,`member_id` 为 `0`,仅保存用户名快照或手机号脱敏值;
- 不保存密码、短信验证码、Token、完整手机号及登录响应正文;
- IP 仅保存密文和脱敏值,后台页面不得返回 IP 密文;
- 日志为审计数据,后台只读,不提供人工新增、编辑和删除功能;
- 操作系统和浏览器名称由 `user_agent` 在查询时解析,无需重复存储。
## 7. 字典设计
继续使用 HotGo 后端统一字典,不在前端硬编码。
```go
const (
UrlPoolStatusEnabled = 1 // 启用
UrlPoolStatusInvalid = 2 // 失效
AppMemberLoginTypePassword = 1 // 账号密码
AppMemberLoginTypeSms = 2 // 手机验证码
)
```
建议字典:
| 字典键 | 说明 |
|---|---|
| `urlPoolStatus` | URL 地址状态 |
| `appMemberLoginTypeOptions` | C 端会员登录方式,新建后端枚举字典 |
| `sys_login_status` | 登录成功/失败状态,直接复用 HotGo 已有字典 |
会员绑定状态不需要单独存储:
- 存在绑定记录:已绑定;
- 不存在绑定记录:待绑定;
- 存在绑定但地址池状态为失效:绑定域名已失效。
## 8. 注册事务设计
### 8.1 注册流程
```text
解析可信注册域名
→ 查询 URL 地址池
→ 开启事务
→ 创建会员主档
→ 初始化会员钱包
→ 如果域名存在且启用,创建绑定
→ 地址池 bind_count 加 1,并更新 last_bound_at
→ 写业务审计
→ 提交事务
```
### 8.2 错误边界
以下情况不阻断注册:
- 没有识别到域名;
- 域名未配置;
- 域名已经失效;
- 前端域名与可信来源不一致。
以上情况注册成功但会员保持待绑定,并写入审计。
以下情况必须回滚注册:
- 查询地址池时数据库异常;
- 创建会员失败;
- 初始化钱包失败;
- 已确认需要绑定,但插入绑定记录失败;
- 更新注册统计失败;
不能将数据库错误误判成“域名不存在”。
### 8.3 并发与幂等
- `hg_member_url_binding(tenant_id, member_id)` 唯一,防止重复绑定;
- `hg_url_pool(tenant_id, domain_hash)` 唯一,防止重复域名;
- 创建绑定前先查询会员现有绑定;
- 同域名重复绑定直接返回成功;
- 注册接口不允许覆盖不同域名的已有绑定。
只有 C 端注册事务实际新建绑定时才增加 `bind_count`。同一会员对同一地址的幂等重试不增加次数。
## 9. 后端服务设计
建议新增共享服务:
```go
type RegisterDomainModel struct {
Scheme string
Domain string
Origin string
Trusted bool
Source string
}
type MemberDomainResult struct {
Bound bool `json:"bound"`
UrlPoolId uint64 `json:"urlPoolId"`
Domain string `json:"domain"`
BaseUrl string `json:"baseUrl"`
StatusLabel string `json:"statusLabel"`
}
```
核心方法:
```text
ResolveRegisterDomain(ctx, registerOrigin, registerHost)
解析并校验真实注册域名
BindOnRegister(ctx, memberId, domain)
注册事务内自动绑定
GetMemberDomain(ctx, memberId)
查询会员当前绑定及域名有效状态
```
会员登录日志服务增加:
```text
PushMemberLoginLog(ctx, member, loginType, err)
收集请求上下文并推送会员登录日志
WriteMemberLoginLog(ctx, log)
队列消费或同步兜底写入日志
UpdateMemberLastLogin(ctx, memberId, ipData)
登录成功后更新会员主表最后登录信息
```
项目现有 `/site/register` 是后台管理员账号注册,不是 `hg_app_member` 的 C 端会员注册。新域名绑定应接入真正的 C 端会员注册接口。
后台当前不提供创建 C 端会员的产品功能,本方案也不新增该入口。会员及其域名绑定只能由 C 端注册流程创建。
## 10. 后台地址池管理
### 10.1 菜单位置
```text
会员管理
└── URL地址池
```
### 10.2 汇总数据
- URL 总数;
- 启用 URL 数;
- 失效 URL 数;
- 已绑定会员数;
- 待绑定会员数;
- 绑定域名已失效会员数。
### 10.3 查询条件
- 地址名称;
- 域名关键词;
- URL 状态;
- 创建时间。
### 10.4 列表字段
- 地址名称;
- 注册域名;
- 注册入口 URL;
- 状态;
- 当前绑定会员数;
- 累计绑定次数;
- 最后绑定时间;
- 备注;
- 创建时间;
- 操作。
列表默认排序固定为:
```sql
ORDER BY bind_count ASC, id ASC
```
即累计绑定次数最少的地址排在最前面;绑定次数相同时,先创建的地址排在前面。分页查询必须由后端应用该排序,不能只在前端对当前页排序。
### 10.5 操作按钮
| 按钮 | 条件 | 功能 |
|---|---|---|
| 新增地址 | 有新增权限 | 添加注册域名和入口地址 |
| Excel批量导入 | 有导入权限 | 按“地址名称、注册入口URL、备注”批量导入,导入地址统一启用 |
| 编辑 | 始终可见 | 编辑名称、入口路径和备注 |
| 启用 | 当前失效 | 手动将地址切换为启用,恢复自动注册绑定 |
| 设为失效 | 当前启用 | 手动将地址切换为失效,停止新的自动绑定 |
| 复制注册链接 | 当前启用 | 复制完整注册入口 URL |
| 详情 | 始终可见 | 进入地址详情页,查看地址信息和全部绑定会员 |
地址池不提供删除功能,历史地址统一通过“启用/失效”管理。启用和失效由管理员手动点击切换。状态变更成功后立即刷新列表,并写入业务审计。地址变为失效后:
- 不再接受新的注册自动绑定;
- 已有会员绑定关系保留;
- 详情页仍然可以查看原有绑定会员。
### 10.6 地址详情页
点击列表中的“详情”进入独立详情页。页面分为地址信息和绑定会员两个区域。
地址信息展示:
- 地址名称;
- 注册域名;
- 注册入口 URL;
- 当前状态;
- 累计绑定次数;
- 当前绑定会员数;
- 最后绑定时间;
- 创建人、创建时间;
- 备注。
绑定会员列表展示当前绑定该地址的全部会员,并支持分页。字段包括:
- 会员编号;
- 用户名;
- 手机号脱敏值;
- 会员状态;
- 绑定时间。
详情页会员列表支持按用户名、手机号和绑定时间查询。这里的“全部用户”指绑定该地址的全部会员,列表仅供查看,不提供添加、解绑或改绑操作。
## 11. 会员列表扩展
会员列表增加字段:
- 注册域名;
- 域名绑定状态;
- 绑定时间。
增加筛选:
| 筛选值 | 条件 |
|---|---|
| 全部 | 不限制 |
| 已绑定 | 存在绑定且域名启用 |
| 待绑定 | 不存在绑定记录 |
| 域名已失效 | 存在绑定但域名已失效 |
操作栏不增加绑定、解绑或重新绑定按钮。会员详情可只读查看域名信息。
会员详情增加“注册域名信息”区域,展示:
- 当前域名;
- 注册入口;
- 域名状态;
- 绑定时间。
## 12. 会员登录与日志设计
### 12.1 建设范围
当前项目的登录日志分为两类理解:
- `hg_sys_login_log` 及“系统登录日志”页面记录的是后台管理员登录。现有后台账号、手机号登录均会调用 `SysLoginLog().Push()` 写入成功或失败结果;
- `hg_app_member` 虽然预留了 `last_login_at`、`last_login_ip_cipher`、`last_login_ip_mask`、`last_login_region` 字段,会员列表也展示最后登录信息,但当前代码中没有找到 C 端会员登录接口对这些字段的更新逻辑;
- 当前也没有独立的会员登录历史表或会员登录日志列表。
因此,现状结论是:**后台管理员登录有日志,C 端会员登录目前没有完整可用的日志功能**。
本次将以下两项能力纳入正式实施范围:
1. 登录成功后更新会员主表中的最后登录时间、IP 脱敏值和地区;
2. 使用独立的 `hg_app_member_login_log` 记录每次会员登录成功或失败,避免与后台管理员的 `hg_sys_login_log` 混用。
### 12.2 登录成功后的会员主表更新
会员身份校验通过后、返回登录成功响应前,更新 `hg_app_member`:
| 字段 | 更新内容 |
|---|---|
| `last_login_at` | 当前登录成功时间 |
| `last_login_ip_cipher` | 使用项目统一加密工具保存的客户端 IP 密文 |
| `last_login_ip_mask` | 用于后台展示的 IP 脱敏值 |
| `last_login_region` | 根据客户端 IP 解析的国家/省/市摘要 |
处理规则:
- 只有登录成功才更新上述字段,登录失败不得覆盖会员最后一次成功登录信息;
- 客户端 IP 使用项目现有 `location.GetClientIp()` 识别,并遵守可信代理配置;
- 地区解析失败不阻断登录,地区保存为空,同时记录 Warning 日志;
- 会员主表更新失败时不签发 Token,向客户端返回“登录失败,请稍后重试”;
- 更新字段必须使用 DAO 列名,禁止硬编码字段字符串;
- 不在业务日志中输出 IP 密文、密码、短信验证码或 Token。
### 12.3 会员登录日志写入流程
所有 C 端会员登录入口必须调用同一个会员登录日志服务,不允许每种登录方式自行拼装日志。
```text
收到登录请求
→ 解析请求ID、登录方式、客户端IP、地区、域名和User-Agent
→ 校验会员及登录凭证
→ 失败:写入失败日志,返回统一登录错误
→ 成功:更新会员最后登录信息
→ 签发Token
→ 写入成功日志
→ 返回登录结果
```
为保证失败请求也能记录,登录控制器或登录服务应使用统一的收口逻辑,在方法返回前根据最终 `err` 和会员识别结果生成日志。日志优先通过项目消息队列异步写入;队列推送失败时同步写库兜底,并记录 Warning。日志写入最终失败不得把已经完成的身份认证改成登录失败,但必须留下服务端告警。
失败原因只记录可审计的标准化文案,例如:
- 账号或密码错误;
- 验证码错误或已过期;
- 会员不存在;
- 会员已禁用;
- 风控限制登录;
- 系统异常。
### 12.4 后台会员登录日志页面
菜单位置:
```text
会员管理
└── 会员登录日志
```
查询条件:
- 会员编号、用户名或手机号关键词;
- 登录方式;
- 登录状态;
- 登录域名;
- IP 脱敏值;
- 登录时间范围。
列表默认按照 `login_at DESC, id DESC` 排序,展示:
- 日志ID;
- 会员编号;
- 用户名;
- 手机号脱敏值;
- 登录方式;
- 登录状态;
- 登录时间;
- 登录IP脱敏值;
- 登录地区;
- 登录域名;
- 操作系统;
- 浏览器;
- 失败原因;
- 操作。
操作栏只提供“详情”。列表顶部提供“导出”,导出内容与当前查询条件一致。详情弹窗补充展示请求ID和完整 User-Agent,但不展示 IP 密文及任何登录凭证。
列表查询必须按 `tenant_id` 隔离数据。使用完整手机号查询时,先在会员表中匹配会员ID,再按 `member_id` 查询日志;对无法识别会员的失败记录,只支持按已保存的手机号脱敏值查询。
## 13. 接口设计
### 13.1 后台接口
| 接口 | 方法 | 说明 |
|---|---|---|
| `/urlPool/list` | GET | 地址池分页列表 |
| `/urlPool/view` | GET | 地址详情及绑定统计 |
| `/urlPool/edit` | POST | 新增或编辑地址 |
| `/urlPool/import` | POST | Excel批量导入地址,成功记录统一设为启用 |
| `/urlPool/status` | POST | 启用或设为失效 |
| `/urlPool/memberList` | GET | 分页查看域名当前绑定的全部会员 |
| `/urlPool/summary` | GET | 地址池汇总数据 |
| `/appMemberLoginLog/list` | GET | 会员登录日志分页列表 |
| `/appMemberLoginLog/view` | GET | 会员登录日志详情 |
| `/appMemberLoginLog/export` | GET | 按当前条件导出会员登录日志 |
### 13.2 C 端接口
注册请求增加:
```json
{
"registerOrigin": "https://register-a.example.com",
"registerHost": "register-a.example.com"
}
```
会员查询当前域名响应建议:
```json
{
"bound": true,
"domain": "register-a.example.com",
"baseUrl": "https://register-a.example.com/register",
"domainStatus": 1,
"domainStatusLabel": "启用"
}
```
未绑定时:
```json
{
"bound": false,
"domain": "",
"baseUrl": "",
"domainStatus": 0,
"domainStatusLabel": "待绑定"
}
```
所有 C 端会员登录接口的响应格式保持不变,日志采集由服务端统一完成,不要求前端额外提交 IP、地区或 User-Agent。跨域部署时,前端可提交 `loginHost` 作为辅助校验值,但最终登录域名仍按可信 `Origin`、可信代理转发头和 `Host` 解析。
## 14. HotGo 模块结构
建议作为主模块开发:
```text
server/
├── api/admin/urlpool/urlpool.go
├── api/admin/appmemberloginlog/appmemberloginlog.go
├── internal/consts/url_pool.go
├── internal/consts/app_member_login_log.go
├── internal/controller/admin/sys/url_pool.go
├── internal/controller/admin/sys/app_member_login_log.go
├── internal/logic/sys/url_pool.go
├── internal/logic/sys/app_member_login_log.go
├── internal/model/input/sysin/url_pool.go
├── internal/model/input/sysin/app_member_login_log.go
├── internal/router/genrouter/url_pool.go
├── internal/router/genrouter/app_member_login_log.go
├── internal/queues/app_member_login_log.go
├── internal/service/sys.go # 追加 ISysUrlPool、ISysAppMemberLoginLog
├── internal/dao/url_pool.go # CLI 生成
├── internal/dao/member_url_binding.go # CLI 生成
├── internal/dao/app_member_login_log.go # CLI 生成
├── storage/data/migrations/<date>_url_pool.sql
└── storage/data/migrations/<date>_app_member_login_log.sql
web/src/
├── api/urlPool/index.ts
├── api/appMemberLoginLog/index.ts
├── views/app-member/url-pool/
├── index.vue
├── edit.vue
├── detail.vue
└── model.ts
└── views/app-member/login-log/
├── index.vue
├── detail.vue
└── model.ts
```
DAO、Entity、DO 和 DAO Internal 必须通过 GoFrame CLI 生成,不手工修改。
## 15. 权限设计
```text
/urlPool/list
/urlPool/view
/urlPool/edit
/urlPool/import
/urlPool/status
/urlPool/memberList
/urlPool/summary
/appMemberLoginLog/list
/appMemberLoginLog/view
/appMemberLoginLog/export
```
域名启用/失效和注册自动绑定均写入业务审计。
## 16. 缓存设计
会员当前域名可以使用项目统一缓存:
```text
urlpool:member:{tenantId}:{memberId}
```
地址池域名查询可以缓存:
```text
urlpool:domain:{tenantId}:{domainHash}
```
建议缓存 10~30 分钟。以下操作后主动清除缓存:
- 地址编辑;
- 地址启用或失效;
- 注册自动绑定;
缓存统一使用 `cache.Instance()`,数据库仍是唯一事实来源。
## 17. 安全要求
1. 不得只信任前端 `registerHost` 参数。
2. `Origin` 必须通过地址池或 CORS 白名单校验。
3. 转发请求头只能在可信代理来源下使用。
4. 后台保存域名前必须进行标准化和 URL 解析。
5. 禁止保存包含用户名、密码、Token 的 URL。
6. 日志只记录标准化域名和 Origin,不记录完整注册查询参数。
7. 后台不得提供会员域名绑定、解绑或改绑接口。
8. 生产环境建议只允许 HTTPS 注册入口。
9. 域名变更不得静默覆盖已有会员绑定。
10. 未知或校验异常域名不自动创建地址池记录。
11. 会员登录日志不得保存密码、短信验证码、Token、完整手机号或登录响应正文。
12. 后台日志接口不得返回 `last_login_ip_cipher` 或 `login_ip_cipher`。
13. 失败日志使用统一错误文案,避免暴露账号是否存在等敏感认证细节。
14. 会员登录日志列表、详情和导出必须执行租户隔离,禁止跨租户查询。
## 18. 异常处理
| 场景 | 处理方式 |
|---|---|
| 地址池为空 | 注册成功,会员待绑定 |
| 注册域名未配置 | 注册成功,会员待绑定并写审计 |
| 注册域名失效 | 注册成功,会员待绑定并写审计 |
| 前端域名与可信来源不一致 | 注册成功但不绑定,记录安全警告 |
| 域名重复添加 | 拒绝保存 |
| 已绑定同一域名 | 幂等返回成功 |
| 注册接口试图覆盖其他域名 | 拒绝覆盖,保留原绑定关系 |
| 地址池查询数据库异常 | 注册失败并回滚 |
| 创建绑定失败 | 注册事务回滚 |
| 已产生绑定的域名被直接修改 | 拒绝修改域名字段 |
| 列表绑定次数相同 | 按地址ID升序稳定排序 |
| 登录成功但会员最后登录信息更新失败 | 不签发Token,返回通用系统错误并记录失败日志 |
| 登录IP地区解析失败 | 登录继续,地区留空并记录Warning |
| 登录日志队列推送失败 | 同步写入日志表兜底并记录Warning |
| 队列与同步写入均失败 | 不撤销已完成登录,记录服务端告警供运维处理 |
| 登录失败且无法识别会员 | 以 `member_id=0` 写入脱敏失败日志 |
## 19. 验收标准
### 19.1 注册绑定
- 用户通过 A 域名注册后,会员绑定 A。
- 用户通过 B 域名注册后,会员绑定 B。
- URL 路径和查询参数不同但域名相同,绑定结果相同。
- 默认端口不会造成重复域名。
- 地址池为空时会员仍注册成功并显示待绑定。
- 未配置域名和失效域名不会建立自动绑定。
### 19.2 部署场景
- 前后端同域部署能够从 Host 正确识别域名。
- 前后端跨域部署能够从通过校验的 Origin 正确识别域名。
- CDN/反向代理部署能够从可信转发头正确识别域名。
- 外部客户端伪造 `X-Forwarded-Host` 不会产生错误绑定。
- 前端伪造 `registerHost` 不会覆盖后端可信域名。
### 19.3 并发和幂等
- 并发提交不会为同一会员创建多条绑定。
- 相同注册请求重试不会重复创建会员或绑定。
- 已绑定会员不能通过普通注册接口更换域名。
### 19.4 后台管理
- 可以新增、编辑、启用和失效注册域名。
- 可以使用 Excel 批量导入地址,表头包含“地址名称、注册入口URL、备注”,所有导入地址默认为启用。
- 可以手动点击“启用”或“设为失效”,状态立即生效。
- 地址池列表默认按累计绑定次数升序排列。
- 可以复制完整注册链接。
- 点击地址详情可以分页查看当前绑定的全部会员。
- 每次实际建立新绑定时,地址累计绑定次数准确加一。
- 幂等重试不会重复增加累计绑定次数。
- 后台不存在创建会员入口。
- 后台不存在会员域名绑定、解绑或重新绑定入口。
- 失效域名不允许新增绑定。
- 所有关键操作均可在业务审计中查询。
### 19.5 会员登录日志
- 会员登录成功后,会员主表最后登录时间、IP密文、IP脱敏值和地区正确更新。
- 登录失败不会覆盖会员最后一次成功登录信息。
- 账号密码登录和手机验证码登录均写入独立会员登录日志。
- 登录成功、密码错误、验证码错误、账号禁用和系统异常场景均能生成对应日志。
- 无法识别会员的失败请求以 `member_id=0` 记录,且不保存完整手机号。
- 后台“会员登录日志”可以按会员、方式、状态、域名、IP和时间查询。
- 日志列表默认按登录时间倒序排列,可以查看详情并按筛选条件导出。
- 后台页面和导出文件均不出现IP密文、密码、验证码或Token。
- 登录日志队列异常时会同步写库兜底;两种写入都失败时产生服务端 Warning 告警。
## 20. 待确认事项
1. 生产环境是否强制只允许 HTTPS。
2. 前端页面和注册 API 是同域部署还是跨域部署。
3. 是否存在 App、桌面客户端等没有浏览器 `Origin` 的注册入口。
4. 注册入口是否统一为 `/register`,还是每个域名可以配置不同路径。
5. 地址池是否需要按业务线、渠道或租户进一步分组。
6. 会员登录日志的在线保留时长,以及是否需要定时归档历史日志。
Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 264 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 264 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 308 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 322 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 347 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 367 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 432 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 445 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 235 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 493 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 284 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 315 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 314 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 411 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 480 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 405 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 475 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 274 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 335 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 518 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 297 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 392 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 235 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 283 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 315 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 493 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 312 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 436 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 480 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 416 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 475 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 309 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 364 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 274 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 335 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 518 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 297 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 392 KiB

Some files were not shown because too many files have changed in this diff Show More