Files
medical-mall/services/admin/marketingService.uts
2026-02-15 16:37:37 +08:00

1246 lines
31 KiB
Plaintext

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<IntegralStats | null> {
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<boolean> {
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<SignInConfig | null> {
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<boolean> {
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<boolean> {
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<boolean> {
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<boolean> {
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<boolean> {
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<boolean> {
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<boolean> {
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<boolean> {
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<boolean> {
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<boolean> {
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<boolean> {
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<boolean> {
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<boolean> {
const { error } = await supa.from('ak_marketing_live_rooms').delete().eq('id', id).execute()
return error == null
}
/**
* 获取主播列表
*/
export async function fetchLiveAnchors() : Promise<LiveAnchor[]> {
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<boolean> {
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<boolean> {
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<MemberType[]> {
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<boolean> {
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<boolean> {
const { error } = await supa.from('ak_marketing_member_types').delete().eq('id', id).execute()
return error == null
}
/**
* 获取会员权益列表
*/
export async function fetchMemberRights() : Promise<MemberRight[]> {
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<boolean> {
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<boolean> {
const { error } = await supa.from('ak_marketing_member_rights').delete().eq('id', id).execute()
return error == null
}
/**
* 获取会员基础配置
*/
export async function fetchMemberConfig() : Promise<MemberConfig | null> {
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<boolean> {
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<RechargeConfig | null> {
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<NewcomerConfig | null> {
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<boolean> {
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<boolean> {
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<RechargeQuota[]> {
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<boolean> {
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<boolean> {
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<boolean> {
const { error } = await supa
.from('ml_coupon_templates')
.delete()
.eq('id', id)
.execute()
if (error != null) {
console.error('删除优惠券失败:', error)
return false
}
return true
}