MiniProgramSettingsLogic.php 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | saiadmin [ saiadmin快速开发框架 ]
  4. // +----------------------------------------------------------------------
  5. namespace plugin\saiadmin\app\logic\miniProgram;
  6. use plugin\saiadmin\app\model\miniProgram\MiniProgramSettings;
  7. use plugin\saiadmin\basic\think\BaseLogic;
  8. use plugin\saiadmin\exception\ApiException;
  9. /**
  10. * 小程序系统设置逻辑层
  11. */
  12. class MiniProgramSettingsLogic extends BaseLogic
  13. {
  14. private const CONFIG_ID = 1;
  15. private const DEFAULTS = [
  16. 'deposit_refund' => [
  17. 'original_refund' => 1,
  18. 'refund_speed' => 'immediate',
  19. 'delay_hours' => 0,
  20. 'wallet_as_deposit' => 0,
  21. ],
  22. 'withdraw_methods' => ['wechat_qrcode'],
  23. 'agent_withdraw' => [
  24. 'review_amount' => 200,
  25. 'other_fee_mode' => 'fee',
  26. 'alipay_fee_mode' => 'tax',
  27. 'tax_rate' => 0.6,
  28. 'fee' => 0,
  29. 'min_amount' => 0.3,
  30. 'max_amount' => 500,
  31. 'schedule_type' => 'day',
  32. 'time_ranges' => [['start' => '00:00', 'end' => '24:00']],
  33. ],
  34. 'merchant_withdraw' => [
  35. 'review_amount' => 200,
  36. 'other_fee_mode' => 'tax',
  37. 'alipay_fee_mode' => 'tax',
  38. 'tax_rate' => 0.6,
  39. 'fee' => 0,
  40. 'min_amount' => 0.3,
  41. 'max_amount' => 500,
  42. 'schedule_type' => 'day',
  43. 'time_ranges' => [['start' => '00:00', 'end' => '24:00']],
  44. ],
  45. 'user_withdraw' => [
  46. 'review_amount' => 100,
  47. 'fee_rate' => 0,
  48. 'min_amount' => 0.01,
  49. 'max_amount' => 100,
  50. 'deposit_refund_mode' => 0,
  51. ],
  52. 'user_settings' => [
  53. 'wechat_phone_authorization' => 0,
  54. 'alipay_phone_authorization' => 0,
  55. ],
  56. 'membership' => [
  57. 'enabled' => 0,
  58. 'month_price' => 0,
  59. 'quarter_price' => 0,
  60. 'year_price' => 0,
  61. ],
  62. 'order_profit_detail' => 1,
  63. 'official_account_login' => 0,
  64. 'new_merchant_dd_prefix' => 0,
  65. ];
  66. public function __construct()
  67. {
  68. $this->model = new MiniProgramSettings();
  69. }
  70. public function getSettings(): array
  71. {
  72. $model = $this->model->findOrEmpty(self::CONFIG_ID);
  73. if ($model->isEmpty()) {
  74. return self::DEFAULTS;
  75. }
  76. $stored = json_decode((string) $model->getAttr('settings'), true);
  77. if (!is_array($stored)) {
  78. return self::DEFAULTS;
  79. }
  80. return $this->normalizeSettings(array_replace_recursive(self::DEFAULTS, $stored));
  81. }
  82. public function saveSettings(array $data): bool
  83. {
  84. $settings = $this->normalizeSettings($data);
  85. $json = json_encode($settings, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
  86. if ($json === false) {
  87. throw new ApiException('系统设置序列化失败');
  88. }
  89. $model = $this->model->findOrEmpty(self::CONFIG_ID);
  90. if ($model->isEmpty()) {
  91. return (bool) $this->model->create(['id' => self::CONFIG_ID, 'settings' => $json])->getKey();
  92. }
  93. return $model->save(['settings' => $json]) !== false;
  94. }
  95. private function normalizeSettings(array $input): array
  96. {
  97. $deposit = $this->arrayValue($input, 'deposit_refund');
  98. $userWithdraw = $this->arrayValue($input, 'user_withdraw');
  99. $userSettings = $this->arrayValue($input, 'user_settings');
  100. $membership = $this->arrayValue($input, 'membership');
  101. return [
  102. 'deposit_refund' => [
  103. 'original_refund' => $this->flag($deposit['original_refund'] ?? 1),
  104. 'refund_speed' => $this->enum($deposit['refund_speed'] ?? 'immediate', ['immediate', 'delayed'], '押金退回速度'),
  105. 'delay_hours' => (int) $this->number($deposit['delay_hours'] ?? 0, 0, 8760, '押金退款延长时间'),
  106. 'wallet_as_deposit' => $this->flag($deposit['wallet_as_deposit'] ?? 0),
  107. ],
  108. 'withdraw_methods' => $this->withdrawMethods($input['withdraw_methods'] ?? []),
  109. 'agent_withdraw' => $this->withdrawProfile($this->arrayValue($input, 'agent_withdraw'), '代理'),
  110. 'merchant_withdraw' => $this->withdrawProfile($this->arrayValue($input, 'merchant_withdraw'), '商家'),
  111. 'user_withdraw' => [
  112. 'review_amount' => $this->number($userWithdraw['review_amount'] ?? 100, 0, 99999999, '用户提现审核金额'),
  113. 'fee_rate' => $this->number($userWithdraw['fee_rate'] ?? 0, 0, 100, '用户提现手续费'),
  114. 'min_amount' => $this->number($userWithdraw['min_amount'] ?? 0.01, 0, 99999999, '用户提现最小金额'),
  115. 'max_amount' => $this->number($userWithdraw['max_amount'] ?? 100, 0, 99999999, '用户提现最大金额'),
  116. 'deposit_refund_mode' => $this->flag($userWithdraw['deposit_refund_mode'] ?? 0),
  117. ],
  118. 'user_settings' => [
  119. 'wechat_phone_authorization' => $this->flag($userSettings['wechat_phone_authorization'] ?? 0),
  120. 'alipay_phone_authorization' => $this->flag($userSettings['alipay_phone_authorization'] ?? 0),
  121. ],
  122. 'membership' => [
  123. 'enabled' => $this->flag($membership['enabled'] ?? 0),
  124. 'month_price' => $this->number($membership['month_price'] ?? 0, 0, 99999999, '月卡金额'),
  125. 'quarter_price' => $this->number($membership['quarter_price'] ?? 0, 0, 99999999, '季卡金额'),
  126. 'year_price' => $this->number($membership['year_price'] ?? 0, 0, 99999999, '年卡金额'),
  127. ],
  128. 'order_profit_detail' => $this->flag($input['order_profit_detail'] ?? 1),
  129. 'official_account_login' => $this->flag($input['official_account_login'] ?? 0),
  130. 'new_merchant_dd_prefix' => (int) $this->number($input['new_merchant_dd_prefix'] ?? 0, 0, 999999, '新商户 DD 规则'),
  131. ];
  132. }
  133. private function withdrawProfile(array $profile, string $label): array
  134. {
  135. $min = $this->number($profile['min_amount'] ?? 0.3, 0, 99999999, $label . '提现最小金额');
  136. $max = $this->number($profile['max_amount'] ?? 500, 0, 99999999, $label . '提现最大金额');
  137. if ($max < $min) {
  138. throw new ApiException($label . '提现最大金额不能小于最小金额');
  139. }
  140. return [
  141. 'review_amount' => $this->number($profile['review_amount'] ?? 200, 0, 99999999, $label . '提现审核金额'),
  142. 'other_fee_mode' => $this->enum($profile['other_fee_mode'] ?? 'tax', ['tax', 'fee', 'both'], $label . '其他手续费模式'),
  143. 'alipay_fee_mode' => $this->enum($profile['alipay_fee_mode'] ?? 'tax', ['tax', 'fee', 'both'], $label . '支付宝手续费模式'),
  144. 'tax_rate' => $this->number($profile['tax_rate'] ?? 0.6, 0, 100, $label . '提现税点'),
  145. 'fee' => $this->number($profile['fee'] ?? 0, 0, 99999999, $label . '提现手续费'),
  146. 'min_amount' => $min,
  147. 'max_amount' => $max,
  148. 'schedule_type' => $this->enum($profile['schedule_type'] ?? 'day', ['day', 'week', 'month'], $label . '提现时间'),
  149. 'time_ranges' => $this->timeRanges($profile['time_ranges'] ?? []),
  150. ];
  151. }
  152. private function timeRanges(mixed $ranges): array
  153. {
  154. if (!is_array($ranges) || $ranges === []) {
  155. return [['start' => '00:00', 'end' => '24:00']];
  156. }
  157. if (count($ranges) > 10) {
  158. throw new ApiException('提现时间段不能超过 10 个');
  159. }
  160. $result = [];
  161. foreach ($ranges as $range) {
  162. if (!is_array($range)) {
  163. throw new ApiException('提现时间段格式不正确');
  164. }
  165. $start = (string) ($range['start'] ?? '');
  166. $end = (string) ($range['end'] ?? '');
  167. if (!$this->validTime($start, false) || !$this->validTime($end, true)) {
  168. throw new ApiException('提现时间段必须使用 HH:mm 格式');
  169. }
  170. $result[] = ['start' => $start, 'end' => $end];
  171. }
  172. return $result;
  173. }
  174. private function validTime(string $value, bool $allowEndOfDay): bool
  175. {
  176. if ($allowEndOfDay && $value === '24:00') {
  177. return true;
  178. }
  179. return preg_match('/^(?:[01]\\d|2[0-3]):[0-5]\\d$/', $value) === 1;
  180. }
  181. private function withdrawMethods(mixed $methods): array
  182. {
  183. $allowed = ['bank_card', 'wechat', 'alipay', 'wechat_qrcode', 'alipay_qrcode'];
  184. if (!is_array($methods)) {
  185. return [];
  186. }
  187. return array_values(array_unique(array_intersect($allowed, array_map('strval', $methods))));
  188. }
  189. private function arrayValue(array $input, string $key): array
  190. {
  191. return isset($input[$key]) && is_array($input[$key]) ? $input[$key] : [];
  192. }
  193. private function flag(mixed $value): int
  194. {
  195. return in_array($value, [1, '1', true], true) ? 1 : 0;
  196. }
  197. private function enum(mixed $value, array $allowed, string $label): string
  198. {
  199. $value = (string) $value;
  200. if (!in_array($value, $allowed, true)) {
  201. throw new ApiException($label . '设置不正确');
  202. }
  203. return $value;
  204. }
  205. private function number(mixed $value, float $min, float $max, string $label): float
  206. {
  207. if (!is_numeric($value)) {
  208. throw new ApiException($label . '必须是数字');
  209. }
  210. $number = (float) $value;
  211. if ($number < $min || $number > $max) {
  212. throw new ApiException($label . '超出允许范围');
  213. }
  214. return $number;
  215. }
  216. }