This commit is contained in:
wushumin
2026-05-11 15:28:27 +08:00
commit 9aac78b8da
289 changed files with 67193 additions and 0 deletions

820
user-app/src/api/app.ts Normal file
View File

@@ -0,0 +1,820 @@
import { parseUploadResponse, request } from "../utils/request";
import { buildAuthHeaders } from "../utils/auth";
import { resolveApiBaseUrl } from "../utils/env";
export interface PageVisualsData {
order_background_image_url?: string;
report_background_image_url?: string;
}
export interface HomeData {
banners: Array<{
title: string;
subtitle: string;
description: string;
background_image_url?: string;
}>;
page_visuals: PageVisualsData;
service_entries: Array<{
service_provider: string;
title: string;
tag: string;
description: string;
meta: string;
}>;
category_entries: Array<{
category_id: number;
category_name: string;
category_code: string;
image_url?: string;
}>;
trust_points: Array<{
title: string;
desc: string;
}>;
quick_entries: Array<{
code: string;
title: string;
desc: string;
}>;
trust_metrics: Array<{
value: string;
label: string;
}>;
faqs: string[];
}
export interface HelpCategoryItem {
code: "all" | "service" | "report" | "shipping" | "support";
title: string;
desc: string;
count: number;
}
export interface HelpArticleSummary {
id: number;
title: string;
category: "service" | "report" | "shipping" | "support";
category_text: string;
summary: string;
keywords: string[];
updated_at: string;
is_recommended: boolean;
}
export interface HelpCenterData {
categories: HelpCategoryItem[];
articles: HelpArticleSummary[];
}
export interface TicketTypeOption {
code: string;
title: string;
hint: string;
quick_desc: string;
}
export interface TicketStatusOption {
code: string;
title: string;
desc: string;
}
export interface MessagePageCopy {
title: string;
desc: string;
}
export interface HelpArticleDetailData {
article: HelpArticleSummary & {
content_blocks: string[];
};
related_articles: HelpArticleSummary[];
}
export interface SettingsData {
profile_info: {
user_id: number;
nickname: string;
mobile: string;
avatar: string;
status: string;
status_text: string;
password_set: boolean;
};
preferences: {
notify_order: boolean;
notify_report: boolean;
notify_supplement: boolean;
notify_ticket: boolean;
marketing_notify: boolean;
privacy_mode: boolean;
};
legal_entries: Array<{
code: string;
title: string;
desc: string;
target_url: string;
}>;
}
export interface MineOverviewData {
profile_info: SettingsData["profile_info"];
asset_summary: {
total_valuation: number;
item_count: number;
report_count: number;
authentic_rate: number;
unread_count: number;
};
}
export interface OrderListItem {
order_id: number;
order_no: string;
appraisal_no: string;
order_status: string;
product_name: string;
product_cover: string;
service_provider: string;
display_status: string;
status_desc: string;
estimated_finish_time: string;
primary_action: string;
}
export interface OrderDetailData {
order_info: {
order_id: number;
order_no: string;
appraisal_no: string;
service_provider: string;
order_status: string;
display_status: string;
status_desc: string;
estimated_finish_time: string;
can_edit_return_address: boolean;
};
product_info: {
product_name: string;
category_name: string;
brand_name: string;
color: string;
size_spec: string;
serial_no: string;
};
extra_info: {
purchase_channel: string;
purchase_price: number;
purchase_date: string;
usage_status: string;
usage_status_text: string;
condition_desc: string;
has_accessories: boolean;
accessories: string[];
remark: string;
};
materials: Array<{
upload_item_id: number;
item_code: string;
item_name: string;
is_required: boolean;
source_type: string;
source_type_text: string;
status: string;
status_text: string;
file_count: number;
files: Array<{
file_id: string;
file_url: string;
thumbnail_url: string;
quality_status: string;
quality_message: string;
}>;
}>;
return_address: null | {
user_address_id: number;
consignee: string;
mobile: string;
province: string;
city: string;
district: string;
detail_address: string;
full_address: string;
};
return_logistics: null | {
express_company: string;
tracking_no: string;
tracking_status: string;
tracking_status_text: string;
latest_desc: string;
latest_time: string;
nodes: Array<{
node_time: string;
node_desc: string;
node_location: string;
}>;
};
timeline: Array<{
node_code: string;
node_text: string;
node_desc: string;
occurred_at: string;
}>;
supplement_task: null | {
task_id: number;
reason: string;
deadline: string;
items: Array<{
item_code: string;
item_name: string;
guide_text: string;
}>;
};
available_actions: {
primary_action: string;
secondary_action: string;
};
}
export interface ShippingDetailData {
order_info: {
order_id: number;
order_no: string;
appraisal_no: string;
service_provider: string;
display_status: string;
estimated_finish_time: string;
product_name: string;
};
shipping_address: {
warehouse_id?: number;
warehouse_name?: string;
warehouse_code?: string;
receiver_name: string;
receiver_mobile: string;
province: string;
city: string;
district: string;
detail_address: string;
service_time: string;
notice: string;
};
shipping_options: {
current_warehouse_id: number;
can_select_warehouse: boolean;
list: Array<{
id: number;
warehouse_name: string;
warehouse_code: string;
warehouse_type: string;
warehouse_type_text: string;
service_provider: string;
service_provider_text: string;
receiver_name: string;
receiver_mobile: string;
province: string;
city: string;
district: string;
detail_address: string;
full_address: string;
service_time: string;
notice: string;
supported_category_ids: number[];
supported_category_names: string[];
status: string;
status_text: string;
is_default: boolean;
is_recommended?: boolean;
recommended_reason?: string;
sort_order: number;
remark: string;
created_at: string;
updated_at: string;
}>;
};
shipping_notice: {
tips: string[];
express_recommendations: string[];
};
logistics_info: {
express_company: string;
tracking_no: string;
tracking_status: string;
tracking_status_text: string;
latest_desc: string;
latest_time: string;
is_submitted: boolean;
};
logistics_nodes: Array<{
node_time: string;
node_desc: string;
node_location: string;
}>;
can_submit_tracking: boolean;
}
export interface UserAddressItem {
id: number;
consignee: string;
mobile: string;
province: string;
city: string;
district: string;
detail_address: string;
full_address: string;
is_default: boolean;
created_at: string;
updated_at: string;
}
export interface ReportListItem {
report_id: number | null;
order_id: number;
report_no: string;
product_name: string;
product_cover: string;
service_provider: string;
status: string;
result_text: string;
institution_name: string;
publish_time: string;
}
export interface ReportDetailData {
evidence_attachments: EvidenceAttachmentAsset[];
report_header: {
report_id: number;
report_no: string;
report_type: string;
report_title: string;
report_status: string;
service_provider: string;
institution_name: string;
publish_time: string;
};
result_info: Record<string, any>;
product_info: Record<string, any>;
appraisal_info: Record<string, any>;
valuation_info: Record<string, any>;
risk_notice_text: string;
verify_info: {
report_no: string;
verify_status: string;
verify_url: string;
verify_qrcode_url: string;
};
file_info: {
pdf_url: string;
};
}
export interface VerifyData {
verify_status: string;
verify_message: string;
evidence_attachments: EvidenceAttachmentAsset[];
report_summary: {
report_no: string;
report_title?: string;
institution_name: string;
publish_time: string;
};
product_summary: Record<string, any>;
result_summary: Record<string, any>;
}
export interface MaterialTagData {
tag_status: "unbound" | "pending_report" | "published" | "not_found";
status_text: string;
message: string;
qr_token: string;
qr_url: string;
scan_count: number;
verify_count: number;
report_summary: null | {
report_id?: number;
report_no: string;
report_title: string;
institution_name: string;
publish_time: string;
};
product_summary: Record<string, any>;
result_summary: Record<string, any>;
verify_passed: boolean;
}
export interface MaterialTagVerifyResult {
verify_passed: boolean;
verify_message: string;
verify_count: number;
}
export interface MessageSummaryData {
total_count: number;
unread_count: number;
category_counts: {
all: number;
order: number;
report: number;
supplement: number;
ticket: number;
};
latest_title: string;
latest_time: string;
}
export interface UserMessageItem {
id: number;
title: string;
content: string;
biz_type: string;
biz_type_text: string;
category: "all" | "order" | "report" | "supplement" | "ticket";
category_text: string;
biz_id: number;
is_read: boolean;
created_at: string;
target_url: string;
target_label: string;
}
export interface UserMessageListData {
list: UserMessageItem[];
summary: {
total_count: number;
unread_count: number;
category_counts: {
all: number;
order: number;
report: number;
supplement: number;
ticket: number;
};
current_count: number;
current_category: "all" | "order" | "report" | "supplement" | "ticket";
unread_only: boolean;
};
}
export interface SupplementFileItem {
id: number;
file_id: string;
file_url: string;
thumbnail_url: string;
}
export interface SupplementDetailData {
order_id: number;
order_no: string;
appraisal_no: string;
reason: string;
deadline: string;
items: Array<{
upload_item_id: number;
item_code: string;
item_name: string;
guide_text: string;
is_required: boolean;
status: string;
files: SupplementFileItem[];
}>;
}
export interface TicketOverviewCard {
title: string;
value: number;
desc: string;
}
export interface TicketAttachmentAsset {
file_id: string;
file_url: string;
thumbnail_url: string;
name?: string;
}
export interface EvidenceAttachmentAsset {
file_id: string;
file_url: string;
thumbnail_url: string;
name?: string;
file_type: string;
mime_type: string;
}
export interface UserTicketListItem {
id: number;
ticket_no: string;
ticket_type: string;
ticket_type_text: string;
status: string;
status_text: string;
priority: string;
priority_text: string;
title: string;
order_id: number;
latest_message: string;
updated_at: string;
created_at: string;
}
export interface UserTicketDetailData {
ticket_info: {
id: number;
ticket_no: string;
ticket_type: string;
ticket_type_text: string;
status: string;
status_text: string;
priority: string;
priority_text: string;
title: string;
content: string;
order_id: number;
created_at: string;
updated_at: string;
};
order_info: null | {
order_id: number;
order_no: string;
display_status: string;
};
messages: Array<{
sender_type: string;
sender_type_text: string;
content: string;
attachments: TicketAttachmentAsset[];
created_at: string;
}>;
}
export const appApi = {
getHomeData() {
return request<HomeData>("/api/app/home/index");
},
getPageVisuals() {
return request<PageVisualsData>("/api/app/content/page-visuals");
},
getHelpCenter(params?: Record<string, string | number | undefined>) {
return request<HelpCenterData>("/api/app/help-center", {
params,
});
},
getHelpArticleDetail(id: number) {
return request<HelpArticleDetailData>("/api/app/help-article/detail", {
params: { id },
});
},
getSettings() {
return request<SettingsData>("/api/app/settings");
},
getMineOverview() {
return request<MineOverviewData>("/api/app/mine/overview");
},
saveSettings(payload: {
nickname: string;
preferences: SettingsData["preferences"];
}) {
return request<SettingsData>("/api/app/settings/save", {
method: "POST",
data: payload,
});
},
getOrders() {
return request<{ list: OrderListItem[] }>("/api/app/orders");
},
getOrderDetail(id: number) {
return request<OrderDetailData>("/api/app/order/detail", {
params: { id },
});
},
getOrderShippingDetail(orderId: number) {
return request<ShippingDetailData>("/api/app/order/shipping", {
params: { order_id: orderId },
});
},
saveOrderShipping(payload: {
order_id: number;
express_company: string;
tracking_no: string;
warehouse_id?: number;
}) {
return request<{
order_id: number;
express_company: string;
tracking_no: string;
}>("/api/app/order/shipping/save", {
method: "POST",
data: payload,
});
},
saveOrderReturnAddress(payload: {
order_id: number;
address_id: number;
}) {
return request<{
order_id: number;
return_address: OrderDetailData["return_address"];
}>("/api/app/order/return-address/save", {
method: "POST",
data: payload,
});
},
getAddresses() {
return request<{ list: UserAddressItem[] }>("/api/app/addresses");
},
getAddressDetail(id: number) {
return request<UserAddressItem>("/api/app/address/detail", {
params: { id },
});
},
saveAddress(payload: {
id?: number;
consignee: string;
mobile: string;
province: string;
city: string;
district: string;
detail_address: string;
is_default: boolean;
}) {
return request<{ id: number; address: UserAddressItem }>("/api/app/address/save", {
method: "POST",
data: payload,
});
},
setDefaultAddress(id: number) {
return request<{ id: number }>("/api/app/address/default", {
method: "POST",
data: { id },
});
},
deleteAddress(id: number) {
return request<{ id: number }>("/api/app/address/delete", {
method: "POST",
data: { id },
});
},
getReports() {
return request<{ list: ReportListItem[] }>("/api/app/reports");
},
getReportDetail(params: { id?: number; report_no?: string }) {
return request<ReportDetailData>("/api/app/report/detail", {
params,
});
},
verifyReport(reportNo: string) {
return request<VerifyData>("/api/app/verify", {
params: { report_no: reportNo },
});
},
getMaterialTag(token: string) {
return request<MaterialTagData>("/api/app/material-tag", {
params: { token },
});
},
verifyMaterialTag(payload: {
token: string;
report_no: string;
verify_code: string;
}) {
return request<MaterialTagVerifyResult>("/api/app/material-tag/verify", {
method: "POST",
data: payload,
});
},
getMessageSummary() {
return request<MessageSummaryData>("/api/app/messages/summary");
},
getMessageMeta() {
return request<{ message_page_copy: MessagePageCopy }>("/api/app/messages/meta");
},
getMessages(params?: Record<string, string | number | undefined>) {
return request<UserMessageListData>("/api/app/messages", {
params,
});
},
readMessage(id: number) {
return request<{ id: number; is_read: boolean }>("/api/app/message/read", {
method: "POST",
data: { id },
});
},
readAllMessages() {
return request<{ affected: number }>("/api/app/messages/read-all", {
method: "POST",
});
},
getSupplementDetail(orderId: number) {
return request<SupplementDetailData>("/api/app/order/supplement", {
params: { order_id: orderId },
});
},
uploadSupplementFile(payload: {
uploadItemId: number;
filePath: string;
}) {
const baseUrl = resolveApiBaseUrl().replace(/\/$/, "");
return new Promise<SupplementFileItem>((resolve, reject) => {
uni.uploadFile({
url: `${baseUrl}/api/app/order/supplement/file/upload`,
filePath: payload.filePath,
name: "file",
header: buildAuthHeaders(),
formData: {
upload_item_id: payload.uploadItemId,
},
success: (response) => {
try {
resolve(parseUploadResponse<SupplementFileItem>(response, "补资料上传失败"));
} catch (error) {
reject(error);
}
},
fail: (error) => reject(error),
});
});
},
deleteSupplementFile(fileId: string) {
return request<{ file_id: string }>("/api/app/order/supplement/file/delete", {
method: "POST",
data: {
file_id: fileId,
},
});
},
submitSupplement(orderId: number) {
return request<{ order_id: number; next_status: string }>("/api/app/order/supplement/submit", {
method: "POST",
data: {
order_id: orderId,
},
});
},
getTicketOverview() {
return request<{ cards: TicketOverviewCard[]; ticket_types: TicketTypeOption[] }>("/api/app/tickets/overview");
},
getTicketMeta() {
return request<{ ticket_types: TicketTypeOption[]; ticket_statuses: TicketStatusOption[] }>("/api/app/ticket/meta");
},
getTickets(params?: Record<string, string | number | undefined>) {
return request<{ list: UserTicketListItem[] }>("/api/app/tickets", {
params,
});
},
getTicketDetail(id: number) {
return request<UserTicketDetailData>("/api/app/ticket/detail", {
params: { id },
});
},
createTicket(payload: {
ticket_type: string;
title: string;
content: string;
order_id?: number;
report_id?: number;
attachments?: TicketAttachmentAsset[];
}) {
return request<{ ticket_id: number; ticket_no: string }>("/api/app/ticket/create", {
method: "POST",
data: payload,
});
},
replyTicket(ticketId: number, content: string, attachments: TicketAttachmentAsset[] = []) {
return request<{ ticket_id: number }>("/api/app/ticket/reply", {
method: "POST",
data: {
ticket_id: ticketId,
content,
attachments,
},
});
},
uploadTicketFile(filePath: string) {
const baseUrl = resolveApiBaseUrl().replace(/\/$/, "");
return new Promise<TicketAttachmentAsset>((resolve, reject) => {
uni.uploadFile({
url: `${baseUrl}/api/app/ticket/file/upload`,
filePath,
name: "file",
header: buildAuthHeaders(),
success: (response) => {
try {
resolve(parseUploadResponse<TicketAttachmentAsset>(response, "附件上传失败"));
} catch (error) {
reject(error);
}
},
fail: (error) => reject(error),
});
});
},
deleteTicketFile(fileUrl: string) {
return request<{ file_url: string }>("/api/app/ticket/file/delete", {
method: "POST",
data: {
file_url: fileUrl,
},
});
},
};

