Gemini 官方有開放選項,讓使用者自行選擇是否要有浮水印,個人是還蠻喜歡浮水印,要不然還要特別打上由 AI 生成之類的警語
~楓花雪岳~
星期三, 8月 19, 2026
星期四, 7月 30, 2026
[SQL] 隱含轉換 - 彙總函數回傳值資料型態
一段 sum 並把結果 insert 進 Table TSQL 語法,拋出下面錯誤訊息
在官方文件 - sum return type 內有說明,彙總函式 sum 結果會發生資料型態隱含轉換,以該例來看就是 sum 把 smallmoney 資料彙總時,因為彙總結果超過 smallmoney 資料範圍,所以隱含轉換為 money,insert 回 smallmoney 欄位時拋出錯誤
簡易範例驗證
結果的空間不足,無法將 money (貨幣) 值轉換成 smallmoney。該欄位資料型態是 smallmoeny,很明顯是 sum 結果超過 smallmoney 資料範圍導致的異常,samllint 資料範圍在 -214,748.3648 到 214,748.3647 之間,但意外的是為什麼針對 smallmoney 欄位進行 sum 時是回傳 moeny 資料型態
在官方文件 - sum return type 內有說明,彙總函式 sum 結果會發生資料型態隱含轉換,以該例來看就是 sum 把 smallmoney 資料彙總時,因為彙總結果超過 smallmoney 資料範圍,所以隱含轉換為 money,insert 回 smallmoney 欄位時拋出錯誤
簡易範例驗證
use tempdb
GO
DROP TABLE IF EXISTS tblDemo
CREATE TABLE tblDemo
(
ID int identity(1,1) ,
ColSmallMoney smallmoney
)
INSERT INTO tblDemo (ColSmallMoney) VALUES(214748)
INSERT INTO tblDemo (ColSmallMoney) VALUES(214748)
GO
SELECT
SQL_VARIANT_PROPERTY(SUM(ColSmallMoney) , 'BaseType') -- 會得到 money 資料型態
FROM tblDemo
星期三, 7月 22, 2026
[GAS] 使用 JavaScript Library 來產生 QRCode
在 GAS 上使用 JavaScript Linrary - Kjua 來產生 QRCode
code.gs
Index.html
code.gs
function doGet() {
return HtmlService.createHtmlOutputFromFile('Index')
.setTitle('kjua QRCode 產生器');
}
Index.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<!-- 引入 kjua CDN -->
<script src="https://cdn.jsdelivr.net/npm/kjua@0.10.0/dist/kjua.min.js"></script>
<style>
body {
font-family: Arial, sans-serif;
text-align: center;
padding: 40px 20px;
}
input {
padding: 10px;
font-size: 16px;
width: 80%;
max-width: 300px;
margin-bottom: 10px;
}
button {
padding: 10px 20px;
font-size: 16px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background-color: #45a049;
}
#qrcode-container {
margin-top: 30px;
}
</style>
</head>
<body>
<h2>QRCode 產生器</h2>
<input type="text" id="text-input" value="https:/www.google.com" placeholder="請輸入要轉換的網址或文字" />
<br>
<button onclick="generateQRCode()">產生 QRCode</button>
<!-- QRCode 渲染區域 -->
<div id="qrcode-container"></div>
<script>
function generateQRCode() {
const textToEncode = document.getElementById('text-input').value;
const container = document.getElementById('qrcode-container');
// 避免重覆產生 QRCode,每次都先清空
container.innerHTML = "";
if (!textToEncode) {
alert("請輸入內容!");
return;
}
// 呼叫 kjua 並產生 QRCode
const qrCodeElement = kjua({
render: 'image', // 渲染為 <img> 標籤
text: textToEncode,
size: 250, // 尺寸設定為 250px
fill: '#000000', // QRCode 顏色
back: '#ffffff', // 背景顏色
rounded: 10 // 模組圓角 (0-100),讓外觀看起來更柔和
});
container.appendChild(qrCodeElement);
}
</script>
</body>
</html>
- 參考資料
- github - kuja
星期一, 7月 20, 2026
[GAS] 透過第三方 API 產生 QRCode
在 GAS 上使用 goQR.me 第三方服務,該服務只有兩個 API 可以使用,分別為
- create-qr-code:產生 QRCode
- read-qr-code:讀取、掃描 QRCode
/**
* 測試與執行範例:呼叫共用模組並儲存到 Google Drive
*/
function testGenerateQRCode() {
const myData = "https://workspace.google.com/";
const mySize = 250; // 將會產生 250x250 的 QR Code
const qrBlob = getQRCodeBlob(myData, mySize);
if (qrBlob) {
const file = DriveApp.getRootFolder().createFile(qrBlob);
Logger.log(`✅ QR Code 建立成功!檔案連結:${file.getUrl()}`);
} else {
Logger.log("❌ 流程中斷:無法取得 QR Code 圖片。");
}
}
/**
* 透過 goQR.me (api.qrserver.com) 產生 QR Code 的共用模組
*
* @param {string} textToEncode - 必填:要轉換為 QR Code 的內容(網址或純文字)
* @param {number} size - 選填:QR Code 的尺寸,預設為 300 (即 300x300 像素)
* @returns {GoogleAppsScript.Base.Blob|null} 成功時回傳圖片 Blob,失敗則回傳 null
*/
function getQRCodeBlob(textToEncode, size = 300) {
if (!textToEncode) {
Logger.log("❌ 錯誤:未傳入 textToEncode 參數");
return null;
}
// 組合 API 網址 (將單一 size 數值轉換為 api.qrserver.com 所需的 "寬x高" 格式)
const apiUrl = `https://api.qrserver.com/v1/create-qr-code/?size=${size}x${size}&data=${encodeURIComponent(textToEncode)}`;
try {
const response = UrlFetchApp.fetch(apiUrl, {
muteHttpExceptions: true
});
// 驗證並回傳 Blob
if (response.getResponseCode() === 200) {
const fileName = `QRCode_${new Date().getTime()}.png`;
return response.getBlob().setName(fileName);
} else {
Logger.log(`❌ 產生失敗,API 伺服器回應狀態碼:${response.getResponseCode()}`);
return null;
}
} catch (error) {
Logger.log(`❌ 執行階段發生例外狀況:${error.message}`);
return null;
}
}
星期六, 7月 11, 2026
[GAS] API 執行檔
根據官方文章-使用 Google Apps Script API 執行函式 的實作筆記,部屬 API 執行檔後在 C# 控制台上使用 OAuth2.0 來存取該 API,該 API 會 insert 資料進入 Google Sheet 內
說明
GCP 標準專案
建立標準專案 gas-api-executable-demo 並啟用 Google Apps Script API
利用搜尋功能尋找 Google Apps Script API 服務
在 Google Enterprise API 和 Google Workspace 分類內都可以找到
啟用 Apps Script API 服務
設定 OAuth 同意畫面
GAS 綁定 GCP 標準專案
在標準專案上取得專案編號 (如下圖)
在 GAS 專案上綁定專案標號
綁定完成
該筆記 [GAS] Goolge Cloud Logging 內有紀錄不同順序的設定流程,也可以參考看看
設定用戶端
Apps Script API 提供 scripts.run 方法,可遠端執行指定的 Google Apps Script 函式。您可以在呼叫應用程式中使用這個方法,從遠端執行其中一個指令碼專案中的函式,並接收回應。
需求條件
必須先完成下列事項,才能使用 scripts.run 方法:
- 將 GAS 部署為 API 執行檔
- 設定 OAuth Scopes 且必須涵蓋 GAS 使用相關服務,不能只是呼叫函式使用的服務,EX:https://www.googleapis.com/auth/spreadsheets
- 確認 GAS 和呼叫應用程式的 OAuth2 用戶端共用使用同一個 GCP 專案。GAS 專案必須是標準 GCP 專案,GAS 專案預設 GCP 專案無法使用
- 在 GCP 專案中啟用 Google Apps Script API。
GAS Code
該 code.gs 單純是 insert 資料進入 Google Sheet 內,可以透過 testAddCheckIn() 進行測試
點選專案設定並勾選「在編輯器中顯示 appsscript.json 資訊清單檔案」
開啟 appsscript.json 並把 oauthScopes 範圍加進去
該 code.gs 單純是 insert 資料進入 Google Sheet 內,可以透過 testAddCheckIn() 進行測試
const SPREADSHEET_ID = '填入 Google Sheet ID';
const SHEET_NAME = 'CheckIn';
/**
* 本機測試用:可先在 Apps Script 編輯器內執行,確認可正常寫入資料
*/
function testAddCheckIn() {
return addCheckIn({
identifier: 'U001',
meetingCode: 'MEET-001',
deviceInfo: 'Apps Script Editor Test'
});
}
/**
* 給外部 API Executable 呼叫的主函式
*/
function addCheckIn(payload) {
payload = payload || {};
const identifier = String(payload.identifier || '').trim();
const meetingCode = String(payload.meetingCode || '').trim();
const deviceInfo = String(payload.deviceInfo || '').trim();
if (!identifier || !meetingCode) {
return {
success: false,
status: 'NO_REQUIRED_DATA',
message: 'identifier 和 meetingCode 為必填'
};
}
const now = new Date();
const checkedAt = Utilities.formatDate(
now,
'Asia/Taipei',
'yyyy-MM-dd HH:mm:ss'
);
const sheet = getCheckInSheet_();
sheet.appendRow([
checkedAt,
identifier,
meetingCode,
deviceInfo,
'API_EXECUTABLE'
]);
return {
success: true,
status: 'CHECK_IN_SUCCESS',
checkedAt: checkedAt,
identifier: identifier,
meetingCode: meetingCode
};
}
function getCheckInSheet_() {
const ss = SpreadsheetApp.openById(SPREADSHEET_ID);
let sheet = ss.getSheetByName(SHEET_NAME);
if (!sheet) {
sheet = ss.insertSheet(SHEET_NAME);
sheet.appendRow(['時間', '識別碼', '會議代碼', '裝置資訊', '來源']);
}
return sheet;
}
設定 appsscript.json
點選專案設定並勾選「在編輯器中顯示 appsscript.json 資訊清單檔案」
開啟 appsscript.json 並把 oauthScopes 範圍加進去
{
"timeZone": "Asia/Taipei",
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8",
"oauthScopes": [
"https://www.googleapis.com/auth/spreadsheets"
]
}
GCP 標準專案
建立標準專案 gas-api-executable-demo 並啟用 Google Apps Script API
利用搜尋功能尋找 Google Apps Script API 服務
在 Google Enterprise API 和 Google Workspace 分類內都可以找到
啟用 Apps Script API 服務
設定 OAuth 同意畫面
GAS 綁定 GCP 標準專案
在標準專案上取得專案編號 (如下圖)
在 GAS 專案上綁定專案標號
綁定完成
該筆記 [GAS] Goolge Cloud Logging 內有紀錄不同順序的設定流程,也可以參考看看
設定用戶端
- 應用程式類別:選擇電腦版應用程式
- 名稱:C# Console
按下 [下載 json] 按鈕後,可以下載 credentials.json 檔案,檔案名稱為 client_secret_用戶端ID.apps.googleusercontent.com
部署 API 執行檔
部屬 => 新增部屬 => API 執行檔,誰可以存取」選項有 「只有我自己」、「所以已登入 Google 帳號使用者」
部屬完成
部屬時假如是預設專案的話,會出現下圖並引導至設定內,要求設定為標準專案
C# Console App 呼叫
首先要先確認部屬 ID,下圖為部屬時畫面上的部屬 ID,事後要取得的話,請從管理部屬進入取的
credentials.json 檔案放在 EXE 執行檔案資料夾內
安裝 NuGet
執行結果
token 會儲存在本機 token.json 資料夾,後續執行就不須再次授權
C# Code,必須填入 deploymentId 和 credentialsFileName
部署 API 執行檔
部屬 => 新增部屬 => API 執行檔,誰可以存取」選項有 「只有我自己」、「所以已登入 Google 帳號使用者」
部屬完成
部屬時假如是預設專案的話,會出現下圖並引導至設定內,要求設定為標準專案
C# Console App 呼叫
首先要先確認部屬 ID,下圖為部屬時畫面上的部屬 ID,事後要取得的話,請從管理部屬進入取的
credentials.json 檔案放在 EXE 執行檔案資料夾內
安裝 NuGet
Install-Package Google.Apis.Script.v1
Install-Package Google.Apis.Auth
第一次執行 C# 程式時,瀏覽器會跳出 Google 授權畫面,看要選擇哪一個帳號來執行,該帳號必須是在 GCP 目標對象內執行結果
token 會儲存在本機 token.json 資料夾,後續執行就不須再次授權
C# Code,必須填入 deploymentId 和 credentialsFileName
using Google.Apis.Auth.OAuth2;
using Google.Apis.Script.v1;
using Google.Apis.Script.v1.Data;
using Google.Apis.Services;
using Google.Apis.Util.Store;
internal class Program
{
private static async Task Main()
{
string deploymentId = "請填入 API Executable 的 Deployment ID";
string credentialsFileName = "請填入 credentials.json 檔案名稱或是完整檔案路徑";
string[] scopes =
{
"https://www.googleapis.com/auth/spreadsheets"
};
UserCredential credential;
using (var stream = new FileStream(credentialsFileName, FileMode.Open, FileAccess.Read))
{
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.FromStream(stream).Secrets,
scopes,
"user",
CancellationToken.None,
new FileDataStore("token.json", true)
);
}
var service = new ScriptService(new BaseClientService.Initializer
{
HttpClientInitializer = credential,
ApplicationName = "GAS API Executable Demo"
});
var payload = new Dictionary<string, object>
{
{ "identifier", "U001" },
{ "meetingCode", "MEET-001" },
{ "deviceInfo", "C# Console App" }
};
var request = new ExecutionRequest
{
Function = "addCheckIn",
Parameters = new List<object> { payload }
};
Operation operation = await service.Scripts.Run(request, deploymentId).ExecuteAsync();
if (operation.Error != null)
{
Console.WriteLine("GAS 執行失敗:");
Console.WriteLine(operation.Error.Message);
return;
}
if (operation.Response != null && operation.Response.ContainsKey("result"))
{
Console.WriteLine("GAS 回傳結果:");
Console.WriteLine(operation.Response["result"]);
}
else
{
Console.WriteLine("GAS 執行完成,但沒有回傳 result。");
}
}
}
Google Sheet 內資料
訂閱:
文章 (Atom)































