Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 47 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Release

on:
push:
branches: [main]
branches: [main, dev]
tags:
- "v*"
workflow_dispatch:
Expand Down Expand Up @@ -38,15 +38,51 @@ jobs:
echo "::error::无法从 pom.xml 读取版本号"
exit 1
fi

yml_version="$(sed -n 's/^version:[[:space:]]*//p' src/main/resources/plugin.yml | head -n1 | tr -d '[:space:]')"
if [ "$yml_version" != "$version" ]; then
echo "::error::plugin.yml 版本(${yml_version})与 pom.xml 版本(${version})不一致,请先同步两处版本号"
exit 1
fi

tag="v${version}"
if [ "${GITHUB_REF_TYPE}" = "tag" ]; then
tag="${GITHUB_REF_NAME}"
if [ "$tag" != "v${version}" ]; then
echo "::error::标签 ${tag} 与 pom.xml 版本 ${version} 不一致,请在对应版本号的提交上打标签"
exit 1
fi
fi

# 测试版与正式版产物命名一致(QShopWebUI-<版本>.jar),仅通过 prerelease 标记区分
files="target/QShopWebUI-*.jar"
case "$version" in
*-*)
prerelease="true"
title="QShopWebUI ${version}(测试版)"
;;
*)
prerelease="false"
title="QShopWebUI v${version}"
;;
esac

enabled="true"
if [ "${GITHUB_REF_TYPE}" = "branch" ] && [ "${GITHUB_REF_NAME}" = "dev" ] && [ "$prerelease" != "true" ]; then
enabled="false"
echo "::notice::dev 分支只发行测试版(版本号需带 -beta 等预发布后缀),本次跳过发行"
fi

echo "version=${version}" >> "$GITHUB_OUTPUT"
echo "tag=${tag}" >> "$GITHUB_OUTPUT"
echo "prerelease=${prerelease}" >> "$GITHUB_OUTPUT"
echo "files=${files}" >> "$GITHUB_OUTPUT"
echo "title=${title}" >> "$GITHUB_OUTPUT"
echo "enabled=${enabled}" >> "$GITHUB_OUTPUT"

- name: 检查是否已发行
id: check
if: steps.meta.outputs.enabled == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
Expand Down Expand Up @@ -74,15 +110,22 @@ jobs:
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.meta.outputs.tag }}
name: QShopWebUI ${{ steps.meta.outputs.tag }}
name: ${{ steps.meta.outputs.title }}
target_commitish: ${{ github.sha }}
files: target/QShopWebUI-*.jar
prerelease: ${{ steps.meta.outputs.prerelease }}
files: ${{ steps.meta.outputs.files }}
generate_release_notes: true

- name: 写入摘要
run: |
if [ "${{ steps.check.outputs.exists }}" = "true" ]; then
if [ "${{ steps.meta.outcome }}" != "success" ]; then
echo "版本读取或校验失败,请查看上方步骤日志。" >> "$GITHUB_STEP_SUMMARY"
elif [ "${{ steps.meta.outputs.enabled }}" != "true" ]; then
echo "dev 分支只发行测试版(版本号需带 -beta 等后缀),本次跳过发行。" >> "$GITHUB_STEP_SUMMARY"
elif [ "${{ steps.check.outputs.exists }}" = "true" ]; then
echo "版本 ${{ steps.meta.outputs.tag }} 已存在,本次跳过发行。" >> "$GITHUB_STEP_SUMMARY"
elif [ "${{ steps.meta.outputs.prerelease }}" = "true" ]; then
echo "已创建预发布(测试版)${{ steps.meta.outputs.tag }},附件 QShopWebUI-${{ steps.meta.outputs.version }}.jar。" >> "$GITHUB_STEP_SUMMARY"
else
echo "已创建发行版本 ${{ steps.meta.outputs.tag }},附件为本次构建的 JAR。" >> "$GITHUB_STEP_SUMMARY"
fi
41 changes: 29 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ QShopWebUI 把 Web 管理界面、HTTP 服务和 QuickShop-Hikari 适配层整
- 物品中文名、英文注册名和拼音搜索
- 按物品、店主、世界、价格和商店类型筛选
- 商店详情、坐标、库存状态和价格区间查看
- 在线购买和离线购买队列(填写游戏 ID + 游戏密码即可下单)
- 在线购买和离线购买队列(填写游戏 ID + 游戏内一次性验证码,验证码在游戏内执行 /qshopwebui code 获取)
- 限购支持:服务器安装 QuickShop「Limited」扩展时,网页购买与游戏内共用每人额度,弹窗显示剩余可买数量
- 数据统计、访问统计和商店数量概览