View File

@@ -0,0 +1,175 @@
import { parseUploadResponse, request } from "../utils/request";
import { buildAuthHeaders } from "../utils/auth";
import { resolveApiBaseUrl } from "../utils/env";
import { resolveOrderSourceChannel } from "../utils/order-source";
export interface CatalogOption {
brand_id?: number;
brand_name?: string;
}
export interface CategoryOption {
category_id: number;
category_name: string;
category_code: string;
}
export interface UploadItem {
item_code: string;
item_name: string;
guide_text: string;
sample_image_url: string;
is_required: boolean;
quality_status: string;
quality_message: string;
files?: UploadFileAsset[];
}
export interface UploadFileAsset {
file_id: string;
file_url: string;
thumbnail_url: string;
name?: string;
}
export interface DraftDetail {
draft_id: number;
service_provider: string;
service_mode: string;
current_step: number;
product_info: Record<string, any>;
extra_info: Record<string, any>;
upload_info?: {
items: UploadItem[];
};
}
export interface PreviewData {
service_summary: {
service_provider: string;
service_provider_text: string;
};
product_summary: {
product_name: string;
category_name: string;
brand_name: string;
price: number;
};
upload_summary: {
uploaded_count: number;
};
fee_detail: {
service_fee: number;
discount_fee: number;
pay_amount: number;
};
agreements: Array<{
code: string;
title: string;
desc: string;
target_url: string;
}>;
}
export interface SubmitResult {
order_id: number;
order_no: string;
appraisal_no: string;
pay_amount: number;
next_status: string;
}
export const appraisalApi = {
createDraft(serviceProvider: string) {
return request<{ draft_id: number; service_provider: string; service_mode: string }>(
"/api/app/appraisal/draft/create",
{
method: "POST",
data: {
service_provider: serviceProvider,
service_mode: "physical",
},
},
);
},
getDraft(draftId: number) {
return request<DraftDetail>("/api/app/appraisal/draft", {
params: { draft_id: draftId },
});
},
saveDraft(payload: Record<string, unknown>) {
return request<{ draft_id: number; current_step: number }>("/api/app/appraisal/draft/save", {
method: "POST",
data: payload,
});
},
getBrands(categoryId: number) {
return request<{ list: CatalogOption[] }>("/api/app/catalog/brands", {
params: { category_id: categoryId },
});
},
getCategories() {
return request<{ list: CategoryOption[] }>("/api/app/catalog/categories");
},
getUploadTemplate(categoryId: number, serviceProvider: string) {
return request<{ template_id: number; required_items: UploadItem[]; optional_items: UploadItem[] }>(
"/api/app/appraisal/upload-template",
{
params: { category_id: categoryId, service_provider: serviceProvider },
},
);
},
preview(draftId: number) {
return request<PreviewData>("/api/app/appraisal/preview", {
method: "POST",
data: { draft_id: draftId },
});
},
submit(draftId: number, returnAddressId?: number) {
return request<SubmitResult>("/api/app/appraisal/submit", {
method: "POST",
data: {
draft_id: draftId,
return_address_id: returnAddressId,
source_channel: resolveOrderSourceChannel(),
},
});
},
uploadFile(payload: {
draftId: number;
itemCode: string;
itemName: string;
filePath: string;
}) {
const baseUrl = resolveApiBaseUrl().replace(/\/$/, "");
return new Promise<UploadFileAsset>((resolve, reject) => {
uni.uploadFile({
url: `${baseUrl}/api/app/appraisal/file/upload`,
filePath: payload.filePath,
name: "file",
header: buildAuthHeaders(),
formData: {
draft_id: payload.draftId,
item_code: payload.itemCode,
item_name: payload.itemName,
},
success: (response) => {
try {
resolve(parseUploadResponse<UploadFileAsset>(response, "图片上传失败"));
} catch (error) {
reject(error);
}
},
fail: (error) => reject(error),
});
});
},
deleteFile(fileUrl: string) {
return request<{ file_url: string }>("/api/app/appraisal/file/delete", {
method: "POST",
data: {
file_url: fileUrl,
},
});
},
};

