| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324 |
- """
- 卡密管理接口
- """
- from fastapi import APIRouter, Depends, Query, Request
- from fastapi.responses import StreamingResponse
- from sqlalchemy.orm import Session
- from pydantic import BaseModel
- import secrets
- import string
- import csv
- import io
- from app.database import get_db
- from app.api.deps import get_current_admin
- from app.models.card import Card
- from datetime import datetime
- from app.utils.timezone import beijing_now
- router = APIRouter()
- class CardGenerateRequest(BaseModel):
- """生成卡密请求"""
- software_code: str
- days: int
- discount: float
- count: int
- def generate_card_code() -> str:
- """生成卡密代码"""
- characters = string.ascii_uppercase + string.digits
- return ''.join(secrets.choice(characters) for _ in range(16))
- @router.post("/generate")
- async def generate_cards(
- req: CardGenerateRequest,
- request: Request,
- db: Session = Depends(get_db),
- current_admin = Depends(get_current_admin)
- ):
- """生成卡密"""
- try:
- cards = []
- for _ in range(req.count):
- card_code = generate_card_code()
- # 确保卡密唯一
- while db.query(Card).filter(Card.card_code == card_code).first():
- card_code = generate_card_code()
- card = Card(
- card_code=card_code,
- software_code=req.software_code,
- days=req.days,
- discount=req.discount,
- status=0,
- created_at=beijing_now(),
- updated_at=beijing_now()
- )
- db.add(card)
- cards.append(card)
- # 刷新以获取ID
- db.flush()
- # 创建历史记录
- from app.utils.history import create_card_history, create_operation_log
- for card in cards:
- create_card_history(db, card, "INSERT")
- # 记录操作日志
- client_ip = request.client.host if request.client else None
- create_operation_log(
- db=db,
- admin_id=current_admin.id,
- module="卡密管理",
- action="生成卡密",
- description=f"批量生成卡密:{req.count}张,软件:{req.software_code},天数:{req.days}天",
- new_data={
- "software_code": req.software_code,
- "days": req.days,
- "discount": req.discount,
- "count": req.count,
- "card_codes": [c.card_code for c in cards]
- },
- ip_address=client_ip
- )
- # 一次性提交所有更改
- db.commit()
- return {
- "code": 200,
- "msg": "生成成功",
- "data": {
- "count": len(cards),
- "cards": [c.card_code for c in cards]
- }
- }
- except Exception as e:
- try:
- db.rollback()
- except Exception:
- # 回滚失败记录警告但继续处理
- pass
- logger.exception("生成卡密失败")
- return {"code": 500, "msg": "系统异常,操作未完成,请稍后重试;若持续发生请联系客服"}
- @router.get("")
- async def get_cards(
- page: int = Query(1, ge=1),
- page_size: int = Query(10, ge=1, le=100),
- keyword: str = Query(None, description="搜索关键词(卡密代码)"),
- software_code: str = Query(None, description="软件特征码筛选"),
- status: int = Query(None, description="状态筛选:0-未激活,1-已激活,2-已过期"),
- db: Session = Depends(get_db),
- current_admin = Depends(get_current_admin)
- ):
- """获取卡密列表"""
- from sqlalchemy import or_
- from app.models.software import Software
- from app.models.user import User
- offset = (page - 1) * page_size
- # 构建查询,关联软件表和用户表
- query = db.query(Card, Software.name.label('software_name'), User.username.label('username'))\
- .join(Software, Card.software_code == Software.code)\
- .outerjoin(User, Card.user_id == User.id)\
- .filter(Card.is_deleted == 0)
- # 搜索
- if keyword:
- query = query.filter(Card.card_code.like(f"%{keyword}%"))
- # 筛选
- if software_code:
- query = query.filter(Card.software_code == software_code)
- if status is not None:
- query = query.filter(Card.status == status)
- # 排序
- query = query.order_by(Card.created_at.desc())
- cards_with_names = query.offset(offset).limit(page_size).all()
- total = query.count()
- return {
- "code": 200,
- "msg": "成功",
- "data": [
- {
- "id": c.id,
- "card_code": c.card_code,
- "software_code": c.software_code,
- "software_name": software_name,
- "days": c.days,
- "discount": float(c.discount),
- "status": c.status,
- "user_id": c.user_id,
- "username": username,
- "activated_at": c.activated_at.isoformat() if c.activated_at else None,
- "expire_time": c.expire_time.isoformat() if c.expire_time else None,
- "created_at": c.created_at.isoformat() if c.created_at else None,
- "updated_at": c.updated_at.isoformat() if c.updated_at else None
- }
- for c, software_name, username in cards_with_names
- ],
- "total": total,
- "page": page,
- "page_size": page_size
- }
- @router.get("/{card_id}")
- async def get_card_detail(
- card_id: int,
- db: Session = Depends(get_db),
- current_admin = Depends(get_current_admin)
- ):
- """获取卡密详情"""
- from app.models.user import User
-
- card = db.query(Card).filter(Card.id == card_id).first()
- if not card:
- return {"code": 404, "msg": "卡密不存在"}
-
- user_info = None
- if card.user_id:
- user = db.query(User).filter(User.id == card.user_id).first()
- if user:
- user_info = {
- "id": user.id,
- "username": user.username,
- "email": user.email,
- "machine_code": user.machine_code,
- }
-
- return {
- "code": 200,
- "msg": "成功",
- "data": {
- "id": card.id,
- "card_code": card.card_code,
- "software_code": card.software_code,
- "days": card.days,
- "discount": float(card.discount),
- "status": card.status,
- "user_id": card.user_id,
- "user_info": user_info,
- "activated_at": card.activated_at.isoformat() if card.activated_at else None,
- "expire_time": card.expire_time.isoformat() if card.expire_time else None,
- "created_at": card.created_at.isoformat() if card.created_at else None,
- "updated_at": card.updated_at.isoformat() if card.updated_at else None
- }
- }
- @router.get("/{card_id}/activate")
- async def get_card_activate_info(
- card_id: int,
- db: Session = Depends(get_db),
- current_admin = Depends(get_current_admin)
- ):
- """获取卡密激活信息"""
- card = db.query(Card).filter(Card.id == card_id).first()
- if not card:
- return {"code": 404, "msg": "卡密不存在"}
-
- return {
- "code": 200,
- "msg": "成功",
- "data": {
- "card_code": card.card_code,
- "user_id": card.user_id,
- "activated_at": card.activated_at.isoformat() if card.activated_at else None,
- "expire_time": card.expire_time.isoformat() if card.expire_time else None
- }
- }
- @router.get("/export/excel")
- async def export_cards_excel(
- request: Request,
- keyword: str = Query(None, description="搜索关键词"),
- software_code: str = Query(None, description="软件特征码筛选"),
- status: int = Query(None, description="状态筛选"),
- db: Session = Depends(get_db),
- current_admin = Depends(get_current_admin)
- ):
- """导出卡密为Excel"""
- from app.models.software import Software
- from app.models.user import User
- # 应用相同的筛选条件构建查询,关联软件表和用户表
- query = db.query(Card, Software.name.label('software_name'), User.username.label('username'))\
- .join(Software, Card.software_code == Software.code)\
- .outerjoin(User, Card.user_id == User.id)\
- .filter(Card.is_deleted == 0)
- # 搜索
- if keyword:
- query = query.filter(Card.card_code.like(f"%{keyword}%"))
- # 筛选
- if software_code:
- query = query.filter(Card.software_code == software_code)
- if status is not None:
- query = query.filter(Card.status == status)
- cards_with_names = query.order_by(Card.created_at.desc()).all()
- # 记录操作日志
- from app.utils.history import create_operation_log
- client_ip = request.client.host if request.client else None
- create_operation_log(
- db=db,
- admin_id=current_admin.id,
- module="卡密管理",
- action="导出卡密Excel",
- description=f"导出卡密数据为Excel,共导出{len(cards_with_names)}条卡密记录",
- new_data={
- "export_count": len(cards_with_names),
- "export_format": "Excel",
- "filters": {
- "keyword": keyword,
- "software_code": software_code,
- "status": status
- }
- },
- ip_address=client_ip
- )
- db.commit()
- # 生成CSV格式(临时方案,后续可以升级为真正的Excel格式)
- output = io.StringIO()
- writer = csv.writer(output)
- writer.writerow([
- 'ID', '卡密代码', '软件特征码', '软件名称', '天数', '折扣', '状态',
- '激活用户ID', '用户名称', '激活时间', '到期时间', '创建时间', '更新时间'
- ])
- status_map = {0: '未激活', 1: '已激活', 2: '已过期'}
- for card, software_name, username in cards_with_names:
- writer.writerow([
- card.id,
- card.card_code,
- card.software_code,
- software_name,
- card.days,
- float(card.discount),
- status_map.get(card.status, '未知'),
- card.user_id or '',
- username or '',
- card.activated_at.isoformat() if card.activated_at else '',
- card.expire_time.isoformat() if card.expire_time else '',
- card.created_at.isoformat() if card.created_at else '',
- card.updated_at.isoformat() if card.updated_at else ''
- ])
- output.seek(0)
- return StreamingResponse(
- iter([output.getvalue()]),
- media_type="text/csv",
- headers={"Content-Disposition": f"attachment; filename=cards_{beijing_now().strftime('%Y%m%d_%H%M%S')}.csv"}
- )
|