Compare commits
No commits in common. "ced1020235d8296dc78493b0a100a5181924c223" and "1aa3cb4151f7922b953d3fe9c3d60687ef336d50" have entirely different histories.
ced1020235
...
1aa3cb4151
@ -1,7 +1,7 @@
|
|||||||
from flask import Blueprint, request, jsonify
|
from flask import Blueprint, request, jsonify
|
||||||
from datetime import timedelta
|
from datetime import datetime, timedelta
|
||||||
from extensions import db
|
from extensions import db
|
||||||
from models import User, Role, Permission, SystemDict, SystemNotification, Order, to_bj_time, get_bj_now
|
from models import User, Role, Permission, SystemDict, SystemNotification, Order, to_bj_time
|
||||||
from middlewares.auth import permission_required
|
from middlewares.auth import permission_required
|
||||||
from services.logger import system_logger
|
from services.logger import system_logger
|
||||||
|
|
||||||
@ -277,7 +277,7 @@ def delete_notification():
|
|||||||
@admin_bp.route('/orders', methods=['GET'])
|
@admin_bp.route('/orders', methods=['GET'])
|
||||||
@permission_required('manage_system') # 仅限超级管理员
|
@permission_required('manage_system') # 仅限超级管理员
|
||||||
def get_orders():
|
def get_orders():
|
||||||
thirty_min_ago = get_bj_now() - timedelta(minutes=30)
|
thirty_min_ago = datetime.utcnow() - timedelta(minutes=30)
|
||||||
|
|
||||||
# 过滤掉超过 30 分钟未支付的订单
|
# 过滤掉超过 30 分钟未支付的订单
|
||||||
orders = Order.query.filter(
|
orders = Order.query.filter(
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
from flask import Blueprint, request, jsonify, session, render_template, redirect, url_for
|
from flask import Blueprint, request, jsonify, session, render_template, redirect, url_for
|
||||||
import json
|
import json
|
||||||
from datetime import timedelta
|
from datetime import datetime, timedelta
|
||||||
from extensions import db
|
from extensions import db
|
||||||
from models import User, get_bj_now
|
from models import User
|
||||||
from services.sms_service import SMSService
|
from services.sms_service import SMSService
|
||||||
from services.captcha_service import CaptchaService
|
from services.captcha_service import CaptchaService
|
||||||
from services.logger import system_logger
|
from services.logger import system_logger
|
||||||
@ -57,7 +57,7 @@ def buy_page():
|
|||||||
user_id = session['user_id']
|
user_id = session['user_id']
|
||||||
user = db.session.get(User, user_id)
|
user = db.session.get(User, user_id)
|
||||||
|
|
||||||
thirty_min_ago = get_bj_now() - timedelta(minutes=30)
|
thirty_min_ago = datetime.utcnow() - timedelta(minutes=30)
|
||||||
|
|
||||||
# 获取用户个人充值记录 (过滤掉超过 30 分钟未支付的订单)
|
# 获取用户个人充值记录 (过滤掉超过 30 分钟未支付的订单)
|
||||||
personal_orders = Order.query.filter(
|
personal_orders = Order.query.filter(
|
||||||
@ -158,7 +158,8 @@ def send_code():
|
|||||||
success, msg = SMSService.send_code(phone)
|
success, msg = SMSService.send_code(phone)
|
||||||
if success:
|
if success:
|
||||||
# 设置各种限制标记
|
# 设置各种限制标记
|
||||||
now = get_bj_now()
|
from datetime import datetime
|
||||||
|
now = datetime.now()
|
||||||
seconds_until_midnight = ((23 - now.hour) * 3600) + ((59 - now.minute) * 60) + (60 - now.second)
|
seconds_until_midnight = ((23 - now.hour) * 3600) + ((59 - now.minute) * 60) + (60 - now.second)
|
||||||
|
|
||||||
redis_client.setex(f"sms_lock:{phone}", 60, "1")
|
redis_client.setex(f"sms_lock:{phone}", 60, "1")
|
||||||
@ -420,7 +421,7 @@ def get_logs():
|
|||||||
for log in pagination.items:
|
for log in pagination.items:
|
||||||
logs_data.append({
|
logs_data.append({
|
||||||
"id": log.id,
|
"id": log.id,
|
||||||
"time": log.created_at_bj.strftime('%Y-%m-%d %H:%M:%S'),
|
"time": log.created_at.strftime('%Y-%m-%d %H:%M:%S'),
|
||||||
"level": log.level,
|
"level": log.level,
|
||||||
"message": log.message,
|
"message": log.message,
|
||||||
"module": log.module,
|
"module": log.module,
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
from flask import Blueprint, request, redirect, url_for, session, jsonify, render_template
|
from flask import Blueprint, request, redirect, url_for, session, jsonify, render_template
|
||||||
from extensions import db
|
from extensions import db
|
||||||
from models import Order, User, to_bj_time, get_bj_now
|
from models import Order, User, to_bj_time
|
||||||
from services.alipay_service import AlipayService
|
from services.alipay_service import AlipayService
|
||||||
from services.logger import system_logger
|
from services.logger import system_logger
|
||||||
import uuid
|
import uuid
|
||||||
from datetime import timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
payment_bp = Blueprint('payment', __name__, url_prefix='/payment')
|
payment_bp = Blueprint('payment', __name__, url_prefix='/payment')
|
||||||
|
|
||||||
@ -91,7 +91,7 @@ def payment_history():
|
|||||||
if 'user_id' not in session:
|
if 'user_id' not in session:
|
||||||
return redirect(url_for('auth.login'))
|
return redirect(url_for('auth.login'))
|
||||||
|
|
||||||
thirty_min_ago = get_bj_now() - timedelta(minutes=30)
|
thirty_min_ago = datetime.utcnow() - timedelta(minutes=30)
|
||||||
|
|
||||||
user_id = session['user_id']
|
user_id = session['user_id']
|
||||||
orders = Order.query.filter(
|
orders = Order.query.filter(
|
||||||
@ -111,7 +111,7 @@ def api_payment_history():
|
|||||||
if 'user_id' not in session:
|
if 'user_id' not in session:
|
||||||
return jsonify({'code': 401, 'msg': '请先登录'}), 401
|
return jsonify({'code': 401, 'msg': '请先登录'}), 401
|
||||||
|
|
||||||
thirty_min_ago = get_bj_now() - timedelta(minutes=30)
|
thirty_min_ago = datetime.utcnow() - timedelta(minutes=30)
|
||||||
|
|
||||||
user_id = session['user_id']
|
user_id = session['user_id']
|
||||||
orders = Order.query.filter(
|
orders = Order.query.filter(
|
||||||
@ -155,7 +155,7 @@ def payment_notify():
|
|||||||
if order and order.status == 'PENDING':
|
if order and order.status == 'PENDING':
|
||||||
order.status = 'PAID'
|
order.status = 'PAID'
|
||||||
order.trade_no = trade_no
|
order.trade_no = trade_no
|
||||||
order.paid_at = get_bj_now()
|
order.paid_at = datetime.now()
|
||||||
|
|
||||||
user = db.session.get(User, order.user_id)
|
user = db.session.get(User, order.user_id)
|
||||||
if user:
|
if user:
|
||||||
|
|||||||
26
models.py
26
models.py
@ -6,20 +6,18 @@ from werkzeug.security import generate_password_hash, check_password_hash
|
|||||||
UTC_TZ = timezone.utc
|
UTC_TZ = timezone.utc
|
||||||
BEIJING_TZ = timezone(timedelta(hours=8))
|
BEIJING_TZ = timezone(timedelta(hours=8))
|
||||||
|
|
||||||
def get_bj_now():
|
|
||||||
"""获取当前北京时间(无时区信息的 naive datetime)"""
|
|
||||||
return datetime.now(BEIJING_TZ).replace(tzinfo=None)
|
|
||||||
|
|
||||||
def to_bj_time(dt):
|
def to_bj_time(dt):
|
||||||
"""返回北京时间
|
"""将 UTC 时间转换为北京时间 (UTC+8)
|
||||||
|
|
||||||
注意:为统一时间处理,现在所有时间存储都直接使用北京时间。
|
无论系统时区设置如何,都能正确转换为北京时间
|
||||||
此函数主要用于兼容性,直接返回输入时间。
|
|
||||||
"""
|
"""
|
||||||
if not dt:
|
if not dt:
|
||||||
return None
|
return None
|
||||||
# 直接返回(数据库存储的已经是北京时间)
|
# 如果 dt 没有时区信息,假设它是 UTC 时间
|
||||||
return dt if dt.tzinfo is None else dt.replace(tzinfo=None)
|
if dt.tzinfo is None:
|
||||||
|
dt = dt.replace(tzinfo=UTC_TZ)
|
||||||
|
# 转换为北京时间
|
||||||
|
return dt.astimezone(BEIJING_TZ).replace(tzinfo=None)
|
||||||
|
|
||||||
|
|
||||||
# 角色与权限的多对多关联表
|
# 角色与权限的多对多关联表
|
||||||
@ -56,7 +54,7 @@ class User(db.Model):
|
|||||||
is_banned = db.Column(db.Boolean, default=False) # 账号是否被封禁
|
is_banned = db.Column(db.Boolean, default=False) # 账号是否被封禁
|
||||||
# 关联角色 ID
|
# 关联角色 ID
|
||||||
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
|
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
|
||||||
created_at = db.Column(db.DateTime, default=get_bj_now)
|
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def created_at_bj(self):
|
def created_at_bj(self):
|
||||||
@ -108,7 +106,7 @@ class GenerationRecord(db.Model):
|
|||||||
cost = db.Column(db.Integer, default=0) # 消耗积分
|
cost = db.Column(db.Integer, default=0) # 消耗积分
|
||||||
# 存储生成的图片 URL 列表 (JSON 字符串)
|
# 存储生成的图片 URL 列表 (JSON 字符串)
|
||||||
image_urls = db.Column(db.Text)
|
image_urls = db.Column(db.Text)
|
||||||
created_at = db.Column(db.DateTime, default=get_bj_now)
|
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def created_at_bj(self):
|
def created_at_bj(self):
|
||||||
@ -130,7 +128,7 @@ class SystemNotification(db.Model):
|
|||||||
title = db.Column(db.String(200), nullable=False)
|
title = db.Column(db.String(200), nullable=False)
|
||||||
content = db.Column(db.Text, nullable=False)
|
content = db.Column(db.Text, nullable=False)
|
||||||
is_active = db.Column(db.Boolean, default=True)
|
is_active = db.Column(db.Boolean, default=True)
|
||||||
created_at = db.Column(db.DateTime, default=get_bj_now)
|
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def created_at_bj(self):
|
def created_at_bj(self):
|
||||||
@ -150,7 +148,7 @@ class Order(db.Model):
|
|||||||
points = db.Column(db.Integer, nullable=False) # 购买的积分
|
points = db.Column(db.Integer, nullable=False) # 购买的积分
|
||||||
status = db.Column(db.String(20), default='PENDING') # PENDING, PAID, CANCELLED
|
status = db.Column(db.String(20), default='PENDING') # PENDING, PAID, CANCELLED
|
||||||
trade_no = db.Column(db.String(64)) # 支付宝交易号
|
trade_no = db.Column(db.String(64)) # 支付宝交易号
|
||||||
created_at = db.Column(db.DateTime, default=get_bj_now)
|
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||||
paid_at = db.Column(db.DateTime)
|
paid_at = db.Column(db.DateTime)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -180,7 +178,7 @@ class SystemLog(db.Model):
|
|||||||
method = db.Column(db.String(10))
|
method = db.Column(db.String(10))
|
||||||
user_agent = db.Column(db.String(255))
|
user_agent = db.Column(db.String(255))
|
||||||
|
|
||||||
created_at = db.Column(db.DateTime, default=get_bj_now)
|
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def created_at_bj(self):
|
def created_at_bj(self):
|
||||||
|
|||||||
@ -86,16 +86,13 @@ class SystemLogger:
|
|||||||
try:
|
try:
|
||||||
from models import SystemLog
|
from models import SystemLog
|
||||||
|
|
||||||
# 直接存储北京时间,避免时区转换问题
|
|
||||||
bj_now = datetime.now(BEIJING_TZ).replace(tzinfo=None)
|
|
||||||
|
|
||||||
log_data = {
|
log_data = {
|
||||||
'level': level,
|
'level': level,
|
||||||
'message': message,
|
'message': message,
|
||||||
'module': module,
|
'module': module,
|
||||||
'user_id': extra.get('user_id') if extra else None,
|
'user_id': extra.get('user_id') if extra else None,
|
||||||
'extra': json.dumps(extra, ensure_ascii=False) if extra else None,
|
'extra': json.dumps(extra, ensure_ascii=False) if extra else None,
|
||||||
'created_at': bj_now # 直接存储北京时间
|
'created_at': datetime.now(timezone.utc).replace(tzinfo=None) # 存储 UTC 时间(无时区信息)
|
||||||
}
|
}
|
||||||
|
|
||||||
# 捕获请求上下文信息
|
# 捕获请求上下文信息
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
from models import GenerationRecord, Order, db, to_bj_time, get_bj_now
|
from models import GenerationRecord, Order, db, to_bj_time
|
||||||
from sqlalchemy import func
|
from sqlalchemy import func
|
||||||
from datetime import timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
def get_point_stats(user_id, days=7):
|
def get_point_stats(user_id, days=7):
|
||||||
"""获取用户积分消耗统计数据 (用于图表)"""
|
"""获取用户积分消耗统计数据 (用于图表)"""
|
||||||
end_date = get_bj_now()
|
end_date = datetime.utcnow()
|
||||||
start_date = end_date - timedelta(days=days-1)
|
start_date = end_date - timedelta(days=days-1)
|
||||||
|
|
||||||
# 1. 获取消耗统计 (从 GenerationRecord)
|
# 1. 获取消耗统计 (从 GenerationRecord)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user