Merge branch 'main' of 47.107.44.202:xionglijia/18xchat

This commit is contained in:
2025-09-11 17:07:35 +08:00
12 changed files with 122 additions and 37 deletions
+1 -1
View File
@@ -41,7 +41,7 @@ export class LoginUI extends li_BaseView {
const deviceId = DeviceIdService.I.id;
const os = MachineInfoService.I.getMachineOs();
const reqData = {
platType: "guset",
platType: "guest",
userId: deviceId,
device: deviceId,
os,
+1
View File
@@ -62,6 +62,7 @@ export class OrderData extends BaseData {
vo.status = mapQueryOrderStatus(res.status);
vo.retCode = res.retCode ?? 0;
this._orders.set(orderId, vo);
console.log("更新订单数据:", vo);
}
/** 获取所有需要继续轮询查询的订单 id */
+44 -10
View File
@@ -6,41 +6,75 @@ import { PaymentApi } from "./PaymentApi";
import proto from 'db://assets/Scripts/proto/proto.pb.js';
export interface PollOptions {
maxDurationMs: number; // 轮询时长(例如 2分钟)
startIntervalMs: number; // 初始间隔(例如 2s
maxIntervalMs: number; // 最大间隔(例如 10s
jitter?: boolean; // 抖动
maxDurationMs: number; // 最大轮询时长(超过就自动退出)(例如 2分钟)
startIntervalMs: number; // 第一次轮询等待的间隔(例如 2s
maxIntervalMs: number; // 最大允许的间隔,防止指数增长过大(例如 10s
jitter?: boolean; // 是否启用抖动(让等待时间带点随机性,避免所有客户端同一时间请求服务器)
}
export class PaymentPoller {
private cancelled = false;
private currentPollCount = 0; // 轮询次数
// 取消轮询
cancel() { this.cancelled = true; }
cancel() {
this.cancelled = true;
}
// 轮询订单
async poll(orderId: string, opt: PollOptions, onTick?: (d: proto.cs.ICSQueryOrderRes) => void): Promise<proto.cs.ICSQueryOrderRes | null> {
// 获取当前轮询次数
public getCurrentPollCount(): number {
return this.currentPollCount;
}
/**
* 轮询订单
* @param orderId 要查询的订单 ID
* @param opt 轮询配置(间隔、最大时长、是否抖动)
* @param onTick 可选回调函数,每次轮询到结果时调用
* @returns 最终查询到的订单结果 ICSQueryOrderRes,或者 null(超时/被取消)
*/
async poll(orderId: string, opt: PollOptions, onTick?: (d: proto.cs.ICSQueryOrderRes) => void): Promise<proto.cs.ICSQueryOrderRes> {
const t0 = Date.now();
let interval = opt.startIntervalMs;
while (!this.cancelled) {
// 增加轮询次数
this.currentPollCount++;
// 查询
let reqData = {orderId: orderId};
console.log("第 ", this.currentPollCount, " 次轮询订单的请求数据:", reqData);
const r = await PaymentApi.I.queryOrder(reqData);
console.log("第 ", this.currentPollCount, " 次轮询订单的响应数据:", r);
if (r.code === proto.cs.EnmRetCode.SUCCESS && r.data) {
// 回调
onTick?.(r.data);
// 订单状态,-1:失败,0:成功,1:处理中
if ([-1, 0].indexOf(r.data.status) !== -1) {
return r.data;
}
}
// 退出条件
if (Date.now() - t0 > opt.maxDurationMs) return null;
// 轮询超时
if (Date.now() - t0 > opt.maxDurationMs) {
let resultData: proto.cs.ICSQueryOrderRes = {
status: 92, // 自定义的状态,代表客户端轮询超时
retCode: 0,
balance: 0,
vipExpire: 0,
}
return resultData;
}
// 等待
await this.sleep(this.jitter(interval, opt));
// 每次轮询后,interval 会按照 1.5 倍增长,直到达到最大间隔 maxIntervalMs,避免在短时间内重复请求
interval = Math.min(interval * 1.5, opt.maxIntervalMs);
}
return null;
// 至此,代表取消了轮询订单
let resultData: proto.cs.ICSQueryOrderRes = {
status: 90, // 自定义的状态,代表取消
retCode: 0,
balance: 0,
vipExpire: 0,
}
return resultData;
}
// 用来暂停一定的时间(ms 毫秒),并让轮询进入等待状态
@@ -32,12 +32,12 @@ export class PaymentService {
}
/** 发起支付:创建订单→打开URL→回到App后开始轮询 */
async startPayment(req: proto.cs.ICSCreateOrderReq, open: OpenStrategy = "system"): Promise<proto.cs.ICSQueryOrderRes | null> {
async startPayment(req: proto.cs.ICSCreateOrderReq, open: OpenStrategy = "system"): Promise<proto.cs.ICSQueryOrderRes> {
// 下单
const create = await PaymentApi.I.createOrder(req);
console.log("创建订单的响应数据:", create);
if (create.code !== proto.cs.EnmRetCode.SUCCESS || !create.data) {
return { status: -1, retCode: -1 };
return { status: -1, retCode: -1, balance: 0, vipExpire: 0};
}
const { orderId, payUrl } = create.data;
this.bus.emit(PaymentEvents.Started, { orderId });
@@ -108,6 +108,7 @@ export class PaymentService {
return new Promise<void>((resolve) => {
let finished = false;
const finish = () => {
console.log("应用回到 finish");
if (finished) return;
finished = true;
// 安全移除监听
@@ -117,6 +118,7 @@ export class PaymentService {
const onShow = () => {
// 应用回到前台
console.log("应用回到了前台");
finish();
};
@@ -124,8 +126,8 @@ export class PaymentService {
game.on(Game.EVENT_SHOW, onShow, this);
// 兜底:某些环境(桌面浏览器)可能不会触发隐藏/显示事件,避免一直卡住
const FALLBACK_MS = 15000; // 按需调整
setTimeout(finish, FALLBACK_MS);
const FALLBACK_MS = 25000; // 按需调整
// setTimeout(finish, FALLBACK_MS);
});
}
}
@@ -22,13 +22,15 @@ export function mapQueryOrderStatus(
case 1: return "PENDING"; // 处理中
case 0: return "SUCCESS"; // 成功
case -1: return "FAILED"; // 失败
case 90: return "CANCELED"; // 取消
case 92: return "TIMEOUT"; // 轮询超时
default: return "CREATED"; // 未知/初始,按“已创建”兜底
}
}
/** 是否为终态(无需再轮询) */
export function isTerminalStatus(s: OrderStatus): boolean {
return s === "SUCCESS" || s === "FAILED" || s === "CANCELED" || s === "EXPIRED";
return s === "SUCCESS" || s === "FAILED" || s === "CANCELED" || s === "EXPIRED" || s === "TIMEOUT";
}
/** 是否应继续轮询查询订单 */
+1
View File
@@ -9,6 +9,7 @@ export type OrderStatus =
| "SUCCESS" // 成功
| "FAILED" // 失败
| "CANCELED" // 取消
| "TIMEOUT" // 轮询超时
| "EXPIRED"; // 过期
// 订单数据的数据结构
@@ -186,10 +186,19 @@ export class PurchasePanel extends li_BaseView {
const reqData = {
goodId
};
// let res = await PaymentService.I.startPayment(reqData);
// if (res) {
// }
let res = await PaymentService.I.startPayment(reqData);
console.log("支付完成的响应数据:", res);
if (res) {
let status = res.status;
if (status === -1) {
console.log("支付失败!!!,错误码:", res.retCode);
} else if (status === 0) {
console.log("支付成功!!!");
const walletData = DataManager.I.getDataById<WalletData>(DataId.Wallet);
walletData.balance = Number(res.balance);
walletData.vipExpire = Number(res.vipExpire);
}
}
}
onDestroy() {
+17 -5
View File
@@ -5802,11 +5802,23 @@ static getTypeUrl(typeUrlPrefix?: string): string;
/** EnmRetCode enum. */
enum EnmRetCode {
SUCCESS = 0,
GenJWTFailed = 5010,
InvalidArg = 5013,
MySQL = 5027,
TokenExpire = 5046,
TokenInvalid = 5047
GenJWTFailed = 5000,
Insufficient = 5001,
InvalidArg = 5002,
InvalidSign = 5003,
JsonFormat = 5004,
MySQL = 5005,
NotRegist = 5006,
NotResConfig = 5007,
Rate_Limited = 5008,
Redis = 5009,
Request_Too_More = 5010,
RMGOrder_Code = 5011,
RMGOrder_Http = 5012,
RMGOrder_MaxQuery = 5013,
RMGOrder_NotFound = 5014,
TokenExpire = 5015,
TokenInvalid = 5016
}
}
+34 -10
View File
@@ -13854,20 +13854,44 @@ $root.cs = (function() {
* @name cs.EnmRetCode
* @enum {number}
* @property {number} SUCCESS=0 SUCCESS value
* @property {number} GenJWTFailed=5010 GenJWTFailed value
* @property {number} InvalidArg=5013 InvalidArg value
* @property {number} MySQL=5027 MySQL value
* @property {number} TokenExpire=5046 TokenExpire value
* @property {number} TokenInvalid=5047 TokenInvalid value
* @property {number} GenJWTFailed=5000 GenJWTFailed value
* @property {number} Insufficient=5001 Insufficient value
* @property {number} InvalidArg=5002 InvalidArg value
* @property {number} InvalidSign=5003 InvalidSign value
* @property {number} JsonFormat=5004 JsonFormat value
* @property {number} MySQL=5005 MySQL value
* @property {number} NotRegist=5006 NotRegist value
* @property {number} NotResConfig=5007 NotResConfig value
* @property {number} Rate_Limited=5008 Rate_Limited value
* @property {number} Redis=5009 Redis value
* @property {number} Request_Too_More=5010 Request_Too_More value
* @property {number} RMGOrder_Code=5011 RMGOrder_Code value
* @property {number} RMGOrder_Http=5012 RMGOrder_Http value
* @property {number} RMGOrder_MaxQuery=5013 RMGOrder_MaxQuery value
* @property {number} RMGOrder_NotFound=5014 RMGOrder_NotFound value
* @property {number} TokenExpire=5015 TokenExpire value
* @property {number} TokenInvalid=5016 TokenInvalid value
*/
cs.EnmRetCode = (function() {
var valuesById = {}, values = Object.create(valuesById);
values[valuesById[0] = "SUCCESS"] = 0;
values[valuesById[5010] = "GenJWTFailed"] = 5010;
values[valuesById[5013] = "InvalidArg"] = 5013;
values[valuesById[5027] = "MySQL"] = 5027;
values[valuesById[5046] = "TokenExpire"] = 5046;
values[valuesById[5047] = "TokenInvalid"] = 5047;
values[valuesById[5000] = "GenJWTFailed"] = 5000;
values[valuesById[5001] = "Insufficient"] = 5001;
values[valuesById[5002] = "InvalidArg"] = 5002;
values[valuesById[5003] = "InvalidSign"] = 5003;
values[valuesById[5004] = "JsonFormat"] = 5004;
values[valuesById[5005] = "MySQL"] = 5005;
values[valuesById[5006] = "NotRegist"] = 5006;
values[valuesById[5007] = "NotResConfig"] = 5007;
values[valuesById[5008] = "Rate_Limited"] = 5008;
values[valuesById[5009] = "Redis"] = 5009;
values[valuesById[5010] = "Request_Too_More"] = 5010;
values[valuesById[5011] = "RMGOrder_Code"] = 5011;
values[valuesById[5012] = "RMGOrder_Http"] = 5012;
values[valuesById[5013] = "RMGOrder_MaxQuery"] = 5013;
values[valuesById[5014] = "RMGOrder_NotFound"] = 5014;
values[valuesById[5015] = "TokenExpire"] = 5015;
values[valuesById[5016] = "TokenInvalid"] = 5016;
return values;
})();
Binary file not shown.