Expand All @@ -74,7 +74,7 @@ QShopWebUI 把 Web 管理界面、HTTP 服务和 QuickShop-Hikari 适配层整
| 服务端 | Paper 或 Spigot 1.18 及以上 |
| Java | Java 17 及以上 |
| 前置插件 | QuickShop-Hikari |
| 可选插件 | Vault 经济插件(购买/收购)、AuthMe(下单密码验证)、QuickShop「Limited」扩展(限购) |
| 可选插件 | Vault 经济插件(购买/收购)、QuickShop「Limited」扩展(限购) |
| 构建环境 | Maven 3.6 及以上 |

插件对 QuickShop-Hikari 使用反射调用,因此编译时不需要安装 QuickShop-Hikari JAR。QuickShop 缺失时插件仍可加载,但商店页面没有实际商店数据。
Expand All @@ -83,7 +83,7 @@ QShopWebUI 把 Web 管理界面、HTTP 服务和 QuickShop-Hikari 适配层整

1. 从 Releases 页面下载最新的 `QShopWebUI-x.y.z.jar`,或按「自己构建」一节自行编译。
2. 将 JAR 放入服务端的 `plugins/` 目录。
3. 安装并启用 QuickShop-Hikari;如果需要网页购买或收购,再安装 Vault 和经济插件;服务器装了 AuthMe 时,下单会自动验证游戏密码。
3. 安装并启用 QuickShop-Hikari;如果需要网页购买或收购,再安装 Vault 和经济插件;下单前玩家需要在游戏内执行 /qshopwebui code 获取一次性验证码。
4. 启动服务器,等待插件生成 `plugins/QShopWebUI/config.yml`。
5. 修改管理员账号、端口和运行模式。
6. 执行 `/qshopwebui reload`,或重启服务器。
Expand Down Expand Up @@ -162,6 +162,8 @@ purchase:
enabled: true
max-amount: 64
allow-offline-buy: true
game-code-ttl-seconds: 300
game-code-max-attempts: 5

pages:
hide-home: false
Expand Down Expand Up @@ -201,11 +203,12 @@ password: sha256:<64位十六进制摘要>
| 命令 | 说明 |
| --- | --- |
| `/qshopwebui` | 显示帮助 |
| `/qshopwebui code` | 生成网页交易一次性验证码(所有玩家可用,需在游戏内执行) |
| `/qshopwebui status` | 查看监听端口、运行模式、QuickShop 状态和商店数量 |
| `/qshopwebui reload` | 重新读取配置并重启网页监听 |
| `/qshopwebui port <端口>` | 修改网页端口并尝试重新绑定 |

命令权限:`qshopwebui.admin`,默认仅 OP 拥有。输入 `/qshopwebui ` 后,插件会自动补全 `status`、`reload` 和 `port`;输入 `port` 后会提示当前端口。
`status`、`reload`、`port` 命令权限:`qshopwebui.admin`,默认仅 OP 拥有;`code` 所有玩家可用。输入 `/qshopwebui ` 后,管理员会自动补全子命令;输入 `port` 后会提示当前端口。

## 演示站

