From abb1c8500cd982107fb91352c28cefdf8cb952eb Mon Sep 17 00:00:00 2001 From: vipg Date: Wed, 19 Nov 2025 17:03:20 +0800 Subject: [PATCH] add --- .../lib/pages/country_add_page.dart | 37 +++- frontend/asset_assistant/lib/pages/create.go | 205 ++++++++++++++++++ 2 files changed, 233 insertions(+), 9 deletions(-) create mode 100644 frontend/asset_assistant/lib/pages/create.go diff --git a/frontend/asset_assistant/lib/pages/country_add_page.dart b/frontend/asset_assistant/lib/pages/country_add_page.dart index dae3c75..4e889b8 100644 --- a/frontend/asset_assistant/lib/pages/country_add_page.dart +++ b/frontend/asset_assistant/lib/pages/country_add_page.dart @@ -11,9 +11,10 @@ class AddCountryPage extends StatefulWidget { } class _AddCountryPageState extends State { - // 输入控制器 + // 输入控制器 - 新增国旗控制器 final TextEditingController _nameController = TextEditingController(); final TextEditingController _codeController = TextEditingController(); + final TextEditingController _flagController = TextEditingController(); // 新增 // 加载状态 bool _isLoading = false; @@ -21,7 +22,7 @@ class _AddCountryPageState extends State { // 表单验证键 final _formKey = GlobalKey(); - // 创建国家 + // 创建国家 - 调整请求数据 Future _createCountry() async { if (!_formKey.currentState!.validate()) { return; @@ -32,7 +33,7 @@ class _AddCountryPageState extends State { }); try { - // 获取用户ID + // 获取用户ID(保持不变) final prefs = await SharedPreferences.getInstance(); final userId = prefs.getString('user_id'); if (userId == null) { @@ -43,7 +44,7 @@ class _AddCountryPageState extends State { return; } - // 准备请求数据 + // 准备请求数据 - 新增flag字段 final baseUrl = HostUtils().currentHost; const path = '/country/create'; final url = '$baseUrl$path'; @@ -51,9 +52,10 @@ class _AddCountryPageState extends State { final requestData = { 'name': _nameController.text.trim(), 'code': _codeController.text.trim(), + 'flag': _flagController.text.trim(), // 新增国旗参数 }; - // 发送请求 + // 发送请求(保持不变) final dio = Dio(); final response = await dio.post( url, @@ -61,7 +63,7 @@ class _AddCountryPageState extends State { options: Options(headers: {'Content-Type': 'application/json'}), ); - // 处理响应 + // 处理响应(保持不变) if (response.statusCode == 200) { final result = response.data; if (result['success'] == true) { @@ -81,6 +83,7 @@ class _AddCountryPageState extends State { } } } on DioException catch (e) { + // 异常处理(保持不变) String errorMessage = '网络请求失败'; if (e.response != null) { errorMessage = '请求失败: ${e.response?.statusCode}'; @@ -106,7 +109,7 @@ class _AddCountryPageState extends State { } } - // 显示对话框 + // 显示对话框(保持不变) void _showDialog(String title, String content, [VoidCallback? onConfirm]) { showDialog( context: context, @@ -159,7 +162,7 @@ class _AddCountryPageState extends State { key: _formKey, child: Column( children: [ - // 国家名称输入框 + // 国家名称输入框(保持不变) TextFormField( controller: _nameController, style: TextStyle(color: theme.colorScheme.onSurface), @@ -180,7 +183,7 @@ class _AddCountryPageState extends State { ), const SizedBox(height: 24), - // 国家代码输入框 + // 国家代码输入框(保持不变) TextFormField( controller: _codeController, style: TextStyle(color: theme.colorScheme.onSurface), @@ -199,6 +202,22 @@ class _AddCountryPageState extends State { return null; }, ), + const SizedBox(height: 24), + + // 新增国旗输入框 + TextFormField( + controller: _flagController, + style: TextStyle(color: theme.colorScheme.onSurface), + decoration: InputDecoration( + labelText: '国旗URL', + hintText: '请输入国旗图片URL(可选)', + prefixIcon: Icon( + Icons.flag, + color: theme.colorScheme.secondary, + ), + ), + // 国旗为可选字段,不添加验证器 + ), ], ), ), diff --git a/frontend/asset_assistant/lib/pages/create.go b/frontend/asset_assistant/lib/pages/create.go new file mode 100644 index 0000000..a9872ca --- /dev/null +++ b/frontend/asset_assistant/lib/pages/create.go @@ -0,0 +1,205 @@ +package logic4country + +import ( + "asset_assistant/db" + "net/http" + "time" + + "github.com/gin-gonic/gin" + "github.com/google/uuid" + "go.uber.org/zap" +) + +// CreateRequest 注册请求参数结构 +type CreateRequest struct { + Name string `json:"name" binding:"required"` // 国家名称,必填 + Code string `json:"code" binding:"required"` // 国家代码,必填 + Flag string `json:"flag"` // 国旗信息,可选 +} + +// CreateResponse 注册响应结构 +type CreateResponse struct { + Success bool `json:"success"` + Message string `json:"message"` + Data CreateData `json:"data"` +} + +// CreateData 响应数据结构 +type CreateData struct { + CountryID string `json:"country_id"` +} + +// CreateHandler 处理国家创建逻辑 +func CreateHandler(c *gin.Context) { + startTime := time.Now() + 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 + 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), + zap.String("flag", req.Flag), // 新增国旗参数日志 + ) + + // 开启数据库事务 + 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 + } + + 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, CreateResponse{ + Success: false, + Message: "系统错误,请稍后重试", + }) + } + }() + + // 1. 创建country主表记录 + 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. 插入国家名称 + _, err = tx.Exec("INSERT INTO country_name (country_id, name) VALUES ($1, $2)", countryID, req.Name) + if err != nil { + tx.Rollback() + zap.L().Error("❌ country_name表插入失败", + zap.String("req_id", reqID), + zap.String("country_id", countryID), + zap.Error(err), + ) + c.JSON(http.StatusInternalServerError, CreateResponse{ + Success: false, + Message: "保存名称信息失败", + }) + return + } + + // 3. 插入国家代码 + _, err = tx.Exec("INSERT INTO country_code (country_id, code) VALUES ($1, $2)", countryID, req.Code) + if err != nil { + tx.Rollback() + zap.L().Error("❌ country_code表插入失败", + zap.String("req_id", reqID), + zap.String("country_id", countryID), + zap.Error(err), + ) + c.JSON(http.StatusInternalServerError, CreateResponse{ + Success: false, + Message: "保存代码信息失败", + }) + return + } + + // 4. 新增:插入国旗信息(如果提供) + if req.Flag != "" { + _, err = tx.Exec("INSERT INTO country_flag (country_id, flag) VALUES ($1, $2)", countryID, req.Flag) + if err != nil { + tx.Rollback() + zap.L().Error("❌ country_flag表插入失败", + zap.String("req_id", reqID), + zap.String("country_id", countryID), + zap.Error(err), + ) + c.JSON(http.StatusInternalServerError, CreateResponse{ + Success: false, + Message: "保存国旗信息失败", + }) + return + } + zap.L().Debug("📝 country_flag表插入成功", + zap.String("req_id", reqID), + zap.String("country_id", countryID), + ) + } + + // 提交事务 + 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), + ) + + c.JSON(http.StatusOK, CreateResponse{ + Success: true, + Message: "创建成功", + Data: CreateData{ + CountryID: countryID, + }, + }) +} \ No newline at end of file