Gradle 下载太慢,使用国内源加速

1、修改 gradle-wrapper

修改 gradle-wrapper.properties 文件的 distributionUrl 参数

1
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip

2、更换仓库

项目根目录下的 build.gradle 文件的 buildscript.repositories 加入:

1
2
maven { url 'https://maven.aliyun.com/repository/google' } // Google Maven镜像
maven { url 'https://maven.aliyun.com/repository/public' } // Maven Central 镜像

3、执行 sync

记一次 tailwind @apply 编译速度极慢的解决过程

新的 Vue 项目使用了 Tailwind + SCSS,突然发现修改 app.scss 文件的时候,热更新需要 14s+ 才能加载完成。和之前项目唯一不同的是scss的 @import 换成了 @use,这地方应该没问题,sass 团队不可能会犯这么低级的错误。

突然想起,前两天把 128k 的地区数据 regions.js 放 src 目录下了,很有可能是这个引起的。试着把该文件移出 src 目录,还真是。解决:把该文件改为 json 格式,放到 assets 目录。

默认的 tailwind 配置会扫描源码目录下的 vue,js,ts,jsx,tsx 文件,js 文件过大太复杂的时候就会引起问题了。我项目只有 vue 文件需要taiwnid 扫描,把其他后缀去掉,热加载速度明显快很多。配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
// tailwind.config.js
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./index.html",
// "./src/**/*.{vue,js,ts,jsx,tsx}",
"./src/**/*.vue",
],
theme: {
extend: {},
},
plugins: [],
}

PhpStorm / WebStorm 中 TailwindCSS 没有自动提示

PhpStorm / WebStorm 新建项目中,有时候 TailwindCSS 自动提示没有出来。
原因如下:

  • 1、只支用 npm,你项目可能用 pnpm 了
  • 2、IDE 设置的 Node 解释器可能跟你项目使用的解释器不对应。

打开 “设置 > 语言和框架 > Node.js”,选择“Node 解释器”为当前项目使用的 node 解释器。有时候项目在 wsl 中开发,默认是本地的 node,会导致问题。

Laravel 新旧版本模型属性访问器、修改器的选用

Laravel 9 之前模型属性访问器、修改器

通过 setFooBarAttribute($value)getFooBarAttribute() 方法来定义和访问自定义动态 fooBar 属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
/**
* 设置用户的名字
*/
public function setFirstNameAttribute($value)
{
$this->attributes['first_name'] = strtolower($value);
}

/**
* 获取用户的全名
*/
public function getFullNameAttribute()
{
return "{$this->first_name} {$this->last_name}";
}
}

Laravel 9 及之后版本的模型属性访问器、修改器

通过返回值类型为 Attribute 类来定义和访问自定义动态属性。

阅读更多

Laravel 多租户组件 spatie/laravel-multitenancy 指南

spatie/laravel-multitenancy 多租户组件,它可以帮助你快速实现 Laravel 多租户应用。比 tenancy/tenancy 更灵活。

数据库支持单库模式和1中心库+多租户库模式。多租户库模式下,用户表和用户tokens表可选择放在中心库,也可以放在租户库。

安装

1
2
3
4
5
composer require spatie/laravel-multitenancy
# 发布配置文件,生成 config/multitenancy.php 配置文件
php artisan vendor:publish --provider="Spatie\Multitenancy\MultitenancyServiceProvider" --tag="multitenancy-config"
# 在 database/migrations/landlord 目录下发布迁移文件,用于创建 tenants 表
php artisan vendor:publish --provider="Spatie\Multitenancy\MultitenancyServiceProvider" --tag="multitenancy-migrations"
1
2
3
4
5
6
7
Schema::create('tenants', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('domain')->unique(); // 用于外部传入id以切换租户,可以改成自己需要的
$table->string('database')->unique(); // 租户数据库
$table->timestamps();
});

