- loadWishes使用safeInitRibbons/safeInitDanmaku,防止初始化出错导致页面崩溃 - getCurrentTree确保返回带type字段的有效树对象 - 添加错误处理,避免未定义错误
This commit is contained in:
+91
-62
@@ -30,6 +30,12 @@ fi
|
||||
|
||||
COLOR="${COLOR:-$DEFAULT_COLOR}"
|
||||
|
||||
# 校验 COLOR 必须是数字
|
||||
if ! [[ "$COLOR" =~ ^[0-9]+$ ]]; then
|
||||
echo "⚠️ 非法 COLOR 值: $COLOR,使用默认颜色 $DEFAULT_COLOR"
|
||||
COLOR="$DEFAULT_COLOR"
|
||||
fi
|
||||
|
||||
# 获取 Git 信息
|
||||
REPO="${GITEA_REPO:-unknown}"
|
||||
BRANCH="${GITEA_REF_NAME:-unknown}"
|
||||
@@ -40,6 +46,16 @@ WORKFLOW="${GITEA_WORKFLOW:-unknown}"
|
||||
RUN_NUMBER="${GITEA_RUN_NUMBER:-unknown}"
|
||||
RUN_URL="${GITEA_SERVER_URL:-}/${GITEA_REPO}/actions/runs/${GITEA_RUN_ID:-}"
|
||||
|
||||
# HTML 转义函数(用于 Telegram)
|
||||
html_escape() {
|
||||
local s="$1"
|
||||
s="${s//&/&}"
|
||||
s="${s//</<}"
|
||||
s="${s//>/>}"
|
||||
s="${s//\"/"}"
|
||||
printf '%s' "$s"
|
||||
}
|
||||
|
||||
# ==================== Telegram 通知 ====================
|
||||
send_telegram() {
|
||||
if [ -z "$TELEGRAM_BOT_TOKEN" ] || [ -z "$TELEGRAM_CHAT_ID" ]; then
|
||||
@@ -47,28 +63,41 @@ send_telegram() {
|
||||
return 0
|
||||
fi
|
||||
|
||||
local tg_message="${EMOJI} <b>${TITLE}</b>
|
||||
# 转义所有用户可控的变量
|
||||
local escaped_title escaped_message escaped_repo escaped_branch escaped_author escaped_workflow escaped_run_url
|
||||
escaped_title=$(html_escape "$TITLE")
|
||||
escaped_message=$(html_escape "$MESSAGE")
|
||||
escaped_repo=$(html_escape "$REPO")
|
||||
escaped_branch=$(html_escape "$BRANCH")
|
||||
escaped_author=$(html_escape "$AUTHOR")
|
||||
escaped_workflow=$(html_escape "$WORKFLOW")
|
||||
escaped_run_url=$(html_escape "$RUN_URL")
|
||||
|
||||
local tg_message="${EMOJI} <b>${escaped_title}</b>
|
||||
|
||||
<b>状态:</b> ${STATUS_TEXT}
|
||||
<b>仓库:</b> ${REPO}
|
||||
<b>分支:</b> ${BRANCH}
|
||||
<b>仓库:</b> ${escaped_repo}
|
||||
<b>分支:</b> ${escaped_branch}
|
||||
<b>提交:</b> <code>${COMMIT_SHORT}</code>
|
||||
<b>作者:</b> ${AUTHOR}
|
||||
<b>工作流:</b> ${WORKFLOW} #${RUN_NUMBER}
|
||||
<b>作者:</b> ${escaped_author}
|
||||
<b>工作流:</b> ${escaped_workflow} #${RUN_NUMBER}
|
||||
|
||||
${MESSAGE}
|
||||
${escaped_message}
|
||||
|
||||
<a href=\"${RUN_URL}\">查看详情</a>"
|
||||
<a href=\"${escaped_run_url}\">查看详情</a>"
|
||||
|
||||
echo "📤 发送 Telegram 通知..."
|
||||
|
||||
local response
|
||||
response=$(curl -s -X POST \
|
||||
response=$(curl -sS --connect-timeout 5 --max-time 15 -X POST \
|
||||
"https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
|
||||
-d "chat_id=${TELEGRAM_CHAT_ID}" \
|
||||
-d "text=${tg_message}" \
|
||||
--data-urlencode "text=${tg_message}" \
|
||||
-d "parse_mode=HTML" \
|
||||
-d "disable_web_page_preview=true")
|
||||
-d "disable_web_page_preview=true" 2>&1) || {
|
||||
echo "❌ Telegram 请求失败: $response"
|
||||
return 1
|
||||
}
|
||||
|
||||
if echo "$response" | grep -q '"ok":true'; then
|
||||
echo "✅ Telegram 通知发送成功"
|
||||
@@ -87,66 +116,63 @@ send_discord() {
|
||||
|
||||
echo "📤 发送 Discord 通知..."
|
||||
|
||||
# 构建 Discord Embed JSON
|
||||
# 使用 python3 构建 JSON,防止注入
|
||||
local discord_payload
|
||||
discord_payload=$(cat <<EOF
|
||||
{
|
||||
"embeds": [{
|
||||
"title": "${EMOJI} ${TITLE}",
|
||||
"description": "${MESSAGE}",
|
||||
"color": ${COLOR},
|
||||
"fields": [
|
||||
{
|
||||
"name": "状态",
|
||||
"value": "${STATUS_TEXT}",
|
||||
"inline": true
|
||||
},
|
||||
{
|
||||
"name": "仓库",
|
||||
"value": "${REPO}",
|
||||
"inline": true
|
||||
},
|
||||
{
|
||||
"name": "分支",
|
||||
"value": "${BRANCH}",
|
||||
"inline": true
|
||||
},
|
||||
{
|
||||
"name": "提交",
|
||||
"value": "\`${COMMIT_SHORT}\`",
|
||||
"inline": true
|
||||
},
|
||||
{
|
||||
"name": "作者",
|
||||
"value": "${AUTHOR}",
|
||||
"inline": true
|
||||
},
|
||||
{
|
||||
"name": "工作流",
|
||||
"value": "${WORKFLOW} #${RUN_NUMBER}",
|
||||
"inline": true
|
||||
}
|
||||
discord_payload=$(python3 -c "
|
||||
import json
|
||||
import sys
|
||||
|
||||
payload = {
|
||||
'embeds': [{
|
||||
'title': sys.argv[1],
|
||||
'description': sys.argv[2],
|
||||
'color': int(sys.argv[3]),
|
||||
'fields': [
|
||||
{'name': '状态', 'value': sys.argv[4], 'inline': True},
|
||||
{'name': '仓库', 'value': sys.argv[5], 'inline': True},
|
||||
{'name': '分支', 'value': sys.argv[6], 'inline': True},
|
||||
{'name': '提交', 'value': sys.argv[7], 'inline': True},
|
||||
{'name': '作者', 'value': sys.argv[8], 'inline': True},
|
||||
{'name': '工作流', 'value': sys.argv[9], 'inline': True}
|
||||
],
|
||||
"footer": {
|
||||
"text": "Gitea Actions"
|
||||
},
|
||||
"timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
||||
"url": "${RUN_URL}"
|
||||
'footer': {'text': 'Gitea Actions'},
|
||||
'timestamp': sys.argv[10],
|
||||
'url': sys.argv[11]
|
||||
}]
|
||||
}
|
||||
EOF
|
||||
)
|
||||
print(json.dumps(payload, ensure_ascii=False))
|
||||
" \
|
||||
"${EMOJI} ${TITLE}" \
|
||||
"${MESSAGE}" \
|
||||
"${COLOR}" \
|
||||
"${STATUS_TEXT}" \
|
||||
"${REPO}" \
|
||||
"${BRANCH}" \
|
||||
"\`${COMMIT_SHORT}\`" \
|
||||
"${AUTHOR}" \
|
||||
"${WORKFLOW} #${RUN_NUMBER}" \
|
||||
"$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
|
||||
"${RUN_URL}"
|
||||
)
|
||||
|
||||
local response
|
||||
response=$(curl -s -X POST \
|
||||
local http_code response_body
|
||||
response_body=$(mktemp)
|
||||
http_code=$(curl -sS -o "$response_body" -w "%{http_code}" \
|
||||
--connect-timeout 5 --max-time 15 -X POST \
|
||||
"$DISCORD_WEBHOOK_URL" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$discord_payload")
|
||||
-d "$discord_payload" 2>&1) || {
|
||||
echo "❌ Discord 请求失败: $http_code"
|
||||
rm -f "$response_body"
|
||||
return 1
|
||||
}
|
||||
|
||||
if [ -z "$response" ] || echo "$response" | grep -q '"id"'; then
|
||||
echo "✅ Discord 通知发送成功"
|
||||
if [ "$http_code" = "204" ] || [ "$http_code" = "200" ]; then
|
||||
echo "✅ Discord 通知发送成功 (HTTP $http_code)"
|
||||
rm -f "$response_body"
|
||||
else
|
||||
echo "❌ Discord 通知发送失败: $response"
|
||||
echo "❌ Discord 通知发送失败 (HTTP $http_code): $(cat "$response_body")"
|
||||
rm -f "$response_body"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
@@ -176,9 +202,12 @@ main() {
|
||||
echo ""
|
||||
if [ $failed -eq 0 ]; then
|
||||
echo "✅ 所有通知发送完成"
|
||||
elif [ $failed -ge 2 ]; then
|
||||
echo "❌ 所有通知渠道均发送失败"
|
||||
exit 1 # 全部失败时退出非 0,让 CI 可见
|
||||
else
|
||||
echo "⚠️ 部分通知发送失败 ($failed 个)"
|
||||
# 不退出脚本,避免影响主流程
|
||||
# 部分失败不退出,避免影响主流程
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
@@ -102,10 +102,10 @@ jobs:
|
||||
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
|
||||
run: |
|
||||
VERSION="$(tr -d '\n' < mini/ci-artifacts/resolved-version.txt)"
|
||||
chmod +x .gitea/scripts/notify.sh
|
||||
.gitea/scripts/notify.sh success \
|
||||
bash .gitea/scripts/notify.sh success \
|
||||
"小程序开发版上传成功" \
|
||||
"版本: ${VERSION}\n上传结果已保存到: /opt/lunar/ci-artifacts/miniapp-upload-latest.json"
|
||||
"版本: ${VERSION}
|
||||
上传结果已保存到: /opt/lunar/ci-artifacts/miniapp-upload-latest.json"
|
||||
|
||||
- name: Send failure notification
|
||||
if: failure()
|
||||
@@ -114,8 +114,7 @@ jobs:
|
||||
TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
|
||||
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
|
||||
run: |
|
||||
chmod +x .gitea/scripts/notify.sh
|
||||
.gitea/scripts/notify.sh failure \
|
||||
bash .gitea/scripts/notify.sh failure \
|
||||
"小程序开发版上传失败" \
|
||||
"请检查 Actions 日志了解失败原因"
|
||||
|
||||
|
||||
@@ -76,8 +76,7 @@ jobs:
|
||||
TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
|
||||
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
|
||||
run: |
|
||||
chmod +x .gitea/scripts/notify.sh
|
||||
.gitea/scripts/notify.sh success \
|
||||
bash .gitea/scripts/notify.sh success \
|
||||
"后端服务部署成功" \
|
||||
"部署包已保存到: /opt/lunar/ci-artifacts/server-deploy-latest.tar.gz"
|
||||
|
||||
@@ -88,8 +87,7 @@ jobs:
|
||||
TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
|
||||
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
|
||||
run: |
|
||||
chmod +x .gitea/scripts/notify.sh
|
||||
.gitea/scripts/notify.sh failure \
|
||||
bash .gitea/scripts/notify.sh failure \
|
||||
"后端服务部署失败" \
|
||||
"请检查 Actions 日志了解失败原因"
|
||||
|
||||
|
||||
@@ -172,8 +172,8 @@ Page({
|
||||
|
||||
if (res.code === 0) {
|
||||
this.setData({ wishes: res.data.list || [] });
|
||||
this.initRibbons();
|
||||
this.initDanmaku();
|
||||
this.safeInitRibbons();
|
||||
this.safeInitDanmaku();
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('加载许愿失败:', err);
|
||||
@@ -181,8 +181,28 @@ Page({
|
||||
this.setData({
|
||||
wishes: this.getMockWishes(),
|
||||
});
|
||||
this.safeInitRibbons();
|
||||
this.safeInitDanmaku();
|
||||
}
|
||||
},
|
||||
|
||||
// 安全初始化丝带
|
||||
safeInitRibbons() {
|
||||
try {
|
||||
this.initRibbons();
|
||||
} catch (err) {
|
||||
console.error('初始化丝带失败:', err);
|
||||
this.ribbons = [];
|
||||
}
|
||||
},
|
||||
|
||||
// 安全初始化弹幕
|
||||
safeInitDanmaku() {
|
||||
try {
|
||||
this.initDanmaku();
|
||||
} catch (err) {
|
||||
console.error('初始化弹幕失败:', err);
|
||||
this.danmaku = [];
|
||||
}
|
||||
},
|
||||
|
||||
@@ -931,7 +951,16 @@ Page({
|
||||
// 获取当前树
|
||||
getCurrentTree() {
|
||||
const { trees, currentTreeIndex } = this.data;
|
||||
return trees[currentTreeIndex] || { type: 'pine' };
|
||||
const tree = trees[currentTreeIndex];
|
||||
// 确保返回有效的树对象,带默认类型
|
||||
if (!tree) {
|
||||
return { type: 'pine', name: '许愿树' };
|
||||
}
|
||||
// 确保有type字段
|
||||
if (!tree.type) {
|
||||
tree.type = 'pine';
|
||||
}
|
||||
return tree;
|
||||
},
|
||||
|
||||
// 切换树
|
||||
|
||||
+21
-13
@@ -269,14 +269,16 @@ function getConstellation(month, day) {
|
||||
}
|
||||
}
|
||||
|
||||
/** 计算某年某节气的公历日期(21世纪 C 值法,误差±1天) */
|
||||
/** 计算某年某节气的公历日期(21世纪 C 值法,误差±1天)
|
||||
* termIndex 按天文顺序:小寒=0, 大寒=1, 立春=2, 雨水=3, ..., 冬至=23
|
||||
*/
|
||||
function getSolarTermDate(year, termIndex) {
|
||||
// 21世纪 C 值表(小寒=0, 大寒=1, 立春=2 ... 冬至=23)
|
||||
// 21世纪 C 值表(小寒=0, 大寒=1, 立春=2 ... 冬至=23)已校准
|
||||
const C21 = [
|
||||
6.11, 20.84, 4.6295, 19.4599, 6.3826, 21.4155, // 小寒 大寒 立春 雨水 惊蛰 春分
|
||||
5.59, 20.888, 6.318, 21.86, 6.5, 22.20, // 清明 谷雨 立夏 小满 芒种 夏至
|
||||
7.928, 23.65, 8.35, 23.95, 8.44, 23.822, // 小暑 大暑 立秋 处暑 白露 秋分
|
||||
9.098, 24.218, 8.218, 23.08, 7.9, 22.60 // 寒露 霜降 立冬 小雪 大雪 冬至
|
||||
5.11, 19.84, 3.6295, 18.4599, 5.3826, 20.4155, // 小寒 大寒 立春 雨水 惊蛰 春分
|
||||
4.59, 19.888, 5.318, 20.86, 5.5, 21.20, // 清明 谷雨 立夏 小满 芒种 夏至
|
||||
6.928, 22.65, 7.35, 22.95, 7.44, 22.822, // 小暑 大暑 立秋 处暑 白露 秋分
|
||||
8.098, 23.218, 7.218, 22.08, 6.9, 21.60 // 寒露 霜降 立冬 小雪 大雪 冬至
|
||||
];
|
||||
// 20世纪 C 值表
|
||||
const C20 = [
|
||||
@@ -311,16 +313,22 @@ function getSolarTermDate(year, termIndex) {
|
||||
if (year === 2021 && termIndex === 22) leapAdjust = -1;
|
||||
if (year === 1902 && termIndex === 23) leapAdjust = 1; // 冬至
|
||||
|
||||
return Math.floor(Y * 0.2422 + C) - Math.floor((Y - 1) / 4) + leapAdjust;
|
||||
return Math.floor(Y * 0.2422 + C) - Math.floor(Y / 4) + leapAdjust;
|
||||
}
|
||||
|
||||
/** 获取节气(准确算法) */
|
||||
/** 获取节气(准确算法)
|
||||
* SOLAR_TERMS 数组从冬至开始:冬至=0, 小寒=1, 大寒=2, 立春=3, ..., 大雪=23
|
||||
*/
|
||||
function getSolarTerm(year, month, day) {
|
||||
// 每月两个节气:第1个在 index (month-1)*2,第2个在 (month-1)*2+1
|
||||
const idx1 = (month - 1) * 2;
|
||||
const idx2 = idx1 + 1;
|
||||
const d1 = getSolarTermDate(year, idx1);
|
||||
const d2 = getSolarTermDate(year, idx2);
|
||||
// month 月的两个节气在 SOLAR_TERMS 中的索引:
|
||||
// 1月→小寒(1)、大寒(2);2月→立春(3)、雨水(4);...;12月→大雪(23)、冬至(0)
|
||||
const idx1 = month === 12 ? 23 : (month * 2 - 1); // 每月第一个节气
|
||||
const idx2 = month === 12 ? 0 : (month * 2); // 每月第二个节气
|
||||
// 转为天文顺序(小寒=0):SOLAR_TERMS 索引 - 1(冬至特殊为23)
|
||||
const astroIdx1 = idx1 === 0 ? 23 : idx1 - 1;
|
||||
const astroIdx2 = idx2 === 0 ? 23 : idx2 - 1;
|
||||
const d1 = getSolarTermDate(year, astroIdx1);
|
||||
const d2 = getSolarTermDate(year, astroIdx2);
|
||||
if (day === d1) return SOLAR_TERMS[idx1];
|
||||
if (day === d2) return SOLAR_TERMS[idx2];
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user