TreeApi.ts 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969
  1. import { DataModel, transformArrayDataModel, transformDataModel, type KeyValue, type NewDataModel } from '@imengyu/js-request-transform';
  2. import { AppServerRequestModule } from '../RequestModules';
  3. import { transformSomeToArray } from '../Utils';
  4. import type { RequestApiResult } from '@imengyu/imengyu-utils';
  5. /** 流水/日志通用字段 */
  6. abstract class GrowthLogBase extends DataModel<GrowthLogBase> {
  7. id!: number;
  8. villageId = null as number | null;
  9. userId = null as number | null;
  10. sourceTable = '';
  11. sourceId = null as number | null;
  12. remark = '';
  13. createtime = '' as string | null;
  14. updatetime = '' as string | null;
  15. deletetime = '' as string | null;
  16. villageName = '';
  17. nickname = '';
  18. username = '';
  19. volunteerName = '';
  20. title = '';
  21. }
  22. export class GrowthTaskLogItem extends GrowthLogBase {
  23. constructor() {
  24. super(GrowthTaskLogItem, '任务日志');
  25. this.setNameMapperCase('Camel', 'Snake');
  26. this._convertTable = {
  27. id: { clientSide: 'number', serverSide: 'number', clientSideRequired: true },
  28. rewardWater: { clientSide: 'number', serverSide: 'number' },
  29. rewardLight: { clientSide: 'number', serverSide: 'number' },
  30. rewardFruit: { clientSide: 'number', serverSide: 'number' },
  31. };
  32. this._afterSolveServer = () => {
  33. this.title = this.nickname + ' ' + this.taskName + ' ';
  34. if (this.rewardWater)
  35. this.title += ' +' + this.rewardWater + '乡源水 ';
  36. if (this.rewardLight)
  37. this.title += ' +' + this.rewardLight + '乡源光 ';
  38. if (this.rewardFruit)
  39. this.title += ' +' + this.rewardFruit + '乡源果 ';
  40. }
  41. }
  42. taskKey = '';
  43. taskName = '';
  44. rewardWater = 0;
  45. rewardLight = 0;
  46. rewardFruit = 0;
  47. }
  48. export class GrowthFruitLogItem extends GrowthLogBase {
  49. constructor() {
  50. super(GrowthFruitLogItem, '乡源果流水');
  51. this.setNameMapperCase('Camel', 'Snake');
  52. this._convertTable = {
  53. id: { clientSide: 'number', serverSide: 'number', clientSideRequired: true },
  54. fruit: { clientSide: 'number', serverSide: 'number' },
  55. beforeFruit: { clientSide: 'number', serverSide: 'number' },
  56. afterFruit: { clientSide: 'number', serverSide: 'number' },
  57. };
  58. this._afterSolveServer = () => {
  59. this.title = this.nickname + ' ' + this.typeText + '获得 ' + this.fruit + ' 乡源果';
  60. }
  61. }
  62. type = '' as 'pick' | 'task' | 'bless' | 'exchange' | string;
  63. typeText = '';
  64. fruit = 0;
  65. beforeFruit = 0;
  66. afterFruit = 0;
  67. }
  68. export class GrowthLightLogItem extends GrowthLogBase {
  69. constructor() {
  70. super(GrowthLightLogItem, '乡源光流水');
  71. this.setNameMapperCase('Camel', 'Snake');
  72. this._convertTable = {
  73. id: { clientSide: 'number', serverSide: 'number', clientSideRequired: true },
  74. light: { clientSide: 'number', serverSide: 'number' },
  75. beforeLight: { clientSide: 'number', serverSide: 'number' },
  76. afterLight: { clientSide: 'number', serverSide: 'number' },
  77. };
  78. this._afterSolveServer = () => {
  79. this.title = this.nickname + ' ' + this.typeText + '获得 ' + this.light + ' 乡源光';
  80. }
  81. }
  82. type = '' as 'water' | 'fertilize' | 'task' | 'bless' | string;
  83. typeText = '';
  84. light = 0;
  85. beforeLight = 0;
  86. afterLight = 0;
  87. }
  88. export class BlessPackageItem extends DataModel<BlessPackageItem> {
  89. constructor() {
  90. super(BlessPackageItem, '赐福套餐');
  91. this.setNameMapperCase('Camel', 'Snake');
  92. this._convertTable = {
  93. id: { clientSide: 'number', serverSide: 'number', clientSideRequired: true },
  94. amount: { clientSide: 'number', serverSide: 'number' },
  95. addLight: { clientSide: 'number', serverSide: 'number' },
  96. addFruit: { clientSide: 'number', serverSide: 'number' },
  97. multiple: { clientSide: 'number', serverSide: 'number' },
  98. days: { clientSide: 'number', serverSide: 'number' },
  99. status: { clientSide: 'number', serverSide: 'number' },
  100. };
  101. }
  102. id!: number;
  103. name = '';
  104. amount = 0;
  105. image = '';
  106. addLight = 0;
  107. addFruit = 0;
  108. multiple = 0;
  109. days = 0;
  110. status = 0;
  111. statusText = '';
  112. createtime = '' as string | null;
  113. updatetime = '' as string | null;
  114. deletetime = '' as string | null;
  115. }
  116. export class BlessOrderItem extends DataModel<BlessOrderItem> {
  117. constructor() {
  118. super(BlessOrderItem, '赐福订单');
  119. this.setNameMapperCase('Camel', 'Snake');
  120. this._convertTable = {
  121. id: { clientSide: 'number', serverSide: 'number', clientSideRequired: true },
  122. villageId: { clientSide: 'number', serverSide: 'number' },
  123. userId: { clientSide: 'number', serverSide: 'number' },
  124. blessId: { clientSide: 'number', serverSide: 'number' },
  125. amount: { clientSide: 'number', serverSide: 'number' },
  126. addLight: { clientSide: 'number', serverSide: 'number' },
  127. addFruit: { clientSide: 'number', serverSide: 'number' },
  128. multiple: { clientSide: 'number', serverSide: 'number' },
  129. status: { clientSide: 'number', serverSide: 'number' },
  130. };
  131. this._convertKeyType = (key) => {
  132. if (key.endsWith('Text')) return undefined;
  133. if (key === 'startTime' || key === 'endTime' || key === 'payTime') {
  134. return { clientSide: 'date', serverSide: 'number' };
  135. }
  136. return undefined;
  137. };
  138. }
  139. id!: number;
  140. /** 村庄id */
  141. villageId = null as number | null;
  142. /** 用户id */
  143. userId = null as number | null;
  144. /** 赋福套餐id */
  145. blessId = null as number | null;
  146. amount = 0;
  147. addLight = 0;
  148. addFruit = 0;
  149. multiple = 0;
  150. startTime = null as Date | null;
  151. endTime = null as Date | null;
  152. /** 状态:-1=已取消,0=待支付,1=已支付 */
  153. status = 0;
  154. remark = '' as string | null;
  155. payTime = null as Date | null;
  156. createtime = '' as string | null;
  157. updatetime = '' as string | null;
  158. deletetime = '' as string | null;
  159. villageName = '';
  160. nickname = '';
  161. username = '';
  162. volunteerName = '';
  163. blessName = '';
  164. startTimeText = '';
  165. endTimeText = '';
  166. statusText = '';
  167. payTimeText = '';
  168. }
  169. export class BlessOrderConfirm extends DataModel<BlessOrderConfirm> {
  170. constructor() {
  171. super(BlessOrderConfirm, '赐福订单确认');
  172. this.setNameMapperCase('Camel', 'Snake');
  173. this._convertTable = {
  174. order: {
  175. clientSide: 'object',
  176. clientSideChildDataModel: BlessOrderItem,
  177. serverSide: 'undefined',
  178. clientSideRequired: true
  179. },
  180. pay: {
  181. clientSide: 'object',
  182. clientSideChildDataModel: {
  183. convertTable: {
  184. appId: { clientSide: 'string', clientSideRequired: true },
  185. timeStamp: { clientSide: 'string', clientSideRequired: true },
  186. nonceStr: { clientSide: 'string', clientSideRequired: true },
  187. package: { clientSide: 'string', clientSideRequired: true },
  188. signType: { clientSide: 'string', clientSideRequired: true },
  189. paySign: { clientSide: 'string', clientSideRequired: true },
  190. }
  191. },
  192. serverSide: 'undefined',
  193. clientSideRequired: true,
  194. },
  195. };
  196. }
  197. order !: BlessOrderItem;
  198. pay !: {
  199. appId: string;
  200. timeStamp: string;
  201. nonceStr: string;
  202. package: string;
  203. signType: string;
  204. paySign: string;
  205. };
  206. }
  207. export class UpgradePackageItem extends DataModel<UpgradePackageItem> {
  208. constructor() {
  209. super(UpgradePackageItem, '升级套餐');
  210. this.setNameMapperCase('Camel', 'Snake');
  211. this._convertTable = {
  212. id: { clientSide: 'number', serverSide: 'number', clientSideRequired: true },
  213. vipLevel: { clientSide: 'number', serverSide: 'number' },
  214. price: { clientSide: 'number', serverSide: 'number' },
  215. imageLimit: { clientSide: 'number', serverSide: 'number' },
  216. storageLimit: { clientSide: 'number', serverSide: 'number' },
  217. managerLimit: { clientSide: 'number', serverSide: 'number' },
  218. isRecommend: { clientSide: 'boolean', serverSide: 'number' },
  219. sort: { clientSide: 'number', serverSide: 'number' },
  220. status: { clientSide: 'number', serverSide: 'number' },
  221. };
  222. }
  223. id!: number;
  224. name = '';
  225. vipLevel = 0;
  226. price = 0;
  227. imageLimit = 0;
  228. storageLimit = 0;
  229. managerLimit = 0;
  230. isRecommend = false;
  231. isRecommendText = '';
  232. desc = '';
  233. content = '';
  234. sort = 0;
  235. status = 0;
  236. statusText = '';
  237. createtime = '' as string | null;
  238. updatetime = '' as string | null;
  239. deletetime = '' as string | null;
  240. }
  241. export class GrowthTaskItem extends DataModel<GrowthTaskItem> {
  242. constructor() {
  243. super(GrowthTaskItem, '成长任务');
  244. this.setNameMapperCase('Camel', 'Snake');
  245. this._convertTable = {
  246. id: { clientSide: 'number', serverSide: 'number', clientSideRequired: true },
  247. villageId: { clientSide: 'number', serverSide: 'number' },
  248. points: { clientSide: 'number', serverSide: 'number' },
  249. limitNum: { clientSide: 'number', serverSide: 'number' },
  250. rewardWater: { clientSide: 'number', serverSide: 'number' },
  251. rewardLight: { clientSide: 'number', serverSide: 'number' },
  252. rewardFruit: { clientSide: 'number', serverSide: 'number' },
  253. status: { clientSide: 'number', serverSide: 'number' },
  254. weight: { clientSide: 'number', serverSide: 'number' },
  255. };
  256. }
  257. id!: number;
  258. taskKey = '';
  259. villageId = 0;
  260. image = '';
  261. name = '';
  262. desc = '';
  263. points = 0;
  264. /** 限制类型: unlimited=无限制, day=每日限制, total=次数限制 */
  265. limitType = '' as 'unlimited' | 'day' | 'total';
  266. limitNum = 0;
  267. rewardWater = 0;
  268. rewardLight = 0;
  269. rewardFruit = 0;
  270. /** 状态: 0=未启用, 1=启用 */
  271. status = 0;
  272. weight = 0;
  273. /** tree=乡源树页面,collect=挖掘页面,post=发贴图页面 */
  274. taskType = '' as 'tree'|'collect'|'post';
  275. limitTypeText = '';
  276. statusText = '';
  277. createtime = '' as string | null;
  278. updatetime = '' as string | null;
  279. deletetime = '' as string | null;
  280. }
  281. export class DropFruitResult extends DataModel<DropFruitResult> {
  282. constructor() {
  283. super(DropFruitResult, '产果结果');
  284. this.setNameMapperCase('Camel', 'Snake');
  285. this._convertTable = {
  286. fruitRemain: { clientSide: 'number', serverSide: 'number' },
  287. };
  288. }
  289. fruitRemain = 0;
  290. }
  291. export class UpgradeOrderItem extends DataModel<UpgradeOrderItem> {
  292. constructor() {
  293. super(UpgradeOrderItem, '升级订单');
  294. this.setNameMapperCase('Camel', 'Snake');
  295. this._convertTable = {
  296. id: { clientSide: 'number', serverSide: 'number', clientSideRequired: true },
  297. villageId: { clientSide: 'number', serverSide: 'number' },
  298. upgradeLevelId: { clientSide: 'number', serverSide: 'number' },
  299. vipLevel: { clientSide: 'number', serverSide: 'number' },
  300. userId: { clientSide: 'number', serverSide: 'number' },
  301. price: { clientSide: 'number', serverSide: 'number' },
  302. imageLimit: { clientSide: 'number', serverSide: 'number' },
  303. storageLimit: { clientSide: 'number', serverSide: 'number' },
  304. managerLimit: { clientSide: 'number', serverSide: 'number' },
  305. money: { clientSide: 'number', serverSide: 'number' },
  306. payType: { clientSide: 'number', serverSide: 'number' },
  307. status: { clientSide: 'number', serverSide: 'number' },
  308. volunteerId: { clientSide: 'number', serverSide: 'number' },
  309. };
  310. this._convertKeyType = (key) => {
  311. if (key.endsWith('Text')) return undefined;
  312. if (key === 'payTime') {
  313. return { clientSide: 'date', serverSide: 'number' };
  314. }
  315. return undefined;
  316. };
  317. }
  318. id!: number;
  319. orderNo = '';
  320. villageId = null as number | null;
  321. upgradeLevelId = null as number | null;
  322. vipLevel = 0;
  323. userId = null as number | null;
  324. levelName = '';
  325. price = 0;
  326. imageLimit = 0;
  327. storageLimit = 0;
  328. managerLimit = 0;
  329. money = 0;
  330. /** 支付方式: 1=线上支付, 2=对公付款 */
  331. payType = 0;
  332. payVoucher = '';
  333. /** 状态: -1=已取消, 0=未支付, 1=已支付, 2=待审核(对公付款审核凭证) */
  334. status = 0;
  335. payTime = null as Date | null;
  336. villageName = '';
  337. volunteerName = '';
  338. volunteerId = null as number | null;
  339. statusText = '';
  340. payTimeText = '';
  341. createtime = '' as string | null;
  342. updatetime = '' as string | null;
  343. deletetime = '' as string | null;
  344. }
  345. export class FruitDepositItem extends DataModel<FruitDepositItem> {
  346. constructor() {
  347. super(FruitDepositItem, '充值套餐');
  348. this.setNameMapperCase('Camel', 'Snake');
  349. this._convertTable = {
  350. id: { clientSide: 'number', serverSide: 'number', clientSideRequired: true },
  351. price: { clientSide: 'number', serverSide: 'number' },
  352. addFruit: { clientSide: 'number', serverSide: 'number' },
  353. sort: { clientSide: 'number', serverSide: 'number' },
  354. status: { clientSide: 'number', serverSide: 'number' },
  355. };
  356. }
  357. id!: number;
  358. name = '';
  359. /** 金额 */
  360. price = 0;
  361. /** 乡源果数量 */
  362. addFruit = 0;
  363. sort = 0;
  364. /** 状态: 0=禁用, 1=启用 */
  365. status = 0;
  366. statusText = '';
  367. createtime = '' as string | null;
  368. updatetime = '' as string | null;
  369. deletetime = '' as string | null;
  370. }
  371. export class FruitDepositOrderItem extends DataModel<FruitDepositOrderItem> {
  372. constructor() {
  373. super(FruitDepositOrderItem, '充值乡源果订单');
  374. this.setNameMapperCase('Camel', 'Snake');
  375. this._convertTable = {
  376. id: { clientSide: 'number', serverSide: 'number', clientSideRequired: true },
  377. userId: { clientSide: 'number', serverSide: 'number' },
  378. orderType: { clientSide: 'number', serverSide: 'number' },
  379. staffLevelId: { clientSide: 'number', serverSide: 'number' },
  380. villageId: { clientSide: 'number', serverSide: 'number' },
  381. amount: { clientSide: 'number', serverSide: 'number' },
  382. days: { clientSide: 'number', serverSide: 'number' },
  383. status: { clientSide: 'number', serverSide: 'number' },
  384. paytime: { clientSide: 'number', serverSide: 'number' },
  385. };
  386. }
  387. id!: number;
  388. orderNo = '';
  389. userId = null as number | null;
  390. /** 订单类型: 3=乡源果充值 */
  391. orderType = 3;
  392. staffLevelId = 0;
  393. villageId = null as number | null;
  394. amount = 0;
  395. days = 0;
  396. /** 状态: 0=待支付, 1=已支付 */
  397. status = 0;
  398. paytime = 0;
  399. createtime = '' as string | null;
  400. updatetime = '' as string | null;
  401. orderTypeText = '';
  402. payTypeText = '';
  403. statusText = '';
  404. paytimeText = '';
  405. rewardStatusText = '';
  406. }
  407. export class FruitDepositOrderConfirm extends DataModel<FruitDepositOrderConfirm> {
  408. constructor() {
  409. super(FruitDepositOrderConfirm, '充值乡源果下单确认');
  410. this.setNameMapperCase('Camel', 'Snake');
  411. this._convertTable = {
  412. order: {
  413. clientSide: 'object',
  414. clientSideChildDataModel: FruitDepositOrderItem,
  415. serverSide: 'undefined',
  416. clientSideRequired: true,
  417. },
  418. pay: {
  419. clientSide: 'object',
  420. clientSideChildDataModel: {
  421. convertTable: {
  422. appId: { clientSide: 'string', clientSideRequired: true },
  423. timeStamp: { clientSide: 'string', clientSideRequired: true },
  424. nonceStr: { clientSide: 'string', clientSideRequired: true },
  425. package: { clientSide: 'string', clientSideRequired: true },
  426. signType: { clientSide: 'string', clientSideRequired: true },
  427. paySign: { clientSide: 'string', clientSideRequired: true },
  428. },
  429. },
  430. serverSide: 'undefined',
  431. clientSideRequired: true,
  432. },
  433. };
  434. }
  435. order!: FruitDepositOrderItem;
  436. pay!: {
  437. appId: string;
  438. timeStamp: string;
  439. nonceStr: string;
  440. package: string;
  441. signType: string;
  442. paySign: string;
  443. };
  444. }
  445. export class UpgradeOrderConfirm extends DataModel<UpgradeOrderConfirm> {
  446. constructor() {
  447. super(UpgradeOrderConfirm, '升级订单确认');
  448. this.setNameMapperCase('Camel', 'Snake');
  449. this._convertTable = {
  450. order: {
  451. clientSide: 'object',
  452. clientSideChildDataModel: UpgradeOrderItem,
  453. serverSide: 'undefined',
  454. clientSideRequired: true,
  455. },
  456. pay: {
  457. clientSide: 'object',
  458. clientSideChildDataModel: {
  459. convertTable: {
  460. appId: { clientSide: 'string', clientSideRequired: true },
  461. timeStamp: { clientSide: 'string', clientSideRequired: true },
  462. nonceStr: { clientSide: 'string', clientSideRequired: true },
  463. package: { clientSide: 'string', clientSideRequired: true },
  464. signType: { clientSide: 'string', clientSideRequired: true },
  465. paySign: { clientSide: 'string', clientSideRequired: true },
  466. },
  467. },
  468. serverSide: 'undefined',
  469. },
  470. };
  471. }
  472. order!: UpgradeOrderItem;
  473. pay?: {
  474. appId: string;
  475. timeStamp: string;
  476. nonceStr: string;
  477. package: string;
  478. signType: string;
  479. paySign: string;
  480. };
  481. }
  482. type GrowthLogSearch = {
  483. page?: number;
  484. pageSize?: number;
  485. keywords?: string;
  486. villageId?: number;
  487. userId?: number;
  488. };
  489. type PagedGrowthResponse = {
  490. total: number;
  491. per_page?: number;
  492. current_page?: number;
  493. last_page?: number;
  494. data: KeyValue[];
  495. };
  496. export type GrowthLogFeedType = 'task' | 'fruit' | 'light';
  497. export type GrowthLogFeedItem =
  498. | { logType: 'task'; item: GrowthTaskLogItem }
  499. | { logType: 'fruit'; item: GrowthFruitLogItem }
  500. | { logType: 'light'; item: GrowthLightLogItem };
  501. export class TreeApi extends AppServerRequestModule<DataModel> {
  502. constructor() {
  503. super();
  504. }
  505. private growthLogParams(search?: GrowthLogSearch) {
  506. return {
  507. page: search?.page,
  508. pageSize: search?.pageSize,
  509. keywords: search?.keywords,
  510. village_id: search?.villageId,
  511. user_id: search?.userId,
  512. };
  513. }
  514. private parsePagedList<T extends DataModel>(
  515. model: NewDataModel,
  516. data: PagedGrowthResponse,
  517. label: string,
  518. ) {
  519. return {
  520. total: data.total ?? 0,
  521. list: transformArrayDataModel<T>(model, transformSomeToArray(data.data), label, true),
  522. };
  523. }
  524. /** 将 total 随机拆成 parts 份(每份 ≥ 0,之和为 total) */
  525. private randomPartition(total: number, parts: number) {
  526. const buckets = Array.from({ length: parts }, () => 0);
  527. for (let i = 0; i < total; i++) {
  528. buckets[Math.floor(Math.random() * parts)]++;
  529. }
  530. return buckets;
  531. }
  532. private shuffle<T>(items: T[]) {
  533. const arr = [...items];
  534. for (let i = arr.length - 1; i > 0; i--) {
  535. const j = Math.floor(Math.random() * (i + 1));
  536. [arr[i], arr[j]] = [arr[j], arr[i]];
  537. }
  538. return arr;
  539. }
  540. private async fetchGrowthLogSlice<T extends DataModel>(
  541. url: string,
  542. label: string,
  543. model: NewDataModel,
  544. pageSize: number,
  545. search?: GrowthLogSearch,
  546. extra?: KeyValue,
  547. ) {
  548. if (pageSize <= 0) return [] as T[];
  549. const res = await this.post<PagedGrowthResponse>(url, label, {
  550. ...this.growthLogParams({ ...search, page: 1, pageSize }),
  551. ...extra,
  552. }, undefined, undefined, {
  553. cacheEnable: true,
  554. cacheTime: 1000 * 60 * 10, //10min
  555. });
  556. return this.parsePagedList<T>(model, res.requireData(), label).list;
  557. }
  558. /**
  559. * 聚合随机成长流水:将 count 随机分配到任务日志 / 乡源果 / 乡源光三个接口并行拉取,打乱后返回。
  560. */
  561. async getRandomGrowthLogFeed(
  562. count: number,
  563. search?: GrowthLogSearch & {
  564. fruitType?: 'pick' | 'task' | 'bless' | 'exchange' | string;
  565. lightType?: 'water' | 'fertilize' | 'task' | 'bless' | string;
  566. },
  567. ) {
  568. if (count <= 0) {
  569. return { list: [] as GrowthLogFeedItem[], total: 0 };
  570. }
  571. const [taskCount, fruitCount, lightCount] = this.randomPartition(count, 3);
  572. const [taskList, fruitList, lightList] = await Promise.all([
  573. this.fetchGrowthLogSlice<GrowthTaskLogItem>(
  574. '/village/growth/taskLogList',
  575. '任务日志',
  576. GrowthTaskLogItem,
  577. taskCount,
  578. search,
  579. ),
  580. this.fetchGrowthLogSlice<GrowthFruitLogItem>(
  581. '/village/growth/fruitLogList',
  582. '乡源果流水',
  583. GrowthFruitLogItem,
  584. fruitCount,
  585. search,
  586. { type: search?.fruitType },
  587. ),
  588. this.fetchGrowthLogSlice<GrowthLightLogItem>(
  589. '/village/growth/lightLogList',
  590. '乡源光流水',
  591. GrowthLightLogItem,
  592. lightCount,
  593. search,
  594. { type: search?.lightType },
  595. ),
  596. ]);
  597. const merged: GrowthLogFeedItem[] = [
  598. ...taskList.map((item) => ({ logType: 'task' as const, item })),
  599. ...fruitList.map((item) => ({ logType: 'fruit' as const, item })),
  600. ...lightList.map((item) => ({ logType: 'light' as const, item })),
  601. ];
  602. const list = this.shuffle(merged).slice(0, count);
  603. return { list, total: list.length };
  604. }
  605. /** 任务列表 */
  606. async getTaskList(options?: {
  607. page?: number;
  608. pageSize?: number;
  609. keywords?: string;
  610. villageId?: number;
  611. }) {
  612. const res = await this.post<PagedGrowthResponse>(
  613. '/village/growth/taskList',
  614. '任务列表',
  615. {
  616. page: options?.page,
  617. pageSize: options?.pageSize,
  618. keywords: options?.keywords,
  619. village_id: options?.villageId,
  620. },
  621. );
  622. return this.parsePagedList<GrowthTaskItem>(
  623. GrowthTaskItem,
  624. res.requireData(),
  625. '成长任务',
  626. );
  627. }
  628. /** 任务日志列表 */
  629. async getTaskLogList(search?: GrowthLogSearch) {
  630. const res = await this.post<PagedGrowthResponse>(
  631. '/village/growth/taskLogList',
  632. '任务日志列表',
  633. this.growthLogParams(search),
  634. );
  635. return this.parsePagedList<GrowthTaskLogItem>(
  636. GrowthTaskLogItem,
  637. res.requireData(),
  638. '任务日志',
  639. );
  640. }
  641. /** 乡源果流水 */
  642. async getFruitLogList(
  643. search?: GrowthLogSearch & {
  644. type?: 'pick' | 'task' | 'bless' | 'exchange' | string;
  645. },
  646. ) {
  647. const res = await this.post<PagedGrowthResponse>(
  648. '/village/growth/fruitLogList',
  649. '乡源果流水',
  650. {
  651. ...this.growthLogParams(search),
  652. type: search?.type,
  653. },
  654. );
  655. return this.parsePagedList<GrowthFruitLogItem>(
  656. GrowthFruitLogItem,
  657. res.requireData(),
  658. '乡源果流水',
  659. );
  660. }
  661. /** 乡源光流水 */
  662. async getLightLogList(
  663. search?: GrowthLogSearch & {
  664. type?: 'water' | 'fertilize' | 'task' | 'bless' | string;
  665. },
  666. ) {
  667. const res = await this.post<PagedGrowthResponse>(
  668. '/village/growth/lightLogList',
  669. '乡源光流水',
  670. {
  671. ...this.growthLogParams(search),
  672. type: search?.type,
  673. },
  674. );
  675. return this.parsePagedList<GrowthLightLogItem>(
  676. GrowthLightLogItem,
  677. res.requireData(),
  678. '乡源光流水',
  679. );
  680. }
  681. /** 赐福套餐列表 */
  682. async getBlessList(options?: {
  683. page?: number;
  684. pageSize?: number;
  685. keywords?: string;
  686. }) {
  687. const res = await this.post<PagedGrowthResponse>(
  688. '/village/growth/blessList',
  689. '赐福套餐列表',
  690. {
  691. page: options?.page,
  692. pageSize: options?.pageSize,
  693. keywords: options?.keywords,
  694. },
  695. );
  696. return this.parsePagedList<BlessPackageItem>(
  697. BlessPackageItem,
  698. res.requireData(),
  699. '赐福套餐',
  700. );
  701. }
  702. /** 赐福订单列表 */
  703. async getBlessOrders(options?: {
  704. page?: number;
  705. pageSize?: number;
  706. keywords?: string;
  707. villageId?: number;
  708. userId?: number;
  709. blessId?: number;
  710. /** -1=已取消, 0=待支付, 1=已支付 */
  711. status?: -1 | 0 | 1 | number;
  712. }) {
  713. const res = await this.post<PagedGrowthResponse>(
  714. '/village/growth/blessOrders',
  715. '赐福订单列表',
  716. {
  717. page: options?.page,
  718. pageSize: options?.pageSize,
  719. keywords: options?.keywords,
  720. village_id: options?.villageId,
  721. user_id: options?.userId,
  722. bless_id: options?.blessId,
  723. status: options?.status,
  724. },
  725. );
  726. return this.parsePagedList<BlessOrderItem>(
  727. BlessOrderItem,
  728. res.requireData(),
  729. '赐福订单',
  730. );
  731. }
  732. /** 升级套餐列表 */
  733. async getUpgradeList(options?: {
  734. page?: number;
  735. pageSize?: number;
  736. keywords?: string;
  737. }) {
  738. const res = await this.post<PagedGrowthResponse>(
  739. '/village/growth/upgradeList',
  740. '升级套餐列表',
  741. {
  742. page: options?.page,
  743. pageSize: options?.pageSize,
  744. keywords: options?.keywords,
  745. },
  746. );
  747. return this.parsePagedList<UpgradePackageItem>(
  748. UpgradePackageItem,
  749. res.requireData(),
  750. '升级套餐',
  751. );
  752. }
  753. /** 升级套餐详情 */
  754. async getUpgradeInfo(id: number) {
  755. const res = await this.post<KeyValue>('/village/growth/upgradeInfo', '升级套餐详情', { id });
  756. return transformDataModel<UpgradePackageItem>(UpgradePackageItem, res.requireData());
  757. }
  758. private getStringDataOrMessage(res: RequestApiResult<any>) {
  759. return typeof res.data === 'string' ? res.data : res.message;
  760. }
  761. /** 浇水(每日限次,仅可关注或认领的村社) */
  762. async water(villageId: number) {
  763. const res = await this.post<null>('/village/growth/water', '浇水', {
  764. village_id: villageId,
  765. });
  766. return this.getStringDataOrMessage(res);
  767. }
  768. /** 施肥(每日限次,仅可关注或认领的村社) */
  769. async fertilize(villageId: number) {
  770. const res = await this.post<null>('/village/growth/fertilize', '施肥', {
  771. village_id: villageId,
  772. });
  773. return this.getStringDataOrMessage(res);
  774. }
  775. /** 拾取果子 */
  776. async pick(villageId: number) {
  777. const res = await this.post<null>('/village/growth/pick', '拾取果子', {
  778. village_id: villageId,
  779. });
  780. return this.getStringDataOrMessage(res);
  781. }
  782. /** 乡源树产果(随机产果,返回可拾取数量) */
  783. async dropFruit(villageId: number) {
  784. const res = await this.post<KeyValue>('/village/growth/dropFruit', '乡源树产果', {
  785. village_id: villageId,
  786. });
  787. return transformDataModel<DropFruitResult>(DropFruitResult, res.requireData());
  788. }
  789. /** 下单赐福套餐 */
  790. async createBlessOrder(villageId: number, blessId: number, payType: 1 | 2 | 3) {
  791. const res = await this.post<KeyValue>('/village/growth/bless', '下单赐福', {
  792. village_id: villageId,
  793. bless_id: blessId,
  794. pay_type: payType,
  795. });
  796. const data = res.requireData();
  797. return transformDataModel<BlessOrderConfirm>(BlessOrderConfirm, data);
  798. }
  799. /**
  800. * 发放赐福奖励(支付成功后调用;文档未列参数,按订单 id 发放)
  801. * @param id 赐福订单 ID(`createBlessOrder` 返回的 id)
  802. */
  803. async grantBlessReward(id: number) {
  804. const res = await this.post<null>('/village/growth/grantBlessReward', '发放赐福奖励', { id });
  805. return res.requireData();
  806. }
  807. /** 已下单赐福套餐支付 */
  808. async blessPay(id: number) {
  809. const res = await this.post<KeyValue>('/village/growth/blessPay', '已下单赐福套餐支付', { id });
  810. return transformDataModel<BlessOrderConfirm>(BlessOrderConfirm, res.requireData());
  811. }
  812. /** 升级订单列表 */
  813. async getUpgradeOrderList(options?: {
  814. page?: number;
  815. pageSize?: number;
  816. keywords?: string;
  817. villageId?: number;
  818. userId?: number;
  819. /** -1=已取消, 0=未支付, 1=已支付, 2=待审核(对公付款审核凭证) */
  820. status?: -1 | 0 | 1 | 2 | number;
  821. }) {
  822. const res = await this.post<PagedGrowthResponse>(
  823. '/village/growth/upgradeOrderList',
  824. '升级订单列表',
  825. {
  826. page: options?.page,
  827. pageSize: options?.pageSize,
  828. keywords: options?.keywords,
  829. village_id: options?.villageId,
  830. user_id: options?.userId,
  831. status: options?.status,
  832. },
  833. );
  834. return this.parsePagedList<UpgradeOrderItem>(
  835. UpgradeOrderItem,
  836. res.requireData(),
  837. '升级订单',
  838. );
  839. }
  840. /** 升级订单详情 */
  841. async getUpgradeOrderInfo(id: number) {
  842. const res = await this.post<KeyValue>('/village/growth/upgradeOrderInfo', '升级订单详情', { id });
  843. return transformDataModel<UpgradeOrderItem>(UpgradeOrderItem, res.requireData());
  844. }
  845. /** 升级订单下单 */
  846. async createUpgradeOrder(
  847. villageId: number,
  848. upgradeLevelId: number,
  849. payType: 1 | 2 | 3,
  850. ) {
  851. const res = await this.post<KeyValue>('/village/growth/upgrade', '升级订单下单', {
  852. village_id: villageId,
  853. upgrade_level_id: upgradeLevelId,
  854. pay_type: payType,
  855. });
  856. return transformDataModel<UpgradeOrderConfirm>(UpgradeOrderConfirm, res.requireData());
  857. }
  858. /** 已下单订单支付 */
  859. async upgradePay(id: number) {
  860. const res = await this.post<KeyValue>('/village/growth/upgradePay', '已下单订单支付', { id });
  861. return transformDataModel<UpgradeOrderConfirm>(UpgradeOrderConfirm, res.requireData());
  862. }
  863. /** 对公付款上传凭证 */
  864. async saveUpgrade(id: number, payVoucher: string) {
  865. const res = await this.post<null>('/village/growth/saveUpgrade', '提交升级订单凭证', {
  866. id,
  867. pay_voucher: payVoucher,
  868. });
  869. return this.getStringDataOrMessage(res);
  870. }
  871. /** 充值套餐列表 */
  872. async getFruitDepositList(options?: {
  873. page?: number;
  874. pageSize?: number;
  875. keywords?: string;
  876. }) {
  877. const res = await this.post<PagedGrowthResponse>(
  878. '/village/growth/fruitDepositList',
  879. '充值套餐列表',
  880. {
  881. page: options?.page,
  882. pageSize: options?.pageSize,
  883. keywords: options?.keywords,
  884. },
  885. );
  886. return this.parsePagedList<FruitDepositItem>(
  887. FruitDepositItem,
  888. res.requireData(),
  889. '充值套餐',
  890. );
  891. }
  892. /** 充值乡源果下单 */
  893. async placeOrder(fruitDepositId: number, villageId?: number) {
  894. const res = await this.post<KeyValue>('/village/growth/placeOrder', '充值乡源果下单', {
  895. fruit_deposit_id: fruitDepositId,
  896. village_id: villageId,
  897. });
  898. return transformDataModel<FruitDepositOrderConfirm>(FruitDepositOrderConfirm, res.requireData());
  899. }
  900. }
  901. export default new TreeApi();