配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
// config/multitenancy.php
return [
/*
* 这个类负责确定哪个租户应该是给定请求的最新租户。
* 必须继承 `Spatie\Multitenancy\TenantFinder\TenantFinder`
*/
'tenant_finder' => \App\Multitenancy\TenantFinder\HeaderTenantFinder::class,

/*
* 这些字段由`tender:artist`命令用于匹配一个或多个租户。
*/
'tenant_artisan_search_fields' => [
'id',
'tenant_code', // 默认是domain,根据自己需要修改
'database'
],

/*
* 这些任务将在切换租户时执行。
*
* 有效的任务是实现 `Spatie\Multitenance\Tasks\SwitchTenantTask` 的任何类
*/
'switch_tenant_tasks' => [
\Spatie\Multitenancy\Tasks\PrefixCacheTask::class,
\Spatie\Multitenancy\Tasks\SwitchTenantDatabaseTask::class,
\Spatie\Multitenancy\Tasks\SwitchRouteCacheTask::class,
],

/*
* 这个类是用于在租户上存储配置的模型。
*
* 必须是或继承 `Spatie\Multitenancy\Models\Tenant::class`
*/
'tenant_model' => \App\Models\Landlord\Tenant::class,

/*
* 如果在分派作业时有当前租户,则会在作业上自动设置当前租户的id。
* 执行作业时,作业上设置的租户将变为当前租户。
*/
'queues_are_tenant_aware_by_default' => true,

/*
* 用于连接租户数据库的连接名称。
*
* 如果设置为 `null`,则使用默认的数据库连接。
*/
'tenant_database_connection_name' => 'tenant',

/*
* 用于连接主数据库(Landlord 数据库)的连接名称。
*/
'landlord_database_connection_name' => 'landlord',

/*
* 此键名用于在 Laravel 服务容器(Container)中绑定当前租户实例。
*/
'current_tenant_container_key' => 'currentTenant',

/**
* 如果你希望使用 `SwitchRouteCacheTask` 将租户的路由缓存
* 到一个共享文件中,请将其设置为 `true`。
*/
'shared_routes_cache' => false,

/*
* 你可以通过自定义 Action(动作类)来定制该包的部分行为。
* 你的自定义 Action 必须继承自默认的 Action 类。
*/
'actions' => [
'make_tenant_current_action' => MakeTenantCurrentAction::class,
'forget_current_tenant_action' => ForgetCurrentTenantAction::class,
'make_queue_tenant_aware_action' => MakeQueueTenantAwareAction::class,
'migrate_tenant' => MigrateTenantAction::class,
],

/*
* 你可以自定义该包将队列任务(Queueable)解析为具体 Job 的方式。
*
* 例如,使用 laravel-actions 包(作者:Loris Leiva)时,
* 你可以将 JobDecorator 解析为 getAction(),配置如下:
* JobDecorator::class => 'getAction'
*/
'queueable_to_job' => [
SendQueuedMailable::class => 'mailable',
SendQueuedNotifications::class => 'notification',
CallQueuedClosure::class => 'closure',
CallQueuedListener::class => 'class',
BroadcastEvent::class => 'event',
],

/*
* 即使以下 Job 没有实现 TenantAware 接口,也会自动使其具备租户感知能力。
*/
'tenant_aware_jobs' => [
// ...
],

/*
* 即使以下 Job 没有实现 NotTenantAware 接口,也会强制使其失去租户感知能力。
*/
'not_tenant_aware_jobs' => [
// ...
],
];

自定义租户查找器

通过 X-Tenant-ID header 来确定当前租户。
在 config/multitenancy.php 中配置 tenant_finder 为这个类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php
namespace App\Multitenancy\TenantFinder;

use App\Models\Landlord\Tenant;
use Illuminate\Http\Request;
use Spatie\Multitenancy\TenantFinder\TenantFinder;

