read-c-users-1-claude-claude-md-before-s-misty-hummingbird.md 7.7 KB

Plan: Alipay SDK Integration into Java Expense Services

Context

Python backend had FULL Alipay SDK integration for ~18 expense-control APIs. Java port has alipay-sdk-java v4.39.218 in pom.xml + AlipayClientFactory bean producing DefaultAlipayClient — but 5 expense module services return empty maps with log.warn("NOT_IMPL") where they should call the SDK.

5 payment modules already use real SDK calls as the pattern reference:

  • AlipayEmployeeService (D:\project2\payment-platform\java\src\main\java\com\payment\platform\module\payment\employee\service\AlipayEmployeeService.java)
  • AlipayTransferService, AlipayEnterpriseService, AlipayDepartmentService, FacetofaceService

Goal: Replace 18 NOT_IMPL stubs in expense module with real alipayClientFactory.getClient().execute(request) calls.

Canonical Java SDK Pattern

From AlipayEmployeeService.java (lines 42-70):

// 1. Build model
AlipayXxxModel model = new AlipayXxxModel();
model.setField(value);
// 2. Build request
AlipayXxxRequest request = new AlipayXxxRequest();
request.setBizModel(model);
// 3. Execute
AlipayXxxResponse response = alipayClientFactory.getClient().execute(request);
// 4. Check
if (!response.isSuccess())
    throw new BusinessException(400, "失败: " +
        (response.getSubMsg() != null ? response.getSubMsg() : response.getMsg()));
// 5. Catch
catch (AlipayApiException e) {
    throw new BusinessException(400, "支付宝异常: " + e.getMessage());
}

Use wildcard imports: import com.alipay.api.domain.*; / request.*; / response.*; / AlipayApiException

Implementation (4 phases, 5 files)

Phase 1: InstitutionService (8 APIs)

File: java/src/main/java/com/payment/platform/module/payment/expense/institution/service/InstitutionService.java Python: backend/app/plugin/module_payment/expense/institution/service.py Already has AlipayClientFactory injected.

API Alipay SDK Class Notes
create() AlipayEbppInvoiceInstitutionCreateModel/Request/Response Replace UUID fallback with response.getInstitutionId()
update() AlipayEbppInvoiceInstitutionModifyModel/Request/Response Map DTO fields to model
delete() AlipayEbppInvoiceInstitutionDeleteModel/Request/Response Best-effort: swallow Alipay errors, always clean up local DB
detail() AlipayEbppInvoiceInstitutionDetailinfoQueryModel/Request/Response Try Alipay first, fall back to DB
listScope() AlipayEbppInvoiceInstitutionScopepageinfoQueryModel/Request/Response Note SDK typo: getOnwerOpenIdList()
modifyScope() AlipayEbppInvoiceInstitutionScopeModifyModel/Request/Response Python delete_owner_id_list → Java setRemoveOwnerIdList
createIssueRule() AlipayEbppInvoiceIssueruleCreateModel/Request/Response Includes issue_target_info_list
updateIssueRule() / deleteIssueRule() Modify/Delete variants

Phase 2: InstitutionScopeSyncService (1 API)

File: java/src/main/java/com/payment/platform/module/payment/expense/institution/service/InstitutionScopeSyncService.java Already has AlipayClientFactory. Replace scope.modify stubs for department/employee removal. Errors logged, don't block caller.

Phase 3: QuotaService (3 stubs)

File: java/src/main/java/com/payment/platform/module/payment/expense/quota/service/QuotaService.java Python: backend/app/plugin/module_payment/expense/quota/service.py Already has AlipayClientFactory (adjustInternal + outsourceNotify already work).

Method Alipay SDK Class
create(QuotaCreateDTO) AlipayEbppInvoiceExpensecontrolQuotaCreateModel/Request/Response
expenseCreate(Map) Same quota.create API, different DTO packaging
expenseQuery(Map) AlipayEbppInvoiceExpensecontrolQuotaQueryModel/Request/Response

