This commit is contained in:
lychang
2026-05-20 03:25:35 +08:00
parent 5e3dac3020
commit 7f5350db62
30 changed files with 3370 additions and 1752 deletions

188
cmd/main.go Normal file
View File

@@ -0,0 +1,188 @@
package main
import (
"encoding/json"
"fmt"
"os"
"strings"
"time"
"wasm-go/internal/api"
"wasm-go/internal/excel"
)
func main() {
fmt.Println("========== Excel转JSON ==========")
excelFile := "yst郑州惠兴财务报表2021(3).xlsx"
// 检查Excel文件是否存在
if _, err := os.Stat(excelFile); os.IsNotExist(err) {
fmt.Printf("❌ Excel文件不存在: %s\n", excelFile)
return
}
// 读取Excel文件为bytes
excelBytes, err := os.ReadFile(excelFile)
if err != nil {
fmt.Printf("❌ 读取Excel文件失败: %v\n", err)
return
}
fmt.Println("✓ 读取Excel文件为字节成功")
// 使用NewXLSXReaderFromBytes从bytes创建读取器
reader, err := excel.NewXLSXReaderFromBytes(excelBytes)
if err != nil {
fmt.Printf("❌ 创建XLSX读取器失败: %v\n", err)
return
}
fmt.Println("✓ 创建XLSX读取器成功")
// 读取所有的sheets并转换为markdown格式
var allMarkdownTables strings.Builder
allMarkdownTables.WriteString("以下是Excel表格的数据:\n\n")
sheets, err := reader.GetSheetNames()
if err != nil {
fmt.Printf("❌ 获取工作表名称失败: %v\n", err)
return
}
for _, sheetName := range sheets {
fmt.Printf("\n========== 工作表: %s ==========\n", sheetName)
data, err := reader.GetSheetData(sheetName)
if err != nil {
fmt.Printf("❌ 读取工作表失败: %v\n", err)
continue
}
if len(data) == 0 {
fmt.Println("⚠️ 工作表为空")
continue
}
fmt.Printf("找到 %d 行数据\n", len(data))
// 转换为markdown格式
markdown := excel.ConvertToMarkdown(data)
allMarkdownTables.WriteString(fmt.Sprintf("## 工作表: %s\n", sheetName))
allMarkdownTables.WriteString(markdown)
allMarkdownTables.WriteString("\n\n")
}
fmt.Println("\n✅ 所有工作表已转换为Markdown格式!")
// 读取quarterly1.txt的prompt
promptBytes, err := os.ReadFile("./quarterly1.txt")
if err != nil {
fmt.Printf("❌ 读取quarterly1.txt失败: %v\n", err)
return
}
prompt := string(promptBytes)
// 合并prompt和markdown表格
finalPrompt := fmt.Sprintf("%s\n\n%s", prompt, allMarkdownTables.String())
// 尝试连接到不同的LLM提供商
var response *api.ChatResponse
var llmErr error
// 首先尝试Ollama
fmt.Println("\n🔄 尝试连接到Ollama服务...")
if ollamaClient := api.NewOllamaClient("http://localhost:11434", "qwen3.5:9b"); ollamaClient != nil {
response, llmErr = ollamaClient.Chat([]api.ChatMessage{
{Role: "user", Content: finalPrompt},
})
if llmErr == nil {
fmt.Println("✓ 成功使用Ollama生成响应")
} else {
fmt.Printf("⚠ Ollama调用失败: %v\n", llmErr)
// 检查是否是连接问题
if strings.Contains(llmErr.Error(), "connection refused") || strings.Contains(llmErr.Error(), "404") {
fmt.Println("💡 提示:Ollama服务可能未启动,或模型未安装")
}
}
}
fmt.Println("\n========== LLM生成的JSON ==========")
// 解析LLM返回的响应,提取```json{内容}```中的JSON部分
parsedJSON := extractJSONFromResponse(response.Choices[0].Message.Content)
fmt.Println(parsedJSON)
fmt.Println("==================================")
// 使用解析出的JSON调用税务健康API
fmt.Println("\n🔄 正在使用解析的JSON调用税务健康API...")
// 创建税务健康服务客户端
// 注意:这里需要替换为实际的用户ID和私钥
taxHealthService := api.NewTaxHealthService("1560142251348287489", "")
// 将解析的JSON解析为map结构
var companyData map[string]interface{}
if err := json.Unmarshal([]byte(parsedJSON), &companyData); err != nil {
fmt.Printf("❌ 解析JSON数据失败: %v\n", err)
return
}
// 构建税务评估请求
request := api.TaxEvalRequest{
ReqNo: fmt.Sprintf("REQ%v", time.Now().Unix()), // 使用时间戳生成请求号
Detail: api.TaxEvalDetail{
CompanyName: "企业名称", // 可以从数据中提取公司名称
Data: companyData,
},
}
// 调用税务健康API
taxResponse, err := taxHealthService.EvaluateTaxHealth(request)
if err != nil {
fmt.Printf("❌ 调用税务健康API失败: %v\n", err)
return
}
fmt.Println("\n========== 税务健康API反馈结果 ==========")
fmt.Printf("API响应: %+v\n", taxResponse)
fmt.Println("======================================")
}
// extractJSONFromResponse 从LLM响应中提取JSON内容
// 查找并提取 ```json {...} ``` 或 ``` {...} ``` 中的内容
func extractJSONFromResponse(response string) string {
// 首先尝试匹配 ```json 开头的代码块
start := strings.Index(response, "```json")
if start != -1 {
start += 7 // 跳过 "```json"
end := strings.Index(response[start:], "```")
if end != -1 {
jsonContent := strings.TrimSpace(response[start : start+end])
return jsonContent
}
}
// 然后尝试匹配 ``` 开头的代码块(假设是JSON)
start = strings.Index(response, "```")
if start != -1 {
start += 3 // 跳过 "```"
end := strings.Index(response[start:], "```")
if end != -1 {
jsonContent := strings.TrimSpace(response[start : start+end])
// 检查是否看起来像JSON
jsonContent = strings.TrimSpace(jsonContent)
if strings.HasPrefix(jsonContent, "{") || strings.HasPrefix(jsonContent, "[") {
return jsonContent
}
}
}
// 如果没有找到代码块,尝试直接解析整个响应是否为JSON
response = strings.TrimSpace(response)
if strings.HasPrefix(response, "{") && strings.HasSuffix(response, "}") ||
strings.HasPrefix(response, "[") && strings.HasSuffix(response, "]") {
return response
}
// 如果以上都失败,返回原内容
return response
}

