/** * finance.ts —— 费用管理接口封装(academic 域;对应旧 PHP 财务可操作面)。 * * 覆盖旧系统用户可操作方法(对齐 modules/user + courses/RequestController): * 账户快照/可退额、充值、消费、退费、自定义退费(§3.1)、转结、坏账、余额清零(§3.2)、 * spend 扣减、资金工单(建/审/驳/撤)、流水查询、仪表盘。 * * 典型使用(views/academic/fee/index.vue): * - 流水列表:useListPage fetcher=listLogs({...scope.toQuery(), userId, page, pageSize}) * - 各操作:直接调对应方法,后端恒 200 + body code;业务错误由调用方判 code * * 契约对齐(academic-frontend-contract-v1 v1.9;v1.6 B1/C1 已回改): * - 金额恒字符串两位小数(元);时间 RFC3339;分页 PageData 五字段; * - 写操作带 requestId 走幂等(重复提交 → repeated=true 回放首次)。 */ import { http } from '@/api/http'; import type { PageR, R } from '@/types/api'; /* ================= 读 ================= */ /** 资金流水 DTO(端点 11 列表行;v1.5:流水表无作用域列,不含 institutionId/campusId) */ export interface ExpenseLogDTO { /** 流水 id(int64 主键) */ id: number; /** 用户 id(canonical_user_id ULID 字符串) */ userId: string; /** 流水类型编码(100 充值 + / 101,201 消费 − / 207 退费 − / 208 清零 − / 106,206 转结 / 108 坏账 +) */ type: number; /** 金额(字符串两位小数) */ money: string; /** 变动后余额(字符串两位小数,全局账户快照) */ balanceAfter: string; /** 业务时间(RFC3339;v1.6 C1:← legacy create_time) */ createTime: string; /** 备注 */ remark: string; } /** 流水查询参数(作用域由 useScope().toQuery() 注入;userId 定位账户) */ export type LogQuery = { institutionId: number; campusId?: number; userId?: string; type?: number; from?: string; to?: string; page?: number; pageSize?: number; }; /** 资金流水分页列表(端点 11,GET /api/admin/finance/logs)。 */ export function listLogs(params: LogQuery): Promise> { return http.get>('/api/admin/finance/logs', { params }).then((r) => r.data); } /** 账户快照 DTO(端点 1;扁平结构) */ export interface AccountDTO { userId: string; institutionId: number; campusId: number; totalBalance: string; campusBalance: string; specialExpenses: string; cumulativeIncome: string; } /** 账户快照(端点 1,GET /api/admin/finance/accounts/:userId)。 */ export function fetchAccount(userId: string, params: { institutionId: number; campusId?: number }): Promise> { return http.get>(`/api/admin/finance/accounts/${userId}`, { params }).then((r) => r.data); } /** 可退额(端点 2,GET /api/admin/finance/accounts/:userId/refundable)。 */ export function fetchRefundable( userId: string, params: { institutionId: number; campusId?: number }, ): Promise> { return http .get>(`/api/admin/finance/accounts/${userId}/refundable`, { params }) .then((r) => r.data); } /** 仪表盘 DTO(端点 21;账户维度 + 校区余额分布) */ export interface DashboardDTO { balance: string; totalIncome: string; totalExpenditure: string; specialExpenses: string; netReceivable: string; balanceDistribution: { campusId: number; balance: string }[]; } /** 仪表盘(端点 21,GET /api/admin/finance/dashboard)。 */ export function fetchDashboard(params: { institutionId: number; campusId?: number; userId?: string }): Promise> { return http.get>('/api/admin/finance/dashboard', { params }).then((r) => r.data); } /* ================= 写(用户可操作;对齐旧 PHP 财务方法) ================= */ /** 资金工单 DTO(端点 12) */ export interface BalanceRequestDTO { id: number; bizNo: string; type: number; userId: string; institutionId: number; campusId: number; money: string; approved: number; // 0 待审 / 1 通过 / -1 驳回 / -2 撤销 applicantId: string; approverId: string; remark: string; /** 申请时间(RFC3339;v1.6 B2:requestTime ← request_time) */ requestTime: string; /** 审批时间(RFC3339;未审批时 null) */ responseTime: string | null; } /** 工单列表(端点 12,GET /api/admin/finance/requests)。 */ export function listRequests(params: { institutionId: number; campusId?: number; type?: number; approved?: number; userId?: string; page?: number; pageSize?: number; }): Promise> { return http.get>('/api/admin/finance/requests', { params }).then((r) => r.data); } /** 充值(端点 3,POST /api/admin/finance/recharges)。入参:userId+campusId+money+requestId。 */ export function recharge(body: { userId: string; campusId: number; money: string; requestId: string; institutionId: number; }): Promise> { return http.post>('/api/admin/finance/recharges', body).then((r) => r.data); } /** 消费(端点 4,POST /api/admin/finance/expenditures)。 */ export function expenditure(body: { userId: string; campusId: number; type?: number; money: string; institutionId: number; }): Promise> { return http.post>('/api/admin/finance/expenditures', body).then((r) => r.data); } /** 退费(端点 5,POST /api/admin/finance/refunds)。余额≤0 → 3102。 */ export function refund(body: { userId: string; campusId: number; money: string; institutionId: number; }): Promise> { return http.post>('/api/admin/finance/refunds', body).then((r) => r.data); } /** 自定义退费结果(端点 6 §3.1;可退不足 → 3103 warn + badDebt;校区不存在 → 3104) */ export interface CustomRefundResult { requested: string; refunded: string; badDebt: string; refundableAmount: string; balance: string; totalBalance: string; totalIncome: string; specialExpenses: string; logId207: number; logId108: number | null; expiredActivityIds: number[]; warnings: { code: number; message: string }[]; repeated: boolean; } /** 自定义退费(端点 6,POST /api/admin/finance/refunds/custom)。 */ export function customRefund(body: { userId: string; campusId: number; money: string; requestId: string; institutionId: number; }): Promise> { return http.post>('/api/admin/finance/refunds/custom', body).then((r) => r.data); } /** 转结(端点 7,POST /api/admin/finance/transfers;校区间转账)。 */ export function transfer(body: { userId: string; fromCampusId: number; toCampusId: number; money: string; institutionId: number; }): Promise> { return http.post>('/api/admin/finance/transfers', body).then((r) => r.data); } /** 坏账(端点 8,POST /api/admin/finance/bad-debts)。 */ export function badDebt(body: { userId: string; campusId: number; money: string; requestId: string; institutionId: number; }): Promise> { return http.post>('/api/admin/finance/bad-debts', body).then((r) => r.data); } /** 余额清零结果(端点 9 §3.2;前置不满足 → 3120) */ export interface ClearBalanceResult { cleared: string; campusBalanceBefore: string; totalBalanceBefore: string; balance: string; totalBalance: string; specialExpenses: string; logId208: number; expiredActivityIds: number[]; repeated: boolean; } /** 余额清零(端点 9,POST /api/admin/finance/cleared-balances)。 */ export function clearBalance(body: { userId: string; campusId: number; institutionId: number }): Promise> { return http.post>('/api/admin/finance/cleared-balances', body).then((r) => r.data); } /** spend 扣减(端点 10,POST /api/admin/finance/spends;课时扣费;v1.6 B1:旧库只 UPDATE spend 列不写流水 → 仅回 `{spend}`,无 logId)。 */ export function spend(body: { userId: string; campusId: number; spendMoney: string; institutionId: number }): Promise> { return http.post>('/api/admin/finance/spends', body).then((r) => r.data); } /** 建工单(端点 13,POST /api/admin/finance/requests)。 */ export function createRequest(body: { type: number; userId: string; campusId: number; money: string; remark?: string; institutionId: number; }): Promise> { return http.post>('/api/admin/finance/requests', body).then((r) => r.data); } /** 审批通过(端点 14,POST /api/admin/finance/requests/:id/approve)。已处理 → 3140。 */ export function approveRequest(id: number, params: { institutionId: number; campusId?: number }): Promise> { return http.post>(`/api/admin/finance/requests/${id}/approve`, {}, { params }).then((r) => r.data); } /** 驳回(端点 15,POST /api/admin/finance/requests/:id/reject)。 */ export function rejectRequest(id: number, params: { institutionId: number; campusId?: number }): Promise> { return http.post>(`/api/admin/finance/requests/${id}/reject`, {}, { params }).then((r) => r.data); } /** 撤销(端点 16,POST /api/admin/finance/requests/:id/cancel;仅待审)。 */ export function cancelRequest(id: number, params: { institutionId: number; campusId?: number }): Promise> { return http.post>(`/api/admin/finance/requests/${id}/cancel`, {}, { params }).then((r) => r.data); } /* ================= 收支记录 / 收支字典(端点 60;v1.9 三条修正) ================= */ /** 收支记录 DTO(端点 60 列表行;记录表无 type/name/direction 列——type/name 由后端 JOIN 字典下发)。 */ export interface ExpenditureRecordDTO { /** 记录 id */ id: number; /** 收支字典 id(← expenditure_id;编辑表单回填用) */ expenditureId: number; /** 收支方向编码:1 = 支出 / 2 = 收入(用 type 判方向,两表无 direction 列) */ type: number; /** 收支类型名(JOIN 字典 t_expenditure.name) */ name: string; /** 金额(字符串两位小数) */ money: string; /** 记账人(← create_user_id;语义 = 记账人,非"收付对象";可空) */ createUserId: string | null; campusId: number; /** 业务单号 */ bizNo: string; /** 业务时间(RFC3339) */ createTime: string; } /** 收支记录查询参数(作用域由 useScope().toQuery() 注入;type 1 支出 / 2 收入) */ export type ExpenditureQuery = { institutionId: number; campusId?: number; type?: number; page?: number; pageSize?: number; }; /** 收支记录分页列表(端点 60,GET /api/admin/expenditures)。 */ export function listExpenditures(params: ExpenditureQuery): Promise> { return http.get>('/api/admin/expenditures', { params }).then((r) => r.data); } /** 收支类型字典项(端点 60 GET /types 行) */ export interface ExpenditureTypeDTO { /** 字典 id(记录 expenditureId 指向它) */ id: number; /** 收支方向编码:1 = 支出 / 2 = 收入 */ type: number; name: string; /** 父级字典 id(0 = 顶级) */ parentId: number; } /** 收支类型字典(端点 60,GET /api/admin/expenditures/types)。 * ⚠ POST /types(新增收支类型)本轮不做——需要时提需求。 */ export function fetchExpenditureTypes(params: { institutionId: number; campusId?: number }): Promise> { return http.get>('/api/admin/expenditures/types', { params }).then((r) => r.data); } /** 新增收支记录(端点 60,POST /api/admin/expenditures)→ {id, bizNo}。 * expenditureId 指向字典(type/name 由后端 JOIN 字典得出,前端不传);createUserId 可空。 */ export function createExpenditure(body: { expenditureId: number; money: string; campusId: number; createUserId?: string | null; institutionId: number; }): Promise> { return http.post>('/api/admin/expenditures', body).then((r) => r.data); }