✨ feat(vscode): 代码阅读的调用嵌套:实参的值又是跨多行的调用时在值那一格里写它的一行、套它的实参表,一层层往下套;埋在式子里后面还跟着东西的调用括起来
This commit is contained in:
1 parent
dc100692b3
commit
285d711740
16 files changed
+471
-55
No files matched your search
@@ -0,0 +1,87 @@
|
||||
# luffy 的两段节选:workspace_repo/service.py 的 WorkspaceRepoService.__init__、commerce/tasks.py 的 _async_check_pending_refunds
|
||||
import base64
|
||||
|
||||
|
||||
class WorkspaceRepoService:
|
||||
def __init__(
|
||||
self,
|
||||
client: GiteaAdminClient,
|
||||
*,
|
||||
public_url: str,
|
||||
internal_url: str,
|
||||
admin_user: str,
|
||||
admin_password: str,
|
||||
) -> None:
|
||||
self._client = client
|
||||
self._public_url = public_url.rstrip("/")
|
||||
self._internal_url = internal_url.rstrip("/")
|
||||
self._admin_basic = base64.b64encode(
|
||||
f"{admin_user}:{admin_password}".encode()
|
||||
).decode()
|
||||
|
||||
|
||||
|
||||
async def _async_check_pending_refunds() -> None:
|
||||
"""超时未回调的退款 cron 兜底: 主动查 → 调 reversal_hook → 推进 REFUNDED.
|
||||
|
||||
与异步回调 ``handle_alipay_refund_notify`` 共用 ``_finalize_refund``
|
||||
helper, 保证账本侧 reversal 与订单状态机推进始终一对一. 此前仅写
|
||||
``refund_trade_no`` 不调 reversal_hook, 会让"用户钱已退 + 账本 grant
|
||||
仍在"成为双花漏洞.
|
||||
|
||||
金额来源说明: 当前 ``RefundQueryResult`` 不带 alipay 实际退款金额, cron
|
||||
无法判断 unused 模式下的部分退款. 折中策略: 按订单金额 (snapshot) 走
|
||||
``_finalize_refund``, full 模式严格相等会通过, unused 模式
|
||||
``0 < refunded <= rmb_amount_minor`` 也通过. 等 driver 后续暴露
|
||||
``refund_amount_minor`` 字段再切换.
|
||||
"""
|
||||
from luffy_agent.commerce.config import get_commerce_config
|
||||
from luffy_agent.commerce.exceptions import UnsupportedPaymentMethodError
|
||||
from luffy_agent.commerce.orders.repositories import order_repository
|
||||
from luffy_agent.commerce.payments.service import _finalize_refund
|
||||
|
||||
config = get_commerce_config()
|
||||
factory = get_pg_session_factory()
|
||||
checked = 0
|
||||
confirmed = 0
|
||||
|
||||
async with factory() as db:
|
||||
orders = await order_repository.list_pending_refunds(
|
||||
db, age_hours=24, limit=100
|
||||
)
|
||||
for order in orders:
|
||||
checked += 1
|
||||
try:
|
||||
result = await config.get_payment_driver(
|
||||
order.payment_method
|
||||
).query_refund_status(
|
||||
out_trade_no=str(order.id),
|
||||
out_request_no=f"{order.id}-refund",
|
||||
)
|
||||
except UnsupportedPaymentMethodError:
|
||||
logger.warning(
|
||||
"check_pending_refunds: skip order %s, no driver for %s",
|
||||
order.id,
|
||||
order.payment_method,
|
||||
)
|
||||
continue
|
||||
except Exception as exc: # noqa: BLE001
|
||||
logger.exception(
|
||||
"query_refund_status failed for order %s: %s", order.id, exc
|
||||
)
|
||||
continue
|
||||
|
||||
if not result.refunded:
|
||||
continue
|
||||
|
||||
# 已加 with_for_update(skip_locked) 行锁, 与 refund-notify 互斥.
|
||||
logger.warning("refund confirmed via active query, order=%s", order.id)
|
||||
confirmed += 1
|
||||
await _finalize_refund(
|
||||
db,
|
||||
order=order,
|
||||
refund_trade_no=result.refund_trade_no,
|
||||
refunded_amount_minor=order.rmb_amount_minor,
|
||||
source="check-refund-cron",
|
||||
)
|
||||
logger.info(
|
||||
@@ -0,0 +1,18 @@
|
||||
// 本仓库 vscode/src/extension.ts 的 activate 节选
|
||||
export function activate(context: ExtensionContext): SuperpowersApi {
|
||||
context.subscriptions.push(
|
||||
treeView,
|
||||
commands.registerCommand('superpowers.openKanban', () => {
|
||||
KanbanWebviewPanel.createOrShow(context)
|
||||
}),
|
||||
commands.registerCommand('superpowers.refresh', () => {
|
||||
KanbanWebviewPanel.refresh()
|
||||
}),
|
||||
commands.registerCommand('superpowers.setGiteaToken', () => {
|
||||
KanbanWebviewPanel.createOrShow(context)
|
||||
KanbanWebviewPanel.requestEditAuth()
|
||||
}),
|
||||
)
|
||||
|
||||
return { codeReader: registerCodeReader(context) }
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
"""支付子域管理后台路由.
|
||||
|
||||
仅含管理后台用的支付流水列表/详情. URL prefix 与 tags 与拆分前完全一致
|
||||
(``/admin/commerce/payments`` + ``commerce-admin-payments``), 保证对外契约不变.
|
||||
共享 schema ``OrderResponse`` 与 repository 从父子域绝对 import.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Annotated, Literal
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from luffy_agent.commerce.bootstrap import _secret_text, jsapi_ready
|
||||
from luffy_agent.commerce.orders.console.schemas import (
|
||||
AdminOrderRow,
|
||||
to_admin_order_row,
|
||||
to_admin_order_rows,
|
||||
)
|
||||
from luffy_agent.commerce.orders.repositories import order_repository
|
||||
from luffy_agent.commerce.payments.wechat_jsapi import repositories as jsapi_repo
|
||||
from luffy_agent.commerce.payments.wechat_jsapi import service as jsapi_service
|
||||
from luffy_agent.core.db import get_db
|
||||
from luffy_agent.core.exceptions import AppException
|
||||
from luffy_agent.core.params import Int4Query
|
||||
from luffy_agent.core.security.fernet import FernetSecretService, MasterKeyMissingError
|
||||
from luffy_agent.core.setting import get_settings
|
||||
from luffy_agent.core.setting.schemas import Settings
|
||||
from luffy_agent.user.auth.deps import ConsolePrincipal, require_permission
|
||||
from luffy_agent.user.console.rbac.service import (
|
||||
bind_console_audit_request,
|
||||
console_rbac_service,
|
||||
)
|
||||
|
||||
router = APIRouter(prefix="/admin/commerce/payments", tags=["commerce-admin-payments"])
|
||||
|
||||
|
||||
class CredentialPresence(BaseModel):
|
||||
mp_app_secret: bool
|
||||
wechat_pay_api_v3_key: bool
|
||||
wechat_pay_private_key: bool
|
||||
jsapi_intent_master_key: bool
|
||||
@router.get("/capability", response_model=PaymentCapabilityDiagnostic)
|
||||
async def get_payment_capability(
|
||||
request: Request,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
_: object = Depends(require_permission("commerce.payments:view")),
|
||||
) -> PaymentCapabilityDiagnostic:
|
||||
"""admin: JSAPI/Native/H5 只读诊断. 不含密钥、openid、签名或密文."""
|
||||
settings = get_settings()
|
||||
oauth_host = urlparse(settings.WECHAT_MP_OAUTH_REDIRECT_URI).hostname or ""
|
||||
allowed = list(settings.PAYMENT_RETURN_ALLOWED_ORIGINS)
|
||||
request_origin = request.headers.get("origin")
|
||||
pay_origin = request_origin if request_origin in allowed else None
|
||||
hint_origins = [pay_origin] if pay_origin else allowed
|
||||
private_key_path = settings.WECHAT_PAY_PRIVATE_KEY_PATH
|
||||
count, earliest = await jsapi_repo.recovery_required_summary(db)
|
||||
return PaymentCapabilityDiagnostic(
|
||||
native_enabled=bool(settings.WECHAT_PAY_MCH_ID),
|
||||
h5_enabled=settings.WECHAT_H5_ENABLED,
|
||||
jsapi_enabled=settings.WECHAT_JSAPI_ENABLED,
|
||||
jsapi_ready=jsapi_ready(settings),
|
||||
pay_app_id_masked=_mask_app_id(settings.WECHAT_PAY_APP_ID),
|
||||
mp_app_id_masked=_mask_app_id(settings.WECHAT_MP_APP_ID),
|
||||
credentials_present=CredentialPresence(
|
||||
mp_app_secret=bool(_secret_text(settings.WECHAT_MP_APP_SECRET)),
|
||||
wechat_pay_api_v3_key=bool(settings.WECHAT_PAY_API_V3_KEY),
|
||||
wechat_pay_private_key=bool(private_key_path)
|
||||
and Path(private_key_path).is_file(),
|
||||
jsapi_intent_master_key=_intent_key_present(settings),
|
||||
),
|
||||
oauth_callback_host=oauth_host,
|
||||
pay_origin=pay_origin,
|
||||
allowed_pay_origins=allowed,
|
||||
jsapi_auth_dir_hint=[f"{origin}/" for origin in hint_origins if origin],
|
||||
domain_whitelist="unknown_manual_check",
|
||||
merchant_binding="unknown_manual_check",
|
||||
recovery_required_count=count,
|
||||
recovery_required_earliest=earliest,
|
||||
)
|
||||
Reference in new issue
Block a user