55
cmd/test/main.go Normal file
View File

@@ -0,0 +1,55 @@
package main
import (
"fmt"
"log"
"wasm-go/internal/api"
)
func main() {
fmt.Println("=== 企业行业分类示例 ===")
// 创建LLM客户端 - 这里使用Ollama作为示例
// 如果Ollama不可用,也可以使用OpenAI
var llmClient *api.LLMClient
// 尝试连接到Ollama
fmt.Println("尝试连接到Ollama服务...")
llmClient = api.NewOllamaClient("http://localhost:11434", "qwen3.5:9b")
// 如果需要使用OpenAI,取消下面的注释并替换API KEY
/*
llmClient = api.NewOpenAIClient("your-openai-api-key", "gpt-3.5-turbo")
*/
// 创建企业行业分类器
classifier := api.NewCompanyIndustryClassifier(llmClient)
// 示例公司名称 - 可以从命令行参数获取
companyName := "深圳市致远速联信息技术有限公司"
fmt.Printf("正在分析公司: %s\n", companyName)
// 分析企业行业
result, err := classifier.ClassifyCompany(companyName)
if err != nil {
log.Printf("分析失败: %v", err)
return
}
// 输出结果
fmt.Printf("\n✅ 企业行业分析完成!\n")
fmt.Printf("公司名称: %s\n", result.CompanyName)
fmt.Printf("主要行业: %s - %s\n", result.Primary.Code, result.Primary.Name)
fmt.Printf("判断依据: %s\n", result.Primary.Reason)
if len(result.Secondary) > 0 {
fmt.Printf("次要行业:\n")
for _, sec := range result.Secondary {
fmt.Printf(" %s - %s\n", sec.Code, sec.Name)
fmt.Printf(" 判断依据: %s\n", sec.Reason)
}
} else {
fmt.Printf("次要行业: 无\n")
}
}

334
cmd/wasm/main.go Normal file
View File