62
user-app/src/api/auth.ts Normal file
View File

@@ -0,0 +1,62 @@
import { request } from "../utils/request";
export interface AuthUserInfo {
id: number;
nickname: string;
mobile: string;
avatar: string;
status: string;
password_set: boolean;
}
export interface SendLoginCodeResult {
mobile: string;
scene: string;
expire_seconds: number;
retry_after_seconds: number;
debug_code?: string;
}
export interface LoginResult {
token: string;
user_info: AuthUserInfo;
}
export const authApi = {
sendLoginCode(mobile: string) {
return request<SendLoginCodeResult>("/api/app/auth/send-code", {
method: "POST",
data: { mobile },
});
},
loginByCode(mobile: string, code: string) {
return request<LoginResult>("/api/app/auth/login/code", {
method: "POST",
data: { mobile, code },
});
},
loginByPassword(mobile: string, password: string) {
return request<LoginResult>("/api/app/auth/login/password", {
method: "POST",
data: { mobile, password },
});
},
getMe() {
return request<{ user_info: AuthUserInfo }>("/api/app/auth/me");
},
savePassword(payload: {
current_password?: string;
new_password: string;
confirm_password: string;
}) {
return request<{ user_id: number; password_set: boolean; had_password: boolean }>("/api/app/auth/password/save", {
method: "POST",
data: payload,
});
},
logout() {
return request<Record<string, never>>("/api/app/auth/logout", {
method: "POST",
});
},
};