feat(aftersale): per-line refund/return flow with ledger-backed completion (add-aftersale-refunds)

This commit is contained in:
Chengdong Zhang
2026-09-23 16:56:30 +08:00
parent 93e5a05d48
commit 2c2b54c21c
43 changed files with 4889 additions and 32 deletions
+47
View File
@@ -1,5 +1,13 @@
import type {
AccountSummary,
Aftersale,
AftersaleApplyBody,
AftersaleArbitration,
AftersaleDetail,
AftersaleMessage,
AftersaleMessageBody,
AftersaleReturnTrackingBody,
AftersaleStatus,
Address,
AddressBookEntry,
AuthTokens,
@@ -231,6 +239,16 @@ export interface ApiClient {
listShopCouponTemplates(shopId: string): Promise<CouponTemplate[]>;
listMyCoupons(): Promise<Coupon[]>;
claimCoupon(templateId: string): Promise<Coupon>;
applyForAftersale(body: AftersaleApplyBody): Promise<AftersaleDetail>;
listMyAftersales(): Promise<Aftersale[]>;
getAftersale(id: string): Promise<AftersaleDetail>;
cancelAftersale(id: string): Promise<Aftersale>;
reopenAftersale(id: string): Promise<Aftersale>;
submitAftersaleReturnTracking(
id: string,
body: AftersaleReturnTrackingBody,
): Promise<Aftersale>;
addAftersaleMessage(id: string, body: AftersaleMessageBody): Promise<AftersaleMessage>;
/** Public points catalog: published products only. */
listPointsProducts(): Promise<IntegralProduct[]>;
listMyRedemptions(): Promise<IntegralOrder[]>;
@@ -280,6 +298,13 @@ export interface ApiClient {
body: GroupBuyingActivityInput,
): Promise<GroupBuyingActivity>;
deleteGroupBuyingActivity(id: string): Promise<void>;
listAftersales(status?: AftersaleStatus): Promise<Aftersale[]>;
getAftersale(id: string): Promise<AftersaleDetail>;
approveAftersale(id: string): Promise<Aftersale>;
rejectAftersale(id: string): Promise<Aftersale>;
confirmAftersaleReceipt(id: string): Promise<Aftersale>;
refundAftersale(id: string): Promise<Aftersale>;
addAftersaleMessage(id: string, body: AftersaleMessageBody): Promise<AftersaleMessage>;
};
admin: {
listUsers(page?: number): Promise<Paged<User>>;
@@ -298,6 +323,9 @@ export interface ApiClient {
kind: K,
items: ContentInputByKind[K],
): Promise<HomeContent>;
listAftersales(status?: AftersaleStatus): Promise<Aftersale[]>;
getAftersale(id: string): Promise<AftersaleDetail>;
arbitrateAftersale(id: string, outcome: AftersaleArbitration): Promise<Aftersale>;
};
}
@@ -353,6 +381,14 @@ export function createApi(opts: ApiClientOptions): ApiClient {
listShopCouponTemplates: (shopId) => r("GET", `/shops/${shopId}/coupon-templates`),
listMyCoupons: () => r("GET", "/me/coupons"),
claimCoupon: (templateId) => r("POST", "/me/coupons", { template_id: templateId }),
applyForAftersale: (body) => r("POST", "/aftersales", body),
listMyAftersales: () => r("GET", "/aftersales"),
getAftersale: (id) => r("GET", `/aftersales/${id}`),
cancelAftersale: (id) => r("POST", `/aftersales/${id}/cancel`),
reopenAftersale: (id) => r("POST", `/aftersales/${id}/reopen`),
submitAftersaleReturnTracking: (id, body) =>
r("POST", `/aftersales/${id}/return-tracking`, body),
addAftersaleMessage: (id, body) => r("POST", `/aftersales/${id}/messages`, body),
listPointsProducts: () => r("GET", "/points/products"),
listMyRedemptions: () => r("GET", "/points/redemptions"),
redeemPoints: (body) => r("POST", "/points/redemptions", body),
@@ -397,6 +433,13 @@ export function createApi(opts: ApiClientOptions): ApiClient {
updateGroupBuyingActivity: (id, body) =>
r("PUT", `/shop/group-buying-activities/${id}`, body),
deleteGroupBuyingActivity: (id) => r("DELETE", `/shop/group-buying-activities/${id}`),
listAftersales: (status) => r("GET", "/shop/aftersales", undefined, { status }),
getAftersale: (id) => r("GET", `/shop/aftersales/${id}`),
approveAftersale: (id) => r("POST", `/shop/aftersales/${id}/approve`),
rejectAftersale: (id) => r("POST", `/shop/aftersales/${id}/reject`),
confirmAftersaleReceipt: (id) => r("POST", `/shop/aftersales/${id}/confirm-receipt`),
refundAftersale: (id) => r("POST", `/shop/aftersales/${id}/refund`),
addAftersaleMessage: (id, body) => r("POST", `/shop/aftersales/${id}/messages`, body),
},
admin: {
listUsers: (page = 1) => r("GET", "/admin/users", undefined, { page }),
@@ -417,6 +460,10 @@ export function createApi(opts: ApiClientOptions): ApiClient {
/** Replaces the whole ordered brand list. */
getBrands: () => r("GET", "/brands"),
replaceBrands: (items) => r("PUT", "/admin/brands", items),
listAftersales: (status) => r("GET", "/admin/aftersales", undefined, { status }),
getAftersale: (id) => r("GET", `/admin/aftersales/${id}`),
arbitrateAftersale: (id, outcome) =>
r("POST", `/admin/aftersales/${id}/arbitrate`, { outcome }),
listPointsProducts: () => r("GET", "/admin/points/products"),
createPointsProduct: (body) => r("POST", "/admin/points/products", body),
updatePointsProduct: (id, body) => r("PUT", `/admin/points/products/${id}`, body),