|
|
@@ -722,6 +722,161 @@ class QuotaService:
|
|
|
out_schema=IssueBatchListOutSchema,
|
|
|
)
|
|
|
|
|
|
+ @classmethod
|
|
|
+ async def list_employee_quota_records_service(
|
|
|
+ cls,
|
|
|
+ auth: AuthSchema,
|
|
|
+ employee_id: str,
|
|
|
+ institution_id: str | None = None,
|
|
|
+ ) -> list[dict]:
|
|
|
+ """查询员工的额度记录列表"""
|
|
|
+ from app.plugin.module_payment.expense.quota.model import QuotaModel
|
|
|
+ from sqlalchemy import select
|
|
|
+
|
|
|
+ where = [QuotaModel.employee_id == employee_id]
|
|
|
+ if institution_id:
|
|
|
+ where.append(QuotaModel.institution_id == institution_id)
|
|
|
+
|
|
|
+ stmt = select(QuotaModel).where(*where).order_by(QuotaModel.created_time.desc())
|
|
|
+ result = await auth.db.execute(stmt)
|
|
|
+ quotas = result.scalars().all()
|
|
|
+ return [
|
|
|
+ {
|
|
|
+ "quota_id": q.quota_id,
|
|
|
+ "out_biz_no": q.out_biz_no,
|
|
|
+ "total_amount": float(q.total_amount) if q.total_amount else 0,
|
|
|
+ "available_amount": float(q.available_amount) if q.available_amount else 0,
|
|
|
+ "quota_type": q.quota_type,
|
|
|
+ "status": q.status,
|
|
|
+ "valid_from": q.valid_from,
|
|
|
+ "valid_to": q.valid_to,
|
|
|
+ "created_time": q.created_time,
|
|
|
+ "institution_id": q.institution_id,
|
|
|
+ }
|
|
|
+ for q in quotas
|
|
|
+ ]
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ async def adjust_quota_service(
|
|
|
+ cls, auth: AuthSchema, data: AdjustQuotaSchema
|
|
|
+ ) -> dict:
|
|
|
+ """调整额度金额 (调Alipay quota.modify + 记录变更日志)"""
|
|
|
+ from .crud import QuotaChangeLogCRUD
|
|
|
+ from .model import QuotaModel
|
|
|
+ from sqlalchemy import select, update as sa_update
|
|
|
+
|
|
|
+ # 查询当前额度记录
|
|
|
+ stmt = select(QuotaModel).where(QuotaModel.quota_id == data.quota_id)
|
|
|
+ result = await auth.db.execute(stmt)
|
|
|
+ quota = result.scalar_one_or_none()
|
|
|
+ if not quota:
|
|
|
+ raise CustomException(msg="额度记录不存在")
|
|
|
+
|
|
|
+ current_available = float(quota.available_amount) if quota.available_amount else 0
|
|
|
+ diff = round(data.amount - current_available, 2)
|
|
|
+ outer_source_id = str(get_snowflake_id())
|
|
|
+
|
|
|
+ # 调Alipay quota.modify
|
|
|
+ try:
|
|
|
+ from alipay.aop.api.request.AlipayEbppInvoiceExpensecontrolQuotaModifyRequest import (
|
|
|
+ AlipayEbppInvoiceExpensecontrolQuotaModifyRequest,
|
|
|
+ )
|
|
|
+ from alipay.aop.api.domain.AlipayEbppInvoiceExpensecontrolQuotaModifyModel import (
|
|
|
+ AlipayEbppInvoiceExpensecontrolQuotaModifyModel,
|
|
|
+ )
|
|
|
+ from alipay.aop.api.response.AlipayEbppInvoiceExpensecontrolQuotaModifyResponse import (
|
|
|
+ AlipayEbppInvoiceExpensecontrolQuotaModifyResponse,
|
|
|
+ )
|
|
|
+ except ImportError:
|
|
|
+ raise CustomException(msg="支付宝SDK未正确安装")
|
|
|
+
|
|
|
+ model = AlipayEbppInvoiceExpensecontrolQuotaModifyModel()
|
|
|
+ model.quota_id = data.quota_id
|
|
|
+ model.action = "ADD" if diff >= 0 else "DEDUCT"
|
|
|
+ model.outer_source_id = outer_source_id
|
|
|
+ model.enterprise_id = data.enterprise_id
|
|
|
+ model.amount = str(abs(diff))
|
|
|
+
|
|
|
+ request = AlipayEbppInvoiceExpensecontrolQuotaModifyRequest()
|
|
|
+ request.biz_model = model
|
|
|
+
|
|
|
+ client = AlipayClient.get_client()
|
|
|
+ response = client.execute(request)
|
|
|
+
|
|
|
+ if not response:
|
|
|
+ raise CustomException(msg="调整额度失败: 无响应")
|
|
|
+
|
|
|
+ mod_result = AlipayEbppInvoiceExpensecontrolQuotaModifyResponse()
|
|
|
+ mod_result.parse_response_content(response)
|
|
|
+
|
|
|
+ if not mod_result.is_success():
|
|
|
+ sub_msg = getattr(mod_result, 'sub_msg', '') or ''
|
|
|
+ log.error(f"支付宝接口调用失败: {mod_result.code} - {mod_result.msg}")
|
|
|
+ raise CustomException(msg=f"调整额度失败: {sub_msg or mod_result.msg}")
|
|
|
+
|
|
|
+ # 更新本地额度记录
|
|
|
+ new_available = data.amount
|
|
|
+ new_total = float(quota.total_amount) if quota.total_amount else 0
|
|
|
+ if diff > 0:
|
|
|
+ new_total += diff
|
|
|
+ else:
|
|
|
+ new_total = max(0, new_total + diff)
|
|
|
+
|
|
|
+ upd = sa_update(QuotaModel).where(
|
|
|
+ QuotaModel.quota_id == data.quota_id
|
|
|
+ ).values(
|
|
|
+ total_amount=new_total,
|
|
|
+ available_amount=new_available,
|
|
|
+ )
|
|
|
+ await auth.db.execute(upd)
|
|
|
+ await auth.db.flush()
|
|
|
+
|
|
|
+ # 记录变更日志
|
|
|
+ try:
|
|
|
+ log_crud = QuotaChangeLogCRUD(auth)
|
|
|
+ await log_crud.create({
|
|
|
+ "quota_id": data.quota_id,
|
|
|
+ "employee_id": quota.employee_id or "",
|
|
|
+ "institution_id": quota.institution_id or "",
|
|
|
+ "change_type": "ADJUST",
|
|
|
+ "coupon_name": quota.out_biz_no or "额度调整",
|
|
|
+ "change_amount": diff,
|
|
|
+ "before_amount": current_available,
|
|
|
+ "after_amount": new_available,
|
|
|
+ "change_desc": data.change_desc or "",
|
|
|
+ "enterprise_id": data.enterprise_id,
|
|
|
+ "tenant_id": auth.user.tenant_id if auth.user else 1,
|
|
|
+ })
|
|
|
+ except Exception as e:
|
|
|
+ log.warning(f"记录变更日志失败(不影响调整): {e}")
|
|
|
+
|
|
|
+ return {
|
|
|
+ "quota_id": data.quota_id,
|
|
|
+ "before_amount": current_available,
|
|
|
+ "after_amount": new_available,
|
|
|
+ "diff": diff,
|
|
|
+ }
|
|
|
+
|
|
|
+ @classmethod
|
|
|
+ async def list_quota_changes_service(
|
|
|
+ cls, auth: AuthSchema, quota_id: str
|
|
|
+ ) -> list[dict]:
|
|
|
+ """查询额度的变更记录"""
|
|
|
+ from .crud import QuotaChangeLogCRUD
|
|
|
+
|
|
|
+ crud = QuotaChangeLogCRUD(auth)
|
|
|
+ logs = await crud.list(search={"quota_id": quota_id}, order_by=[{"id": "desc"}])
|
|
|
+ return [
|
|
|
+ {
|
|
|
+ "coupon_name": log.coupon_name,
|
|
|
+ "change_time": log.created_time,
|
|
|
+ "change_amount": float(log.change_amount) if log.change_amount else 0,
|
|
|
+ "change_desc": log.change_desc,
|
|
|
+ "change_type": log.change_type,
|
|
|
+ }
|
|
|
+ for log in (logs or [])
|
|
|
+ ]
|
|
|
+
|
|
|
@classmethod
|
|
|
async def list_service(
|
|
|
cls,
|