upload_source
This commit is contained in:
parent
17752f0fd7
commit
ceb0f74ccc
@ -285,6 +285,7 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th>客户名称</th>
|
||||
<th>来源</th>
|
||||
<th>状态</th>
|
||||
<th>开始时间</th>
|
||||
<th>结束时间</th>
|
||||
@ -791,6 +792,10 @@
|
||||
<label for="trialCustomerInput">客户名称</label>
|
||||
<input type="text" id="trialCustomerInput" name="customerName" placeholder="请输入客户名称" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="trialCustomerSource">客户来源</label>
|
||||
<input type="text" id="trialCustomerSource" name="source" placeholder="请输入客户来源(如:官网、展会、推荐等)">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>是否试用</label>
|
||||
<div style="display: flex; gap: 20px; align-items: center;">
|
||||
@ -837,6 +842,10 @@
|
||||
<div class="modal-body">
|
||||
<form id="editTrialPeriodForm">
|
||||
<input type="hidden" id="editTrialPeriodId">
|
||||
<div class="form-group">
|
||||
<label for="editTrialCustomerSource">客户来源</label>
|
||||
<input type="text" id="editTrialCustomerSource" name="source" placeholder="请输入客户来源:如社区、销售、推荐等">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>是否试用</label>
|
||||
<div style="display: flex; gap: 20px; align-items: center;">
|
||||
|
||||
@ -274,7 +274,7 @@ function renderTrialPeriodsTable() {
|
||||
if (filteredTrialPeriodsData.length === 0) {
|
||||
const row = document.createElement('tr');
|
||||
row.innerHTML = `
|
||||
<td colspan="6" class="empty-state">
|
||||
<td colspan="7" class="empty-state">
|
||||
<i class="fas fa-users"></i>
|
||||
<h3>👥 还没有客户试用信息</h3>
|
||||
<p>点击上方「添加试用时间」开始管理客户试用</p>
|
||||
@ -324,9 +324,11 @@ function renderTrialPeriodsTable() {
|
||||
const startTime = formatDateTime(period.startTime);
|
||||
const endTime = formatDateTime(period.endTime);
|
||||
const createdAt = formatDateTime(period.createdAt);
|
||||
const source = period.source || '';
|
||||
|
||||
row.innerHTML = `
|
||||
<td><strong>${customerName}</strong></td>
|
||||
<td>${source}</td>
|
||||
<td>${statusBadge}</td>
|
||||
<td>${startTime}</td>
|
||||
<td>${endTime}</td>
|
||||
@ -405,6 +407,9 @@ function openAddTrialModal() {
|
||||
const inputEl = document.getElementById('trialCustomerInput');
|
||||
if (inputEl) inputEl.value = '';
|
||||
|
||||
const sourceEl = document.getElementById('trialCustomerSource');
|
||||
if (sourceEl) sourceEl.value = '';
|
||||
|
||||
document.querySelector('input[name="isTrial"][value="true"]').checked = true;
|
||||
document.getElementById('trialStartTime').value = '';
|
||||
document.getElementById('trialEndTime').value = '';
|
||||
@ -418,6 +423,10 @@ function openEditTrialModal(periodId) {
|
||||
|
||||
document.getElementById('editTrialPeriodId').value = period.id;
|
||||
|
||||
// Set customer source
|
||||
const sourceEl = document.getElementById('editTrialCustomerSource');
|
||||
if (sourceEl) sourceEl.value = period.source || '';
|
||||
|
||||
const startDate = new Date(period.startTime);
|
||||
const endDate = new Date(period.endTime);
|
||||
|
||||
@ -460,9 +469,12 @@ async function deleteTrialPeriodFromPage(periodId) {
|
||||
// Create trial period from page
|
||||
async function createTrialPeriodFromPage() {
|
||||
const inputEl = document.getElementById('trialCustomerInput');
|
||||
const sourceEl = document.getElementById('trialCustomerSource');
|
||||
|
||||
// Get customer name from input
|
||||
const customerName = inputEl ? inputEl.value.trim() : '';
|
||||
// Get customer source from input
|
||||
const source = sourceEl ? sourceEl.value.trim() : '';
|
||||
|
||||
const isTrialValue = document.querySelector('input[name="isTrial"]:checked').value;
|
||||
const isTrial = isTrialValue === 'true';
|
||||
@ -482,6 +494,7 @@ async function createTrialPeriodFromPage() {
|
||||
// 直接使用 customerName,不再需要查找或创建 customerId
|
||||
const formData = {
|
||||
customerName: customerName,
|
||||
source: source,
|
||||
startTime: new Date(startTime).toISOString(),
|
||||
endTime: new Date(endTime).toISOString(),
|
||||
isTrial: isTrial
|
||||
|
||||
@ -232,6 +232,10 @@ async function updateTrialPeriod() {
|
||||
const startTime = document.getElementById('editTrialStartTime').value;
|
||||
const endTime = document.getElementById('editTrialEndTime').value;
|
||||
|
||||
// Get source value
|
||||
const sourceEl = document.getElementById('editTrialCustomerSource');
|
||||
const source = sourceEl ? sourceEl.value.trim() : '';
|
||||
|
||||
// Get isTrial value from radio buttons
|
||||
const isTrialRadio = document.querySelector('input[name="editIsTrial"]:checked');
|
||||
const isTrial = isTrialRadio ? isTrialRadio.value === 'true' : true;
|
||||
@ -242,6 +246,7 @@ async function updateTrialPeriod() {
|
||||
}
|
||||
|
||||
const formData = {
|
||||
source: source,
|
||||
startTime: new Date(startTime).toISOString(),
|
||||
endTime: new Date(endTime).toISOString(),
|
||||
isTrial: isTrial
|
||||
|
||||
@ -84,6 +84,7 @@ func (h *TrialPeriodHandler) CreateTrialPeriod(w http.ResponseWriter, r *http.Re
|
||||
|
||||
trialPeriod := models.TrialPeriod{
|
||||
CustomerName: req.CustomerName,
|
||||
Source: req.Source,
|
||||
StartTime: startTime,
|
||||
EndTime: endTime,
|
||||
IsTrial: req.IsTrial,
|
||||
|
||||
@ -23,7 +23,7 @@ func NewMySQLTrialPeriodStorage() TrialPeriodStorage {
|
||||
|
||||
func (ts *mysqlTrialPeriodStorage) GetAllTrialPeriods() ([]models.TrialPeriod, error) {
|
||||
query := `
|
||||
SELECT id, customer_name, start_time, end_time, is_trial, created_at
|
||||
SELECT id, customer_name, COALESCE(source, '') as source, start_time, end_time, is_trial, created_at
|
||||
FROM trial_periods
|
||||
ORDER BY created_at DESC
|
||||
`
|
||||
@ -40,7 +40,7 @@ func (ts *mysqlTrialPeriodStorage) GetAllTrialPeriods() ([]models.TrialPeriod, e
|
||||
var isTrial int
|
||||
|
||||
err := rows.Scan(
|
||||
&tp.ID, &tp.CustomerName, &tp.StartTime,
|
||||
&tp.ID, &tp.CustomerName, &tp.Source, &tp.StartTime,
|
||||
&tp.EndTime, &isTrial, &tp.CreatedAt,
|
||||
)
|
||||
if err != nil {
|
||||
@ -56,7 +56,7 @@ func (ts *mysqlTrialPeriodStorage) GetAllTrialPeriods() ([]models.TrialPeriod, e
|
||||
|
||||
func (ts *mysqlTrialPeriodStorage) GetTrialPeriodsByCustomerID(customerID string) ([]models.TrialPeriod, error) {
|
||||
query := `
|
||||
SELECT id, customer_name, start_time, end_time, is_trial, created_at
|
||||
SELECT id, customer_name, COALESCE(source, '') as source, start_time, end_time, is_trial, created_at
|
||||
FROM trial_periods
|
||||
WHERE customer_name = ?
|
||||
ORDER BY end_time DESC
|
||||
@ -74,7 +74,7 @@ func (ts *mysqlTrialPeriodStorage) GetTrialPeriodsByCustomerID(customerID string
|
||||
var isTrial int
|
||||
|
||||
err := rows.Scan(
|
||||
&tp.ID, &tp.CustomerName, &tp.StartTime,
|
||||
&tp.ID, &tp.CustomerName, &tp.Source, &tp.StartTime,
|
||||
&tp.EndTime, &isTrial, &tp.CreatedAt,
|
||||
)
|
||||
if err != nil {
|
||||
@ -90,7 +90,7 @@ func (ts *mysqlTrialPeriodStorage) GetTrialPeriodsByCustomerID(customerID string
|
||||
|
||||
func (ts *mysqlTrialPeriodStorage) GetTrialPeriodByID(id string) (*models.TrialPeriod, error) {
|
||||
query := `
|
||||
SELECT id, customer_name, start_time, end_time, is_trial, created_at
|
||||
SELECT id, customer_name, COALESCE(source, '') as source, start_time, end_time, is_trial, created_at
|
||||
FROM trial_periods
|
||||
WHERE id = ?
|
||||
`
|
||||
@ -99,7 +99,7 @@ func (ts *mysqlTrialPeriodStorage) GetTrialPeriodByID(id string) (*models.TrialP
|
||||
var isTrial int
|
||||
|
||||
err := ts.db.QueryRow(query, id).Scan(
|
||||
&tp.ID, &tp.CustomerName, &tp.StartTime,
|
||||
&tp.ID, &tp.CustomerName, &tp.Source, &tp.StartTime,
|
||||
&tp.EndTime, &isTrial, &tp.CreatedAt,
|
||||
)
|
||||
|
||||
@ -123,8 +123,8 @@ func (ts *mysqlTrialPeriodStorage) CreateTrialPeriod(trialPeriod models.TrialPer
|
||||
}
|
||||
|
||||
query := `
|
||||
INSERT INTO trial_periods (id, customer_name, start_time, end_time, is_trial, created_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
INSERT INTO trial_periods (id, customer_name, source, start_time, end_time, is_trial, created_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
`
|
||||
|
||||
isTrial := 0
|
||||
@ -133,7 +133,7 @@ func (ts *mysqlTrialPeriodStorage) CreateTrialPeriod(trialPeriod models.TrialPer
|
||||
}
|
||||
|
||||
_, err := ts.db.Exec(query,
|
||||
trialPeriod.ID, trialPeriod.CustomerName, trialPeriod.StartTime,
|
||||
trialPeriod.ID, trialPeriod.CustomerName, trialPeriod.Source, trialPeriod.StartTime,
|
||||
trialPeriod.EndTime, isTrial, trialPeriod.CreatedAt,
|
||||
)
|
||||
|
||||
@ -155,6 +155,9 @@ func (ts *mysqlTrialPeriodStorage) UpdateTrialPeriod(id string, updates models.U
|
||||
if updates.CustomerName != nil {
|
||||
existing.CustomerName = *updates.CustomerName
|
||||
}
|
||||
if updates.Source != nil {
|
||||
existing.Source = *updates.Source
|
||||
}
|
||||
if updates.StartTime != nil {
|
||||
startTime, err := time.Parse(time.RFC3339, *updates.StartTime)
|
||||
if err == nil {
|
||||
@ -173,7 +176,7 @@ func (ts *mysqlTrialPeriodStorage) UpdateTrialPeriod(id string, updates models.U
|
||||
|
||||
query := `
|
||||
UPDATE trial_periods
|
||||
SET customer_name = ?, start_time = ?, end_time = ?, is_trial = ?
|
||||
SET customer_name = ?, source = ?, start_time = ?, end_time = ?, is_trial = ?
|
||||
WHERE id = ?
|
||||
`
|
||||
|
||||
@ -183,7 +186,7 @@ func (ts *mysqlTrialPeriodStorage) UpdateTrialPeriod(id string, updates models.U
|
||||
}
|
||||
|
||||
_, err = ts.db.Exec(query,
|
||||
existing.CustomerName, existing.StartTime, existing.EndTime,
|
||||
existing.CustomerName, existing.Source, existing.StartTime, existing.EndTime,
|
||||
isTrial, id,
|
||||
)
|
||||
|
||||
|
||||
@ -136,6 +136,9 @@ func (ts *trialPeriodStorage) UpdateTrialPeriod(id string, updates models.Update
|
||||
|
||||
for i, period := range trialPeriods {
|
||||
if period.ID == id {
|
||||
if updates.Source != nil {
|
||||
trialPeriods[i].Source = *updates.Source
|
||||
}
|
||||
if updates.StartTime != nil {
|
||||
startTime, err := time.Parse(time.RFC3339, *updates.StartTime)
|
||||
if err == nil {
|
||||
|
||||
@ -6,6 +6,7 @@ import "time"
|
||||
type TrialPeriod struct {
|
||||
ID string `json:"id"`
|
||||
CustomerName string `json:"customerName"` // 直接存储客户名称
|
||||
Source string `json:"source"` // 客户来源
|
||||
StartTime time.Time `json:"startTime"`
|
||||
EndTime time.Time `json:"endTime"`
|
||||
IsTrial bool `json:"isTrial"`
|
||||
@ -15,6 +16,7 @@ type TrialPeriod struct {
|
||||
// CreateTrialPeriodRequest represents the request to create a trial period
|
||||
type CreateTrialPeriodRequest struct {
|
||||
CustomerName string `json:"customerName"` // 直接使用客户名称
|
||||
Source string `json:"source"` // 客户来源
|
||||
StartTime string `json:"startTime"`
|
||||
EndTime string `json:"endTime"`
|
||||
IsTrial bool `json:"isTrial"`
|
||||
@ -23,6 +25,7 @@ type CreateTrialPeriodRequest struct {
|
||||
// UpdateTrialPeriodRequest represents the request to update a trial period
|
||||
type UpdateTrialPeriodRequest struct {
|
||||
CustomerName *string `json:"customerName,omitempty"`
|
||||
Source *string `json:"source,omitempty"` // 客户来源
|
||||
StartTime *string `json:"startTime,omitempty"`
|
||||
EndTime *string `json:"endTime,omitempty"`
|
||||
IsTrial *bool `json:"isTrial,omitempty"`
|
||||
|
||||
64
tools/add_source_column.go
Normal file
64
tools/add_source_column.go
Normal file
@ -0,0 +1,64 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 从环境变量或默认值获取数据库配置
|
||||
host := getEnv("DB_HOST", "mysql1.rdsmbk3ednsgnnt.rds.bj.baidubce.com")
|
||||
port := getEnv("DB_PORT", "3306")
|
||||
user := getEnv("DB_USER", "root_dev")
|
||||
password := getEnv("DB_PASSWORD", "Kdse89sd")
|
||||
dbname := getEnv("DB_NAME", "crm_db")
|
||||
|
||||
dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?parseTime=true", user, password, host, port, dbname)
|
||||
|
||||
db, err := sql.Open("mysql", dsn)
|
||||
if err != nil {
|
||||
log.Fatalf("连接数据库失败: %v", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
if err := db.Ping(); err != nil {
|
||||
log.Fatalf("数据库连接测试失败: %v", err)
|
||||
}
|
||||
log.Println("✅ 数据库连接成功")
|
||||
|
||||
// 检查 source 列是否存在
|
||||
var columnExists bool
|
||||
err = db.QueryRow(`
|
||||
SELECT COUNT(*) > 0
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = ? AND TABLE_NAME = 'trial_periods' AND COLUMN_NAME = 'source'
|
||||
`, dbname).Scan(&columnExists)
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("检查列是否存在失败: %v", err)
|
||||
}
|
||||
|
||||
if columnExists {
|
||||
log.Println("✅ source 列已存在,无需迁移")
|
||||
return
|
||||
}
|
||||
|
||||
// 添加 source 列
|
||||
_, err = db.Exec(`ALTER TABLE trial_periods ADD COLUMN source VARCHAR(255) DEFAULT '' AFTER customer_name`)
|
||||
if err != nil {
|
||||
log.Fatalf("添加 source 列失败: %v", err)
|
||||
}
|
||||
|
||||
log.Println("✅ 成功添加 source (客户来源) 列到 trial_periods 表")
|
||||
}
|
||||
|
||||
func getEnv(key, defaultValue string) string {
|
||||
if value := os.Getenv(key); value != "" {
|
||||
return value
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user