上一篇我們進行的是將「整個程式碼」的重構開發都交給ChatGPT
但這樣要處理工作的話,要花太多時間在詠唱
這一篇會介紹比較分片、碎片式的方式來撰寫程式!
什麼意思呢?
也就是說,為了減少詠唱所花費的精神,我們交給ChatGPT的任務會選擇比較單一的內容
不會讓ChatGPT從頭幫我們又搭建環境、又要設置NestJS的專案結構、模組與路由
又要它同時兼顧一些零零叩叩的規則與要求
目前的ChatGPT免費版,也就是GPT-3.5,要做到這樣的程度,可能還是有一些難度
所以,我們這篇與ChatGPT之間的關係,就像是美髮院的設計師與助理的關係
助理要負責洗頭、把頭髮吹乾、打掃地板、整理推車
甚至在設計師調好染膏或藥水之類的,要幫忙上染膏
這些工作都很花時間、費時,也有專業度的成分在裡面
但對設計師來說就是瑣碎、設計師自己沒有時間去做的事情
ChatGPT也是一樣,我們現在要給他交付的任務,就是這樣單一任務
他就能夠把任務做得很好
一起來看範例,Go Go!
有關swagger與 class-validator 的介紹可以看這兩篇:
【Day44】ChatGPT請教教我:NestJS!(三)- Controller & 資料驗證 !class-validator & class-transformer!
【Day47】ChatGPT請教教我:NestJS!(六)- API測試兼文件!swagger!
對我來說,先定義好最基本的TypeScript型別,然後再交給ChatGPT幫我補上對應的裝飾器
可以省下我很多時間,省去既要定義 name: string
又要加上 @IsString()
裝飾器的麻煩
直接上範例!我這邊有一個type的一部分
以下的type範例出自於我自己的side project,與商用、工作用程式碼無關
type AppendTerm = {
name: string;
money: number;
};
好的,我會幫你將這段程式碼改寫成 DTO 並加上 class-validator 與 swagger 的裝飾器。以下是改寫後的程式碼:
import { IsString, IsNumber } from 'class-validator'; import { ApiProperty } from '@nestjs/swagger'; export class AppendTermDto { @ApiProperty({ description: '名稱', example: '節氣活動' }) @IsString() name: string; @ApiProperty({ description: '金額', example: 1000 }) @IsNumber() money: number; }
這樣就將
AppendTerm
改寫成了一個名為AppendTermDto
的 DTO,並加上了 class-validator 與 swagger 的裝飾器,同時也加上了說明與範例。如果還有其他需要幫忙的地方,請隨時告訴我。
接下來的type轉換都不需要再給其他內容了,直接貼都沒問題,像這樣
好的,我會幫你將這段程式碼改寫成 DTO 並加上 class-validator 與 swagger 的裝飾器。以下是改寫後的程式碼:
import { IsString, IsNumber, ValidateNested, ArrayNotEmpty } from 'class-validator'; import { Type } from 'class-transformer'; import { ApiProperty } from '@nestjs/swagger'; export class AppendTermDto { @ApiProperty({ description: '名稱', example: '節氣活動' }) @IsString() name: string; @ApiProperty({ description: '金額', example: 1000 }) @IsNumber() money: number; } export class MenuDataDto { @ApiProperty({ description: '名稱', example: '套餐一' }) @IsString() name: string; @ApiProperty({ description: '金額', example: 2000 }) @IsNumber() money: number; @ApiProperty({ description: '附加項目', type: [AppendTermDto], example: [{ name: '節氣活動', money: 1000 }] }) @ValidateNested({ each: true }) @ArrayNotEmpty() @Type(() => AppendTermDto) appendMenu: AppendTermDto[]; }
這樣就將
MenuData
改寫成了一個名為MenuDataDto
的 DTO,並加上了 class-validator 與 swagger 的裝飾器,同時也加上了說明與範例。如果還有其他需要幫忙的地方,請隨時告訴我。
然後再進階一層,這是一個有用到剛剛AppendTerm 之外,還有另外用Enum的
是一個更複雜的case
enum ReceiptType {
'尚未出貨',
'已出貨,物流中',
'已到貨尚未簽收',
'已簽收完畢',
}
type GoodsData = {
name: string,
money: number,
number: number , //數量 ,需要>=1
note: string ,
appendTermList: Array<AppendTerm>, // 額外指定項目
receiptType:ReceiptType,
uid: string,
}
好的,我會幫你將這段程式碼改寫成 DTO 並加上 class-validator 與 swagger 的裝飾器,包括處理
enum
類型。以下是改寫後的程式碼:import { IsString, IsNumber, Min, IsEnum, ArrayNotEmpty, ValidateNested } from 'class-validator'; import { Type } from 'class-transformer'; import { ApiProperty, ApiPropertyOptions } from '@nestjs/swagger'; enum ReceiptType { '尚未出貨', '已出貨,物流中', '已到貨尚未簽收', '已簽收完畢', } export class GoodsDataDto { @ApiProperty({ description: '名稱', example: '商品名稱' }) @IsString() name: string; @ApiProperty({ description: '金額', example: 1000 }) @IsNumber() money: number; @ApiProperty({ description: '數量', example: 5 }) @IsNumber() @Min(1) number: number; @ApiProperty({ description: '備註', example: '商品備註' }) @IsString() note: string; @ApiProperty({ description: '附加項目', type: [AppendTermDto], example: [{ name: '節氣活動', money: 1000 }] }) @ValidateNested({ each: true }) @ArrayNotEmpty() @Type(() => AppendTermDto) appendTermList: AppendTermDto[]; @ApiProperty({ description: '收據狀態', enum: ReceiptType, enumName: 'ReceiptType', example: ReceiptType['尚未出貨'] }) @IsEnum(ReceiptType, { message: '請提供有效的收據狀態' }) receiptType: ReceiptType; @ApiProperty({ description: '用戶 ID', example: 'user123' }) @IsString() uid: string; }
這樣就將
GoodsData
改寫成了一個名為GoodsDataDto
的 DTO,並加上了 class-validator 與 swagger 的裝飾器,同時也加上了說明與範例,並處理了enum
類型的欄位。如果還有其他需要幫忙的地方,請隨時告訴我。
全部舒舒服服。
複雜的裝飾器設定,以及繁雜的description、example都幫你根據命名自動判斷
只要順過一次,確定ChatGPT的判斷是不是正確的,再做範例內容或是要求的微幅調整就好
而且假設我對限制條件的裝飾器不熟,也可以這樣問
你可以使用
MaxLength
裝飾器來限制note
欄位的最大長度為 100 字。以下是修改後的程式碼:import { IsString, IsNumber, Min, IsEnum, ArrayNotEmpty, ValidateNested, MaxLength } from 'class-validator'; import { Type } from 'class-transformer'; import { ApiProperty, ApiPropertyOptions } from '@nestjs/swagger'; // ... (前面的程式碼) export class GoodsDataDto { // ... (其他欄位) @ApiProperty({ description: '備註', example: '商品備註' }) @IsString() @MaxLength(100, { message: '備註不能超過100字' }) note: string; // ... (其他欄位) }
現在
note
欄位的最大長度被限制為 100 字。如果還有其他需要幫忙的地方,請隨時告訴我。
可以省去google 「class-validator string長度限制 」之類字眼的時間
有關SQL語法的介紹可以看這幾篇:
【Day38】ChatGPT請教教我:資料庫!MySQL!MySQL Workbench!
【Day39】ChatGPT請教教我:基礎SQL語法!NodeJS連結SQL!
【Day40】ChatGPT請教教我:進階SQL語法!JOIN!
雖然會了SQL語法,但很多時候可能因為需要的query比較複雜
操作資料的技巧又不夠純熟的時候,不仿試看看請ChatGPT產生看看
假設我們有一個這兩個表格
訂購紀錄表:
名稱 | 訂購數量 | 交易時間 |
---|---|---|
A - 05 | 1 | 2023-11-05 12:49:55 |
B - 24 | 3 | 2023-11-05 09:00:23 |
B - 03 | 2 | 2023-11-05 09:50:45 |
A - 12 | 2 | 2023-11-05 21:53:27 |
類別表:
名稱 | 類型 |
---|---|
A - 05 | 家電 |
A - 12 | 家電 |
B - 24 | 美妝 |
B - 03 | 美妝 |
希望得出「每天、每小時,每個類型的訂購數量統計」
以下是你可以使用的 SQL 語法來得出「每天、每小時,每種類型的訂購數量統計」:
SELECT DATE(orders.transaction_time) as order_date, HOUR(orders.transaction_time) as order_hour, categories.type_name, SUM(orders.quantity) as total_quantity FROM orders JOIN categories ON orders.name = categories.name GROUP BY order_date, order_hour, categories.type_name ORDER BY order_date, order_hour, categories.type_name;
這個 SQL 語法會將訂單表 (
orders
) 與類別對照表 (categories
) 進行連接,並以交易時間 (transaction_time
) 的日期和小時、類型名稱進行分組,最後計算每個分組的訂購數量總和。請確保你在資料庫中有正確的表格和欄位名稱,並進行適當的資料庫連接。如果還有其他需要幫忙的地方,請隨時告訴我。
然後我這邊額外提供這個demo用表格創建與資料創建的sql語法
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
name VARCHAR(255),
quantity INT,
transaction_time TIMESTAMP
);
CREATE TABLE categories (
id SERIAL PRIMARY KEY,
name VARCHAR(255),
type_name VARCHAR(255)
);
-- 插入訂單資料
INSERT INTO orders (name, quantity, transaction_time) VALUES
('A - 05', 1, '2023-11-05 12:49:55'),
('B - 24', 3, '2023-11-05 09:00:23'),
('B - 03', 2, '2023-11-05 09:50:45'),
('A - 12', 2, '2023-11-05 21:53:27');
-- 插入類別資料
INSERT INTO categories (name, type_name) VALUES
('A - 05', '家電'),
('A - 12', '家電'),
('B - 03', '美妝'),
('B - 24', '美妝');
接著就可以看到Query 出來的結果,確實就是我們要的
有關knex的介紹可以看看這篇:
【Day49】ChatGPT請教教我:NestJS!(八)- SQL & Knex!SQL注入攻擊!?
先來將剛剛的Query做改寫看看
當然,我可以幫你將這段 SQL 語法轉換成 Knex 的方法鏈語法。以下是轉換後的程式碼:
const knex = require('knex')({ client: 'mysql', connection: { host : 'your_host', user : 'your_user', password : 'your_password', database : 'your_database' } }); knex('orders') .join('categories', 'orders.name', 'categories.name') .select( knex.raw('DATE(orders.transaction_time) as order_date'), knex.raw('HOUR(orders.transaction_time) as order_hour'), 'categories.type_name', knex.raw('SUM(orders.quantity) as total_quantity') ) .groupBy('order_date', 'order_hour', 'categories.type_name') .orderBy('order_date') .orderBy('order_hour') .orderBy('categories.type_name') .then(rows => { console.log(rows); }) .catch(error => { console.error(error); }) .finally(() => { knex.destroy(); });
請將
your_host
、your_user
、your_password
、your_database
替換成你實際的資料庫連接資訊。這段程式碼使用了 Knex 的方法鏈,依照你的 SQL 語法進行了轉換。它會執行查詢並輸出結果,或是在發生錯誤時輸出錯誤訊息。如果你有任何其他問題,請隨時告訴我。
執行起來的結果,相當不錯
很複雜的query好像只能靠.raw() 處理,不過來一個應該不需要那麽多.raw的例子看看
`SELECT isSuccess, count(*) as count ,save FROM job_table
WHERE endTime >= '${startDate}' and endTime<= '${endDate}'
and type in ('typeA','typeC')
GROUP BY isSuccess,saveWae`
以下是將你的 SQL 查詢轉換為 Knex 語法並使用
await
的程式碼:const knex = require('knex')({ client: 'mysql', connection: { host: 'your_host', user: 'your_user', password: 'your_password', database: 'your_database' } }); async function fetchJobData(startDate, endDate) { try { const result = await knex('job_table') .select('isSuccess') .count('* as count') .select('save') .where('endTime', '>=', startDate) .andWhere('endTime', '<=', endDate) .whereIn('type', ['typeA', 'typeC']) .groupBy('isSuccess', 'save'); return result; } catch (error) { throw error; } finally { knex.destroy(); } } const startDate = 'your_start_date'; // 替換為實際的起始日期 const endDate = 'your_end_date'; // 替換為實際的結束日期 fetchJobData(startDate, endDate) .then(result => { console.log(result); }) .catch(error => { console.error(error); });
請將
'your_host'
、'your_user'
、'your_password'
、'your_database'
、'your_start_date'
和'your_end_date'
替換為你的資料庫連接資訊以及日期範圍。這個程式碼使用了await
語法,以非同步方式執行查詢,並返回結果或處理錯誤。
不錯不錯,方便方便
這個功能在我把程式碼進行改寫翻新,改用knex語法的時候很常使用到
有關migration的介紹,可以看看這篇的後半段:
【Day50】ChatGPT請教教我:NestJS!(九)- Knex!transaction!migration!
在學了migration 之後,想針對現有的表格進行migration的設置
但可能連一開始建立這個結構的CREAT SQL語法都不太清楚,先來問看看ChatGPT要怎麼取得
要取得現有 MySQL 表格的 CREATE 語法,你可以使用以下的 SQL 查詢:
SHOW CREATE TABLE your_table_name;
這個查詢會返回指定表格的 CREATE 語法。請將
your_table_name
替換為你想要取得 CREATE 語法的表格名稱。例如,如果你想要取得名為
orders
的表格的 CREATE 語法,你可以執行以下的 SQL 查詢:SHOW CREATE TABLE orders;
系統會返回類似以下的結果:
Table Create Table orders CREATE TABLE `orders` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, `quantity` int(11) DEFAULT NULL, `transaction_time` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
這段文字中包含了
orders
表格的 CREATE 語法。
實際執行之後,我得到了一段creat的語法,直接丟給他
接著問他要怎麼建立這個表格的migration
要建立這個表格的 migration 語法,你可以使用 Knex 的方法來定義表格的結構。以下是對應於你提供的 CREATE TABLE 語法的 Knex migration 語法:
exports.up = function(knex) { return knex.schema.createTable('orders', function(table) { table.bigIncrements('id').unsigned().primary(); table.string('name', 255).nullable(); table.integer('quantity').nullable(); table.timestamp('transaction_time').nullable(); }); }; exports.down = function(knex) { return knex.schema.dropTable('orders'); };
這個 migration 將會建立一個名為
orders
的表格,並定義了相應的欄位,類型和限制。在up
函數中,我們使用了 Knex 的方法來建立表格,並在down
函數中使用了dropTable
來刪除表格。如果你使用 Knex CLI 來管理 migrations,你可以將這個 migration 存放在你的 migrations 資料夾中,並使用
knex migrate:latest
來執行 migration。
然後追加需求
要在現有的表格中增加一個
user_id
欄位,你可以使用 Knex 的 migration 來實現。以下是對應於你的需求的 migration 語法:exports.up = function(knex) { return knex.schema.alterTable('orders', function(table) { table.string('user_id', 20).notNullable().defaultTo(''); }); }; exports.down = function(knex) { return knex.schema.alterTable('orders', function(table) { table.dropColumn('user_id'); }); };
在
up
函數中,我們使用了alterTable
方法來修改現有的表格orders
。然後我們使用string
方法來添加一個user_id
欄位,並指定了類型為字串,長度最大為 20,並設定為不可為空,預設值為空字串。在
down
函數中,我們再次使用alterTable
方法來修改表格,這次使用dropColumn
來刪除user_id
欄位。請將這個 migration 存放在你的 migrations 資料夾中,並使用
knex migrate:latest
來執行 migration。這樣就會在你的表格中新增了user_id
欄位。
接下來的這一點的內容比較是前後端都一樣可能遇到的!
時常在做處理的時候,可能會需要擷取某個特定格式中的字串
如果對於「正規表達式」這樣的語法不夠熟練的話
嗯,問ChatGPT方便到不行
假設,我們今天要處理這樣的一段文字
青菜 1 份 - 35 元
滷蛋 1 顆 - 10 元
滷肉飯 3 碗 - 90 元
希望可以
直接請ChatGPT來處理這樣的正規表達式吧
你可以使用 JavaScript 搭配正則表達式來達到你的目的。以下是一段範例程式碼:
const text = ` 青菜 1 份 - 35 元 滷蛋 1 顆 - 10 元 滷肉飯 3 碗 - 90 元 `; const regex = /(.+)\s(\d+)\s(?:份|顆|碗)\s-\s(\d+)\s元/g; let match; while ((match = regex.exec(text)) !== null) { const name = match[1]; const number = parseInt(match[2]); const money = parseInt(match[3]); console.log(`名稱:${name.trim()}, 數量:${number}, 金額:${money}`); }
這段程式碼會在給定的文字中搜尋符合正則表達式的部分,並將相應的變數取出來。具體來說:
name
取得了符合正則表達式中第一個括號的內容,即所有文字。number
取得了符合正則表達式中第二個括號的內容,即數量。money
取得了符合正則表達式中第三個括號的內容,即金額。這樣你就可以在循環中獲得每一行的相關資訊了。請注意,這段程式碼假設了文字格式的一致性,如果格式有變化,可能需要做進一步的調整。
執行結果:
再來一個範例,判斷「元」前面的數字,但是有可能有逗號
像這樣「15,056」這類的文字
你可以使用正則表達式來達到這個目的。以下是一段 JavaScript 代碼:
const text = "switch一台12,535元"; const regex = /(\d{1,3}(,\d{3})*(\.\d+)?)元/; const match = text.match(regex); if (match && match[1]) { const numberWithCommas = match[1]; const numberWithoutCommas = numberWithCommas.replace(/,/g, ''); console.log(numberWithoutCommas); } else { console.log('未找到匹配的數字'); }
這段代碼首先定義了一個正則表達式
(\\d{1,3}(,\\d{3})*(\\.\\d+)?)元
,它將匹配包含逗號的數字,直到找到 "元" 為止。接著使用
text.match(regex)
來在文本中尋找匹配,並將結果儲存在match
中。如果找到了匹配,則從
match
中取得匹配到的數字字串,並使用.replace(/,/g, '')
去掉逗號,最終得到了你想要的數字。如果未找到匹配,則會輸出 "未找到匹配的數字"。
爆改一下文字內容測試他能不能正確匹配
好耶可以~
本次對話記錄在這邊:
https://chat.openai.com/share/a7a73590-1a54-43ad-87fc-92505162d084
上面幾種方式都是我自己在工作上覺得很實用的部分
那也比起上一篇直接貼一大串給ChatGPT,效果更穩定
但也更需要自己去「組合」這些取得的程式碼
把自身的問題切割成小問題,然後丟給ChatGPT處理
自己再把相對應的程式碼融合在一起
輕鬆解決那些枯燥的繁重工作,甚至那些自己不熟悉的部分
另外預告一下,本系列預計為54篇 + 1篇完結感言
這一篇沒能來得及在昨天發出,連續發文的挑戰小小的斷掉了有一些可惜
不過因為在思考與規劃篇幅的部分,還有生出一些範例來示範真的花了一些時間
謝謝各位的閱讀!
我是開著ChatGPT工作的前端工程師一宵三筵,我們有可能明天見!