import { rpcOrValue } from '@/services/analytics/rpc.uts' import supa from '@/components/supadb/aksupainstance.uts' /** * 优惠券模板模型 */ export type CouponTemplate = { id?: string cid?: number merchant_id?: string name: string description: string | null coupon_type: number // 1:满减, 2:折扣, 3:免运费 discount_type: number // 1:固定金额, 2:百分比 discount_value: number min_order_amount: number max_discount_amount: number | null total_quantity: number | null per_user_limit: number usage_limit: number applicable_products: any[] applicable_categories: any[] start_time: string end_time: string status: number // 1:正常, 2:暂停, 3:已结束 created_at?: string updated_at?: string } /** * 积分统计模型 */ export type IntegralStats = { totals: { current: number income: number expend: number } trend: Array<{ date_group: string income: number expend: number }> sources: Array<{ label: string value: number percent: number }> consumes: Array<{ label: string value: number percent: number }> } export type CouponQuery = { name?: string | null type?: number | null status?: number | null page?: number pageSize?: number } function getCurrentUid(): string | null { try { const session = supa.getSession() return session?.user?.getString('id') ?? null } catch (e) { return null } } /** * 分页获取优惠券模板列表 */ export async function fetchAdminCoupons(query?: CouponQuery): Promise<{ total: number; items: CouponTemplate[] }> { let q = supa.from('ml_coupon_templates').select('*', { count: 'exact' }) if (query?.name != null && query.name !== '') { q = q.ilike('name', `%${query.name}%`) } if (query?.type != null) { q = q.eq('coupon_type', query.type) } if (query?.status != null) { q = q.eq('status', query.status) } const p = query?.page ?? 1 const ps = query?.pageSize ?? 20 const from = (p - 1) * ps const to = from + ps - 1 const { data, error, count } = await q .order('created_at', { ascending: false }) .range(from, to) .execute() if (error != null) { console.error('获取优惠券列表失败:', error) return { total: 0, items: [] as CouponTemplate[] } } return { total: count ?? 0, items: (data ?? []) as CouponTemplate[] } } /** * 获取积分统计概况 */ export async function fetchIntegralStats(startTime: string, endTime: string): Promise { const res = await rpcOrValue('rpc_admin_get_integral_stats', { p_start_time: startTime, p_end_time: endTime } as any) return res as IntegralStats | null } /** * 保存优惠券模板(新增/更新) */ export async function saveCouponTemplate(tpl: CouponTemplate): Promise { const uid = getCurrentUid() if (uid == null) return false const { error } = await supa .from('ml_coupon_templates') .upsert({ ...tpl, merchant_id: uid, updated_at: new Date().toISOString() }) .execute() if (error != null) { console.error('保存优惠券模板失败:', error) return false } return true } /** * 签到奖励项 */ export type SignInReward = { day : number points : number } /** * 签到配置模型 */ export type SignInConfig = { id ?: string merchant_id ?: string is_enabled : boolean daily_points : number continuous_rewards : SignInReward[] rules_description : string updated_at ?: string } /** * 获取签到规则配置 */ export async function fetchSignInConfig() : Promise { const { data, error } = await supa .from('ak_signin_configs') .select('*') .eq('id', 'signin_config') .single() .execute() if (error != null) { // 如果是 406 或不存在,返回 null 由页面处理初始化 return null } return data as SignInConfig | null } /** * 保存签到规则配置 */ export async function saveSignInConfig(config : SignInConfig) : Promise { const uid = getCurrentUid() if (uid == null) return false const { error } = await supa .from('ak_signin_configs') .upsert({ ...config, id: 'signin_config', merchant_id: uid, updated_at: new Date().toISOString() }) .execute() if (error != null) { console.error('保存签到配置失败:', error) return false } return true } /** * 签到配置模型(含模式、提醒、积分、经验) */ export type CheckinConfig = { id ?: string merchant_id ?: string is_open : boolean mode : string // 'none' | 'week' | 'month' notice_enabled : boolean integral_reward : number exp_reward : number updated_at ?: string } /** * 获取签到配置(扩展版) */ export async function fetchCheckinConfig() : Promise { const { data, error } = await supa .from('ak_signin_configs') .select('*') .eq('id', 'signin_config') .single() .execute() if (error != null) { return null } return data as CheckinConfig | null } /** * 保存签到配置(扩展版) */ export async function saveCheckinConfig(config : CheckinConfig) : Promise { const uid = getCurrentUid() if (uid == null) return false const { error } = await supa .from('ak_signin_configs') .upsert({ ...config, id: 'signin_config', merchant_id: uid, updated_at: new Date().toISOString() }) .execute() if (error != null) { console.error('保存签到配置(扩展)失败:', error) return false } return true } /** * 切换优惠券状态 */ export async function toggleCouponStatus(id: string, isOpen: boolean): Promise { const nextStatus = isOpen ? 1 : 2 // 1:正常, 2:暂停 const { error } = await supa .from('ml_coupon_templates') .update({ status: nextStatus, updated_at: new Date().toISOString() }) .eq('id', id) .execute() if (error != null) { console.error('更新优惠券状态失败:', error) return false } return true } /** * 秒杀活动模型 */ export type SeckillActivity = { id ?: string merchant_id ?: string title : string single_limit : number total_limit : number product_count : number time_range : string start_date : string end_date : string status : boolean created_at ?: string updated_at ?: string } /** * 拼团活动模型 */ export type CombinationActivity = { id ?: string merchant_id ?: string uid : string nickname ?: string avatar ?: string product_id : string title ?: string cid ?: number people : number count_people : number start_time : string stop_time : string status : string // ongoing, pending, ended created_at ?: string updated_at ?: string } /** * 获取秒杀活动列表 */ export async function fetchSeckillActivities(query ?: { search ?: string, status ?: boolean, page ?: number, pageSize ?: number }) : Promise<{ total : number, items : SeckillActivity[] }> { let q = supa.from('ak_seckill_activities').select('*', { count: 'exact' }) if (query?.search != null && query.search !== '') { q = q.ilike('title', `%${query.search}%`) } if (query?.status != null) { q = q.eq('status', query.status) } const p = query?.page ?? 1 const ps = query?.pageSize ?? 20 const from = (p - 1) * ps const to = from + ps - 1 const { data, error, count } = await q .order('created_at', { ascending: false }) .range(from, to) .execute() if (error != null) { console.error('获取秒杀列表失败:', error) return { total: 0, items: [] as SeckillActivity[] } } return { total: count ?? 0, items: (data ?? []) as SeckillActivity[] } } /** * 保存秒杀活动 */ export async function saveSeckillActivity(activity : SeckillActivity) : Promise { const uid = getCurrentUid() if (uid == null) return false const { error } = await supa .from('ak_seckill_activities') .upsert({ ...activity, merchant_id: uid, updated_at: new Date().toISOString() }) .execute() return error == null } /** * 删除秒杀活动 */ export async function deleteSeckillActivity(id : string) : Promise { const { error } = await supa.from('ak_seckill_activities').delete().eq('id', id).execute() return error == null } /** * 获取拼团活动列表 */ export async function fetchCombinationActivities(query ?: { status ?: string, page ?: number, pageSize ?: number }) : Promise<{ total : number, items : CombinationActivity[] }> { // 关联用户和商品信息展示 let q = supa.from('ak_combination_activities').select('*, ak_users!uid(username, avatar_url), ml_products!product_id(name, cid)', { count: 'exact' }) if (query?.status != null && query.status !== '') { q = q.eq('status', query.status) } const p = query?.page ?? 1 const ps = query?.pageSize ?? 20 const from = (p - 1) * ps const to = from + ps - 1 const { data, error, count } = await q .order('created_at', { ascending: false }) .range(from, to) .execute() if (error != null) { console.error('获取拼团列表失败:', error) return { total: 0, items: [] as CombinationActivity[] } } const items = (data ?? []).map((item : any) : CombinationActivity => { return { ...item, nickname: item.ak_users?.username, avatar: item.ak_users?.avatar_url, title: item.ml_products?.name, cid: item.ml_products?.cid } as CombinationActivity }) return { total: count ?? 0, items } } /** * 获取拼团统计数据 */ export async function getCombinationStats() : Promise<{ participantCount : number, successCount : number }> { const { data: participants, error: err1 } = await supa.from('ak_combination_activities').select('count_people').execute() const { count: successCount, error: err2 } = await supa.from('ak_combination_activities').select('*', { count: 'exact', head: true }).eq('status', 'ended').execute() let totalParticipants = 0 if (participants != null) { participants.forEach((p: any) => { totalParticipants += (p.count_people as number) }) } return { participantCount: totalParticipants, successCount: successCount ?? 0 } } /** * 立即成团 */ export async function completeCombinationGroup(id : string) : Promise { const { error } = await supa .from('ak_combination_activities') .update({ status: 'ended', updated_at: new Date().toISOString() }) .eq('id', id) .execute() return error == null } /** * 砍价活动模型 */ export type BargainActivity = { id ?: string merchant_id ?: string product_id : string product_name ?: string product_image ?: string title : string min_price : number stock : number start_time : string stop_time : string status : boolean created_at ?: string updated_at ?: string } /** * 团购活动模型 */ export type GroupbuyActivity = { id ?: string merchant_id ?: string product_id : string product_name ?: string product_image ?: string title : string price : number people : number stock : number start_time : string stop_time : string status : boolean created_at ?: string updated_at ?: string } /** * 获取砍价活动列表 */ export async function fetchBargainActivities(query ?: { search ?: string, page ?: number, pageSize ?: number }) : Promise<{ total : number, items : BargainActivity[] }> { let q = supa.from('ak_marketing_bargains').select('*, ml_products!product_id(name, main_image_url)', { count: 'exact' }) if (query?.search != null && query.search !== '') { q = q.ilike('title', `%${query.search}%`) } const p = query?.page ?? 1 const ps = query?.pageSize ?? 20 const from = (p - 1) * ps const to = from + ps - 1 const { data, error, count } = await q .order('created_at', { ascending: false }) .range(from, to) .execute() if (error != null) { console.error('获取砍价列表失败:', error) return { total: 0, items: [] as BargainActivity[] } } const items = (data ?? []).map((item : any) : BargainActivity => { return { ...item, product_name: item.ml_products?.name, product_image: item.ml_products?.main_image_url } as BargainActivity }) return { total: count ?? 0, items } } /** * 保存砍价活动 */ export async function saveBargainActivity(activity : BargainActivity) : Promise { const uid = getCurrentUid() if (uid == null) return false const { error } = await supa .from('ak_marketing_bargains') .upsert({ ...activity, merchant_id: uid, updated_at: new Date().toISOString() }) .execute() return error == null } /** * 删除砍价活动 */ export async function deleteBargainActivity(id : string) : Promise { const { error } = await supa.from('ak_marketing_bargains').delete().eq('id', id).execute() return error == null } /** * 获取团购活动列表 */ export async function fetchGroupbuyActivities(query ?: { search ?: string, page ?: number, pageSize ?: number }) : Promise<{ total : number, items : GroupbuyActivity[] }> { let q = supa.from('ak_marketing_groupbuys').select('*, ml_products!product_id(name, main_image_url)', { count: 'exact' }) if (query?.search != null && query.search !== '') { q = q.ilike('title', `%${query.search}%`) } const p = query?.page ?? 1 const ps = query?.pageSize ?? 20 const from = (p - 1) * ps const to = from + ps - 1 const { data, error, count } = await q .order('created_at', { ascending: false }) .range(from, to) .execute() if (error != null) { console.error('获取团购列表失败:', error) return { total: 0, items: [] as GroupbuyActivity[] } } const items = (data ?? []).map((item : any) : GroupbuyActivity => { return { ...item, product_name: item.ml_products?.name, product_image: item.ml_products?.main_image_url } as GroupbuyActivity }) return { total: count ?? 0, items } } /** * 保存团购活动 */ export async function saveGroupbuyActivity(activity : GroupbuyActivity) : Promise { const uid = getCurrentUid() if (uid == null) return false const { error } = await supa .from('ak_marketing_groupbuys') .upsert({ ...activity, merchant_id: uid, updated_at: new Date().toISOString() }) .execute() return error == null } /** * 删除团购活动 */ export async function deleteGroupbuyActivity(id : string) : Promise { const { error } = await supa.from('ak_marketing_groupbuys').delete().eq('id', id).execute() return error == null } /** * 抽奖活动模型 */ export type LotteryActivity = { id ?: string merchant_id ?: string name : string type : number // 1积分抽奖, 2订单评价, 3订单支付 start_time : string end_time : string is_open : boolean created_at ?: string updated_at ?: string // 统计字段 memberCount ?: number winningMemberCount ?: number lotteryTimes ?: number winningTimes ?: number } /** * 抽奖奖品模型 */ export type LotteryPrize = { id ?: string lottery_id : string name : string prize_type : string // points, balance, coupon, physical amount : number stock : number probability : number sort_order : number } /** * 获取抽奖活动列表 */ export async function fetchLotteryList(query ?: { search ?: string, status ?: number, type ?: number, page ?: number, pageSize ?: number }) : Promise<{ total : number, items : LotteryActivity[] }> { let q = supa.from('ak_marketing_lotteries').select('*', { count: 'exact' }) if (query?.search != null && query.search !== '') { q = q.ilike('name', `%${query.search}%`) } if (query?.type != null && query.type !== 0) { q = q.eq('type', query.type) } // status 过滤逻辑通常涉及时间判断,此处简化为 is_open if (query?.status != null && query.status !== 0) { q = q.eq('is_open', query.status === 1) } const p = query?.page ?? 1 const ps = query?.pageSize ?? 20 const from = (p - 1) * ps const to = from + ps - 1 const { data, error, count } = await q .order('created_at', { ascending: false }) .range(from, to) .execute() if (error != null) { console.error('获取抽奖列表失败:', error) return { total: 0, items: [] as LotteryActivity[] } } return { total: count ?? 0, items: (data ?? []) as LotteryActivity[] } } /** * 保存抽奖活动 */ export async function saveLotteryActivity(activity : LotteryActivity) : Promise { const uid = getCurrentUid() if (uid == null) return false const { error } = await supa .from('ak_marketing_lotteries') .upsert({ ...activity, merchant_id: uid, updated_at: new Date().toISOString() }) .execute() return error == null } /** * 删除抽奖活动 */ export async function deleteLotteryActivity(id : string) : Promise { const { error } = await supa.from('ak_marketing_lotteries').delete().eq('id', id).execute() return error == null } /** * 主播模型 */ export type LiveAnchor = { id ?: string merchant_id ?: string nickname : string wechat : string | null phone : string | null avatar_url : string | null status : boolean created_at ?: string } /** * 直播间模型 */ export type LiveRoom = { id ?: string merchant_id ?: string anchor_id : string | null name : string background_url : string | null share_img_url : string | null start_time : string end_time : string sort : number type : string like_enabled : boolean sale_enabled : boolean comment_enabled : boolean is_show : boolean live_status : number // 1未开始, 2直播中, 3暂停, 4已结束 created_at ?: string updated_at ?: string // 关联字段 anchor_nick ?: string anchor_wechat ?: string } /** * 获取直播间列表 */ export async function fetchLiveRooms(query ?: { search ?: string, page ?: number, pageSize ?: number }) : Promise<{ total : number, items : LiveRoom[] }> { let q = supa.from('ak_marketing_live_rooms').select('*, ak_marketing_live_anchors(nickname, wechat)', { count: 'exact' }) if (query?.search != null && query.search !== '') { q = q.or(`name.ilike.%${query.search}%,ak_marketing_live_anchors.nickname.ilike.%${query.search}%`) } const p = query?.page ?? 1 const ps = query?.pageSize ?? 20 const from = (p - 1) * ps const to = from + ps - 1 const { data, error, count } = await q .order('sort', { ascending: false }) .range(from, to) .execute() if (error != null) { console.error('获取直播间列表失败:', error) return { total: 0, items: [] as LiveRoom[] } } const items = (data ?? []).map((item : any) : LiveRoom => { return { ...item, anchor_nick: item.ak_marketing_live_anchors?.nickname, anchor_wechat: item.ak_marketing_live_anchors?.wechat } as LiveRoom }) return { total: count ?? 0, items } } /** * 保存直播间 */ export async function saveLiveRoom(room : LiveRoom) : Promise { const uid = getCurrentUid() if (uid == null) return false const { error } = await supa .from('ak_marketing_live_rooms') .upsert({ ...room, merchant_id: uid, updated_at: new Date().toISOString() }) .execute() return error == null } /** * 删除直播间 */ export async function deleteLiveRoom(id : string) : Promise { const { error } = await supa.from('ak_marketing_live_rooms').delete().eq('id', id).execute() return error == null } /** * 获取主播列表 */ export async function fetchLiveAnchors() : Promise { const { data, error } = await supa .from('ak_marketing_live_anchors') .select('*') .eq('status', true) .order('created_at', { ascending: false }) .execute() if (error != null) return [] as LiveAnchor[] return (data ?? []) as LiveAnchor[] } /** * 保存主播 */ export async function saveLiveAnchor(anchor : LiveAnchor) : Promise { const uid = getCurrentUid() if (uid == null) return false const { error } = await supa .from('ak_marketing_live_anchors') .upsert({ ...anchor, merchant_id: uid }) .execute() return error == null } /** * 删除主播 */ export async function deleteLiveAnchor(id : string) : Promise { const { error } = await supa.from('ak_marketing_live_anchors').delete().eq('id', id).execute() return error == null } /** * 会员卡类型模型 */ export type MemberType = { id ?: string merchant_id ?: string name : string duration_days : number price : number discount_price : number is_open : boolean sort_order : number created_at ?: string updated_at ?: string } /** * 会员权益模型 */ export type MemberRight = { id ?: string merchant_id ?: string name : string description : string | null icon_url : string | null is_show : boolean sort_order : number created_at ?: string updated_at ?: string } /** * 会员基础配置模型 */ export type MemberConfig = { id ?: string merchant_id ?: string is_enabled : boolean bg_img_url : string | null expire_bg_img_url : string | null rules_description : string | null updated_at ?: string } /** * 获取会员类型列表 */ export async function fetchMemberTypes() : Promise { const { data, error } = await supa .from('ak_marketing_member_types') .select('*') .order('sort_order', { ascending: true }) .execute() if (error != null) { console.error('获取会员类型失败:', error) return [] as MemberType[] } return (data ?? []) as MemberType[] } /** * 保存会员类型 */ export async function saveMemberType(item : MemberType) : Promise { const uid = getCurrentUid() if (uid == null) return false const { error } = await supa .from('ak_marketing_member_types') .upsert({ ...item, merchant_id: uid, updated_at: new Date().toISOString() }) .execute() return error == null } /** * 删除会员类型 */ export async function deleteMemberType(id : string) : Promise { const { error } = await supa.from('ak_marketing_member_types').delete().eq('id', id).execute() return error == null } /** * 获取会员权益列表 */ export async function fetchMemberRights() : Promise { const { data, error } = await supa .from('ak_marketing_member_rights') .select('*') .order('sort_order', { ascending: true }) .execute() if (error != null) { console.error('获取会员权益失败:', error) return [] as MemberRight[] } return (data ?? []) as MemberRight[] } /** * 保存会员权益 */ export async function saveMemberRight(item : MemberRight) : Promise { const uid = getCurrentUid() if (uid == null) return false const { error } = await supa .from('ak_marketing_member_rights') .upsert({ ...item, merchant_id: uid, updated_at: new Date().toISOString() }) .execute() return error == null } /** * 删除会员权益 */ export async function deleteMemberRight(id : string) : Promise { const { error } = await supa.from('ak_marketing_member_rights').delete().eq('id', id).execute() return error == null } /** * 获取会员基础配置 */ export async function fetchMemberConfig() : Promise { const { data, error } = await supa .from('ak_marketing_member_config') .select('*') .eq('id', 'member_config') .single() .execute() if (error != null) return null return data as MemberConfig | null } /** * 保存会员基础配置 */ export async function saveMemberConfig(config : MemberConfig) : Promise { const uid = getCurrentUid() if (uid == null) return false const { error } = await supa .from('ak_marketing_member_config') .upsert({ ...config, id: 'member_config', merchant_id: uid, updated_at: new Date().toISOString() }) .execute() return error == null } /** * 充值基础配置模型 */ export type RechargeConfig = { id ?: string merchant_id ?: string balance_enabled : boolean recharge_notice : string | null mp_recharge_enabled : boolean min_recharge_amount : number updated_at ?: string } /** * 充值额度模板模型 */ export type RechargeQuota = { id ?: string merchant_id ?: string price : number bonus_price : number is_open : boolean sort_order : number created_at ?: string updated_at ?: string } /** * 充值记录模型 */ export type RechargeRecord = { id : string uid : string nickname ?: string avatar ?: string order_no : string recharge_type : string price : number give_price : number paid : number pay_time : string | null created_at : string } /** * 新人礼配置模型 */ export type NewcomerConfig = { id ?: string merchant_id ?: string balance_reward : number integral_reward : number coupons_json : any[] updated_at ?: string } /** * 获取充值基础配置 */ export async function fetchRechargeConfig() : Promise { const { data, error } = await supa .from('ak_recharge_configs') .select('*') .eq('id', 'recharge_config') .single() .execute() if (error != null) return null return data as RechargeConfig | null } /** * 获取新人礼配置 */ export async function fetchNewcomerConfig() : Promise { const { data, error } = await supa .from('ak_marketing_newcomer_config') .select('*') .eq('id', 'newcomer_config') .single() .execute() if (error != null) return null return data as NewcomerConfig | null } /** * 保存新人礼配置 */ export async function saveNewcomerConfig(config : NewcomerConfig) : Promise { const uid = getCurrentUid() if (uid == null) return false const { error } = await supa .from('ak_marketing_newcomer_config') .upsert({ ...config, id: 'newcomer_config', merchant_id: uid, updated_at: new Date().toISOString() }) .execute() return error == null } /** * 签到记录模型 */ export type SignInLog = { id : string uid : string nickname ?: string avatar ?: string points : number is_continuous_reward : boolean created_at : string } /** * 用户优惠券记录模型 */ export type UserCouponRecord = { id : string uid : string nickname ?: string avatar ?: string coupon_id : string coupon_name ?: string coupon_code : string status : number // 1:未使用, 2:已使用, 3:已过期 received_at : string used_at : string | null } /** * 获取签到记录列表 */ export async function fetchSignInRecords(query ?: { search ?: string, page ?: number, pageSize ?: number }) : Promise<{ total : number, items : SignInLog[] }> { let q = supa.from('ak_marketing_signin_logs').select('*, ak_users!uid(username, avatar_url)', { count: 'exact' }) if (query?.search != null && query.search !== '') { q = q.or(`ak_users.username.ilike.%${query.search}%`) } const p = query?.page ?? 1 const ps = query?.pageSize ?? 20 const from = (p - 1) * ps const to = from + ps - 1 const { data, error, count } = await q .order('created_at', { ascending: false }) .range(from, to) .execute() if (error != null) { console.error('获取签到记录失败:', error) return { total: 0, items: [] as SignInLog[] } } const items = (data ?? []).map((item : any) : SignInLog => { return { ...item, nickname: item.ak_users?.username, avatar: item.ak_users?.avatar_url } as SignInLog }) return { total: count ?? 0, items } } /** * 获取优惠券领取记录列表 */ export async function fetchCouponReceiveRecords(query ?: { search ?: string, page ?: number, pageSize ?: number }) : Promise<{ total : number, items : UserCouponRecord[] }> { let q = supa.from('ml_user_coupons').select('*, ak_users!uid(username, avatar_url), ml_coupon_templates!template_id(name)', { count: 'exact' }) if (query?.search != null && query.search !== '') { q = q.or(`ak_users.username.ilike.%${query.search}%,coupon_code.ilike.%${query.search}%`) } const p = query?.page ?? 1 const ps = query?.pageSize ?? 20 const from = (p - 1) * ps const to = from + ps - 1 const { data, error, count } = await q .order('received_at', { ascending: false }) .range(from, to) .execute() if (error != null) { console.error('获取优惠券领取记录失败:', error) return { total: 0, items: [] as UserCouponRecord[] } } const items = (data ?? []).map((item : any) : UserCouponRecord => { return { ...item, nickname: item.ak_users?.username, avatar: item.ak_users?.avatar_url, coupon_name: item.ml_coupon_templates?.name } as UserCouponRecord }) return { total: count ?? 0, items } } /** * 获取积分变动记录列表 */ export async function fetchPointsRecords(query ?: { search ?: string, page ?: number, pageSize ?: number }) : Promise<{ total : number, items : any[] }> { let q = supa.from('ml_user_bill').select('*, ak_users!uid(username, avatar_url)', { count: 'exact' }) q = q.eq('category', 'integral') if (query?.search != null && query.search !== '') { q = q.or(`ak_users.username.ilike.%${query.search}%,title.ilike.%${query.search}%`) } const p = query?.page ?? 1 const ps = query?.pageSize ?? 20 const from = (p - 1) * ps const to = from + ps - 1 const { data, error, count } = await q .order('created_at', { ascending: false }) .range(from, to) .execute() if (error != null) { console.error('获取积分记录失败:', error) return { total: 0, items: [] as any[] } } const items = (data ?? []).map((item : any) : any => { return { ...item, nickname: item.ak_users?.username, avatar: item.ak_users?.avatar_url } }) return { total: count ?? 0, items } } /** * 保存充值基础配置 */ export async function saveRechargeConfig(config : RechargeConfig) : Promise { const uid = getCurrentUid() if (uid == null) return false const { error } = await supa .from('ak_recharge_configs') .upsert({ ...config, id: 'recharge_config', merchant_id: uid, updated_at: new Date().toISOString() }) .execute() return error == null } /** * 获取充值额度列表 */ export async function fetchRechargeQuotas() : Promise { const { data, error } = await supa .from('ak_recharge_quotas') .select('*') .order('sort_order', { ascending: true }) .execute() if (error != null) return [] as RechargeQuota[] return (data ?? []) as RechargeQuota[] } /** * 保存充值额度 */ export async function saveRechargeQuota(item : RechargeQuota) : Promise { const uid = getCurrentUid() if (uid == null) return false const { error } = await supa .from('ak_recharge_quotas') .upsert({ ...item, merchant_id: uid, updated_at: new Date().toISOString() }) .execute() return error == null } /** * 删除充值额度 */ export async function deleteRechargeQuota(id : string) : Promise { const { error } = await supa.from('ak_recharge_quotas').delete().eq('id', id).execute() return error == null } /** * 获取充值记录列表 */ export async function fetchRechargeRecords(query ?: { search ?: string, page ?: number, pageSize ?: number }) : Promise<{ total : number, items : RechargeRecord[] }> { // 关联用户昵称展示 let q = supa.from('ml_user_recharge').select('*, ak_users!uid(username, avatar_url)', { count: 'exact' }) if (query?.search != null && query.search !== '') { q = q.or(`order_no.ilike.%${query.search}%,ak_users.username.ilike.%${query.search}%`) } const p = query?.page ?? 1 const ps = query?.pageSize ?? 20 const from = (p - 1) * ps const to = from + ps - 1 const { data, error, count } = await q .order('created_at', { ascending: false }) .range(from, to) .execute() if (error != null) { console.error('获取充值记录失败:', error) return { total: 0, items: [] as RechargeRecord[] } } const items = (data ?? []).map((item : any) : RechargeRecord => { return { ...item, nickname: item.ak_users?.username, avatar: item.ak_users?.avatar_url } as RechargeRecord }) return { total: count ?? 0, items } } /** * 删除优惠券模板 */ export async function deleteCouponTemplate(id: string): Promise { const { error } = await supa .from('ml_coupon_templates') .delete() .eq('id', id) .execute() if (error != null) { console.error('删除优惠券失败:', error) return false } return true }