feat(points): 新增积分赠送
This commit is contained in:
@@ -29,4 +29,6 @@ PAY_MCH_ID=
|
|||||||
PAY_API_KEY=
|
PAY_API_KEY=
|
||||||
PAY_NOTIFY_URL=
|
PAY_NOTIFY_URL=
|
||||||
|
|
||||||
|
# 会员配置
|
||||||
USER_MAX_CREATED_CARD_NUM=18
|
USER_MAX_CREATED_CARD_NUM=18
|
||||||
|
UPLOAD_REWARD_POINTS_GIFT_ID=-1
|
||||||
69
alembic/versions/4a97e1258bbb_feat_points.py
Normal file
69
alembic/versions/4a97e1258bbb_feat_points.py
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
"""feat points
|
||||||
|
|
||||||
|
Revision ID: 4a97e1258bbb
|
||||||
|
Revises: e2a204afe670
|
||||||
|
Create Date: 2025-05-07 20:08:47.923509
|
||||||
|
|
||||||
|
"""
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy.dialects import mysql
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = '4a97e1258bbb'
|
||||||
|
down_revision: Union[str, None] = 'e2a204afe670'
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.create_table('system_points_transaction',
|
||||||
|
sa.Column('user_id', sa.Integer(), nullable=False, comment='用户id'),
|
||||||
|
sa.Column('transaction_type', sa.Integer(), nullable=False, comment='交易类型:1-充值 2-消费 3-赠送 4-过期 5-系统调整'),
|
||||||
|
sa.Column('product_id', sa.Integer(), nullable=True, comment='关联产品id'),
|
||||||
|
sa.Column('points', sa.Integer(), nullable=False, comment='积分数量(正数表示增加,负数表示减少)'),
|
||||||
|
sa.Column('balance', sa.Integer(), nullable=False, comment='交易后余额'),
|
||||||
|
sa.Column('status', sa.Integer(), nullable=False, comment='状态:0-无效 1-有效 2-已撤销'),
|
||||||
|
sa.Column('description', sa.String(length=255), nullable=True, comment='交易描述'),
|
||||||
|
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False, comment='自增id'),
|
||||||
|
sa.Column('created_at', sa.DateTime(), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=False, comment='创建时间'),
|
||||||
|
sa.Column('updated_at', sa.DateTime(), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=False, comment='更新时间'),
|
||||||
|
sa.PrimaryKeyConstraint('id'),
|
||||||
|
comment='积分消费记录表'
|
||||||
|
)
|
||||||
|
op.add_column('system_orders', sa.Column('ip_address', sa.String(length=255), nullable=True, comment='操作ip'))
|
||||||
|
op.add_column('system_products', sa.Column('gift_id', sa.Integer(), nullable=True, comment='礼物产品id'))
|
||||||
|
op.alter_column('system_products', 'category',
|
||||||
|
existing_type=mysql.ENUM('VIP', 'POINTS', 'GIFT'),
|
||||||
|
comment='商品分类, vip-会员服务 points-积分服务 gift-礼物',
|
||||||
|
existing_comment='商品分类, vip-会员服务 points-积分服务',
|
||||||
|
existing_nullable=False)
|
||||||
|
op.alter_column('system_products', 'action',
|
||||||
|
existing_type=mysql.VARCHAR(length=30),
|
||||||
|
type_=sa.Enum('VIP_RENEWAL', 'RECHARGE_POINTS', 'FREE_GIFT', name='productaction', inherit_schema=True),
|
||||||
|
comment='付款后的动作,会员续期:vip_renewal 积分:recharge_points 赠送产品:free_gift',
|
||||||
|
existing_comment='付款后的动作,会员续期:vip_renewal 积分:recharge_points',
|
||||||
|
existing_nullable=False)
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.alter_column('system_products', 'action',
|
||||||
|
existing_type=sa.Enum('VIP_RENEWAL', 'RECHARGE_POINTS', 'FREE_GIFT', name='productaction', inherit_schema=True),
|
||||||
|
type_=mysql.VARCHAR(length=30),
|
||||||
|
comment='付款后的动作,会员续期:vip_renewal 积分:recharge_points',
|
||||||
|
existing_comment='付款后的动作,会员续期:vip_renewal 积分:recharge_points 赠送产品:free_gift',
|
||||||
|
existing_nullable=False)
|
||||||
|
op.alter_column('system_products', 'category',
|
||||||
|
existing_type=mysql.ENUM('VIP', 'POINTS', 'GIFT'),
|
||||||
|
comment='商品分类, vip-会员服务 points-积分服务',
|
||||||
|
existing_comment='商品分类, vip-会员服务 points-积分服务 gift-礼物',
|
||||||
|
existing_nullable=False)
|
||||||
|
op.drop_column('system_products', 'gift_id')
|
||||||
|
op.drop_column('system_orders', 'ip_address')
|
||||||
|
op.drop_table('system_points_transaction')
|
||||||
|
# ### end Alembic commands ###
|
||||||
@@ -68,6 +68,7 @@ class Settings(BaseSettings):
|
|||||||
|
|
||||||
# 会员配置
|
# 会员配置
|
||||||
USER_MAX_CREATED_CARD_NUM: int = 18
|
USER_MAX_CREATED_CARD_NUM: int = 18
|
||||||
|
UPLOAD_REWARD_POINTS_GIFT_ID: int = -1
|
||||||
|
|
||||||
model_config = SettingsConfigDict(env_file=f'.env.{ENVIRONMENT}', env_file_encoding='utf-8')
|
model_config = SettingsConfigDict(env_file=f'.env.{ENVIRONMENT}', env_file_encoding='utf-8')
|
||||||
|
|
||||||
|
|||||||
@@ -61,11 +61,13 @@ class CardCategoryType(enum.Enum):
|
|||||||
class ProductCategory(enum.Enum):
|
class ProductCategory(enum.Enum):
|
||||||
VIP = 'vip'
|
VIP = 'vip'
|
||||||
POINTS = 'points'
|
POINTS = 'points'
|
||||||
|
GIFT = 'gift'
|
||||||
|
|
||||||
|
|
||||||
class ProductAction(enum.Enum):
|
class ProductAction(enum.Enum):
|
||||||
VIP_RENEWAL = 'vip_renewal'
|
VIP_RENEWAL = 'vip_renewal'
|
||||||
RECHARGE_POINTS = 'recharge_points'
|
RECHARGE_POINTS = 'recharge_points'
|
||||||
|
FREE_GIFT = 'free_gift'
|
||||||
|
|
||||||
|
|
||||||
class OrderStatusType(enum.Enum):
|
class OrderStatusType(enum.Enum):
|
||||||
|
|||||||
@@ -148,11 +148,11 @@ class Card(BaseModel, DateModel):
|
|||||||
create_user_id: Mapped[int] = mapped_column(Integer, nullable=True, comment='创建者')
|
create_user_id: Mapped[int] = mapped_column(Integer, nullable=True, comment='创建者')
|
||||||
perfect_user_id: Mapped[int] = mapped_column(Integer, nullable=True, comment='完善者')
|
perfect_user_id: Mapped[int] = mapped_column(Integer, nullable=True, comment='完善者')
|
||||||
|
|
||||||
category: Mapped[str] = mapped_column(
|
category: Mapped[base_schema.CardCategoryType] = mapped_column(
|
||||||
Enum(base_schema.CardCategoryType, inherit_schema=True),
|
Enum(base_schema.CardCategoryType, inherit_schema=True),
|
||||||
nullable=True, server_default=base_schema.CardCategoryType.OTHER.value, comment='卡片分类'
|
nullable=True, server_default=base_schema.CardCategoryType.OTHER.value, comment='卡片分类'
|
||||||
)
|
)
|
||||||
source: Mapped[str] = mapped_column(
|
source: Mapped[base_schema.PlatformSourceType] = mapped_column(
|
||||||
Enum(base_schema.PlatformSourceType, inherit_schema=True),
|
Enum(base_schema.PlatformSourceType, inherit_schema=True),
|
||||||
comment='来源,PSA、BGS、CGC、GBTC、CCG、BCTC、CIC :采集 、OTHER:自定义'
|
comment='来源,PSA、BGS、CGC、GBTC、CCG、BCTC、CIC :采集 、OTHER:自定义'
|
||||||
)
|
)
|
||||||
@@ -205,7 +205,7 @@ class SearchCardCache(BaseModel, DateModel):
|
|||||||
cert_number: Mapped[str] = mapped_column(String(50), nullable=False, comment='评级卡号')
|
cert_number: Mapped[str] = mapped_column(String(50), nullable=False, comment='评级卡号')
|
||||||
data: Mapped[dict] = mapped_column(JSON, comment='标准数据')
|
data: Mapped[dict] = mapped_column(JSON, comment='标准数据')
|
||||||
raw_data: Mapped[dict] = mapped_column(JSON, comment='原始数据')
|
raw_data: Mapped[dict] = mapped_column(JSON, comment='原始数据')
|
||||||
source: Mapped[str] = mapped_column(
|
source: Mapped[base_schema.SupportPlatformType] = mapped_column(
|
||||||
Enum(base_schema.SupportPlatformType, inherit_schema=True),
|
Enum(base_schema.SupportPlatformType, inherit_schema=True),
|
||||||
comment='数据来源:PSA、BGS、CGC、GBTC、CCG、BCTC、CIC'
|
comment='数据来源:PSA、BGS、CGC、GBTC、CCG、BCTC、CIC'
|
||||||
)
|
)
|
||||||
@@ -216,12 +216,17 @@ class Product(BaseModel, DateModel):
|
|||||||
__table_args__ = {'comment': '产品信息表'}
|
__table_args__ = {'comment': '产品信息表'}
|
||||||
|
|
||||||
name: Mapped[str] = mapped_column(String(20), comment='产品名')
|
name: Mapped[str] = mapped_column(String(20), comment='产品名')
|
||||||
category: Mapped[str] = mapped_column(
|
category: Mapped[base_schema.ProductCategory] = mapped_column(
|
||||||
Enum(base_schema.ProductCategory, inherit_schema=True), comment='商品分类, vip-会员服务 points-积分服务'
|
Enum(base_schema.ProductCategory, inherit_schema=True),
|
||||||
|
comment='商品分类, vip-会员服务 points-积分服务 gift-礼物'
|
||||||
)
|
)
|
||||||
price: Mapped[int] = mapped_column(Integer, comment='价格,不带小数点 单位为分')
|
price: Mapped[int] = mapped_column(Integer, comment='价格,不带小数点 单位为分')
|
||||||
action: Mapped[str] = mapped_column(String(30), comment='付款后的动作,会员续期:vip_renewal 积分:recharge_points')
|
action: Mapped[base_schema.ProductAction] = mapped_column(
|
||||||
|
Enum(base_schema.ProductAction, inherit_schema=True),
|
||||||
|
comment='付款后的动作,会员续期:vip_renewal 积分:recharge_points 赠送产品:free_gift'
|
||||||
|
)
|
||||||
number: Mapped[int] = mapped_column(Integer, comment='执行动作的增量值')
|
number: Mapped[int] = mapped_column(Integer, comment='执行动作的增量值')
|
||||||
|
gift_id: Mapped[int] = mapped_column(Integer, nullable=True, comment='礼物产品id')
|
||||||
description: Mapped[str] = mapped_column(Text, nullable=True, comment='商品描述')
|
description: Mapped[str] = mapped_column(Text, nullable=True, comment='商品描述')
|
||||||
|
|
||||||
|
|
||||||
@@ -244,6 +249,7 @@ class Order(BaseModel, DateModel):
|
|||||||
server_default=base_schema.OrderStatusType.INIT.value,
|
server_default=base_schema.OrderStatusType.INIT.value,
|
||||||
comment='订单状态,init:初始化 fail:失败 ok:完成 wait payment:待支付 cancel. 取消'
|
comment='订单状态,init:初始化 fail:失败 ok:完成 wait payment:待支付 cancel. 取消'
|
||||||
)
|
)
|
||||||
|
ip_address: Mapped[str] = mapped_column(String(255), nullable=True, comment='操作ip')
|
||||||
|
|
||||||
|
|
||||||
class Chat(BaseModel, DateModel):
|
class Chat(BaseModel, DateModel):
|
||||||
@@ -305,6 +311,22 @@ class ReadStatus(BaseModel, DateModel):
|
|||||||
return f'User: {self.user_id}, Message: {self.last_read_message_id}'
|
return f'User: {self.user_id}, Message: {self.last_read_message_id}'
|
||||||
|
|
||||||
|
|
||||||
|
class PointsTransaction(BaseModel, DateModel):
|
||||||
|
__tablename__ = 'system_points_transaction'
|
||||||
|
__table_args__ = (
|
||||||
|
{'comment': '积分消费记录表'}
|
||||||
|
)
|
||||||
|
|
||||||
|
user_id: Mapped[int] = mapped_column(Integer, comment='用户id')
|
||||||
|
transaction_type: Mapped[int] = mapped_column(Integer, comment='交易类型:1-充值 2-消费 3-赠送 4-过期 5-系统调整')
|
||||||
|
product_id: Mapped[int] = mapped_column(Integer, nullable=True, comment='关联产品id')
|
||||||
|
points : Mapped[int] = mapped_column(Integer, comment='积分数量(正数表示增加,负数表示减少)')
|
||||||
|
balance : Mapped[int] = mapped_column(Integer, comment='交易后余额')
|
||||||
|
status : Mapped[int] = mapped_column(Integer, default=1, comment='状态:0-无效 1-有效 2-已撤销')
|
||||||
|
description: Mapped[str] = mapped_column(String(255), nullable=True, comment='交易描述')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
chat_participant = Table(
|
chat_participant = Table(
|
||||||
'chat_participant',
|
'chat_participant',
|
||||||
BaseModel.metadata,
|
BaseModel.metadata,
|
||||||
|
|||||||
39
core/routers/card/actions.py
Normal file
39
core/routers/card/actions.py
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# @Project : X25020501_card_book
|
||||||
|
# @File : actions.py.py
|
||||||
|
# @Date : 2025/5/7 20:38
|
||||||
|
# @Software : PyCharm
|
||||||
|
# @Author : xingc
|
||||||
|
# @Desc :
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from core.base.schema import ProductCategory
|
||||||
|
from core.db.crud import CRUD
|
||||||
|
from core.db.models import User, Product
|
||||||
|
from core.routers.auth.schema import LoginUser
|
||||||
|
from core.routers.pay.actions import actions_handler
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
async def reward_points_after_uploading_pictures(
|
||||||
|
db_crud: CRUD,
|
||||||
|
gift_product_id: int,
|
||||||
|
login_user: LoginUser
|
||||||
|
):
|
||||||
|
product: Product = await db_crud.get(
|
||||||
|
model=Product, filters={
|
||||||
|
'id': gift_product_id,
|
||||||
|
'category': ProductCategory.GIFT
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if not product:
|
||||||
|
logger.warning(f'Product not found -> {gift_product_id}')
|
||||||
|
return
|
||||||
|
|
||||||
|
user: User = await db_crud.get(model=User, filters={'user_id': login_user.user_id})
|
||||||
|
|
||||||
|
try:
|
||||||
|
await actions_handler(db_crud, product, user)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f'Reward Points After Uploading Pictures -> {e}')
|
||||||
@@ -23,6 +23,7 @@ from core.routers.auth.services import get_current_user_form_http
|
|||||||
from core.utils.helper import generate_keyname, check_picture_for_empty
|
from core.utils.helper import generate_keyname, check_picture_for_empty
|
||||||
|
|
||||||
from . import schema
|
from . import schema
|
||||||
|
from .actions import reward_points_after_uploading_pictures
|
||||||
from .scraper import card_scraper
|
from .scraper import card_scraper
|
||||||
from .services import check_user_card_created_permission
|
from .services import check_user_card_created_permission
|
||||||
|
|
||||||
@@ -190,17 +191,18 @@ async def created_card(
|
|||||||
login_user: LoginUser = Depends(get_current_user_form_http),
|
login_user: LoginUser = Depends(get_current_user_form_http),
|
||||||
db_session: AsyncSession = Depends(get_async_session)
|
db_session: AsyncSession = Depends(get_async_session)
|
||||||
):
|
):
|
||||||
card_crud = CRUD(db_session, Card)
|
db_crud = CRUD(db_session, Card)
|
||||||
# 确认用户存卡权限
|
# 确认用户存卡权限
|
||||||
if await check_user_card_created_permission(
|
if await check_user_card_created_permission(
|
||||||
db_session, login_user=login_user
|
db_session, login_user=login_user
|
||||||
) is False:
|
) is False:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_400_BAD_REQUEST, detail=f'非会员仅支持存入{settings.USER_MAX_CREATED_CARD_NUM}张卡片'
|
status_code=status.HTTP_400_BAD_REQUEST,
|
||||||
|
detail=f'非会员仅支持存入{settings.USER_MAX_CREATED_CARD_NUM}张卡片'
|
||||||
)
|
)
|
||||||
|
|
||||||
# 卡片库查询
|
# 卡片库查询
|
||||||
card: Card = await card_crud.get(filters={'cert_number': payload.cert_number})
|
card: Card = await db_crud.get(filters={'cert_number': payload.cert_number})
|
||||||
if payload.source.value != 'other':
|
if payload.source.value != 'other':
|
||||||
if card is None:
|
if card is None:
|
||||||
# 卡片缓存库转正至卡片库, 重新查询保障数据准确性
|
# 卡片缓存库转正至卡片库, 重新查询保障数据准确性
|
||||||
@@ -208,7 +210,7 @@ async def created_card(
|
|||||||
cache_card = await cache_card_crud.get(
|
cache_card = await cache_card_crud.get(
|
||||||
filters={'cert_number': payload.cert_number, 'source': payload.source.value}
|
filters={'cert_number': payload.cert_number, 'source': payload.source.value}
|
||||||
)
|
)
|
||||||
card = await card_crud.create(
|
card = await db_crud.create(
|
||||||
data={
|
data={
|
||||||
**cache_card.data,
|
**cache_card.data,
|
||||||
'category': payload.category,
|
'category': payload.category,
|
||||||
@@ -232,17 +234,23 @@ async def created_card(
|
|||||||
if card_picture.get(key) is None:
|
if card_picture.get(key) is None:
|
||||||
card_picture[key] = value
|
card_picture[key] = value
|
||||||
|
|
||||||
await card_crud.update(
|
await db_crud.update(
|
||||||
data={
|
data={
|
||||||
'card_picture': card_picture,
|
'card_picture': card_picture,
|
||||||
'perfect_user_id': login_user.user_id,
|
'perfect_user_id': login_user.user_id,
|
||||||
},
|
},
|
||||||
filters={'id': card.id}
|
filters={'id': card.id}
|
||||||
)
|
)
|
||||||
|
# 上传图片奖励
|
||||||
|
if payload.reward and settings.UPLOAD_REWARD_POINTS_GIFT_ID > 0:
|
||||||
|
await reward_points_after_uploading_pictures(
|
||||||
|
db_crud, gift_product_id=settings.UPLOAD_REWARD_POINTS_GIFT_ID, login_user=login_user
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if card is None:
|
if card is None:
|
||||||
card: Card = await card_crud.create(
|
card: Card = await db_crud.create(
|
||||||
data={
|
data={
|
||||||
**payload.dict(exclude_none=True, exclude={'reward'}),
|
**payload.dict(exclude_none=True, exclude={'reward'}),
|
||||||
'create_user_id': login_user.user_id,
|
'create_user_id': login_user.user_id,
|
||||||
@@ -251,12 +259,12 @@ async def created_card(
|
|||||||
filters={'cert_number': payload.cert_number, 'source': payload.source.value}
|
filters={'cert_number': payload.cert_number, 'source': payload.source.value}
|
||||||
)
|
)
|
||||||
|
|
||||||
collection_crud = CRUD(db_session, UserCollection)
|
|
||||||
record = {
|
record = {
|
||||||
'user_id': login_user.user_id,
|
'user_id': login_user.user_id,
|
||||||
'card_id': card.id
|
'card_id': card.id
|
||||||
}
|
}
|
||||||
result = await collection_crud.create(
|
await db_crud.create(
|
||||||
|
model=UserCollection,
|
||||||
data=record,
|
data=record,
|
||||||
filters=record
|
filters=record
|
||||||
)
|
)
|
||||||
@@ -299,7 +307,8 @@ async def display_card_status(
|
|||||||
db_session, login_user=login_user, other_conditions={'displayed': True}
|
db_session, login_user=login_user, other_conditions={'displayed': True}
|
||||||
) is False and action == 'displayed':
|
) is False and action == 'displayed':
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_400_BAD_REQUEST, detail=f'非会员仅支持展示{settings.USER_MAX_CREATED_CARD_NUM}张卡片'
|
status_code=status.HTTP_400_BAD_REQUEST,
|
||||||
|
detail=f'非会员仅支持展示{settings.USER_MAX_CREATED_CARD_NUM}张卡片'
|
||||||
)
|
)
|
||||||
|
|
||||||
db_crud = CRUD(db_session, UserCollection)
|
db_crud = CRUD(db_session, UserCollection)
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
|||||||
from core.base.schema import ProductCategory
|
from core.base.schema import ProductCategory
|
||||||
from core.db.crud import CRUD
|
from core.db.crud import CRUD
|
||||||
from core.db.engine import get_async_session
|
from core.db.engine import get_async_session
|
||||||
from core.db.models import User, Order, Product
|
from core.db.models import User, Order, Product, PointsTransaction
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ async def actions_after_payment(
|
|||||||
logger.error(f'Order not found -> {order_id}')
|
logger.error(f'Order not found -> {order_id}')
|
||||||
return
|
return
|
||||||
|
|
||||||
product: Product = await db_crud.get(model=Product, filters={'product_id': order.product_id})
|
product: Product = await db_crud.get(model=Product, filters={'id': order.product_id})
|
||||||
if not product:
|
if not product:
|
||||||
logger.warning(f'Product not found -> {order.product_id}')
|
logger.warning(f'Product not found -> {order.product_id}')
|
||||||
return
|
return
|
||||||
@@ -40,6 +40,22 @@ async def actions_after_payment(
|
|||||||
logger.warning(f'User not found -> {order.order_id}')
|
logger.warning(f'User not found -> {order.order_id}')
|
||||||
return
|
return
|
||||||
|
|
||||||
|
await actions_handler(db_crud, product, user)
|
||||||
|
if product.gift_id:
|
||||||
|
gift_product = await db_crud.get(
|
||||||
|
model=Product, filters={
|
||||||
|
'id': product.gift_id,
|
||||||
|
'category': ProductCategory.GIFT
|
||||||
|
}
|
||||||
|
)
|
||||||
|
await actions_handler(db_crud, gift_product, user)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f'Payment Actions Exception occurred -> {e}')
|
||||||
|
logger.exception(e)
|
||||||
|
|
||||||
|
|
||||||
|
async def actions_handler(db_crud: CRUD, product: Product, user: User):
|
||||||
if product.action == ProductCategory.VIP:
|
if product.action == ProductCategory.VIP:
|
||||||
vip_expire_date = (
|
vip_expire_date = (
|
||||||
user.vip_expire_date or datetime.now()
|
user.vip_expire_date or datetime.now()
|
||||||
@@ -49,7 +65,7 @@ async def actions_after_payment(
|
|||||||
data={
|
data={
|
||||||
'vip_expire_date': vip_expire_date.date()
|
'vip_expire_date': vip_expire_date.date()
|
||||||
},
|
},
|
||||||
filters={'user_id': order.user_id}
|
filters={'user_id': user.user_id}
|
||||||
)
|
)
|
||||||
elif product.action == ProductCategory.POINTS:
|
elif product.action == ProductCategory.POINTS:
|
||||||
points = (
|
points = (
|
||||||
@@ -60,10 +76,26 @@ async def actions_after_payment(
|
|||||||
data={
|
data={
|
||||||
'points': points
|
'points': points
|
||||||
},
|
},
|
||||||
filters={'user_id': order.user_id}
|
filters={'user_id': user.user_id}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if product.category.value == 'gift':
|
||||||
|
transaction_type = 3
|
||||||
|
else:
|
||||||
|
transaction_type = 1
|
||||||
|
|
||||||
|
await db_crud.update(
|
||||||
|
model=PointsTransaction,
|
||||||
|
data={
|
||||||
|
'user_id': user.user_id,
|
||||||
|
'transaction_type': transaction_type,
|
||||||
|
'points': product.number,
|
||||||
|
'product_id': product.id,
|
||||||
|
'balance': points,
|
||||||
|
'description': f'{product.name} - {product.number}'
|
||||||
|
},
|
||||||
|
filters={'id': -1}
|
||||||
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
logger.error(f'Unknown action -> {product.action}')
|
logger.error(f'Unknown action -> {product.action}')
|
||||||
except Exception as e:
|
|
||||||
logger.error(f'Payment Actions Exception occurred -> {e}')
|
|
||||||
logger.exception(e)
|
|
||||||
@@ -14,7 +14,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
|||||||
from core.base import schema as base_schema
|
from core.base import schema as base_schema
|
||||||
from core.db.crud import CRUD
|
from core.db.crud import CRUD
|
||||||
from core.db.engine import get_async_session
|
from core.db.engine import get_async_session
|
||||||
from core.db.models import Order
|
from core.db.models import Order, Product
|
||||||
from core.routers.auth.schema import LoginUser
|
from core.routers.auth.schema import LoginUser
|
||||||
from core.routers.auth.services import get_current_user_form_http
|
from core.routers.auth.services import get_current_user_form_http
|
||||||
from core.utils.helper import generate_keyname, generate_order_number
|
from core.utils.helper import generate_keyname, generate_order_number
|
||||||
@@ -55,6 +55,14 @@ async def create_order(
|
|||||||
login_user: LoginUser = Depends(get_current_user_form_http),
|
login_user: LoginUser = Depends(get_current_user_form_http),
|
||||||
db_session: AsyncSession = Depends(get_async_session),
|
db_session: AsyncSession = Depends(get_async_session),
|
||||||
):
|
):
|
||||||
|
db_crud = CRUD(db_session, Order)
|
||||||
|
product = await db_crud.get(model=Product, filters={'id': payload.product_id})
|
||||||
|
if product is None:
|
||||||
|
raise HTTPException(status.HTTP_404_NOT_FOUND, detail='产品不存在')
|
||||||
|
|
||||||
|
if product.number != payload.total_fee:
|
||||||
|
raise HTTPException(status.HTTP_400_BAD_REQUEST, detail='产品费用不合法')
|
||||||
|
|
||||||
client_ip = (
|
client_ip = (
|
||||||
request.headers.get('X-Real-IP') or
|
request.headers.get('X-Real-IP') or
|
||||||
request.headers.get('x-real-ip') or
|
request.headers.get('x-real-ip') or
|
||||||
@@ -67,7 +75,7 @@ async def create_order(
|
|||||||
create_ip=client_ip
|
create_ip=client_ip
|
||||||
)
|
)
|
||||||
payment_params = wechat_pay_api.generate_payment_params(prepay_id)
|
payment_params = wechat_pay_api.generate_payment_params(prepay_id)
|
||||||
db_crud = CRUD(db_session, Order)
|
|
||||||
order: Order = await db_crud.create(
|
order: Order = await db_crud.create(
|
||||||
data={
|
data={
|
||||||
'order_id': out_trade_no,
|
'order_id': out_trade_no,
|
||||||
@@ -112,7 +120,7 @@ async def wechat_notify(
|
|||||||
# 处理支付结果
|
# 处理支付结果
|
||||||
db_crud = CRUD(db_session, Order)
|
db_crud = CRUD(db_session, Order)
|
||||||
record = {
|
record = {
|
||||||
'out_trade_no': params['out_trade_no'],
|
'order_id': params['out_trade_no'],
|
||||||
'transaction_id': params['transaction_id']
|
'transaction_id': params['transaction_id']
|
||||||
}
|
}
|
||||||
if params['return_code'] == 'SUCCESS' and params['result_code'] == 'SUCCESS':
|
if params['return_code'] == 'SUCCESS' and params['result_code'] == 'SUCCESS':
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ class PaymentParamsOut(BaseModel):
|
|||||||
timeStamp: str
|
timeStamp: str
|
||||||
nonceStr: str
|
nonceStr: str
|
||||||
package: str
|
package: str
|
||||||
package: str
|
paySign: str
|
||||||
signType: str
|
signType: str
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import logging
|
|||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends
|
from fastapi import APIRouter, Depends
|
||||||
|
from sqlalchemy import select
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
from core.base import schema as base_schema
|
from core.base import schema as base_schema
|
||||||
@@ -38,6 +39,24 @@ async def get_products(
|
|||||||
):
|
):
|
||||||
db_crud = CRUD(db_session, Product)
|
db_crud = CRUD(db_session, Product)
|
||||||
products = await db_crud.get_all(filters={'category': category})
|
products = await db_crud.get_all(filters={'category': category})
|
||||||
|
gift_product_ids = set()
|
||||||
|
for product in products:
|
||||||
|
if product.gift_id:
|
||||||
|
gift_product_ids.add(product.gift_id)
|
||||||
|
|
||||||
|
gift_products = {
|
||||||
|
p.id: p
|
||||||
|
for p in
|
||||||
|
await db_crud.get_all(
|
||||||
|
filters={
|
||||||
|
'category': base_schema.ProductCategory.GIFT,
|
||||||
|
'id__in': gift_product_ids
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
for product in products:
|
||||||
|
product.gift_product = gift_products.get(product.gift_id)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'data': products
|
'data': products
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,10 +10,14 @@ from pydantic import BaseModel
|
|||||||
from core.base.schema import ProductCategory
|
from core.base.schema import ProductCategory
|
||||||
|
|
||||||
|
|
||||||
class ProductOut(BaseModel):
|
class Product(BaseModel):
|
||||||
id: int
|
id: int
|
||||||
name: str
|
name: str
|
||||||
category: ProductCategory
|
category: ProductCategory
|
||||||
price: int
|
price: int
|
||||||
number: int
|
number: int
|
||||||
description: str | None
|
description: str | None
|
||||||
|
|
||||||
|
|
||||||
|
class ProductOut(Product):
|
||||||
|
gift_product: Product | None
|
||||||
|
|||||||
Reference in New Issue
Block a user