consumerm模块完成度90%,完善消费者和商家端数据库表,商品、聊天、订单数据对接好了supabase,和商家端对接了聊天功能,安卓端编译通过了css样式,剩余几个页面在处理函数规范问题

This commit is contained in:
cyh666666
2026-02-24 17:17:49 +08:00
parent e2f1dfb097
commit e606c597ca
174 changed files with 37917 additions and 4444 deletions

View File

@@ -79,11 +79,11 @@ export class AkSupaQueryBuilder {
like(field : string, value : any) : AkSupaQueryBuilder { return this._addCond(field, 'like', value); }
ilike(field : string, value : any) : AkSupaQueryBuilder { return this._addCond(field, 'ilike', value); }
in(field : string, value : any[]) : AkSupaQueryBuilder { return this._addCond(field, 'in', value); }
is(field : string, value : any) : AkSupaQueryBuilder { return this._addCond(field, 'is', value); }
is(field : string, value : any | null) : AkSupaQueryBuilder { return this._addCond(field, 'is', value); }
contains(field : string, value : any) : AkSupaQueryBuilder { return this._addCond(field, 'cs', value); }
containedBy(field : string, value : any) : AkSupaQueryBuilder { return this._addCond(field, 'cd', value); }
not(field : string, opOrValue : any, value?: any) : AkSupaQueryBuilder {
if (value !== undefined) {
not(field : string, opOrValue : any, value: any | null = null) : AkSupaQueryBuilder {
if (value != null) {
// 三元形式field, operator, value
// 例如 not('badge', 'is', null) -> badge=not.is.null
const combinedOp = 'not.' + opOrValue;
@@ -113,7 +113,7 @@ export class AkSupaQueryBuilder {
return this;
}
private _addCond(afield : string, op : string, value : any) : AkSupaQueryBuilder {
private _addCond(afield : string, op : string, value : any | null) : AkSupaQueryBuilder {
//console.log('add cond:', op, afield, value)
const field = encodeURIComponent(afield)!!
// 将 null 转换为字符串 'null',避免构造对象时缺少 value 属性
@@ -266,7 +266,7 @@ export class AkSupaQueryBuilder {
return params.length > 0 ? params.join('&') : null;
}
select(columns ?: string, opt ?: UTSJSONObject) : AkSupaQueryBuilder {
select(columns : string = "*", opt : UTSJSONObject | null = null) : AkSupaQueryBuilder {
this._action = 'select';
if (columns != null) {
this._options.columns = columns;
@@ -692,7 +692,7 @@ export class AkSupa {
token_type: 'bearer',
expires_in: 0,
raw: user
} as any;
} as AkSupaSignInResult;
}
return true;
} catch (e) {
@@ -999,6 +999,22 @@ async delete(table : string, filter : string | null) : Promise<AkReqResponse<any
from(tableName : string) : AkSupaQueryBuilder {
return new AkSupaQueryBuilder(this, tableName);
}
/**
* 创建实时订阅通道 (兼容 Supabase Realtime 接口,目前使用轮询模拟)
* @param topic 通道名称,如 public:table
*/
channel(topic: string): AkSupaRealtimeChannel {
return new AkSupaRealtimeChannel(this, topic);
}
/**
* 移除通道
*/
removeChannel(channel: AkSupaRealtimeChannel): Promise<string> {
channel.unsubscribe();
return Promise.resolve('ok');
}
// AkSupa类内新增自动刷新session
async refreshSession() : Promise<boolean> {
if (this.session == null || this.session?.refresh_token == null) return false;
@@ -1133,4 +1149,122 @@ export function createClient(url : string, key : string) : AkSupa {
return new AkSupa(url, key);
}
// 模拟 Realtime Channel 类 (Polling Fallback)
export class AkSupaRealtimeChannel {
private _supa: AkSupa;
private _topic: string;
private _timer: number = 0;
private _callback: ((payload: any) => void) | null = null;
private _table: string = '';
private _lastTime: string = new Date().toISOString();
private _isSubscribed: boolean = false;
constructor(supa: AkSupa, topic: string) {
this._supa = supa;
this._topic = topic;
}
// 绑定事件 (仅支持 postgres_changes INSERT)
on(type: string, filter: UTSJSONObject, callback: (payload: any) => void): AkSupaRealtimeChannel {
// 解析 table
const table = filter.getString('table');
if (table != null) {
this._table = table;
}
this._callback = callback;
return this;
}
// 开始订阅
subscribe(callback?: (status: string, err: any | null) => void): AkSupaRealtimeChannel {
if (this._isSubscribed) return this;
this._isSubscribed = true;
// 初始回调
if (callback != null) {
callback('SUBSCRIBED', null);
}
// 如果没有指定 table无法轮询
if (this._table == '') {
console.warn('Realtime check: No table specified for polling.');
return this;
}
// 开始轮询 (每3秒)
this._timer = setInterval(() => {
this._checkUpdates();
}, 3000);
return this;
}
// 停止订阅
unsubscribe() {
if (this._timer > 0) {
clearInterval(this._timer);
this._timer = 0;
}
this._isSubscribed = false;
}
// 检查更新
private async _checkUpdates() {
if (!this._isSubscribed || this._table == '') return;
try {
const now = new Date().toISOString();
const res = await this._supa
.from(this._table)
.select('*')
.gt('created_at', this._lastTime)
.order('created_at', { ascending: true })
.execute();
if (res.error == null && res.data != null) {
let list: any[] = [];
if (Array.isArray(res.data)) {
list = res.data as any[];
}
if (list.length > 0) {
// 更新最后时间
const lastItem = list[list.length - 1];
let lastTimeStr: string | null = null;
if (lastItem instanceof UTSJSONObject) {
lastTimeStr = lastItem.getString('created_at');
} else {
// 尝试转 json
const j = JSON.parse(JSON.stringify(lastItem)) as UTSJSONObject;
lastTimeStr = j.getString('created_at');
}
if (lastTimeStr != null) {
this._lastTime = lastTimeStr;
} else {
this._lastTime = now;
}
// 触发回调
if (this._callback != null) {
// 模拟 Realtime payload
list.forEach(item => {
const payload = {
new: item,
eventType: 'INSERT',
old: null
};
this._callback?.(payload);
});
}
}
}
} catch (e) {
console.error('Realtime polling error:', e);
}
}
}
export default AkSupa;