Expand All @@ -229,7 +232,7 @@ password: sha256:<64位十六进制摘要>
mvn clean package
```

产物:`target/QShopWebUI-1.0.1.jar`。
产物:`target/QShopWebUI-<版本>.jar`,例如测试版为 `QShopWebUI-1.1.0-beta.1.jar`。

构建请带上 `clean`:否则可能把编辑器(VS Code / IDEA)增量编译到 `target/classes` 的旧产物直接打进 jar。

Expand All @@ -252,16 +255,26 @@ node tools/verify-jar.mjs
| 工作流 | 触发方式 | 做什么 |
| --- | --- | --- |
| CI | 推送 `main`、提交 PR、手动触发 | 构建插件、校验 JAR 内容、上传构建产物 |
| Release | 推送 `main`、推送 `v*` 标签、手动触发 | 发现新版本号时自动打标签、创建 Release、附加 JAR |
| Release | 推送 `main` / `dev`、推送 `v*` 标签、手动触发 | 发现新版本号时自动打标签、创建 Release、附加 JAR;带 `-` 的版本号(如 `1.1.0-beta.1`)按测试版预发布处理 |

发行一个新版本的完整流程:
发行前请同时修改 **`pom.xml`** 和 **`src/main/resources/plugin.yml`** 的 `version`(两处不一致时工作流会直接报错,避免产出内含版本号不一致的 JAR)。版本号未变化时自动跳过,不会重复发行。

1. 修改 `pom.xml` 里的 `version`,例如从 `1.0.0` 改成 `1.1.0`。
### 正式版(main 分支)

1. 把 `pom.xml` 与 `plugin.yml` 的版本改成正式号,例如从 `1.0.1` 改成 `1.1.0`。
2. 提交并推送到 `main`。
3. 工作流检测到 `v1.1.0` 还不存在,自动构建、创建标签、创建 Release 并上传 JAR。
4. 版本号没变时自动跳过,不会重复发行。
3. 工作流检测到 `v1.1.0` 还不存在,自动构建、创建标签、创建 Release 并上传 `QShopWebUI-1.1.0.jar`。

### 测试版(dev 分支)

版本号格式为 `A.b.c-beta.N`,例如 `1.1.0-beta.1`:

习惯手动打标签也可以:推送 `v1.1.0` 标签会直接触发发行,工作流也可以在 Actions 页面手动运行。
1. 在 `dev` 分支把 `pom.xml` 与 `plugin.yml` 的版本改成 `1.1.0-beta.1`;下一个测试版递增为 `1.1.0-beta.2`,以此类推。
2. 提交并推送到 `dev`。
3. 工作流检测到 `v1.1.0-beta.1` 还不存在,自动构建并创建**预发布(Pre-release)**:标题 `QShopWebUI 1.1.0-beta.1(测试版)`,附件 `QShopWebUI-1.1.0-beta.1.jar`。预发布不会成为 Releases 页面的 Latest,不影响正式版用户。
4. `dev` 分支使用不带 `-` 的正式版本号时不会发行;要发正式版请合并到 `main` 后操作。

习惯手动打标签也可以:在 `dev` 上执行 `git tag v1.1.0-beta.1 && git push origin v1.1.0-beta.1` 会直接触发发行(标签需与 `pom.xml` 版本一致);也可以在 Actions 页面选择 Release 工作流、目标分支选 `dev` 手动运行。

## 目录结构

Expand Down Expand Up @@ -310,7 +323,11 @@ QshopWebUI/

### 网页购买失败?

确认购买功能已启用,并安装可用的 Vault 经济后端。玩家在线时物品直接发放;玩家离线时需要开启 `purchase.allow-offline-buy`;服务器安装 AuthMe 时,下单还需输入该玩家的游戏密码。
确认购买功能已启用,并安装可用的 Vault 经济后端;下单需要先在游戏内执行 `/qshopwebui code` 获取一次性验证码。玩家在线时物品直接发放;玩家离线时需要开启 `purchase.allow-offline-buy`。

### 提示验证码无效或已过期?

验证码需在游戏内执行 `/qshopwebui code` 生成,默认 5 分钟有效、只能使用一次,错误尝试次数过多会作废。重新执行命令获取新验证码即可;有效期和尝试次数可在配置文件的 `purchase.game-code-ttl-seconds`、`purchase.game-code-max-attempts` 中调整。

### 提示超出限购?

Expand Down
92 changes: 92 additions & 0 deletions ROADMAP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# QShopWebUI 开发路线图

本文件记录 v1.0.1 之后的完整开发范围,不代表功能已经全部实现或发布日期承诺。开发改动进入 `dev`,经过验证后再合并到 `main`;正式发布时再统一更新版本号。

## 现有基础

当前已有中文和拼音搜索、出售与收购筛选、实际库存联动、在线交易、离线购买队列、Limited 限购、批量管理、数据导入导出与备份恢复。抓取日志和商店删除记录不能替代完整的网页订单流水。

## P0:订单状态与交易流水

让玩家确认下单结果,让管理员能排查扣款、发货和离线队列问题。这是建议优先实现的功能。

- 为每笔网页交易生成唯一订单号,记录交易方向、玩家 UUID、商店标识、数量、成交单价、总价和时间。
- 区分待处理、处理中、成功、失败状态;失败时保留可理解的原因,不能把进入离线队列当作成交成功。
- 玩家经过身份验证后只能查看自己的订单;管理员可按玩家、时间、状态和商店检索并导出。
- 重复点击和请求重试使用幂等标识,不能重复扣款或重复发货。
- 商店删除后仍能查看当时的交易信息;定义订单保留期限及清理策略。

验收标准:覆盖在线购买、收购、离线购买、库存不足、余额不足、重复提交和服务重启场景。交易状态必须与实际扣款及物品转移结果一致;中断后不能自动重试结果不确定的交易,应明确标记并供管理员核查。流水不得保存密码、登录令牌或会话凭据。

## P0:玩家身份验证与交易防护

现有下单方式涉及游戏 ID 和密码。建议提供游戏内一次性验证码绑定网页会话,减少网页反复输入游戏密码的需求。

- 游戏内执行 `/qshopwebui code` 生成短时、单次有效验证码,绑定在线玩家 UUID;不依赖 AuthMe。
- 验证码尝试次数、验证码生成和交易请求分别限流;登录错误不暴露敏感账号信息。
- 明确网页会话有效期、退出登录、服务重启后的失效规则,以及管理端和玩家端的权限隔离。
- HTTP 明文传输仍有风险;验证码登录不能替代 HTTPS,公网部署应提供反向代理和 TLS 配置说明。

验收标准:验证码过期、重复使用、错误尝试及越权访问均被拒绝;密码和验证码不出现在应用日志或持久化流水中。先确认与 AuthMe 和离线模式服务器的兼容边界,再调整现有登录方式。

## P1:库存与收购容量提醒

帮助店主及时补货和清理收购箱,降低玩家遇到无法成交商店的频率。

- 店主可查看自己商店的缺货、低库存、收购箱满状态。
- 阈值可配置,无限商店不产生普通库存告警。
- 首版采用网页提醒;游戏内通知作为后续项,不默认接入外部消息平台。
- 对重复告警设置冷却,库存恢复后解除告警。

验收标准:区分出售库存与收购容量,店主身份在服务端验证;页面展示数据更新时间,不把过期快照当成实时成交保证。

## P1:同物品比价与商店收藏

在已有价格查询和筛选之上,降低玩家重复寻找商店的成本。

- 比价区分出售最低价与收购最高价,并展示库存或容量、世界及坐标。
- 按单件价格比较,处理批量数量差异;带不同 NBT、附魔、药水或自定义名称的物品不能仅按材质合并。
- 收藏首版可使用浏览器本地存储,明确不跨设备同步,不在本地保存登录凭据。
- 商店已删除时标记失效,不能将旧收藏误关联到另一家商店。

验收标准:买卖方向、批量单位和物品变体均正确;刷新后收藏保留,商店失效后有明确状态。

## P1:管理操作审计与批量预览

在现有批量改价、改类型和删除基础上,补足操作追踪和误操作防护。

- 批量操作前展示命中数量、影响范围和价格变化预览;危险操作需确认。
- 审计记录操作者、操作时间、商店标识、修改前后值和逐项成功或失败结果。
- 审计记录与普通抓取日志分离,受管理权限保护。
- 不把商店数据备份称为交易回滚:已经发生的经济变动和物品交付不能通过恢复商店配置撤销。

验收标准:部分失败不会显示为全部成功;预览之后数据发生变化时重新验证;审计不记录敏感凭据。

## P2:店主经营统计

在交易流水稳定后增加成交额、成交数量、热门商品及时间趋势。首版明确只统计本插件记录的网页成交;若要统计游戏内交易,应另行适配 QuickShop 事件或历史记录并做去重,不能以网页数据冒充全服成交数据。

验收标准:购买与收购方向分开,失败和待处理订单不计入成交,金额精度与经济后端保持一致,统计页面明确数据来源和时间范围。

## P1:商店状态与经营保护

- 增加营业状态、临时隐藏、维护模式和交易熔断;QuickShop 连接异常时明确提示,禁止把旧快照当成交保证。
- 增加价格异常保护、最低/最高价格限制和大额交易二次确认,降低误改价与恶意刷交易风险。
- 增加订单查询、导出和人工退款审核;退款必须基于实际扣款与发货状态,不能用恢复商店备份代替退款。

## P2:通知与客户端体验

- 增加移动端优化、多语言、坐标复制、地图导航和游戏内快捷入口。
- 增加店主库存、收购容量、订单和异常交易提醒,首版优先网页提醒并设置告警冷却。
- Webhook、Discord、飞书等外部通知作为可选适配器,不默认引入第三方服务或保存外部凭据。
- 增加定期备份、数据完整性校验、迁移工具和更细粒度管理员权限。

## 建议实施顺序

1. 确定订单数据结构、持久化方式、权限和故障恢复规则。
2. 完成订单流水及幂等校验,并验证与库存、经济和离线队列的联动。
3. 增加验证码登录与限流,验证 AuthMe 兼容性。
4. 增加库存提醒、比价与收藏、管理审计。
5. 最后基于可靠成交数据增加经营统计。

每项实现需补充对应的聚焦验证;涉及交易、权限或恢复路径的功能还需在真实 Paper + QuickShop-Hikari + Vault 环境中验收。路线图文档本身不改变交易行为,也不发布新 JAR。
Loading
Loading