# shilangsong_2026 **Repository Path**: chd/shilangsong_2026 ## Basic Information - **Project Name**: shilangsong_2026 - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-14 - **Last Updated**: 2026-09-15 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 唐诗诵 / shilangsong 诗词朗读应用:微信小程序提供诗词浏览、录音、点赞和个人作品;Thymeleaf 后台提供诗词维护、录音管理及举报审核。 ## 技术栈 | 组件 | 版本 / 用途 | |--------------|-------------------------------------| | Java | 21 | | Spring Boot | 4.1.1,Spring MVC,内嵌 Tomcat | | MyBatis-Plus | 3.5.17,实体 CRUD、分页与 XML SQL | | Sa-Token | 1.46.0,后台与小程序分离认证 | | Thymeleaf | 由 Spring Boot 管理版本,服务端模板 | | Maven | Wrapper 固定 3.9.16 | | 数据库 | MySQL 8.x;测试使用 H2 MySQL 模式 | 不再依赖 Eclipse/Ant 构建、外部 JAR 目录、Nutz、Shiro 或 JSP。产物为可执行 JAR。 ## 管理后台 UI `src/main/resources/static/css/admin.css` 使用统一设计令牌驱动颜色、字阶、间距、圆角、阴影和动效;页面包含浅色/深色模式、响应式布局、跳转主内容链接和键盘焦点样式。组件规范见 [UI 设计系统](doc/ui-design-system.html)。 ## 构建与验证 Windows PowerShell: ```powershell .\mvnw.cmd clean verify npm ci --prefix client npm test --prefix client ``` Linux/macOS 使用 `sh ./mvnw`。测试数据库在内存中创建,微信调用由测试替身处理;无需业务数据库或真实微信凭据。 ## 首次启动 1. 创建使用 `utf8mb4` 的 MySQL 数据库及应用账号。 2. 在 **空数据库**中执行 `src/main/resources/db/schema-mysql.sql`。 3. 可选:依次导入 `seed-res_type.sql`、`seed-res_info.sql`、`seed-res_content.sql` 。脚本都位于同一目录,使用显式列名并移除了历史零日期。不导入种子数据也可使用:登录后台,在“添加诗词”页面先创建分类,再录入第一首诗词。 4. 设置环境变量并启动: ```powershell $env:DB_URL = 'jdbc:mysql://localhost:3306/shilangsong?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai' $env:DB_USERNAME = 'shilangsong' $env:DB_PASSWORD = '<数据库密码>' $env:ADMIN_USERNAME = '<首次创建的管理员账号>' $env:ADMIN_PASSWORD = '<至少8位的管理员密码>' $env:UPLOAD_ROOT = 'D:/shilangsong-data/uploads' java -jar target/shilangsong_2026-1.0.0-SNAPSHOT.jar ``` 访问 `http://localhost:8080/login`。没有预置生产账号或默认密码。管理员引导仅在用户表为空时创建账号;账号存在时不会覆盖其密码。首次创建成功后应移除 ` ADMIN_USERNAME` 和 `ADMIN_PASSWORD` 环境变量。 开发模式也可执行 `.\mvnw.cmd spring-boot:run`。数据库初始化默认关闭,不会在启动时自动更改现有表。 ## 已有数据库迁移 **不要对已有数据库执行新建表脚本。** 若已经部署过本项目较早的 Spring Boot 版本,还需审核并执行 `src/main/resources/db/upgrade-review-fixes.sql`,为订阅表增加待发送唯一键;新版空库脚本和 Nutz 升级脚本已经包含该约束,不要重复执行。 先阅读 [迁移说明](doc/MIGRATION.md),核对 `upgrade-from-nutz.sql` 中的预检查及一次性变更。仓库原先没有完整 DDL,因此必须以实际数据库结构为准。 历史录音文件无需改名。将旧站点的 `files/` 目录路径配置为 `LEGACY_UPLOAD_ROOT`;新录音写入 `UPLOAD_ROOT`。存储目录不要配置为 Web 静态目录。 ## 微信小程序 - 最低功能基线为微信基础库 **2.21.2**,开发工具 `project.config.json` 已固定到该版本用于验证下限。发布时还需在微信公众平台配置相同或更高的最低基础库版本,本地配置不会自动修改线上设置。 - 昵称输入通过 `wx.canIUse` 检测:不支持原生昵称填写时使用普通文本输入;订阅提醒接口不存在时提示更新微信。此降级仅覆盖对应功能,不代表完整支持低于基线的所有旧版本。 - 服务端设置 `WECHAT_APP_ID` 和 `WECHAT_APP_SECRET`。 - 在微信开发者工具打开 `client/qixi/`,将 `config.js` 的 `baseUrl` 改为服务地址。真机/正式环境需配置 HTTPS 合法域名。 - 缓存会话通过 `GET /api/auth/session` 验证。请求收到 401 时共享一次登录刷新,并最多自动重试一次;上传保留原文件和 `requestId`。失败后当前页面显示“重试上传”,完成前不要关闭或重启小程序。 - 小程序使用 `wx.login`,服务端换取 openId 后签发 `mini-token`;客户端不会获得 AppSecret 或 `session_key`。 - 小程序与后端需一起发布:写接口现使用 POST 和 token,录音上传需携带每次录音唯一的 `requestId`。 - 昵称在“我的”页面填写;不再依赖旧 `getUserInfo` 授权弹窗获取身份。 ## 订阅消息 旧 formId 模板通知已替换为用户主动授权的订阅消息。默认关闭,避免迁移启动时触发外部发送。 需要启用时设置 `WECHAT_SUBSCRIPTION_ENABLED=true` 和 `WECHAT_TEMPLATE_ID`,并确认模板字段与 `SubscriptionService` 的 `thing1` / `time2` 一致。实际微信账号权限、模板和基础库需完成真机验收。发送不确定或失败的记录保留为状态 2,需人工核对,不自动重复发送。状态 0/2 保留唯一 `pending_key`,同一用户和模板的并发请求不会新增重复待发送记录;确认发送成功后释放该键。 ## 配置速查 | 环境变量 | 说明 | |------------------------------------------------------|-------------------------------------------------------------------------------| | `PORT` | HTTP 端口,默认 8080 | | `APP_TRUSTED_PROXIES` | 允许提供 X-Forwarded-For 的代理 IP/CIDR,逗号分隔,默认不信任任何代理 | | `DB_URL` / `DB_USERNAME` / `DB_PASSWORD` | MySQL 连接 | | `UPLOAD_ROOT` | 新录音目录,默认 `./data/uploads`;生产建议配置绝对路径 | | `LEGACY_UPLOAD_ROOT` | 旧 `files/` 目录,可选 | | `UPLOAD_DAILY_RECORDS` | 单用户每日录音条数上限,默认 100 | | `UPLOAD_TOTAL_MB` | 单用户录音总容量上限(MB),默认 512 | | `ADMIN_USERNAME` / `ADMIN_PASSWORD` | 空用户表首次创建管理员 | | `MINI_TOKEN_TIMEOUT` | 小程序 token 有效期(秒),默认 2592000(30 天) | | `MINI_TOKEN_NAME` / `MINI_TOKEN_READ_COOKIE` | 小程序 token 的 header 名(默认 `mini-token`)与是否读取 Cookie(默认 false) | | `LOG_FILE` / `LOG_MAX_FILE_SIZE` / `LOG_MAX_HISTORY` | 滚动日志文件与保留策略,默认 `./logs/shilangsong.log`、20MB、14 份 | | `WECHAT_APP_ID` / `WECHAT_APP_SECRET` | 微信登录凭据 | | `WECHAT_SUBSCRIPTION_ENABLED` / `WECHAT_TEMPLATE_ID` | 订阅通知开关和模板 | | `WECHAT_NOTIFICATION_CRON` | 提醒计划,默认周一、周四 10:15 | | `WECHAT_SEND_INTERVAL_MS` | 连续通知发送间隔,默认100毫秒,范围0–60000 | | `WECHAT_MAX_RUN_SECONDS` | 单次通知任务领取新记录的时间预算,默认600秒,范围1–86400 | Sa-Token 默认使用单实例内存会话;应用重启后需重新登录。多实例部署前需配置共享会话存储。通过 HTTPS 部署时按代理方式配置安全 Cookie。客户端 IP 使用下述显式可信代理策略。 ## 反向代理与登录限流 默认 `server.forward-headers-strategy=none`,应用直接使用连接来源 IP;不会信任客户端随意提交的 `X-Forwarded-For` 。使用反向代理时,将实际代理地址加入 `APP_TRUSTED_PROXIES`,例如仅限本机代理的 `127.0.0.1,::1`,或具体内网代理网段 `10.20.0.0/24`。不要配置不受控的大网段,也不要同时启用其他组件重新改写来源 IP。 可信代理需要覆盖或正确追加 `X-Forwarded-For`。解析从右往左进行,在第一个非可信跳点停止;直接连接者伪造该头不会改变限流身份。例如 Nginx 可配置: ```nginx location / { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } ``` 管理员账号仍在连续失败 5 次后限制 15 分钟;来源 IP 使用单独的每分钟 120 次请求额度,后台与微信登录独立计数。五次失败不再锁住同一代理/NAT 后的其他账号。单地址使用 15 分钟滑动窗口统计失败:窗口内失败达到 200 次即触发地址级封禁,持续喷洒会一直保持封禁,停止失败 15 分钟后自愈,防止随机用户名喷洒灌满限流表。会话和限流均为单实例内存实现,多实例环境需共享存储或网关限流。 ## 小程序界面 读诗、诗友榜、我的诗集和登录页已统一为纸白与墨绿风格,图标和山水插画随小程序本地打包。[查看四页视觉预览](doc/ui-preview/overview.png) ,或用浏览器打开 `doc/ui-preview/preview.html`。预览使用示例数据,并非微信真机截图。 ## 全量自动化复测 ```powershell .\mvnw.cmd -B -ntp clean verify npm ci --prefix client npm test --prefix client ``` 小程序测试依赖位于 `client/package.json`,不进入小程序发布目录 `client/qixi/`。`npm test` 包含36项交互回归以及WXML/WXSS编译、13个编译后渲染场景。后端38项测试通过(含2026-09-12安全加固回归);变更与验证边界见 [迁移说明](doc/MIGRATION.md) ,此前验证记录见 [全量测试报告](doc/TEST_REPORT.md)。