diff --git a/app/terminal/model/dishes/Foods.php b/app/terminal/model/dishes/Foods.php index 3cc1b96..d1eb8bd 100644 --- a/app/terminal/model/dishes/Foods.php +++ b/app/terminal/model/dishes/Foods.php @@ -11,9 +11,22 @@ namespace app\terminal\model\dishes; use app\common\model\dishes\Foods as FoodsModel; use app\common\enum\dishes\FoodsStatus as FoodsStatusEnum; use app\common\enum\dishes\GasFood as GasFoodEnum; +use app\common\service\FixedScaleDecimalResponse; class Foods extends FoodsModel { + /** Preserve MySQL DECIMAL(10,2) JSON strings without changing raw model values. */ + public function toArray(): array + { + return FixedScaleDecimalResponse::fields(parent::toArray(), [ + 'energy', 'carbohydrate', 'protein', 'fat', 'sfa', 'mufa', 'pufa', 'cholesterol', + 'va', 'renieratene', 'retinol', 'vb1', 'vb2', 'vpp', 'vc', 've', 'folic_acid', + 'biotin', 'pantothenic_acid', 'choline', 'potassium', 'sodium', 'calcium', + 'magnesium', 'iron', 'zinc', 'selenium', 'iodine', 'phosphorus', 'cuprum', + 'manganese', 'purine', 'dietary_fiber', 'moisture', 'ash', 'gi', 'gl', + ]); + } + /** * 列表 * @param array $param @@ -113,4 +126,4 @@ class Foods extends FoodsModel $createData = $this->createData($data); return $this->save($createData); } -} \ No newline at end of file +} diff --git a/app/terminal/model/dishes/Dishes.php b/app/terminal/model/dishes/Dishes.php index 2082a52..adf9b4f 100644 --- a/app/terminal/model/dishes/Dishes.php +++ b/app/terminal/model/dishes/Dishes.php @@ -11,9 +11,34 @@ namespace app\terminal\model\dishes; use app\common\model\dishes\Dishes as DishesModel; use app\common\enum\dishes\MealTimes as MealTimesEnum; use app\common\enum\dishes\RawFlag as RawFlagEnum; +use app\common\service\FixedScaleDecimalResponse; class Dishes extends DishesModel { + /** Normalize only observed DECIMAL response projections, including recipe pivots. */ + public function toArray(): array + { + $row = FixedScaleDecimalResponse::fields(parent::toArray(), [ + 'weight', 'raw_flag_weight', + 'energy', 'carbohydrate', 'protein', 'fat', 'sfa', 'mufa', 'pufa', 'cholesterol', + 'va', 'renieratene', 'retinol', 'vb1', 'vb2', 'vpp', 'vc', 've', 'folic_acid', + 'biotin', 'pantothenic_acid', 'choline', 'potassium', 'sodium', 'calcium', + 'magnesium', 'iron', 'zinc', 'selenium', 'iodine', 'phosphorus', 'cuprum', + 'manganese', 'purine', 'dietary_fiber', 'moisture', 'ash', 'gi', 'gl', + ]); + if (isset($row['foods']) && is_array($row['foods'])) { + foreach ($row['foods'] as &$food) { + if (isset($food['pivot']) && is_array($food['pivot'])) { + $food['pivot'] = FixedScaleDecimalResponse::fields($food['pivot'], [ + 'foods_weight', 'foods_ratio', + ]); + } + } + unset($food); + } + return $row; + } + /** * 列表 * @param array $param @@ -151,4 +176,4 @@ class Dishes extends DishesModel //todo return true; } -} \ No newline at end of file +} diff --git a/tests/TerminalCatalogDecimalResponseContractTest.php b/tests/TerminalCatalogDecimalResponseContractTest.php new file mode 100644 --- /dev/null +++ b/tests/TerminalCatalogDecimalResponseContractTest.php @@ -0,0 +1,63 @@ +setPsr4('app\\', $overlay . '/app'); + $loader->setPsr4('cores\\', $overlay . '/cores'); +} +if (!function_exists('app_name')) { function app_name(): string { return 'terminal'; } } +if (!function_exists('getStoreId')) { function getStoreId(): int { return 10001; } } + +$assertions = 0; +$assert = static function ($actual, $expected, string $message) use (&$assertions): void { + ++$assertions; + if ($actual !== $expected) throw new RuntimeException($message); +}; +$nutrition = [ + 'energy','carbohydrate','protein','fat','sfa','mufa','pufa','cholesterol', + 'va','renieratene','retinol','vb1','vb2','vpp','vc','ve','folic_acid', + 'biotin','pantothenic_acid','choline','potassium','sodium','calcium', + 'magnesium','iron','zinc','selenium','iodine','phosphorus','cuprum', + 'manganese','purine','dietary_fiber','moisture','ash','gi','gl', +]; +foreach ([\app\terminal\model\dishes\Foods::class => $nutrition, + \app\terminal\model\dishes\Dishes::class => array_merge(['weight','raw_flag_weight'], $nutrition)] as $class => $fields) { + foreach ([[1.25,'1.25'],[0,'0.00'],[-1.25,'-1.25'],[null,null], + ['12.50','12.50'],['99999999.99','99999999.99'], + ['9007199254740993.01','9007199254740993.01']] as [$raw,$expected]) { + $data = array_fill_keys($fields, $raw) + ['id'=>9007199254740993, + 'category'=>3,'is_delete'=>0,'float_probe'=>1.2345,'name'=>'test']; + $model = new $class($data); + $row = $model->toArray(); + foreach ($fields as $field) { + $assert($row[$field], $expected, $class . ' ' . $field . ' serialized DECIMAL type/value'); + $assert($model[$field], $raw, $class . ' ' . $field . ' must not mutate raw model data'); + } + $assert($row['id'], 9007199254740993, 'Identifier must remain exact integer'); + $assert($row['category'], 3, 'Integer category must not stringify'); + $assert($row['float_probe'], 1.2345, 'Non-whitelisted float must not change'); + $assert(json_decode(json_encode($model), true)['energy'], $expected, 'JSON must use the same DECIMAL contract'); + } + $empty = new $class(['id'=>1]); + $assert(array_key_exists('energy', $empty->toArray()), false, 'Do not synthesize absent columns'); +} + +$dish = new \app\terminal\model\dishes\Dishes(['id'=>1,'weight'=>12.5]); +$food = new \app\terminal\model\dishes\Foods(['id'=>2,'name'=>'fixture','energy'=>3.5]); +$pivot = new \think\model\Pivot(['dishes_id'=>1,'foods_id'=>2, + 'foods_weight'=>2.5,'foods_ratio'=>0,'ssb'=>1.2345]); +$food->setRelation('pivot', $pivot); +$dish->setRelation('foods', new \think\model\Collection([$food])); +$row = $dish->toArray(); +$assert($row['foods'][0]['pivot']['foods_weight'], '2.50', 'Nested foods_weight must retain two-place string'); +$assert($row['foods'][0]['pivot']['foods_ratio'], '0.00', 'Nested foods_ratio must retain two-place string'); +$assert($row['foods'][0]['pivot']['foods_id'], 2, 'Nested pivot ID must not stringify'); +$assert($row['foods'][0]['pivot']['ssb'], 1.2345, 'Unobserved pivot fields remain unchanged'); +$assert($pivot['foods_weight'], 2.5, 'Nested raw pivot must not mutate'); +$pivot->setAttr('foods_weight', null); +$assert($dish->toArray()['foods'][0]['pivot']['foods_weight'], null, 'Nested null must remain null'); + +echo 'TERMINAL_CATALOG_DECIMAL_CONTRACT_PASS assertions=' . $assertions . "\n";