diff --git a/devlop.py b/devlop.py new file mode 100644 index 00000000..5219b34d --- /dev/null +++ b/devlop.py @@ -0,0 +1,775 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from __future__ import print_function + +import os +import sys +import shutil +import tempfile +import argparse +import json +import zipfile +import tarfile +import subprocess +import time +import hashlib + +try: + import paramiko +except ImportError: + print("错误: 请先安装 paramiko") + print(" pip install paramiko") + sys.exit(1) + +try: + import requests + import urllib3 + urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) +except ImportError: + print("错误: 请先安装 requests") + print(" pip install requests") + sys.exit(1) + + +# ==================== 配置 ==================== + +# 端口统一从环境变量 DEPLOY_PORT 读取,未设置时使用此默认值(需与 Nginx proxy_pass、ecosystem.config.cjs 一致) +DEPLOY_PM2_APP = "soul" +DEFAULT_DEPLOY_PORT = 3006 +DEPLOY_PROJECT_PATH = "/www/wwwroot/自营/soul" +DEPLOY_SITE_URL = "https://soul.quwanzhi.com" +# SSH 端口(支持环境变量 DEPLOY_SSH_PORT,未设置时默认为 22022) +DEFAULT_SSH_PORT = int(os.environ.get("DEPLOY_SSH_PORT", "22022")) + +def get_cfg(): + """获取基础部署配置(deploy 模式与 devlop 共用 SSH/宝塔)""" + return { + "host": os.environ.get("DEPLOY_HOST", "43.139.27.93"), + "user": os.environ.get("DEPLOY_USER", "root"), + "password": os.environ.get("DEPLOY_PASSWORD", "Zhiqun1984"), + "ssh_key": os.environ.get("DEPLOY_SSH_KEY", ""), + "project_path": os.environ.get("DEPLOY_PROJECT_PATH", DEPLOY_PROJECT_PATH), + "panel_url": os.environ.get("BAOTA_PANEL_URL", "https://43.139.27.93:9988"), + "api_key": os.environ.get("BAOTA_API_KEY", "hsAWqFSi0GOCrunhmYdkxy92tBXfqYjd"), + "pm2_name": os.environ.get("DEPLOY_PM2_APP", DEPLOY_PM2_APP), + "site_url": os.environ.get("DEPLOY_SITE_URL", DEPLOY_SITE_URL), + "port": int(os.environ.get("DEPLOY_PORT", str(DEFAULT_DEPLOY_PORT))), + "node_version": os.environ.get("DEPLOY_NODE_VERSION", "v22.14.0"), + "node_path": os.environ.get("DEPLOY_NODE_PATH", "/www/server/nodejs/v22.14.0/bin"), + } + + +def get_cfg_devlop(): + """devlop 模式配置:在基础配置上增加 base_path / dist / dist2。 + 实际运行目录为 dist_path(切换后新版本在 dist),宝塔 PM2 项目路径必须指向 dist_path, + 否则会从错误目录启动导致 .next/static 等静态资源 404。""" + cfg = get_cfg().copy() + cfg["base_path"] = os.environ.get("DEVOP_BASE_PATH", DEPLOY_PROJECT_PATH) + cfg["dist_path"] = cfg["base_path"] + "/dist" + cfg["dist2_path"] = cfg["base_path"] + "/dist2" + return cfg + + +# ==================== 宝塔 API ==================== + +def _get_sign(api_key): + now_time = int(time.time()) + sign_str = str(now_time) + hashlib.md5(api_key.encode("utf-8")).hexdigest() + request_token = hashlib.md5(sign_str.encode("utf-8")).hexdigest() + return now_time, request_token + + +def _baota_request(panel_url, api_key, path, data=None): + req_time, req_token = _get_sign(api_key) + payload = {"request_time": req_time, "request_token": req_token} + if data: + payload.update(data) + url = panel_url.rstrip("/") + "/" + path.lstrip("/") + try: + r = requests.post(url, data=payload, verify=False, timeout=30) + return r.json() if r.text else {} + except Exception as e: + print(" API 请求失败: %s" % str(e)) + return None + + +def get_node_project_list(panel_url, api_key): + for path in ["/project/nodejs/get_project_list", "/plugin?action=a&name=nodejs&s=get_project_list"]: + result = _baota_request(panel_url, api_key, path) + if result and (result.get("status") is True or "data" in result): + return result.get("data", []) + return None + + +def get_node_project_status(panel_url, api_key, pm2_name): + projects = get_node_project_list(panel_url, api_key) + if projects: + for p in projects: + if p.get("name") == pm2_name: + return p + return None + + +def start_node_project(panel_url, api_key, pm2_name): + for path in ["/project/nodejs/start_project", "/plugin?action=a&name=nodejs&s=start_project"]: + result = _baota_request(panel_url, api_key, path, {"project_name": pm2_name}) + if result and (result.get("status") is True or result.get("msg") or "成功" in str(result)): + print(" [成功] 启动成功: %s" % pm2_name) + return True + return False + + +def stop_node_project(panel_url, api_key, pm2_name): + for path in ["/project/nodejs/stop_project", "/plugin?action=a&name=nodejs&s=stop_project"]: + result = _baota_request(panel_url, api_key, path, {"project_name": pm2_name}) + if result and (result.get("status") is True or result.get("msg") or "成功" in str(result)): + print(" [成功] 停止成功: %s" % pm2_name) + return True + return False + + +def restart_node_project(panel_url, api_key, pm2_name): + project_status = get_node_project_status(panel_url, api_key, pm2_name) + if project_status: + print(" 项目状态: %s" % project_status.get("status", "未知")) + for path in ["/project/nodejs/restart_project", "/plugin?action=a&name=nodejs&s=restart_project"]: + result = _baota_request(panel_url, api_key, path, {"project_name": pm2_name}) + if result and (result.get("status") is True or result.get("msg") or "成功" in str(result)): + print(" [成功] 重启成功: %s" % pm2_name) + return True + if result and "msg" in result: + print(" API 返回: %s" % result.get("msg")) + print(" [警告] 重启失败,请检查宝塔 Node 插件是否安装、API 密钥是否正确") + return False + + +def add_or_update_node_project(panel_url, api_key, pm2_name, project_path, port=None, node_path=None): + if port is None: + port = int(os.environ.get("DEPLOY_PORT", str(DEFAULT_DEPLOY_PORT))) + port_env = "PORT=%d " % port + run_cmd = port_env + ("%s/node server.js" % node_path if node_path else "node server.js") + payload = {"name": pm2_name, "path": project_path, "run_cmd": run_cmd, "port": str(port)} + for path in ["/project/nodejs/add_project", "/plugin?action=a&name=nodejs&s=add_project"]: + result = _baota_request(panel_url, api_key, path, payload) + if result and result.get("status") is True: + print(" [成功] 项目配置已更新: %s" % pm2_name) + return True + if result and "msg" in result: + print(" API 返回: %s" % result.get("msg")) + return False + + +# ==================== 本地构建 ==================== + +def run_build(root): + """执行本地 pnpm build""" + use_shell = sys.platform == "win32" + standalone = os.path.join(root, ".next", "standalone") + server_js = os.path.join(standalone, "server.js") + + try: + r = subprocess.run( + ["pnpm", "build"], + cwd=root, + shell=use_shell, + timeout=600, + capture_output=True, + text=True, + encoding="utf-8", + errors="replace", + ) + stdout_text = r.stdout or "" + stderr_text = r.stderr or "" + combined = stdout_text + stderr_text + is_windows_symlink_error = ( + sys.platform == "win32" + and r.returncode != 0 + and ("EPERM" in combined or "symlink" in combined.lower() or "operation not permitted" in combined.lower() or "errno: -4048" in combined) + ) + + if r.returncode != 0: + if is_windows_symlink_error: + print(" [警告] Windows 符号链接权限错误(EPERM)") + print(" 解决方案:开启开发者模式 / 以管理员运行 / 或使用 --no-build") + if os.path.isdir(standalone) and os.path.isfile(server_js): + print(" [成功] standalone 输出可用,继续部署") + return True + return False + print(" [失败] 构建失败,退出码:", r.returncode) + for line in (stdout_text.strip().split("\n") or [])[-10:]: + print(" " + line) + return False + except subprocess.TimeoutExpired: + print(" [失败] 构建超时(超过10分钟)") + return False + except FileNotFoundError: + print(" [失败] 未找到 pnpm,请安装: npm install -g pnpm") + return False + except Exception as e: + print(" [失败] 构建异常:", str(e)) + if os.path.isdir(standalone) and os.path.isfile(server_js): + print(" [提示] 可尝试使用 --no-build 跳过构建") + return False + + if not os.path.isdir(standalone) or not os.path.isfile(server_js): + print(" [失败] 未找到 .next/standalone 或 server.js") + return False + print(" [成功] 构建完成") + return True + + +def clean_standalone_before_build(root, retries=3, delay=2): + """构建前删除 .next/standalone,避免 Windows EBUSY""" + standalone = os.path.join(root, ".next", "standalone") + if not os.path.isdir(standalone): + return True + for attempt in range(1, retries + 1): + try: + shutil.rmtree(standalone) + print(" [清理] 已删除 .next/standalone(第 %d 次尝试)" % attempt) + return True + except (OSError, PermissionError): + if attempt < retries: + print(" [清理] 被占用,%ds 后重试 (%d/%d) ..." % (delay, attempt, retries)) + time.sleep(delay) + else: + print(" [失败] 无法删除 .next/standalone,可改用 --no-build") + return False + return False + + +# ==================== 打包(deploy 模式:tar.gz) ==================== + +def _copy_with_dereference(src, dst): + if os.path.islink(src): + link_target = os.readlink(src) + real_path = link_target if os.path.isabs(link_target) else os.path.join(os.path.dirname(src), link_target) + if os.path.exists(real_path): + if os.path.isdir(real_path): + shutil.copytree(real_path, dst, symlinks=False, dirs_exist_ok=True) + else: + shutil.copy2(real_path, dst) + else: + shutil.copy2(src, dst, follow_symlinks=False) + elif os.path.isdir(src): + if os.path.exists(dst): + shutil.rmtree(dst) + shutil.copytree(src, dst, symlinks=False, dirs_exist_ok=True) + else: + shutil.copy2(src, dst) + + +def pack_standalone_tar(root): + """打包 standalone 为 tar.gz(deploy 模式用)""" + print("[2/4] 打包 standalone ...") + standalone = os.path.join(root, ".next", "standalone") + static_src = os.path.join(root, ".next", "static") + public_src = os.path.join(root, "public") + ecosystem_src = os.path.join(root, "ecosystem.config.cjs") + + if not os.path.isdir(standalone) or not os.path.isdir(static_src): + print(" [失败] 未找到 .next/standalone 或 .next/static") + return None + chunks_dir = os.path.join(static_src, "chunks") + if not os.path.isdir(chunks_dir): + print(" [失败] .next/static/chunks 不存在,请先完整执行 pnpm build(本地 pnpm start 能正常打开页面后再部署)") + return None + + staging = tempfile.mkdtemp(prefix="soul_deploy_") + try: + for name in os.listdir(standalone): + _copy_with_dereference(os.path.join(standalone, name), os.path.join(staging, name)) + node_modules_dst = os.path.join(staging, "node_modules") + pnpm_dir = os.path.join(node_modules_dst, ".pnpm") + if os.path.isdir(pnpm_dir): + for dep in ["styled-jsx"]: + dep_in_root = os.path.join(node_modules_dst, dep) + if not os.path.exists(dep_in_root): + for pnpm_pkg in os.listdir(pnpm_dir): + if pnpm_pkg.startswith(dep + "@"): + src_dep = os.path.join(pnpm_dir, pnpm_pkg, "node_modules", dep) + if os.path.isdir(src_dep): + shutil.copytree(src_dep, dep_in_root, symlinks=False, dirs_exist_ok=True) + break + static_dst = os.path.join(staging, ".next", "static") + if os.path.exists(static_dst): + shutil.rmtree(static_dst) + os.makedirs(os.path.dirname(static_dst), exist_ok=True) + shutil.copytree(static_src, static_dst) + # 同步构建索引(与 start-standalone.js 一致),避免宝塔上 server 用错导致页面空白/404 + next_root = os.path.join(root, ".next") + next_staging = os.path.join(staging, ".next") + index_files = [ + "BUILD_ID", + "build-manifest.json", + "app-path-routes-manifest.json", + "routes-manifest.json", + "prerender-manifest.json", + "required-server-files.json", + "fallback-build-manifest.json", + ] + for name in index_files: + src = os.path.join(next_root, name) + if os.path.isfile(src): + shutil.copy2(src, os.path.join(next_staging, name)) + print(" [已同步] 构建索引: BUILD_ID, build-manifest, routes-manifest 等") + if os.path.isdir(public_src): + shutil.copytree(public_src, os.path.join(staging, "public"), dirs_exist_ok=True) + if os.path.isfile(ecosystem_src): + shutil.copy2(ecosystem_src, os.path.join(staging, "ecosystem.config.cjs")) + pkg_json = os.path.join(staging, "package.json") + if os.path.isfile(pkg_json): + try: + with open(pkg_json, "r", encoding="utf-8") as f: + data = json.load(f) + data.setdefault("scripts", {})["start"] = "node server.js" + with open(pkg_json, "w", encoding="utf-8") as f: + json.dump(data, f, indent=2, ensure_ascii=False) + except Exception: + pass + tarball = os.path.join(tempfile.gettempdir(), "soul_deploy.tar.gz") + with tarfile.open(tarball, "w:gz") as tf: + for name in os.listdir(staging): + tf.add(os.path.join(staging, name), arcname=name) + print(" [成功] 打包完成: %s (%.2f MB)" % (tarball, os.path.getsize(tarball) / 1024 / 1024)) + return tarball + except Exception as e: + print(" [失败] 打包异常:", str(e)) + return None + finally: + shutil.rmtree(staging, ignore_errors=True) + + +# ==================== Node 环境检查 & SSH 上传(deploy 模式) ==================== + +def check_node_environments(cfg): + print("[检查] Node 环境 ...") + client = paramiko.SSHClient() + client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + try: + if cfg.get("ssh_key"): + client.connect(cfg["host"], port=DEFAULT_SSH_PORT, username=cfg["user"], key_filename=cfg["ssh_key"], timeout=15) + else: + client.connect(cfg["host"], port=DEFAULT_SSH_PORT, username=cfg["user"], password=cfg["password"], timeout=15) + stdin, stdout, stderr = client.exec_command("which node && node -v", timeout=10) + print(" 默认 Node: %s" % (stdout.read().decode("utf-8", errors="replace").strip() or "未找到")) + node_path = cfg.get("node_path", "/www/server/nodejs/v22.14.0/bin") + stdin, stdout, stderr = client.exec_command("%s/node -v 2>/dev/null" % node_path, timeout=5) + print(" 配置 Node: %s" % (stdout.read().decode("utf-8", errors="replace").strip() or "不可用")) + return True + except Exception as e: + print(" [警告] %s" % str(e)) + return False + finally: + client.close() + + +def upload_and_extract(cfg, tarball_path): + """SSH 上传 tar.gz 并解压到 project_path(deploy 模式)""" + print("[3/4] SSH 上传并解压 ...") + if not cfg.get("password") and not cfg.get("ssh_key"): + print(" [失败] 请设置 DEPLOY_PASSWORD 或 DEPLOY_SSH_KEY") + return False + client = paramiko.SSHClient() + client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + try: + if cfg.get("ssh_key") and os.path.isfile(cfg["ssh_key"]): + client.connect(cfg["host"], port=DEFAULT_SSH_PORT, username=cfg["user"], key_filename=cfg["ssh_key"], timeout=15) + else: + client.connect(cfg["host"], port=DEFAULT_SSH_PORT, username=cfg["user"], password=cfg["password"], timeout=15) + sftp = client.open_sftp() + remote_tar = "/tmp/soul_deploy.tar.gz" + remote_script = "/tmp/soul_deploy_extract.sh" + sftp.put(tarball_path, remote_tar) + node_path = cfg.get("node_path", "/www/server/nodejs/v22.14.0/bin") + project_path = cfg["project_path"] + script_content = """#!/bin/bash +export PATH=%s:$PATH +cd %s +rm -rf .next public ecosystem.config.cjs server.js package.json 2>/dev/null +tar -xzf %s +rm -f %s +echo OK +""" % (node_path, project_path, remote_tar, remote_tar) + with sftp.open(remote_script, "w") as f: + f.write(script_content) + sftp.close() + client.exec_command("chmod +x %s" % remote_script, timeout=10) + stdin, stdout, stderr = client.exec_command("bash %s" % remote_script, timeout=120) + out = stdout.read().decode("utf-8", errors="replace").strip() + exit_status = stdout.channel.recv_exit_status() + if exit_status != 0 or "OK" not in out: + print(" [失败] 解压失败,退出码:", exit_status) + return False + print(" [成功] 解压完成: %s" % project_path) + return True + except Exception as e: + print(" [失败] SSH 错误:", str(e)) + return False + finally: + client.close() + + +def deploy_via_baota_api(cfg): + """宝塔 API 重启 Node 项目(deploy 模式)""" + print("[4/4] 宝塔 API 管理 Node 项目 ...") + panel_url, api_key, pm2_name = cfg["panel_url"], cfg["api_key"], cfg["pm2_name"] + project_path = cfg["project_path"] + node_path = cfg.get("node_path", "/www/server/nodejs/v22.14.0/bin") + port = cfg["port"] + + if not get_node_project_status(panel_url, api_key, pm2_name): + add_or_update_node_project(panel_url, api_key, pm2_name, project_path, port, node_path) + stop_node_project(panel_url, api_key, pm2_name) + time.sleep(2) + ok = restart_node_project(panel_url, api_key, pm2_name) + if not ok: + ok = start_node_project(panel_url, api_key, pm2_name) + if not ok: + print(" 请到宝塔 Node 项目手动重启 %s,路径: %s" % (pm2_name, project_path)) + return ok + + +# ==================== 打包(devlop 模式:zip) ==================== + +ZIP_EXCLUDE_DIRS = {".cache", "__pycache__", ".git", "node_modules", "cache", "test", "tests", "coverage", ".nyc_output", ".turbo", "开发文档"} +ZIP_EXCLUDE_FILE_NAMES = {".DS_Store", "Thumbs.db"} +ZIP_EXCLUDE_FILE_SUFFIXES = (".log", ".map") + + +def _should_exclude_from_zip(arcname, is_file=True): + parts = arcname.replace("\\", "/").split("/") + for part in parts: + if part in ZIP_EXCLUDE_DIRS: + return True + if is_file and parts: + name = parts[-1] + if name in ZIP_EXCLUDE_FILE_NAMES or any(name.endswith(s) for s in ZIP_EXCLUDE_FILE_SUFFIXES): + return True + return False + + +def pack_standalone_zip(root): + """打包 standalone 为 zip(devlop 模式用)""" + print("[2/7] 打包 standalone 为 zip ...") + standalone = os.path.join(root, ".next", "standalone") + static_src = os.path.join(root, ".next", "static") + public_src = os.path.join(root, "public") + ecosystem_src = os.path.join(root, "ecosystem.config.cjs") + + if not os.path.isdir(standalone) or not os.path.isdir(static_src): + print(" [失败] 未找到 .next/standalone 或 .next/static") + return None + chunks_dir = os.path.join(static_src, "chunks") + if not os.path.isdir(chunks_dir): + print(" [失败] .next/static/chunks 不存在,请先完整执行 pnpm build(本地 pnpm start 能正常打开页面后再部署)") + return None + + staging = tempfile.mkdtemp(prefix="soul_devlop_") + try: + for name in os.listdir(standalone): + _copy_with_dereference(os.path.join(standalone, name), os.path.join(staging, name)) + node_modules_dst = os.path.join(staging, "node_modules") + pnpm_dir = os.path.join(node_modules_dst, ".pnpm") + if os.path.isdir(pnpm_dir): + for dep in ["styled-jsx"]: + dep_in_root = os.path.join(node_modules_dst, dep) + if not os.path.exists(dep_in_root): + for pnpm_pkg in os.listdir(pnpm_dir): + if pnpm_pkg.startswith(dep + "@"): + src_dep = os.path.join(pnpm_dir, pnpm_pkg, "node_modules", dep) + if os.path.isdir(src_dep): + shutil.copytree(src_dep, dep_in_root, symlinks=False, dirs_exist_ok=True) + break + os.makedirs(os.path.join(staging, ".next"), exist_ok=True) + shutil.copytree(static_src, os.path.join(staging, ".next", "static"), dirs_exist_ok=True) + # 同步构建索引(与 start-standalone.js 一致),避免宝塔上 server 用错导致页面空白/404 + next_root = os.path.join(root, ".next") + next_staging = os.path.join(staging, ".next") + index_files = [ + "BUILD_ID", + "build-manifest.json", + "app-path-routes-manifest.json", + "routes-manifest.json", + "prerender-manifest.json", + "required-server-files.json", + "fallback-build-manifest.json", + ] + for name in index_files: + src = os.path.join(next_root, name) + if os.path.isfile(src): + shutil.copy2(src, os.path.join(next_staging, name)) + print(" [已同步] 构建索引: BUILD_ID, build-manifest, routes-manifest 等") + if os.path.isdir(public_src): + shutil.copytree(public_src, os.path.join(staging, "public"), dirs_exist_ok=True) + if os.path.isfile(ecosystem_src): + shutil.copy2(ecosystem_src, os.path.join(staging, "ecosystem.config.cjs")) + pkg_json = os.path.join(staging, "package.json") + if os.path.isfile(pkg_json): + try: + with open(pkg_json, "r", encoding="utf-8") as f: + data = json.load(f) + data.setdefault("scripts", {})["start"] = "node server.js" + with open(pkg_json, "w", encoding="utf-8") as f: + json.dump(data, f, indent=2, ensure_ascii=False) + except Exception: + pass + server_js = os.path.join(staging, "server.js") + if os.path.isfile(server_js): + try: + deploy_port = int(os.environ.get("DEPLOY_PORT", str(DEFAULT_DEPLOY_PORT))) + with open(server_js, "r", encoding="utf-8") as f: + c = f.read() + if "|| 3000" in c: + with open(server_js, "w", encoding="utf-8") as f: + f.write(c.replace("|| 3000", "|| %d" % deploy_port)) + except Exception: + pass + zip_path = os.path.join(tempfile.gettempdir(), "soul_devlop.zip") + with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf: + for name in os.listdir(staging): + path = os.path.join(staging, name) + if os.path.isfile(path): + if not _should_exclude_from_zip(name): + zf.write(path, name) + else: + for dirpath, dirs, filenames in os.walk(path): + dirs[:] = [d for d in dirs if not _should_exclude_from_zip(os.path.join(name, os.path.relpath(os.path.join(dirpath, d), path)), is_file=False)] + for f in filenames: + full = os.path.join(dirpath, f) + arcname = os.path.join(name, os.path.relpath(full, path)) + if not _should_exclude_from_zip(arcname): + zf.write(full, arcname) + print(" [成功] 打包完成: %s (%.2f MB)" % (zip_path, os.path.getsize(zip_path) / 1024 / 1024)) + return zip_path + except Exception as e: + print(" [失败] 打包异常:", str(e)) + return None + finally: + shutil.rmtree(staging, ignore_errors=True) + + +def upload_zip_and_extract_to_dist2(cfg, zip_path): + """上传 zip 并解压到 dist2(devlop 模式)""" + print("[3/7] SSH 上传 zip 并解压到 dist2 ...") + sys.stdout.flush() + if not cfg.get("password") and not cfg.get("ssh_key"): + print(" [失败] 请设置 DEPLOY_PASSWORD 或 DEPLOY_SSH_KEY") + return False + zip_size_mb = os.path.getsize(zip_path) / (1024 * 1024) + client = paramiko.SSHClient() + client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + try: + print(" 正在连接 %s@%s:%s ..." % (cfg["user"], cfg["host"], DEFAULT_SSH_PORT)) + sys.stdout.flush() + if cfg.get("ssh_key") and os.path.isfile(cfg["ssh_key"]): + client.connect(cfg["host"], port=DEFAULT_SSH_PORT, username=cfg["user"], key_filename=cfg["ssh_key"], timeout=30, banner_timeout=30) + else: + client.connect(cfg["host"], port=DEFAULT_SSH_PORT, username=cfg["user"], password=cfg["password"], timeout=30, banner_timeout=30) + print(" [OK] SSH 已连接,正在上传 zip(%.1f MB)..." % zip_size_mb) + sys.stdout.flush() + remote_zip = cfg["base_path"].rstrip("/") + "/soul_devlop.zip" + sftp = client.open_sftp() + # 上传进度:每 5MB 打印一次 + chunk_mb = 5.0 + last_reported = [0] + + def _progress(transferred, total): + if total and total > 0: + now_mb = transferred / (1024 * 1024) + if now_mb - last_reported[0] >= chunk_mb or transferred >= total: + last_reported[0] = now_mb + print("\r 上传进度: %.1f / %.1f MB" % (now_mb, total / (1024 * 1024)), end="") + sys.stdout.flush() + + sftp.put(zip_path, remote_zip, callback=_progress) + if zip_size_mb >= chunk_mb: + print("") + print(" [OK] zip 已上传,正在服务器解压(约 1–3 分钟)...") + sys.stdout.flush() + sftp.close() + dist2 = cfg["dist2_path"] + cmd = "rm -rf %s && mkdir -p %s && unzip -o -q %s -d %s && rm -f %s && echo OK" % (dist2, dist2, remote_zip, dist2, remote_zip) + stdin, stdout, stderr = client.exec_command(cmd, timeout=300) + out = stdout.read().decode("utf-8", errors="replace").strip() + err = stderr.read().decode("utf-8", errors="replace").strip() + if err: + print(" 服务器 stderr: %s" % err[:500]) + exit_status = stdout.channel.recv_exit_status() + if exit_status != 0 or "OK" not in out: + print(" [失败] 解压失败,退出码: %s" % exit_status) + if out: + print(" stdout: %s" % out[:300]) + return False + print(" [成功] 已解压到: %s" % dist2) + return True + except Exception as e: + print(" [失败] SSH 错误: %s" % str(e)) + import traceback + traceback.print_exc() + return False + finally: + client.close() + + +def run_pnpm_install_in_dist2(cfg): + """服务器 dist2 内执行 pnpm install,阻塞等待完成后再返回(改目录前必须完成)""" + print("[4/7] 服务器 dist2 内执行 pnpm install(等待完成后再切换目录)...") + sys.stdout.flush() + client = paramiko.SSHClient() + client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + try: + if cfg.get("ssh_key") and os.path.isfile(cfg["ssh_key"]): + client.connect(cfg["host"], port=DEFAULT_SSH_PORT, username=cfg["user"], key_filename=cfg["ssh_key"], timeout=15) + else: + client.connect(cfg["host"], port=DEFAULT_SSH_PORT, username=cfg["user"], password=cfg["password"], timeout=15) + stdin, stdout, stderr = client.exec_command("bash -lc 'which pnpm'", timeout=10) + pnpm_path = stdout.read().decode("utf-8", errors="replace").strip() + if not pnpm_path: + return False, "未找到 pnpm,请服务器安装: npm install -g pnpm" + cmd = "bash -lc 'cd %s && %s install'" % (cfg["dist2_path"], pnpm_path) + stdin, stdout, stderr = client.exec_command(cmd, timeout=300) + out = stdout.read().decode("utf-8", errors="replace").strip() + err = stderr.read().decode("utf-8", errors="replace").strip() + if stdout.channel.recv_exit_status() != 0: + return False, "pnpm install 失败\n" + (err or out) + print(" [成功] dist2 内 pnpm install 已执行完成,可安全切换目录") + return True, None + except Exception as e: + return False, str(e) + finally: + client.close() + + +def remote_swap_dist_and_restart(cfg): + """暂停 → dist→dist1, dist2→dist → 删除 dist1 → 更新 PM2 项目路径 → 重启(devlop 模式)""" + print("[5/7] 宝塔 API 暂停 Node 项目 ...") + stop_node_project(cfg["panel_url"], cfg["api_key"], cfg["pm2_name"]) + time.sleep(2) + print("[6/7] 服务器切换目录: dist→dist1, dist2→dist ...") + client = paramiko.SSHClient() + client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + try: + if cfg.get("ssh_key") and os.path.isfile(cfg["ssh_key"]): + client.connect(cfg["host"], port=DEFAULT_SSH_PORT, username=cfg["user"], key_filename=cfg["ssh_key"], timeout=15) + else: + client.connect(cfg["host"], port=DEFAULT_SSH_PORT, username=cfg["user"], password=cfg["password"], timeout=15) + cmd = "cd %s && mv dist dist1 2>/dev/null; mv dist2 dist && rm -rf dist1 && echo OK" % cfg["base_path"] + stdin, stdout, stderr = client.exec_command(cmd, timeout=60) + out = stdout.read().decode("utf-8", errors="replace").strip() + if stdout.channel.recv_exit_status() != 0 or "OK" not in out: + print(" [失败] 切换失败") + return False + print(" [成功] 新版本位于 %s" % cfg["dist_path"]) + finally: + client.close() + # 关键:devlop 实际运行目录是 dist_path,必须让宝塔 PM2 从该目录启动,否则会从错误目录跑导致静态资源 404 + print("[7/7] 更新宝塔 Node 项目路径并重启 ...") + add_or_update_node_project( + cfg["panel_url"], cfg["api_key"], cfg["pm2_name"], + cfg["dist_path"], # 使用 dist_path,不是 project_path + port=cfg["port"], + node_path=cfg.get("node_path"), + ) + if not start_node_project(cfg["panel_url"], cfg["api_key"], cfg["pm2_name"]): + print(" [警告] 请到宝塔手动启动 %s,并确认项目路径为: %s" % (cfg["pm2_name"], cfg["dist_path"])) + return False + return True + + +# ==================== 主函数 ==================== + +def main(): + parser = argparse.ArgumentParser(description="Soul 创业派对 - 统一部署脚本", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=__doc__) + parser.add_argument("--mode", choices=["devlop", "deploy"], default="devlop", help="devlop=dist切换(默认), deploy=直接覆盖") + parser.add_argument("--no-build", action="store_true", help="跳过本地构建") + parser.add_argument("--no-upload", action="store_true", help="仅 deploy 模式:跳过 SSH 上传") + parser.add_argument("--no-api", action="store_true", help="仅 deploy 模式:上传后不调宝塔 API") + args = parser.parse_args() + + script_dir = os.path.dirname(os.path.abspath(__file__)) + # 支持 devlop.py 在项目根或 scripts/ 下:以含 package.json 的目录为 root + if os.path.isfile(os.path.join(script_dir, "package.json")): + root = script_dir + else: + root = os.path.dirname(script_dir) + + if args.mode == "devlop": + cfg = get_cfg_devlop() + print("=" * 60) + print(" Soul 自动部署(dist 切换)") + print("=" * 60) + print(" 服务器: %s@%s 目录: %s Node: %s" % (cfg["user"], cfg["host"], cfg["base_path"], cfg["pm2_name"])) + print("=" * 60) + if not args.no_build: + print("[1/7] 本地构建 pnpm build ...") + if sys.platform == "win32" and not clean_standalone_before_build(root): + return 1 + if not run_build(root): + return 1 + elif not os.path.isfile(os.path.join(root, ".next", "standalone", "server.js")): + print("[错误] 未找到 .next/standalone/server.js") + return 1 + else: + print("[1/7] 跳过本地构建") + zip_path = pack_standalone_zip(root) + if not zip_path: + return 1 + if not upload_zip_and_extract_to_dist2(cfg, zip_path): + return 1 + try: + os.remove(zip_path) + except Exception: + pass + # 必须在 dist2 内 pnpm install 执行完成后再切换目录 + ok, err = run_pnpm_install_in_dist2(cfg) + if not ok: + print(" [失败] %s" % (err or "pnpm install 失败")) + return 1 + # install 已完成,再执行 dist→dist1、dist2→dist 切换 + if not remote_swap_dist_and_restart(cfg): + return 1 + print("") + print(" 部署完成!运行目录: %s" % cfg["dist_path"]) + return 0 + + # deploy 模式 + cfg = get_cfg() + print("=" * 60) + print(" Soul 一键部署(直接覆盖)") + print("=" * 60) + print(" 服务器: %s@%s 项目路径: %s PM2: %s" % (cfg["user"], cfg["host"], cfg["project_path"], cfg["pm2_name"])) + print("=" * 60) + if not args.no_upload: + check_node_environments(cfg) + if not args.no_build: + print("[1/4] 本地构建 ...") + if not run_build(root): + return 1 + elif not os.path.isfile(os.path.join(root, ".next", "standalone", "server.js")): + print("[错误] 未找到 .next/standalone/server.js") + return 1 + else: + print("[1/4] 跳过本地构建") + tarball = pack_standalone_tar(root) + if not tarball: + return 1 + if not args.no_upload: + if not upload_and_extract(cfg, tarball): + return 1 + try: + os.remove(tarball) + except Exception: + pass + else: + print(" 压缩包: %s" % tarball) + if not args.no_api and not args.no_upload: + deploy_via_baota_api(cfg) + print("") + print(" 部署完成!站点: %s" % cfg["site_url"]) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/next-project/开发文档/0、Mycontent-book 项目总览.md b/next-project/开发文档/0、Mycontent-book 项目总览.md deleted file mode 100644 index c534fee4..00000000 --- a/next-project/开发文档/0、Mycontent-book 项目总览.md +++ /dev/null @@ -1,89 +0,0 @@ -# Mycontent-book 项目总览 - -**我是卡若。** - -做这个项目,逻辑很简单:**把书卖出去,把私域做起来,把钱分下去。** - -这就不是一个普通的博客网站,这是一个**内容变现系统**。所有的技术架构,都要围绕着“阅读体验”、“流量承接”和“变现转化”来做。 - -别整那些虚头巴脑的概念,咱们直接看这个盘子怎么搭。 - -## 1. 核心逻辑 - -这个项目的生意逻辑就是三层: -1. **流量层(前端)**:让用户看着爽,像刷抖音、看公众号一样流畅。必须移动端优先,模拟 iOS 的原生质感。 -2. **内容层(数据)**:`book/` 目录下的 Markdown 文件就是我们的资产。改个字,推送到 GitHub,网站立马更新。 -3. **变现层(后端/接口)**:谁看了?谁买了?谁推荐的?这些数据要跑通。 - -## 2. 为什么这么架构? - -我选 Next.js,不是因为流行,是因为它**省事**。 -- **SSR(服务端渲染)**:SEO 友好,百度谷歌能搜到,自带流量。 -- **API Routes**:不用单独起个 Java 或 Python 服务,省服务器钱。 -- **Vercel/宝塔部署**:自动化流水线,我只管写文章,代码自动跑。 - -## 3. 当前项目结构(Next 前端) - -- **app/view/**:移动端(C 端)页面。根路径 `/` 重定向到 `/view`;路由为 `/view`、`/view/chapters`、`/view/read/[id]`、`/view/match`、`/view/my`、`/view/about`、`/view/login` 等。 -- **app/admin/**:管理端页面,路由为 `/admin`、`/admin/*`。 -- **app/api/**:接口不变,仍为 `/api/*`。 -- **components/view/**:移动端布局与组件(如 layout-wrapper、bottom-nav、config);**components/admin/**:管理端用 UI(如 admin/ui)。 -- 通用 UI 在 view 与 admin 各保留一份(见 `components/README.md`)。 - -## 4. 目录导航(别迷路) - -- **[1、需求](1、需求/业务需求.md)**:我们要干啥,成本多少,技术要求;[TDD 需求方案](1、需求/TDD_创业派对项目方案_v1.0.md)。 -- **[2、架构](2、架构/系统架构.md)**:整体怎么搭,前后端怎么分。 - - [技术选型与全景图](2、架构/技术选型与全景图.md)、[前后端架构分离策略](2、架构/前后端架构分离策略.md) -- **[3、原型](3、原型/原型设计规范.md)**:原型设计规范。 -- **[4、前端](4、前端/前端架构.md)**:前端架构、模块详解、开发规范;[当前小程序开发细则](4、前端/当前小程序开发细则.md);[ui 子目录](4、前端/ui/):项目概述、页面功能、组件清单、API/状态/分销/支付/管理后台/部署说明等。 -- **[5、接口](5、接口/API接口.md)**:前后端怎么说话。 -- **[6、后端](6、后端/后端架构.md)**:数据处理,后端开发规范。 -- **[7、数据库](7、数据库/数据库设计.md)**:数据存哪,怎么存。 -- **[8、部署](8、部署/部署总览.md)**:怎么上线、本地运行、宝塔部署、新分销部署、修复与优化记录等。 -- **[9、手册](9、手册/写作与结构维护手册.md)**:怎么写书,怎么维护。 -- **[10、项目管理](10、项目管理/项目落地推进表.md)**:项目推进与提示词。 - -## 5. 开发约束(重要) - -> **2026-02-04 起生效** - -### 5.1 前端开发策略 - -| 端 | 路径 | 开发状态 | 说明 | -|---|------|---------|-----| -| **微信小程序** | `miniprogram/` | ✅ 活跃开发 | 所有 C 端新功能在此开发 | -| **Next.js C端** | `app/view/` | 🔒 冻结维护 | 不再新增功能,仅修复严重 Bug | -| **Next.js 管理端** | `app/admin/` | ✅ 活跃开发 | 管理后台继续在此开发 | -| **API 接口** | `app/api/` | ✅ 活跃开发 | 小程序和管理端共用 | - -### 5.2 核心原则 - -1. **小程序优先**:所有面向用户的新功能,只在小程序端开发 -2. **Next.js C端冻结**:`app/view/` 目录不再新增功能,保持现状作为 Web 备用入口 -3. **管理端继续**:`app/admin/` 管理后台功能继续在 Next.js 开发 -4. **API 统一**:接口层保持统一,小程序和 Web 端共用同一套 API - -### 5.3 登录体系差异 - -| 端 | 登录方式 | 说明 | -|---|---------|-----| -| 小程序 | 微信一键登录 / 手机号快速授权 | 保持原生体验,不复刻 Next.js 登录页 | -| Next.js | 手机号 + 密码 | 保持现状 | -| 账号统一 | 以手机号为唯一标识 | 两端数据互通 | - -### 5.4 为什么这样做? - -- **用户体量**:90%+ 用户来自小程序,优先保障主要渠道体验 -- **开发效率**:集中精力做好一端,避免两端同步维护的成本 -- **原生体验**:小程序有更好的分享、支付、订阅消息等微信生态能力 - -## 6. 这里的规矩 - -- **行动至上**:文档是用来指导干活的,不是写来看的。 -- **数据说话**:所有优化要有数据支撑,加载快了多少?转化高了多少? -- **保持简单**:能用现成的库就别自己造轮子。 - ---- -**复盘:** -目前项目处于“文件数据库”阶段,适合我这种单人高频写作。等流量上来了,用户系统一接,立马切 MongoDB。这一步步来,别贪多。 diff --git a/next-project/开发文档/2、架构/前后端架构分离策略.md b/next-project/开发文档/2、架构/前后端架构分离策略.md deleted file mode 100644 index 3e762f99..00000000 --- a/next-project/开发文档/2、架构/前后端架构分离策略.md +++ /dev/null @@ -1,70 +0,0 @@ -# 前后端分离开发架构图 - -**我是卡若。** - -为了让你(开发人员)能闭着眼睛把活干了,我把前后端分离的流程画得清清楚楚。 - -我们采用**接口契约驱动开发 (Contract-First Development)**。 - -## 1. 分离开发流程 - -\`\`\`mermaid -sequenceDiagram - participant PM as 卡若 (PM) - participant Doc as API 文档 - participant FE as 前端开发 - participant BE as 后端开发 - - PM->>Doc: 1. 定义需求与接口 (API接口.md) - Note over Doc: 确定 URL, Params, Response - - par 并行开发 - FE->>Doc: 2. 查阅接口定义 - FE->>FE: 3. Mock 数据 (假数据) - FE->>FE: 4. 开发 UI 与交互 - - BE->>Doc: 2. 查阅接口定义 - BE->>BE: 3. 实现业务逻辑 - BE->>BE: 4. 单元测试 (Postman) - end - - FE->>BE: 5. 联调 (对接真实接口) - BE-->>FE: 返回真实数据 - - FE->>PM: 6. 验收与上线 -\`\`\` - -## 2. 架构交互图 (Data Flow) - -\`\`\`mermaid -graph LR - subgraph "前端域 (Browser)" - UI[页面组件] --> API_Client[API 请求封装层] - API_Client -- JSON 请求 --> API_Route - end - - subgraph "后端域 (Server/Next.js)" - API_Route[API 路由入口] --> Controller[业务控制器] - Controller --> Service[逻辑服务层] - - Service --> Content[内容解析器] - Service --> Config[配置加载器] - - Content -- 读取 --> FS[文件系统 book/] - Config -- 读取 --> DB[(MongoDB/JSON)] - end - - Service -- JSON 响应 --> Controller - Controller -- HTTP 响应 --> API_Client - API_Client -- 数据 --> UI -\`\`\` - -## 3. 落地执行规范 - -1. **接口先行**: 没定义好接口文档,不许写代码。 -2. **Mock 优先**: 前端别等后端,自己造个 JSON 数据先跑起来。 -3. **统一封装**: 前端所有请求必须走 `lib/api.ts`,禁止在组件里直接写 `fetch('/api/...')`。 - ---- -**卡若说:** -按这个流程走,前后端吵架的概率降低 90%。效率就是这么抠出来的。 diff --git a/next-project/开发文档/2、架构/系统架构.md b/next-project/开发文档/2、架构/系统架构.md deleted file mode 100644 index 46da2830..00000000 --- a/next-project/开发文档/2、架构/系统架构.md +++ /dev/null @@ -1,61 +0,0 @@ -# 系统架构 - -**我是卡若。** - -架构不是为了画图好看,是为了**省事**和**赚钱**。 - -我们的架构设计,核心围绕两个字:**分离**。 -- 内容和代码分离。 -- 前端和后端分离。 -- 静态和动态分离。 - -## 1. 架构全景图 - -\`\`\`mermaid -graph TD - subgraph "内容生产 (Content)" - Typora[本地写作] --> Git[Git 仓库] - Git --> AutoSync[自动同步脚本] - end - - subgraph "应用层 (Next.js)" - AutoSync --> FileSys[文件系统 book/] - FileSys --> Backend[后端 API] - Backend --> Frontend[前端 UI] - end - - subgraph "用户触达 (User)" - Frontend --> Wechat[微信环境] - Frontend --> Browser[手机浏览器] - end -\`\`\` - -## 2. 核心设计理念 - -### 2.1 “内容即产品” -我们的核心资产不是代码,是 `book/` 目录下的文章。 -- 代码丢了可以重写,文章丢了就完了。 -- 所以,文章用 Markdown 存,Git 管,最安全。 - -### 2.2 前后端分离 (即使在 Next.js 里) -为了以后能扩展(比如招人开发,或者把后端换成 Java),我们在代码逻辑上做了强制隔离。 -- 详见:**[前后端架构分离策略](file:///Users/karuo/Documents/个人/2、我写的书/《一场soul的创业实验》/开发文档/2、架构/前后端架构分离策略.md)** - -### 2.3 极简部署 -- 不要 Docker(除非必要)。 -- 不要 K8s。 -- 既然是 Node.js 项目,PM2 或者 Vercel 就够了。宝塔面板配个 Webhook 自动拉代码,是最适合个人开发者的方案。 - -## 3. 关键约束 - -1. **本地优先**: 写作在本地,代码开发也在本地。 -2. **稀疏检出 (Sparse Checkout)**: 如果你只负责写作,就别拉取代码;如果你负责开发,就拉取全部。 -3. **单向流动**: 数据流向是 `Markdown -> API -> UI`。除非是评论或订单,否则 UI 不反向修改 Markdown。 - -## 4. 详细技术栈 - -详见:**[技术选型与全景图](file:///Users/karuo/Documents/个人/2、我写的书/《一场soul的创业实验》/开发文档/2、架构/技术选型与全景图.md)** - ---- -**总结:** -保持架构的简单性,就是保持业务的灵活性。能在文件里解决的,就别上数据库;能在 Next.js 里解决的,就别拆微服务。 diff --git a/next-project/开发文档/4、前端/前端开发规范.md b/next-project/开发文档/4、前端/前端开发规范.md deleted file mode 100644 index 4f155cb0..00000000 --- a/next-project/开发文档/4、前端/前端开发规范.md +++ /dev/null @@ -1,56 +0,0 @@ -# 前端开发规范 (Frontend Specs) - 智能自生长文档 - -> **提示词功能 (Prompt Function)**: 将本文件拖入 AI 对话框,即可激活“前端技术专家”角色,生成符合 iOS 风格的 React 代码。 - -## 1. 基础上下文 (The Two Basic Files) -### 1.1 角色档案:卡若 (Karuo) -- **视觉标准**:像素级复刻 iOS (San Francisco, 1:1 间距, 弥散阴影)。 -- **体验标准**:无白屏 (Skeleton),丝滑转场 (Transition)。 - -### 1.2 技术栈 -- **核心**:React + Shadcn UI + Tailwind CSS。 -- **辅助**:Vant UI (移动端组件)。 -- **构建**:Vite / Next.js。 - -## 2. 开发规范核心 (Master Content) -### 2.1 视觉与风格 (iOS) -- **字体**:San Francisco > PingFang SC。 -- **色彩**: - - 背景:`#F2F2F7` (Grouped Background)。 - - 分割:`#C6C6C8`。 - - 交互:`#007AFF` (System Blue)。 -- **细节**: - - 圆角:统一 `rounded-lg` 或 `rounded-xl`。 - - 阴影:柔和弥散,非生硬投影。 - -### 2.2 交互与性能 (Mandatory) -- **骨架屏**:数据加载必须显示 Skeleton,严禁 Spinner。 -- **转场**:路由切换必须有动画。 -- **图片**:懒加载 + 失败占位。 - -### 2.3 目录结构 -- `/src/components`: 原子组件。 -- `/scenarios/new`: 场景获客页。 -- `/src/hooks`: 逻辑复用。 - -## 3. AI 协作指令 (Expanded Function) -**角色**:你是我(卡若)的前端主程。 -**任务**: -1. **代码生成**:生成 React 组件代码,**必须**包含 Tailwind 类名。 -2. **样式检查**:确保所有 UI 元素符合 iOS 规范(检查圆角、阴影、字体)。 -3. **结构分析**:用 Mermaid 展示组件依赖。 - -### 示例 Mermaid (组件结构) -\`\`\`mermaid -classDiagram - Page <|-- Header - Page <|-- Content - Page <|-- Footer - Content <|-- SkeletonLoader - Content <|-- DataList - DataList <|-- ListItem - class Page{ - +state: loading - +effect: fetchData() - } -\`\`\` diff --git a/next-project/开发文档/4、前端/前端架构.md b/next-project/开发文档/4、前端/前端架构.md deleted file mode 100644 index 2d2779b7..00000000 --- a/next-project/开发文档/4、前端/前端架构.md +++ /dev/null @@ -1,94 +0,0 @@ -# 前端架构 - -**我是卡若。** - -前端就是项目的脸。用户不管是通过朋友圈、抖音还是私域进来,第一眼看到的就是这个页面。如果加载慢、长得丑、滑动卡,人家转头就走,我的流量就浪费了。 - -所以,前端的核心目标只有一个:**极致的移动端阅读体验,像原生 App 一样丝滑。** - -## 1. 技术底座 - -别跟我说什么技术先进,我要的是**稳**和**快**。 - -- **框架**: Next.js 14 (App Router) - 必须用最新的 App Router,路由管理更清晰。 -- **语言**: TypeScript - 必须用 TS,类型安全,少出低级 Bug。 -- **样式**: Tailwind CSS - 写样式最快,没有之一。配合 `globals.css` 做全局控制。 -- **UI 组件库**: Shadcn UI (基于 Radix UI) + Vant UI (风格参考)。 - - *注意*:我们要像素级复刻 iOS 风格,字体用 San Francisco,圆角、阴影都要对齐。 - -## 2. 目录结构(我的地盘) - -前端代码主要集中在 `app/` 和 `components/`。 - -\`\`\` -app/ -├── (routes)/ # 路由组,逻辑隔离 -│ ├── page.tsx # 首页:封面、简介、购买按钮 -│ ├── chapters/ # 目录页:章节列表 -│ ├── read/[id]/ # 阅读页:核心体验区 -│ ├── my/ # 个人中心:购买记录、分销 -│ ├── admin/ # 管理后台:给自己用的 -│ └── documentation/ # 文档生成:内部工具 -├── layout.tsx # 全局布局:导航栏、SEO Meta -├── globals.css # 全局样式 -└── error.tsx # 错误处理页面 - -components/ -├── ui/ # 通用组件 (Button, Input, Skeleton) -├── modules/ # 业务模块组件 (新增) -│ ├── auth/ # 认证模块 (AuthModal) -│ ├── payment/ # 支付模块 (PaymentModal) -│ ├── marketing/ # 营销模块 (QRCodeModal) -│ └── referral/ # 分销模块 (ReferralShare) -├── book-cover.tsx # 书籍封面展示 -├── chapter-content.tsx # 章节内容渲染器 -├── bottom-nav.tsx # 底部导航栏 (手机端核心) -└── theme-provider.tsx # 主题管理 (深色/浅色模式) -\`\`\` - -## 2.1 业务模块化 (Modularization) - -为了支持“云阿米巴”模式的快速迭代,我们将核心业务逻辑封装为独立模块: - -- **支付模块 (Payment)**: 统一管理微信、支付宝、USDT 等支付方式,支持整书/单章购买。 -- **营销模块 (Marketing)**: 负责引流(如二维码弹窗、倒计时Banner),连接私域流量池。 -- **分销模块 (Referral)**: 负责裂变传播(如分享按钮、返利计算),让用户帮我们卖书。 -- **认证模块 (Auth)**: 统一的用户登录与权限校验。 - -这种设计允许我们在不修改页面核心逻辑的情况下,插拔不同的变现策略。 - -## 3. 核心交互设计 - -### 3.1 骨架屏 (Skeleton) -**规则**:凡是需要加载数据的地方,必须先展示骨架屏。 -- 用户不能看白屏,哪怕等 0.5 秒,也要让他看到“东西正在来”的样子。 -- 强制引入 `Skeleton` 组件。 - -### 3.2 路由动画 (Transition) -**规则**:页面切换不能生硬地跳。 -- 使用 Framer Motion 或 CSS Transition。 -- 模拟 iOS 的滑动切换或淡入淡出。 - -### 3.3 阅读体验 -- **字体**:针对不同设备优化,保证字号适中,行间距舒服(建议 1.6-1.8)。 -- **图片**:懒加载 (Lazy Load),点击可放大预览。 -- **代码块**:虽然是书,但如果有代码,要有高亮和复制按钮。 - -## 4. 数据获取 (Fetching) - -- **服务端组件 (Server Components)**: - - `page.tsx`, `read/[id]/page.tsx` 默认都是服务端组件。 - - 直接在组件内 `await` 获取数据(通过 `lib/book-data.ts`),SEO 极佳。 -- **客户端组件 (Client Components)**: - - 需要交互的(点击、弹窗、状态变化),头部加 `'use client'`。 - - 比如 `auth-modal.tsx`, `purchase-section.tsx`。 - -## 5. 待办事项 (Todo) - -- [ ] 全局引入 Skeleton,替换掉所有的 `Loading...` 文字。 -- [ ] 检查所有页面的 Mobile 适配,在 Chrome 开发者工具里用 iPhone SE 和 iPhone 14 Pro Max 两个尺寸测。 -- [ ] 优化字体栈,确保在安卓上也不难看。 - ---- -**总结**: -前端不仅是写代码,是**做产品**。每一个像素的偏移都影响用户的信任感。把细节抠好,转化率自然就高了。 diff --git a/next-project/开发文档/6、后端/后端开发规范.md b/next-project/开发文档/6、后端/后端开发规范.md deleted file mode 100644 index 75b360a1..00000000 --- a/next-project/开发文档/6、后端/后端开发规范.md +++ /dev/null @@ -1,64 +0,0 @@ -# 后端开发规范 (Backend Specs) - 智能自生长文档 - -> **提示词功能 (Prompt Function)**: 将本文件拖入 AI 对话框,即可激活“Python 后端专家”角色,生成高效、规范的 FastAPI 代码。 - -## 1. 基础上下文 (The Two Basic Files) -### 1.1 角色档案:卡若 (Karuo) -- **核心**:开发快、性能好、支持 AI。 -- **习惯**:优先使用异步 (`async/await`),强制类型提示 (`Type Hints`)。 - -### 1.2 技术栈 -- **语言**:Python 3.10+。 -- **框架**:FastAPI (Web), Pydantic (Validation), LangChain (AI)。 -- **数据**:Motor (Async Mongo), Redis。 - -## 2. 开发规范核心 (Master Content) -### 2.1 代码规范 -- **风格**:遵循 PEP 8,使用 Black 格式化。 -- **类型**:**强制 Type Hints** (如 `def get_user(id: int) -> User:`)。 -- **注释**:**强制中文注释**,解释“业务逻辑”与“AI 处理流程”。 -- **结构**: - - `app/routers`: 路由 - - `app/models`: Pydantic 模型 - - `app/services`: 业务逻辑 - - `app/core`: 配置与工具 - -### 2.2 AI 与安全规范 -- **AI 调用**:所有 LLM 调用必须封装在 Service 层,并包含重试机制与超时控制。 -- **安全**: - - **命令执行**:严禁使用 `os.system`,必须使用 `subprocess` 并校验参数。 - - **SQL/NoSQL**:使用 ORM 或参数化查询,防止注入。 - -### 2.3 异常与日志 -- **异常**:使用 FastAPI `HTTPException` 或自定义 Exception Handler。 -- **日志**:使用 `loguru` 或 Python 标准 `logging`,必须记录 Traceback。 - -### 2.4 依赖管理 -- **工具**:`pip` 或 `poetry`。 -- **原则**:提交代码前更新 `requirements.txt` 或 `pyproject.toml`。 - -## 3. AI 协作指令 (Expanded Function) -**角色**:你是我(卡若)的 Python 架构师。 -**任务**: -1. **代码实现**:生成 FastAPI 的 Router/Model/Service 代码。 -2. **AI 集成**:编写 LangChain 调用逻辑或向量检索代码。 -3. **逻辑图解**:用 Mermaid 展示异步处理流程。 - -### 示例 Mermaid (类图) -\`\`\`mermaid -classDiagram - class UserRouter { - +get_user() - +create_user() - } - class UserService { - +verify_token() - +process_ai_request() - } - class VectorStore { - +search_similarity() - +add_documents() - } - UserRouter --> UserService - UserService --> VectorStore -\`\`\` diff --git a/next-project/开发文档/6、后端/后端架构.md b/next-project/开发文档/6、后端/后端架构.md deleted file mode 100644 index 16c5f9dd..00000000 --- a/next-project/开发文档/6、后端/后端架构.md +++ /dev/null @@ -1,68 +0,0 @@ -# 后端架构与业务逻辑 - -**我是卡若。** - -后端不仅仅是读写数据库,它是**业务逻辑的翻译官**。 - -我们要把“私域引流”、“内容分发”这些生意话术,翻译成代码逻辑。 - -## 1. 核心业务模块 - -### 1.1 内容服务 (Content Service) -这是最基础的。 -- **逻辑**: - - 扫描 `book/` 目录,生成目录树 (Tree)。 - - 解析 Markdown,提取 Frontmatter (标题、日期、标签)。 - - **缓存策略**: 既然是读文件,IO 慢。要在内存里做一个 LRU 缓存,读取一次后由内存直接返回,直到文件发生变更。 - -### 1.2 配置服务 (Config Service) -我的微信号、群二维码、价格,这些东西会变,不能写死在代码里。 -- **实现**: - - 一个 `config/settings.json` 文件(或者未来的 MongoDB `settings` 表)。 - - 接口: `GET /api/config`。 - - 前端拿到配置,动态展示微信号。 - -### 1.3 引流服务 (Lead Service) -这是赚钱的关键。 -- **埋点逻辑**: - - 记录 `UserView` (用户看了哪章)。 - - 记录 `UserClick` (用户点了“加微信”)。 - - 虽然不存库,但可以先打到日志文件里,或者调一个飞书的 Webhook,实时通知我“有人对这章感兴趣”。 - -## 2. 接口设计原则 - -- **RESTful**: 资源导向。`GET /articles`, `GET /articles/:id`。 -- **统一响应体**: - \`\`\`typescript - interface ApiResponse { - code: number; // 0 成功, >0 错误 - data: T; - msg: string; - } - \`\`\` - -## 3. 目录结构 (后端专用) - -\`\`\` -app/api/ -├── content/ # 内容相关 -├── config/ # 全局配置 -└── track/ # 埋点上报 - -lib/ -├── content/ -│ ├── parser.ts # Markdown 解析器 -│ └── cache.ts # 内存缓存 -├── config/ -│ └── loader.ts # 配置加载器 -└── db/ # 数据库连接 (预留) -\`\`\` - -## 4. 扩展性预留 - -- **鉴权中间件**: 现在是裸奔,未来加 `middleware.ts` 拦截 `/admin` 开头的请求。 -- **任务队列**: 未来如果生成文档太慢,就扔到 Redis 队列里异步处理。 - ---- -**卡若说:** -后端代码要写得像瑞士军刀一样,功能明确,结实耐用。 diff --git a/soul-admin/.env.development b/soul-admin/.env.development index 5b4688fe..32cfa9c1 100644 --- a/soul-admin/.env.development +++ b/soul-admin/.env.development @@ -1,3 +1,5 @@ # 对接后端 base URL(不改 API 路径,仅改此处即可切换 Next → Gin) # VITE_API_BASE_URL=http://localhost:3006 -VITE_API_BASE_URL=http://localhost:8080 +# VITE_API_BASE_URL=http://localhost:8080 +VITE_API_BASE_URL=http://soulapi.quwanzhi.com + diff --git a/soul-admin/.gitignore b/soul-admin/.gitignore new file mode 100644 index 00000000..40b878db --- /dev/null +++ b/soul-admin/.gitignore @@ -0,0 +1 @@ +node_modules/ \ No newline at end of file diff --git a/soul-admin/src/main.tsx b/soul-admin/src/main.tsx index 8e0deb1c..4e55d7d1 100644 --- a/soul-admin/src/main.tsx +++ b/soul-admin/src/main.tsx @@ -6,7 +6,7 @@ import './index.css' createRoot(document.getElementById('root')!).render( - + , diff --git a/soul-api/devlop.py b/soul-api/devlop.py new file mode 100644 index 00000000..ddac3449 --- /dev/null +++ b/soul-api/devlop.py @@ -0,0 +1,238 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +soul-api Go 项目一键部署到宝塔 +- 本地交叉编译 Linux 二进制 +- 上传到 /www/wwwroot/自营/soul-api +- 重启服务(nohup 或跳过) +""" + +from __future__ import print_function + +import os +import sys +import tempfile +import argparse +import subprocess +import shutil +import tarfile + +try: + import paramiko +except ImportError: + print("错误: 请先安装 paramiko") + print(" pip install paramiko") + sys.exit(1) + +# ==================== 配置 ==================== + +DEPLOY_PROJECT_PATH = "/www/wwwroot/自营/soul-api" +DEFAULT_SSH_PORT = int(os.environ.get("DEPLOY_SSH_PORT", "22022")) + + +def get_cfg(): + return { + "host": os.environ.get("DEPLOY_HOST", "43.139.27.93"), + "user": os.environ.get("DEPLOY_USER", "root"), + "password": os.environ.get("DEPLOY_PASSWORD", "Zhiqun1984"), + "ssh_key": os.environ.get("DEPLOY_SSH_KEY", ""), + "project_path": os.environ.get("DEPLOY_PROJECT_PATH", DEPLOY_PROJECT_PATH), + } + + +# ==================== 本地构建 ==================== + + +def run_build(root): + """交叉编译 Go 二进制(Linux amd64)""" + print("[1/4] 本地交叉编译 Go 二进制 ...") + env = os.environ.copy() + env["GOOS"] = "linux" + env["GOARCH"] = "amd64" + env["CGO_ENABLED"] = "0" + try: + r = subprocess.run( + ["go", "build", "-o", "soul-api", "./cmd/server"], + cwd=root, + env=env, + shell=(sys.platform == "win32"), + timeout=120, + capture_output=True, + text=True, + encoding="utf-8", + errors="replace", + ) + if r.returncode != 0: + print(" [失败] go build 失败,退出码:", r.returncode) + if r.stderr: + for line in (r.stderr or "").strip().split("\n")[-10:]: + print(" " + line) + return None + out_path = os.path.join(root, "soul-api") + if not os.path.isfile(out_path): + print(" [失败] 未找到编译产物 soul-api") + return None + print(" [成功] 编译完成: %s (%.2f MB)" % (out_path, os.path.getsize(out_path) / 1024 / 1024)) + return out_path + except subprocess.TimeoutExpired: + print(" [失败] 编译超时") + return None + except FileNotFoundError: + print(" [失败] 未找到 go 命令,请安装 Go") + return None + except Exception as e: + print(" [失败] 编译异常:", str(e)) + return None + + +# ==================== 打包 ==================== + + +def pack_deploy(root, binary_path, include_env=True): + """打包二进制和 .env 为 tar.gz""" + print("[2/4] 打包部署文件 ...") + staging = tempfile.mkdtemp(prefix="soul_api_deploy_") + try: + shutil.copy2(binary_path, os.path.join(staging, "soul-api")) + env_src = os.path.join(root, ".env") + if include_env and os.path.isfile(env_src): + shutil.copy2(env_src, os.path.join(staging, ".env")) + print(" [已包含] .env") + else: + env_example = os.path.join(root, ".env.example") + if os.path.isfile(env_example): + shutil.copy2(env_example, os.path.join(staging, ".env")) + print(" [已包含] .env.example -> .env (请服务器上检查配置)") + tarball = os.path.join(tempfile.gettempdir(), "soul_api_deploy.tar.gz") + with tarfile.open(tarball, "w:gz") as tf: + for name in os.listdir(staging): + tf.add(os.path.join(staging, name), arcname=name) + print(" [成功] 打包完成: %s (%.2f MB)" % (tarball, os.path.getsize(tarball) / 1024 / 1024)) + return tarball + except Exception as e: + print(" [失败] 打包异常:", str(e)) + return None + finally: + shutil.rmtree(staging, ignore_errors=True) + + +# ==================== SSH 上传 ==================== + + +def upload_and_extract(cfg, tarball_path, no_restart=False): + """上传 tar.gz 到服务器并解压、重启""" + print("[3/4] SSH 上传并解压 ...") + if not cfg.get("password") and not cfg.get("ssh_key"): + print(" [失败] 请设置 DEPLOY_PASSWORD 或 DEPLOY_SSH_KEY") + return False + client = paramiko.SSHClient() + client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + try: + if cfg.get("ssh_key") and os.path.isfile(cfg["ssh_key"]): + client.connect( + cfg["host"], port=DEFAULT_SSH_PORT, + username=cfg["user"], key_filename=cfg["ssh_key"], + timeout=15, + ) + else: + client.connect( + cfg["host"], port=DEFAULT_SSH_PORT, + username=cfg["user"], password=cfg["password"], + timeout=15, + ) + sftp = client.open_sftp() + remote_tar = "/tmp/soul_api_deploy.tar.gz" + project_path = cfg["project_path"] + sftp.put(tarball_path, remote_tar) + sftp.close() + + cmd = ( + "mkdir -p %s && cd %s && tar -xzf %s && " + "chmod +x soul-api && rm -f %s && echo OK" + ) % (project_path, project_path, remote_tar, remote_tar) + stdin, stdout, stderr = client.exec_command(cmd, timeout=60) + out = stdout.read().decode("utf-8", errors="replace").strip() + exit_status = stdout.channel.recv_exit_status() + if exit_status != 0 or "OK" not in out: + print(" [失败] 解压失败,退出码:", exit_status) + return False + print(" [成功] 已解压到: %s" % project_path) + + if not no_restart: + print("[4/4] 重启 soul-api 服务 ...") + restart_cmd = ( + "cd %s && pkill -f 'soul-api' 2>/dev/null; sleep 2; " + "nohup ./soul-api >> soul-api.log 2>&1 & sleep 1; " + "pgrep -f soul-api >/dev/null && echo RESTART_OK || echo RESTART_FAIL" + ) % project_path + stdin, stdout, stderr = client.exec_command(restart_cmd, timeout=15) + out = stdout.read().decode("utf-8", errors="replace").strip() + if "RESTART_OK" in out: + print(" [成功] soul-api 已重启") + else: + print(" [警告] 重启状态未知,请手动检查: cd %s && ./soul-api" % project_path) + else: + print("[4/4] 跳过重启 (--no-restart)") + + return True + except Exception as e: + print(" [失败] SSH 错误:", str(e)) + return False + finally: + client.close() + + +# ==================== 主函数 ==================== + + +def main(): + parser = argparse.ArgumentParser( + description="soul-api Go 项目一键部署到宝塔", + formatter_class=argparse.RawDescriptionHelpFormatter, + ) + parser.add_argument("--no-build", action="store_true", help="跳过本地编译(使用已有 soul-api 二进制)") + parser.add_argument("--no-env", action="store_true", help="不打包 .env(保留服务器现有 .env)") + parser.add_argument("--no-restart", action="store_true", help="上传后不重启服务") + args = parser.parse_args() + + script_dir = os.path.dirname(os.path.abspath(__file__)) + root = script_dir + + cfg = get_cfg() + print("=" * 60) + print(" soul-api 一键部署到宝塔") + print("=" * 60) + print(" 服务器: %s@%s:%s" % (cfg["user"], cfg["host"], DEFAULT_SSH_PORT)) + print(" 目标目录: %s" % cfg["project_path"]) + print("=" * 60) + + binary_path = os.path.join(root, "soul-api") + if not args.no_build: + p = run_build(root) + if not p: + return 1 + else: + if not os.path.isfile(binary_path): + print("[错误] 未找到 soul-api 二进制,请先编译或去掉 --no-build") + return 1 + print("[1/4] 跳过编译,使用现有 soul-api") + + tarball = pack_deploy(root, binary_path, include_env=not args.no_env) + if not tarball: + return 1 + + if not upload_and_extract(cfg, tarball, no_restart=args.no_restart): + return 1 + + try: + os.remove(tarball) + except Exception: + pass + + print("") + print(" 部署完成!目录: %s" % cfg["project_path"]) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/soul-api/soul-api b/soul-api/soul-api new file mode 100644 index 00000000..cbb6e66b Binary files /dev/null and b/soul-api/soul-api differ diff --git a/开发文档/.DS_Store b/开发文档/.DS_Store new file mode 100644 index 00000000..40f00a37 Binary files /dev/null and b/开发文档/.DS_Store differ diff --git a/next-project/开发文档/.github/workflows/sync_from_coding.yml b/开发文档/.github/workflows/sync_from_coding.yml similarity index 100% rename from next-project/开发文档/.github/workflows/sync_from_coding.yml rename to 开发文档/.github/workflows/sync_from_coding.yml diff --git a/开发文档/0、Mycontent-book 项目总览.md b/开发文档/0、Mycontent-book 项目总览.md new file mode 100644 index 00000000..00d84e25 --- /dev/null +++ b/开发文档/0、Mycontent-book 项目总览.md @@ -0,0 +1,99 @@ +# Mycontent-book 项目总览 + +**我是卡若。** + +做这个项目,逻辑很简单:**把书卖出去,把私域做起来,把钱分下去。** + +这就不是一个普通的博客网站,这是一个**内容变现系统**。所有的技术架构,都要围绕着“阅读体验”、“流量承接”和“变现转化”来做。 + +别整那些虚头巴脑的概念,咱们直接看这个盘子怎么搭。 + +## 1. 核心逻辑 + +这个项目的生意逻辑就是三层: +1. **流量层(前端)**:让用户看着爽,像刷抖音、看公众号一样流畅。必须移动端优先,模拟 iOS 的原生质感。 +2. **内容层(数据)**:`book/` 目录下的 Markdown 文件就是我们的资产。改个字,推送到 GitHub,网站立马更新。 +3. **变现层(后端/接口)**:谁看了?谁买了?谁推荐的?这些数据要跑通。 + +## 2. 为什么这么架构? + +我选 Next.js,不是因为流行,是因为它**省事**。 +- **SSR(服务端渲染)**:SEO 友好,百度谷歌能搜到,自带流量。 +- **API Routes**:不用单独起个 Java 或 Python 服务,省服务器钱。 +- **Vercel/宝塔部署**:自动化流水线,我只管写文章,代码自动跑。 + +## 3. 当前项目结构(前后端已分离) + +### 3.1 仓库根目录 + +| 目录/项目 | 技术栈 | 说明 | +|-----------|--------|------| +| **soul-api/** | Go 1.25 + Gin + GORM + MySQL | 独立后端 API 服务,路径与现网一致 `/api/*` | +| **soul-admin/** | React 18 + Vite 6 + TypeScript + Tailwind 4 + Radix UI | 管理后台 SPA,请求通过 `VITE_API_BASE_URL` 对接 soul-api 或 Next | +| **miniprogram/** | 微信小程序原生 | C 端主阵地,用户阅读、购买、分销、提现等 | +| **next-project/** | Next.js(可选保留) | 原单体:含 `app/view/` C 端、`app/admin/` 管理端、`app/api/`;可作备用或逐步下线 | + +### 3.2 接口与前端对应关系 + +- **API 服务**:由 **soul-api**(Go)提供,端口默认 8080;路径与现网完全一致(如 `/api/user/profile`、`/api/admin/withdrawals`)。 +- **管理端**:**soul-admin** 独立部署,环境变量 `VITE_API_BASE_URL` 指向 soul-api 或 Next 的 API 基地址。 +- **小程序**:通过 `app.request()` 等封装请求 API,baseUrl 可配置,与 soul-api 对接。 +- **API 字段规范**:对外请求/响应**统一使用小写开头驼峰(camelCase)**,如 `userId`、`referralCode`、`createdAt`;数据库列名仍为 snake_case,仅在服务端内部使用。 + +## 4. 目录导航(别迷路) + +- **[1、需求](1、需求/业务需求.md)**:我们要干啥,成本多少,技术要求;[TDD 需求方案](1、需求/TDD_创业派对项目方案_v1.0.md)。 +- **[2、架构](2、架构/系统架构.md)**:整体怎么搭,前后端怎么分。 + - [技术选型与全景图](2、架构/技术选型与全景图.md)、[前后端架构分离策略](2、架构/前后端架构分离策略.md) +- **[3、原型](3、原型/原型设计规范.md)**:原型设计规范。 +- **[4、前端](4、前端/前端架构.md)**:前端架构(含 **soul-admin**、小程序)、模块详解、开发规范;[当前小程序开发细则](4、前端/当前小程序开发细则.md);[ui 子目录](4、前端/ui/):项目概述、页面功能、组件清单、API/状态/分销/支付/管理后台/部署说明等。 +- **[5、接口](5、接口/API接口.md)**:前后端怎么说话。 +- **[6、后端](6、后端/后端架构.md)**:**soul-api**(Go + Gin + GORM)架构与业务模块,后端开发规范。 +- **[7、数据库](7、数据库/数据库设计.md)**:数据存哪,怎么存。 +- **[8、部署](8、部署/部署总览.md)**:怎么上线、本地运行、宝塔部署、新分销部署、修复与优化记录等。 +- **[9、手册](9、手册/写作与结构维护手册.md)**:怎么写书,怎么维护。 +- **[10、项目管理](10、项目管理/项目落地推进表.md)**:项目推进与提示词。 + +## 5. 开发约束(重要) + +> **2026-02-07 更新:前后端已分离,soul-api + soul-admin 为主力** + +### 5.1 项目与开发策略 + +| 项目/端 | 路径/端口 | 开发状态 | 说明 | +|---------|-----------|----------|------| +| **soul-api** | 端口 8080 | ✅ 主力后端 | Go + Gin,提供全部 `/api/*` 接口,MySQL + GORM | +| **soul-admin** | 独立 SPA | ✅ 主力管理端 | React + Vite,通过 `VITE_API_BASE_URL` 对接 soul-api | +| **微信小程序** | `miniprogram/` | ✅ 主力 C 端 | 所有面向用户的新功能在此开发 | +| **Next.js** | `next-project/` 或原 app | 🔒 备用/冻结 | C 端 `app/view/` 冻结;`app/api/` 可作过渡或下线 | + +### 5.2 核心原则 + +1. **后端统一走 soul-api**:新接口在 soul-api 实现,路径与现网一致,响应字段统一 **camelCase**。 +2. **管理端统一走 soul-admin**:新管理功能在 soul-admin 开发,请求体与展示字段统一 **camelCase**。 +3. **小程序优先**:C 端新功能只在小程序开发,请求/响应已按 camelCase 对接。 +4. **API 契约统一**:查询、新增、编辑、删除的请求/响应字段全部小写开头驼峰(如 `userId`、`createdAt`、`referralCode`)。 + +### 5.3 登录与鉴权 + +| 端 | 登录方式 | 说明 | +|---|----------|------| +| 小程序 | 微信一键登录 / 手机号快速授权 | 与 soul-api 或 Next 的 `/api/miniprogram/login` 等对接 | +| soul-admin | 手机号 + 密码 | POST `/api/admin` 登录,GET 鉴权,Cookie/Token | +| 账号统一 | 以手机号/用户 ID 为唯一标识 | 多端数据互通 | + +### 5.4 为什么前后端分离? + +- **独立部署与扩展**:后端 Go 可单独扩容,前端静态资源可 CDN。 +- **多端复用 API**:小程序、soul-admin、未来 App 共用同一套 soul-api。 +- **开发效率**:前后端并行、接口契约清晰(camelCase 统一)。 + +## 6. 这里的规矩 + +- **行动至上**:文档是用来指导干活的,不是写来看的。 +- **数据说话**:所有优化要有数据支撑,加载快了多少?转化高了多少? +- **保持简单**:能用现成的库就别自己造轮子。 + +--- +**复盘:** +项目已完成前后端分离:soul-api(Go)负责接口与数据库,soul-admin(React)负责管理后台,miniprogram 负责 C 端。API 与前端字段已统一为 camelCase,便于多端对接与后续扩展。 diff --git a/next-project/开发文档/10、项目管理/小程序20260129-2.pdf b/开发文档/10、项目管理/小程序20260129-2.pdf similarity index 100% rename from next-project/开发文档/10、项目管理/小程序20260129-2.pdf rename to 开发文档/10、项目管理/小程序20260129-2.pdf diff --git a/next-project/开发文档/10、项目管理/小程序20260129.pdf b/开发文档/10、项目管理/小程序20260129.pdf similarity index 100% rename from next-project/开发文档/10、项目管理/小程序20260129.pdf rename to 开发文档/10、项目管理/小程序20260129.pdf diff --git a/next-project/开发文档/10、项目管理/项目管理提示词.md b/开发文档/10、项目管理/项目管理提示词.md similarity index 100% rename from next-project/开发文档/10、项目管理/项目管理提示词.md rename to 开发文档/10、项目管理/项目管理提示词.md diff --git a/next-project/开发文档/10、项目管理/项目落地推进表.md b/开发文档/10、项目管理/项目落地推进表.md similarity index 100% rename from next-project/开发文档/10、项目管理/项目落地推进表.md rename to 开发文档/10、项目管理/项目落地推进表.md diff --git a/开发文档/1、需求/.DS_Store b/开发文档/1、需求/.DS_Store new file mode 100644 index 00000000..013a065f Binary files /dev/null and b/开发文档/1、需求/.DS_Store differ diff --git a/next-project/开发文档/1、需求/TDD_创业派对项目方案_v1.0.md b/开发文档/1、需求/TDD_创业派对项目方案_v1.0.md similarity index 100% rename from next-project/开发文档/1、需求/TDD_创业派对项目方案_v1.0.md rename to 开发文档/1、需求/TDD_创业派对项目方案_v1.0.md diff --git a/next-project/开发文档/1、需求/业务需求.md b/开发文档/1、需求/业务需求.md similarity index 100% rename from next-project/开发文档/1、需求/业务需求.md rename to 开发文档/1、需求/业务需求.md diff --git a/开发文档/1、需求/修改/.DS_Store b/开发文档/1、需求/修改/.DS_Store new file mode 100644 index 00000000..5008ddfc Binary files /dev/null and b/开发文档/1、需求/修改/.DS_Store differ diff --git a/next-project/开发文档/1、需求/修改/1.doc b/开发文档/1、需求/修改/1.doc similarity index 100% rename from next-project/开发文档/1、需求/修改/1.doc rename to 开发文档/1、需求/修改/1.doc diff --git a/next-project/开发文档/1、需求/修改/Soul 20260118.pdf b/开发文档/1、需求/修改/Soul 20260118.pdf similarity index 100% rename from next-project/开发文档/1、需求/修改/Soul 20260118.pdf rename to 开发文档/1、需求/修改/Soul 20260118.pdf diff --git a/next-project/开发文档/1、需求/卡若角色设定.md b/开发文档/1、需求/卡若角色设定.md similarity index 100% rename from next-project/开发文档/1、需求/卡若角色设定.md rename to 开发文档/1、需求/卡若角色设定.md diff --git a/next-project/开发文档/1、需求/小程序改造TDD需求方案_v1.0.md b/开发文档/1、需求/小程序改造TDD需求方案_v1.0.md similarity index 100% rename from next-project/开发文档/1、需求/小程序改造TDD需求方案_v1.0.md rename to 开发文档/1、需求/小程序改造TDD需求方案_v1.0.md diff --git a/next-project/开发文档/1、需求/技术需求.md b/开发文档/1、需求/技术需求.md similarity index 100% rename from next-project/开发文档/1、需求/技术需求.md rename to 开发文档/1、需求/技术需求.md diff --git a/next-project/开发文档/2、架构/Gin技术栈-Go1.25依赖清单.md b/开发文档/2、架构/Gin技术栈-Go1.25依赖清单.md similarity index 100% rename from next-project/开发文档/2、架构/Gin技术栈-Go1.25依赖清单.md rename to 开发文档/2、架构/Gin技术栈-Go1.25依赖清单.md diff --git a/开发文档/2、架构/前后端架构分离策略.md b/开发文档/2、架构/前后端架构分离策略.md new file mode 100644 index 00000000..01e55e8b --- /dev/null +++ b/开发文档/2、架构/前后端架构分离策略.md @@ -0,0 +1,66 @@ +# 前后端分离开发架构 + +**我是卡若。** + +当前项目已完成**前后端物理分离**:后端 soul-api(Go),管理端 soul-admin(React),C 端小程序。开发流程仍采用**接口契约驱动**。 + +## 1. 当前架构(已落地) + +\`\`\`mermaid +graph LR + subgraph "前端" + Admin[soul-admin
React+Vite] + Mini[微信小程序] + end + + subgraph "后端 soul-api (Go)" + Gin[Gin Router /api/*] + Handler[Handler 层] + GORM[GORM] + Gin --> Handler --> GORM + end + + MySQL[(MySQL)] + GORM --> MySQL + + Admin -->|VITE_API_BASE_URL + path| Gin + Mini -->|baseUrl + path| Gin +\`\`\` + +## 2. 分离开发流程 + +\`\`\`mermaid +sequenceDiagram + participant Doc as API 文档 + participant FE as 前端 (soul-admin / 小程序) + participant BE as 后端 (soul-api) + + Doc->>FE: 1. 接口定义 (URL、camelCase 字段) + Doc->>BE: 1. 同上 + + par 并行开发 + FE->>FE: 2. Mock 或对接已有 API + FE->>FE: 3. UI 与交互,字段用 camelCase + BE->>BE: 2. Handler + Model,json 标签 camelCase + BE->>BE: 3. 单元测试 / 联调 + end + + FE->>BE: 4. 联调(path 一致,body/response camelCase) + BE-->>FE: 返回 camelCase JSON +\`\`\` + +## 3. 数据流与规范 + +- **请求**:前端只发 camelCase(如 `userId`、`referralCode`)。soul-api 的 Go 结构体用 `json:"userId"` 等接收。 +- **响应**:soul-api 通过 GORM 模型 `json:"userId"` 等输出 camelCase;前端 TypeScript 类型与接口字段一致(camelCase)。 +- **数据库**:表名列名保持 snake_case(如 `user_id`、`created_at`),仅在 soul-api 内部使用,不暴露给前端。 + +## 4. 落地执行规范 + +1. **接口先行**:新增/修改接口先在文档约定 URL、方法、请求/响应体(字段 camelCase)。 +2. **统一封装**:soul-admin 所有请求走 `src/api/client.ts`(get/post/put/del),path 与现网一致;小程序走 `app.request()`,不写死域名。 +3. **字段统一**:禁止在 API 响应或前端类型中使用 snake_case 对外字段;表单提交、列表展示一律 camelCase。 + +--- +**卡若说:** +按这个流程走,前后端对接清晰。路径一致、字段 camelCase 统一,多端复用同一套 soul-api 即可。 diff --git a/next-project/开发文档/2、架构/变现模块设计.md b/开发文档/2、架构/变现模块设计.md similarity index 100% rename from next-project/开发文档/2、架构/变现模块设计.md rename to 开发文档/2、架构/变现模块设计.md diff --git a/next-project/开发文档/2、架构/技术选型与全景图.md b/开发文档/2、架构/技术选型与全景图.md similarity index 100% rename from next-project/开发文档/2、架构/技术选型与全景图.md rename to 开发文档/2、架构/技术选型与全景图.md diff --git a/next-project/开发文档/2、架构/拆解计划_管理端抽离与API转Gin.md b/开发文档/2、架构/拆解计划_管理端抽离与API转Gin.md similarity index 100% rename from next-project/开发文档/2、架构/拆解计划_管理端抽离与API转Gin.md rename to 开发文档/2、架构/拆解计划_管理端抽离与API转Gin.md diff --git a/next-project/开发文档/2、架构/数据库.md b/开发文档/2、架构/数据库.md similarity index 100% rename from next-project/开发文档/2、架构/数据库.md rename to 开发文档/2、架构/数据库.md diff --git a/开发文档/2、架构/系统架构.md b/开发文档/2、架构/系统架构.md new file mode 100644 index 00000000..e5b1d3e8 --- /dev/null +++ b/开发文档/2、架构/系统架构.md @@ -0,0 +1,78 @@ +# 系统架构 + +**我是卡若。** + +架构不是为了画图好看,是为了**省事**和**赚钱**。 + +我们的架构设计,核心围绕两个字:**分离**。 +- 内容和代码分离。 +- 前端和后端分离(已落地:soul-api + soul-admin + 小程序)。 +- 静态和动态分离。 + +## 1. 架构全景图(当前) + +\`\`\`mermaid +graph TD + subgraph "内容生产 (Content)" + Typora[本地写作] --> Git[Git 仓库] + Git --> AutoSync[自动同步] + end + + subgraph "后端 (soul-api - Go)" + API[Gin API /api/*] + API --> GORM[GORM] + GORM --> MySQL[(MySQL)] + AutoSync --> FileSys[文件/配置] + FileSys --> API + end + + subgraph "前端" + Admin[soul-admin React+Vite] + Mini[微信小程序] + Admin --> API + Mini --> API + end + + subgraph "用户触达" + Mini --> Wechat[微信] + Admin --> Browser[浏览器] + end +\`\`\` + +## 2. 当前项目分工 + +| 层级 | 项目 | 技术 | 说明 | +|------|------|------|------| +| **后端** | soul-api | Go 1.25 + Gin + GORM + MySQL | 提供全部 `/api/*`,路径与现网一致,响应 camelCase | +| **管理端** | soul-admin | React 18 + Vite 6 + TS + Tailwind + Radix UI | SPA,`VITE_API_BASE_URL` 指向 soul-api | +| **C 端** | miniprogram | 微信小程序原生 | 阅读、购买、分销、提现,请求/响应 camelCase | +| **备用** | next-project | Next.js | 原单体,可保留 app/api 过渡或逐步下线 | + +## 3. 核心设计理念 + +### 3.1 “内容即产品” +核心资产是内容(书籍章节、配置)。文章用 Markdown/数据库存,Git 或脚本同步,安全可控。 + +### 3.2 前后端已分离 +- **后端**:soul-api(Go)独立部署,可单独扩容、多端复用。 +- **管理端**:soul-admin 纯静态 SPA,部署到任意静态托管或 CDN。 +- **接口契约**:路径不变,请求/响应字段统一 **camelCase**,详见 [前后端架构分离策略](前后端架构分离策略.md)。 + +### 3.3 极简部署 +- 后端:Go 二进制 + 环境变量,或 Docker 单容器。 +- 管理端:`npm run build` 后部署到 Nginx/Vercel/OSS。 +- 小程序:微信后台发布。宝塔/Webhook 可按需做自动拉代码。 + +## 4. 关键约束 + +1. **API 字段统一**:对外一律 camelCase(如 `userId`、`createdAt`);数据库列名保持 snake_case 仅在服务端使用。 +2. **本地优先**:写作与开发在本地,通过 soul-api 连接数据库。 +3. **单向流动**:数据流向 `内容/配置 -> API -> 前端`;订单、用户等由 API 写库。 + +## 5. 详细技术栈 + +详见:**[技术选型与全景图](技术选型与全景图.md)**、**[前后端架构分离策略](前后端架构分离策略.md)**。 + +--- +**总结:** +当前已实现前后端分离:soul-api 负责接口与数据,soul-admin 负责管理后台,小程序负责 C 端。保持接口路径与字段规范统一,便于多端对接与扩展。 diff --git a/next-project/开发文档/3、原型/原型设计规范.md b/开发文档/3、原型/原型设计规范.md similarity index 100% rename from next-project/开发文档/3、原型/原型设计规范.md rename to 开发文档/3、原型/原型设计规范.md diff --git a/next-project/开发文档/4、前端/ui/01-项目概述.md b/开发文档/4、前端/ui/01-项目概述.md similarity index 100% rename from next-project/开发文档/4、前端/ui/01-项目概述.md rename to 开发文档/4、前端/ui/01-项目概述.md diff --git a/next-project/开发文档/4、前端/ui/02-页面功能说明.md b/开发文档/4、前端/ui/02-页面功能说明.md similarity index 100% rename from next-project/开发文档/4、前端/ui/02-页面功能说明.md rename to 开发文档/4、前端/ui/02-页面功能说明.md diff --git a/next-project/开发文档/4、前端/ui/03-组件清单.md b/开发文档/4、前端/ui/03-组件清单.md similarity index 100% rename from next-project/开发文档/4、前端/ui/03-组件清单.md rename to 开发文档/4、前端/ui/03-组件清单.md diff --git a/next-project/开发文档/4、前端/ui/04-API接口说明.md b/开发文档/4、前端/ui/04-API接口说明.md similarity index 100% rename from next-project/开发文档/4、前端/ui/04-API接口说明.md rename to 开发文档/4、前端/ui/04-API接口说明.md diff --git a/next-project/开发文档/4、前端/ui/05-状态管理说明.md b/开发文档/4、前端/ui/05-状态管理说明.md similarity index 100% rename from next-project/开发文档/4、前端/ui/05-状态管理说明.md rename to 开发文档/4、前端/ui/05-状态管理说明.md diff --git a/next-project/开发文档/4、前端/ui/06-分销系统说明.md b/开发文档/4、前端/ui/06-分销系统说明.md similarity index 100% rename from next-project/开发文档/4、前端/ui/06-分销系统说明.md rename to 开发文档/4、前端/ui/06-分销系统说明.md diff --git a/next-project/开发文档/4、前端/ui/07-找伙伴功能说明.md b/开发文档/4、前端/ui/07-找伙伴功能说明.md similarity index 100% rename from next-project/开发文档/4、前端/ui/07-找伙伴功能说明.md rename to 开发文档/4、前端/ui/07-找伙伴功能说明.md diff --git a/next-project/开发文档/4、前端/ui/08-支付系统说明.md b/开发文档/4、前端/ui/08-支付系统说明.md similarity index 100% rename from next-project/开发文档/4、前端/ui/08-支付系统说明.md rename to 开发文档/4、前端/ui/08-支付系统说明.md diff --git a/next-project/开发文档/4、前端/ui/09-管理后台说明.md b/开发文档/4、前端/ui/09-管理后台说明.md similarity index 100% rename from next-project/开发文档/4、前端/ui/09-管理后台说明.md rename to 开发文档/4、前端/ui/09-管理后台说明.md diff --git a/next-project/开发文档/4、前端/ui/10-部署上线指南.md b/开发文档/4、前端/ui/10-部署上线指南.md similarity index 100% rename from next-project/开发文档/4、前端/ui/10-部署上线指南.md rename to 开发文档/4、前端/ui/10-部署上线指南.md diff --git a/next-project/开发文档/4、前端/ui/11-页面截图与还原指南.md b/开发文档/4、前端/ui/11-页面截图与还原指南.md similarity index 100% rename from next-project/开发文档/4、前端/ui/11-页面截图与还原指南.md rename to 开发文档/4、前端/ui/11-页面截图与还原指南.md diff --git a/开发文档/4、前端/前端开发规范.md b/开发文档/4、前端/前端开发规范.md new file mode 100644 index 00000000..3b534011 --- /dev/null +++ b/开发文档/4、前端/前端开发规范.md @@ -0,0 +1,52 @@ +# 前端开发规范 (Frontend Specs) - 智能自生长文档 + +> **提示词功能 (Prompt Function)**: 将本文件拖入 AI 对话框,即可激活“前端技术专家”角色,生成符合项目规范的代码。 + +## 1. 基础上下文 + +### 1.1 角色档案:卡若 (Karuo) +- **管理端 (soul-admin)**:深色后台风格,稳、快、信息密度合理。 +- **C 端 (小程序)**:移动端优先,阅读与转化体验顺畅。 + +### 1.2 技术栈(当前) + +| 项目 | 技术栈 | +|------|--------| +| **soul-admin** | React 18 + Vite 6 + TypeScript + Tailwind 4 + Radix UI(类 Shadcn)+ React Router 6 | +| **miniprogram** | 微信小程序原生 (JS/WXML/WXSS) | + +## 2. 开发规范核心 + +### 2.1 API 与字段规范(强制) + +- **请求/响应字段**:一律**小写开头驼峰(camelCase)**。 + - 正确:`userId`、`referralCode`、`createdAt`、`hasFullBook`、`pendingEarnings`。 + - 错误:`user_id`、`referral_code`、`created_at`(仅数据库内部使用,不暴露给前端)。 +- **类型定义**:TypeScript 接口与 API 约定一致,全部 camelCase。 +- **表单提交**:提交 body 的字段名使用 camelCase(如 `isAdmin`、`hasFullBook`)。 + +### 2.2 soul-admin 规范 + +- **请求**:所有接口请求通过 `src/api/client.ts` 的 get/post/put/del,path 与现网一致,不写死域名。 +- **环境变量**:`VITE_API_BASE_URL` 指向 soul-api 或现有 API 基地址(开发/生产分开配置)。 +- **目录**:页面在 `src/pages/`,通用 UI 在 `src/components/ui/`,业务模块在 `src/components/modules/`。 +- **样式**:Tailwind 为主,深色主题可用 `#0f2137`、`#38bdac` 等品牌色。 + +### 2.3 小程序规范 + +- **请求**:通过 `app.request()` 等封装,baseUrl 可配置;请求体与后端约定一致(camelCase)。 +- **存储**:需要与后端一致的标识(如 `referral_code` 存为业务值可保留 key 名)时,注意与接口字段区分;接口层面仍用 camelCase。 + +### 2.4 交互与性能 + +- **加载**:列表/详情需 loading 或骨架屏,避免白屏。 +- **错误**:接口失败需有明确提示(Toast/Alert)。 +- **图片**:懒加载 + 失败占位。 + +## 3. AI 协作指令 + +**角色**:前端主程(soul-admin + 小程序)。 +**任务**: +1. **代码生成**:React 组件带 Tailwind 类名;类型与 API 字段 camelCase 一致。 +2. **接口对接**:使用统一 client(get/post/put/del),不直接写死 fetch 域名;请求体/响应类型为 camelCase。 +3. **结构分析**:复杂页面可用 Mermaid 展示组件或数据流依赖。 diff --git a/开发文档/4、前端/前端架构.md b/开发文档/4、前端/前端架构.md new file mode 100644 index 00000000..49c51895 --- /dev/null +++ b/开发文档/4、前端/前端架构.md @@ -0,0 +1,75 @@ +# 前端架构 + +**我是卡若。** + +前端分两块:**管理端 soul-admin**(给运营/自己用)、**C 端 miniprogram**(给用户用)。目标都是稳、快、体验好。 + +## 1. 当前前端分工 + +| 项目 | 技术栈 | 说明 | +|------|--------|------| +| **soul-admin** | React 18 + Vite 6 + TypeScript + Tailwind 4 + Radix UI (Shadcn 风格) | 管理后台 SPA,对接 soul-api,路由与现网 /admin/* 对应 | +| **miniprogram** | 微信小程序原生 (JS/WXML/WXSS) | C 端主阵地:阅读、购买、分销、提现等 | + +(原 Next.js 的 `app/view/`、`app/admin/` 可保留为备用或逐步下线。) + +## 2. soul-admin 技术底座 + +- **框架**: React 18 + Vite 6,构建快、热更新稳。 +- **语言**: TypeScript,类型与 API 字段一致(camelCase)。 +- **样式**: Tailwind CSS 4,全局与组件级样式。 +- **UI**: Radix UI 系(Dialog、Tabs、Switch 等),类 Shadcn 用法,深色管理后台风格。 +- **路由**: React Router 6,路径与现网管理端一致(如 `/dashboard`、`/users`、`/withdrawals`)。 +- **请求**: 统一走 `src/api/client.ts`(get/post/put/del),baseUrl 为 `VITE_API_BASE_URL`,path 与 soul-api 一致;**请求体与响应字段一律 camelCase**。 + +## 3. soul-admin 目录结构 + +\`\`\` +soul-admin/src/ +├── api/ +│ └── client.ts # 统一请求封装,baseUrl + path +├── components/ +│ ├── ui/ # 通用 UI (Button, Card, Dialog, Table...) +│ └── modules/ # 业务模块 (如 UserDetailModal) +├── layouts/ +│ └── AdminLayout.tsx # 管理端布局与侧栏 +├── pages/ # 页面(与路由一一对应) +│ ├── login/ +│ ├── dashboard/ +│ ├── users/ +│ ├── orders/ +│ ├── distribution/ +│ ├── withdrawals/ +│ ├── content/ +│ ├── chapters/ +│ ├── settings/ +│ ├── referral-settings/ +│ ├── payment/ +│ ├── match/ +│ └── ... +├── lib/ +│ └── utils.ts +├── App.tsx +└── main.tsx +\`\`\` + +## 4. 小程序 (miniprogram) + +- **技术**: 微信原生,`app.js` 全局、`app.request()` 封装请求,baseUrl 可配置指向 soul-api。 +- **页面**: 首页、阅读、个人中心、分销、提现等;与 soul-api 接口对接,**请求/响应字段已统一为 camelCase**(如 `userId`、`referralCode`、`createdAt`)。 +- **登录**: 微信登录 + 手机号授权,与后端 `/api/miniprogram/login` 等对接。 + +## 5. API 与数据规范 + +- **路径**: 与现网完全一致(如 `/api/user/profile`、`/api/admin/withdrawals`),由 soul-api 提供。 +- **字段**: 所有请求体、响应体、前端类型定义**统一小写开头驼峰(camelCase)**:`userId`、`createdAt`、`referralCode`、`hasFullBook` 等。禁止对外使用 snake_case。 +- **类型**: TypeScript 接口与 API 响应一一对应,便于联调与维护。 + +## 6. 交互与体验(通用) + +- **加载**: 列表/详情加载时使用骨架屏或明确 loading 状态,避免白屏。 +- **错误**: 接口失败要有提示(Toast/Alert),必要处可重试。 +- **表单**: 提交字段与后端约定一致(camelCase),校验与错误信息清晰。 + +--- +**总结**:管理端在 soul-admin(React+Vite),C 端在小程序;两者都通过统一 API 封装对接 soul-api,字段规范统一为 camelCase。 diff --git a/next-project/开发文档/4、前端/当前小程序开发细则.md b/开发文档/4、前端/当前小程序开发细则.md similarity index 100% rename from next-project/开发文档/4、前端/当前小程序开发细则.md rename to 开发文档/4、前端/当前小程序开发细则.md diff --git a/next-project/开发文档/4、前端/模块详解.md b/开发文档/4、前端/模块详解.md similarity index 100% rename from next-project/开发文档/4、前端/模块详解.md rename to 开发文档/4、前端/模块详解.md diff --git a/next-project/开发文档/5、接口/API接口.md b/开发文档/5、接口/API接口.md similarity index 100% rename from next-project/开发文档/5、接口/API接口.md rename to 开发文档/5、接口/API接口.md diff --git a/next-project/开发文档/5、接口/接口定义规范.md b/开发文档/5、接口/接口定义规范.md similarity index 100% rename from next-project/开发文档/5、接口/接口定义规范.md rename to 开发文档/5、接口/接口定义规范.md diff --git a/next-project/开发文档/6、后端/image.png b/开发文档/6、后端/image.png similarity index 100% rename from next-project/开发文档/6、后端/image.png rename to 开发文档/6、后端/image.png diff --git a/开发文档/6、后端/后端开发规范.md b/开发文档/6、后端/后端开发规范.md new file mode 100644 index 00000000..80133793 --- /dev/null +++ b/开发文档/6、后端/后端开发规范.md @@ -0,0 +1,54 @@ +# 后端开发规范 (Backend Specs) - 智能自生长文档 + +> **提示词功能 (Prompt Function)**: 将本文件拖入 AI 对话框,即可激活“Go 后端专家”角色,生成符合项目规范的 soul-api 代码。 + +## 1. 基础上下文 + +### 1.1 角色档案:卡若 (Karuo) +- **核心**:接口稳、性能好、与现网路径和契约一致。 +- **习惯**:请求/响应统一 camelCase,数据库列名 snake_case 仅内部使用。 + +### 1.2 技术栈(当前) + +- **语言**:Go 1.25+。 +- **框架**:Gin (HTTP),GORM (ORM)。 +- **数据**:MySQL。 +- **配置**:环境变量(godotenv),.env 不提交。 + +## 2. 开发规范核心 + +### 2.1 项目结构 (soul-api) + +- **handler**:按业务拆分文件(如 `user.go`、`order.go`、`admin_withdrawals.go`),每个 handler 对应现网 API 路径与行为。 +- **model**:GORM 模型,表名与列名 snake_case;**JSON 标签必须 camelCase**(如 `json:"userId"`、`json:"createdAt"`)。 +- **router**:在 `router.go` 中集中注册,路径与现网 `/api/*` 一致。 +- **middleware**:CORS、AdminAuth、限流、安全头等。 + +### 2.2 接口与字段规范(强制) + +- **路径**:与现网完全一致,例如 `GET /api/user/profile`、`PUT /api/admin/withdrawals`。 +- **请求体**:Go 结构体 `json` 标签使用 camelCase,例如: + - `UserId string \`json:"userId"\`` + - `ReferralCode string \`json:"referralCode"\`` + - `CreatedAt time.Time \`json:"createdAt"\`` +- **响应**:通过 GORM 模型或 `gin.H` 返回时,键名一律 camelCase;禁止对外返回 `user_id`、`created_at` 等 snake_case。 +- **数据库**:表/列名保持 snake_case,仅在 GORM 与 SQL 中使用。 + +### 2.3 安全与错误 + +- **SQL**:一律使用 GORM 或参数化查询,禁止拼接 SQL。 +- **鉴权**:管理端接口使用 `middleware.AdminAuth()`,未登录返回 401 或统一错误体。 +- **错误响应**:统一格式如 `gin.H{"success": false, "error": "错误说明"}`。 + +### 2.4 配置与依赖 + +- **配置**:从环境变量读取(如 `DB_HOST`、`PORT`、`CORS_ORIGINS`),参考 `.env.example`。 +- **依赖**:`go mod tidy`,提交前确保 go.mod/go.sum 已更新。 + +## 3. AI 协作指令 + +**角色**:Go 后端架构师(soul-api)。 +**任务**: +1. **代码实现**:新增或修改 handler/model/router,路径与现网一致,请求/响应字段 camelCase。 +2. **模型定义**:GORM 的 `gorm` 标签用 snake_case,`json` 标签用 camelCase。 +3. **逻辑图解**:复杂流程可用 Mermaid 展示调用关系或数据流。 diff --git a/开发文档/6、后端/后端架构.md b/开发文档/6、后端/后端架构.md new file mode 100644 index 00000000..77c15fcb --- /dev/null +++ b/开发文档/6、后端/后端架构.md @@ -0,0 +1,69 @@ +# 后端架构与业务逻辑 + +**我是卡若。** + +当前后端为独立项目 **soul-api**(Go + Gin + GORM),提供全部 `/api/*` 接口,与 soul-admin、小程序对接。 + +## 1. 技术栈(当前) + +| 项目 | 技术 | 说明 | +|------|------|------| +| **soul-api** | Go 1.25 + Gin + GORM + MySQL | 独立 API 服务,路径与现网一致,响应 camelCase | + +## 2. soul-api 目录结构 + +\`\`\` +soul-api/ +├── cmd/ +│ └── server/ +│ └── main.go # 入口 +├── internal/ +│ ├── config/ # 配置(环境变量) +│ ├── database/ # 数据库连接 (GORM) +│ ├── handler/ # API 处理函数(按业务拆分) +│ │ ├── admin.go, admin_chapters.go, admin_withdrawals.go ... +│ │ ├── auth.go, user.go, book.go, orders.go +│ │ ├── withdraw.go, referral.go, distribution.go +│ │ ├── config.go, db.go, miniprogram.go ... +│ ├── middleware/ # 中间件(CORS、鉴权、限流、安全头) +│ ├── model/ # 数据模型(GORM + json 标签 camelCase) +│ │ ├── user.go, order.go, chapter.go, withdrawal.go ... +│ └── router/ +│ └── router.go # 路由注册,路径与现网 /api/* 一致 +├── .env, .env.example +├── go.mod, go.sum +└── Makefile +\`\`\` + +## 3. 核心业务模块(在 handler 中实现) + +- **鉴权**:`/api/admin` 登录与鉴权,管理端 Cookie/Token;小程序走 `/api/miniprogram/login` 等。 +- **用户**:`/api/user/profile`、`/api/user/update`、`/api/user/track`、`/api/user/purchase-status` 等。 +- **书籍/章节**:`/api/book/*`(目录、章节、搜索、统计)。 +- **订单与支付**:`/api/orders`、`/api/miniprogram/pay`、支付回调等。 +- **分销与提现**:`/api/referral/*`、`/api/distribution/*`、`/api/withdraw/*`、`/api/admin/withdrawals`。 +- **配置与内容**:`/api/config`、`/api/content`、`/api/db/config`、`/api/db/book` 等。 +- **管理端**:`/api/admin/*`(章节、分销概览、提现审核等)、`/api/db/*`(用户、配置、初始化等)。 + +## 4. 接口与字段规范 + +- **路径**:与现网完全一致(如 `GET /api/user/profile`、`PUT /api/admin/withdrawals`),便于前端只改 baseUrl 切换后端。 +- **请求/响应字段**:一律**小写开头驼峰(camelCase)**。 + - Go 结构体:`json:"userId"`、`json:"createdAt"`、`json:"referralCode"`。 + - 数据库列名:保持 snake_case(如 `user_id`、`created_at`),仅在 GORM 与 SQL 中使用,不暴露给前端。 +- **统一响应**:成功可返回 `gin.H{"success": true, "data": ...}`;失败 `gin.H{"success": false, "error": "..."}`。 + +## 5. 数据库与配置 + +- **数据库**:MySQL,GORM 连接;表结构可与原 Next/Prisma 保持一致,便于迁移。 +- **配置**:环境变量(.env),如 `DB_*`、`PORT`、`CORS_ORIGINS`、`JWT_SECRET` 等;敏感信息不提交仓库。 + +## 6. 扩展性 + +- **鉴权**:管理端已用 `middleware.AdminAuth()` 保护 `/api/admin/*`、`/api/db/*`。 +- **CORS**:已配置 AllowOrigins/AllowCredentials,支持 soul-admin 与小程序跨域。 +- **限流**:可按需在 middleware 中增加 rate limit。 + +--- +**卡若说:** +soul-api 是唯一对外后端,功能明确、路径与字段规范统一,便于多端复用与维护。 diff --git a/next-project/开发文档/6、后端/小程序支付参数.png b/开发文档/6、后端/小程序支付参数.png similarity index 100% rename from next-project/开发文档/6、后端/小程序支付参数.png rename to 开发文档/6、后端/小程序支付参数.png diff --git a/next-project/开发文档/7、数据库/数据库管理规范.md b/开发文档/7、数据库/数据库管理规范.md similarity index 100% rename from next-project/开发文档/7、数据库/数据库管理规范.md rename to 开发文档/7、数据库/数据库管理规范.md diff --git a/next-project/开发文档/7、数据库/数据库设计.md b/开发文档/7、数据库/数据库设计.md similarity index 100% rename from next-project/开发文档/7、数据库/数据库设计.md rename to 开发文档/7、数据库/数据库设计.md diff --git a/next-project/开发文档/8、部署/API接入说明.md b/开发文档/8、部署/API接入说明.md similarity index 100% rename from next-project/开发文档/8、部署/API接入说明.md rename to 开发文档/8、部署/API接入说明.md diff --git a/next-project/开发文档/8、部署/MCP-MySQL配置说明.md b/开发文档/8、部署/MCP-MySQL配置说明.md similarity index 100% rename from next-project/开发文档/8、部署/MCP-MySQL配置说明.md rename to 开发文档/8、部署/MCP-MySQL配置说明.md diff --git a/next-project/开发文档/8、部署/Next.js宝塔部署方案.md b/开发文档/8、部署/Next.js宝塔部署方案.md similarity index 100% rename from next-project/开发文档/8、部署/Next.js宝塔部署方案.md rename to 开发文档/8、部署/Next.js宝塔部署方案.md diff --git a/next-project/开发文档/8、部署/Next.js自动化部署流程.md b/开发文档/8、部署/Next.js自动化部署流程.md similarity index 100% rename from next-project/开发文档/8、部署/Next.js自动化部署流程.md rename to 开发文档/8、部署/Next.js自动化部署流程.md diff --git a/next-project/开发文档/8、部署/Prisma ORM完整迁移总结.md b/开发文档/8、部署/Prisma ORM完整迁移总结.md similarity index 100% rename from next-project/开发文档/8、部署/Prisma ORM完整迁移总结.md rename to 开发文档/8、部署/Prisma ORM完整迁移总结.md diff --git a/next-project/开发文档/8、部署/Prisma ORM迁移最终报告.md b/开发文档/8、部署/Prisma ORM迁移最终报告.md similarity index 100% rename from next-project/开发文档/8、部署/Prisma ORM迁移最终报告.md rename to 开发文档/8、部署/Prisma ORM迁移最终报告.md diff --git a/next-project/开发文档/8、部署/Prisma ORM迁移进度.md b/开发文档/8、部署/Prisma ORM迁移进度.md similarity index 100% rename from next-project/开发文档/8、部署/Prisma ORM迁移进度.md rename to 开发文档/8、部署/Prisma ORM迁移进度.md diff --git a/next-project/开发文档/8、部署/Soul-MySQL-MCP配置说明.md b/开发文档/8、部署/Soul-MySQL-MCP配置说明.md similarity index 100% rename from next-project/开发文档/8、部署/Soul-MySQL-MCP配置说明.md rename to 开发文档/8、部署/Soul-MySQL-MCP配置说明.md diff --git a/next-project/开发文档/8、部署/Standalone模式说明.md b/开发文档/8、部署/Standalone模式说明.md similarity index 100% rename from next-project/开发文档/8、部署/Standalone模式说明.md rename to 开发文档/8、部署/Standalone模式说明.md diff --git a/next-project/开发文档/8、部署/交易中心Tab按需加载优化.md b/开发文档/8、部署/交易中心Tab按需加载优化.md similarity index 100% rename from next-project/开发文档/8、部署/交易中心Tab按需加载优化.md rename to 开发文档/8、部署/交易中心Tab按需加载优化.md diff --git a/next-project/开发文档/8、部署/代码逻辑和数据库最终检查清单.md b/开发文档/8、部署/代码逻辑和数据库最终检查清单.md similarity index 100% rename from next-project/开发文档/8、部署/代码逻辑和数据库最终检查清单.md rename to 开发文档/8、部署/代码逻辑和数据库最终检查清单.md diff --git a/next-project/开发文档/8、部署/佣金计算逻辑检查.md b/开发文档/8、部署/佣金计算逻辑检查.md similarity index 100% rename from next-project/开发文档/8、部署/佣金计算逻辑检查.md rename to 开发文档/8、部署/佣金计算逻辑检查.md diff --git a/next-project/开发文档/8、部署/佣金问题-快速诊断和修复.md b/开发文档/8、部署/佣金问题-快速诊断和修复.md similarity index 100% rename from next-project/开发文档/8、部署/佣金问题-快速诊断和修复.md rename to 开发文档/8、部署/佣金问题-快速诊断和修复.md diff --git a/next-project/开发文档/8、部署/修复与优化记录/README.md b/开发文档/8、部署/修复与优化记录/README.md similarity index 100% rename from next-project/开发文档/8、部署/修复与优化记录/README.md rename to 开发文档/8、部署/修复与优化记录/README.md diff --git a/next-project/开发文档/8、部署/分销与绑定流程图.md b/开发文档/8、部署/分销与绑定流程图.md similarity index 100% rename from next-project/开发文档/8、部署/分销与绑定流程图.md rename to 开发文档/8、部署/分销与绑定流程图.md diff --git a/next-project/开发文档/8、部署/分销中心loading优化说明-v2.md b/开发文档/8、部署/分销中心loading优化说明-v2.md similarity index 100% rename from next-project/开发文档/8、部署/分销中心loading优化说明-v2.md rename to 开发文档/8、部署/分销中心loading优化说明-v2.md diff --git a/next-project/开发文档/8、部署/分销中心接口优化实施记录.md b/开发文档/8、部署/分销中心接口优化实施记录.md similarity index 100% rename from next-project/开发文档/8、部署/分销中心接口优化实施记录.md rename to 开发文档/8、部署/分销中心接口优化实施记录.md diff --git a/next-project/开发文档/8、部署/分销中心接口优化方案.md b/开发文档/8、部署/分销中心接口优化方案.md similarity index 100% rename from next-project/开发文档/8、部署/分销中心接口优化方案.md rename to 开发文档/8、部署/分销中心接口优化方案.md diff --git a/next-project/开发文档/8、部署/分销中心数据库连接错误修复.md b/开发文档/8、部署/分销中心数据库连接错误修复.md similarity index 100% rename from next-project/开发文档/8、部署/分销中心数据库连接错误修复.md rename to 开发文档/8、部署/分销中心数据库连接错误修复.md diff --git a/next-project/开发文档/8、部署/分销中心用户列表数据对接说明.md b/开发文档/8、部署/分销中心用户列表数据对接说明.md similarity index 100% rename from next-project/开发文档/8、部署/分销中心用户列表数据对接说明.md rename to 开发文档/8、部署/分销中心用户列表数据对接说明.md diff --git a/next-project/开发文档/8、部署/分销中心设置功能说明.md b/开发文档/8、部署/分销中心设置功能说明.md similarity index 100% rename from next-project/开发文档/8、部署/分销中心设置功能说明.md rename to 开发文档/8、部署/分销中心设置功能说明.md diff --git a/next-project/开发文档/8、部署/删除referred_by字段说明.md b/开发文档/8、部署/删除referred_by字段说明.md similarity index 100% rename from next-project/开发文档/8、部署/删除referred_by字段说明.md rename to 开发文档/8、部署/删除referred_by字段说明.md diff --git a/next-project/开发文档/8、部署/可提现金额计算修复.md b/开发文档/8、部署/可提现金额计算修复.md similarity index 100% rename from next-project/开发文档/8、部署/可提现金额计算修复.md rename to 开发文档/8、部署/可提现金额计算修复.md diff --git a/next-project/开发文档/8、部署/后台提现审核-快速测试指南.md b/开发文档/8、部署/后台提现审核-快速测试指南.md similarity index 100% rename from next-project/开发文档/8、部署/后台提现审核-快速测试指南.md rename to 开发文档/8、部署/后台提现审核-快速测试指南.md diff --git a/next-project/开发文档/8、部署/后台提现审核功能完善说明.md b/开发文档/8、部署/后台提现审核功能完善说明.md similarity index 100% rename from next-project/开发文档/8、部署/后台提现审核功能完善说明.md rename to 开发文档/8、部署/后台提现审核功能完善说明.md diff --git a/next-project/开发文档/8、部署/后台提现审核数据对接.md b/开发文档/8、部署/后台提现审核数据对接.md similarity index 100% rename from next-project/开发文档/8、部署/后台提现审核数据对接.md rename to 开发文档/8、部署/后台提现审核数据对接.md diff --git a/next-project/开发文档/8、部署/后台订单显示优化说明.md b/开发文档/8、部署/后台订单显示优化说明.md similarity index 100% rename from next-project/开发文档/8、部署/后台订单显示优化说明.md rename to 开发文档/8、部署/后台订单显示优化说明.md diff --git a/next-project/开发文档/8、部署/宝塔配置检查说明.md b/开发文档/8、部署/宝塔配置检查说明.md similarity index 100% rename from next-project/开发文档/8、部署/宝塔配置检查说明.md rename to 开发文档/8、部署/宝塔配置检查说明.md diff --git a/next-project/开发文档/8、部署/宝塔面板配置订单同步定时任务.md b/开发文档/8、部署/宝塔面板配置订单同步定时任务.md similarity index 100% rename from next-project/开发文档/8、部署/宝塔面板配置订单同步定时任务.md rename to 开发文档/8、部署/宝塔面板配置订单同步定时任务.md diff --git a/next-project/开发文档/8、部署/小程序头像上传优化说明.md b/开发文档/8、部署/小程序头像上传优化说明.md similarity index 100% rename from next-project/开发文档/8、部署/小程序头像上传优化说明.md rename to 开发文档/8、部署/小程序头像上传优化说明.md diff --git a/next-project/开发文档/8、部署/小程序提现金额对接说明.md b/开发文档/8、部署/小程序提现金额对接说明.md similarity index 100% rename from next-project/开发文档/8、部署/小程序提现金额对接说明.md rename to 开发文档/8、部署/小程序提现金额对接说明.md diff --git a/next-project/开发文档/8、部署/小程序昵称自动填充说明.md b/开发文档/8、部署/小程序昵称自动填充说明.md similarity index 100% rename from next-project/开发文档/8、部署/小程序昵称自动填充说明.md rename to 开发文档/8、部署/小程序昵称自动填充说明.md diff --git a/next-project/开发文档/8、部署/小程序调整说明.md b/开发文档/8、部署/小程序调整说明.md similarity index 100% rename from next-project/开发文档/8、部署/小程序调整说明.md rename to 开发文档/8、部署/小程序调整说明.md diff --git a/next-project/开发文档/8、部署/推广设置功能-完整修复清单.md b/开发文档/8、部署/推广设置功能-完整修复清单.md similarity index 100% rename from next-project/开发文档/8、部署/推广设置功能-完整修复清单.md rename to 开发文档/8、部署/推广设置功能-完整修复清单.md diff --git a/next-project/开发文档/8、部署/推广设置页面测试清单.md b/开发文档/8、部署/推广设置页面测试清单.md similarity index 100% rename from next-project/开发文档/8、部署/推广设置页面测试清单.md rename to 开发文档/8、部署/推广设置页面测试清单.md diff --git a/next-project/开发文档/8、部署/提现卡片数据优化说明.md b/开发文档/8、部署/提现卡片数据优化说明.md similarity index 100% rename from next-project/开发文档/8、部署/提现卡片数据优化说明.md rename to 开发文档/8、部署/提现卡片数据优化说明.md diff --git a/next-project/开发文档/8、部署/提现双向校验实现.md b/开发文档/8、部署/提现双向校验实现.md similarity index 100% rename from next-project/开发文档/8、部署/提现双向校验实现.md rename to 开发文档/8、部署/提现双向校验实现.md diff --git a/next-project/开发文档/8、部署/提现审核流程优化.md b/开发文档/8、部署/提现审核流程优化.md similarity index 100% rename from next-project/开发文档/8、部署/提现审核流程优化.md rename to 开发文档/8、部署/提现审核流程优化.md diff --git a/next-project/开发文档/8、部署/提现按钮状态修复说明.md b/开发文档/8、部署/提现按钮状态修复说明.md similarity index 100% rename from next-project/开发文档/8、部署/提现按钮状态修复说明.md rename to 开发文档/8、部署/提现按钮状态修复说明.md diff --git a/next-project/开发文档/8、部署/提现按钮逻辑修正.md b/开发文档/8、部署/提现按钮逻辑修正.md similarity index 100% rename from next-project/开发文档/8、部署/提现按钮逻辑修正.md rename to 开发文档/8、部署/提现按钮逻辑修正.md diff --git a/next-project/开发文档/8、部署/提现接口数据查询错误修复.md b/开发文档/8、部署/提现接口数据查询错误修复.md similarity index 100% rename from next-project/开发文档/8、部署/提现接口数据查询错误修复.md rename to 开发文档/8、部署/提现接口数据查询错误修复.md diff --git a/next-project/开发文档/8、部署/提现接口统一修复完成.md b/开发文档/8、部署/提现接口统一修复完成.md similarity index 100% rename from next-project/开发文档/8、部署/提现接口统一修复完成.md rename to 开发文档/8、部署/提现接口统一修复完成.md diff --git a/next-project/开发文档/8、部署/提现接口逻辑修正.md b/开发文档/8、部署/提现接口逻辑修正.md similarity index 100% rename from next-project/开发文档/8、部署/提现接口逻辑修正.md rename to 开发文档/8、部署/提现接口逻辑修正.md diff --git a/next-project/开发文档/8、部署/提现接口重复问题修复.md b/开发文档/8、部署/提现接口重复问题修复.md similarity index 100% rename from next-project/开发文档/8、部署/提现接口重复问题修复.md rename to 开发文档/8、部署/提现接口重复问题修复.md diff --git a/next-project/开发文档/8、部署/提现记录获取失败诊断指南.md b/开发文档/8、部署/提现记录获取失败诊断指南.md similarity index 100% rename from next-project/开发文档/8、部署/提现记录获取失败诊断指南.md rename to 开发文档/8、部署/提现记录获取失败诊断指南.md diff --git a/next-project/开发文档/8、部署/支付接口清单.md b/开发文档/8、部署/支付接口清单.md similarity index 100% rename from next-project/开发文档/8、部署/支付接口清单.md rename to 开发文档/8、部署/支付接口清单.md diff --git a/next-project/开发文档/8、部署/收益明细优化说明.md b/开发文档/8、部署/收益明细优化说明.md similarity index 100% rename from next-project/开发文档/8、部署/收益明细优化说明.md rename to 开发文档/8、部署/收益明细优化说明.md diff --git a/next-project/开发文档/8、部署/新分销逻辑-代码修改总结.md b/开发文档/8、部署/新分销逻辑-代码修改总结.md similarity index 100% rename from next-project/开发文档/8、部署/新分销逻辑-代码修改总结.md rename to 开发文档/8、部署/新分销逻辑-代码修改总结.md diff --git a/next-project/开发文档/8、部署/新分销逻辑-宝塔操作清单.md b/开发文档/8、部署/新分销逻辑-宝塔操作清单.md similarity index 100% rename from next-project/开发文档/8、部署/新分销逻辑-宝塔操作清单.md rename to 开发文档/8、部署/新分销逻辑-宝塔操作清单.md diff --git a/next-project/开发文档/8、部署/新分销逻辑-部署步骤.md b/开发文档/8、部署/新分销逻辑-部署步骤.md similarity index 100% rename from next-project/开发文档/8、部署/新分销逻辑-部署步骤.md rename to 开发文档/8、部署/新分销逻辑-部署步骤.md diff --git a/next-project/开发文档/8、部署/新分销逻辑设计方案.md b/开发文档/8、部署/新分销逻辑设计方案.md similarity index 100% rename from next-project/开发文档/8、部署/新分销逻辑设计方案.md rename to 开发文档/8、部署/新分销逻辑设计方案.md diff --git a/next-project/开发文档/8、部署/本地运行.md b/开发文档/8、部署/本地运行.md similarity index 100% rename from next-project/开发文档/8、部署/本地运行.md rename to 开发文档/8、部署/本地运行.md diff --git a/next-project/开发文档/8、部署/章节阅读付费标准流程设计.md b/开发文档/8、部署/章节阅读付费标准流程设计.md similarity index 100% rename from next-project/开发文档/8、部署/章节阅读付费标准流程设计.md rename to 开发文档/8、部署/章节阅读付费标准流程设计.md diff --git a/next-project/开发文档/8、部署/章节阅读页集成示例.md b/开发文档/8、部署/章节阅读页集成示例.md similarity index 100% rename from next-project/开发文档/8、部署/章节阅读页集成示例.md rename to 开发文档/8、部署/章节阅读页集成示例.md diff --git a/next-project/开发文档/8、部署/管理端推广配置与小程序对接说明.md b/开发文档/8、部署/管理端推广配置与小程序对接说明.md similarity index 100% rename from next-project/开发文档/8、部署/管理端推广配置与小程序对接说明.md rename to 开发文档/8、部署/管理端推广配置与小程序对接说明.md diff --git a/next-project/开发文档/8、部署/累计佣金计算修复说明.md b/开发文档/8、部署/累计佣金计算修复说明.md similarity index 100% rename from next-project/开发文档/8、部署/累计佣金计算修复说明.md rename to 开发文档/8、部署/累计佣金计算修复说明.md diff --git a/next-project/开发文档/8、部署/绑定关系存储方案分析.md b/开发文档/8、部署/绑定关系存储方案分析.md similarity index 100% rename from next-project/开发文档/8、部署/绑定关系存储方案分析.md rename to 开发文档/8、部署/绑定关系存储方案分析.md diff --git a/next-project/开发文档/8、部署/自动同步与分支策略.md b/开发文档/8、部署/自动同步与分支策略.md similarity index 100% rename from next-project/开发文档/8、部署/自动同步与分支策略.md rename to 开发文档/8、部署/自动同步与分支策略.md diff --git a/next-project/开发文档/8、部署/自动解绑API配置说明.md b/开发文档/8、部署/自动解绑API配置说明.md similarity index 100% rename from next-project/开发文档/8、部署/自动解绑API配置说明.md rename to 开发文档/8、部署/自动解绑API配置说明.md diff --git a/next-project/开发文档/8、部署/自定义导航组件方案.md b/开发文档/8、部署/自定义导航组件方案.md similarity index 100% rename from next-project/开发文档/8、部署/自定义导航组件方案.md rename to 开发文档/8、部署/自定义导航组件方案.md diff --git a/next-project/开发文档/8、部署/订单管理商品显示优化.md b/开发文档/8、部署/订单管理商品显示优化.md similarity index 100% rename from next-project/开发文档/8、部署/订单管理商品显示优化.md rename to 开发文档/8、部署/订单管理商品显示优化.md diff --git a/next-project/开发文档/8、部署/订单管理数据类型错误修复.md b/开发文档/8、部署/订单管理数据类型错误修复.md similarity index 100% rename from next-project/开发文档/8、部署/订单管理数据类型错误修复.md rename to 开发文档/8、部署/订单管理数据类型错误修复.md diff --git a/next-project/开发文档/8、部署/邀请码分销规则说明.md b/开发文档/8、部署/邀请码分销规则说明.md similarity index 100% rename from next-project/开发文档/8、部署/邀请码分销规则说明.md rename to 开发文档/8、部署/邀请码分销规则说明.md diff --git a/next-project/开发文档/8、部署/部署总览.md b/开发文档/8、部署/部署总览.md similarity index 100% rename from next-project/开发文档/8、部署/部署总览.md rename to 开发文档/8、部署/部署总览.md diff --git a/next-project/开发文档/8、部署/阅读页标准流程改造说明.md b/开发文档/8、部署/阅读页标准流程改造说明.md similarity index 100% rename from next-project/开发文档/8、部署/阅读页标准流程改造说明.md rename to 开发文档/8、部署/阅读页标准流程改造说明.md diff --git a/next-project/开发文档/9、手册/使用手册提示词.md b/开发文档/9、手册/使用手册提示词.md similarity index 100% rename from next-project/开发文档/9、手册/使用手册提示词.md rename to 开发文档/9、手册/使用手册提示词.md diff --git a/next-project/开发文档/9、手册/写作与结构维护手册.md b/开发文档/9、手册/写作与结构维护手册.md similarity index 100% rename from next-project/开发文档/9、手册/写作与结构维护手册.md rename to 开发文档/9、手册/写作与结构维护手册.md diff --git a/next-project/开发文档/9、手册/提示词/使用手册提示词.md b/开发文档/9、手册/提示词/使用手册提示词.md similarity index 100% rename from next-project/开发文档/9、手册/提示词/使用手册提示词.md rename to 开发文档/9、手册/提示词/使用手册提示词.md diff --git a/next-project/开发文档/9、手册/落地方案提示词.md b/开发文档/9、手册/落地方案提示词.md similarity index 100% rename from next-project/开发文档/9、手册/落地方案提示词.md rename to 开发文档/9、手册/落地方案提示词.md diff --git a/next-project/开发文档/9、手册/说明手册提示词.md b/开发文档/9、手册/说明手册提示词.md similarity index 100% rename from next-project/开发文档/9、手册/说明手册提示词.md rename to 开发文档/9、手册/说明手册提示词.md diff --git a/next-project/开发文档/API/配置清单.md b/开发文档/API/配置清单.md similarity index 100% rename from next-project/开发文档/API/配置清单.md rename to 开发文档/API/配置清单.md diff --git a/next-project/开发文档/提现功能完整技术文档.md b/开发文档/提现功能完整技术文档.md similarity index 100% rename from next-project/开发文档/提现功能完整技术文档.md rename to 开发文档/提现功能完整技术文档.md