@@ -0,0 +1,334 @@
package main
import (
"encoding/json"
"fmt"
"syscall/js"
"wasm-go/internal/api"
"wasm-go/internal/excel"
)
func main() {
// 注册各种功能函数
js.Global().Set("analyzeCompanyIndustryAsync", js.FuncOf(analyzeCompanyIndustryAsync))
js.Global().Set("parseExcelAsync", js.FuncOf(parseExcelAsync))
js.Global().Set("callLLMAsync", js.FuncOf(callLLMAsync))
js.Global().Set("callTaxHealthAsync", js.FuncOf(callTaxHealthAsync))
// 保持程序运行
select {} // 无限循环,保持程序运行
}
// analyzeCompanyIndustryAsync 异步版本的函数,返回Promise
func analyzeCompanyIndustryAsync(this js.Value, args []js.Value) interface{} {
// 获取公司名称参数
companyName := args[0].String()
// 创建一个通道来处理结果
resultChan := make(chan interface{}, 1)
errorChan := make(chan error, 1) // 使用error类型而不是interface{}
// 在goroutine中执行异步操作
go func() {
// 创建LLM客户端
llmClient := api.NewOllamaClient("http://localhost:11434", "qwen3.5:9b")
// 创建企业行业分类器
classifier := api.NewCompanyIndustryClassifier(llmClient)
// 分析企业行业
result, err := classifier.ClassifyCompany(companyName)
if err != nil {
// 发送错误到错误通道
errorChan <- err
} else {
// 发送结果到结果通道
resultChan <- result
}
}()
// 创建Promise
promise := js.Global().Get("Promise").New(js.FuncOf(func(this js.Value, promiseArgs []js.Value) interface{} {
if len(promiseArgs) < 2 {
// 如果参数不足,返回一个立即拒绝的Promise
return nil
}
resolve := promiseArgs[0]
reject := promiseArgs[1]
// 启动一个goroutine来处理结果
go func() {
select {
case result := <-resultChan:
// 将复杂结构体序列化为JSON
jsonResult, err := json.Marshal(result)
if err != nil {
// 序列化失败,返回错误
reject.Invoke(js.ValueOf(map[string]string{
"error": "JSON序列化失败: " + err.Error(),
}))
return
}
// 将JSON字符串解析为JavaScript对象
jsResult := js.Global().Get("JSON").Call("parse", string(jsonResult))
resolve.Invoke(jsResult)
case err := <-errorChan:
// 将错误转换为JavaScript值
reject.Invoke(js.ValueOf(map[string]string{
"error": err.Error(),
}))
}
}()
return nil
}))
return promise
}
// parseExcelAsync 解析Excel文件的异步函数
func parseExcelAsync(this js.Value, args []js.Value) interface{} {
// 获取Excel文件的ArrayBuffer
arrayBuffer := args[0]
uint8Array := js.Global().Get("Uint8Array").New(arrayBuffer)
// 创建字节切片
length := uint8Array.Get("length").Int()
excelBytes := make([]byte, length)
// 将数据复制到Go字节切片
for i := 0; i < length; i++ {
excelBytes[i] = byte(uint8Array.Index(i).Int())
}
// 创建一个通道来处理结果
resultChan := make(chan interface{}, 1)
errorChan := make(chan error, 1)
// 在goroutine中执行异步操作
go func() {
// 创建Excel读取器
reader, err := excel.NewXLSXReaderFromBytes(excelBytes)
if err != nil {
errorChan <- err
return
}
// 读取所有工作表
sheetNames, err := reader.GetSheetNames()
if err != nil {
errorChan <- err
return
}
// 创建一个map来存储所有工作表的数据
sheets := make(map[string][][]string)
// 读取每个工作表的数据
for _, sheetName := range sheetNames {
sheetData, err := reader.GetSheetData(sheetName)
if err != nil {
// 记录错误但继续处理其他工作表
continue
}
sheets[sheetName] = sheetData
}
// 将结果转换为适当的格式
result := map[string]interface{}{
"sheets": sheets,
}
resultChan <- result
}()
// 创建Promise
promise := js.Global().Get("Promise").New(js.FuncOf(func(this js.Value, promiseArgs []js.Value) interface{} {
if len(promiseArgs) < 2 {
// 如果参数不足,返回一个立即拒绝的Promise
return nil
}
resolve := promiseArgs[0]
reject := promiseArgs[1]
// 启动一个goroutine来处理结果
go func() {
select {
case result := <-resultChan:
// 将复杂结构体序列化为JSON
jsonResult, err := json.Marshal(result)
if err != nil {
// 序列化失败,返回错误
reject.Invoke(js.ValueOf(map[string]string{
"error": "JSON序列化失败: " + err.Error(),
}))
return
}
// 将JSON字符串解析为JavaScript对象
jsResult := js.Global().Get("JSON").Call("parse", string(jsonResult))
resolve.Invoke(jsResult)
case err := <-errorChan:
// 将错误转换为JavaScript值
reject.Invoke(js.ValueOf(map[string]string{
"error": err.Error(),
}))
}
}()
return nil
}))
return promise
}
// callLLMAsync 调用大模型的异步函数
func callLLMAsync(this js.Value, args []js.Value) interface{} {
// 获取提示词参数
prompt := args[0].String()
// 创建一个通道来处理结果
resultChan := make(chan interface{}, 1)
errorChan := make(chan error, 1)
// 在goroutine中执行异步操作
go func() {
// 创建LLM客户端(可以根据需要选择不同的模型)
llmClient := api.NewOllamaClient("http://localhost:11434", "qwen3.5:9b")
// 调用大模型
response, err := llmClient.SimpleChat(prompt)
if err != nil {
errorChan <- err
return
}
result := map[string]string{
"response": response,
}
resultChan <- result
}()
// 创建Promise
promise := js.Global().Get("Promise").New(js.FuncOf(func(this js.Value, promiseArgs []js.Value) interface{} {
if len(promiseArgs) < 2 {
// 如果参数不足,返回一个立即拒绝的Promise
return nil
}
resolve := promiseArgs[0]
reject := promiseArgs[1]
// 启动一个goroutine来处理结果
go func() {
select {
case result := <-resultChan:
// 将复杂结构体序列化为JSON
jsonResult, err := json.Marshal(result)
if err != nil {
// 序列化失败,返回错误
reject.Invoke(js.ValueOf(map[string]string{
"error": "JSON序列化失败: " + err.Error(),
}))
return
}
// 将JSON字符串解析为JavaScript对象
jsResult := js.Global().Get("JSON").Call("parse", string(jsonResult))
resolve.Invoke(jsResult)
case err := <-errorChan:
// 将错误转换为JavaScript值
reject.Invoke(js.ValueOf(map[string]string{
"error": err.Error(),
}))
}
}()
return nil
}))
return promise
}
// callTaxHealthAsync 调用税务健康API的异步函数
// 注意:由于WASM环境的CORS限制,我们在这里提供模拟实现
func callTaxHealthAsync(this js.Value, args []js.Value) interface{} {
// 获取公司信息参数
companyInfoJSON := args[0].String()
// 创建一个通道来处理结果
resultChan := make(chan interface{}, 1)
errorChan := make(chan error, 1)
// 在goroutine中执行异步操作
go func() {
// 由于WASM环境无法直接调用外部API(CORS限制)
// 我们返回一个模拟的响应
simulatedResponse := map[string]interface{}{
"code": 200,
"msg": "success",
"data": map[string]interface{}{
"req_no": "REQ" + fmt.Sprint(js.Global().Get("Date").New().Call("getTime").Int()),
"order_no": "ORDER" + fmt.Sprint(js.Global().Get("Date").New().Call("getTime").Int()),
},
"message": "模拟的税务健康评估结果",
}
// 解析输入的JSON以获取一些信息用于模拟
var companyInfo map[string]interface{}
err := json.Unmarshal([]byte(companyInfoJSON), &companyInfo)
if err != nil {
errorChan <- err
return
}
// 根据输入数据添加一些模拟的评估结果
simulatedResponse["company_data"] = companyInfo
resultChan <- simulatedResponse
}()
// 创建Promise
promise := js.Global().Get("Promise").New(js.FuncOf(func(this js.Value, promiseArgs []js.Value) interface{} {
if len(promiseArgs) < 2 {
// 如果参数不足,返回一个立即拒绝的Promise
return nil
}
resolve := promiseArgs[0]
reject := promiseArgs[1]
// 启动一个goroutine来处理结果
go func() {
select {
case result := <-resultChan:
// 将复杂结构体序列化为JSON
jsonResult, err := json.Marshal(result)
if err != nil {
// 序列化失败,返回错误
reject.Invoke(js.ValueOf(map[string]string{
"error": "JSON序列化失败: " + err.Error(),
}))
return
}
// 将JSON字符串解析为JavaScript对象
jsResult := js.Global().Get("JSON").Call("parse", string(jsonResult))
resolve.Invoke(jsResult)
case err := <-errorChan:
// 将错误转换为JavaScript值
reject.Invoke(js.ValueOf(map[string]string{
"error": err.Error(),
}))
}
}()
return nil
}))
return promise
}