class HeaderTenantFinder extends TenantFinder
{
/**
* 3.x 签名:必须返回 Tenant 实例
*/
public function findForRequest(Request $request): \App\Models\Landlord\Tenant|null
{
// 这里可以换成子域名
$id = (int)trim($request->headers->get('X-Tenant-ID', ''));

if ($id === 0 || (!$tenant = Tenant::find($id))) {
abort(400, '无效的商户ID');
}

return $tenant;
}
}

模型集成

  • Spatie\Multitenancy\Models\Tenant 类有切换租户的能力。
  • Spatie\Multitenancy\Models\Concerns\UsesLandlordConnection 自动把模型数据库连接切换到中心库。
  • Spatie\Multitenancy\Models\Concerns\UsesTenantConnection 自动把模型数据库连接切换到租户库。

租户分配模型通过继承 Spatie\Multitenancy\Models\Tenant 类,使用 Spatie\Multitenancy\Models\Concerns\UsesLandlordConnection Trait来集成。
中心库模型使用 Spatie\Multitenancy\Models\Concerns\UsesLandlordConnection Trait 集成。
租户模型使用 Spatie\Multitenancy\Models\Concerns\UsesTenantConnection Trait 集成。

授权

App\Http\Kernel::$middlewareGroups 中添加:

1
2
3
4
5
6
7

'tenant' => [
// 获取当前租户
\Spatie\Multitenancy\Http\Middleware\NeedsTenant::class,
// 用 session 的时候需要添加
// \Spatie\Multitenancy\Http\Middleware\EnsureValidTenantSession::class,
],

在路由中使用中间件:

1
2
3
4
5
6
Route::middleware(['tenant', 'auth:sanctum'])->group(function () {
Route::get('/api/xxx', function () {
// 这里已经切换到租户数据库连接
$user = request()->user();
});
});

使用了 tenant 中间件后,自动调用 HeaderTenantFinder::findForRequest() 查找到租户,并切换到租户数据库。

tenant 和 auth:sanctum 放的顺序很重要。
如果用户表放在中心库,那么 auth:sanctum 中间件必须放在 tenant 中间件之前。
如果用户表放在租户库,那么 tenant 中间件必须放在 auth:sanctum 中间件之前,并且还要设置中间件的优先级,把
\Spatie\Multitenancy\Http\Middleware\NeedsTenant::class, 放到 App\Http\Kernel::$middlewarePriority 最前面。

App\Http\Kernel 没看到 $middlewarePriority,则到父类连其列表值一起复制下来。

临时切换切换到租户数据库

$tenant->execute(closure):临时切换、自动恢复、安全隔离。
只在闭包内生效,执行完自动切回原来的租户 / 主库,不会污染全局状态。

1
2
3
4
$tenant = Spatie\Multitenancy\Models\Tenant::find(1);
$tenant->execute(function() {
// 这里可以执行租户数据库的操作
});

永久切换切换到租户数据库

$tenant->makeCurrent():永久切换(直到手动改)、全局生效、需手动恢复。
一旦调用,整个后续请求生命周期都用这个租户库,直到你 forgetCurrent() 或切别的租户。

1
2
3
$tenant = Spatie\Multitenancy\Models\Tenant::find(1);
// 直接把当前租户数据库连接切换到租户库
$tenant->makeCurrent();

最简单的集成方式

上面的集成方式比较优雅,通过子域名或header来集成。

最简单的集成方式是通过URL参数来集成。

  1. 这个方式不需要实现 App\Multitenancy\TenantFinder\TenantFinder 类。
    在 config/multitenancy.php 中配置 tenant_finder 为 ``。

  2. 不需要添加切换租户的路由中间件。

例如:

1
2
3
4
5
Route::get('/api/{tenant}/other', function ($tenant) {
$tenant->execute(function() {
// 这里已经切换到租户数据库连接了
});
});

