status.ts 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. /**
  2. * 状态查询相关API
  3. * 用于查询设备状态、识别结果和订单信息
  4. */
  5. import { post } from '../utils/request';
  6. /**
  7. * 设备状态响应
  8. */
  9. export interface DeviceStatusResponse {
  10. deviceId: string;
  11. doorStatus: 'open' | 'close' | 'error' | 'busy' | 'unknown';
  12. activityId: string;
  13. userId: string;
  14. status: string;
  15. openType: string;
  16. timestamp: string;
  17. }
  18. /**
  19. * 识别结果响应
  20. */
  21. export interface RecognizeResultResponse {
  22. activityId: string;
  23. deviceId: string;
  24. userId: string;
  25. nobuy: string;
  26. result: string;
  27. skuList: string;
  28. resourceInfo: string;
  29. timestamp: string;
  30. }
  31. /**
  32. * 订单信息响应
  33. */
  34. export interface OrderInfoResponse {
  35. orderId: string;
  36. activityId: string;
  37. deviceId: string;
  38. userId: string;
  39. orderName: string;
  40. totalAmount: string;
  41. products: string;
  42. timestamp: string;
  43. }
  44. /**
  45. * 综合状态响应
  46. */
  47. export interface AllStatusResponse {
  48. deviceStatus: DeviceStatusResponse;
  49. recognizeResult?: RecognizeResultResponse;
  50. orderInfo?: OrderInfoResponse;
  51. }
  52. /**
  53. * 查询设备状态
  54. * @param deviceId 设备ID
  55. */
  56. export const queryDeviceStatus = (deviceId: string): Promise<DeviceStatusResponse> => {
  57. return post<DeviceStatusResponse>('/status/device', { deviceId }, { silent: true });
  58. };
  59. /**
  60. * 查询识别结果
  61. * @param activityId 活动ID
  62. */
  63. export const queryRecognizeResult = (activityId: string): Promise<RecognizeResultResponse> => {
  64. return post<RecognizeResultResponse>('/status/recognize', { activityId }, { silent: true });
  65. };
  66. /**
  67. * 查询订单信息
  68. * @param orderId 订单ID
  69. * @param activityId 活动ID(与orderId二选一)
  70. */
  71. export const queryOrderInfo = (orderId?: string, activityId?: string): Promise<OrderInfoResponse> => {
  72. return post<OrderInfoResponse>('/status/order', { orderId, activityId }, { silent: true });
  73. };
  74. /**
  75. * 综合状态查询
  76. * @param deviceId 设备ID
  77. */
  78. export const queryAllStatus = (deviceId: string): Promise<AllStatusResponse> => {
  79. return post<AllStatusResponse>('/status/all', { deviceId }, { silent: true });
  80. };
  81. /**
  82. * 轮询设备状态
  83. * @param deviceId 设备ID
  84. * @param timeout 超时时间(毫秒)
  85. * @param interval 轮询间隔(毫秒)
  86. */
  87. export const pollDeviceStatus = (
  88. deviceId: string,
  89. timeout: number = 60000,
  90. interval: number = 3000,
  91. maxConsecutiveFailures: number = 3
  92. ): Promise<DeviceStatusResponse> => {
  93. return new Promise((resolve, reject) => {
  94. const startTime = Date.now();
  95. let timerId: ReturnType<typeof setTimeout> | null = null;
  96. let settled = false;
  97. let failCount = 0;
  98. const cleanup = () => {
  99. if (timerId !== null) {
  100. clearTimeout(timerId);
  101. timerId = null;
  102. }
  103. };
  104. const done = (result: DeviceStatusResponse | null, err?: Error) => {
  105. if (settled) return;
  106. settled = true;
  107. cleanup();
  108. if (err) {
  109. reject(err);
  110. } else if (result) {
  111. resolve(result);
  112. }
  113. };
  114. const poll = async () => {
  115. if (settled) return;
  116. try {
  117. const response = await queryDeviceStatus(deviceId);
  118. if (settled) return;
  119. // 成功一次就重置失败计数
  120. failCount = 0;
  121. if (response && response.doorStatus && response.doorStatus !== 'unknown') {
  122. const statusTime = parseInt(response.timestamp) || 0;
  123. if (statusTime > startTime - 10000) {
  124. done(response);
  125. return;
  126. }
  127. }
  128. if (Date.now() - startTime >= timeout) {
  129. done(null, new Error('轮询超时'));
  130. return;
  131. }
  132. timerId = setTimeout(poll, interval);
  133. } catch (error: any) {
  134. if (settled) return;
  135. // 认证失败立即中止,不重试
  136. if (error?.isAuthError) {
  137. done(null, new Error('登录已过期,请重新登录'));
  138. return;
  139. }
  140. failCount++;
  141. if (failCount >= maxConsecutiveFailures) {
  142. done(null, new Error('网络异常,轮询中断'));
  143. return;
  144. }
  145. if (Date.now() - startTime >= timeout) {
  146. done(null, new Error('轮询超时'));
  147. return;
  148. }
  149. timerId = setTimeout(poll, interval);
  150. }
  151. };
  152. poll();
  153. });
  154. };
  155. /**
  156. * 轮询识别结果
  157. * @param activityId 活动ID
  158. * @param timeout 超时时间(毫秒)
  159. * @param interval 轮询间隔(毫秒)
  160. */
  161. export const pollRecognizeResult = (
  162. activityId: string,
  163. timeout: number = 30000,
  164. interval: number = 3000,
  165. maxConsecutiveFailures: number = 3
  166. ): Promise<RecognizeResultResponse> => {
  167. return new Promise((resolve, reject) => {
  168. const startTime = Date.now();
  169. let timerId: ReturnType<typeof setTimeout> | null = null;
  170. let settled = false;
  171. let failCount = 0;
  172. const cleanup = () => {
  173. if (timerId !== null) {
  174. clearTimeout(timerId);
  175. timerId = null;
  176. }
  177. };
  178. const done = (result: RecognizeResultResponse | null, err?: Error) => {
  179. if (settled) return;
  180. settled = true;
  181. cleanup();
  182. if (err) {
  183. reject(err);
  184. } else if (result) {
  185. resolve(result);
  186. }
  187. };
  188. const poll = async () => {
  189. if (settled) return;
  190. try {
  191. const result = await queryRecognizeResult(activityId);
  192. if (settled) return;
  193. failCount = 0;
  194. if (result && result.result) {
  195. done(result);
  196. return;
  197. }
  198. if (Date.now() - startTime >= timeout) {
  199. done(null, new Error('识别超时'));
  200. return;
  201. }
  202. timerId = setTimeout(poll, interval);
  203. } catch (error: any) {
  204. if (settled) return;
  205. // 认证失败立即中止,不重试
  206. if (error?.isAuthError) {
  207. done(null, new Error('登录已过期,请重新登录'));
  208. return;
  209. }
  210. failCount++;
  211. if (failCount >= maxConsecutiveFailures) {
  212. done(null, new Error('网络异常,轮询中断'));
  213. return;
  214. }
  215. if (Date.now() - startTime >= timeout) {
  216. done(null, new Error('识别超时'));
  217. return;
  218. }
  219. timerId = setTimeout(poll, interval);
  220. }
  221. };
  222. poll();
  223. });
  224. };
  225. /**
  226. * 轮询订单信息
  227. * @param activityId 活动ID
  228. * @param timeout 超时时间(毫秒)
  229. * @param interval 轮询间隔(毫秒)
  230. */
  231. export const pollOrderInfo = (
  232. activityId: string,
  233. timeout: number = 30000,
  234. interval: number = 3000,
  235. maxConsecutiveFailures: number = 3
  236. ): Promise<OrderInfoResponse> => {
  237. return new Promise((resolve, reject) => {
  238. const startTime = Date.now();
  239. let timerId: ReturnType<typeof setTimeout> | null = null;
  240. let settled = false;
  241. let failCount = 0;
  242. const cleanup = () => {
  243. if (timerId !== null) {
  244. clearTimeout(timerId);
  245. timerId = null;
  246. }
  247. };
  248. const done = (result: OrderInfoResponse | null, err?: Error) => {
  249. if (settled) return;
  250. settled = true;
  251. cleanup();
  252. if (err) {
  253. reject(err);
  254. } else if (result) {
  255. resolve(result);
  256. }
  257. };
  258. const poll = async () => {
  259. if (settled) return;
  260. try {
  261. const order = await queryOrderInfo(undefined, activityId);
  262. if (settled) return;
  263. failCount = 0;
  264. if (order && order.orderId) {
  265. done(order);
  266. return;
  267. }
  268. if (Date.now() - startTime >= timeout) {
  269. done(null, new Error('获取订单超时'));
  270. return;
  271. }
  272. timerId = setTimeout(poll, interval);
  273. } catch (error: any) {
  274. if (settled) return;
  275. // 认证失败立即中止,不重试
  276. if (error?.isAuthError) {
  277. done(null, new Error('登录已过期,请重新登录'));
  278. return;
  279. }
  280. failCount++;
  281. if (failCount >= maxConsecutiveFailures) {
  282. done(null, new Error('网络异常,轮询中断'));
  283. return;
  284. }
  285. if (Date.now() - startTime >= timeout) {
  286. done(null, new Error('获取订单超时'));
  287. return;
  288. }
  289. timerId = setTimeout(poll, interval);
  290. }
  291. };
  292. poll();
  293. });
  294. };