update pay ui

This commit is contained in:
2025-12-13 18:28:21 +08:00
parent 648d672a35
commit 4380976787

167
app_vx.py
View File

@@ -1433,6 +1433,25 @@ class Comment(db.Model):
replies = db.relationship('Comment', backref=db.backref('parent', remote_side=[id]))
class Feedback(db.Model):
"""用户反馈"""
__tablename__ = 'user_feedback'
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=True) # 可匿名反馈
feedback_type = db.Column(db.String(50), default='general') # 反馈类型: general/bug/suggestion/complaint
title = db.Column(db.String(200)) # 反馈标题
content = db.Column(db.Text, nullable=False) # 反馈内容
contact = db.Column(db.String(100)) # 联系方式(可选)
images = db.Column(db.JSON) # 附带图片URL列表
status = db.Column(db.String(20), default='pending') # pending/processing/resolved/closed
admin_reply = db.Column(db.Text) # 管理员回复
created_at = db.Column(db.DateTime, default=beijing_now)
updated_at = db.Column(db.DateTime, default=beijing_now, onupdate=beijing_now)
user = db.relationship('User', backref='feedbacks')
class StockBasicInfo(db.Model):
__tablename__ = 'ea_stocklist'
@@ -4300,7 +4319,19 @@ def api_bindphone_wechat():
'data': None
}), 400
# 4. 检查手机号是否已被其他用户绑定
# 4. 检查当前用户是否已绑定此手机号
if user.phone == phone_number and user.phone_confirmed:
# 已经绑定过了,直接返回成功
return jsonify({
'code': 200,
'message': '手机号已绑定',
'data': {
'phone': phone_number,
'bindcd': True
}
})
# 5. 检查手机号是否已被其他用户绑定(只查确认绑定的)
existing_user = User.query.filter(
User.phone == phone_number,
User.phone_confirmed == True,
@@ -4308,13 +4339,14 @@ def api_bindphone_wechat():
).first()
if existing_user:
logger.warning(f"手机号 {phone_number[:3]}****{phone_number[-4:]} 已被用户 {existing_user.id} 绑定,当前用户 {user.id} 尝试绑定失败")
return jsonify({
'code': 400,
'message': '该手机号已被其他账号绑定',
'data': None
}), 400
# 5. 更新用户手机号
# 6. 更新用户手机号
user.phone = phone_number
user.phone_confirmed = True
user.phone_confirm_time = beijing_now()
@@ -5752,11 +5784,15 @@ def parse_best_matches(best_matches_value):
for item in data:
if isinstance(item, dict):
# 新结构:包含研报信息的字典
# 将相关度转为整数百分比 (0.928 -> 93)
raw_score = item.get('best_report_match_ratio', 0)
int_score = int(round(raw_score * 100)) if raw_score and raw_score <= 1 else int(round(raw_score)) if raw_score else 0
stock_info = {
'code': item.get('stock_code', ''),
'name': item.get('company_name', ''),
'description': item.get('original_description', ''),
'score': item.get('best_report_match_ratio', 0),
'score': int_score,
# 研报引用信息
'report': {
'title': item.get('best_report_title', ''),
@@ -6010,11 +6046,14 @@ def api_calendar_events():
if stock_data:
for stock_info in stock_data:
if isinstance(stock_info, list) and len(stock_info) >= 2:
# 将相关度转为整数百分比
raw_score = stock_info[3] if len(stock_info) > 3 else 0
int_score = int(round(raw_score * 100)) if raw_score and raw_score <= 1 else int(round(raw_score)) if raw_score else 0
parsed_stocks.append({
'code': stock_info[0],
'name': stock_info[1],
'description': stock_info[2] if len(stock_info) > 2 else '',
'score': stock_info[3] if len(stock_info) > 3 else 0,
'score': int_score,
'report': None
})
except Exception as e:
@@ -6741,12 +6780,16 @@ def api_user_profile():
# 总评论数(发出的评论 + 收到的评论和回复)
total_comments = comments_made + comments_received + replies_received
# 判断手机号绑定状态
phone_bindcd = bool(user.phone and user.phone_confirmed)
profile_data = {
'basic_info': {
'user_id': user.id,
'username': user.username,
'email': user.email,
'phone': user.phone,
'phone': user.phone if phone_bindcd else None,
'phone_bindcd': phone_bindcd,
'nickname': user.nickname,
'avatar_url': get_full_avatar_url(user.avatar_url), # 修改这里
'bio': user.bio,
@@ -6809,6 +6852,116 @@ def api_user_profile():
}), 500
@app.route('/api/user/feedback', methods=['POST'])
@token_required
def api_user_feedback():
"""用户反馈接口"""
try:
user = request.user
# 获取请求数据(兼容多种格式)
data = request.get_json(force=True, silent=True)
if not data:
data = request.form.to_dict()
content = data.get('content', '').strip()
if not content:
return jsonify({
'code': 400,
'message': '反馈内容不能为空',
'data': None
}), 400
feedback_type = data.get('type', 'general')
title = data.get('title', '').strip()
contact = data.get('contact', '').strip()
images = data.get('images', [])
# 确保images是列表
if isinstance(images, str):
images = [images] if images else []
# 创建反馈记录
feedback = Feedback(
user_id=user.id,
feedback_type=feedback_type,
title=title if title else None,
content=content,
contact=contact if contact else user.phone or user.email,
images=images if images else None,
status='pending'
)
db.session.add(feedback)
db.session.commit()
return jsonify({
'code': 200,
'message': '反馈提交成功,感谢您的宝贵意见!',
'data': {
'feedback_id': feedback.id,
'status': feedback.status,
'created_at': feedback.created_at.isoformat() if feedback.created_at else None
}
})
except Exception as e:
db.session.rollback()
return jsonify({
'code': 500,
'message': f'反馈提交失败: {str(e)}',
'data': None
}), 500
@app.route('/api/user/feedback', methods=['GET'])
@token_required
def api_user_feedback_list():
"""获取用户反馈列表"""
try:
user = request.user
page = request.args.get('page', 1, type=int)
per_page = request.args.get('per_page', 10, type=int)
feedbacks = Feedback.query.filter_by(user_id=user.id) \
.order_by(Feedback.created_at.desc()) \
.paginate(page=page, per_page=per_page, error_out=False)
feedback_list = []
for fb in feedbacks.items:
feedback_list.append({
'id': fb.id,
'type': fb.feedback_type,
'title': fb.title,
'content': fb.content,
'status': fb.status,
'admin_reply': fb.admin_reply,
'created_at': fb.created_at.isoformat() if fb.created_at else None,
'updated_at': fb.updated_at.isoformat() if fb.updated_at else None
})
return jsonify({
'code': 200,
'message': 'success',
'data': {
'feedbacks': feedback_list,
'pagination': {
'page': page,
'per_page': per_page,
'total': feedbacks.total,
'pages': feedbacks.pages
}
}
})
except Exception as e:
return jsonify({
'code': 500,
'message': str(e),
'data': None
}), 500
# 在文件开头添加缓存变量
_agreements_cache = {}
_cache_loaded = False
@@ -7262,8 +7415,8 @@ if __name__ == '__main__':
# SSL 配置
ssl_context = None
if not args.no_ssl:
cert_file = '/etc/letsencrypt/live/api.valuefrontier.cn/fullchain.pem'
key_file = '/etc/letsencrypt/live/api.valuefrontier.cn/privkey.pem'
cert_file = '/etc/nginx/ssl/api.valuefrontier.cn/fullchain.pem'
key_file = '/etc/nginx/ssl/api.valuefrontier.cn/privkey.pem'
if os.path.exists(cert_file) and os.path.exists(key_file):
ssl_context = (cert_file, key_file)
else: