jianghaifeng
|
b567365460
|
feat(deploy): D1 后端部署就绪(配置外置 + prod fail-fast + 自包含 Compose + 冒烟/回滚)
为什么:用户下午要部署,但全仓没有任何部署产物;且生产漏配密钥会静默使用代码里的 dev 默认值,
属上线阻塞项(04-部署架构 / 12-上线检查清单)。
- 配置外置(三件):DB_URL、JWT 双密钥(**发现 app-secret 此前没外置,一直用 dev 默认值**)、
MEDIA_SIGNATURE_SECRET、MEDIA_SIGNATURE_REQUIRED、CORS_ALLOWED_ORIGINS、MEDIA_ROOT_DIR
- 新增三份 application-prod.yml:签名强制开启、CORS 收紧、媒资指向挂载点、日志级别
- 新增 ProdSecretGuard(@Profile("prod")):密钥缺失/仍是 dev 默认值/不足 32 位、签名未开、
DB_URL 或 CORS 为空 → 一次列全并终止启动(exit 1)
- 新增 /healthz 轻量健康端点:查明 /actuator/health 的"超时"是旧 jar 未重启所致,项目本就
没有 actuator 依赖,故不引新依赖;同时把 /healthz 加进安全白名单
- Docker 化:多阶段 Dockerfile(非 root、tini、依赖层缓存、ffmpeg 只给 worker)、.dockerignore、
自包含 docker-compose.yml(含 PostgreSQL 18 + Redis + 命名卷 + 按序初始化 sql + healthcheck)
- tools/smoke.sh(失败非 0 退出)+ DEPLOY.md(Compose 主线 / 裸 jar 兜底 / 回滚 / 故障速查)
- 实测:mvn test 83 条全过;两入口 package 通过;prod 缺密钥退出码 1 且报错明确、配齐后正常启动;
冒烟脚本全 PASS;本机无 Docker,镜像与 compose 未经构建验证(已在回执如实标注)
|
2026-10-16 13:01:08 +08:00 |
|
jianghaifeng
|
1dc97b4bf6
|
feat(commerce): B1-2 恢复购买/退款拒付回收/对账真实比对 + Apple 端口预留
为什么:风险清单 R-03 把"票据校验 + 对账打通"列为上线阻塞项。勘察发现 ReconcileService 与
/admin/reconcile 两个接口 B1 之前就已实现,真缺口是:对账没跟商店比对、差额用 0 兜底(违反
"取不到就用 null"的契约)、退款回收只有人工入口且不幂等。按实际缺口做,不重复造。
- 恢复购买:补专项测试(换设备重提同一票据 → 同一订单、原交付时间不改写、不新增流水);
补单能力沿用 B1-1 的服务端建单路径,恢复列表沿用 GET /app/orders,不新增接口
- 退款/拒付回收:GooglePlayPurchaseClient 增加 listVoidedPurchases(purchases.voidedpurchases,
带分页);新增 StoreRefundService 拉取并按 (platform, orderId) 幂等回收,本地无单计入差异;
OrderService 增加 refundByStoreCallback(已退款直接返回,不二次回收/不写第二条流水),
人工 refund() 的既有契约(已退款报错)保持不变
- 对账:reconcile_task 增 store_amount_sum(可空)与 notes 两列(幂等 ALTER);
run() 改为集合比对(本地缺票据 + 票据无本地单 + 商店已作废但本地无单),并在同一次 run 里
顺带执行退款回收;storeAmountSum 恒为 null 并在 notes 说明需从 Play Console 财务报告获取;
商店调用失败置 status=FAILED 且不写 SUCCESS+0
- Apple IAP:只留端口/枚举与明确提示,不实现真调用(B2 再实现)
- 契约同步 docs/api-phase1.md §1.8/§2.6;新增测试 17 条(commerce 14 + admin-api 3)
- 实测:mvn test 71 条全过(commerce 27 + admin-api 3 + app-api 41,B1-1 的 54 条未被打红);
两个入口 package 通过;端到端手动对账返回 storeAmountSum=null + notes 说明缺什么
|
2026-10-16 11:52:10 +08:00 |
|
Zhou Weidong
|
db897d50d6
|
feat(commerce): Google Play 票据校验闭环(校验→建单→发权益 + 幂等/防重放)
为什么:风险清单 R-03 把支付票据校验列为上线阻塞项;/app/orders/verify 此前只有 disabled
占位,且 verifyAndDeliver 要求订单事先存在——而 IAP 没有下单接口,接了校验也走不通。
- 新增 GooglePlayStoreVerifier(校验 purchaseState/productId/orderId)+ StorePlatforms(平台归一化)
- 新增 GooglePlayPurchaseClient 端口与 HttpGooglePlayPurchaseClient:JDK HttpClient + 服务账号
RS256 JWT 换 token,不引入 Google SDK 依赖;token 按过期缓存
- StoreVerifier 实现由 fruitshorts.commerce.store.verifier 选择,默认 disabled,无凭据时对外行为不变
- OrderService 补齐闭环:校验通过后按 (platform, storeProductId) 找商品建单、写 store_receipt 审计、
发金币并推进到 DELIVERED;幂等键 (platform, storeTransactionId) 与唯一索引双保险
- 防重放:重复提交同用户返回既有订单且不再发币;票据已被他人使用返回 403;校验失败不写任何数据
- 凭据只走配置/环境变量(packageName + service account JSON),不入库不提交
- 契约同步 docs/api-phase1.md §1.8;金额微单位换算与回退价有单测覆盖
- 实测:mvn test 54 条全过(commerce 13 + app-api 41,M2 的 37 条未被打红);
两个入口 package 通过;默认 disabled 端到端返回 200003 且 orders 表零行
|
2026-10-16 11:03:33 +08:00 |
|
Zhou Weidong
|
27e689c771
|
fix(config): 修复合并后 app-api 重复的 springdoc 配置键
|
2026-10-16 10:52:17 +08:00 |
|
Vicky
|
5ce6de1e3b
|
Merge remote-tracking branch 'origin/main'
|
2026-10-16 10:51:15 +08:00 |
|
Vicky
|
c6fcfcc6ea
|
docs(backend): S1 接口文档在线化——补 springdoc 配置 + 39 个 Controller 中文注解
为什么:前端联调依赖在线接口文档。S1 条目要求两个端口的 /v3/api-docs 能看、
且每个接口有中文分组与中文 summary。
诊断(与 PM 初步定位不同,有硬证据):app-api 的 /v3/api-docs、/doc.html、/、
/error 全部"挂住"(8s 超时 0 字节),根因不是 Spring Security 拦截,而是
运行中的进程握着一个被重新打包替换掉的旧 jar——spring-boot:repackage 把原 fat jar
原地改名为 .jar.original(同一 inode)再写新 jar,进程再去加载"启动后未加载过的类"
就必然 NoClassDefFoundError(日志里连 logback 的 ThrowableProxy 都加载不到),
线程直接死掉、既不响应也记不了日志。SecurityConfig.PUBLIC_PATHS 本来就包含
/doc.html、/swagger-ui/**、/v3/api-docs/**,未改任何安全配置。
- app-api 的 application.yml 补 springdoc.swagger-ui.path: /doc.html
- 39 个 Controller(app-api 10 + admin-api 29)补 @Tag(name=中文分组)
与 @Operation(summary=中文描述),共 172 个接口;描述取自 api-phase1.md 与既有 Javadoc
- 两个入口类加 @OpenAPIDefinition:文档标题 + 顶层 tags 清单(springdoc 默认顶层 tags 为空)
- 纯新增 357 行、删除 0 行:业务逻辑、路径、字段零改动
- 实测:mvn test 37 条契约测试全过;两个入口 package BUILD SUCCESS;
两端口 /v3/api-docs 中文 tags + 中文 summary,/doc.html 可打开,
非白名单路径 5ms 内返回 {"code":401,...} 不再挂住
|
2026-10-16 10:44:37 +08:00 |
|
Zhou Weidong
|
66e6fc1555
|
feat: app-api 启用 Swagger 在线接口文档 (springdoc /doc.html)
|
2026-10-16 10:13:47 +08:00 |
|
Vicky
|
fafadfc6ec
|
test(backend): C 端核心链路契约测试 37 条,mvn test 不再是 No tests to run
为什么:红线第 8 条要求后端 mvn test 通过且核心接口有契约测试;此前 13 个模块全是
No tests to run.,这个"通过"是空的。按 M2 口径只补 C 端核心链路最小集。
- 接入层契约(MockMvc,真控制器 + 真全局异常处理器 + service 替身):
/app/auth 注册与登录、目录四条路由、播放票据 FREE 与 COIN_REQUIRED 的字段形态
- 业务规则(真 service + 替身 Mapper):
EMAIL 免码不走验证码且 verified=0、PHONE 必须带码、验证码错/过期被拒、
密码强度、注册频控 429、账号重复 200001、密码错误 100000、封禁 100001、
免费/已购/未解锁/无片源四个播放分支
- 依赖只加到 fruitshorts-app-api(spring-boot-starter-test,test scope),不动其它模块
- 测试放在接入层模块,避免动 01-08 冻结模块目录;只新增测试,未改业务逻辑
- 实测:mvn test BUILD SUCCESS,app-api Tests run: 37, Failures: 0, Errors: 0;
mvn -DskipTests package 仍 BUILD SUCCESS
|
2026-10-16 09:52:52 +08:00 |
|
Vicky
|
37c9a141d3
|
feat: S0 主键改雪花(DDL 去 identity + 实体 ASSIGN_ID + 雪花配置)
为什么:自增主键把 ID 分配锁在单库序列上,分库分表时无法横向扩展。趁线上业务数据
近零(只有系统种子表有量)先改,代价最低。
- sql/schema.sql、sql/schema-phase1.sql、sql/partition-maintenance.sql:61 处
GENERATED BY DEFAULT AS IDENTITY 去掉,主键改 BIGINT 无默认值
- 52 个实体 @TableId IdType.AUTO -> IdType.ASSIGN_ID;两个入口 id-type: assign_id,
并加 global-config.sequence.worker-id/datacenter-id(单机固定值,多实例需按实例分配)
- 新增 sql/migrate-s0-snowflake.sql:存量库幂等迁移,走 DROP IDENTITY 保留数据,不重建表
- 实测:三份建表脚本在新库连跑两遍幂等;线上 57 张表 identity 归零;
watch_history/playback_ticket/order_event 的主键与唯一索引仍含分区键
|
2026-10-16 09:00:38 +08:00 |
|
Zhou Weidong
|
35be37c193
|
style: 全量补充作者标识 // Author: fruit.com.cn
|
2026-10-16 00:01:53 +08:00 |
|
Vicky
|
7e730fbbca
|
feat: 08 订单与权益落地(金币解锁 / 权益判定生效 / 补发回收 / 对账)
为什么:
1) 第一阶段要能真正收到钱,而收钱方式是金币解锁分集。本提交把钱包、金币流水、
权益、订单四样打通,客户端可以解锁并立刻播放,不再只是展示。
2) 权益判定此前只是端口占位(一律视为未解锁)。交易模块用 @Primary 提供实现后,
播放入口与下载入口的付费判定同时生效 —— 两条链路一行没改。
3) 钱的事必须可追溯、可幂等:所有金币变动都带幂等键并写 coin_ledger,
余额与流水同事务;订单每次状态流转都写 order_event(月分区),支付争议可回溯。
4) 后台要能兜住线上问题:补发(重复提交幂等)、退款(回收权益 + 扣回金币,
余额不足时留人工跟进备注)、人工补币(先校验用户存在,避免给不存在的用户建钱包)、
手动对账(默认每天 04:20 自动跑前一天)。
5) 09 商店支付按约束只做端口预留:StoreVerifier 未接入时明确报"尚未配置",
宁可不收钱也不产生假订单。
|
2026-10-15 12:26:27 +08:00 |
|
Vicky
|
af1dc48494
|
feat: 06 互动与内容治理落地(评论 / 点赞 / 举报 / 敏感词 / 审核留证)
为什么:
1) 产品侧要先把内容治理闭环跑通才能谈上线:评论可发布、可翻页、可删除,
违规内容能在后台被审核与驳回,举报要有工单可追溯。本提交把这套补齐。
2) 审核不能只靠人肉:敏感词按 BLOCK/REPLACE/REVIEW 三档处置,
机审通过即发布(可配置为 PRE_REVIEW 全部先审后发),人审与机审的结论
都写进 content_audit_record,争议时可回答"当时凭什么放行/驳回"。
3) 举报处理必须与处置打通:结论成立会联动驳回被举报评论,并可选同时封禁被举报用户,
避免"工单处理完了但违规内容还挂着"。
4) 顺带修复一个真实缺陷:user_favorite / content_like 上有唯一索引,而全局配置会把
deleteById 改写成逻辑删除,导致取消收藏/点赞后旧的 deleted 行仍占位,
再次收藏或点赞直接撞唯一索引报错。关系类数据没有留证价值,改为显式物理删除,
并补了"取消后再来一次"的回归自测。
|
2026-10-15 12:03:32 +08:00 |
|
Vicky
|
b3179fc836
|
refactor: 后端标识统一为 FruitShorts,并落地 05 离线下载
为什么:
1) 产品已定名 FruitShorts,但后端仍是 reelshort 系列标识 —— Java 包 com.reelshort、
模块名与 jar 名 reelshort-*、spring.application.name、配置前缀 reelshort.*,
这些会出现在启动日志、命令行和 IDE 里,与产品名不一致。本次整体替换为 fruitshorts,
数据库连接名上一轮已改名,保持不变。
2) 第一阶段下一批第 1 步(05 离线下载)要求把下载授权(设备绑定/有效期/吊销)、
下载任务与断点续传、配额与过期清理、仅 Wi-Fi 规则做成完整接口,本提交一并落地。
3) 顺带修复:schema-phase1.sql 里 playback_ticket / order_event 仍按普通表定义唯一索引,
表分区化之后重跑脚本必定报错(分区表的唯一索引必须包含分区键)。改为在本脚本里就
按分区表定义,并验证脚本可重复执行。
|
2026-10-15 11:44:26 +08:00 |
|