fix_upload

This commit is contained in:
hangyu.tao 2026-01-07 17:34:34 +08:00
parent 2b50b3a2f7
commit a778b0b62c
5 changed files with 171 additions and 171 deletions

View File

@ -1,22 +1,22 @@
[
{
"id": "24849eb6e78a42998d0b7b08b5979756",
"createdAt": "2026-01-04T12:14:26.825475+08:00",
"customerName": "2025-12-22",
"intendedProduct": "予芯",
"id": "b933a0edb8cb4d1a85fdd6ca50fc4376",
"createdAt": "2026-01-07T16:40:38.094131+08:00",
"customerName": "予芯",
"intendedProduct": "2025/12/22",
"version": "1.9.4",
"description": "训练失败看不到错误日志",
"solution": "已解决",
"type": "功能问题",
"module": "模型工坊",
"statusProgress": "",
"statusProgress": "已修复",
"reporter": ""
},
{
"id": "a10e4beaa8134473bfd80fba4b33aee7",
"createdAt": "2026-01-04T12:14:26.826755+08:00",
"customerName": "2025/12/23",
"intendedProduct": "诺因智能",
"id": "179dd08fe93d4fbf9c078c362486aa65",
"createdAt": "2026-01-07T16:40:38.095578+08:00",
"customerName": "诺因智能",
"intendedProduct": "2025/12/23",
"version": "1.9.4",
"description": "客户端上传大文件失败",
"solution": "技术优化,修复中",
@ -26,10 +26,10 @@
"reporter": ""
},
{
"id": "20188f28fe1e40e589e171a542cf3a26",
"createdAt": "2026-01-04T12:14:26.827195+08:00",
"customerName": "2025/12/23",
"intendedProduct": "良业集团",
"id": "2700f88f74c94903a48369c86e05221a",
"createdAt": "2026-01-07T16:40:38.096324+08:00",
"customerName": "良业",
"intendedProduct": "2025/12/23",
"version": "1.9.4",
"description": "数据集发布感觉操作繁琐,如果在模型工坊里直接选择需要训练的图片会更方便",
"solution": "",
@ -39,10 +39,10 @@
"reporter": ""
},
{
"id": "b1f033d8f2b54b5cae52764f9e4eb727",
"createdAt": "2026-01-04T12:14:26.827545+08:00",
"customerName": "2025/12/25",
"intendedProduct": "斯蒂尔",
"id": "7ae437bfeaa84e579b012a4ec5fae794",
"createdAt": "2026-01-07T16:40:38.097176+08:00",
"customerName": "斯蒂尔",
"intendedProduct": "2025/12/25",
"version": "1.9.4",
"description": "1.试用账号图片生成速度比较慢(2分钟1张图)\n2.生图效果不理想",
"solution": "试用资源有限",
@ -52,10 +52,10 @@
"reporter": ""
},
{
"id": "ce7df9ffeab34998a040ee0af5b21789",
"createdAt": "2026-01-04T12:14:26.827889+08:00",
"customerName": "2025/12/26",
"intendedProduct": "求之",
"id": "e45d0a13be724c129a3c4e446517a940",
"createdAt": "2026-01-07T16:40:38.098053+08:00",
"customerName": "求之",
"intendedProduct": "2025/12/26",
"version": "1.9.4",
"description": "1.训练过程不太透明,以及无法提前终止模型训练\n2.训练过程无法断点继续训练;\n3.试用账号资源有限,训练时长过长",
"solution": "1、训练过程的透明化日志化和目前的易用化有一些产品设计矛盾之前主要还是在做易用化。\n接下来我们会在更透明更强的客户对过程的操控粒度上提升。\n2、训练时长的事情实在抱歉最近客户比较多我们资源有限",
@ -65,10 +65,10 @@
"reporter": ""
},
{
"id": "be855bfa554e4e71abcee1cb5c614c3d",
"createdAt": "2026-01-04T12:14:26.828428+08:00",
"customerName": "2025/12/26",
"intendedProduct": "诺因智能",
"id": "157ee3180f9c47efb38ab52e3a3b2b4f",
"createdAt": "2026-01-07T16:40:38.099239+08:00",
"customerName": "诺因智能",
"intendedProduct": "2025/12/26",
"version": "1.9.4",
"description": "训练进程显示什么时候上线",
"solution": "训练日志可视化,我们大约需要一个月的时间可以满足上线使用",
@ -78,10 +78,10 @@
"reporter": ""
},
{
"id": "93924ee960434cf1b3a358321efcf5a5",
"createdAt": "2026-01-04T12:14:26.828857+08:00",
"customerName": "2025/12/26",
"intendedProduct": "诺因智能",
"id": "b7371743ec5549af8ce643e136334ccf",
"createdAt": "2026-01-07T16:40:38.100073+08:00",
"customerName": "诺因智能",
"intendedProduct": "2025/12/26",
"version": "1.9.4",
"description": "(igev模型)全量训练是指从0开始 增量是在现在的ckpt上做微调是吗 ",
"solution": "全量训练:在加载开源基础的权重上进行增量训练\n增量训练在您的数据上训练的checkpoint上进行的增量训练",
@ -91,10 +91,10 @@
"reporter": ""
},
{
"id": "6d748e5ec8814e938509c0784902e5bc",
"createdAt": "2026-01-04T12:14:26.829476+08:00",
"customerName": "2025/12/26",
"intendedProduct": "斯蒂尔",
"id": "c87211e0dc6b42f1bb2127259cf64e81",
"createdAt": "2026-01-07T16:40:38.101288+08:00",
"customerName": "斯蒂尔",
"intendedProduct": "2025/12/26",
"version": "1.9.4",
"description": "数据生成效果改进优化方式?试用账号配置卡数",
"solution": "409024g然后生图是单卡单线程\n目前系统上挂载了2张GPU",
@ -104,10 +104,10 @@
"reporter": ""
},
{
"id": "74e79089d5e7477f8388ad62515704f2",
"createdAt": "2026-01-04T12:14:26.830017+08:00",
"customerName": "2025/12/26",
"intendedProduct": "良业",
"id": "d14245e86d3d4ee399996ba6d26b23de",
"createdAt": "2026-01-07T16:40:38.101877+08:00",
"customerName": "良业",
"intendedProduct": "2025/12/26",
"version": "1.9.4",
"description": "1、训练时需要添加测试图无法理解模型分析结果可以用训练集\n2、训练失败时需提示原因方便下一步操作。",
"solution": "1、训练过程中根据训练集的数据来更新模型参数在通过验证集来验证\n2、训练失败有日志可以查看您滚动下屏幕至右侧可以看到哈",
@ -117,10 +117,10 @@
"reporter": ""
},
{
"id": "f4a97d749e9843419dc74c9990e0035f",
"createdAt": "2026-01-04T12:14:26.830529+08:00",
"customerName": "2025/12/29",
"intendedProduct": "良业",
"id": "e6fe3693b67d406e9edd056fc48c7119",
"createdAt": "2026-01-07T16:40:38.102556+08:00",
"customerName": "良业",
"intendedProduct": "2025/12/29",
"version": "1.9.4",
"description": "1.试了几个模型,没训练出来,感觉不出效果\n",
"solution": "补充数据集",
@ -130,10 +130,10 @@
"reporter": ""
},
{
"id": "d8aa7b048820406d932b410ecc3fd83f",
"createdAt": "2026-01-04T12:14:26.830915+08:00",
"customerName": "2025/12/29",
"intendedProduct": "诺因智能",
"id": "ec24619e11014cb4a3e2f6d9e4b2d618",
"createdAt": "2026-01-07T16:40:38.103257+08:00",
"customerName": "诺因智能",
"intendedProduct": "2025/12/29",
"version": "1.9.4",
"description": "1.总轮数与拓展参数(num_steps)不一致的时候,以哪个为准结束训练",
"solution": "目前StereoNet-V2.5支持使用steps参数其他的还是以epoch为准",
@ -143,10 +143,10 @@
"reporter": ""
},
{
"id": "fa1a6afdc1cf47728eb3940be1a554d9",
"createdAt": "2026-01-04T12:14:26.831304+08:00",
"customerName": "2025/12/30",
"intendedProduct": "良业",
"id": "a9fc0da9bee44acba2d43d15e06079ed",
"createdAt": "2026-01-07T16:40:38.10396+08:00",
"customerName": "良业",
"intendedProduct": "2025/12/30",
"version": "1.9.4",
"description": "户外同一个草坪测试用yolo检测640*480图片检测时间\u003c100ms分割精度\u003c10像素出错率1/10万使用x3可以吗",
"solution": "100ms可以",
@ -156,10 +156,10 @@
"reporter": ""
},
{
"id": "515c58d6e5184393a60462701ee97768",
"createdAt": "2026-01-04T12:14:26.831727+08:00",
"customerName": "2025/12/30",
"intendedProduct": "良业",
"id": "0bd2e8d859d7466293182181490d56b4",
"createdAt": "2026-01-07T16:40:38.104695+08:00",
"customerName": "良业",
"intendedProduct": "2025/12/30",
"version": "1.9.4",
"description": "训练任务无效果",
"solution": "数据集少了,补数后需要保存快照",
@ -169,10 +169,10 @@
"reporter": ""
},
{
"id": "751cbd5497a849888641270f88012b0d",
"createdAt": "2026-01-04T12:14:26.832123+08:00",
"customerName": "2025/12/30",
"intendedProduct": "诺因智能",
"id": "3576f30f7b7d489e91f2c638b9aae7af",
"createdAt": "2026-01-07T16:40:38.105387+08:00",
"customerName": "诺因智能",
"intendedProduct": "2025/12/30",
"version": "1.9.4",
"description": "现在平台上的V2.5就可以需要完成的ckpt这个预计什么时候可以更新啊",
"solution": "",
@ -182,25 +182,25 @@
"reporter": ""
},
{
"id": "c754c98a22684a9c8cec940869ca7511",
"createdAt": "2026-01-04T12:14:26.832483+08:00",
"customerName": "2025-12-31",
"intendedProduct": "诺因智能",
"id": "1cdb5f53ee734caf859fc0597a60f13a",
"createdAt": "2026-01-07T16:40:38.106029+08:00",
"customerName": "诺因智能",
"intendedProduct": "2025/12/31",
"version": "1.9.4",
"description": "境外网站的加速,下载部分数据集时速度会很慢",
"solution": "",
"type": "反馈",
"module": "",
"module": "数据集",
"statusProgress": "",
"reporter": ""
},
{
"id": "876f234b07da45138ec2d85ea4294c9b",
"createdAt": "2026-01-04T12:14:26.83287+08:00",
"customerName": "2025/12/31",
"intendedProduct": "诺因智能",
"version": "1.9.6",
"description": "模型ckpt的匹配这个可以公开镜像也可以是根据账户权限设置的私有镜像都OK但是得有",
"id": "e01001a4667d45e0872d506df39be397",
"createdAt": "2026-01-07T16:40:38.106833+08:00",
"customerName": "诺因智能",
"intendedProduct": "2025/12/31",
"version": "1.9.4",
"description": "诺因智能需要完整的模型 checkpoint而不仅仅是 backbone 部分",
"solution": "",
"type": "需求",
"module": "模型工坊",
@ -208,11 +208,11 @@
"reporter": ""
},
{
"id": "7db1edba452d48f8b5b2e56854bbd92c",
"createdAt": "2026-01-04T12:14:26.833264+08:00",
"customerName": "2025/12/31",
"intendedProduct": "诺因智能",
"version": "1.9.7",
"id": "e52a8badaab840a2a6dd27a614d1e929",
"createdAt": "2026-01-07T16:40:38.107656+08:00",
"customerName": "诺因智能",
"intendedProduct": "2025/12/31",
"version": "1.9.4",
"description": "模型推理的指标对比按钮无法选择",
"solution": "",
"type": "需求",
@ -221,11 +221,11 @@
"reporter": ""
},
{
"id": "beca6e1a2791409fb750c4a1ca1c16e8",
"createdAt": "2026-01-04T12:14:26.834896+08:00",
"customerName": "2025/12/31",
"intendedProduct": "诺因智能",
"version": "1.9.8",
"id": "bbf8dd91213b4d96b96bbeb43ed9930e",
"createdAt": "2026-01-07T16:40:38.108654+08:00",
"customerName": "诺因智能",
"intendedProduct": "2025/12/31",
"version": "1.9.4",
"description": "推理评测可以增加定量参数推理的时候能否增加一下epe衡量模型精度的指标",
"solution": "",
"type": "需求",

View File

@ -266,12 +266,12 @@
<form id="createCustomerForm">
<div class="form-row">
<div class="form-group">
<label for="customerName">日期</label>
<input type="date" id="customerName" name="customerName" required>
<label for="createIntendedProduct">日期</label>
<input type="date" id="createIntendedProduct" name="intendedProduct">
</div>
<div class="form-group">
<label for="intendedProduct">客户名称</label>
<input type="text" id="intendedProduct" name="intendedProduct" required>
<label for="createCustomerName">客户名称</label>
<input type="text" id="createCustomerName" name="customerName" required>
</div>
</div>
<div class="form-row">
@ -371,12 +371,12 @@
<input type="hidden" id="editCustomerId">
<div class="form-row">
<div class="form-group">
<label for="editCustomerName">日期</label>
<input type="date" id="editCustomerName" name="customerName">
<label for="editIntendedProduct">日期</label>
<input type="date" id="editIntendedProduct" name="intendedProduct">
</div>
<div class="form-group">
<label for="editIntendedProduct">客户名称</label>
<input type="text" id="editIntendedProduct" name="intendedProduct">
<label for="editCustomerName">客户名称</label>
<input type="text" id="editCustomerName" name="customerName">
</div>
</div>
<div class="form-row">

View File

@ -351,7 +351,7 @@ document.addEventListener('DOMContentLoaded', function () {
// Populate customer filter dropdown
function populateCustomerFilter() {
console.log('Populating filter, allCustomers:', allCustomers);
const uniqueCustomers = [...new Set(allCustomers.map(c => c.intendedProduct).filter(c => c))];
const uniqueCustomers = [...new Set(allCustomers.map(c => c.customerName).filter(c => c))];
console.log('Unique customers:', uniqueCustomers);
customerFilter.innerHTML = '<option value="">全部客户</option>';
@ -405,7 +405,7 @@ document.addEventListener('DOMContentLoaded', function () {
let next = [...allCustomers];
if (selectedCustomerFilter) {
next = next.filter(c => c.intendedProduct === selectedCustomerFilter);
next = next.filter(c => c.customerName === selectedCustomerFilter);
}
if (customerSearchQuery) {
@ -450,9 +450,9 @@ document.addEventListener('DOMContentLoaded', function () {
].map(toCsvCell).join(',');
const lines = customers.map(c => {
const date = normalizeDateValue(c.customerName) || (c.customerName || '');
const date = normalizeDateValue(c.intendedProduct) || (c.intendedProduct || '');
const cells = [
c.intendedProduct || '',
c.customerName || '',
c.version || '',
c.description || '',
c.solution || '',
@ -560,10 +560,10 @@ document.addEventListener('DOMContentLoaded', function () {
customers.forEach(customer => {
const row = document.createElement('tr');
const date = customer.customerName || '';
const date = customer.intendedProduct || '';
const fields = [
{ value: customer.intendedProduct || '', name: 'intendedProduct' },
{ value: customer.customerName || '', name: 'customerName' },
{ value: customer.version || '', name: 'version' },
{ value: customer.description || '', name: 'description' },
{ value: customer.solution || '', name: 'solution' },
@ -677,8 +677,8 @@ document.addEventListener('DOMContentLoaded', function () {
e.preventDefault();
const formData = {
customerName: document.getElementById('customerName').value,
intendedProduct: document.getElementById('intendedProduct').value,
customerName: document.getElementById('createCustomerName').value,
intendedProduct: document.getElementById('createIntendedProduct').value,
version: document.getElementById('createVersion').value,
description: document.getElementById('createDescription').value,
solution: document.getElementById('createSolution').value,
@ -755,8 +755,8 @@ document.addEventListener('DOMContentLoaded', function () {
const customer = await response.json();
document.getElementById('editCustomerId').value = customer.id;
document.getElementById('editCustomerName').value = normalizeDateValue(customer.customerName);
document.getElementById('editIntendedProduct').value = customer.intendedProduct || '';
document.getElementById('editCustomerName').value = customer.customerName || '';
document.getElementById('editIntendedProduct').value = normalizeDateValue(customer.intendedProduct);
document.getElementById('editVersion').value = customer.version || '';
document.getElementById('editDescription').value = customer.description || '';
document.getElementById('editSolution').value = customer.solution || '';

View File

@ -8,10 +8,10 @@ import (
"strconv"
"strings"
"time"
"crm-go/internal/storage"
"crm-go/models"
"github.com/xuri/excelize/v2"
)
@ -28,31 +28,31 @@ func NewCustomerHandler(storage storage.CustomerStorage) *CustomerHandler {
func (h *CustomerHandler) GetCustomers(w http.ResponseWriter, r *http.Request) {
page := 1
pageSize := 10
if pageStr := r.URL.Query().Get("page"); pageStr != "" {
if p, err := strconv.Atoi(pageStr); err == nil && p > 0 {
page = p
}
}
if pageSizeStr := r.URL.Query().Get("pageSize"); pageSizeStr != "" {
if ps, err := strconv.Atoi(pageSizeStr); err == nil && ps > 0 {
pageSize = ps
}
}
customers, err := h.storage.GetAllCustomers()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
total := len(customers)
totalPages := (total + pageSize - 1) / pageSize
start := (page - 1) * pageSize
end := start + pageSize
if start >= total {
customers = []models.Customer{}
} else if end > total {
@ -60,7 +60,7 @@ func (h *CustomerHandler) GetCustomers(w http.ResponseWriter, r *http.Request) {
} else {
customers = customers[start:end]
}
response := map[string]interface{}{
"customers": customers,
"total": total,
@ -68,7 +68,7 @@ func (h *CustomerHandler) GetCustomers(w http.ResponseWriter, r *http.Request) {
"pageSize": pageSize,
"totalPages": totalPages,
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}
@ -78,30 +78,30 @@ func (h *CustomerHandler) GetCustomerByID(w http.ResponseWriter, r *http.Request
urlPath := r.URL.Path
// The path should be /api/customers/{id}
pathParts := strings.Split(urlPath, "/")
if len(pathParts) < 4 {
http.Error(w, "Invalid URL format", http.StatusBadRequest)
return
}
id := pathParts[3] // Get the ID from the path
// Remove query parameters if any
if idx := strings.Index(id, "?"); idx != -1 {
id = id[:idx]
}
customer, err := h.storage.GetCustomerByID(id)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if customer == nil {
http.Error(w, "Customer not found", http.StatusNotFound)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(customer)
}
@ -112,7 +112,7 @@ func (h *CustomerHandler) CreateCustomer(w http.ResponseWriter, r *http.Request)
http.Error(w, "Invalid request body", http.StatusBadRequest)
return
}
customer := models.Customer{
CustomerName: req.CustomerName,
IntendedProduct: req.IntendedProduct,
@ -125,12 +125,12 @@ func (h *CustomerHandler) CreateCustomer(w http.ResponseWriter, r *http.Request)
Reporter: req.Reporter,
CreatedAt: time.Now(),
}
if err := h.storage.CreateCustomer(customer); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(customer)
@ -141,30 +141,30 @@ func (h *CustomerHandler) UpdateCustomer(w http.ResponseWriter, r *http.Request)
urlPath := r.URL.Path
// The path should be /api/customers/{id}
pathParts := strings.Split(urlPath, "/")
if len(pathParts) < 4 {
http.Error(w, "Invalid URL format", http.StatusBadRequest)
return
}
id := pathParts[3] // Get the ID from the path
// Remove query parameters if any
if idx := strings.Index(id, "?"); idx != -1 {
id = id[:idx]
}
var req models.UpdateCustomerRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "Invalid request body", http.StatusBadRequest)
return
}
if err := h.storage.UpdateCustomer(id, req); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
}
@ -173,24 +173,24 @@ func (h *CustomerHandler) DeleteCustomer(w http.ResponseWriter, r *http.Request)
urlPath := r.URL.Path
// The path should be /api/customers/{id}
pathParts := strings.Split(urlPath, "/")
if len(pathParts) < 4 {
http.Error(w, "Invalid URL format", http.StatusBadRequest)
return
}
id := pathParts[3] // Get the ID from the path
// Remove query parameters if any
if idx := strings.Index(id, "?"); idx != -1 {
id = id[:idx]
}
if err := h.storage.DeleteCustomer(id); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
}
@ -200,21 +200,21 @@ func (h *CustomerHandler) ImportCustomers(w http.ResponseWriter, r *http.Request
http.Error(w, "Unable to parse form", http.StatusBadRequest)
return
}
file, handler, err := r.FormFile("file")
if err != nil {
http.Error(w, "Unable to get file", http.StatusBadRequest)
return
}
defer file.Close()
// Check file extension
filename := handler.Filename
isExcel := strings.HasSuffix(strings.ToLower(filename), ".xlsx")
importedCount := 0
duplicateCount := 0
if isExcel {
// Parse as Excel file
f, err := excelize.OpenReader(file)
@ -223,62 +223,62 @@ func (h *CustomerHandler) ImportCustomers(w http.ResponseWriter, r *http.Request
return
}
defer f.Close()
// Get the first sheet
sheets := f.GetSheetList()
if len(sheets) == 0 {
http.Error(w, "No sheets found in Excel file", http.StatusBadRequest)
return
}
// Read all rows from the first sheet
rows, err := f.GetRows(sheets[0])
if err != nil {
http.Error(w, "Unable to read Excel file", http.StatusBadRequest)
return
}
// Skip header row if exists
for i, row := range rows {
if i == 0 {
continue // Skip header row
}
if len(row) < 2 {
continue // Skip rows with insufficient data
}
customer := models.Customer{
ID: "", // Will be generated by the storage
CreatedAt: time.Now(),
CustomerName: getValue(row, 0),
IntendedProduct: getValue(row, 1),
Version: getValue(row, 2),
Description: getValue(row, 3),
Solution: getValue(row, 4),
Type: getValue(row, 5),
Module: getValue(row, 6),
StatusProgress: getValue(row, 7),
Reporter: getValue(row, 8),
Version: getValue(row, 1),
Description: getValue(row, 2),
Solution: getValue(row, 3),
Type: getValue(row, 4),
Module: getValue(row, 5),
StatusProgress: getValue(row, 6),
Reporter: getValue(row, 7),
IntendedProduct: getValue(row, 8),
}
// Check for duplicate
exists, err := h.storage.CustomerExists(customer)
if err != nil {
http.Error(w, "Error checking for duplicate customer", http.StatusInternalServerError)
return
}
if exists {
duplicateCount++
continue // Skip duplicate
}
if err := h.storage.CreateCustomer(customer); err != nil {
http.Error(w, "Error saving customer", http.StatusInternalServerError)
return
}
importedCount++
}
} else {
@ -288,64 +288,64 @@ func (h *CustomerHandler) ImportCustomers(w http.ResponseWriter, r *http.Request
http.Error(w, "Unable to read file", http.StatusBadRequest)
return
}
reader := csv.NewReader(strings.NewReader(string(content)))
records, err := reader.ReadAll()
if err != nil {
http.Error(w, "Unable to parse CSV file", http.StatusBadRequest)
return
}
// Skip header row if exists
for i, row := range records {
if i == 0 {
continue // Skip header row
}
if len(row) < 2 {
continue // Skip rows with insufficient data
}
customer := models.Customer{
ID: "", // Will be generated by the storage
CreatedAt: time.Now(),
CustomerName: getValue(row, 0),
IntendedProduct: getValue(row, 1),
Version: getValue(row, 2),
Description: getValue(row, 3),
Solution: getValue(row, 4),
Type: getValue(row, 5),
Module: getValue(row, 6),
StatusProgress: getValue(row, 7),
Reporter: getValue(row, 8),
Version: getValue(row, 1),
Description: getValue(row, 2),
Solution: getValue(row, 3),
Type: getValue(row, 4),
Module: getValue(row, 5),
StatusProgress: getValue(row, 6),
Reporter: getValue(row, 7),
IntendedProduct: getValue(row, 8),
}
// Check for duplicate
exists, err := h.storage.CustomerExists(customer)
if err != nil {
http.Error(w, "Error checking for duplicate customer", http.StatusInternalServerError)
return
}
if exists {
duplicateCount++
continue // Skip duplicate
}
if err := h.storage.CreateCustomer(customer); err != nil {
http.Error(w, "Error saving customer", http.StatusInternalServerError)
return
}
importedCount++
}
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]interface{}{
"message": "Customers imported successfully",
"importedCount": importedCount,
"message": "Customers imported successfully",
"importedCount": importedCount,
"duplicateCount": duplicateCount,
})
}

View File

@ -1,5 +1,5 @@
Customer Name,Intended Product,Version,Description,Solution,Type,Module,Status & Progress,Reporter
ABC Company,Product A,v1.0,Enterprise client,Cloud solution,Enterprise,CRM,In Progress,John Doe
XYZ Corp,Product B,v2.1,New prospect,On-premise,Prospect,Sales,Lead,Jane Smith
Tech Solutions,Product C,v1.5,Existing customer,Hybrid,Existing,Support,Closed Won,Mike Johnson
Global Inc,Product A,v2.0,International client,Cloud solution,Enterprise,CRM,Negotiation,Sarah Wilson
客户,版本,描述,解决方案,类型,模块,状态与进度,报告人,时间
予芯,1.9.4,训练失败看不到错误日志,已解决,功能问题,模型工坊,,管理员,2025-12-22
诺因智能,1.9.4,客户端上传大文件失败,技术优化,功能问题,数据空间,已修复,管理员,2025-12-23
良业集团,1.9.4,"数据集发布感觉操作繁琐,如果在模型工坊里直接选择需要训练的图片会更方便",,反馈,"数据集,模型工坊",,管理员,2025-12-23
斯蒂尔,1.9.4,"1.试用账号图片生成速度比较慢(2分钟1张图); 2.生图效果不理想",试用资源有限,反馈,数据生成,,管理员,2025-12-25
1 Customer Name 客户 Intended Product 版本 Version 描述 Description 解决方案 Solution 类型 Type 模块 Module 状态与进度 Status & Progress 报告人 Reporter 时间
2 ABC Company 予芯 Product A 1.9.4 v1.0 训练失败看不到错误日志 Enterprise client 已解决 Cloud solution 功能问题 Enterprise 模型工坊 CRM In Progress 管理员 John Doe 2025-12-22
3 XYZ Corp 诺因智能 Product B 1.9.4 v2.1 客户端上传大文件失败 New prospect 技术优化 On-premise 功能问题 Prospect 数据空间 Sales 已修复 Lead 管理员 Jane Smith 2025-12-23
4 Tech Solutions 良业集团 Product C 1.9.4 v1.5 数据集发布感觉操作繁琐,如果在模型工坊里直接选择需要训练的图片会更方便 Existing customer Hybrid 反馈 Existing 数据集,模型工坊 Support Closed Won 管理员 Mike Johnson 2025-12-23
5 Global Inc 斯蒂尔 Product A 1.9.4 v2.0 1.试用账号图片生成速度比较慢(2分钟1张图); 2.生图效果不理想 International client 试用资源有限 Cloud solution 反馈 Enterprise 数据生成 CRM Negotiation 管理员 Sarah Wilson 2025-12-25