Phase 4: RuleService + IssueBatchService (5 APIs)

Need AlipayClientFactory injection added — both currently lack it.

RuleService: java/.../expense/rule/service/RuleService.java Python: backend/app/plugin/module_payment/expense/rule/service.py

Method Alipay SDK Class
createExpense(Map) AlipayEbppInvoiceInstitutionExpenseruleCreateModel/Request/Response
modifyExpense(String, Map) AlipayEbppInvoiceInstitutionExpenseruleModifyModel/Request/Response
deleteExpense(String, Map) AlipayEbppInvoiceInstitutionExpenseruleDeleteModel/Request/Response

IssueBatchService: java/.../expense/quota/service/IssueBatchService.java

Method Alipay SDK Class
create(Map) AlipayEbppInvoiceExpensecontrolIssuebatchCreateModel/Request/Response
cancel(String) AlipayEbppInvoiceExpensecontrolIssuebatchCancelModel/Request/Response

Phase 5: QuotaService expenseModify + expenseDelete (2 remaining stubs)

File: java/.../expense/quota/service/QuotaService.java Python: backend/app/plugin/module_payment/expense/quota/service.py Already has AlipayClientFactory injected.

Method Alipay SDK Class Notes
expenseModify(String, Map) AlipayEbppInvoiceExpensecontrolQuotaModifyModel/Request/Response action=ADD/DEDUCT/MODIFY_SHARE_MODE, need quota_id from local DB via out_biz_no
expenseDelete(String) AlipayCommerceEcExpenseDeleteModel/Request/Response Simple delete by out_biz_no

Phase 6: Python↔Java 全量字段对齐修复

Files: IssueBatchService, RuleService, QuotaService, InstitutionService

CRITICAL (wrong data / SDK rejection): | File | Method | Fix | |------|--------|-----| | IssueBatchService.create | target_idowner_id, target_typeowner_type, quota_amountissue_quota (3 wrong JSON keys) | | IssueBatchService.create | Add effective_start_date / effective_end_date to model (Python required) | | RuleService.modifyExpense | Add institution_id on model (Python required) | | QuotaService.expenseModify | quota_id from data.get("quota_id"), outer_source_id from data.get("outer_source_id") — NOT from local DB / parameter |

SEVERE (logic mismatch): | File | Method | Fix | |------|--------|-----| | InstitutionService.createFullFlow | Rollback: delete Alipay institution if scope/issuerule fails (Python re-raises) | | InstitutionService.createFullFlow | Map standard_id_info_list from Alipay response instead of using input data | | RuleService.modifyExpense | action from data.get("action") not hardcoded | | IssueBatchService.create | Add issue_desc, owner_open_id, user_name optional fields |

MEDIUM (optional fields): | File | Method | Fix | |------|--------|-----| | RuleService.createExpense | Add payment_policy, consume_mode, open_rule_id, rule_name conditionals | | RuleService.modifyExpense | Add standard_desc, open_rule_id, payment_policy, consume_mode conditionals | | InstitutionService.createFullFlow | Add currency to institution create model | | QuotaService.expenseDelete | Raise BusinessException when not found (match Python), keep SDK gap note | | QuotaService.expenseModify | Add null response guard, add error log before throw, handle share_mode empty string |

Error handling rules

  • Normal APIs: Throw BusinessException on !response.isSuccess()
  • Institution delete: Swallow Alipay errors (Python best-effort pattern)
  • Institution detail: Alipay failure → DB fallback
  • Scope sync: Log + continue, don't block

Verification

  1. mvn compile — all SDK classes from alipay-sdk-java-4.39.218.ALL.jar compile
  2. Field mapping: each Python data["field_name"] → Java model.setFieldName(...) verified against Python reference
  3. SDK naming quirks handled: removeOwnerIdList (not delete), getOnwerOpenIdList() (typo)
  4. All 5 services have AlipayClientFactory injection