这个方式的缺点是不优雅,所有路由都需要添加租户参数、所有调用租户数据库的地方都是通过 $tenant->execute(closure) 来切换租户。

跨境电商系统多语言内容的设计方案

方案1:一个表设计一个多语言内容关联表

表解构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- product_categories 
- id
- product_categorie_l10ns
- category_id # 关联 product_categories.id
- locale
- name
- products
- id
- category_id # 关联 product_categories.id
- product_l10ns
- product_id # 关联 products.id
- locale
- title
- desciption
- detail
阅读更多

WSL下配置AlpineLinux作为PHP开发环境

Alpine Linux 占用空间少,可定制性高,除了很适合跑 docker 容器外,也很适合跑在 wsl 中。

下载 WSL 版 Alpine Linux

打开链接 https://github.com/yuk7/AlpineWSL/releases 找到系统对应的 Alpine 版本,如 Alpine.zip,解压到你想要存放的位置,点击 Alpine.exe 文件即可安装好。

安装好后,将在 Alpine.exe 所在文件夹中创建 ext4.vhdx 虚拟磁盘文件,作为 AlpineLinux 的磁盘。控制台执行命令 wsl -l -v,将看到新安装的 wsl 发行版 Alpine

启动 Alpine

命令 wsl -d Alpine

使用国内镜像源

打开源配置:

1
vi /etc/apk/repositories

注释掉默认源:

1
2
#https://dl-cdn.alpinelinux.org/alpine/v3.18/main
#https://dl-cdn.alpinelinux.org/alpine/v3.18/community

加入阿里源配置:

1
2
http://mirrors.aliyun.com/alpine/v3.19/main
http://mirrors.aliyun.com/alpine/v3.19/community

或 加入腾讯源配置:

1
2
https://mirrors.cloud.tencent.com/alpine/v3.19/main
https://mirrors.cloud.tencent.com/alpine/v3.19/community

执行更新命令更新源数据和已安装的apk包:

1
apk -U upgrade

部署PHP 8.2

1
2
3
4
5
apk add php82 php82-pear php82-dev php82-pdo_mysql \
php82-iconv php82-mbstring php82-tokenizer \
php82-session php82-dom curl php82-curl \
php82-xmlwriter php82-fileinfo php82-posix \
redis php82-pecl-redis

82 改为 8183 即为安装 php-8.1 或 php-8.3
如果出现错误,apk add gcc,``

如果安装 php-8.1 出现一下错误:

1
2
3
4
5
6
7
ERROR: unable to select packages:
php81-pecl-msgpack-2.2.0-r0:
conflicts: php82-pecl-msgpack-2.2.0-r0[php-msgpack=2.2.0-r0]
satisfies: php81-pecl-redis-6.0.2-r0[php81-pecl-msgpack]
php82-pecl-msgpack-2.2.0-r0:
conflicts: php81-pecl-msgpack-2.2.0-r0[php-msgpack=2.2.0-r0]
satisfies: php82-pecl-redis-6.0.2-r0[php82-pecl-msgpack]

安装脚本中去掉 php82-pecl-redis再执行。

php8 版本切换

如果同时安装多个 php 版本,可通过脚本来更换默认版本php。

创建脚本 vi /usr/php-switch,输入以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/bin/bash
cd /usr/bin

rm -f php php-config phpize pecl phar phar.phar

v=${1:-82}

ln -s php${v} php
ln -s php-config${v} php-config
ln -s phpize${v} phpize
ln -s pecl${v} pecl
ln -s phar.phar${v} phar
ln -s phar.phar${v} phar.phar

echo '已切换到php'$v

设置权限为可执行 chmod +x /usr/php-switch

1
2
3
4
5
6
7
8
# 默认切换到php 8.2
php-switch

# 切换到php 8.1
php-switch 81

# 切换到php 8.3
php-switch 83

安装 Nodejs

1
apk add icu-data-full nodejs npm

什么是编程思维?简化问题的艺术

