修正今日
This commit is contained in:
parent
616fe10b02
commit
339f8211e4
11
README.md
11
README.md
@ -64,6 +64,17 @@ curl -X PUT http://localhost:5000/api/admin/turnover \
|
|||||||
--data-binary @revenue.csv
|
--data-binary @revenue.csv
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 本地运行(默认数据库路径)
|
||||||
|
- 默认数据库:`sqlite:///data/data.db`(项目根目录下的 `data/data.db`)
|
||||||
|
- 启动:
|
||||||
|
```powershell
|
||||||
|
$env:PORT=57778; $env:ADMIN_TOKEN="<你的口令>"; python backend/app.py
|
||||||
|
```
|
||||||
|
- 自定义路径(Windows 绝对路径示例):
|
||||||
|
```powershell
|
||||||
|
$env:DATABASE_URL="sqlite:////e:/2025Code/python/YixuanYingye/data/data.db"; python backend/app.py
|
||||||
|
```
|
||||||
|
|
||||||
## 环境优化(最优解)
|
## 环境优化(最优解)
|
||||||
- 基础镜像切换为 `python:3.11-alpine`,构建更快、体积更小
|
- 基础镜像切换为 `python:3.11-alpine`,构建更快、体积更小
|
||||||
- 依赖安装走腾讯云 PyPI 镜像:`PIP_INDEX_URL=https://mirrors.cloud.tencent.com/pypi/simple`
|
- 依赖安装走腾讯云 PyPI 镜像:`PIP_INDEX_URL=https://mirrors.cloud.tencent.com/pypi/simple`
|
||||||
|
|||||||
@ -18,13 +18,21 @@ import io
|
|||||||
load_dotenv()
|
load_dotenv()
|
||||||
app = Flask(__name__, static_folder="../frontend", static_url_path="/static")
|
app = Flask(__name__, static_folder="../frontend", static_url_path="/static")
|
||||||
CORS(app)
|
CORS(app)
|
||||||
app.config['SQLALCHEMY_DATABASE_URI'] = os.getenv("DATABASE_URL", "sqlite:///data.db")
|
app.config['SQLALCHEMY_DATABASE_URI'] = os.getenv("DATABASE_URL", "sqlite:///data/data.db")
|
||||||
def _ensure_sqlite_dir(url):
|
def _ensure_sqlite_dir(url):
|
||||||
if not url.startswith('sqlite:'):
|
if not url.startswith('sqlite:'):
|
||||||
return
|
return
|
||||||
p = url
|
p = url
|
||||||
if p.startswith('sqlite:////'):
|
if p.startswith('sqlite:////'):
|
||||||
db_path = p.replace('sqlite:////', '/')
|
db_path = p.replace('sqlite:////', '')
|
||||||
|
if db_path[1:3] == ':/' or db_path[1:3] == ':\\':
|
||||||
|
# path like 'E:/...' with a leading slash
|
||||||
|
pass
|
||||||
|
elif db_path[0:2] == ':/':
|
||||||
|
# unlikely
|
||||||
|
db_path = db_path[1:]
|
||||||
|
# Normalize Windows backslashes
|
||||||
|
db_path = db_path.replace('/', os.sep)
|
||||||
elif p.startswith('sqlite:///'):
|
elif p.startswith('sqlite:///'):
|
||||||
db_path = os.path.join(os.getcwd(), p.replace('sqlite:///', ''))
|
db_path = os.path.join(os.getcwd(), p.replace('sqlite:///', ''))
|
||||||
else:
|
else:
|
||||||
@ -95,13 +103,24 @@ def daily_job():
|
|||||||
existing.source = existing.source or 'generator'
|
existing.source = existing.source or 'generator'
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
return
|
return
|
||||||
amount = generate_mock_revenue()
|
amount = gen_amount_for_date(today, cfg)
|
||||||
rev = DailyRevenue(date=today, amount=amount, is_final=True, source='generator')
|
rev = DailyRevenue(date=today, amount=amount, is_final=True, source='generator')
|
||||||
db.session.add(rev)
|
db.session.add(rev)
|
||||||
db.session.add(AuditLog(date=today, old_amount=None, new_amount=amount, reason='daily_generate', actor='system', type='generate'))
|
db.session.add(AuditLog(date=today, old_amount=None, new_amount=amount, reason='daily_generate', actor='system', type='generate'))
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
_append_log_line(today.isoformat(), amount, shop_name)
|
_append_log_line(today.isoformat(), amount, shop_name)
|
||||||
|
|
||||||
|
def settle_today_if_due():
|
||||||
|
cfg = load_config()
|
||||||
|
cutoff = cfg.get("cutoff_hour", 23)
|
||||||
|
try:
|
||||||
|
cutoff = int(cutoff)
|
||||||
|
except Exception:
|
||||||
|
cutoff = 23
|
||||||
|
if datetime.now().hour < cutoff:
|
||||||
|
return
|
||||||
|
daily_job()
|
||||||
|
|
||||||
# ---- 日志解析与聚合 ----
|
# ---- 日志解析与聚合 ----
|
||||||
def load_config():
|
def load_config():
|
||||||
cfg_path = os.path.join(os.path.dirname(__file__), "..", "config.json")
|
cfg_path = os.path.join(os.path.dirname(__file__), "..", "config.json")
|
||||||
@ -349,6 +368,7 @@ if __name__ == "__main__":
|
|||||||
with app.app_context():
|
with app.app_context():
|
||||||
sync_log_to_db()
|
sync_log_to_db()
|
||||||
auto_import_csv_on_start()
|
auto_import_csv_on_start()
|
||||||
|
settle_today_if_due()
|
||||||
app.run(host="0.0.0.0", port=int(os.getenv("PORT", "5000")))
|
app.run(host="0.0.0.0", port=int(os.getenv("PORT", "5000")))
|
||||||
|
|
||||||
@app.route('/api/events')
|
@app.route('/api/events')
|
||||||
@ -358,7 +378,8 @@ def sse_events():
|
|||||||
now = datetime.now()
|
now = datetime.now()
|
||||||
payload = {"type": "tick", "server_now": now.isoformat(timespec='seconds')}
|
payload = {"type": "tick", "server_now": now.isoformat(timespec='seconds')}
|
||||||
yield f"data: {json.dumps(payload)}\n\n"
|
yield f"data: {json.dumps(payload)}\n\n"
|
||||||
if now.hour == 23 and now.minute == 1:
|
if now.minute in (0, 1):
|
||||||
|
settle_today_if_due()
|
||||||
yield "data: {\"type\": \"force_refresh\"}\n\n"
|
yield "data: {\"type\": \"force_refresh\"}\n\n"
|
||||||
time.sleep(30)
|
time.sleep(30)
|
||||||
return Response(stream_with_context(event_stream()), mimetype='text/event-stream')
|
return Response(stream_with_context(event_stream()), mimetype='text/event-stream')
|
||||||
|
|||||||
0
data/data.db
Normal file
0
data/data.db
Normal file
BIN
instance/data.db
BIN
instance/data.db
Binary file not shown.
Loading…
Reference in New Issue
Block a user