add
This commit is contained in:
@@ -1 +1,387 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
|
|
||||||
|
// 国家数据模型
|
||||||
|
class Country {
|
||||||
|
final String countryId;
|
||||||
|
final String name;
|
||||||
|
final String code;
|
||||||
|
|
||||||
|
Country({required this.countryId, required this.name, required this.code});
|
||||||
|
|
||||||
|
// 从JSON构建对象
|
||||||
|
factory Country.fromJson(Map<String, dynamic> json) {
|
||||||
|
return Country(
|
||||||
|
countryId: json['country_id'],
|
||||||
|
name: json['name'],
|
||||||
|
code: json['code'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 转换为JSON
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {'country_id': countryId, 'name': name, 'code': code};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CountryHomePage extends StatefulWidget {
|
||||||
|
const CountryHomePage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<CountryHomePage> createState() => _CountryHomePageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CountryHomePageState extends State<CountryHomePage> {
|
||||||
|
// 文本编辑控制器
|
||||||
|
final TextEditingController _nameController = TextEditingController();
|
||||||
|
final TextEditingController _codeController = TextEditingController();
|
||||||
|
|
||||||
|
// 国家列表
|
||||||
|
List<Country> _countries = [];
|
||||||
|
bool _isLoading = false;
|
||||||
|
|
||||||
|
// Dio实例
|
||||||
|
final Dio _dio = Dio();
|
||||||
|
final String _baseUrl = 'https://api.fishestlife.com';
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_fetchCountries();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取国家列表
|
||||||
|
Future<void> _fetchCountries() async {
|
||||||
|
setState(() {
|
||||||
|
_isLoading = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
final response = await _dio.post(
|
||||||
|
'$_baseUrl/country/read',
|
||||||
|
data: {}, // 空参数表示获取所有国家
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.data['success'] == true) {
|
||||||
|
final List<dynamic> items = response.data['data']['items'];
|
||||||
|
setState(() {
|
||||||
|
_countries = items.map((item) => Country.fromJson(item)).toList();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
_showErrorDialog('获取失败', response.data['message'] ?? '未知错误');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
_showErrorDialog('网络错误', e.toString());
|
||||||
|
} finally {
|
||||||
|
setState(() {
|
||||||
|
_isLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建新国家
|
||||||
|
Future<void> _createCountry() async {
|
||||||
|
final name = _nameController.text.trim();
|
||||||
|
final code = _codeController.text.trim();
|
||||||
|
|
||||||
|
if (name.isEmpty || code.isEmpty) {
|
||||||
|
_showErrorDialog('输入错误', '国家名称和代码不能为空');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
_isLoading = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
final response = await _dio.post(
|
||||||
|
'$_baseUrl/country/create',
|
||||||
|
data: {'name': name, 'code': code},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.data['success'] == true) {
|
||||||
|
// 清空输入框
|
||||||
|
_nameController.clear();
|
||||||
|
_codeController.clear();
|
||||||
|
// 重新获取列表
|
||||||
|
_fetchCountries();
|
||||||
|
ScaffoldMessenger.of(
|
||||||
|
context,
|
||||||
|
).showSnackBar(const SnackBar(content: Text('创建成功')));
|
||||||
|
} else {
|
||||||
|
_showErrorDialog('创建失败', response.data['message'] ?? '未知错误');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
_showErrorDialog('网络错误', e.toString());
|
||||||
|
} finally {
|
||||||
|
setState(() {
|
||||||
|
_isLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新国家信息
|
||||||
|
Future<void> _updateCountry(Country country) async {
|
||||||
|
setState(() {
|
||||||
|
_isLoading = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
final response = await _dio.post(
|
||||||
|
'$_baseUrl/country/update',
|
||||||
|
data: country.toJson(),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.data['success'] == true) {
|
||||||
|
_fetchCountries();
|
||||||
|
ScaffoldMessenger.of(
|
||||||
|
context,
|
||||||
|
).showSnackBar(const SnackBar(content: Text('更新成功')));
|
||||||
|
} else {
|
||||||
|
_showErrorDialog('更新失败', response.data['message'] ?? '未知错误');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
_showErrorDialog('网络错误', e.toString());
|
||||||
|
} finally {
|
||||||
|
setState(() {
|
||||||
|
_isLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除国家
|
||||||
|
Future<void> _deleteCountry(String countryId) async {
|
||||||
|
if (!await _showConfirmationDialog('确认删除', '确定要删除这个国家吗?')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
_isLoading = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
final response = await _dio.post(
|
||||||
|
'$_baseUrl/country/delete',
|
||||||
|
data: {'country_id': countryId},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.data['success'] == true) {
|
||||||
|
_fetchCountries();
|
||||||
|
ScaffoldMessenger.of(
|
||||||
|
context,
|
||||||
|
).showSnackBar(const SnackBar(content: Text('删除成功')));
|
||||||
|
} else {
|
||||||
|
_showErrorDialog('删除失败', response.data['message'] ?? '未知错误');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
_showErrorDialog('网络错误', e.toString());
|
||||||
|
} finally {
|
||||||
|
setState(() {
|
||||||
|
_isLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示编辑对话框
|
||||||
|
void _showEditDialog(Country country) {
|
||||||
|
final TextEditingController nameController = TextEditingController(
|
||||||
|
text: country.name,
|
||||||
|
);
|
||||||
|
final TextEditingController codeController = TextEditingController(
|
||||||
|
text: country.code,
|
||||||
|
);
|
||||||
|
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AlertDialog(
|
||||||
|
title: const Text('编辑国家'),
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
TextField(
|
||||||
|
controller: nameController,
|
||||||
|
decoration: const InputDecoration(labelText: '国家名称'),
|
||||||
|
),
|
||||||
|
TextField(
|
||||||
|
controller: codeController,
|
||||||
|
decoration: const InputDecoration(labelText: '国家代码'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.pop(context),
|
||||||
|
child: const Text('取消'),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
final updatedCountry = Country(
|
||||||
|
countryId: country.countryId,
|
||||||
|
name: nameController.text.trim(),
|
||||||
|
code: codeController.text.trim(),
|
||||||
|
);
|
||||||
|
_updateCountry(updatedCountry);
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
child: const Text('保存'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示错误对话框
|
||||||
|
void _showErrorDialog(String title, String message) {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AlertDialog(
|
||||||
|
title: Text(title),
|
||||||
|
content: Text(message),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.pop(context),
|
||||||
|
child: const Text('确定'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示确认对话框
|
||||||
|
Future<bool> _showConfirmationDialog(String title, String message) async {
|
||||||
|
return await showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AlertDialog(
|
||||||
|
title: Text(title),
|
||||||
|
content: Text(message),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.pop(context, false),
|
||||||
|
child: const Text('取消'),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.pop(context, true),
|
||||||
|
child: const Text('确认'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
) ??
|
||||||
|
false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(title: const Text('国家管理')),
|
||||||
|
body: Column(
|
||||||
|
children: [
|
||||||
|
// 顶部输入区域
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Card(
|
||||||
|
elevation: 4,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
const Text(
|
||||||
|
'添加新国家',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
TextField(
|
||||||
|
controller: _nameController,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
labelText: '国家名称',
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
TextField(
|
||||||
|
controller: _codeController,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
labelText: '国家代码',
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: _isLoading ? null : _createCountry,
|
||||||
|
child: _isLoading
|
||||||
|
? const CircularProgressIndicator()
|
||||||
|
: const Text('保存'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// 底部列表区域
|
||||||
|
Expanded(
|
||||||
|
child: _isLoading
|
||||||
|
? const Center(child: CircularProgressIndicator())
|
||||||
|
: _countries.isEmpty
|
||||||
|
? const Center(child: Text('没有国家数据'))
|
||||||
|
: GridView.builder(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
gridDelegate:
|
||||||
|
const SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
|
crossAxisCount: 1,
|
||||||
|
childAspectRatio: 4,
|
||||||
|
crossAxisSpacing: 10,
|
||||||
|
mainAxisSpacing: 10,
|
||||||
|
),
|
||||||
|
itemCount: _countries.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final country = _countries[index];
|
||||||
|
return Card(
|
||||||
|
elevation: 2,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
// 国家名称(可点击编辑)
|
||||||
|
InkWell(
|
||||||
|
onTap: () => _showEditDialog(country),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
country.name,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text('代码: ${country.code}'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// 删除按钮
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.delete,
|
||||||
|
color: Colors.red,
|
||||||
|
),
|
||||||
|
onPressed: () =>
|
||||||
|
_deleteCountry(country.countryId),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,193 +0,0 @@
|
|||||||
package logic
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"country/db" // 数据库操作相关包
|
|
||||||
"time" // 时间处理包
|
|
||||||
"github.com/google/uuid" // UUID生成工具
|
|
||||||
"github.com/gin-gonic/gin" // Gin框架,用于处理HTTP请求
|
|
||||||
"go.uber.org/zap" // 日志库
|
|
||||||
)
|
|
||||||
|
|
||||||
// CreateRequest 注册请求参数结构
|
|
||||||
// 用于接收客户端发送的JSON数据,绑定并验证必填字段
|
|
||||||
type CreateRequest struct {
|
|
||||||
Name string `json:"name" binding:"required"` // 国家名称,必填
|
|
||||||
Code string `json:"code" binding:"required"` // 国家代码,必填
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateResponse 注册响应结构
|
|
||||||
// 统一的API响应格式,包含成功状态、提示信息和数据
|
|
||||||
type CreateResponse struct {
|
|
||||||
Success bool `json:"success"` // 操作是否成功
|
|
||||||
Message string `json:"message"` // 提示信息
|
|
||||||
Data CreateData `json:"data"` // 响应数据
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateData 响应数据结构
|
|
||||||
// 包含创建成功后的国家ID
|
|
||||||
type CreateData struct {
|
|
||||||
CountryID string `json:"country_id"` // 国家唯一标识ID
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateHandler 处理国家创建逻辑
|
|
||||||
// 接收HTTP请求,完成参数验证、数据库事务处理并返回响应
|
|
||||||
func CreateHandler(c *gin.Context) {
|
|
||||||
startTime := time.Now() // 记录请求开始时间,用于统计耗时
|
|
||||||
// 获取或生成请求ID,用于追踪整个请求链路
|
|
||||||
reqID := c.Request.Header.Get("X-RegisterRequest-ID")
|
|
||||||
if reqID == "" {
|
|
||||||
reqID = uuid.New().String()
|
|
||||||
zap.L().Debug("✨ 生成新的请求ID", zap.String("req_id", reqID))
|
|
||||||
}
|
|
||||||
|
|
||||||
// 记录请求接收日志,包含关键追踪信息
|
|
||||||
zap.L().Info("📥 收到国家创建请求",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.String("path", c.Request.URL.Path),
|
|
||||||
zap.String("method", c.Request.Method),
|
|
||||||
)
|
|
||||||
|
|
||||||
var req CreateRequest
|
|
||||||
// 绑定并验证请求参数(检查name和code是否存在)
|
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
|
||||||
zap.L().Warn("⚠️ 请求参数验证失败",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.Error(err),
|
|
||||||
zap.Any("request_body", c.Request.Body),
|
|
||||||
)
|
|
||||||
// 返回参数错误响应
|
|
||||||
c.JSON(http.StatusBadRequest, CreateResponse{
|
|
||||||
Success: false,
|
|
||||||
Message: "请求参数错误:name和code为必填项",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 记录通过验证的请求参数
|
|
||||||
zap.L().Debug("✅ 请求参数验证通过",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.String("name", req.Name),
|
|
||||||
zap.String("code", req.Code),
|
|
||||||
)
|
|
||||||
|
|
||||||
// 开启数据库事务,确保多表操作原子性(要么全成功,要么全失败)
|
|
||||||
tx, err := db.DB.Begin()
|
|
||||||
if err != nil {
|
|
||||||
zap.L().Error("❌ 事务开启失败",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.Error(err),
|
|
||||||
)
|
|
||||||
c.JSON(http.StatusInternalServerError, CreateResponse{
|
|
||||||
Success: false,
|
|
||||||
Message: "系统错误,请稍后重试",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 延迟执行的恢复函数,处理panic情况
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil { // 捕获panic
|
|
||||||
// 回滚事务
|
|
||||||
if err := tx.Rollback(); err != nil {
|
|
||||||
zap.L().Error("💥 panic后事务回滚失败",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.Error(err),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
zap.L().Error("💥 事务处理发生panic",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.Any("recover", r),
|
|
||||||
)
|
|
||||||
// 返回系统错误响应
|
|
||||||
c.JSON(http.StatusInternalServerError, CreateResponse{
|
|
||||||
Success: false,
|
|
||||||
Message: "系统错误,请稍后重试",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// 1. 在country表中创建记录并获取自动生成的ID
|
|
||||||
var countryID string
|
|
||||||
err = tx.QueryRow("INSERT INTO country DEFAULT VALUES RETURNING id").Scan(&countryID)
|
|
||||||
if err != nil {
|
|
||||||
tx.Rollback() // 操作失败,回滚事务
|
|
||||||
zap.L().Error("❌ country表插入失败",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.Error(err),
|
|
||||||
)
|
|
||||||
c.JSON(http.StatusInternalServerError, CreateResponse{
|
|
||||||
Success: false,
|
|
||||||
Message: "创建国家记录失败",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
zap.L().Debug("📝 country表插入成功",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.String("country_id", countryID),
|
|
||||||
)
|
|
||||||
|
|
||||||
// 2. 插入国家名称到name表(与country_id关联)
|
|
||||||
_, err = tx.Exec("INSERT INTO name (country_id, name) VALUES ($1, $2)", countryID, req.Name)
|
|
||||||
if err != nil {
|
|
||||||
tx.Rollback() // 操作失败,回滚事务
|
|
||||||
zap.L().Error("❌ name表插入失败",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.String("country_id", countryID),
|
|
||||||
zap.Error(err),
|
|
||||||
)
|
|
||||||
c.JSON(http.StatusInternalServerError, CreateResponse{
|
|
||||||
Success: false,
|
|
||||||
Message: "保存名称信息失败",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. 插入国家代码到code表(与country_id关联)
|
|
||||||
_, err = tx.Exec("INSERT INTO code (country_id, code) VALUES ($1, $2)", countryID, req.Code)
|
|
||||||
if err != nil {
|
|
||||||
tx.Rollback() // 操作失败,回滚事务
|
|
||||||
zap.L().Error("❌ code表插入失败",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.String("country_id", countryID),
|
|
||||||
zap.Error(err),
|
|
||||||
)
|
|
||||||
c.JSON(http.StatusInternalServerError, CreateResponse{
|
|
||||||
Success: false,
|
|
||||||
Message: "保存代码信息失败",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 提交事务(所有操作成功后确认提交)
|
|
||||||
if err := tx.Commit(); err != nil {
|
|
||||||
tx.Rollback() // 提交失败时尝试回滚
|
|
||||||
zap.L().Error("❌ 事务提交失败",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.String("country_id", countryID),
|
|
||||||
zap.Error(err),
|
|
||||||
)
|
|
||||||
c.JSON(http.StatusInternalServerError, CreateResponse{
|
|
||||||
Success: false,
|
|
||||||
Message: "数据提交失败,请稍后重试",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 记录请求处理耗时
|
|
||||||
duration := time.Since(startTime)
|
|
||||||
zap.L().Info("✅ 国家创建请求处理完成",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.String("country_id", countryID),
|
|
||||||
zap.Duration("duration", duration),
|
|
||||||
)
|
|
||||||
|
|
||||||
// 返回成功响应,包含创建的国家ID
|
|
||||||
c.JSON(http.StatusOK, CreateResponse{
|
|
||||||
Success: true,
|
|
||||||
Message: "创建成功",
|
|
||||||
Data: CreateData{
|
|
||||||
CountryID: countryID,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,167 +0,0 @@
|
|||||||
package logic
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"country/db"
|
|
||||||
"time"
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
)
|
|
||||||
|
|
||||||
// DeleteRequest 删除请求参数结构
|
|
||||||
type DeleteRequest struct {
|
|
||||||
CountryID string `json:"country_id" binding:"required"` // 国家ID,必填
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteResponse 删除响应结构
|
|
||||||
type DeleteResponse struct {
|
|
||||||
Success bool `json:"success"` // 操作是否成功
|
|
||||||
Message string `json:"message"` // 提示信息
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteHandler 处理国家删除逻辑(软删除)
|
|
||||||
func DeleteHandler(c *gin.Context) {
|
|
||||||
startTime := time.Now()
|
|
||||||
reqID := c.Request.Header.Get("X-DeleteRequest-ID")
|
|
||||||
if reqID == "" {
|
|
||||||
reqID = uuid.New().String()
|
|
||||||
zap.L().Debug("✨ 生成新的请求ID", zap.String("req_id", reqID))
|
|
||||||
}
|
|
||||||
|
|
||||||
zap.L().Info("📥 收到国家删除请求",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.String("path", c.Request.URL.Path),
|
|
||||||
zap.String("method", c.Request.Method),
|
|
||||||
)
|
|
||||||
|
|
||||||
var req DeleteRequest
|
|
||||||
// 绑定并验证请求参数
|
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
|
||||||
zap.L().Warn("⚠️ 请求参数验证失败",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.Error(err),
|
|
||||||
)
|
|
||||||
c.JSON(http.StatusBadRequest, DeleteResponse{
|
|
||||||
Success: false,
|
|
||||||
Message: "请求参数错误:country_id为必填项",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
zap.L().Debug("✅ 请求参数验证通过",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.String("country_id", req.CountryID),
|
|
||||||
)
|
|
||||||
|
|
||||||
// 开启数据库事务
|
|
||||||
tx, err := db.DB.Begin()
|
|
||||||
if err != nil {
|
|
||||||
zap.L().Error("❌ 事务开启失败",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.Error(err),
|
|
||||||
)
|
|
||||||
c.JSON(http.StatusInternalServerError, DeleteResponse{
|
|
||||||
Success: false,
|
|
||||||
Message: "系统错误,请稍后重试",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 延迟处理panic情况
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
if err := tx.Rollback(); err != nil {
|
|
||||||
zap.L().Error("💥 panic后事务回滚失败",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.Error(err),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
zap.L().Error("💥 事务处理发生panic",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.Any("recover", r),
|
|
||||||
)
|
|
||||||
c.JSON(http.StatusInternalServerError, DeleteResponse{
|
|
||||||
Success: false,
|
|
||||||
Message: "系统错误,请稍后重试",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// 3.1 更新country表
|
|
||||||
_, err = tx.Exec("UPDATE country SET deleted = TRUE WHERE id = $1", req.CountryID)
|
|
||||||
if err != nil {
|
|
||||||
tx.Rollback()
|
|
||||||
zap.L().Error("❌ country表更新失败",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.String("country_id", req.CountryID),
|
|
||||||
zap.Error(err),
|
|
||||||
)
|
|
||||||
c.JSON(http.StatusInternalServerError, DeleteResponse{
|
|
||||||
Success: false,
|
|
||||||
Message: "删除国家记录失败",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3.2 更新name表
|
|
||||||
_, err = tx.Exec("UPDATE name SET deleted = TRUE WHERE country_id = $1", req.CountryID)
|
|
||||||
if err != nil {
|
|
||||||
tx.Rollback()
|
|
||||||
zap.L().Error("❌ name表更新失败",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.String("country_id", req.CountryID),
|
|
||||||
zap.Error(err),
|
|
||||||
)
|
|
||||||
c.JSON(http.StatusInternalServerError, DeleteResponse{
|
|
||||||
Success: false,
|
|
||||||
Message: "删除名称信息失败",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3.3 更新code表
|
|
||||||
_, err = tx.Exec("UPDATE code SET deleted = TRUE WHERE country_id = $1", req.CountryID)
|
|
||||||
if err != nil {
|
|
||||||
tx.Rollback()
|
|
||||||
zap.L().Error("❌ code表更新失败",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.String("country_id", req.CountryID),
|
|
||||||
zap.Error(err),
|
|
||||||
)
|
|
||||||
c.JSON(http.StatusInternalServerError, DeleteResponse{
|
|
||||||
Success: false,
|
|
||||||
Message: "删除代码信息失败",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 提交事务
|
|
||||||
if err := tx.Commit(); err != nil {
|
|
||||||
tx.Rollback()
|
|
||||||
zap.L().Error("❌ 事务提交失败",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.String("country_id", req.CountryID),
|
|
||||||
zap.Error(err),
|
|
||||||
)
|
|
||||||
c.JSON(http.StatusInternalServerError, DeleteResponse{
|
|
||||||
Success: false,
|
|
||||||
Message: "数据提交失败,请稍后重试",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 记录请求处理耗时
|
|
||||||
duration := time.Since(startTime)
|
|
||||||
zap.L().Info("✅ 国家删除请求处理完成",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.String("country_id", req.CountryID),
|
|
||||||
zap.Duration("duration", duration),
|
|
||||||
)
|
|
||||||
|
|
||||||
// 返回成功响应
|
|
||||||
c.JSON(http.StatusOK, DeleteResponse{
|
|
||||||
Success: true,
|
|
||||||
Message: "删除成功",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,229 +0,0 @@
|
|||||||
package logic
|
|
||||||
|
|
||||||
import (
|
|
||||||
"country/db"
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ReadRequest 读取请求参数结构
|
|
||||||
type ReadRequest struct {
|
|
||||||
CountryID string `form:"country_id"` // 国家ID,可选
|
|
||||||
Name string `form:"name"` // 国家名称,可选
|
|
||||||
Code string `form:"code"` // 国家代码,可选
|
|
||||||
Page string `form:"page"` // 页码,可选
|
|
||||||
PageSize string `form:"page_size"` // 每页条数,可选
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadData 读取响应数据结构
|
|
||||||
type ReadData struct {
|
|
||||||
Total int64 `json:"total"` // 总条数
|
|
||||||
Page int `json:"page"` // 当前页码
|
|
||||||
PageSize int `json:"page_size"`// 每页条数
|
|
||||||
Items []CountryInfoViewItem `json:"items"` // 数据列表
|
|
||||||
}
|
|
||||||
|
|
||||||
// CountryInfoViewItem 视图数据项结构
|
|
||||||
type CountryInfoViewItem struct {
|
|
||||||
CountryID string `json:"country_id"` // 国家ID
|
|
||||||
Name string `json:"name"` // 国家名称
|
|
||||||
Code string `json:"code"` // 国家代码
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadResponse 读取响应结构
|
|
||||||
type ReadResponse struct {
|
|
||||||
Success bool `json:"success"` // 操作是否成功
|
|
||||||
Message string `json:"message"` // 提示信息
|
|
||||||
Data ReadData `json:"data"` // 响应数据
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadHandler 处理国家信息查询逻辑
|
|
||||||
func ReadHandler(c *gin.Context) {
|
|
||||||
startTime := time.Now()
|
|
||||||
// 获取或生成请求ID
|
|
||||||
reqID := c.Request.Header.Get("X-ReadRequest-ID")
|
|
||||||
if reqID == "" {
|
|
||||||
reqID = uuid.New().String()
|
|
||||||
zap.L().Debug("✨ 生成新的请求ID", zap.String("req_id", reqID))
|
|
||||||
}
|
|
||||||
|
|
||||||
// 记录请求接收日志
|
|
||||||
zap.L().Info("📥 收到国家查询请求",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.String("path", c.Request.URL.Path),
|
|
||||||
zap.String("method", c.Request.Method),
|
|
||||||
)
|
|
||||||
|
|
||||||
// 绑定请求参数
|
|
||||||
var req ReadRequest
|
|
||||||
if err := c.ShouldBindQuery(&req); err != nil {
|
|
||||||
zap.L().Warn("⚠️ 请求参数解析失败",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.Error(err),
|
|
||||||
)
|
|
||||||
c.JSON(http.StatusBadRequest, ReadResponse{
|
|
||||||
Success: false,
|
|
||||||
Message: "请求参数格式错误",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证查询条件至少有一个不为空
|
|
||||||
if req.CountryID == "" && req.Name == "" && req.Code == "" {
|
|
||||||
zap.L().Warn("⚠️ 请求参数验证失败",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.String("reason", "country_id、name、code不能同时为空"),
|
|
||||||
)
|
|
||||||
c.JSON(http.StatusBadRequest, ReadResponse{
|
|
||||||
Success: false,
|
|
||||||
Message: "请求参数错误:country_id、name、code不能同时为空",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理分页参数默认值
|
|
||||||
page, err := strconv.Atoi(req.Page)
|
|
||||||
if err != nil || page < 1 {
|
|
||||||
page = 1
|
|
||||||
}
|
|
||||||
pageSize, err := strconv.Atoi(req.PageSize)
|
|
||||||
if err != nil || pageSize < 1 {
|
|
||||||
pageSize = 20
|
|
||||||
}
|
|
||||||
|
|
||||||
zap.L().Debug("✅ 请求参数验证通过",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.String("country_id", req.CountryID),
|
|
||||||
zap.String("name", req.Name),
|
|
||||||
zap.String("code", req.Code),
|
|
||||||
zap.Int("page", page),
|
|
||||||
zap.Int("page_size", pageSize),
|
|
||||||
)
|
|
||||||
|
|
||||||
// 构建查询条件和参数
|
|
||||||
whereClauses := []string{}
|
|
||||||
args := []interface{}{}
|
|
||||||
paramIndex := 1
|
|
||||||
|
|
||||||
if req.CountryID != "" {
|
|
||||||
whereClauses = append(whereClauses, "country_id = $"+strconv.Itoa(paramIndex))
|
|
||||||
args = append(args, req.CountryID)
|
|
||||||
paramIndex++
|
|
||||||
}
|
|
||||||
if req.Name != "" {
|
|
||||||
whereClauses = append(whereClauses, "name LIKE $"+strconv.Itoa(paramIndex))
|
|
||||||
args = append(args, "%"+req.Name+"%")
|
|
||||||
paramIndex++
|
|
||||||
}
|
|
||||||
if req.Code != "" {
|
|
||||||
whereClauses = append(whereClauses, "code LIKE $"+strconv.Itoa(paramIndex))
|
|
||||||
args = append(args, "%"+req.Code+"%")
|
|
||||||
paramIndex++
|
|
||||||
}
|
|
||||||
|
|
||||||
// 构建基础SQL
|
|
||||||
baseSQL := "SELECT country_id, name, code FROM country_info_view"
|
|
||||||
countSQL := "SELECT COUNT(*) FROM country_info_view"
|
|
||||||
if len(whereClauses) > 0 {
|
|
||||||
whereStr := " WHERE " + strings.Join(whereClauses, " AND ")
|
|
||||||
baseSQL += whereStr
|
|
||||||
countSQL += whereStr
|
|
||||||
}
|
|
||||||
|
|
||||||
// 计算分页偏移量
|
|
||||||
offset := (page - 1) * pageSize
|
|
||||||
|
|
||||||
// 拼接分页SQL(使用fmt.Sprintf更清晰)
|
|
||||||
querySQL := fmt.Sprintf("%s ORDER BY country_id LIMIT $%d OFFSET $%d", baseSQL, paramIndex, paramIndex+1)
|
|
||||||
args = append(args, pageSize, offset)
|
|
||||||
|
|
||||||
// 查询总条数(修正参数传递方式)
|
|
||||||
var total int64
|
|
||||||
countArgs := args[:len(args)-2] // 排除分页参数
|
|
||||||
err = db.DB.QueryRow(countSQL, countArgs...).Scan(&total)
|
|
||||||
if err != nil {
|
|
||||||
zap.L().Error("❌ 查询总条数失败",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.Error(err),
|
|
||||||
)
|
|
||||||
c.JSON(http.StatusInternalServerError, ReadResponse{
|
|
||||||
Success: false,
|
|
||||||
Message: "查询数据失败,请稍后重试",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 执行分页查询
|
|
||||||
rows, err := db.DB.Query(querySQL, args...)
|
|
||||||
if err != nil {
|
|
||||||
zap.L().Error("❌ 分页查询失败",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.Error(err),
|
|
||||||
)
|
|
||||||
c.JSON(http.StatusInternalServerError, ReadResponse{
|
|
||||||
Success: false,
|
|
||||||
Message: "查询数据失败,请稍后重试",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
// 处理查询结果
|
|
||||||
var items []CountryInfoViewItem
|
|
||||||
for rows.Next() {
|
|
||||||
var item CountryInfoViewItem
|
|
||||||
if err := rows.Scan(&item.CountryID, &item.Name, &item.Code); err != nil {
|
|
||||||
zap.L().Error("❌ 解析查询结果失败",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.Error(err),
|
|
||||||
)
|
|
||||||
c.JSON(http.StatusInternalServerError, ReadResponse{
|
|
||||||
Success: false,
|
|
||||||
Message: "数据处理失败,请稍后重试",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
items = append(items, item)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查行迭代过程中是否发生错误
|
|
||||||
if err := rows.Err(); err != nil {
|
|
||||||
zap.L().Error("❌ 行迭代错误",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.Error(err),
|
|
||||||
)
|
|
||||||
c.JSON(http.StatusInternalServerError, ReadResponse{
|
|
||||||
Success: false,
|
|
||||||
Message: "查询数据失败,请稍后重试",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 记录请求处理耗时
|
|
||||||
duration := time.Since(startTime)
|
|
||||||
zap.L().Info("✅ 国家查询请求处理完成",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.Int64("total", total),
|
|
||||||
zap.Int("page", page),
|
|
||||||
zap.Int("page_size", pageSize),
|
|
||||||
zap.Duration("duration", duration),
|
|
||||||
)
|
|
||||||
|
|
||||||
// 返回成功响应
|
|
||||||
c.JSON(http.StatusOK, ReadResponse{
|
|
||||||
Success: true,
|
|
||||||
Message: "查询成功",
|
|
||||||
Data: ReadData{
|
|
||||||
Total: total,
|
|
||||||
Page: page,
|
|
||||||
PageSize: pageSize,
|
|
||||||
Items: items,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,184 +0,0 @@
|
|||||||
package logic
|
|
||||||
|
|
||||||
import (
|
|
||||||
"country/db"
|
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
)
|
|
||||||
|
|
||||||
// UpdateRequest 更新请求参数结构
|
|
||||||
type UpdateRequest struct {
|
|
||||||
CountryID string `json:"country_id" binding:"required"` // 国家ID,必填
|
|
||||||
Name string `json:"name"` // 国家名称,可选
|
|
||||||
Code string `json:"code"` // 国家代码,可选
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateResponse 更新响应结构
|
|
||||||
type UpdateResponse struct {
|
|
||||||
Success bool `json:"success"` // 操作是否成功
|
|
||||||
Message string `json:"message"` // 提示信息
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateHandler 处理国家信息更新逻辑
|
|
||||||
func UpdateHandler(c *gin.Context) {
|
|
||||||
startTime := time.Now()
|
|
||||||
// 获取或生成请求ID
|
|
||||||
reqID := c.Request.Header.Get("X-UpdateRequest-ID")
|
|
||||||
if reqID == "" {
|
|
||||||
reqID = uuid.New().String()
|
|
||||||
zap.L().Debug("✨ 生成新的请求ID", zap.String("req_id", reqID))
|
|
||||||
}
|
|
||||||
|
|
||||||
// 记录请求接收日志
|
|
||||||
zap.L().Info("📥 收到国家更新请求",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.String("path", c.Request.URL.Path),
|
|
||||||
zap.String("method", c.Request.Method),
|
|
||||||
)
|
|
||||||
|
|
||||||
var req UpdateRequest
|
|
||||||
// 绑定并验证请求参数(主要验证country_id必填)
|
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
|
||||||
zap.L().Warn("⚠️ 请求参数验证失败",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.Error(err),
|
|
||||||
)
|
|
||||||
c.JSON(http.StatusBadRequest, UpdateResponse{
|
|
||||||
Success: false,
|
|
||||||
Message: "请求参数错误:country_id为必填项",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证name和code不能同时为空
|
|
||||||
if req.Name == "" && req.Code == "" {
|
|
||||||
zap.L().Warn("⚠️ 请求参数验证失败",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.String("country_id", req.CountryID),
|
|
||||||
zap.String("reason", "name和code不能同时为空"),
|
|
||||||
)
|
|
||||||
c.JSON(http.StatusBadRequest, UpdateResponse{
|
|
||||||
Success: false,
|
|
||||||
Message: "请求参数错误:name和code不能同时为空",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
zap.L().Debug("✅ 请求参数验证通过",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.String("country_id", req.CountryID),
|
|
||||||
zap.String("name", req.Name),
|
|
||||||
zap.String("code", req.Code),
|
|
||||||
)
|
|
||||||
|
|
||||||
// 开启数据库事务
|
|
||||||
tx, err := db.DB.Begin()
|
|
||||||
if err != nil {
|
|
||||||
zap.L().Error("❌ 事务开启失败",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.Error(err),
|
|
||||||
)
|
|
||||||
c.JSON(http.StatusInternalServerError, UpdateResponse{
|
|
||||||
Success: false,
|
|
||||||
Message: "系统错误,请稍后重试",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 延迟处理panic情况
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
if err := tx.Rollback(); err != nil {
|
|
||||||
zap.L().Error("💥 panic后事务回滚失败",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.Error(err),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
zap.L().Error("💥 事务处理发生panic",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.Any("recover", r),
|
|
||||||
)
|
|
||||||
c.JSON(http.StatusInternalServerError, UpdateResponse{
|
|
||||||
Success: false,
|
|
||||||
Message: "系统错误,请稍后重试",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// 如果name不为空,更新name表
|
|
||||||
if req.Name != "" {
|
|
||||||
_, err = tx.Exec("UPDATE name SET name = $1 WHERE country_id = $2", req.Name, req.CountryID)
|
|
||||||
if err != nil {
|
|
||||||
tx.Rollback()
|
|
||||||
zap.L().Error("❌ name表更新失败",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.String("country_id", req.CountryID),
|
|
||||||
zap.Error(err),
|
|
||||||
)
|
|
||||||
c.JSON(http.StatusInternalServerError, UpdateResponse{
|
|
||||||
Success: false,
|
|
||||||
Message: "更新名称信息失败",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
zap.L().Debug("📝 name表更新成功",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.String("country_id", req.CountryID),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果code不为空,更新code表
|
|
||||||
if req.Code != "" {
|
|
||||||
_, err = tx.Exec("UPDATE code SET code = $1 WHERE country_id = $2", req.Code, req.CountryID)
|
|
||||||
if err != nil {
|
|
||||||
tx.Rollback()
|
|
||||||
zap.L().Error("❌ code表更新失败",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.String("country_id", req.CountryID),
|
|
||||||
zap.Error(err),
|
|
||||||
)
|
|
||||||
c.JSON(http.StatusInternalServerError, UpdateResponse{
|
|
||||||
Success: false,
|
|
||||||
Message: "更新代码信息失败",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
zap.L().Debug("📝 code表更新成功",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.String("country_id", req.CountryID),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 提交事务
|
|
||||||
if err := tx.Commit(); err != nil {
|
|
||||||
tx.Rollback()
|
|
||||||
zap.L().Error("❌ 事务提交失败",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.String("country_id", req.CountryID),
|
|
||||||
zap.Error(err),
|
|
||||||
)
|
|
||||||
c.JSON(http.StatusInternalServerError, UpdateResponse{
|
|
||||||
Success: false,
|
|
||||||
Message: "数据提交失败,请稍后重试",
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 记录请求处理耗时
|
|
||||||
duration := time.Since(startTime)
|
|
||||||
zap.L().Info("✅ 国家更新请求处理完成",
|
|
||||||
zap.String("req_id", reqID),
|
|
||||||
zap.String("country_id", req.CountryID),
|
|
||||||
zap.Duration("duration", duration),
|
|
||||||
)
|
|
||||||
|
|
||||||
// 返回成功响应
|
|
||||||
c.JSON(http.StatusOK, UpdateResponse{
|
|
||||||
Success: true,
|
|
||||||
Message: "更新成功",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user