1、编程思维

编程思维 不是编写程序的技巧,而是一种解决问题的思维方式,它强调的是把问题分解成更小的部分,然后逐个解决。编程思维的核心是抽象模块化自动化

  • 抽象 是指将问题简化为更基本的概念。例如,在编写一个计算两个数之和的程序时,我们可以将问题抽象为“将两个数字相加”。

  • 模块化 是指将问题分解成更小的部分。例如,在编写一个绘制正方形的程序时,我们可以将问题分解为以下几个步骤:

    • 绘制一条线
    • 旋转 90 度
    • 再绘制一条线
    • 再旋转 90 度
    • 重复步骤 1 到 4,直到绘制完成
  • 自动化 是指使用计算机程序来解决问题。例如,在编写一个计算两个数之和的程序时,我们可以使用计算机程序自动完成计算过程。

阅读更多

少儿学习编程有什么意义?

少儿学习编程有很多意义,主要体现在以下几个方面:

1、培养编程思维能力

编程思维(也叫计算思维)是一种解决问题的思维方式,它强调的是把问题分解成更小的部分,然后逐个解决。编程思维的核心是抽象模块化自动化。在学习编程的过程中,孩子可以练习如何运用编程思维来解决问题,孩子需要理解问题、拆解问题、抽象问题、设计解决方案、实施方案等一系列步骤。这种思维方式强调从问题到解决方案的转化过程,培养了孩子的逻辑思维和分析问题的能力。

这可以帮助孩子提高解决问题的能力,并为未来的学习和工作打下基础。

阅读更多

SadTalker 安装笔记

环境

  • 阿里云PAI-DSW
  • ubuntu20.04
  • pytorch:1.12
  • gpu-cu113
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
git clone https://github.com/OpenTalker/SadTalker.git

cd SadTalker

conda create -n sadtalker python=3.8

conda activate sadtalker

conda info

# 把 pip 源设为腾讯源
pip config set global.index-url https://mirrors.cloud.tencent.com/pypi/simple

pip install torch==1.12.1+cu113 torchvision==0.13.1+cu113 torchaudio==0.12.1 --extra-index-url https://download.pytorch.org/whl/cu113

# cpu 版
# pip --default-timeout=1000 install torch==1.12.1+cpu torchvision==0.13.1+cpu torchaudio==0.12.1 -f https://download.pytorch.org/whl/torch_stable.html

conda install ffmpeg

pip install -r requirements.txt

### Coqui TTS is optional for gradio demo.
### pip install TTS

# 下载模型

python inference.py
–driven_audio ../ds1.wav
–source_image ../2.jpg
–enhancer gfpgan
–preprocess full
–still

python inference.py –driven_audio test/ds1.wav –source_image test/1.jpg –enhancer gfpgan –cpu

3D 版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
git clone https://github.com/OpenTalker/SadTalker.git
cd SadTalker
conda create -n sadtalker3d python=3.8
source activate sadtalker3d

conda install ffmpeg
pip install fvcore iopath
conda install libgcc gmp

pip install torch==1.11.0+cu113 torchvision==0.12.0+cu113 torchaudio==0.11.0 --extra-index-url https://download.pytorch.org/whl/cu113

# insintall pytorch3d
pip install --no-index --no-cache-dir pytorch3d -f https://dl.fbaipublicfiles.com/pytorch3d/packaging/wheels/py38_cu113_pyt1110/download.html

pip install -r requirements3d.txt

### install gpfgan for enhancer
pip install git+https://github.com/TencentARC/GFPGAN


### when occurs gcc version problem `from pytorch import _C` from pytorch3d, add the anaconda path to LD_LIBRARY_PATH
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/$YOUR_ANACONDA_PATH/lib/

python inference.py \
--driven_audio ../yiyi-tts.wav \
--source_image ../3.jpg \
--still \
--preprocess full \
--enhancer gfpgan