chore: 停止上传开发文档并同步代码

- 从仓库索引移除 开发文档/(本地保留)
- 忽略 wechat/info.log 与 soul-api-linux
- 同步小程序/管理端/API改动

Made-with: Cursor
This commit is contained in:
卡若
2026-03-17 15:25:26 +08:00
parent c6904e4a32
commit f9d5e85b4e
350 changed files with 2588 additions and 64437 deletions

View File

@@ -32,12 +32,12 @@ except ImportError:
SCRIPT_DIR = Path(__file__).resolve().parent
# 默认发到 Soul 彩民团队飞书群
WEBHOOK = "https://open.feishu.cn/open-apis/bot/v2/hook/34b762fc-5b9b-4abb-a05a-96c8fb9599f1"
BACKEND_QRCODE_URL = "https://soul.quwanzhi.com/api/miniprogram/qrcode"
BACKEND_QRCODE_URL = "https://soulapi.quwanzhi.com/api/miniprogram/qrcode"
MINIPROGRAM_READ_BASE = "https://soul.quwanzhi.com/read"
# 海报尺寸(与小程序内一致,便于统一风格
POSTER_W = 300
POSTER_H = 450
# 海报尺寸(2x 放大,提升二维码识别率
POSTER_W = 600
POSTER_H = 900
# 高级感配色:深蓝渐变 + 青金点缀
COLOR_BG_TOP = (26, 26, 46) # #1a1a2e
COLOR_BG_BOTTOM = (22, 33, 62) # #16213e
@@ -167,11 +167,10 @@ def _excerpt_to_feishu_text(excerpt: str) -> str:
def build_poster(title: str, excerpt: str, qrcode_bytes: bytes) -> bytes:
"""绘制海报图PNG bytes标题=章节标题,样式与小程序内一致并略提升质感"""
"""绘制海报图PNG bytes600x900 大图确保二维码可扫"""
img = Image.new("RGB", (POSTER_W, POSTER_H), COLOR_BG_TOP)
draw = ImageDraw.Draw(img)
# 背景渐变(从上到下)
for y in range(POSTER_H):
t = y / POSTER_H
r = int(COLOR_BG_TOP[0] * (1 - t) + COLOR_BG_BOTTOM[0] * t)
@@ -179,37 +178,31 @@ def build_poster(title: str, excerpt: str, qrcode_bytes: bytes) -> bytes:
b = int(COLOR_BG_TOP[2] * (1 - t) + COLOR_BG_BOTTOM[2] * t)
draw.line([(0, y), (POSTER_W, y)], fill=(r, g, b))
# 顶部装饰条(加高一点更醒目)
draw.rectangle([0, 0, POSTER_W, 5], fill=COLOR_ACCENT)
draw.rectangle([0, 0, POSTER_W, 8], fill=COLOR_ACCENT)
# 小品牌标识(可选,弱化)
font_small = _font(11)
draw.text((20, 22), "Soul创业派对", fill=(150, 160, 180), font=font_small)
font_small = _font(20)
draw.text((40, 40), "Soul创业派对", fill=(150, 160, 180), font=font_small)
# 主标题:章节标题(字体加粗、略大,更易读)
font_title = _font(19, bold=True)
title_lines = _wrap_text(draw, title, font_title, POSTER_W - 40)
y = 50
for line in title_lines[:2]:
draw.text((20, y), line, fill=COLOR_TITLE, font=font_title)
y += 26
font_title = _font(36, bold=True)
title_lines = _wrap_text(draw, title, font_title, POSTER_W - 80)
y = 90
for line in title_lines[:3]:
draw.text((40, y), line, fill=COLOR_TITLE, font=font_title)
y += 48
# 分隔线
y += 8
draw.line([(20, y), (POSTER_W - 20, y)], fill=(100, 110, 130), width=1)
y += 12
draw.line([(40, y), (POSTER_W - 40, y)], fill=(100, 110, 130), width=1)
# 摘要(严格限制在摘要框内;每句空一行,避免挤/溢出)
y += 28
font_summary = _font(13)
y += 36
font_summary = _font(24)
excerpt = (excerpt or "扫码阅读全文").strip()
# 摘要框(增加边界感,但不抢视觉)
bottom_h = 100
box_x1, box_y1 = 16, y - 6
box_x2, box_y2 = POSTER_W - 16, POSTER_H - bottom_h - 14
bottom_h = 200
box_x1, box_y1 = 32, y - 10
box_x2, box_y2 = POSTER_W - 32, POSTER_H - bottom_h - 20
try:
draw.rounded_rectangle(
[box_x1, box_y1, box_x2, box_y2],
radius=10,
radius=16,
fill=(18, 26, 46),
outline=(40, 55, 80),
width=1,
@@ -222,10 +215,10 @@ def build_poster(title: str, excerpt: str, qrcode_bytes: bytes) -> bytes:
width=1,
)
text_x = 20
text_y = y
max_w = POSTER_W - 40
line_h = 18 # 12px 字体更舒服的行高
text_x = 44
text_y = y + 4
max_w = POSTER_W - 88
line_h = 34
max_lines = max(1, int((box_y2 - text_y) / line_h))
summary_lines = _excerpt_to_poster_lines(draw, excerpt, font_summary, max_w, max_lines)
for line in summary_lines:
@@ -233,26 +226,23 @@ def build_poster(title: str, excerpt: str, qrcode_bytes: bytes) -> bytes:
draw.text((text_x, text_y), line, fill=COLOR_SUMMARY, font=font_summary)
text_y += line_h
# 底部区域背景(深色块)
draw.rectangle([0, POSTER_H - bottom_h, POSTER_W, POSTER_H], fill=COLOR_BOTTOM_BG)
# 底部文字
font_hint = _font(13)
draw.text((20, POSTER_H - 62), "长按识别小程序码", fill=COLOR_TITLE, font=font_hint)
font_hint2 = _font(11)
draw.text((20, POSTER_H - 40), "长按小程序码阅读全文", fill=COLOR_HINT, font=font_hint2)
font_hint = _font(24)
draw.text((40, POSTER_H - 130), "长按识别小程序码", fill=COLOR_TITLE, font=font_hint)
font_hint2 = _font(20)
draw.text((40, POSTER_H - 90), "长按小程序码阅读全文", fill=COLOR_HINT, font=font_hint2)
# 二维码(右下角,略留白;不放置手指图标)
qr_size = 72
qr_x = POSTER_W - 20 - qr_size
qr_y = POSTER_H - 20 - qr_size
qr_size = 160
qr_x = POSTER_W - 40 - qr_size
qr_y = POSTER_H - 30 - qr_size
try:
qr_img = Image.open(io.BytesIO(qrcode_bytes)).convert("RGB")
qr_img = qr_img.resize((qr_size, qr_size), Image.Resampling.LANCZOS)
img.paste(qr_img, (qr_x, qr_y))
except Exception:
draw.rectangle([qr_x, qr_y, qr_x + qr_size, qr_y + qr_size], outline=COLOR_ACCENT, width=2)
draw.text((qr_x + 18, qr_y + 26), "扫码", fill=COLOR_ACCENT, font=_font(12))
draw.text((qr_x + 40, qr_y + 60), "扫码", fill=COLOR_ACCENT, font=_font(24))
buf = io.BytesIO()
img.save(buf, format="PNG")
@@ -284,7 +274,7 @@ def fetch_qrcode_image(section_id: str) -> bytes | None:
scene = f"id={section_id}"
payload = {"scene": scene, "page": "pages/read/read", "width": 280}
try:
r = requests.post(BACKEND_QRCODE_URL, json=payload, timeout=15)
r = requests.post(BACKEND_QRCODE_URL, json=payload, timeout=15, verify=False)
body = r.json() if r.headers.get("content-type", "").startswith("application/json") else None
if body and body.get("success") and body.get("image"):
raw = body["image"]
@@ -371,8 +361,14 @@ def main():
qrcode_bytes = fetch_qrcode_image(section_id)
if not qrcode_bytes:
print("无法获取小程序码,请确认后端 soul.quwanzhi.com 可访问且已配置小程序码接口")
sys.exit(1)
print("⚠️ 小程序码获取失败,使用占位图继续生成海报")
# 生成一个简单的占位 PNG白底+提示文字)
_ph = Image.new("RGB", (280, 280), (240, 240, 240))
_d = ImageDraw.Draw(_ph)
_d.text((60, 120), "扫码阅读全文", fill=(100, 100, 100), font=_font(18))
_buf = io.BytesIO()
_ph.save(_buf, format="PNG")
qrcode_bytes = _buf.getvalue()
poster_bytes = build_poster(title, excerpt, qrcode_bytes)